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