1 /* 2 * ng_mppc.c 3 */ 4 5 /*- 6 * Copyright (c) 1996-2000 Whistle Communications, Inc. 7 * All rights reserved. 8 * 9 * Subject to the following obligations and disclaimer of warranty, use and 10 * redistribution of this software, in source or object code forms, with or 11 * without modifications are expressly permitted by Whistle Communications; 12 * provided, however, that: 13 * 1. Any and all reproductions of the source or object code must include the 14 * copyright notice above and the following disclaimer of warranties; and 15 * 2. No rights are granted, in any manner or form, to use Whistle 16 * Communications, Inc. trademarks, including the mark "WHISTLE 17 * COMMUNICATIONS" on advertising, endorsements, or otherwise except as 18 * such appears in the above copyright notice or in the software. 19 * 20 * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND 21 * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO 22 * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE, 23 * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF 24 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. 25 * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY 26 * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS 27 * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE. 28 * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES 29 * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING 30 * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, 31 * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR 32 * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY 33 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 34 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 35 * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY 36 * OF SUCH DAMAGE. 37 * 38 * Author: Archie Cobbs <archie@freebsd.org> 39 * 40 * $Whistle: ng_mppc.c,v 1.4 1999/11/25 00:10:12 archie Exp $ 41 * $FreeBSD$ 42 */ 43 44 /* 45 * Microsoft PPP compression (MPPC) and encryption (MPPE) netgraph node type. 46 * 47 * You must define one or both of the NETGRAPH_MPPC_COMPRESSION and/or 48 * NETGRAPH_MPPC_ENCRYPTION options for this node type to be useful. 49 */ 50 51 #include <sys/param.h> 52 #include <sys/systm.h> 53 #include <sys/kernel.h> 54 #include <sys/mbuf.h> 55 #include <sys/malloc.h> 56 #include <sys/errno.h> 57 #include <sys/syslog.h> 58 59 #include <netgraph/ng_message.h> 60 #include <netgraph/netgraph.h> 61 #include <netgraph/ng_mppc.h> 62 63 #include "opt_netgraph.h" 64 65 #if !defined(NETGRAPH_MPPC_COMPRESSION) && !defined(NETGRAPH_MPPC_ENCRYPTION) 66 #ifdef KLD_MODULE 67 /* XXX NETGRAPH_MPPC_COMPRESSION isn't functional yet */ 68 #define NETGRAPH_MPPC_ENCRYPTION 69 #else 70 /* This case is indicative of an error in sys/conf files */ 71 #error Need either NETGRAPH_MPPC_COMPRESSION or NETGRAPH_MPPC_ENCRYPTION 72 #endif 73 #endif 74 75 #ifdef NG_SEPARATE_MALLOC 76 MALLOC_DEFINE(M_NETGRAPH_MPPC, "netgraph_mppc", "netgraph mppc node "); 77 #else 78 #define M_NETGRAPH_MPPC M_NETGRAPH 79 #endif 80 81 #ifdef NETGRAPH_MPPC_COMPRESSION 82 /* XXX this file doesn't exist yet, but hopefully someday it will... */ 83 #include <net/mppc.h> 84 #endif 85 #ifdef NETGRAPH_MPPC_ENCRYPTION 86 #include <crypto/rc4/rc4.h> 87 #endif 88 #include <crypto/sha1.h> 89 90 /* Decompression blowup */ 91 #define MPPC_DECOMP_BUFSIZE 8092 /* allocate buffer this big */ 92 #define MPPC_DECOMP_SAFETY 100 /* plus this much margin */ 93 94 /* MPPC/MPPE header length */ 95 #define MPPC_HDRLEN 2 96 97 /* Key length */ 98 #define KEYLEN(b) (((b) & MPPE_128) ? 16 : 8) 99 100 /* 101 * When packets are lost with MPPE, we may have to re-key arbitrarily 102 * many times to 'catch up' to the new jumped-ahead sequence number. 103 * Since this can be expensive, we pose a limit on how many re-keyings 104 * we will do at one time to avoid a possible D.O.S. vulnerability. 105 * This should instead be a configurable parameter. 106 */ 107 #define MPPE_MAX_REKEY 1000 108 109 /* MPPC packet header bits */ 110 #define MPPC_FLAG_FLUSHED 0x8000 /* xmitter reset state */ 111 #define MPPC_FLAG_RESTART 0x4000 /* compress history restart */ 112 #define MPPC_FLAG_COMPRESSED 0x2000 /* packet is compresed */ 113 #define MPPC_FLAG_ENCRYPTED 0x1000 /* packet is encrypted */ 114 #define MPPC_CCOUNT_MASK 0x0fff /* sequence number mask */ 115 116 #define MPPE_UPDATE_MASK 0xff /* coherency count when we're */ 117 #define MPPE_UPDATE_FLAG 0xff /* supposed to update key */ 118 119 #define MPPC_COMP_OK 0x05 120 #define MPPC_DECOMP_OK 0x05 121 122 /* Per direction info */ 123 struct ng_mppc_dir { 124 struct ng_mppc_config cfg; /* configuration */ 125 hook_p hook; /* netgraph hook */ 126 u_int16_t cc:12; /* coherency count */ 127 u_char flushed; /* clean history (xmit only) */ 128 #ifdef NETGRAPH_MPPC_COMPRESSION 129 u_char *history; /* compression history */ 130 #endif 131 #ifdef NETGRAPH_MPPC_ENCRYPTION 132 u_char key[MPPE_KEY_LEN]; /* session key */ 133 struct rc4_state rc4; /* rc4 state */ 134 #endif 135 }; 136 137 /* Node private data */ 138 struct ng_mppc_private { 139 struct ng_mppc_dir xmit; /* compress/encrypt config */ 140 struct ng_mppc_dir recv; /* decompress/decrypt config */ 141 ng_ID_t ctrlnode; /* path to controlling node */ 142 }; 143 typedef struct ng_mppc_private *priv_p; 144 145 /* Netgraph node methods */ 146 static ng_constructor_t ng_mppc_constructor; 147 static ng_rcvmsg_t ng_mppc_rcvmsg; 148 static ng_shutdown_t ng_mppc_shutdown; 149 static ng_newhook_t ng_mppc_newhook; 150 static ng_rcvdata_t ng_mppc_rcvdata; 151 static ng_disconnect_t ng_mppc_disconnect; 152 153 /* Helper functions */ 154 static int ng_mppc_compress(node_p node, 155 struct mbuf *m, struct mbuf **resultp); 156 static int ng_mppc_decompress(node_p node, 157 struct mbuf *m, struct mbuf **resultp); 158 static void ng_mppc_getkey(const u_char *h, u_char *h2, int len); 159 static void ng_mppc_updatekey(u_int32_t bits, 160 u_char *key0, u_char *key, struct rc4_state *rc4); 161 static void ng_mppc_reset_req(node_p node); 162 163 /* Node type descriptor */ 164 static struct ng_type ng_mppc_typestruct = { 165 .version = NG_ABI_VERSION, 166 .name = NG_MPPC_NODE_TYPE, 167 .constructor = ng_mppc_constructor, 168 .rcvmsg = ng_mppc_rcvmsg, 169 .shutdown = ng_mppc_shutdown, 170 .newhook = ng_mppc_newhook, 171 .rcvdata = ng_mppc_rcvdata, 172 .disconnect = ng_mppc_disconnect, 173 }; 174 NETGRAPH_INIT(mppc, &ng_mppc_typestruct); 175 176 #ifdef NETGRAPH_MPPC_ENCRYPTION 177 /* Depend on separate rc4 module */ 178 MODULE_DEPEND(ng_mppc, rc4, 1, 1, 1); 179 #endif 180 181 /* Fixed bit pattern to weaken keysize down to 40 or 56 bits */ 182 static const u_char ng_mppe_weakenkey[3] = { 0xd1, 0x26, 0x9e }; 183 184 #define ERROUT(x) do { error = (x); goto done; } while (0) 185 186 /************************************************************************ 187 NETGRAPH NODE STUFF 188 ************************************************************************/ 189 190 /* 191 * Node type constructor 192 */ 193 static int 194 ng_mppc_constructor(node_p node) 195 { 196 priv_p priv; 197 198 /* Allocate private structure */ 199 MALLOC(priv, priv_p, sizeof(*priv), M_NETGRAPH_MPPC, M_NOWAIT | M_ZERO); 200 if (priv == NULL) 201 return (ENOMEM); 202 203 NG_NODE_SET_PRIVATE(node, priv); 204 205 /* This node is not thread safe. */ 206 NG_NODE_FORCE_WRITER(node); 207 208 /* Done */ 209 return (0); 210 } 211 212 /* 213 * Give our OK for a hook to be added 214 */ 215 static int 216 ng_mppc_newhook(node_p node, hook_p hook, const char *name) 217 { 218 const priv_p priv = NG_NODE_PRIVATE(node); 219 hook_p *hookPtr; 220 221 /* Check hook name */ 222 if (strcmp(name, NG_MPPC_HOOK_COMP) == 0) 223 hookPtr = &priv->xmit.hook; 224 else if (strcmp(name, NG_MPPC_HOOK_DECOMP) == 0) 225 hookPtr = &priv->recv.hook; 226 else 227 return (EINVAL); 228 229 /* See if already connected */ 230 if (*hookPtr != NULL) 231 return (EISCONN); 232 233 /* OK */ 234 *hookPtr = hook; 235 return (0); 236 } 237 238 /* 239 * Receive a control message 240 */ 241 static int 242 ng_mppc_rcvmsg(node_p node, item_p item, hook_p lasthook) 243 { 244 const priv_p priv = NG_NODE_PRIVATE(node); 245 struct ng_mesg *resp = NULL; 246 int error = 0; 247 struct ng_mesg *msg; 248 249 NGI_GET_MSG(item, msg); 250 switch (msg->header.typecookie) { 251 case NGM_MPPC_COOKIE: 252 switch (msg->header.cmd) { 253 case NGM_MPPC_CONFIG_COMP: 254 case NGM_MPPC_CONFIG_DECOMP: 255 { 256 struct ng_mppc_config *const cfg 257 = (struct ng_mppc_config *)msg->data; 258 const int isComp = 259 msg->header.cmd == NGM_MPPC_CONFIG_COMP; 260 struct ng_mppc_dir *const d = isComp ? 261 &priv->xmit : &priv->recv; 262 263 /* Check configuration */ 264 if (msg->header.arglen != sizeof(*cfg)) 265 ERROUT(EINVAL); 266 if (cfg->enable) { 267 if ((cfg->bits & ~MPPC_VALID_BITS) != 0) 268 ERROUT(EINVAL); 269 #ifndef NETGRAPH_MPPC_COMPRESSION 270 if ((cfg->bits & MPPC_BIT) != 0) 271 ERROUT(EPROTONOSUPPORT); 272 #endif 273 #ifndef NETGRAPH_MPPC_ENCRYPTION 274 if ((cfg->bits & MPPE_BITS) != 0) 275 ERROUT(EPROTONOSUPPORT); 276 #endif 277 } else 278 cfg->bits = 0; 279 280 /* Save return address so we can send reset-req's */ 281 if (!isComp) 282 priv->ctrlnode = NGI_RETADDR(item); 283 284 /* Configuration is OK, reset to it */ 285 d->cfg = *cfg; 286 287 #ifdef NETGRAPH_MPPC_COMPRESSION 288 /* Initialize state buffers for compression */ 289 if (d->history != NULL) { 290 FREE(d->history, M_NETGRAPH_MPPC); 291 d->history = NULL; 292 } 293 if ((cfg->bits & MPPC_BIT) != 0) { 294 MALLOC(d->history, u_char *, 295 isComp ? MPPC_SizeOfCompressionHistory() : 296 MPPC_SizeOfDecompressionHistory(), 297 M_NETGRAPH_MPPC, M_NOWAIT); 298 if (d->history == NULL) 299 ERROUT(ENOMEM); 300 if (isComp) 301 MPPC_InitCompressionHistory(d->history); 302 else { 303 MPPC_InitDecompressionHistory( 304 d->history); 305 } 306 } 307 #endif 308 309 #ifdef NETGRAPH_MPPC_ENCRYPTION 310 /* Generate initial session keys for encryption */ 311 if ((cfg->bits & MPPE_BITS) != 0) { 312 const int keylen = KEYLEN(cfg->bits); 313 314 bcopy(cfg->startkey, d->key, keylen); 315 ng_mppc_getkey(cfg->startkey, d->key, keylen); 316 if ((cfg->bits & MPPE_40) != 0) 317 bcopy(&ng_mppe_weakenkey, d->key, 3); 318 else if ((cfg->bits & MPPE_56) != 0) 319 bcopy(&ng_mppe_weakenkey, d->key, 1); 320 rc4_init(&d->rc4, d->key, keylen); 321 } 322 #endif 323 324 /* Initialize other state */ 325 d->cc = 0; 326 d->flushed = 0; 327 break; 328 } 329 330 case NGM_MPPC_RESETREQ: 331 ng_mppc_reset_req(node); 332 break; 333 334 default: 335 error = EINVAL; 336 break; 337 } 338 break; 339 default: 340 error = EINVAL; 341 break; 342 } 343 done: 344 NG_RESPOND_MSG(error, node, item, resp); 345 NG_FREE_MSG(msg); 346 return (error); 347 } 348 349 /* 350 * Receive incoming data on our hook. 351 */ 352 static int 353 ng_mppc_rcvdata(hook_p hook, item_p item) 354 { 355 const node_p node = NG_HOOK_NODE(hook); 356 const priv_p priv = NG_NODE_PRIVATE(node); 357 struct mbuf *out; 358 int error; 359 struct mbuf *m; 360 361 NGI_GET_M(item, m); 362 /* Compress and/or encrypt */ 363 if (hook == priv->xmit.hook) { 364 if (!priv->xmit.cfg.enable) { 365 NG_FREE_M(m); 366 NG_FREE_ITEM(item); 367 return (ENXIO); 368 } 369 if ((error = ng_mppc_compress(node, m, &out)) != 0) { 370 NG_FREE_M(m); 371 NG_FREE_ITEM(item); 372 return(error); 373 } 374 NG_FREE_M(m); 375 NG_FWD_NEW_DATA(error, item, priv->xmit.hook, out); 376 return (error); 377 } 378 379 /* Decompress and/or decrypt */ 380 if (hook == priv->recv.hook) { 381 if (!priv->recv.cfg.enable) { 382 NG_FREE_M(m); 383 NG_FREE_ITEM(item); 384 return (ENXIO); 385 } 386 if ((error = ng_mppc_decompress(node, m, &out)) != 0) { 387 NG_FREE_M(m); 388 NG_FREE_ITEM(item); 389 if (error == EINVAL && priv->ctrlnode != 0) { 390 struct ng_mesg *msg; 391 392 /* Need to send a reset-request */ 393 NG_MKMESSAGE(msg, NGM_MPPC_COOKIE, 394 NGM_MPPC_RESETREQ, 0, M_NOWAIT); 395 if (msg == NULL) 396 return (error); 397 NG_SEND_MSG_ID(error, node, msg, 398 priv->ctrlnode, 0); 399 } 400 return (error); 401 } 402 NG_FREE_M(m); 403 NG_FWD_NEW_DATA(error, item, priv->recv.hook, out); 404 return (error); 405 } 406 407 /* Oops */ 408 panic("%s: unknown hook", __func__); 409 #ifdef RESTARTABLE_PANICS 410 return (EINVAL); 411 #endif 412 } 413 414 /* 415 * Destroy node 416 */ 417 static int 418 ng_mppc_shutdown(node_p node) 419 { 420 const priv_p priv = NG_NODE_PRIVATE(node); 421 422 /* Take down netgraph node */ 423 #ifdef NETGRAPH_MPPC_COMPRESSION 424 if (priv->xmit.history != NULL) 425 FREE(priv->xmit.history, M_NETGRAPH_MPPC); 426 if (priv->recv.history != NULL) 427 FREE(priv->recv.history, M_NETGRAPH_MPPC); 428 #endif 429 bzero(priv, sizeof(*priv)); 430 FREE(priv, M_NETGRAPH_MPPC); 431 NG_NODE_SET_PRIVATE(node, NULL); 432 NG_NODE_UNREF(node); /* let the node escape */ 433 return (0); 434 } 435 436 /* 437 * Hook disconnection 438 */ 439 static int 440 ng_mppc_disconnect(hook_p hook) 441 { 442 const node_p node = NG_HOOK_NODE(hook); 443 const priv_p priv = NG_NODE_PRIVATE(node); 444 445 /* Zero out hook pointer */ 446 if (hook == priv->xmit.hook) 447 priv->xmit.hook = NULL; 448 if (hook == priv->recv.hook) 449 priv->recv.hook = NULL; 450 451 /* Go away if no longer connected */ 452 if ((NG_NODE_NUMHOOKS(node) == 0) 453 && NG_NODE_IS_VALID(node)) 454 ng_rmnode_self(node); 455 return (0); 456 } 457 458 /************************************************************************ 459 HELPER STUFF 460 ************************************************************************/ 461 462 /* 463 * Compress/encrypt a packet and put the result in a new mbuf at *resultp. 464 * The original mbuf is not free'd. 465 */ 466 static int 467 ng_mppc_compress(node_p node, struct mbuf *m, struct mbuf **resultp) 468 { 469 const priv_p priv = NG_NODE_PRIVATE(node); 470 struct ng_mppc_dir *const d = &priv->xmit; 471 u_char *inbuf, *outbuf; 472 int outlen, inlen; 473 u_int16_t header; 474 475 /* Initialize */ 476 *resultp = NULL; 477 header = d->cc; 478 if (d->flushed) { 479 header |= MPPC_FLAG_FLUSHED; 480 d->flushed = 0; 481 } 482 483 /* Work with contiguous regions of memory */ 484 inlen = m->m_pkthdr.len; 485 MALLOC(inbuf, u_char *, inlen, M_NETGRAPH_MPPC, M_NOWAIT); 486 if (inbuf == NULL) 487 return (ENOMEM); 488 m_copydata(m, 0, inlen, (caddr_t)inbuf); 489 if ((d->cfg.bits & MPPC_BIT) != 0) 490 outlen = MPPC_MAX_BLOWUP(inlen); 491 else 492 outlen = MPPC_HDRLEN + inlen; 493 MALLOC(outbuf, u_char *, outlen, M_NETGRAPH_MPPC, M_NOWAIT); 494 if (outbuf == NULL) { 495 FREE(inbuf, M_NETGRAPH_MPPC); 496 return (ENOMEM); 497 } 498 499 /* Compress "inbuf" into "outbuf" (if compression enabled) */ 500 #ifdef NETGRAPH_MPPC_COMPRESSION 501 if ((d->cfg.bits & MPPC_BIT) != 0) { 502 u_short flags = MPPC_MANDATORY_COMPRESS_FLAGS; 503 u_char *source, *dest; 504 u_long sourceCnt, destCnt; 505 int rtn; 506 507 /* Prepare to compress */ 508 source = inbuf; 509 sourceCnt = inlen; 510 dest = outbuf + MPPC_HDRLEN; 511 destCnt = outlen - MPPC_HDRLEN; 512 if ((d->cfg.bits & MPPE_STATELESS) == 0) 513 flags |= MPPC_SAVE_HISTORY; 514 515 /* Compress */ 516 rtn = MPPC_Compress(&source, &dest, &sourceCnt, 517 &destCnt, d->history, flags, 0); 518 519 /* Check return value */ 520 KASSERT(rtn != MPPC_INVALID, ("%s: invalid", __func__)); 521 if ((rtn & MPPC_EXPANDED) == 0 522 && (rtn & MPPC_COMP_OK) == MPPC_COMP_OK) { 523 outlen -= destCnt; 524 header |= MPPC_FLAG_COMPRESSED; 525 if ((rtn & MPPC_RESTART_HISTORY) != 0) 526 header |= MPPC_FLAG_RESTART; 527 } 528 d->flushed = (rtn & MPPC_EXPANDED) != 0 529 || (flags & MPPC_SAVE_HISTORY) == 0; 530 } 531 #endif 532 533 /* If we did not compress this packet, copy it to output buffer */ 534 if ((header & MPPC_FLAG_COMPRESSED) == 0) { 535 bcopy(inbuf, outbuf + MPPC_HDRLEN, inlen); 536 outlen = MPPC_HDRLEN + inlen; 537 } 538 FREE(inbuf, M_NETGRAPH_MPPC); 539 540 /* Always set the flushed bit in stateless mode */ 541 if ((d->cfg.bits & MPPE_STATELESS) != 0) 542 header |= MPPC_FLAG_FLUSHED; 543 544 /* Now encrypt packet (if encryption enabled) */ 545 #ifdef NETGRAPH_MPPC_ENCRYPTION 546 if ((d->cfg.bits & MPPE_BITS) != 0) { 547 548 /* Set header bits; need to reset key if we say we did */ 549 header |= MPPC_FLAG_ENCRYPTED; 550 if ((header & MPPC_FLAG_FLUSHED) != 0) 551 rc4_init(&d->rc4, d->key, KEYLEN(d->cfg.bits)); 552 553 /* Update key if it's time */ 554 if ((d->cfg.bits & MPPE_STATELESS) != 0 555 || (d->cc & MPPE_UPDATE_MASK) == MPPE_UPDATE_FLAG) { 556 ng_mppc_updatekey(d->cfg.bits, 557 d->cfg.startkey, d->key, &d->rc4); 558 } 559 560 /* Encrypt packet */ 561 rc4_crypt(&d->rc4, outbuf + MPPC_HDRLEN, 562 outbuf + MPPC_HDRLEN, outlen - MPPC_HDRLEN); 563 } 564 #endif 565 566 /* Update sequence number */ 567 d->cc++; 568 569 /* Install header */ 570 *((u_int16_t *)outbuf) = htons(header); 571 572 /* Return packet in an mbuf */ 573 *resultp = m_devget((caddr_t)outbuf, outlen, 0, NULL, NULL); 574 FREE(outbuf, M_NETGRAPH_MPPC); 575 return (*resultp == NULL ? ENOBUFS : 0); 576 } 577 578 /* 579 * Decompress/decrypt packet and put the result in a new mbuf at *resultp. 580 * The original mbuf is not free'd. 581 */ 582 static int 583 ng_mppc_decompress(node_p node, struct mbuf *m, struct mbuf **resultp) 584 { 585 const priv_p priv = NG_NODE_PRIVATE(node); 586 struct ng_mppc_dir *const d = &priv->recv; 587 u_int16_t header, cc; 588 u_int numLost; 589 u_char *buf; 590 int len; 591 592 /* Pull off header */ 593 if (m->m_pkthdr.len < MPPC_HDRLEN) 594 return (EINVAL); 595 m_copydata(m, 0, MPPC_HDRLEN, (caddr_t)&header); 596 header = ntohs(header); 597 cc = (header & MPPC_CCOUNT_MASK); 598 599 /* Copy payload into a contiguous region of memory */ 600 len = m->m_pkthdr.len - MPPC_HDRLEN; 601 MALLOC(buf, u_char *, len, M_NETGRAPH_MPPC, M_NOWAIT); 602 if (buf == NULL) 603 return (ENOMEM); 604 m_copydata(m, MPPC_HDRLEN, len, (caddr_t)buf); 605 606 /* Check for an unexpected jump in the sequence number */ 607 numLost = ((cc - d->cc) & MPPC_CCOUNT_MASK); 608 609 /* If flushed bit set, we can always handle packet */ 610 if ((header & MPPC_FLAG_FLUSHED) != 0) { 611 #ifdef NETGRAPH_MPPC_COMPRESSION 612 if (d->history != NULL) 613 MPPC_InitDecompressionHistory(d->history); 614 #endif 615 #ifdef NETGRAPH_MPPC_ENCRYPTION 616 if ((d->cfg.bits & MPPE_BITS) != 0) { 617 u_int rekey; 618 619 /* How many times are we going to have to re-key? */ 620 rekey = ((d->cfg.bits & MPPE_STATELESS) != 0) ? 621 numLost : (numLost / (MPPE_UPDATE_MASK + 1)); 622 if (rekey > MPPE_MAX_REKEY) { 623 log(LOG_ERR, "%s: too many (%d) packets" 624 " dropped, disabling node %p!", 625 __func__, numLost, node); 626 priv->recv.cfg.enable = 0; 627 goto failed; 628 } 629 630 /* Re-key as necessary to catch up to peer */ 631 while (d->cc != cc) { 632 if ((d->cfg.bits & MPPE_STATELESS) != 0 633 || (d->cc & MPPE_UPDATE_MASK) 634 == MPPE_UPDATE_FLAG) { 635 ng_mppc_updatekey(d->cfg.bits, 636 d->cfg.startkey, d->key, &d->rc4); 637 } 638 d->cc++; 639 } 640 641 /* Reset key (except in stateless mode, see below) */ 642 if ((d->cfg.bits & MPPE_STATELESS) == 0) 643 rc4_init(&d->rc4, d->key, KEYLEN(d->cfg.bits)); 644 } 645 #endif 646 d->cc = cc; /* skip over lost seq numbers */ 647 numLost = 0; /* act like no packets were lost */ 648 } 649 650 /* Can't decode non-sequential packets without a flushed bit */ 651 if (numLost != 0) 652 goto failed; 653 654 /* Decrypt packet */ 655 if ((header & MPPC_FLAG_ENCRYPTED) != 0) { 656 657 /* Are we not expecting encryption? */ 658 if ((d->cfg.bits & MPPE_BITS) == 0) { 659 log(LOG_ERR, "%s: rec'd unexpectedly %s packet", 660 __func__, "encrypted"); 661 goto failed; 662 } 663 664 #ifdef NETGRAPH_MPPC_ENCRYPTION 665 /* Update key if it's time (always in stateless mode) */ 666 if ((d->cfg.bits & MPPE_STATELESS) != 0 667 || (d->cc & MPPE_UPDATE_MASK) == MPPE_UPDATE_FLAG) { 668 ng_mppc_updatekey(d->cfg.bits, 669 d->cfg.startkey, d->key, &d->rc4); 670 } 671 672 /* Decrypt packet */ 673 rc4_crypt(&d->rc4, buf, buf, len); 674 #endif 675 } else { 676 677 /* Are we expecting encryption? */ 678 if ((d->cfg.bits & MPPE_BITS) != 0) { 679 log(LOG_ERR, "%s: rec'd unexpectedly %s packet", 680 __func__, "unencrypted"); 681 goto failed; 682 } 683 } 684 685 /* Update coherency count for next time (12 bit arithmetic) */ 686 d->cc++; 687 688 /* Check for unexpected compressed packet */ 689 if ((header & MPPC_FLAG_COMPRESSED) != 0 690 && (d->cfg.bits & MPPC_BIT) == 0) { 691 log(LOG_ERR, "%s: rec'd unexpectedly %s packet", 692 __func__, "compressed"); 693 failed: 694 FREE(buf, M_NETGRAPH_MPPC); 695 return (EINVAL); 696 } 697 698 #ifdef NETGRAPH_MPPC_COMPRESSION 699 /* Decompress packet */ 700 if ((header & MPPC_FLAG_COMPRESSED) != 0) { 701 int flags = MPPC_MANDATORY_DECOMPRESS_FLAGS; 702 u_char *decompbuf, *source, *dest; 703 u_long sourceCnt, destCnt; 704 int decomplen, rtn; 705 706 /* Allocate a buffer for decompressed data */ 707 MALLOC(decompbuf, u_char *, MPPC_DECOMP_BUFSIZE 708 + MPPC_DECOMP_SAFETY, M_NETGRAPH_MPPC, M_NOWAIT); 709 if (decompbuf == NULL) { 710 FREE(buf, M_NETGRAPH_MPPC); 711 return (ENOMEM); 712 } 713 decomplen = MPPC_DECOMP_BUFSIZE; 714 715 /* Prepare to decompress */ 716 source = buf; 717 sourceCnt = len; 718 dest = decompbuf; 719 destCnt = decomplen; 720 if ((header & MPPC_FLAG_RESTART) != 0) 721 flags |= MPPC_RESTART_HISTORY; 722 723 /* Decompress */ 724 rtn = MPPC_Decompress(&source, &dest, 725 &sourceCnt, &destCnt, d->history, flags); 726 727 /* Check return value */ 728 KASSERT(rtn != MPPC_INVALID, ("%s: invalid", __func__)); 729 if ((rtn & MPPC_DEST_EXHAUSTED) != 0 730 || (rtn & MPPC_DECOMP_OK) != MPPC_DECOMP_OK) { 731 log(LOG_ERR, "%s: decomp returned 0x%x", 732 __func__, rtn); 733 FREE(decompbuf, M_NETGRAPH_MPPC); 734 goto failed; 735 } 736 737 /* Replace compressed data with decompressed data */ 738 FREE(buf, M_NETGRAPH_MPPC); 739 buf = decompbuf; 740 len = decomplen - destCnt; 741 } 742 #endif 743 744 /* Return result in an mbuf */ 745 *resultp = m_devget((caddr_t)buf, len, 0, NULL, NULL); 746 FREE(buf, M_NETGRAPH_MPPC); 747 return (*resultp == NULL ? ENOBUFS : 0); 748 } 749 750 /* 751 * The peer has sent us a CCP ResetRequest, so reset our transmit state. 752 */ 753 static void 754 ng_mppc_reset_req(node_p node) 755 { 756 const priv_p priv = NG_NODE_PRIVATE(node); 757 struct ng_mppc_dir *const d = &priv->xmit; 758 759 #ifdef NETGRAPH_MPPC_COMPRESSION 760 if (d->history != NULL) 761 MPPC_InitCompressionHistory(d->history); 762 #endif 763 #ifdef NETGRAPH_MPPC_ENCRYPTION 764 if ((d->cfg.bits & MPPE_STATELESS) == 0) 765 rc4_init(&d->rc4, d->key, KEYLEN(d->cfg.bits)); 766 #endif 767 d->flushed = 1; 768 } 769 770 /* 771 * Generate a new encryption key 772 */ 773 static void 774 ng_mppc_getkey(const u_char *h, u_char *h2, int len) 775 { 776 static const u_char pad1[10] = 777 { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; 778 static const u_char pad2[10] = 779 { 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, }; 780 u_char hash[20]; 781 SHA1_CTX c; 782 int k; 783 784 bzero(&hash, sizeof(hash)); 785 SHA1Init(&c); 786 SHA1Update(&c, h, len); 787 for (k = 0; k < 4; k++) 788 SHA1Update(&c, pad1, sizeof(pad2)); 789 SHA1Update(&c, h2, len); 790 for (k = 0; k < 4; k++) 791 SHA1Update(&c, pad2, sizeof(pad2)); 792 SHA1Final(hash, &c); 793 bcopy(hash, h2, len); 794 } 795 796 /* 797 * Update the encryption key 798 */ 799 static void 800 ng_mppc_updatekey(u_int32_t bits, 801 u_char *key0, u_char *key, struct rc4_state *rc4) 802 { 803 const int keylen = KEYLEN(bits); 804 805 ng_mppc_getkey(key0, key, keylen); 806 rc4_init(rc4, key, keylen); 807 rc4_crypt(rc4, key, key, keylen); 808 if ((bits & MPPE_40) != 0) 809 bcopy(&ng_mppe_weakenkey, key, 3); 810 else if ((bits & MPPE_56) != 0) 811 bcopy(&ng_mppe_weakenkey, key, 1); 812 rc4_init(rc4, key, keylen); 813 } 814 815