]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/netgraph/ng_mppc.c
sysctl(9): Fix a few mandoc related issues
[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 | CTLFLAG_MPSAFE, 0,
111     "MPPE");
112
113 static int mppe_block_on_max_rekey = 0;
114 SYSCTL_INT(_net_graph_mppe, OID_AUTO, block_on_max_rekey, CTLFLAG_RWTUN,
115     &mppe_block_on_max_rekey, 0, "Block node on max MPPE key re-calculations");
116
117 static int mppe_log_max_rekey = 1;
118 SYSCTL_INT(_net_graph_mppe, OID_AUTO, log_max_rekey, CTLFLAG_RWTUN,
119     &mppe_log_max_rekey, 0, "Log max MPPE key re-calculations event");
120
121 static int mppe_max_rekey = MPPE_MAX_REKEY;
122 SYSCTL_INT(_net_graph_mppe, OID_AUTO, max_rekey, CTLFLAG_RWTUN,
123     &mppe_max_rekey, 0, "Maximum number of MPPE key re-calculations");
124
125 /* MPPC packet header bits */
126 #define MPPC_FLAG_FLUSHED       0x8000          /* xmitter reset state */
127 #define MPPC_FLAG_RESTART       0x4000          /* compress history restart */
128 #define MPPC_FLAG_COMPRESSED    0x2000          /* packet is compresed */
129 #define MPPC_FLAG_ENCRYPTED     0x1000          /* packet is encrypted */
130 #define MPPC_CCOUNT_MASK        0x0fff          /* sequence number mask */
131
132 #define MPPC_CCOUNT_INC(d)      ((d) = (((d) + 1) & MPPC_CCOUNT_MASK))
133
134 #define MPPE_UPDATE_MASK        0xff            /* coherency count when we're */
135 #define MPPE_UPDATE_FLAG        0xff            /*   supposed to update key */
136
137 #define MPPC_COMP_OK            0x05
138 #define MPPC_DECOMP_OK          0x05
139
140 /* Per direction info */
141 struct ng_mppc_dir {
142         struct ng_mppc_config   cfg;            /* configuration */
143         hook_p                  hook;           /* netgraph hook */
144         u_int16_t               cc:12;          /* coherency count */
145         u_char                  flushed;        /* clean history (xmit only) */
146 #ifdef NETGRAPH_MPPC_COMPRESSION
147         u_char                  *history;       /* compression history */
148 #endif
149 #ifdef NETGRAPH_MPPC_ENCRYPTION
150         u_char                  key[MPPE_KEY_LEN];      /* session key */
151         struct rc4_state        rc4;                    /* rc4 state */
152 #endif
153 };
154
155 /* Node private data */
156 struct ng_mppc_private {
157         struct ng_mppc_dir      xmit;           /* compress/encrypt config */
158         struct ng_mppc_dir      recv;           /* decompress/decrypt config */
159         ng_ID_t                 ctrlnode;       /* path to controlling node */
160 };
161 typedef struct ng_mppc_private *priv_p;
162
163 /* Netgraph node methods */
164 static ng_constructor_t ng_mppc_constructor;
165 static ng_rcvmsg_t      ng_mppc_rcvmsg;
166 static ng_shutdown_t    ng_mppc_shutdown;
167 static ng_newhook_t     ng_mppc_newhook;
168 static ng_rcvdata_t     ng_mppc_rcvdata;
169 static ng_disconnect_t  ng_mppc_disconnect;
170
171 /* Helper functions */
172 static int      ng_mppc_compress(node_p node,
173                         struct mbuf **datap);
174 static int      ng_mppc_decompress(node_p node,
175                         struct mbuf **datap);
176 #ifdef NETGRAPH_MPPC_ENCRYPTION
177 static void     ng_mppc_getkey(const u_char *h, u_char *h2, int len);
178 static void     ng_mppc_updatekey(u_int32_t bits,
179                         u_char *key0, u_char *key, struct rc4_state *rc4);
180 #endif
181 static void     ng_mppc_reset_req(node_p node);
182
183 /* Node type descriptor */
184 static struct ng_type ng_mppc_typestruct = {
185         .version =      NG_ABI_VERSION,
186         .name =         NG_MPPC_NODE_TYPE,
187         .constructor =  ng_mppc_constructor,
188         .rcvmsg =       ng_mppc_rcvmsg,
189         .shutdown =     ng_mppc_shutdown,
190         .newhook =      ng_mppc_newhook,
191         .rcvdata =      ng_mppc_rcvdata,
192         .disconnect =   ng_mppc_disconnect,
193 };
194 NETGRAPH_INIT(mppc, &ng_mppc_typestruct);
195
196 #ifdef NETGRAPH_MPPC_ENCRYPTION
197 /* Depend on separate rc4 module */
198 MODULE_DEPEND(ng_mppc, rc4, 1, 1, 1);
199 #endif
200
201 /* Fixed bit pattern to weaken keysize down to 40 or 56 bits */
202 static const u_char ng_mppe_weakenkey[3] = { 0xd1, 0x26, 0x9e };
203
204 #define ERROUT(x)       do { error = (x); goto done; } while (0)
205
206 /************************************************************************
207                         NETGRAPH NODE STUFF
208  ************************************************************************/
209
210 /*
211  * Node type constructor
212  */
213 static int
214 ng_mppc_constructor(node_p node)
215 {
216         priv_p priv;
217
218         /* Allocate private structure */
219         priv = malloc(sizeof(*priv), M_NETGRAPH_MPPC, M_WAITOK | M_ZERO);
220
221         NG_NODE_SET_PRIVATE(node, priv);
222
223         /* This node is not thread safe. */
224         NG_NODE_FORCE_WRITER(node);
225
226         /* Done */
227         return (0);
228 }
229
230 /*
231  * Give our OK for a hook to be added
232  */
233 static int
234 ng_mppc_newhook(node_p node, hook_p hook, const char *name)
235 {
236         const priv_p priv = NG_NODE_PRIVATE(node);
237         hook_p *hookPtr;
238
239         /* Check hook name */
240         if (strcmp(name, NG_MPPC_HOOK_COMP) == 0)
241                 hookPtr = &priv->xmit.hook;
242         else if (strcmp(name, NG_MPPC_HOOK_DECOMP) == 0)
243                 hookPtr = &priv->recv.hook;
244         else
245                 return (EINVAL);
246
247         /* See if already connected */
248         if (*hookPtr != NULL)
249                 return (EISCONN);
250
251         /* OK */
252         *hookPtr = hook;
253         return (0);
254 }
255
256 /*
257  * Receive a control message
258  */
259 static int
260 ng_mppc_rcvmsg(node_p node, item_p item, hook_p lasthook)
261 {
262         const priv_p priv = NG_NODE_PRIVATE(node);
263         struct ng_mesg *resp = NULL;
264         int error = 0;
265         struct ng_mesg *msg;
266
267         NGI_GET_MSG(item, msg);
268         switch (msg->header.typecookie) {
269         case NGM_MPPC_COOKIE:
270                 switch (msg->header.cmd) {
271                 case NGM_MPPC_CONFIG_COMP:
272                 case NGM_MPPC_CONFIG_DECOMP:
273                     {
274                         struct ng_mppc_config *const cfg
275                             = (struct ng_mppc_config *)msg->data;
276                         const int isComp =
277                             msg->header.cmd == NGM_MPPC_CONFIG_COMP;
278                         struct ng_mppc_dir *const d = isComp ?
279                             &priv->xmit : &priv->recv;
280
281                         /* Check configuration */
282                         if (msg->header.arglen != sizeof(*cfg))
283                                 ERROUT(EINVAL);
284                         if (cfg->enable) {
285                                 if ((cfg->bits & ~MPPC_VALID_BITS) != 0)
286                                         ERROUT(EINVAL);
287 #ifndef NETGRAPH_MPPC_COMPRESSION
288                                 if ((cfg->bits & MPPC_BIT) != 0)
289                                         ERROUT(EPROTONOSUPPORT);
290 #endif
291 #ifndef NETGRAPH_MPPC_ENCRYPTION
292                                 if ((cfg->bits & MPPE_BITS) != 0)
293                                         ERROUT(EPROTONOSUPPORT);
294 #endif
295                         } else
296                                 cfg->bits = 0;
297
298                         /* Save return address so we can send reset-req's */
299                         if (!isComp)
300                                 priv->ctrlnode = NGI_RETADDR(item);
301
302                         /* Configuration is OK, reset to it */
303                         d->cfg = *cfg;
304
305 #ifdef NETGRAPH_MPPC_COMPRESSION
306                         /* Initialize state buffers for compression */
307                         if (d->history != NULL) {
308                                 free(d->history, M_NETGRAPH_MPPC);
309                                 d->history = NULL;
310                         }
311                         if ((cfg->bits & MPPC_BIT) != 0) {
312                                 d->history = malloc(isComp ?
313                                     MPPC_SizeOfCompressionHistory() :
314                                     MPPC_SizeOfDecompressionHistory(),
315                                     M_NETGRAPH_MPPC, M_NOWAIT);
316                                 if (d->history == NULL)
317                                         ERROUT(ENOMEM);
318                                 if (isComp)
319                                         MPPC_InitCompressionHistory(d->history);
320                                 else {
321                                         MPPC_InitDecompressionHistory(
322                                             d->history);
323                                 }
324                         }
325 #endif
326
327 #ifdef NETGRAPH_MPPC_ENCRYPTION
328                         /* Generate initial session keys for encryption */
329                         if ((cfg->bits & MPPE_BITS) != 0) {
330                                 const int keylen = KEYLEN(cfg->bits);
331
332                                 bcopy(cfg->startkey, d->key, keylen);
333                                 ng_mppc_getkey(cfg->startkey, d->key, keylen);
334                                 if ((cfg->bits & MPPE_40) != 0)
335                                         bcopy(&ng_mppe_weakenkey, d->key, 3);
336                                 else if ((cfg->bits & MPPE_56) != 0)
337                                         bcopy(&ng_mppe_weakenkey, d->key, 1);
338                                 rc4_init(&d->rc4, d->key, keylen);
339                         }
340 #endif
341
342                         /* Initialize other state */
343                         d->cc = 0;
344                         d->flushed = 0;
345                         break;
346                     }
347
348                 case NGM_MPPC_RESETREQ:
349                         ng_mppc_reset_req(node);
350                         break;
351
352                 default:
353                         error = EINVAL;
354                         break;
355                 }
356                 break;
357         default:
358                 error = EINVAL;
359                 break;
360         }
361 done:
362         NG_RESPOND_MSG(error, node, item, resp);
363         NG_FREE_MSG(msg);
364         return (error);
365 }
366
367 /*
368  * Receive incoming data on our hook.
369  */
370 static int
371 ng_mppc_rcvdata(hook_p hook, item_p item)
372 {
373         const node_p node = NG_HOOK_NODE(hook);
374         const priv_p priv = NG_NODE_PRIVATE(node);
375         int error;
376         struct mbuf *m;
377
378         NGI_GET_M(item, m);
379         /* Compress and/or encrypt */
380         if (hook == priv->xmit.hook) {
381                 if (!priv->xmit.cfg.enable) {
382                         NG_FREE_M(m);
383                         NG_FREE_ITEM(item);
384                         return (ENXIO);
385                 }
386                 if ((error = ng_mppc_compress(node, &m)) != 0) {
387                         NG_FREE_ITEM(item);
388                         return(error);
389                 }
390                 NG_FWD_NEW_DATA(error, item, priv->xmit.hook, m);
391                 return (error);
392         }
393
394         /* Decompress and/or decrypt */
395         if (hook == priv->recv.hook) {
396                 if (!priv->recv.cfg.enable) {
397                         NG_FREE_M(m);
398                         NG_FREE_ITEM(item);
399                         return (ENXIO);
400                 }
401                 if ((error = ng_mppc_decompress(node, &m)) != 0) {
402                         NG_FREE_ITEM(item);
403                         if (error == EINVAL && priv->ctrlnode != 0) {
404                                 struct ng_mesg *msg;
405
406                                 /* Need to send a reset-request */
407                                 NG_MKMESSAGE(msg, NGM_MPPC_COOKIE,
408                                     NGM_MPPC_RESETREQ, 0, M_NOWAIT);
409                                 if (msg == NULL)
410                                         return (error);
411                                 NG_SEND_MSG_ID(error, node, msg,
412                                         priv->ctrlnode, 0); 
413                         }
414                         return (error);
415                 }
416                 NG_FWD_NEW_DATA(error, item, priv->recv.hook, m);
417                 return (error);
418         }
419
420         /* Oops */
421         panic("%s: unknown hook", __func__);
422 }
423
424 /*
425  * Destroy node
426  */
427 static int
428 ng_mppc_shutdown(node_p node)
429 {
430         const priv_p priv = NG_NODE_PRIVATE(node);
431
432         /* Take down netgraph node */
433 #ifdef NETGRAPH_MPPC_COMPRESSION
434         if (priv->xmit.history != NULL)
435                 free(priv->xmit.history, M_NETGRAPH_MPPC);
436         if (priv->recv.history != NULL)
437                 free(priv->recv.history, M_NETGRAPH_MPPC);
438 #endif
439         bzero(priv, sizeof(*priv));
440         free(priv, M_NETGRAPH_MPPC);
441         NG_NODE_SET_PRIVATE(node, NULL);
442         NG_NODE_UNREF(node);            /* let the node escape */
443         return (0);
444 }
445
446 /*
447  * Hook disconnection
448  */
449 static int
450 ng_mppc_disconnect(hook_p hook)
451 {
452         const node_p node = NG_HOOK_NODE(hook);
453         const priv_p priv = NG_NODE_PRIVATE(node);
454
455         /* Zero out hook pointer */
456         if (hook == priv->xmit.hook)
457                 priv->xmit.hook = NULL;
458         if (hook == priv->recv.hook)
459                 priv->recv.hook = NULL;
460
461         /* Go away if no longer connected */
462         if ((NG_NODE_NUMHOOKS(node) == 0)
463         && NG_NODE_IS_VALID(node))
464                 ng_rmnode_self(node);
465         return (0);
466 }
467
468 /************************************************************************
469                         HELPER STUFF
470  ************************************************************************/
471
472 /*
473  * Compress/encrypt a packet and put the result in a new mbuf at *resultp.
474  * The original mbuf is not free'd.
475  */
476 static int
477 ng_mppc_compress(node_p node, struct mbuf **datap)
478 {
479         const priv_p priv = NG_NODE_PRIVATE(node);
480         struct ng_mppc_dir *const d = &priv->xmit;
481         u_int16_t header;
482         struct mbuf *m = *datap;
483
484         /* We must own the mbuf chain exclusively to modify it. */
485         m = m_unshare(m, M_NOWAIT);
486         if (m == NULL)
487                 return (ENOMEM);
488
489         /* Initialize */
490         header = d->cc;
491
492         /* Always set the flushed bit in stateless mode */
493         if (d->flushed || ((d->cfg.bits & MPPE_STATELESS) != 0)) {
494                 header |= MPPC_FLAG_FLUSHED;
495                 d->flushed = 0;
496         }
497
498         /* Compress packet (if compression enabled) */
499 #ifdef NETGRAPH_MPPC_COMPRESSION
500         if ((d->cfg.bits & MPPC_BIT) != 0) {
501                 u_short flags = MPPC_MANDATORY_COMPRESS_FLAGS;
502                 u_char *inbuf, *outbuf;
503                 int outlen, inlen, ina;
504                 u_char *source, *dest;
505                 u_long sourceCnt, destCnt;
506                 int rtn;
507
508                 /* Work with contiguous regions of memory. */
509                 inlen = m->m_pkthdr.len;
510                 if (m->m_next == NULL) {
511                         inbuf = mtod(m, u_char *);
512                         ina = 0;
513                 } else {
514                         inbuf = malloc(inlen, M_NETGRAPH_MPPC, M_NOWAIT);
515                         if (inbuf == NULL)
516                                 goto err1;
517                         m_copydata(m, 0, inlen, (caddr_t)inbuf);
518                         ina = 1;
519                 }
520
521                 outlen = MPPC_MAX_BLOWUP(inlen);
522                 outbuf = malloc(outlen, M_NETGRAPH_MPPC, M_NOWAIT);
523                 if (outbuf == NULL) {
524                         if (ina)
525                                 free(inbuf, M_NETGRAPH_MPPC);
526 err1:
527                         m_freem(m);
528                         MPPC_InitCompressionHistory(d->history);
529                         d->flushed = 1;
530                         return (ENOMEM);
531                 }
532
533                 /* Prepare to compress */
534                 source = inbuf;
535                 sourceCnt = inlen;
536                 dest = outbuf;
537                 destCnt = outlen;
538                 if ((d->cfg.bits & MPPE_STATELESS) == 0)
539                         flags |= MPPC_SAVE_HISTORY;
540
541                 /* Compress */
542                 rtn = MPPC_Compress(&source, &dest, &sourceCnt,
543                         &destCnt, d->history, flags, 0);
544
545                 /* Check return value */
546                 /* KASSERT(rtn != MPPC_INVALID, ("%s: invalid", __func__)); */
547                 if ((rtn & MPPC_EXPANDED) == 0
548                     && (rtn & MPPC_COMP_OK) == MPPC_COMP_OK) {
549                         outlen -= destCnt;     
550                         header |= MPPC_FLAG_COMPRESSED;
551                         if ((rtn & MPPC_RESTART_HISTORY) != 0)
552                                 header |= MPPC_FLAG_RESTART;  
553                                 
554                         /* Replace m by the compresed one. */
555                         m_copyback(m, 0, outlen, (caddr_t)outbuf);
556                         if (m->m_pkthdr.len < outlen) {
557                                 m_freem(m);
558                                 m = NULL;
559                         } else if (outlen < m->m_pkthdr.len)
560                                 m_adj(m, outlen - m->m_pkthdr.len);
561                 }
562                 d->flushed = (rtn & MPPC_EXPANDED) != 0
563                     || (flags & MPPC_SAVE_HISTORY) == 0;
564
565                 if (ina)
566                         free(inbuf, M_NETGRAPH_MPPC);
567                 free(outbuf, M_NETGRAPH_MPPC);
568
569                 /* Check mbuf chain reload result. */
570                 if (m == NULL) {
571                         if (!d->flushed) {
572                                 MPPC_InitCompressionHistory(d->history);
573                                 d->flushed = 1;
574                         }
575                         return (ENOMEM);
576                 }
577         }
578 #endif
579
580         /* Now encrypt packet (if encryption enabled) */
581 #ifdef NETGRAPH_MPPC_ENCRYPTION
582         if ((d->cfg.bits & MPPE_BITS) != 0) {
583                 struct mbuf *m1;
584
585                 /* Set header bits */
586                 header |= MPPC_FLAG_ENCRYPTED;
587
588                 /* Update key if it's time */
589                 if ((d->cfg.bits & MPPE_STATELESS) != 0
590                     || (d->cc & MPPE_UPDATE_MASK) == MPPE_UPDATE_FLAG) {
591                         ng_mppc_updatekey(d->cfg.bits,
592                             d->cfg.startkey, d->key, &d->rc4);
593                 } else if ((header & MPPC_FLAG_FLUSHED) != 0) {
594                         /* Need to reset key if we say we did 
595                            and ng_mppc_updatekey wasn't called to do it also. */
596                         rc4_init(&d->rc4, d->key, KEYLEN(d->cfg.bits));
597                 }
598
599                 /* Encrypt packet */
600                 m1 = m;
601                 while (m1) {
602                         rc4_crypt(&d->rc4, mtod(m1, u_char *),
603                             mtod(m1, u_char *), m1->m_len);
604                         m1 = m1->m_next;
605                 }
606         }
607 #endif
608
609         /* Update coherency count for next time (12 bit arithmetic) */
610         MPPC_CCOUNT_INC(d->cc);
611
612         /* Install header */
613         M_PREPEND(m, MPPC_HDRLEN, M_NOWAIT);
614         if (m != NULL)
615                 be16enc(mtod(m, void *), header);
616
617         *datap = m;
618         return (*datap == NULL ? ENOBUFS : 0);
619 }
620
621 /*
622  * Decompress/decrypt packet and put the result in a new mbuf at *resultp.
623  * The original mbuf is not free'd.
624  */
625 static int
626 ng_mppc_decompress(node_p node, struct mbuf **datap)
627 {
628         const priv_p priv = NG_NODE_PRIVATE(node);
629         struct ng_mppc_dir *const d = &priv->recv;
630         u_int16_t header, cc;
631         u_int numLost;
632         struct mbuf *m = *datap;
633
634         /* We must own the mbuf chain exclusively to modify it. */
635         m = m_unshare(m, M_NOWAIT);
636         if (m == NULL)
637                 return (ENOMEM);
638
639         /* Pull off header */
640         if (m->m_pkthdr.len < MPPC_HDRLEN) {
641                 m_freem(m);
642                 return (EINVAL);
643         }
644         header = be16dec(mtod(m, void *));
645         cc = (header & MPPC_CCOUNT_MASK);
646         m_adj(m, MPPC_HDRLEN);
647
648         /* Check for an unexpected jump in the sequence number */
649         numLost = ((cc - d->cc) & MPPC_CCOUNT_MASK);
650
651         /* If flushed bit set, we can always handle packet */
652         if ((header & MPPC_FLAG_FLUSHED) != 0) {
653 #ifdef NETGRAPH_MPPC_COMPRESSION
654                 if (d->history != NULL)
655                         MPPC_InitDecompressionHistory(d->history);
656 #endif
657 #ifdef NETGRAPH_MPPC_ENCRYPTION
658                 if ((d->cfg.bits & MPPE_BITS) != 0) {
659                         u_int rekey;
660
661                         /* How many times are we going to have to re-key? */
662                         rekey = ((d->cfg.bits & MPPE_STATELESS) != 0) ?
663                             numLost : (numLost / (MPPE_UPDATE_MASK + 1));
664                         if (rekey > mppe_max_rekey) {
665                             if (mppe_block_on_max_rekey) {
666                                 if (mppe_log_max_rekey) {
667                                     log(LOG_ERR, "%s: too many (%d) packets"
668                                         " dropped, disabling node %p!\n",
669                                         __func__, numLost, node);
670                                 }
671                                 priv->recv.cfg.enable = 0;
672                                 goto failed;
673                             } else {
674                                 if (mppe_log_max_rekey) {
675                                     log(LOG_ERR, "%s: %d packets"
676                                         " dropped, node %p\n",
677                                         __func__, numLost, node);
678                                 }
679                                 goto failed;
680                             }
681                         }
682
683                         /* Re-key as necessary to catch up to peer */
684                         while (d->cc != cc) {
685                                 if ((d->cfg.bits & MPPE_STATELESS) != 0
686                                     || (d->cc & MPPE_UPDATE_MASK)
687                                       == MPPE_UPDATE_FLAG) {
688                                         ng_mppc_updatekey(d->cfg.bits,
689                                             d->cfg.startkey, d->key, &d->rc4);
690                                 }
691                                 MPPC_CCOUNT_INC(d->cc);
692                         }
693
694                         /* Reset key (except in stateless mode, see below) */
695                         if ((d->cfg.bits & MPPE_STATELESS) == 0)
696                                 rc4_init(&d->rc4, d->key, KEYLEN(d->cfg.bits));
697                 }
698 #endif
699                 d->cc = cc;             /* skip over lost seq numbers */
700                 numLost = 0;            /* act like no packets were lost */
701         }
702
703         /* Can't decode non-sequential packets without a flushed bit */
704         if (numLost != 0)
705                 goto failed;
706
707         /* Decrypt packet */
708         if ((header & MPPC_FLAG_ENCRYPTED) != 0) {
709 #ifdef NETGRAPH_MPPC_ENCRYPTION
710                 struct mbuf *m1;
711 #endif
712
713                 /* Are we not expecting encryption? */
714                 if ((d->cfg.bits & MPPE_BITS) == 0) {
715                         log(LOG_ERR, "%s: rec'd unexpectedly %s packet",
716                                 __func__, "encrypted");
717                         goto failed;
718                 }
719
720 #ifdef NETGRAPH_MPPC_ENCRYPTION
721                 /* Update key if it's time (always in stateless mode) */
722                 if ((d->cfg.bits & MPPE_STATELESS) != 0
723                     || (d->cc & MPPE_UPDATE_MASK) == MPPE_UPDATE_FLAG) {
724                         ng_mppc_updatekey(d->cfg.bits,
725                             d->cfg.startkey, d->key, &d->rc4);
726                 }
727
728                 /* Decrypt packet */
729                 m1 = m;
730                 while (m1 != NULL) {
731                         rc4_crypt(&d->rc4, mtod(m1, u_char *),
732                             mtod(m1, u_char *), m1->m_len);
733                         m1 = m1->m_next;
734                 }
735 #endif
736         } else {
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