]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/netgraph/ng_mppc.c
awk: Merge upstream 2nd Edition Awk Book
[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  */
42
43 /*
44  * Microsoft PPP compression (MPPC) and encryption (MPPE) netgraph node type.
45  *
46  * You must define one or both of the NETGRAPH_MPPC_COMPRESSION and/or
47  * NETGRAPH_MPPC_ENCRYPTION options for this node type to be useful.
48  */
49
50 #include <sys/param.h>
51 #include <sys/systm.h>
52 #include <sys/kernel.h>
53 #include <sys/mbuf.h>
54 #include <sys/malloc.h>
55 #include <sys/endian.h>
56 #include <sys/errno.h>
57 #include <sys/sysctl.h>
58 #include <sys/syslog.h>
59
60 #include <netgraph/ng_message.h>
61 #include <netgraph/netgraph.h>
62 #include <netgraph/ng_mppc.h>
63
64 #include "opt_netgraph.h"
65
66 #if !defined(NETGRAPH_MPPC_COMPRESSION) && !defined(NETGRAPH_MPPC_ENCRYPTION)
67 #ifdef KLD_MODULE
68 #define NETGRAPH_MPPC_COMPRESSION
69 #define NETGRAPH_MPPC_ENCRYPTION
70 #else
71 /* This case is indicative of an error in sys/conf files */
72 #error Need either NETGRAPH_MPPC_COMPRESSION or NETGRAPH_MPPC_ENCRYPTION
73 #endif
74 #endif
75
76 #ifdef NG_SEPARATE_MALLOC
77 static MALLOC_DEFINE(M_NETGRAPH_MPPC, "netgraph_mppc", "netgraph mppc node");
78 #else
79 #define M_NETGRAPH_MPPC M_NETGRAPH
80 #endif
81
82 #ifdef NETGRAPH_MPPC_COMPRESSION
83 #include <net/mppc.h>
84 #endif
85 #ifdef NETGRAPH_MPPC_ENCRYPTION
86 #include <crypto/rc4/rc4.h>
87 #endif
88 #include <crypto/sha1.h>
89
90 /* Decompression blowup */
91 #define MPPC_DECOMP_BUFSIZE     8092            /* allocate buffer this big */
92 #define MPPC_DECOMP_SAFETY      100             /*   plus this much margin */
93
94 /* MPPC/MPPE header length */
95 #define MPPC_HDRLEN             2
96
97 /* Key length */
98 #define KEYLEN(b)               (((b) & MPPE_128) ? 16 : 8)
99
100 /*
101  * When packets are lost with MPPE, we may have to re-key arbitrarily
102  * many times to 'catch up' to the new jumped-ahead sequence number.
103  * Since this can be expensive, we pose a limit on how many re-keyings
104  * we will do at one time to avoid a possible D.O.S. vulnerability.
105  * This should instead be a configurable parameter.
106  */
107 #define MPPE_MAX_REKEY          1000
108
109 SYSCTL_NODE(_net_graph, OID_AUTO, mppe, CTLFLAG_RW | CTLFLAG_MPSAFE, 0,
110     "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                 /* Are we expecting encryption? */
737                 if ((d->cfg.bits & MPPE_BITS) != 0) {
738                         log(LOG_ERR, "%s: rec'd unexpectedly %s packet",
739                                 __func__, "unencrypted");
740                         goto failed;
741                 }
742         }
743
744         /* Update coherency count for next time (12 bit arithmetic) */
745         MPPC_CCOUNT_INC(d->cc);
746
747         /* Check for unexpected compressed packet */
748         if ((header & MPPC_FLAG_COMPRESSED) != 0
749             && (d->cfg.bits & MPPC_BIT) == 0) {
750                 log(LOG_ERR, "%s: rec'd unexpectedly %s packet",
751                         __func__, "compressed");
752 failed:
753                 m_freem(m);
754                 return (EINVAL);
755         }
756
757 #ifdef NETGRAPH_MPPC_COMPRESSION
758         /* Decompress packet */
759         if ((header & MPPC_FLAG_COMPRESSED) != 0) {
760                 int flags = MPPC_MANDATORY_DECOMPRESS_FLAGS;
761                 u_char *inbuf, *outbuf;
762                 int inlen, outlen, ina;
763                 u_char *source, *dest;
764                 u_long sourceCnt, destCnt;
765                 int rtn;
766
767                 /* Copy payload into a contiguous region of memory. */
768                 inlen = m->m_pkthdr.len;
769                 if (m->m_next == NULL) {
770                         inbuf = mtod(m, u_char *);
771                         ina = 0;
772                 } else {
773                         inbuf = malloc(inlen, M_NETGRAPH_MPPC, M_NOWAIT);
774                         if (inbuf == NULL) {
775                                 m_freem(m);
776                                 return (ENOMEM);
777                         }
778                         m_copydata(m, 0, inlen, (caddr_t)inbuf);
779                         ina = 1;
780                 }
781
782                 /* Allocate a buffer for decompressed data */
783                 outbuf = malloc(MPPC_DECOMP_BUFSIZE + MPPC_DECOMP_SAFETY,
784                     M_NETGRAPH_MPPC, M_NOWAIT);
785                 if (outbuf == NULL) {
786                         m_freem(m);
787                         if (ina)
788                                 free(inbuf, M_NETGRAPH_MPPC);
789                         return (ENOMEM);
790                 }
791                 outlen = MPPC_DECOMP_BUFSIZE;
792
793                 /* Prepare to decompress */
794                 source = inbuf;
795                 sourceCnt = inlen;
796                 dest = outbuf;
797                 destCnt = outlen;
798                 if ((header & MPPC_FLAG_RESTART) != 0)
799                         flags |= MPPC_RESTART_HISTORY;
800
801                 /* Decompress */
802                 rtn = MPPC_Decompress(&source, &dest,
803                         &sourceCnt, &destCnt, d->history, flags);
804
805                 /* Check return value */
806                 /* KASSERT(rtn != MPPC_INVALID, ("%s: invalid", __func__)); */
807                 if ((rtn & MPPC_DEST_EXHAUSTED) != 0
808                     || (rtn & MPPC_DECOMP_OK) != MPPC_DECOMP_OK) {
809                         log(LOG_ERR, "%s: decomp returned 0x%x",
810                             __func__, rtn);
811                         if (ina)
812                                 free(inbuf, M_NETGRAPH_MPPC);
813                         free(outbuf, M_NETGRAPH_MPPC);
814                         goto failed;
815                 }
816
817                 /* Replace compressed data with decompressed data */
818                 if (ina)
819                         free(inbuf, M_NETGRAPH_MPPC);
820                 outlen -= destCnt;
821
822                 m_copyback(m, 0, outlen, (caddr_t)outbuf);
823                 if (m->m_pkthdr.len < outlen) {
824                         m_freem(m);
825                         m = NULL;
826                 } else if (outlen < m->m_pkthdr.len)
827                         m_adj(m, outlen - m->m_pkthdr.len);
828                 free(outbuf, M_NETGRAPH_MPPC);
829         }
830 #endif
831
832         /* Return result in an mbuf */
833         *datap = m;
834         return (*datap == NULL ? ENOBUFS : 0);
835 }
836
837 /*
838  * The peer has sent us a CCP ResetRequest, so reset our transmit state.
839  */
840 static void
841 ng_mppc_reset_req(node_p node)
842 {   
843         const priv_p priv = NG_NODE_PRIVATE(node);
844         struct ng_mppc_dir *const d = &priv->xmit;
845
846 #ifdef NETGRAPH_MPPC_COMPRESSION
847         if (d->history != NULL)
848                 MPPC_InitCompressionHistory(d->history);
849 #endif
850 #ifdef NETGRAPH_MPPC_ENCRYPTION
851         if ((d->cfg.bits & MPPE_STATELESS) == 0)
852                 rc4_init(&d->rc4, d->key, KEYLEN(d->cfg.bits));
853 #endif
854         d->flushed = 1;
855 }   
856
857 #ifdef NETGRAPH_MPPC_ENCRYPTION
858 /*
859  * Generate a new encryption key
860  */
861 static void
862 ng_mppc_getkey(const u_char *h, u_char *h2, int len)
863 {
864         static const u_char pad1[40] =
865             { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
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         static const u_char pad2[40] =
870             { 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2,
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         u_char hash[20];
875         SHA1_CTX c;
876
877         SHA1Init(&c);
878         SHA1Update(&c, h, len);
879         SHA1Update(&c, pad1, sizeof(pad1));
880         SHA1Update(&c, h2, len);
881         SHA1Update(&c, pad2, sizeof(pad2));
882         SHA1Final(hash, &c);
883         bcopy(hash, h2, len);
884 }
885
886 /*
887  * Update the encryption key
888  */
889 static void
890 ng_mppc_updatekey(u_int32_t bits,
891         u_char *key0, u_char *key, struct rc4_state *rc4)
892
893         const int keylen = KEYLEN(bits);
894
895         ng_mppc_getkey(key0, key, keylen);
896         rc4_init(rc4, key, keylen);
897         rc4_crypt(rc4, key, key, keylen);
898         if ((bits & MPPE_40) != 0)
899                 bcopy(&ng_mppe_weakenkey, key, 3);
900         else if ((bits & MPPE_56) != 0)
901                 bcopy(&ng_mppe_weakenkey, key, 1);
902         rc4_init(rc4, key, keylen);
903 }
904 #endif