xref: /freebsd/sys/netgraph/ng_mppc.c (revision 8fc257994d0ce2396196d7a06d50d20c8015f4b7)
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 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_NOWAIT | M_ZERO);
205 	if (priv == NULL)
206 		return (ENOMEM);
207 
208 	NG_NODE_SET_PRIVATE(node, priv);
209 
210 	/* This node is not thread safe. */
211 	NG_NODE_FORCE_WRITER(node);
212 
213 	/* Done */
214 	return (0);
215 }
216 
217 /*
218  * Give our OK for a hook to be added
219  */
220 static int
221 ng_mppc_newhook(node_p node, hook_p hook, const char *name)
222 {
223 	const priv_p priv = NG_NODE_PRIVATE(node);
224 	hook_p *hookPtr;
225 
226 	/* Check hook name */
227 	if (strcmp(name, NG_MPPC_HOOK_COMP) == 0)
228 		hookPtr = &priv->xmit.hook;
229 	else if (strcmp(name, NG_MPPC_HOOK_DECOMP) == 0)
230 		hookPtr = &priv->recv.hook;
231 	else
232 		return (EINVAL);
233 
234 	/* See if already connected */
235 	if (*hookPtr != NULL)
236 		return (EISCONN);
237 
238 	/* OK */
239 	*hookPtr = hook;
240 	return (0);
241 }
242 
243 /*
244  * Receive a control message
245  */
246 static int
247 ng_mppc_rcvmsg(node_p node, item_p item, hook_p lasthook)
248 {
249 	const priv_p priv = NG_NODE_PRIVATE(node);
250 	struct ng_mesg *resp = NULL;
251 	int error = 0;
252 	struct ng_mesg *msg;
253 
254 	NGI_GET_MSG(item, msg);
255 	switch (msg->header.typecookie) {
256 	case NGM_MPPC_COOKIE:
257 		switch (msg->header.cmd) {
258 		case NGM_MPPC_CONFIG_COMP:
259 		case NGM_MPPC_CONFIG_DECOMP:
260 		    {
261 			struct ng_mppc_config *const cfg
262 			    = (struct ng_mppc_config *)msg->data;
263 			const int isComp =
264 			    msg->header.cmd == NGM_MPPC_CONFIG_COMP;
265 			struct ng_mppc_dir *const d = isComp ?
266 			    &priv->xmit : &priv->recv;
267 
268 			/* Check configuration */
269 			if (msg->header.arglen != sizeof(*cfg))
270 				ERROUT(EINVAL);
271 			if (cfg->enable) {
272 				if ((cfg->bits & ~MPPC_VALID_BITS) != 0)
273 					ERROUT(EINVAL);
274 #ifndef NETGRAPH_MPPC_COMPRESSION
275 				if ((cfg->bits & MPPC_BIT) != 0)
276 					ERROUT(EPROTONOSUPPORT);
277 #endif
278 #ifndef NETGRAPH_MPPC_ENCRYPTION
279 				if ((cfg->bits & MPPE_BITS) != 0)
280 					ERROUT(EPROTONOSUPPORT);
281 #endif
282 			} else
283 				cfg->bits = 0;
284 
285 			/* Save return address so we can send reset-req's */
286 			if (!isComp)
287 				priv->ctrlnode = NGI_RETADDR(item);
288 
289 			/* Configuration is OK, reset to it */
290 			d->cfg = *cfg;
291 
292 #ifdef NETGRAPH_MPPC_COMPRESSION
293 			/* Initialize state buffers for compression */
294 			if (d->history != NULL) {
295 				free(d->history, M_NETGRAPH_MPPC);
296 				d->history = NULL;
297 			}
298 			if ((cfg->bits & MPPC_BIT) != 0) {
299 				d->history = malloc(isComp ?
300 				    MPPC_SizeOfCompressionHistory() :
301 				    MPPC_SizeOfDecompressionHistory(),
302 				    M_NETGRAPH_MPPC, M_NOWAIT);
303 				if (d->history == NULL)
304 					ERROUT(ENOMEM);
305 				if (isComp)
306 					MPPC_InitCompressionHistory(d->history);
307 				else {
308 					MPPC_InitDecompressionHistory(
309 					    d->history);
310 				}
311 			}
312 #endif
313 
314 #ifdef NETGRAPH_MPPC_ENCRYPTION
315 			/* Generate initial session keys for encryption */
316 			if ((cfg->bits & MPPE_BITS) != 0) {
317 				const int keylen = KEYLEN(cfg->bits);
318 
319 				bcopy(cfg->startkey, d->key, keylen);
320 				ng_mppc_getkey(cfg->startkey, d->key, keylen);
321 				if ((cfg->bits & MPPE_40) != 0)
322 					bcopy(&ng_mppe_weakenkey, d->key, 3);
323 				else if ((cfg->bits & MPPE_56) != 0)
324 					bcopy(&ng_mppe_weakenkey, d->key, 1);
325 				rc4_init(&d->rc4, d->key, keylen);
326 			}
327 #endif
328 
329 			/* Initialize other state */
330 			d->cc = 0;
331 			d->flushed = 0;
332 			break;
333 		    }
334 
335 		case NGM_MPPC_RESETREQ:
336 			ng_mppc_reset_req(node);
337 			break;
338 
339 		default:
340 			error = EINVAL;
341 			break;
342 		}
343 		break;
344 	default:
345 		error = EINVAL;
346 		break;
347 	}
348 done:
349 	NG_RESPOND_MSG(error, node, item, resp);
350 	NG_FREE_MSG(msg);
351 	return (error);
352 }
353 
354 /*
355  * Receive incoming data on our hook.
356  */
357 static int
358 ng_mppc_rcvdata(hook_p hook, item_p item)
359 {
360 	const node_p node = NG_HOOK_NODE(hook);
361 	const priv_p priv = NG_NODE_PRIVATE(node);
362 	int error;
363 	struct mbuf *m;
364 
365 	NGI_GET_M(item, m);
366 	/* Compress and/or encrypt */
367 	if (hook == priv->xmit.hook) {
368 		if (!priv->xmit.cfg.enable) {
369 			NG_FREE_M(m);
370 			NG_FREE_ITEM(item);
371 			return (ENXIO);
372 		}
373 		if ((error = ng_mppc_compress(node, &m)) != 0) {
374 			NG_FREE_ITEM(item);
375 			return(error);
376 		}
377 		NG_FWD_NEW_DATA(error, item, priv->xmit.hook, m);
378 		return (error);
379 	}
380 
381 	/* Decompress and/or decrypt */
382 	if (hook == priv->recv.hook) {
383 		if (!priv->recv.cfg.enable) {
384 			NG_FREE_M(m);
385 			NG_FREE_ITEM(item);
386 			return (ENXIO);
387 		}
388 		if ((error = ng_mppc_decompress(node, &m)) != 0) {
389 			NG_FREE_ITEM(item);
390 			if (error == EINVAL && priv->ctrlnode != 0) {
391 				struct ng_mesg *msg;
392 
393 				/* Need to send a reset-request */
394 				NG_MKMESSAGE(msg, NGM_MPPC_COOKIE,
395 				    NGM_MPPC_RESETREQ, 0, M_NOWAIT);
396 				if (msg == NULL)
397 					return (error);
398 				NG_SEND_MSG_ID(error, node, msg,
399 					priv->ctrlnode, 0);
400 			}
401 			return (error);
402 		}
403 		NG_FWD_NEW_DATA(error, item, priv->recv.hook, m);
404 		return (error);
405 	}
406 
407 	/* Oops */
408 	panic("%s: unknown hook", __func__);
409 #ifdef RESTARTABLE_PANICS
410 	return (EINVAL);
411 #endif
412 }
413 
414 /*
415  * Destroy node
416  */
417 static int
418 ng_mppc_shutdown(node_p node)
419 {
420 	const priv_p priv = NG_NODE_PRIVATE(node);
421 
422 	/* Take down netgraph node */
423 #ifdef NETGRAPH_MPPC_COMPRESSION
424 	if (priv->xmit.history != NULL)
425 		free(priv->xmit.history, M_NETGRAPH_MPPC);
426 	if (priv->recv.history != NULL)
427 		free(priv->recv.history, M_NETGRAPH_MPPC);
428 #endif
429 	bzero(priv, sizeof(*priv));
430 	free(priv, M_NETGRAPH_MPPC);
431 	NG_NODE_SET_PRIVATE(node, NULL);
432 	NG_NODE_UNREF(node);		/* let the node escape */
433 	return (0);
434 }
435 
436 /*
437  * Hook disconnection
438  */
439 static int
440 ng_mppc_disconnect(hook_p hook)
441 {
442 	const node_p node = NG_HOOK_NODE(hook);
443 	const priv_p priv = NG_NODE_PRIVATE(node);
444 
445 	/* Zero out hook pointer */
446 	if (hook == priv->xmit.hook)
447 		priv->xmit.hook = NULL;
448 	if (hook == priv->recv.hook)
449 		priv->recv.hook = NULL;
450 
451 	/* Go away if no longer connected */
452 	if ((NG_NODE_NUMHOOKS(node) == 0)
453 	&& NG_NODE_IS_VALID(node))
454 		ng_rmnode_self(node);
455 	return (0);
456 }
457 
458 /************************************************************************
459 			HELPER STUFF
460  ************************************************************************/
461 
462 /*
463  * Compress/encrypt a packet and put the result in a new mbuf at *resultp.
464  * The original mbuf is not free'd.
465  */
466 static int
467 ng_mppc_compress(node_p node, struct mbuf **datap)
468 {
469 	const priv_p priv = NG_NODE_PRIVATE(node);
470 	struct ng_mppc_dir *const d = &priv->xmit;
471 	u_int16_t header;
472 	struct mbuf *m = *datap;
473 
474 	/* We must own the mbuf chain exclusively to modify it. */
475 	m = m_unshare(m, M_DONTWAIT);
476 	if (m == NULL)
477 		return (ENOMEM);
478 
479 	/* Initialize */
480 	header = d->cc;
481 
482 	/* Always set the flushed bit in stateless mode */
483 	if (d->flushed || ((d->cfg.bits & MPPE_STATELESS) != 0)) {
484 		header |= MPPC_FLAG_FLUSHED;
485 		d->flushed = 0;
486 	}
487 
488 	/* Compress packet (if compression enabled) */
489 #ifdef NETGRAPH_MPPC_COMPRESSION
490 	if ((d->cfg.bits & MPPC_BIT) != 0) {
491 		u_short flags = MPPC_MANDATORY_COMPRESS_FLAGS;
492 		u_char *inbuf, *outbuf;
493 		int outlen, inlen, ina;
494 		u_char *source, *dest;
495 		u_long sourceCnt, destCnt;
496 		int rtn;
497 
498 		/* Work with contiguous regions of memory. */
499 		inlen = m->m_pkthdr.len;
500 		if (m->m_next == NULL) {
501 			inbuf = mtod(m, u_char *);
502 			ina = 0;
503 		} else {
504 			inbuf = malloc(inlen, M_NETGRAPH_MPPC, M_NOWAIT);
505 			if (inbuf == NULL)
506 				goto err1;
507 			m_copydata(m, 0, inlen, (caddr_t)inbuf);
508 			ina = 1;
509 		}
510 
511 		outlen = MPPC_MAX_BLOWUP(inlen);
512 		outbuf = malloc(outlen, M_NETGRAPH_MPPC, M_NOWAIT);
513 		if (outbuf == NULL) {
514 			if (ina)
515 				free(inbuf, M_NETGRAPH_MPPC);
516 err1:
517 			m_freem(m);
518 			MPPC_InitCompressionHistory(d->history);
519 			d->flushed = 1;
520 			return (ENOMEM);
521 		}
522 
523 		/* Prepare to compress */
524 		source = inbuf;
525 		sourceCnt = inlen;
526 		dest = outbuf;
527 		destCnt = outlen;
528 		if ((d->cfg.bits & MPPE_STATELESS) == 0)
529 			flags |= MPPC_SAVE_HISTORY;
530 
531 		/* Compress */
532 		rtn = MPPC_Compress(&source, &dest, &sourceCnt,
533 			&destCnt, d->history, flags, 0);
534 
535 		/* Check return value */
536 		KASSERT(rtn != MPPC_INVALID, ("%s: invalid", __func__));
537 		if ((rtn & MPPC_EXPANDED) == 0
538 		    && (rtn & MPPC_COMP_OK) == MPPC_COMP_OK) {
539 			outlen -= destCnt;
540 			header |= MPPC_FLAG_COMPRESSED;
541 			if ((rtn & MPPC_RESTART_HISTORY) != 0)
542 				header |= MPPC_FLAG_RESTART;
543 
544 			/* Replace m by the compresed one. */
545 			m_copyback(m, 0, outlen, (caddr_t)outbuf);
546 			if (m->m_pkthdr.len < outlen) {
547 				m_freem(m);
548 				m = NULL;
549 			} else if (outlen < m->m_pkthdr.len)
550 				m_adj(m, outlen - m->m_pkthdr.len);
551 		}
552 		d->flushed = (rtn & MPPC_EXPANDED) != 0
553 		    || (flags & MPPC_SAVE_HISTORY) == 0;
554 
555 		if (ina)
556 			free(inbuf, M_NETGRAPH_MPPC);
557 		free(outbuf, M_NETGRAPH_MPPC);
558 
559 		/* Check mbuf chain reload result. */
560 		if (m == NULL) {
561 			if (!d->flushed) {
562 				MPPC_InitCompressionHistory(d->history);
563 				d->flushed = 1;
564 			}
565 			return (ENOMEM);
566 		}
567 	}
568 #endif
569 
570 	/* Now encrypt packet (if encryption enabled) */
571 #ifdef NETGRAPH_MPPC_ENCRYPTION
572 	if ((d->cfg.bits & MPPE_BITS) != 0) {
573 		struct mbuf *m1;
574 
575 		/* Set header bits */
576 		header |= MPPC_FLAG_ENCRYPTED;
577 
578 		/* Update key if it's time */
579 		if ((d->cfg.bits & MPPE_STATELESS) != 0
580 		    || (d->cc & MPPE_UPDATE_MASK) == MPPE_UPDATE_FLAG) {
581 			ng_mppc_updatekey(d->cfg.bits,
582 			    d->cfg.startkey, d->key, &d->rc4);
583 		} else if ((header & MPPC_FLAG_FLUSHED) != 0) {
584 			/* Need to reset key if we say we did
585 			   and ng_mppc_updatekey wasn't called to do it also. */
586 			rc4_init(&d->rc4, d->key, KEYLEN(d->cfg.bits));
587 		}
588 
589 		/* Encrypt packet */
590 		m1 = m;
591 		while (m1) {
592 			rc4_crypt(&d->rc4, mtod(m1, u_char *),
593 			    mtod(m1, u_char *), m1->m_len);
594 			m1 = m1->m_next;
595 		}
596 	}
597 #endif
598 
599 	/* Update coherency count for next time (12 bit arithmetic) */
600 	MPPC_CCOUNT_INC(d->cc);
601 
602 	/* Install header */
603 	M_PREPEND(m, MPPC_HDRLEN, M_DONTWAIT);
604 	if (m != NULL)
605 		be16enc(mtod(m, void *), header);
606 
607 	*datap = m;
608 	return (*datap == NULL ? ENOBUFS : 0);
609 }
610 
611 /*
612  * Decompress/decrypt packet and put the result in a new mbuf at *resultp.
613  * The original mbuf is not free'd.
614  */
615 static int
616 ng_mppc_decompress(node_p node, struct mbuf **datap)
617 {
618 	const priv_p priv = NG_NODE_PRIVATE(node);
619 	struct ng_mppc_dir *const d = &priv->recv;
620 	u_int16_t header, cc;
621 	u_int numLost;
622 	struct mbuf *m = *datap;
623 
624 	/* We must own the mbuf chain exclusively to modify it. */
625 	m = m_unshare(m, M_DONTWAIT);
626 	if (m == NULL)
627 		return (ENOMEM);
628 
629 	/* Pull off header */
630 	if (m->m_pkthdr.len < MPPC_HDRLEN) {
631 		m_freem(m);
632 		return (EINVAL);
633 	}
634 	header = be16dec(mtod(m, void *));
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