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