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