From 449299014aa55e5ac4c4481aaebcf30d282568eb Mon Sep 17 00:00:00 2001
From: Steve Holme <steve_holme@hotmail.com>
Date: Fri, 29 Jul 2011 12:11:02 +0100
Subject: [PATCH] Created a new ntlm module which is based on the NTLM code that was present in http_ntlm (v7.21.7).

Modified the smtp module to support AUTH NTLM.

Modified the order of the preferred authentication method to AUTH NTLM, AUTH CRAM-MD5, AUTH LOGIN then AUTH PLAIN. AUTH PLAIN should be the last as it is the most insecure.

Added support for the AUTH parameter in the MAIL FROM command. This is ONLY for authenticated connections and can be set by the user via the CURLOPT_MAIL_AUTH option - If is not set then the CURLOPT_MAIL_FROM value will be used instead.

Added the ability for the user to specify whether authenticated connections (AUTH NTLM, AUTH LOGIN and AUTH PLAIN) send the initial response in the AUTH command or in the next command. This can be set via the CURLOPT_MAIL_SINGLE_AUTH option but defaults to TRUE to maintain compatibility with the v7.21.7 source.

Started to integrate the NTLM module into http_ntlm but do to recent changes in the source this it taking longer than I had expect so it still needs to be fully completed.
---
 include/curl/curl.h |    6 +
 lib/http_ntlm.c     |   22 +-
 lib/http_ntlm.h     |  108 +-----
 lib/ntlm.c          | 1220 +++++++++++++++++++++++++++++++++++++++++++++++++++
 lib/ntlm.h          |  140 ++++++
 lib/smtp.c          |  190 ++++++++-
 lib/smtp.h          |    4 +
 lib/url.c           |   16 +-
 lib/urldata.h       |    2 +
 9 files changed, 1566 insertions(+), 142 deletions(-)
 create mode 100644 lib/ntlm.c
 create mode 100644 lib/ntlm.h

diff --git a/include/curl/curl.h b/include/curl/curl.h
index 998c109..14578b2 100644
--- a/include/curl/curl.h
+++ b/include/curl/curl.h
@@ -1484,6 +1484,12 @@ typedef enum {
   CINIT(CLOSESOCKETFUNCTION, FUNCTIONPOINT, 208),
   CINIT(CLOSESOCKETDATA, OBJECTPOINT, 209),
 
+  /* set the SMTP auth originator */
+  CINIT(MAIL_AUTH, OBJECTPOINT, 210),
+
+  /* set whether the SMTP auth should contain the initial response */
+  CINIT(MAIL_SINGLE_AUTH, LONG, 211),
+
   CURLOPT_LASTENTRY /* the last unused */
 } CURLoption;
 
diff --git a/lib/http_ntlm.c b/lib/http_ntlm.c
index efc38c7..6561520 100644
--- a/lib/http_ntlm.c
+++ b/lib/http_ntlm.c
@@ -22,12 +22,6 @@
 
 #include "setup.h"
 
-/* NTLM details:
-
-   http://davenport.sourceforge.net/ntlm.html
-   http://www.innovation.ch/java/ntlm.html
-*/
-
 #ifndef CURL_DISABLE_HTTP
 #ifdef USE_NTLM
 
@@ -52,6 +46,7 @@
 #include "non-ascii.h"  /* for Curl_convert_... prototypes */
 #include "sendf.h"
 #include "rawstr.h"
+#include "ntlm.h"
 #include "curl_base64.h"
 #include "http_ntlm.h"
 #include "url.h"
@@ -660,6 +655,7 @@ ntlm_sspi_cleanup(struct ntlmdata *ntlm)
 #define LONGQUARTET(x) ((x) & 0xff), (((x) >> 8)&0xff), \
   (((x) >>16)&0xff), (((x)>>24) & 0xff)
 
+/* Local constants */
 #define HOSTNAME_MAX 1024
 
 #ifndef USE_WINDOWS_SSPI
@@ -1635,21 +1631,15 @@ CURLcode Curl_output_ntlm(struct connectdata *conn,
   return CURLE_OK;
 }
 
-
-void
-Curl_ntlm_cleanup(struct connectdata *conn)
+void Curl_http_ntlm_cleanup(struct connectdata *conn)
 {
-#ifdef USE_WINDOWS_SSPI
-  ntlm_sspi_cleanup(&conn->ntlm);
-  ntlm_sspi_cleanup(&conn->proxyntlm);
-#else
+  Curl_ntlm_cleanup(&conn->ntlm);
+  Curl_ntlm_cleanup(&conn->proxyntlm);
+
 #ifdef USE_NTLM_SSO
   sso_ntlm_close(conn);
 #endif
-  (void)conn;
-#endif
 }
 
-
 #endif /* USE_NTLM */
 #endif /* !CURL_DISABLE_HTTP */
diff --git a/lib/http_ntlm.h b/lib/http_ntlm.h
index faa7b0f..38ad3c2 100644
--- a/lib/http_ntlm.h
+++ b/lib/http_ntlm.h
@@ -44,112 +44,6 @@ CURLcode Curl_output_ntlm(struct connectdata *conn, bool proxy);
 CURLcode Curl_output_ntlm_sso(struct connectdata *conn, bool proxy);
 #endif
 
-void Curl_ntlm_cleanup(struct connectdata *conn);
-#ifndef USE_NTLM
-#define Curl_ntlm_cleanup(x)
-#endif
-
-/* Flag bits definitions based on http://davenport.sourceforge.net/ntlm.html */
-
-#define NTLMFLAG_NEGOTIATE_UNICODE               (1<<0)
-/* Indicates that Unicode strings are supported for use in security buffer
-   data. */
-
-#define NTLMFLAG_NEGOTIATE_OEM                   (1<<1)
-/* Indicates that OEM strings are supported for use in security buffer data. */
-
-#define NTLMFLAG_REQUEST_TARGET                  (1<<2)
-/* Requests that the server's authentication realm be included in the Type 2
-   message. */
-
-/* unknown (1<<3) */
-#define NTLMFLAG_NEGOTIATE_SIGN                  (1<<4)
-/* Specifies that authenticated communication between the client and server
-   should carry a digital signature (message integrity). */
-
-#define NTLMFLAG_NEGOTIATE_SEAL                  (1<<5)
-/* Specifies that authenticated communication between the client and server
-   should be encrypted (message confidentiality). */
-
-#define NTLMFLAG_NEGOTIATE_DATAGRAM_STYLE        (1<<6)
-/* Indicates that datagram authentication is being used. */
-
-#define NTLMFLAG_NEGOTIATE_LM_KEY                (1<<7)
-/* Indicates that the LAN Manager session key should be used for signing and
-   sealing authenticated communications. */
-
-#define NTLMFLAG_NEGOTIATE_NETWARE               (1<<8)
-/* unknown purpose */
-
-#define NTLMFLAG_NEGOTIATE_NTLM_KEY              (1<<9)
-/* Indicates that NTLM authentication is being used. */
-
-/* unknown (1<<10) */
-
-#define NTLMFLAG_NEGOTIATE_ANONYMOUS             (1<<11)
-/* Sent by the client in the Type 3 message to indicate that an anonymous
-   context has been established. This also affects the response fields. */
-
-#define NTLMFLAG_NEGOTIATE_DOMAIN_SUPPLIED       (1<<12)
-/* Sent by the client in the Type 1 message to indicate that a desired
-   authentication realm is included in the message. */
-
-#define NTLMFLAG_NEGOTIATE_WORKSTATION_SUPPLIED  (1<<13)
-/* Sent by the client in the Type 1 message to indicate that the client
-   workstation's name is included in the message. */
-
-#define NTLMFLAG_NEGOTIATE_LOCAL_CALL            (1<<14)
-/* Sent by the server to indicate that the server and client are on the same
-   machine. Implies that the client may use a pre-established local security
-   context rather than responding to the challenge. */
-
-#define NTLMFLAG_NEGOTIATE_ALWAYS_SIGN           (1<<15)
-/* Indicates that authenticated communication between the client and server
-   should be signed with a "dummy" signature. */
-
-#define NTLMFLAG_TARGET_TYPE_DOMAIN              (1<<16)
-/* Sent by the server in the Type 2 message to indicate that the target
-   authentication realm is a domain. */
-
-#define NTLMFLAG_TARGET_TYPE_SERVER              (1<<17)
-/* Sent by the server in the Type 2 message to indicate that the target
-   authentication realm is a server. */
-
-#define NTLMFLAG_TARGET_TYPE_SHARE               (1<<18)
-/* Sent by the server in the Type 2 message to indicate that the target
-   authentication realm is a share. Presumably, this is for share-level
-   authentication. Usage is unclear. */
-
-#define NTLMFLAG_NEGOTIATE_NTLM2_KEY             (1<<19)
-/* Indicates that the NTLM2 signing and sealing scheme should be used for
-   protecting authenticated communications. */
-
-#define NTLMFLAG_REQUEST_INIT_RESPONSE           (1<<20)
-/* unknown purpose */
-
-#define NTLMFLAG_REQUEST_ACCEPT_RESPONSE         (1<<21)
-/* unknown purpose */
-
-#define NTLMFLAG_REQUEST_NONNT_SESSION_KEY       (1<<22)
-/* unknown purpose */
-
-#define NTLMFLAG_NEGOTIATE_TARGET_INFO           (1<<23)
-/* Sent by the server in the Type 2 message to indicate that it is including a
-   Target Information block in the message. */
-
-/* unknown (1<24) */
-/* unknown (1<25) */
-/* unknown (1<26) */
-/* unknown (1<27) */
-/* unknown (1<28) */
-
-#define NTLMFLAG_NEGOTIATE_128                   (1<<29)
-/* Indicates that 128-bit encryption is supported. */
-
-#define NTLMFLAG_NEGOTIATE_KEY_EXCHANGE          (1<<30)
-/* Indicates that the client will provide an encrypted master key in
-   the "Session Key" field of the Type 3 message. */
+void Curl_http_ntlm_cleanup(struct connectdata *conn);
 
-#define NTLMFLAG_NEGOTIATE_56                    (1<<31)
-/* Indicates that 56-bit encryption is supported. */
 #endif
diff --git a/lib/ntlm.c b/lib/ntlm.c
new file mode 100644
index 0000000..58982b3
--- /dev/null
+++ b/lib/ntlm.c
@@ -0,0 +1,1220 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2011, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+#include "setup.h"
+
+/* NTLM details:
+
+   http://davenport.sourceforge.net/ntlm.html
+   http://www.innovation.ch/java/ntlm.html
+*/
+
+#ifdef USE_NTLM
+
+#define DEBUG_ME 0
+
+/* -- WIN32 approved -- */
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <ctype.h>
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#if (defined(NETWARE) && !defined(__NOVELL_LIBC__))
+#include <netdb.h>
+#endif
+
+#include "urldata.h"
+#include "non-ascii.h"  /* for Curl_convert_... prototypes */
+#include "sendf.h"
+#include "rawstr.h"
+#include "curl_base64.h"
+#include "ntlm.h"
+
+#define _MPRINTF_REPLACE /* use our functions only */
+#include <curl/mprintf.h>
+
+/* "NTLMSSP" signature is always in ASCII regardless of the platform */
+#define NTLMSSP_SIGNATURE "\x4e\x54\x4c\x4d\x53\x53\x50"
+
+/* Local debug */
+#if DEBUG_ME
+# define DEBUG_OUT(x) x
+#else
+# define DEBUG_OUT(x)
+#endif
+
+#ifdef USE_SSLEAY
+#include "ssluse.h"
+#    ifdef USE_OPENSSL
+#      include <openssl/des.h>
+#      ifndef OPENSSL_NO_MD4
+#        include <openssl/md4.h>
+#      endif
+#      include <openssl/md5.h>
+#      include <openssl/ssl.h>
+#      include <openssl/rand.h>
+#    else
+#      include <des.h>
+#      ifndef OPENSSL_NO_MD4
+#        include <md4.h>
+#      endif
+#      include <md5.h>
+#      include <ssl.h>
+#      include <rand.h>
+#    endif
+
+#ifndef OPENSSL_VERSION_NUMBER
+#error "OPENSSL_VERSION_NUMBER not defined"
+#endif
+
+#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
+
+#ifdef OPENSSL_NO_MD4
+/* This requires MD4, but OpenSSL was compiled without it */
+#define USE_NTRESPONSES 0
+#define USE_NTLM2SESSION 0
+#endif
+
+#elif defined(USE_GNUTLS)
+
+#include "gtls.h"
+#include <gcrypt.h>
+
+#define MD5_DIGEST_LENGTH 16
+#define MD4_DIGEST_LENGTH 16
+
+#elif defined(USE_NSS)
+
+#include "curl_md4.h"
+#include "nssg.h"
+#include <nss.h>
+#include <pk11pub.h>
+#include <hasht.h>
+#define MD5_DIGEST_LENGTH MD5_LENGTH
+
+#elif defined(USE_WINDOWS_SSPI)
+
+#include "curl_sspi.h"
+
+#else
+#    error "Can't compile NTLM support without a crypto library."
+#endif
+
+/* The last #include file should be: */
+#include "memdebug.h"
+
+#ifndef USE_NTRESPONSES
+/* Define this to make the type-3 message include the NT response message */
+#define USE_NTRESPONSES 1
+
+/* Define this to make the type-3 message include the NTLM2Session response
+   message, requires USE_NTRESPONSES. */
+#define USE_NTLM2SESSION 1
+#endif
+
+#ifndef USE_WINDOWS_SSPI
+/* this function converts from the little endian format used in the incoming
+   package to whatever endian format we're using natively */
+static unsigned int readint_le(unsigned char *buf) /* must point to a
+                                                      4 bytes buffer*/
+{
+  return ((unsigned int)buf[0]) | ((unsigned int)buf[1] << 8) |
+    ((unsigned int)buf[2] << 16) | ((unsigned int)buf[3] << 24);
+}
+#endif
+
+static void ntlm_print_flags(FILE *handle, unsigned long flags)
+{
+  if(flags & NTLMFLAG_NEGOTIATE_UNICODE)
+    fprintf(handle, "NTLMFLAG_NEGOTIATE_UNICODE ");
+  if(flags & NTLMFLAG_NEGOTIATE_OEM)
+    fprintf(handle, "NTLMFLAG_NEGOTIATE_OEM ");
+  if(flags & NTLMFLAG_REQUEST_TARGET)
+    fprintf(handle, "NTLMFLAG_REQUEST_TARGET ");
+  if(flags & (1<<3))
+    fprintf(handle, "NTLMFLAG_UNKNOWN_3 ");
+  if(flags & NTLMFLAG_NEGOTIATE_SIGN)
+    fprintf(handle, "NTLMFLAG_NEGOTIATE_SIGN ");
+  if(flags & NTLMFLAG_NEGOTIATE_SEAL)
+    fprintf(handle, "NTLMFLAG_NEGOTIATE_SEAL ");
+  if(flags & NTLMFLAG_NEGOTIATE_DATAGRAM_STYLE)
+    fprintf(handle, "NTLMFLAG_NEGOTIATE_DATAGRAM_STYLE ");
+  if(flags & NTLMFLAG_NEGOTIATE_LM_KEY)
+    fprintf(handle, "NTLMFLAG_NEGOTIATE_LM_KEY ");
+  if(flags & NTLMFLAG_NEGOTIATE_NETWARE)
+    fprintf(handle, "NTLMFLAG_NEGOTIATE_NETWARE ");
+  if(flags & NTLMFLAG_NEGOTIATE_NTLM_KEY)
+    fprintf(handle, "NTLMFLAG_NEGOTIATE_NTLM_KEY ");
+  if(flags & (1<<10))
+    fprintf(handle, "NTLMFLAG_UNKNOWN_10 ");
+  if(flags & NTLMFLAG_NEGOTIATE_ANONYMOUS)
+    fprintf(handle, "NTLMFLAG_NEGOTIATE_ANONYMOUS ");
+  if(flags & NTLMFLAG_NEGOTIATE_DOMAIN_SUPPLIED)
+    fprintf(handle, "NTLMFLAG_NEGOTIATE_DOMAIN_SUPPLIED ");
+  if(flags & NTLMFLAG_NEGOTIATE_WORKSTATION_SUPPLIED)
+    fprintf(handle, "NTLMFLAG_NEGOTIATE_WORKSTATION_SUPPLIED ");
+  if(flags & NTLMFLAG_NEGOTIATE_LOCAL_CALL)
+    fprintf(handle, "NTLMFLAG_NEGOTIATE_LOCAL_CALL ");
+  if(flags & NTLMFLAG_NEGOTIATE_ALWAYS_SIGN)
+    fprintf(handle, "NTLMFLAG_NEGOTIATE_ALWAYS_SIGN ");
+  if(flags & NTLMFLAG_TARGET_TYPE_DOMAIN)
+    fprintf(handle, "NTLMFLAG_TARGET_TYPE_DOMAIN ");
+  if(flags & NTLMFLAG_TARGET_TYPE_SERVER)
+    fprintf(handle, "NTLMFLAG_TARGET_TYPE_SERVER ");
+  if(flags & NTLMFLAG_TARGET_TYPE_SHARE)
+    fprintf(handle, "NTLMFLAG_TARGET_TYPE_SHARE ");
+  if(flags & NTLMFLAG_NEGOTIATE_NTLM2_KEY)
+    fprintf(handle, "NTLMFLAG_NEGOTIATE_NTLM2_KEY ");
+  if(flags & NTLMFLAG_REQUEST_INIT_RESPONSE)
+    fprintf(handle, "NTLMFLAG_REQUEST_INIT_RESPONSE ");
+  if(flags & NTLMFLAG_REQUEST_ACCEPT_RESPONSE)
+    fprintf(handle, "NTLMFLAG_REQUEST_ACCEPT_RESPONSE ");
+  if(flags & NTLMFLAG_REQUEST_NONNT_SESSION_KEY)
+    fprintf(handle, "NTLMFLAG_REQUEST_NONNT_SESSION_KEY ");
+  if(flags & NTLMFLAG_NEGOTIATE_TARGET_INFO)
+    fprintf(handle, "NTLMFLAG_NEGOTIATE_TARGET_INFO ");
+  if(flags & (1<<24))
+    fprintf(handle, "NTLMFLAG_UNKNOWN_24 ");
+  if(flags & (1<<25))
+    fprintf(handle, "NTLMFLAG_UNKNOWN_25 ");
+  if(flags & (1<<26))
+    fprintf(handle, "NTLMFLAG_UNKNOWN_26 ");
+  if(flags & (1<<27))
+    fprintf(handle, "NTLMFLAG_UNKNOWN_27 ");
+  if(flags & (1<<28))
+    fprintf(handle, "NTLMFLAG_UNKNOWN_28 ");
+  if(flags & NTLMFLAG_NEGOTIATE_128)
+    fprintf(handle, "NTLMFLAG_NEGOTIATE_128 ");
+  if(flags & NTLMFLAG_NEGOTIATE_KEY_EXCHANGE)
+    fprintf(handle, "NTLMFLAG_NEGOTIATE_KEY_EXCHANGE ");
+  if(flags & NTLMFLAG_NEGOTIATE_56)
+    fprintf(handle, "NTLMFLAG_NEGOTIATE_56 ");
+}
+
+static void ntlm_print_hex(FILE *handle, const char *buf, size_t len)
+{
+  const char *p = buf;
+  fprintf(stderr, "0x");
+  while(len-- > 0)
+    fprintf(stderr, "%02.2x", (unsigned int)*p++);
+}
+
+#ifndef USE_WINDOWS_SSPI
+
+#ifdef USE_SSLEAY
+/*
+ * 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] = (unsigned char)(((key_56[0] << 7) & 0xFF) | (key_56[1] >> 1));
+  key[2] = (unsigned char)(((key_56[1] << 6) & 0xFF) | (key_56[2] >> 2));
+  key[3] = (unsigned char)(((key_56[2] << 5) & 0xFF) | (key_56[3] >> 3));
+  key[4] = (unsigned char)(((key_56[3] << 4) & 0xFF) | (key_56[4] >> 4));
+  key[5] = (unsigned char)(((key_56[4] << 3) & 0xFF) | (key_56[5] >> 5));
+  key[6] = (unsigned char)(((key_56[5] << 2) & 0xFF) | (key_56[6] >> 6));
+  key[7] = (unsigned char) ((key_56[6] << 1) & 0xFF);
+
+  DES_set_odd_parity(&key);
+  DES_set_key(&key, ks);
+}
+
+#else /* defined(USE_SSLEAY) */
+
+/*
+ * Turns a 56 bit key into the 64 bit, odd parity key.  Used by GnuTLS and NSS.
+ */
+static void extend_key_56_to_64(const unsigned char *key_56, char *key)
+{
+  key[0] = key_56[0];
+  key[1] = (unsigned char)(((key_56[0] << 7) & 0xFF) | (key_56[1] >> 1));
+  key[2] = (unsigned char)(((key_56[1] << 6) & 0xFF) | (key_56[2] >> 2));
+  key[3] = (unsigned char)(((key_56[2] << 5) & 0xFF) | (key_56[3] >> 3));
+  key[4] = (unsigned char)(((key_56[3] << 4) & 0xFF) | (key_56[4] >> 4));
+  key[5] = (unsigned char)(((key_56[4] << 3) & 0xFF) | (key_56[5] >> 5));
+  key[6] = (unsigned char)(((key_56[5] << 2) & 0xFF) | (key_56[6] >> 6));
+  key[7] = (unsigned char) ((key_56[6] << 1) & 0xFF);
+}
+
+#if defined(USE_GNUTLS)
+
+/*
+ * Turns a 56 bit key into the 64 bit, odd parity key and sets the key.
+ */
+static void setup_des_key(const unsigned char *key_56,
+                          gcry_cipher_hd_t *des)
+{
+  char key[8];
+  extend_key_56_to_64(key_56, key);
+  gcry_cipher_setkey(*des, key, 8);
+}
+
+#elif defined(USE_NSS)
+
+/*
+ * Expands a 56 bit key KEY_56 to 64 bit and encrypts 64 bit of data, using
+ * the expanded key.  The caller is responsible for giving 64 bit of valid
+ * data is IN and (at least) 64 bit large buffer as OUT.
+ */
+static bool encrypt_des(const unsigned char *in, unsigned char *out,
+                        const unsigned char *key_56)
+{
+  const CK_MECHANISM_TYPE mech = CKM_DES_ECB; /* DES cipher in ECB mode */
+  PK11SlotInfo *slot = NULL;
+  char key[8];                                /* expanded 64 bit key */
+  SECItem key_item;
+  PK11SymKey *symkey = NULL;
+  SECItem *param = NULL;
+  PK11Context *ctx = NULL;
+  int out_len;                                /* not used, required by NSS */
+  bool rv = FALSE;
+
+  /* use internal slot for DES encryption (requires NSS to be initialized) */
+  slot = PK11_GetInternalKeySlot();
+  if(!slot)
+    return FALSE;
+
+  /* expand the 56 bit key to 64 bit and wrap by NSS */
+  extend_key_56_to_64(key_56, key);
+  key_item.data = (unsigned char *)key;
+  key_item.len = /* hard-wired */ 8;
+  symkey = PK11_ImportSymKey(slot, mech, PK11_OriginUnwrap, CKA_ENCRYPT,
+                             &key_item, NULL);
+  if(!symkey)
+    goto fail;
+
+  /* create DES encryption context */
+  param = PK11_ParamFromIV(mech, /* no IV in ECB mode */ NULL);
+  if(!param)
+    goto fail;
+  ctx = PK11_CreateContextBySymKey(mech, CKA_ENCRYPT, symkey, param);
+  if(!ctx)
+    goto fail;
+
+  /* perform the encryption */
+  if(SECSuccess == PK11_CipherOp(ctx, out, &out_len, /* outbuflen */ 8,
+                                 (unsigned char *)in, /* inbuflen */ 8)
+      && SECSuccess == PK11_Finalize(ctx))
+    rv = /* all OK */ TRUE;
+
+fail:
+  /* cleanup */
+  if(ctx)
+    PK11_DestroyContext(ctx, PR_TRUE);
+  if(symkey)
+    PK11_FreeSymKey(symkey);
+  if(param)
+    SECITEM_FreeItem(param, PR_TRUE);
+  PK11_FreeSlot(slot);
+  return rv;
+}
+
+#endif /* defined(USE_NSS) */
+
+#endif /* defined(USE_SSLEAY) */
+
+ /*
+  * 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 lm_resp(const unsigned char *keys,
+                    const unsigned char *plaintext,
+                    unsigned char *results)
+{
+#ifdef USE_SSLEAY
+  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);
+#elif defined(USE_GNUTLS)
+  gcry_cipher_hd_t des;
+
+  gcry_cipher_open(&des, GCRY_CIPHER_DES, GCRY_CIPHER_MODE_ECB, 0);
+  setup_des_key(keys, &des);
+  gcry_cipher_encrypt(des, results, 8, plaintext, 8);
+  gcry_cipher_close(des);
+
+  gcry_cipher_open(&des, GCRY_CIPHER_DES, GCRY_CIPHER_MODE_ECB, 0);
+  setup_des_key(keys+7, &des);
+  gcry_cipher_encrypt(des, results+8, 8, plaintext, 8);
+  gcry_cipher_close(des);
+
+  gcry_cipher_open(&des, GCRY_CIPHER_DES, GCRY_CIPHER_MODE_ECB, 0);
+  setup_des_key(keys+14, &des);
+  gcry_cipher_encrypt(des, results+16, 8, plaintext, 8);
+  gcry_cipher_close(des);
+#elif defined(USE_NSS)
+  encrypt_des(plaintext, results,    keys);
+  encrypt_des(plaintext, results+8,  keys+7);
+  encrypt_des(plaintext, results+16, keys+14);
+#endif
+}
+
+
+/*
+ * Set up lanmanager hashed password
+ */
+static void mk_lm_hash(struct SessionHandle *data,
+                       const char *password,
+                       unsigned char *lmbuffer /* 21 bytes */)
+{
+  CURLcode res;
+  unsigned char pw[14];
+  static const unsigned char magic[] = {
+    0x4B, 0x47, 0x53, 0x21, 0x40, 0x23, 0x24, 0x25 /* i.e. KGS!@#$% */
+  };
+  size_t len = CURLMIN(strlen(password), 14);
+
+  Curl_strntoupper((char *)pw, password, len);
+  memset(&pw[len], 0, 14-len);
+
+  /*
+   * The LanManager hashed password needs to be created using the
+   * password in the network encoding not the host encoding.
+   */
+  res = Curl_convert_to_network(data, (char *)pw, 14);
+  if(res)
+    return;
+
+  {
+    /* Create LanManager hashed password. */
+
+#ifdef USE_SSLEAY
+    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);
+#elif defined(USE_GNUTLS)
+    gcry_cipher_hd_t des;
+
+    gcry_cipher_open(&des, GCRY_CIPHER_DES, GCRY_CIPHER_MODE_ECB, 0);
+    setup_des_key(pw, &des);
+    gcry_cipher_encrypt(des, lmbuffer, 8, magic, 8);
+    gcry_cipher_close(des);
+
+    gcry_cipher_open(&des, GCRY_CIPHER_DES, GCRY_CIPHER_MODE_ECB, 0);
+    setup_des_key(pw+7, &des);
+    gcry_cipher_encrypt(des, lmbuffer+8, 8, magic, 8);
+    gcry_cipher_close(des);
+#elif defined(USE_NSS)
+    encrypt_des(magic, lmbuffer,   pw);
+    encrypt_des(magic, lmbuffer+8, pw+7);
+#endif
+
+    memset(lmbuffer + 16, 0, 21 - 16);
+  }
+}
+
+#if USE_NTRESPONSES
+static void ascii_to_unicode_le(unsigned char *dest, const char *src,
+                               size_t srclen)
+{
+  size_t i;
+  for(i=0; i<srclen; i++) {
+    dest[2*i]   = (unsigned char)src[i];
+    dest[2*i+1] =   '\0';
+  }
+}
+
+/*
+ * Set up nt hashed passwords
+ */
+static CURLcode mk_nt_hash(struct SessionHandle *data,
+                           const char *password,
+                           unsigned char *ntbuffer /* 21 bytes */)
+{
+  size_t len = strlen(password);
+  unsigned char *pw = malloc(len*2);
+  CURLcode result;
+  if(!pw)
+    return CURLE_OUT_OF_MEMORY;
+
+  ascii_to_unicode_le(pw, password, len);
+
+  /*
+   * The NT hashed password needs to be created using the password in the
+   * network encoding not the host encoding.
+   */
+  result = Curl_convert_to_network(data, (char *)pw, len*2);
+  if(result)
+    return result;
+
+  {
+    /* Create NT hashed password. */
+#ifdef USE_SSLEAY
+    MD4_CTX MD4pw;
+    MD4_Init(&MD4pw);
+    MD4_Update(&MD4pw, pw, 2*len);
+    MD4_Final(ntbuffer, &MD4pw);
+#elif defined(USE_GNUTLS)
+    gcry_md_hd_t MD4pw;
+    gcry_md_open(&MD4pw, GCRY_MD_MD4, 0);
+    gcry_md_write(MD4pw, pw, 2*len);
+    memcpy (ntbuffer, gcry_md_read (MD4pw, 0), MD4_DIGEST_LENGTH);
+    gcry_md_close(MD4pw);
+#elif defined(USE_NSS)
+    Curl_md4it(ntbuffer, pw, 2*len);
+#endif
+
+    memset(ntbuffer + 16, 0, 21 - 16);
+  }
+
+  free(pw);
+  return CURLE_OK;
+}
+#endif
+
+#endif
+
+#define SHORTPAIR(x) ((x) & 0xff), (((x) >> 8) & 0xff)
+#define LONGQUARTET(x) ((x) & 0xff), (((x) >> 8)&0xff), \
+  (((x) >>16)&0xff), (((x)>>24) & 0xff)
+
+#define HOSTNAME_MAX 1024
+
+/* copy the source to the destination and fill in zeroes in every
+   other destination byte! */
+static void unicodecpy(unsigned char *dest,
+                       const char *src, size_t length)
+{
+  size_t i;
+  for(i=0; i<length; i++) {
+    dest[2*i] = (unsigned char)src[i];
+    dest[2*i+1] = '\0';
+  }
+}
+
+#ifdef USE_WINDOWS_SSPI
+static void ntlm_sspi_cleanup(struct ntlmdata *ntlm)
+{
+  if(ntlm->type_2) {
+    free(ntlm->type_2);
+    ntlm->type_2 = NULL;
+  }
+  if(ntlm->has_handles) {
+    s_pSecFn->DeleteSecurityContext(&ntlm->c_handle);
+    s_pSecFn->FreeCredentialsHandle(&ntlm->handle);
+    ntlm->has_handles = 0;
+  }
+  if(ntlm->p_identity) {
+    if(ntlm->identity.User) free(ntlm->identity.User);
+    if(ntlm->identity.Password) free(ntlm->identity.Password);
+    if(ntlm->identity.Domain) free(ntlm->identity.Domain);
+    ntlm->p_identity = NULL;
+  }
+}
+#endif
+
+size_t Curl_ntlm_create_type1_message(struct SessionHandle *data, const char *userp, const char *passwdp, const char *hostp, struct ntlmdata *ntlm, char **outptr)
+{
+  /* Create 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
+
+  */
+
+  size_t size = 0;
+  unsigned char ntlmbuf[1024]; /* enough, unless the user+host+domain is very
+                                  long */
+#ifdef USE_WINDOWS_SSPI
+  SecBuffer buf;
+  SecBufferDesc desc;
+  SECURITY_STATUS status;
+  ULONG attrs;
+  const char *domain = "";
+  const char *user = "";
+  const char *passwd = "";
+  size_t domlen = 0;
+  size_t userlen = 0;
+  size_t passwdlen = 0;
+  TimeStamp tsDummy; /* For Windows 9x compatibility of SPPI calls */
+
+  ntlm_sspi_cleanup(ntlm);
+
+  if(userp != NULL) {
+    user = strchr(userp, '\\');
+    if(!user)
+      user = strchr(userp, '/');
+
+    if(user) {
+      domain = userp;
+      domlen = (user - domain);
+      user++;
+    }
+	else {
+      domain = "";
+      domlen = 0;
+      user = userp;
+	}
+
+    userlen = strlen(user);
+  }
+
+  if(passwdp != NULL)
+  {
+    passwd = passwdp;
+  }
+  passwdlen = strlen(passwd);
+
+  if(user && *user) {
+    /* note: initialize all of this before doing the mallocs so that
+     * it can be cleaned up later without leaking memory.
+     */
+    ntlm->p_identity = &ntlm->identity;
+    memset(ntlm->p_identity, 0, sizeof(*ntlm->p_identity));
+
+    if((ntlm->identity.User = (unsigned char *)strdup(user)) == NULL)
+      return CURLE_OUT_OF_MEMORY;
+    ntlm->identity.UserLength = userlen;
+
+    if((ntlm->identity.Password = (unsigned char *)strdup(passwdp)) == NULL)
+      return CURLE_OUT_OF_MEMORY;
+    ntlm->identity.PasswordLength = passwdlen;
+
+    if((ntlm->identity.Domain = malloc(domlen+1)) == NULL)
+      return CURLE_OUT_OF_MEMORY;
+    strncpy((char *)ntlm->identity.Domain, domain, domlen);
+
+    ntlm->identity.Domain[domlen] = '\0';
+    ntlm->identity.DomainLength = domlen;
+    ntlm->identity.Flags = SEC_WINNT_AUTH_IDENTITY_ANSI;
+  }
+  else {
+    ntlm->p_identity = NULL;
+  }
+
+  if(s_pSecFn->AcquireCredentialsHandleA(
+     NULL, (char *)"NTLM", SECPKG_CRED_OUTBOUND, NULL, ntlm->p_identity,
+     NULL, NULL, &ntlm->handle, &tsDummy) != SEC_E_OK)
+    return CURLE_OUT_OF_MEMORY;
+
+  desc.ulVersion = SECBUFFER_VERSION;
+  desc.cBuffers  = 1;
+  desc.pBuffers  = &buf;
+  buf.cbBuffer   = sizeof(ntlmbuf);
+  buf.BufferType = SECBUFFER_TOKEN;
+  buf.pvBuffer   = ntlmbuf;
+
+  status = s_pSecFn->InitializeSecurityContextA(&ntlm->handle, NULL,
+                                                (char *) hostp,
+                                                ISC_REQ_CONFIDENTIALITY |
+                                                ISC_REQ_REPLAY_DETECT |
+                                                ISC_REQ_CONNECTION,
+                                                0, SECURITY_NETWORK_DREP,
+                                                NULL, 0,
+                                                &ntlm->c_handle, &desc,
+                                                &attrs, &tsDummy);
+
+  if(status == SEC_I_COMPLETE_AND_CONTINUE || status == SEC_I_CONTINUE_NEEDED) {
+    s_pSecFn->CompleteAuthToken(&ntlm->c_handle, &desc);
+  }
+  else if(status != SEC_E_OK) {
+    s_pSecFn->FreeCredentialsHandle(&ntlm->handle);
+    return CURLE_RECV_ERROR;
+  }
+
+  ntlm->has_handles = 1;
+  size = buf.cbBuffer;
+#else
+  const char *domain = ""; /* empty */
+  char host [HOSTNAME_MAX + 1] = ""; /* empty */
+  size_t domlen = strlen(domain);
+  size_t hostlen = strlen(host);
+  size_t hostoff = 0; /* host name offset */
+  size_t domoff = hostoff + hostlen;  /* domain name offset */
+
+#if USE_NTLM2SESSION
+#define NTLM2FLAG NTLMFLAG_NEGOTIATE_NTLM2_KEY
+#else
+#define NTLM2FLAG 0
+#endif
+  snprintf((char *)ntlmbuf, sizeof(ntlmbuf),
+           NTLMSSP_SIGNATURE "%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|
+                       NTLMFLAG_REQUEST_TARGET|
+                       NTLMFLAG_NEGOTIATE_NTLM_KEY|
+                       NTLM2FLAG|
+                       NTLMFLAG_NEGOTIATE_ALWAYS_SIGN
+                       ),
+           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;
+#endif
+
+  DEBUG_OUT({
+      fprintf(stderr, "* TYPE1 header flags=0x%02.2x%02.2x%02.2x%02.2x "
+              "0x%08.8x ",
+              LONGQUARTET(NTLMFLAG_NEGOTIATE_OEM|
+                          NTLMFLAG_REQUEST_TARGET|
+                          NTLMFLAG_NEGOTIATE_NTLM_KEY|
+                          NTLM2FLAG|
+                          NTLMFLAG_NEGOTIATE_ALWAYS_SIGN),
+              NTLMFLAG_NEGOTIATE_OEM|
+              NTLMFLAG_REQUEST_TARGET|
+              NTLMFLAG_NEGOTIATE_NTLM_KEY|
+              NTLM2FLAG|
+              NTLMFLAG_NEGOTIATE_ALWAYS_SIGN);
+      ntlm_print_flags(stderr,
+                       NTLMFLAG_NEGOTIATE_OEM|
+                       NTLMFLAG_REQUEST_TARGET|
+                       NTLMFLAG_NEGOTIATE_NTLM_KEY|
+                       NTLM2FLAG|
+                       NTLMFLAG_NEGOTIATE_ALWAYS_SIGN);
+      fprintf(stderr, "\n****\n");
+  });
+
+  /* now size is the size of the base64 encoded package size */
+  size = Curl_base64_encode(NULL, (char *)ntlmbuf, size, outptr);
+  
+  return size;
+}
+
+size_t Curl_ntlm_create_type3_message(struct SessionHandle *data, const char *userp, const char *passwdp, const char *hostp, struct ntlmdata *ntlm, char **outptr)
+{
+  /* 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
+
+  */
+
+  size_t size = 0;
+  unsigned char ntlmbuf[1024]; /* enough, unless the user+host+domain is very
+                                  long */
+
+  char host[HOSTNAME_MAX + 1] = "";
+  size_t hostlen = 0;
+
+#ifdef USE_WINDOWS_SSPI
+  SecBuffer type_2, type_3;
+  SecBufferDesc type_2_desc, type_3_desc;
+  SECURITY_STATUS status;
+  ULONG attrs;
+  TimeStamp tsDummy; /* For Windows 9x compatibility of SPPI calls */
+
+  /* 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.
+   */
+  if(hostp != NULL) {
+    char *dot;
+    strcpy(host, hostp);
+    dot = strchr(hostp, '.');
+    if(dot)
+      *dot = '\0';
+    hostlen = strlen(host);
+  }
+
+  type_2_desc.ulVersion  = type_3_desc.ulVersion  = SECBUFFER_VERSION;
+  type_2_desc.cBuffers   = type_3_desc.cBuffers   = 1;
+  type_2_desc.pBuffers   = &type_2;
+  type_3_desc.pBuffers   = &type_3;
+
+  type_2.BufferType = SECBUFFER_TOKEN;
+  type_2.pvBuffer   = ntlm->type_2;
+  type_2.cbBuffer   = ntlm->n_type_2;
+  type_3.BufferType = SECBUFFER_TOKEN;
+  type_3.pvBuffer   = ntlmbuf;
+  type_3.cbBuffer   = sizeof(ntlmbuf);
+
+  status = s_pSecFn->InitializeSecurityContextA(&ntlm->handle,
+                                                &ntlm->c_handle,
+                                                (char *) host,
+                                                ISC_REQ_CONFIDENTIALITY |
+                                                ISC_REQ_REPLAY_DETECT |
+                                                ISC_REQ_CONNECTION,
+                                                0, SECURITY_NETWORK_DREP,
+                                                &type_2_desc,
+                                                0, &ntlm->c_handle,
+                                                &type_3_desc,
+                                                &attrs, &tsDummy);
+
+  if(status != SEC_E_OK)
+    return CURLE_RECV_ERROR;
+
+  size = type_3.cbBuffer;
+
+  ntlm_sspi_cleanup(ntlm);
+#else
+  int lmrespoff;
+  unsigned char lmresp[24]; /* fixed-size */
+#if USE_NTRESPONSES
+  int ntrespoff;
+  unsigned char ntresp[24]; /* fixed-size */
+#endif
+  bool unicode = (ntlm->flags & NTLMFLAG_NEGOTIATE_UNICODE) ? true : false;
+  size_t useroff;
+  const char *domain = "";
+  const char *user = "";
+  const char *passwd = "";
+  size_t domlen = 0;
+  size_t userlen = 0;
+  size_t hostoff = 0;
+  size_t domoff = hostoff + hostlen;
+  CURLcode res;
+
+  if(userp != NULL) {
+    user = strchr(userp, '\\');
+    if(!user)
+      user = strchr(userp, '/');
+
+    if(user) {
+      domain = userp;
+      domlen = (user - domain);
+      user++;
+    }
+	else {
+      domain = "";
+      domlen = 0;
+      user = userp;
+	}
+
+    userlen = strlen(user);
+  }
+
+  if(passwdp != NULL)
+  {
+    passwd = passwdp;
+  }
+
+  if(unicode) {
+    domlen = domlen * 2;
+    userlen = userlen * 2;
+    hostlen = hostlen * 2;
+  }
+
+  /* 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.
+   */
+  if(hostp != NULL) {
+    char *dot;
+    strcpy(host, hostp);
+    dot = strchr(hostp, '.');
+    if(dot)
+      *dot = '\0';
+    hostlen = strlen(host);
+  }
+
+#if USE_NTLM2SESSION
+  /* We don't support NTLM2 if we don't have USE_NTRESPONSES */
+  if(ntlm->flags & NTLMFLAG_NEGOTIATE_NTLM2_KEY) {
+    unsigned char ntbuffer[0x18];
+    unsigned char tmp[0x18];
+    unsigned char md5sum[MD5_DIGEST_LENGTH];
+    unsigned char entropy[8];
+
+    /* Need to create 8 bytes random data */
+#ifdef USE_SSLEAY
+    MD5_CTX MD5pw;
+    Curl_ossl_seed(data); /* Initiate the seed if not already done */
+    RAND_bytes(entropy,8);
+#elif defined(USE_GNUTLS)
+    gcry_md_hd_t MD5pw;
+    Curl_gtls_seed(conn->data); /* Initiate the seed if not already done */
+    gcry_randomize(entropy, 8, GCRY_STRONG_RANDOM);
+#elif defined(USE_NSS)
+    PK11Context *MD5pw;
+    unsigned int outlen;
+    Curl_nss_seed(conn->data);  /* Initiate the seed if not already done */
+    PK11_GenerateRandom(entropy, 8);
+#endif
+
+    /* 8 bytes random data as challenge in lmresp */
+    memcpy(lmresp,entropy,8);
+    /* Pad with zeros */
+    memset(lmresp+8,0,0x10);
+
+    /* Fill tmp with challenge(nonce?) + entropy */
+    memcpy(tmp,&ntlm->nonce[0],8);
+    memcpy(tmp+8,entropy,8);
+
+#ifdef USE_SSLEAY
+    MD5_Init(&MD5pw);
+    MD5_Update(&MD5pw, tmp, 16);
+    MD5_Final(md5sum, &MD5pw);
+#elif defined(USE_GNUTLS)
+    gcry_md_open(&MD5pw, GCRY_MD_MD5, 0);
+    gcry_md_write(MD5pw, tmp, MD5_DIGEST_LENGTH);
+    memcpy(md5sum, gcry_md_read (MD5pw, 0), MD5_DIGEST_LENGTH);
+    gcry_md_close(MD5pw);
+#elif defined(USE_NSS)
+    MD5pw = PK11_CreateDigestContext(SEC_OID_MD5);
+    PK11_DigestOp(MD5pw, tmp, 16);
+    PK11_DigestFinal(MD5pw, md5sum, &outlen, MD5_DIGEST_LENGTH);
+    PK11_DestroyContext(MD5pw, PR_TRUE);
+#endif
+
+    /* We shall only use the first 8 bytes of md5sum,
+       but the des code in lm_resp only encrypt the first 8 bytes */
+    if(mk_nt_hash(data, passwd, ntbuffer) == CURLE_OUT_OF_MEMORY)
+      return CURLE_OUT_OF_MEMORY;
+    lm_resp(ntbuffer, md5sum, ntresp);
+
+    /* End of NTLM2 Session code */
+  }
+  else
+#endif
+  {
+
+#if USE_NTRESPONSES
+    unsigned char ntbuffer[0x18];
+#endif
+    unsigned char lmbuffer[0x18];
+
+#if USE_NTRESPONSES
+    if(mk_nt_hash(data, passwd, ntbuffer) == CURLE_OUT_OF_MEMORY)
+      return CURLE_OUT_OF_MEMORY;
+    lm_resp(ntbuffer, &ntlm->nonce[0], ntresp);
+#endif
+
+    mk_lm_hash(data, passwd, lmbuffer);
+    lm_resp(lmbuffer, &ntlm->nonce[0], lmresp);
+    /* A safer but less compatible alternative is:
+     *   lm_resp(ntbuffer, &ntlm->nonce[0], lmresp);
+     * See http://davenport.sourceforge.net/ntlm.html#ntlmVersion2 */
+  }
+
+  lmrespoff = 64; /* size of the message header */
+#if USE_NTRESPONSES
+  ntrespoff = lmrespoff + 0x18;
+  domoff = ntrespoff + 0x18;
+#else
+  domoff = lmrespoff + 0x18;
+#endif
+  useroff = domoff + domlen;
+  hostoff = useroff + userlen;
+
+  /* Create the big type-3 message binary blob */
+  size = snprintf((char *)ntlmbuf, sizeof(ntlmbuf),
+                  NTLMSSP_SIGNATURE "%c"
+                  "\x03%c%c%c" /* type-3, 32 bits */
+
+                  "%c%c" /* LanManager length */
+                  "%c%c" /* LanManager 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"  /* 2 zeroes */
+
+                  "%c%c"  /* session key length (unknown purpose) */
+                  "%c%c"  /* session key allocated space (unknown purpose) */
+                  "%c%c"  /* session key offset (unknown purpose) */
+                  "%c%c"  /* 2 zeroes */
+
+                  "%c%c%c%c" /* flags */
+
+                  /* 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,
+
+#if USE_NTRESPONSES
+                  SHORTPAIR(0x18),  /* NT-response length, twice */
+                  SHORTPAIR(0x18),
+                  SHORTPAIR(ntrespoff),
+                  0x0, 0x0,
+#else
+                  0x0, 0x0,
+                  0x0, 0x0,
+                  0x0, 0x0,
+                  0x0, 0x0,
+#endif
+                  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,
+
+                  LONGQUARTET(ntlm->flags));
+  
+  DEBUGASSERT(size==64);
+  DEBUGASSERT(size == (size_t)lmrespoff);
+
+  /* We append the binary hashes */
+  if(size < (sizeof(ntlmbuf) - 0x18)) {
+    memcpy(&ntlmbuf[size], lmresp, 0x18);
+    size += 0x18;
+  }
+
+  DEBUG_OUT({
+      fprintf(stderr, "**** TYPE3 header lmresp=");
+      ntlm_print_hex(stderr, (char *)&ntlmbuf[lmrespoff], 0x18);
+  });
+
+#if USE_NTRESPONSES
+  if(size < (sizeof(ntlmbuf) - 0x18)) {
+    DEBUGASSERT(size == (size_t)ntrespoff);
+    memcpy(&ntlmbuf[size], ntresp, 0x18);
+    size += 0x18;
+  }
+
+  DEBUG_OUT({
+      fprintf(stderr, "\n   ntresp=");
+      ntlm_print_hex(stderr, (char *)&ntlmbuf[ntrespoff], 0x18);
+  });
+#endif
+
+  DEBUG_OUT({
+      fprintf(stderr, "\n   flags=0x%02.2x%02.2x%02.2x%02.2x 0x%08.8x ",
+              LONGQUARTET(ntlm->flags), ntlm->flags);
+      ntlm_print_flags(stderr, ntlm->flags);
+      fprintf(stderr, "\n****\n");
+  });
+
+  /* Make sure that the domain, user and host strings fit in the target
+     buffer before we copy them there. */
+  if(size + userlen + domlen + hostlen >= sizeof(ntlmbuf)) {
+    failf(data, "user + domain + host name too big");
+    return CURLE_OUT_OF_MEMORY;
+  }
+
+  DEBUGASSERT(size == domoff);
+  if(unicode)
+    unicodecpy(&ntlmbuf[size], domain, domlen/2);
+  else
+    memcpy(&ntlmbuf[size], domain, domlen);
+
+  size += domlen;
+
+  DEBUGASSERT(size == useroff);
+  if(unicode)
+    unicodecpy(&ntlmbuf[size], user, userlen/2);
+  else
+    memcpy(&ntlmbuf[size], user, userlen);
+
+  size += userlen;
+
+  DEBUGASSERT(size == hostoff);
+  if(unicode)
+    unicodecpy(&ntlmbuf[size], host, hostlen/2);
+  else
+    memcpy(&ntlmbuf[size], host, hostlen);
+
+  size += hostlen;
+
+  /* convert domain, user, and host to ASCII but leave the rest as-is */
+  res = Curl_convert_to_network(data, (char *)&ntlmbuf[domoff], size-domoff);
+  if(res)
+    return 0;
+#endif
+
+  /* convert the binary blob into base64 */
+  size = Curl_base64_encode(NULL, (char *)ntlmbuf, size, outptr);
+
+  return size;
+}
+
+bool Curl_ntlm_decode_type2_message(const char* header, struct ntlmdata* ntlm)
+{
+  /* Decode a type-2 message:
+
+     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
+  */
+
+#ifndef USE_WINDOWS_SSPI
+  static const char type2_marker[] = { 0x02, 0x00, 0x00, 0x00 };
+#endif
+
+  unsigned char *buffer;
+  size_t size;
+
+  size = Curl_base64_decode(header, &buffer);
+  if(!buffer)
+    return false;
+
+#ifdef USE_WINDOWS_SSPI
+  ntlm->type_2 = malloc(size+1);
+  if(ntlm->type_2 == NULL) {
+    free(buffer);
+    return CURLE_OUT_OF_MEMORY;
+  }
+  ntlm->n_type_2 = size;
+  memcpy(ntlm->type_2, buffer, size);
+#else
+  ntlm->flags = 0;
+
+  if((size < 32) ||
+     (memcmp(buffer, NTLMSSP_SIGNATURE, 8) != 0) ||
+     (memcmp(buffer+8, type2_marker, sizeof(type2_marker)) != 0)) {
+     /* This was not a good enough type-2 message */
+     free(buffer);
+     return false;
+  }
+
+  ntlm->flags = readint_le(&buffer[20]);
+  memcpy(ntlm->nonce, &buffer[24], 8);
+
+  DEBUG_OUT({
+    fprintf(stderr, "**** TYPE2 header flags=0x%08.8lx ", ntlm->flags);
+    ntlm_print_flags(stderr, ntlm->flags);
+    fprintf(stderr, "\n                  nonce=");
+    ntlm_print_hex(stderr, (char *)ntlm->nonce, 8);
+    fprintf(stderr, "\n****\n");
+    fprintf(stderr, "**** Header %s\n ", header);
+  });
+#endif
+
+  free(buffer);
+
+  return true;
+}
+
+void Curl_ntlm_cleanup(struct ntlmdata* ntlm)
+{
+#ifdef USE_WINDOWS_SSPI
+  ntlm_sspi_cleanup(ntlm);
+#endif
+}
+
+#endif /* USE_NTLM */
diff --git a/lib/ntlm.h b/lib/ntlm.h
new file mode 100644
index 0000000..ca2a548
--- /dev/null
+++ b/lib/ntlm.h
@@ -0,0 +1,140 @@
+#ifndef __NTLM_H
+#define __NTLM_H
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2009, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+
+/* This is to generate a ntlm type-1 message */
+size_t Curl_ntlm_create_type1_message(struct SessionHandle *data, const char *userp, const char *passwdp, const char *hostp, struct ntlmdata *ntlm, char **outptr);
+
+/* This is to generate a ntlm type-3 message */
+size_t Curl_ntlm_create_type3_message(struct SessionHandle *data, const char *userp, const char *passwdp, const char *hostp, struct ntlmdata *ntlm, char **outptr);
+
+/* This is to decode a ntlm type-2 message */
+bool Curl_ntlm_decode_type2_message(const char* header, struct ntlmdata* ntlm);
+
+/* this is to clean up the ntlm sspi structure */
+void Curl_ntlm_cleanup(struct ntlmdata *ntlm);
+
+/* Flag bits definitions based on http://davenport.sourceforge.net/ntlm.html */
+
+#define NTLMFLAG_NEGOTIATE_UNICODE               (1<<0)
+/* Indicates that Unicode strings are supported for use in security buffer
+   data. */
+
+#define NTLMFLAG_NEGOTIATE_OEM                   (1<<1)
+/* Indicates that OEM strings are supported for use in security buffer data. */
+
+#define NTLMFLAG_REQUEST_TARGET                  (1<<2)
+/* Requests that the server's authentication realm be included in the Type 2
+   message. */
+
+/* unknown (1<<3) */
+#define NTLMFLAG_NEGOTIATE_SIGN                  (1<<4)
+/* Specifies that authenticated communication between the client and server
+   should carry a digital signature (message integrity). */
+
+#define NTLMFLAG_NEGOTIATE_SEAL                  (1<<5)
+/* Specifies that authenticated communication between the client and server
+   should be encrypted (message confidentiality). */
+
+#define NTLMFLAG_NEGOTIATE_DATAGRAM_STYLE        (1<<6)
+/* Indicates that datagram authentication is being used. */
+
+#define NTLMFLAG_NEGOTIATE_LM_KEY                (1<<7)
+/* Indicates that the LAN Manager session key should be used for signing and
+   sealing authenticated communications. */
+
+#define NTLMFLAG_NEGOTIATE_NETWARE               (1<<8)
+/* unknown purpose */
+
+#define NTLMFLAG_NEGOTIATE_NTLM_KEY              (1<<9)
+/* Indicates that NTLM authentication is being used. */
+
+/* unknown (1<<10) */
+
+#define NTLMFLAG_NEGOTIATE_ANONYMOUS             (1<<11)
+/* Sent by the client in the Type 3 message to indicate that an anonymous
+   context has been established. This also affects the response fields. */
+
+#define NTLMFLAG_NEGOTIATE_DOMAIN_SUPPLIED       (1<<12)
+/* Sent by the client in the Type 1 message to indicate that a desired
+   authentication realm is included in the message. */
+
+#define NTLMFLAG_NEGOTIATE_WORKSTATION_SUPPLIED  (1<<13)
+/* Sent by the client in the Type 1 message to indicate that the client
+   workstation's name is included in the message. */
+
+#define NTLMFLAG_NEGOTIATE_LOCAL_CALL            (1<<14)
+/* Sent by the server to indicate that the server and client are on the same
+   machine. Implies that the client may use a pre-established local security
+   context rather than responding to the challenge. */
+
+#define NTLMFLAG_NEGOTIATE_ALWAYS_SIGN           (1<<15)
+/* Indicates that authenticated communication between the client and server
+   should be signed with a "dummy" signature. */
+
+#define NTLMFLAG_TARGET_TYPE_DOMAIN              (1<<16)
+/* Sent by the server in the Type 2 message to indicate that the target
+   authentication realm is a domain. */
+
+#define NTLMFLAG_TARGET_TYPE_SERVER              (1<<17)
+/* Sent by the server in the Type 2 message to indicate that the target
+   authentication realm is a server. */
+
+#define NTLMFLAG_TARGET_TYPE_SHARE               (1<<18)
+/* Sent by the server in the Type 2 message to indicate that the target
+   authentication realm is a share. Presumably, this is for share-level
+   authentication. Usage is unclear. */
+
+#define NTLMFLAG_NEGOTIATE_NTLM2_KEY             (1<<19)
+/* Indicates that the NTLM2 signing and sealing scheme should be used for
+   protecting authenticated communications. */
+
+#define NTLMFLAG_REQUEST_INIT_RESPONSE           (1<<20)
+/* unknown purpose */
+
+#define NTLMFLAG_REQUEST_ACCEPT_RESPONSE         (1<<21)
+/* unknown purpose */
+
+#define NTLMFLAG_REQUEST_NONNT_SESSION_KEY       (1<<22)
+/* unknown purpose */
+
+#define NTLMFLAG_NEGOTIATE_TARGET_INFO           (1<<23)
+/* Sent by the server in the Type 2 message to indicate that it is including a
+   Target Information block in the message. */
+
+/* unknown (1<24) */
+/* unknown (1<25) */
+/* unknown (1<26) */
+/* unknown (1<27) */
+/* unknown (1<28) */
+
+#define NTLMFLAG_NEGOTIATE_128                   (1<<29)
+/* Indicates that 128-bit encryption is supported. */
+
+#define NTLMFLAG_NEGOTIATE_KEY_EXCHANGE          (1<<30)
+/* Indicates that the client will provide an encrypted master key in
+   the "Session Key" field of the Type 3 message. */
+
+#define NTLMFLAG_NEGOTIATE_56                    (1<<31)
+/* Indicates that 56-bit encryption is supported. */
+#endif
diff --git a/lib/smtp.c b/lib/smtp.c
index aab4f80..df3013b 100644
--- a/lib/smtp.c
+++ b/lib/smtp.c
@@ -70,6 +70,7 @@
 #include "http.h" /* for HTTP proxy tunnel stuff */
 #include "socks.h"
 #include "smtp.h"
+#include "ntlm.h"
 
 #include "strtoofft.h"
 #include "strequal.h"
@@ -95,6 +96,9 @@
 /* The last #include file should be: */
 #include "memdebug.h"
 
+/* Local constants */
+#define HOSTNAME_MAX 1024
+
 /* Local API functions */
 static CURLcode smtp_regular_transfer(struct connectdata *conn, bool *done);
 static CURLcode smtp_do(struct connectdata *conn, bool *done);
@@ -267,6 +271,8 @@ static int smtp_endofresp(struct pingpong *pp, int *resp)
         smtpc->authmechs |= SMTP_AUTH_GSSAPI;
       else if(wordlen == 8 && !memcmp(line, "EXTERNAL", 8))
         smtpc->authmechs |= SMTP_AUTH_EXTERNAL;
+      else if(wordlen == 4 && !memcmp(line, "NTLM", 4))
+        smtpc->authmechs |= SMTP_AUTH_NTLM;
 
       line += wordlen;
       len -= wordlen;
@@ -294,6 +300,7 @@ static void state(struct connectdata *conn,
     "AUTHLOGIN",
     "AUTHPASSWD",
     "AUTHCRAM",
+    "AUTHNTLM",
     "AUTH",
     "MAIL",
     "RCPT",
@@ -315,6 +322,7 @@ static CURLcode smtp_state_ehlo(struct connectdata *conn)
   struct smtp_conn *smtpc = &conn->proto.smtpc;
 
   smtpc->authmechs = 0;         /* No known authentication mechanisms yet. */
+  smtpc->authused = 0;          /* Clear the authentication mechanism used for esmtp connections. */
 
   /* send EHLO */
   result = Curl_pp_sendf(&smtpc->pp, "EHLO %s", smtpc->domain);
@@ -331,6 +339,8 @@ static CURLcode smtp_state_helo(struct connectdata *conn)
   CURLcode result;
   struct smtp_conn *smtpc = &conn->proto.smtpc;
 
+  smtpc->authused = 0;          /* No authentication mechanism used in smtp connections. */
+
   /* send HELO */
   result = Curl_pp_sendf(&smtpc->pp, "HELO %s", smtpc->domain);
 
@@ -376,6 +386,20 @@ static size_t smtp_auth_login_user(struct connectdata * conn, char * * outptr)
   return Curl_base64_encode(conn->data, conn->user, ulen, outptr);
 }
 
+#ifdef USE_NTLM
+
+static size_t smtp_auth_ntlm(struct connectdata *conn, char **outptr)
+{
+  char host[HOSTNAME_MAX + 1];
+
+  if(Curl_gethostname(host, HOSTNAME_MAX))
+    infof(conn->data, "gethostname() failed, continuing without!");
+
+  return Curl_ntlm_create_type1_message(conn->data, conn->user, conn->passwd, host, &conn->ntlm, outptr);
+}
+
+#endif
+
 static CURLcode smtp_authenticate(struct connectdata *conn)
 {
   CURLcode result = CURLE_OK;
@@ -398,24 +422,43 @@ static CURLcode smtp_authenticate(struct connectdata *conn)
     state1 = SMTP_STOP;
     state2 = SMTP_STOP;
 
+#ifdef USE_NTLM
+    if(smtpc->authmechs & SMTP_AUTH_NTLM) {
+      mech = "NTLM";
+      state1 = SMTP_AUTHNTLM;
+      state2 = SMTP_AUTHNTLMTYPE2;
+      smtpc->authused = SMTP_AUTH_NTLM;
+
+	  if(conn->data->set.smtp_single_auth)
+        l = smtp_auth_ntlm(conn, &initresp);
+	}
+	else
+#endif
 #ifndef CURL_DISABLE_CRYPTO_AUTH
     if(smtpc->authmechs & SMTP_AUTH_CRAM_MD5) {
       mech = "CRAM-MD5";
       state1 = SMTP_AUTHCRAM;
+      smtpc->authused = SMTP_AUTH_CRAM_MD5;
     }
     else
 #endif
-    if(smtpc->authmechs & SMTP_AUTH_PLAIN) {
-      mech = "PLAIN";
-      state1 = SMTP_AUTHPLAIN;
-      state2 = SMTP_AUTH;
-      l = smtp_auth_plain_data(conn, &initresp);
-    }
-    else if(smtpc->authmechs & SMTP_AUTH_LOGIN) {
+    if(smtpc->authmechs & SMTP_AUTH_LOGIN) {
       mech = "LOGIN";
       state1 = SMTP_AUTHLOGIN;
       state2 = SMTP_AUTHPASSWD;
-      l = smtp_auth_login_user(conn, &initresp);
+      smtpc->authused = SMTP_AUTH_LOGIN;
+
+      if(conn->data->set.smtp_single_auth)
+        l = smtp_auth_login_user(conn, &initresp);
+    }
+    else if(smtpc->authmechs & SMTP_AUTH_PLAIN) {
+      mech = "PLAIN";
+      state1 = SMTP_AUTHPLAIN;
+      state2 = SMTP_AUTH;
+      smtpc->authused = SMTP_AUTH_PLAIN;
+
+      if(conn->data->set.smtp_single_auth)
+        l = smtp_auth_plain_data(conn, &initresp);
     }
     else {
       infof(conn->data, "No known auth mechanisms supported!\n");
@@ -763,6 +806,84 @@ static CURLcode smtp_state_authcram_resp(struct connectdata *conn,
 
 #endif
 
+#ifdef USE_NTLM
+
+/* for the ntlm response. */
+static CURLcode smtp_state_auth_ntlm_resp(struct connectdata *conn,
+                                          int smtpcode,
+                                          smtpstate instate)
+{
+  CURLcode result = CURLE_OK;
+  struct SessionHandle *data = conn->data;
+  size_t l;
+  char *type1;
+
+  (void)instate; /* no use for this yet */
+
+  if(smtpcode != 334) {
+    failf(data, "Access denied: %d", smtpcode);
+    result = CURLE_LOGIN_DENIED;
+  }
+  else {
+    l = smtp_auth_ntlm(conn, &type1);
+
+    if(!l)
+      result = CURLE_OUT_OF_MEMORY;
+    else {
+      result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", type1);
+      free(type1);
+
+      if(!result)
+        state(conn, SMTP_AUTHNTLMTYPE2);
+    }
+  }
+
+  return result;
+}
+
+static CURLcode smtp_state_auth_ntlm_type2_resp(struct connectdata *conn,
+                                          int smtpcode,
+                                          smtpstate instate)
+{
+  CURLcode result = CURLE_OK;
+  struct SessionHandle *data = conn->data;
+  size_t l;
+  char *type3 = NULL;
+  char host[HOSTNAME_MAX + 1];
+
+  (void)instate; /* no use for this yet */
+
+  if(smtpcode != 334) {
+    failf(data, "Access denied: %d", smtpcode);
+    result = CURLE_LOGIN_DENIED;
+  }
+  else {
+	if(!Curl_ntlm_decode_type2_message(data->state.buffer + 4, &conn->ntlm))
+		result = CURLE_LOGIN_DENIED;
+	else
+	{
+      if(Curl_gethostname(host, HOSTNAME_MAX))
+        infof(conn->data, "gethostname() failed, continuing without!");
+      
+	  l = Curl_ntlm_create_type3_message(conn->data, conn->user, conn->passwd, host, &conn->ntlm, &type3);
+
+      if(!l)
+        result = CURLE_OUT_OF_MEMORY;
+      else {
+        result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", type3);
+        free(type3);
+
+        if(!result)
+          state(conn, SMTP_AUTH);
+      }
+    }
+  }
+
+  return result;
+}
+
+#endif 
+
 /* for final responses to AUTH sequences. */
 static CURLcode smtp_state_auth_resp(struct connectdata *conn,
                                      int smtpcode,
@@ -786,16 +907,34 @@ static CURLcode smtp_state_auth_resp(struct connectdata *conn,
 /* start the DO phase */
 static CURLcode smtp_mail(struct connectdata *conn)
 {
+  char *from;
+  char *auth;
   CURLcode result = CURLE_OK;
   struct SessionHandle *data = conn->data;
 
+  from = data->set.str[STRING_MAIL_FROM];
+
   /* send MAIL FROM */
-  if(data->set.str[STRING_MAIL_FROM][0] == '<')
-    result = Curl_pp_sendf(&conn->proto.smtpc.pp, "MAIL FROM:%s",
-                           data->set.str[STRING_MAIL_FROM]);
-  else
-    result = Curl_pp_sendf(&conn->proto.smtpc.pp, "MAIL FROM:<%s>",
-                           data->set.str[STRING_MAIL_FROM]);
+  if(conn->proto.smtpc.authused == 0) {
+    if(from[0] == '<')
+      result = Curl_pp_sendf(&conn->proto.smtpc.pp, "MAIL FROM:%s", from);
+    else
+      result = Curl_pp_sendf(&conn->proto.smtpc.pp, "MAIL FROM:<%s>", from);
+  }
+  else{
+    auth = data->set.str[STRING_MAIL_AUTH] != NULL ? data->set.str[STRING_MAIL_AUTH] :
+                                                           data->set.str[STRING_MAIL_FROM];
+
+    if(from[0] == '<' && auth[0] == '<')
+      result = Curl_pp_sendf(&conn->proto.smtpc.pp, "MAIL FROM:%s AUTH=%s", from, auth);
+    else if(from[0] != '<' && auth[0] == '<')
+      result = Curl_pp_sendf(&conn->proto.smtpc.pp, "MAIL FROM:%s AUTH=<%s>", from, auth);
+    else if(from[0] == '<' && auth[0] != '<')
+      result = Curl_pp_sendf(&conn->proto.smtpc.pp, "MAIL FROM:<%s> AUTH=%s", from, auth);
+    else
+      result = Curl_pp_sendf(&conn->proto.smtpc.pp, "MAIL FROM:<%s> AUTH=<%s>", from, auth);
+  }
+
   if(result)
     return result;
 
@@ -988,6 +1127,16 @@ static CURLcode smtp_statemach_act(struct connectdata *conn)
       break;
 #endif
 
+#ifdef USE_NTLM
+    case SMTP_AUTHNTLM:
+      result = smtp_state_auth_ntlm_resp(conn, smtpcode, smtpc->state);
+      break;
+
+	case SMTP_AUTHNTLMTYPE2:
+      result = smtp_state_auth_ntlm_type2_resp(conn, smtpcode, smtpc->state);
+      break;
+#endif
+
     case SMTP_AUTH:
       result = smtp_state_auth_resp(conn, smtpcode, smtpc->state);
       break;
@@ -1257,8 +1406,7 @@ static CURLcode smtp_done(struct connectdata *conn, CURLcode status,
  * the options previously setup.
  */
 
-static
-CURLcode smtp_perform(struct connectdata *conn,
+static CURLcode smtp_perform(struct connectdata *conn,
                      bool *connected,  /* connect status after PASV / PORT */
                      bool *dophase_done)
 {
@@ -1360,6 +1508,13 @@ static CURLcode smtp_disconnect(struct connectdata *conn, bool dead_connection)
 {
   struct smtp_conn *smtpc= &conn->proto.smtpc;
 
+  /* Cleanup the ntlm structure if necessary*/
+#ifdef USE_NTLM
+  if(smtpc->authused == SMTP_AUTH_NTLM) {
+    Curl_ntlm_cleanup(&conn->ntlm);
+  }
+#endif
+
   /* We cannot send quit unconditionally. If this connection is stale or
      bad in any way, sending quit and waiting around here will make the
      disconnect wait in vain and cause more problems than we need to.
@@ -1421,8 +1576,7 @@ static CURLcode smtp_doing(struct connectdata *conn,
  * Performs all commands done before a regular transfer between a local and a
  * remote host.
  */
-static
-CURLcode smtp_regular_transfer(struct connectdata *conn,
+static CURLcode smtp_regular_transfer(struct connectdata *conn,
                               bool *dophase_done)
 {
   CURLcode result=CURLE_OK;
diff --git a/lib/smtp.h b/lib/smtp.h
index bc4b91e..e2688d3 100644
--- a/lib/smtp.h
+++ b/lib/smtp.h
@@ -40,6 +40,8 @@ typedef enum {
   SMTP_AUTHLOGIN,
   SMTP_AUTHPASSWD,
   SMTP_AUTHCRAM,
+  SMTP_AUTHNTLM,
+  SMTP_AUTHNTLMTYPE2,
   SMTP_AUTH,
   SMTP_MAIL, /* MAIL FROM */
   SMTP_RCPT, /* RCPT TO */
@@ -57,6 +59,7 @@ struct smtp_conn {
   size_t eob;         /* number of bytes of the EOB (End Of Body) that has been
                          received thus far */
   unsigned int authmechs;       /* Accepted authentication methods. */
+  unsigned int authused;
   smtpstate state; /* always use smtp.c:state() to change state! */
   struct curl_slist *rcpt;
   bool ssldone; /* is connect() over SSL done? only relevant in multi mode */
@@ -69,6 +72,7 @@ struct smtp_conn {
 #define SMTP_AUTH_DIGEST_MD5    0x0008
 #define SMTP_AUTH_GSSAPI        0x0010
 #define SMTP_AUTH_EXTERNAL      0x0020
+#define SMTP_AUTH_NTLM			0x0040
 
 extern const struct Curl_handler Curl_handler_smtp;
 extern const struct Curl_handler Curl_handler_smtps;
diff --git a/lib/url.c b/lib/url.c
index aa1e6f8..eb82db1 100644
--- a/lib/url.c
+++ b/lib/url.c
@@ -732,6 +732,9 @@ CURLcode Curl_init_userdefined(struct UserDefined *set)
   set->chunk_bgn      = ZERO_NULL;
   set->chunk_end      = ZERO_NULL;
 
+  /* Default SMTP single auth to TRUE to maintain compatibility with the v7.21.7 source */
+  set->smtp_single_auth = TRUE;
+
   return res;
 }
 
@@ -2348,11 +2351,20 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option,
     data->set.redir_protocols = va_arg(param, long);
     break;
 
+  case CURLOPT_MAIL_SINGLE_AUTH:
+    data->set.smtp_single_auth = (bool)(0 != va_arg(param, long));
+    break;
+
   case CURLOPT_MAIL_FROM:
     result = setstropt(&data->set.str[STRING_MAIL_FROM],
                        va_arg(param, char *));
     break;
 
+  case CURLOPT_MAIL_AUTH:
+    result = setstropt(&data->set.str[STRING_MAIL_AUTH],
+                       va_arg(param, char *));
+    break;
+
   case CURLOPT_MAIL_RCPT:
     /* get a list of mail recipients */
     data->set.mail_rcpt = va_arg(param, struct curl_slist *);
@@ -2611,7 +2623,9 @@ CURLcode Curl_disconnect(struct connectdata *conn, bool dead_connection)
     if(has_host_ntlm || has_proxy_ntlm) {
       data->state.authproblem = FALSE;
 
-      Curl_ntlm_cleanup(conn);
+#ifdef USE_NTLM
+      Curl_http_ntlm_cleanup(conn);
+#endif
     }
   }
 
diff --git a/lib/urldata.h b/lib/urldata.h
index d2638aa..e8f0bd2 100644
--- a/lib/urldata.h
+++ b/lib/urldata.h
@@ -1326,6 +1326,7 @@ enum dupstring {
   STRING_SOCKS5_GSSAPI_SERVICE,  /* GSSAPI service name */
 #endif
   STRING_MAIL_FROM,
+  STRING_MAIL_AUTH,
 
 #ifdef USE_TLS_SRP
   STRING_TLSAUTH_USERNAME,     /* TLS auth <username> */
@@ -1507,6 +1508,7 @@ struct UserDefined {
   long new_directory_perms; /* Permissions to use when creating remote dirs */
   bool proxy_transfer_mode; /* set transfer mode (;type=<a|i>) when doing FTP
                                via an HTTP proxy */
+  bool smtp_single_auth;  /* True to send the initial response in the AUTH */
   char *str[STRING_LAST]; /* array of strings, pointing to allocated memory */
   unsigned int scope;    /* address scope for IPv6 */
   long allowed_protocols;
-- 
1.7.4.msysgit.0

