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