ng_mppc.c revision 187405
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: head/sys/netgraph/ng_mppc.c 187405 2009-01-18 19:25:36Z mav $
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
76MALLOC_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 */
125struct 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 */
140struct 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};
145typedef struct ng_mppc_private *priv_p;
146
147/* Netgraph node methods */
148static ng_constructor_t	ng_mppc_constructor;
149static ng_rcvmsg_t	ng_mppc_rcvmsg;
150static ng_shutdown_t	ng_mppc_shutdown;
151static ng_newhook_t	ng_mppc_newhook;
152static ng_rcvdata_t	ng_mppc_rcvdata;
153static ng_disconnect_t	ng_mppc_disconnect;
154
155/* Helper functions */
156static int	ng_mppc_compress(node_p node,
157			struct mbuf **datap);
158static int	ng_mppc_decompress(node_p node,
159			struct mbuf **datap);
160#ifdef NETGRAPH_MPPC_ENCRYPTION
161static void	ng_mppc_getkey(const u_char *h, u_char *h2, int len);
162static void	ng_mppc_updatekey(u_int32_t bits,
163			u_char *key0, u_char *key, struct rc4_state *rc4);
164#endif
165static void	ng_mppc_reset_req(node_p node);
166
167/* Node type descriptor */
168static 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};
178NETGRAPH_INIT(mppc, &ng_mppc_typestruct);
179
180#ifdef NETGRAPH_MPPC_ENCRYPTION
181/* Depend on separate rc4 module */
182MODULE_DEPEND(ng_mppc, rc4, 1, 1, 1);
183#endif
184
185/* Fixed bit pattern to weaken keysize down to 40 or 56 bits */
186static 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 */
197static int
198ng_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 */
219static int
220ng_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 */
245static int
246ng_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	}
347done:
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 */
356static int
357ng_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 */
416static int
417ng_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 */
438static int
439ng_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 */
465static int
466ng_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;
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		inbuf = malloc(inlen, M_NETGRAPH_MPPC, M_NOWAIT);
500		if (inbuf == NULL)
501			goto err1;
502		m_copydata(m, 0, inlen, (caddr_t)inbuf);
503
504		outlen = MPPC_MAX_BLOWUP(inlen);
505		outbuf = malloc(outlen, M_NETGRAPH_MPPC, M_NOWAIT);
506		if (outbuf == NULL) {
507			free(inbuf, M_NETGRAPH_MPPC);
508err1:
509			m_freem(m);
510			MPPC_InitCompressionHistory(d->history);
511			d->flushed = 1;
512			return (ENOMEM);
513		}
514
515		/* Prepare to compress */
516		source = inbuf;
517		sourceCnt = inlen;
518		dest = outbuf;
519		destCnt = outlen;
520		if ((d->cfg.bits & MPPE_STATELESS) == 0)
521			flags |= MPPC_SAVE_HISTORY;
522
523		/* Compress */
524		rtn = MPPC_Compress(&source, &dest, &sourceCnt,
525			&destCnt, d->history, flags, 0);
526
527		/* Check return value */
528		KASSERT(rtn != MPPC_INVALID, ("%s: invalid", __func__));
529		if ((rtn & MPPC_EXPANDED) == 0
530		    && (rtn & MPPC_COMP_OK) == MPPC_COMP_OK) {
531			outlen -= destCnt;
532			header |= MPPC_FLAG_COMPRESSED;
533			if ((rtn & MPPC_RESTART_HISTORY) != 0)
534				header |= MPPC_FLAG_RESTART;
535
536			/* Replace m by the compresed one. */
537			m_copyback(m, 0, outlen, (caddr_t)outbuf);
538			if (m->m_pkthdr.len < outlen) {
539				m_freem(m);
540				m = NULL;
541			} else if (outlen < m->m_pkthdr.len)
542				m_adj(m, outlen - m->m_pkthdr.len);
543		}
544		d->flushed = (rtn & MPPC_EXPANDED) != 0
545		    || (flags & MPPC_SAVE_HISTORY) == 0;
546
547		free(inbuf, M_NETGRAPH_MPPC);
548		free(outbuf, M_NETGRAPH_MPPC);
549
550		/* Check mbuf chain reload result. */
551		if (m == NULL) {
552			if (!d->flushed) {
553				MPPC_InitCompressionHistory(d->history);
554				d->flushed = 1;
555			}
556			return (ENOMEM);
557		}
558	}
559#endif
560
561	/* Now encrypt packet (if encryption enabled) */
562#ifdef NETGRAPH_MPPC_ENCRYPTION
563	if ((d->cfg.bits & MPPE_BITS) != 0) {
564		struct mbuf *m1;
565
566		/* Set header bits */
567		header |= MPPC_FLAG_ENCRYPTED;
568
569		/* Update key if it's time */
570		if ((d->cfg.bits & MPPE_STATELESS) != 0
571		    || (d->cc & MPPE_UPDATE_MASK) == MPPE_UPDATE_FLAG) {
572			ng_mppc_updatekey(d->cfg.bits,
573			    d->cfg.startkey, d->key, &d->rc4);
574		} else if ((header & MPPC_FLAG_FLUSHED) != 0) {
575			/* Need to reset key if we say we did
576			   and ng_mppc_updatekey wasn't called to do it also. */
577			rc4_init(&d->rc4, d->key, KEYLEN(d->cfg.bits));
578		}
579
580		/* Encrypt packet */
581		m1 = m;
582		while (m1) {
583			rc4_crypt(&d->rc4, mtod(m1, u_char *),
584			    mtod(m1, u_char *), m1->m_len);
585			m1 = m1->m_next;
586		}
587	}
588#endif
589
590	/* Update coherency count for next time (12 bit arithmetic) */
591	MPPC_CCOUNT_INC(d->cc);
592
593	/* Install header */
594	M_PREPEND(m, MPPC_HDRLEN, M_DONTWAIT);
595	if (m != NULL)
596		*(mtod(m, uint16_t *)) = htons(header);
597
598	*datap = m;
599	return (*datap == NULL ? ENOBUFS : 0);
600}
601
602/*
603 * Decompress/decrypt packet and put the result in a new mbuf at *resultp.
604 * The original mbuf is not free'd.
605 */
606static int
607ng_mppc_decompress(node_p node, struct mbuf **datap)
608{
609	const priv_p priv = NG_NODE_PRIVATE(node);
610	struct ng_mppc_dir *const d = &priv->recv;
611	u_int16_t header, cc;
612	u_int numLost;
613	struct mbuf *m = *datap;
614
615	/* We must own the mbuf chain exclusively to modify it. */
616	m = m_unshare(m, M_DONTWAIT);
617	if (m == NULL)
618		return (ENOMEM);
619
620	/* Pull off header */
621	if (m->m_pkthdr.len < MPPC_HDRLEN) {
622		m_freem(m);
623		return (EINVAL);
624	}
625	m_copydata(m, 0, MPPC_HDRLEN, (caddr_t)&header);
626	header = ntohs(header);
627	cc = (header & MPPC_CCOUNT_MASK);
628	m_adj(m, MPPC_HDRLEN);
629
630	/* Check for an unexpected jump in the sequence number */
631	numLost = ((cc - d->cc) & MPPC_CCOUNT_MASK);
632
633	/* If flushed bit set, we can always handle packet */
634	if ((header & MPPC_FLAG_FLUSHED) != 0) {
635#ifdef NETGRAPH_MPPC_COMPRESSION
636		if (d->history != NULL)
637			MPPC_InitDecompressionHistory(d->history);
638#endif
639#ifdef NETGRAPH_MPPC_ENCRYPTION
640		if ((d->cfg.bits & MPPE_BITS) != 0) {
641			u_int rekey;
642
643			/* How many times are we going to have to re-key? */
644			rekey = ((d->cfg.bits & MPPE_STATELESS) != 0) ?
645			    numLost : (numLost / (MPPE_UPDATE_MASK + 1));
646			if (rekey > MPPE_MAX_REKEY) {
647				log(LOG_ERR, "%s: too many (%d) packets"
648				    " dropped, disabling node %p!",
649				    __func__, numLost, node);
650				priv->recv.cfg.enable = 0;
651				goto failed;
652			}
653
654			/* Re-key as necessary to catch up to peer */
655			while (d->cc != cc) {
656				if ((d->cfg.bits & MPPE_STATELESS) != 0
657				    || (d->cc & MPPE_UPDATE_MASK)
658				      == MPPE_UPDATE_FLAG) {
659					ng_mppc_updatekey(d->cfg.bits,
660					    d->cfg.startkey, d->key, &d->rc4);
661				}
662				MPPC_CCOUNT_INC(d->cc);
663			}
664
665			/* Reset key (except in stateless mode, see below) */
666			if ((d->cfg.bits & MPPE_STATELESS) == 0)
667				rc4_init(&d->rc4, d->key, KEYLEN(d->cfg.bits));
668		}
669#endif
670		d->cc = cc;		/* skip over lost seq numbers */
671		numLost = 0;		/* act like no packets were lost */
672	}
673
674	/* Can't decode non-sequential packets without a flushed bit */
675	if (numLost != 0)
676		goto failed;
677
678	/* Decrypt packet */
679	if ((header & MPPC_FLAG_ENCRYPTED) != 0) {
680#ifdef NETGRAPH_MPPC_ENCRYPTION
681		struct mbuf *m1;
682#endif
683
684		/* Are we not expecting encryption? */
685		if ((d->cfg.bits & MPPE_BITS) == 0) {
686			log(LOG_ERR, "%s: rec'd unexpectedly %s packet",
687				__func__, "encrypted");
688			goto failed;
689		}
690
691#ifdef NETGRAPH_MPPC_ENCRYPTION
692		/* Update key if it's time (always in stateless mode) */
693		if ((d->cfg.bits & MPPE_STATELESS) != 0
694		    || (d->cc & MPPE_UPDATE_MASK) == MPPE_UPDATE_FLAG) {
695			ng_mppc_updatekey(d->cfg.bits,
696			    d->cfg.startkey, d->key, &d->rc4);
697		}
698
699		/* Decrypt packet */
700		m1 = m;
701		while (m1 != NULL) {
702			rc4_crypt(&d->rc4, mtod(m1, u_char *),
703			    mtod(m1, u_char *), m1->m_len);
704			m1 = m1->m_next;
705		}
706#endif
707	} else {
708
709		/* Are we expecting encryption? */
710		if ((d->cfg.bits & MPPE_BITS) != 0) {
711			log(LOG_ERR, "%s: rec'd unexpectedly %s packet",
712				__func__, "unencrypted");
713			goto failed;
714		}
715	}
716
717	/* Update coherency count for next time (12 bit arithmetic) */
718	MPPC_CCOUNT_INC(d->cc);
719
720	/* Check for unexpected compressed packet */
721	if ((header & MPPC_FLAG_COMPRESSED) != 0
722	    && (d->cfg.bits & MPPC_BIT) == 0) {
723		log(LOG_ERR, "%s: rec'd unexpectedly %s packet",
724			__func__, "compressed");
725failed:
726		m_freem(m);
727		return (EINVAL);
728	}
729
730#ifdef NETGRAPH_MPPC_COMPRESSION
731	/* Decompress packet */
732	if ((header & MPPC_FLAG_COMPRESSED) != 0) {
733		int flags = MPPC_MANDATORY_DECOMPRESS_FLAGS;
734		u_char *decompbuf, *source, *dest;
735		u_long sourceCnt, destCnt;
736		int decomplen, rtn;
737		u_char *buf;
738		int len;
739
740		/* Copy payload into a contiguous region of memory. */
741		len = m->m_pkthdr.len;
742		buf = malloc(len, M_NETGRAPH_MPPC, M_NOWAIT);
743		if (buf == NULL) {
744			m_freem(m);
745			return (ENOMEM);
746		}
747		m_copydata(m, 0, len, (caddr_t)buf);
748
749		/* Allocate a buffer for decompressed data */
750		decompbuf = malloc(MPPC_DECOMP_BUFSIZE + MPPC_DECOMP_SAFETY,
751		    M_NETGRAPH_MPPC, M_NOWAIT);
752		if (decompbuf == NULL) {
753			m_freem(m);
754			free(buf, M_NETGRAPH_MPPC);
755			return (ENOMEM);
756		}
757		decomplen = MPPC_DECOMP_BUFSIZE;
758
759		/* Prepare to decompress */
760		source = buf;
761		sourceCnt = len;
762		dest = decompbuf;
763		destCnt = decomplen;
764		if ((header & MPPC_FLAG_RESTART) != 0)
765			flags |= MPPC_RESTART_HISTORY;
766
767		/* Decompress */
768		rtn = MPPC_Decompress(&source, &dest,
769			&sourceCnt, &destCnt, d->history, flags);
770
771		/* Check return value */
772		KASSERT(rtn != MPPC_INVALID, ("%s: invalid", __func__));
773		if ((rtn & MPPC_DEST_EXHAUSTED) != 0
774		    || (rtn & MPPC_DECOMP_OK) != MPPC_DECOMP_OK) {
775			log(LOG_ERR, "%s: decomp returned 0x%x",
776			    __func__, rtn);
777			free(buf, M_NETGRAPH_MPPC);
778			free(decompbuf, M_NETGRAPH_MPPC);
779			goto failed;
780		}
781
782		/* Replace compressed data with decompressed data */
783		free(buf, M_NETGRAPH_MPPC);
784		len = decomplen - destCnt;
785
786		m_copyback(m, 0, len, (caddr_t)decompbuf);
787		if (m->m_pkthdr.len < len) {
788			m_freem(m);
789			m = NULL;
790		} else if (len < m->m_pkthdr.len)
791			m_adj(m, len - m->m_pkthdr.len);
792		free(decompbuf, M_NETGRAPH_MPPC);
793	}
794#endif
795
796	/* Return result in an mbuf */
797	*datap = m;
798	return (*datap == NULL ? ENOBUFS : 0);
799}
800
801/*
802 * The peer has sent us a CCP ResetRequest, so reset our transmit state.
803 */
804static void
805ng_mppc_reset_req(node_p node)
806{
807	const priv_p priv = NG_NODE_PRIVATE(node);
808	struct ng_mppc_dir *const d = &priv->xmit;
809
810#ifdef NETGRAPH_MPPC_COMPRESSION
811	if (d->history != NULL)
812		MPPC_InitCompressionHistory(d->history);
813#endif
814#ifdef NETGRAPH_MPPC_ENCRYPTION
815	if ((d->cfg.bits & MPPE_STATELESS) == 0)
816		rc4_init(&d->rc4, d->key, KEYLEN(d->cfg.bits));
817#endif
818	d->flushed = 1;
819}
820
821#ifdef NETGRAPH_MPPC_ENCRYPTION
822/*
823 * Generate a new encryption key
824 */
825static void
826ng_mppc_getkey(const u_char *h, u_char *h2, int len)
827{
828	static const u_char pad1[40] =
829	    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
830	      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
831	      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
832	      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
833	static const u_char pad2[40] =
834	    { 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2,
835	      0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2,
836	      0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2,
837	      0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2 };
838	u_char hash[20];
839	SHA1_CTX c;
840
841	SHA1Init(&c);
842	SHA1Update(&c, h, len);
843	SHA1Update(&c, pad1, sizeof(pad1));
844	SHA1Update(&c, h2, len);
845	SHA1Update(&c, pad2, sizeof(pad2));
846	SHA1Final(hash, &c);
847	bcopy(hash, h2, len);
848}
849
850/*
851 * Update the encryption key
852 */
853static void
854ng_mppc_updatekey(u_int32_t bits,
855	u_char *key0, u_char *key, struct rc4_state *rc4)
856{
857	const int keylen = KEYLEN(bits);
858
859	ng_mppc_getkey(key0, key, keylen);
860	rc4_init(rc4, key, keylen);
861	rc4_crypt(rc4, key, key, keylen);
862	if ((bits & MPPE_40) != 0)
863		bcopy(&ng_mppe_weakenkey, key, 3);
864	else if ((bits & MPPE_56) != 0)
865		bcopy(&ng_mppe_weakenkey, key, 1);
866	rc4_init(rc4, key, keylen);
867}
868#endif
869
870