]> icculus.org git repositories - icculus/iodoom3.git/blob - neo/curl/lib/http_ntlm.c
Various Mac OS X tweaks to get this to build. Probably breaking things.
[icculus/iodoom3.git] / neo / curl / lib / http_ntlm.c
1 /***************************************************************************
2  *                                  _   _ ____  _     
3  *  Project                     ___| | | |  _ \| |    
4  *                             / __| | | | |_) | |    
5  *                            | (__| |_| |  _ <| |___ 
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) 1998 - 2004, Daniel Stenberg, <daniel@haxx.se>, et al.
9  *
10  * This software is licensed as described in the file COPYING, which
11  * you should have received as part of this distribution. The terms
12  * are also available at http://curl.haxx.se/docs/copyright.html.
13  * 
14  * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15  * copies of the Software, and permit persons to whom the Software is
16  * furnished to do so, under the terms of the COPYING file.
17  *
18  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19  * KIND, either express or implied.
20  *
21  * $Id: http_ntlm.c,v 1.28 2004/03/08 16:20:51 bagder Exp $
22  ***************************************************************************/
23 #include "setup.h"
24
25 /* NTLM details:
26    
27    http://davenport.sourceforge.net/ntlm.html
28    http://www.innovation.ch/java/ntlm.html
29
30 */
31
32 #ifndef CURL_DISABLE_HTTP
33 #ifdef USE_SSLEAY
34 /* We need OpenSSL for the crypto lib to provide us with MD4 and DES */
35
36 /* -- WIN32 approved -- */
37 #include <stdio.h>
38 #include <string.h>
39 #include <stdarg.h>
40 #include <stdlib.h>
41 #include <ctype.h>
42
43 #include "urldata.h"
44 #include "sendf.h"
45 #include "strequal.h"
46 #include "base64.h"
47 #include "http_ntlm.h"
48 #include "url.h"
49 #include "http.h" /* for Curl_http_auth_stage() */
50
51 #define _MPRINTF_REPLACE /* use our functions only */
52 #include <curl/mprintf.h>
53
54 #include <openssl/des.h>
55 #include <openssl/md4.h>
56 #include <openssl/ssl.h>
57
58 #if OPENSSL_VERSION_NUMBER < 0x00907001L
59 #define DES_key_schedule des_key_schedule
60 #define DES_cblock des_cblock
61 #define DES_set_odd_parity des_set_odd_parity
62 #define DES_set_key des_set_key
63 #define DES_ecb_encrypt des_ecb_encrypt
64
65 /* This is how things were done in the old days */
66 #define DESKEY(x) x
67 #define DESKEYARG(x) x
68 #else
69 /* Modern version */
70 #define DESKEYARG(x) *x
71 #define DESKEY(x) &x
72 #endif
73
74 /* The last #include file should be: */
75 #ifdef CURLDEBUG
76 #include "memdebug.h"
77 #endif
78
79 /* Define this to make the type-3 message include the NT response message */
80 #undef USE_NTRESPONSES
81
82 /*
83   (*) = A "security buffer" is a triplet consisting of two shorts and one
84   long:
85
86   1. a 'short' containing the length of the buffer in bytes
87   2. a 'short' containing the allocated space for the buffer in bytes
88   3. a 'long' containing the offset to the start of the buffer from the
89      beginning of the NTLM message, in bytes.
90 */
91
92
93 CURLntlm Curl_input_ntlm(struct connectdata *conn,
94                          bool proxy,   /* if proxy or not */
95                          char *header) /* rest of the www-authenticate:
96                                           header */
97 {
98   /* point to the correct struct with this */
99   struct ntlmdata *ntlm;
100
101   ntlm = proxy?&conn->proxyntlm:&conn->ntlm;
102
103   /* skip initial whitespaces */
104   while(*header && isspace((int)*header))
105     header++;
106
107   if(checkprefix("NTLM", header)) {
108     unsigned char buffer[256];
109     header += strlen("NTLM");
110
111     while(*header && isspace((int)*header))
112       header++;
113
114     if(*header) {
115       /* We got a type-2 message here:
116
117          Index   Description         Content
118          0       NTLMSSP Signature   Null-terminated ASCII "NTLMSSP"
119                                      (0x4e544c4d53535000)
120          8       NTLM Message Type   long (0x02000000)
121          12      Target Name         security buffer(*)
122          20      Flags               long
123          24      Challenge           8 bytes
124          (32)    Context (optional)  8 bytes (two consecutive longs)
125          (40)    Target Information  (optional) security buffer(*)
126          32 (48) start of data block
127       */
128
129       size_t size = Curl_base64_decode(header, (char *)buffer);
130
131       ntlm->state = NTLMSTATE_TYPE2; /* we got a type-2 */
132
133       if(size >= 48)
134         /* the nonce of interest is index [24 .. 31], 8 bytes */
135         memcpy(ntlm->nonce, &buffer[24], 8);
136
137       /* at index decimal 20, there's a 32bit NTLM flag field */
138       
139     }
140     else {
141       if(ntlm->state >= NTLMSTATE_TYPE1)
142         return CURLNTLM_BAD;
143
144       ntlm->state = NTLMSTATE_TYPE1; /* we should sent away a type-1 */
145     }
146   }
147   return CURLNTLM_FINE;
148 }
149
150 /*
151  * Turns a 56 bit key into the 64 bit, odd parity key and sets the key.  The
152  * key schedule ks is also set.
153  */
154 static void setup_des_key(unsigned char *key_56,
155                           DES_key_schedule DESKEYARG(ks))
156 {
157   DES_cblock key;
158
159   key[0] = key_56[0];
160   key[1] = ((key_56[0] << 7) & 0xFF) | (key_56[1] >> 1);
161   key[2] = ((key_56[1] << 6) & 0xFF) | (key_56[2] >> 2);
162   key[3] = ((key_56[2] << 5) & 0xFF) | (key_56[3] >> 3);
163   key[4] = ((key_56[3] << 4) & 0xFF) | (key_56[4] >> 4);
164   key[5] = ((key_56[4] << 3) & 0xFF) | (key_56[5] >> 5);
165   key[6] = ((key_56[5] << 2) & 0xFF) | (key_56[6] >> 6);
166   key[7] =  (key_56[6] << 1) & 0xFF;
167
168   DES_set_odd_parity(&key);
169   DES_set_key(&key, ks);
170 }
171
172  /*
173   * takes a 21 byte array and treats it as 3 56-bit DES keys. The
174   * 8 byte plaintext is encrypted with each key and the resulting 24
175   * bytes are stored in the results array.
176   */
177 static void calc_resp(unsigned char *keys,
178                       unsigned char *plaintext,
179                       unsigned char *results)
180 {
181   DES_key_schedule ks;
182
183   setup_des_key(keys, DESKEY(ks));
184   DES_ecb_encrypt((DES_cblock*) plaintext, (DES_cblock*) results,
185                   DESKEY(ks), DES_ENCRYPT);
186
187   setup_des_key(keys+7, DESKEY(ks));
188   DES_ecb_encrypt((DES_cblock*) plaintext, (DES_cblock*) (results+8),
189                   DESKEY(ks), DES_ENCRYPT);
190
191   setup_des_key(keys+14, DESKEY(ks));
192   DES_ecb_encrypt((DES_cblock*) plaintext, (DES_cblock*) (results+16),
193                   DESKEY(ks), DES_ENCRYPT);
194 }
195
196 /*
197  * Set up lanmanager and nt hashed passwords
198  */
199 static void mkhash(char *password,
200                    unsigned char *nonce,  /* 8 bytes */
201                    unsigned char *lmresp  /* must fit 0x18 bytes */
202 #ifdef USE_NTRESPONSES
203                    , unsigned char *ntresp  /* must fit 0x18 bytes */
204 #endif
205   )
206 {
207   unsigned char lmbuffer[21];
208 #ifdef USE_NTRESPONSES
209   unsigned char ntbuffer[21];
210 #endif
211   unsigned char *pw;
212   static const unsigned char magic[] = {
213     0x4B, 0x47, 0x53, 0x21, 0x40, 0x23, 0x24, 0x25
214   };
215   unsigned int i;
216   size_t len = strlen(password);
217
218   /* make it fit at least 14 bytes */
219   pw = malloc(len<7?14:len*2);
220   if(!pw)
221     return; /* this will lead to a badly generated package */
222
223   if (len > 14)
224     len = 14;
225   
226   for (i=0; i<len; i++)
227     pw[i] = toupper(password[i]);
228
229   for (; i<14; i++)
230     pw[i] = 0;
231
232   {
233     /* create LanManager hashed password */
234     DES_key_schedule ks;
235
236     setup_des_key(pw, DESKEY(ks));
237     DES_ecb_encrypt((DES_cblock *)magic, (DES_cblock *)lmbuffer,
238                     DESKEY(ks), DES_ENCRYPT);
239   
240     setup_des_key(pw+7, DESKEY(ks));
241     DES_ecb_encrypt((DES_cblock *)magic, (DES_cblock *)(lmbuffer+8),
242                     DESKEY(ks), DES_ENCRYPT);
243
244     memset(lmbuffer+16, 0, 5);
245   }
246   /* create LM responses */
247   calc_resp(lmbuffer, nonce, lmresp);
248
249 #ifdef USE_NTRESPONSES
250   {
251     /* create NT hashed password */
252     MD4_CTX MD4;
253
254     len = strlen(password);
255
256     for (i=0; i<len; i++) {
257       pw[2*i]   = password[i];
258       pw[2*i+1] = 0;
259     }
260
261     MD4_Init(&MD4);
262     MD4_Update(&MD4, pw, 2*len);
263     MD4_Final(ntbuffer, &MD4);
264
265     memset(ntbuffer+16, 0, 8);
266   }
267
268   calc_resp(ntbuffer, nonce, ntresp);
269 #endif
270
271   free(pw);
272 }
273
274 #define SHORTPAIR(x) ((x) & 0xff), ((x) >> 8)
275 #define LONGQUARTET(x) ((x) & 0xff), (((x) >> 8)&0xff), \
276   (((x) >>16)&0xff), ((x)>>24)
277
278 /* this is for creating ntlm header output */
279 CURLcode Curl_output_ntlm(struct connectdata *conn,
280                           bool proxy,
281                           bool *ready)
282 {
283   const char *domain=""; /* empty */
284   const char *host=""; /* empty */
285   int domlen=(int)strlen(domain);
286   int hostlen = (int)strlen(host);
287   int hostoff; /* host name offset */
288   int domoff;  /* domain name offset */
289   size_t size;
290   char *base64=NULL;
291   unsigned char ntlmbuf[256]; /* enough, unless the host/domain is very long */
292
293   /* point to the address of the pointer that holds the string to sent to the
294      server, which is for a plain host or for a HTTP proxy */
295   char **allocuserpwd;
296
297   /* point to the name and password for this */
298   char *userp;
299   char *passwdp;
300   /* point to the correct struct with this */
301   struct ntlmdata *ntlm;
302
303   *ready = FALSE;
304
305   if(proxy) {
306     allocuserpwd = &conn->allocptr.proxyuserpwd;
307     userp = conn->proxyuser;
308     passwdp = conn->proxypasswd;
309     ntlm = &conn->proxyntlm;
310   }
311   else {
312     allocuserpwd = &conn->allocptr.userpwd;
313     userp = conn->user;
314     passwdp = conn->passwd;
315     ntlm = &conn->ntlm;
316   }
317
318   /* not set means empty */
319   if(!userp)
320     userp=(char *)"";
321
322   if(!passwdp)
323     passwdp=(char *)"";
324   
325   switch(ntlm->state) {
326   case NTLMSTATE_TYPE1:
327   default: /* for the weird cases we (re)start here */
328     hostoff = 32;
329     domoff = hostoff + hostlen;
330     
331     /* Create and send a type-1 message:
332
333     Index Description          Content
334     0     NTLMSSP Signature    Null-terminated ASCII "NTLMSSP"
335                                (0x4e544c4d53535000)
336     8     NTLM Message Type    long (0x01000000)
337     12    Flags                long
338     16    Supplied Domain      security buffer(*)
339     24    Supplied Workstation security buffer(*)
340     32    start of data block
341
342     */
343
344     snprintf((char *)ntlmbuf, sizeof(ntlmbuf), "NTLMSSP%c"
345              "\x01%c%c%c" /* 32-bit type = 1 */
346              "%c%c%c%c"   /* 32-bit NTLM flag field */
347              "%c%c"  /* domain length */
348              "%c%c"  /* domain allocated space */
349              "%c%c"  /* domain name offset */
350              "%c%c"  /* 2 zeroes */
351              "%c%c"  /* host length */
352              "%c%c"  /* host allocated space */
353              "%c%c"  /* host name offset */
354              "%c%c"  /* 2 zeroes */
355              "%s"   /* host name */
356              "%s",  /* domain string */
357              0,     /* trailing zero */
358              0,0,0, /* part of type-1 long */
359
360              LONGQUARTET(
361                NTLMFLAG_NEGOTIATE_OEM|      /*   2 */
362                NTLMFLAG_NEGOTIATE_NTLM_KEY  /* 200 */
363                /* equals 0x0202 */
364                ),
365              SHORTPAIR(domlen),
366              SHORTPAIR(domlen),
367              SHORTPAIR(domoff),
368              0,0,
369              SHORTPAIR(hostlen),
370              SHORTPAIR(hostlen),
371              SHORTPAIR(hostoff),
372              0,0,
373              host, domain);
374
375     /* initial packet length */
376     size = 32 + hostlen + domlen;
377
378     /* now keeper of the base64 encoded package size */
379     size = Curl_base64_encode((char *)ntlmbuf, size, &base64);
380
381     if(size >0 ) {
382       Curl_safefree(*allocuserpwd);
383       *allocuserpwd = aprintf("%sAuthorization: NTLM %s\r\n",
384                               proxy?"Proxy-":"",
385                               base64);
386       free(base64);
387     }
388     else
389       return CURLE_OUT_OF_MEMORY; /* FIX TODO */
390
391     break;
392     
393   case NTLMSTATE_TYPE2:
394     /* We received the type-2 already, create a type-3 message:
395
396     Index   Description            Content
397     0       NTLMSSP Signature      Null-terminated ASCII "NTLMSSP"
398                                    (0x4e544c4d53535000)
399     8       NTLM Message Type      long (0x03000000)
400     12      LM/LMv2 Response       security buffer(*)
401     20      NTLM/NTLMv2 Response   security buffer(*)
402     28      Domain Name            security buffer(*)
403     36      User Name              security buffer(*)
404     44      Workstation Name       security buffer(*)
405     (52)    Session Key (optional) security buffer(*)
406     (60)    Flags (optional)       long
407     52 (64) start of data block
408
409     */
410   
411   {
412     int lmrespoff;
413     int ntrespoff;
414     int useroff;
415     unsigned char lmresp[0x18]; /* fixed-size */
416 #ifdef USE_NTRESPONSES
417     unsigned char ntresp[0x18]; /* fixed-size */
418 #endif
419     const char *user;
420     int userlen;
421
422     user = strchr(userp, '\\');
423     if(!user)
424       user = strchr(userp, '/');
425
426     if (user) {
427       domain = userp;
428       domlen = user - domain;
429       user++;
430     }
431     else
432       user = userp;
433     userlen = strlen(user);
434
435     mkhash(passwdp, &ntlm->nonce[0], lmresp
436 #ifdef USE_NTRESPONSES
437            , ntresp
438 #endif
439       );
440
441     domoff = 64; /* always */
442     useroff = domoff + domlen;
443     hostoff = useroff + userlen;
444     lmrespoff = hostoff + hostlen;
445     ntrespoff = lmrespoff + 0x18;
446
447     /* Create the big type-3 message binary blob */
448     size = snprintf((char *)ntlmbuf, sizeof(ntlmbuf),
449                     "NTLMSSP%c"
450                     "\x03%c%c%c" /* type-3, 32 bits */
451
452                     "%c%c%c%c" /* LanManager length + allocated space */
453                     "%c%c" /* LanManager offset */
454                     "%c%c" /* 2 zeroes */
455
456                     "%c%c" /* NT-response length */
457                     "%c%c" /* NT-response allocated space */
458                     "%c%c" /* NT-response offset */
459                     "%c%c" /* 2 zeroes */
460                     
461                     "%c%c"  /* domain length */
462                     "%c%c"  /* domain allocated space */
463                     "%c%c"  /* domain name offset */
464                     "%c%c"  /* 2 zeroes */
465                     
466                     "%c%c"  /* user length */
467                     "%c%c"  /* user allocated space */
468                     "%c%c"  /* user offset */
469                     "%c%c"  /* 2 zeroes */
470                     
471                     "%c%c"  /* host length */
472                     "%c%c"  /* host allocated space */
473                     "%c%c"  /* host offset */
474                     "%c%c%c%c%c%c"  /* 6 zeroes */
475                     
476                     "\xff\xff"  /* message length */
477                     "%c%c"  /* 2 zeroes */
478                     
479                     "\x01\x82" /* flags */
480                     "%c%c"  /* 2 zeroes */
481
482                     /* domain string */
483                     /* user string */
484                     /* host string */
485                     /* LanManager response */
486                     /* NT response */
487                     ,
488                     0, /* zero termination */
489                     0,0,0, /* type-3 long, the 24 upper bits */
490
491                     SHORTPAIR(0x18),  /* LanManager response length, twice */
492                     SHORTPAIR(0x18),
493                     SHORTPAIR(lmrespoff),
494                     0x0, 0x0,
495                     
496 #ifdef USE_NTRESPONSES
497                     SHORTPAIR(0x18),  /* NT-response length, twice */
498                     SHORTPAIR(0x18),
499 #else
500                     0x0, 0x0,
501                     0x0, 0x0,
502 #endif
503                     SHORTPAIR(ntrespoff),
504                     0x0, 0x0,
505
506                     SHORTPAIR(domlen),
507                     SHORTPAIR(domlen),
508                     SHORTPAIR(domoff),
509                     0x0, 0x0,
510
511                     SHORTPAIR(userlen),
512                     SHORTPAIR(userlen),
513                     SHORTPAIR(useroff),
514                     0x0, 0x0,
515                     
516                     SHORTPAIR(hostlen),
517                     SHORTPAIR(hostlen),
518                     SHORTPAIR(hostoff),
519                     0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
520              
521                     0x0, 0x0,
522
523                     0x0, 0x0);
524
525     /* size is now 64 */
526     size=64;
527     ntlmbuf[62]=ntlmbuf[63]=0;
528
529     memcpy(&ntlmbuf[size], domain, domlen);
530     size += domlen;
531
532     memcpy(&ntlmbuf[size], user, userlen);
533     size += userlen;
534
535     /* we append the binary hashes to the end of the blob */
536     if(size < ((int)sizeof(ntlmbuf) - 0x18)) {
537       memcpy(&ntlmbuf[size], lmresp, 0x18);
538       size += 0x18;
539     }
540
541 #ifdef USE_NTRESPONSES
542     if(size < ((int)sizeof(ntlmbuf) - 0x18)) {      
543       memcpy(&ntlmbuf[size], ntresp, 0x18);
544       size += 0x18;
545     }
546 #endif
547
548     ntlmbuf[56] = size & 0xff;
549     ntlmbuf[57] = size >> 8;
550
551     /* convert the binary blob into base64 */
552     size = Curl_base64_encode((char *)ntlmbuf, size, &base64);
553
554     if(size >0 ) {
555       Curl_safefree(*allocuserpwd);
556       *allocuserpwd = aprintf("%sAuthorization: NTLM %s\r\n",
557                               proxy?"Proxy-":"",
558                               base64);
559       free(base64);
560     }
561     else
562       return CURLE_OUT_OF_MEMORY; /* FIX TODO */
563
564     ntlm->state = NTLMSTATE_TYPE3; /* we sent a type-3 */
565     *ready = TRUE;
566
567     /* Switch to web authentication after proxy authentication is done */
568     if (proxy)
569       Curl_http_auth_stage(conn->data, 401);
570   }
571   break;
572
573   case NTLMSTATE_TYPE3:
574     /* connection is already authenticated,
575      * don't send a header in future requests */
576     if(*allocuserpwd) {
577       free(*allocuserpwd);
578       *allocuserpwd=NULL;
579     }
580     *ready = TRUE;
581     break;
582   }
583
584   return CURLE_OK;
585 }
586 #endif /* USE_SSLEAY */
587 #endif /* !CURL_DISABLE_HTTP */