]> granicus.if.org Git - curl/commitdiff
smtp: Added support for NTLM authentication
authorSteve Holme <steve_holme@hotmail.com>
Sat, 1 Oct 2011 13:46:14 +0000 (14:46 +0100)
committerDaniel Stenberg <daniel@haxx.se>
Mon, 3 Oct 2011 21:28:17 +0000 (23:28 +0200)
Modified smtp_endofresp() to detect NTLM from the server specified list
of supported authentication mechanisms.

Modified smtp_authenticate() to start the sending of the NTLM data.

Added smtp_auth_ntlm_type1_message() which creates a NTLM type-1
message. This function is used by authenticate() to start the sending
of data and by smtp_state_auth_ntlm_resp() when the AUTH command
doesn't contain the type-1 message as part of the initial response.
This lack of initial response can happen if an OOM error occurs or the
type-1 message is longer than 504 characters. As the main AUTH command
is limited to 512 character the data has to be transmitted in two
parts; one containing the AUTH NTLM and the second containing the
type-1 message.

Added smtp_state_auth_ntlm_type2msg_resp() which handles the incoming
type-2 message and sends an outgoing type-3 message. This type-2
message is sent by the server in response to our type-1 message.

Modified smtp_state_auth_resp() to handle the response to: the AUTH
NTLM without the initial response and the type-2 response.

Modified smtp_disconnect() to cleanup the NTLM SSPI stack.

lib/smtp.c
lib/smtp.h

index 6d99d36dded01bd3b851e3fd079e66a3a8dd68f4..945397af85f1a81116a5a56d46cf2140712b9c55 100644 (file)
@@ -85,6 +85,7 @@
 #include "curl_md5.h"
 #include "curl_hmac.h"
 #include "curl_gethostname.h"
+#include "curl_ntlm_msgs.h"
 #include "warnless.h"
 #include "http_proxy.h"
 
@@ -263,6 +264,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;
@@ -290,6 +293,8 @@ static void state(struct connectdata *conn,
     "AUTHLOGIN",
     "AUTHPASSWD",
     "AUTHCRAM",
+    "AUTHNTLM",
+    "AUTHNTLM_TYPE2MSG",
     "AUTH",
     "MAIL",
     "RCPT",
@@ -311,6 +316,8 @@ 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);
@@ -327,6 +334,9 @@ 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);
 
@@ -380,6 +390,15 @@ static CURLcode smtp_auth_login_user(struct connectdata *conn,
   return Curl_base64_encode(conn->data, conn->user, ulen, outptr, outlen);
 }
 
+#ifdef USE_NTLM
+static CURLcode smtp_auth_ntlm_type1_message(struct connectdata *conn,
+                                             char **outptr, size_t *outlen)
+{
+  return Curl_ntlm_create_type1_message(conn->user, conn->passwd,
+                                        &conn->ntlm, outptr, outlen);
+}
+#endif
+
 static CURLcode smtp_authenticate(struct connectdata *conn)
 {
   CURLcode result = CURLE_OK;
@@ -404,6 +423,17 @@ static CURLcode smtp_authenticate(struct connectdata *conn)
   if(smtpc->authmechs & SMTP_AUTH_CRAM_MD5) {
     mech = "CRAM-MD5";
     state1 = SMTP_AUTHCRAM;
+    smtpc->authused = SMTP_AUTH_CRAM_MD5;
+  }
+  else
+#endif
+#ifdef USE_NTLM
+  if(smtpc->authmechs & SMTP_AUTH_NTLM) {
+    mech = "NTLM";
+    state1 = SMTP_AUTHNTLM;
+    state2 = SMTP_AUTHNTLM_TYPE2MSG;
+    smtpc->authused = SMTP_AUTH_NTLM;
+    result = smtp_auth_ntlm_type1_message(conn, &initresp, &len);
   }
   else
 #endif
@@ -411,12 +441,14 @@ static CURLcode smtp_authenticate(struct connectdata *conn)
     mech = "LOGIN";
     state1 = SMTP_AUTHLOGIN;
     state2 = SMTP_AUTHPASSWD;
+    smtpc->authused = SMTP_AUTH_LOGIN;
     result = smtp_auth_login_user(conn, &initresp, &len);
   }
   else if(smtpc->authmechs & SMTP_AUTH_PLAIN) {
     mech = "PLAIN";
     state1 = SMTP_AUTHPLAIN;
     state2 = SMTP_AUTH;
+    smtpc->authused = SMTP_AUTH_PLAIN;
     result = smtp_auth_plain_data(conn, &initresp, &len);
   }
   else {
@@ -759,6 +791,78 @@ static CURLcode smtp_state_authcram_resp(struct connectdata *conn,
 
 #endif
 
+#ifdef USE_NTLM
+/* for the AUTH NTLM (without initial response) response. */
+static CURLcode smtp_state_auth_ntlm_resp(struct connectdata *conn,
+                                          int smtpcode,
+                                          smtpstate instate)
+{
+  CURLcode result = CURLE_OK;
+  struct SessionHandle *data = conn->data;
+  char *type1msg = NULL;
+  size_t len = 0;
+
+  (void)instate; /* no use for this yet */
+
+  if(smtpcode != 334) {
+    failf(data, "Access denied: %d", smtpcode);
+    result = CURLE_LOGIN_DENIED;
+  }
+  else {
+    result = smtp_auth_ntlm_type1_message(conn, &type1msg, &len);
+
+    if(!result) {
+      if(type1msg) {
+        result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", type1msg);
+
+        if(!result)
+          state(conn, SMTP_AUTHNTLM_TYPE2MSG);
+      }
+      Curl_safefree(type1msg);
+    }
+  }
+
+  return result;
+}
+
+/* for the NTLM type-2 response (sent in reponse to our type-1 message). */
+static CURLcode smtp_state_auth_ntlm_type2msg_resp(struct connectdata *conn,
+                                                   int smtpcode,
+                                                   smtpstate instate)
+{
+  CURLcode result = CURLE_OK;
+  struct SessionHandle *data = conn->data;
+  char *type3msg = NULL;
+  size_t len = 0;
+
+  (void)instate; /* no use for this yet */
+
+  if(smtpcode != 334) {
+    failf(data, "Access denied: %d", smtpcode);
+    result = CURLE_LOGIN_DENIED;
+  }
+  else {
+         result = Curl_ntlm_decode_type2_message(data, data->state.buffer + 4, &conn->ntlm);
+
+       if(!result) {
+           result = Curl_ntlm_create_type3_message(conn->data, conn->user, conn->passwd, &conn->ntlm, &type3msg, &len);
+
+      if(!result) {
+        if(type3msg) {
+          result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", type3msg);
+
+          if(!result)
+            state(conn, SMTP_AUTH);
+        }
+        Curl_safefree(type3msg);
+      }
+    }
+  }
+
+  return result;
+}
+#endif
+
 /* for final responses to AUTH sequences. */
 static CURLcode smtp_state_auth_resp(struct connectdata *conn,
                                      int smtpcode,
@@ -1016,6 +1120,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_AUTHNTLM_TYPE2MSG:
+      result = smtp_state_auth_ntlm_type2msg_resp(conn, smtpcode, smtpc->state);
+      break;
+#endif
+
     case SMTP_AUTH:
       result = smtp_state_auth_resp(conn, smtpcode, smtpc->state);
       break;
@@ -1402,6 +1516,13 @@ static CURLcode smtp_disconnect(struct connectdata *conn,
 
   Curl_pp_disconnect(&smtpc->pp);
 
+#ifdef USE_NTLM
+  /* Cleanup the ntlm structure */
+  if(smtpc->authused == SMTP_AUTH_NTLM) {
+    Curl_ntlm_sspi_cleanup(&conn->ntlm);
+  }
+#endif
+
   /* This won't already be freed in some error cases */
   Curl_safefree(smtpc->domain);
   smtpc->domain = NULL;
index bc4b91eaa05c9a4abec2d4be9f01a1d9b9458324..144d496157dfafafb299094309fe0c29a9e5eeaf 100644 (file)
@@ -40,6 +40,8 @@ typedef enum {
   SMTP_AUTHLOGIN,
   SMTP_AUTHPASSWD,
   SMTP_AUTHCRAM,
+  SMTP_AUTHNTLM,
+  SMTP_AUTHNTLM_TYPE2MSG,
   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;  /* Authentication method used for the connection */
   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;