From 4d327d20c62d856c45f8ecf84d16fd7804ace0f1 Mon Sep 17 00:00:00 2001 From: Steve Holme Date: Sat, 1 Oct 2011 14:46:14 +0100 Subject: [PATCH] smtp: Added support for NTLM authentication 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 | 121 +++++++++++++++++++++++++++++++++++++++++++++++++++++ lib/smtp.h | 4 ++ 2 files changed, 125 insertions(+) diff --git a/lib/smtp.c b/lib/smtp.c index 6d99d36dd..945397af8 100644 --- a/lib/smtp.c +++ b/lib/smtp.c @@ -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; diff --git a/lib/smtp.h b/lib/smtp.h index bc4b91eaa..144d49615 100644 --- a/lib/smtp.h +++ b/lib/smtp.h @@ -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;