xref: /freebsd/sys/netgraph/ng_mppc.c (revision 82431678fce5c893ef9c7418ad6d998ad4187de6)
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 	/* We must own the mbuf chain exclusively to modify it. */
474 	m = m_unshare(m, M_DONTWAIT);
475 	if (m == NULL)
476 		return (ENOMEM);
477 
478 	/* Initialize */
479 	header = d->cc;
480 
481 	/* Always set the flushed bit in stateless mode */
482 	if (d->flushed || ((d->cfg.bits & MPPE_STATELESS) != 0)) {
483 		header |= MPPC_FLAG_FLUSHED;
484 		d->flushed = 0;
485 	}
486 
487 	/* Compress packet (if compression enabled) */
488 #ifdef NETGRAPH_MPPC_COMPRESSION
489 	if ((d->cfg.bits & MPPC_BIT) != 0) {
490 		u_short flags = MPPC_MANDATORY_COMPRESS_FLAGS;
491 		u_char *inbuf, *outbuf;
492 		int outlen, inlen, ina;
493 		u_char *source, *dest;
494 		u_long sourceCnt, destCnt;
495 		int rtn;
496 
497 		/* Work with contiguous regions of memory. */
498 		inlen = m->m_pkthdr.len;
499 		if (m->m_next == NULL) {
500 			inbuf = mtod(m, u_char *);
501 			ina = 0;
502 		} else {
503 			inbuf = malloc(inlen, M_NETGRAPH_MPPC, M_NOWAIT);
504 			if (inbuf == NULL)
505 				goto err1;
506 			m_copydata(m, 0, inlen, (caddr_t)inbuf);
507 			ina = 1;
508 		}
509 
510 		outlen = MPPC_MAX_BLOWUP(inlen);
511 		outbuf = malloc(outlen, M_NETGRAPH_MPPC, M_NOWAIT);
512 		if (outbuf == NULL) {
513 			if (ina)
514 				free(inbuf, M_NETGRAPH_MPPC);
515 err1:
516 			m_freem(m);
517 			MPPC_InitCompressionHistory(d->history);
518 			d->flushed = 1;
519 			return (ENOMEM);
520 		}
521 
522 		/* Prepare to compress */
523 		source = inbuf;
524 		sourceCnt = inlen;
525 		dest = outbuf;
526 		destCnt = outlen;
527 		if ((d->cfg.bits & MPPE_STATELESS) == 0)
528 			flags |= MPPC_SAVE_HISTORY;
529 
530 		/* Compress */
531 		rtn = MPPC_Compress(&source, &dest, &sourceCnt,
532 			&destCnt, d->history, flags, 0);
533 
534 		/* Check return value */
535 		KASSERT(rtn != MPPC_INVALID, ("%s: invalid", __func__));
536 		if ((rtn & MPPC_EXPANDED) == 0
537 		    && (rtn & MPPC_COMP_OK) == MPPC_COMP_OK) {
538 			outlen -= destCnt;
539 			header |= MPPC_FLAG_COMPRESSED;
540 			if ((rtn & MPPC_RESTART_HISTORY) != 0)
541 				header |= MPPC_FLAG_RESTART;
542 
543 			/* Replace m by the compresed one. */
544 			m_copyback(m, 0, outlen, (caddr_t)outbuf);
545 			if (m->m_pkthdr.len < outlen) {
546 				m_freem(m);
547 				m = NULL;
548 			} else if (outlen < m->m_pkthdr.len)
549 				m_adj(m, outlen - m->m_pkthdr.len);
550 		}
551 		d->flushed = (rtn & MPPC_EXPANDED) != 0
552 		    || (flags & MPPC_SAVE_HISTORY) == 0;
553 
554 		if (ina)
555 			free(inbuf, M_NETGRAPH_MPPC);
556 		free(outbuf, M_NETGRAPH_MPPC);
557 
558 		/* Check mbuf chain reload result. */
559 		if (m == NULL) {
560 			if (!d->flushed) {
561 				MPPC_InitCompressionHistory(d->history);
562 				d->flushed = 1;
563 			}
564 			return (ENOMEM);
565 		}
566 	}
567 #endif
568 
569 	/* Now encrypt packet (if encryption enabled) */
570 #ifdef NETGRAPH_MPPC_ENCRYPTION
571 	if ((d->cfg.bits & MPPE_BITS) != 0) {
572 		struct mbuf *m1;
573 
574 		/* Set header bits */
575 		header |= MPPC_FLAG_ENCRYPTED;
576 
577 		/* Update key if it's time */
578 		if ((d->cfg.bits & MPPE_STATELESS) != 0
579 		    || (d->cc & MPPE_UPDATE_MASK) == MPPE_UPDATE_FLAG) {
580 			ng_mppc_updatekey(d->cfg.bits,
581 			    d->cfg.startkey, d->key, &d->rc4);
582 		} else if ((header & MPPC_FLAG_FLUSHED) != 0) {
583 			/* Need to reset key if we say we did
584 			   and ng_mppc_updatekey wasn't called to do it also. */
585 			rc4_init(&d->rc4, d->key, KEYLEN(d->cfg.bits));
586 		}
587 
588 		/* Encrypt packet */
589 		m1 = m;
590 		while (m1) {
591 			rc4_crypt(&d->rc4, mtod(m1, u_char *),
592 			    mtod(m1, u_char *), m1->m_len);
593 			m1 = m1->m_next;
594 		}
595 	}
596 #endif
597 
598 	/* Update coherency count for next time (12 bit arithmetic) */
599 	MPPC_CCOUNT_INC(d->cc);
600 
601 	/* Install header */
602 	M_PREPEND(m, MPPC_HDRLEN, M_DONTWAIT);
603 	if (m != NULL)
604 		*(mtod(m, uint16_t *)) = htons(header);
605 
606 	*datap = m;
607 	return (*datap == NULL ? ENOBUFS : 0);
608 }
609 
610 /*
611  * Decompress/decrypt packet and put the result in a new mbuf at *resultp.
612  * The original mbuf is not free'd.
613  */
614 static int
615 ng_mppc_decompress(node_p node, struct mbuf **datap)
616 {
617 	const priv_p priv = NG_NODE_PRIVATE(node);
618 	struct ng_mppc_dir *const d = &priv->recv;
619 	u_int16_t header, cc;
620 	u_int numLost;
621 	struct mbuf *m = *datap;
622 
623 	/* We must own the mbuf chain exclusively to modify it. */
624 	m = m_unshare(m, M_DONTWAIT);
625 	if (m == NULL)
626 		return (ENOMEM);
627 
628 	/* Pull off header */
629 	if (m->m_pkthdr.len < MPPC_HDRLEN) {
630 		m_freem(m);
631 		return (EINVAL);
632 	}
633 	m_copydata(m, 0, MPPC_HDRLEN, (caddr_t)&header);
634 	header = ntohs(header);
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