| Commit | Line | Data |
|---|---|---|
| b06ebda0 MD |
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: src/sys/netgraph/ng_mppc.c,v 1.31 2007/05/18 15:28:01 mav Exp $ | |
| 5a975a3d | 42 | * $DragonFly: src/sys/netgraph7/ng_mppc.c,v 1.2 2008/06/26 23:05:35 dillon Exp $ |
| b06ebda0 MD |
43 | */ |
| 44 | ||
| 45 | /* | |
| 46 | * Microsoft PPP compression (MPPC) and encryption (MPPE) netgraph node type. | |
| 47 | * | |
| 48 | * You must define one or both of the NETGRAPH_MPPC_COMPRESSION and/or | |
| 49 | * NETGRAPH_MPPC_ENCRYPTION options for this node type to be useful. | |
| 50 | */ | |
| 51 | ||
| 52 | #include <sys/param.h> | |
| 53 | #include <sys/systm.h> | |
| 54 | #include <sys/kernel.h> | |
| 55 | #include <sys/mbuf.h> | |
| 56 | #include <sys/malloc.h> | |
| 57 | #include <sys/errno.h> | |
| 58 | #include <sys/syslog.h> | |
| 59 | ||
| 5a975a3d MD |
60 | #include "ng_message.h" |
| 61 | #include "netgraph.h" | |
| 62 | #include "ng_mppc.h" | |
| b06ebda0 MD |
63 | |
| 64 | #include "opt_netgraph.h" | |
| 65 | ||
| 5a975a3d | 66 | #if !defined(NETGRAPH7_MPPC_COMPRESSION) && !defined(NETGRAPH7_MPPC_ENCRYPTION) |
| b06ebda0 | 67 | #ifdef KLD_MODULE |
| 5a975a3d MD |
68 | /* XXX NETGRAPH7_MPPC_COMPRESSION isn't functional yet */ |
| 69 | #define NETGRAPH7_MPPC_ENCRYPTION | |
| b06ebda0 MD |
70 | #else |
| 71 | /* This case is indicative of an error in sys/conf files */ | |
| 5a975a3d | 72 | #error Need either NETGRAPH7_MPPC_COMPRESSION or NETGRAPH7_MPPC_ENCRYPTION |
| b06ebda0 MD |
73 | #endif |
| 74 | #endif | |
| 75 | ||
| 76 | #ifdef NG_SEPARATE_MALLOC | |
| 77 | MALLOC_DEFINE(M_NETGRAPH_MPPC, "netgraph_mppc", "netgraph mppc node "); | |
| 78 | #else | |
| 79 | #define M_NETGRAPH_MPPC M_NETGRAPH | |
| 80 | #endif | |
| 81 | ||
| 5a975a3d | 82 | #ifdef NETGRAPH7_MPPC_COMPRESSION |
| b06ebda0 MD |
83 | /* XXX this file doesn't exist yet, but hopefully someday it will... */ |
| 84 | #include <net/mppc.h> | |
| 85 | #endif | |
| 5a975a3d | 86 | #ifdef NETGRAPH7_MPPC_ENCRYPTION |
| b06ebda0 MD |
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) */ | |
| 5a975a3d | 131 | #ifdef NETGRAPH7_MPPC_COMPRESSION |
| b06ebda0 MD |
132 | u_char *history; /* compression history */ |
| 133 | #endif | |
| 5a975a3d | 134 | #ifdef NETGRAPH7_MPPC_ENCRYPTION |
| b06ebda0 MD |
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); | |
| 5a975a3d | 161 | #ifdef NETGRAPH7_MPPC_ENCRYPTION |
| b06ebda0 MD |
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 | ||
| 5a975a3d | 181 | #ifdef NETGRAPH7_MPPC_ENCRYPTION |
| b06ebda0 MD |
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 */ | |
| fc025606 SW |
204 | priv = kmalloc(sizeof(*priv), M_NETGRAPH_MPPC, |
| 205 | M_WAITOK | M_NULLOK | M_ZERO); | |
| b06ebda0 MD |
206 | if (priv == NULL) |
| 207 | return (ENOMEM); | |
| 208 | ||
| 209 | NG_NODE_SET_PRIVATE(node, priv); | |
| 210 | ||
| 211 | /* This node is not thread safe. */ | |
| 212 | NG_NODE_FORCE_WRITER(node); | |
| 213 | ||
| 214 | /* Done */ | |
| 215 | return (0); | |
| 216 | } | |
| 217 | ||
| 218 | /* | |
| 219 | * Give our OK for a hook to be added | |
| 220 | */ | |
| 221 | static int | |
| 222 | ng_mppc_newhook(node_p node, hook_p hook, const char *name) | |
| 223 | { | |
| 224 | const priv_p priv = NG_NODE_PRIVATE(node); | |
| 225 | hook_p *hookPtr; | |
| 226 | ||
| 227 | /* Check hook name */ | |
| 228 | if (strcmp(name, NG_MPPC_HOOK_COMP) == 0) | |
| 229 | hookPtr = &priv->xmit.hook; | |
| 230 | else if (strcmp(name, NG_MPPC_HOOK_DECOMP) == 0) | |
| 231 | hookPtr = &priv->recv.hook; | |
| 232 | else | |
| 233 | return (EINVAL); | |
| 234 | ||
| 235 | /* See if already connected */ | |
| 236 | if (*hookPtr != NULL) | |
| 237 | return (EISCONN); | |
| 238 | ||
| 239 | /* OK */ | |
| 240 | *hookPtr = hook; | |
| 241 | return (0); | |
| 242 | } | |
| 243 | ||
| 244 | /* | |
| 245 | * Receive a control message | |
| 246 | */ | |
| 247 | static int | |
| 248 | ng_mppc_rcvmsg(node_p node, item_p item, hook_p lasthook) | |
| 249 | { | |
| 250 | const priv_p priv = NG_NODE_PRIVATE(node); | |
| 251 | struct ng_mesg *resp = NULL; | |
| 252 | int error = 0; | |
| 253 | struct ng_mesg *msg; | |
| 254 | ||
| 255 | NGI_GET_MSG(item, msg); | |
| 256 | switch (msg->header.typecookie) { | |
| 257 | case NGM_MPPC_COOKIE: | |
| 258 | switch (msg->header.cmd) { | |
| 259 | case NGM_MPPC_CONFIG_COMP: | |
| 260 | case NGM_MPPC_CONFIG_DECOMP: | |
| 261 | { | |
| 262 | struct ng_mppc_config *const cfg | |
| 263 | = (struct ng_mppc_config *)msg->data; | |
| 264 | const int isComp = | |
| 265 | msg->header.cmd == NGM_MPPC_CONFIG_COMP; | |
| 266 | struct ng_mppc_dir *const d = isComp ? | |
| 267 | &priv->xmit : &priv->recv; | |
| 268 | ||
| 269 | /* Check configuration */ | |
| 270 | if (msg->header.arglen != sizeof(*cfg)) | |
| 271 | ERROUT(EINVAL); | |
| 272 | if (cfg->enable) { | |
| 273 | if ((cfg->bits & ~MPPC_VALID_BITS) != 0) | |
| 274 | ERROUT(EINVAL); | |
| 5a975a3d | 275 | #ifndef NETGRAPH7_MPPC_COMPRESSION |
| b06ebda0 MD |
276 | if ((cfg->bits & MPPC_BIT) != 0) |
| 277 | ERROUT(EPROTONOSUPPORT); | |
| 278 | #endif | |
| 5a975a3d | 279 | #ifndef NETGRAPH7_MPPC_ENCRYPTION |
| b06ebda0 MD |
280 | if ((cfg->bits & MPPE_BITS) != 0) |
| 281 | ERROUT(EPROTONOSUPPORT); | |
| 282 | #endif | |
| 283 | } else | |
| 284 | cfg->bits = 0; | |
| 285 | ||
| 286 | /* Save return address so we can send reset-req's */ | |
| 287 | if (!isComp) | |
| 288 | priv->ctrlnode = NGI_RETADDR(item); | |
| 289 | ||
| 290 | /* Configuration is OK, reset to it */ | |
| 291 | d->cfg = *cfg; | |
| 292 | ||
| 5a975a3d | 293 | #ifdef NETGRAPH7_MPPC_COMPRESSION |
| b06ebda0 MD |
294 | /* Initialize state buffers for compression */ |
| 295 | if (d->history != NULL) { | |
| fc025606 | 296 | kfree(d->history, M_NETGRAPH_MPPC); |
| b06ebda0 MD |
297 | d->history = NULL; |
| 298 | } | |
| 299 | if ((cfg->bits & MPPC_BIT) != 0) { | |
| fc025606 SW |
300 | d->history = kmalloc(isComp ? MPPC_SizeOfCompressionHistory() : MPPC_SizeOfDecompressionHistory(), |
| 301 | M_NETGRAPH_MPPC, | |
| 302 | M_WAITOK | M_NULLOK); | |
| b06ebda0 MD |
303 | if (d->history == NULL) |
| 304 | ERROUT(ENOMEM); | |
| 305 | if (isComp) | |
| 306 | MPPC_InitCompressionHistory(d->history); | |
| 307 | else { | |
| 308 | MPPC_InitDecompressionHistory( | |
| 309 | d->history); | |
| 310 | } | |
| 311 | } | |
| 312 | #endif | |
| 313 | ||
| 5a975a3d | 314 | #ifdef NETGRAPH7_MPPC_ENCRYPTION |
| b06ebda0 MD |
315 | /* Generate initial session keys for encryption */ |
| 316 | if ((cfg->bits & MPPE_BITS) != 0) { | |
| 317 | const int keylen = KEYLEN(cfg->bits); | |
| 318 | ||
| 319 | bcopy(cfg->startkey, d->key, keylen); | |
| 320 | ng_mppc_getkey(cfg->startkey, d->key, keylen); | |
| 321 | if ((cfg->bits & MPPE_40) != 0) | |
| 322 | bcopy(&ng_mppe_weakenkey, d->key, 3); | |
| 323 | else if ((cfg->bits & MPPE_56) != 0) | |
| 324 | bcopy(&ng_mppe_weakenkey, d->key, 1); | |
| 325 | rc4_init(&d->rc4, d->key, keylen); | |
| 326 | } | |
| 327 | #endif | |
| 328 | ||
| 329 | /* Initialize other state */ | |
| 330 | d->cc = 0; | |
| 331 | d->flushed = 0; | |
| 332 | break; | |
| 333 | } | |
| 334 | ||
| 335 | case NGM_MPPC_RESETREQ: | |
| 336 | ng_mppc_reset_req(node); | |
| 337 | break; | |
| 338 | ||
| 339 | default: | |
| 340 | error = EINVAL; | |
| 341 | break; | |
| 342 | } | |
| 343 | break; | |
| 344 | default: | |
| 345 | error = EINVAL; | |
| 346 | break; | |
| 347 | } | |
| 348 | done: | |
| 349 | NG_RESPOND_MSG(error, node, item, resp); | |
| 350 | NG_FREE_MSG(msg); | |
| 351 | return (error); | |
| 352 | } | |
| 353 | ||
| 354 | /* | |
| 355 | * Receive incoming data on our hook. | |
| 356 | */ | |
| 357 | static int | |
| 358 | ng_mppc_rcvdata(hook_p hook, item_p item) | |
| 359 | { | |
| 360 | const node_p node = NG_HOOK_NODE(hook); | |
| 361 | const priv_p priv = NG_NODE_PRIVATE(node); | |
| 362 | int error; | |
| 363 | struct mbuf *m; | |
| 364 | ||
| 365 | NGI_GET_M(item, m); | |
| 366 | /* Compress and/or encrypt */ | |
| 367 | if (hook == priv->xmit.hook) { | |
| 368 | if (!priv->xmit.cfg.enable) { | |
| 369 | NG_FREE_M(m); | |
| 370 | NG_FREE_ITEM(item); | |
| 371 | return (ENXIO); | |
| 372 | } | |
| 373 | if ((error = ng_mppc_compress(node, &m)) != 0) { | |
| 374 | NG_FREE_ITEM(item); | |
| 375 | return(error); | |
| 376 | } | |
| 377 | NG_FWD_NEW_DATA(error, item, priv->xmit.hook, m); | |
| 378 | return (error); | |
| 379 | } | |
| 380 | ||
| 381 | /* Decompress and/or decrypt */ | |
| 382 | if (hook == priv->recv.hook) { | |
| 383 | if (!priv->recv.cfg.enable) { | |
| 384 | NG_FREE_M(m); | |
| 385 | NG_FREE_ITEM(item); | |
| 386 | return (ENXIO); | |
| 387 | } | |
| 388 | if ((error = ng_mppc_decompress(node, &m)) != 0) { | |
| 389 | NG_FREE_ITEM(item); | |
| 390 | if (error == EINVAL && priv->ctrlnode != 0) { | |
| 391 | struct ng_mesg *msg; | |
| 392 | ||
| 393 | /* Need to send a reset-request */ | |
| 394 | NG_MKMESSAGE(msg, NGM_MPPC_COOKIE, | |
| 5a975a3d | 395 | NGM_MPPC_RESETREQ, 0, M_WAITOK | M_NULLOK); |
| b06ebda0 MD |
396 | if (msg == NULL) |
| 397 | return (error); | |
| 398 | NG_SEND_MSG_ID(error, node, msg, | |
| 399 | priv->ctrlnode, 0); | |
| 400 | } | |
| 401 | return (error); | |
| 402 | } | |
| 403 | NG_FWD_NEW_DATA(error, item, priv->recv.hook, m); | |
| 404 | return (error); | |
| 405 | } | |
| 406 | ||
| 407 | /* Oops */ | |
| 408 | panic("%s: unknown hook", __func__); | |
| 409 | #ifdef RESTARTABLE_PANICS | |
| 410 | return (EINVAL); | |
| 411 | #endif | |
| 412 | } | |
| 413 | ||
| 414 | /* | |
| 415 | * Destroy node | |
| 416 | */ | |
| 417 | static int | |
| 418 | ng_mppc_shutdown(node_p node) | |
| 419 | { | |
| 420 | const priv_p priv = NG_NODE_PRIVATE(node); | |
| 421 | ||
| 422 | /* Take down netgraph node */ | |
| 5a975a3d | 423 | #ifdef NETGRAPH7_MPPC_COMPRESSION |
| b06ebda0 | 424 | if (priv->xmit.history != NULL) |
| fc025606 | 425 | kfree(priv->xmit.history, M_NETGRAPH_MPPC); |
| b06ebda0 | 426 | if (priv->recv.history != NULL) |
| fc025606 | 427 | kfree(priv->recv.history, M_NETGRAPH_MPPC); |
| b06ebda0 MD |
428 | #endif |
| 429 | bzero(priv, sizeof(*priv)); | |
| fc025606 | 430 | kfree(priv, M_NETGRAPH_MPPC); |
| b06ebda0 MD |
431 | NG_NODE_SET_PRIVATE(node, NULL); |
| 432 | NG_NODE_UNREF(node); /* let the node escape */ | |
| 433 | return (0); | |
| 434 | } | |
| 435 | ||
| 436 | /* | |
| 437 | * Hook disconnection | |
| 438 | */ | |
| 439 | static int | |
| 440 | ng_mppc_disconnect(hook_p hook) | |
| 441 | { | |
| 442 | const node_p node = NG_HOOK_NODE(hook); | |
| 443 | const priv_p priv = NG_NODE_PRIVATE(node); | |
| 444 | ||
| 445 | /* Zero out hook pointer */ | |
| 446 | if (hook == priv->xmit.hook) | |
| 447 | priv->xmit.hook = NULL; | |
| 448 | if (hook == priv->recv.hook) | |
| 449 | priv->recv.hook = NULL; | |
| 450 | ||
| 451 | /* Go away if no longer connected */ | |
| 452 | if ((NG_NODE_NUMHOOKS(node) == 0) | |
| 453 | && NG_NODE_IS_VALID(node)) | |
| 454 | ng_rmnode_self(node); | |
| 455 | return (0); | |
| 456 | } | |
| 457 | ||
| 458 | /************************************************************************ | |
| 459 | HELPER STUFF | |
| 460 | ************************************************************************/ | |
| 461 | ||
| 462 | /* | |
| 463 | * Compress/encrypt a packet and put the result in a new mbuf at *resultp. | |
| 464 | * The original mbuf is not free'd. | |
| 465 | */ | |
| 466 | static int | |
| 467 | ng_mppc_compress(node_p node, struct mbuf **datap) | |
| 468 | { | |
| 469 | const priv_p priv = NG_NODE_PRIVATE(node); | |
| 470 | struct ng_mppc_dir *const d = &priv->xmit; | |
| 471 | u_int16_t header; | |
| 472 | struct mbuf *m = *datap; | |
| 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) */ | |
| 5a975a3d | 484 | #ifdef NETGRAPH7_MPPC_COMPRESSION |
| b06ebda0 MD |
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; | |
| 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; | |
| 5a975a3d | 495 | inbuf = kmalloc(inlen, M_NETGRAPH_MPPC, M_WAITOK | M_NULLOK); |
| b06ebda0 MD |
496 | if (inbuf == NULL) { |
| 497 | m_freem(m); | |
| 498 | return (ENOMEM); | |
| 499 | } | |
| 500 | m_copydata(m, 0, inlen, (caddr_t)inbuf); | |
| 501 | ||
| 502 | outlen = MPPC_MAX_BLOWUP(inlen); | |
| 5a975a3d | 503 | outbuf = kmalloc(outlen, M_NETGRAPH_MPPC, M_WAITOK | M_NULLOK); |
| b06ebda0 MD |
504 | if (outbuf == NULL) { |
| 505 | m_freem(m); | |
| 5a975a3d | 506 | kfree(inbuf, M_NETGRAPH_MPPC); |
| b06ebda0 MD |
507 | return (ENOMEM); |
| 508 | } | |
| 509 | ||
| 510 | /* Prepare to compress */ | |
| 511 | source = inbuf; | |
| 512 | sourceCnt = inlen; | |
| 513 | dest = outbuf; | |
| 514 | destCnt = outlen; | |
| 515 | if ((d->cfg.bits & MPPE_STATELESS) == 0) | |
| 516 | flags |= MPPC_SAVE_HISTORY; | |
| 517 | ||
| 518 | /* Compress */ | |
| 519 | rtn = MPPC_Compress(&source, &dest, &sourceCnt, | |
| 520 | &destCnt, d->history, flags, 0); | |
| 521 | ||
| 522 | /* Check return value */ | |
| 523 | KASSERT(rtn != MPPC_INVALID, ("%s: invalid", __func__)); | |
| 524 | if ((rtn & MPPC_EXPANDED) == 0 | |
| 525 | && (rtn & MPPC_COMP_OK) == MPPC_COMP_OK) { | |
| 526 | outlen -= destCnt; | |
| 527 | header |= MPPC_FLAG_COMPRESSED; | |
| 528 | if ((rtn & MPPC_RESTART_HISTORY) != 0) | |
| 529 | header |= MPPC_FLAG_RESTART; | |
| 530 | ||
| 531 | /* Replace m by the compresed one. */ | |
| 532 | m_freem(m); | |
| 533 | m = m_devget((caddr_t)outbuf, outlen, 0, NULL, NULL); | |
| 534 | } | |
| 535 | d->flushed = (rtn & MPPC_EXPANDED) != 0 | |
| 536 | || (flags & MPPC_SAVE_HISTORY) == 0; | |
| 537 | ||
| 5a975a3d MD |
538 | kfree(inbuf, M_NETGRAPH_MPPC); |
| 539 | kfree(outbuf, M_NETGRAPH_MPPC); | |
| b06ebda0 MD |
540 | |
| 541 | /* Check m_devget() result. */ | |
| 542 | if (m == NULL) | |
| 543 | return (ENOMEM); | |
| 544 | } | |
| 545 | #endif | |
| 546 | ||
| 547 | /* Now encrypt packet (if encryption enabled) */ | |
| 5a975a3d | 548 | #ifdef NETGRAPH7_MPPC_ENCRYPTION |
| b06ebda0 MD |
549 | if ((d->cfg.bits & MPPE_BITS) != 0) { |
| 550 | struct mbuf *m1; | |
| 551 | ||
| 552 | /* Set header bits */ | |
| 553 | header |= MPPC_FLAG_ENCRYPTED; | |
| 554 | ||
| 555 | /* Update key if it's time */ | |
| 556 | if ((d->cfg.bits & MPPE_STATELESS) != 0 | |
| 557 | || (d->cc & MPPE_UPDATE_MASK) == MPPE_UPDATE_FLAG) { | |
| 558 | ng_mppc_updatekey(d->cfg.bits, | |
| 559 | d->cfg.startkey, d->key, &d->rc4); | |
| 560 | } else if ((header & MPPC_FLAG_FLUSHED) != 0) { | |
| 561 | /* Need to reset key if we say we did | |
| 562 | and ng_mppc_updatekey wasn't called to do it also. */ | |
| 563 | rc4_init(&d->rc4, d->key, KEYLEN(d->cfg.bits)); | |
| 564 | } | |
| 565 | ||
| 566 | /* We must own the mbuf chain exclusively to modify it. */ | |
| 5a975a3d | 567 | m = m_unshare(m, MB_DONTWAIT); |
| b06ebda0 MD |
568 | if (m == NULL) |
| 569 | return (ENOMEM); | |
| 570 | ||
| 571 | /* Encrypt packet */ | |
| 572 | m1 = m; | |
| 573 | while (m1) { | |
| 574 | rc4_crypt(&d->rc4, mtod(m1, u_char *), | |
| 575 | mtod(m1, u_char *), m1->m_len); | |
| 576 | m1 = m1->m_next; | |
| 577 | } | |
| 578 | } | |
| 579 | #endif | |
| 580 | ||
| 581 | /* Update coherency count for next time (12 bit arithmetic) */ | |
| 582 | MPPC_CCOUNT_INC(d->cc); | |
| 583 | ||
| 584 | /* Install header */ | |
| 5a975a3d | 585 | M_PREPEND(m, MPPC_HDRLEN, MB_DONTWAIT); |
| b06ebda0 MD |
586 | if (m != NULL) |
| 587 | *(mtod(m, uint16_t *)) = htons(header); | |
| 588 | ||
| 589 | *datap = m; | |
| 590 | return (*datap == NULL ? ENOBUFS : 0); | |
| 591 | } | |
| 592 | ||
| 593 | /* | |
| 594 | * Decompress/decrypt packet and put the result in a new mbuf at *resultp. | |
| 595 | * The original mbuf is not free'd. | |
| 596 | */ | |
| 597 | static int | |
| 598 | ng_mppc_decompress(node_p node, struct mbuf **datap) | |
| 599 | { | |
| 600 | const priv_p priv = NG_NODE_PRIVATE(node); | |
| 601 | struct ng_mppc_dir *const d = &priv->recv; | |
| 602 | u_int16_t header, cc; | |
| 603 | u_int numLost; | |
| 604 | struct mbuf *m = *datap; | |
| 605 | ||
| 606 | /* Pull off header */ | |
| 607 | if (m->m_pkthdr.len < MPPC_HDRLEN) { | |
| 608 | m_freem(m); | |
| 609 | return (EINVAL); | |
| 610 | } | |
| 611 | m_copydata(m, 0, MPPC_HDRLEN, (caddr_t)&header); | |
| 612 | header = ntohs(header); | |
| 613 | cc = (header & MPPC_CCOUNT_MASK); | |
| 614 | m_adj(m, MPPC_HDRLEN); | |
| 615 | ||
| 616 | /* Check for an unexpected jump in the sequence number */ | |
| 617 | numLost = ((cc - d->cc) & MPPC_CCOUNT_MASK); | |
| 618 | ||
| 619 | /* If flushed bit set, we can always handle packet */ | |
| 620 | if ((header & MPPC_FLAG_FLUSHED) != 0) { | |
| 5a975a3d | 621 | #ifdef NETGRAPH7_MPPC_COMPRESSION |
| b06ebda0 MD |
622 | if (d->history != NULL) |
| 623 | MPPC_InitDecompressionHistory(d->history); | |
| 624 | #endif | |
| 5a975a3d | 625 | #ifdef NETGRAPH7_MPPC_ENCRYPTION |
| b06ebda0 MD |
626 | if ((d->cfg.bits & MPPE_BITS) != 0) { |
| 627 | u_int rekey; | |
| 628 | ||
| 629 | /* How many times are we going to have to re-key? */ | |
| 630 | rekey = ((d->cfg.bits & MPPE_STATELESS) != 0) ? | |
| 631 | numLost : (numLost / (MPPE_UPDATE_MASK + 1)); | |
| 632 | if (rekey > MPPE_MAX_REKEY) { | |
| 633 | log(LOG_ERR, "%s: too many (%d) packets" | |
| 634 | " dropped, disabling node %p!", | |
| 635 | __func__, numLost, node); | |
| 636 | priv->recv.cfg.enable = 0; | |
| 637 | goto failed; | |
| 638 | } | |
| 639 | ||
| 640 | /* Re-key as necessary to catch up to peer */ | |
| 641 | while (d->cc != cc) { | |
| 642 | if ((d->cfg.bits & MPPE_STATELESS) != 0 | |
| 643 | || (d->cc & MPPE_UPDATE_MASK) | |
| 644 | == MPPE_UPDATE_FLAG) { | |
| 645 | ng_mppc_updatekey(d->cfg.bits, | |
| 646 | d->cfg.startkey, d->key, &d->rc4); | |
| 647 | } | |
| 648 | MPPC_CCOUNT_INC(d->cc); | |
| 649 | } | |
| 650 | ||
| 651 | /* Reset key (except in stateless mode, see below) */ | |
| 652 | if ((d->cfg.bits & MPPE_STATELESS) == 0) | |
| 653 | rc4_init(&d->rc4, d->key, KEYLEN(d->cfg.bits)); | |
| 654 | } | |
| 655 | #endif | |
| 656 | d->cc = cc; /* skip over lost seq numbers */ | |
| 657 | numLost = 0; /* act like no packets were lost */ | |
| 658 | } | |
| 659 | ||
| 660 | /* Can't decode non-sequential packets without a flushed bit */ | |
| 661 | if (numLost != 0) | |
| 662 | goto failed; | |
| 663 | ||
| 664 | /* Decrypt packet */ | |
| 665 | if ((header & MPPC_FLAG_ENCRYPTED) != 0) { | |
| 5a975a3d | 666 | #ifdef NETGRAPH7_MPPC_ENCRYPTION |
| b06ebda0 MD |
667 | struct mbuf *m1; |
| 668 | #endif | |
| 669 | ||
| 670 | /* Are we not expecting encryption? */ | |
| 671 | if ((d->cfg.bits & MPPE_BITS) == 0) { | |
| 672 | log(LOG_ERR, "%s: rec'd unexpectedly %s packet", | |
| 673 | __func__, "encrypted"); | |
| 674 | goto failed; | |
| 675 | } | |
| 676 | ||
| 5a975a3d | 677 | #ifdef NETGRAPH7_MPPC_ENCRYPTION |
| b06ebda0 MD |
678 | /* Update key if it's time (always in stateless mode) */ |
| 679 | if ((d->cfg.bits & MPPE_STATELESS) != 0 | |
| 680 | || (d->cc & MPPE_UPDATE_MASK) == MPPE_UPDATE_FLAG) { | |
| 681 | ng_mppc_updatekey(d->cfg.bits, | |
| 682 | d->cfg.startkey, d->key, &d->rc4); | |
| 683 | } | |
| 684 | ||
| 685 | /* We must own the mbuf chain exclusively to modify it. */ | |
| 5a975a3d | 686 | m = m_unshare(m, MB_DONTWAIT); |
| b06ebda0 MD |
687 | if (m == NULL) |
| 688 | return (ENOMEM); | |
| 689 | ||
| 690 | /* Decrypt packet */ | |
| 691 | m1 = m; | |
| 692 | while (m1 != NULL) { | |
| 693 | rc4_crypt(&d->rc4, mtod(m1, u_char *), | |
| 694 | mtod(m1, u_char *), m1->m_len); | |
| 695 | m1 = m1->m_next; | |
| 696 | } | |
| 697 | #endif | |
| 698 | } else { | |
| 699 | ||
| 700 | /* Are we expecting encryption? */ | |
| 701 | if ((d->cfg.bits & MPPE_BITS) != 0) { | |
| 702 | log(LOG_ERR, "%s: rec'd unexpectedly %s packet", | |
| 703 | __func__, "unencrypted"); | |
| 704 | goto failed; | |
| 705 | } | |
| 706 | } | |
| 707 | ||
| 708 | /* Update coherency count for next time (12 bit arithmetic) */ | |
| 709 | MPPC_CCOUNT_INC(d->cc); | |
| 710 | ||
| 711 | /* Check for unexpected compressed packet */ | |
| 712 | if ((header & MPPC_FLAG_COMPRESSED) != 0 | |
| 713 | && (d->cfg.bits & MPPC_BIT) == 0) { | |
| 714 | log(LOG_ERR, "%s: rec'd unexpectedly %s packet", | |
| 715 | __func__, "compressed"); | |
| 716 | failed: | |
| 717 | m_freem(m); | |
| 718 | return (EINVAL); | |
| 719 | } | |
| 720 | ||
| 5a975a3d | 721 | #ifdef NETGRAPH7_MPPC_COMPRESSION |
| b06ebda0 MD |
722 | /* Decompress packet */ |
| 723 | if ((header & MPPC_FLAG_COMPRESSED) != 0) { | |
| 724 | int flags = MPPC_MANDATORY_DECOMPRESS_FLAGS; | |
| 725 | u_char *decompbuf, *source, *dest; | |
| 726 | u_long sourceCnt, destCnt; | |
| 727 | int decomplen, rtn; | |
| 728 | u_char *buf; | |
| 729 | int len; | |
| 730 | ||
| 731 | /* Copy payload into a contiguous region of memory. */ | |
| 732 | len = m->m_pkthdr.len; | |
| 5a975a3d | 733 | buf = kmalloc(len, M_NETGRAPH_MPPC, M_WAITOK | M_NULLOK); |
| b06ebda0 MD |
734 | if (buf == NULL) { |
| 735 | m_freem(m); | |
| 736 | return (ENOMEM); | |
| 737 | } | |
| 738 | m_copydata(m, 0, len, (caddr_t)buf); | |
| 739 | ||
| 740 | /* Allocate a buffer for decompressed data */ | |
| 5a975a3d MD |
741 | decompbuf = kmalloc(MPPC_DECOMP_BUFSIZE + MPPC_DECOMP_SAFETY, |
| 742 | M_NETGRAPH_MPPC, M_WAITOK | M_NULLOK); | |
| b06ebda0 MD |
743 | if (decompbuf == NULL) { |
| 744 | m_freem(m); | |
| 5a975a3d | 745 | kfree(buf, M_NETGRAPH_MPPC); |
| b06ebda0 MD |
746 | return (ENOMEM); |
| 747 | } | |
| 748 | decomplen = MPPC_DECOMP_BUFSIZE; | |
| 749 | ||
| 750 | /* Prepare to decompress */ | |
| 751 | source = buf; | |
| 752 | sourceCnt = len; | |
| 753 | dest = decompbuf; | |
| 754 | destCnt = decomplen; | |
| 755 | if ((header & MPPC_FLAG_RESTART) != 0) | |
| 756 | flags |= MPPC_RESTART_HISTORY; | |
| 757 | ||
| 758 | /* Decompress */ | |
| 759 | rtn = MPPC_Decompress(&source, &dest, | |
| 760 | &sourceCnt, &destCnt, d->history, flags); | |
| 761 | ||
| 762 | /* Check return value */ | |
| 763 | KASSERT(rtn != MPPC_INVALID, ("%s: invalid", __func__)); | |
| 764 | if ((rtn & MPPC_DEST_EXHAUSTED) != 0 | |
| 765 | || (rtn & MPPC_DECOMP_OK) != MPPC_DECOMP_OK) { | |
| 766 | log(LOG_ERR, "%s: decomp returned 0x%x", | |
| 767 | __func__, rtn); | |
| 5a975a3d MD |
768 | kfree(buf, M_NETGRAPH_MPPC); |
| 769 | kfree(decompbuf, M_NETGRAPH_MPPC); | |
| b06ebda0 MD |
770 | goto failed; |
| 771 | } | |
| 772 | ||
| 773 | /* Replace compressed data with decompressed data */ | |
| 5a975a3d | 774 | kfree(buf, M_NETGRAPH_MPPC); |
| b06ebda0 MD |
775 | len = decomplen - destCnt; |
| 776 | ||
| 777 | m_freem(m); | |
| 778 | m = m_devget((caddr_t)decompbuf, len, 0, NULL, NULL); | |
| 5a975a3d | 779 | kfree(decompbuf, M_NETGRAPH_MPPC); |
| b06ebda0 MD |
780 | } |
| 781 | #endif | |
| 782 | ||
| 783 | /* Return result in an mbuf */ | |
| 784 | *datap = m; | |
| 785 | return (*datap == NULL ? ENOBUFS : 0); | |
| 786 | } | |
| 787 | ||
| 788 | /* | |
| 789 | * The peer has sent us a CCP ResetRequest, so reset our transmit state. | |
| 790 | */ | |
| 791 | static void | |
| 792 | ng_mppc_reset_req(node_p node) | |
| 793 | { | |
| 794 | const priv_p priv = NG_NODE_PRIVATE(node); | |
| 795 | struct ng_mppc_dir *const d = &priv->xmit; | |
| 796 | ||
| 5a975a3d | 797 | #ifdef NETGRAPH7_MPPC_COMPRESSION |
| b06ebda0 MD |
798 | if (d->history != NULL) |
| 799 | MPPC_InitCompressionHistory(d->history); | |
| 800 | #endif | |
| 5a975a3d | 801 | #ifdef NETGRAPH7_MPPC_ENCRYPTION |
| b06ebda0 MD |
802 | if ((d->cfg.bits & MPPE_STATELESS) == 0) |
| 803 | rc4_init(&d->rc4, d->key, KEYLEN(d->cfg.bits)); | |
| 804 | #endif | |
| 805 | d->flushed = 1; | |
| 806 | } | |
| 807 | ||
| 5a975a3d | 808 | #ifdef NETGRAPH7_MPPC_ENCRYPTION |
| b06ebda0 MD |
809 | /* |
| 810 | * Generate a new encryption key | |
| 811 | */ | |
| 812 | static void | |
| 813 | ng_mppc_getkey(const u_char *h, u_char *h2, int len) | |
| 814 | { | |
| 815 | static const u_char pad1[10] = | |
| 816 | { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; | |
| 817 | static const u_char pad2[10] = | |
| 818 | { 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, }; | |
| 819 | u_char hash[20]; | |
| 820 | SHA1_CTX c; | |
| 821 | int k; | |
| 822 | ||
| 823 | SHA1Init(&c); | |
| 824 | SHA1Update(&c, h, len); | |
| 825 | for (k = 0; k < 4; k++) | |
| 826 | SHA1Update(&c, pad1, sizeof(pad1)); | |
| 827 | SHA1Update(&c, h2, len); | |
| 828 | for (k = 0; k < 4; k++) | |
| 829 | SHA1Update(&c, pad2, sizeof(pad2)); | |
| 830 | SHA1Final(hash, &c); | |
| 831 | bcopy(hash, h2, len); | |
| 832 | } | |
| 833 | ||
| 834 | /* | |
| 835 | * Update the encryption key | |
| 836 | */ | |
| 837 | static void | |
| 838 | ng_mppc_updatekey(u_int32_t bits, | |
| 839 | u_char *key0, u_char *key, struct rc4_state *rc4) | |
| 840 | { | |
| 841 | const int keylen = KEYLEN(bits); | |
| 842 | ||
| 843 | ng_mppc_getkey(key0, key, keylen); | |
| 844 | rc4_init(rc4, key, keylen); | |
| 845 | rc4_crypt(rc4, key, key, keylen); | |
| 846 | if ((bits & MPPE_40) != 0) | |
| 847 | bcopy(&ng_mppe_weakenkey, key, 3); | |
| 848 | else if ((bits & MPPE_56) != 0) | |
| 849 | bcopy(&ng_mppe_weakenkey, key, 1); | |
| 850 | rc4_init(rc4, key, keylen); | |
| 851 | } | |
| 852 | #endif | |
| 853 |