1 /* $OpenBSD: chap.c,v 1.21 2024/08/09 05:16:13 deraadt Exp $ */ 2 3 /* 4 * chap.c - Challenge Handshake Authentication Protocol. 5 * 6 * Copyright (c) 1989-2002 Paul Mackerras. All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in 17 * the documentation and/or other materials provided with the 18 * distribution. 19 * 20 * 3. The name(s) of the authors of this software must not be used to 21 * endorse or promote products derived from this software without 22 * prior written permission. 23 * 24 * 4. Redistributions of any form whatsoever must retain the following 25 * acknowledgment: 26 * "This product includes software developed by Paul Mackerras 27 * <paulus@samba.org>". 28 * 29 * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO 30 * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 31 * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY 32 * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 33 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN 34 * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING 35 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 36 * 37 * Copyright (c) 1991 Gregory M. Christy. 38 * All rights reserved. 39 * 40 * Redistribution and use in source and binary forms are permitted 41 * provided that the above copyright notice and this paragraph are 42 * duplicated in all such forms and that any documentation, 43 * advertising materials, and other materials related to such 44 * distribution and use acknowledge that the software was developed 45 * by Gregory M. Christy. The name of the author may not be used to 46 * endorse or promote products derived from this software without 47 * specific prior written permission. 48 * 49 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 50 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 51 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. 52 */ 53 54 /* 55 * TODO: 56 */ 57 58 #include <stdio.h> 59 #include <stdlib.h> 60 #include <string.h> 61 #include <sys/types.h> 62 #include <sys/time.h> 63 #include <syslog.h> 64 #include <md5.h> 65 66 #include "pppd.h" 67 #include "chap.h" 68 69 /* 70 * Protocol entry points. 71 */ 72 static void ChapInit(int); 73 static void ChapLowerUp(int); 74 static void ChapLowerDown(int); 75 static void ChapInput(int, u_char *, int); 76 static void ChapProtocolReject(int); 77 static int ChapPrintPkt(u_char *, int, void (*)(void *, char *, ...), void *); 78 79 struct protent chap_protent = { 80 PPP_CHAP, 81 ChapInit, 82 ChapInput, 83 ChapProtocolReject, 84 ChapLowerUp, 85 ChapLowerDown, 86 NULL, 87 NULL, 88 ChapPrintPkt, 89 NULL, 90 1, 91 "CHAP", 92 NULL, 93 NULL, 94 NULL 95 }; 96 97 chap_state chap[NUM_PPP]; /* CHAP state; one for each unit */ 98 99 static void ChapChallengeTimeout(void *); 100 static void ChapResponseTimeout(void *); 101 static void ChapReceiveChallenge(chap_state *, u_char *, int, int); 102 static void ChapRechallenge(void *); 103 static void ChapReceiveResponse(chap_state *, u_char *, int, int); 104 static void ChapReceiveSuccess(chap_state *, u_char *, int, int); 105 static void ChapReceiveFailure(chap_state *, u_char *, int, int); 106 static void ChapSendStatus(chap_state *, int); 107 static void ChapSendChallenge(chap_state *); 108 static void ChapSendResponse(chap_state *); 109 static void ChapGenChallenge(chap_state *); 110 111 /* 112 * ChapInit - Initialize a CHAP unit. 113 */ 114 static void 115 ChapInit(int unit) 116 { 117 chap_state *cstate = &chap[unit]; 118 119 BZERO(cstate, sizeof(*cstate)); 120 cstate->unit = unit; 121 cstate->clientstate = CHAPCS_INITIAL; 122 cstate->serverstate = CHAPSS_INITIAL; 123 cstate->timeouttime = CHAP_DEFTIMEOUT; 124 cstate->max_transmits = CHAP_DEFTRANSMITS; 125 /* random number generator is initialized in magic_init */ 126 } 127 128 129 /* 130 * ChapAuthWithPeer - Authenticate us with our peer (start client). 131 * 132 */ 133 void 134 ChapAuthWithPeer(int unit, char *our_name, int digest) 135 { 136 chap_state *cstate = &chap[unit]; 137 138 cstate->resp_name = our_name; 139 cstate->resp_type = digest; 140 141 if (cstate->clientstate == CHAPCS_INITIAL || 142 cstate->clientstate == CHAPCS_PENDING) { 143 /* lower layer isn't up - wait until later */ 144 cstate->clientstate = CHAPCS_PENDING; 145 return; 146 } 147 148 /* 149 * We get here as a result of LCP coming up. 150 * So even if CHAP was open before, we will 151 * have to re-authenticate ourselves. 152 */ 153 cstate->clientstate = CHAPCS_LISTEN; 154 } 155 156 157 /* 158 * ChapAuthPeer - Authenticate our peer (start server). 159 */ 160 void 161 ChapAuthPeer(int unit, char *our_name, int digest) 162 { 163 chap_state *cstate = &chap[unit]; 164 165 cstate->chal_name = our_name; 166 cstate->chal_type = digest; 167 168 if (cstate->serverstate == CHAPSS_INITIAL || 169 cstate->serverstate == CHAPSS_PENDING) { 170 /* lower layer isn't up - wait until later */ 171 cstate->serverstate = CHAPSS_PENDING; 172 return; 173 } 174 175 ChapGenChallenge(cstate); 176 ChapSendChallenge(cstate); /* crank it up dude! */ 177 cstate->serverstate = CHAPSS_INITIAL_CHAL; 178 } 179 180 181 /* 182 * ChapChallengeTimeout - Timeout expired on sending challenge. 183 */ 184 static void 185 ChapChallengeTimeout(void *arg) 186 { 187 chap_state *cstate = (chap_state *) arg; 188 189 /* if we aren't sending challenges, don't worry. then again we */ 190 /* probably shouldn't be here either */ 191 if (cstate->serverstate != CHAPSS_INITIAL_CHAL && 192 cstate->serverstate != CHAPSS_RECHALLENGE) 193 return; 194 195 if (cstate->chal_transmits >= cstate->max_transmits) { 196 /* give up on peer */ 197 syslog(LOG_ERR, "Peer failed to respond to CHAP challenge"); 198 cstate->serverstate = CHAPSS_BADAUTH; 199 auth_peer_fail(cstate->unit, PPP_CHAP); 200 return; 201 } 202 203 ChapSendChallenge(cstate); /* Re-send challenge */ 204 } 205 206 207 /* 208 * ChapResponseTimeout - Timeout expired on sending response. 209 */ 210 static void 211 ChapResponseTimeout(void *arg) 212 { 213 chap_state *cstate = (chap_state *) arg; 214 215 /* if we aren't sending a response, don't worry. */ 216 if (cstate->clientstate != CHAPCS_RESPONSE) 217 return; 218 219 ChapSendResponse(cstate); /* re-send response */ 220 } 221 222 223 /* 224 * ChapRechallenge - Time to challenge the peer again. 225 */ 226 static void 227 ChapRechallenge(void *arg) 228 { 229 chap_state *cstate = (chap_state *) arg; 230 231 /* if we aren't sending a response, don't worry. */ 232 if (cstate->serverstate != CHAPSS_OPEN) 233 return; 234 235 ChapGenChallenge(cstate); 236 ChapSendChallenge(cstate); 237 cstate->serverstate = CHAPSS_RECHALLENGE; 238 } 239 240 241 /* 242 * ChapLowerUp - The lower layer is up. 243 * 244 * Start up if we have pending requests. 245 */ 246 static void 247 ChapLowerUp(int unit) 248 { 249 chap_state *cstate = &chap[unit]; 250 251 if (cstate->clientstate == CHAPCS_INITIAL) 252 cstate->clientstate = CHAPCS_CLOSED; 253 else if (cstate->clientstate == CHAPCS_PENDING) 254 cstate->clientstate = CHAPCS_LISTEN; 255 256 if (cstate->serverstate == CHAPSS_INITIAL) 257 cstate->serverstate = CHAPSS_CLOSED; 258 else if (cstate->serverstate == CHAPSS_PENDING) { 259 ChapGenChallenge(cstate); 260 ChapSendChallenge(cstate); 261 cstate->serverstate = CHAPSS_INITIAL_CHAL; 262 } 263 } 264 265 266 /* 267 * ChapLowerDown - The lower layer is down. 268 * 269 * Cancel all timeouts. 270 */ 271 static void 272 ChapLowerDown(int unit) 273 { 274 chap_state *cstate = &chap[unit]; 275 276 /* Timeout(s) pending? Cancel if so. */ 277 if (cstate->serverstate == CHAPSS_INITIAL_CHAL || 278 cstate->serverstate == CHAPSS_RECHALLENGE) 279 UNTIMEOUT(ChapChallengeTimeout, cstate); 280 else if (cstate->serverstate == CHAPSS_OPEN 281 && cstate->chal_interval != 0) 282 UNTIMEOUT(ChapRechallenge, cstate); 283 if (cstate->clientstate == CHAPCS_RESPONSE) 284 UNTIMEOUT(ChapResponseTimeout, cstate); 285 286 cstate->clientstate = CHAPCS_INITIAL; 287 cstate->serverstate = CHAPSS_INITIAL; 288 } 289 290 291 /* 292 * ChapProtocolReject - Peer doesn't grok CHAP. 293 */ 294 static void 295 ChapProtocolReject(int unit) 296 { 297 chap_state *cstate = &chap[unit]; 298 299 if (cstate->serverstate != CHAPSS_INITIAL && 300 cstate->serverstate != CHAPSS_CLOSED) 301 auth_peer_fail(unit, PPP_CHAP); 302 if (cstate->clientstate != CHAPCS_INITIAL && 303 cstate->clientstate != CHAPCS_CLOSED) 304 auth_withpeer_fail(unit, PPP_CHAP); 305 ChapLowerDown(unit); /* shutdown chap */ 306 } 307 308 309 /* 310 * ChapInput - Input CHAP packet. 311 */ 312 static void 313 ChapInput(int unit, u_char *inpacket, int packet_len) 314 { 315 chap_state *cstate = &chap[unit]; 316 u_char *inp; 317 u_char code, id; 318 int len; 319 320 /* 321 * Parse header (code, id and length). 322 * If packet too short, drop it. 323 */ 324 inp = inpacket; 325 if (packet_len < CHAP_HEADERLEN) { 326 CHAPDEBUG((LOG_INFO, "ChapInput: rcvd short header.")); 327 return; 328 } 329 GETCHAR(code, inp); 330 GETCHAR(id, inp); 331 GETSHORT(len, inp); 332 if (len < CHAP_HEADERLEN) { 333 CHAPDEBUG((LOG_INFO, "ChapInput: rcvd illegal length.")); 334 return; 335 } 336 if (len > packet_len) { 337 CHAPDEBUG((LOG_INFO, "ChapInput: rcvd short packet.")); 338 return; 339 } 340 len -= CHAP_HEADERLEN; 341 342 /* 343 * Action depends on code (as in fact it usually does :-). 344 */ 345 switch (code) { 346 case CHAP_CHALLENGE: 347 ChapReceiveChallenge(cstate, inp, id, len); 348 break; 349 350 case CHAP_RESPONSE: 351 ChapReceiveResponse(cstate, inp, id, len); 352 break; 353 354 case CHAP_FAILURE: 355 ChapReceiveFailure(cstate, inp, id, len); 356 break; 357 358 case CHAP_SUCCESS: 359 ChapReceiveSuccess(cstate, inp, id, len); 360 break; 361 362 default: /* Need code reject? */ 363 syslog(LOG_WARNING, "Unknown CHAP code (%d) received.", code); 364 break; 365 } 366 } 367 368 369 /* 370 * ChapReceiveChallenge - Receive Challenge and send Response. 371 */ 372 static void 373 ChapReceiveChallenge(chap_state *cstate, u_char *inp, int id, int len) 374 { 375 int rchallenge_len; 376 u_char *rchallenge; 377 int secret_len; 378 char secret[MAXSECRETLEN]; 379 char rhostname[256]; 380 MD5_CTX mdContext; 381 u_char hash[MD5_SIGNATURE_SIZE]; 382 383 CHAPDEBUG((LOG_INFO, "ChapReceiveChallenge: Rcvd id %d.", id)); 384 if (cstate->clientstate == CHAPCS_CLOSED || 385 cstate->clientstate == CHAPCS_PENDING) { 386 CHAPDEBUG((LOG_INFO, "ChapReceiveChallenge: in state %d", 387 cstate->clientstate)); 388 return; 389 } 390 391 if (len < 2) { 392 CHAPDEBUG((LOG_INFO, "ChapReceiveChallenge: rcvd short packet.")); 393 return; 394 } 395 396 GETCHAR(rchallenge_len, inp); 397 len -= sizeof (u_char) + rchallenge_len; /* now name field length */ 398 if (len < 0) { 399 CHAPDEBUG((LOG_INFO, "ChapReceiveChallenge: rcvd short packet.")); 400 return; 401 } 402 rchallenge = inp; 403 INCPTR(rchallenge_len, inp); 404 405 if (len >= sizeof(rhostname)) 406 len = sizeof(rhostname) - 1; 407 BCOPY(inp, rhostname, len); 408 rhostname[len] = '\000'; 409 410 CHAPDEBUG((LOG_INFO, "ChapReceiveChallenge: received name field '%s'", 411 rhostname)); 412 413 /* Microsoft doesn't send their name back in the PPP packet */ 414 if (remote_name[0] != 0 && (explicit_remote || rhostname[0] == 0)) { 415 strlcpy(rhostname, remote_name, sizeof(rhostname)); 416 CHAPDEBUG((LOG_INFO, "ChapReceiveChallenge: using '%s' as remote name", 417 rhostname)); 418 } 419 420 /* get secret for authenticating ourselves with the specified host */ 421 if (!get_secret(cstate->unit, cstate->resp_name, rhostname, 422 secret, &secret_len, 0)) { 423 secret_len = 0; /* assume null secret if can't find one */ 424 syslog(LOG_WARNING, "No CHAP secret found for authenticating us to %s", 425 rhostname); 426 } 427 428 /* cancel response send timeout if necessary */ 429 if (cstate->clientstate == CHAPCS_RESPONSE) 430 UNTIMEOUT(ChapResponseTimeout, cstate); 431 432 cstate->resp_id = id; 433 cstate->resp_transmits = 0; 434 435 /* generate MD based on negotiated type */ 436 switch (cstate->resp_type) { 437 438 case CHAP_DIGEST_MD5: 439 MD5Init(&mdContext); 440 MD5Update(&mdContext, &cstate->resp_id, 1); 441 MD5Update(&mdContext, secret, secret_len); 442 MD5Update(&mdContext, rchallenge, rchallenge_len); 443 MD5Final(hash, &mdContext); 444 BCOPY(hash, cstate->response, MD5_SIGNATURE_SIZE); 445 cstate->resp_length = MD5_SIGNATURE_SIZE; 446 break; 447 448 default: 449 CHAPDEBUG((LOG_INFO, "unknown digest type %d", cstate->resp_type)); 450 return; 451 } 452 453 EXPLICIT_BZERO(secret, sizeof(secret)); 454 ChapSendResponse(cstate); 455 } 456 457 458 /* 459 * ChapReceiveResponse - Receive and process response. 460 */ 461 static void 462 ChapReceiveResponse(chap_state *cstate, u_char *inp, int id, int len) 463 { 464 u_char *remmd, remmd_len; 465 int secret_len, old_state; 466 int code; 467 char rhostname[256]; 468 MD5_CTX mdContext; 469 char secret[MAXSECRETLEN]; 470 u_char hash[MD5_SIGNATURE_SIZE]; 471 472 CHAPDEBUG((LOG_INFO, "ChapReceiveResponse: Rcvd id %d.", id)); 473 474 if (cstate->serverstate == CHAPSS_CLOSED || 475 cstate->serverstate == CHAPSS_PENDING) { 476 CHAPDEBUG((LOG_INFO, "ChapReceiveResponse: in state %d", 477 cstate->serverstate)); 478 return; 479 } 480 481 if (id != cstate->chal_id) 482 return; /* doesn't match ID of last challenge */ 483 484 /* 485 * If we have received a duplicate or bogus Response, 486 * we have to send the same answer (Success/Failure) 487 * as we did for the first Response we saw. 488 */ 489 if (cstate->serverstate == CHAPSS_OPEN) { 490 ChapSendStatus(cstate, CHAP_SUCCESS); 491 return; 492 } 493 if (cstate->serverstate == CHAPSS_BADAUTH) { 494 ChapSendStatus(cstate, CHAP_FAILURE); 495 return; 496 } 497 498 if (len < 2) { 499 CHAPDEBUG((LOG_INFO, "ChapReceiveResponse: rcvd short packet.")); 500 return; 501 } 502 GETCHAR(remmd_len, inp); /* get length of MD */ 503 remmd = inp; /* get pointer to MD */ 504 INCPTR(remmd_len, inp); 505 506 len -= sizeof (u_char) + remmd_len; 507 if (len < 0) { 508 CHAPDEBUG((LOG_INFO, "ChapReceiveResponse: rcvd short packet.")); 509 return; 510 } 511 512 UNTIMEOUT(ChapChallengeTimeout, cstate); 513 514 if (len >= sizeof(rhostname)) 515 len = sizeof(rhostname) - 1; 516 BCOPY(inp, rhostname, len); 517 rhostname[len] = '\000'; 518 519 CHAPDEBUG((LOG_INFO, "ChapReceiveResponse: received name field: %s", 520 rhostname)); 521 522 /* 523 * Get secret for authenticating them with us, 524 * do the hash ourselves, and compare the result. 525 */ 526 code = CHAP_FAILURE; 527 if (!get_secret(cstate->unit, rhostname, cstate->chal_name, 528 secret, &secret_len, 1)) { 529 syslog(LOG_WARNING, "No CHAP secret found for authenticating %s", 530 rhostname); 531 } else { 532 533 /* generate MD based on negotiated type */ 534 switch (cstate->chal_type) { 535 536 case CHAP_DIGEST_MD5: /* only MD5 is defined for now */ 537 if (remmd_len != MD5_SIGNATURE_SIZE) 538 break; /* it's not even the right length */ 539 MD5Init(&mdContext); 540 MD5Update(&mdContext, &cstate->chal_id, 1); 541 MD5Update(&mdContext, secret, secret_len); 542 MD5Update(&mdContext, cstate->challenge, cstate->chal_len); 543 MD5Final(hash, &mdContext); 544 545 /* compare local and remote MDs and send the appropriate status */ 546 if (memcmp (hash, remmd, MD5_SIGNATURE_SIZE) == 0) 547 code = CHAP_SUCCESS; /* they are the same! */ 548 break; 549 550 default: 551 CHAPDEBUG((LOG_INFO, "unknown digest type %d", cstate->chal_type)); 552 } 553 } 554 555 EXPLICIT_BZERO(secret, sizeof(secret)); 556 ChapSendStatus(cstate, code); 557 558 if (code == CHAP_SUCCESS) { 559 old_state = cstate->serverstate; 560 cstate->serverstate = CHAPSS_OPEN; 561 if (old_state == CHAPSS_INITIAL_CHAL) { 562 auth_peer_success(cstate->unit, PPP_CHAP, rhostname, len); 563 } 564 if (cstate->chal_interval != 0) 565 TIMEOUT(ChapRechallenge, cstate, cstate->chal_interval); 566 syslog(LOG_NOTICE, "CHAP peer authentication succeeded for %s", 567 rhostname); 568 569 } else { 570 syslog(LOG_ERR, "CHAP peer authentication failed for remote host %s", 571 rhostname); 572 cstate->serverstate = CHAPSS_BADAUTH; 573 auth_peer_fail(cstate->unit, PPP_CHAP); 574 } 575 } 576 577 /* 578 * ChapReceiveSuccess - Receive Success 579 */ 580 static void 581 ChapReceiveSuccess(chap_state *cstate, u_char *inp, int id, int len) 582 { 583 584 CHAPDEBUG((LOG_INFO, "ChapReceiveSuccess: Rcvd id %d.", id)); 585 586 if (cstate->clientstate == CHAPCS_OPEN) 587 /* presumably an answer to a duplicate response */ 588 return; 589 590 if (cstate->clientstate != CHAPCS_RESPONSE) { 591 /* don't know what this is */ 592 CHAPDEBUG((LOG_INFO, "ChapReceiveSuccess: in state %d\n", 593 cstate->clientstate)); 594 return; 595 } 596 597 UNTIMEOUT(ChapResponseTimeout, cstate); 598 599 /* 600 * Print message. 601 */ 602 if (len > 0) 603 PRINTMSG(inp, len); 604 605 cstate->clientstate = CHAPCS_OPEN; 606 607 auth_withpeer_success(cstate->unit, PPP_CHAP); 608 } 609 610 611 /* 612 * ChapReceiveFailure - Receive failure. 613 */ 614 static void 615 ChapReceiveFailure(chap_state *cstate, u_char *inp, int id, int len) 616 { 617 CHAPDEBUG((LOG_INFO, "ChapReceiveFailure: Rcvd id %d.", id)); 618 619 if (cstate->clientstate != CHAPCS_RESPONSE) { 620 /* don't know what this is */ 621 CHAPDEBUG((LOG_INFO, "ChapReceiveFailure: in state %d\n", 622 cstate->clientstate)); 623 return; 624 } 625 626 UNTIMEOUT(ChapResponseTimeout, cstate); 627 628 /* 629 * Print message. 630 */ 631 if (len > 0) 632 PRINTMSG(inp, len); 633 634 syslog(LOG_ERR, "CHAP authentication failed"); 635 auth_withpeer_fail(cstate->unit, PPP_CHAP); 636 } 637 638 639 /* 640 * ChapSendChallenge - Send an Authenticate challenge. 641 */ 642 static void 643 ChapSendChallenge(chap_state *cstate) 644 { 645 u_char *outp; 646 int chal_len, name_len; 647 int outlen; 648 649 chal_len = cstate->chal_len; 650 name_len = strlen(cstate->chal_name); 651 outlen = CHAP_HEADERLEN + sizeof (u_char) + chal_len + name_len; 652 outp = outpacket_buf; 653 654 MAKEHEADER(outp, PPP_CHAP); /* paste in a CHAP header */ 655 656 PUTCHAR(CHAP_CHALLENGE, outp); 657 PUTCHAR(cstate->chal_id, outp); 658 PUTSHORT(outlen, outp); 659 660 PUTCHAR(chal_len, outp); /* put length of challenge */ 661 BCOPY(cstate->challenge, outp, chal_len); 662 INCPTR(chal_len, outp); 663 664 BCOPY(cstate->chal_name, outp, name_len); /* append hostname */ 665 666 output(cstate->unit, outpacket_buf, outlen + PPP_HDRLEN); 667 668 CHAPDEBUG((LOG_INFO, "ChapSendChallenge: Sent id %d.", cstate->chal_id)); 669 670 TIMEOUT(ChapChallengeTimeout, cstate, cstate->timeouttime); 671 ++cstate->chal_transmits; 672 } 673 674 675 /* 676 * ChapSendStatus - Send a status response (ack or nak). 677 */ 678 static void 679 ChapSendStatus(chap_state *cstate, int code) 680 { 681 u_char *outp; 682 int outlen, msglen; 683 char msg[256]; 684 685 if (code == CHAP_SUCCESS) 686 snprintf(msg, sizeof msg, "Welcome to %s.", hostname); 687 else 688 snprintf(msg, sizeof msg, "I don't like you. Go 'way."); 689 msglen = strlen(msg); 690 691 outlen = CHAP_HEADERLEN + msglen; 692 outp = outpacket_buf; 693 694 MAKEHEADER(outp, PPP_CHAP); /* paste in a header */ 695 696 PUTCHAR(code, outp); 697 PUTCHAR(cstate->chal_id, outp); 698 PUTSHORT(outlen, outp); 699 BCOPY(msg, outp, msglen); 700 output(cstate->unit, outpacket_buf, outlen + PPP_HDRLEN); 701 702 CHAPDEBUG((LOG_INFO, "ChapSendStatus: Sent code %d, id %d.", code, 703 cstate->chal_id)); 704 } 705 706 /* 707 * ChapGenChallenge is used to generate a pseudo-random challenge string of 708 * a pseudo-random length between min_len and max_len. The challenge 709 * string and its length are stored in *cstate, and various other fields of 710 * *cstate are initialized. 711 */ 712 713 static void 714 ChapGenChallenge(chap_state *cstate) 715 { 716 int chal_len; 717 718 /* pick a random challenge length >= MIN_CHALLENGE_LENGTH and 719 <= MAX_CHALLENGE_LENGTH */ 720 chal_len = MIN_CHALLENGE_LENGTH + 721 arc4random_uniform(MAX_CHALLENGE_LENGTH - MIN_CHALLENGE_LENGTH + 1); 722 723 cstate->chal_len = chal_len; 724 cstate->chal_id = ++cstate->id; 725 cstate->chal_transmits = 0; 726 727 /* generate a random string */ 728 arc4random_buf(cstate->challenge, chal_len); 729 } 730 731 /* 732 * ChapSendResponse - send a response packet with values as specified 733 * in *cstate. 734 */ 735 static void 736 ChapSendResponse(chap_state *cstate) 737 { 738 u_char *outp; 739 int outlen, md_len, name_len; 740 741 md_len = cstate->resp_length; 742 name_len = strlen(cstate->resp_name); 743 outlen = CHAP_HEADERLEN + sizeof (u_char) + md_len + name_len; 744 outp = outpacket_buf; 745 746 MAKEHEADER(outp, PPP_CHAP); 747 748 PUTCHAR(CHAP_RESPONSE, outp); /* we are a response */ 749 PUTCHAR(cstate->resp_id, outp); /* copy id from challenge packet */ 750 PUTSHORT(outlen, outp); /* packet length */ 751 752 PUTCHAR(md_len, outp); /* length of MD */ 753 BCOPY(cstate->response, outp, md_len); /* copy MD to buffer */ 754 INCPTR(md_len, outp); 755 756 BCOPY(cstate->resp_name, outp, name_len); /* append our name */ 757 758 /* send the packet */ 759 output(cstate->unit, outpacket_buf, outlen + PPP_HDRLEN); 760 761 cstate->clientstate = CHAPCS_RESPONSE; 762 TIMEOUT(ChapResponseTimeout, cstate, cstate->timeouttime); 763 ++cstate->resp_transmits; 764 } 765 766 /* 767 * ChapPrintPkt - print the contents of a CHAP packet. 768 */ 769 static char *ChapCodenames[] = { 770 "Challenge", "Response", "Success", "Failure" 771 }; 772 773 static int 774 ChapPrintPkt(u_char *p, int plen, void (*printer)(void *, char *, ...), void *arg) 775 { 776 int code, id, len; 777 int clen, nlen; 778 u_char x; 779 780 if (plen < CHAP_HEADERLEN) 781 return 0; 782 GETCHAR(code, p); 783 GETCHAR(id, p); 784 GETSHORT(len, p); 785 if (len < CHAP_HEADERLEN || len > plen) 786 return 0; 787 788 if (code >= 1 && code <= sizeof(ChapCodenames) / sizeof(char *)) 789 printer(arg, " %s", ChapCodenames[code-1]); 790 else 791 printer(arg, " code=0x%x", code); 792 printer(arg, " id=0x%x", id); 793 len -= CHAP_HEADERLEN; 794 switch (code) { 795 case CHAP_CHALLENGE: 796 case CHAP_RESPONSE: 797 if (len < 1) 798 break; 799 clen = p[0]; 800 if (len < clen + 1) 801 break; 802 ++p; 803 nlen = len - clen - 1; 804 printer(arg, " <"); 805 for (; clen > 0; --clen) { 806 GETCHAR(x, p); 807 printer(arg, "%.2x", x); 808 } 809 printer(arg, ">, name = "); 810 print_string((char *)p, nlen, printer, arg); 811 break; 812 case CHAP_FAILURE: 813 case CHAP_SUCCESS: 814 printer(arg, " "); 815 print_string((char *)p, len, printer, arg); 816 break; 817 default: 818 for (clen = len; clen > 0; --clen) { 819 GETCHAR(x, p); 820 printer(arg, " %.2x", x); 821 } 822 } 823 824 return len + CHAP_HEADERLEN; 825 } 826