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