// #define HARDCODE_RANDOM 1 /* NTLM code. Copyright (C) 2005, 2006, 2007, 2008 Free Software Foundation, Inc. Contributed by Daniel Stenberg. This file is part of GNU Wget. GNU Wget is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. GNU Wget is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Wget. If not, see . Additional permission under GNU GPL version 3 section 7 If you modify this program, or any covered work, by linking or combining it with the OpenSSL project's OpenSSL library (or a modified version of that library), containing parts covered by the terms of the OpenSSL or SSLeay licenses, the Free Software Foundation grants you additional permission to convey the resulting work. Corresponding Source for a non-source form of such a combination shall include the source code for the parts of OpenSSL used as well as that of the covered work. */ #include /* NTLM details: http://davenport.sourceforge.net/ntlm.html http://www.innovation.ch/java/ntlm.html */ #include #include #include #include #ifdef HAVE_UNISTD_H #include #endif #include #include #include #include #include "wget.h" #include "utils.h" #include "http-ntlm.h" #if OPENSSL_VERSION_NUMBER < 0x00907001L #define DES_key_schedule des_key_schedule #define DES_cblock des_cblock #define DES_set_odd_parity des_set_odd_parity #define DES_set_key des_set_key #define DES_ecb_encrypt des_ecb_encrypt /* This is how things were done in the old days */ #define DESKEY(x) x #define DESKEYARG(x) x #else /* Modern version */ #define DESKEYARG(x) *x #define DESKEY(x) &x #endif /* Define this to make the type-3 message include the NT response message */ #define USE_NTRESPONSES 1 #define SHORT_TO_LE2(x) ((x) & 0xff), ((x) >> 8) #define LONG_TO_LE4(x) ((x) & 0xff), (((x) >> 8)&0xff), (((x) >>16)&0xff), ((x)>>24) #define LE_TO_SHORT(x) ((x[1] & 0xff) << 8) | (x[0] & 0xff) #define LE_TO_LONG(x) ((x[3] & 0xff) << 24) | ((x[2] & 0xff) << 16) | ((x[1] & 0xff) << 8) | (x[0] & 0xff) #define NTLM_SIGNATURE "NTLMSSP" /* The following definitions allows the same code to compile in wget 1.11.4 and 1.12.x; * the code references to TOUPPER and ISSPACE can eventually be changed and then these * definitions can be removed. */ #ifndef TOUPPER #define TOUPPER(c) c_toupper (c) #define ISSPACE(c) c_isspace (c) #endif struct sb_list { size_t size; char *buffer; char *content; bool autofree; }; struct ntlm_message { size_t size; int type; int data_offset; int n_sb; int max_sb; char *p_signature; char *p_optional; char sb[0]; }; struct ntlm_initiate { char signature[8]; /* "NTLMSSP" */ char type[4]; /* 32-bit little-endian integer */ char flags[4]; /* 32-bit little-endian integer */ struct { char sb_domain[8]; /* security buffer */ char sb_workstation[8]; /* security buffer */ char os_version[8]; /* vendor-specific version information */ } optional; }; struct ntlm_challenge { char signature[8]; /* "NTLMSSP" */ char type[4]; /* 32-bit little-endian integer */ char sb_target[8]; /* security buffer */ char flags[4]; /* 32-bit little-endian integer */ char challenge[8]; struct { char context[8]; /* two 32-bit little-endian integers */ char sb_target_info[8]; /* security buffer */ char os_version[8]; /* vendor-specific version information */ } optional; }; struct ntlm_response { char signature[8]; /* "NTLMSSP" */ char type[4]; /* 32-bit little-endian integer */ char sb_lm_response[8]; /* security buffer */ char sb_ntlm_response[8]; /* security buffer */ char sb_target[8]; /* security buffer */ char sb_user[8]; /* security buffer */ char sb_workstation[8]; /* security buffer */ struct { char sb_session_key[8]; /* security buffer */ char flags[4]; /* 32-bit little-endian integer */ char os_version[8]; /* vendor-specific version information */ } optional; }; struct ntlm_message_initiate { struct ntlm_message msg; struct sb_list sb[2]; struct ntlm_initiate m; }; struct ntlm_message_challenge { struct ntlm_message msg; struct sb_list sb[2]; struct ntlm_challenge m; }; struct ntlm_message_response { struct ntlm_message msg; struct sb_list sb[6]; struct ntlm_response m; }; /* Routines to convert between ASCII (a) and UTF16 (w) strings. Assumes that buffers for the destination are allocated of sufficient length. */ int wstrlen(const unsigned char *s) { int x; for (x=0; s[x]; x+=2) ; return x/2; } void atow(unsigned char *d, const unsigned char *s) { int x; for (x=0; x <= strlen(s); x++) { d[x*2] = s[x]; d[x*2+1] = 0; } } void wtoa(unsigned char *d, const unsigned char *s) { int x; int w = wstrlen(s); for (x=0; x < w; x++) { d[x] = s[x*2]; if (!d[x]) break; } } void ntlm_message_init (void *message, const int type) { size_t size = 0; switch (type) { case 1: size = sizeof(struct ntlm_message_initiate); break; case 2: size = sizeof(struct ntlm_message_challenge); break; case 3: size = sizeof(struct ntlm_message_response); break; } memset(message, 0, size); struct ntlm_message *msg; msg = message; msg->size = size; msg->type = type; switch (type) { case 1: msg->max_sb = 2; msg->p_signature = ((struct ntlm_message_initiate *)msg)->m.signature; msg->p_optional = (char *)&((struct ntlm_message_initiate *)msg)->m.optional; break; case 2: msg->max_sb = 2; msg->p_signature = ((struct ntlm_message_challenge *)msg)->m.signature; msg->p_optional = (char *)&((struct ntlm_message_challenge *)msg)->m.optional; break; case 3: msg->max_sb = 6; msg->p_signature = ((struct ntlm_message_response *)msg)->m.signature; msg->p_optional = (char *)&((struct ntlm_message_response *)msg)->m.optional; break; } msg->data_offset = msg->p_optional - msg->p_signature; strcpy(msg->p_signature, NTLM_SIGNATURE); msg->p_signature[8] = type; } void ntlm_message_set_buffer_free (void *message, char *sb, char *content, const size_t size, const bool autofree) { struct ntlm_message *msg; msg = message; assert(msg->n_sb < msg->max_sb); assert((sb > (char *)msg) && ((char *)msg + msg->size > sb + 8)); struct sb_list *sb_list; sb_list = (struct sb_list *)&msg->sb; sb_list[msg->n_sb].size = size; sb_list[msg->n_sb].buffer = sb; sb_list[msg->n_sb].content = content; sb_list[msg->n_sb].autofree = autofree; msg->n_sb++; if (sb > msg->p_optional) { int off = sb - msg->p_signature + 8; if (off > msg->data_offset) msg->data_offset = off; } } void ntlm_message_set_buffer (void *message, char *sb, char *content, const size_t size) { ntlm_message_set_buffer_free (message, sb, content, size, false); } void ntlm_message_set_raw (void *message, char *target, const char *content, const size_t size) { struct ntlm_message *msg; msg = message; assert((target > msg->p_signature) && ((char *)msg + msg->size >= target + size)); memcpy(target, content, size); if (target > msg->p_optional) { int off = target - msg->p_signature + size; if (off > msg->data_offset) msg->data_offset = off; } } void ntlm_message_set_string (void *message, char *target, char *s) { ntlm_message_set_buffer (message, target, s, strlen(s)); } void ntlm_message_set_string_wide (void *message, char *target, const char *narrow) { int size = strlen(narrow) * 2; char *wide = xmalloc(size + 2); // include room for trailing nulls atow(wide, narrow); ntlm_message_set_buffer_free (message, target, wide, size, true); } void ntlm_message_set_flags (void *message, char *target, const int flags) { char value[4]; value[0] = 0xff & flags; value[1] = 0xff & (flags >> 8); value[2] = 0xff & (flags >> 16); value[3] = 0xff & (flags >> 24); ntlm_message_set_raw(message, target, value, 4); } size_t ntlm_message_size (const void *message) { const struct ntlm_message *msg; msg = message; struct sb_list *sb_list; sb_list = (struct sb_list *)&msg->sb; size_t size = msg->data_offset; int x; for (x = 0; x < msg->n_sb; x++) { size += sb_list[x].size; } return size; } void ntlm_message_emit (const void *message, char *buffer) { const struct ntlm_message *msg; msg = message; struct sb_list *sb_list; sb_list = (struct sb_list *)&msg->sb; // Make a pass over the security buffers and assign locations in the emitted message int x; size_t off = msg->data_offset; for (x = 0; x < msg->n_sb; x++) { if (sb_list[x].size > 0) { char buffer[9]; sprintf(buffer, "%c%c%c%c%c%c%c%c", SHORT_TO_LE2(sb_list[x].size), SHORT_TO_LE2(sb_list[x].size), LONG_TO_LE4(off)); memcpy(sb_list[x].buffer, buffer, 8); off += sb_list[x].size; } } // Copy the message memcpy(buffer, msg->p_signature, msg->data_offset); buffer += msg->data_offset; // ...and append the security buffers for (x = 0; x < msg->n_sb; x++) { if (sb_list[x].size > 0) { memcpy(buffer, sb_list[x].content, sb_list[x].size); if (sb_list[x].autofree) xfree(sb_list[x].content); buffer += sb_list[x].size; } } } #define DEBUGPB(args) do { IF_DEBUG { debug_logprintf_buffer args; } } while (0) static void debug_logprintf_buffer (const char *format, const char *buffer, size_t size) { if (opt.debug) { char *output = alloca(size * 2 + 1); int i; for (i = 0; i < size; i++) sprintf(&output[i*2], "%2.2x", (unsigned char)buffer[i]); debug_logprintf (format, output); } } /* * Turns a 56 bit key into the 64 bit, odd parity key and sets the key. The * key schedule ks is also set. */ static void setup_des_key(const unsigned char *key_56, DES_key_schedule DESKEYARG(ks)) { DES_cblock key; key[0] = key_56[0]; key[1] = ((key_56[0] << 7) & 0xFF) | (key_56[1] >> 1); key[2] = ((key_56[1] << 6) & 0xFF) | (key_56[2] >> 2); key[3] = ((key_56[2] << 5) & 0xFF) | (key_56[3] >> 3); key[4] = ((key_56[3] << 4) & 0xFF) | (key_56[4] >> 4); key[5] = ((key_56[4] << 3) & 0xFF) | (key_56[5] >> 5); key[6] = ((key_56[5] << 2) & 0xFF) | (key_56[6] >> 6); key[7] = (key_56[6] << 1) & 0xFF; DES_set_odd_parity(&key); DES_set_key(&key, ks); } /* * takes a 21 byte array and treats it as 3 56-bit DES keys. The * 8 byte plaintext is encrypted with each key and the resulting 24 * bytes are stored in the results array. */ static void calc_resp(const unsigned char *keys, const unsigned char *plaintext, unsigned char *results) { DES_key_schedule ks; setup_des_key(keys, DESKEY(ks)); DES_ecb_encrypt((DES_cblock*) plaintext, (DES_cblock*) results, DESKEY(ks), DES_ENCRYPT); setup_des_key(keys+7, DESKEY(ks)); DES_ecb_encrypt((DES_cblock*) plaintext, (DES_cblock*) (results+8), DESKEY(ks), DES_ENCRYPT); setup_des_key(keys+14, DESKEY(ks)); DES_ecb_encrypt((DES_cblock*) plaintext, (DES_cblock*) (results+16), DESKEY(ks), DES_ENCRYPT); } /* * Set up lanmanager and nt hashed passwords */ static void mkhash(const char *password, int flags, const unsigned char *nonce, /* 8 bytes */ unsigned char *lmresp, /* must fit 0x18 bytes */ unsigned char *ntresp /* must fit 0x18 bytes */ ) { static const unsigned char magic[] = { 0x4B, 0x47, 0x53, 0x21, 0x40, 0x23, 0x24, 0x25 }; memset(lmresp, 0, 24); memset(ntresp, 0, 24); /* The flags received in the type 2 response control the contents generated: if NTLMFLAG_NEGOTIATE_NTLM2_KEY: NTLM 2 session response else if NTLMFLAG_NEGOTIATE_NTLM_KEY: NTLM response else: LM response */ int type = 0; if (flags & NTLMFLAG_NEGOTIATE_NTLM2_KEY) type = 2; else if (flags & NTLMFLAG_NEGOTIATE_NTLM_KEY) type = 1; DEBUGP(("NTLM: calculate hash: %s\n", (type == 0 ? "LM response" : (type == 1 ? "NTLM response" : "NTLM 2 session response")))); if (type < 2) { // Both LM and NTLM response messages include the LM response unsigned char lmhash[21]; if (strlen(password) <= 14) { /* make the password fit at least 14 bytes */ int len = strlen(password); char *pw = (unsigned char *) alloca (len < 7 ? 14 : len * 2); int i; for (i=0; istate <= NTLMSTATE_TYPE1) { // ... and expecting it. DEBUGP (("NTLM: Empty message, starting transaction.\n")); ntlm->state = NTLMSTATE_TYPE1; /* we should respond by sending a type-1 message */ } else { // ... but not expecting it. DEBUGP (("NTLM: Unexpected empty message.\n")); return false; /* this is an error */ } } else { /* Received a non-null NTLM header; it should be a type-2 message */ int size; char *buffer = (char *) alloca (strlen (header)); size = base64_decode (header, buffer); if (size < 0) { DEBUGP (("NTLM: Received malformed base64 in header from server.\n")); return false; /* malformed base64 from server */ } if (strcmp (buffer, NTLM_SIGNATURE)) { DEBUGP (("NTLM: header does not have a valid signature.\n")); return false; } struct ntlm_challenge *ntlm_challenge = (struct ntlm_challenge *)buffer; int type = LE_TO_LONG(ntlm_challenge->type); if (type != 2) { DEBUGP (("NTLM Expecting type 2 message, but received %i\n", type)); return false; } if (size < (void *)(&ntlm_challenge->optional) - (void *)ntlm_challenge) { DEBUGP (("NTLM: message is too short (%i bytes, expecting %i at least bytes)\n", size, (void *)&ntlm_challenge->optional - (void *)ntlm_challenge)); return false; } memset(ntlm, 0, sizeof(struct ntlmdata)); ntlm->state = NTLMSTATE_TYPE2; /* we should respond by sending a type-3 message */ assert(sizeof(ntlm_challenge->challenge) <= sizeof(ntlm->nonce)); memcpy (ntlm->nonce, ntlm_challenge->challenge, sizeof(ntlm_challenge->challenge)); ntlm->flags = LE_TO_LONG(ntlm_challenge->flags); } return true; } /* this is for creating ntlm header output */ char * ntlm_output (struct ntlmdata *ntlm, const char *user, const char *passwd, bool *ready) { struct ntlm_message_initiate ntlm_initiate; struct ntlm_message_response ntlm_response; size_t size; char *base64; /* point to the address of the pointer that holds the string to sent to the server, which is for a plain host or for a HTTP proxy */ char *output; *ready = false; /* not set means empty */ if(!user) user=""; if(!passwd) passwd=""; switch(ntlm->state) { case NTLMSTATE_TYPE1: default: /* for the weird cases we (re)start here */ { DEBUGP (("NTLM: Creating a type 1 message.\n")); ntlm_message_init (&ntlm_initiate, 1); ntlm_message_set_flags(&ntlm_initiate, ntlm_initiate.m.flags, NTLMFLAG_NEGOTIATE_OEM | NTLMFLAG_NEGOTIATE_UNICODE | NTLMFLAG_NEGOTIATE_NTLM_KEY | NTLMFLAG_NEGOTIATE_NTLM2_KEY); size = ntlm_message_size(&ntlm_initiate); char *initiate = alloca(size); ntlm_message_emit(&ntlm_initiate, initiate); base64 = (char *) alloca (BASE64_LENGTH (size) + 1); base64_encode (initiate, size, base64); output = concat_strings ("NTLM ", base64, (char *) 0); break; } case NTLMSTATE_TYPE2: { unsigned char lmresp[0x18]; /* fixed-size */ unsigned char ntresp[0x18]; /* fixed-size */ ntlm_message_init (&ntlm_response, 3); mkhash(passwd, ntlm->flags, ntlm->nonce, lmresp, ntresp); ntlm_message_set_buffer (&ntlm_response, ntlm_response.m.sb_lm_response, lmresp, 24); ntlm_message_set_buffer (&ntlm_response, ntlm_response.m.sb_ntlm_response, ntresp, 24); // Add the user, domain, and workstation (that is, the current 'host' computer) // Look for DOMAIN\user, DOMAIN/user, or simply user char *domain = ""; char *u = alloca(strlen(user) + 1); strcpy(u, user); char *usr = strchr(u, '\\'); if(!usr) usr = strchr(u, '/'); if (usr) { int domlen = usr - u; domain = alloca(domlen + 1); strncpy(domain, u, domlen); domain[domlen] = '\x0'; usr++; } else usr = u; #define HOSTNAME_MAX 1024 char host[HOSTNAME_MAX]; if (gethostname(host, HOSTNAME_MAX)) host[0] = '\0'; else { /* If the workstation if configured with a full DNS name (i.e. * workstation.somewhere.net) gethostname() returns the fully qualified * name, which NTLM doesn't like. */ char *dot = strchr(host, '.'); if (dot) *dot = '\0'; } if (ntlm->flags & NTLMFLAG_NEGOTIATE_UNICODE) { ntlm_message_set_string_wide (&ntlm_response, ntlm_response.m.sb_user, usr); ntlm_message_set_string_wide (&ntlm_response, ntlm_response.m.sb_target, domain); if (*host) ntlm_message_set_string_wide (&ntlm_response, ntlm_response.m.sb_workstation, host); } else { ntlm_message_set_string (&ntlm_response, ntlm_response.m.sb_user, usr); ntlm_message_set_string (&ntlm_response, ntlm_response.m.sb_target, domain); if (*host) ntlm_message_set_string (&ntlm_response, ntlm_response.m.sb_workstation, host); } size = ntlm_message_size(&ntlm_response); char *response = alloca(size); ntlm_message_emit(&ntlm_response, response); base64 = (char *) alloca (BASE64_LENGTH (size) + 1); base64_encode (response, size, base64); output = concat_strings ("NTLM ", base64, (char *) 0); ntlm->state = NTLMSTATE_TYPE3; *ready = true; } break; case NTLMSTATE_TYPE3: /* connection is already authenticated, * don't send a header in future requests */ *ready = true; output = NULL; break; } return output; }