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