chap.c revision 44122
1/* 2 * PPP CHAP Module 3 * 4 * Written by Toshiharu OHNO (tony-o@iij.ad.jp) 5 * 6 * Copyright (C) 1993, Internet Initiative Japan, Inc. All rights reserverd. 7 * 8 * Redistribution and use in source and binary forms are permitted 9 * provided that the above copyright notice and this paragraph are 10 * duplicated in all such forms and that any documentation, 11 * advertising materials, and other materials related to such 12 * distribution and use acknowledge that the software was developed 13 * by the Internet Initiative Japan, Inc. The name of the 14 * IIJ may not be used to endorse or promote products derived 15 * from this software without specific prior written permission. 16 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 17 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 18 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. 19 * 20 * $Id: chap.c,v 1.44 1999/02/18 00:52:12 brian Exp $ 21 * 22 * TODO: 23 */ 24#include <sys/param.h> 25#include <netinet/in.h> 26#include <netinet/in_systm.h> 27#include <netinet/ip.h> 28#include <sys/un.h> 29 30#include <errno.h> 31#include <fcntl.h> 32#ifdef HAVE_DES 33#include <md4.h> 34#endif 35#include <md5.h> 36#include <paths.h> 37#include <signal.h> 38#include <stdlib.h> 39#include <string.h> 40#include <sys/wait.h> 41#include <termios.h> 42#include <unistd.h> 43 44#include "mbuf.h" 45#include "log.h" 46#include "defs.h" 47#include "timer.h" 48#include "fsm.h" 49#include "lcpproto.h" 50#include "lcp.h" 51#include "lqr.h" 52#include "hdlc.h" 53#include "auth.h" 54#include "async.h" 55#include "throughput.h" 56#include "descriptor.h" 57#include "chap.h" 58#include "iplist.h" 59#include "slcompress.h" 60#include "ipcp.h" 61#include "filter.h" 62#include "ccp.h" 63#include "link.h" 64#include "physical.h" 65#include "mp.h" 66#ifndef NORADIUS 67#include "radius.h" 68#endif 69#include "bundle.h" 70#include "chat.h" 71#include "cbcp.h" 72#include "command.h" 73#include "datalink.h" 74#ifdef HAVE_DES 75#include "chap_ms.h" 76#endif 77 78static const char *chapcodes[] = { 79 "???", "CHALLENGE", "RESPONSE", "SUCCESS", "FAILURE" 80}; 81#define MAXCHAPCODE (sizeof chapcodes / sizeof chapcodes[0] - 1) 82 83static void 84ChapOutput(struct physical *physical, u_int code, u_int id, 85 const u_char *ptr, int count, const char *text) 86{ 87 int plen; 88 struct fsmheader lh; 89 struct mbuf *bp; 90 91 plen = sizeof(struct fsmheader) + count; 92 lh.code = code; 93 lh.id = id; 94 lh.length = htons(plen); 95 bp = mbuf_Alloc(plen, MB_FSM); 96 memcpy(MBUF_CTOP(bp), &lh, sizeof(struct fsmheader)); 97 if (count) 98 memcpy(MBUF_CTOP(bp) + sizeof(struct fsmheader), ptr, count); 99 log_DumpBp(LogDEBUG, "ChapOutput", bp); 100 if (text == NULL) 101 log_Printf(LogPHASE, "Chap Output: %s\n", chapcodes[code]); 102 else 103 log_Printf(LogPHASE, "Chap Output: %s (%s)\n", chapcodes[code], text); 104 hdlc_Output(&physical->link, PRI_LINK, PROTO_CHAP, bp); 105} 106 107static char * 108chap_BuildAnswer(char *name, char *key, u_char id, char *challenge, 109 u_char type, int lanman) 110{ 111 char *result, *digest; 112 size_t nlen, klen; 113 114 nlen = strlen(name); 115 klen = strlen(key); 116 117#ifdef HAVE_DES 118 if (type == 0x80) { 119 char expkey[AUTHLEN << 2]; 120 MD4_CTX MD4context; 121 int f; 122 123 if ((result = malloc(1 + nlen + MS_CHAP_RESPONSE_LEN)) == NULL) 124 return result; 125 126 digest = result; /* the response */ 127 *digest++ = MS_CHAP_RESPONSE_LEN; /* 49 */ 128 memcpy(digest + MS_CHAP_RESPONSE_LEN, name, nlen); 129 if (lanman) { 130 memset(digest + 24, '\0', 25); 131 mschap_LANMan(digest, challenge + 1, key); /* LANMan response */ 132 } else { 133 memset(digest, '\0', 25); 134 digest += 24; 135 136 for (f = 0; f < klen; f++) { 137 expkey[2*f] = key[f]; 138 expkey[2*f+1] = '\0'; 139 } 140 /* 141 * ----------- 142 * expkey = | k\0e\0y\0 | 143 * ----------- 144 */ 145 MD4Init(&MD4context); 146 MD4Update(&MD4context, expkey, klen << 1); 147 MD4Final(digest, &MD4context); 148 149 /* 150 * ---- -------- ---------------- ------- ------ 151 * result = | 49 | LANMan | 16 byte digest | 9 * ? | name | 152 * ---- -------- ---------------- ------- ------ 153 */ 154 mschap_NT(digest, challenge + 1); 155 } 156 /* 157 * ---- -------- ------------- ----- ------ 158 * | | struct MS_ChapResponse24 | | 159 * result = | 49 | LANMan | NT digest | 0/1 | name | 160 * ---- -------- ------------- ----- ------ 161 * where only one of LANMan & NT digest are set. 162 */ 163 } else 164#endif 165 if ((result = malloc(nlen + 17)) != NULL) { 166 /* Normal MD5 stuff */ 167 MD5_CTX MD5context; 168 169 digest = result; 170 *digest++ = 16; /* value size */ 171 172 MD5Init(&MD5context); 173 MD5Update(&MD5context, &id, 1); 174 MD5Update(&MD5context, key, klen); 175 MD5Update(&MD5context, challenge + 1, *challenge); 176 MD5Final(digest, &MD5context); 177 178 memcpy(digest + 16, name, nlen); 179 /* 180 * ---- -------- ------ 181 * result = | 16 | digest | name | 182 * ---- -------- ------ 183 */ 184 } 185 186 return result; 187} 188 189static void 190chap_StartChild(struct chap *chap, char *prog, const char *name) 191{ 192 char *argv[MAXARGS], *nargv[MAXARGS]; 193 int argc, fd; 194 int in[2], out[2]; 195 196 if (chap->child.fd != -1) { 197 log_Printf(LogWARN, "Chap: %s: Program already running\n", prog); 198 return; 199 } 200 201 if (pipe(in) == -1) { 202 log_Printf(LogERROR, "Chap: pipe: %s\n", strerror(errno)); 203 return; 204 } 205 206 if (pipe(out) == -1) { 207 log_Printf(LogERROR, "Chap: pipe: %s\n", strerror(errno)); 208 close(in[0]); 209 close(in[1]); 210 return; 211 } 212 213 switch ((chap->child.pid = fork())) { 214 case -1: 215 log_Printf(LogERROR, "Chap: fork: %s\n", strerror(errno)); 216 close(in[0]); 217 close(in[1]); 218 close(out[0]); 219 close(out[1]); 220 chap->child.pid = 0; 221 return; 222 223 case 0: 224 timer_TermService(); 225 close(in[1]); 226 close(out[0]); 227 if (out[1] == STDIN_FILENO) { 228 fd = dup(out[1]); 229 close(out[1]); 230 out[1] = fd; 231 } 232 dup2(in[0], STDIN_FILENO); 233 dup2(out[1], STDOUT_FILENO); 234 if ((fd = open(_PATH_DEVNULL, O_RDWR)) == -1) { 235 log_Printf(LogALERT, "Chap: Failed to open %s: %s\n", 236 _PATH_DEVNULL, strerror(errno)); 237 exit(1); 238 } 239 dup2(fd, STDERR_FILENO); 240 fcntl(3, F_SETFD, 1); /* Set close-on-exec flag */ 241 242 setuid(geteuid()); 243 argc = command_Interpret(prog, strlen(prog), argv); 244 command_Expand(nargv, argc, (char const *const *)argv, 245 chap->auth.physical->dl->bundle, 0); 246 execvp(nargv[0], nargv); 247 248 log_Printf(LogWARN, "exec() of %s failed: %s\n", 249 nargv[0], strerror(errno)); 250 exit(255); 251 252 default: 253 close(in[0]); 254 close(out[1]); 255 chap->child.fd = out[0]; 256 chap->child.buf.len = 0; 257 write(in[1], chap->auth.in.name, strlen(chap->auth.in.name)); 258 write(in[1], "\n", 1); 259 write(in[1], chap->challenge + 1, *chap->challenge); 260 write(in[1], "\n", 1); 261 write(in[1], name, strlen(name)); 262 write(in[1], "\n", 1); 263 close(in[1]); 264 break; 265 } 266} 267 268static void 269chap_Cleanup(struct chap *chap, int sig) 270{ 271 if (chap->child.pid) { 272 int status; 273 274 close(chap->child.fd); 275 chap->child.fd = -1; 276 if (sig) 277 kill(chap->child.pid, SIGTERM); 278 chap->child.pid = 0; 279 chap->child.buf.len = 0; 280 281 if (wait(&status) == -1) 282 log_Printf(LogERROR, "Chap: wait: %s\n", strerror(errno)); 283 else if (WIFSIGNALED(status)) 284 log_Printf(LogWARN, "Chap: Child received signal %d\n", WTERMSIG(status)); 285 else if (WIFEXITED(status) && WEXITSTATUS(status)) 286 log_Printf(LogERROR, "Chap: Child exited %d\n", WEXITSTATUS(status)); 287 } 288 *chap->challenge = 0; 289 chap->peertries = 0; 290} 291 292static void 293chap_Respond(struct chap *chap, char *name, char *key, u_char type, int lm) 294{ 295 u_char *ans; 296 297 ans = chap_BuildAnswer(name, key, chap->auth.id, chap->challenge, type, lm); 298 299 if (ans) { 300 ChapOutput(chap->auth.physical, CHAP_RESPONSE, chap->auth.id, 301 ans, *ans + 1 + strlen(name), name); 302 chap->NTRespSent = !lm; 303 free(ans); 304 } else 305 ChapOutput(chap->auth.physical, CHAP_FAILURE, chap->auth.id, 306 "Out of memory!", 14, NULL); 307} 308 309static int 310chap_UpdateSet(struct descriptor *d, fd_set *r, fd_set *w, fd_set *e, int *n) 311{ 312 struct chap *chap = descriptor2chap(d); 313 314 if (r && chap && chap->child.fd != -1) { 315 FD_SET(chap->child.fd, r); 316 if (*n < chap->child.fd + 1) 317 *n = chap->child.fd + 1; 318 log_Printf(LogTIMER, "Chap: fdset(r) %d\n", chap->child.fd); 319 return 1; 320 } 321 322 return 0; 323} 324 325static int 326chap_IsSet(struct descriptor *d, const fd_set *fdset) 327{ 328 struct chap *chap = descriptor2chap(d); 329 330 return chap && chap->child.fd != -1 && FD_ISSET(chap->child.fd, fdset); 331} 332 333static void 334chap_Read(struct descriptor *d, struct bundle *bundle, const fd_set *fdset) 335{ 336 struct chap *chap = descriptor2chap(d); 337 int got; 338 339 got = read(chap->child.fd, chap->child.buf.ptr + chap->child.buf.len, 340 sizeof chap->child.buf.ptr - chap->child.buf.len - 1); 341 if (got == -1) { 342 log_Printf(LogERROR, "Chap: Read: %s\n", strerror(errno)); 343 chap_Cleanup(chap, SIGTERM); 344 } else if (got == 0) { 345 log_Printf(LogWARN, "Chap: Read: Child terminated connection\n"); 346 chap_Cleanup(chap, SIGTERM); 347 } else { 348 char *name, *key, *end; 349 350 chap->child.buf.len += got; 351 chap->child.buf.ptr[chap->child.buf.len] = '\0'; 352 name = chap->child.buf.ptr; 353 name += strspn(name, " \t"); 354 if ((key = strchr(name, '\n')) == NULL) 355 end = NULL; 356 else 357 end = strchr(++key, '\n'); 358 359 if (end == NULL) { 360 if (chap->child.buf.len == sizeof chap->child.buf.ptr - 1) { 361 log_Printf(LogWARN, "Chap: Read: Input buffer overflow\n"); 362 chap_Cleanup(chap, SIGTERM); 363 } 364 } else { 365 int lanman = chap->auth.physical->link.lcp.his_authtype == 0x80 && 366 ((chap->NTRespSent && 367 IsAccepted(chap->auth.physical->link.lcp.cfg.chap80lm)) || 368 !IsAccepted(chap->auth.physical->link.lcp.cfg.chap80nt)); 369 370 while (end >= name && strchr(" \t\r\n", *end)) 371 *end-- = '\0'; 372 end = key - 1; 373 while (end >= name && strchr(" \t\r\n", *end)) 374 *end-- = '\0'; 375 key += strspn(key, " \t"); 376 377 chap_Respond(chap, name, key, 378 chap->auth.physical->link.lcp.his_authtype, lanman); 379 chap_Cleanup(chap, 0); 380 } 381 } 382} 383 384static int 385chap_Write(struct descriptor *d, struct bundle *bundle, const fd_set *fdset) 386{ 387 /* We never want to write here ! */ 388 log_Printf(LogALERT, "chap_Write: Internal error: Bad call !\n"); 389 return 0; 390} 391 392static void 393chap_Challenge(struct authinfo *authp) 394{ 395 struct chap *chap = auth2chap(authp); 396 int len, i; 397 char *cp; 398 399 len = strlen(authp->physical->dl->bundle->cfg.auth.name); 400 401 if (!*chap->challenge) { 402 randinit(); 403 cp = chap->challenge; 404 405#ifndef NORADIUS 406 if (*authp->physical->dl->bundle->radius.cfg.file) { 407 /* For radius, our challenge is 16 readable NUL terminated bytes :*/ 408 *cp++ = 16; 409 for (i = 0; i < 16; i++) 410 *cp++ = (random() % 10) + '0'; 411 } else 412#endif 413 { 414#ifdef HAVE_DES 415 if (authp->physical->link.lcp.want_authtype == 0x80) 416 *cp++ = 8; /* MS does 8 byte callenges :-/ */ 417 else 418#endif 419 *cp++ = random() % (CHAPCHALLENGELEN-16) + 16; 420 for (i = 0; i < *chap->challenge; i++) 421 *cp++ = random() & 0xff; 422 } 423 memcpy(cp, authp->physical->dl->bundle->cfg.auth.name, len); 424 } 425 ChapOutput(authp->physical, CHAP_CHALLENGE, authp->id, chap->challenge, 426 1 + *chap->challenge + len, NULL); 427} 428 429static void 430chap_Success(struct authinfo *authp) 431{ 432 datalink_GotAuthname(authp->physical->dl, authp->in.name); 433 ChapOutput(authp->physical, CHAP_SUCCESS, authp->id, "Welcome!!", 10, NULL); 434 authp->physical->link.lcp.auth_ineed = 0; 435 if (Enabled(authp->physical->dl->bundle, OPT_UTMP)) 436 physical_Login(authp->physical, authp->in.name); 437 438 if (authp->physical->link.lcp.auth_iwait == 0) 439 /* 440 * Either I didn't need to authenticate, or I've already been 441 * told that I got the answer right. 442 */ 443 datalink_AuthOk(authp->physical->dl); 444} 445 446static void 447chap_Failure(struct authinfo *authp) 448{ 449 ChapOutput(authp->physical, CHAP_FAILURE, authp->id, "Invalid!!", 9, NULL); 450 datalink_AuthNotOk(authp->physical->dl); 451} 452 453static int 454chap_Cmp(u_char type, int lm, char *myans, int mylen, char *hisans, int hislen) 455{ 456 if (mylen != hislen) 457 return 0; 458 else if (type == 0x80) { 459 int off = lm ? 0 : 24; 460 461 if (memcmp(myans + off, hisans + off, 24)) 462 return 0; 463 } else if (memcmp(myans, hisans, mylen)) 464 return 0; 465 466 return 1; 467} 468 469static int 470chap_HaveAnotherGo(struct chap *chap) 471{ 472 if (++chap->peertries < 3) { 473 /* Give the peer another shot */ 474 *chap->challenge = '\0'; 475 chap_Challenge(&chap->auth); 476 return 1; 477 } 478 479 return 0; 480} 481 482void 483chap_Init(struct chap *chap, struct physical *p) 484{ 485 chap->desc.type = CHAP_DESCRIPTOR; 486 chap->desc.UpdateSet = chap_UpdateSet; 487 chap->desc.IsSet = chap_IsSet; 488 chap->desc.Read = chap_Read; 489 chap->desc.Write = chap_Write; 490 chap->child.pid = 0; 491 chap->child.fd = -1; 492 auth_Init(&chap->auth, p, chap_Challenge, chap_Success, chap_Failure); 493 *chap->challenge = 0; 494 chap->NTRespSent = 0; 495 chap->peertries = 0; 496} 497 498void 499chap_ReInit(struct chap *chap) 500{ 501 chap_Cleanup(chap, SIGTERM); 502} 503 504void 505chap_Input(struct physical *p, struct mbuf *bp) 506{ 507 struct chap *chap = &p->dl->chap; 508 char *name, *key, *ans; 509 int len, nlen, lanman; 510 u_char alen; 511 512 if ((bp = auth_ReadHeader(&chap->auth, bp)) == NULL) 513 log_Printf(LogERROR, "Chap Input: Truncated header !\n"); 514 else if (chap->auth.in.hdr.code == 0 || chap->auth.in.hdr.code > MAXCHAPCODE) 515 log_Printf(LogPHASE, "Chap Input: %d: Bad CHAP code !\n", 516 chap->auth.in.hdr.code); 517 else { 518 len = mbuf_Length(bp); 519 ans = NULL; 520 521 if (chap->auth.in.hdr.code != CHAP_CHALLENGE && 522 chap->auth.id != chap->auth.in.hdr.id && 523 Enabled(p->dl->bundle, OPT_IDCHECK)) { 524 /* Wrong conversation dude ! */ 525 log_Printf(LogPHASE, "Chap Input: %s dropped (got id %d, not %d)\n", 526 chapcodes[chap->auth.in.hdr.code], chap->auth.in.hdr.id, 527 chap->auth.id); 528 mbuf_Free(bp); 529 return; 530 } 531 chap->auth.id = chap->auth.in.hdr.id; /* We respond with this id */ 532 533 lanman = 0; 534 switch (chap->auth.in.hdr.code) { 535 case CHAP_CHALLENGE: 536 bp = mbuf_Read(bp, &alen, 1); 537 len -= alen + 1; 538 if (len < 0) { 539 log_Printf(LogERROR, "Chap Input: Truncated challenge !\n"); 540 mbuf_Free(bp); 541 return; 542 } 543 *chap->challenge = alen; 544 bp = mbuf_Read(bp, chap->challenge + 1, alen); 545 bp = auth_ReadName(&chap->auth, bp, len); 546 lanman = p->link.lcp.his_authtype == 0x80 && 547 ((chap->NTRespSent && IsAccepted(p->link.lcp.cfg.chap80lm)) || 548 !IsAccepted(p->link.lcp.cfg.chap80nt)); 549 break; 550 551 case CHAP_RESPONSE: 552 auth_StopTimer(&chap->auth); 553 bp = mbuf_Read(bp, &alen, 1); 554 len -= alen + 1; 555 if (len < 0) { 556 log_Printf(LogERROR, "Chap Input: Truncated response !\n"); 557 mbuf_Free(bp); 558 return; 559 } 560 if ((ans = malloc(alen + 2)) == NULL) { 561 log_Printf(LogERROR, "Chap Input: Out of memory !\n"); 562 mbuf_Free(bp); 563 return; 564 } 565 *ans = chap->auth.id; 566 bp = mbuf_Read(bp, ans + 1, alen); 567 ans[alen+1] = '\0'; 568 bp = auth_ReadName(&chap->auth, bp, len); 569 lanman = alen == 49 && ans[alen] == 0; 570 break; 571 572 case CHAP_SUCCESS: 573 case CHAP_FAILURE: 574 /* chap->auth.in.name is already set up at CHALLENGE time */ 575 if ((ans = malloc(len + 1)) == NULL) { 576 log_Printf(LogERROR, "Chap Input: Out of memory !\n"); 577 mbuf_Free(bp); 578 return; 579 } 580 bp = mbuf_Read(bp, ans, len); 581 ans[len] = '\0'; 582 break; 583 } 584 585 switch (chap->auth.in.hdr.code) { 586 case CHAP_CHALLENGE: 587 case CHAP_RESPONSE: 588 if (*chap->auth.in.name) 589 log_Printf(LogPHASE, "Chap Input: %s (%d bytes from %s%s)\n", 590 chapcodes[chap->auth.in.hdr.code], alen, 591 chap->auth.in.name, 592 lanman && chap->auth.in.hdr.code == CHAP_RESPONSE ? 593 " - lanman" : ""); 594 else 595 log_Printf(LogPHASE, "Chap Input: %s (%d bytes%s)\n", 596 chapcodes[chap->auth.in.hdr.code], alen, 597 lanman && chap->auth.in.hdr.code == CHAP_RESPONSE ? 598 " - lanman" : ""); 599 break; 600 601 case CHAP_SUCCESS: 602 case CHAP_FAILURE: 603 if (*ans) 604 log_Printf(LogPHASE, "Chap Input: %s (%s)\n", 605 chapcodes[chap->auth.in.hdr.code], ans); 606 else 607 log_Printf(LogPHASE, "Chap Input: %s\n", 608 chapcodes[chap->auth.in.hdr.code]); 609 break; 610 } 611 612 switch (chap->auth.in.hdr.code) { 613 case CHAP_CHALLENGE: 614 if (*p->dl->bundle->cfg.auth.key == '!') 615 chap_StartChild(chap, p->dl->bundle->cfg.auth.key + 1, 616 p->dl->bundle->cfg.auth.name); 617 else 618 chap_Respond(chap, p->dl->bundle->cfg.auth.name, 619 p->dl->bundle->cfg.auth.key, 620 p->link.lcp.his_authtype, lanman); 621 break; 622 623 case CHAP_RESPONSE: 624 name = chap->auth.in.name; 625 nlen = strlen(name); 626#ifndef NORADIUS 627 if (*p->dl->bundle->radius.cfg.file) { 628 chap->challenge[*chap->challenge+1] = '\0'; 629 radius_Authenticate(&p->dl->bundle->radius, &chap->auth, 630 chap->auth.in.name, ans, chap->challenge + 1); 631 } else 632#endif 633 { 634 key = auth_GetSecret(p->dl->bundle, name, nlen, p); 635 if (key) { 636 char *myans; 637 if (lanman && !IsEnabled(p->link.lcp.cfg.chap80lm)) { 638 log_Printf(LogPHASE, "Auth failure: LANMan not enabled\n"); 639 if (chap_HaveAnotherGo(chap)) 640 break; 641 key = NULL; 642 } else if (!lanman && !IsEnabled(p->link.lcp.cfg.chap80nt) && 643 p->link.lcp.want_authtype == 0x80) { 644 log_Printf(LogPHASE, "Auth failure: mschap not enabled\n"); 645 if (chap_HaveAnotherGo(chap)) 646 break; 647 key = NULL; 648 } else { 649 myans = chap_BuildAnswer(name, key, chap->auth.id, 650 chap->challenge, 651 p->link.lcp.want_authtype, lanman); 652 if (myans == NULL) 653 key = NULL; 654 else { 655 if (!chap_Cmp(p->link.lcp.want_authtype, lanman, 656 myans + 1, *myans, ans + 1, alen)) 657 key = NULL; 658 free(myans); 659 } 660 } 661 } 662 663 if (key) 664 chap_Success(&chap->auth); 665 else 666 chap_Failure(&chap->auth); 667 } 668 669 break; 670 671 case CHAP_SUCCESS: 672 if (p->link.lcp.auth_iwait == PROTO_CHAP) { 673 p->link.lcp.auth_iwait = 0; 674 if (p->link.lcp.auth_ineed == 0) 675 /* 676 * We've succeeded in our ``login'' 677 * If we're not expecting the peer to authenticate (or he already 678 * has), proceed to network phase. 679 */ 680 datalink_AuthOk(p->dl); 681 } 682 break; 683 684 case CHAP_FAILURE: 685 datalink_AuthNotOk(p->dl); 686 break; 687 } 688 free(ans); 689 } 690 691 mbuf_Free(bp); 692} 693