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 MPPC_CCOUNT_INC(d) ((d) = (((d) + 1) & MPPC_CCOUNT_MASK)) 117 118 #define MPPE_UPDATE_MASK 0xff /* coherency count when we're */ 119 #define MPPE_UPDATE_FLAG 0xff /* supposed to update key */ 120 121 #define MPPC_COMP_OK 0x05 122 #define MPPC_DECOMP_OK 0x05 123 124 /* Per direction info */ 125 struct ng_mppc_dir { 126 struct ng_mppc_config cfg; /* configuration */ 127 hook_p hook; /* netgraph hook */ 128 u_int16_t cc:12; /* coherency count */ 129 u_char flushed; /* clean history (xmit only) */ 130 #ifdef NETGRAPH_MPPC_COMPRESSION 131 u_char *history; /* compression history */ 132 #endif 133 #ifdef NETGRAPH_MPPC_ENCRYPTION 134 u_char key[MPPE_KEY_LEN]; /* session key */ 135 struct rc4_state rc4; /* rc4 state */ 136 #endif 137 }; 138 139 /* Node private data */ 140 struct ng_mppc_private { 141 struct ng_mppc_dir xmit; /* compress/encrypt config */ 142 struct ng_mppc_dir recv; /* decompress/decrypt config */ 143 ng_ID_t ctrlnode; /* path to controlling node */ 144 }; 145 typedef struct ng_mppc_private *priv_p; 146 147 /* Netgraph node methods */ 148 static ng_constructor_t ng_mppc_constructor; 149 static ng_rcvmsg_t ng_mppc_rcvmsg; 150 static ng_shutdown_t ng_mppc_shutdown; 151 static ng_newhook_t ng_mppc_newhook; 152 static ng_rcvdata_t ng_mppc_rcvdata; 153 static ng_disconnect_t ng_mppc_disconnect; 154 155 /* Helper functions */ 156 static int ng_mppc_compress(node_p node, 157 struct mbuf **datap); 158 static int ng_mppc_decompress(node_p node, 159 struct mbuf **datap); 160 #ifdef NETGRAPH_MPPC_ENCRYPTION 161 static void ng_mppc_getkey(const u_char *h, u_char *h2, int len); 162 static void ng_mppc_updatekey(u_int32_t bits, 163 u_char *key0, u_char *key, struct rc4_state *rc4); 164 #endif 165 static void ng_mppc_reset_req(node_p node); 166 167 /* Node type descriptor */ 168 static struct ng_type ng_mppc_typestruct = { 169 .version = NG_ABI_VERSION, 170 .name = NG_MPPC_NODE_TYPE, 171 .constructor = ng_mppc_constructor, 172 .rcvmsg = ng_mppc_rcvmsg, 173 .shutdown = ng_mppc_shutdown, 174 .newhook = ng_mppc_newhook, 175 .rcvdata = ng_mppc_rcvdata, 176 .disconnect = ng_mppc_disconnect, 177 }; 178 NETGRAPH_INIT(mppc, &ng_mppc_typestruct); 179 180 #ifdef NETGRAPH_MPPC_ENCRYPTION 181 /* Depend on separate rc4 module */ 182 MODULE_DEPEND(ng_mppc, rc4, 1, 1, 1); 183 #endif 184 185 /* Fixed bit pattern to weaken keysize down to 40 or 56 bits */ 186 static const u_char ng_mppe_weakenkey[3] = { 0xd1, 0x26, 0x9e }; 187 188 #define ERROUT(x) do { error = (x); goto done; } while (0) 189 190 /************************************************************************ 191 NETGRAPH NODE STUFF 192 ************************************************************************/ 193 194 /* 195 * Node type constructor 196 */ 197 static int 198 ng_mppc_constructor(node_p node) 199 { 200 priv_p priv; 201 202 /* Allocate private structure */ 203 priv = malloc(sizeof(*priv), M_NETGRAPH_MPPC, M_NOWAIT | M_ZERO); 204 if (priv == NULL) 205 return (ENOMEM); 206 207 NG_NODE_SET_PRIVATE(node, priv); 208 209 /* This node is not thread safe. */ 210 NG_NODE_FORCE_WRITER(node); 211 212 /* Done */ 213 return (0); 214 } 215 216 /* 217 * Give our OK for a hook to be added 218 */ 219 static int 220 ng_mppc_newhook(node_p node, hook_p hook, const char *name) 221 { 222 const priv_p priv = NG_NODE_PRIVATE(node); 223 hook_p *hookPtr; 224 225 /* Check hook name */ 226 if (strcmp(name, NG_MPPC_HOOK_COMP) == 0) 227 hookPtr = &priv->xmit.hook; 228 else if (strcmp(name, NG_MPPC_HOOK_DECOMP) == 0) 229 hookPtr = &priv->recv.hook; 230 else 231 return (EINVAL); 232 233 /* See if already connected */ 234 if (*hookPtr != NULL) 235 return (EISCONN); 236 237 /* OK */ 238 *hookPtr = hook; 239 return (0); 240 } 241 242 /* 243 * Receive a control message 244 */ 245 static int 246 ng_mppc_rcvmsg(node_p node, item_p item, hook_p lasthook) 247 { 248 const priv_p priv = NG_NODE_PRIVATE(node); 249 struct ng_mesg *resp = NULL; 250 int error = 0; 251 struct ng_mesg *msg; 252 253 NGI_GET_MSG(item, msg); 254 switch (msg->header.typecookie) { 255 case NGM_MPPC_COOKIE: 256 switch (msg->header.cmd) { 257 case NGM_MPPC_CONFIG_COMP: 258 case NGM_MPPC_CONFIG_DECOMP: 259 { 260 struct ng_mppc_config *const cfg 261 = (struct ng_mppc_config *)msg->data; 262 const int isComp = 263 msg->header.cmd == NGM_MPPC_CONFIG_COMP; 264 struct ng_mppc_dir *const d = isComp ? 265 &priv->xmit : &priv->recv; 266 267 /* Check configuration */ 268 if (msg->header.arglen != sizeof(*cfg)) 269 ERROUT(EINVAL); 270 if (cfg->enable) { 271 if ((cfg->bits & ~MPPC_VALID_BITS) != 0) 272 ERROUT(EINVAL); 273 #ifndef NETGRAPH_MPPC_COMPRESSION 274 if ((cfg->bits & MPPC_BIT) != 0) 275 ERROUT(EPROTONOSUPPORT); 276 #endif 277 #ifndef NETGRAPH_MPPC_ENCRYPTION 278 if ((cfg->bits & MPPE_BITS) != 0) 279 ERROUT(EPROTONOSUPPORT); 280 #endif 281 } else 282 cfg->bits = 0; 283 284 /* Save return address so we can send reset-req's */ 285 if (!isComp) 286 priv->ctrlnode = NGI_RETADDR(item); 287 288 /* Configuration is OK, reset to it */ 289 d->cfg = *cfg; 290 291 #ifdef NETGRAPH_MPPC_COMPRESSION 292 /* Initialize state buffers for compression */ 293 if (d->history != NULL) { 294 free(d->history, M_NETGRAPH_MPPC); 295 d->history = NULL; 296 } 297 if ((cfg->bits & MPPC_BIT) != 0) { 298 d->history = malloc(isComp ? 299 MPPC_SizeOfCompressionHistory() : 300 MPPC_SizeOfDecompressionHistory(), 301 M_NETGRAPH_MPPC, M_NOWAIT); 302 if (d->history == NULL) 303 ERROUT(ENOMEM); 304 if (isComp) 305 MPPC_InitCompressionHistory(d->history); 306 else { 307 MPPC_InitDecompressionHistory( 308 d->history); 309 } 310 } 311 #endif 312 313 #ifdef NETGRAPH_MPPC_ENCRYPTION 314 /* Generate initial session keys for encryption */ 315 if ((cfg->bits & MPPE_BITS) != 0) { 316 const int keylen = KEYLEN(cfg->bits); 317 318 bcopy(cfg->startkey, d->key, keylen); 319 ng_mppc_getkey(cfg->startkey, d->key, keylen); 320 if ((cfg->bits & MPPE_40) != 0) 321 bcopy(&ng_mppe_weakenkey, d->key, 3); 322 else if ((cfg->bits & MPPE_56) != 0) 323 bcopy(&ng_mppe_weakenkey, d->key, 1); 324 rc4_init(&d->rc4, d->key, keylen); 325 } 326 #endif 327 328 /* Initialize other state */ 329 d->cc = 0; 330 d->flushed = 0; 331 break; 332 } 333 334 case NGM_MPPC_RESETREQ: 335 ng_mppc_reset_req(node); 336 break; 337 338 default: 339 error = EINVAL; 340 break; 341 } 342 break; 343 default: 344 error = EINVAL; 345 break; 346 } 347 done: 348 NG_RESPOND_MSG(error, node, item, resp); 349 NG_FREE_MSG(msg); 350 return (error); 351 } 352 353 /* 354 * Receive incoming data on our hook. 355 */ 356 static int 357 ng_mppc_rcvdata(hook_p hook, item_p item) 358 { 359 const node_p node = NG_HOOK_NODE(hook); 360 const priv_p priv = NG_NODE_PRIVATE(node); 361 int error; 362 struct mbuf *m; 363 364 NGI_GET_M(item, m); 365 /* Compress and/or encrypt */ 366 if (hook == priv->xmit.hook) { 367 if (!priv->xmit.cfg.enable) { 368 NG_FREE_M(m); 369 NG_FREE_ITEM(item); 370 return (ENXIO); 371 } 372 if ((error = ng_mppc_compress(node, &m)) != 0) { 373 NG_FREE_ITEM(item); 374 return(error); 375 } 376 NG_FWD_NEW_DATA(error, item, priv->xmit.hook, m); 377 return (error); 378 } 379 380 /* Decompress and/or decrypt */ 381 if (hook == priv->recv.hook) { 382 if (!priv->recv.cfg.enable) { 383 NG_FREE_M(m); 384 NG_FREE_ITEM(item); 385 return (ENXIO); 386 } 387 if ((error = ng_mppc_decompress(node, &m)) != 0) { 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_FWD_NEW_DATA(error, item, priv->recv.hook, m); 403 return (error); 404 } 405 406 /* Oops */ 407 panic("%s: unknown hook", __func__); 408 #ifdef RESTARTABLE_PANICS 409 return (EINVAL); 410 #endif 411 } 412 413 /* 414 * Destroy node 415 */ 416 static int 417 ng_mppc_shutdown(node_p node) 418 { 419 const priv_p priv = NG_NODE_PRIVATE(node); 420 421 /* Take down netgraph node */ 422 #ifdef NETGRAPH_MPPC_COMPRESSION 423 if (priv->xmit.history != NULL) 424 free(priv->xmit.history, M_NETGRAPH_MPPC); 425 if (priv->recv.history != NULL) 426 free(priv->recv.history, M_NETGRAPH_MPPC); 427 #endif 428 bzero(priv, sizeof(*priv)); 429 free(priv, M_NETGRAPH_MPPC); 430 NG_NODE_SET_PRIVATE(node, NULL); 431 NG_NODE_UNREF(node); /* let the node escape */ 432 return (0); 433 } 434 435 /* 436 * Hook disconnection 437 */ 438 static int 439 ng_mppc_disconnect(hook_p hook) 440 { 441 const node_p node = NG_HOOK_NODE(hook); 442 const priv_p priv = NG_NODE_PRIVATE(node); 443 444 /* Zero out hook pointer */ 445 if (hook == priv->xmit.hook) 446 priv->xmit.hook = NULL; 447 if (hook == priv->recv.hook) 448 priv->recv.hook = NULL; 449 450 /* Go away if no longer connected */ 451 if ((NG_NODE_NUMHOOKS(node) == 0) 452 && NG_NODE_IS_VALID(node)) 453 ng_rmnode_self(node); 454 return (0); 455 } 456 457 /************************************************************************ 458 HELPER STUFF 459 ************************************************************************/ 460 461 /* 462 * Compress/encrypt a packet and put the result in a new mbuf at *resultp. 463 * The original mbuf is not free'd. 464 */ 465 static int 466 ng_mppc_compress(node_p node, struct mbuf **datap) 467 { 468 const priv_p priv = NG_NODE_PRIVATE(node); 469 struct ng_mppc_dir *const d = &priv->xmit; 470 u_int16_t header; 471 struct mbuf *m = *datap; 472 473 /* We must own the mbuf chain exclusively to modify it. */ 474 m = m_unshare(m, M_DONTWAIT); 475 if (m == NULL) 476 return (ENOMEM); 477 478 /* Initialize */ 479 header = d->cc; 480 481 /* Always set the flushed bit in stateless mode */ 482 if (d->flushed || ((d->cfg.bits & MPPE_STATELESS) != 0)) { 483 header |= MPPC_FLAG_FLUSHED; 484 d->flushed = 0; 485 } 486 487 /* Compress packet (if compression enabled) */ 488 #ifdef NETGRAPH_MPPC_COMPRESSION 489 if ((d->cfg.bits & MPPC_BIT) != 0) { 490 u_short flags = MPPC_MANDATORY_COMPRESS_FLAGS; 491 u_char *inbuf, *outbuf; 492 int outlen, inlen, ina; 493 u_char *source, *dest; 494 u_long sourceCnt, destCnt; 495 int rtn; 496 497 /* Work with contiguous regions of memory. */ 498 inlen = m->m_pkthdr.len; 499 if (m->m_next == NULL) { 500 inbuf = mtod(m, u_char *); 501 ina = 0; 502 } else { 503 inbuf = malloc(inlen, M_NETGRAPH_MPPC, M_NOWAIT); 504 if (inbuf == NULL) 505 goto err1; 506 m_copydata(m, 0, inlen, (caddr_t)inbuf); 507 ina = 1; 508 } 509 510 outlen = MPPC_MAX_BLOWUP(inlen); 511 outbuf = malloc(outlen, M_NETGRAPH_MPPC, M_NOWAIT); 512 if (outbuf == NULL) { 513 if (ina) 514 free(inbuf, M_NETGRAPH_MPPC); 515 err1: 516 m_freem(m); 517 MPPC_InitCompressionHistory(d->history); 518 d->flushed = 1; 519 return (ENOMEM); 520 } 521 522 /* Prepare to compress */ 523 source = inbuf; 524 sourceCnt = inlen; 525 dest = outbuf; 526 destCnt = outlen; 527 if ((d->cfg.bits & MPPE_STATELESS) == 0) 528 flags |= MPPC_SAVE_HISTORY; 529 530 /* Compress */ 531 rtn = MPPC_Compress(&source, &dest, &sourceCnt, 532 &destCnt, d->history, flags, 0); 533 534 /* Check return value */ 535 KASSERT(rtn != MPPC_INVALID, ("%s: invalid", __func__)); 536 if ((rtn & MPPC_EXPANDED) == 0 537 && (rtn & MPPC_COMP_OK) == MPPC_COMP_OK) { 538 outlen -= destCnt; 539 header |= MPPC_FLAG_COMPRESSED; 540 if ((rtn & MPPC_RESTART_HISTORY) != 0) 541 header |= MPPC_FLAG_RESTART; 542 543 /* Replace m by the compresed one. */ 544 m_copyback(m, 0, outlen, (caddr_t)outbuf); 545 if (m->m_pkthdr.len < outlen) { 546 m_freem(m); 547 m = NULL; 548 } else if (outlen < m->m_pkthdr.len) 549 m_adj(m, outlen - m->m_pkthdr.len); 550 } 551 d->flushed = (rtn & MPPC_EXPANDED) != 0 552 || (flags & MPPC_SAVE_HISTORY) == 0; 553 554 if (ina) 555 free(inbuf, M_NETGRAPH_MPPC); 556 free(outbuf, M_NETGRAPH_MPPC); 557 558 /* Check mbuf chain reload result. */ 559 if (m == NULL) { 560 if (!d->flushed) { 561 MPPC_InitCompressionHistory(d->history); 562 d->flushed = 1; 563 } 564 return (ENOMEM); 565 } 566 } 567 #endif 568 569 /* Now encrypt packet (if encryption enabled) */ 570 #ifdef NETGRAPH_MPPC_ENCRYPTION 571 if ((d->cfg.bits & MPPE_BITS) != 0) { 572 struct mbuf *m1; 573 574 /* Set header bits */ 575 header |= MPPC_FLAG_ENCRYPTED; 576 577 /* Update key if it's time */ 578 if ((d->cfg.bits & MPPE_STATELESS) != 0 579 || (d->cc & MPPE_UPDATE_MASK) == MPPE_UPDATE_FLAG) { 580 ng_mppc_updatekey(d->cfg.bits, 581 d->cfg.startkey, d->key, &d->rc4); 582 } else if ((header & MPPC_FLAG_FLUSHED) != 0) { 583 /* Need to reset key if we say we did 584 and ng_mppc_updatekey wasn't called to do it also. */ 585 rc4_init(&d->rc4, d->key, KEYLEN(d->cfg.bits)); 586 } 587 588 /* Encrypt packet */ 589 m1 = m; 590 while (m1) { 591 rc4_crypt(&d->rc4, mtod(m1, u_char *), 592 mtod(m1, u_char *), m1->m_len); 593 m1 = m1->m_next; 594 } 595 } 596 #endif 597 598 /* Update coherency count for next time (12 bit arithmetic) */ 599 MPPC_CCOUNT_INC(d->cc); 600 601 /* Install header */ 602 M_PREPEND(m, MPPC_HDRLEN, M_DONTWAIT); 603 if (m != NULL) 604 *(mtod(m, uint16_t *)) = htons(header); 605 606 *datap = m; 607 return (*datap == NULL ? ENOBUFS : 0); 608 } 609 610 /* 611 * Decompress/decrypt packet and put the result in a new mbuf at *resultp. 612 * The original mbuf is not free'd. 613 */ 614 static int 615 ng_mppc_decompress(node_p node, struct mbuf **datap) 616 { 617 const priv_p priv = NG_NODE_PRIVATE(node); 618 struct ng_mppc_dir *const d = &priv->recv; 619 u_int16_t header, cc; 620 u_int numLost; 621 struct mbuf *m = *datap; 622 623 /* We must own the mbuf chain exclusively to modify it. */ 624 m = m_unshare(m, M_DONTWAIT); 625 if (m == NULL) 626 return (ENOMEM); 627 628 /* Pull off header */ 629 if (m->m_pkthdr.len < MPPC_HDRLEN) { 630 m_freem(m); 631 return (EINVAL); 632 } 633 m_copydata(m, 0, MPPC_HDRLEN, (caddr_t)&header); 634 header = ntohs(header); 635 cc = (header & MPPC_CCOUNT_MASK); 636 m_adj(m, MPPC_HDRLEN); 637 638 /* Check for an unexpected jump in the sequence number */ 639 numLost = ((cc - d->cc) & MPPC_CCOUNT_MASK); 640 641 /* If flushed bit set, we can always handle packet */ 642 if ((header & MPPC_FLAG_FLUSHED) != 0) { 643 #ifdef NETGRAPH_MPPC_COMPRESSION 644 if (d->history != NULL) 645 MPPC_InitDecompressionHistory(d->history); 646 #endif 647 #ifdef NETGRAPH_MPPC_ENCRYPTION 648 if ((d->cfg.bits & MPPE_BITS) != 0) { 649 u_int rekey; 650 651 /* How many times are we going to have to re-key? */ 652 rekey = ((d->cfg.bits & MPPE_STATELESS) != 0) ? 653 numLost : (numLost / (MPPE_UPDATE_MASK + 1)); 654 if (rekey > MPPE_MAX_REKEY) { 655 log(LOG_ERR, "%s: too many (%d) packets" 656 " dropped, disabling node %p!", 657 __func__, numLost, node); 658 priv->recv.cfg.enable = 0; 659 goto failed; 660 } 661 662 /* Re-key as necessary to catch up to peer */ 663 while (d->cc != cc) { 664 if ((d->cfg.bits & MPPE_STATELESS) != 0 665 || (d->cc & MPPE_UPDATE_MASK) 666 == MPPE_UPDATE_FLAG) { 667 ng_mppc_updatekey(d->cfg.bits, 668 d->cfg.startkey, d->key, &d->rc4); 669 } 670 MPPC_CCOUNT_INC(d->cc); 671 } 672 673 /* Reset key (except in stateless mode, see below) */ 674 if ((d->cfg.bits & MPPE_STATELESS) == 0) 675 rc4_init(&d->rc4, d->key, KEYLEN(d->cfg.bits)); 676 } 677 #endif 678 d->cc = cc; /* skip over lost seq numbers */ 679 numLost = 0; /* act like no packets were lost */ 680 } 681 682 /* Can't decode non-sequential packets without a flushed bit */ 683 if (numLost != 0) 684 goto failed; 685 686 /* Decrypt packet */ 687 if ((header & MPPC_FLAG_ENCRYPTED) != 0) { 688 #ifdef NETGRAPH_MPPC_ENCRYPTION 689 struct mbuf *m1; 690 #endif 691 692 /* Are we not expecting encryption? */ 693 if ((d->cfg.bits & MPPE_BITS) == 0) { 694 log(LOG_ERR, "%s: rec'd unexpectedly %s packet", 695 __func__, "encrypted"); 696 goto failed; 697 } 698 699 #ifdef NETGRAPH_MPPC_ENCRYPTION 700 /* Update key if it's time (always in stateless mode) */ 701 if ((d->cfg.bits & MPPE_STATELESS) != 0 702 || (d->cc & MPPE_UPDATE_MASK) == MPPE_UPDATE_FLAG) { 703 ng_mppc_updatekey(d->cfg.bits, 704 d->cfg.startkey, d->key, &d->rc4); 705 } 706 707 /* Decrypt packet */ 708 m1 = m; 709 while (m1 != NULL) { 710 rc4_crypt(&d->rc4, mtod(m1, u_char *), 711 mtod(m1, u_char *), m1->m_len); 712 m1 = m1->m_next; 713 } 714 #endif 715 } else { 716 717 /* Are we expecting encryption? */ 718 if ((d->cfg.bits & MPPE_BITS) != 0) { 719 log(LOG_ERR, "%s: rec'd unexpectedly %s packet", 720 __func__, "unencrypted"); 721 goto failed; 722 } 723 } 724 725 /* Update coherency count for next time (12 bit arithmetic) */ 726 MPPC_CCOUNT_INC(d->cc); 727 728 /* Check for unexpected compressed packet */ 729 if ((header & MPPC_FLAG_COMPRESSED) != 0 730 && (d->cfg.bits & MPPC_BIT) == 0) { 731 log(LOG_ERR, "%s: rec'd unexpectedly %s packet", 732 __func__, "compressed"); 733 failed: 734 m_freem(m); 735 return (EINVAL); 736 } 737 738 #ifdef NETGRAPH_MPPC_COMPRESSION 739 /* Decompress packet */ 740 if ((header & MPPC_FLAG_COMPRESSED) != 0) { 741 int flags = MPPC_MANDATORY_DECOMPRESS_FLAGS; 742 u_char *inbuf, *outbuf; 743 int inlen, outlen, ina; 744 u_char *source, *dest; 745 u_long sourceCnt, destCnt; 746 int rtn; 747 748 /* Copy payload into a contiguous region of memory. */ 749 inlen = m->m_pkthdr.len; 750 if (m->m_next == NULL) { 751 inbuf = mtod(m, u_char *); 752 ina = 0; 753 } else { 754 inbuf = malloc(inlen, M_NETGRAPH_MPPC, M_NOWAIT); 755 if (inbuf == NULL) { 756 m_freem(m); 757 return (ENOMEM); 758 } 759 m_copydata(m, 0, inlen, (caddr_t)inbuf); 760 ina = 1; 761 } 762 763 /* Allocate a buffer for decompressed data */ 764 outbuf = malloc(MPPC_DECOMP_BUFSIZE + MPPC_DECOMP_SAFETY, 765 M_NETGRAPH_MPPC, M_NOWAIT); 766 if (outbuf == NULL) { 767 m_freem(m); 768 if (ina) 769 free(inbuf, M_NETGRAPH_MPPC); 770 return (ENOMEM); 771 } 772 outlen = MPPC_DECOMP_BUFSIZE; 773 774 /* Prepare to decompress */ 775 source = inbuf; 776 sourceCnt = inlen; 777 dest = outbuf; 778 destCnt = outlen; 779 if ((header & MPPC_FLAG_RESTART) != 0) 780 flags |= MPPC_RESTART_HISTORY; 781 782 /* Decompress */ 783 rtn = MPPC_Decompress(&source, &dest, 784 &sourceCnt, &destCnt, d->history, flags); 785 786 /* Check return value */ 787 KASSERT(rtn != MPPC_INVALID, ("%s: invalid", __func__)); 788 if ((rtn & MPPC_DEST_EXHAUSTED) != 0 789 || (rtn & MPPC_DECOMP_OK) != MPPC_DECOMP_OK) { 790 log(LOG_ERR, "%s: decomp returned 0x%x", 791 __func__, rtn); 792 if (ina) 793 free(inbuf, M_NETGRAPH_MPPC); 794 free(outbuf, M_NETGRAPH_MPPC); 795 goto failed; 796 } 797 798 /* Replace compressed data with decompressed data */ 799 if (ina) 800 free(inbuf, M_NETGRAPH_MPPC); 801 outlen -= destCnt; 802 803 m_copyback(m, 0, outlen, (caddr_t)outbuf); 804 if (m->m_pkthdr.len < outlen) { 805 m_freem(m); 806 m = NULL; 807 } else if (outlen < m->m_pkthdr.len) 808 m_adj(m, outlen - m->m_pkthdr.len); 809 free(outbuf, M_NETGRAPH_MPPC); 810 } 811 #endif 812 813 /* Return result in an mbuf */ 814 *datap = m; 815 return (*datap == NULL ? ENOBUFS : 0); 816 } 817 818 /* 819 * The peer has sent us a CCP ResetRequest, so reset our transmit state. 820 */ 821 static void 822 ng_mppc_reset_req(node_p node) 823 { 824 const priv_p priv = NG_NODE_PRIVATE(node); 825 struct ng_mppc_dir *const d = &priv->xmit; 826 827 #ifdef NETGRAPH_MPPC_COMPRESSION 828 if (d->history != NULL) 829 MPPC_InitCompressionHistory(d->history); 830 #endif 831 #ifdef NETGRAPH_MPPC_ENCRYPTION 832 if ((d->cfg.bits & MPPE_STATELESS) == 0) 833 rc4_init(&d->rc4, d->key, KEYLEN(d->cfg.bits)); 834 #endif 835 d->flushed = 1; 836 } 837 838 #ifdef NETGRAPH_MPPC_ENCRYPTION 839 /* 840 * Generate a new encryption key 841 */ 842 static void 843 ng_mppc_getkey(const u_char *h, u_char *h2, int len) 844 { 845 static const u_char pad1[40] = 846 { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 847 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 848 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 849 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; 850 static const u_char pad2[40] = 851 { 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 852 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 853 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 854 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2 }; 855 u_char hash[20]; 856 SHA1_CTX c; 857 858 SHA1Init(&c); 859 SHA1Update(&c, h, len); 860 SHA1Update(&c, pad1, sizeof(pad1)); 861 SHA1Update(&c, h2, len); 862 SHA1Update(&c, pad2, sizeof(pad2)); 863 SHA1Final(hash, &c); 864 bcopy(hash, h2, len); 865 } 866 867 /* 868 * Update the encryption key 869 */ 870 static void 871 ng_mppc_updatekey(u_int32_t bits, 872 u_char *key0, u_char *key, struct rc4_state *rc4) 873 { 874 const int keylen = KEYLEN(bits); 875 876 ng_mppc_getkey(key0, key, keylen); 877 rc4_init(rc4, key, keylen); 878 rc4_crypt(rc4, key, key, keylen); 879 if ((bits & MPPE_40) != 0) 880 bcopy(&ng_mppe_weakenkey, key, 3); 881 else if ((bits & MPPE_56) != 0) 882 bcopy(&ng_mppe_weakenkey, key, 1); 883 rc4_init(rc4, key, keylen); 884 } 885 #endif 886 887