Bug 1935611 - Fix libyuv/libpng link failed for loongarch64. r=glandium,tnikkel,ng
[gecko.git] / security / manager / ssl / nsNTLMAuthModule.cpp
blob0bb9452548153fef9a2762355af6d4ef20d1d404
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/. */
6 #include "nsNTLMAuthModule.h"
8 #include <time.h>
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/Telemetry.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"
32 static mozilla::LazyLogModule sNTLMLog("NTLM");
34 #define LOG(x) MOZ_LOG(sNTLMLog, mozilla::LogLevel::Debug, x)
35 #define LOG_ENABLED() MOZ_LOG_TEST(sNTLMLog, mozilla::LogLevel::Debug)
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);
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 //-----------------------------------------------------------------------------
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
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)
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};
88 #define NTLM_TYPE1_HEADER_LEN 32
89 #define NTLM_TYPE2_HEADER_LEN 48
90 #define NTLM_TYPE3_HEADER_LEN 64
92 /**
93 * We don't actually send a LM response, but we still have to send something in
94 * this spot
96 #define LM_RESP_LEN 24
98 #define NTLM_CHAL_LEN 8
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
106 //-----------------------------------------------------------------------------
109 * Prints a description of flags to the NSPR Log, if enabled.
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)
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);
150 #undef TEST
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
159 static void LogBuf(const char* tag, const uint8_t* buf, uint32_t bufLen) {
160 int i;
162 if (!LOG_ENABLED()) return;
164 PR_LogPrint("%s =\n", tag);
165 char line[80];
166 while (bufLen > 0) {
167 int count = bufLen;
168 if (count > 8) count = 8;
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]));
175 for (; i < 8; ++i) {
176 int len = strlen(line);
177 snprintf(line + len, sizeof(line) - len, " ");
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, ".");
190 PR_LogPrint("%s\n", line);
192 bufLen -= count;
193 buf += count;
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
203 static void LogToken(const char* name, const void* token, uint32_t tokenLen) {
204 if (!LOG_ENABLED()) {
205 return;
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;
215 PR_LogPrint("%s: %s\n", name, base64Token.get());
218 //-----------------------------------------------------------------------------
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)))
224 static void* WriteBytes(void* buf, const void* data, uint32_t dataLen) {
225 memcpy(buf, data, dataLen);
226 return (uint8_t*)buf + dataLen;
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));
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;
248 #ifdef IS_BIG_ENDIAN
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.
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;
265 return buf;
267 #endif
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;
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;
282 //-----------------------------------------------------------------------------
284 static void ZapBuf(void* buf, size_t bufLen) { memset(buf, 0, bufLen); }
286 static void ZapString(nsString& s) { ZapBuf(s.BeginWriting(), s.Length() * 2); }
289 * NTLM_Hash computes the NTLM hash of the given password.
291 * @param password
292 * null-terminated unicode password.
293 * @param hash
294 * 16-byte result buffer
296 static void NTLM_Hash(const nsString& password, unsigned char* hash) {
297 uint32_t len = password.Length();
298 uint8_t* passbuf;
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
307 md4sum(passbuf, len * 2, hash);
309 #ifdef IS_BIG_ENDIAN
310 ZapBuf(passbuf, len * 2);
311 free(passbuf);
312 #endif
315 //-----------------------------------------------------------------------------
318 * LM_Response generates the LM response given a 16-byte password hash and the
319 * challenge from the Type-2 message.
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
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];
332 memcpy(keybytes, hash, 16);
333 ZapBuf(keybytes + 16, 5);
335 des_makekey(keybytes, k1);
336 des_makekey(keybytes + 7, k2);
337 des_makekey(keybytes + 14, k3);
339 des_encrypt(k1, challenge, response);
340 des_encrypt(k2, challenge, response + 8);
341 des_encrypt(k3, challenge, response + 16);
344 //-----------------------------------------------------------------------------
346 static nsresult GenerateType1Msg(void** outBuf, uint32_t* outLen) {
348 // verify that bufLen is sufficient
350 *outLen = NTLM_TYPE1_HEADER_LEN;
351 *outBuf = moz_xmalloc(*outLen);
354 // write out type 1 msg
356 void* cursor = *outBuf;
358 // 0 : signature
359 cursor = WriteBytes(cursor, NTLM_SIGNATURE, sizeof(NTLM_SIGNATURE));
361 // 8 : marker
362 cursor = WriteBytes(cursor, NTLM_TYPE1_MARKER, sizeof(NTLM_TYPE1_MARKER));
364 // 12 : flags
365 cursor = WriteDWORD(cursor, NTLM_TYPE1_FLAGS);
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 ;-)
375 // 16 : supplied domain security buffer (empty)
376 cursor = WriteSecBuf(cursor, 0, 0);
378 // 24 : supplied workstation security buffer (empty)
379 cursor = WriteSecBuf(cursor, 0, 0);
381 return NS_OK;
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
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.
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
406 if (inLen < NTLM_TYPE2_HEADER_LEN) return NS_ERROR_UNEXPECTED;
408 const auto* cursor = static_cast<const uint8_t*>(inBuf);
410 // verify NTLMSSP signature
411 if (memcmp(cursor, NTLM_SIGNATURE, sizeof(NTLM_SIGNATURE)) != 0) {
412 return NS_ERROR_UNEXPECTED;
415 cursor += sizeof(NTLM_SIGNATURE);
417 // verify Type-2 marker
418 if (memcmp(cursor, NTLM_TYPE2_MARKER, sizeof(NTLM_TYPE2_MARKER)) != 0) {
419 return NS_ERROR_UNEXPECTED;
422 cursor += sizeof(NTLM_TYPE2_MARKER);
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;
444 // read flags
445 msg->flags = ReadUint32(cursor);
447 // read challenge
448 memcpy(msg->challenge, cursor, sizeof(msg->challenge));
449 cursor += sizeof(msg->challenge);
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));
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;
480 return NS_OK;
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{};
493 rv = ParseType2Msg(inBuf, inLen, &msg);
494 if (NS_FAILED(rv)) return rv;
496 bool unicode = (msg.flags & NTLM_NegotiateUnicode);
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();
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;
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;
519 // get domain name
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();
540 // get user name
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();
560 // get workstation name
561 // (do not use local machine's hostname after bug 1046421)
563 rv = mozilla::Preferences::GetCString("network.generic-ntlm-auth.workstation",
564 hostBuf);
565 if (NS_FAILED(rv)) {
566 return rv;
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();
583 // now that we have generated all of the strings, we can allocate outBuf.
586 // next, we compute the NTLM or NTLM2 responses.
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;
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;
605 if (msg.targetInfoLen == 0) {
606 NS_ERROR("failed to get NTLMv2 target info, can not do NTLMv2");
607 return NS_ERROR_UNEXPECTED;
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
627 NTLM_Hash(password, ntlmHash);
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;
635 rv = ntlmv2HashHmac.Update(static_cast<const uint8_t*>(userUpperPtr),
636 userUpperLen);
637 if (NS_FAILED(rv)) {
638 return rv;
640 rv = ntlmv2HashHmac.Update(static_cast<const uint8_t*>(domainUpperPtr),
641 domainUpperLen);
642 if (NS_FAILED(rv)) {
643 return rv;
645 nsTArray<uint8_t> ntlmv2Hash;
646 rv = ntlmv2HashHmac.End(ntlmv2Hash);
647 if (NS_FAILED(rv)) {
648 return rv;
651 uint8_t client_random[NTLM_CHAL_LEN];
652 PK11_GenerateRandom(client_random, NTLM_CHAL_LEN);
654 mozilla::HMAC lmv2ResponseHmac;
655 rv = lmv2ResponseHmac.Begin(SEC_OID_MD5, mozilla::Span(ntlmv2Hash));
656 if (NS_FAILED(rv)) {
657 return rv;
659 rv = lmv2ResponseHmac.Update(msg.challenge, NTLM_CHAL_LEN);
660 if (NS_FAILED(rv)) {
661 return rv;
663 rv = lmv2ResponseHmac.Update(client_random, NTLM_CHAL_LEN);
664 if (NS_FAILED(rv)) {
665 return rv;
667 nsTArray<uint8_t> lmv2Response;
668 rv = lmv2ResponseHmac.End(lmv2Response);
669 if (NS_FAILED(rv)) {
670 return rv;
673 if (lmv2Response.Length() != NTLMv2_HASH_LEN) {
674 return NS_ERROR_UNEXPECTED;
677 memcpy(lmResp, lmv2Response.Elements(), NTLMv2_HASH_LEN);
678 memcpy(lmResp + NTLMv2_HASH_LEN, client_random, NTLM_CHAL_LEN);
680 memset(ntlmv2_blob1, 0, NTLMv2_BLOB1_LEN);
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
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);
692 mozilla::HMAC ntlmv2ResponseHmac;
693 rv = ntlmv2ResponseHmac.Begin(SEC_OID_MD5, mozilla::Span(ntlmv2Hash));
694 if (NS_FAILED(rv)) {
695 return rv;
697 rv = ntlmv2ResponseHmac.Update(msg.challenge, NTLM_CHAL_LEN);
698 if (NS_FAILED(rv)) {
699 return rv;
701 rv = ntlmv2ResponseHmac.Update(ntlmv2_blob1, NTLMv2_BLOB1_LEN);
702 if (NS_FAILED(rv)) {
703 return rv;
705 rv = ntlmv2ResponseHmac.Update(msg.targetInfo, msg.targetInfoLen);
706 if (NS_FAILED(rv)) {
707 return rv;
709 nsTArray<uint8_t> ntlmv2Response;
710 rv = ntlmv2ResponseHmac.End(ntlmv2Response);
711 if (NS_FAILED(rv)) {
712 return rv;
715 if (ntlmv2Response.Length() != NTLMv2_RESP_LEN) {
716 return NS_ERROR_UNEXPECTED;
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;
726 } else if (msg.flags & NTLM_NegotiateNTLM2Key) {
727 // compute NTLM2 session response
728 nsCString sessionHashString;
730 PK11_GenerateRandom(lmResp, NTLM_CHAL_LEN);
731 memset(lmResp + NTLM_CHAL_LEN, 0, LM_RESP_LEN - NTLM_CHAL_LEN);
733 nsCOMPtr<nsICryptoHash> hasher =
734 do_CreateInstance(NS_CRYPTO_HASH_CONTRACTID, &rv);
735 if (NS_FAILED(rv)) {
736 return rv;
738 rv = hasher->Init(nsICryptoHash::MD5);
739 if (NS_FAILED(rv)) {
740 return rv;
742 rv = hasher->Update(msg.challenge, NTLM_CHAL_LEN);
743 if (NS_FAILED(rv)) {
744 return rv;
746 rv = hasher->Update(lmResp, NTLM_CHAL_LEN);
747 if (NS_FAILED(rv)) {
748 return rv;
750 rv = hasher->Finish(false, sessionHashString);
751 if (NS_FAILED(rv)) {
752 return rv;
755 const auto* sessionHash = mozilla::BitwiseCast<const uint8_t*, const char*>(
756 sessionHashString.get());
758 LogBuf("NTLM2 effective key: ", sessionHash, 8);
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);
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);
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();
778 if (!totalLen.isValid()) {
779 NS_ERROR("failed preparing to allocate NTLM response: integer overflow?!?");
780 return NS_ERROR_FAILURE;
782 *outBuf = moz_xmalloc(totalLen.value());
783 *outLen = totalLen.value();
786 // finally, we assemble the Type-3 msg :-)
788 void* cursor = *outBuf;
789 mozilla::CheckedInt<uint32_t> offset;
791 // 0 : signature
792 cursor = WriteBytes(cursor, NTLM_SIGNATURE, sizeof(NTLM_SIGNATURE));
794 // 8 : marker
795 cursor = WriteBytes(cursor, NTLM_TYPE3_MARKER, sizeof(NTLM_TYPE3_MARKER));
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;
806 cursor = WriteSecBuf(cursor, LM_RESP_LEN, offset.value());
807 memcpy(static_cast<uint8_t*>(*outBuf) + offset.value(), lmResp, LM_RESP_LEN);
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;
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;
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;
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);
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);
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;
848 cursor = WriteSecBuf(cursor, userLen, offset.value());
849 memcpy(static_cast<uint8_t*>(*outBuf) + offset.value(), userPtr, userLen);
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;
857 cursor = WriteSecBuf(cursor, hostLen, offset.value());
858 memcpy(static_cast<uint8_t*>(*outBuf) + offset.value(), hostPtr, hostLen);
860 // 52 : session key sec buf (not used)
861 cursor = WriteSecBuf(cursor, 0, 0);
863 // 60 : negotiated flags
864 cursor = WriteDWORD(cursor, msg.flags & NTLM_TYPE1_FLAGS);
866 return NS_OK;
869 //-----------------------------------------------------------------------------
871 NS_IMPL_ISUPPORTS(nsNTLMAuthModule, nsIAuthModule)
873 nsNTLMAuthModule::~nsNTLMAuthModule() { ZapString(mPassword); }
875 nsresult nsNTLMAuthModule::InitTest() {
876 // disable NTLM authentication when FIPS mode is enabled.
877 return PK11_IsFIPS() ? NS_ERROR_NOT_AVAILABLE : NS_OK;
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");
888 mDomain = domain;
889 mUsername = username;
890 mPassword = password;
891 mNTLMNegotiateSent = false;
893 static bool sTelemetrySent = false;
894 if (!sTelemetrySent) {
895 mozilla::Telemetry::Accumulate(mozilla::Telemetry::NTLM_MODULE_USED_2,
896 serviceFlags & nsIAuthModule::REQ_PROXY_AUTH
897 ? NTLM_MODULE_GENERIC_PROXY
898 : NTLM_MODULE_GENERIC_DIRECT);
899 sTelemetrySent = true;
902 return NS_OK;
905 NS_IMETHODIMP
906 nsNTLMAuthModule::GetNextToken(const void* inToken, uint32_t inTokenLen,
907 void** outToken, uint32_t* outTokenLen) {
908 nsresult rv;
910 // disable NTLM authentication when FIPS mode is enabled.
911 if (PK11_IsFIPS()) {
912 return NS_ERROR_NOT_AVAILABLE;
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;
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;
941 if (NS_SUCCEEDED(rv)) LogToken("out-token", *outToken, *outTokenLen);
943 return rv;
946 NS_IMETHODIMP
947 nsNTLMAuthModule::Unwrap(const void* inToken, uint32_t inTokenLen,
948 void** outToken, uint32_t* outTokenLen) {
949 return NS_ERROR_NOT_IMPLEMENTED;
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;
959 //-----------------------------------------------------------------------------
960 // DES support code
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;
971 return x;
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));
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;
996 mozilla::UniquePK11SlotInfo slot(PK11_GetBestSlot(cipherMech, nullptr));
997 if (!slot) {
998 NS_ERROR("no slot");
999 goto done;
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;
1011 // no initialization vector required
1012 param = mozilla::UniqueSECItem(PK11_ParamFromIV(cipherMech, nullptr));
1013 if (!param) {
1014 NS_ERROR("no param");
1015 goto done;
1018 ctxt =
1019 PK11_CreateContextBySymKey(cipherMech, CKA_ENCRYPT, symkey, param.get());
1020 if (!ctxt) {
1021 NS_ERROR("no context");
1022 goto done;
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;
1031 rv = PK11_DigestFinal(ctxt, hash + 8, &n, 0);
1032 if (rv != SECSuccess) {
1033 NS_ERROR("des failure");
1034 goto done;
1037 done:
1038 if (ctxt) PK11_DestroyContext(ctxt, true);
1039 if (symkey) PK11_FreeSymKey(symkey);