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: stable/11/sys/netgraph/ng_mppc.c 312656 2017-01-23 01:20:58Z pfg $
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/endian.h>
57#include <sys/errno.h>
58#include <sys/sysctl.h>
59#include <sys/syslog.h>
60
61#include <netgraph/ng_message.h>
62#include <netgraph/netgraph.h>
63#include <netgraph/ng_mppc.h>
64
65#include "opt_netgraph.h"
66
67#if !defined(NETGRAPH_MPPC_COMPRESSION) && !defined(NETGRAPH_MPPC_ENCRYPTION)
68#ifdef KLD_MODULE
69#define NETGRAPH_MPPC_COMPRESSION
70#define NETGRAPH_MPPC_ENCRYPTION
71#else
72/* This case is indicative of an error in sys/conf files */
73#error Need either NETGRAPH_MPPC_COMPRESSION or NETGRAPH_MPPC_ENCRYPTION
74#endif
75#endif
76
77#ifdef NG_SEPARATE_MALLOC
78static MALLOC_DEFINE(M_NETGRAPH_MPPC, "netgraph_mppc", "netgraph mppc node");
79#else
80#define M_NETGRAPH_MPPC M_NETGRAPH
81#endif
82
83#ifdef NETGRAPH_MPPC_COMPRESSION
84#include <net/mppc.h>
85#endif
86#ifdef NETGRAPH_MPPC_ENCRYPTION
87#include <crypto/rc4/rc4.h>
88#endif
89#include <crypto/sha1.h>
90
91/* Decompression blowup */
92#define MPPC_DECOMP_BUFSIZE	8092            /* allocate buffer this big */
93#define MPPC_DECOMP_SAFETY	100             /*   plus this much margin */
94
95/* MPPC/MPPE header length */
96#define MPPC_HDRLEN		2
97
98/* Key length */
99#define KEYLEN(b)		(((b) & MPPE_128) ? 16 : 8)
100
101/*
102 * When packets are lost with MPPE, we may have to re-key arbitrarily
103 * many times to 'catch up' to the new jumped-ahead sequence number.
104 * Since this can be expensive, we pose a limit on how many re-keyings
105 * we will do at one time to avoid a possible D.O.S. vulnerability.
106 * This should instead be a configurable parameter.
107 */
108#define MPPE_MAX_REKEY		1000
109
110SYSCTL_NODE(_net_graph, OID_AUTO, mppe, CTLFLAG_RW, 0, "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
737		/* Are we expecting encryption? */
738		if ((d->cfg.bits & MPPE_BITS) != 0) {
739			log(LOG_ERR, "%s: rec'd unexpectedly %s packet",
740				__func__, "unencrypted");
741			goto failed;
742		}
743	}
744
745	/* Update coherency count for next time (12 bit arithmetic) */
746	MPPC_CCOUNT_INC(d->cc);
747
748	/* Check for unexpected compressed packet */
749	if ((header & MPPC_FLAG_COMPRESSED) != 0
750	    && (d->cfg.bits & MPPC_BIT) == 0) {
751		log(LOG_ERR, "%s: rec'd unexpectedly %s packet",
752			__func__, "compressed");
753failed:
754		m_freem(m);
755		return (EINVAL);
756	}
757
758#ifdef NETGRAPH_MPPC_COMPRESSION
759	/* Decompress packet */
760	if ((header & MPPC_FLAG_COMPRESSED) != 0) {
761		int flags = MPPC_MANDATORY_DECOMPRESS_FLAGS;
762		u_char *inbuf, *outbuf;
763		int inlen, outlen, ina;
764		u_char *source, *dest;
765		u_long sourceCnt, destCnt;
766		int rtn;
767
768		/* Copy payload into a contiguous region of memory. */
769		inlen = m->m_pkthdr.len;
770		if (m->m_next == NULL) {
771                	inbuf = mtod(m, u_char *);
772			ina = 0;
773		} else {
774		        inbuf = malloc(inlen, M_NETGRAPH_MPPC, M_NOWAIT);
775			if (inbuf == NULL) {
776				m_freem(m);
777				return (ENOMEM);
778			}
779			m_copydata(m, 0, inlen, (caddr_t)inbuf);
780			ina = 1;
781		}
782
783		/* Allocate a buffer for decompressed data */
784		outbuf = malloc(MPPC_DECOMP_BUFSIZE + MPPC_DECOMP_SAFETY,
785		    M_NETGRAPH_MPPC, M_NOWAIT);
786		if (outbuf == NULL) {
787			m_freem(m);
788			if (ina)
789				free(inbuf, M_NETGRAPH_MPPC);
790			return (ENOMEM);
791		}
792		outlen = MPPC_DECOMP_BUFSIZE;
793
794		/* Prepare to decompress */
795		source = inbuf;
796		sourceCnt = inlen;
797		dest = outbuf;
798		destCnt = outlen;
799		if ((header & MPPC_FLAG_RESTART) != 0)
800			flags |= MPPC_RESTART_HISTORY;
801
802		/* Decompress */
803		rtn = MPPC_Decompress(&source, &dest,
804			&sourceCnt, &destCnt, d->history, flags);
805
806		/* Check return value */
807		/* KASSERT(rtn != MPPC_INVALID, ("%s: invalid", __func__)); */
808		if ((rtn & MPPC_DEST_EXHAUSTED) != 0
809		    || (rtn & MPPC_DECOMP_OK) != MPPC_DECOMP_OK) {
810			log(LOG_ERR, "%s: decomp returned 0x%x",
811			    __func__, rtn);
812			if (ina)
813				free(inbuf, M_NETGRAPH_MPPC);
814			free(outbuf, M_NETGRAPH_MPPC);
815			goto failed;
816		}
817
818		/* Replace compressed data with decompressed data */
819		if (ina)
820			free(inbuf, M_NETGRAPH_MPPC);
821		outlen -= destCnt;
822
823		m_copyback(m, 0, outlen, (caddr_t)outbuf);
824		if (m->m_pkthdr.len < outlen) {
825			m_freem(m);
826			m = NULL;
827		} else if (outlen < m->m_pkthdr.len)
828			m_adj(m, outlen - m->m_pkthdr.len);
829		free(outbuf, M_NETGRAPH_MPPC);
830	}
831#endif
832
833	/* Return result in an mbuf */
834	*datap = m;
835	return (*datap == NULL ? ENOBUFS : 0);
836}
837
838/*
839 * The peer has sent us a CCP ResetRequest, so reset our transmit state.
840 */
841static void
842ng_mppc_reset_req(node_p node)
843{
844	const priv_p priv = NG_NODE_PRIVATE(node);
845	struct ng_mppc_dir *const d = &priv->xmit;
846
847#ifdef NETGRAPH_MPPC_COMPRESSION
848	if (d->history != NULL)
849		MPPC_InitCompressionHistory(d->history);
850#endif
851#ifdef NETGRAPH_MPPC_ENCRYPTION
852	if ((d->cfg.bits & MPPE_STATELESS) == 0)
853		rc4_init(&d->rc4, d->key, KEYLEN(d->cfg.bits));
854#endif
855	d->flushed = 1;
856}
857
858#ifdef NETGRAPH_MPPC_ENCRYPTION
859/*
860 * Generate a new encryption key
861 */
862static void
863ng_mppc_getkey(const u_char *h, u_char *h2, int len)
864{
865	static const u_char pad1[40] =
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	      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
870	static const u_char pad2[40] =
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	      0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2 };
875	u_char hash[20];
876	SHA1_CTX c;
877
878	SHA1Init(&c);
879	SHA1Update(&c, h, len);
880	SHA1Update(&c, pad1, sizeof(pad1));
881	SHA1Update(&c, h2, len);
882	SHA1Update(&c, pad2, sizeof(pad2));
883	SHA1Final(hash, &c);
884	bcopy(hash, h2, len);
885}
886
887/*
888 * Update the encryption key
889 */
890static void
891ng_mppc_updatekey(u_int32_t bits,
892	u_char *key0, u_char *key, struct rc4_state *rc4)
893{
894	const int keylen = KEYLEN(bits);
895
896	ng_mppc_getkey(key0, key, keylen);
897	rc4_init(rc4, key, keylen);
898	rc4_crypt(rc4, key, key, keylen);
899	if ((bits & MPPE_40) != 0)
900		bcopy(&ng_mppe_weakenkey, key, 3);
901	else if ((bits & MPPE_56) != 0)
902		bcopy(&ng_mppe_weakenkey, key, 1);
903	rc4_init(rc4, key, keylen);
904}
905#endif
906
907