]> sjero.net Git - wget/blob - src/http-ntlm.c
[svn] Remove K&R support.
[wget] / src / http-ntlm.c
1 /* NTLM code.
2    Copyright (C) 2005 Free Software Foundation, Inc.
3    Donated by Daniel Stenberg.
4
5 This file is part of GNU Wget.
6
7 GNU Wget is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10  (at your option) any later version.
11
12 GNU Wget is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with Wget; if not, write to the Free Software
19 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20
21 In addition, as a special exception, the Free Software Foundation
22 gives permission to link the code of its release of Wget with the
23 OpenSSL project's "OpenSSL" library (or with modified versions of it
24 that use the same license as the "OpenSSL" library), and distribute
25 the linked executables.  You must obey the GNU General Public License
26 in all respects for all of the code used other than "OpenSSL".  If you
27 modify this file, you may extend this exception to your version of the
28 file, but you are not obligated to do so.  If you do not wish to do
29 so, delete this exception statement from your version.  */
30
31 #include <config.h>
32
33 /* NTLM details:
34    
35    http://davenport.sourceforge.net/ntlm.html
36    http://www.innovation.ch/java/ntlm.html
37
38 */
39
40 /* -- WIN32 approved -- */
41 #include <stdio.h>
42 #include <string.h>
43 #include <stdlib.h>
44
45 #include <openssl/des.h>
46 #include <openssl/md4.h>
47
48 #include "wget.h"
49 #include "utils.h"
50 #include "http-ntlm.h"
51
52 #if OPENSSL_VERSION_NUMBER < 0x00907001L
53 #define DES_key_schedule des_key_schedule
54 #define DES_cblock des_cblock
55 #define DES_set_odd_parity des_set_odd_parity
56 #define DES_set_key des_set_key
57 #define DES_ecb_encrypt des_ecb_encrypt
58
59 /* This is how things were done in the old days */
60 #define DESKEY(x) x
61 #define DESKEYARG(x) x
62 #else
63 /* Modern version */
64 #define DESKEYARG(x) *x
65 #define DESKEY(x) &x
66 #endif
67
68 /* Define this to make the type-3 message include the NT response message */
69 #define USE_NTRESPONSES 1
70 \f
71 /* Flag bits definitions available at on
72    http://davenport.sourceforge.net/ntlm.html */
73
74 #define NTLMFLAG_NEGOTIATE_UNICODE               (1<<0)
75 #define NTLMFLAG_NEGOTIATE_OEM                   (1<<1)
76 #define NTLMFLAG_REQUEST_TARGET                  (1<<2)
77 /* unknown (1<<3) */
78 #define NTLMFLAG_NEGOTIATE_SIGN                  (1<<4)
79 #define NTLMFLAG_NEGOTIATE_SEAL                  (1<<5)
80 #define NTLMFLAG_NEGOTIATE_DATAGRAM_STYLE        (1<<6)
81 #define NTLMFLAG_NEGOTIATE_LM_KEY                (1<<7)
82 #define NTLMFLAG_NEGOTIATE_NETWARE               (1<<8)
83 #define NTLMFLAG_NEGOTIATE_NTLM_KEY              (1<<9)
84 /* unknown (1<<10) */
85 /* unknown (1<<11) */
86 #define NTLMFLAG_NEGOTIATE_DOMAIN_SUPPLIED       (1<<12)
87 #define NTLMFLAG_NEGOTIATE_WORKSTATION_SUPPLIED  (1<<13)
88 #define NTLMFLAG_NEGOTIATE_LOCAL_CALL            (1<<14)
89 #define NTLMFLAG_NEGOTIATE_ALWAYS_SIGN           (1<<15)
90 #define NTLMFLAG_TARGET_TYPE_DOMAIN              (1<<16)
91 #define NTLMFLAG_TARGET_TYPE_SERVER              (1<<17)
92 #define NTLMFLAG_TARGET_TYPE_SHARE               (1<<18)
93 #define NTLMFLAG_NEGOTIATE_NTLM2_KEY             (1<<19)
94 #define NTLMFLAG_REQUEST_INIT_RESPONSE           (1<<20)
95 #define NTLMFLAG_REQUEST_ACCEPT_RESPONSE         (1<<21)
96 #define NTLMFLAG_REQUEST_NONNT_SESSION_KEY       (1<<22)
97 #define NTLMFLAG_NEGOTIATE_TARGET_INFO           (1<<23)
98 /* unknown (1<24) */
99 /* unknown (1<25) */
100 /* unknown (1<26) */
101 /* unknown (1<27) */
102 /* unknown (1<28) */
103 #define NTLMFLAG_NEGOTIATE_128                   (1<<29)
104 #define NTLMFLAG_NEGOTIATE_KEY_EXCHANGE          (1<<30)
105 #define NTLMFLAG_NEGOTIATE_56                    (1<<31)
106 \f
107 /*
108   (*) = A "security buffer" is a triplet consisting of two shorts and one
109   long:
110
111   1. a 'short' containing the length of the buffer in bytes
112   2. a 'short' containing the allocated space for the buffer in bytes
113   3. a 'long' containing the offset to the start of the buffer from the
114      beginning of the NTLM message, in bytes.
115 */
116
117 /* return 1 on success, 0 otherwise */
118 int
119 ntlm_input (struct ntlmdata *ntlm, const char *header)
120 {
121   if (0 != strncmp (header, "NTLM", 4))
122     return 0;
123
124   header += 4;
125   while (*header && ISSPACE(*header))
126     header++;
127
128   if (*header)
129     {
130       /* We got a type-2 message here:
131
132          Index   Description         Content
133          0       NTLMSSP Signature   Null-terminated ASCII "NTLMSSP"
134                                      (0x4e544c4d53535000)
135          8       NTLM Message Type   long (0x02000000)
136          12      Target Name         security buffer(*)
137          20      Flags               long
138          24      Challenge           8 bytes
139          (32)    Context (optional)  8 bytes (two consecutive longs)
140          (40)    Target Information  (optional) security buffer(*)
141          32 (48) start of data block
142       */
143       int size;
144       char *buffer = (char *) alloca (strlen (header));
145
146       DEBUGP (("Received a type-2 NTLM message.\n"));
147
148       size = base64_decode (header, buffer);
149       if (size < 0)
150         return 0;               /* malformed base64 from server */
151
152       ntlm->state = NTLMSTATE_TYPE2; /* we got a type-2 */
153
154       if (size >= 48)
155         /* the nonce of interest is index [24 .. 31], 8 bytes */
156         memcpy (ntlm->nonce, &buffer[24], 8);
157
158       /* at index decimal 20, there's a 32bit NTLM flag field */
159     }
160   else
161     {
162       if (ntlm->state >= NTLMSTATE_TYPE1)
163         {
164           DEBUGP (("Unexpected empty NTLM message.\n"));
165           return 0; /* this is an error */
166         }
167
168       DEBUGP (("Empty NTLM message, starting transaction.\n"));
169       ntlm->state = NTLMSTATE_TYPE1; /* we should sent away a type-1 */
170     }
171
172   return 1;
173 }
174
175 /*
176  * Turns a 56 bit key into the 64 bit, odd parity key and sets the key.  The
177  * key schedule ks is also set.
178  */
179 static void
180 setup_des_key(unsigned char *key_56,
181               DES_key_schedule DESKEYARG(ks))
182 {
183   DES_cblock key;
184
185   key[0] = key_56[0];
186   key[1] = ((key_56[0] << 7) & 0xFF) | (key_56[1] >> 1);
187   key[2] = ((key_56[1] << 6) & 0xFF) | (key_56[2] >> 2);
188   key[3] = ((key_56[2] << 5) & 0xFF) | (key_56[3] >> 3);
189   key[4] = ((key_56[3] << 4) & 0xFF) | (key_56[4] >> 4);
190   key[5] = ((key_56[4] << 3) & 0xFF) | (key_56[5] >> 5);
191   key[6] = ((key_56[5] << 2) & 0xFF) | (key_56[6] >> 6);
192   key[7] =  (key_56[6] << 1) & 0xFF;
193
194   DES_set_odd_parity(&key);
195   DES_set_key(&key, ks);
196 }
197
198  /*
199   * takes a 21 byte array and treats it as 3 56-bit DES keys. The
200   * 8 byte plaintext is encrypted with each key and the resulting 24
201   * bytes are stored in the results array.
202   */
203 static void
204 calc_resp(unsigned char *keys, unsigned char *plaintext, unsigned char *results)
205 {
206   DES_key_schedule ks;
207
208   setup_des_key(keys, DESKEY(ks));
209   DES_ecb_encrypt((DES_cblock*) plaintext, (DES_cblock*) results,
210                   DESKEY(ks), DES_ENCRYPT);
211
212   setup_des_key(keys+7, DESKEY(ks));
213   DES_ecb_encrypt((DES_cblock*) plaintext, (DES_cblock*) (results+8),
214                   DESKEY(ks), DES_ENCRYPT);
215
216   setup_des_key(keys+14, DESKEY(ks));
217   DES_ecb_encrypt((DES_cblock*) plaintext, (DES_cblock*) (results+16),
218                   DESKEY(ks), DES_ENCRYPT);
219 }
220
221 /*
222  * Set up lanmanager and nt hashed passwords
223  */
224 static void
225 mkhash(const char *password,
226        unsigned char *nonce,    /* 8 bytes */
227        unsigned char *lmresp    /* must fit 0x18 bytes */
228 #ifdef USE_NTRESPONSES
229        , unsigned char *ntresp  /* must fit 0x18 bytes */
230 #endif
231   )
232 {
233   unsigned char lmbuffer[21];
234 #ifdef USE_NTRESPONSES
235   unsigned char ntbuffer[21];
236 #endif
237   unsigned char *pw;
238   static const unsigned char magic[] = {
239     0x4B, 0x47, 0x53, 0x21, 0x40, 0x23, 0x24, 0x25
240   };
241   int i;
242   int len = strlen(password);
243
244   /* make it fit at least 14 bytes */
245   pw = (unsigned char *) alloca (len < 7 ? 14 : len * 2);
246
247   if (len > 14)
248     len = 14;
249   
250   for (i=0; i<len; i++)
251     pw[i] = TOUPPER (password[i]);
252
253   for (; i<14; i++)
254     pw[i] = 0;
255
256   {
257     /* create LanManager hashed password */
258     DES_key_schedule ks;
259
260     setup_des_key(pw, DESKEY(ks));
261     DES_ecb_encrypt((DES_cblock *)magic, (DES_cblock *)lmbuffer,
262                     DESKEY(ks), DES_ENCRYPT);
263   
264     setup_des_key(pw+7, DESKEY(ks));
265     DES_ecb_encrypt((DES_cblock *)magic, (DES_cblock *)(lmbuffer+8),
266                     DESKEY(ks), DES_ENCRYPT);
267
268     memset(lmbuffer+16, 0, 5);
269   }
270   /* create LM responses */
271   calc_resp(lmbuffer, nonce, lmresp);
272
273 #ifdef USE_NTRESPONSES
274   {
275     /* create NT hashed password */
276     MD4_CTX MD4;
277
278     len = strlen(password);
279
280     for (i=0; i<len; i++) {
281       pw[2*i]   = password[i];
282       pw[2*i+1] = 0;
283     }
284
285     MD4_Init(&MD4);
286     MD4_Update(&MD4, pw, 2*len);
287     MD4_Final(ntbuffer, &MD4);
288
289     memset(ntbuffer+16, 0, 5);
290   }
291
292   calc_resp(ntbuffer, nonce, ntresp);
293 #endif
294 }
295
296 #define SHORTPAIR(x) ((x) & 0xff), ((x) >> 8)
297 #define LONGQUARTET(x) ((x) & 0xff), (((x) >> 8)&0xff), \
298   (((x) >>16)&0xff), ((x)>>24)
299
300 /* this is for creating ntlm header output */
301 char *
302 ntlm_output (struct ntlmdata *ntlm, const char *user, const char *passwd,
303              int *ready)
304 {
305   const char *domain=""; /* empty */
306   const char *host=""; /* empty */
307   int domlen=strlen(domain);
308   int hostlen = strlen(host);
309   int hostoff; /* host name offset */
310   int domoff;  /* domain name offset */
311   int size;
312   char *base64;
313   char ntlmbuf[256]; /* enough, unless the host/domain is very long */
314
315   /* point to the address of the pointer that holds the string to sent to the
316      server, which is for a plain host or for a HTTP proxy */
317   char *output;
318
319   *ready = 0;
320
321   /* not set means empty */
322   if(!user)
323     user="";
324
325   if(!passwd)
326     passwd="";
327   
328   switch(ntlm->state) {
329   case NTLMSTATE_TYPE1:
330   default: /* for the weird cases we (re)start here */
331     hostoff = 32;
332     domoff = hostoff + hostlen;
333
334     DEBUGP (("Creating a type-1 NTLM message.\n"));
335     
336     /* Create and send a type-1 message:
337
338     Index Description          Content
339     0     NTLMSSP Signature    Null-terminated ASCII "NTLMSSP"
340                                (0x4e544c4d53535000)
341     8     NTLM Message Type    long (0x01000000)
342     12    Flags                long
343     16    Supplied Domain      security buffer(*)
344     24    Supplied Workstation security buffer(*)
345     32    start of data block
346
347     */
348
349     snprintf (ntlmbuf, sizeof(ntlmbuf), "NTLMSSP%c"
350               "\x01%c%c%c" /* 32-bit type = 1 */
351               "%c%c%c%c"   /* 32-bit NTLM flag field */
352               "%c%c"  /* domain length */
353               "%c%c"  /* domain allocated space */
354               "%c%c"  /* domain name offset */
355               "%c%c"  /* 2 zeroes */
356               "%c%c"  /* host length */
357               "%c%c"  /* host allocated space */
358               "%c%c"  /* host name offset */
359               "%c%c"  /* 2 zeroes */
360               "%s"   /* host name */
361               "%s",  /* domain string */
362               0,     /* trailing zero */
363               0,0,0, /* part of type-1 long */
364
365               LONGQUARTET(
366                 NTLMFLAG_NEGOTIATE_OEM|      /*   2 */
367                 NTLMFLAG_NEGOTIATE_NTLM_KEY  /* 200 */
368                 /* equals 0x0202 */
369                 ),
370               SHORTPAIR(domlen),
371               SHORTPAIR(domlen),
372               SHORTPAIR(domoff),
373               0,0,
374               SHORTPAIR(hostlen),
375               SHORTPAIR(hostlen),
376               SHORTPAIR(hostoff),
377               0,0,
378               host, domain);
379
380     /* initial packet length */
381     size = 32 + hostlen + domlen;
382
383     base64 = (char *) alloca (BASE64_LENGTH (size) + 1);
384     base64_encode (ntlmbuf, size, base64);
385
386     output = concat_strings ("NTLM ", base64, (char *) 0);
387     break;
388     
389   case NTLMSTATE_TYPE2:
390     /* We received the type-2 already, create a type-3 message:
391
392     Index   Description            Content
393     0       NTLMSSP Signature      Null-terminated ASCII "NTLMSSP"
394                                    (0x4e544c4d53535000)
395     8       NTLM Message Type      long (0x03000000)
396     12      LM/LMv2 Response       security buffer(*)
397     20      NTLM/NTLMv2 Response   security buffer(*)
398     28      Domain Name            security buffer(*)
399     36      User Name              security buffer(*)
400     44      Workstation Name       security buffer(*)
401     (52)    Session Key (optional) security buffer(*)
402     (60)    Flags (optional)       long
403     52 (64) start of data block
404
405     */
406   
407   {
408     int lmrespoff;
409     int ntrespoff;
410     int useroff;
411     unsigned char lmresp[0x18]; /* fixed-size */
412 #ifdef USE_NTRESPONSES
413     unsigned char ntresp[0x18]; /* fixed-size */
414 #endif
415     const char *usr;
416     int userlen;
417
418     DEBUGP (("Creating a type-3 NTLM message.\n"));
419
420     usr = strchr(user, '\\');
421     if(!usr)
422       usr = strchr(user, '/');
423
424     if (usr) {
425       domain = user;
426       domlen = usr - domain;
427       usr++;
428     }
429     else
430       usr = user;
431     userlen = strlen(usr);
432
433     mkhash(passwd, &ntlm->nonce[0], lmresp
434 #ifdef USE_NTRESPONSES
435            , ntresp
436 #endif
437       );
438
439     domoff = 64; /* always */
440     useroff = domoff + domlen;
441     hostoff = useroff + userlen;
442     lmrespoff = hostoff + hostlen;
443     ntrespoff = lmrespoff + 0x18;
444
445     /* Create the big type-3 message binary blob */
446
447     size = snprintf (ntlmbuf, sizeof(ntlmbuf),
448                      "NTLMSSP%c"
449                      "\x03%c%c%c" /* type-3, 32 bits */
450
451                      "%c%c%c%c" /* LanManager length + allocated space */
452                      "%c%c" /* LanManager offset */
453                      "%c%c" /* 2 zeroes */
454
455                      "%c%c" /* NT-response length */
456                      "%c%c" /* NT-response allocated space */
457                      "%c%c" /* NT-response offset */
458                      "%c%c" /* 2 zeroes */
459
460                      "%c%c"  /* domain length */
461                      "%c%c"  /* domain allocated space */
462                      "%c%c"  /* domain name offset */
463                      "%c%c"  /* 2 zeroes */
464                     
465                      "%c%c"  /* user length */
466                      "%c%c"  /* user allocated space */
467                      "%c%c"  /* user offset */
468                      "%c%c"  /* 2 zeroes */
469                     
470                      "%c%c"  /* host length */
471                      "%c%c"  /* host allocated space */
472                      "%c%c"  /* host offset */
473                      "%c%c%c%c%c%c"  /* 6 zeroes */
474                     
475                      "\xff\xff"  /* message length */
476                      "%c%c"  /* 2 zeroes */
477                     
478                      "\x01\x82" /* flags */
479                      "%c%c"  /* 2 zeroes */
480
481                      /* domain string */
482                      /* user string */
483                      /* host string */
484                      /* LanManager response */
485                      /* NT response */
486                      ,
487                      0, /* zero termination */
488                      0,0,0, /* type-3 long, the 24 upper bits */
489
490                      SHORTPAIR(0x18),  /* LanManager response length, twice */
491                      SHORTPAIR(0x18),
492                      SHORTPAIR(lmrespoff),
493                      0x0, 0x0,
494
495 #ifdef USE_NTRESPONSES
496                      SHORTPAIR(0x18),  /* NT-response length, twice */
497                      SHORTPAIR(0x18),
498 #else
499                      0x0, 0x0,
500                      0x0, 0x0,
501 #endif
502                      SHORTPAIR(ntrespoff),
503                      0x0, 0x0,
504
505                      SHORTPAIR(domlen),
506                      SHORTPAIR(domlen),
507                      SHORTPAIR(domoff),
508                      0x0, 0x0,
509
510                      SHORTPAIR(userlen),
511                      SHORTPAIR(userlen),
512                      SHORTPAIR(useroff),
513                      0x0, 0x0,
514
515                      SHORTPAIR(hostlen),
516                      SHORTPAIR(hostlen),
517                      SHORTPAIR(hostoff),
518                      0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
519
520                      0x0, 0x0,
521
522                      0x0, 0x0);
523
524     /* size is now 64 */
525     size=64;
526     ntlmbuf[62]=ntlmbuf[63]=0;
527
528     memcpy(&ntlmbuf[size], domain, domlen);
529     size += domlen;
530
531     memcpy(&ntlmbuf[size], usr, userlen);
532     size += userlen;
533
534     /* we append the binary hashes to the end of the blob */
535     if(size < ((int)sizeof(ntlmbuf) - 0x18)) {
536       memcpy(&ntlmbuf[size], lmresp, 0x18);
537       size += 0x18;
538     }
539
540 #ifdef USE_NTRESPONSES
541     if(size < ((int)sizeof(ntlmbuf) - 0x18)) {      
542       memcpy(&ntlmbuf[size], ntresp, 0x18);
543       size += 0x18;
544     }
545 #endif
546
547     ntlmbuf[56] = size & 0xff;
548     ntlmbuf[57] = size >> 8;
549
550     /* convert the binary blob into base64 */
551     base64 = (char *) alloca (BASE64_LENGTH (size) + 1);
552     base64_encode (ntlmbuf, size, base64);
553
554     output = concat_strings ("NTLM ", base64, (char *) 0);
555
556     ntlm->state = NTLMSTATE_TYPE3; /* we sent a type-3 */
557     *ready = 1;
558   }
559   break;
560
561   case NTLMSTATE_TYPE3:
562     /* connection is already authenticated,
563      * don't send a header in future requests */
564     *ready = 1;
565     output = NULL;
566     break;
567   }
568
569   return output;
570 }