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