ng_mppc.c revision 220768
159109Sarchie/*
259109Sarchie * ng_mppc.c
3139823Simp */
4139823Simp
5139823Simp/*-
659109Sarchie * Copyright (c) 1996-2000 Whistle Communications, Inc.
759109Sarchie * All rights reserved.
859109Sarchie *
959109Sarchie * Subject to the following obligations and disclaimer of warranty, use and
1059109Sarchie * redistribution of this software, in source or object code forms, with or
1159109Sarchie * without modifications are expressly permitted by Whistle Communications;
1259109Sarchie * provided, however, that:
1359109Sarchie * 1. Any and all reproductions of the source or object code must include the
1459109Sarchie *    copyright notice above and the following disclaimer of warranties; and
1559109Sarchie * 2. No rights are granted, in any manner or form, to use Whistle
1659109Sarchie *    Communications, Inc. trademarks, including the mark "WHISTLE
1759109Sarchie *    COMMUNICATIONS" on advertising, endorsements, or otherwise except as
1859109Sarchie *    such appears in the above copyright notice or in the software.
1959109Sarchie *
2059109Sarchie * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND
2159109Sarchie * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO
2259109Sarchie * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE,
2359109Sarchie * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF
2459109Sarchie * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
2559109Sarchie * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY
2659109Sarchie * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS
2759109Sarchie * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE.
2859109Sarchie * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES
2959109Sarchie * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING
3059109Sarchie * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
3159109Sarchie * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR
3259109Sarchie * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY
3359109Sarchie * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
3459109Sarchie * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
3559109Sarchie * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY
3659109Sarchie * OF SUCH DAMAGE.
3759109Sarchie *
3867506Sjulian * Author: Archie Cobbs <archie@freebsd.org>
3959109Sarchie *
4059109Sarchie * $Whistle: ng_mppc.c,v 1.4 1999/11/25 00:10:12 archie Exp $
4159109Sarchie * $FreeBSD: head/sys/netgraph/ng_mppc.c 220768 2011-04-18 09:12:27Z glebius $
4259109Sarchie */
4359109Sarchie
4459109Sarchie/*
4559109Sarchie * Microsoft PPP compression (MPPC) and encryption (MPPE) netgraph node type.
4659109Sarchie *
4759109Sarchie * You must define one or both of the NETGRAPH_MPPC_COMPRESSION and/or
4859109Sarchie * NETGRAPH_MPPC_ENCRYPTION options for this node type to be useful.
4959109Sarchie */
5059109Sarchie
5159109Sarchie#include <sys/param.h>
5259109Sarchie#include <sys/systm.h>
5359109Sarchie#include <sys/kernel.h>
5459109Sarchie#include <sys/mbuf.h>
5559109Sarchie#include <sys/malloc.h>
56206021Smav#include <sys/endian.h>
5759109Sarchie#include <sys/errno.h>
5859109Sarchie#include <sys/syslog.h>
5959109Sarchie
6059109Sarchie#include <netgraph/ng_message.h>
6159109Sarchie#include <netgraph/netgraph.h>
6259109Sarchie#include <netgraph/ng_mppc.h>
6359109Sarchie
6459109Sarchie#include "opt_netgraph.h"
6559109Sarchie
6659109Sarchie#if !defined(NETGRAPH_MPPC_COMPRESSION) && !defined(NETGRAPH_MPPC_ENCRYPTION)
67151349Syar#ifdef KLD_MODULE
68151349Syar/* XXX NETGRAPH_MPPC_COMPRESSION isn't functional yet */
69151349Syar#define NETGRAPH_MPPC_ENCRYPTION
70151349Syar#else
71151349Syar/* This case is indicative of an error in sys/conf files */
7259109Sarchie#error Need either NETGRAPH_MPPC_COMPRESSION or NETGRAPH_MPPC_ENCRYPTION
7359109Sarchie#endif
74151349Syar#endif
7559109Sarchie
7670870Sjulian#ifdef NG_SEPARATE_MALLOC
7770870SjulianMALLOC_DEFINE(M_NETGRAPH_MPPC, "netgraph_mppc", "netgraph mppc node ");
7870870Sjulian#else
7970870Sjulian#define M_NETGRAPH_MPPC M_NETGRAPH
8070870Sjulian#endif
8170870Sjulian
8259109Sarchie#ifdef NETGRAPH_MPPC_COMPRESSION
8359109Sarchie/* XXX this file doesn't exist yet, but hopefully someday it will... */
8459109Sarchie#include <net/mppc.h>
8559109Sarchie#endif
8659109Sarchie#ifdef NETGRAPH_MPPC_ENCRYPTION
8759109Sarchie#include <crypto/rc4/rc4.h>
8859109Sarchie#endif
8959109Sarchie#include <crypto/sha1.h>
9059109Sarchie
9159109Sarchie/* Decompression blowup */
9259109Sarchie#define MPPC_DECOMP_BUFSIZE	8092            /* allocate buffer this big */
9359109Sarchie#define MPPC_DECOMP_SAFETY	100             /*   plus this much margin */
9459109Sarchie
9559109Sarchie/* MPPC/MPPE header length */
9659109Sarchie#define MPPC_HDRLEN		2
9759109Sarchie
9859109Sarchie/* Key length */
9959109Sarchie#define KEYLEN(b)		(((b) & MPPE_128) ? 16 : 8)
10059109Sarchie
101107845Sarchie/*
102107845Sarchie * When packets are lost with MPPE, we may have to re-key arbitrarily
103107845Sarchie * many times to 'catch up' to the new jumped-ahead sequence number.
104107845Sarchie * Since this can be expensive, we pose a limit on how many re-keyings
105107845Sarchie * we will do at one time to avoid a possible D.O.S. vulnerability.
106107845Sarchie * This should instead be a configurable parameter.
107107845Sarchie */
108107845Sarchie#define MPPE_MAX_REKEY		1000
10959109Sarchie
11059109Sarchie/* MPPC packet header bits */
11159109Sarchie#define MPPC_FLAG_FLUSHED	0x8000		/* xmitter reset state */
11259109Sarchie#define MPPC_FLAG_RESTART	0x4000		/* compress history restart */
11359109Sarchie#define MPPC_FLAG_COMPRESSED	0x2000		/* packet is compresed */
11459109Sarchie#define MPPC_FLAG_ENCRYPTED	0x1000		/* packet is encrypted */
11559109Sarchie#define MPPC_CCOUNT_MASK	0x0fff		/* sequence number mask */
11659109Sarchie
117169261Smav#define MPPC_CCOUNT_INC(d)	((d) = (((d) + 1) & MPPC_CCOUNT_MASK))
118169261Smav
11959109Sarchie#define MPPE_UPDATE_MASK	0xff		/* coherency count when we're */
12059109Sarchie#define MPPE_UPDATE_FLAG	0xff		/*   supposed to update key */
12159109Sarchie
12259109Sarchie#define MPPC_COMP_OK		0x05
12359109Sarchie#define MPPC_DECOMP_OK		0x05
12459109Sarchie
12559109Sarchie/* Per direction info */
12659109Sarchiestruct ng_mppc_dir {
12759109Sarchie	struct ng_mppc_config	cfg;		/* configuration */
12859109Sarchie	hook_p			hook;		/* netgraph hook */
12959109Sarchie	u_int16_t		cc:12;		/* coherency count */
13059109Sarchie	u_char			flushed;	/* clean history (xmit only) */
13159109Sarchie#ifdef NETGRAPH_MPPC_COMPRESSION
13259109Sarchie	u_char			*history;	/* compression history */
13359109Sarchie#endif
13459109Sarchie#ifdef NETGRAPH_MPPC_ENCRYPTION
13559109Sarchie	u_char			key[MPPE_KEY_LEN];	/* session key */
13659109Sarchie	struct rc4_state	rc4;			/* rc4 state */
13759109Sarchie#endif
13859109Sarchie};
13959109Sarchie
14059109Sarchie/* Node private data */
14159109Sarchiestruct ng_mppc_private {
14259109Sarchie	struct ng_mppc_dir	xmit;		/* compress/encrypt config */
14359109Sarchie	struct ng_mppc_dir	recv;		/* decompress/decrypt config */
14470700Sjulian	ng_ID_t			ctrlnode;	/* path to controlling node */
14559109Sarchie};
14659109Sarchietypedef struct ng_mppc_private *priv_p;
14759109Sarchie
14859109Sarchie/* Netgraph node methods */
14959109Sarchiestatic ng_constructor_t	ng_mppc_constructor;
15059109Sarchiestatic ng_rcvmsg_t	ng_mppc_rcvmsg;
15170700Sjulianstatic ng_shutdown_t	ng_mppc_shutdown;
15259109Sarchiestatic ng_newhook_t	ng_mppc_newhook;
15359109Sarchiestatic ng_rcvdata_t	ng_mppc_rcvdata;
15459109Sarchiestatic ng_disconnect_t	ng_mppc_disconnect;
15559109Sarchie
15659109Sarchie/* Helper functions */
15759109Sarchiestatic int	ng_mppc_compress(node_p node,
158169474Smav			struct mbuf **datap);
15959109Sarchiestatic int	ng_mppc_decompress(node_p node,
160169474Smav			struct mbuf **datap);
161169678Smav#ifdef NETGRAPH_MPPC_ENCRYPTION
16259109Sarchiestatic void	ng_mppc_getkey(const u_char *h, u_char *h2, int len);
16359109Sarchiestatic void	ng_mppc_updatekey(u_int32_t bits,
16459109Sarchie			u_char *key0, u_char *key, struct rc4_state *rc4);
165169678Smav#endif
16659109Sarchiestatic void	ng_mppc_reset_req(node_p node);
16759109Sarchie
16859109Sarchie/* Node type descriptor */
16959109Sarchiestatic struct ng_type ng_mppc_typestruct = {
170129823Sjulian	.version =	NG_ABI_VERSION,
171129823Sjulian	.name =		NG_MPPC_NODE_TYPE,
172129823Sjulian	.constructor =	ng_mppc_constructor,
173129823Sjulian	.rcvmsg =	ng_mppc_rcvmsg,
174129823Sjulian	.shutdown =	ng_mppc_shutdown,
175129823Sjulian	.newhook =	ng_mppc_newhook,
176129823Sjulian	.rcvdata =	ng_mppc_rcvdata,
177129823Sjulian	.disconnect =	ng_mppc_disconnect,
17859109Sarchie};
17959109SarchieNETGRAPH_INIT(mppc, &ng_mppc_typestruct);
18059109Sarchie
181110409Sambrisko#ifdef NETGRAPH_MPPC_ENCRYPTION
182110409Sambrisko/* Depend on separate rc4 module */
183110409SambriskoMODULE_DEPEND(ng_mppc, rc4, 1, 1, 1);
184110409Sambrisko#endif
185110409Sambrisko
18687971Sarchie/* Fixed bit pattern to weaken keysize down to 40 or 56 bits */
18759109Sarchiestatic const u_char ng_mppe_weakenkey[3] = { 0xd1, 0x26, 0x9e };
18859109Sarchie
18959109Sarchie#define ERROUT(x)	do { error = (x); goto done; } while (0)
19059109Sarchie
19159109Sarchie/************************************************************************
19259109Sarchie			NETGRAPH NODE STUFF
19359109Sarchie ************************************************************************/
19459109Sarchie
19559109Sarchie/*
19659109Sarchie * Node type constructor
19759109Sarchie */
19859109Sarchiestatic int
19970700Sjulianng_mppc_constructor(node_p node)
20059109Sarchie{
20159109Sarchie	priv_p priv;
20259109Sarchie
20359109Sarchie	/* Allocate private structure */
204220768Sglebius	priv = malloc(sizeof(*priv), M_NETGRAPH_MPPC, M_WAITOK | M_ZERO);
20559109Sarchie
20670784Sjulian	NG_NODE_SET_PRIVATE(node, priv);
20759109Sarchie
208146919Sglebius	/* This node is not thread safe. */
209146919Sglebius	NG_NODE_FORCE_WRITER(node);
210146919Sglebius
21159109Sarchie	/* Done */
21259109Sarchie	return (0);
21359109Sarchie}
21459109Sarchie
21559109Sarchie/*
21659109Sarchie * Give our OK for a hook to be added
21759109Sarchie */
21859109Sarchiestatic int
21959109Sarchieng_mppc_newhook(node_p node, hook_p hook, const char *name)
22059109Sarchie{
22170784Sjulian	const priv_p priv = NG_NODE_PRIVATE(node);
22259109Sarchie	hook_p *hookPtr;
22359109Sarchie
22459109Sarchie	/* Check hook name */
22559109Sarchie	if (strcmp(name, NG_MPPC_HOOK_COMP) == 0)
22659109Sarchie		hookPtr = &priv->xmit.hook;
22759109Sarchie	else if (strcmp(name, NG_MPPC_HOOK_DECOMP) == 0)
22859109Sarchie		hookPtr = &priv->recv.hook;
22959109Sarchie	else
23059109Sarchie		return (EINVAL);
23159109Sarchie
23259109Sarchie	/* See if already connected */
23359109Sarchie	if (*hookPtr != NULL)
23459109Sarchie		return (EISCONN);
23559109Sarchie
23659109Sarchie	/* OK */
23759109Sarchie	*hookPtr = hook;
23859109Sarchie	return (0);
23959109Sarchie}
24059109Sarchie
24159109Sarchie/*
24259109Sarchie * Receive a control message
24359109Sarchie */
24459109Sarchiestatic int
24570700Sjulianng_mppc_rcvmsg(node_p node, item_p item, hook_p lasthook)
24659109Sarchie{
24770784Sjulian	const priv_p priv = NG_NODE_PRIVATE(node);
24859109Sarchie	struct ng_mesg *resp = NULL;
24959109Sarchie	int error = 0;
25070700Sjulian	struct ng_mesg *msg;
25159109Sarchie
25270700Sjulian	NGI_GET_MSG(item, msg);
25359109Sarchie	switch (msg->header.typecookie) {
25459109Sarchie	case NGM_MPPC_COOKIE:
25559109Sarchie		switch (msg->header.cmd) {
25659109Sarchie		case NGM_MPPC_CONFIG_COMP:
25759109Sarchie		case NGM_MPPC_CONFIG_DECOMP:
25859109Sarchie		    {
25959109Sarchie			struct ng_mppc_config *const cfg
26059109Sarchie			    = (struct ng_mppc_config *)msg->data;
26159109Sarchie			const int isComp =
26259109Sarchie			    msg->header.cmd == NGM_MPPC_CONFIG_COMP;
26359109Sarchie			struct ng_mppc_dir *const d = isComp ?
26459109Sarchie			    &priv->xmit : &priv->recv;
26559109Sarchie
26659109Sarchie			/* Check configuration */
26759109Sarchie			if (msg->header.arglen != sizeof(*cfg))
26859109Sarchie				ERROUT(EINVAL);
26959109Sarchie			if (cfg->enable) {
27059109Sarchie				if ((cfg->bits & ~MPPC_VALID_BITS) != 0)
27159109Sarchie					ERROUT(EINVAL);
27259109Sarchie#ifndef NETGRAPH_MPPC_COMPRESSION
27359109Sarchie				if ((cfg->bits & MPPC_BIT) != 0)
27459109Sarchie					ERROUT(EPROTONOSUPPORT);
27559109Sarchie#endif
27659109Sarchie#ifndef NETGRAPH_MPPC_ENCRYPTION
27759109Sarchie				if ((cfg->bits & MPPE_BITS) != 0)
27859109Sarchie					ERROUT(EPROTONOSUPPORT);
27959109Sarchie#endif
28059109Sarchie			} else
28159109Sarchie				cfg->bits = 0;
28259109Sarchie
28359109Sarchie			/* Save return address so we can send reset-req's */
284107845Sarchie			if (!isComp)
285107845Sarchie				priv->ctrlnode = NGI_RETADDR(item);
28659109Sarchie
28759109Sarchie			/* Configuration is OK, reset to it */
28859109Sarchie			d->cfg = *cfg;
28959109Sarchie
29059109Sarchie#ifdef NETGRAPH_MPPC_COMPRESSION
29159109Sarchie			/* Initialize state buffers for compression */
29259109Sarchie			if (d->history != NULL) {
293184205Sdes				free(d->history, M_NETGRAPH_MPPC);
29459109Sarchie				d->history = NULL;
29559109Sarchie			}
29659109Sarchie			if ((cfg->bits & MPPC_BIT) != 0) {
297184214Sdes				d->history = malloc(isComp ?
298184214Sdes				    MPPC_SizeOfCompressionHistory() :
29959109Sarchie				    MPPC_SizeOfDecompressionHistory(),
30070870Sjulian				    M_NETGRAPH_MPPC, M_NOWAIT);
30159109Sarchie				if (d->history == NULL)
30259109Sarchie					ERROUT(ENOMEM);
30359109Sarchie				if (isComp)
30459109Sarchie					MPPC_InitCompressionHistory(d->history);
30559109Sarchie				else {
30659109Sarchie					MPPC_InitDecompressionHistory(
30759109Sarchie					    d->history);
30859109Sarchie				}
30959109Sarchie			}
31059109Sarchie#endif
31159109Sarchie
31259109Sarchie#ifdef NETGRAPH_MPPC_ENCRYPTION
31359109Sarchie			/* Generate initial session keys for encryption */
31459109Sarchie			if ((cfg->bits & MPPE_BITS) != 0) {
31559109Sarchie				const int keylen = KEYLEN(cfg->bits);
31659109Sarchie
31759109Sarchie				bcopy(cfg->startkey, d->key, keylen);
31859109Sarchie				ng_mppc_getkey(cfg->startkey, d->key, keylen);
31987971Sarchie				if ((cfg->bits & MPPE_40) != 0)
32087971Sarchie					bcopy(&ng_mppe_weakenkey, d->key, 3);
32187971Sarchie				else if ((cfg->bits & MPPE_56) != 0)
32287971Sarchie					bcopy(&ng_mppe_weakenkey, d->key, 1);
32359109Sarchie				rc4_init(&d->rc4, d->key, keylen);
32459109Sarchie			}
32559109Sarchie#endif
32659109Sarchie
32759109Sarchie			/* Initialize other state */
32859109Sarchie			d->cc = 0;
32959109Sarchie			d->flushed = 0;
33059109Sarchie			break;
33159109Sarchie		    }
33259109Sarchie
33359109Sarchie		case NGM_MPPC_RESETREQ:
33459109Sarchie			ng_mppc_reset_req(node);
33559109Sarchie			break;
33659109Sarchie
33759109Sarchie		default:
33859109Sarchie			error = EINVAL;
33959109Sarchie			break;
34059109Sarchie		}
34159109Sarchie		break;
34259109Sarchie	default:
34359109Sarchie		error = EINVAL;
34459109Sarchie		break;
34559109Sarchie	}
34659109Sarchiedone:
34770700Sjulian	NG_RESPOND_MSG(error, node, item, resp);
34870700Sjulian	NG_FREE_MSG(msg);
34959109Sarchie	return (error);
35059109Sarchie}
35159109Sarchie
35259109Sarchie/*
35359109Sarchie * Receive incoming data on our hook.
35459109Sarchie */
35559109Sarchiestatic int
35670700Sjulianng_mppc_rcvdata(hook_p hook, item_p item)
35759109Sarchie{
35870784Sjulian	const node_p node = NG_HOOK_NODE(hook);
35970784Sjulian	const priv_p priv = NG_NODE_PRIVATE(node);
36059109Sarchie	int error;
36170700Sjulian	struct mbuf *m;
36259109Sarchie
36370700Sjulian	NGI_GET_M(item, m);
36459109Sarchie	/* Compress and/or encrypt */
36559109Sarchie	if (hook == priv->xmit.hook) {
36659109Sarchie		if (!priv->xmit.cfg.enable) {
36770700Sjulian			NG_FREE_M(m);
36870700Sjulian			NG_FREE_ITEM(item);
36959109Sarchie			return (ENXIO);
37059109Sarchie		}
371169474Smav		if ((error = ng_mppc_compress(node, &m)) != 0) {
37270700Sjulian			NG_FREE_ITEM(item);
37359109Sarchie			return(error);
37459109Sarchie		}
375169474Smav		NG_FWD_NEW_DATA(error, item, priv->xmit.hook, m);
37659109Sarchie		return (error);
37759109Sarchie	}
37859109Sarchie
37959109Sarchie	/* Decompress and/or decrypt */
38059109Sarchie	if (hook == priv->recv.hook) {
38159109Sarchie		if (!priv->recv.cfg.enable) {
38270700Sjulian			NG_FREE_M(m);
38370700Sjulian			NG_FREE_ITEM(item);
38459109Sarchie			return (ENXIO);
38559109Sarchie		}
386169474Smav		if ((error = ng_mppc_decompress(node, &m)) != 0) {
38770700Sjulian			NG_FREE_ITEM(item);
388102244Sarchie			if (error == EINVAL && priv->ctrlnode != 0) {
38959109Sarchie				struct ng_mesg *msg;
39059109Sarchie
39159109Sarchie				/* Need to send a reset-request */
39259109Sarchie				NG_MKMESSAGE(msg, NGM_MPPC_COOKIE,
39359109Sarchie				    NGM_MPPC_RESETREQ, 0, M_NOWAIT);
39459109Sarchie				if (msg == NULL)
39559109Sarchie					return (error);
39670700Sjulian				NG_SEND_MSG_ID(error, node, msg,
397102244Sarchie					priv->ctrlnode, 0);
39859109Sarchie			}
39959109Sarchie			return (error);
40059109Sarchie		}
401169474Smav		NG_FWD_NEW_DATA(error, item, priv->recv.hook, m);
40259109Sarchie		return (error);
40359109Sarchie	}
40459109Sarchie
40559109Sarchie	/* Oops */
40687599Sobrien	panic("%s: unknown hook", __func__);
40783366Sjulian#ifdef RESTARTABLE_PANICS
40883366Sjulian	return (EINVAL);
40983366Sjulian#endif
41059109Sarchie}
41159109Sarchie
41259109Sarchie/*
41359109Sarchie * Destroy node
41459109Sarchie */
41559109Sarchiestatic int
41670700Sjulianng_mppc_shutdown(node_p node)
41759109Sarchie{
41870784Sjulian	const priv_p priv = NG_NODE_PRIVATE(node);
41959109Sarchie
42059109Sarchie	/* Take down netgraph node */
42159109Sarchie#ifdef NETGRAPH_MPPC_COMPRESSION
42259109Sarchie	if (priv->xmit.history != NULL)
423184205Sdes		free(priv->xmit.history, M_NETGRAPH_MPPC);
42459109Sarchie	if (priv->recv.history != NULL)
425184205Sdes		free(priv->recv.history, M_NETGRAPH_MPPC);
42659109Sarchie#endif
42759109Sarchie	bzero(priv, sizeof(*priv));
428184205Sdes	free(priv, M_NETGRAPH_MPPC);
42970784Sjulian	NG_NODE_SET_PRIVATE(node, NULL);
43070784Sjulian	NG_NODE_UNREF(node);		/* let the node escape */
43159109Sarchie	return (0);
43259109Sarchie}
43359109Sarchie
43459109Sarchie/*
43559109Sarchie * Hook disconnection
43659109Sarchie */
43759109Sarchiestatic int
43859109Sarchieng_mppc_disconnect(hook_p hook)
43959109Sarchie{
44070784Sjulian	const node_p node = NG_HOOK_NODE(hook);
44170784Sjulian	const priv_p priv = NG_NODE_PRIVATE(node);
44259109Sarchie
44359109Sarchie	/* Zero out hook pointer */
44459109Sarchie	if (hook == priv->xmit.hook)
44559109Sarchie		priv->xmit.hook = NULL;
44659109Sarchie	if (hook == priv->recv.hook)
44759109Sarchie		priv->recv.hook = NULL;
44859109Sarchie
44959109Sarchie	/* Go away if no longer connected */
45070784Sjulian	if ((NG_NODE_NUMHOOKS(node) == 0)
45170784Sjulian	&& NG_NODE_IS_VALID(node))
45270700Sjulian		ng_rmnode_self(node);
45359109Sarchie	return (0);
45459109Sarchie}
45559109Sarchie
45659109Sarchie/************************************************************************
45759109Sarchie			HELPER STUFF
45859109Sarchie ************************************************************************/
45959109Sarchie
46059109Sarchie/*
46159109Sarchie * Compress/encrypt a packet and put the result in a new mbuf at *resultp.
46259109Sarchie * The original mbuf is not free'd.
46359109Sarchie */
46459109Sarchiestatic int
465169474Smavng_mppc_compress(node_p node, struct mbuf **datap)
46659109Sarchie{
46770784Sjulian	const priv_p priv = NG_NODE_PRIVATE(node);
46859109Sarchie	struct ng_mppc_dir *const d = &priv->xmit;
46959109Sarchie	u_int16_t header;
470169474Smav	struct mbuf *m = *datap;
47159109Sarchie
472187405Smav	/* We must own the mbuf chain exclusively to modify it. */
473187405Smav	m = m_unshare(m, M_DONTWAIT);
474187405Smav	if (m == NULL)
475187405Smav		return (ENOMEM);
476187405Smav
47759109Sarchie	/* Initialize */
47859109Sarchie	header = d->cc;
479169262Smav
480169262Smav	/* Always set the flushed bit in stateless mode */
481169262Smav	if (d->flushed || ((d->cfg.bits & MPPE_STATELESS) != 0)) {
48259109Sarchie		header |= MPPC_FLAG_FLUSHED;
48359109Sarchie		d->flushed = 0;
48459109Sarchie	}
48559109Sarchie
486169474Smav	/* Compress packet (if compression enabled) */
48759109Sarchie#ifdef NETGRAPH_MPPC_COMPRESSION
48859109Sarchie	if ((d->cfg.bits & MPPC_BIT) != 0) {
48959109Sarchie		u_short flags = MPPC_MANDATORY_COMPRESS_FLAGS;
490169474Smav		u_char *inbuf, *outbuf;
491187410Smav		int outlen, inlen, ina;
49259109Sarchie		u_char *source, *dest;
49359109Sarchie		u_long sourceCnt, destCnt;
49459109Sarchie		int rtn;
49559109Sarchie
496169474Smav		/* Work with contiguous regions of memory. */
497169474Smav		inlen = m->m_pkthdr.len;
498187410Smav		if (m->m_next == NULL) {
499187410Smav			inbuf = mtod(m, u_char *);
500187410Smav			ina = 0;
501187410Smav		} else {
502187410Smav			inbuf = malloc(inlen, M_NETGRAPH_MPPC, M_NOWAIT);
503187410Smav			if (inbuf == NULL)
504187410Smav				goto err1;
505187410Smav			m_copydata(m, 0, inlen, (caddr_t)inbuf);
506187410Smav			ina = 1;
507187410Smav		}
508169474Smav
509169474Smav		outlen = MPPC_MAX_BLOWUP(inlen);
510169474Smav		outbuf = malloc(outlen, M_NETGRAPH_MPPC, M_NOWAIT);
511169474Smav		if (outbuf == NULL) {
512187410Smav			if (ina)
513187410Smav				free(inbuf, M_NETGRAPH_MPPC);
514185723Smaverr1:
515169474Smav			m_freem(m);
516185723Smav			MPPC_InitCompressionHistory(d->history);
517185723Smav			d->flushed = 1;
518169474Smav			return (ENOMEM);
519169474Smav		}
520169474Smav
52159109Sarchie		/* Prepare to compress */
52259109Sarchie		source = inbuf;
52359109Sarchie		sourceCnt = inlen;
524169474Smav		dest = outbuf;
525169474Smav		destCnt = outlen;
52659109Sarchie		if ((d->cfg.bits & MPPE_STATELESS) == 0)
52759109Sarchie			flags |= MPPC_SAVE_HISTORY;
52859109Sarchie
52959109Sarchie		/* Compress */
53059109Sarchie		rtn = MPPC_Compress(&source, &dest, &sourceCnt,
53159109Sarchie			&destCnt, d->history, flags, 0);
53259109Sarchie
53359109Sarchie		/* Check return value */
53487599Sobrien		KASSERT(rtn != MPPC_INVALID, ("%s: invalid", __func__));
53559109Sarchie		if ((rtn & MPPC_EXPANDED) == 0
53659109Sarchie		    && (rtn & MPPC_COMP_OK) == MPPC_COMP_OK) {
53759109Sarchie			outlen -= destCnt;
53859109Sarchie			header |= MPPC_FLAG_COMPRESSED;
53959109Sarchie			if ((rtn & MPPC_RESTART_HISTORY) != 0)
54059109Sarchie				header |= MPPC_FLAG_RESTART;
541169474Smav
542169474Smav			/* Replace m by the compresed one. */
543187405Smav			m_copyback(m, 0, outlen, (caddr_t)outbuf);
544187405Smav			if (m->m_pkthdr.len < outlen) {
545187405Smav				m_freem(m);
546187405Smav				m = NULL;
547187405Smav			} else if (outlen < m->m_pkthdr.len)
548187405Smav				m_adj(m, outlen - m->m_pkthdr.len);
54959109Sarchie		}
55059109Sarchie		d->flushed = (rtn & MPPC_EXPANDED) != 0
55159109Sarchie		    || (flags & MPPC_SAVE_HISTORY) == 0;
552169474Smav
553187410Smav		if (ina)
554187410Smav			free(inbuf, M_NETGRAPH_MPPC);
555169474Smav		free(outbuf, M_NETGRAPH_MPPC);
556169474Smav
557187405Smav		/* Check mbuf chain reload result. */
558185723Smav		if (m == NULL) {
559185723Smav			if (!d->flushed) {
560185723Smav				MPPC_InitCompressionHistory(d->history);
561185723Smav				d->flushed = 1;
562185723Smav			}
563169474Smav			return (ENOMEM);
564185723Smav		}
56559109Sarchie	}
56659109Sarchie#endif
56759109Sarchie
56859109Sarchie	/* Now encrypt packet (if encryption enabled) */
56959109Sarchie#ifdef NETGRAPH_MPPC_ENCRYPTION
57059109Sarchie	if ((d->cfg.bits & MPPE_BITS) != 0) {
571169474Smav		struct mbuf *m1;
57259109Sarchie
573169263Smav		/* Set header bits */
57459109Sarchie		header |= MPPC_FLAG_ENCRYPTED;
57559109Sarchie
57659109Sarchie		/* Update key if it's time */
57759109Sarchie		if ((d->cfg.bits & MPPE_STATELESS) != 0
57859109Sarchie		    || (d->cc & MPPE_UPDATE_MASK) == MPPE_UPDATE_FLAG) {
579169263Smav			ng_mppc_updatekey(d->cfg.bits,
580169263Smav			    d->cfg.startkey, d->key, &d->rc4);
581169263Smav		} else if ((header & MPPC_FLAG_FLUSHED) != 0) {
582169263Smav			/* Need to reset key if we say we did
583169263Smav			   and ng_mppc_updatekey wasn't called to do it also. */
584169263Smav			rc4_init(&d->rc4, d->key, KEYLEN(d->cfg.bits));
58559109Sarchie		}
58659109Sarchie
58759109Sarchie		/* Encrypt packet */
588169474Smav		m1 = m;
589169474Smav		while (m1) {
590169474Smav			rc4_crypt(&d->rc4, mtod(m1, u_char *),
591169474Smav			    mtod(m1, u_char *), m1->m_len);
592169474Smav			m1 = m1->m_next;
593169474Smav		}
59459109Sarchie	}
59559109Sarchie#endif
59659109Sarchie
597169261Smav	/* Update coherency count for next time (12 bit arithmetic) */
598169261Smav	MPPC_CCOUNT_INC(d->cc);
59959109Sarchie
60059109Sarchie	/* Install header */
601169474Smav	M_PREPEND(m, MPPC_HDRLEN, M_DONTWAIT);
602169474Smav	if (m != NULL)
603206021Smav		be16enc(mtod(m, void *), header);
60459109Sarchie
605169474Smav	*datap = m;
606169474Smav	return (*datap == NULL ? ENOBUFS : 0);
60759109Sarchie}
60859109Sarchie
60959109Sarchie/*
61059109Sarchie * Decompress/decrypt packet and put the result in a new mbuf at *resultp.
61159109Sarchie * The original mbuf is not free'd.
61259109Sarchie */
61359109Sarchiestatic int
614169474Smavng_mppc_decompress(node_p node, struct mbuf **datap)
61559109Sarchie{
61670784Sjulian	const priv_p priv = NG_NODE_PRIVATE(node);
61759109Sarchie	struct ng_mppc_dir *const d = &priv->recv;
618107845Sarchie	u_int16_t header, cc;
619107845Sarchie	u_int numLost;
620169474Smav	struct mbuf *m = *datap;
62159109Sarchie
622187405Smav	/* We must own the mbuf chain exclusively to modify it. */
623187405Smav	m = m_unshare(m, M_DONTWAIT);
624187405Smav	if (m == NULL)
625187405Smav		return (ENOMEM);
626187405Smav
62759109Sarchie	/* Pull off header */
628169474Smav	if (m->m_pkthdr.len < MPPC_HDRLEN) {
629169474Smav		m_freem(m);
63059109Sarchie		return (EINVAL);
631169474Smav	}
632206021Smav	header = be16dec(mtod(m, void *));
63359109Sarchie	cc = (header & MPPC_CCOUNT_MASK);
634169474Smav	m_adj(m, MPPC_HDRLEN);
63559109Sarchie
636107845Sarchie	/* Check for an unexpected jump in the sequence number */
63759109Sarchie	numLost = ((cc - d->cc) & MPPC_CCOUNT_MASK);
63859109Sarchie
63959109Sarchie	/* If flushed bit set, we can always handle packet */
64059109Sarchie	if ((header & MPPC_FLAG_FLUSHED) != 0) {
64159109Sarchie#ifdef NETGRAPH_MPPC_COMPRESSION
64259109Sarchie		if (d->history != NULL)
64359109Sarchie			MPPC_InitDecompressionHistory(d->history);
64459109Sarchie#endif
64559109Sarchie#ifdef NETGRAPH_MPPC_ENCRYPTION
64659109Sarchie		if ((d->cfg.bits & MPPE_BITS) != 0) {
647107845Sarchie			u_int rekey;
64859109Sarchie
649107845Sarchie			/* How many times are we going to have to re-key? */
650107845Sarchie			rekey = ((d->cfg.bits & MPPE_STATELESS) != 0) ?
651107845Sarchie			    numLost : (numLost / (MPPE_UPDATE_MASK + 1));
652107845Sarchie			if (rekey > MPPE_MAX_REKEY) {
653107845Sarchie				log(LOG_ERR, "%s: too many (%d) packets"
654107845Sarchie				    " dropped, disabling node %p!",
655107845Sarchie				    __func__, numLost, node);
656107845Sarchie				priv->recv.cfg.enable = 0;
657107845Sarchie				goto failed;
658107845Sarchie			}
659107845Sarchie
660107845Sarchie			/* Re-key as necessary to catch up to peer */
66159109Sarchie			while (d->cc != cc) {
662107845Sarchie				if ((d->cfg.bits & MPPE_STATELESS) != 0
66359109Sarchie				    || (d->cc & MPPE_UPDATE_MASK)
66459109Sarchie				      == MPPE_UPDATE_FLAG) {
66559109Sarchie					ng_mppc_updatekey(d->cfg.bits,
66659109Sarchie					    d->cfg.startkey, d->key, &d->rc4);
66759109Sarchie				}
668169261Smav				MPPC_CCOUNT_INC(d->cc);
66959109Sarchie			}
67059109Sarchie
67159109Sarchie			/* Reset key (except in stateless mode, see below) */
67259109Sarchie			if ((d->cfg.bits & MPPE_STATELESS) == 0)
67359109Sarchie				rc4_init(&d->rc4, d->key, KEYLEN(d->cfg.bits));
67459109Sarchie		}
67559109Sarchie#endif
67659109Sarchie		d->cc = cc;		/* skip over lost seq numbers */
67759109Sarchie		numLost = 0;		/* act like no packets were lost */
67859109Sarchie	}
67959109Sarchie
68059109Sarchie	/* Can't decode non-sequential packets without a flushed bit */
68159109Sarchie	if (numLost != 0)
68259109Sarchie		goto failed;
68359109Sarchie
68459109Sarchie	/* Decrypt packet */
68559109Sarchie	if ((header & MPPC_FLAG_ENCRYPTED) != 0) {
686169474Smav#ifdef NETGRAPH_MPPC_ENCRYPTION
687169474Smav		struct mbuf *m1;
688169474Smav#endif
68959109Sarchie
69059109Sarchie		/* Are we not expecting encryption? */
69159109Sarchie		if ((d->cfg.bits & MPPE_BITS) == 0) {
69259109Sarchie			log(LOG_ERR, "%s: rec'd unexpectedly %s packet",
69387599Sobrien				__func__, "encrypted");
69459109Sarchie			goto failed;
69559109Sarchie		}
69659109Sarchie
69759109Sarchie#ifdef NETGRAPH_MPPC_ENCRYPTION
69859109Sarchie		/* Update key if it's time (always in stateless mode) */
69959109Sarchie		if ((d->cfg.bits & MPPE_STATELESS) != 0
70059109Sarchie		    || (d->cc & MPPE_UPDATE_MASK) == MPPE_UPDATE_FLAG) {
70159109Sarchie			ng_mppc_updatekey(d->cfg.bits,
70259109Sarchie			    d->cfg.startkey, d->key, &d->rc4);
70359109Sarchie		}
70459109Sarchie
70559109Sarchie		/* Decrypt packet */
706169474Smav		m1 = m;
707169474Smav		while (m1 != NULL) {
708169474Smav			rc4_crypt(&d->rc4, mtod(m1, u_char *),
709169474Smav			    mtod(m1, u_char *), m1->m_len);
710169474Smav			m1 = m1->m_next;
711169474Smav		}
71259109Sarchie#endif
71359109Sarchie	} else {
71459109Sarchie
71559109Sarchie		/* Are we expecting encryption? */
71659109Sarchie		if ((d->cfg.bits & MPPE_BITS) != 0) {
71759109Sarchie			log(LOG_ERR, "%s: rec'd unexpectedly %s packet",
71887599Sobrien				__func__, "unencrypted");
71959109Sarchie			goto failed;
72059109Sarchie		}
72159109Sarchie	}
72259109Sarchie
72359109Sarchie	/* Update coherency count for next time (12 bit arithmetic) */
724169261Smav	MPPC_CCOUNT_INC(d->cc);
72559109Sarchie
72659109Sarchie	/* Check for unexpected compressed packet */
72759109Sarchie	if ((header & MPPC_FLAG_COMPRESSED) != 0
72859109Sarchie	    && (d->cfg.bits & MPPC_BIT) == 0) {
72959109Sarchie		log(LOG_ERR, "%s: rec'd unexpectedly %s packet",
73087599Sobrien			__func__, "compressed");
73159109Sarchiefailed:
732169474Smav		m_freem(m);
73359109Sarchie		return (EINVAL);
73459109Sarchie	}
73559109Sarchie
73659109Sarchie#ifdef NETGRAPH_MPPC_COMPRESSION
73759109Sarchie	/* Decompress packet */
73859109Sarchie	if ((header & MPPC_FLAG_COMPRESSED) != 0) {
73959109Sarchie		int flags = MPPC_MANDATORY_DECOMPRESS_FLAGS;
740187410Smav		u_char *inbuf, *outbuf;
741187410Smav		int inlen, outlen, ina;
742187410Smav		u_char *source, *dest;
74359109Sarchie		u_long sourceCnt, destCnt;
744187410Smav		int rtn;
74559109Sarchie
746169474Smav		/* Copy payload into a contiguous region of memory. */
747187410Smav		inlen = m->m_pkthdr.len;
748187410Smav		if (m->m_next == NULL) {
749187410Smav                	inbuf = mtod(m, u_char *);
750187410Smav			ina = 0;
751187410Smav		} else {
752187410Smav		        inbuf = malloc(inlen, M_NETGRAPH_MPPC, M_NOWAIT);
753187410Smav			if (inbuf == NULL) {
754187410Smav				m_freem(m);
755187410Smav				return (ENOMEM);
756187410Smav			}
757187410Smav			m_copydata(m, 0, inlen, (caddr_t)inbuf);
758187410Smav			ina = 1;
759169474Smav		}
760169474Smav
76159109Sarchie		/* Allocate a buffer for decompressed data */
762187410Smav		outbuf = malloc(MPPC_DECOMP_BUFSIZE + MPPC_DECOMP_SAFETY,
763169474Smav		    M_NETGRAPH_MPPC, M_NOWAIT);
764187410Smav		if (outbuf == NULL) {
765169474Smav			m_freem(m);
766187410Smav			if (ina)
767187410Smav				free(inbuf, M_NETGRAPH_MPPC);
76859109Sarchie			return (ENOMEM);
76959109Sarchie		}
770187410Smav		outlen = MPPC_DECOMP_BUFSIZE;
77159109Sarchie
77259109Sarchie		/* Prepare to decompress */
773187410Smav		source = inbuf;
774187410Smav		sourceCnt = inlen;
775187410Smav		dest = outbuf;
776187410Smav		destCnt = outlen;
77759109Sarchie		if ((header & MPPC_FLAG_RESTART) != 0)
77859109Sarchie			flags |= MPPC_RESTART_HISTORY;
77959109Sarchie
78059109Sarchie		/* Decompress */
78159109Sarchie		rtn = MPPC_Decompress(&source, &dest,
78259109Sarchie			&sourceCnt, &destCnt, d->history, flags);
78359109Sarchie
78459109Sarchie		/* Check return value */
78587599Sobrien		KASSERT(rtn != MPPC_INVALID, ("%s: invalid", __func__));
78659109Sarchie		if ((rtn & MPPC_DEST_EXHAUSTED) != 0
78759109Sarchie		    || (rtn & MPPC_DECOMP_OK) != MPPC_DECOMP_OK) {
78859109Sarchie			log(LOG_ERR, "%s: decomp returned 0x%x",
78987599Sobrien			    __func__, rtn);
790187410Smav			if (ina)
791187410Smav				free(inbuf, M_NETGRAPH_MPPC);
792187410Smav			free(outbuf, M_NETGRAPH_MPPC);
79359109Sarchie			goto failed;
79459109Sarchie		}
79559109Sarchie
79659109Sarchie		/* Replace compressed data with decompressed data */
797187410Smav		if (ina)
798187410Smav			free(inbuf, M_NETGRAPH_MPPC);
799187410Smav		outlen -= destCnt;
800169474Smav
801187410Smav		m_copyback(m, 0, outlen, (caddr_t)outbuf);
802187410Smav		if (m->m_pkthdr.len < outlen) {
803187405Smav			m_freem(m);
804187405Smav			m = NULL;
805187410Smav		} else if (outlen < m->m_pkthdr.len)
806187410Smav			m_adj(m, outlen - m->m_pkthdr.len);
807187410Smav		free(outbuf, M_NETGRAPH_MPPC);
80859109Sarchie	}
80959109Sarchie#endif
81059109Sarchie
81159109Sarchie	/* Return result in an mbuf */
812169474Smav	*datap = m;
813169474Smav	return (*datap == NULL ? ENOBUFS : 0);
81459109Sarchie}
81559109Sarchie
81659109Sarchie/*
81759109Sarchie * The peer has sent us a CCP ResetRequest, so reset our transmit state.
81859109Sarchie */
81959109Sarchiestatic void
82059109Sarchieng_mppc_reset_req(node_p node)
82159109Sarchie{
82270784Sjulian	const priv_p priv = NG_NODE_PRIVATE(node);
82359109Sarchie	struct ng_mppc_dir *const d = &priv->xmit;
82459109Sarchie
82559109Sarchie#ifdef NETGRAPH_MPPC_COMPRESSION
82659109Sarchie	if (d->history != NULL)
82759109Sarchie		MPPC_InitCompressionHistory(d->history);
82859109Sarchie#endif
82959109Sarchie#ifdef NETGRAPH_MPPC_ENCRYPTION
83059109Sarchie	if ((d->cfg.bits & MPPE_STATELESS) == 0)
83159109Sarchie		rc4_init(&d->rc4, d->key, KEYLEN(d->cfg.bits));
83259109Sarchie#endif
83359109Sarchie	d->flushed = 1;
83459109Sarchie}
83559109Sarchie
836169678Smav#ifdef NETGRAPH_MPPC_ENCRYPTION
83759109Sarchie/*
83859109Sarchie * Generate a new encryption key
83959109Sarchie */
84059109Sarchiestatic void
84159109Sarchieng_mppc_getkey(const u_char *h, u_char *h2, int len)
84259109Sarchie{
843186189Smav	static const u_char pad1[40] =
844186189Smav	    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
845186189Smav	      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
846186189Smav	      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
847186189Smav	      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
848186189Smav	static const u_char pad2[40] =
849186189Smav	    { 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2,
850186189Smav	      0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2,
851186189Smav	      0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2,
852186189Smav	      0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2 };
85359109Sarchie	u_char hash[20];
85459109Sarchie	SHA1_CTX c;
85559109Sarchie
85659109Sarchie	SHA1Init(&c);
85759109Sarchie	SHA1Update(&c, h, len);
858186189Smav	SHA1Update(&c, pad1, sizeof(pad1));
85959109Sarchie	SHA1Update(&c, h2, len);
860186189Smav	SHA1Update(&c, pad2, sizeof(pad2));
86159109Sarchie	SHA1Final(hash, &c);
86259109Sarchie	bcopy(hash, h2, len);
86359109Sarchie}
86459109Sarchie
86559109Sarchie/*
86659109Sarchie * Update the encryption key
86759109Sarchie */
86859109Sarchiestatic void
86959109Sarchieng_mppc_updatekey(u_int32_t bits,
87059109Sarchie	u_char *key0, u_char *key, struct rc4_state *rc4)
87159109Sarchie{
87259109Sarchie	const int keylen = KEYLEN(bits);
87359109Sarchie
87459109Sarchie	ng_mppc_getkey(key0, key, keylen);
87559109Sarchie	rc4_init(rc4, key, keylen);
87659109Sarchie	rc4_crypt(rc4, key, key, keylen);
87787971Sarchie	if ((bits & MPPE_40) != 0)
87887971Sarchie		bcopy(&ng_mppe_weakenkey, key, 3);
87987971Sarchie	else if ((bits & MPPE_56) != 0)
88087971Sarchie		bcopy(&ng_mppe_weakenkey, key, 1);
88159109Sarchie	rc4_init(rc4, key, keylen);
88259109Sarchie}
883169678Smav#endif
88459109Sarchie
885