]> granicus.if.org Git - curl/commitdiff
Many fixes, most of them based on comments by Eric Glass
authorDaniel Stenberg <daniel@haxx.se>
Thu, 26 Jun 2003 11:26:26 +0000 (11:26 +0000)
committerDaniel Stenberg <daniel@haxx.se>
Thu, 26 Jun 2003 11:26:26 +0000 (11:26 +0000)
lib/http_ntlm.c

index 066807784077cdf28dd433439b75c184ceace99e..c874ca8a2e48afe73c944b61a13df2329a97f02b 100644 (file)
  ***************************************************************************/
 #include "setup.h"
 
-/* All NTLM details here: http://www.innovation.ch/java/ntlm.html */
+/* NTLM details:
+   
+   http://davenport.sourceforge.net/ntlm.html
+   http://www.innovation.ch/java/ntlm.html
+
+*/
 
 #ifndef CURL_DISABLE_HTTP
 #ifdef USE_SSLEAY
 #endif
 
 /* The last #include file should be: */
-#ifdef MALLOCDEBUG
+#ifdef CURLDEBUG
 #include "memdebug.h"
 #endif
 
-/*
-  The one and only master resource for NTLM "hacking":
+/* Define this to make the type-3 message include the NT response message */
+#undef USE_NTRESPONSES
 
-  ====> http://www.innovation.ch/java/ntlm.html <====
+/*
+  (*) = A "security buffer" is a triplet consisting of two shorts and one
+  long:
 
-  Brought to the world by Ronald Tschalär.
+  1. a 'short' containing the length of the buffer in bytes
+  2. a 'short' containing the allocated space for the buffer in bytes
+  3. a 'long' containing the offset to the start of the buffer from the
+     beginning of the NTLM message, in bytes.
 */
 
-/* Test example header:
-
-WWW-Authenticate: NTLM
-
-*/
 
 CURLntlm Curl_input_ntlm(struct connectdata *conn,
                          char *header) /* rest of the www-authenticate:
@@ -101,31 +106,18 @@ CURLntlm Curl_input_ntlm(struct connectdata *conn,
       header++;
 
     if(*header) {
-      /* we got a type-2 message here */
-
-      /* My test-IE session reveived this type-2:
-
-      TlRMTVNTUAACAAAAAgACADAAAAAGgoEAc51AYVDgyNcAAAAAAAAAAG4AbgA\
-      yAAAAQ0MCAAQAQwBDAAEAEgBFAEwASQBTAEEAQgBFAFQASAAEABgAYwBjAC4\
-      AaQBjAGUAZABlAHYALgBuAHUAAwAsAGUAbABpAHMAYQBiAGUAdABoAC4AYwB\
-      jAC4AaQBjAGUAZABlAHYALgBuAHUAAAAAAA==
-
-      which translates to this:
-
-0x00: 4e 54 4c 4d 53 53 50 00 02 00 00 00 02 00 02 00  | NTLMSSP.........
-0x10: 30 00 00 00 06 82 81 00 73 9d 40 61 50 e0 c8 d7  | 0.......s.@aP...
-0x20: 00 00 00 00 00 00 00 00 6e 00 6e 00 32 00 00 00  | ........n.n.2...
-0x30: 43 43 02 00 04 00 43 00 43 00 01 00 12 00 45 00  | CC....C.C.....E.
-0x40: 4c 00 49 00 53 00 41 00 42 00 45 00 54 00 48 00  | L.I.S.A.B.E.T.H.
-0x50: 04 00 18 00 63 00 63 00 2e 00 69 00 63 00 65 00  | ....c.c...i.c.e.
-0x60: 64 00 65 00 76 00 2e 00 6e 00 75 00 03 00 2c 00  | d.e.v...n.u...,.
-0x70: 65 00 6c 00 69 00 73 00 61 00 62 00 65 00 74 00  | e.l.i.s.a.b.e.t.
-0x80: 68 00 2e 00 63 00 63 00 2e 00 69 00 63 00 65 00  | h...c.c...i.c.e.
-0x90: 64 00 65 00 76 00 2e 00 6e 00 75 00 00 00 00 00  | d.e.v...n.u.....
-
-This is not the same format as described on the web page, but doing repeated
-requests show that 0x18-0x1f seems to be the nonce anyway.
-
+      /* We got a type-2 message here:
+
+         Index   Description         Content
+         0       NTLMSSP Signature   Null-terminated ASCII "NTLMSSP"
+                                     (0x4e544c4d53535000)
+         8       NTLM Message Type   long (0x02000000)
+         12      Target Name         security buffer(*)
+         20      Flags               long
+         24      Challenge           8 bytes
+         (32)    Context (optional)  8 bytes (two consecutive longs)
+         (40)    Target Information  (optional) security buffer(*)
+         32 (48) start of data block
       */
 
       int size = Curl_base64_decode(header, buffer);
@@ -135,6 +127,9 @@ requests show that 0x18-0x1f seems to be the nonce anyway.
       if(size >= 48)
         /* the nonce of interest is index [24 .. 31], 8 bytes */
         memcpy(data->state.ntlm.nonce, &buffer[24], 8);
+
+      /* at index decimal 20, there's a 32bit NTLM flag field */
+
     }
     else {
       if(data->state.ntlm.state >= NTLMSTATE_TYPE1)
@@ -198,12 +193,16 @@ static void calc_resp(unsigned char *keys,
  */
 static void mkhash(char *password,
                    unsigned char *nonce,  /* 8 bytes */
-                   unsigned char *lmresp, /* must fit 0x18 bytes */
-                   unsigned char *ntresp) /* must fit 0x18 bytes */
+                   unsigned char *lmresp  /* must fit 0x18 bytes */
+#ifdef USE_NTRESPONSES
+                   , unsigned char *ntresp  /* must fit 0x18 bytes */
+#endif
+  )
 {
   unsigned char lmbuffer[21];
+#ifdef USE_NTRESPONSES
   unsigned char ntbuffer[21];
-  
+#endif
   unsigned char *pw;
   static const unsigned char magic[] = {
     0x4B, 0x47, 0x53, 0x21, 0x40, 0x23, 0x24, 0x25
@@ -239,7 +238,10 @@ static void mkhash(char *password,
 
     memset(lmbuffer+16, 0, 5);
   }
+  /* create LM responses */
+  calc_resp(lmbuffer, nonce, lmresp);
 
+#ifdef USE_NTRESPONSES
   {
     /* create NT hashed password */
     MD4_CTX MD4;
@@ -258,35 +260,22 @@ static void mkhash(char *password,
     memset(ntbuffer+16, 0, 8);
   }
 
-  /* create responses */
-  calc_resp(lmbuffer, nonce, lmresp);
   calc_resp(ntbuffer, nonce, ntresp);
+#endif
 
   free(pw);
 }
 
-/* convert an ascii string to upper case unicode, the destination buffer
-   must fit twice the source size */
-static void ascii_to_unicode(unsigned char *destunicode,
-                             unsigned char *sourceascii,
-                             bool conv)
-{
-  while (*sourceascii) {
-    destunicode[0] = conv?toupper(*sourceascii):*sourceascii;
-    destunicode[1] = '\0';
-    destunicode += 2;
-    sourceascii++;
-  }
-}
-
 #define SHORTPAIR(x) ((x) & 0xff), ((x) >> 8)
+#define LONGQUARTET(x) ((x) & 0xff), (((x) >> 8)&0xff), \
+  (((x) >>16)&0xff), ((x)>>24)
 
 /* this is for creating ntlm header output */
 CURLcode Curl_output_ntlm(struct connectdata *conn)
 {
   struct SessionHandle *data=conn->data;
-  const char *domain="CURL";
-  const char *host="HAXX";
+  const char *domain=""; /* empty */
+  const char *host=""; /* empty */
   int domlen=strlen(domain);
   int hostlen = strlen(host);
   int hostoff; /* host name offset */
@@ -295,49 +284,46 @@ CURLcode Curl_output_ntlm(struct connectdata *conn)
   char *base64=NULL;
 
   unsigned char ntlm[256]; /* enough, unless the host/domain is very long */
-  if(NTLMSTATE_TYPE1 == data->state.ntlm.state) {
+  switch(data->state.ntlm.state) {
+  case NTLMSTATE_TYPE1:
+  default: /* for the weird cases we (re)start here */
     hostoff = 32;
     domoff = hostoff + hostlen;
     
-    /* IE used this as type-1 maessage:
+    /* Create and send a type-1 message:
 
-    Authorization: NTLM \
-    TlRMTVNTUAABAAAABoIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAAAAAAAwAAAA\r\n
-
-    This translates into:
-
-0x00: 4e 54 4c 4d 53 53 50 00 01 00 00 00 06 82 00 00  | NTLMSSP.........
-0x10: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  | ................
-0x20: 00 00 00 00 30 00 00 00 00 00 00 00 30 00 00 00  | ....0.......0...
-
-    Which isn't following the web spec. This uses 0x8206 instead of 0xb203
-    and sends a longer chunk of data than we do! Interestingly, there's no
-    host or domain either.
-
-    We want to send something like this:
-
-0x00: 4e 54 4c 4d 53 53 50 00 01 00 00 00 03 b2 00 00  | NTLMSSP.........
-0x10: 05 00 05 00 2b 00 00 00 0b 00 0b 00 20 00 00 00  | ....+...........
-0x20: 4c 49 4c 4c 41 53 59 53 54 45 52 48 45 4d 4d 41  | LILLASYSTERHEMMA
+    Index Description          Content
+    0     NTLMSSP Signature    Null-terminated ASCII "NTLMSSP"
+                               (0x4e544c4d53535000)
+    8     NTLM Message Type    long (0x01000000)
+    12    Flags                long
+    16    Supplied Domain      security buffer(*)
+    24    Supplied Workstation security buffer(*)
+    32    start of data block
 
     */
 
     snprintf((char *)ntlm, sizeof(ntlm), "NTLMSSP%c"
-             "\x01" /* type 1 */
-             "%c%c%c"
-             "\x03\xb2"
-             "%c%c"
-             "%c%c"  /* domain length */
+             "\x01%c%c%c" /* 32-bit type = 1 */
+             "%c%c%c%c"   /* 32-bit NTLM flag field */
              "%c%c"  /* domain length */
+             "%c%c"  /* domain allocated space */
              "%c%c"  /* domain name offset */
              "%c%c"  /* 2 zeroes */
              "%c%c"  /* host length */
-             "%c%c"  /* host length */
+             "%c%c"  /* host allocated space */
              "%c%c"  /* host name offset */
              "%c%c"  /* 2 zeroes */
-             "%s" /* host name */
-             "%s", /* domain string */
-             0,0,0,0,0,0,
+             "%s"   /* host name */
+             "%s",  /* domain string */
+             0,     /* trailing zero */
+             0,0,0, /* part of type-1 long */
+
+             LONGQUARTET(
+               NTLMFLAG_NEGOTIATE_OEM|      /*   2 */
+               NTLMFLAG_NEGOTIATE_NTLM_KEY  /* 200 */
+               /* equals 0x0202 */
+               ),
              SHORTPAIR(domlen),
              SHORTPAIR(domlen),
              SHORTPAIR(domoff),
@@ -350,11 +336,7 @@ CURLcode Curl_output_ntlm(struct connectdata *conn)
 
     /* initial packet length */
     size = 32 + hostlen + domlen;
-#if 0
-    #define CHUNK "\x4e\x54\x4c\x4d\x53\x53\x50\x00\x01\x00\x00\x00\x06\x82\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x30\x00\x00\x00\x00\x00\x00\x00\x30\x00\x00\x00"
-    memcpy(ntlm, CHUNK, sizeof(CHUNK)-1);
-    size = sizeof(CHUNK)-1;
-#endif    
+
     /* now keeper of the base64 encoded package size */
     size = Curl_base64_encode(ntlm, size, &base64);
 
@@ -365,55 +347,43 @@ CURLcode Curl_output_ntlm(struct connectdata *conn)
     }
     else
       return CURLE_OUT_OF_MEMORY; /* FIX TODO */
-  }
-  else {
-    if(NTLMSTATE_TYPE2 == data->state.ntlm.state) {
-      /* We received the type-2 already, create a type-3 message */
-
-    /*
-      My test-IE session sent this type-3:
-
-      TlRMTVNTUAADAAAAGAAYAEoAAAAAAAAAYgAAAAUABQA0AAAABgAGADk\
-      AAAALAAsAPwAAAEhFTU1BZGFuaWVsTElMTEFTWVNURVJPVPJELoebUg\
-      4SvW0ed2QmKu0SjX4qNrI=
-
-      Which translates to:
-
-0x00: 4e 54 4c 4d 53 53 50 00 03 00 00 00 18 00 18 00  | NTLMSSP.........
-0x10: 4a 00 00 00 00 00 00 00 62 00 00 00 05 00 05 00  | J.......b.......
-0x20: 34 00 00 00 06 00 06 00 39 00 00 00 0b 00 0b 00  | 4.......9.......
-0x30: 3f 00 00 00 48 45 4d 4d 41 64 61 6e 69 65 6c 4c  | ?...HEMMAdanielL
-0x40: 49 4c 4c 41 53 59 53 54 45 52 4f 54 f2 44 2e 87  | ILLASYSTEROT.D..
-0x50: 9b 52 0e 12 bd 6d 1e 77 64 26 2a ed 12 8d 7e 2a  | .R...m.wd&*...~*
-0x60: 36 b2                                            | 6.
-
-      Note how the domain + username + hostname ARE NOT unicoded in any way.
-      Domain and hostname are uppercase, while username are case sensitive.
-
-      We send something like this:
-
-0x00: 4e 54 4c 4d 53 53 50 00 03 00 00 00 18 00 18 00  | NTLMSSP.........
-0x10: 6c 00 00 00 18 00 18 00 84 00 00 00 0a 00 0a 00  | l...............
-0x20: 40 00 00 00 0c 00 0c 00 4a 00 00 00 16 00 16 00  | @.......J.......
-0x30: 56 00 00 00 00 00 00 00 9c 00 00 00 01 82 00 00  | V...............
-0x40: 48 00 45 00 4d 00 4d 00 41 00 64 00 61 00 6e 00  | H.E.M.M.A.d.a.n.
-0x50: 69 00 65 00 6c 00 4c 00 49 00 4c 00 4c 00 41 00  | i.e.l.L.I.L.L.A.
-0x60: 53 00 59 00 53 00 54 00 45 00 52 00 bc ed 28 c9  | S.Y.S.T.E.R...(.
-0x70: 16 c4 1b 16 d7 c9 b4 0e ef ef 02 6d 26 8d c0 ba  | ...........m&...
-0x80: ac b6 5a c1 26 8d c0 ba ac b6 5a c1 26 8d c0 ba  | ..Z.&.....Z.&...
-0x90: ac b6 5a c1 26 8d c0 ba ac b6 5a c1              | ..Z.&.....Z.
 
-    */
+    break;
+    
+  case NTLMSTATE_TYPE2:
+    /* We received the type-2 already, create a type-3 message:
+
+    Index   Description            Content
+    0       NTLMSSP Signature      Null-terminated ASCII "NTLMSSP"
+                                   (0x4e544c4d53535000)
+    8       NTLM Message Type      long (0x03000000)
+    12      LM/LMv2 Response       security buffer(*)
+    20      NTLM/NTLMv2 Response   security buffer(*)
+    28      Domain Name            security buffer(*)
+    36      User Name              security buffer(*)
+    44      Workstation Name       security buffer(*)
+    (52)    Session Key (optional) security buffer(*)
+    (60)    Flags (optional)       long
+    52 (64) start of data block
 
+    */
+  
+  {
     int lmrespoff;
     int ntrespoff;
     int useroff;
     unsigned char lmresp[0x18]; /* fixed-size */
+#ifdef USE_NTRESPONSES
     unsigned char ntresp[0x18]; /* fixed-size */
-
+#endif
     int userlen = strlen(data->state.user);
     
-    mkhash(data->state.passwd, &data->state.ntlm.nonce[0], lmresp, ntresp);
+    mkhash(data->state.passwd, &data->state.ntlm.nonce[0], lmresp
+#ifdef USE_NTRESPONSES
+           , ntresp
+#endif
+
+);
 
     /* these are going unicode */
     domlen *= 2;
@@ -429,29 +399,29 @@ CURLcode Curl_output_ntlm(struct connectdata *conn)
     /* Create the big type-3 message binary blob */
     size = snprintf((char *)ntlm, sizeof(ntlm),
                     "NTLMSSP%c"
-                    "\x03" /* type 3 */
-                    "%c%c%c" /* 3 zeroes */
+                    "\x03%c%c%c" /* type-3, 32 bits */
 
-                    "%c%c%c%c" /* LanManager length twice */
+                    "%c%c%c%c" /* LanManager length + allocated space */
                     "%c%c" /* LanManager offset */
                     "%c%c" /* 2 zeroes */
 
-                    "%c%c%c%c" /* NT-response length twice */
+                    "%c%c" /* NT-response length */
+                    "%c%c" /* NT-response allocated space */
                     "%c%c" /* NT-response offset */
                     "%c%c" /* 2 zeroes */
                     
                     "%c%c"  /* domain length */
-                    "%c%c"  /* domain length */
+                    "%c%c"  /* domain allocated space */
                     "%c%c"  /* domain name offset */
                     "%c%c"  /* 2 zeroes */
                     
                     "%c%c"  /* user length */
-                    "%c%c"  /* user length */
+                    "%c%c"  /* user allocated space */
                     "%c%c"  /* user offset */
                     "%c%c"  /* 2 zeroes */
                     
                     "%c%c"  /* host length */
-                    "%c%c"  /* host length */
+                    "%c%c"  /* host allocated space */
                     "%c%c"  /* host offset */
                     "%c%c%c%c%c%c"  /* 6 zeroes */
                     
@@ -467,16 +437,21 @@ CURLcode Curl_output_ntlm(struct connectdata *conn)
                     /* LanManager response */
                     /* NT response */
                     ,
-                    0,
-                    0,0,0,
+                    0, /* zero termination */
+                    0,0,0, /* type-3 long, the 24 upper bits */
 
                     SHORTPAIR(0x18),  /* LanManager response length, twice */
                     SHORTPAIR(0x18),
                     SHORTPAIR(lmrespoff),
                     0x0, 0x0,
                     
+#ifdef USE_NTRESPONSES
                     SHORTPAIR(0x18),  /* NT-response length, twice */
                     SHORTPAIR(0x18),
+#else
+                    0x0, 0x0,
+                    0x0, 0x0,
+#endif
                     SHORTPAIR(ntrespoff),
                     0x0, 0x0,
 
@@ -503,50 +478,25 @@ CURLcode Curl_output_ntlm(struct connectdata *conn)
     size=64;
     ntlm[62]=ntlm[63]=0;
 
-#if 1
-    ascii_to_unicode(&ntlm[size], (unsigned char *)domain, TRUE);
-    size += domlen;
-    
-    ascii_to_unicode(&ntlm[size], (unsigned char *)data->state.user, FALSE);
-    size += userlen;
-
-    ascii_to_unicode(&ntlm[size], (unsigned char *)host, TRUE);
-    size += hostlen;
-#else
-    strcpy(&ntlm[size], (unsigned char *)domain);
-    size += domlen;
-
-    strcpy(&ntlm[size], (unsigned char *)data->state.user);
+    memcpy(&ntlm[size], data->state.user, userlen);
     size += userlen;
 
-    strcpy(&ntlm[size], (unsigned char *)host);
-    size += hostlen;
-#endif
-
     /* we append the binary hashes to the end of the blob */
     if(size < ((int)sizeof(ntlm) - 0x18)) {
       memcpy(&ntlm[size], lmresp, 0x18);
       size += 0x18;
     }
 
+#ifdef USE_NTRESPONSES
     if(size < ((int)sizeof(ntlm) - 0x18)) {      
       memcpy(&ntlm[size], ntresp, 0x18);
       size += 0x18;
     }
-
+#endif
 
     ntlm[56] = size & 0xff;
     ntlm[57] = size >> 8;
 
-#if 0
-#undef CHUNK
-
-#define CHUNK "\x4e\x54\x4c\x4d\x53\x53\x50\x00\x03\x00\x00\x00\x18\x00\x18\x00\x4a\x00\x00\x00\x00\x00\x00\x00\x62\x00\x00\x00\x05\x00\x05\x00\x34\x00\x00\x00\x06\x00\x06\x00\x39\x00\x00\x00\x0b\x00\x0b\x00\x3f\x00\x00\x00\x48\x45\x4d\x4d\x41\x64\x61\x6e\x69\x65\x6c\x4c\x49\x4c\x4c\x41\x53\x59\x53\x54\x45\x52\x4f\x54\xf2\x44\x2e\x87\x9b\x52\x0e\x12\xbd\x6d\x1e\x77\x64\x26\x2a\xed\x12\x8d\x7e\x2a\x36\xb2"
-    memcpy(ntlm, CHUNK, sizeof(CHUNK)-1);
-    size = sizeof(CHUNK)-1;
-#endif
-
-    
     /* convert the binary blob into base64 */
     size = Curl_base64_encode(ntlm, size, &base64);
 
@@ -558,17 +508,19 @@ CURLcode Curl_output_ntlm(struct connectdata *conn)
     else
       return CURLE_OUT_OF_MEMORY; /* FIX TODO */
 
-      data->state.ntlm.state = NTLMSTATE_TYPE3; /* we sent a type-3 */
-
-    } else 
-      if(NTLMSTATE_TYPE3 == data->state.ntlm.state) {
-        /* connection is already authenticated,
-         * don't send a header in future requests */
-          if(conn->allocptr.userpwd) {
-            free(conn->allocptr.userpwd);
-            conn->allocptr.userpwd=NULL;
-          }
-      }
+    data->state.ntlm.state = NTLMSTATE_TYPE3; /* we sent a type-3 */
+    
+  }
+  break;
+
+  case NTLMSTATE_TYPE3:
+    /* connection is already authenticated,
+     * don't send a header in future requests */
+    if(conn->allocptr.userpwd) {
+      free(conn->allocptr.userpwd);
+      conn->allocptr.userpwd=NULL;
+    }
+    break;
   }
 
   return CURLE_OK;