]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - sys/netgraph/ng_mppc.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.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 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 /* 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 }
408
409 /*
410  * Destroy node
411  */
412 static int
413 ng_mppc_shutdown(node_p node)
414 {
415         const priv_p priv = NG_NODE_PRIVATE(node);
416
417         /* Take down netgraph node */
418 #ifdef NETGRAPH_MPPC_COMPRESSION
419         if (priv->xmit.history != NULL)
420                 free(priv->xmit.history, M_NETGRAPH_MPPC);
421         if (priv->recv.history != NULL)
422                 free(priv->recv.history, M_NETGRAPH_MPPC);
423 #endif
424         bzero(priv, sizeof(*priv));
425         free(priv, M_NETGRAPH_MPPC);
426         NG_NODE_SET_PRIVATE(node, NULL);
427         NG_NODE_UNREF(node);            /* let the node escape */
428         return (0);
429 }
430
431 /*
432  * Hook disconnection
433  */
434 static int
435 ng_mppc_disconnect(hook_p hook)
436 {
437         const node_p node = NG_HOOK_NODE(hook);
438         const priv_p priv = NG_NODE_PRIVATE(node);
439
440         /* Zero out hook pointer */
441         if (hook == priv->xmit.hook)
442                 priv->xmit.hook = NULL;
443         if (hook == priv->recv.hook)
444                 priv->recv.hook = NULL;
445
446         /* Go away if no longer connected */
447         if ((NG_NODE_NUMHOOKS(node) == 0)
448         && NG_NODE_IS_VALID(node))
449                 ng_rmnode_self(node);
450         return (0);
451 }
452
453 /************************************************************************
454                         HELPER STUFF
455  ************************************************************************/
456
457 /*
458  * Compress/encrypt a packet and put the result in a new mbuf at *resultp.
459  * The original mbuf is not free'd.
460  */
461 static int
462 ng_mppc_compress(node_p node, struct mbuf **datap)
463 {
464         const priv_p priv = NG_NODE_PRIVATE(node);
465         struct ng_mppc_dir *const d = &priv->xmit;
466         u_int16_t header;
467         struct mbuf *m = *datap;
468
469         /* We must own the mbuf chain exclusively to modify it. */
470         m = m_unshare(m, M_NOWAIT);
471         if (m == NULL)
472                 return (ENOMEM);
473
474         /* Initialize */
475         header = d->cc;
476
477         /* Always set the flushed bit in stateless mode */
478         if (d->flushed || ((d->cfg.bits & MPPE_STATELESS) != 0)) {
479                 header |= MPPC_FLAG_FLUSHED;
480                 d->flushed = 0;
481         }
482
483         /* Compress packet (if compression enabled) */
484 #ifdef NETGRAPH_MPPC_COMPRESSION
485         if ((d->cfg.bits & MPPC_BIT) != 0) {
486                 u_short flags = MPPC_MANDATORY_COMPRESS_FLAGS;
487                 u_char *inbuf, *outbuf;
488                 int outlen, inlen, ina;
489                 u_char *source, *dest;
490                 u_long sourceCnt, destCnt;
491                 int rtn;
492
493                 /* Work with contiguous regions of memory. */
494                 inlen = m->m_pkthdr.len;
495                 if (m->m_next == NULL) {
496                         inbuf = mtod(m, u_char *);
497                         ina = 0;
498                 } else {
499                         inbuf = malloc(inlen, M_NETGRAPH_MPPC, M_NOWAIT);
500                         if (inbuf == NULL)
501                                 goto err1;
502                         m_copydata(m, 0, inlen, (caddr_t)inbuf);
503                         ina = 1;
504                 }
505
506                 outlen = MPPC_MAX_BLOWUP(inlen);
507                 outbuf = malloc(outlen, M_NETGRAPH_MPPC, M_NOWAIT);
508                 if (outbuf == NULL) {
509                         if (ina)
510                                 free(inbuf, M_NETGRAPH_MPPC);
511 err1:
512                         m_freem(m);
513                         MPPC_InitCompressionHistory(d->history);
514                         d->flushed = 1;
515                         return (ENOMEM);
516                 }
517
518                 /* Prepare to compress */
519                 source = inbuf;
520                 sourceCnt = inlen;
521                 dest = outbuf;
522                 destCnt = outlen;
523                 if ((d->cfg.bits & MPPE_STATELESS) == 0)
524                         flags |= MPPC_SAVE_HISTORY;
525
526                 /* Compress */
527                 rtn = MPPC_Compress(&source, &dest, &sourceCnt,
528                         &destCnt, d->history, flags, 0);
529
530                 /* Check return value */
531                 KASSERT(rtn != MPPC_INVALID, ("%s: invalid", __func__));
532                 if ((rtn & MPPC_EXPANDED) == 0
533                     && (rtn & MPPC_COMP_OK) == MPPC_COMP_OK) {
534                         outlen -= destCnt;     
535                         header |= MPPC_FLAG_COMPRESSED;
536                         if ((rtn & MPPC_RESTART_HISTORY) != 0)
537                                 header |= MPPC_FLAG_RESTART;  
538                                 
539                         /* Replace m by the compresed one. */
540                         m_copyback(m, 0, outlen, (caddr_t)outbuf);
541                         if (m->m_pkthdr.len < outlen) {
542                                 m_freem(m);
543                                 m = NULL;
544                         } else if (outlen < m->m_pkthdr.len)
545                                 m_adj(m, outlen - m->m_pkthdr.len);
546                 }
547                 d->flushed = (rtn & MPPC_EXPANDED) != 0
548                     || (flags & MPPC_SAVE_HISTORY) == 0;
549
550                 if (ina)
551                         free(inbuf, M_NETGRAPH_MPPC);
552                 free(outbuf, M_NETGRAPH_MPPC);
553
554                 /* Check mbuf chain reload result. */
555                 if (m == NULL) {
556                         if (!d->flushed) {
557                                 MPPC_InitCompressionHistory(d->history);
558                                 d->flushed = 1;
559                         }
560                         return (ENOMEM);
561                 }
562         }
563 #endif
564
565         /* Now encrypt packet (if encryption enabled) */
566 #ifdef NETGRAPH_MPPC_ENCRYPTION
567         if ((d->cfg.bits & MPPE_BITS) != 0) {
568                 struct mbuf *m1;
569
570                 /* Set header bits */
571                 header |= MPPC_FLAG_ENCRYPTED;
572
573                 /* Update key if it's time */
574                 if ((d->cfg.bits & MPPE_STATELESS) != 0
575                     || (d->cc & MPPE_UPDATE_MASK) == MPPE_UPDATE_FLAG) {
576                         ng_mppc_updatekey(d->cfg.bits,
577                             d->cfg.startkey, d->key, &d->rc4);
578                 } else if ((header & MPPC_FLAG_FLUSHED) != 0) {
579                         /* Need to reset key if we say we did 
580                            and ng_mppc_updatekey wasn't called to do it also. */
581                         rc4_init(&d->rc4, d->key, KEYLEN(d->cfg.bits));
582                 }
583
584                 /* Encrypt packet */
585                 m1 = m;
586                 while (m1) {
587                         rc4_crypt(&d->rc4, mtod(m1, u_char *),
588                             mtod(m1, u_char *), m1->m_len);
589                         m1 = m1->m_next;
590                 }
591         }
592 #endif
593
594         /* Update coherency count for next time (12 bit arithmetic) */
595         MPPC_CCOUNT_INC(d->cc);
596
597         /* Install header */
598         M_PREPEND(m, MPPC_HDRLEN, M_NOWAIT);
599         if (m != NULL)
600                 be16enc(mtod(m, void *), header);
601
602         *datap = m;
603         return (*datap == NULL ? ENOBUFS : 0);
604 }
605
606 /*
607  * Decompress/decrypt packet and put the result in a new mbuf at *resultp.
608  * The original mbuf is not free'd.
609  */
610 static int
611 ng_mppc_decompress(node_p node, struct mbuf **datap)
612 {
613         const priv_p priv = NG_NODE_PRIVATE(node);
614         struct ng_mppc_dir *const d = &priv->recv;
615         u_int16_t header, cc;
616         u_int numLost;
617         struct mbuf *m = *datap;
618
619         /* We must own the mbuf chain exclusively to modify it. */
620         m = m_unshare(m, M_NOWAIT);
621         if (m == NULL)
622                 return (ENOMEM);
623
624         /* Pull off header */
625         if (m->m_pkthdr.len < MPPC_HDRLEN) {
626                 m_freem(m);
627                 return (EINVAL);
628         }
629         header = be16dec(mtod(m, void *));
630         cc = (header & MPPC_CCOUNT_MASK);
631         m_adj(m, MPPC_HDRLEN);
632
633         /* Check for an unexpected jump in the sequence number */
634         numLost = ((cc - d->cc) & MPPC_CCOUNT_MASK);
635
636         /* If flushed bit set, we can always handle packet */
637         if ((header & MPPC_FLAG_FLUSHED) != 0) {
638 #ifdef NETGRAPH_MPPC_COMPRESSION
639                 if (d->history != NULL)
640                         MPPC_InitDecompressionHistory(d->history);
641 #endif
642 #ifdef NETGRAPH_MPPC_ENCRYPTION
643                 if ((d->cfg.bits & MPPE_BITS) != 0) {
644                         u_int rekey;
645
646                         /* How many times are we going to have to re-key? */
647                         rekey = ((d->cfg.bits & MPPE_STATELESS) != 0) ?
648                             numLost : (numLost / (MPPE_UPDATE_MASK + 1));
649                         if (rekey > MPPE_MAX_REKEY) {
650                                 log(LOG_ERR, "%s: too many (%d) packets"
651                                     " dropped, disabling node %p!",
652                                     __func__, numLost, node);
653                                 priv->recv.cfg.enable = 0;
654                                 goto failed;
655                         }
656
657                         /* Re-key as necessary to catch up to peer */
658                         while (d->cc != cc) {
659                                 if ((d->cfg.bits & MPPE_STATELESS) != 0
660                                     || (d->cc & MPPE_UPDATE_MASK)
661                                       == MPPE_UPDATE_FLAG) {
662                                         ng_mppc_updatekey(d->cfg.bits,
663                                             d->cfg.startkey, d->key, &d->rc4);
664                                 }
665                                 MPPC_CCOUNT_INC(d->cc);
666                         }
667
668                         /* Reset key (except in stateless mode, see below) */
669                         if ((d->cfg.bits & MPPE_STATELESS) == 0)
670                                 rc4_init(&d->rc4, d->key, KEYLEN(d->cfg.bits));
671                 }
672 #endif
673                 d->cc = cc;             /* skip over lost seq numbers */
674                 numLost = 0;            /* act like no packets were lost */
675         }
676
677         /* Can't decode non-sequential packets without a flushed bit */
678         if (numLost != 0)
679                 goto failed;
680
681         /* Decrypt packet */
682         if ((header & MPPC_FLAG_ENCRYPTED) != 0) {
683 #ifdef NETGRAPH_MPPC_ENCRYPTION
684                 struct mbuf *m1;
685 #endif
686
687                 /* Are we not expecting encryption? */
688                 if ((d->cfg.bits & MPPE_BITS) == 0) {
689                         log(LOG_ERR, "%s: rec'd unexpectedly %s packet",
690                                 __func__, "encrypted");
691                         goto failed;
692                 }
693
694 #ifdef NETGRAPH_MPPC_ENCRYPTION
695                 /* Update key if it's time (always in stateless mode) */
696                 if ((d->cfg.bits & MPPE_STATELESS) != 0
697                     || (d->cc & MPPE_UPDATE_MASK) == MPPE_UPDATE_FLAG) {
698                         ng_mppc_updatekey(d->cfg.bits,
699                             d->cfg.startkey, d->key, &d->rc4);
700                 }
701
702                 /* Decrypt packet */
703                 m1 = m;
704                 while (m1 != NULL) {
705                         rc4_crypt(&d->rc4, mtod(m1, u_char *),
706                             mtod(m1, u_char *), m1->m_len);
707                         m1 = m1->m_next;
708                 }
709 #endif
710         } else {
711
712                 /* Are we expecting encryption? */
713                 if ((d->cfg.bits & MPPE_BITS) != 0) {
714                         log(LOG_ERR, "%s: rec'd unexpectedly %s packet",
715                                 __func__, "unencrypted");
716                         goto failed;
717                 }
718         }
719
720         /* Update coherency count for next time (12 bit arithmetic) */
721         MPPC_CCOUNT_INC(d->cc);
722
723         /* Check for unexpected compressed packet */
724         if ((header & MPPC_FLAG_COMPRESSED) != 0
725             && (d->cfg.bits & MPPC_BIT) == 0) {
726                 log(LOG_ERR, "%s: rec'd unexpectedly %s packet",
727                         __func__, "compressed");
728 failed:
729                 m_freem(m);
730                 return (EINVAL);
731         }
732
733 #ifdef NETGRAPH_MPPC_COMPRESSION
734         /* Decompress packet */
735         if ((header & MPPC_FLAG_COMPRESSED) != 0) {
736                 int flags = MPPC_MANDATORY_DECOMPRESS_FLAGS;
737                 u_char *inbuf, *outbuf;
738                 int inlen, outlen, ina;
739                 u_char *source, *dest;
740                 u_long sourceCnt, destCnt;
741                 int rtn;
742
743                 /* Copy payload into a contiguous region of memory. */
744                 inlen = m->m_pkthdr.len;
745                 if (m->m_next == NULL) {
746                         inbuf = mtod(m, u_char *);
747                         ina = 0;
748                 } else {
749                         inbuf = malloc(inlen, M_NETGRAPH_MPPC, M_NOWAIT);
750                         if (inbuf == NULL) {
751                                 m_freem(m);
752                                 return (ENOMEM);
753                         }
754                         m_copydata(m, 0, inlen, (caddr_t)inbuf);
755                         ina = 1;
756                 }
757
758                 /* Allocate a buffer for decompressed data */
759                 outbuf = malloc(MPPC_DECOMP_BUFSIZE + MPPC_DECOMP_SAFETY,
760                     M_NETGRAPH_MPPC, M_NOWAIT);
761                 if (outbuf == NULL) {
762                         m_freem(m);
763                         if (ina)
764                                 free(inbuf, M_NETGRAPH_MPPC);
765                         return (ENOMEM);
766                 }
767                 outlen = MPPC_DECOMP_BUFSIZE;
768
769                 /* Prepare to decompress */
770                 source = inbuf;
771                 sourceCnt = inlen;
772                 dest = outbuf;
773                 destCnt = outlen;
774                 if ((header & MPPC_FLAG_RESTART) != 0)
775                         flags |= MPPC_RESTART_HISTORY;
776
777                 /* Decompress */
778                 rtn = MPPC_Decompress(&source, &dest,
779                         &sourceCnt, &destCnt, d->history, flags);
780
781                 /* Check return value */
782                 KASSERT(rtn != MPPC_INVALID, ("%s: invalid", __func__));
783                 if ((rtn & MPPC_DEST_EXHAUSTED) != 0
784                     || (rtn & MPPC_DECOMP_OK) != MPPC_DECOMP_OK) {
785                         log(LOG_ERR, "%s: decomp returned 0x%x",
786                             __func__, rtn);
787                         if (ina)
788                                 free(inbuf, M_NETGRAPH_MPPC);
789                         free(outbuf, M_NETGRAPH_MPPC);
790                         goto failed;
791                 }
792
793                 /* Replace compressed data with decompressed data */
794                 if (ina)
795                         free(inbuf, M_NETGRAPH_MPPC);
796                 outlen -= destCnt;
797         
798                 m_copyback(m, 0, outlen, (caddr_t)outbuf);
799                 if (m->m_pkthdr.len < outlen) {
800                         m_freem(m);
801                         m = NULL;
802                 } else if (outlen < m->m_pkthdr.len)
803                         m_adj(m, outlen - m->m_pkthdr.len);
804                 free(outbuf, M_NETGRAPH_MPPC);
805         }
806 #endif
807
808         /* Return result in an mbuf */
809         *datap = m;
810         return (*datap == NULL ? ENOBUFS : 0);
811 }
812
813 /*
814  * The peer has sent us a CCP ResetRequest, so reset our transmit state.
815  */
816 static void
817 ng_mppc_reset_req(node_p node)
818 {   
819         const priv_p priv = NG_NODE_PRIVATE(node);
820         struct ng_mppc_dir *const d = &priv->xmit;
821
822 #ifdef NETGRAPH_MPPC_COMPRESSION
823         if (d->history != NULL)
824                 MPPC_InitCompressionHistory(d->history);
825 #endif
826 #ifdef NETGRAPH_MPPC_ENCRYPTION
827         if ((d->cfg.bits & MPPE_STATELESS) == 0)
828                 rc4_init(&d->rc4, d->key, KEYLEN(d->cfg.bits));
829 #endif
830         d->flushed = 1;
831 }   
832
833 #ifdef NETGRAPH_MPPC_ENCRYPTION
834 /*
835  * Generate a new encryption key
836  */
837 static void
838 ng_mppc_getkey(const u_char *h, u_char *h2, int len)
839 {
840         static const u_char pad1[40] =
841             { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
842               0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
843               0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
844               0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
845         static const u_char pad2[40] =
846             { 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2,
847               0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2,
848               0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2,
849               0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2 };
850         u_char hash[20];
851         SHA1_CTX c;
852
853         SHA1Init(&c);
854         SHA1Update(&c, h, len);
855         SHA1Update(&c, pad1, sizeof(pad1));
856         SHA1Update(&c, h2, len);
857         SHA1Update(&c, pad2, sizeof(pad2));
858         SHA1Final(hash, &c);
859         bcopy(hash, h2, len);
860 }
861
862 /*
863  * Update the encryption key
864  */
865 static void
866 ng_mppc_updatekey(u_int32_t bits,
867         u_char *key0, u_char *key, struct rc4_state *rc4)
868
869         const int keylen = KEYLEN(bits);
870
871         ng_mppc_getkey(key0, key, keylen);
872         rc4_init(rc4, key, keylen);
873         rc4_crypt(rc4, key, key, keylen);
874         if ((bits & MPPE_40) != 0)
875                 bcopy(&ng_mppe_weakenkey, key, 3);
876         else if ((bits & MPPE_56) != 0)
877                 bcopy(&ng_mppe_weakenkey, key, 1);
878         rc4_init(rc4, key, keylen);
879 }
880 #endif
881