diff --git a/AUTHORS b/AUTHORS index 5b4be05b..a916111a 100644 --- a/AUTHORS +++ b/AUTHORS @@ -34,3 +34,6 @@ Mauro Tortonesi. Improved IPv6 support, adding support for dual family systems. Refactored and enhanced FTP IPv6 code. Nicolas Schodet. Contributed to cookie code and documentation. + +Daniel Stenberg. NTLM authentication in http-ntlm.c and http-ntlm.h +originally written curl donated for use in GNU Wget. diff --git a/ChangeLog b/ChangeLog index 5ca6ea74..ebb94753 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,11 @@ +2005-04-06 Hrvoje Niksic + + * configure.in: Allow the user to disable NTLM authorization. + Make sure NTLM is disabled if OpenSSL is unavailable. If NTLM is + *explicitly* requested and OpenSSL is unavailable, abort. + + * configure.in: Renamed USE_* to ENABLE_*. + 2005-03-23 Hrvoje Niksic * po/POTFILES.in: Removed headers.c and rbuf.c. diff --git a/Makefile.in b/Makefile.in index ef584e5e..3d1a69fa 100644 --- a/Makefile.in +++ b/Makefile.in @@ -182,7 +182,7 @@ $(srcdir)/configure: configure.in aclocal.m4 # autoheader might not change config.h.in, so touch a stamp file. $(srcdir)/src/config.h.in: stamp-h.in $(srcdir)/stamp-h.in: configure.in aclocal.m4 - cd $(srcdir) && autoheader + -cd $(srcdir) && autoheader echo timestamp > $(srcdir)/stamp-h.in src/config.h: stamp-h diff --git a/configure.in b/configure.in index 03dd4342..11f969eb 100644 --- a/configure.in +++ b/configure.in @@ -58,16 +58,20 @@ AC_ARG_WITH(ssl, AC_ARG_ENABLE(opie, [ --disable-opie disable support for opie or s/key FTP login], -USE_OPIE=$enableval, USE_OPIE=yes) -test x"${USE_OPIE}" = xyes && AC_DEFINE([USE_OPIE], 1, +ENABLE_OPIE=$enableval, ENABLE_OPIE=yes) +test x"${ENABLE_OPIE}" = xyes && AC_DEFINE([ENABLE_OPIE], 1, [Define if you want the Opie support for FTP compiled in.]) AC_ARG_ENABLE(digest, [ --disable-digest disable support for HTTP digest authorization], -USE_DIGEST=$enableval, USE_DIGEST=yes) -test x"${USE_DIGEST}" = xyes && AC_DEFINE([USE_DIGEST], 1, +ENABLE_DIGEST=$enableval, ENABLE_DIGEST=yes) +test x"${ENABLE_DIGEST}" = xyes && AC_DEFINE([ENABLE_DIGEST], 1, [Define if you want the HTTP Digest Authorization compiled in.]) +AC_ARG_ENABLE(ntlm, +[ --disable-ntlm disable support for NTLM authorization], +[ENABLE_NTLM=$enableval], [ENABLE_NTLM=auto]) + AC_ARG_ENABLE(debug, [ --disable-debug disable support for debugging output], ENABLE_DEBUG=$enableval, ENABLE_DEBUG=yes) @@ -76,11 +80,11 @@ test x"${ENABLE_DEBUG}" = xyes && AC_DEFINE([ENABLE_DEBUG], 1, wget_need_md5=no -case "${USE_OPIE}${USE_DIGEST}" in +case "${ENABLE_OPIE}${ENABLE_DIGEST}" in *yes*) wget_need_md5=yes esac -if test x"$USE_OPIE" = xyes; then +if test x"$ENABLE_OPIE" = xyes; then OPIE_OBJ='ftp-opie$o' fi AC_SUBST(OPIE_OBJ) @@ -413,6 +417,26 @@ main(){return 0;} CPPFLAGS=$wget_save_CPPFLAGS fi +dnl Enable NTLM if requested and if SSL is available. +NTLM_OBJ='' +if test x"$ssl_success" = xyes +then + if test x"$ENABLE_NTLM" != xno + then + AC_DEFINE([ENABLE_NTLM], 1, + [Define if you want the NTLM authorization support compiled in.]) + NTLM_OBJ='http-ntlm$o' + fi +else + dnl If SSL is unavailable and the user explicitly requested NTLM, + dnl abort. + if test x"$ENABLE_NTLM" = xyes + then + AC_MSG_ERROR([NTLM authorization requested and OpenSSL not found; aborting]) + fi +fi +AC_SUBST(NTLM_OBJ) + dnl dnl Find an md5 implementation. dnl diff --git a/src/ChangeLog b/src/ChangeLog index 36b8928c..67694db1 100644 --- a/src/ChangeLog +++ b/src/ChangeLog @@ -1,3 +1,24 @@ +2005-04-06 Hrvoje Niksic + + * http.c (pconn): Include NTLM data, which is per-connection. + (known_authentication_scheme_p): Recognize NTLM authorization. + (create_authorization_line): Call ntlm_input and ntlm_output. + + * http-ntlm.c: New file, donated by Daniel Stenberg and originally + written for curl, heavily modified for Wget. + + * utils.c (base64_encode): Relocated from http.c, since it is now + used by http-ntlm.c, and will possibly be used elsewhere. + (base64_decode): New function, originally based on code from GNU + recode. + +2005-04-02 Hrvoje Niksic + + * ftp.c (ftp_loop): Ditto. + + * ftp-basic.c (ftp_pasv): Use the xzero shorthand for memset(0). + (ftp_lpsv): Ditto. + 2005-04-05 Mauro Tortonesi * Makefile.in: removed string_t.c from list of source files. diff --git a/src/Makefile.in b/src/Makefile.in index da7bab41..e0acdc80 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -69,13 +69,14 @@ ETAGS = etags ALLOCA = @ALLOCA@ MD5_OBJ = @MD5_OBJ@ OPIE_OBJ = @OPIE_OBJ@ +NTLM_OBJ = @NTLM_OBJ@ SSL_OBJ = @SSL_OBJ@ GETOPT_OBJ = @GETOPT_OBJ@ OBJ = $(ALLOCA) cmpt$o connect$o convert$o cookies$o \ ftp$o ftp-basic$o ftp-ls$o $(OPIE_OBJ) $(GETOPT_OBJ) hash$o \ - host$o html-parse$o html-url$o http$o init$o \ - log$o main$o $(MD5_OBJ) netrc$o progress$o recur$o \ + host$o html-parse$o html-url$o http$o $(NTLM_OBJ) init$o \ + log$o main$o $(MD5_OBJ) netrc$o progress$o recur$o \ res$o retr$o safe-ctype$o snprintf$o $(SSL_OBJ) url$o \ utils$o version$o xmalloc$o diff --git a/src/ftp-basic.c b/src/ftp-basic.c index d3714a03..592dd59a 100644 --- a/src/ftp-basic.c +++ b/src/ftp-basic.c @@ -158,7 +158,7 @@ ftp_login (int csock, const char *acc, const char *pass) xfree (respline); return FTPLOGREFUSED; } -#ifdef USE_OPIE +#ifdef ENABLE_OPIE { static const char *skey_head[] = { "331 s/key ", @@ -195,7 +195,7 @@ ftp_login (int csock, const char *acc, const char *pass) pass = skey_response (skey_sequence, seed, pass); } } -#endif /* USE_OPIE */ +#endif /* ENABLE_OPIE */ xfree (respline); /* Send PASS password. */ request = ftp_request ("PASS", pass); diff --git a/src/ftp.h b/src/ftp.h index 85ed5730..0c8116fe 100644 --- a/src/ftp.h +++ b/src/ftp.h @@ -62,7 +62,7 @@ uerr_t ftp_syst PARAMS ((int, enum stype *)); uerr_t ftp_pwd PARAMS ((int, char **)); uerr_t ftp_size PARAMS ((int, const char *, wgint *)); -#ifdef USE_OPIE +#ifdef ENABLE_OPIE const char *skey_response PARAMS ((int, const char *, const char *)); #endif diff --git a/src/http-ntlm.c b/src/http-ntlm.c new file mode 100644 index 00000000..ce7dd984 --- /dev/null +++ b/src/http-ntlm.c @@ -0,0 +1,561 @@ +/* NTLM code. + Copyright (C) 2005 Free Software Foundation, Inc. + Donated 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 2 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, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +In addition, as a special exception, the Free Software Foundation +gives permission to link the code of its release of Wget with the +OpenSSL project's "OpenSSL" library (or with modified versions of it +that use the same license as the "OpenSSL" library), and distribute +the linked executables. You must obey the GNU General Public License +in all respects for all of the code used other than "OpenSSL". If you +modify this file, you may extend this exception to your version of the +file, but you are not obligated to do so. If you do not wish to do +so, delete this exception statement from your version. */ + +#include + +/* NTLM details: + + http://davenport.sourceforge.net/ntlm.html + http://www.innovation.ch/java/ntlm.html + +*/ + +/* -- WIN32 approved -- */ +#include +#ifdef HAVE_STRING_H +# include +#else +# 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 */ +#undef USE_NTRESPONSES + +/* Flag bits definitions available at on + http://davenport.sourceforge.net/ntlm.html */ + +#define NTLMFLAG_NEGOTIATE_UNICODE (1<<0) +#define NTLMFLAG_NEGOTIATE_OEM (1<<1) +#define NTLMFLAG_REQUEST_TARGET (1<<2) +/* unknown (1<<3) */ +#define NTLMFLAG_NEGOTIATE_SIGN (1<<4) +#define NTLMFLAG_NEGOTIATE_SEAL (1<<5) +#define NTLMFLAG_NEGOTIATE_DATAGRAM_STYLE (1<<6) +#define NTLMFLAG_NEGOTIATE_LM_KEY (1<<7) +#define NTLMFLAG_NEGOTIATE_NETWARE (1<<8) +#define NTLMFLAG_NEGOTIATE_NTLM_KEY (1<<9) +/* unknown (1<<10) */ +/* unknown (1<<11) */ +#define NTLMFLAG_NEGOTIATE_DOMAIN_SUPPLIED (1<<12) +#define NTLMFLAG_NEGOTIATE_WORKSTATION_SUPPLIED (1<<13) +#define NTLMFLAG_NEGOTIATE_LOCAL_CALL (1<<14) +#define NTLMFLAG_NEGOTIATE_ALWAYS_SIGN (1<<15) +#define NTLMFLAG_TARGET_TYPE_DOMAIN (1<<16) +#define NTLMFLAG_TARGET_TYPE_SERVER (1<<17) +#define NTLMFLAG_TARGET_TYPE_SHARE (1<<18) +#define NTLMFLAG_NEGOTIATE_NTLM2_KEY (1<<19) +#define NTLMFLAG_REQUEST_INIT_RESPONSE (1<<20) +#define NTLMFLAG_REQUEST_ACCEPT_RESPONSE (1<<21) +#define NTLMFLAG_REQUEST_NONNT_SESSION_KEY (1<<22) +#define NTLMFLAG_NEGOTIATE_TARGET_INFO (1<<23) +/* unknown (1<24) */ +/* unknown (1<25) */ +/* unknown (1<26) */ +/* unknown (1<27) */ +/* unknown (1<28) */ +#define NTLMFLAG_NEGOTIATE_128 (1<<29) +#define NTLMFLAG_NEGOTIATE_KEY_EXCHANGE (1<<30) +#define NTLMFLAG_NEGOTIATE_56 (1<<31) + +/* + (*) = A "security buffer" is a triplet consisting of two shorts and one + long: + + 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. +*/ + +/* return 1 on success, 0 otherwise */ +int ntlm_input (struct ntlmdata *ntlm, const char *header) +{ + if (0 != strncmp (header, "NTLM", 4)) + return 0; + + header += 4; + while (*header && ISSPACE(*header)) + header++; + + if (*header) + { + /* 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; + unsigned char *buffer = (unsigned char *) alloca (strlen (header)); + + size = base64_decode (header, buffer); + if (size < 0) + return 0; /* malformed base64 from server */ + + ntlm->state = NTLMSTATE_TYPE2; /* we got a type-2 */ + + if (size >= 48) + /* the nonce of interest is index [24 .. 31], 8 bytes */ + memcpy (ntlm->nonce, &buffer[24], 8); + + /* at index decimal 20, there's a 32bit NTLM flag field */ + } + else + { + if (ntlm->state >= NTLMSTATE_TYPE1) + return 0; /* this is an error */ + + ntlm->state = NTLMSTATE_TYPE1; /* we should sent away a type-1 */ + } + + return 1; +} + +/* + * 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(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(unsigned char *keys, + 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, + unsigned char *nonce, /* 8 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 + }; + int i; + int len = strlen(password); + + /* make it fit at least 14 bytes */ + pw = (unsigned char *) alloca (len < 7 ? 14 : len * 2); + + if (len > 14) + len = 14; + + for (i=0; i> 8) +#define LONGQUARTET(x) ((x) & 0xff), (((x) >> 8)&0xff), \ + (((x) >>16)&0xff), ((x)>>24) + +/* this is for creating ntlm header output */ +char *ntlm_output (struct ntlmdata *ntlm, const char *user, const char *passwd, + int *ready) +{ + const char *domain=""; /* empty */ + const char *host=""; /* empty */ + int domlen=strlen(domain); + int hostlen = strlen(host); + int hostoff; /* host name offset */ + int domoff; /* domain name offset */ + int size; + char *base64; + unsigned char ntlmbuf[256]; /* enough, unless the host/domain is very long */ + + /* 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 = 0; + + /* 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 */ + hostoff = 32; + domoff = hostoff + hostlen; + + /* Create and send a type-1 message: + + 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 *)ntlmbuf, sizeof(ntlmbuf), "NTLMSSP%c" + "\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 allocated space */ + "%c%c" /* host name offset */ + "%c%c" /* 2 zeroes */ + "%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), + 0,0, + SHORTPAIR(hostlen), + SHORTPAIR(hostlen), + SHORTPAIR(hostoff), + 0,0, + host, domain); + + /* initial packet length */ + size = 32 + hostlen + domlen; + + base64 = (char *) alloca (BASE64_LENGTH (size) + 1); + base64_encode (ntlmbuf, base64, size); + + output = concat_strings ("NTLM ", base64, (char *) 0); + 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 + const char *usr; + int userlen; + + usr = strchr(user, '\\'); + if(!usr) + usr = strchr(user, '/'); + + if (usr) { + domain = usr; + domlen = usr - domain; + usr++; + } + else + usr = user; + userlen = strlen(usr); + + mkhash(passwd, &ntlm->nonce[0], lmresp +#ifdef USE_NTRESPONSES + , ntresp +#endif + ); + + domoff = 64; /* always */ + useroff = domoff + domlen; + hostoff = useroff + userlen; + lmrespoff = hostoff + hostlen; + ntrespoff = lmrespoff + 0x18; + + /* Create the big type-3 message binary blob */ + size = snprintf((char *)ntlmbuf, sizeof(ntlmbuf), + "NTLMSSP%c" + "\x03%c%c%c" /* type-3, 32 bits */ + + "%c%c%c%c" /* LanManager length + allocated space */ + "%c%c" /* LanManager offset */ + "%c%c" /* 2 zeroes */ + + "%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 allocated space */ + "%c%c" /* domain name offset */ + "%c%c" /* 2 zeroes */ + + "%c%c" /* user length */ + "%c%c" /* user allocated space */ + "%c%c" /* user offset */ + "%c%c" /* 2 zeroes */ + + "%c%c" /* host length */ + "%c%c" /* host allocated space */ + "%c%c" /* host offset */ + "%c%c%c%c%c%c" /* 6 zeroes */ + + "\xff\xff" /* message length */ + "%c%c" /* 2 zeroes */ + + "\x01\x82" /* flags */ + "%c%c" /* 2 zeroes */ + + /* domain string */ + /* user string */ + /* host string */ + /* LanManager response */ + /* NT response */ + , + 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, + + SHORTPAIR(domlen), + SHORTPAIR(domlen), + SHORTPAIR(domoff), + 0x0, 0x0, + + SHORTPAIR(userlen), + SHORTPAIR(userlen), + SHORTPAIR(useroff), + 0x0, 0x0, + + SHORTPAIR(hostlen), + SHORTPAIR(hostlen), + SHORTPAIR(hostoff), + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + + 0x0, 0x0, + + 0x0, 0x0); + + /* size is now 64 */ + size=64; + ntlmbuf[62]=ntlmbuf[63]=0; + + memcpy(&ntlmbuf[size], domain, domlen); + size += domlen; + + memcpy(&ntlmbuf[size], usr, userlen); + size += userlen; + + /* we append the binary hashes to the end of the blob */ + if(size < ((int)sizeof(ntlmbuf) - 0x18)) { + memcpy(&ntlmbuf[size], lmresp, 0x18); + size += 0x18; + } + +#ifdef USE_NTRESPONSES + if(size < ((int)sizeof(ntlmbuf) - 0x18)) { + memcpy(&ntlmbuf[size], ntresp, 0x18); + size += 0x18; + } +#endif + + ntlmbuf[56] = size & 0xff; + ntlmbuf[57] = size >> 8; + + /* convert the binary blob into base64 */ + base64 = (char *) alloca (BASE64_LENGTH (size) + 1); + base64_encode (ntlmbuf, base64, size); + + output = concat_strings ("NTLM ", base64, (char *) 0); + + ntlm->state = NTLMSTATE_TYPE3; /* we sent a type-3 */ + *ready = 1; + } + break; + + case NTLMSTATE_TYPE3: + /* connection is already authenticated, + * don't send a header in future requests */ + *ready = 1; + output = NULL; + break; + } + + return output; +} diff --git a/src/http-ntlm.h b/src/http-ntlm.h new file mode 100644 index 00000000..ec6ddc3b --- /dev/null +++ b/src/http-ntlm.h @@ -0,0 +1,52 @@ +#ifndef __HTTP_NTLM_H +#define __HTTP_NTLM_H +/* Declarations for http_ntlm.c + Copyright (C) 1995, 1996, 1997, 2000 Free Software Foundation, Inc. + +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 2 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, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +In addition, as a special exception, the Free Software Foundation +gives permission to link the code of its release of Wget with the +OpenSSL project's "OpenSSL" library (or with modified versions of it +that use the same license as the "OpenSSL" library), and distribute +the linked executables. You must obey the GNU General Public License +in all respects for all of the code used other than "OpenSSL". If you +modify this file, you may extend this exception to your version of the +file, but you are not obligated to do so. If you do not wish to do +so, delete this exception statement from your version. */ + +typedef enum { + NTLMSTATE_NONE, + NTLMSTATE_TYPE1, + NTLMSTATE_TYPE2, + NTLMSTATE_TYPE3, + NTLMSTATE_LAST +} wgetntlm; + +/* Struct used for NTLM challenge-response authentication */ +struct ntlmdata { + wgetntlm state; + unsigned char nonce[8]; +}; + +/* this is for ntlm header input */ +int ntlm_input PARAMS ((struct ntlmdata *, const char *)); + +/* this is for creating ntlm header output */ +char *ntlm_output PARAMS ((struct ntlmdata *, + const char *, const char *, int *)); +#endif diff --git a/src/http.c b/src/http.c index 47355c91..c509b4ae 100644 --- a/src/http.c +++ b/src/http.c @@ -65,9 +65,12 @@ extern int errno; #include "netrc.h" #ifdef HAVE_SSL # include "gen_sslfunc.h" -#endif /* HAVE_SSL */ +#endif +#ifdef ENABLE_NTLM +# include "http-ntlm.h" +#endif #include "cookies.h" -#ifdef USE_DIGEST +#ifdef ENABLE_DIGEST # include "gen-md5.h" #endif #include "convert.h" @@ -824,6 +827,11 @@ static struct { /* Whether a ssl handshake has occoured on this connection. */ int ssl; + +#ifdef ENABLE_NTLM + /* NTLM data of the current connection. */ + struct ntlmdata ntlm; +#endif } pconn; /* Mark the persistent connection as invalid and free the resources it @@ -2574,47 +2582,6 @@ http_atotm (const char *time_string) consisting of answering to the server's challenge with the proper MD5 digests. */ -/* How many bytes it will take to store LEN bytes in base64. */ -#define BASE64_LENGTH(len) (4 * (((len) + 2) / 3)) - -/* Encode the string S of length LENGTH to base64 format and place it - to STORE. STORE will be 0-terminated, and must point to a writable - buffer of at least 1+BASE64_LENGTH(length) bytes. */ -static void -base64_encode (const char *s, char *store, int length) -{ - /* Conversion table. */ - static char tbl[64] = { - 'A','B','C','D','E','F','G','H', - 'I','J','K','L','M','N','O','P', - 'Q','R','S','T','U','V','W','X', - 'Y','Z','a','b','c','d','e','f', - 'g','h','i','j','k','l','m','n', - 'o','p','q','r','s','t','u','v', - 'w','x','y','z','0','1','2','3', - '4','5','6','7','8','9','+','/' - }; - int i; - unsigned char *p = (unsigned char *)store; - - /* Transform the 3x8 bits to 4x6 bits, as required by base64. */ - for (i = 0; i < length; i += 3) - { - *p++ = tbl[s[0] >> 2]; - *p++ = tbl[((s[0] & 3) << 4) + (s[1] >> 4)]; - *p++ = tbl[((s[1] & 0xf) << 2) + (s[2] >> 6)]; - *p++ = tbl[s[2] & 0x3f]; - s += 3; - } - /* Pad the result if necessary... */ - if (i == length + 1) - *(p - 1) = '='; - else if (i == length + 2) - *(p - 1) = *(p - 2) = '='; - /* ...and zero-terminate it. */ - *p = '\0'; -} - /* Create the authentication header contents for the `Basic' scheme. This is done by encoding the string `USER:PASS' in base64 and prepending `HEADER: Basic ' to it. */ @@ -2639,7 +2606,7 @@ basic_authentication_encode (const char *user, const char *passwd) ++(x); \ } while (0) -#ifdef USE_DIGEST +#ifdef ENABLE_DIGEST /* Parse HTTP `WWW-Authenticate:' header. AU points to the beginning of a field in such a header. If the field is the one specified by ATTR_NAME ("realm", "opaque", and "nonce" are used by the current @@ -2825,7 +2792,7 @@ username=\"%s\", realm=\"%s\", nonce=\"%s\", uri=\"%s\", response=\"%s\"", } return res; } -#endif /* USE_DIGEST */ +#endif /* ENABLE_DIGEST */ #define BEGINS_WITH(line, string_constant) \ @@ -2837,8 +2804,13 @@ static int known_authentication_scheme_p (const char *au) { return BEGINS_WITH (au, "Basic") +#ifdef ENABLE_DIGEST || BEGINS_WITH (au, "Digest") - || BEGINS_WITH (au, "NTLM"); +#endif +#ifdef ENABLE_NTLM + || BEGINS_WITH (au, "NTLM") +#endif + ; } #undef BEGINS_WITH @@ -2855,10 +2827,20 @@ create_authorization_line (const char *au, const char *user, { if (0 == strncasecmp (au, "Basic", 5)) return basic_authentication_encode (user, passwd); -#ifdef USE_DIGEST +#ifdef ENABLE_DIGEST if (0 == strncasecmp (au, "Digest", 6)) return digest_authentication_encode (au, user, passwd, method, path); -#endif /* USE_DIGEST */ +#endif +#ifdef ENABLE_NTLM + if (0 == strncasecmp (au, "NTLM", 4)) + { + int ok = ntlm_input (&pconn.ntlm, au); + if (!ok) + return NULL; + /* #### we shouldn't ignore the OK that ntlm_output returns. */ + return ntlm_output (&pconn.ntlm, user, passwd, &ok); + } +#endif return NULL; } diff --git a/src/mswindows.c b/src/mswindows.c index a16b4b81..0d083e29 100644 --- a/src/mswindows.c +++ b/src/mswindows.c @@ -357,7 +357,7 @@ fake_fork (void) /* Create the child process detached form the current console and in a suspended state. */ - memset (&si, 0, sizeof (si)); + xzero (si); si.cb = sizeof (si); rv = CreateProcess (exe, GetCommandLine (), NULL, NULL, TRUE, CREATE_SUSPENDED | DETACHED_PROCESS, diff --git a/src/utils.c b/src/utils.c index 2894d8f3..2e45c4d5 100644 --- a/src/utils.c +++ b/src/utils.c @@ -2170,3 +2170,143 @@ xsleep (double seconds) } #endif /* not WINDOWS */ + +/* Encode the string S of length LENGTH to base64 format and place it + to STORE. STORE will be 0-terminated, and must point to a writable + buffer of at least 1+BASE64_LENGTH(length) bytes. */ + +void +base64_encode (const char *s, char *store, int length) +{ + /* Conversion table. */ + static char tbl[64] = { + 'A','B','C','D','E','F','G','H', + 'I','J','K','L','M','N','O','P', + 'Q','R','S','T','U','V','W','X', + 'Y','Z','a','b','c','d','e','f', + 'g','h','i','j','k','l','m','n', + 'o','p','q','r','s','t','u','v', + 'w','x','y','z','0','1','2','3', + '4','5','6','7','8','9','+','/' + }; + int i; + unsigned char *p = (unsigned char *)store; + + /* Transform the 3x8 bits to 4x6 bits, as required by base64. */ + for (i = 0; i < length; i += 3) + { + *p++ = tbl[s[0] >> 2]; + *p++ = tbl[((s[0] & 3) << 4) + (s[1] >> 4)]; + *p++ = tbl[((s[1] & 0xf) << 2) + (s[2] >> 6)]; + *p++ = tbl[s[2] & 0x3f]; + s += 3; + } + /* Pad the result if necessary... */ + if (i == length + 1) + *(p - 1) = '='; + else if (i == length + 2) + *(p - 1) = *(p - 2) = '='; + /* ...and zero-terminate it. */ + *p = '\0'; +} + +#define IS_ASCII(c) (((c) & 0x80) == 0) +#define IS_BASE64(c) ((IS_ASCII (c) && base64_char_to_value[c] >= 0) || c == '=') + +/* Get next character from the string, except that non-base64 + characters are ignored, as mandated by rfc2045. */ +#define NEXT_BASE64_CHAR(c, p) do { \ + c = *p++; \ +} while (c != '\0' && !IS_BASE64 (c)) + +/* Decode data from BASE64 (assumed to be encoded as base64) into + memory pointed to by TO. TO should be large enough to accomodate + the decoded data, which is guaranteed to be less than + strlen(base64). + + Since TO is assumed to contain binary data, it is not + NUL-terminated. The function returns the length of the data + written to TO. -1 is returned in case of error caused by malformed + base64 input. */ + +int +base64_decode (const char *base64, char *to) +{ + /* Table of base64 values for first 128 characters. */ + static short base64_char_to_value[128] = + { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0- 9 */ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 10- 19 */ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 20- 29 */ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 30- 39 */ + -1, -1, -1, 62, -1, -1, -1, 63, 52, 53, /* 40- 49 */ + 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, /* 50- 59 */ + -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, /* 60- 69 */ + 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, /* 70- 79 */ + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, /* 80- 89 */ + 25, -1, -1, -1, -1, -1, -1, 26, 27, 28, /* 90- 99 */ + 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, /* 100-109 */ + 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, /* 110-119 */ + 49, 50, 51, -1, -1, -1, -1, -1 /* 120-127 */ + }; + + const char *p = base64; + char *q = to; + + while (1) + { + unsigned char c; + unsigned long value; + + /* Process first byte of a quadruplet. */ + NEXT_BASE64_CHAR (c, p); + if (!c) + break; + if (c == '=') + return -1; /* illegal '=' while decoding base64 */ + value = base64_char_to_value[c] << 18; + + /* Process scond byte of a quadruplet. */ + NEXT_BASE64_CHAR (c, p); + if (!c) + return -1; /* premature EOF while decoding base64 */ + if (c == '=') + return -1; /* illegal `=' while decoding base64 */ + value |= base64_char_to_value[c] << 12; + *q++ = value >> 16; + + /* Process third byte of a quadruplet. */ + NEXT_BASE64_CHAR (c, p); + if (!c) + return -1; /* premature EOF while decoding base64 */ + + if (c == '=') + { + NEXT_BASE64_CHAR (c, p); + if (!c) + return -1; /* premature EOF while dcoding base64 */ + if (c != '=') + return -1; /* padding `=' expected but not found */ + continue; + } + + value |= base64_char_to_value[c] << 6; + *q++ = 0xff & value >> 8; + + /* Process fourth byte of a quadruplet. */ + NEXT_BASE64_CHAR (c, p); + if (!c) + return -1; /* premature EOF while dcoding base64 */ + if (c == '=') + continue; + + value |= base64_char_to_value[c]; + *q++ = 0xff & value; + } + + return q - to; +} + +#undef IS_ASCII +#undef IS_BASE64 +#undef NEXT_BASE64_CHAR diff --git a/src/utils.h b/src/utils.h index f93d2ae9..119cd281 100644 --- a/src/utils.h +++ b/src/utils.h @@ -125,4 +125,10 @@ double random_float PARAMS ((void)); int run_with_timeout PARAMS ((double, void (*) (void *), void *)); void xsleep PARAMS ((double)); +/* How many bytes it will take to store LEN bytes in base64. */ +#define BASE64_LENGTH(len) (4 * (((len) + 2) / 3)) + +void base64_encode PARAMS ((const char *, char *, int)); +int base64_decode PARAMS ((const char *, char *)); + #endif /* UTILS_H */