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