Back to index

lightning-sunbird  0.9+nobinonly
nsNTLMAuthModule.cpp
Go to the documentation of this file.
00001 /* vim:set ts=2 sw=2 et cindent: */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is Mozilla.
00016  *
00017  * The Initial Developer of the Original Code is IBM Corporation.
00018  * Portions created by IBM Corporation are Copyright (C) 2003
00019  * IBM Corporation. All Rights Reserved.
00020  *
00021  * Contributor(s):
00022  *   Darin Fisher <darin@meer.net>
00023  *
00024  * Alternatively, the contents of this file may be used under the terms of
00025  * either the GNU General Public License Version 2 or later (the "GPL"), or
00026  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00027  * in which case the provisions of the GPL or the LGPL are applicable instead
00028  * of those above. If you wish to allow use of your version of this file only
00029  * under the terms of either the GPL or the LGPL, and not to allow others to
00030  * use your version of this file under the terms of the MPL, indicate your
00031  * decision by deleting the provisions above and replace them with the notice
00032  * and other provisions required by the GPL or the LGPL. If you do not delete
00033  * the provisions above, a recipient may use your version of this file under
00034  * the terms of any one of the MPL, the GPL or the LGPL.
00035  *
00036  * ***** END LICENSE BLOCK ***** */
00037 
00038 #include "prlog.h"
00039 
00040 #include <stdlib.h>
00041 #include "nsIPrefService.h"
00042 #include "nsIPrefBranch.h"
00043 #include "nsServiceManagerUtils.h"
00044 #include "nsCOMPtr.h"
00045 #include "nsNSSShutDown.h"
00046 #include "nsNTLMAuthModule.h"
00047 #include "nsNativeCharsetUtils.h"
00048 #include "nsReadableUtils.h"
00049 #include "nsString.h"
00050 #include "prsystem.h"
00051 #include "nss.h"
00052 #include "pk11func.h"
00053 #include "md4.h"
00054 
00055 #ifdef PR_LOGGING
00056 PRLogModuleInfo *gNTLMLog = PR_NewLogModule("NTLM");
00057 
00058 #define LOG(x) PR_LOG(gNTLMLog, PR_LOG_DEBUG, x)
00059 #define LOG_ENABLED() PR_LOG_TEST(gNTLMLog, PR_LOG_DEBUG)
00060 #else
00061 #define LOG(x)
00062 #endif
00063 
00064 static void des_makekey(const PRUint8 *raw, PRUint8 *key);
00065 static void des_encrypt(const PRUint8 *key, const PRUint8 *src, PRUint8 *hash);
00066 static void md5sum(const PRUint8 *input, PRUint32 inputLen, PRUint8 *result);
00067 
00068 //-----------------------------------------------------------------------------
00069 // this file contains a cross-platform NTLM authentication implementation. it
00070 // is based on documentation from: http://davenport.sourceforge.net/ntlm.html
00071 //-----------------------------------------------------------------------------
00072 
00073 #define NTLM_NegotiateUnicode               0x00000001
00074 #define NTLM_NegotiateOEM                   0x00000002
00075 #define NTLM_RequestTarget                  0x00000004
00076 #define NTLM_Unknown1                       0x00000008
00077 #define NTLM_NegotiateSign                  0x00000010
00078 #define NTLM_NegotiateSeal                  0x00000020
00079 #define NTLM_NegotiateDatagramStyle         0x00000040
00080 #define NTLM_NegotiateLanManagerKey         0x00000080
00081 #define NTLM_NegotiateNetware               0x00000100
00082 #define NTLM_NegotiateNTLMKey               0x00000200
00083 #define NTLM_Unknown2                       0x00000400
00084 #define NTLM_Unknown3                       0x00000800
00085 #define NTLM_NegotiateDomainSupplied        0x00001000
00086 #define NTLM_NegotiateWorkstationSupplied   0x00002000
00087 #define NTLM_NegotiateLocalCall             0x00004000
00088 #define NTLM_NegotiateAlwaysSign            0x00008000
00089 #define NTLM_TargetTypeDomain               0x00010000
00090 #define NTLM_TargetTypeServer               0x00020000
00091 #define NTLM_TargetTypeShare                0x00040000
00092 #define NTLM_NegotiateNTLM2Key              0x00080000
00093 #define NTLM_RequestInitResponse            0x00100000
00094 #define NTLM_RequestAcceptResponse          0x00200000
00095 #define NTLM_RequestNonNTSessionKey         0x00400000
00096 #define NTLM_NegotiateTargetInfo            0x00800000
00097 #define NTLM_Unknown4                       0x01000000
00098 #define NTLM_Unknown5                       0x02000000
00099 #define NTLM_Unknown6                       0x04000000
00100 #define NTLM_Unknown7                       0x08000000
00101 #define NTLM_Unknown8                       0x10000000
00102 #define NTLM_Negotiate128                   0x20000000
00103 #define NTLM_NegotiateKeyExchange           0x40000000
00104 #define NTLM_Negotiate56                    0x80000000
00105 
00106 // we send these flags with our type 1 message
00107 #define NTLM_TYPE1_FLAGS      \
00108   (NTLM_NegotiateUnicode |    \
00109    NTLM_NegotiateOEM |        \
00110    NTLM_RequestTarget |       \
00111    NTLM_NegotiateNTLMKey |    \
00112    NTLM_NegotiateAlwaysSign | \
00113    NTLM_NegotiateNTLM2Key)
00114 
00115 static const char NTLM_SIGNATURE[] = "NTLMSSP";
00116 static const char NTLM_TYPE1_MARKER[] = { 0x01, 0x00, 0x00, 0x00 };
00117 static const char NTLM_TYPE2_MARKER[] = { 0x02, 0x00, 0x00, 0x00 };
00118 static const char NTLM_TYPE3_MARKER[] = { 0x03, 0x00, 0x00, 0x00 };
00119 
00120 #define NTLM_TYPE1_HEADER_LEN 32
00121 #define NTLM_TYPE2_HEADER_LEN 32
00122 #define NTLM_TYPE3_HEADER_LEN 64
00123 
00124 #define LM_HASH_LEN 16
00125 #define LM_RESP_LEN 24
00126 
00127 #define NTLM_HASH_LEN 16
00128 #define NTLM_RESP_LEN 24
00129 
00130 //-----------------------------------------------------------------------------
00131 
00132 static PRBool SendLM()
00133 {
00134   nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
00135   if (!prefs)
00136     return PR_FALSE;
00137 
00138   PRBool val;
00139   nsresult rv = prefs->GetBoolPref("network.ntlm.send-lm-response", &val);
00140   return NS_SUCCEEDED(rv) && val;
00141 }
00142 
00143 //-----------------------------------------------------------------------------
00144 
00145 #ifdef PR_LOGGING
00146 
00150 static void LogFlags(PRUint32 flags)
00151 {
00152   if (!LOG_ENABLED())
00153     return;
00154 #define TEST(_flag) \
00155   if (flags & NTLM_ ## _flag) \
00156     PR_LogPrint("    0x%08x (" # _flag ")\n", NTLM_ ## _flag)
00157 
00158   TEST(NegotiateUnicode);
00159   TEST(NegotiateOEM);
00160   TEST(RequestTarget);
00161   TEST(Unknown1);
00162   TEST(NegotiateSign);
00163   TEST(NegotiateSeal);
00164   TEST(NegotiateDatagramStyle);
00165   TEST(NegotiateLanManagerKey);
00166   TEST(NegotiateNetware);
00167   TEST(NegotiateNTLMKey);
00168   TEST(Unknown2);
00169   TEST(Unknown3);
00170   TEST(NegotiateDomainSupplied);
00171   TEST(NegotiateWorkstationSupplied);
00172   TEST(NegotiateLocalCall);
00173   TEST(NegotiateAlwaysSign);
00174   TEST(TargetTypeDomain);
00175   TEST(TargetTypeServer);
00176   TEST(TargetTypeShare);
00177   TEST(NegotiateNTLM2Key);
00178   TEST(RequestInitResponse);
00179   TEST(RequestAcceptResponse);
00180   TEST(RequestNonNTSessionKey);
00181   TEST(NegotiateTargetInfo);
00182   TEST(Unknown4);
00183   TEST(Unknown5);
00184   TEST(Unknown6);
00185   TEST(Unknown7);
00186   TEST(Unknown8);
00187   TEST(Negotiate128);
00188   TEST(NegotiateKeyExchange);
00189   TEST(Negotiate56);
00190 
00191 #undef TEST
00192 }
00193 
00200 static void
00201 LogBuf(const char *tag, const PRUint8 *buf, PRUint32 bufLen)
00202 {
00203   int i;
00204 
00205   if (!LOG_ENABLED())
00206     return;
00207 
00208   PR_LogPrint("%s =\n", tag);
00209   char line[80];
00210   while (bufLen > 0)
00211   {
00212     int count = bufLen;
00213     if (count > 8)
00214       count = 8;
00215 
00216     strcpy(line, "    ");
00217     for (i=0; i<count; ++i)
00218     {
00219       int len = strlen(line);
00220       PR_snprintf(line + len, sizeof(line) - len, "0x%02x ", int(buf[i]));
00221     }
00222     for (; i<8; ++i)
00223     {
00224       int len = strlen(line);
00225       PR_snprintf(line + len, sizeof(line) - len, "     ");
00226     }
00227 
00228     int len = strlen(line);
00229     PR_snprintf(line + len, sizeof(line) - len, "   ");
00230     for (i=0; i<count; ++i)
00231     {
00232       len = strlen(line);
00233       if (isprint(buf[i]))
00234         PR_snprintf(line + len, sizeof(line) - len, "%c", buf[i]);
00235       else
00236         PR_snprintf(line + len, sizeof(line) - len, ".");
00237     }
00238     PR_LogPrint("%s\n", line);
00239 
00240     bufLen -= count;
00241     buf += count;
00242   }
00243 }
00244 
00245 #include "plbase64.h"
00246 #include "prmem.h"
00253 static void LogToken(const char *name, const void *token, PRUint32 tokenLen)
00254 {
00255   if (!LOG_ENABLED())
00256     return;
00257 
00258   char *b64data = PL_Base64Encode((const char *) token, tokenLen, NULL);
00259   if (b64data)
00260   {
00261     PR_LogPrint("%s: %s\n", name, b64data);
00262     PR_Free(b64data);
00263   }
00264 }
00265 
00266 #else
00267 #define LogFlags(x)
00268 #define LogBuf(a,b,c)
00269 #define LogToken(a,b,c)
00270 
00271 #endif // PR_LOGGING
00272 
00273 //-----------------------------------------------------------------------------
00274 
00275 // byte order swapping
00276 #define SWAP16(x) ((((x) & 0xff) << 8) | (((x) >> 8) & 0xff))
00277 #define SWAP32(x) ((SWAP16((x) & 0xffff) << 16) | (SWAP16((x) >> 16)))
00278 
00279 static void *
00280 WriteBytes(void *buf, const void *data, PRUint32 dataLen)
00281 {
00282   memcpy(buf, data, dataLen);
00283   return (PRUint8 *) buf + dataLen;
00284 }
00285 
00286 static void *
00287 WriteDWORD(void *buf, PRUint32 dword)
00288 {
00289 #ifdef IS_BIG_ENDIAN 
00290   // NTLM uses little endian on the wire
00291   dword = SWAP32(dword);
00292 #endif
00293   return WriteBytes(buf, &dword, sizeof(dword));
00294 }
00295 
00296 static void *
00297 WriteSecBuf(void *buf, PRUint16 length, PRUint32 offset)
00298 {
00299 #ifdef IS_BIG_ENDIAN
00300   length = SWAP16(length);
00301   offset = SWAP32(offset);
00302 #endif
00303   buf = WriteBytes(buf, &length, sizeof(length));
00304   buf = WriteBytes(buf, &length, sizeof(length));
00305   buf = WriteBytes(buf, &offset, sizeof(offset));
00306   return buf;
00307 }
00308 
00309 #ifdef IS_BIG_ENDIAN
00310 
00317 static void *
00318 WriteUnicodeLE(void *buf, const PRUnichar *str, PRUint32 strLen)
00319 {
00320   // convert input string from BE to LE
00321   PRUint8 *cursor = (PRUint8 *) buf,
00322           *input  = (PRUint8 *) str;
00323   for (PRUint32 i=0; i<strLen; ++i, input+=2, cursor+=2)
00324   {
00325     // allow for the case where |buf == str|
00326     PRUint8 temp = input[0];
00327     cursor[0] = input[1];
00328     cursor[1] = temp;
00329   }
00330   return buf;
00331 }
00332 #endif
00333 
00334 static PRUint16
00335 ReadUint16(const PRUint8 *&buf)
00336 {
00337   PRUint16 x = ((PRUint16) buf[0]) | ((PRUint16) buf[1] << 8);
00338   buf += sizeof(x);
00339   return x;
00340 }
00341 
00342 static PRUint32
00343 ReadUint32(const PRUint8 *&buf)
00344 {
00345   PRUint32 x = ( (PRUint32) buf[0])        |
00346                (((PRUint32) buf[1]) << 8)  |
00347                (((PRUint32) buf[2]) << 16) |
00348                (((PRUint32) buf[3]) << 24);
00349   buf += sizeof(x);
00350   return x;
00351 }
00352 
00353 //-----------------------------------------------------------------------------
00354 
00355 static void
00356 ZapBuf(void *buf, size_t bufLen)
00357 {
00358   memset(buf, 0, bufLen);
00359 }
00360 
00361 static void
00362 ZapString(nsCString &s)
00363 {
00364   ZapBuf(s.BeginWriting(), s.Length());
00365 }
00366 
00367 static void
00368 ZapString(nsString &s)
00369 {
00370   ZapBuf(s.BeginWriting(), s.Length() * 2);
00371 }
00372 
00373 static const unsigned char LM_MAGIC[] = "KGS!@#$%";
00374 
00383 static void
00384 LM_Hash(const nsString &password, unsigned char *hash)
00385 {
00386   // convert password to OEM character set.  we'll just use the native
00387   // filesystem charset.
00388   nsCAutoString passbuf;
00389   NS_CopyUnicodeToNative(password, passbuf);
00390   ToUpperCase(passbuf);
00391   PRUint32 n = passbuf.Length();
00392   passbuf.SetLength(14);
00393   for (PRUint32 i=n; i<14; ++i)
00394     passbuf.SetCharAt('\0', i);
00395 
00396   unsigned char k1[8], k2[8];
00397   des_makekey((const unsigned char *) passbuf.get()    , k1);
00398   des_makekey((const unsigned char *) passbuf.get() + 7, k2);
00399   ZapString(passbuf);
00400 
00401   // use password keys to hash LM magic string twice.
00402   des_encrypt(k1, LM_MAGIC, hash);
00403   des_encrypt(k2, LM_MAGIC, hash + 8);
00404 }
00405 
00414 static void
00415 NTLM_Hash(const nsString &password, unsigned char *hash)
00416 {
00417   PRUint32 len = password.Length();
00418   PRUint8 *passbuf;
00419   
00420 #ifdef IS_BIG_ENDIAN
00421   passbuf = (PRUint8 *) malloc(len * 2);
00422   WriteUnicodeLE(passbuf, password.get(), len);
00423 #else
00424   passbuf = (PRUint8 *) password.get();
00425 #endif
00426 
00427   md4sum(passbuf, len * 2, hash);
00428 
00429 #ifdef IS_BIG_ENDIAN
00430   ZapBuf(passbuf, len * 2);
00431   free(passbuf);
00432 #endif
00433 }
00434 
00435 //-----------------------------------------------------------------------------
00436 
00448 static void
00449 LM_Response(const PRUint8 *hash, const PRUint8 *challenge, PRUint8 *response)
00450 {
00451   PRUint8 keybytes[21], k1[8], k2[8], k3[8];
00452 
00453   memcpy(keybytes, hash, 16);
00454   ZapBuf(keybytes + 16, 5);
00455 
00456   des_makekey(keybytes     , k1);
00457   des_makekey(keybytes +  7, k2);
00458   des_makekey(keybytes + 14, k3);
00459 
00460   des_encrypt(k1, challenge, response);
00461   des_encrypt(k2, challenge, response + 8);
00462   des_encrypt(k3, challenge, response + 16);
00463 }
00464 
00465 //-----------------------------------------------------------------------------
00466 
00467 static nsresult
00468 GenerateType1Msg(void **outBuf, PRUint32 *outLen)
00469 {
00470   //
00471   // verify that bufLen is sufficient
00472   //
00473   *outLen = NTLM_TYPE1_HEADER_LEN;
00474   *outBuf = nsMemory::Alloc(*outLen);
00475   if (!*outBuf)
00476     return NS_ERROR_OUT_OF_MEMORY;
00477 
00478   //
00479   // write out type 1 msg
00480   //
00481   void *cursor = *outBuf;
00482 
00483   // 0 : signature
00484   cursor = WriteBytes(cursor, NTLM_SIGNATURE, sizeof(NTLM_SIGNATURE));
00485 
00486   // 8 : marker
00487   cursor = WriteBytes(cursor, NTLM_TYPE1_MARKER, sizeof(NTLM_TYPE1_MARKER));
00488 
00489   // 12 : flags
00490   cursor = WriteDWORD(cursor, NTLM_TYPE1_FLAGS);
00491 
00492   //
00493   // NOTE: it is common for the domain and workstation fields to be empty.
00494   //       this is true of Win2k clients, and my guess is that there is
00495   //       little utility to sending these strings before the charset has
00496   //       been negotiated.  we follow suite -- anyways, it doesn't hurt
00497   //       to save some bytes on the wire ;-)
00498   //
00499 
00500   // 16 : supplied domain security buffer (empty)
00501   cursor = WriteSecBuf(cursor, 0, 0);
00502 
00503   // 24 : supplied workstation security buffer (empty)
00504   cursor = WriteSecBuf(cursor, 0, 0);
00505 
00506   return NS_OK;
00507 }
00508 
00509 struct Type2Msg
00510 {
00511   PRUint32    flags;         // NTLM_Xxx bitwise combination
00512   PRUint8     challenge[8];  // 8 byte challenge
00513   const void *target;        // target string (type depends on flags)
00514   PRUint32    targetLen;     // target length in bytes
00515 };
00516 
00517 static nsresult
00518 ParseType2Msg(const void *inBuf, PRUint32 inLen, Type2Msg *msg)
00519 {
00520   // make sure inBuf is long enough to contain a meaningful type2 msg.
00521   //
00522   // 0  NTLMSSP Signature
00523   // 8  NTLM Message Type
00524   // 12 Target Name
00525   // 20 Flags
00526   // 24 Challenge
00527   // 32 end of header, start of optional data blocks
00528   //
00529   if (inLen < NTLM_TYPE2_HEADER_LEN)
00530     return NS_ERROR_UNEXPECTED;
00531 
00532   const PRUint8 *cursor = (const PRUint8 *) inBuf;
00533 
00534   // verify NTLMSSP signature
00535   if (memcmp(cursor, NTLM_SIGNATURE, sizeof(NTLM_SIGNATURE)) != 0)
00536     return NS_ERROR_UNEXPECTED;
00537   cursor += sizeof(NTLM_SIGNATURE);
00538 
00539   // verify Type-2 marker
00540   if (memcmp(cursor, NTLM_TYPE2_MARKER, sizeof(NTLM_TYPE2_MARKER)) != 0)
00541     return NS_ERROR_UNEXPECTED;
00542   cursor += sizeof(NTLM_TYPE2_MARKER);
00543 
00544   // read target name security buffer
00545   msg->targetLen = ReadUint16(cursor);
00546   ReadUint16(cursor); // discard next 16-bit value
00547   PRUint32 offset = ReadUint32(cursor); // get offset from inBuf
00548   msg->target = ((const PRUint8 *) inBuf) + offset;
00549 
00550   // read flags
00551   msg->flags = ReadUint32(cursor);
00552 
00553   // read challenge
00554   memcpy(msg->challenge, cursor, sizeof(msg->challenge));
00555   cursor += sizeof(msg->challenge);
00556 
00557 
00558   LOG(("NTLM type 2 message:\n"));
00559   LogBuf("target", (const PRUint8 *) msg->target, msg->targetLen);
00560   LogBuf("flags", (const PRUint8 *) &msg->flags, 4);
00561   LogFlags(msg->flags);
00562   LogBuf("challenge", msg->challenge, sizeof(msg->challenge));
00563 
00564   // we currently do not implement LMv2/NTLMv2 or NTLM2 responses,
00565   // so we can ignore target information.  we may want to enable
00566   // support for these alternate mechanisms in the future.
00567   return NS_OK;
00568 }
00569 
00570 static nsresult
00571 GenerateType3Msg(const nsString &domain,
00572                  const nsString &username,
00573                  const nsString &password,
00574                  const void     *inBuf,
00575                  PRUint32        inLen,
00576                  void          **outBuf,
00577                  PRUint32       *outLen)
00578 {
00579   // inBuf contains Type-2 msg (the challenge) from server
00580 
00581   nsresult rv;
00582   Type2Msg msg;
00583 
00584   rv = ParseType2Msg(inBuf, inLen, &msg);
00585   if (NS_FAILED(rv))
00586     return rv;
00587 
00588   PRBool unicode = (msg.flags & NTLM_NegotiateUnicode);
00589 
00590   // temporary buffers for unicode strings
00591 #ifdef IS_BIG_ENDIAN
00592   nsAutoString ucsDomainBuf, ucsUserBuf;
00593 #endif
00594   nsAutoString ucsHostBuf; 
00595   // temporary buffers for oem strings
00596   nsCAutoString oemDomainBuf, oemUserBuf, oemHostBuf;
00597   // pointers and lengths for the string buffers; encoding is unicode if
00598   // the "negotiate unicode" flag was set in the Type-2 message.
00599   const void *domainPtr, *userPtr, *hostPtr;
00600   PRUint32 domainLen, userLen, hostLen;
00601 
00602   //
00603   // get domain name
00604   //
00605   if (unicode)
00606   {
00607 #ifdef IS_BIG_ENDIAN
00608     ucsDomainBuf = domain;
00609     domainPtr = ucsDomainBuf.get();
00610     domainLen = ucsDomainBuf.Length() * 2;
00611     WriteUnicodeLE((void *) domainPtr, (const PRUnichar *) domainPtr,
00612                    ucsDomainBuf.Length());
00613 #else
00614     domainPtr = domain.get();
00615     domainLen = domain.Length() * 2;
00616 #endif
00617   }
00618   else
00619   {
00620     NS_CopyUnicodeToNative(domain, oemDomainBuf);
00621     domainPtr = oemDomainBuf.get();
00622     domainLen = oemDomainBuf.Length();
00623   }
00624 
00625   //
00626   // get user name
00627   //
00628   if (unicode)
00629   {
00630 #ifdef IS_BIG_ENDIAN
00631     ucsUserBuf = username;
00632     userPtr = ucsUserBuf.get();
00633     userLen = ucsUserBuf.Length() * 2;
00634     WriteUnicodeLE((void *) userPtr, (const PRUnichar *) userPtr,
00635                    ucsUserBuf.Length());
00636 #else
00637     userPtr = username.get();
00638     userLen = username.Length() * 2;
00639 #endif
00640   }
00641   else
00642   {
00643     NS_CopyUnicodeToNative(username, oemUserBuf);
00644     userPtr = oemUserBuf.get();
00645     userLen = oemUserBuf.Length();
00646   }
00647 
00648   //
00649   // get workstation name (use local machine's hostname)
00650   //
00651   char hostBuf[SYS_INFO_BUFFER_LENGTH];
00652   if (PR_GetSystemInfo(PR_SI_HOSTNAME, hostBuf, sizeof(hostBuf)) == PR_FAILURE)
00653     return NS_ERROR_UNEXPECTED;
00654   hostLen = strlen(hostBuf);
00655   if (unicode)
00656   {
00657     // hostname is ASCII, so we can do a simple zero-pad expansion:
00658     CopyASCIItoUCS2(nsDependentCString(hostBuf, hostLen), ucsHostBuf);
00659     hostPtr = ucsHostBuf.get();
00660     hostLen = ucsHostBuf.Length() * 2;
00661 #ifdef IS_BIG_ENDIAN
00662     WriteUnicodeLE((void *) hostPtr, (const PRUnichar *) hostPtr,
00663                    ucsHostBuf.Length());
00664 #endif
00665   }
00666   else
00667     hostPtr = hostBuf;
00668 
00669   //
00670   // now that we have generated all of the strings, we can allocate outBuf.
00671   //
00672   *outLen = NTLM_TYPE3_HEADER_LEN + hostLen + domainLen + userLen +
00673             LM_RESP_LEN + NTLM_RESP_LEN;
00674   *outBuf = nsMemory::Alloc(*outLen);
00675   if (!*outBuf)
00676     return NS_ERROR_OUT_OF_MEMORY;
00677 
00678   //
00679   // next, we compute the LM and NTLM responses.
00680   //
00681   PRUint8 lmResp[LM_RESP_LEN], ntlmResp[NTLM_RESP_LEN], ntlmHash[NTLM_HASH_LEN];
00682   if (msg.flags & NTLM_NegotiateNTLM2Key)
00683   {
00684     // compute NTLM2 session response
00685     PRUint8 sessionHash[16], temp[16];
00686 
00687     PK11_GenerateRandom(lmResp, 8);
00688     memset(lmResp + 8, 0, LM_RESP_LEN - 8);
00689 
00690     memcpy(temp, msg.challenge, 8);
00691     memcpy(temp + 8, lmResp, 8);
00692     md5sum(temp, 16, sessionHash);
00693 
00694     NTLM_Hash(password, ntlmHash);
00695     LM_Response(ntlmHash, sessionHash, ntlmResp);
00696   }
00697   else
00698   {
00699     NTLM_Hash(password, ntlmHash);
00700     LM_Response(ntlmHash, msg.challenge, ntlmResp);
00701 
00702     if (SendLM())
00703     {
00704       PRUint8 lmHash[LM_HASH_LEN];
00705       LM_Hash(password, lmHash);
00706       LM_Response(lmHash, msg.challenge, lmResp);
00707     }
00708     else
00709     {
00710       // According to http://davenport.sourceforge.net/ntlm.html#ntlmVersion2,
00711       // the correct way to not send the LM hash is to send the NTLM hash twice
00712       // in both the LM and NTLM response fields.
00713       LM_Response(ntlmHash, msg.challenge, lmResp);
00714     }
00715   }
00716 
00717   //
00718   // finally, we assemble the Type-3 msg :-)
00719   //
00720   void *cursor = *outBuf;
00721   PRUint32 offset;
00722 
00723   // 0 : signature
00724   cursor = WriteBytes(cursor, NTLM_SIGNATURE, sizeof(NTLM_SIGNATURE));
00725 
00726   // 8 : marker
00727   cursor = WriteBytes(cursor, NTLM_TYPE3_MARKER, sizeof(NTLM_TYPE3_MARKER));
00728 
00729   // 12 : LM response sec buf
00730   offset = NTLM_TYPE3_HEADER_LEN + domainLen + userLen + hostLen;
00731   cursor = WriteSecBuf(cursor, LM_RESP_LEN, offset);
00732   memcpy((PRUint8 *) *outBuf + offset, lmResp, LM_RESP_LEN);
00733 
00734   // 20 : NTLM response sec buf
00735   offset += LM_RESP_LEN;
00736   cursor = WriteSecBuf(cursor, NTLM_RESP_LEN, offset);
00737   memcpy((PRUint8 *) *outBuf + offset, ntlmResp, NTLM_RESP_LEN);
00738 
00739   // 28 : domain name sec buf
00740   offset = NTLM_TYPE3_HEADER_LEN;
00741   cursor = WriteSecBuf(cursor, domainLen, offset);
00742   memcpy((PRUint8 *) *outBuf + offset, domainPtr, domainLen);
00743 
00744   // 36 : user name sec buf
00745   offset += domainLen;
00746   cursor = WriteSecBuf(cursor, userLen, offset);
00747   memcpy((PRUint8 *) *outBuf + offset, userPtr, userLen);
00748 
00749   // 44 : workstation (host) name sec buf
00750   offset += userLen;
00751   cursor = WriteSecBuf(cursor, hostLen, offset);
00752   memcpy((PRUint8 *) *outBuf + offset, hostPtr, hostLen);
00753 
00754   // 52 : session key sec buf (not used)
00755   cursor = WriteSecBuf(cursor, 0, 0);
00756 
00757   // 60 : negotiated flags
00758   cursor = WriteDWORD(cursor, msg.flags & NTLM_TYPE1_FLAGS);
00759 
00760   return NS_OK;
00761 }
00762 
00763 //-----------------------------------------------------------------------------
00764 
00765 NS_IMPL_ISUPPORTS1(nsNTLMAuthModule, nsIAuthModule)
00766 
00767 nsNTLMAuthModule::~nsNTLMAuthModule()
00768 {
00769   ZapString(mPassword);
00770 }
00771 
00772 nsresult
00773 nsNTLMAuthModule::InitTest()
00774 {
00775   nsNSSShutDownPreventionLock locker;
00776   //
00777   // disable NTLM authentication when FIPS mode is enabled.
00778   //
00779   return PK11_IsFIPS() ? NS_ERROR_NOT_AVAILABLE : NS_OK;
00780 }
00781 
00782 NS_IMETHODIMP
00783 nsNTLMAuthModule::Init(const char      *serviceName,
00784                        PRUint32         serviceFlags,
00785                        const PRUnichar *domain,
00786                        const PRUnichar *username,
00787                        const PRUnichar *password)
00788 {
00789   NS_ASSERTION(serviceName == nsnull, "unexpected service name");
00790   NS_ASSERTION(serviceFlags == nsIAuthModule::REQ_DEFAULT, "unexpected service flags");
00791 
00792   mDomain = domain;
00793   mUsername = username;
00794   mPassword = password;
00795   return NS_OK;
00796 }
00797 
00798 NS_IMETHODIMP
00799 nsNTLMAuthModule::GetNextToken(const void *inToken,
00800                                PRUint32    inTokenLen,
00801                                void      **outToken,
00802                                PRUint32   *outTokenLen)
00803 {
00804   nsresult rv;
00805   nsNSSShutDownPreventionLock locker;
00806   //
00807   // disable NTLM authentication when FIPS mode is enabled.
00808   //
00809   if (PK11_IsFIPS())
00810     return NS_ERROR_NOT_AVAILABLE;
00811 
00812   // if inToken is non-null, then assume it contains a type 2 message...
00813   if (inToken)
00814   {
00815     LogToken("in-token", inToken, inTokenLen);
00816     rv = GenerateType3Msg(mDomain, mUsername, mPassword, inToken,
00817                           inTokenLen, outToken, outTokenLen);
00818   }
00819   else
00820   {
00821     rv = GenerateType1Msg(outToken, outTokenLen);
00822   }
00823 
00824   if (NS_SUCCEEDED(rv))
00825     LogToken("out-token", *outToken, *outTokenLen);
00826 
00827   return rv;
00828 }
00829 
00830 NS_IMETHODIMP
00831 nsNTLMAuthModule::Unwrap(const void *inToken,
00832                         PRUint32    inTokenLen,
00833                         void      **outToken,
00834                         PRUint32   *outTokenLen)
00835 {
00836   return NS_ERROR_NOT_IMPLEMENTED;
00837 }
00838 
00839 NS_IMETHODIMP
00840 nsNTLMAuthModule::Wrap(const void *inToken,
00841                        PRUint32    inTokenLen,
00842                        PRBool      confidential,
00843                        void      **outToken,
00844                        PRUint32   *outTokenLen)
00845 {
00846   return NS_ERROR_NOT_IMPLEMENTED;
00847 }
00848 
00849 //-----------------------------------------------------------------------------
00850 // DES support code
00851 
00852 // set odd parity bit (in least significant bit position)
00853 static PRUint8
00854 des_setkeyparity(PRUint8 x)
00855 {
00856   if ((((x >> 7) ^ (x >> 6) ^ (x >> 5) ^
00857         (x >> 4) ^ (x >> 3) ^ (x >> 2) ^
00858         (x >> 1)) & 0x01) == 0)
00859     x |= 0x01;
00860   else
00861     x &= 0xfe;
00862   return x;
00863 }
00864 
00865 // build 64-bit des key from 56-bit raw key
00866 static void
00867 des_makekey(const PRUint8 *raw, PRUint8 *key)
00868 {
00869   key[0] = des_setkeyparity(raw[0]);
00870   key[1] = des_setkeyparity((raw[0] << 7) | (raw[1] >> 1));
00871   key[2] = des_setkeyparity((raw[1] << 6) | (raw[2] >> 2));
00872   key[3] = des_setkeyparity((raw[2] << 5) | (raw[3] >> 3));
00873   key[4] = des_setkeyparity((raw[3] << 4) | (raw[4] >> 4));
00874   key[5] = des_setkeyparity((raw[4] << 3) | (raw[5] >> 5));
00875   key[6] = des_setkeyparity((raw[5] << 2) | (raw[6] >> 6));
00876   key[7] = des_setkeyparity((raw[6] << 1));
00877 }
00878 
00879 // run des encryption algorithm (using NSS)
00880 static void
00881 des_encrypt(const PRUint8 *key, const PRUint8 *src, PRUint8 *hash)
00882 {
00883   CK_MECHANISM_TYPE cipherMech = CKM_DES_ECB;
00884   PK11SlotInfo *slot = nsnull;
00885   PK11SymKey *symkey = nsnull;
00886   PK11Context *ctxt = nsnull;
00887   SECItem keyItem, *param = nsnull;
00888   SECStatus rv;
00889   unsigned int n;
00890   
00891   slot = PK11_GetBestSlot(cipherMech, nsnull);
00892   if (!slot)
00893   {
00894     NS_ERROR("no slot");
00895     goto done;
00896   }
00897 
00898   keyItem.data = (PRUint8 *) key;
00899   keyItem.len = 8;
00900   symkey = PK11_ImportSymKey(slot, cipherMech,
00901                              PK11_OriginUnwrap, CKA_ENCRYPT,
00902                              &keyItem, nsnull);
00903   if (!symkey)
00904   {
00905     NS_ERROR("no symkey");
00906     goto done;
00907   }
00908 
00909   // no initialization vector required
00910   param = PK11_ParamFromIV(cipherMech, nsnull);
00911   if (!param)
00912   {
00913     NS_ERROR("no param");
00914     goto done;
00915   }
00916 
00917   ctxt = PK11_CreateContextBySymKey(cipherMech, CKA_ENCRYPT,
00918                                     symkey, param);
00919   if (!ctxt)
00920   {
00921     NS_ERROR("no context");
00922     goto done;
00923   }
00924 
00925   rv = PK11_CipherOp(ctxt, hash, (int *) &n, 8, (PRUint8 *) src, 8);
00926   if (rv != SECSuccess)
00927   {
00928     NS_ERROR("des failure");
00929     goto done;
00930   }
00931 
00932   rv = PK11_DigestFinal(ctxt, hash+8, &n, 0);
00933   if (rv != SECSuccess)
00934   {
00935     NS_ERROR("des failure");
00936     goto done;
00937   }
00938 
00939 done:
00940   if (ctxt)
00941     PK11_DestroyContext(ctxt, PR_TRUE);
00942   if (symkey)
00943     PK11_FreeSymKey(symkey);
00944   if (param)
00945     SECITEM_FreeItem(param, PR_TRUE);
00946   if (slot)
00947     PK11_FreeSlot(slot);
00948 }
00949 
00950 //-----------------------------------------------------------------------------
00951 // MD5 support code
00952 
00953 static void md5sum(const PRUint8 *input, PRUint32 inputLen, PRUint8 *result)
00954 {
00955   PK11Context *ctxt = PK11_CreateDigestContext(SEC_OID_MD5);
00956   if (ctxt)
00957   {
00958     if (PK11_DigestBegin(ctxt) == SECSuccess)
00959     {
00960       if (PK11_DigestOp(ctxt, input, inputLen) == SECSuccess)
00961       {
00962         PRUint32 resultLen = 16;
00963         PK11_DigestFinal(ctxt, result, &resultLen, resultLen);
00964       }
00965     }
00966     PK11_DestroyContext(ctxt, PR_TRUE);
00967   }
00968 }