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