* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2013, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2014, 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
#include "strtoofft.h"
#include "strequal.h"
-#include "sslgen.h"
+#include "vtls/vtls.h"
#include "connect.h"
#include "strerror.h"
#include "select.h"
static CURLcode smtp_parse_url_options(struct connectdata *conn);
static CURLcode smtp_parse_url_path(struct connectdata *conn);
static CURLcode smtp_parse_custom_request(struct connectdata *conn);
+static CURLcode smtp_calc_sasl_details(struct connectdata *conn,
+ const char **mech,
+ char **initresp, size_t *len,
+ smtpstate *state1, smtpstate *state2);
/*
* SMTP protocol handler.
smtp_disconnect, /* disconnect */
ZERO_NULL, /* readwrite */
PORT_SMTPS, /* defport */
- CURLPROTO_SMTP | CURLPROTO_SMTPS, /* protocol */
+ CURLPROTO_SMTPS, /* protocol */
PROTOPT_CLOSEACTION | PROTOPT_SSL
| PROTOPT_NOURLQUERY /* flags */
};
CURLcode result = CURLE_OK;
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 */
- smtpc->tls_supported = FALSE; /* Clear the TLS capability */
+ smtpc->authmechs = 0; /* No known authentication mechanisms yet */
+ smtpc->authused = 0; /* Clear the authentication mechanism used
+ for esmtp connections */
+ smtpc->tls_supported = FALSE; /* Clear the TLS capability */
+ smtpc->auth_supported = FALSE; /* Clear the AUTH capability */
/* Send the EHLO command */
result = Curl_pp_sendf(&smtpc->pp, "EHLO %s", smtpc->domain);
/***********************************************************************
*
- * smtp_perform_authenticate()
+ * smtp_perform_auth()
*
- * Sends an AUTH command allowing the client to login with the appropriate
- * SASL authentication mechanism.
+ * Sends an AUTH command allowing the client to login with the given SASL
+ * authentication mechanism.
*/
-static CURLcode smtp_perform_authenticate(struct connectdata *conn)
+static CURLcode smtp_perform_auth(struct connectdata *conn,
+ const char *mech,
+ const char *initresp, size_t len,
+ smtpstate state1, smtpstate state2)
+{
+ CURLcode result = CURLE_OK;
+ struct smtp_conn *smtpc = &conn->proto.smtpc;
+
+ if(initresp && 8 + strlen(mech) + len <= 512) { /* AUTH <mech> ...<crlf> */
+ /* Send the AUTH command with the initial response */
+ result = Curl_pp_sendf(&smtpc->pp, "AUTH %s %s", mech, initresp);
+
+ if(!result)
+ state(conn, state2);
+ }
+ else {
+ /* Send the AUTH command */
+ result = Curl_pp_sendf(&smtpc->pp, "AUTH %s", mech);
+
+ if(!result)
+ state(conn, state1);
+ }
+
+ return result;
+}
+
+/***********************************************************************
+ *
+ * smtp_perform_authentication()
+ *
+ * Initiates the authentication sequence, with the appropriate SASL
+ * authentication mechanism.
+ */
+static CURLcode smtp_perform_authentication(struct connectdata *conn)
{
CURLcode result = CURLE_OK;
- struct SessionHandle *data = conn->data;
struct smtp_conn *smtpc = &conn->proto.smtpc;
const char *mech = NULL;
char *initresp = NULL;
smtpstate state1 = SMTP_STOP;
smtpstate state2 = SMTP_STOP;
- /* Check we have a username and password to authenticate with and end the
- connect phase if we don't */
- if(!conn->bits.user_passwd) {
+ /* Check we have a username and password to authenticate with, and the
+ server supports authentiation, and end the connect phase if not */
+ if(!conn->bits.user_passwd || !smtpc->auth_supported) {
state(conn, SMTP_STOP);
return result;
}
- /* Calculate the supported authentication mechanism, by decreasing order of
- security, as well as the initial response where appropriate */
-#ifndef CURL_DISABLE_CRYPTO_AUTH
- if((smtpc->authmechs & SASL_MECH_DIGEST_MD5) &&
- (smtpc->prefmech & SASL_MECH_DIGEST_MD5)) {
- mech = SASL_MECH_STRING_DIGEST_MD5;
- state1 = SMTP_AUTH_DIGESTMD5;
- smtpc->authused = SASL_MECH_DIGEST_MD5;
- }
- else if((smtpc->authmechs & SASL_MECH_CRAM_MD5) &&
- (smtpc->prefmech & SASL_MECH_CRAM_MD5)) {
- mech = SASL_MECH_STRING_CRAM_MD5;
- state1 = SMTP_AUTH_CRAMMD5;
- smtpc->authused = SASL_MECH_CRAM_MD5;
- }
- else
-#endif
-#ifdef USE_NTLM
- if((smtpc->authmechs & SASL_MECH_NTLM) &&
- (smtpc->prefmech & SASL_MECH_NTLM)) {
- mech = SASL_MECH_STRING_NTLM;
- state1 = SMTP_AUTH_NTLM;
- state2 = SMTP_AUTH_NTLM_TYPE2MSG;
- smtpc->authused = SASL_MECH_NTLM;
-
- if(data->set.sasl_ir)
- result = Curl_sasl_create_ntlm_type1_message(conn->user, conn->passwd,
- &conn->ntlm,
- &initresp, &len);
- }
- else
-#endif
- if(((smtpc->authmechs & SASL_MECH_XOAUTH2) &&
- (smtpc->prefmech & SASL_MECH_XOAUTH2) &&
- (smtpc->prefmech != SASL_AUTH_ANY)) || conn->xoauth2_bearer) {
- mech = SASL_MECH_STRING_XOAUTH2;
- state1 = SMTP_AUTH_XOAUTH2;
- state2 = SMTP_AUTH_FINAL;
- smtpc->authused = SASL_MECH_XOAUTH2;
-
- if(data->set.sasl_ir)
- result = Curl_sasl_create_xoauth2_message(conn->data, conn->user,
- conn->xoauth2_bearer,
- &initresp, &len);
- }
- else if((smtpc->authmechs & SASL_MECH_LOGIN) &&
- (smtpc->prefmech & SASL_MECH_LOGIN)) {
- mech = SASL_MECH_STRING_LOGIN;
- state1 = SMTP_AUTH_LOGIN;
- state2 = SMTP_AUTH_LOGIN_PASSWD;
- smtpc->authused = SASL_MECH_LOGIN;
-
- if(data->set.sasl_ir)
- result = Curl_sasl_create_login_message(conn->data, conn->user,
- &initresp, &len);
- }
- else if((smtpc->authmechs & SASL_MECH_PLAIN) &&
- (smtpc->prefmech & SASL_MECH_PLAIN)) {
- mech = SASL_MECH_STRING_PLAIN;
- state1 = SMTP_AUTH_PLAIN;
- state2 = SMTP_AUTH_FINAL;
- smtpc->authused = SASL_MECH_PLAIN;
-
- if(data->set.sasl_ir)
- result = Curl_sasl_create_plain_message(conn->data, conn->user,
- conn->passwd, &initresp, &len);
- }
+ /* Calculate the SASL login details */
+ result = smtp_calc_sasl_details(conn, &mech, &initresp, &len, &state1,
+ &state2);
if(!result) {
if(mech) {
/* Perform SASL based authentication */
- if(initresp &&
- 8 + strlen(mech) + len <= 512) { /* AUTH <mech> ...<crlf> */
- result = Curl_pp_sendf(&smtpc->pp, "AUTH %s %s", mech, initresp);
-
- if(!result)
- state(conn, state2);
- }
- else {
- result = Curl_pp_sendf(&smtpc->pp, "AUTH %s", mech);
-
- if(!result)
- state(conn, state1);
- }
+ result = smtp_perform_auth(conn, mech, initresp, len, state1, state2);
Curl_safefree(initresp);
}
struct SessionHandle *data = conn->data;
struct SMTP *smtp = data->req.protop;
- if(smtp->custom && smtp->custom[0] != '\0')
- /* Send the custom command */
- result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s %s", smtp->custom,
- smtp->rcpt ? smtp->rcpt->data : "");
- else if(smtp->rcpt)
- /* Send the VRFY command */
- result = Curl_pp_sendf(&conn->proto.smtpc.pp, "VRFY %s", smtp->rcpt->data);
+ /* Send the command */
+ if(smtp->rcpt)
+ result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s %s",
+ smtp->custom && smtp->custom[0] != '\0' ?
+ smtp->custom : "VRFY",
+ smtp->rcpt->data);
else
- /* Send the HELP command */
- result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", "HELP");
+ result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s",
+ smtp->custom && smtp->custom[0] != '\0' ?
+ smtp->custom : "HELP");
if(!result)
state(conn, SMTP_COMMAND);
}
/* Calculate the optional SIZE parameter */
- if(conn->proto.smtpc.size_supported && conn->data->set.infilesize > 0) {
- size = aprintf("%" FORMAT_OFF_T, data->set.infilesize);
+ if(conn->proto.smtpc.size_supported && conn->data->state.infilesize > 0) {
+ size = aprintf("%" CURL_FORMAT_CURL_OFF_T, data->state.infilesize);
if(!size) {
Curl_safefree(from);
result = CURLE_USE_SSL_FAILED;
}
else
- result = smtp_perform_authenticate(conn);
+ result = smtp_perform_authentication(conn);
}
else
result = smtp_perform_upgrade_tls(conn);
(void)instate; /* no use for this yet */
if(smtpcode/100 != 2 && smtpcode != 1) {
- if((data->set.use_ssl <= CURLUSESSL_TRY || conn->ssl[FIRSTSOCKET].use) &&
- !conn->bits.user_passwd)
+ if(data->set.use_ssl <= CURLUSESSL_TRY || conn->ssl[FIRSTSOCKET].use)
result = smtp_perform_helo(conn);
else {
failf(data, "Remote access denied: %d", smtpcode);
else if(len >= 4 && !memcmp(line, "SIZE", 4))
smtpc->size_supported = TRUE;
- /* Do we have the authentication mechanism list? */
+ /* Does the server support authentication? */
else if(len >= 5 && !memcmp(line, "AUTH ", 5)) {
+ smtpc->auth_supported = TRUE;
+
+ /* Advance past the AUTH keyword */
line += 5;
len -= 5;
result = smtp_perform_starttls(conn);
else if(data->set.use_ssl == CURLUSESSL_TRY)
/* Fallback and carry on with authentication */
- result = smtp_perform_authenticate(conn);
+ result = smtp_perform_authentication(conn);
else {
failf(data, "STARTTLS not supported.");
result = CURLE_USE_SSL_FAILED;
}
}
else
- result = smtp_perform_authenticate(conn);
+ result = smtp_perform_authentication(conn);
}
}
char *rplyb64 = NULL;
size_t len = 0;
- char nonce[64];
- char realm[128];
- char algorithm[64];
-
(void)instate; /* no use for this yet */
if(smtpcode != 334) {
/* Get the challenge message */
smtp_get_message(data->state.buffer, &chlg64);
- /* Decode the challange message */
- result = Curl_sasl_decode_digest_md5_message(chlg64, nonce, sizeof(nonce),
- realm, sizeof(realm),
- algorithm, sizeof(algorithm));
- if(result || strcmp(algorithm, "md5-sess") != 0) {
- /* Send the cancellation */
- result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", "*");
+ /* Create the response message */
+ result = Curl_sasl_create_digest_md5_message(data, chlg64,
+ conn->user, conn->passwd,
+ "smtp", &rplyb64, &len);
+ if(result) {
+ if(result == CURLE_BAD_CONTENT_ENCODING) {
+ /* Send the cancellation */
+ result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", "*");
- if(!result)
- state(conn, SMTP_AUTH_CANCEL);
+ if(!result)
+ state(conn, SMTP_AUTH_CANCEL);
+ }
}
else {
- /* Create the response message */
- result = Curl_sasl_create_digest_md5_message(data, nonce, realm,
- conn->user, conn->passwd,
- "smtp", &rplyb64, &len);
- if(!result && rplyb64) {
- /* Send the response */
- result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", rplyb64);
+ /* Send the response */
+ result = Curl_pp_sendf(&conn->proto.smtpc.pp, "%s", rplyb64);
- if(!result)
- state(conn, SMTP_AUTH_DIGESTMD5_RESP);
- }
+ if(!result)
+ state(conn, SMTP_AUTH_DIGESTMD5_RESP);
}
Curl_safefree(rplyb64);
int smtpcode,
smtpstate instate)
{
+ CURLcode result = CURLE_OK;
struct SessionHandle *data = conn->data;
+ struct smtp_conn *smtpc = &conn->proto.smtpc;
+ const char *mech = NULL;
+ char *initresp = NULL;
+ size_t len = 0;
+ smtpstate state1 = SMTP_STOP;
+ smtpstate state2 = SMTP_STOP;
(void)smtpcode;
(void)instate; /* no use for this yet */
- failf(data, "Authentication cancelled");
+ /* Remove the offending mechanism from the supported list */
+ smtpc->authmechs ^= smtpc->authused;
+
+ /* Calculate alternative SASL login details */
+ result = smtp_calc_sasl_details(conn, &mech, &initresp, &len, &state1,
+ &state2);
+
+ if(!result) {
+ /* Do we have any mechanisms left? */
+ if(mech) {
+ /* Retry SASL based authentication */
+ result = smtp_perform_auth(conn, mech, initresp, len, state1, state2);
+
+ Curl_safefree(initresp);
+ }
+ else {
+ failf(data, "Authentication cancelled");
+
+ result = CURLE_LOGIN_DENIED;
+ }
+ }
- return CURLE_LOGIN_DENIED;
+ return result;
}
/* For final responses in the AUTH sequence */
if(smtpcode/100 != 2) {
failf(data, "MAIL failed: %d", smtpcode);
result = CURLE_SEND_ERROR;
- state(conn, SMTP_STOP);
}
else
/* Start the RCPT TO command */
if(smtpcode/100 != 2) {
failf(data, "RCPT failed: %d", smtpcode);
result = CURLE_SEND_ERROR;
- state(conn, SMTP_STOP);
}
else {
smtp->rcpt = smtp->rcpt->next;
static CURLcode smtp_state_data_resp(struct connectdata *conn, int smtpcode,
smtpstate instate)
{
+ CURLcode result = CURLE_OK;
struct SessionHandle *data = conn->data;
(void)instate; /* no use for this yet */
if(smtpcode != 354) {
- state(conn, SMTP_STOP);
- return CURLE_SEND_ERROR;
+ failf(data, "DATA failed: %d", smtpcode);
+ result = CURLE_SEND_ERROR;
}
+ else {
+ /* Set the progress upload size */
+ Curl_pgrsSetUploadSize(data, data->state.infilesize);
- /* Set the progress upload size */
- Curl_pgrsSetUploadSize(data, data->set.infilesize);
-
- /* SMTP upload */
- Curl_setup_transfer(conn, -1, -1, FALSE, NULL, FIRSTSOCKET, NULL);
+ /* SMTP upload */
+ Curl_setup_transfer(conn, -1, -1, FALSE, NULL, FIRSTSOCKET, NULL);
- /* End of DO phase */
- state(conn, SMTP_STOP);
+ /* End of DO phase */
+ state(conn, SMTP_STOP);
+ }
- return CURLE_OK;
+ return result;
}
/* For POSTDATA responses, which are received after the entire DATA
*done = FALSE; /* default to not done yet */
/* We always support persistent connections in SMTP */
- conn->bits.close = FALSE;
+ connkeep(conn, "SMTP default");
/* Set the default response time-out */
pp->response_time = RESP_TIMEOUT;
return CURLE_OK;
if(status) {
- conn->bits.close = TRUE; /* marked for closure */
+ connclose(conn, "SMTP done with bad status"); /* marked for closure */
result = status; /* use the already set error code */
}
else if(!data->set.connect_only && data->set.upload && data->set.mail_rcpt) {
is "no mail data". RFC-5321, sect. 4.1.1.4. */
eob = SMTP_EOB;
len = SMTP_EOB_LEN;
- if(smtp->trailing_crlf || !conn->data->set.infilesize) {
+ if(smtp->trailing_crlf || !conn->data->state.infilesize) {
eob += 2;
len -= 2;
}
struct smtp_conn *smtpc = &conn->proto.smtpc;
const char *options = conn->options;
const char *ptr = options;
+ bool reset = TRUE;
- if(options) {
+ while(ptr && *ptr) {
const char *key = ptr;
while(*ptr && *ptr != '=')
ptr++;
if(strnequal(key, "AUTH", 4)) {
- const char *value = ptr + 1;
+ size_t len = 0;
+ const char *value = ++ptr;
- if(strequal(value, "*"))
- smtpc->prefmech = SASL_AUTH_ANY;
- else if(strequal(value, SASL_MECH_STRING_LOGIN))
- smtpc->prefmech = SASL_MECH_LOGIN;
- else if(strequal(value, SASL_MECH_STRING_PLAIN))
- smtpc->prefmech = SASL_MECH_PLAIN;
- else if(strequal(value, SASL_MECH_STRING_CRAM_MD5))
- smtpc->prefmech = SASL_MECH_CRAM_MD5;
- else if(strequal(value, SASL_MECH_STRING_DIGEST_MD5))
- smtpc->prefmech = SASL_MECH_DIGEST_MD5;
- else if(strequal(value, SASL_MECH_STRING_GSSAPI))
- smtpc->prefmech = SASL_MECH_GSSAPI;
- else if(strequal(value, SASL_MECH_STRING_NTLM))
- smtpc->prefmech = SASL_MECH_NTLM;
- else if(strequal(value, SASL_MECH_STRING_XOAUTH2))
- smtpc->prefmech = SASL_MECH_XOAUTH2;
- else
+ if(reset) {
+ reset = FALSE;
smtpc->prefmech = SASL_AUTH_NONE;
+ }
+
+ while(*ptr && *ptr != ';') {
+ ptr++;
+ len++;
+ }
+
+ if(strnequal(value, "*", len))
+ smtpc->prefmech = SASL_AUTH_ANY;
+ else if(strnequal(value, SASL_MECH_STRING_LOGIN, len))
+ smtpc->prefmech |= SASL_MECH_LOGIN;
+ else if(strnequal(value, SASL_MECH_STRING_PLAIN, len))
+ smtpc->prefmech |= SASL_MECH_PLAIN;
+ else if(strnequal(value, SASL_MECH_STRING_CRAM_MD5, len))
+ smtpc->prefmech |= SASL_MECH_CRAM_MD5;
+ else if(strnequal(value, SASL_MECH_STRING_DIGEST_MD5, len))
+ smtpc->prefmech |= SASL_MECH_DIGEST_MD5;
+ else if(strnequal(value, SASL_MECH_STRING_GSSAPI, len))
+ smtpc->prefmech |= SASL_MECH_GSSAPI;
+ else if(strnequal(value, SASL_MECH_STRING_NTLM, len))
+ smtpc->prefmech |= SASL_MECH_NTLM;
+ else if(strnequal(value, SASL_MECH_STRING_XOAUTH2, len))
+ smtpc->prefmech |= SASL_MECH_XOAUTH2;
+
+ if(*ptr == ';')
+ ptr++;
}
else
result = CURLE_URL_MALFORMAT;
return result;
}
+/***********************************************************************
+ *
+ * smtp_calc_sasl_details()
+ *
+ * Calculate the required login details for SASL authentication.
+ */
+static CURLcode smtp_calc_sasl_details(struct connectdata *conn,
+ const char **mech,
+ char **initresp, size_t *len,
+ smtpstate *state1, smtpstate *state2)
+{
+ CURLcode result = CURLE_OK;
+ struct SessionHandle *data = conn->data;
+ struct smtp_conn *smtpc = &conn->proto.smtpc;
+
+ /* Calculate the supported authentication mechanism, by decreasing order of
+ security, as well as the initial response where appropriate */
+#ifndef CURL_DISABLE_CRYPTO_AUTH
+ if((smtpc->authmechs & SASL_MECH_DIGEST_MD5) &&
+ (smtpc->prefmech & SASL_MECH_DIGEST_MD5)) {
+ *mech = SASL_MECH_STRING_DIGEST_MD5;
+ *state1 = SMTP_AUTH_DIGESTMD5;
+ smtpc->authused = SASL_MECH_DIGEST_MD5;
+ }
+ else if((smtpc->authmechs & SASL_MECH_CRAM_MD5) &&
+ (smtpc->prefmech & SASL_MECH_CRAM_MD5)) {
+ *mech = SASL_MECH_STRING_CRAM_MD5;
+ *state1 = SMTP_AUTH_CRAMMD5;
+ smtpc->authused = SASL_MECH_CRAM_MD5;
+ }
+ else
+#endif
+#ifdef USE_NTLM
+ if((smtpc->authmechs & SASL_MECH_NTLM) &&
+ (smtpc->prefmech & SASL_MECH_NTLM)) {
+ *mech = SASL_MECH_STRING_NTLM;
+ *state1 = SMTP_AUTH_NTLM;
+ *state2 = SMTP_AUTH_NTLM_TYPE2MSG;
+ smtpc->authused = SASL_MECH_NTLM;
+
+ if(data->set.sasl_ir)
+ result = Curl_sasl_create_ntlm_type1_message(conn->user, conn->passwd,
+ &conn->ntlm,
+ initresp, len);
+ }
+ else
+#endif
+ if(((smtpc->authmechs & SASL_MECH_XOAUTH2) &&
+ (smtpc->prefmech & SASL_MECH_XOAUTH2) &&
+ (smtpc->prefmech != SASL_AUTH_ANY)) || conn->xoauth2_bearer) {
+ *mech = SASL_MECH_STRING_XOAUTH2;
+ *state1 = SMTP_AUTH_XOAUTH2;
+ *state2 = SMTP_AUTH_FINAL;
+ smtpc->authused = SASL_MECH_XOAUTH2;
+
+ if(data->set.sasl_ir)
+ result = Curl_sasl_create_xoauth2_message(data, conn->user,
+ conn->xoauth2_bearer,
+ initresp, len);
+ }
+ else if((smtpc->authmechs & SASL_MECH_LOGIN) &&
+ (smtpc->prefmech & SASL_MECH_LOGIN)) {
+ *mech = SASL_MECH_STRING_LOGIN;
+ *state1 = SMTP_AUTH_LOGIN;
+ *state2 = SMTP_AUTH_LOGIN_PASSWD;
+ smtpc->authused = SASL_MECH_LOGIN;
+
+ if(data->set.sasl_ir)
+ result = Curl_sasl_create_login_message(data, conn->user, initresp, len);
+ }
+ else if((smtpc->authmechs & SASL_MECH_PLAIN) &&
+ (smtpc->prefmech & SASL_MECH_PLAIN)) {
+ *mech = SASL_MECH_STRING_PLAIN;
+ *state1 = SMTP_AUTH_PLAIN;
+ *state2 = SMTP_AUTH_FINAL;
+ smtpc->authused = SASL_MECH_PLAIN;
+
+ if(data->set.sasl_ir)
+ result = Curl_sasl_create_plain_message(data, conn->user, conn->passwd,
+ initresp, len);
+ }
+
+ return result;
+}
+
CURLcode Curl_smtp_escape_eob(struct connectdata *conn, ssize_t nread)
{
/* When sending a SMTP payload we must detect CRLF. sequences making sure