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