xref: /freebsd/sys/netgraph/ng_mppc.c (revision 884a2a699669ec61e2366e3e358342dbc94be24a)
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_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 #ifdef RESTARTABLE_PANICS
408 	return (EINVAL);
409 #endif
410 }
411 
412 /*
413  * Destroy node
414  */
415 static int
416 ng_mppc_shutdown(node_p node)
417 {
418 	const priv_p priv = NG_NODE_PRIVATE(node);
419 
420 	/* Take down netgraph node */
421 #ifdef NETGRAPH_MPPC_COMPRESSION
422 	if (priv->xmit.history != NULL)
423 		free(priv->xmit.history, M_NETGRAPH_MPPC);
424 	if (priv->recv.history != NULL)
425 		free(priv->recv.history, M_NETGRAPH_MPPC);
426 #endif
427 	bzero(priv, sizeof(*priv));
428 	free(priv, M_NETGRAPH_MPPC);
429 	NG_NODE_SET_PRIVATE(node, NULL);
430 	NG_NODE_UNREF(node);		/* let the node escape */
431 	return (0);
432 }
433 
434 /*
435  * Hook disconnection
436  */
437 static int
438 ng_mppc_disconnect(hook_p hook)
439 {
440 	const node_p node = NG_HOOK_NODE(hook);
441 	const priv_p priv = NG_NODE_PRIVATE(node);
442 
443 	/* Zero out hook pointer */
444 	if (hook == priv->xmit.hook)
445 		priv->xmit.hook = NULL;
446 	if (hook == priv->recv.hook)
447 		priv->recv.hook = NULL;
448 
449 	/* Go away if no longer connected */
450 	if ((NG_NODE_NUMHOOKS(node) == 0)
451 	&& NG_NODE_IS_VALID(node))
452 		ng_rmnode_self(node);
453 	return (0);
454 }
455 
456 /************************************************************************
457 			HELPER STUFF
458  ************************************************************************/
459 
460 /*
461  * Compress/encrypt a packet and put the result in a new mbuf at *resultp.
462  * The original mbuf is not free'd.
463  */
464 static int
465 ng_mppc_compress(node_p node, struct mbuf **datap)
466 {
467 	const priv_p priv = NG_NODE_PRIVATE(node);
468 	struct ng_mppc_dir *const d = &priv->xmit;
469 	u_int16_t header;
470 	struct mbuf *m = *datap;
471 
472 	/* We must own the mbuf chain exclusively to modify it. */
473 	m = m_unshare(m, M_DONTWAIT);
474 	if (m == NULL)
475 		return (ENOMEM);
476 
477 	/* Initialize */
478 	header = d->cc;
479 
480 	/* Always set the flushed bit in stateless mode */
481 	if (d->flushed || ((d->cfg.bits & MPPE_STATELESS) != 0)) {
482 		header |= MPPC_FLAG_FLUSHED;
483 		d->flushed = 0;
484 	}
485 
486 	/* Compress packet (if compression enabled) */
487 #ifdef NETGRAPH_MPPC_COMPRESSION
488 	if ((d->cfg.bits & MPPC_BIT) != 0) {
489 		u_short flags = MPPC_MANDATORY_COMPRESS_FLAGS;
490 		u_char *inbuf, *outbuf;
491 		int outlen, inlen, ina;
492 		u_char *source, *dest;
493 		u_long sourceCnt, destCnt;
494 		int rtn;
495 
496 		/* Work with contiguous regions of memory. */
497 		inlen = m->m_pkthdr.len;
498 		if (m->m_next == NULL) {
499 			inbuf = mtod(m, u_char *);
500 			ina = 0;
501 		} else {
502 			inbuf = malloc(inlen, M_NETGRAPH_MPPC, M_NOWAIT);
503 			if (inbuf == NULL)
504 				goto err1;
505 			m_copydata(m, 0, inlen, (caddr_t)inbuf);
506 			ina = 1;
507 		}
508 
509 		outlen = MPPC_MAX_BLOWUP(inlen);
510 		outbuf = malloc(outlen, M_NETGRAPH_MPPC, M_NOWAIT);
511 		if (outbuf == NULL) {
512 			if (ina)
513 				free(inbuf, M_NETGRAPH_MPPC);
514 err1:
515 			m_freem(m);
516 			MPPC_InitCompressionHistory(d->history);
517 			d->flushed = 1;
518 			return (ENOMEM);
519 		}
520 
521 		/* Prepare to compress */
522 		source = inbuf;
523 		sourceCnt = inlen;
524 		dest = outbuf;
525 		destCnt = outlen;
526 		if ((d->cfg.bits & MPPE_STATELESS) == 0)
527 			flags |= MPPC_SAVE_HISTORY;
528 
529 		/* Compress */
530 		rtn = MPPC_Compress(&source, &dest, &sourceCnt,
531 			&destCnt, d->history, flags, 0);
532 
533 		/* Check return value */
534 		KASSERT(rtn != MPPC_INVALID, ("%s: invalid", __func__));
535 		if ((rtn & MPPC_EXPANDED) == 0
536 		    && (rtn & MPPC_COMP_OK) == MPPC_COMP_OK) {
537 			outlen -= destCnt;
538 			header |= MPPC_FLAG_COMPRESSED;
539 			if ((rtn & MPPC_RESTART_HISTORY) != 0)
540 				header |= MPPC_FLAG_RESTART;
541 
542 			/* Replace m by the compresed one. */
543 			m_copyback(m, 0, outlen, (caddr_t)outbuf);
544 			if (m->m_pkthdr.len < outlen) {
545 				m_freem(m);
546 				m = NULL;
547 			} else if (outlen < m->m_pkthdr.len)
548 				m_adj(m, outlen - m->m_pkthdr.len);
549 		}
550 		d->flushed = (rtn & MPPC_EXPANDED) != 0
551 		    || (flags & MPPC_SAVE_HISTORY) == 0;
552 
553 		if (ina)
554 			free(inbuf, M_NETGRAPH_MPPC);
555 		free(outbuf, M_NETGRAPH_MPPC);
556 
557 		/* Check mbuf chain reload result. */
558 		if (m == NULL) {
559 			if (!d->flushed) {
560 				MPPC_InitCompressionHistory(d->history);
561 				d->flushed = 1;
562 			}
563 			return (ENOMEM);
564 		}
565 	}
566 #endif
567 
568 	/* Now encrypt packet (if encryption enabled) */
569 #ifdef NETGRAPH_MPPC_ENCRYPTION
570 	if ((d->cfg.bits & MPPE_BITS) != 0) {
571 		struct mbuf *m1;
572 
573 		/* Set header bits */
574 		header |= MPPC_FLAG_ENCRYPTED;
575 
576 		/* Update key if it's time */
577 		if ((d->cfg.bits & MPPE_STATELESS) != 0
578 		    || (d->cc & MPPE_UPDATE_MASK) == MPPE_UPDATE_FLAG) {
579 			ng_mppc_updatekey(d->cfg.bits,
580 			    d->cfg.startkey, d->key, &d->rc4);
581 		} else if ((header & MPPC_FLAG_FLUSHED) != 0) {
582 			/* Need to reset key if we say we did
583 			   and ng_mppc_updatekey wasn't called to do it also. */
584 			rc4_init(&d->rc4, d->key, KEYLEN(d->cfg.bits));
585 		}
586 
587 		/* Encrypt packet */
588 		m1 = m;
589 		while (m1) {
590 			rc4_crypt(&d->rc4, mtod(m1, u_char *),
591 			    mtod(m1, u_char *), m1->m_len);
592 			m1 = m1->m_next;
593 		}
594 	}
595 #endif
596 
597 	/* Update coherency count for next time (12 bit arithmetic) */
598 	MPPC_CCOUNT_INC(d->cc);
599 
600 	/* Install header */
601 	M_PREPEND(m, MPPC_HDRLEN, M_DONTWAIT);
602 	if (m != NULL)
603 		be16enc(mtod(m, void *), header);
604 
605 	*datap = m;
606 	return (*datap == NULL ? ENOBUFS : 0);
607 }
608 
609 /*
610  * Decompress/decrypt packet and put the result in a new mbuf at *resultp.
611  * The original mbuf is not free'd.
612  */
613 static int
614 ng_mppc_decompress(node_p node, struct mbuf **datap)
615 {
616 	const priv_p priv = NG_NODE_PRIVATE(node);
617 	struct ng_mppc_dir *const d = &priv->recv;
618 	u_int16_t header, cc;
619 	u_int numLost;
620 	struct mbuf *m = *datap;
621 
622 	/* We must own the mbuf chain exclusively to modify it. */
623 	m = m_unshare(m, M_DONTWAIT);
624 	if (m == NULL)
625 		return (ENOMEM);
626 
627 	/* Pull off header */
628 	if (m->m_pkthdr.len < MPPC_HDRLEN) {
629 		m_freem(m);
630 		return (EINVAL);
631 	}
632 	header = be16dec(mtod(m, void *));
633 	cc = (header & MPPC_CCOUNT_MASK);
634 	m_adj(m, MPPC_HDRLEN);
635 
636 	/* Check for an unexpected jump in the sequence number */
637 	numLost = ((cc - d->cc) & MPPC_CCOUNT_MASK);
638 
639 	/* If flushed bit set, we can always handle packet */
640 	if ((header & MPPC_FLAG_FLUSHED) != 0) {
641 #ifdef NETGRAPH_MPPC_COMPRESSION
642 		if (d->history != NULL)
643 			MPPC_InitDecompressionHistory(d->history);
644 #endif
645 #ifdef NETGRAPH_MPPC_ENCRYPTION
646 		if ((d->cfg.bits & MPPE_BITS) != 0) {
647 			u_int rekey;
648 
649 			/* How many times are we going to have to re-key? */
650 			rekey = ((d->cfg.bits & MPPE_STATELESS) != 0) ?
651 			    numLost : (numLost / (MPPE_UPDATE_MASK + 1));
652 			if (rekey > MPPE_MAX_REKEY) {
653 				log(LOG_ERR, "%s: too many (%d) packets"
654 				    " dropped, disabling node %p!",
655 				    __func__, numLost, node);
656 				priv->recv.cfg.enable = 0;
657 				goto failed;
658 			}
659 
660 			/* Re-key as necessary to catch up to peer */
661 			while (d->cc != cc) {
662 				if ((d->cfg.bits & MPPE_STATELESS) != 0
663 				    || (d->cc & MPPE_UPDATE_MASK)
664 				      == MPPE_UPDATE_FLAG) {
665 					ng_mppc_updatekey(d->cfg.bits,
666 					    d->cfg.startkey, d->key, &d->rc4);
667 				}
668 				MPPC_CCOUNT_INC(d->cc);
669 			}
670 
671 			/* Reset key (except in stateless mode, see below) */
672 			if ((d->cfg.bits & MPPE_STATELESS) == 0)
673 				rc4_init(&d->rc4, d->key, KEYLEN(d->cfg.bits));
674 		}
675 #endif
676 		d->cc = cc;		/* skip over lost seq numbers */
677 		numLost = 0;		/* act like no packets were lost */
678 	}
679 
680 	/* Can't decode non-sequential packets without a flushed bit */
681 	if (numLost != 0)
682 		goto failed;
683 
684 	/* Decrypt packet */
685 	if ((header & MPPC_FLAG_ENCRYPTED) != 0) {
686 #ifdef NETGRAPH_MPPC_ENCRYPTION
687 		struct mbuf *m1;
688 #endif
689 
690 		/* Are we not expecting encryption? */
691 		if ((d->cfg.bits & MPPE_BITS) == 0) {
692 			log(LOG_ERR, "%s: rec'd unexpectedly %s packet",
693 				__func__, "encrypted");
694 			goto failed;
695 		}
696 
697 #ifdef NETGRAPH_MPPC_ENCRYPTION
698 		/* Update key if it's time (always in stateless mode) */
699 		if ((d->cfg.bits & MPPE_STATELESS) != 0
700 		    || (d->cc & MPPE_UPDATE_MASK) == MPPE_UPDATE_FLAG) {
701 			ng_mppc_updatekey(d->cfg.bits,
702 			    d->cfg.startkey, d->key, &d->rc4);
703 		}
704 
705 		/* Decrypt packet */
706 		m1 = m;
707 		while (m1 != NULL) {
708 			rc4_crypt(&d->rc4, mtod(m1, u_char *),
709 			    mtod(m1, u_char *), m1->m_len);
710 			m1 = m1->m_next;
711 		}
712 #endif
713 	} else {
714 
715 		/* Are we expecting encryption? */
716 		if ((d->cfg.bits & MPPE_BITS) != 0) {
717 			log(LOG_ERR, "%s: rec'd unexpectedly %s packet",
718 				__func__, "unencrypted");
719 			goto failed;
720 		}
721 	}
722 
723 	/* Update coherency count for next time (12 bit arithmetic) */
724 	MPPC_CCOUNT_INC(d->cc);
725 
726 	/* Check for unexpected compressed packet */
727 	if ((header & MPPC_FLAG_COMPRESSED) != 0
728 	    && (d->cfg.bits & MPPC_BIT) == 0) {
729 		log(LOG_ERR, "%s: rec'd unexpectedly %s packet",
730 			__func__, "compressed");
731 failed:
732 		m_freem(m);
733 		return (EINVAL);
734 	}
735 
736 #ifdef NETGRAPH_MPPC_COMPRESSION
737 	/* Decompress packet */
738 	if ((header & MPPC_FLAG_COMPRESSED) != 0) {
739 		int flags = MPPC_MANDATORY_DECOMPRESS_FLAGS;
740 		u_char *inbuf, *outbuf;
741 		int inlen, outlen, ina;
742 		u_char *source, *dest;
743 		u_long sourceCnt, destCnt;
744 		int rtn;
745 
746 		/* Copy payload into a contiguous region of memory. */
747 		inlen = m->m_pkthdr.len;
748 		if (m->m_next == NULL) {
749                 	inbuf = mtod(m, u_char *);
750 			ina = 0;
751 		} else {
752 		        inbuf = malloc(inlen, M_NETGRAPH_MPPC, M_NOWAIT);
753 			if (inbuf == NULL) {
754 				m_freem(m);
755 				return (ENOMEM);
756 			}
757 			m_copydata(m, 0, inlen, (caddr_t)inbuf);
758 			ina = 1;
759 		}
760 
761 		/* Allocate a buffer for decompressed data */
762 		outbuf = malloc(MPPC_DECOMP_BUFSIZE + MPPC_DECOMP_SAFETY,
763 		    M_NETGRAPH_MPPC, M_NOWAIT);
764 		if (outbuf == NULL) {
765 			m_freem(m);
766 			if (ina)
767 				free(inbuf, M_NETGRAPH_MPPC);
768 			return (ENOMEM);
769 		}
770 		outlen = MPPC_DECOMP_BUFSIZE;
771 
772 		/* Prepare to decompress */
773 		source = inbuf;
774 		sourceCnt = inlen;
775 		dest = outbuf;
776 		destCnt = outlen;
777 		if ((header & MPPC_FLAG_RESTART) != 0)
778 			flags |= MPPC_RESTART_HISTORY;
779 
780 		/* Decompress */
781 		rtn = MPPC_Decompress(&source, &dest,
782 			&sourceCnt, &destCnt, d->history, flags);
783 
784 		/* Check return value */
785 		KASSERT(rtn != MPPC_INVALID, ("%s: invalid", __func__));
786 		if ((rtn & MPPC_DEST_EXHAUSTED) != 0
787 		    || (rtn & MPPC_DECOMP_OK) != MPPC_DECOMP_OK) {
788 			log(LOG_ERR, "%s: decomp returned 0x%x",
789 			    __func__, rtn);
790 			if (ina)
791 				free(inbuf, M_NETGRAPH_MPPC);
792 			free(outbuf, M_NETGRAPH_MPPC);
793 			goto failed;
794 		}
795 
796 		/* Replace compressed data with decompressed data */
797 		if (ina)
798 			free(inbuf, M_NETGRAPH_MPPC);
799 		outlen -= destCnt;
800 
801 		m_copyback(m, 0, outlen, (caddr_t)outbuf);
802 		if (m->m_pkthdr.len < outlen) {
803 			m_freem(m);
804 			m = NULL;
805 		} else if (outlen < m->m_pkthdr.len)
806 			m_adj(m, outlen - m->m_pkthdr.len);
807 		free(outbuf, M_NETGRAPH_MPPC);
808 	}
809 #endif
810 
811 	/* Return result in an mbuf */
812 	*datap = m;
813 	return (*datap == NULL ? ENOBUFS : 0);
814 }
815 
816 /*
817  * The peer has sent us a CCP ResetRequest, so reset our transmit state.
818  */
819 static void
820 ng_mppc_reset_req(node_p node)
821 {
822 	const priv_p priv = NG_NODE_PRIVATE(node);
823 	struct ng_mppc_dir *const d = &priv->xmit;
824 
825 #ifdef NETGRAPH_MPPC_COMPRESSION
826 	if (d->history != NULL)
827 		MPPC_InitCompressionHistory(d->history);
828 #endif
829 #ifdef NETGRAPH_MPPC_ENCRYPTION
830 	if ((d->cfg.bits & MPPE_STATELESS) == 0)
831 		rc4_init(&d->rc4, d->key, KEYLEN(d->cfg.bits));
832 #endif
833 	d->flushed = 1;
834 }
835 
836 #ifdef NETGRAPH_MPPC_ENCRYPTION
837 /*
838  * Generate a new encryption key
839  */
840 static void
841 ng_mppc_getkey(const u_char *h, u_char *h2, int len)
842 {
843 	static const u_char pad1[40] =
844 	    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
845 	      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
846 	      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
847 	      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
848 	static const u_char pad2[40] =
849 	    { 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2,
850 	      0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2,
851 	      0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2,
852 	      0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2 };
853 	u_char hash[20];
854 	SHA1_CTX c;
855 
856 	SHA1Init(&c);
857 	SHA1Update(&c, h, len);
858 	SHA1Update(&c, pad1, sizeof(pad1));
859 	SHA1Update(&c, h2, len);
860 	SHA1Update(&c, pad2, sizeof(pad2));
861 	SHA1Final(hash, &c);
862 	bcopy(hash, h2, len);
863 }
864 
865 /*
866  * Update the encryption key
867  */
868 static void
869 ng_mppc_updatekey(u_int32_t bits,
870 	u_char *key0, u_char *key, struct rc4_state *rc4)
871 {
872 	const int keylen = KEYLEN(bits);
873 
874 	ng_mppc_getkey(key0, key, keylen);
875 	rc4_init(rc4, key, keylen);
876 	rc4_crypt(rc4, key, key, keylen);
877 	if ((bits & MPPE_40) != 0)
878 		bcopy(&ng_mppe_weakenkey, key, 3);
879 	else if ((bits & MPPE_56) != 0)
880 		bcopy(&ng_mppe_weakenkey, key, 1);
881 	rc4_init(rc4, key, keylen);
882 }
883 #endif
884 
885