/* * Copyright (c) 2001-2002 Packet Design, LLC. * All rights reserved. * * Subject to the following obligations and disclaimer of warranty, * use and redistribution of this software, in source or object code * forms, with or without modifications are expressly permitted by * Packet Design; provided, however, that: * * (i) Any and all reproductions of the source or object code * must include the copyright notice above and the following * disclaimer of warranties; and * (ii) No rights are granted, in any manner or form, to use * Packet Design trademarks, including the mark "PACKET DESIGN" * on advertising, endorsements, or otherwise except as such * appears in the above copyright notice or in the software. * * THIS SOFTWARE IS BEING PROVIDED BY PACKET DESIGN "AS IS", AND * TO THE MAXIMUM EXTENT PERMITTED BY LAW, PACKET DESIGN MAKES NO * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING * THIS SOFTWARE, INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, * OR NON-INFRINGEMENT. PACKET DESIGN DOES NOT WARRANT, GUARANTEE, * OR MAKE ANY REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS * OF THE USE OF THIS SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, * RELIABILITY OR OTHERWISE. IN NO EVENT SHALL PACKET DESIGN BE * LIABLE FOR ANY DAMAGES RESULTING FROM OR ARISING OUT OF ANY USE * OF THIS SOFTWARE, INCLUDING WITHOUT LIMITATION, ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, PUNITIVE, OR CONSEQUENTIAL * DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, LOSS OF * USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF * THE USE OF THIS SOFTWARE, EVEN IF PACKET DESIGN IS ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. * * Author: Archie Cobbs */ #include "ppp/ppp_defs.h" #include "ppp/ppp_log.h" #include "ppp/ppp_fsm_option.h" #include "ppp/ppp_fsm.h" #include "ppp/ppp_auth.h" #include "ppp/ppp_node.h" #include "ppp/ppp_ccp.h" #include #ifndef MPPE_56 #define MPPE_56 0x00000080 #undef MPPE_BITS #define MPPE_BITS 0x000000e0 #endif /* * CCP FSM * * XXX This is hard coded to only support MPPC/MPPE compression. * XXX It should be generalized to support multiple PPP compression types. */ #define MPPC_SUPPORTED (MPPC_BIT | MPPE_BITS | MPPE_STATELESS) /* Memory type */ #define CCP_MTYPE "ccp" /* CCP compression types */ enum ccp_type { CCP_OPT_OUI =0, /* oui */ CCP_OPT_PRED1 =1, /* predictor type 1 */ CCP_OPT_PRED2 =2, /* predictor type 2 */ CCP_OPT_PUDDLE =3, /* puddle jumper */ CCP_OPT_HWPPC =16, /* hewlett-packard ppc */ CCP_OPT_STAC =17, /* stac electronics lzs */ CCP_OPT_MPPC =18, /* microsoft mppc/mppe */ CCP_OPT_GAND =19, /* gandalf fza */ CCP_OPT_V42BIS =20, /* v.42bis compression */ CCP_OPT_BSD =21, /* bsd lzw compress */ CCP_OPT_DEFLATE =24, /* gzip "deflate" compression */ }; /* Supported and required FSM codes */ #define CCP_SUPPORTED_CODES \ (1 << FSM_CODE_CONFIGREQ) \ | (1 << FSM_CODE_CONFIGACK) \ | (1 << FSM_CODE_CONFIGNAK) \ | (1 << FSM_CODE_CONFIGREJ) \ | (1 << FSM_CODE_TERMREQ) \ | (1 << FSM_CODE_TERMACK) \ | (1 << FSM_CODE_CODEREJ) \ | (1 << FSM_CODE_RESETREQ) \ | (1 << FSM_CODE_RESETACK) #define CCP_REQUIRED_CODES \ (1 << FSM_CODE_CONFIGREQ) \ | (1 << FSM_CODE_CONFIGACK) \ | (1 << FSM_CODE_CONFIGNAK) \ | (1 << FSM_CODE_CONFIGREJ) \ | (1 << FSM_CODE_TERMREQ) \ | (1 << FSM_CODE_TERMACK) \ | (1 << FSM_CODE_CODEREJ) \ | (1 << FSM_CODE_RESETREQ) \ | (1 << FSM_CODE_RESETACK) /* FSM options descriptors */ static opt_pr_t ppp_cpp_pr_mppc; static const struct ppp_fsm_optdesc ccp_opt_desc[] = { { "OUI", CCP_OPT_OUI, 0, 255, 0, NULL }, { "Predictor-1",CCP_OPT_PRED1, 0, 255, 0, NULL }, { "Predictor-2",CCP_OPT_PRED2, 0, 255, 0, NULL }, { "Puddle", CCP_OPT_PUDDLE, 0, 255, 0, NULL }, { "HWPPC", CCP_OPT_HWPPC, 0, 255, 0, NULL }, { "Stac", CCP_OPT_STAC, 0, 255, 0, NULL }, { "MPPC/MPPE", CCP_OPT_MPPC, 4, 4, 1, ppp_cpp_pr_mppc }, { "Gandalf", CCP_OPT_GAND, 0, 255, 0, NULL }, { "v.42bis", CCP_OPT_V42BIS, 0, 255, 0, NULL }, { "BSD-LZW", CCP_OPT_BSD, 0, 255, 0, NULL }, { "Deflate", CCP_OPT_DEFLATE,0, 255, 0, NULL }, { NULL, 0, 0, 0, 0, NULL } }; /* FSM type for CCP */ static ppp_fsm_type_destroy_t ppp_ccp_destroy; static ppp_fsm_type_build_conf_req_t ppp_ccp_build_conf_req; static ppp_fsm_type_recv_conf_req_t ppp_ccp_recv_conf_req; static ppp_fsm_type_recv_conf_rej_t ppp_ccp_recv_conf_rej; static ppp_fsm_type_recv_conf_nak_t ppp_ccp_recv_conf_nak; static ppp_fsm_type_recv_reset_req_t ppp_ccp_recv_reset_req; static ppp_fsm_type_recv_reset_ack_t ppp_ccp_recv_reset_ack; const struct ppp_fsm_type ppp_fsm_ccp = { "CCP", PPP_PROTO_CCP, CCP_SUPPORTED_CODES, CCP_REQUIRED_CODES, ccp_opt_desc, NULL, ppp_ccp_destroy, ppp_ccp_build_conf_req, ppp_ccp_recv_conf_req, ppp_ccp_recv_conf_rej, ppp_ccp_recv_conf_nak, NULL, ppp_ccp_recv_reset_req, ppp_ccp_recv_reset_ack, NULL }; /* CCP instance state */ struct ccp { struct ppp_fsm_instance *inst; /* backpointer to instance */ struct ppp_ccp_config conf; /* initial config */ struct ppp_ccp_req req; /* current request state */ struct ppp_node *node; /* ng_ppp(4) node */ }; /* Internal functions */ static ppp_node_recvmsg_t ppp_ccp_send_reset_req; /*********************************************************************** PUBLIC FUNCTIONS ***********************************************************************/ struct ppp_fsm_instance * ppp_ccp_create(struct ppp_ccp_config *conf, struct ppp_node *node) { struct ppp_fsm_instance *inst; struct ppp_ccp_req *req; struct ccp *ccp = NULL; /* Construct instance object */ if ((inst = MALLOC(CCP_MTYPE, sizeof(*inst))) == NULL) return (NULL); memset(inst, 0, sizeof(*inst)); inst->type = &ppp_fsm_ccp; /* Attach private data */ if ((ccp = MALLOC(CCP_MTYPE, sizeof(*ccp))) == NULL) goto fail; memset(ccp, 0, sizeof(*ccp)); ccp->conf = *conf; ccp->node = node; ccp->inst = inst; inst->arg = ccp; /* Initialize local request state */ req = &ccp->req; req->mppc[PPP_SELF] = conf->mppc[PPP_SELF]; req->mppe40[PPP_SELF] = conf->mppe40[PPP_SELF]; req->mppe56[PPP_SELF] = conf->mppe56[PPP_SELF]; req->mppe128[PPP_SELF] = conf->mppe128[PPP_SELF]; req->mppe_stateless[PPP_SELF] = conf->mppe_stateless[PPP_SELF]; /* Register handler for reset-req control message from ng_mppc(4) */ if (ppp_node_set_recvmsg(ccp->node, NGM_MPPC_COOKIE, NGM_MPPC_RESETREQ, ppp_ccp_send_reset_req, ccp) == -1) goto fail; /* Done */ return (inst); fail: /* Clean up after failure */ if (ccp != NULL) FREE(CCP_MTYPE, ccp); FREE(CCP_MTYPE, inst); return (NULL); } /* * Get CCP request state. */ void ppp_ccp_get_req(struct ppp_fsm *fsm, struct ppp_ccp_req *req) { struct ppp_fsm_instance *const inst = ppp_fsm_get_instance(fsm); struct ccp *const ccp = inst->arg; assert(inst->type == &ppp_fsm_ccp); memcpy(req, &ccp->req, sizeof(*req)); } /*********************************************************************** FSM CALLBACKS ***********************************************************************/ static void ppp_ccp_destroy(struct ppp_fsm_instance *inst) { struct ccp *const ccp = inst->arg; ppp_node_set_recvmsg(ccp->node, NGM_MPPC_COOKIE, NGM_MPPC_RESETREQ, NULL, NULL); FREE(CCP_MTYPE, ccp); FREE(CCP_MTYPE, inst); } static int ppp_ccp_build_conf_req(struct ppp_fsm_instance *fsm, struct ppp_fsm_options *opts) { struct ccp *const ccp = (struct ccp *)fsm->arg; struct ppp_ccp_req *const req = &ccp->req; u_int32_t mppo = 0; /* Add MPPC/MPPE requested config options */ if (req->mppc[PPP_SELF]) mppo |= MPPC_BIT; if (req->mppe40[PPP_SELF]) mppo |= MPPE_40; if (req->mppe56[PPP_SELF]) mppo |= MPPE_56; if (req->mppe128[PPP_SELF]) mppo |= MPPE_128; if (req->mppe_stateless[PPP_SELF]) mppo |= MPPE_STATELESS; /* If no options left to try, don't ask for anything */ if ((mppo & (MPPC_BIT | MPPE_BITS)) == 0) { errno = EINVAL; return (-1); } /* Add option */ mppo = htonl(mppo); if (ppp_fsm_option_add(opts, CCP_OPT_MPPC, 4, &mppo) == -1) return (-1); /* Done */ return (0); } static int ppp_ccp_recv_conf_req(struct ppp_fsm_instance *fsm, struct ppp_fsm_options *crq, struct ppp_fsm_options *nak, struct ppp_fsm_options *rej) { struct ccp *const ccp = (struct ccp *)fsm->arg; struct ppp_ccp_config *const conf = &ccp->conf; struct ppp_ccp_req *const req = &ccp->req; int i; /* Initialize peer's request state */ req->mppc[PPP_PEER] = 0; req->mppe40[PPP_PEER] = 0; req->mppe56[PPP_PEER] = 0; req->mppe128[PPP_PEER] = 0; req->mppe_stateless[PPP_PEER] = 0; /* Process options */ for (i = 0; i < crq->num; i++) { const struct ppp_fsm_option *const opt = &crq->opts[i]; switch (opt->type) { case CCP_OPT_MPPC: { u_int32_t obits; u_int32_t bits; /* Get requested bits */ memcpy(&obits, opt->data, 4); obits = ntohl(obits); bits = obits; /* Filter out bits we can't handle */ bits &= MPPC_SUPPORTED; if ((bits & MPPC_BIT) != 0 && !conf->mppc[PPP_PEER]) bits &= ~MPPC_BIT; if ((bits & MPPE_40) != 0 && !conf->mppe40[PPP_PEER]) bits &= ~MPPE_40; if ((bits & MPPE_56) != 0 && !conf->mppe56[PPP_PEER]) bits &= ~MPPE_56; if ((bits & MPPE_128) != 0 && !conf->mppe128[PPP_PEER]) bits &= ~MPPE_128; if ((bits & MPPE_STATELESS) != 0 && !conf->mppe_stateless[PPP_PEER]) bits &= ~MPPE_STATELESS; /* * It doesn't really make sense to do MPPE encryption * in only one direction. Also, Win95/98 PPTP can't * handle uni-directional encryption. So if the remote * side doesn't request encryption, try to prompt it. * This is broken wrt. normal PPP negotiation: typical * Microsoft. */ if ((bits & MPPE_BITS) == 0) { if (req->mppe40[PPP_SELF]) bits |= MPPE_40; if (req->mppe56[PPP_SELF]) bits |= MPPE_56; if (req->mppe128[PPP_SELF]) bits |= MPPE_128; } /* Make sure we're not left with no options */ if ((bits & MPPE_BITS) == 0) { if (ccp->conf.mppe40[PPP_SELF]) bits |= MPPE_40; if (ccp->conf.mppe56[PPP_SELF]) bits |= MPPE_56; if (ccp->conf.mppe128[PPP_SELF]) bits |= MPPE_128; } /* Now choose the strongest encryption available */ if ((bits & MPPE_128) != 0) bits &= ~(MPPE_56|MPPE_40); else if ((bits & MPPE_56) != 0) bits &= ~(MPPE_128|MPPE_40); else if ((bits & MPPE_40) != 0) bits &= ~(MPPE_128|MPPE_56); /* Send back a nak if there were any changes */ if (bits != obits) { bits = htonl(bits); if (ppp_fsm_option_add(nak, opt->type, 4, &bits) == -1) return (-1); break; } /* Peer's request is accepted */ req->mppc[PPP_PEER] = (bits & MPPC_BIT) != 0; req->mppe40[PPP_PEER] = (bits & MPPE_40) != 0; req->mppe56[PPP_PEER] = (bits & MPPE_56) != 0; req->mppe128[PPP_PEER] = (bits & MPPE_128) != 0; req->mppe_stateless[PPP_PEER] = (bits & MPPE_STATELESS) != 0; break; } default: goto reject; } /* OK */ continue; reject: /* Reject this requested option */ if (ppp_fsm_option_add(rej, opt->type, opt->len, opt->data) == -1) return (-1); } /* If there were no options, shut down */ if (crq->num == 0) { errno = EINVAL; return (-1); } /* Done */ return (0); } static int ppp_ccp_recv_conf_rej(struct ppp_fsm_instance *fsm, struct ppp_fsm_options *rej) { int i; for (i = 0; i < rej->num; i++) { const struct ppp_fsm_option *const opt = &rej->opts[i]; switch (opt->type) { case CCP_OPT_MPPC: errno = EINVAL; return (-1); default: break; } } /* Done */ return (0); } static int ppp_ccp_recv_conf_nak(struct ppp_fsm_instance *fsm, struct ppp_fsm_options *nak) { struct ccp *const ccp = (struct ccp *)fsm->arg; struct ppp_ccp_req *const req = &ccp->req; int i; for (i = 0; i < nak->num; i++) { const struct ppp_fsm_option *const opt = &nak->opts[i]; switch (opt->type) { case CCP_OPT_MPPC: { u_int32_t bits; memcpy(&bits, opt->data, 4); bits = ntohl(bits); /* Mask away bits the client didn't like */ if ((bits & MPPC_BIT) == 0) req->mppc[PPP_SELF] = 0; if ((bits & MPPE_40) == 0) req->mppe40[PPP_SELF] = 0; if ((bits & MPPE_56) == 0) req->mppe56[PPP_SELF] = 0; if ((bits & MPPE_128) == 0) req->mppe128[PPP_SELF] = 0; if ((bits & MPPE_STATELESS) == 0) req->mppe_stateless[PPP_SELF] = 0; /* Make sure we're not left with no options */ if ((bits & MPPE_BITS) == 0) { if (ccp->conf.mppe40[PPP_SELF]) bits |= MPPE_40; if (ccp->conf.mppe56[PPP_SELF]) bits |= MPPE_56; if (ccp->conf.mppe128[PPP_SELF]) bits |= MPPE_128; } break; } default: break; } } /* Done */ return (0); } /* * Receive a Reset-Req from peer. Relay request to the ng_mppc(4) node. */ static void ppp_ccp_recv_reset_req(struct ppp_fsm_instance *fsm, const u_char *data, u_int len) { struct ccp *const ccp = (struct ccp *)fsm->arg; ppp_node_send_msg(ccp->node, NG_PPP_HOOK_COMPRESS, NGM_MPPC_COOKIE, NGM_MPPC_RESETREQ, NULL, 0); } /* * Receive a Reset-Ack. */ static void ppp_ccp_recv_reset_ack(struct ppp_fsm_instance *fsm, const u_char *data, u_int len) { return; /* not used by MPPC/MPPE, just ignore it */ } /*********************************************************************** INTERNAL FUNCTIONS ***********************************************************************/ static void ppp_ccp_send_reset_req(void *arg, struct ng_mesg *msg) { struct ccp *const ccp = arg; struct ppp_fsm *const fsm = ccp->inst->fsm; ppp_fsm_send_reset_req(fsm, NULL, 0); } static void ppp_cpp_pr_mppc(const struct ppp_fsm_optdesc *desc, const struct ppp_fsm_option *opt, char *buf, size_t bmax) { static const struct { u_int32_t bit; const char *name; } mppe_bits[] = { { MPPC_BIT, "MPPC" }, { MPPE_BITS, "MPPE" }, { MPPE_40, "40 bit" }, { MPPE_56, "56 bit" }, { MPPE_128, "128 bit" }, { MPPE_STATELESS, "stateless" }, { 0 } }; u_int32_t mppo; int first; int i; if (opt->len < 4) { snprintf(buf, bmax, ""); return; } memcpy(&mppo, opt->data, 4); mppo = ntohl(mppo); *buf = '\0'; for (first = 1, i = 0; mppe_bits[i].bit != 0; i++) { if ((mppo & mppe_bits[i].bit) != 0) { snprintf(buf + strlen(buf), bmax - strlen(buf), "%s%s", first ? "" : ", ", mppe_bits[i].name); first = 0; } } }