xref: /freebsd/sys/netgraph/ng_mppc.c (revision 9162f64b58d01ec01481d60b6cdc06ffd8e8c7fc)
1 /*
2  * ng_mppc.c
3  */
4 
5 /*-
6  * Copyright (c) 1996-2000 Whistle Communications, Inc.
7  * All rights reserved.
8  *
9  * Subject to the following obligations and disclaimer of warranty, use and
10  * redistribution of this software, in source or object code forms, with or
11  * without modifications are expressly permitted by Whistle Communications;
12  * provided, however, that:
13  * 1. Any and all reproductions of the source or object code must include the
14  *    copyright notice above and the following disclaimer of warranties; and
15  * 2. No rights are granted, in any manner or form, to use Whistle
16  *    Communications, Inc. trademarks, including the mark "WHISTLE
17  *    COMMUNICATIONS" on advertising, endorsements, or otherwise except as
18  *    such appears in the above copyright notice or in the software.
19  *
20  * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND
21  * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO
22  * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE,
23  * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF
24  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
25  * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY
26  * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS
27  * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE.
28  * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES
29  * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING
30  * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
31  * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR
32  * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY
33  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
34  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
35  * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY
36  * OF SUCH DAMAGE.
37  *
38  * Author: Archie Cobbs <archie@freebsd.org>
39  *
40  * $Whistle: ng_mppc.c,v 1.4 1999/11/25 00:10:12 archie Exp $
41  * $FreeBSD$
42  */
43 
44 /*
45  * Microsoft PPP compression (MPPC) and encryption (MPPE) netgraph node type.
46  *
47  * You must define one or both of the NETGRAPH_MPPC_COMPRESSION and/or
48  * NETGRAPH_MPPC_ENCRYPTION options for this node type to be useful.
49  */
50 
51 #include <sys/param.h>
52 #include <sys/systm.h>
53 #include <sys/kernel.h>
54 #include <sys/mbuf.h>
55 #include <sys/malloc.h>
56 #include <sys/errno.h>
57 #include <sys/syslog.h>
58 
59 #include <netgraph/ng_message.h>
60 #include <netgraph/netgraph.h>
61 #include <netgraph/ng_mppc.h>
62 
63 #include "opt_netgraph.h"
64 
65 #if !defined(NETGRAPH_MPPC_COMPRESSION) && !defined(NETGRAPH_MPPC_ENCRYPTION)
66 #ifdef KLD_MODULE
67 /* XXX NETGRAPH_MPPC_COMPRESSION isn't functional yet */
68 #define NETGRAPH_MPPC_ENCRYPTION
69 #else
70 /* This case is indicative of an error in sys/conf files */
71 #error Need either NETGRAPH_MPPC_COMPRESSION or NETGRAPH_MPPC_ENCRYPTION
72 #endif
73 #endif
74 
75 #ifdef NG_SEPARATE_MALLOC
76 MALLOC_DEFINE(M_NETGRAPH_MPPC, "netgraph_mppc", "netgraph mppc node ");
77 #else
78 #define M_NETGRAPH_MPPC M_NETGRAPH
79 #endif
80 
81 #ifdef NETGRAPH_MPPC_COMPRESSION
82 /* XXX this file doesn't exist yet, but hopefully someday it will... */
83 #include <net/mppc.h>
84 #endif
85 #ifdef NETGRAPH_MPPC_ENCRYPTION
86 #include <crypto/rc4/rc4.h>
87 #endif
88 #include <crypto/sha1.h>
89 
90 /* Decompression blowup */
91 #define MPPC_DECOMP_BUFSIZE	8092            /* allocate buffer this big */
92 #define MPPC_DECOMP_SAFETY	100             /*   plus this much margin */
93 
94 /* MPPC/MPPE header length */
95 #define MPPC_HDRLEN		2
96 
97 /* Key length */
98 #define KEYLEN(b)		(((b) & MPPE_128) ? 16 : 8)
99 
100 /*
101  * When packets are lost with MPPE, we may have to re-key arbitrarily
102  * many times to 'catch up' to the new jumped-ahead sequence number.
103  * Since this can be expensive, we pose a limit on how many re-keyings
104  * we will do at one time to avoid a possible D.O.S. vulnerability.
105  * This should instead be a configurable parameter.
106  */
107 #define MPPE_MAX_REKEY		1000
108 
109 /* MPPC packet header bits */
110 #define MPPC_FLAG_FLUSHED	0x8000		/* xmitter reset state */
111 #define MPPC_FLAG_RESTART	0x4000		/* compress history restart */
112 #define MPPC_FLAG_COMPRESSED	0x2000		/* packet is compresed */
113 #define MPPC_FLAG_ENCRYPTED	0x1000		/* packet is encrypted */
114 #define MPPC_CCOUNT_MASK	0x0fff		/* sequence number mask */
115 
116 #define MPPC_CCOUNT_INC(d)	((d) = (((d) + 1) & MPPC_CCOUNT_MASK))
117 
118 #define MPPE_UPDATE_MASK	0xff		/* coherency count when we're */
119 #define MPPE_UPDATE_FLAG	0xff		/*   supposed to update key */
120 
121 #define MPPC_COMP_OK		0x05
122 #define MPPC_DECOMP_OK		0x05
123 
124 /* Per direction info */
125 struct ng_mppc_dir {
126 	struct ng_mppc_config	cfg;		/* configuration */
127 	hook_p			hook;		/* netgraph hook */
128 	u_int16_t		cc:12;		/* coherency count */
129 	u_char			flushed;	/* clean history (xmit only) */
130 #ifdef NETGRAPH_MPPC_COMPRESSION
131 	u_char			*history;	/* compression history */
132 #endif
133 #ifdef NETGRAPH_MPPC_ENCRYPTION
134 	u_char			key[MPPE_KEY_LEN];	/* session key */
135 	struct rc4_state	rc4;			/* rc4 state */
136 #endif
137 };
138 
139 /* Node private data */
140 struct ng_mppc_private {
141 	struct ng_mppc_dir	xmit;		/* compress/encrypt config */
142 	struct ng_mppc_dir	recv;		/* decompress/decrypt config */
143 	ng_ID_t			ctrlnode;	/* path to controlling node */
144 };
145 typedef struct ng_mppc_private *priv_p;
146 
147 /* Netgraph node methods */
148 static ng_constructor_t	ng_mppc_constructor;
149 static ng_rcvmsg_t	ng_mppc_rcvmsg;
150 static ng_shutdown_t	ng_mppc_shutdown;
151 static ng_newhook_t	ng_mppc_newhook;
152 static ng_rcvdata_t	ng_mppc_rcvdata;
153 static ng_disconnect_t	ng_mppc_disconnect;
154 
155 /* Helper functions */
156 static int	ng_mppc_compress(node_p node,
157 			struct mbuf **datap);
158 static int	ng_mppc_decompress(node_p node,
159 			struct mbuf **datap);
160 #ifdef NETGRAPH_MPPC_ENCRYPTION
161 static void	ng_mppc_getkey(const u_char *h, u_char *h2, int len);
162 static void	ng_mppc_updatekey(u_int32_t bits,
163 			u_char *key0, u_char *key, struct rc4_state *rc4);
164 #endif
165 static void	ng_mppc_reset_req(node_p node);
166 
167 /* Node type descriptor */
168 static struct ng_type ng_mppc_typestruct = {
169 	.version =	NG_ABI_VERSION,
170 	.name =		NG_MPPC_NODE_TYPE,
171 	.constructor =	ng_mppc_constructor,
172 	.rcvmsg =	ng_mppc_rcvmsg,
173 	.shutdown =	ng_mppc_shutdown,
174 	.newhook =	ng_mppc_newhook,
175 	.rcvdata =	ng_mppc_rcvdata,
176 	.disconnect =	ng_mppc_disconnect,
177 };
178 NETGRAPH_INIT(mppc, &ng_mppc_typestruct);
179 
180 #ifdef NETGRAPH_MPPC_ENCRYPTION
181 /* Depend on separate rc4 module */
182 MODULE_DEPEND(ng_mppc, rc4, 1, 1, 1);
183 #endif
184 
185 /* Fixed bit pattern to weaken keysize down to 40 or 56 bits */
186 static const u_char ng_mppe_weakenkey[3] = { 0xd1, 0x26, 0x9e };
187 
188 #define ERROUT(x)	do { error = (x); goto done; } while (0)
189 
190 /************************************************************************
191 			NETGRAPH NODE STUFF
192  ************************************************************************/
193 
194 /*
195  * Node type constructor
196  */
197 static int
198 ng_mppc_constructor(node_p node)
199 {
200 	priv_p priv;
201 
202 	/* Allocate private structure */
203 	priv = malloc(sizeof(*priv), M_NETGRAPH_MPPC, M_NOWAIT | M_ZERO);
204 	if (priv == NULL)
205 		return (ENOMEM);
206 
207 	NG_NODE_SET_PRIVATE(node, priv);
208 
209 	/* This node is not thread safe. */
210 	NG_NODE_FORCE_WRITER(node);
211 
212 	/* Done */
213 	return (0);
214 }
215 
216 /*
217  * Give our OK for a hook to be added
218  */
219 static int
220 ng_mppc_newhook(node_p node, hook_p hook, const char *name)
221 {
222 	const priv_p priv = NG_NODE_PRIVATE(node);
223 	hook_p *hookPtr;
224 
225 	/* Check hook name */
226 	if (strcmp(name, NG_MPPC_HOOK_COMP) == 0)
227 		hookPtr = &priv->xmit.hook;
228 	else if (strcmp(name, NG_MPPC_HOOK_DECOMP) == 0)
229 		hookPtr = &priv->recv.hook;
230 	else
231 		return (EINVAL);
232 
233 	/* See if already connected */
234 	if (*hookPtr != NULL)
235 		return (EISCONN);
236 
237 	/* OK */
238 	*hookPtr = hook;
239 	return (0);
240 }
241 
242 /*
243  * Receive a control message
244  */
245 static int
246 ng_mppc_rcvmsg(node_p node, item_p item, hook_p lasthook)
247 {
248 	const priv_p priv = NG_NODE_PRIVATE(node);
249 	struct ng_mesg *resp = NULL;
250 	int error = 0;
251 	struct ng_mesg *msg;
252 
253 	NGI_GET_MSG(item, msg);
254 	switch (msg->header.typecookie) {
255 	case NGM_MPPC_COOKIE:
256 		switch (msg->header.cmd) {
257 		case NGM_MPPC_CONFIG_COMP:
258 		case NGM_MPPC_CONFIG_DECOMP:
259 		    {
260 			struct ng_mppc_config *const cfg
261 			    = (struct ng_mppc_config *)msg->data;
262 			const int isComp =
263 			    msg->header.cmd == NGM_MPPC_CONFIG_COMP;
264 			struct ng_mppc_dir *const d = isComp ?
265 			    &priv->xmit : &priv->recv;
266 
267 			/* Check configuration */
268 			if (msg->header.arglen != sizeof(*cfg))
269 				ERROUT(EINVAL);
270 			if (cfg->enable) {
271 				if ((cfg->bits & ~MPPC_VALID_BITS) != 0)
272 					ERROUT(EINVAL);
273 #ifndef NETGRAPH_MPPC_COMPRESSION
274 				if ((cfg->bits & MPPC_BIT) != 0)
275 					ERROUT(EPROTONOSUPPORT);
276 #endif
277 #ifndef NETGRAPH_MPPC_ENCRYPTION
278 				if ((cfg->bits & MPPE_BITS) != 0)
279 					ERROUT(EPROTONOSUPPORT);
280 #endif
281 			} else
282 				cfg->bits = 0;
283 
284 			/* Save return address so we can send reset-req's */
285 			if (!isComp)
286 				priv->ctrlnode = NGI_RETADDR(item);
287 
288 			/* Configuration is OK, reset to it */
289 			d->cfg = *cfg;
290 
291 #ifdef NETGRAPH_MPPC_COMPRESSION
292 			/* Initialize state buffers for compression */
293 			if (d->history != NULL) {
294 				free(d->history, M_NETGRAPH_MPPC);
295 				d->history = NULL;
296 			}
297 			if ((cfg->bits & MPPC_BIT) != 0) {
298 				d->history = malloc(isComp ?
299 				    MPPC_SizeOfCompressionHistory() :
300 				    MPPC_SizeOfDecompressionHistory(),
301 				    M_NETGRAPH_MPPC, M_NOWAIT);
302 				if (d->history == NULL)
303 					ERROUT(ENOMEM);
304 				if (isComp)
305 					MPPC_InitCompressionHistory(d->history);
306 				else {
307 					MPPC_InitDecompressionHistory(
308 					    d->history);
309 				}
310 			}
311 #endif
312 
313 #ifdef NETGRAPH_MPPC_ENCRYPTION
314 			/* Generate initial session keys for encryption */
315 			if ((cfg->bits & MPPE_BITS) != 0) {
316 				const int keylen = KEYLEN(cfg->bits);
317 
318 				bcopy(cfg->startkey, d->key, keylen);
319 				ng_mppc_getkey(cfg->startkey, d->key, keylen);
320 				if ((cfg->bits & MPPE_40) != 0)
321 					bcopy(&ng_mppe_weakenkey, d->key, 3);
322 				else if ((cfg->bits & MPPE_56) != 0)
323 					bcopy(&ng_mppe_weakenkey, d->key, 1);
324 				rc4_init(&d->rc4, d->key, keylen);
325 			}
326 #endif
327 
328 			/* Initialize other state */
329 			d->cc = 0;
330 			d->flushed = 0;
331 			break;
332 		    }
333 
334 		case NGM_MPPC_RESETREQ:
335 			ng_mppc_reset_req(node);
336 			break;
337 
338 		default:
339 			error = EINVAL;
340 			break;
341 		}
342 		break;
343 	default:
344 		error = EINVAL;
345 		break;
346 	}
347 done:
348 	NG_RESPOND_MSG(error, node, item, resp);
349 	NG_FREE_MSG(msg);
350 	return (error);
351 }
352 
353 /*
354  * Receive incoming data on our hook.
355  */
356 static int
357 ng_mppc_rcvdata(hook_p hook, item_p item)
358 {
359 	const node_p node = NG_HOOK_NODE(hook);
360 	const priv_p priv = NG_NODE_PRIVATE(node);
361 	int error;
362 	struct mbuf *m;
363 
364 	NGI_GET_M(item, m);
365 	/* Compress and/or encrypt */
366 	if (hook == priv->xmit.hook) {
367 		if (!priv->xmit.cfg.enable) {
368 			NG_FREE_M(m);
369 			NG_FREE_ITEM(item);
370 			return (ENXIO);
371 		}
372 		if ((error = ng_mppc_compress(node, &m)) != 0) {
373 			NG_FREE_ITEM(item);
374 			return(error);
375 		}
376 		NG_FWD_NEW_DATA(error, item, priv->xmit.hook, m);
377 		return (error);
378 	}
379 
380 	/* Decompress and/or decrypt */
381 	if (hook == priv->recv.hook) {
382 		if (!priv->recv.cfg.enable) {
383 			NG_FREE_M(m);
384 			NG_FREE_ITEM(item);
385 			return (ENXIO);
386 		}
387 		if ((error = ng_mppc_decompress(node, &m)) != 0) {
388 			NG_FREE_ITEM(item);
389 			if (error == EINVAL && priv->ctrlnode != 0) {
390 				struct ng_mesg *msg;
391 
392 				/* Need to send a reset-request */
393 				NG_MKMESSAGE(msg, NGM_MPPC_COOKIE,
394 				    NGM_MPPC_RESETREQ, 0, M_NOWAIT);
395 				if (msg == NULL)
396 					return (error);
397 				NG_SEND_MSG_ID(error, node, msg,
398 					priv->ctrlnode, 0);
399 			}
400 			return (error);
401 		}
402 		NG_FWD_NEW_DATA(error, item, priv->recv.hook, m);
403 		return (error);
404 	}
405 
406 	/* Oops */
407 	panic("%s: unknown hook", __func__);
408 #ifdef RESTARTABLE_PANICS
409 	return (EINVAL);
410 #endif
411 }
412 
413 /*
414  * Destroy node
415  */
416 static int
417 ng_mppc_shutdown(node_p node)
418 {
419 	const priv_p priv = NG_NODE_PRIVATE(node);
420 
421 	/* Take down netgraph node */
422 #ifdef NETGRAPH_MPPC_COMPRESSION
423 	if (priv->xmit.history != NULL)
424 		free(priv->xmit.history, M_NETGRAPH_MPPC);
425 	if (priv->recv.history != NULL)
426 		free(priv->recv.history, M_NETGRAPH_MPPC);
427 #endif
428 	bzero(priv, sizeof(*priv));
429 	free(priv, M_NETGRAPH_MPPC);
430 	NG_NODE_SET_PRIVATE(node, NULL);
431 	NG_NODE_UNREF(node);		/* let the node escape */
432 	return (0);
433 }
434 
435 /*
436  * Hook disconnection
437  */
438 static int
439 ng_mppc_disconnect(hook_p hook)
440 {
441 	const node_p node = NG_HOOK_NODE(hook);
442 	const priv_p priv = NG_NODE_PRIVATE(node);
443 
444 	/* Zero out hook pointer */
445 	if (hook == priv->xmit.hook)
446 		priv->xmit.hook = NULL;
447 	if (hook == priv->recv.hook)
448 		priv->recv.hook = NULL;
449 
450 	/* Go away if no longer connected */
451 	if ((NG_NODE_NUMHOOKS(node) == 0)
452 	&& NG_NODE_IS_VALID(node))
453 		ng_rmnode_self(node);
454 	return (0);
455 }
456 
457 /************************************************************************
458 			HELPER STUFF
459  ************************************************************************/
460 
461 /*
462  * Compress/encrypt a packet and put the result in a new mbuf at *resultp.
463  * The original mbuf is not free'd.
464  */
465 static int
466 ng_mppc_compress(node_p node, struct mbuf **datap)
467 {
468 	const priv_p priv = NG_NODE_PRIVATE(node);
469 	struct ng_mppc_dir *const d = &priv->xmit;
470 	u_int16_t header;
471 	struct mbuf *m = *datap;
472 
473 	/* Initialize */
474 	header = d->cc;
475 
476 	/* Always set the flushed bit in stateless mode */
477 	if (d->flushed || ((d->cfg.bits & MPPE_STATELESS) != 0)) {
478 		header |= MPPC_FLAG_FLUSHED;
479 		d->flushed = 0;
480 	}
481 
482 	/* Compress packet (if compression enabled) */
483 #ifdef NETGRAPH_MPPC_COMPRESSION
484 	if ((d->cfg.bits & MPPC_BIT) != 0) {
485 		u_short flags = MPPC_MANDATORY_COMPRESS_FLAGS;
486 		u_char *inbuf, *outbuf;
487 		int outlen, inlen;
488 		u_char *source, *dest;
489 		u_long sourceCnt, destCnt;
490 		int rtn;
491 
492 		/* Work with contiguous regions of memory. */
493 		inlen = m->m_pkthdr.len;
494 		inbuf = malloc(inlen, M_NETGRAPH_MPPC, M_NOWAIT);
495 		if (inbuf == NULL)
496 			goto err1;
497 		m_copydata(m, 0, inlen, (caddr_t)inbuf);
498 
499 		outlen = MPPC_MAX_BLOWUP(inlen);
500 		outbuf = malloc(outlen, M_NETGRAPH_MPPC, M_NOWAIT);
501 		if (outbuf == NULL) {
502 			free(inbuf, M_NETGRAPH_MPPC);
503 err1:
504 			m_freem(m);
505 			MPPC_InitCompressionHistory(d->history);
506 			d->flushed = 1;
507 			return (ENOMEM);
508 		}
509 
510 		/* Prepare to compress */
511 		source = inbuf;
512 		sourceCnt = inlen;
513 		dest = outbuf;
514 		destCnt = outlen;
515 		if ((d->cfg.bits & MPPE_STATELESS) == 0)
516 			flags |= MPPC_SAVE_HISTORY;
517 
518 		/* Compress */
519 		rtn = MPPC_Compress(&source, &dest, &sourceCnt,
520 			&destCnt, d->history, flags, 0);
521 
522 		/* Check return value */
523 		KASSERT(rtn != MPPC_INVALID, ("%s: invalid", __func__));
524 		if ((rtn & MPPC_EXPANDED) == 0
525 		    && (rtn & MPPC_COMP_OK) == MPPC_COMP_OK) {
526 			outlen -= destCnt;
527 			header |= MPPC_FLAG_COMPRESSED;
528 			if ((rtn & MPPC_RESTART_HISTORY) != 0)
529 				header |= MPPC_FLAG_RESTART;
530 
531 			/* Replace m by the compresed one. */
532 			m_freem(m);
533 			m = m_devget((caddr_t)outbuf, outlen, 0, NULL, NULL);
534 		}
535 		d->flushed = (rtn & MPPC_EXPANDED) != 0
536 		    || (flags & MPPC_SAVE_HISTORY) == 0;
537 
538 		free(inbuf, M_NETGRAPH_MPPC);
539 		free(outbuf, M_NETGRAPH_MPPC);
540 
541 		/* Check m_devget() result. */
542 		if (m == NULL) {
543 			if (!d->flushed) {
544 				MPPC_InitCompressionHistory(d->history);
545 				d->flushed = 1;
546 			}
547 			return (ENOMEM);
548 		}
549 	}
550 #endif
551 
552 	/* Now encrypt packet (if encryption enabled) */
553 #ifdef NETGRAPH_MPPC_ENCRYPTION
554 	if ((d->cfg.bits & MPPE_BITS) != 0) {
555 		struct mbuf *m1;
556 
557 		/* Set header bits */
558 		header |= MPPC_FLAG_ENCRYPTED;
559 
560 		/* We must own the mbuf chain exclusively to modify it. */
561 		m = m_unshare(m, M_DONTWAIT);
562 		if (m == NULL) {
563 			if (!d->flushed) {
564 #ifdef NETGRAPH_MPPC_COMPRESSION
565 				MPPC_InitCompressionHistory(d->history);
566 #endif
567 				d->flushed = 1;
568 			}
569 			return (ENOMEM);
570 		}
571 
572 		/* Update key if it's time */
573 		if ((d->cfg.bits & MPPE_STATELESS) != 0
574 		    || (d->cc & MPPE_UPDATE_MASK) == MPPE_UPDATE_FLAG) {
575 			ng_mppc_updatekey(d->cfg.bits,
576 			    d->cfg.startkey, d->key, &d->rc4);
577 		} else if ((header & MPPC_FLAG_FLUSHED) != 0) {
578 			/* Need to reset key if we say we did
579 			   and ng_mppc_updatekey wasn't called to do it also. */
580 			rc4_init(&d->rc4, d->key, KEYLEN(d->cfg.bits));
581 		}
582 
583 		/* Encrypt packet */
584 		m1 = m;
585 		while (m1) {
586 			rc4_crypt(&d->rc4, mtod(m1, u_char *),
587 			    mtod(m1, u_char *), m1->m_len);
588 			m1 = m1->m_next;
589 		}
590 	}
591 #endif
592 
593 	/* Update coherency count for next time (12 bit arithmetic) */
594 	MPPC_CCOUNT_INC(d->cc);
595 
596 	/* Install header */
597 	M_PREPEND(m, MPPC_HDRLEN, M_DONTWAIT);
598 	if (m != NULL)
599 		*(mtod(m, uint16_t *)) = htons(header);
600 
601 	*datap = m;
602 	return (*datap == NULL ? ENOBUFS : 0);
603 }
604 
605 /*
606  * Decompress/decrypt packet and put the result in a new mbuf at *resultp.
607  * The original mbuf is not free'd.
608  */
609 static int
610 ng_mppc_decompress(node_p node, struct mbuf **datap)
611 {
612 	const priv_p priv = NG_NODE_PRIVATE(node);
613 	struct ng_mppc_dir *const d = &priv->recv;
614 	u_int16_t header, cc;
615 	u_int numLost;
616 	struct mbuf *m = *datap;
617 
618 	/* Pull off header */
619 	if (m->m_pkthdr.len < MPPC_HDRLEN) {
620 		m_freem(m);
621 		return (EINVAL);
622 	}
623 	m_copydata(m, 0, MPPC_HDRLEN, (caddr_t)&header);
624 	header = ntohs(header);
625 	cc = (header & MPPC_CCOUNT_MASK);
626 	m_adj(m, MPPC_HDRLEN);
627 
628 	/* Check for an unexpected jump in the sequence number */
629 	numLost = ((cc - d->cc) & MPPC_CCOUNT_MASK);
630 
631 	/* If flushed bit set, we can always handle packet */
632 	if ((header & MPPC_FLAG_FLUSHED) != 0) {
633 #ifdef NETGRAPH_MPPC_COMPRESSION
634 		if (d->history != NULL)
635 			MPPC_InitDecompressionHistory(d->history);
636 #endif
637 #ifdef NETGRAPH_MPPC_ENCRYPTION
638 		if ((d->cfg.bits & MPPE_BITS) != 0) {
639 			u_int rekey;
640 
641 			/* How many times are we going to have to re-key? */
642 			rekey = ((d->cfg.bits & MPPE_STATELESS) != 0) ?
643 			    numLost : (numLost / (MPPE_UPDATE_MASK + 1));
644 			if (rekey > MPPE_MAX_REKEY) {
645 				log(LOG_ERR, "%s: too many (%d) packets"
646 				    " dropped, disabling node %p!",
647 				    __func__, numLost, node);
648 				priv->recv.cfg.enable = 0;
649 				goto failed;
650 			}
651 
652 			/* Re-key as necessary to catch up to peer */
653 			while (d->cc != cc) {
654 				if ((d->cfg.bits & MPPE_STATELESS) != 0
655 				    || (d->cc & MPPE_UPDATE_MASK)
656 				      == MPPE_UPDATE_FLAG) {
657 					ng_mppc_updatekey(d->cfg.bits,
658 					    d->cfg.startkey, d->key, &d->rc4);
659 				}
660 				MPPC_CCOUNT_INC(d->cc);
661 			}
662 
663 			/* Reset key (except in stateless mode, see below) */
664 			if ((d->cfg.bits & MPPE_STATELESS) == 0)
665 				rc4_init(&d->rc4, d->key, KEYLEN(d->cfg.bits));
666 		}
667 #endif
668 		d->cc = cc;		/* skip over lost seq numbers */
669 		numLost = 0;		/* act like no packets were lost */
670 	}
671 
672 	/* Can't decode non-sequential packets without a flushed bit */
673 	if (numLost != 0)
674 		goto failed;
675 
676 	/* Decrypt packet */
677 	if ((header & MPPC_FLAG_ENCRYPTED) != 0) {
678 #ifdef NETGRAPH_MPPC_ENCRYPTION
679 		struct mbuf *m1;
680 #endif
681 
682 		/* Are we not expecting encryption? */
683 		if ((d->cfg.bits & MPPE_BITS) == 0) {
684 			log(LOG_ERR, "%s: rec'd unexpectedly %s packet",
685 				__func__, "encrypted");
686 			goto failed;
687 		}
688 
689 #ifdef NETGRAPH_MPPC_ENCRYPTION
690 		/* Update key if it's time (always in stateless mode) */
691 		if ((d->cfg.bits & MPPE_STATELESS) != 0
692 		    || (d->cc & MPPE_UPDATE_MASK) == MPPE_UPDATE_FLAG) {
693 			ng_mppc_updatekey(d->cfg.bits,
694 			    d->cfg.startkey, d->key, &d->rc4);
695 		}
696 
697 		/* We must own the mbuf chain exclusively to modify it. */
698 		m = m_unshare(m, M_DONTWAIT);
699 		if (m == NULL)
700 			return (ENOMEM);
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 *decompbuf, *source, *dest;
738 		u_long sourceCnt, destCnt;
739 		int decomplen, rtn;
740 		u_char *buf;
741 		int len;
742 
743 		/* Copy payload into a contiguous region of memory. */
744 		len = m->m_pkthdr.len;
745 		buf = malloc(len, M_NETGRAPH_MPPC, M_NOWAIT);
746 		if (buf == NULL) {
747 			m_freem(m);
748 			return (ENOMEM);
749 		}
750 		m_copydata(m, 0, len, (caddr_t)buf);
751 
752 		/* Allocate a buffer for decompressed data */
753 		decompbuf = malloc(MPPC_DECOMP_BUFSIZE + MPPC_DECOMP_SAFETY,
754 		    M_NETGRAPH_MPPC, M_NOWAIT);
755 		if (decompbuf == NULL) {
756 			m_freem(m);
757 			free(buf, M_NETGRAPH_MPPC);
758 			return (ENOMEM);
759 		}
760 		decomplen = MPPC_DECOMP_BUFSIZE;
761 
762 		/* Prepare to decompress */
763 		source = buf;
764 		sourceCnt = len;
765 		dest = decompbuf;
766 		destCnt = decomplen;
767 		if ((header & MPPC_FLAG_RESTART) != 0)
768 			flags |= MPPC_RESTART_HISTORY;
769 
770 		/* Decompress */
771 		rtn = MPPC_Decompress(&source, &dest,
772 			&sourceCnt, &destCnt, d->history, flags);
773 
774 		/* Check return value */
775 		KASSERT(rtn != MPPC_INVALID, ("%s: invalid", __func__));
776 		if ((rtn & MPPC_DEST_EXHAUSTED) != 0
777 		    || (rtn & MPPC_DECOMP_OK) != MPPC_DECOMP_OK) {
778 			log(LOG_ERR, "%s: decomp returned 0x%x",
779 			    __func__, rtn);
780 			free(buf, M_NETGRAPH_MPPC);
781 			free(decompbuf, M_NETGRAPH_MPPC);
782 			goto failed;
783 		}
784 
785 		/* Replace compressed data with decompressed data */
786 		free(buf, M_NETGRAPH_MPPC);
787 		len = decomplen - destCnt;
788 
789 		m_freem(m);
790 		m = m_devget((caddr_t)decompbuf, len, 0, NULL, NULL);
791 		free(decompbuf, M_NETGRAPH_MPPC);
792 	}
793 #endif
794 
795 	/* Return result in an mbuf */
796 	*datap = m;
797 	return (*datap == NULL ? ENOBUFS : 0);
798 }
799 
800 /*
801  * The peer has sent us a CCP ResetRequest, so reset our transmit state.
802  */
803 static void
804 ng_mppc_reset_req(node_p node)
805 {
806 	const priv_p priv = NG_NODE_PRIVATE(node);
807 	struct ng_mppc_dir *const d = &priv->xmit;
808 
809 #ifdef NETGRAPH_MPPC_COMPRESSION
810 	if (d->history != NULL)
811 		MPPC_InitCompressionHistory(d->history);
812 #endif
813 #ifdef NETGRAPH_MPPC_ENCRYPTION
814 	if ((d->cfg.bits & MPPE_STATELESS) == 0)
815 		rc4_init(&d->rc4, d->key, KEYLEN(d->cfg.bits));
816 #endif
817 	d->flushed = 1;
818 }
819 
820 #ifdef NETGRAPH_MPPC_ENCRYPTION
821 /*
822  * Generate a new encryption key
823  */
824 static void
825 ng_mppc_getkey(const u_char *h, u_char *h2, int len)
826 {
827 	static const u_char pad1[40] =
828 	    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
829 	      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
830 	      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
831 	      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
832 	static const u_char pad2[40] =
833 	    { 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2,
834 	      0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2,
835 	      0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2,
836 	      0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2 };
837 	u_char hash[20];
838 	SHA1_CTX c;
839 
840 	SHA1Init(&c);
841 	SHA1Update(&c, h, len);
842 	SHA1Update(&c, pad1, sizeof(pad1));
843 	SHA1Update(&c, h2, len);
844 	SHA1Update(&c, pad2, sizeof(pad2));
845 	SHA1Final(hash, &c);
846 	bcopy(hash, h2, len);
847 }
848 
849 /*
850  * Update the encryption key
851  */
852 static void
853 ng_mppc_updatekey(u_int32_t bits,
854 	u_char *key0, u_char *key, struct rc4_state *rc4)
855 {
856 	const int keylen = KEYLEN(bits);
857 
858 	ng_mppc_getkey(key0, key, keylen);
859 	rc4_init(rc4, key, keylen);
860 	rc4_crypt(rc4, key, key, keylen);
861 	if ((bits & MPPE_40) != 0)
862 		bcopy(&ng_mppe_weakenkey, key, 3);
863 	else if ((bits & MPPE_56) != 0)
864 		bcopy(&ng_mppe_weakenkey, key, 1);
865 	rc4_init(rc4, key, keylen);
866 }
867 #endif
868 
869