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