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"
10 #include "ScopedNSSTypes.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"
24 #include "nsComponentManagerUtils.h"
25 #include "nsICryptoHash.h"
26 #include "nsNativeCharsetUtils.h"
28 #include "nsUnicharUtils.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
93 * We don't actually send a LM response, but we still have to send something in
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
);
123 TEST(NegotiateDatagramStyle
);
124 TEST(NegotiateLanManagerKey
);
125 TEST(NegotiateNetware
);
126 TEST(NegotiateNTLMKey
);
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
);
147 TEST(NegotiateKeyExchange
);
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
) {
162 if (!LOG_ENABLED()) return;
164 PR_LogPrint("%s =\n", tag
);
168 if (count
> 8) count
= 8;
171 for (i
= 0; i
< count
; ++i
) {
172 int len
= strlen(line
);
173 snprintf(line
+ len
, sizeof(line
) - len
, "0x%02x ", int(buf
[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
) {
184 if (isprint(buf
[i
])) {
185 snprintf(line
+ len
, sizeof(line
) - len
, "%c", buf
[i
]);
187 snprintf(line
+ len
, sizeof(line
) - len
, ".");
190 PR_LogPrint("%s\n", line
);
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()) {
208 nsDependentCSubstring
tokenString(static_cast<const char*>(token
), tokenLen
);
209 nsAutoCString base64Token
;
210 nsresult rv
= mozilla::Base64Encode(tokenString
, base64Token
);
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
) {
231 // NTLM uses little endian on the wire
232 dword
= SWAP32(dword
);
234 return WriteBytes(buf
, &dword
, sizeof(dword
));
237 static void* WriteSecBuf(void* buf
, uint16_t length
, uint32_t offset
) {
239 length
= SWAP16(length
);
240 offset
= SWAP32(offset
);
242 buf
= WriteBytes(buf
, &length
, sizeof(length
));
243 buf
= WriteBytes(buf
, &length
, sizeof(length
));
244 buf
= WriteBytes(buf
, &offset
, sizeof(offset
));
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];
269 static uint16_t ReadUint16(const uint8_t*& buf
) {
270 uint16_t x
= ((uint16_t)buf
[0]) | ((uint16_t)buf
[1] << 8);
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);
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.
292 * null-terminated unicode password.
294 * 16-byte result buffer
296 static void NTLM_Hash(const nsString
& password
, unsigned char* hash
) {
297 uint32_t len
= password
.Length();
301 passbuf
= (uint8_t*)malloc(len
* 2);
302 WriteUnicodeLE(passbuf
, password
.get(), len
);
304 passbuf
= (uint8_t*)password
.get();
307 md4sum(passbuf
, len
* 2, hash
);
310 ZapBuf(passbuf
, len
* 2);
315 //-----------------------------------------------------------------------------
318 * LM_Response generates the LM response given a 16-byte password hash and the
319 * challenge from the Type-2 message.
322 * 16-byte password hash
324 * 8-byte challenge from Type-2 message
326 * 24-byte buffer to contain the LM response upon return
328 static void LM_Response(const uint8_t* hash
, const uint8_t* challenge
,
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
;
359 cursor
= WriteBytes(cursor
, NTLM_SIGNATURE
, sizeof(NTLM_SIGNATURE
));
362 cursor
= WriteBytes(cursor
, NTLM_TYPE1_MARKER
, sizeof(NTLM_TYPE1_MARKER
));
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);
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
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
,
396 // make sure inBuf is long enough to contain a meaningful type2 msg.
398 // 0 NTLMSSP Signature
399 // 8 NTLM Message Type
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.
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
;
439 // Do not error out, for (conservative) backward compatibility.
441 msg
->target
= nullptr;
445 msg
->flags
= ReadUint32(cursor
);
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
);
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
461 // Read target name security buffer: ...
462 // ... read target length.
463 uint32_t targetInfoLen
= ReadUint16(cursor
);
464 // ... skip next 16-bit "allocated space" value.
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
;
476 NS_ERROR("failed to get NTLMv2 target info");
477 return NS_ERROR_UNEXPECTED
;
483 static nsresult
GenerateType3Msg(const nsString
& domain
,
484 const nsString
& username
,
485 const nsString
& password
, const void* inBuf
,
486 uint32_t inLen
, void** outBuf
,
488 // inBuf contains Type-2 msg (the challenge) from server
489 MOZ_ASSERT(NS_IsMainThread());
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
504 nsAutoString ucsDomainBuf
, ucsUserBuf
;
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
;
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());
530 domainPtr
= domain
.get();
531 domainLen
= domain
.Length() * 2;
534 NS_CopyUnicodeToNative(domain
, oemDomainBuf
);
535 domainPtr
= oemDomainBuf
.get();
536 domainLen
= oemDomainBuf
.Length();
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());
550 userPtr
= username
.get();
551 userLen
= username
.Length() * 2;
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",
570 CopyUTF8toUTF16(hostBuf
, ucsHostBuf
);
571 hostPtr
= ucsHostBuf
.get();
572 hostLen
= ucsHostBuf
.Length() * 2;
574 WriteUnicodeLE(const_cast<void*>(hostPtr
),
575 static_cast<const char16_t
*>(hostPtr
), ucsHostBuf
.Length());
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
];
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;
614 WriteUnicodeLE(const_cast<void*>(userUpperPtr
),
615 static_cast<const char16_t
*>(userUpperPtr
),
616 ucsUserUpperBuf
.Length());
618 ToUpperCase(domain
, ucsDomainUpperBuf
);
619 domainUpperPtr
= ucsDomainUpperBuf
.get();
620 domainUpperLen
= ucsDomainUpperBuf
.Length() * 2;
622 WriteUnicodeLE(const_cast<void*>(domainUpperPtr
),
623 static_cast<const char16_t
*>(domainUpperPtr
),
624 ucsDomainUpperBuf
.Length());
627 NTLM_Hash(password
, ntlmHash
);
629 mozilla::HMAC ntlmv2HashHmac
;
630 rv
= ntlmv2HashHmac
.Begin(SEC_OID_MD5
,
631 mozilla::Span(ntlmHash
, NTLM_HASH_LEN
));
635 rv
= ntlmv2HashHmac
.Update(static_cast<const uint8_t*>(userUpperPtr
),
640 rv
= ntlmv2HashHmac
.Update(static_cast<const uint8_t*>(domainUpperPtr
),
645 nsTArray
<uint8_t> ntlmv2Hash
;
646 rv
= ntlmv2HashHmac
.End(ntlmv2Hash
);
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
));
659 rv
= lmv2ResponseHmac
.Update(msg
.challenge
, NTLM_CHAL_LEN
);
663 rv
= lmv2ResponseHmac
.Update(client_random
, NTLM_CHAL_LEN
);
667 nsTArray
<uint8_t> lmv2Response
;
668 rv
= lmv2ResponseHmac
.End(lmv2Response
);
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
);
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
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
));
697 rv
= ntlmv2ResponseHmac
.Update(msg
.challenge
, NTLM_CHAL_LEN
);
701 rv
= ntlmv2ResponseHmac
.Update(ntlmv2_blob1
, NTLMv2_BLOB1_LEN
);
705 rv
= ntlmv2ResponseHmac
.Update(msg
.targetInfo
, msg
.targetInfoLen
);
709 nsTArray
<uint8_t> ntlmv2Response
;
710 rv
= ntlmv2ResponseHmac
.End(ntlmv2Response
);
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
);
738 rv
= hasher
->Init(nsICryptoHash::MD5
);
742 rv
= hasher
->Update(msg
.challenge
, NTLM_CHAL_LEN
);
746 rv
= hasher
->Update(lmResp
, NTLM_CHAL_LEN
);
750 rv
= hasher
->Finish(false, sessionHashString
);
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
);
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
;
774 totalLen
+= domainLen
;
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
;
792 cursor
= WriteBytes(cursor
, NTLM_SIGNATURE
, sizeof(NTLM_SIGNATURE
));
795 cursor
= WriteBytes(cursor
, NTLM_TYPE3_MARKER
, sizeof(NTLM_TYPE3_MARKER
));
797 // 12 : LM response sec buf
798 offset
= NTLM_TYPE3_HEADER_LEN
;
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());
817 memcpy(static_cast<uint8_t*>(*outBuf
) + offset
.value(), ntlmv2Resp
,
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
,
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
,
834 memcpy(static_cast<uint8_t*>(*outBuf
) + offset
.value(), ntlmResp
,
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
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
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
);
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
;
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");
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;
906 nsNTLMAuthModule::GetNextToken(const void* inToken
, uint32_t inTokenLen
,
907 void** outToken
, uint32_t* outTokenLen
) {
910 // disable NTLM authentication when FIPS mode is enabled.
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
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
);
925 ("NTLMSSP_NEGOTIATE already sent and presumably "
926 "rejected by the server, refusing to send another"));
927 rv
= NS_ERROR_UNEXPECTED
;
931 LOG(("NTLMSSP_NEGOTIATE not sent but NTLM reply already received?!?"));
932 rv
= NS_ERROR_UNEXPECTED
;
934 rv
= GenerateType1Msg(outToken
, outTokenLen
);
935 if (NS_SUCCEEDED(rv
)) {
936 mNTLMNegotiateSent
= true;
941 if (NS_SUCCEEDED(rv
)) LogToken("out-token", *outToken
, *outTokenLen
);
947 nsNTLMAuthModule::Unwrap(const void* inToken
, uint32_t inTokenLen
,
948 void** outToken
, uint32_t* outTokenLen
) {
949 return NS_ERROR_NOT_IMPLEMENTED
;
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 //-----------------------------------------------------------------------------
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) ^
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;
992 mozilla::UniqueSECItem param
;
996 mozilla::UniquePK11SlotInfo
slot(PK11_GetBestSlot(cipherMech
, nullptr));
1002 keyItem
.data
= const_cast<uint8_t*>(key
);
1004 symkey
= PK11_ImportSymKey(slot
.get(), cipherMech
, PK11_OriginUnwrap
,
1005 CKA_ENCRYPT
, &keyItem
, nullptr);
1007 NS_ERROR("no symkey");
1011 // no initialization vector required
1012 param
= mozilla::UniqueSECItem(PK11_ParamFromIV(cipherMech
, nullptr));
1014 NS_ERROR("no param");
1019 PK11_CreateContextBySymKey(cipherMech
, CKA_ENCRYPT
, symkey
, param
.get());
1021 NS_ERROR("no context");
1025 rv
= PK11_CipherOp(ctxt
, hash
, (int*)&n
, 8, (uint8_t*)src
, 8);
1026 if (rv
!= SECSuccess
) {
1027 NS_ERROR("des failure");
1031 rv
= PK11_DigestFinal(ctxt
, hash
+ 8, &n
, 0);
1032 if (rv
!= SECSuccess
) {
1033 NS_ERROR("des failure");
1038 if (ctxt
) PK11_DestroyContext(ctxt
, true);
1039 if (symkey
) PK11_FreeSymKey(symkey
);