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