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$
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
77227293Sedstatic MALLOC_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
101235979Sglebius/*
102235979Sglebius * When packets are lost with MPPE, we may have to re-key arbitrarily
103235979Sglebius * many times to 'catch up' to the new jumped-ahead sequence number.
104235979Sglebius * Since this can be expensive, we pose a limit on how many re-keyings
105235979Sglebius * we will do at one time to avoid a possible D.O.S. vulnerability.
106235979Sglebius * This should instead be a configurable parameter.
107235979Sglebius */
108235979Sglebius#define MPPE_MAX_REKEY		1000
109235979Sglebius
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__);
40759109Sarchie}
40859109Sarchie
40959109Sarchie/*
41059109Sarchie * Destroy node
41159109Sarchie */
41259109Sarchiestatic int
41370700Sjulianng_mppc_shutdown(node_p node)
41459109Sarchie{
41570784Sjulian	const priv_p priv = NG_NODE_PRIVATE(node);
41659109Sarchie
41759109Sarchie	/* Take down netgraph node */
41859109Sarchie#ifdef NETGRAPH_MPPC_COMPRESSION
41959109Sarchie	if (priv->xmit.history != NULL)
420184205Sdes		free(priv->xmit.history, M_NETGRAPH_MPPC);
42159109Sarchie	if (priv->recv.history != NULL)
422184205Sdes		free(priv->recv.history, M_NETGRAPH_MPPC);
42359109Sarchie#endif
42459109Sarchie	bzero(priv, sizeof(*priv));
425184205Sdes	free(priv, M_NETGRAPH_MPPC);
42670784Sjulian	NG_NODE_SET_PRIVATE(node, NULL);
42770784Sjulian	NG_NODE_UNREF(node);		/* let the node escape */
42859109Sarchie	return (0);
42959109Sarchie}
43059109Sarchie
43159109Sarchie/*
43259109Sarchie * Hook disconnection
43359109Sarchie */
43459109Sarchiestatic int
43559109Sarchieng_mppc_disconnect(hook_p hook)
43659109Sarchie{
43770784Sjulian	const node_p node = NG_HOOK_NODE(hook);
43870784Sjulian	const priv_p priv = NG_NODE_PRIVATE(node);
43959109Sarchie
44059109Sarchie	/* Zero out hook pointer */
44159109Sarchie	if (hook == priv->xmit.hook)
44259109Sarchie		priv->xmit.hook = NULL;
44359109Sarchie	if (hook == priv->recv.hook)
44459109Sarchie		priv->recv.hook = NULL;
44559109Sarchie
44659109Sarchie	/* Go away if no longer connected */
44770784Sjulian	if ((NG_NODE_NUMHOOKS(node) == 0)
44870784Sjulian	&& NG_NODE_IS_VALID(node))
44970700Sjulian		ng_rmnode_self(node);
45059109Sarchie	return (0);
45159109Sarchie}
45259109Sarchie
45359109Sarchie/************************************************************************
45459109Sarchie			HELPER STUFF
45559109Sarchie ************************************************************************/
45659109Sarchie
45759109Sarchie/*
45859109Sarchie * Compress/encrypt a packet and put the result in a new mbuf at *resultp.
45959109Sarchie * The original mbuf is not free'd.
46059109Sarchie */
46159109Sarchiestatic int
462169474Smavng_mppc_compress(node_p node, struct mbuf **datap)
46359109Sarchie{
46470784Sjulian	const priv_p priv = NG_NODE_PRIVATE(node);
46559109Sarchie	struct ng_mppc_dir *const d = &priv->xmit;
46659109Sarchie	u_int16_t header;
467169474Smav	struct mbuf *m = *datap;
46859109Sarchie
469187405Smav	/* We must own the mbuf chain exclusively to modify it. */
470243882Sglebius	m = m_unshare(m, M_NOWAIT);
471187405Smav	if (m == NULL)
472187405Smav		return (ENOMEM);
473187405Smav
47459109Sarchie	/* Initialize */
47559109Sarchie	header = d->cc;
476169262Smav
477169262Smav	/* Always set the flushed bit in stateless mode */
478169262Smav	if (d->flushed || ((d->cfg.bits & MPPE_STATELESS) != 0)) {
47959109Sarchie		header |= MPPC_FLAG_FLUSHED;
48059109Sarchie		d->flushed = 0;
48159109Sarchie	}
48259109Sarchie
483169474Smav	/* Compress packet (if compression enabled) */
48459109Sarchie#ifdef NETGRAPH_MPPC_COMPRESSION
48559109Sarchie	if ((d->cfg.bits & MPPC_BIT) != 0) {
48659109Sarchie		u_short flags = MPPC_MANDATORY_COMPRESS_FLAGS;
487169474Smav		u_char *inbuf, *outbuf;
488187410Smav		int outlen, inlen, ina;
48959109Sarchie		u_char *source, *dest;
49059109Sarchie		u_long sourceCnt, destCnt;
49159109Sarchie		int rtn;
49259109Sarchie
493169474Smav		/* Work with contiguous regions of memory. */
494169474Smav		inlen = m->m_pkthdr.len;
495187410Smav		if (m->m_next == NULL) {
496187410Smav			inbuf = mtod(m, u_char *);
497187410Smav			ina = 0;
498187410Smav		} else {
499187410Smav			inbuf = malloc(inlen, M_NETGRAPH_MPPC, M_NOWAIT);
500187410Smav			if (inbuf == NULL)
501187410Smav				goto err1;
502187410Smav			m_copydata(m, 0, inlen, (caddr_t)inbuf);
503187410Smav			ina = 1;
504187410Smav		}
505169474Smav
506169474Smav		outlen = MPPC_MAX_BLOWUP(inlen);
507169474Smav		outbuf = malloc(outlen, M_NETGRAPH_MPPC, M_NOWAIT);
508169474Smav		if (outbuf == NULL) {
509187410Smav			if (ina)
510187410Smav				free(inbuf, M_NETGRAPH_MPPC);
511185723Smaverr1:
512169474Smav			m_freem(m);
513185723Smav			MPPC_InitCompressionHistory(d->history);
514185723Smav			d->flushed = 1;
515169474Smav			return (ENOMEM);
516169474Smav		}
517169474Smav
51859109Sarchie		/* Prepare to compress */
51959109Sarchie		source = inbuf;
52059109Sarchie		sourceCnt = inlen;
521169474Smav		dest = outbuf;
522169474Smav		destCnt = outlen;
52359109Sarchie		if ((d->cfg.bits & MPPE_STATELESS) == 0)
52459109Sarchie			flags |= MPPC_SAVE_HISTORY;
52559109Sarchie
52659109Sarchie		/* Compress */
52759109Sarchie		rtn = MPPC_Compress(&source, &dest, &sourceCnt,
52859109Sarchie			&destCnt, d->history, flags, 0);
52959109Sarchie
53059109Sarchie		/* Check return value */
53187599Sobrien		KASSERT(rtn != MPPC_INVALID, ("%s: invalid", __func__));
53259109Sarchie		if ((rtn & MPPC_EXPANDED) == 0
53359109Sarchie		    && (rtn & MPPC_COMP_OK) == MPPC_COMP_OK) {
53459109Sarchie			outlen -= destCnt;
53559109Sarchie			header |= MPPC_FLAG_COMPRESSED;
53659109Sarchie			if ((rtn & MPPC_RESTART_HISTORY) != 0)
53759109Sarchie				header |= MPPC_FLAG_RESTART;
538169474Smav
539169474Smav			/* Replace m by the compresed one. */
540187405Smav			m_copyback(m, 0, outlen, (caddr_t)outbuf);
541187405Smav			if (m->m_pkthdr.len < outlen) {
542187405Smav				m_freem(m);
543187405Smav				m = NULL;
544187405Smav			} else if (outlen < m->m_pkthdr.len)
545187405Smav				m_adj(m, outlen - m->m_pkthdr.len);
54659109Sarchie		}
54759109Sarchie		d->flushed = (rtn & MPPC_EXPANDED) != 0
54859109Sarchie		    || (flags & MPPC_SAVE_HISTORY) == 0;
549169474Smav
550187410Smav		if (ina)
551187410Smav			free(inbuf, M_NETGRAPH_MPPC);
552169474Smav		free(outbuf, M_NETGRAPH_MPPC);
553169474Smav
554187405Smav		/* Check mbuf chain reload result. */
555185723Smav		if (m == NULL) {
556185723Smav			if (!d->flushed) {
557185723Smav				MPPC_InitCompressionHistory(d->history);
558185723Smav				d->flushed = 1;
559185723Smav			}
560169474Smav			return (ENOMEM);
561185723Smav		}
56259109Sarchie	}
56359109Sarchie#endif
56459109Sarchie
56559109Sarchie	/* Now encrypt packet (if encryption enabled) */
56659109Sarchie#ifdef NETGRAPH_MPPC_ENCRYPTION
56759109Sarchie	if ((d->cfg.bits & MPPE_BITS) != 0) {
568169474Smav		struct mbuf *m1;
56959109Sarchie
570169263Smav		/* Set header bits */
57159109Sarchie		header |= MPPC_FLAG_ENCRYPTED;
57259109Sarchie
57359109Sarchie		/* Update key if it's time */
57459109Sarchie		if ((d->cfg.bits & MPPE_STATELESS) != 0
57559109Sarchie		    || (d->cc & MPPE_UPDATE_MASK) == MPPE_UPDATE_FLAG) {
576169263Smav			ng_mppc_updatekey(d->cfg.bits,
577169263Smav			    d->cfg.startkey, d->key, &d->rc4);
578169263Smav		} else if ((header & MPPC_FLAG_FLUSHED) != 0) {
579169263Smav			/* Need to reset key if we say we did
580169263Smav			   and ng_mppc_updatekey wasn't called to do it also. */
581169263Smav			rc4_init(&d->rc4, d->key, KEYLEN(d->cfg.bits));
58259109Sarchie		}
58359109Sarchie
58459109Sarchie		/* Encrypt packet */
585169474Smav		m1 = m;
586169474Smav		while (m1) {
587169474Smav			rc4_crypt(&d->rc4, mtod(m1, u_char *),
588169474Smav			    mtod(m1, u_char *), m1->m_len);
589169474Smav			m1 = m1->m_next;
590169474Smav		}
59159109Sarchie	}
59259109Sarchie#endif
59359109Sarchie
594169261Smav	/* Update coherency count for next time (12 bit arithmetic) */
595169261Smav	MPPC_CCOUNT_INC(d->cc);
59659109Sarchie
59759109Sarchie	/* Install header */
598243882Sglebius	M_PREPEND(m, MPPC_HDRLEN, M_NOWAIT);
599169474Smav	if (m != NULL)
600206021Smav		be16enc(mtod(m, void *), header);
60159109Sarchie
602169474Smav	*datap = m;
603169474Smav	return (*datap == NULL ? ENOBUFS : 0);
60459109Sarchie}
60559109Sarchie
60659109Sarchie/*
60759109Sarchie * Decompress/decrypt packet and put the result in a new mbuf at *resultp.
60859109Sarchie * The original mbuf is not free'd.
60959109Sarchie */
61059109Sarchiestatic int
611169474Smavng_mppc_decompress(node_p node, struct mbuf **datap)
61259109Sarchie{
61370784Sjulian	const priv_p priv = NG_NODE_PRIVATE(node);
61459109Sarchie	struct ng_mppc_dir *const d = &priv->recv;
615107845Sarchie	u_int16_t header, cc;
616107845Sarchie	u_int numLost;
617169474Smav	struct mbuf *m = *datap;
61859109Sarchie
619187405Smav	/* We must own the mbuf chain exclusively to modify it. */
620243882Sglebius	m = m_unshare(m, M_NOWAIT);
621187405Smav	if (m == NULL)
622187405Smav		return (ENOMEM);
623187405Smav
62459109Sarchie	/* Pull off header */
625169474Smav	if (m->m_pkthdr.len < MPPC_HDRLEN) {
626169474Smav		m_freem(m);
62759109Sarchie		return (EINVAL);
628169474Smav	}
629206021Smav	header = be16dec(mtod(m, void *));
63059109Sarchie	cc = (header & MPPC_CCOUNT_MASK);
631169474Smav	m_adj(m, MPPC_HDRLEN);
63259109Sarchie
633107845Sarchie	/* Check for an unexpected jump in the sequence number */
63459109Sarchie	numLost = ((cc - d->cc) & MPPC_CCOUNT_MASK);
63559109Sarchie
63659109Sarchie	/* If flushed bit set, we can always handle packet */
63759109Sarchie	if ((header & MPPC_FLAG_FLUSHED) != 0) {
63859109Sarchie#ifdef NETGRAPH_MPPC_COMPRESSION
63959109Sarchie		if (d->history != NULL)
64059109Sarchie			MPPC_InitDecompressionHistory(d->history);
64159109Sarchie#endif
64259109Sarchie#ifdef NETGRAPH_MPPC_ENCRYPTION
64359109Sarchie		if ((d->cfg.bits & MPPE_BITS) != 0) {
644235979Sglebius			u_int rekey;
64559109Sarchie
646235979Sglebius			/* How many times are we going to have to re-key? */
647235979Sglebius			rekey = ((d->cfg.bits & MPPE_STATELESS) != 0) ?
648235979Sglebius			    numLost : (numLost / (MPPE_UPDATE_MASK + 1));
649235979Sglebius			if (rekey > MPPE_MAX_REKEY) {
650235979Sglebius				log(LOG_ERR, "%s: too many (%d) packets"
651235979Sglebius				    " dropped, disabling node %p!",
652235979Sglebius				    __func__, numLost, node);
653235979Sglebius				priv->recv.cfg.enable = 0;
654235979Sglebius				goto failed;
655235979Sglebius			}
656235979Sglebius
657235979Sglebius			/* Re-key as necessary to catch up to peer */
65859109Sarchie			while (d->cc != cc) {
659107845Sarchie				if ((d->cfg.bits & MPPE_STATELESS) != 0
66059109Sarchie				    || (d->cc & MPPE_UPDATE_MASK)
66159109Sarchie				      == MPPE_UPDATE_FLAG) {
66259109Sarchie					ng_mppc_updatekey(d->cfg.bits,
66359109Sarchie					    d->cfg.startkey, d->key, &d->rc4);
66459109Sarchie				}
665169261Smav				MPPC_CCOUNT_INC(d->cc);
66659109Sarchie			}
66759109Sarchie
66859109Sarchie			/* Reset key (except in stateless mode, see below) */
66959109Sarchie			if ((d->cfg.bits & MPPE_STATELESS) == 0)
67059109Sarchie				rc4_init(&d->rc4, d->key, KEYLEN(d->cfg.bits));
67159109Sarchie		}
67259109Sarchie#endif
67359109Sarchie		d->cc = cc;		/* skip over lost seq numbers */
67459109Sarchie		numLost = 0;		/* act like no packets were lost */
67559109Sarchie	}
67659109Sarchie
67759109Sarchie	/* Can't decode non-sequential packets without a flushed bit */
67859109Sarchie	if (numLost != 0)
67959109Sarchie		goto failed;
68059109Sarchie
68159109Sarchie	/* Decrypt packet */
68259109Sarchie	if ((header & MPPC_FLAG_ENCRYPTED) != 0) {
683169474Smav#ifdef NETGRAPH_MPPC_ENCRYPTION
684169474Smav		struct mbuf *m1;
685169474Smav#endif
68659109Sarchie
68759109Sarchie		/* Are we not expecting encryption? */
68859109Sarchie		if ((d->cfg.bits & MPPE_BITS) == 0) {
68959109Sarchie			log(LOG_ERR, "%s: rec'd unexpectedly %s packet",
69087599Sobrien				__func__, "encrypted");
69159109Sarchie			goto failed;
69259109Sarchie		}
69359109Sarchie
69459109Sarchie#ifdef NETGRAPH_MPPC_ENCRYPTION
69559109Sarchie		/* Update key if it's time (always in stateless mode) */
69659109Sarchie		if ((d->cfg.bits & MPPE_STATELESS) != 0
69759109Sarchie		    || (d->cc & MPPE_UPDATE_MASK) == MPPE_UPDATE_FLAG) {
69859109Sarchie			ng_mppc_updatekey(d->cfg.bits,
69959109Sarchie			    d->cfg.startkey, d->key, &d->rc4);
70059109Sarchie		}
70159109Sarchie
70259109Sarchie		/* Decrypt packet */
703169474Smav		m1 = m;
704169474Smav		while (m1 != NULL) {
705169474Smav			rc4_crypt(&d->rc4, mtod(m1, u_char *),
706169474Smav			    mtod(m1, u_char *), m1->m_len);
707169474Smav			m1 = m1->m_next;
708169474Smav		}
70959109Sarchie#endif
71059109Sarchie	} else {
71159109Sarchie
71259109Sarchie		/* Are we expecting encryption? */
71359109Sarchie		if ((d->cfg.bits & MPPE_BITS) != 0) {
71459109Sarchie			log(LOG_ERR, "%s: rec'd unexpectedly %s packet",
71587599Sobrien				__func__, "unencrypted");
71659109Sarchie			goto failed;
71759109Sarchie		}
71859109Sarchie	}
71959109Sarchie
72059109Sarchie	/* Update coherency count for next time (12 bit arithmetic) */
721169261Smav	MPPC_CCOUNT_INC(d->cc);
72259109Sarchie
72359109Sarchie	/* Check for unexpected compressed packet */
72459109Sarchie	if ((header & MPPC_FLAG_COMPRESSED) != 0
72559109Sarchie	    && (d->cfg.bits & MPPC_BIT) == 0) {
72659109Sarchie		log(LOG_ERR, "%s: rec'd unexpectedly %s packet",
72787599Sobrien			__func__, "compressed");
72859109Sarchiefailed:
729169474Smav		m_freem(m);
73059109Sarchie		return (EINVAL);
73159109Sarchie	}
73259109Sarchie
73359109Sarchie#ifdef NETGRAPH_MPPC_COMPRESSION
73459109Sarchie	/* Decompress packet */
73559109Sarchie	if ((header & MPPC_FLAG_COMPRESSED) != 0) {
73659109Sarchie		int flags = MPPC_MANDATORY_DECOMPRESS_FLAGS;
737187410Smav		u_char *inbuf, *outbuf;
738187410Smav		int inlen, outlen, ina;
739187410Smav		u_char *source, *dest;
74059109Sarchie		u_long sourceCnt, destCnt;
741187410Smav		int rtn;
74259109Sarchie
743169474Smav		/* Copy payload into a contiguous region of memory. */
744187410Smav		inlen = m->m_pkthdr.len;
745187410Smav		if (m->m_next == NULL) {
746187410Smav                	inbuf = mtod(m, u_char *);
747187410Smav			ina = 0;
748187410Smav		} else {
749187410Smav		        inbuf = malloc(inlen, M_NETGRAPH_MPPC, M_NOWAIT);
750187410Smav			if (inbuf == NULL) {
751187410Smav				m_freem(m);
752187410Smav				return (ENOMEM);
753187410Smav			}
754187410Smav			m_copydata(m, 0, inlen, (caddr_t)inbuf);
755187410Smav			ina = 1;
756169474Smav		}
757169474Smav
75859109Sarchie		/* Allocate a buffer for decompressed data */
759187410Smav		outbuf = malloc(MPPC_DECOMP_BUFSIZE + MPPC_DECOMP_SAFETY,
760169474Smav		    M_NETGRAPH_MPPC, M_NOWAIT);
761187410Smav		if (outbuf == NULL) {
762169474Smav			m_freem(m);
763187410Smav			if (ina)
764187410Smav				free(inbuf, M_NETGRAPH_MPPC);
76559109Sarchie			return (ENOMEM);
76659109Sarchie		}
767187410Smav		outlen = MPPC_DECOMP_BUFSIZE;
76859109Sarchie
76959109Sarchie		/* Prepare to decompress */
770187410Smav		source = inbuf;
771187410Smav		sourceCnt = inlen;
772187410Smav		dest = outbuf;
773187410Smav		destCnt = outlen;
77459109Sarchie		if ((header & MPPC_FLAG_RESTART) != 0)
77559109Sarchie			flags |= MPPC_RESTART_HISTORY;
77659109Sarchie
77759109Sarchie		/* Decompress */
77859109Sarchie		rtn = MPPC_Decompress(&source, &dest,
77959109Sarchie			&sourceCnt, &destCnt, d->history, flags);
78059109Sarchie
78159109Sarchie		/* Check return value */
78287599Sobrien		KASSERT(rtn != MPPC_INVALID, ("%s: invalid", __func__));
78359109Sarchie		if ((rtn & MPPC_DEST_EXHAUSTED) != 0
78459109Sarchie		    || (rtn & MPPC_DECOMP_OK) != MPPC_DECOMP_OK) {
78559109Sarchie			log(LOG_ERR, "%s: decomp returned 0x%x",
78687599Sobrien			    __func__, rtn);
787187410Smav			if (ina)
788187410Smav				free(inbuf, M_NETGRAPH_MPPC);
789187410Smav			free(outbuf, M_NETGRAPH_MPPC);
79059109Sarchie			goto failed;
79159109Sarchie		}
79259109Sarchie
79359109Sarchie		/* Replace compressed data with decompressed data */
794187410Smav		if (ina)
795187410Smav			free(inbuf, M_NETGRAPH_MPPC);
796187410Smav		outlen -= destCnt;
797169474Smav
798187410Smav		m_copyback(m, 0, outlen, (caddr_t)outbuf);
799187410Smav		if (m->m_pkthdr.len < outlen) {
800187405Smav			m_freem(m);
801187405Smav			m = NULL;
802187410Smav		} else if (outlen < m->m_pkthdr.len)
803187410Smav			m_adj(m, outlen - m->m_pkthdr.len);
804187410Smav		free(outbuf, M_NETGRAPH_MPPC);
80559109Sarchie	}
80659109Sarchie#endif
80759109Sarchie
80859109Sarchie	/* Return result in an mbuf */
809169474Smav	*datap = m;
810169474Smav	return (*datap == NULL ? ENOBUFS : 0);
81159109Sarchie}
81259109Sarchie
81359109Sarchie/*
81459109Sarchie * The peer has sent us a CCP ResetRequest, so reset our transmit state.
81559109Sarchie */
81659109Sarchiestatic void
81759109Sarchieng_mppc_reset_req(node_p node)
81859109Sarchie{
81970784Sjulian	const priv_p priv = NG_NODE_PRIVATE(node);
82059109Sarchie	struct ng_mppc_dir *const d = &priv->xmit;
82159109Sarchie
82259109Sarchie#ifdef NETGRAPH_MPPC_COMPRESSION
82359109Sarchie	if (d->history != NULL)
82459109Sarchie		MPPC_InitCompressionHistory(d->history);
82559109Sarchie#endif
82659109Sarchie#ifdef NETGRAPH_MPPC_ENCRYPTION
82759109Sarchie	if ((d->cfg.bits & MPPE_STATELESS) == 0)
82859109Sarchie		rc4_init(&d->rc4, d->key, KEYLEN(d->cfg.bits));
82959109Sarchie#endif
83059109Sarchie	d->flushed = 1;
83159109Sarchie}
83259109Sarchie
833169678Smav#ifdef NETGRAPH_MPPC_ENCRYPTION
83459109Sarchie/*
83559109Sarchie * Generate a new encryption key
83659109Sarchie */
83759109Sarchiestatic void
83859109Sarchieng_mppc_getkey(const u_char *h, u_char *h2, int len)
83959109Sarchie{
840186189Smav	static const u_char pad1[40] =
841186189Smav	    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
842186189Smav	      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
843186189Smav	      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
844186189Smav	      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
845186189Smav	static const u_char pad2[40] =
846186189Smav	    { 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2,
847186189Smav	      0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2,
848186189Smav	      0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2,
849186189Smav	      0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2 };
85059109Sarchie	u_char hash[20];
85159109Sarchie	SHA1_CTX c;
85259109Sarchie
85359109Sarchie	SHA1Init(&c);
85459109Sarchie	SHA1Update(&c, h, len);
855186189Smav	SHA1Update(&c, pad1, sizeof(pad1));
85659109Sarchie	SHA1Update(&c, h2, len);
857186189Smav	SHA1Update(&c, pad2, sizeof(pad2));
85859109Sarchie	SHA1Final(hash, &c);
85959109Sarchie	bcopy(hash, h2, len);
86059109Sarchie}
86159109Sarchie
86259109Sarchie/*
86359109Sarchie * Update the encryption key
86459109Sarchie */
86559109Sarchiestatic void
86659109Sarchieng_mppc_updatekey(u_int32_t bits,
86759109Sarchie	u_char *key0, u_char *key, struct rc4_state *rc4)
86859109Sarchie{
86959109Sarchie	const int keylen = KEYLEN(bits);
87059109Sarchie
87159109Sarchie	ng_mppc_getkey(key0, key, keylen);
87259109Sarchie	rc4_init(rc4, key, keylen);
87359109Sarchie	rc4_crypt(rc4, key, key, keylen);
87487971Sarchie	if ((bits & MPPE_40) != 0)
87587971Sarchie		bcopy(&ng_mppe_weakenkey, key, 3);
87687971Sarchie	else if ((bits & MPPE_56) != 0)
87787971Sarchie		bcopy(&ng_mppe_weakenkey, key, 1);
87859109Sarchie	rc4_init(rc4, key, keylen);
87959109Sarchie}
880169678Smav#endif
88159109Sarchie
882