2 Copyright (C) 2005 Free Software Foundation, Inc.
3 Donated by Daniel Stenberg.
5 This file is part of GNU Wget.
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.
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.
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.
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. */
35 http://davenport.sourceforge.net/ntlm.html
36 http://www.innovation.ch/java/ntlm.html
40 /* -- WIN32 approved -- */
49 #include <openssl/des.h>
50 #include <openssl/md4.h>
51 #include <openssl/ssl.h>
55 #include "http-ntlm.h"
57 #if OPENSSL_VERSION_NUMBER < 0x00907001L
58 #define DES_key_schedule des_key_schedule
59 #define DES_cblock des_cblock
60 #define DES_set_odd_parity des_set_odd_parity
61 #define DES_set_key des_set_key
62 #define DES_ecb_encrypt des_ecb_encrypt
64 /* This is how things were done in the old days */
66 #define DESKEYARG(x) x
69 #define DESKEYARG(x) *x
73 /* Define this to make the type-3 message include the NT response message */
74 #define USE_NTRESPONSES 1
76 /* Flag bits definitions available at on
77 http://davenport.sourceforge.net/ntlm.html */
79 #define NTLMFLAG_NEGOTIATE_UNICODE (1<<0)
80 #define NTLMFLAG_NEGOTIATE_OEM (1<<1)
81 #define NTLMFLAG_REQUEST_TARGET (1<<2)
83 #define NTLMFLAG_NEGOTIATE_SIGN (1<<4)
84 #define NTLMFLAG_NEGOTIATE_SEAL (1<<5)
85 #define NTLMFLAG_NEGOTIATE_DATAGRAM_STYLE (1<<6)
86 #define NTLMFLAG_NEGOTIATE_LM_KEY (1<<7)
87 #define NTLMFLAG_NEGOTIATE_NETWARE (1<<8)
88 #define NTLMFLAG_NEGOTIATE_NTLM_KEY (1<<9)
91 #define NTLMFLAG_NEGOTIATE_DOMAIN_SUPPLIED (1<<12)
92 #define NTLMFLAG_NEGOTIATE_WORKSTATION_SUPPLIED (1<<13)
93 #define NTLMFLAG_NEGOTIATE_LOCAL_CALL (1<<14)
94 #define NTLMFLAG_NEGOTIATE_ALWAYS_SIGN (1<<15)
95 #define NTLMFLAG_TARGET_TYPE_DOMAIN (1<<16)
96 #define NTLMFLAG_TARGET_TYPE_SERVER (1<<17)
97 #define NTLMFLAG_TARGET_TYPE_SHARE (1<<18)
98 #define NTLMFLAG_NEGOTIATE_NTLM2_KEY (1<<19)
99 #define NTLMFLAG_REQUEST_INIT_RESPONSE (1<<20)
100 #define NTLMFLAG_REQUEST_ACCEPT_RESPONSE (1<<21)
101 #define NTLMFLAG_REQUEST_NONNT_SESSION_KEY (1<<22)
102 #define NTLMFLAG_NEGOTIATE_TARGET_INFO (1<<23)
108 #define NTLMFLAG_NEGOTIATE_128 (1<<29)
109 #define NTLMFLAG_NEGOTIATE_KEY_EXCHANGE (1<<30)
110 #define NTLMFLAG_NEGOTIATE_56 (1<<31)
113 (*) = A "security buffer" is a triplet consisting of two shorts and one
116 1. a 'short' containing the length of the buffer in bytes
117 2. a 'short' containing the allocated space for the buffer in bytes
118 3. a 'long' containing the offset to the start of the buffer from the
119 beginning of the NTLM message, in bytes.
122 /* return 1 on success, 0 otherwise */
124 ntlm_input (struct ntlmdata *ntlm, const char *header)
126 if (0 != strncmp (header, "NTLM", 4))
130 while (*header && ISSPACE(*header))
135 /* We got a type-2 message here:
137 Index Description Content
138 0 NTLMSSP Signature Null-terminated ASCII "NTLMSSP"
140 8 NTLM Message Type long (0x02000000)
141 12 Target Name security buffer(*)
144 (32) Context (optional) 8 bytes (two consecutive longs)
145 (40) Target Information (optional) security buffer(*)
146 32 (48) start of data block
149 char *buffer = (char *) alloca (strlen (header));
151 DEBUGP (("Received a type-2 NTLM message.\n"));
153 size = base64_decode (header, buffer);
155 return 0; /* malformed base64 from server */
157 ntlm->state = NTLMSTATE_TYPE2; /* we got a type-2 */
160 /* the nonce of interest is index [24 .. 31], 8 bytes */
161 memcpy (ntlm->nonce, &buffer[24], 8);
163 /* at index decimal 20, there's a 32bit NTLM flag field */
167 if (ntlm->state >= NTLMSTATE_TYPE1)
169 DEBUGP (("Unexpected empty NTLM message.\n"));
170 return 0; /* this is an error */
173 DEBUGP (("Empty NTLM message, starting transaction.\n"));
174 ntlm->state = NTLMSTATE_TYPE1; /* we should sent away a type-1 */
181 * Turns a 56 bit key into the 64 bit, odd parity key and sets the key. The
182 * key schedule ks is also set.
185 setup_des_key(unsigned char *key_56,
186 DES_key_schedule DESKEYARG(ks))
191 key[1] = ((key_56[0] << 7) & 0xFF) | (key_56[1] >> 1);
192 key[2] = ((key_56[1] << 6) & 0xFF) | (key_56[2] >> 2);
193 key[3] = ((key_56[2] << 5) & 0xFF) | (key_56[3] >> 3);
194 key[4] = ((key_56[3] << 4) & 0xFF) | (key_56[4] >> 4);
195 key[5] = ((key_56[4] << 3) & 0xFF) | (key_56[5] >> 5);
196 key[6] = ((key_56[5] << 2) & 0xFF) | (key_56[6] >> 6);
197 key[7] = (key_56[6] << 1) & 0xFF;
199 DES_set_odd_parity(&key);
200 DES_set_key(&key, ks);
204 * takes a 21 byte array and treats it as 3 56-bit DES keys. The
205 * 8 byte plaintext is encrypted with each key and the resulting 24
206 * bytes are stored in the results array.
209 calc_resp(unsigned char *keys, unsigned char *plaintext, unsigned char *results)
213 setup_des_key(keys, DESKEY(ks));
214 DES_ecb_encrypt((DES_cblock*) plaintext, (DES_cblock*) results,
215 DESKEY(ks), DES_ENCRYPT);
217 setup_des_key(keys+7, DESKEY(ks));
218 DES_ecb_encrypt((DES_cblock*) plaintext, (DES_cblock*) (results+8),
219 DESKEY(ks), DES_ENCRYPT);
221 setup_des_key(keys+14, DESKEY(ks));
222 DES_ecb_encrypt((DES_cblock*) plaintext, (DES_cblock*) (results+16),
223 DESKEY(ks), DES_ENCRYPT);
227 * Set up lanmanager and nt hashed passwords
230 mkhash(const char *password,
231 unsigned char *nonce, /* 8 bytes */
232 unsigned char *lmresp /* must fit 0x18 bytes */
233 #ifdef USE_NTRESPONSES
234 , unsigned char *ntresp /* must fit 0x18 bytes */
238 unsigned char lmbuffer[21];
239 #ifdef USE_NTRESPONSES
240 unsigned char ntbuffer[21];
243 static const unsigned char magic[] = {
244 0x4B, 0x47, 0x53, 0x21, 0x40, 0x23, 0x24, 0x25
247 int len = strlen(password);
249 /* make it fit at least 14 bytes */
250 pw = (unsigned char *) alloca (len < 7 ? 14 : len * 2);
255 for (i=0; i<len; i++)
256 pw[i] = TOUPPER (password[i]);
262 /* create LanManager hashed password */
265 setup_des_key(pw, DESKEY(ks));
266 DES_ecb_encrypt((DES_cblock *)magic, (DES_cblock *)lmbuffer,
267 DESKEY(ks), DES_ENCRYPT);
269 setup_des_key(pw+7, DESKEY(ks));
270 DES_ecb_encrypt((DES_cblock *)magic, (DES_cblock *)(lmbuffer+8),
271 DESKEY(ks), DES_ENCRYPT);
273 memset(lmbuffer+16, 0, 5);
275 /* create LM responses */
276 calc_resp(lmbuffer, nonce, lmresp);
278 #ifdef USE_NTRESPONSES
280 /* create NT hashed password */
283 len = strlen(password);
285 for (i=0; i<len; i++) {
286 pw[2*i] = password[i];
291 MD4_Update(&MD4, pw, 2*len);
292 MD4_Final(ntbuffer, &MD4);
294 memset(ntbuffer+16, 0, 5);
297 calc_resp(ntbuffer, nonce, ntresp);
301 #define SHORTPAIR(x) ((x) & 0xff), ((x) >> 8)
302 #define LONGQUARTET(x) ((x) & 0xff), (((x) >> 8)&0xff), \
303 (((x) >>16)&0xff), ((x)>>24)
305 /* this is for creating ntlm header output */
307 ntlm_output (struct ntlmdata *ntlm, const char *user, const char *passwd,
310 const char *domain=""; /* empty */
311 const char *host=""; /* empty */
312 int domlen=strlen(domain);
313 int hostlen = strlen(host);
314 int hostoff; /* host name offset */
315 int domoff; /* domain name offset */
318 char ntlmbuf[256]; /* enough, unless the host/domain is very long */
320 /* point to the address of the pointer that holds the string to sent to the
321 server, which is for a plain host or for a HTTP proxy */
326 /* not set means empty */
333 switch(ntlm->state) {
334 case NTLMSTATE_TYPE1:
335 default: /* for the weird cases we (re)start here */
337 domoff = hostoff + hostlen;
339 DEBUGP (("Creating a type-1 NTLM message.\n"));
341 /* Create and send a type-1 message:
343 Index Description Content
344 0 NTLMSSP Signature Null-terminated ASCII "NTLMSSP"
346 8 NTLM Message Type long (0x01000000)
348 16 Supplied Domain security buffer(*)
349 24 Supplied Workstation security buffer(*)
350 32 start of data block
352 Format string (merged for pre-ANSI compilers):
354 "\x01%c%c%c" 32-bit type = 1
355 "%c%c%c%c" 32-bit NTLM flag field
357 "%c%c" domain allocated space
358 "%c%c" domain name offset
361 "%c%c" host allocated space
362 "%c%c" host name offset
368 snprintf(ntlmbuf, sizeof(ntlmbuf),
369 "NTLMSSP%c\x01%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%s%s",
370 0, /* trailing zero */
371 0,0,0, /* part of type-1 long */
374 NTLMFLAG_NEGOTIATE_OEM| /* 2 */
375 NTLMFLAG_NEGOTIATE_NTLM_KEY /* 200 */
388 /* initial packet length */
389 size = 32 + hostlen + domlen;
391 base64 = (char *) alloca (BASE64_LENGTH (size) + 1);
392 base64_encode (ntlmbuf, size, base64);
394 output = concat_strings ("NTLM ", base64, (char *) 0);
397 case NTLMSTATE_TYPE2:
398 /* We received the type-2 already, create a type-3 message:
400 Index Description Content
401 0 NTLMSSP Signature Null-terminated ASCII "NTLMSSP"
403 8 NTLM Message Type long (0x03000000)
404 12 LM/LMv2 Response security buffer(*)
405 20 NTLM/NTLMv2 Response security buffer(*)
406 28 Domain Name security buffer(*)
407 36 User Name security buffer(*)
408 44 Workstation Name security buffer(*)
409 (52) Session Key (optional) security buffer(*)
410 (60) Flags (optional) long
411 52 (64) start of data block
419 unsigned char lmresp[0x18]; /* fixed-size */
420 #ifdef USE_NTRESPONSES
421 unsigned char ntresp[0x18]; /* fixed-size */
426 DEBUGP (("Creating a type-3 NTLM message.\n"));
428 usr = strchr(user, '\\');
430 usr = strchr(user, '/');
434 domlen = usr - domain;
439 userlen = strlen(usr);
441 mkhash(passwd, &ntlm->nonce[0], lmresp
442 #ifdef USE_NTRESPONSES
447 domoff = 64; /* always */
448 useroff = domoff + domlen;
449 hostoff = useroff + userlen;
450 lmrespoff = hostoff + hostlen;
451 ntrespoff = lmrespoff + 0x18;
453 /* Create the big type-3 message binary blob:
455 "\x03%c%c%c" type-3, 32 bits
457 "%c%c%c%c" LanManager length + allocated space
458 "%c%c" LanManager offset
461 "%c%c" NT-response length
462 "%c%c" NT-response allocated space
463 "%c%c" NT-response offset
467 "%c%c" domain allocated space
468 "%c%c" domain name offset
472 "%c%c" user allocated space
477 "%c%c" host allocated space
479 "%c%c%c%c%c%c" 6 zeroes
481 "\xff\xff" message length
487 size = snprintf(ntlmbuf, sizeof(ntlmbuf),
488 "NTLMSSP%c\x03%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c\xff\xff%c%c\x01\x82%c%c",
489 0, /* zero termination */
490 0,0,0, /* type-3 long, the 24 upper bits */
492 SHORTPAIR(0x18), /* LanManager response length, twice */
494 SHORTPAIR(lmrespoff),
497 #ifdef USE_NTRESPONSES
498 SHORTPAIR(0x18), /* NT-response length, twice */
504 SHORTPAIR(ntrespoff),
520 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
528 ntlmbuf[62]=ntlmbuf[63]=0;
530 memcpy(&ntlmbuf[size], domain, domlen);
533 memcpy(&ntlmbuf[size], usr, userlen);
536 /* we append the binary hashes to the end of the blob */
537 if(size < ((int)sizeof(ntlmbuf) - 0x18)) {
538 memcpy(&ntlmbuf[size], lmresp, 0x18);
542 #ifdef USE_NTRESPONSES
543 if(size < ((int)sizeof(ntlmbuf) - 0x18)) {
544 memcpy(&ntlmbuf[size], ntresp, 0x18);
549 ntlmbuf[56] = size & 0xff;
550 ntlmbuf[57] = size >> 8;
552 /* convert the binary blob into base64 */
553 base64 = (char *) alloca (BASE64_LENGTH (size) + 1);
554 base64_encode (ntlmbuf, size, base64);
556 output = concat_strings ("NTLM ", base64, (char *) 0);
558 ntlm->state = NTLMSTATE_TYPE3; /* we sent a type-3 */
563 case NTLMSTATE_TYPE3:
564 /* connection is already authenticated,
565 * don't send a header in future requests */