]> sjero.net Git - wget/commitdiff
[svn] Added NTLM support.
authorhniksic <devnull@localhost>
Wed, 6 Apr 2005 20:42:22 +0000 (13:42 -0700)
committerhniksic <devnull@localhost>
Wed, 6 Apr 2005 20:42:22 +0000 (13:42 -0700)
14 files changed:
AUTHORS
ChangeLog
Makefile.in
configure.in
src/ChangeLog
src/Makefile.in
src/ftp-basic.c
src/ftp.h
src/http-ntlm.c [new file with mode: 0644]
src/http-ntlm.h [new file with mode: 0644]
src/http.c
src/mswindows.c
src/utils.c
src/utils.h

diff --git a/AUTHORS b/AUTHORS
index 5b4be05bd09b6af4a46401571bcf6dbae69e05c4..a916111a800393487b8dbdfc600b3e4ad1d73a37 100644 (file)
--- 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.
index 5ca6ea74569addcf16d1fc8a59ecdbe5754292af..ebb947539541658141a8f1fecf050c31ee9a5928 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,11 @@
+2005-04-06  Hrvoje Niksic  <hniksic@xemacs.org>
+
+       * 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  <hniksic@xemacs.org>
 
        * po/POTFILES.in: Removed headers.c and rbuf.c.
index ef584e5e9ec2eb23a39b0431602e48903aa1ed99..3d1a69fad64b123c4088f7cfeade429003ca96c5 100644 (file)
@@ -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
index 03dd4342c12614ace4a0e237c0f408119924cbac..11f969eb5a2c3be363f42117d401b0ea3e246bb1 100644 (file)
@@ -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
index 36b8928cd87b9abd328c4c46685be8e5a4a8aa18..67694db14aa9520ba2c54a539398e4361832413f 100644 (file)
@@ -1,3 +1,24 @@
+2005-04-06  Hrvoje Niksic  <hniksic@xemacs.org>
+
+       * 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  <hniksic@xemacs.org>
+
+       * 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  <mauro@ferrara.linux.it>
 
        * Makefile.in: removed string_t.c from list of source files.
index da7bab417cc51df347ebffd78a4d2eebf81ba26e..e0acdc809703c69d5a683256e874cf1a12ad58bc 100644 (file)
@@ -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 
 
index d3714a03891c4ea6fcba8128717791c29ed4eb84..592dd59adac304cee0993ba35f66c8fbac90f51c 100644 (file)
@@ -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);
index 85ed5730cf8a3f7fb7eb06a911dd0b77d52231d4..0c8116fe4474970718aacfc95c5d225d7c7abdcc 100644 (file)
--- 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 (file)
index 0000000..ce7dd98
--- /dev/null
@@ -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 <config.h>
+
+/* NTLM details:
+   
+   http://davenport.sourceforge.net/ntlm.html
+   http://www.innovation.ch/java/ntlm.html
+
+*/
+
+/* -- WIN32 approved -- */
+#include <stdio.h>
+#ifdef HAVE_STRING_H
+# include <string.h>
+#else
+# include <strings.h>
+#endif
+#include <stdlib.h>
+
+#include <openssl/des.h>
+#include <openssl/md4.h>
+#include <openssl/ssl.h>
+
+#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
+\f
+/* 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)
+\f
+/*
+  (*) = 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<len; i++)
+    pw[i] = TOUPPER (password[i]);
+
+  for (; i<14; i++)
+    pw[i] = 0;
+
+  {
+    /* create LanManager hashed password */
+    DES_key_schedule ks;
+
+    setup_des_key(pw, DESKEY(ks));
+    DES_ecb_encrypt((DES_cblock *)magic, (DES_cblock *)lmbuffer,
+                    DESKEY(ks), DES_ENCRYPT);
+  
+    setup_des_key(pw+7, DESKEY(ks));
+    DES_ecb_encrypt((DES_cblock *)magic, (DES_cblock *)(lmbuffer+8),
+                    DESKEY(ks), DES_ENCRYPT);
+
+    memset(lmbuffer+16, 0, 5);
+  }
+  /* create LM responses */
+  calc_resp(lmbuffer, nonce, lmresp);
+
+#ifdef USE_NTRESPONSES
+  {
+    /* create NT hashed password */
+    MD4_CTX MD4;
+
+    len = strlen(password);
+
+    for (i=0; i<len; i++) {
+      pw[2*i]   = password[i];
+      pw[2*i+1] = 0;
+    }
+
+    MD4_Init(&MD4);
+    MD4_Update(&MD4, pw, 2*len);
+    MD4_Final(ntbuffer, &MD4);
+
+    memset(ntbuffer+16, 0, 5);
+  }
+
+  calc_resp(ntbuffer, nonce, ntresp);
+#endif
+}
+
+#define SHORTPAIR(x) ((x) & 0xff), ((x) >> 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 (file)
index 0000000..ec6ddc3
--- /dev/null
@@ -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
index 47355c91299ed381a04581b5cc1a9b05491c2d6b..c509b4aea1fcace5e612339dc917d6f870ddf993 100644 (file)
@@ -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;
 }
 \f
index a16b4b81fbc217cf05b13142316694b312e9eb8a..0d083e2982b8bf1c2033dc73850f00fb236edca3 100644 (file)
@@ -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,
index 2894d8f30a69b62253b3a5a83358e3f3d6322070..2e45c4d5812edb4966cd83aa9ae98e0c88d6a53c 100644 (file)
@@ -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
index f93d2ae95b04a11f1e075f02b5fa5805c1a06008..119cd281e104b8101dad5671dbd8b1da722c2062 100644 (file)
@@ -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 */