1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 * Copyright 2013 Nexenta Systems, Inc. All rights reserved. 25 */ 26 27 #include <strings.h> 28 #include <stdlib.h> 29 #include <smbsrv/string.h> 30 #include <smbsrv/libsmb.h> 31 #include <assert.h> 32 33 /* 34 * smb_auth_qnd_unicode 35 * 36 * Quick and dirty unicode conversion! 37 * Returns the length of dst in bytes. 38 */ 39 int 40 smb_auth_qnd_unicode(smb_wchar_t *dst, const char *src, int length) 41 { 42 int i; 43 unsigned int count; 44 smb_wchar_t new_char; 45 46 if ((count = oemtoucs(dst, src, length, OEM_CPG_1252)) == 0) { 47 for (i = 0; i < length; ++i) { 48 new_char = (smb_wchar_t)src[i] & 0xff; 49 dst[i] = LE_IN16(&new_char); 50 } 51 dst[i] = 0; 52 count = length; 53 } 54 55 return (count * sizeof (smb_wchar_t)); 56 } 57 58 /* 59 * smb_auth_lmupr 60 * 61 * Converts the given LM password to all uppercase. 62 * The standard strupr cannot 63 * be used here because lm_pwd doesn't have to be 64 * nul terminated. 65 */ 66 static void 67 smb_auth_lmupr(unsigned char *lm_pwd) 68 { 69 unsigned char *p = lm_pwd; 70 int i; 71 72 for (i = 0; (*p) && (i < SMBAUTH_LM_PWD_SZ); i++) { 73 if (smb_isascii(*p)) { 74 *p = smb_toupper(*p); 75 p++; 76 } 77 } 78 } 79 80 /* 81 * smb_auth_lm_hash 82 * 83 * Source: Implementing CIFS (Chris Hertel) 84 * 85 * 1. The password, as entered by user, is either padded with nulls 86 * or trimmed to 14 bytes. 87 * . Note that the 14-byte result string is not handled as a 88 * nul-terminated string. 89 * . The given password is OEM not Unicode 90 * 91 * 2. The 14-byte password is converted to all uppercase 92 * 93 * 3. The result is used as key to encrypt the KGS magic string to 94 * make a 16-byte hash. 95 */ 96 int 97 smb_auth_lm_hash(const char *password, unsigned char *lm_hash) 98 { 99 unsigned char lm_pwd[SMBAUTH_LM_PWD_SZ]; 100 101 bzero((void *)lm_pwd, SMBAUTH_LM_PWD_SZ); 102 (void) strncpy((char *)lm_pwd, password, SMBAUTH_LM_PWD_SZ); 103 smb_auth_lmupr(lm_pwd); 104 105 return (smb_auth_DES(lm_hash, SMBAUTH_HASH_SZ, lm_pwd, 106 SMBAUTH_LM_PWD_SZ, (unsigned char *)SMBAUTH_LM_MAGIC_STR, 107 sizeof (SMBAUTH_LM_MAGIC_STR))); 108 } 109 110 /* 111 * smb_auth_lm_response 112 * 113 * Create a LM response from the given LM hash and challenge. 114 * 115 * Returns SMBAUTH_FAILURE if any problems occur, SMBAUTH_SUCCESS if 116 * all goes well. 117 */ 118 static int 119 smb_auth_lm_response(unsigned char *hash, 120 unsigned char *challenge, int clen, 121 unsigned char *lm_rsp) 122 { 123 unsigned char S21[21]; 124 125 /* 126 * 14-byte LM Hash should be padded with 5 nul bytes to create 127 * a 21-byte string to be used in producing LM response 128 */ 129 bzero(&S21[SMBAUTH_HASH_SZ], 5); 130 bcopy(hash, S21, SMBAUTH_HASH_SZ); 131 132 /* padded LM Hash -> LM Response */ 133 return (smb_auth_DES(lm_rsp, SMBAUTH_LM_RESP_SZ, S21, 21, 134 challenge, clen)); 135 } 136 137 /* 138 * smb_auth_ntlm_hash 139 * 140 * Make NTLM Hash (using MD4) from the given password. 141 * The result will contain a 16-byte NTLM hash. 142 */ 143 int 144 smb_auth_ntlm_hash(const char *password, unsigned char *hash) 145 { 146 smb_wchar_t *unicode_password; 147 int length, unicode_len; 148 int rc; 149 150 if (password == NULL || hash == NULL) 151 return (SMBAUTH_FAILURE); 152 153 length = strlen(password); 154 unicode_len = (length + 1) * sizeof (smb_wchar_t); 155 unicode_password = malloc(unicode_len); 156 157 if (unicode_password == NULL) 158 return (SMBAUTH_FAILURE); 159 160 length = smb_auth_qnd_unicode(unicode_password, password, length); 161 rc = smb_auth_md4(hash, (unsigned char *)unicode_password, length); 162 163 (void) memset(unicode_password, 0, unicode_len); 164 free(unicode_password); 165 166 return (rc); 167 } 168 169 /* 170 * smb_auth_ntlm_response 171 * 172 * Make LM/NTLM response from the given LM/NTLM Hash and given 173 * challenge. 174 */ 175 static int 176 smb_auth_ntlm_response(unsigned char *hash, 177 unsigned char *challenge, int clen, 178 unsigned char *ntlm_rsp) 179 { 180 unsigned char S21[21]; 181 182 bcopy(hash, S21, SMBAUTH_HASH_SZ); 183 bzero(&S21[SMBAUTH_HASH_SZ], 5); 184 if (smb_auth_DES((unsigned char *)ntlm_rsp, SMBAUTH_LM_RESP_SZ, 185 S21, 21, challenge, clen) == SMBAUTH_FAILURE) 186 return (0); 187 return (SMBAUTH_LM_RESP_SZ); 188 } 189 190 /* 191 * smb_auth_ntlmv2_hash 192 * 193 * The NTLM v2 hash will be created from the given NTLM hash, username, 194 * and the NETBIOS name of the domain. 195 * 196 * The NTLMv2 hash will be returned via the ntlmv2_hash parameter which 197 * will be used in the calculation of the NTLMv2 and LMv2 responses. 198 */ 199 int 200 smb_auth_ntlmv2_hash(unsigned char *ntlm_hash, 201 char *username, 202 char *ntdomain, 203 unsigned char *ntlmv2_hash) 204 { 205 smb_wchar_t *data; 206 int data_len; 207 unsigned char *buf; 208 int rc; 209 210 if (username == NULL || ntdomain == NULL) 211 return (SMBAUTH_FAILURE); 212 213 (void) smb_strupr(username); 214 215 data_len = strlen(username) + strlen(ntdomain); 216 buf = (unsigned char *)malloc((data_len + 1) * sizeof (char)); 217 if (buf == NULL) 218 return (SMBAUTH_FAILURE); 219 220 (void) snprintf((char *)buf, data_len + 1, "%s%s", username, ntdomain); 221 data = (smb_wchar_t *)malloc((data_len + 1) * sizeof (smb_wchar_t)); 222 if (data == NULL) { 223 free(buf); 224 return (SMBAUTH_FAILURE); 225 } 226 227 data_len = smb_auth_qnd_unicode(data, (char *)buf, data_len); 228 rc = SMBAUTH_HMACT64((unsigned char *)data, data_len, ntlm_hash, 229 SMBAUTH_HASH_SZ, ntlmv2_hash); 230 231 free(buf); 232 free(data); 233 return (rc); 234 } 235 236 /* 237 * smb_auth_v2_response 238 * 239 * Caculates either the LMv2 or NTLMv2 response. 240 * 241 * Same algorithm is used for calculating both LMv2 or NTLMv2 responses. 242 * This routine will return NTLMv2 response if the data blob information 243 * is passed in as the clnt_data. Otherwise, it will return LMv2 response 244 * with the 8-byte client challenge(a.k.a blip) as the clnt_data. 245 * 246 * (LM/NTLM)v2 response is the hmac-md5 hash of the specified data 247 * (server challenge + NTLMv2 data blob or LMv2 client challenge) 248 * using the NTLMv2 hash as the key. 249 * 250 * Returns the size of the corresponding v2 response upon success. 251 * Otherwise, returns -1 on error. 252 */ 253 static int 254 smb_auth_v2_response( 255 unsigned char *hash, 256 unsigned char *srv_challenge, int slen, 257 unsigned char *clnt_data, int clen, 258 unsigned char *v2_rsp) 259 { 260 unsigned char *hmac_data; 261 262 hmac_data = (unsigned char *)malloc((slen + clen) * sizeof (char)); 263 if (!hmac_data) { 264 return (-1); 265 } 266 267 (void) memcpy(hmac_data, srv_challenge, slen); 268 (void) memcpy(&hmac_data[slen], clnt_data, clen); 269 if (SMBAUTH_HMACT64(hmac_data, slen + clen, (unsigned char *)hash, 270 SMBAUTH_HASH_SZ, (unsigned char *)v2_rsp) != SMBAUTH_SUCCESS) 271 return (-1); 272 (void) memcpy(&v2_rsp[SMBAUTH_HASH_SZ], clnt_data, clen); 273 274 free(hmac_data); 275 return (SMBAUTH_HASH_SZ + clen); 276 } 277 278 /* 279 * smb_auth_gen_session_key 280 * 281 * Generate the NTLM user session key if LMCompatibilityLevel is 2 or 282 * NTLMv2 user session key if LMCompatibilityLevel is 3 or above. 283 * 284 * NTLM_Session_Key = MD4(NTLM_Hash); 285 * 286 * NTLMv2_Session_Key = HMAC_MD5(NTLMv2Hash, 16, NTLMv2_HMAC, 16) 287 * 288 * Prior to calling this function, the auth instance should be set 289 * via smb_auth_set_info(). 290 * 291 * Returns the appropriate session key. 292 */ 293 int 294 smb_auth_gen_session_key(smb_auth_info_t *auth, unsigned char *session_key) 295 { 296 int rc; 297 298 if (auth->lmcompatibility_lvl == 2) 299 rc = smb_auth_md4(session_key, auth->hash, SMBAUTH_HASH_SZ); 300 else 301 rc = SMBAUTH_HMACT64((unsigned char *)auth->cs, 302 SMBAUTH_HASH_SZ, (unsigned char *)auth->hash_v2, 303 SMBAUTH_SESSION_KEY_SZ, session_key); 304 305 return (rc); 306 } 307 308 static boolean_t 309 smb_lm_password_ok( 310 unsigned char *challenge, 311 uint32_t clen, 312 unsigned char *lm_hash, 313 unsigned char *passwd) 314 { 315 unsigned char lm_resp[SMBAUTH_LM_RESP_SZ]; 316 int rc; 317 318 rc = smb_auth_lm_response(lm_hash, challenge, clen, lm_resp); 319 if (rc != SMBAUTH_SUCCESS) 320 return (B_FALSE); 321 322 return (bcmp(lm_resp, passwd, SMBAUTH_LM_RESP_SZ) == 0); 323 } 324 325 static boolean_t 326 smb_ntlm_password_ok( 327 unsigned char *challenge, 328 uint32_t clen, 329 unsigned char *ntlm_hash, 330 unsigned char *passwd, 331 unsigned char *session_key) 332 { 333 unsigned char ntlm_resp[SMBAUTH_LM_RESP_SZ]; 334 int rc; 335 boolean_t ok; 336 337 rc = smb_auth_ntlm_response(ntlm_hash, challenge, clen, ntlm_resp); 338 if (rc != SMBAUTH_LM_RESP_SZ) 339 return (B_FALSE); 340 341 ok = (bcmp(ntlm_resp, passwd, SMBAUTH_LM_RESP_SZ) == 0); 342 if (ok && (session_key)) { 343 rc = smb_auth_md4(session_key, ntlm_hash, SMBAUTH_HASH_SZ); 344 if (rc != SMBAUTH_SUCCESS) 345 ok = B_FALSE; 346 } 347 return (ok); 348 } 349 350 static boolean_t 351 smb_ntlmv2_password_ok( 352 unsigned char *challenge, 353 uint32_t clen, 354 unsigned char *ntlm_hash, 355 unsigned char *passwd, 356 int pwdlen, 357 char *domain, 358 char *username, 359 uchar_t *session_key) 360 { 361 unsigned char *clnt_blob; 362 int clnt_blob_len; 363 unsigned char ntlmv2_hash[SMBAUTH_HASH_SZ]; 364 unsigned char *ntlmv2_resp; 365 boolean_t ok = B_FALSE; 366 char *dest[3]; 367 int i; 368 int rc; 369 370 clnt_blob_len = pwdlen - SMBAUTH_HASH_SZ; 371 clnt_blob = &passwd[SMBAUTH_HASH_SZ]; 372 dest[0] = domain; 373 if ((dest[1] = strdup(domain)) == NULL) 374 return (B_FALSE); 375 (void) smb_strupr(dest[1]); 376 dest[2] = ""; 377 378 /* 379 * 15.5.2 The NTLMv2 Password Hash, pg. 279, of the "Implementing CIFS" 380 * 381 * The NTLMv2 Hash is created from: 382 * - NTLM hash 383 * - user's username, and 384 * - the name of the logon destination(i.e. the NetBIOS name of either 385 * the SMB server or NT Domain against which the user is trying to 386 * authenticate. 387 * 388 * Experiments show this is not exactly the case. 389 * For Windows Server 2003, the domain name needs to be included and 390 * converted to uppercase. For Vista, the domain name needs to be 391 * included also, but leave the case alone. And in some cases it needs 392 * to be empty. All three variants are tried here. 393 */ 394 395 ntlmv2_resp = (unsigned char *)malloc(SMBAUTH_HASH_SZ + clnt_blob_len); 396 if (ntlmv2_resp == NULL) { 397 free(dest[1]); 398 return (B_FALSE); 399 } 400 401 for (i = 0; i < (sizeof (dest) / sizeof (char *)); i++) { 402 if (smb_auth_ntlmv2_hash(ntlm_hash, username, dest[i], 403 ntlmv2_hash) != SMBAUTH_SUCCESS) 404 break; 405 406 if (smb_auth_v2_response(ntlmv2_hash, challenge, 407 clen, clnt_blob, clnt_blob_len, ntlmv2_resp) < 0) 408 break; 409 410 ok = (bcmp(passwd, ntlmv2_resp, pwdlen) == 0); 411 if (ok && session_key) { 412 rc = SMBAUTH_HMACT64(ntlmv2_resp, 413 SMBAUTH_HASH_SZ, ntlmv2_hash, 414 SMBAUTH_SESSION_KEY_SZ, session_key); 415 if (rc != SMBAUTH_SUCCESS) { 416 ok = B_FALSE; 417 } 418 break; 419 } 420 } 421 422 free(dest[1]); 423 free(ntlmv2_resp); 424 return (ok); 425 } 426 427 static boolean_t 428 smb_lmv2_password_ok( 429 unsigned char *challenge, 430 uint32_t clen, 431 unsigned char *ntlm_hash, 432 unsigned char *passwd, 433 char *domain, 434 char *username) 435 { 436 unsigned char *clnt_challenge; 437 unsigned char ntlmv2_hash[SMBAUTH_HASH_SZ]; 438 unsigned char lmv2_resp[SMBAUTH_LM_RESP_SZ]; 439 boolean_t ok = B_FALSE; 440 char *dest[3]; 441 int i; 442 443 clnt_challenge = &passwd[SMBAUTH_HASH_SZ]; 444 dest[0] = domain; 445 if ((dest[1] = strdup(domain)) == NULL) 446 return (B_FALSE); 447 (void) smb_strupr(dest[1]); 448 dest[2] = ""; 449 450 /* 451 * 15.5.2 The NTLMv2 Password Hash, pg. 279, of the "Implementing CIFS" 452 * 453 * The NTLMv2 Hash is created from: 454 * - NTLM hash 455 * - user's username, and 456 * - the name of the logon destination(i.e. the NetBIOS name of either 457 * the SMB server or NT Domain against which the suer is trying to 458 * authenticate. 459 * 460 * Experiments show this is not exactly the case. 461 * For Windows Server 2003, the domain name needs to be included and 462 * converted to uppercase. For Vista, the domain name needs to be 463 * included also, but leave the case alone. And in some cases it needs 464 * to be empty. All three variants are tried here. 465 */ 466 467 for (i = 0; i < (sizeof (dest) / sizeof (char *)); i++) { 468 if (smb_auth_ntlmv2_hash(ntlm_hash, username, dest[i], 469 ntlmv2_hash) != SMBAUTH_SUCCESS) 470 break; 471 472 if (smb_auth_v2_response(ntlmv2_hash, challenge, 473 clen, clnt_challenge, SMBAUTH_V2_CLNT_CHALLENGE_SZ, 474 lmv2_resp) < 0) 475 break; 476 477 ok = (bcmp(passwd, lmv2_resp, SMBAUTH_LM_RESP_SZ) == 0); 478 if (ok) 479 break; 480 } 481 482 free(dest[1]); 483 return (ok); 484 } 485 486 /* 487 * smb_auth_validate_lm 488 * 489 * Validates given LM/LMv2 client response, passed in passwd arg, against 490 * stored user's password, passed in smbpw 491 * 492 * If LM level <=3 server accepts LM responses, otherwise LMv2 493 */ 494 boolean_t 495 smb_auth_validate_lm( 496 unsigned char *challenge, 497 uint32_t clen, 498 smb_passwd_t *smbpw, 499 unsigned char *passwd, 500 int pwdlen, 501 char *domain, 502 char *username) 503 { 504 boolean_t ok = B_FALSE; 505 int64_t lmlevel; 506 507 if (pwdlen != SMBAUTH_LM_RESP_SZ) 508 return (B_FALSE); 509 510 if (smb_config_getnum(SMB_CI_LM_LEVEL, &lmlevel) != SMBD_SMF_OK) 511 return (B_FALSE); 512 513 if (lmlevel <= 3) { 514 ok = smb_lm_password_ok(challenge, clen, smbpw->pw_lmhash, 515 passwd); 516 } 517 518 if (!ok) 519 ok = smb_lmv2_password_ok(challenge, clen, smbpw->pw_nthash, 520 passwd, domain, username); 521 522 return (ok); 523 } 524 525 /* 526 * smb_auth_validate_nt 527 * 528 * Validates given NTLM/NTLMv2 client response, passed in passwd arg, against 529 * stored user's password, passed in smbpw 530 * 531 * If LM level <=4 server accepts NTLM/NTLMv2 responses, otherwise only NTLMv2 532 */ 533 boolean_t 534 smb_auth_validate_nt( 535 unsigned char *challenge, 536 uint32_t clen, 537 smb_passwd_t *smbpw, 538 unsigned char *passwd, 539 int pwdlen, 540 char *domain, 541 char *username, 542 uchar_t *session_key) 543 { 544 int64_t lmlevel; 545 boolean_t ok; 546 547 if (smb_config_getnum(SMB_CI_LM_LEVEL, &lmlevel) != SMBD_SMF_OK) 548 return (B_FALSE); 549 550 if ((lmlevel == 5) && (pwdlen <= SMBAUTH_LM_RESP_SZ)) 551 return (B_FALSE); 552 553 if (pwdlen > SMBAUTH_LM_RESP_SZ) 554 ok = smb_ntlmv2_password_ok(challenge, clen, 555 smbpw->pw_nthash, passwd, pwdlen, 556 domain, username, session_key); 557 else 558 ok = smb_ntlm_password_ok(challenge, clen, 559 smbpw->pw_nthash, passwd, session_key); 560 561 return (ok); 562 } 563 564 /* 565 * smb_gen_random_passwd(buf, len) 566 * Generate a random password of length len-1, and store it in buf, 567 * null terminated. This is used as a machine account password, 568 * which we set when we join a domain. 569 * 570 * [MS-DISO] A machine password is an ASCII string of randomly chosen 571 * characters. Each character's ASCII code is between 32 and 122 inclusive. 572 * That's space through 'z'. 573 */ 574 575 int 576 smb_gen_random_passwd(char *buf, size_t len) 577 { 578 const uchar_t start = ' '; 579 const uchar_t modulus = 'z' - ' ' + 1; 580 uchar_t t; 581 int i; 582 583 /* Last byte is the null. */ 584 len--; 585 586 /* Temporarily put random data in the caller's buffer. */ 587 randomize(buf, len); 588 589 /* Convert the random data to printable characters. */ 590 for (i = 0; i < len; i++) { 591 /* need unsigned math */ 592 t = (uchar_t)buf[i]; 593 t = (t % modulus) + start; 594 assert(' ' <= t && t <= 'z'); 595 buf[i] = (char)t; 596 } 597 598 buf[len] = '\0'; 599 600 return (0); 601 } 602