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