1 /* 2 * chap_ms.c - Microsoft MS-CHAP compatible implementation. 3 * 4 * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 5 * Use is subject to license terms. 6 * 7 * Copyright (c) 1995 Eric Rosenquist, Strata Software Limited. 8 * http://www.strataware.com/ 9 * 10 * All rights reserved. 11 * 12 * Redistribution and use in source and binary forms are permitted 13 * provided that the above copyright notice and this paragraph are 14 * duplicated in all such forms and that any documentation, 15 * advertising materials, and other materials related to such 16 * distribution and use acknowledge that the software was developed 17 * by Eric Rosenquist. The name of the author may not be used to 18 * endorse or promote products derived from this software without 19 * specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 22 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 23 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. 24 */ 25 26 /* 27 * This module implements MS-CHAPv1 (RFC 2433) and MS-CHAPv2 (RFC 2759). 28 * 29 * Modifications by Lauri Pesonen / lpesonen@clinet.fi, april 1997 30 * 31 * Implemented LANManager type password response to MS-CHAP challenges. 32 * Now pppd provides both NT style and LANMan style blocks, and the 33 * prefered is set by option "ms-lanman". Default is to use NT. 34 * The hash text (StdText) was taken from Win95 RASAPI32.DLL. 35 * 36 * You should also use DOMAIN\\USERNAME as described in README.MSCHAP80 37 * 38 * Modifications by James Carlson / james.d.carlson@sun.com, June 1st, 2000. 39 * 40 * Added MS-CHAPv2 support. 41 */ 42 43 #if defined(CHAPMS) || defined(CHAPMSV2) 44 45 #include <stdio.h> 46 #include <stdlib.h> 47 #include <string.h> 48 #include <ctype.h> 49 #include <sys/types.h> 50 #include <sys/time.h> 51 #include <unistd.h> 52 #ifdef HAVE_CRYPT_H 53 #include <crypt.h> 54 #endif 55 56 #ifdef CHAPMSV2 57 #include "sha1.h" 58 #endif 59 60 #ifndef USE_CRYPT 61 #include <des.h> 62 #endif 63 64 #include "pppd.h" 65 #include "chap.h" 66 #include "chap_ms.h" 67 #include "md4.h" 68 69 typedef struct { 70 u_char LANManResp[24]; 71 u_char NTResp[24]; 72 u_char UseNT; /* If 1, ignore the LANMan response field */ 73 } MS_ChapResponse; 74 /* We use MS_CHAP_RESPONSE_LEN, rather than sizeof(MS_ChapResponse), 75 in case this struct gets padded. */ 76 77 typedef struct { 78 u_char PeerChallenge[16]; 79 u_char MustBeZero[8]; 80 u_char NTResp[24]; 81 u_char Flags; /* Should be zero (Win98 sends 04) */ 82 } MS_Chapv2Response; 83 /* We use MS_CHAPV2_RESPONSE_LEN, rather than sizeof(MS_Chapv2Response), 84 in case this struct gets padded. */ 85 86 static void ChallengeResponse __P((u_char *, u_char *, u_char *)); 87 static void DesEncrypt __P((u_char *, u_char *, u_char *)); 88 static void MakeKey __P((u_char *, u_char *)); 89 static u_char Get7Bits __P((u_char *, int)); 90 #ifdef CHAPMS 91 static void ChapMS_NT __P((u_char *, char *, int, MS_ChapResponse *)); 92 #ifdef MSLANMAN 93 static void ChapMS_LANMan __P((u_char *, char *, int, MS_ChapResponse *)); 94 #endif 95 #endif 96 #ifdef CHAPMSV2 97 static void ChapMSv2_NT __P((char *, u_char *, char *, int, 98 MS_Chapv2Response *)); 99 #endif 100 101 #ifdef USE_CRYPT 102 static void Expand __P((u_char *, char *)); 103 static void Collapse __P((char *, u_char *)); 104 #endif 105 106 #if defined(MSLANMAN) && defined(CHAPMS) 107 bool ms_lanman = 0; /* Use LanMan password instead of NT */ 108 /* Has meaning only with MS-CHAP challenges */ 109 #endif 110 111 #ifdef CHAPMSV2 112 /* Specially-formatted Microsoft CHAP response message. */ 113 static char status_message[256]; 114 #endif 115 116 static void 117 ChallengeResponse(challenge, pwHash, response) 118 u_char *challenge; /* IN 8 octets */ 119 u_char *pwHash; /* IN 16 octets */ 120 u_char *response; /* OUT 24 octets */ 121 { 122 u_char ZPasswordHash[21]; 123 124 BZERO(ZPasswordHash, sizeof(ZPasswordHash)); 125 BCOPY(pwHash, ZPasswordHash, MD4_SIGNATURE_SIZE); 126 127 #if 0 128 dbglog("ChallengeResponse - ZPasswordHash %.*B", 129 sizeof(ZPasswordHash), ZPasswordHash); 130 #endif 131 132 DesEncrypt(challenge, ZPasswordHash + 0, response + 0); 133 DesEncrypt(challenge, ZPasswordHash + 7, response + 8); 134 DesEncrypt(challenge, ZPasswordHash + 14, response + 16); 135 136 #if 0 137 dbglog("ChallengeResponse - response %.24B", response); 138 #endif 139 } 140 141 142 #ifdef USE_CRYPT 143 static void 144 DesEncrypt(clear, key, cipher) 145 u_char *clear; /* IN 8 octets */ 146 u_char *key; /* IN 7 octets */ 147 u_char *cipher; /* OUT 8 octets */ 148 { 149 u_char des_key[8]; 150 char crypt_key[66]; 151 char des_input[66]; 152 153 MakeKey(key, des_key); 154 155 Expand(des_key, crypt_key); 156 setkey(crypt_key); 157 158 #if 0 159 CHAPDEBUG((LOG_INFO, "DesEncrypt: 8 octet input : %.8B", clear)); 160 #endif 161 162 Expand(clear, des_input); 163 encrypt(des_input, 0); 164 Collapse(des_input, cipher); 165 166 #if 0 167 CHAPDEBUG((LOG_INFO, "DesEncrypt: 8 octet output: %.8B", cipher)); 168 #endif 169 } 170 171 #else /* USE_CRYPT */ 172 173 static void 174 DesEncrypt(clear, key, cipher) 175 u_char *clear; /* IN 8 octets */ 176 u_char *key; /* IN 7 octets */ 177 u_char *cipher; /* OUT 8 octets */ 178 { 179 des_cblock des_key; 180 des_key_schedule key_schedule; 181 182 MakeKey(key, des_key); 183 184 des_set_key(&des_key, key_schedule); 185 186 #if 0 187 CHAPDEBUG((LOG_INFO, "DesEncrypt: 8 octet input : %.8B", clear)); 188 #endif 189 190 des_ecb_encrypt((des_cblock *)clear, (des_cblock *)cipher, key_schedule, 1); 191 192 #if 0 193 CHAPDEBUG((LOG_INFO, "DesEncrypt: 8 octet output: %.8B", cipher)); 194 #endif 195 } 196 197 #endif /* USE_CRYPT */ 198 199 200 static u_char Get7Bits(input, startBit) 201 u_char *input; 202 int startBit; 203 { 204 register unsigned int word; 205 206 word = (unsigned)input[startBit / 8] << 8; 207 word |= (unsigned)input[startBit / 8 + 1]; 208 209 word >>= 15 - (startBit % 8 + 7); 210 211 return word & 0xFE; 212 } 213 214 #ifdef USE_CRYPT 215 216 /* in == 8-byte string (expanded version of the 56-bit key) 217 * out == 64-byte string where each byte is either 1 or 0 218 * Note that the low-order "bit" is always ignored by by setkey() 219 */ 220 static void Expand(in, out) 221 u_char *in; 222 char *out; 223 { 224 int j, c; 225 int i; 226 227 for(i = 0; i < 64; in++){ 228 c = *in; 229 for(j = 7; j >= 0; j--) 230 *out++ = (c >> j) & 01; 231 i += 8; 232 } 233 } 234 235 /* The inverse of Expand 236 */ 237 static void Collapse(in, out) 238 char *in; 239 u_char *out; 240 { 241 int j; 242 int i; 243 unsigned int c; 244 245 for (i = 0; i < 64; i += 8, out++) { 246 c = 0; 247 for (j = 7; j >= 0; j--, in++) 248 c |= *(u_char *)in << j; 249 *out = c & 0xff; 250 } 251 } 252 #endif 253 254 static void MakeKey(key, des_key) 255 u_char *key; /* IN 56 bit DES key missing parity bits */ 256 u_char *des_key; /* OUT 64 bit DES key with parity bits added */ 257 { 258 des_key[0] = Get7Bits(key, 0); 259 des_key[1] = Get7Bits(key, 7); 260 des_key[2] = Get7Bits(key, 14); 261 des_key[3] = Get7Bits(key, 21); 262 des_key[4] = Get7Bits(key, 28); 263 des_key[5] = Get7Bits(key, 35); 264 des_key[6] = Get7Bits(key, 42); 265 des_key[7] = Get7Bits(key, 49); 266 267 #ifndef USE_CRYPT 268 des_set_odd_parity((des_cblock *)des_key); 269 #endif 270 271 #if 0 272 CHAPDEBUG((LOG_INFO, "MakeKey: 56-bit input : %.7B", key)); 273 CHAPDEBUG((LOG_INFO, "MakeKey: 64-bit output: %.8B", des_key)); 274 #endif 275 } 276 277 #ifdef CHAPMS 278 static void 279 ChapMS_NT(rchallenge, secret, secret_len, response) 280 u_char *rchallenge; 281 char *secret; 282 int secret_len; 283 MS_ChapResponse *response; 284 { 285 int i; 286 #if defined(__NetBSD__) || defined(HAVE_LIBMD) 287 /* NetBSD uses the libc md4 routines which take bytes instead of bits */ 288 int mdlen = secret_len * 2; 289 #else 290 int mdlen = secret_len * 2 * 8; 291 #endif 292 MD4_CTX md4Context; 293 u_char hash[MD4_SIGNATURE_SIZE]; 294 u_char unicodePassword[MAX_NT_PASSWORD * 2]; 295 296 /* Initialize the Unicode version of the secret (== password). */ 297 /* This implicitly supports 8-bit ISO8859/1 characters. */ 298 BZERO(unicodePassword, sizeof(unicodePassword)); 299 for (i = 0; i < secret_len; i++) 300 unicodePassword[i * 2] = (u_char)secret[i]; 301 302 MD4Init(&md4Context); 303 MD4Update(&md4Context, unicodePassword, mdlen); 304 305 MD4Final(hash, &md4Context); /* Tell MD4 we're done */ 306 307 ChallengeResponse(rchallenge, hash, response->NTResp); 308 } 309 310 #ifdef MSLANMAN 311 static u_char *StdText = (u_char *)"KGS!@#$%"; /* key from rasapi32.dll */ 312 313 static void 314 ChapMS_LANMan(rchallenge, secret, secret_len, response) 315 u_char *rchallenge; 316 char *secret; 317 int secret_len; 318 MS_ChapResponse *response; 319 { 320 int i; 321 u_char UcasePassword[MAX_NT_PASSWORD]; /* max is actually 14 */ 322 u_char PasswordHash[MD4_SIGNATURE_SIZE]; 323 324 /* LANMan password is case insensitive */ 325 BZERO(UcasePassword, sizeof(UcasePassword)); 326 for (i = 0; i < secret_len; i++) 327 UcasePassword[i] = (u_char)( 328 islower(secret[i]) ? toupper(secret[i]) : secret[i]); 329 DesEncrypt( StdText, UcasePassword + 0, PasswordHash + 0 ); 330 DesEncrypt( StdText, UcasePassword + 7, PasswordHash + 8 ); 331 ChallengeResponse(rchallenge, PasswordHash, response->LANManResp); 332 } 333 #endif 334 335 void 336 ChapMS(cstate, rchallenge, rchallenge_len, secret, secret_len) 337 chap_state *cstate; 338 u_char *rchallenge; 339 int rchallenge_len; 340 char *secret; 341 int secret_len; 342 { 343 MS_ChapResponse response; 344 345 if (rchallenge_len < 8) { 346 cstate->resp_length = 0; 347 return; 348 } 349 350 #if 0 351 CHAPDEBUG((LOG_INFO, "ChapMS: secret is '%.*s'", secret_len, secret)); 352 #endif 353 BZERO(&response, sizeof(response)); 354 355 /* Calculate both always */ 356 ChapMS_NT(rchallenge, secret, secret_len, &response); 357 358 #ifdef MSLANMAN 359 ChapMS_LANMan(rchallenge, secret, secret_len, &response); 360 361 /* prefered method is set by option */ 362 response.UseNT = !ms_lanman; 363 #else 364 response.UseNT = 1; 365 #endif 366 367 BCOPY(&response, cstate->response, MS_CHAP_RESPONSE_LEN); 368 cstate->resp_length = MS_CHAP_RESPONSE_LEN; 369 } 370 371 static int 372 ChapMSStatus(cstate, flag) 373 chap_state *cstate; 374 int flag; 375 { 376 if (flag != 0) { 377 cstate->stat_message = NULL; 378 cstate->stat_length = 0; 379 } else { 380 cstate->stat_message = "E=691 R=0 M=\"Authentication failed\""; 381 cstate->stat_length = strlen(cstate->stat_message); 382 } 383 return (flag); 384 } 385 386 int 387 ChapMSValidate(cstate, response, response_len, secret, secret_len) 388 chap_state *cstate; 389 u_char *response; 390 int response_len; 391 char *secret; 392 int secret_len; 393 { 394 MS_ChapResponse ckresp; 395 396 if (response_len < MS_CHAP_RESPONSE_LEN || cstate->chal_len < 8) 397 return (0); 398 399 BZERO(&ckresp, sizeof(ckresp)); 400 401 if (response[MS_CHAP_RESPONSE_LEN-1]) { 402 ChapMS_NT(cstate->challenge, secret, secret_len, &ckresp); 403 return (ChapMSStatus(cstate, memcmp(ckresp.NTResp, response+24, 404 24) == 0)); 405 } 406 407 #ifdef MSLANMAN 408 ChapMS_LANMan(cstate->challenge, secret, secret_len, &ckresp); 409 return (ChapMSStatus(cstate, 410 memcmp(ckresp.LANManResp, response, 24) == 0)); 411 #else 412 return (ChapMSStatus(cstate, 0)); 413 #endif 414 } 415 #endif /* CHAPMS */ 416 417 #ifdef CHAPMSV2 418 static void 419 ChallengeHash(peerchallenge, authenticatorchallenge, username, challenge) 420 u_char *peerchallenge, *authenticatorchallenge, *challenge; 421 char *username; 422 { 423 uint8_t digest[20]; 424 SHA1_CTX sha1Context; 425 char *cp; 426 427 SHA1Init(&sha1Context); 428 SHA1Update(&sha1Context, peerchallenge, 16); 429 SHA1Update(&sha1Context, authenticatorchallenge, 16); 430 431 /* 432 * Only the user name (as presented by the peer and 433 * excluding any prepended domain name) 434 * is used as input to SHAUpdate(). 435 */ 436 if ((cp = strchr(username,'\\')) != NULL) 437 username = cp; 438 439 SHA1Update(&sha1Context, (uint8_t *)username, strlen(username)); 440 SHA1Final(digest, &sha1Context); 441 BCOPY(digest, challenge, 8); 442 } 443 444 static void 445 ChapMSv2_NT(username, rchallenge, secret, secret_len, response) 446 char *username; 447 u_char *rchallenge; 448 char *secret; 449 int secret_len; 450 MS_Chapv2Response *response; 451 { 452 int i; 453 #if defined(__NetBSD__) || defined(HAVE_LIBMD) 454 /* NetBSD uses the libc md4 routines that take bytes instead of bits */ 455 int mdlen = secret_len * 2; 456 #else 457 int mdlen = secret_len * 2 * 8; 458 #endif 459 MD4_CTX md4Context; 460 u_char hash[MD4_SIGNATURE_SIZE]; 461 u_char challenge[8]; 462 u_char unicodePassword[MAX_NT_PASSWORD * 2]; 463 464 /* Initialize the Unicode version of the secret (== password). */ 465 /* This implicitly supports 8-bit ISO8859/1 characters. */ 466 BZERO(unicodePassword, sizeof(unicodePassword)); 467 for (i = 0; i < secret_len && i < MAX_NT_PASSWORD; i++) 468 if ((unicodePassword[i * 2] = (u_char)secret[i]) == '\0') 469 break; 470 471 ChallengeHash(response->PeerChallenge, rchallenge, username, challenge); 472 473 MD4Init(&md4Context); 474 MD4Update(&md4Context, unicodePassword, mdlen); 475 476 MD4Final(hash, &md4Context); /* Tell MD4 we're done */ 477 478 ChallengeResponse(challenge, hash, response->NTResp); 479 } 480 481 void 482 ChapMSv2(cstate, rchallenge, rchallenge_len, secret, secret_len) 483 chap_state *cstate; 484 u_char *rchallenge; 485 int rchallenge_len; 486 char *secret; 487 int secret_len; 488 { 489 MS_Chapv2Response response; 490 u_char *ptr; 491 int i; 492 493 if (rchallenge_len < 8) { 494 cstate->resp_length = 0; 495 return; 496 } 497 498 BZERO(&response, sizeof(response)); 499 500 ptr = response.PeerChallenge; 501 for (i = 0; i < 16; i++) 502 *ptr++ = (u_char) (drand48() * 0xff); 503 504 ChapMSv2_NT(cstate->resp_name, rchallenge, secret, secret_len, &response); 505 506 BCOPY(&response, cstate->response, MS_CHAPV2_RESPONSE_LEN); 507 cstate->resp_length = MS_CHAPV2_RESPONSE_LEN; 508 } 509 510 static void 511 ChapMSv2Success(cstate, msresp, authchall, rhostname, secret, secret_len) 512 chap_state *cstate; 513 MS_Chapv2Response *msresp; 514 u_char *authchall; 515 char *rhostname, *secret; 516 int secret_len; 517 { 518 static const u_char Magic1[39] = "Magic server to client signing constant"; 519 static const u_char Magic2[41] = 520 "Pad to make it do more than one iteration"; 521 #if defined(__NetBSD__) || defined(HAVE_LIBMD) 522 /* NetBSD uses the libc md4 routines that take bytes instead of bits */ 523 int mdlen = 1; 524 #else 525 int mdlen = 8; 526 #endif 527 u_char unicodePassword[MAX_NT_PASSWORD * 2]; 528 MD4_CTX md4Context; 529 u_char hash[MD4_SIGNATURE_SIZE]; 530 u_char hashhash[MD4_SIGNATURE_SIZE]; 531 SHA1_CTX sha1Context; 532 uint8_t digest[20]; 533 u_char challenge[8]; 534 char *cp; 535 static const char hexdig[] = "0123456789ABCDEF"; 536 int i; 537 538 /* Initialize the Unicode version of the secret (== password). */ 539 /* This implicitly supports 8-bit ISO8859/1 characters. */ 540 BZERO(unicodePassword, sizeof(unicodePassword)); 541 for (i = 0; i < secret_len && i < MAX_NT_PASSWORD; i++) 542 if ((unicodePassword[i * 2] = (u_char)secret[i]) == '\0') 543 break; 544 545 /* Hash the password with MD4 */ 546 MD4Init(&md4Context); 547 MD4Update(&md4Context, unicodePassword, secret_len * 2 * mdlen); 548 MD4Final(hash, &md4Context); 549 550 /* Now hash the hash */ 551 MD4Init(&md4Context); 552 MD4Update(&md4Context, hash, MD4_SIGNATURE_SIZE * mdlen); 553 MD4Final(hashhash, &md4Context); 554 555 SHA1Init(&sha1Context); 556 SHA1Update(&sha1Context, hashhash, MD4_SIGNATURE_SIZE); 557 SHA1Update(&sha1Context, msresp->NTResp, sizeof (msresp->NTResp)); 558 SHA1Update(&sha1Context, Magic1, 39); 559 SHA1Final(digest, &sha1Context); 560 561 ChallengeHash(msresp->PeerChallenge, authchall, rhostname, challenge); 562 563 SHA1Init(&sha1Context); 564 SHA1Update(&sha1Context, digest, 20); 565 SHA1Update(&sha1Context, challenge, 8); 566 SHA1Update(&sha1Context, Magic2, 41); 567 SHA1Final(digest, &sha1Context); 568 569 cp = status_message; 570 *cp++ = 'S'; 571 *cp++ = '='; 572 for (i = 0; i < 20; i++) { 573 *cp++ = hexdig[digest[i]>>4]; 574 *cp++ = hexdig[digest[i]&15]; 575 } 576 /* 577 * RFC 2759 says that a M=<string> greeting message is possible 578 * here. It lies. Any such greeting causes Windoze-98 to give 579 * error number 742, "Dial-Up Networking was unable to complete 580 * the connection. The computer you're dialing in to does not 581 * support the data encryption requirements specified. Please 582 * check your encryption settings in the properties of the 583 * connection. If this problem persists, contact your network 584 * administrator." 585 */ 586 *cp = '\0'; 587 #if 0 588 slprintf(cp, sizeof (status_message) - (cp-status_message), 589 "M=\"Welcome to %s.\"", hostname); 590 #endif 591 cstate->stat_message = status_message; 592 cstate->stat_length = strlen(status_message); 593 } 594 595 int 596 ChapMSv2Validate(cstate, rhostname, response, response_len, secret, secret_len) 597 chap_state *cstate; 598 char *rhostname; 599 u_char *response; 600 int response_len; 601 char *secret; 602 int secret_len; 603 { 604 MS_Chapv2Response ckresp; 605 606 if (response_len < MS_CHAPV2_RESPONSE_LEN || 607 /* response[MS_CHAPV2_RESPONSE_LEN-1] != 0 || */cstate->chal_len < 8) { 608 cstate->stat_message = NULL; 609 cstate->stat_length = 0; 610 return 0; 611 } 612 613 BZERO(&ckresp, sizeof(ckresp)); 614 615 BCOPY(response, ckresp.PeerChallenge, 16); 616 617 ChapMSv2_NT(rhostname, cstate->challenge, secret, secret_len, &ckresp); 618 if (memcmp(ckresp.NTResp, response+24, 24) != 0) { 619 cstate->stat_message = "E=691 R=0 C=11111111111111111111111111111111 V=3 M=\"Authentication failed\""; 620 cstate->stat_length = strlen(cstate->stat_message); 621 return (0); 622 } 623 ChapMSv2Success(cstate, (MS_Chapv2Response *)response, cstate->challenge, 624 rhostname, secret, secret_len); 625 return (1); 626 } 627 #endif /* CHAPMSV2 */ 628 629 #endif /* CHAPMS or CHAPMSV2 */ 630