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