ng_mppc.c revision 107845
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 107845 2002-12-14 00:56:07Z 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/*
94 * When packets are lost with MPPE, we may have to re-key arbitrarily
95 * many times to 'catch up' to the new jumped-ahead sequence number.
96 * Since this can be expensive, we pose a limit on how many re-keyings
97 * we will do at one time to avoid a possible D.O.S. vulnerability.
98 * This should instead be a configurable parameter.
99 */
100#define MPPE_MAX_REKEY		1000
101
102/* MPPC packet header bits */
103#define MPPC_FLAG_FLUSHED	0x8000		/* xmitter reset state */
104#define MPPC_FLAG_RESTART	0x4000		/* compress history restart */
105#define MPPC_FLAG_COMPRESSED	0x2000		/* packet is compresed */
106#define MPPC_FLAG_ENCRYPTED	0x1000		/* packet is encrypted */
107#define MPPC_CCOUNT_MASK	0x0fff		/* sequence number mask */
108
109#define MPPE_UPDATE_MASK	0xff		/* coherency count when we're */
110#define MPPE_UPDATE_FLAG	0xff		/*   supposed to update key */
111
112#define MPPC_COMP_OK		0x05
113#define MPPC_DECOMP_OK		0x05
114
115/* Per direction info */
116struct ng_mppc_dir {
117	struct ng_mppc_config	cfg;		/* configuration */
118	hook_p			hook;		/* netgraph hook */
119	u_int16_t		cc:12;		/* coherency count */
120	u_char			flushed;	/* clean history (xmit only) */
121#ifdef NETGRAPH_MPPC_COMPRESSION
122	u_char			*history;	/* compression history */
123#endif
124#ifdef NETGRAPH_MPPC_ENCRYPTION
125	u_char			key[MPPE_KEY_LEN];	/* session key */
126	struct rc4_state	rc4;			/* rc4 state */
127#endif
128};
129
130/* Node private data */
131struct ng_mppc_private {
132	struct ng_mppc_dir	xmit;		/* compress/encrypt config */
133	struct ng_mppc_dir	recv;		/* decompress/decrypt config */
134	ng_ID_t			ctrlnode;	/* path to controlling node */
135};
136typedef struct ng_mppc_private *priv_p;
137
138/* Netgraph node methods */
139static ng_constructor_t	ng_mppc_constructor;
140static ng_rcvmsg_t	ng_mppc_rcvmsg;
141static ng_shutdown_t	ng_mppc_shutdown;
142static ng_newhook_t	ng_mppc_newhook;
143static ng_rcvdata_t	ng_mppc_rcvdata;
144static ng_disconnect_t	ng_mppc_disconnect;
145
146/* Helper functions */
147static int	ng_mppc_compress(node_p node,
148			struct mbuf *m, struct mbuf **resultp);
149static int	ng_mppc_decompress(node_p node,
150			struct mbuf *m, struct mbuf **resultp);
151static void	ng_mppc_getkey(const u_char *h, u_char *h2, int len);
152static void	ng_mppc_updatekey(u_int32_t bits,
153			u_char *key0, u_char *key, struct rc4_state *rc4);
154static void	ng_mppc_reset_req(node_p node);
155
156/* Node type descriptor */
157static struct ng_type ng_mppc_typestruct = {
158	NG_ABI_VERSION,
159	NG_MPPC_NODE_TYPE,
160	NULL,
161	ng_mppc_constructor,
162	ng_mppc_rcvmsg,
163	ng_mppc_shutdown,
164	ng_mppc_newhook,
165	NULL,
166	NULL,
167	ng_mppc_rcvdata,
168	ng_mppc_disconnect,
169	NULL
170};
171NETGRAPH_INIT(mppc, &ng_mppc_typestruct);
172
173/* Fixed bit pattern to weaken keysize down to 40 or 56 bits */
174static const u_char ng_mppe_weakenkey[3] = { 0xd1, 0x26, 0x9e };
175
176#define ERROUT(x)	do { error = (x); goto done; } while (0)
177
178/************************************************************************
179			NETGRAPH NODE STUFF
180 ************************************************************************/
181
182/*
183 * Node type constructor
184 */
185static int
186ng_mppc_constructor(node_p node)
187{
188	priv_p priv;
189
190	/* Allocate private structure */
191	MALLOC(priv, priv_p, sizeof(*priv), M_NETGRAPH_MPPC, M_NOWAIT | M_ZERO);
192	if (priv == NULL)
193		return (ENOMEM);
194
195	NG_NODE_SET_PRIVATE(node, priv);
196
197	/* Done */
198	return (0);
199}
200
201/*
202 * Give our OK for a hook to be added
203 */
204static int
205ng_mppc_newhook(node_p node, hook_p hook, const char *name)
206{
207	const priv_p priv = NG_NODE_PRIVATE(node);
208	hook_p *hookPtr;
209
210	/* Check hook name */
211	if (strcmp(name, NG_MPPC_HOOK_COMP) == 0)
212		hookPtr = &priv->xmit.hook;
213	else if (strcmp(name, NG_MPPC_HOOK_DECOMP) == 0)
214		hookPtr = &priv->recv.hook;
215	else
216		return (EINVAL);
217
218	/* See if already connected */
219	if (*hookPtr != NULL)
220		return (EISCONN);
221
222	/* OK */
223	*hookPtr = hook;
224	return (0);
225}
226
227/*
228 * Receive a control message
229 */
230static int
231ng_mppc_rcvmsg(node_p node, item_p item, hook_p lasthook)
232{
233	const priv_p priv = NG_NODE_PRIVATE(node);
234	struct ng_mesg *resp = NULL;
235	int error = 0;
236	struct ng_mesg *msg;
237
238	NGI_GET_MSG(item, msg);
239	switch (msg->header.typecookie) {
240	case NGM_MPPC_COOKIE:
241		switch (msg->header.cmd) {
242		case NGM_MPPC_CONFIG_COMP:
243		case NGM_MPPC_CONFIG_DECOMP:
244		    {
245			struct ng_mppc_config *const cfg
246			    = (struct ng_mppc_config *)msg->data;
247			const int isComp =
248			    msg->header.cmd == NGM_MPPC_CONFIG_COMP;
249			struct ng_mppc_dir *const d = isComp ?
250			    &priv->xmit : &priv->recv;
251
252			/* Check configuration */
253			if (msg->header.arglen != sizeof(*cfg))
254				ERROUT(EINVAL);
255			if (cfg->enable) {
256				if ((cfg->bits & ~MPPC_VALID_BITS) != 0)
257					ERROUT(EINVAL);
258#ifndef NETGRAPH_MPPC_COMPRESSION
259				if ((cfg->bits & MPPC_BIT) != 0)
260					ERROUT(EPROTONOSUPPORT);
261#endif
262#ifndef NETGRAPH_MPPC_ENCRYPTION
263				if ((cfg->bits & MPPE_BITS) != 0)
264					ERROUT(EPROTONOSUPPORT);
265#endif
266			} else
267				cfg->bits = 0;
268
269			/* Save return address so we can send reset-req's */
270			if (!isComp)
271				priv->ctrlnode = NGI_RETADDR(item);
272
273			/* Configuration is OK, reset to it */
274			d->cfg = *cfg;
275
276#ifdef NETGRAPH_MPPC_COMPRESSION
277			/* Initialize state buffers for compression */
278			if (d->history != NULL) {
279				FREE(d->history, M_NETGRAPH_MPPC);
280				d->history = NULL;
281			}
282			if ((cfg->bits & MPPC_BIT) != 0) {
283				MALLOC(d->history, u_char *,
284				    isComp ? MPPC_SizeOfCompressionHistory() :
285				    MPPC_SizeOfDecompressionHistory(),
286				    M_NETGRAPH_MPPC, M_NOWAIT);
287				if (d->history == NULL)
288					ERROUT(ENOMEM);
289				if (isComp)
290					MPPC_InitCompressionHistory(d->history);
291				else {
292					MPPC_InitDecompressionHistory(
293					    d->history);
294				}
295			}
296#endif
297
298#ifdef NETGRAPH_MPPC_ENCRYPTION
299			/* Generate initial session keys for encryption */
300			if ((cfg->bits & MPPE_BITS) != 0) {
301				const int keylen = KEYLEN(cfg->bits);
302
303				bcopy(cfg->startkey, d->key, keylen);
304				ng_mppc_getkey(cfg->startkey, d->key, keylen);
305				if ((cfg->bits & MPPE_40) != 0)
306					bcopy(&ng_mppe_weakenkey, d->key, 3);
307				else if ((cfg->bits & MPPE_56) != 0)
308					bcopy(&ng_mppe_weakenkey, d->key, 1);
309				rc4_init(&d->rc4, d->key, keylen);
310			}
311#endif
312
313			/* Initialize other state */
314			d->cc = 0;
315			d->flushed = 0;
316			break;
317		    }
318
319		case NGM_MPPC_RESETREQ:
320			ng_mppc_reset_req(node);
321			break;
322
323		default:
324			error = EINVAL;
325			break;
326		}
327		break;
328	default:
329		error = EINVAL;
330		break;
331	}
332done:
333	NG_RESPOND_MSG(error, node, item, resp);
334	NG_FREE_MSG(msg);
335	return (error);
336}
337
338/*
339 * Receive incoming data on our hook.
340 */
341static int
342ng_mppc_rcvdata(hook_p hook, item_p item)
343{
344	const node_p node = NG_HOOK_NODE(hook);
345	const priv_p priv = NG_NODE_PRIVATE(node);
346	struct mbuf *out;
347	int error;
348	struct mbuf *m;
349
350	NGI_GET_M(item, m);
351	/* Compress and/or encrypt */
352	if (hook == priv->xmit.hook) {
353		if (!priv->xmit.cfg.enable) {
354			NG_FREE_M(m);
355			NG_FREE_ITEM(item);
356			return (ENXIO);
357		}
358		if ((error = ng_mppc_compress(node, m, &out)) != 0) {
359			NG_FREE_M(m);
360			NG_FREE_ITEM(item);
361			return(error);
362		}
363		NG_FREE_M(m);
364		NG_FWD_NEW_DATA(error, item, priv->xmit.hook, out);
365		return (error);
366	}
367
368	/* Decompress and/or decrypt */
369	if (hook == priv->recv.hook) {
370		if (!priv->recv.cfg.enable) {
371			NG_FREE_M(m);
372			NG_FREE_ITEM(item);
373			return (ENXIO);
374		}
375		if ((error = ng_mppc_decompress(node, m, &out)) != 0) {
376			NG_FREE_M(m);
377			NG_FREE_ITEM(item);
378			if (error == EINVAL && priv->ctrlnode != 0) {
379				struct ng_mesg *msg;
380
381				/* Need to send a reset-request */
382				NG_MKMESSAGE(msg, NGM_MPPC_COOKIE,
383				    NGM_MPPC_RESETREQ, 0, M_NOWAIT);
384				if (msg == NULL)
385					return (error);
386				NG_SEND_MSG_ID(error, node, msg,
387					priv->ctrlnode, 0);
388			}
389			return (error);
390		}
391		NG_FREE_M(m);
392		NG_FWD_NEW_DATA(error, item, priv->recv.hook, out);
393		return (error);
394	}
395
396	/* Oops */
397	panic("%s: unknown hook", __func__);
398#ifdef RESTARTABLE_PANICS
399	return (EINVAL);
400#endif
401}
402
403/*
404 * Destroy node
405 */
406static int
407ng_mppc_shutdown(node_p node)
408{
409	const priv_p priv = NG_NODE_PRIVATE(node);
410
411	/* Take down netgraph node */
412#ifdef NETGRAPH_MPPC_COMPRESSION
413	if (priv->xmit.history != NULL)
414		FREE(priv->xmit.history, M_NETGRAPH_MPPC);
415	if (priv->recv.history != NULL)
416		FREE(priv->recv.history, M_NETGRAPH_MPPC);
417#endif
418	bzero(priv, sizeof(*priv));
419	FREE(priv, M_NETGRAPH_MPPC);
420	NG_NODE_SET_PRIVATE(node, NULL);
421	NG_NODE_UNREF(node);		/* let the node escape */
422	return (0);
423}
424
425/*
426 * Hook disconnection
427 */
428static int
429ng_mppc_disconnect(hook_p hook)
430{
431	const node_p node = NG_HOOK_NODE(hook);
432	const priv_p priv = NG_NODE_PRIVATE(node);
433
434	/* Zero out hook pointer */
435	if (hook == priv->xmit.hook)
436		priv->xmit.hook = NULL;
437	if (hook == priv->recv.hook)
438		priv->recv.hook = NULL;
439
440	/* Go away if no longer connected */
441	if ((NG_NODE_NUMHOOKS(node) == 0)
442	&& NG_NODE_IS_VALID(node))
443		ng_rmnode_self(node);
444	return (0);
445}
446
447/************************************************************************
448			HELPER STUFF
449 ************************************************************************/
450
451/*
452 * Compress/encrypt a packet and put the result in a new mbuf at *resultp.
453 * The original mbuf is not free'd.
454 */
455static int
456ng_mppc_compress(node_p node, struct mbuf *m, struct mbuf **resultp)
457{
458	const priv_p priv = NG_NODE_PRIVATE(node);
459	struct ng_mppc_dir *const d = &priv->xmit;
460	u_char *inbuf, *outbuf;
461	int outlen, inlen;
462	u_int16_t header;
463
464	/* Initialize */
465	*resultp = NULL;
466	header = d->cc;
467	if (d->flushed) {
468		header |= MPPC_FLAG_FLUSHED;
469		d->flushed = 0;
470	}
471
472	/* Work with contiguous regions of memory */
473	inlen = m->m_pkthdr.len;
474	MALLOC(inbuf, u_char *, inlen, M_NETGRAPH_MPPC, M_NOWAIT);
475	if (inbuf == NULL)
476		return (ENOMEM);
477	m_copydata(m, 0, inlen, (caddr_t)inbuf);
478	if ((d->cfg.bits & MPPC_BIT) != 0)
479		outlen = MPPC_MAX_BLOWUP(inlen);
480	else
481		outlen = MPPC_HDRLEN + inlen;
482	MALLOC(outbuf, u_char *, outlen, M_NETGRAPH_MPPC, M_NOWAIT);
483	if (outbuf == NULL) {
484		FREE(inbuf, M_NETGRAPH_MPPC);
485		return (ENOMEM);
486	}
487
488	/* Compress "inbuf" into "outbuf" (if compression enabled) */
489#ifdef NETGRAPH_MPPC_COMPRESSION
490	if ((d->cfg.bits & MPPC_BIT) != 0) {
491		u_short flags = MPPC_MANDATORY_COMPRESS_FLAGS;
492		u_char *source, *dest;
493		u_long sourceCnt, destCnt;
494		int rtn;
495
496		/* Prepare to compress */
497		source = inbuf;
498		sourceCnt = inlen;
499		dest = outbuf + MPPC_HDRLEN;
500		destCnt = outlen - MPPC_HDRLEN;
501		if ((d->cfg.bits & MPPE_STATELESS) == 0)
502			flags |= MPPC_SAVE_HISTORY;
503
504		/* Compress */
505		rtn = MPPC_Compress(&source, &dest, &sourceCnt,
506			&destCnt, d->history, flags, 0);
507
508		/* Check return value */
509		KASSERT(rtn != MPPC_INVALID, ("%s: invalid", __func__));
510		if ((rtn & MPPC_EXPANDED) == 0
511		    && (rtn & MPPC_COMP_OK) == MPPC_COMP_OK) {
512			outlen -= destCnt;
513			header |= MPPC_FLAG_COMPRESSED;
514			if ((rtn & MPPC_RESTART_HISTORY) != 0)
515				header |= MPPC_FLAG_RESTART;
516		}
517		d->flushed = (rtn & MPPC_EXPANDED) != 0
518		    || (flags & MPPC_SAVE_HISTORY) == 0;
519	}
520#endif
521
522	/* If we did not compress this packet, copy it to output buffer */
523	if ((header & MPPC_FLAG_COMPRESSED) == 0) {
524		bcopy(inbuf, outbuf + MPPC_HDRLEN, inlen);
525		outlen = MPPC_HDRLEN + inlen;
526	}
527	FREE(inbuf, M_NETGRAPH_MPPC);
528
529	/* Always set the flushed bit in stateless mode */
530	if ((d->cfg.bits & MPPE_STATELESS) != 0)
531		header |= MPPC_FLAG_FLUSHED;
532
533	/* Now encrypt packet (if encryption enabled) */
534#ifdef NETGRAPH_MPPC_ENCRYPTION
535	if ((d->cfg.bits & MPPE_BITS) != 0) {
536
537		/* Set header bits; need to reset key if we say we did */
538		header |= MPPC_FLAG_ENCRYPTED;
539		if ((header & MPPC_FLAG_FLUSHED) != 0)
540			rc4_init(&d->rc4, d->key, KEYLEN(d->cfg.bits));
541
542		/* Update key if it's time */
543		if ((d->cfg.bits & MPPE_STATELESS) != 0
544		    || (d->cc & MPPE_UPDATE_MASK) == MPPE_UPDATE_FLAG) {
545			  ng_mppc_updatekey(d->cfg.bits,
546			      d->cfg.startkey, d->key, &d->rc4);
547		}
548
549		/* Encrypt packet */
550		rc4_crypt(&d->rc4, outbuf + MPPC_HDRLEN,
551			outbuf + MPPC_HDRLEN, outlen - MPPC_HDRLEN);
552	}
553#endif
554
555	/* Update sequence number */
556	d->cc++;
557
558	/* Install header */
559	*((u_int16_t *)outbuf) = htons(header);
560
561	/* Return packet in an mbuf */
562	*resultp = m_devget((caddr_t)outbuf, outlen, 0, NULL, NULL);
563	FREE(outbuf, M_NETGRAPH_MPPC);
564	return (*resultp == NULL ? ENOBUFS : 0);
565}
566
567/*
568 * Decompress/decrypt packet and put the result in a new mbuf at *resultp.
569 * The original mbuf is not free'd.
570 */
571static int
572ng_mppc_decompress(node_p node, struct mbuf *m, struct mbuf **resultp)
573{
574	const priv_p priv = NG_NODE_PRIVATE(node);
575	struct ng_mppc_dir *const d = &priv->recv;
576	u_int16_t header, cc;
577	u_int numLost;
578	u_char *buf;
579	int len;
580
581	/* Pull off header */
582	if (m->m_pkthdr.len < MPPC_HDRLEN)
583		return (EINVAL);
584	m_copydata(m, 0, MPPC_HDRLEN, (caddr_t)&header);
585	header = ntohs(header);
586	cc = (header & MPPC_CCOUNT_MASK);
587
588	/* Copy payload into a contiguous region of memory */
589	len = m->m_pkthdr.len - MPPC_HDRLEN;
590	MALLOC(buf, u_char *, len, M_NETGRAPH_MPPC, M_NOWAIT);
591	if (buf == NULL)
592		return (ENOMEM);
593	m_copydata(m, MPPC_HDRLEN, len, (caddr_t)buf);
594
595	/* Check for an unexpected jump in the sequence number */
596	numLost = ((cc - d->cc) & MPPC_CCOUNT_MASK);
597
598	/* If flushed bit set, we can always handle packet */
599	if ((header & MPPC_FLAG_FLUSHED) != 0) {
600#ifdef NETGRAPH_MPPC_COMPRESSION
601		if (d->history != NULL)
602			MPPC_InitDecompressionHistory(d->history);
603#endif
604#ifdef NETGRAPH_MPPC_ENCRYPTION
605		if ((d->cfg.bits & MPPE_BITS) != 0) {
606			u_int rekey;
607
608			/* How many times are we going to have to re-key? */
609			rekey = ((d->cfg.bits & MPPE_STATELESS) != 0) ?
610			    numLost : (numLost / (MPPE_UPDATE_MASK + 1));
611			if (rekey > MPPE_MAX_REKEY) {
612				log(LOG_ERR, "%s: too many (%d) packets"
613				    " dropped, disabling node %p!",
614				    __func__, numLost, node);
615				priv->recv.cfg.enable = 0;
616				goto failed;
617			}
618
619			/* Re-key as necessary to catch up to peer */
620			while (d->cc != cc) {
621				if ((d->cfg.bits & MPPE_STATELESS) != 0
622				    || (d->cc & MPPE_UPDATE_MASK)
623				      == MPPE_UPDATE_FLAG) {
624					ng_mppc_updatekey(d->cfg.bits,
625					    d->cfg.startkey, d->key, &d->rc4);
626				}
627				d->cc++;
628			}
629
630			/* Reset key (except in stateless mode, see below) */
631			if ((d->cfg.bits & MPPE_STATELESS) == 0)
632				rc4_init(&d->rc4, d->key, KEYLEN(d->cfg.bits));
633		}
634#endif
635		d->cc = cc;		/* skip over lost seq numbers */
636		numLost = 0;		/* act like no packets were lost */
637	}
638
639	/* Can't decode non-sequential packets without a flushed bit */
640	if (numLost != 0)
641		goto failed;
642
643	/* Decrypt packet */
644	if ((header & MPPC_FLAG_ENCRYPTED) != 0) {
645
646		/* Are we not expecting encryption? */
647		if ((d->cfg.bits & MPPE_BITS) == 0) {
648			log(LOG_ERR, "%s: rec'd unexpectedly %s packet",
649				__func__, "encrypted");
650			goto failed;
651		}
652
653#ifdef NETGRAPH_MPPC_ENCRYPTION
654		/* Update key if it's time (always in stateless mode) */
655		if ((d->cfg.bits & MPPE_STATELESS) != 0
656		    || (d->cc & MPPE_UPDATE_MASK) == MPPE_UPDATE_FLAG) {
657			ng_mppc_updatekey(d->cfg.bits,
658			    d->cfg.startkey, d->key, &d->rc4);
659		}
660
661		/* Decrypt packet */
662		rc4_crypt(&d->rc4, buf, buf, len);
663#endif
664	} else {
665
666		/* Are we expecting encryption? */
667		if ((d->cfg.bits & MPPE_BITS) != 0) {
668			log(LOG_ERR, "%s: rec'd unexpectedly %s packet",
669				__func__, "unencrypted");
670			goto failed;
671		}
672	}
673
674	/* Update coherency count for next time (12 bit arithmetic) */
675	d->cc++;
676
677	/* Check for unexpected compressed packet */
678	if ((header & MPPC_FLAG_COMPRESSED) != 0
679	    && (d->cfg.bits & MPPC_BIT) == 0) {
680		log(LOG_ERR, "%s: rec'd unexpectedly %s packet",
681			__func__, "compressed");
682failed:
683		FREE(buf, M_NETGRAPH_MPPC);
684		return (EINVAL);
685	}
686
687#ifdef NETGRAPH_MPPC_COMPRESSION
688	/* Decompress packet */
689	if ((header & MPPC_FLAG_COMPRESSED) != 0) {
690		int flags = MPPC_MANDATORY_DECOMPRESS_FLAGS;
691		u_char *decompbuf, *source, *dest;
692		u_long sourceCnt, destCnt;
693		int decomplen, rtn;
694
695		/* Allocate a buffer for decompressed data */
696		MALLOC(decompbuf, u_char *, MPPC_DECOMP_BUFSIZE
697		    + MPPC_DECOMP_SAFETY, M_NETGRAPH_MPPC, M_NOWAIT);
698		if (decompbuf == NULL) {
699			FREE(buf, M_NETGRAPH_MPPC);
700			return (ENOMEM);
701		}
702		decomplen = MPPC_DECOMP_BUFSIZE;
703
704		/* Prepare to decompress */
705		source = buf;
706		sourceCnt = len;
707		dest = decompbuf;
708		destCnt = decomplen;
709		if ((header & MPPC_FLAG_RESTART) != 0)
710			flags |= MPPC_RESTART_HISTORY;
711
712		/* Decompress */
713		rtn = MPPC_Decompress(&source, &dest,
714			&sourceCnt, &destCnt, d->history, flags);
715
716		/* Check return value */
717		KASSERT(rtn != MPPC_INVALID, ("%s: invalid", __func__));
718		if ((rtn & MPPC_DEST_EXHAUSTED) != 0
719		    || (rtn & MPPC_DECOMP_OK) != MPPC_DECOMP_OK) {
720			log(LOG_ERR, "%s: decomp returned 0x%x",
721			    __func__, rtn);
722			FREE(decompbuf, M_NETGRAPH_MPPC);
723			goto failed;
724		}
725
726		/* Replace compressed data with decompressed data */
727		FREE(buf, M_NETGRAPH_MPPC);
728		buf = decompbuf;
729		len = decomplen - destCnt;
730	}
731#endif
732
733	/* Return result in an mbuf */
734	*resultp = m_devget((caddr_t)buf, len, 0, NULL, NULL);
735	FREE(buf, M_NETGRAPH_MPPC);
736	return (*resultp == NULL ? ENOBUFS : 0);
737}
738
739/*
740 * The peer has sent us a CCP ResetRequest, so reset our transmit state.
741 */
742static void
743ng_mppc_reset_req(node_p node)
744{
745	const priv_p priv = NG_NODE_PRIVATE(node);
746	struct ng_mppc_dir *const d = &priv->xmit;
747
748#ifdef NETGRAPH_MPPC_COMPRESSION
749	if (d->history != NULL)
750		MPPC_InitCompressionHistory(d->history);
751#endif
752#ifdef NETGRAPH_MPPC_ENCRYPTION
753	if ((d->cfg.bits & MPPE_STATELESS) == 0)
754		rc4_init(&d->rc4, d->key, KEYLEN(d->cfg.bits));
755#endif
756	d->flushed = 1;
757}
758
759/*
760 * Generate a new encryption key
761 */
762static void
763ng_mppc_getkey(const u_char *h, u_char *h2, int len)
764{
765	static const u_char pad1[10] =
766	    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, };
767	static const u_char pad2[10] =
768	    { 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, };
769	u_char hash[20];
770	SHA1_CTX c;
771	int k;
772
773	bzero(&hash, sizeof(hash));
774	SHA1Init(&c);
775	SHA1Update(&c, h, len);
776	for (k = 0; k < 4; k++)
777		SHA1Update(&c, pad1, sizeof(pad2));
778	SHA1Update(&c, h2, len);
779	for (k = 0; k < 4; k++)
780		SHA1Update(&c, pad2, sizeof(pad2));
781	SHA1Final(hash, &c);
782	bcopy(hash, h2, len);
783}
784
785/*
786 * Update the encryption key
787 */
788static void
789ng_mppc_updatekey(u_int32_t bits,
790	u_char *key0, u_char *key, struct rc4_state *rc4)
791{
792	const int keylen = KEYLEN(bits);
793
794	ng_mppc_getkey(key0, key, keylen);
795	rc4_init(rc4, key, keylen);
796	rc4_crypt(rc4, key, key, keylen);
797	if ((bits & MPPE_40) != 0)
798		bcopy(&ng_mppe_weakenkey, key, 3);
799	else if ((bits & MPPE_56) != 0)
800		bcopy(&ng_mppe_weakenkey, key, 1);
801	rc4_init(rc4, key, keylen);
802}
803
804