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
ng_mppc_constructor(node_p node)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
ng_mppc_newhook(node_p node,hook_p hook,const char * name)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
ng_mppc_rcvmsg(node_p node,item_p item,hook_p lasthook)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
ng_mppc_rcvdata(hook_p hook,item_p item)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
ng_mppc_shutdown(node_p node)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
ng_mppc_disconnect(hook_p hook)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
ng_mppc_compress(node_p node,struct mbuf ** datap)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
ng_mppc_decompress(node_p node,struct mbuf ** datap)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
ng_mppc_reset_req(node_p node)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
ng_mppc_getkey(const u_char * h,u_char * h2,int len)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
ng_mppc_updatekey(u_int32_t bits,u_char * key0,u_char * key,struct rc4_state * rc4)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