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