nsNTLMAuthModule.cpp (32169B)
1 /* vim:set ts=2 sw=2 et cindent: */ 2 /* This Source Code Form is subject to the terms of the Mozilla Public 3 * License, v. 2.0. If a copy of the MPL was not distributed with this 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 5 6 #include "nsNTLMAuthModule.h" 7 8 #include <time.h> 9 10 #include "ScopedNSSTypes.h" 11 #include "md4.h" 12 #include "mozilla/Assertions.h" 13 #include "mozilla/Base64.h" 14 #include "mozilla/Casting.h" 15 #include "mozilla/CheckedInt.h" 16 #include "mozilla/EndianUtils.h" 17 #include "mozilla/Likely.h" 18 #include "mozilla/Logging.h" 19 #include "mozilla/Preferences.h" 20 #include "mozilla/Sprintf.h" 21 #include "mozilla/StaticPrefs_network.h" 22 #include "mozilla/glean/SecurityManagerSslMetrics.h" 23 #include "nsCOMPtr.h" 24 #include "nsComponentManagerUtils.h" 25 #include "nsICryptoHash.h" 26 #include "nsNativeCharsetUtils.h" 27 #include "nsNetCID.h" 28 #include "nsUnicharUtils.h" 29 #include "pk11pub.h" 30 #include "prsystem.h" 31 32 static mozilla::LazyLogModule sNTLMLog("NTLM"); 33 34 #define LOG(x) MOZ_LOG(sNTLMLog, mozilla::LogLevel::Debug, x) 35 #define LOG_ENABLED() MOZ_LOG_TEST(sNTLMLog, mozilla::LogLevel::Debug) 36 37 static void des_makekey(const uint8_t* raw, uint8_t* key); 38 static void des_encrypt(const uint8_t* key, const uint8_t* src, uint8_t* hash); 39 40 //----------------------------------------------------------------------------- 41 // this file contains a cross-platform NTLM authentication implementation. it 42 // is based on documentation from: http://davenport.sourceforge.net/ntlm.html 43 //----------------------------------------------------------------------------- 44 45 #define NTLM_NegotiateUnicode 0x00000001 46 #define NTLM_NegotiateOEM 0x00000002 47 #define NTLM_RequestTarget 0x00000004 48 #define NTLM_Unknown1 0x00000008 49 #define NTLM_NegotiateSign 0x00000010 50 #define NTLM_NegotiateSeal 0x00000020 51 #define NTLM_NegotiateDatagramStyle 0x00000040 52 #define NTLM_NegotiateLanManagerKey 0x00000080 53 #define NTLM_NegotiateNetware 0x00000100 54 #define NTLM_NegotiateNTLMKey 0x00000200 55 #define NTLM_Unknown2 0x00000400 56 #define NTLM_Unknown3 0x00000800 57 #define NTLM_NegotiateDomainSupplied 0x00001000 58 #define NTLM_NegotiateWorkstationSupplied 0x00002000 59 #define NTLM_NegotiateLocalCall 0x00004000 60 #define NTLM_NegotiateAlwaysSign 0x00008000 61 #define NTLM_TargetTypeDomain 0x00010000 62 #define NTLM_TargetTypeServer 0x00020000 63 #define NTLM_TargetTypeShare 0x00040000 64 #define NTLM_NegotiateNTLM2Key 0x00080000 65 #define NTLM_RequestInitResponse 0x00100000 66 #define NTLM_RequestAcceptResponse 0x00200000 67 #define NTLM_RequestNonNTSessionKey 0x00400000 68 #define NTLM_NegotiateTargetInfo 0x00800000 69 #define NTLM_Unknown4 0x01000000 70 #define NTLM_Unknown5 0x02000000 71 #define NTLM_Unknown6 0x04000000 72 #define NTLM_Unknown7 0x08000000 73 #define NTLM_Unknown8 0x10000000 74 #define NTLM_Negotiate128 0x20000000 75 #define NTLM_NegotiateKeyExchange 0x40000000 76 #define NTLM_Negotiate56 0x80000000 77 78 // we send these flags with our type 1 message 79 #define NTLM_TYPE1_FLAGS \ 80 (NTLM_NegotiateUnicode | NTLM_NegotiateOEM | NTLM_RequestTarget | \ 81 NTLM_NegotiateNTLMKey | NTLM_NegotiateAlwaysSign | NTLM_NegotiateNTLM2Key) 82 83 static const char NTLM_SIGNATURE[] = "NTLMSSP"; 84 static const char NTLM_TYPE1_MARKER[] = {0x01, 0x00, 0x00, 0x00}; 85 static const char NTLM_TYPE2_MARKER[] = {0x02, 0x00, 0x00, 0x00}; 86 static const char NTLM_TYPE3_MARKER[] = {0x03, 0x00, 0x00, 0x00}; 87 88 #define NTLM_TYPE1_HEADER_LEN 32 89 #define NTLM_TYPE2_HEADER_LEN 48 90 #define NTLM_TYPE3_HEADER_LEN 64 91 92 /** 93 * We don't actually send a LM response, but we still have to send something in 94 * this spot 95 */ 96 #define LM_RESP_LEN 24 97 98 #define NTLM_CHAL_LEN 8 99 100 #define NTLM_HASH_LEN 16 101 #define NTLMv2_HASH_LEN 16 102 #define NTLM_RESP_LEN 24 103 #define NTLMv2_RESP_LEN 16 104 #define NTLMv2_BLOB1_LEN 28 105 106 //----------------------------------------------------------------------------- 107 108 /** 109 * Prints a description of flags to the NSPR Log, if enabled. 110 */ 111 static void LogFlags(uint32_t flags) { 112 if (!LOG_ENABLED()) return; 113 #define TEST(_flag) \ 114 if (flags & NTLM_##_flag) \ 115 PR_LogPrint(" 0x%08x (" #_flag ")\n", NTLM_##_flag) 116 117 TEST(NegotiateUnicode); 118 TEST(NegotiateOEM); 119 TEST(RequestTarget); 120 TEST(Unknown1); 121 TEST(NegotiateSign); 122 TEST(NegotiateSeal); 123 TEST(NegotiateDatagramStyle); 124 TEST(NegotiateLanManagerKey); 125 TEST(NegotiateNetware); 126 TEST(NegotiateNTLMKey); 127 TEST(Unknown2); 128 TEST(Unknown3); 129 TEST(NegotiateDomainSupplied); 130 TEST(NegotiateWorkstationSupplied); 131 TEST(NegotiateLocalCall); 132 TEST(NegotiateAlwaysSign); 133 TEST(TargetTypeDomain); 134 TEST(TargetTypeServer); 135 TEST(TargetTypeShare); 136 TEST(NegotiateNTLM2Key); 137 TEST(RequestInitResponse); 138 TEST(RequestAcceptResponse); 139 TEST(RequestNonNTSessionKey); 140 TEST(NegotiateTargetInfo); 141 TEST(Unknown4); 142 TEST(Unknown5); 143 TEST(Unknown6); 144 TEST(Unknown7); 145 TEST(Unknown8); 146 TEST(Negotiate128); 147 TEST(NegotiateKeyExchange); 148 TEST(Negotiate56); 149 150 #undef TEST 151 } 152 153 /** 154 * Prints a hexdump of buf to the NSPR Log, if enabled. 155 * @param tag Description of the data, will be printed in front of the data 156 * @param buf the data to print 157 * @param bufLen length of the data 158 */ 159 static void LogBuf(const char* tag, const uint8_t* buf, uint32_t bufLen) { 160 int i; 161 162 if (!LOG_ENABLED()) return; 163 164 PR_LogPrint("%s =\n", tag); 165 char line[80]; 166 while (bufLen > 0) { 167 int count = bufLen; 168 if (count > 8) count = 8; 169 170 strcpy(line, " "); 171 for (i = 0; i < count; ++i) { 172 int len = strlen(line); 173 snprintf(line + len, sizeof(line) - len, "0x%02x ", int(buf[i])); 174 } 175 for (; i < 8; ++i) { 176 int len = strlen(line); 177 snprintf(line + len, sizeof(line) - len, " "); 178 } 179 180 int len = strlen(line); 181 snprintf(line + len, sizeof(line) - len, " "); 182 for (i = 0; i < count; ++i) { 183 len = strlen(line); 184 if (isprint(buf[i])) { 185 snprintf(line + len, sizeof(line) - len, "%c", buf[i]); 186 } else { 187 snprintf(line + len, sizeof(line) - len, "."); 188 } 189 } 190 PR_LogPrint("%s\n", line); 191 192 bufLen -= count; 193 buf += count; 194 } 195 } 196 197 /** 198 * Print base64-encoded token to the NSPR Log. 199 * @param name Description of the token, will be printed in front 200 * @param token The token to print 201 * @param tokenLen length of the data in token 202 */ 203 static void LogToken(const char* name, const void* token, uint32_t tokenLen) { 204 if (!LOG_ENABLED()) { 205 return; 206 } 207 208 nsDependentCSubstring tokenString(static_cast<const char*>(token), tokenLen); 209 nsAutoCString base64Token; 210 nsresult rv = mozilla::Base64Encode(tokenString, base64Token); 211 if (NS_FAILED(rv)) { 212 return; 213 } 214 215 PR_LogPrint("%s: %s\n", name, base64Token.get()); 216 } 217 218 //----------------------------------------------------------------------------- 219 220 // byte order swapping 221 #define SWAP16(x) ((((x) & 0xff) << 8) | (((x) >> 8) & 0xff)) 222 #define SWAP32(x) ((SWAP16((x) & 0xffff) << 16) | (SWAP16((x) >> 16))) 223 224 static void* WriteBytes(void* buf, const void* data, uint32_t dataLen) { 225 memcpy(buf, data, dataLen); 226 return (uint8_t*)buf + dataLen; 227 } 228 229 static void* WriteDWORD(void* buf, uint32_t dword) { 230 #ifdef IS_BIG_ENDIAN 231 // NTLM uses little endian on the wire 232 dword = SWAP32(dword); 233 #endif 234 return WriteBytes(buf, &dword, sizeof(dword)); 235 } 236 237 static void* WriteSecBuf(void* buf, uint16_t length, uint32_t offset) { 238 #ifdef IS_BIG_ENDIAN 239 length = SWAP16(length); 240 offset = SWAP32(offset); 241 #endif 242 buf = WriteBytes(buf, &length, sizeof(length)); 243 buf = WriteBytes(buf, &length, sizeof(length)); 244 buf = WriteBytes(buf, &offset, sizeof(offset)); 245 return buf; 246 } 247 248 #ifdef IS_BIG_ENDIAN 249 /** 250 * WriteUnicodeLE copies a unicode string from one buffer to another. The 251 * resulting unicode string is in little-endian format. The input string is 252 * assumed to be in the native endianness of the local machine. It is safe 253 * to pass the same buffer as both input and output, which is a handy way to 254 * convert the unicode buffer to little-endian on big-endian platforms. 255 */ 256 static void* WriteUnicodeLE(void* buf, const char16_t* str, uint32_t strLen) { 257 // convert input string from BE to LE 258 uint8_t *cursor = (uint8_t*)buf, *input = (uint8_t*)str; 259 for (uint32_t i = 0; i < strLen; ++i, input += 2, cursor += 2) { 260 // allow for the case where |buf == str| 261 uint8_t temp = input[0]; 262 cursor[0] = input[1]; 263 cursor[1] = temp; 264 } 265 return buf; 266 } 267 #endif 268 269 static uint16_t ReadUint16(const uint8_t*& buf) { 270 uint16_t x = ((uint16_t)buf[0]) | ((uint16_t)buf[1] << 8); 271 buf += sizeof(x); 272 return x; 273 } 274 275 static uint32_t ReadUint32(const uint8_t*& buf) { 276 uint32_t x = ((uint32_t)buf[0]) | (((uint32_t)buf[1]) << 8) | 277 (((uint32_t)buf[2]) << 16) | (((uint32_t)buf[3]) << 24); 278 buf += sizeof(x); 279 return x; 280 } 281 282 //----------------------------------------------------------------------------- 283 284 static void ZapBuf(void* buf, size_t bufLen) { memset(buf, 0, bufLen); } 285 286 static void ZapString(nsString& s) { ZapBuf(s.BeginWriting(), s.Length() * 2); } 287 288 /** 289 * NTLM_Hash computes the NTLM hash of the given password. 290 * 291 * @param password 292 * null-terminated unicode password. 293 * @param hash 294 * 16-byte result buffer 295 */ 296 static void NTLM_Hash(const nsString& password, unsigned char* hash) { 297 uint32_t len = password.Length(); 298 uint8_t* passbuf; 299 300 #ifdef IS_BIG_ENDIAN 301 passbuf = (uint8_t*)malloc(len * 2); 302 WriteUnicodeLE(passbuf, password.get(), len); 303 #else 304 passbuf = (uint8_t*)password.get(); 305 #endif 306 307 md4sum(passbuf, len * 2, hash); 308 309 #ifdef IS_BIG_ENDIAN 310 ZapBuf(passbuf, len * 2); 311 free(passbuf); 312 #endif 313 } 314 315 //----------------------------------------------------------------------------- 316 317 /** 318 * LM_Response generates the LM response given a 16-byte password hash and the 319 * challenge from the Type-2 message. 320 * 321 * @param hash 322 * 16-byte password hash 323 * @param challenge 324 * 8-byte challenge from Type-2 message 325 * @param response 326 * 24-byte buffer to contain the LM response upon return 327 */ 328 static void LM_Response(const uint8_t* hash, const uint8_t* challenge, 329 uint8_t* response) { 330 uint8_t keybytes[21], k1[8], k2[8], k3[8]; 331 332 memcpy(keybytes, hash, 16); 333 ZapBuf(keybytes + 16, 5); 334 335 des_makekey(keybytes, k1); 336 des_makekey(keybytes + 7, k2); 337 des_makekey(keybytes + 14, k3); 338 339 des_encrypt(k1, challenge, response); 340 des_encrypt(k2, challenge, response + 8); 341 des_encrypt(k3, challenge, response + 16); 342 } 343 344 //----------------------------------------------------------------------------- 345 346 static nsresult GenerateType1Msg(void** outBuf, uint32_t* outLen) { 347 // 348 // verify that bufLen is sufficient 349 // 350 *outLen = NTLM_TYPE1_HEADER_LEN; 351 *outBuf = moz_xmalloc(*outLen); 352 353 // 354 // write out type 1 msg 355 // 356 void* cursor = *outBuf; 357 358 // 0 : signature 359 cursor = WriteBytes(cursor, NTLM_SIGNATURE, sizeof(NTLM_SIGNATURE)); 360 361 // 8 : marker 362 cursor = WriteBytes(cursor, NTLM_TYPE1_MARKER, sizeof(NTLM_TYPE1_MARKER)); 363 364 // 12 : flags 365 cursor = WriteDWORD(cursor, NTLM_TYPE1_FLAGS); 366 367 // 368 // NOTE: it is common for the domain and workstation fields to be empty. 369 // this is true of Win2k clients, and my guess is that there is 370 // little utility to sending these strings before the charset has 371 // been negotiated. we follow suite -- anyways, it doesn't hurt 372 // to save some bytes on the wire ;-) 373 // 374 375 // 16 : supplied domain security buffer (empty) 376 cursor = WriteSecBuf(cursor, 0, 0); 377 378 // 24 : supplied workstation security buffer (empty) 379 cursor = WriteSecBuf(cursor, 0, 0); 380 381 return NS_OK; 382 } 383 384 struct Type2Msg { 385 uint32_t flags; // NTLM_Xxx bitwise combination 386 uint8_t challenge[NTLM_CHAL_LEN]; // 8 byte challenge 387 const uint8_t* target; // target string (type depends on flags) 388 uint32_t targetLen; // target length in bytes 389 const uint8_t* 390 targetInfo; // target Attribute-Value pairs (DNS domain, et al) 391 uint32_t targetInfoLen; // target AV pairs length in bytes 392 }; 393 394 static nsresult ParseType2Msg(const void* inBuf, uint32_t inLen, 395 Type2Msg* msg) { 396 // make sure inBuf is long enough to contain a meaningful type2 msg. 397 // 398 // 0 NTLMSSP Signature 399 // 8 NTLM Message Type 400 // 12 Target Name 401 // 20 Flags 402 // 24 Challenge 403 // 32 targetInfo 404 // 48 start of optional data blocks 405 // 406 if (inLen < NTLM_TYPE2_HEADER_LEN) return NS_ERROR_UNEXPECTED; 407 408 const auto* cursor = static_cast<const uint8_t*>(inBuf); 409 410 // verify NTLMSSP signature 411 if (memcmp(cursor, NTLM_SIGNATURE, sizeof(NTLM_SIGNATURE)) != 0) { 412 return NS_ERROR_UNEXPECTED; 413 } 414 415 cursor += sizeof(NTLM_SIGNATURE); 416 417 // verify Type-2 marker 418 if (memcmp(cursor, NTLM_TYPE2_MARKER, sizeof(NTLM_TYPE2_MARKER)) != 0) { 419 return NS_ERROR_UNEXPECTED; 420 } 421 422 cursor += sizeof(NTLM_TYPE2_MARKER); 423 424 // Read target name security buffer: ... 425 // ... read target length. 426 uint32_t targetLen = ReadUint16(cursor); 427 // ... skip next 16-bit "allocated space" value. 428 ReadUint16(cursor); 429 // ... read offset from inBuf. 430 uint32_t offset = ReadUint32(cursor); 431 mozilla::CheckedInt<uint32_t> targetEnd = offset; 432 targetEnd += targetLen; 433 // Check the offset / length combo is in range of the input buffer, including 434 // integer overflow checking. 435 if (MOZ_LIKELY(targetEnd.isValid() && targetEnd.value() <= inLen)) { 436 msg->targetLen = targetLen; 437 msg->target = static_cast<const uint8_t*>(inBuf) + offset; 438 } else { 439 // Do not error out, for (conservative) backward compatibility. 440 msg->targetLen = 0; 441 msg->target = nullptr; 442 } 443 444 // read flags 445 msg->flags = ReadUint32(cursor); 446 447 // read challenge 448 memcpy(msg->challenge, cursor, sizeof(msg->challenge)); 449 cursor += sizeof(msg->challenge); 450 451 LOG(("NTLM type 2 message:\n")); 452 LogBuf("target", msg->target, msg->targetLen); 453 LogBuf("flags", 454 mozilla::BitwiseCast<const uint8_t*, const uint32_t*>(&msg->flags), 4); 455 LogFlags(msg->flags); 456 LogBuf("challenge", msg->challenge, sizeof(msg->challenge)); 457 458 // Read (and skip) the reserved field 459 ReadUint32(cursor); 460 ReadUint32(cursor); 461 // Read target name security buffer: ... 462 // ... read target length. 463 uint32_t targetInfoLen = ReadUint16(cursor); 464 // ... skip next 16-bit "allocated space" value. 465 ReadUint16(cursor); 466 // ... read offset from inBuf. 467 offset = ReadUint32(cursor); 468 mozilla::CheckedInt<uint32_t> targetInfoEnd = offset; 469 targetInfoEnd += targetInfoLen; 470 // Check the offset / length combo is in range of the input buffer, including 471 // integer overflow checking. 472 if (MOZ_LIKELY(targetInfoEnd.isValid() && targetInfoEnd.value() <= inLen)) { 473 msg->targetInfoLen = targetInfoLen; 474 msg->targetInfo = static_cast<const uint8_t*>(inBuf) + offset; 475 } else { 476 NS_ERROR("failed to get NTLMv2 target info"); 477 return NS_ERROR_UNEXPECTED; 478 } 479 480 return NS_OK; 481 } 482 483 static nsresult GenerateType3Msg(const nsString& domain, 484 const nsString& username, 485 const nsString& password, const void* inBuf, 486 uint32_t inLen, void** outBuf, 487 uint32_t* outLen) { 488 // inBuf contains Type-2 msg (the challenge) from server 489 MOZ_ASSERT(NS_IsMainThread()); 490 nsresult rv; 491 Type2Msg msg{}; 492 493 rv = ParseType2Msg(inBuf, inLen, &msg); 494 if (NS_FAILED(rv)) return rv; 495 496 bool unicode = (msg.flags & NTLM_NegotiateUnicode); 497 498 // There is no negotiation for NTLMv2, so we just do it unless we are forced 499 // by explict user configuration to use the older DES-based cryptography. 500 bool ntlmv2 = !mozilla::StaticPrefs::network_auth_force_generic_ntlm_v1(); 501 502 // temporary buffers for unicode strings 503 #ifdef IS_BIG_ENDIAN 504 nsAutoString ucsDomainBuf, ucsUserBuf; 505 #endif 506 nsAutoCString hostBuf; 507 nsAutoString ucsHostBuf; 508 // temporary buffers for oem strings 509 nsAutoCString oemDomainBuf, oemUserBuf, oemHostBuf; 510 // pointers and lengths for the string buffers; encoding is unicode if 511 // the "negotiate unicode" flag was set in the Type-2 message. 512 const void *domainPtr, *userPtr, *hostPtr; 513 uint32_t domainLen, userLen, hostLen; 514 515 // This is for NTLM, for NTLMv2 we set the new full length once we know it 516 mozilla::CheckedInt<uint16_t> ntlmRespLen = NTLM_RESP_LEN; 517 518 // 519 // get domain name 520 // 521 if (unicode) { 522 #ifdef IS_BIG_ENDIAN 523 ucsDomainBuf = domain; 524 domainPtr = ucsDomainBuf.get(); 525 domainLen = ucsDomainBuf.Length() * 2; 526 WriteUnicodeLE(const_cast<void*>(domainPtr), 527 static_cast<const char16_t*>(domainPtr), 528 ucsDomainBuf.Length()); 529 #else 530 domainPtr = domain.get(); 531 domainLen = domain.Length() * 2; 532 #endif 533 } else { 534 NS_CopyUnicodeToNative(domain, oemDomainBuf); 535 domainPtr = oemDomainBuf.get(); 536 domainLen = oemDomainBuf.Length(); 537 } 538 539 // 540 // get user name 541 // 542 if (unicode) { 543 #ifdef IS_BIG_ENDIAN 544 ucsUserBuf = username; 545 userPtr = ucsUserBuf.get(); 546 userLen = ucsUserBuf.Length() * 2; 547 WriteUnicodeLE(const_cast<void*>(userPtr), 548 static_cast<const char16_t*>(userPtr), ucsUserBuf.Length()); 549 #else 550 userPtr = username.get(); 551 userLen = username.Length() * 2; 552 #endif 553 } else { 554 NS_CopyUnicodeToNative(username, oemUserBuf); 555 userPtr = oemUserBuf.get(); 556 userLen = oemUserBuf.Length(); 557 } 558 559 // 560 // get workstation name 561 // (do not use local machine's hostname after bug 1046421) 562 // 563 rv = mozilla::Preferences::GetCString("network.generic-ntlm-auth.workstation", 564 hostBuf); 565 if (NS_FAILED(rv)) { 566 return rv; 567 } 568 569 if (unicode) { 570 CopyUTF8toUTF16(hostBuf, ucsHostBuf); 571 hostPtr = ucsHostBuf.get(); 572 hostLen = ucsHostBuf.Length() * 2; 573 #ifdef IS_BIG_ENDIAN 574 WriteUnicodeLE(const_cast<void*>(hostPtr), 575 static_cast<const char16_t*>(hostPtr), ucsHostBuf.Length()); 576 #endif 577 } else { 578 hostPtr = hostBuf.get(); 579 hostLen = hostBuf.Length(); 580 } 581 582 // 583 // now that we have generated all of the strings, we can allocate outBuf. 584 // 585 // 586 // next, we compute the NTLM or NTLM2 responses. 587 // 588 uint8_t lmResp[LM_RESP_LEN]; 589 uint8_t ntlmResp[NTLM_RESP_LEN]; 590 uint8_t ntlmv2Resp[NTLMv2_RESP_LEN]; 591 uint8_t ntlmHash[NTLM_HASH_LEN]; 592 uint8_t ntlmv2_blob1[NTLMv2_BLOB1_LEN]; 593 if (ntlmv2) { 594 // NTLMv2 mode, the default 595 nsString userUpper, domainUpper; 596 597 // temporary buffers for unicode strings 598 nsAutoString ucsDomainUpperBuf; 599 nsAutoString ucsUserUpperBuf; 600 const void* domainUpperPtr; 601 const void* userUpperPtr; 602 uint32_t domainUpperLen; 603 uint32_t userUpperLen; 604 605 if (msg.targetInfoLen == 0) { 606 NS_ERROR("failed to get NTLMv2 target info, can not do NTLMv2"); 607 return NS_ERROR_UNEXPECTED; 608 } 609 610 ToUpperCase(username, ucsUserUpperBuf); 611 userUpperPtr = ucsUserUpperBuf.get(); 612 userUpperLen = ucsUserUpperBuf.Length() * 2; 613 #ifdef IS_BIG_ENDIAN 614 WriteUnicodeLE(const_cast<void*>(userUpperPtr), 615 static_cast<const char16_t*>(userUpperPtr), 616 ucsUserUpperBuf.Length()); 617 #endif 618 ToUpperCase(domain, ucsDomainUpperBuf); 619 domainUpperPtr = ucsDomainUpperBuf.get(); 620 domainUpperLen = ucsDomainUpperBuf.Length() * 2; 621 #ifdef IS_BIG_ENDIAN 622 WriteUnicodeLE(const_cast<void*>(domainUpperPtr), 623 static_cast<const char16_t*>(domainUpperPtr), 624 ucsDomainUpperBuf.Length()); 625 #endif 626 627 NTLM_Hash(password, ntlmHash); 628 629 mozilla::HMAC ntlmv2HashHmac; 630 rv = ntlmv2HashHmac.Begin(SEC_OID_MD5, 631 mozilla::Span(ntlmHash, NTLM_HASH_LEN)); 632 if (NS_FAILED(rv)) { 633 return rv; 634 } 635 rv = ntlmv2HashHmac.Update(static_cast<const uint8_t*>(userUpperPtr), 636 userUpperLen); 637 if (NS_FAILED(rv)) { 638 return rv; 639 } 640 rv = ntlmv2HashHmac.Update(static_cast<const uint8_t*>(domainUpperPtr), 641 domainUpperLen); 642 if (NS_FAILED(rv)) { 643 return rv; 644 } 645 nsTArray<uint8_t> ntlmv2Hash; 646 rv = ntlmv2HashHmac.End(ntlmv2Hash); 647 if (NS_FAILED(rv)) { 648 return rv; 649 } 650 651 uint8_t client_random[NTLM_CHAL_LEN]; 652 PK11_GenerateRandom(client_random, NTLM_CHAL_LEN); 653 654 mozilla::HMAC lmv2ResponseHmac; 655 rv = lmv2ResponseHmac.Begin(SEC_OID_MD5, mozilla::Span(ntlmv2Hash)); 656 if (NS_FAILED(rv)) { 657 return rv; 658 } 659 rv = lmv2ResponseHmac.Update(msg.challenge, NTLM_CHAL_LEN); 660 if (NS_FAILED(rv)) { 661 return rv; 662 } 663 rv = lmv2ResponseHmac.Update(client_random, NTLM_CHAL_LEN); 664 if (NS_FAILED(rv)) { 665 return rv; 666 } 667 nsTArray<uint8_t> lmv2Response; 668 rv = lmv2ResponseHmac.End(lmv2Response); 669 if (NS_FAILED(rv)) { 670 return rv; 671 } 672 673 if (lmv2Response.Length() != NTLMv2_HASH_LEN) { 674 return NS_ERROR_UNEXPECTED; 675 } 676 677 memcpy(lmResp, lmv2Response.Elements(), NTLMv2_HASH_LEN); 678 memcpy(lmResp + NTLMv2_HASH_LEN, client_random, NTLM_CHAL_LEN); 679 680 memset(ntlmv2_blob1, 0, NTLMv2_BLOB1_LEN); 681 682 time_t unix_time; 683 uint64_t nt_time = time(&unix_time); 684 nt_time += 11644473600LL; // Number of seconds betwen 1601 and 1970 685 nt_time *= 1000 * 1000 * 10; // Convert seconds to 100 ns units 686 687 ntlmv2_blob1[0] = 1; 688 ntlmv2_blob1[1] = 1; 689 mozilla::LittleEndian::writeUint64(&ntlmv2_blob1[8], nt_time); 690 PK11_GenerateRandom(&ntlmv2_blob1[16], NTLM_CHAL_LEN); 691 692 mozilla::HMAC ntlmv2ResponseHmac; 693 rv = ntlmv2ResponseHmac.Begin(SEC_OID_MD5, mozilla::Span(ntlmv2Hash)); 694 if (NS_FAILED(rv)) { 695 return rv; 696 } 697 rv = ntlmv2ResponseHmac.Update(msg.challenge, NTLM_CHAL_LEN); 698 if (NS_FAILED(rv)) { 699 return rv; 700 } 701 rv = ntlmv2ResponseHmac.Update(ntlmv2_blob1, NTLMv2_BLOB1_LEN); 702 if (NS_FAILED(rv)) { 703 return rv; 704 } 705 rv = ntlmv2ResponseHmac.Update(msg.targetInfo, msg.targetInfoLen); 706 if (NS_FAILED(rv)) { 707 return rv; 708 } 709 nsTArray<uint8_t> ntlmv2Response; 710 rv = ntlmv2ResponseHmac.End(ntlmv2Response); 711 if (NS_FAILED(rv)) { 712 return rv; 713 } 714 715 if (ntlmv2Response.Length() != NTLMv2_RESP_LEN) { 716 return NS_ERROR_UNEXPECTED; 717 } 718 719 memcpy(ntlmv2Resp, ntlmv2Response.Elements(), NTLMv2_RESP_LEN); 720 ntlmRespLen = NTLMv2_RESP_LEN + NTLMv2_BLOB1_LEN; 721 ntlmRespLen += msg.targetInfoLen; 722 if (!ntlmRespLen.isValid()) { 723 NS_ERROR("failed to do NTLMv2: integer overflow?!?"); 724 return NS_ERROR_UNEXPECTED; 725 } 726 } else if (msg.flags & NTLM_NegotiateNTLM2Key) { 727 // compute NTLM2 session response 728 nsCString sessionHashString; 729 730 PK11_GenerateRandom(lmResp, NTLM_CHAL_LEN); 731 memset(lmResp + NTLM_CHAL_LEN, 0, LM_RESP_LEN - NTLM_CHAL_LEN); 732 733 nsCOMPtr<nsICryptoHash> hasher = 734 do_CreateInstance(NS_CRYPTO_HASH_CONTRACTID, &rv); 735 if (NS_FAILED(rv)) { 736 return rv; 737 } 738 rv = hasher->Init(nsICryptoHash::MD5); 739 if (NS_FAILED(rv)) { 740 return rv; 741 } 742 rv = hasher->Update(msg.challenge, NTLM_CHAL_LEN); 743 if (NS_FAILED(rv)) { 744 return rv; 745 } 746 rv = hasher->Update(lmResp, NTLM_CHAL_LEN); 747 if (NS_FAILED(rv)) { 748 return rv; 749 } 750 rv = hasher->Finish(false, sessionHashString); 751 if (NS_FAILED(rv)) { 752 return rv; 753 } 754 755 const auto* sessionHash = mozilla::BitwiseCast<const uint8_t*, const char*>( 756 sessionHashString.get()); 757 758 LogBuf("NTLM2 effective key: ", sessionHash, 8); 759 760 NTLM_Hash(password, ntlmHash); 761 LM_Response(ntlmHash, sessionHash, ntlmResp); 762 } else { 763 NTLM_Hash(password, ntlmHash); 764 LM_Response(ntlmHash, msg.challenge, ntlmResp); 765 766 // According to http://davenport.sourceforge.net/ntlm.html#ntlmVersion2, 767 // the correct way to not send the LM hash is to send the NTLM hash twice 768 // in both the LM and NTLM response fields. 769 LM_Response(ntlmHash, msg.challenge, lmResp); 770 } 771 772 mozilla::CheckedInt<uint32_t> totalLen = NTLM_TYPE3_HEADER_LEN + LM_RESP_LEN; 773 totalLen += hostLen; 774 totalLen += domainLen; 775 totalLen += userLen; 776 totalLen += ntlmRespLen.value(); 777 778 if (!totalLen.isValid()) { 779 NS_ERROR("failed preparing to allocate NTLM response: integer overflow?!?"); 780 return NS_ERROR_FAILURE; 781 } 782 *outBuf = moz_xmalloc(totalLen.value()); 783 *outLen = totalLen.value(); 784 785 // 786 // finally, we assemble the Type-3 msg :-) 787 // 788 void* cursor = *outBuf; 789 mozilla::CheckedInt<uint32_t> offset; 790 791 // 0 : signature 792 cursor = WriteBytes(cursor, NTLM_SIGNATURE, sizeof(NTLM_SIGNATURE)); 793 794 // 8 : marker 795 cursor = WriteBytes(cursor, NTLM_TYPE3_MARKER, sizeof(NTLM_TYPE3_MARKER)); 796 797 // 12 : LM response sec buf 798 offset = NTLM_TYPE3_HEADER_LEN; 799 offset += domainLen; 800 offset += userLen; 801 offset += hostLen; 802 if (!offset.isValid()) { 803 NS_ERROR("failed preparing to write NTLM response: integer overflow?!?"); 804 return NS_ERROR_UNEXPECTED; 805 } 806 cursor = WriteSecBuf(cursor, LM_RESP_LEN, offset.value()); 807 memcpy(static_cast<uint8_t*>(*outBuf) + offset.value(), lmResp, LM_RESP_LEN); 808 809 // 20 : NTLM or NTLMv2 response sec buf 810 offset += LM_RESP_LEN; 811 if (!offset.isValid()) { 812 NS_ERROR("failed preparing to write NTLM response: integer overflow?!?"); 813 return NS_ERROR_UNEXPECTED; 814 } 815 cursor = WriteSecBuf(cursor, ntlmRespLen.value(), offset.value()); 816 if (ntlmv2) { 817 memcpy(static_cast<uint8_t*>(*outBuf) + offset.value(), ntlmv2Resp, 818 NTLMv2_RESP_LEN); 819 offset += NTLMv2_RESP_LEN; 820 if (!offset.isValid()) { 821 NS_ERROR("failed preparing to write NTLM response: integer overflow?!?"); 822 return NS_ERROR_UNEXPECTED; 823 } 824 memcpy(static_cast<uint8_t*>(*outBuf) + offset.value(), ntlmv2_blob1, 825 NTLMv2_BLOB1_LEN); 826 offset += NTLMv2_BLOB1_LEN; 827 if (!offset.isValid()) { 828 NS_ERROR("failed preparing to write NTLM response: integer overflow?!?"); 829 return NS_ERROR_UNEXPECTED; 830 } 831 memcpy(static_cast<uint8_t*>(*outBuf) + offset.value(), msg.targetInfo, 832 msg.targetInfoLen); 833 } else { 834 memcpy(static_cast<uint8_t*>(*outBuf) + offset.value(), ntlmResp, 835 NTLM_RESP_LEN); 836 } 837 // 28 : domain name sec buf 838 offset = NTLM_TYPE3_HEADER_LEN; 839 cursor = WriteSecBuf(cursor, domainLen, offset.value()); 840 memcpy(static_cast<uint8_t*>(*outBuf) + offset.value(), domainPtr, domainLen); 841 842 // 36 : user name sec buf 843 offset += domainLen; 844 if (!offset.isValid()) { 845 NS_ERROR("failed preparing to write NTLM response: integer overflow?!?"); 846 return NS_ERROR_UNEXPECTED; 847 } 848 cursor = WriteSecBuf(cursor, userLen, offset.value()); 849 memcpy(static_cast<uint8_t*>(*outBuf) + offset.value(), userPtr, userLen); 850 851 // 44 : workstation (host) name sec buf 852 offset += userLen; 853 if (!offset.isValid()) { 854 NS_ERROR("failed preparing to write NTLM response: integer overflow?!?"); 855 return NS_ERROR_UNEXPECTED; 856 } 857 cursor = WriteSecBuf(cursor, hostLen, offset.value()); 858 memcpy(static_cast<uint8_t*>(*outBuf) + offset.value(), hostPtr, hostLen); 859 860 // 52 : session key sec buf (not used) 861 cursor = WriteSecBuf(cursor, 0, 0); 862 863 // 60 : negotiated flags 864 cursor = WriteDWORD(cursor, msg.flags & NTLM_TYPE1_FLAGS); 865 866 return NS_OK; 867 } 868 869 //----------------------------------------------------------------------------- 870 871 NS_IMPL_ISUPPORTS(nsNTLMAuthModule, nsIAuthModule) 872 873 nsNTLMAuthModule::~nsNTLMAuthModule() { ZapString(mPassword); } 874 875 nsresult nsNTLMAuthModule::InitTest() { 876 // disable NTLM authentication when FIPS mode is enabled. 877 return PK11_IsFIPS() ? NS_ERROR_NOT_AVAILABLE : NS_OK; 878 } 879 880 NS_IMETHODIMP 881 nsNTLMAuthModule::Init(const nsACString& serviceName, uint32_t serviceFlags, 882 const nsAString& domain, const nsAString& username, 883 const nsAString& password) { 884 MOZ_ASSERT((serviceFlags & ~nsIAuthModule::REQ_PROXY_AUTH) == 885 nsIAuthModule::REQ_DEFAULT, 886 "Unexpected service flags"); 887 888 mDomain = domain; 889 mUsername = username; 890 mPassword = password; 891 mNTLMNegotiateSent = false; 892 893 static bool sTelemetrySent = false; 894 if (!sTelemetrySent) { 895 mozilla::glean::security::ntlm_module_used.AccumulateSingleSample( 896 serviceFlags & nsIAuthModule::REQ_PROXY_AUTH 897 ? NTLM_MODULE_GENERIC_PROXY 898 : NTLM_MODULE_GENERIC_DIRECT); 899 sTelemetrySent = true; 900 } 901 902 return NS_OK; 903 } 904 905 NS_IMETHODIMP 906 nsNTLMAuthModule::GetNextToken(const void* inToken, uint32_t inTokenLen, 907 void** outToken, uint32_t* outTokenLen) { 908 nsresult rv; 909 910 // disable NTLM authentication when FIPS mode is enabled. 911 if (PK11_IsFIPS()) { 912 return NS_ERROR_NOT_AVAILABLE; 913 } 914 915 if (mNTLMNegotiateSent) { 916 // if inToken is non-null, and we have sent the NTLMSSP_NEGOTIATE (type 1), 917 // then the NTLMSSP_CHALLENGE (type 2) is expected 918 if (inToken) { 919 LogToken("in-token", inToken, inTokenLen); 920 // Now generate the NTLMSSP_AUTH (type 3) 921 rv = GenerateType3Msg(mDomain, mUsername, mPassword, inToken, inTokenLen, 922 outToken, outTokenLen); 923 } else { 924 LOG( 925 ("NTLMSSP_NEGOTIATE already sent and presumably " 926 "rejected by the server, refusing to send another")); 927 rv = NS_ERROR_UNEXPECTED; 928 } 929 } else { 930 if (inToken) { 931 LOG(("NTLMSSP_NEGOTIATE not sent but NTLM reply already received?!?")); 932 rv = NS_ERROR_UNEXPECTED; 933 } else { 934 rv = GenerateType1Msg(outToken, outTokenLen); 935 if (NS_SUCCEEDED(rv)) { 936 mNTLMNegotiateSent = true; 937 } 938 } 939 } 940 941 if (NS_SUCCEEDED(rv)) LogToken("out-token", *outToken, *outTokenLen); 942 943 return rv; 944 } 945 946 NS_IMETHODIMP 947 nsNTLMAuthModule::Unwrap(const void* inToken, uint32_t inTokenLen, 948 void** outToken, uint32_t* outTokenLen) { 949 return NS_ERROR_NOT_IMPLEMENTED; 950 } 951 952 NS_IMETHODIMP 953 nsNTLMAuthModule::Wrap(const void* inToken, uint32_t inTokenLen, 954 bool confidential, void** outToken, 955 uint32_t* outTokenLen) { 956 return NS_ERROR_NOT_IMPLEMENTED; 957 } 958 959 //----------------------------------------------------------------------------- 960 // DES support code 961 962 // set odd parity bit (in least significant bit position) 963 static uint8_t des_setkeyparity(uint8_t x) { 964 if ((((x >> 7) ^ (x >> 6) ^ (x >> 5) ^ (x >> 4) ^ (x >> 3) ^ (x >> 2) ^ 965 (x >> 1)) & 966 0x01) == 0) { 967 x |= 0x01; 968 } else { 969 x &= 0xfe; 970 } 971 return x; 972 } 973 974 // build 64-bit des key from 56-bit raw key 975 static void des_makekey(const uint8_t* raw, uint8_t* key) { 976 key[0] = des_setkeyparity(raw[0]); 977 key[1] = des_setkeyparity((raw[0] << 7) | (raw[1] >> 1)); 978 key[2] = des_setkeyparity((raw[1] << 6) | (raw[2] >> 2)); 979 key[3] = des_setkeyparity((raw[2] << 5) | (raw[3] >> 3)); 980 key[4] = des_setkeyparity((raw[3] << 4) | (raw[4] >> 4)); 981 key[5] = des_setkeyparity((raw[4] << 3) | (raw[5] >> 5)); 982 key[6] = des_setkeyparity((raw[5] << 2) | (raw[6] >> 6)); 983 key[7] = des_setkeyparity((raw[6] << 1)); 984 } 985 986 // run des encryption algorithm (using NSS) 987 static void des_encrypt(const uint8_t* key, const uint8_t* src, uint8_t* hash) { 988 CK_MECHANISM_TYPE cipherMech = CKM_DES_ECB; 989 PK11SymKey* symkey = nullptr; 990 PK11Context* ctxt = nullptr; 991 SECItem keyItem; 992 mozilla::UniqueSECItem param; 993 SECStatus rv; 994 unsigned int n; 995 996 mozilla::UniquePK11SlotInfo slot(PK11_GetBestSlot(cipherMech, nullptr)); 997 if (!slot) { 998 NS_ERROR("no slot"); 999 goto done; 1000 } 1001 1002 keyItem.data = const_cast<uint8_t*>(key); 1003 keyItem.len = 8; 1004 symkey = PK11_ImportSymKey(slot.get(), cipherMech, PK11_OriginUnwrap, 1005 CKA_ENCRYPT, &keyItem, nullptr); 1006 if (!symkey) { 1007 NS_ERROR("no symkey"); 1008 goto done; 1009 } 1010 1011 // no initialization vector required 1012 param = mozilla::UniqueSECItem(PK11_ParamFromIV(cipherMech, nullptr)); 1013 if (!param) { 1014 NS_ERROR("no param"); 1015 goto done; 1016 } 1017 1018 ctxt = 1019 PK11_CreateContextBySymKey(cipherMech, CKA_ENCRYPT, symkey, param.get()); 1020 if (!ctxt) { 1021 NS_ERROR("no context"); 1022 goto done; 1023 } 1024 1025 rv = PK11_CipherOp(ctxt, hash, (int*)&n, 8, (uint8_t*)src, 8); 1026 if (rv != SECSuccess) { 1027 NS_ERROR("des failure"); 1028 goto done; 1029 } 1030 1031 rv = PK11_DigestFinal(ctxt, hash + 8, &n, 0); 1032 if (rv != SECSuccess) { 1033 NS_ERROR("des failure"); 1034 goto done; 1035 } 1036 1037 done: 1038 if (ctxt) PK11_DestroyContext(ctxt, true); 1039 if (symkey) PK11_FreeSymKey(symkey); 1040 }