From 338553eda38a7ed163f935e28461eeda6dde294f Mon Sep 17 00:00:00 2001 From: Patrick Monnerat Date: Mon, 22 Feb 2010 12:41:02 +0000 Subject: - Proper handling of STARTTLS on SMTP, taking CURLUSESSL_TRY into account. - SMTP falls back to RFC821 HELO when EHLO fails (and SSL is not required). - Use of true local host name (i.e.: via gethostname()) when available, as default argument to SMTP HELO/EHLO. - Test case 804 for HELO fallback. --- CHANGES | 7 ++++ docs/RESOURCES | 3 ++ lib/smtp.c | 91 +++++++++++++++++++++++++++++++++++++++++--------- lib/smtp.h | 1 + tests/data/DISABLED | 1 + tests/data/Makefile.am | 2 +- tests/data/test804 | 59 ++++++++++++++++++++++++++++++++ 7 files changed, 147 insertions(+), 17 deletions(-) create mode 100644 tests/data/test804 diff --git a/CHANGES b/CHANGES index 99c93dc9e..79f0c6ed4 100644 --- a/CHANGES +++ b/CHANGES @@ -6,6 +6,13 @@ Changelog +Patrick Monnerat (22 Feb 2010) +- Proper handling of STARTTLS on SMTP, taking CURLUSESSL_TRY into account. +- SMTP falls back to RFC821 HELO when EHLO fails (and SSL is not required). +- Use of true local host name (i.e.: via gethostname()) when available, as + default argument to SMTP HELO/EHLO. +- Test case 804 for HELO fallback. + Daniel Stenberg (20 Feb 2010) - Fixed the SMTP compliance by making sure RCPT TO addresses are specified properly in angle brackets. Recipients provided with CURLOPT_MAIL_RCPT now diff --git a/docs/RESOURCES b/docs/RESOURCES index b8bfc2fbd..4d6465d6e 100644 --- a/docs/RESOURCES +++ b/docs/RESOURCES @@ -66,7 +66,10 @@ This document lists documents and standards used by curl. RFC 2818 - HTTP Over TLS (TLS is the successor to SSL) + RFC 2821 - SMTP protocol + RFC 2964 - Use of HTTP State Management RFC 2965 - HTTP State Management Mechanism. Cookies. Obsoletes RFC2109 + RFC 3207 - SMTP over TLS diff --git a/lib/smtp.c b/lib/smtp.c index 238fc2dbb..45df3042e 100644 --- a/lib/smtp.c +++ b/lib/smtp.c @@ -19,6 +19,7 @@ * KIND, either express or implied. * * RFC2821 SMTP protocol + * RFC3207 SMTP over TLS * * $Id$ ***************************************************************************/ @@ -228,6 +229,7 @@ static void state(struct connectdata *conn, "STOP", "SERVERGREET", "EHLO", + "HELO", "STARTTLS", "MAIL", "RCPT", @@ -253,11 +255,26 @@ static CURLcode smtp_state_ehlo(struct connectdata *conn) /* send EHLO */ result = Curl_pp_sendf(&conn->proto.smtpc.pp, "EHLO %s", smtpc->domain); + if(result) return result; state(conn, SMTP_EHLO); + return CURLE_OK; +} + +static CURLcode smtp_state_helo(struct connectdata *conn) +{ + CURLcode result; + struct smtp_conn *smtpc = &conn->proto.smtpc; + + /* send HELO */ + result = Curl_pp_sendf(&conn->proto.smtpc.pp, "HELO %s", smtpc->domain); + + if(result) + return result; + state(conn, SMTP_HELO); return CURLE_OK; } @@ -278,9 +295,13 @@ static CURLcode smtp_state_starttls_resp(struct connectdata *conn, struct SessionHandle *data = conn->data; (void)instate; /* no use for this yet */ - if(smtpcode != 'O') { - failf(data, "STARTTLS denied. %c", smtpcode); - result = CURLE_LOGIN_DENIED; + if(smtpcode != 220) { + if(data->set.ftp_ssl == CURLUSESSL_TRY) + state(conn, SMTP_STOP); + else { + failf(data, "STARTTLS denied. %c", smtpcode); + result = CURLE_LOGIN_DENIED; + } } else { /* Curl_ssl_connect is BLOCKING */ @@ -290,7 +311,6 @@ static CURLcode smtp_state_starttls_resp(struct connectdata *conn, result = smtp_state_ehlo(conn); } } - state(conn, SMTP_STOP); return result; } @@ -304,13 +324,45 @@ static CURLcode smtp_state_ehlo_resp(struct connectdata *conn, (void)instate; /* no use for this yet */ + if(smtpcode/100 != 2) { + if(data->set.ftp_ssl > CURLUSESSL_TRY && !conn->ssl[FIRSTSOCKET].use) + result = smtp_state_helo(conn); + else { + failf(data, "Access denied: %d", smtpcode); + result = CURLE_LOGIN_DENIED; + } + } + else if(data->set.ftp_ssl && !conn->ssl[FIRSTSOCKET].use) { + /* We don't have a SSL/TLS connection yet, but SSL is requested. Switch + to TLS connection now */ + result = Curl_pp_sendf(&conn->proto.smtpc.pp, "STARTTLS", NULL); + state(conn, SMTP_STARTTLS); + } + else { + /* end the connect phase */ + state(conn, SMTP_STOP); + } + return result; +} + +/* for HELO responses */ +static CURLcode smtp_state_helo_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/100 != 2) { failf(data, "Access denied: %d", smtpcode); result = CURLE_LOGIN_DENIED; + } + else { + /* end the connect phase */ + state(conn, SMTP_STOP); } - - /* end the connect phase */ - state(conn, SMTP_STOP); return result; } @@ -478,14 +530,7 @@ static CURLcode smtp_statemach_act(struct connectdata *conn) return CURLE_FTP_WEIRD_SERVER_REPLY; } - if(data->set.ftp_ssl && !conn->ssl[FIRSTSOCKET].use) { - /* We don't have a SSL/TLS connection yet, but SSL is requested. Switch - to TLS connection now */ - result = Curl_pp_sendf(&smtpc->pp, "STARTTLS", NULL); - state(conn, SMTP_STARTTLS); - } - else - result = smtp_state_ehlo(conn); + result = smtp_state_ehlo(conn); if(result) return result; break; @@ -494,6 +539,10 @@ static CURLcode smtp_statemach_act(struct connectdata *conn) result = smtp_state_ehlo_resp(conn, smtpcode, smtpc->state); break; + case SMTP_HELO: + result = smtp_state_helo_resp(conn, smtpcode, smtpc->state); + break; + case SMTP_MAIL: result = smtp_state_mail_resp(conn, smtpcode, smtpc->state); break; @@ -597,6 +646,10 @@ static CURLcode smtp_connect(struct connectdata *conn, const char *path = conn->data->state.path; int len; +#ifdef HAVE_GETHOSTNAME + char localhost[1024 + 1]; +#endif + *done = FALSE; /* default to not done yet */ /* If there already is a protocol-specific struct allocated for this @@ -660,8 +713,14 @@ static CURLcode smtp_connect(struct connectdata *conn, pp->endofresp = smtp_endofresp; pp->conn = conn; - if(!*path) + if(!*path) { +#ifdef HAVE_GETHOSTNAME + if(!gethostname(localhost, sizeof localhost)) + path = localhost; + else +#endif path = "localhost"; + } /* url decode the path and use it as domain with EHLO */ smtpc->domain = curl_easy_unescape(conn->data, path, 0, &len); diff --git a/lib/smtp.h b/lib/smtp.h index 02cd467f5..41efabba4 100644 --- a/lib/smtp.h +++ b/lib/smtp.h @@ -33,6 +33,7 @@ typedef enum { SMTP_SERVERGREET, /* waiting for the initial greeting immediately after a connect */ SMTP_EHLO, + SMTP_HELO, SMTP_STARTTLS, SMTP_MAIL, /* MAIL FROM */ SMTP_RCPT, /* RCPT TO */ diff --git a/tests/data/DISABLED b/tests/data/DISABLED index c65cdd90b..24bc532c2 100644 --- a/tests/data/DISABLED +++ b/tests/data/DISABLED @@ -7,3 +7,4 @@ 564 802 803 +804 diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am index 0a9ac547c..1878e013c 100644 --- a/tests/data/Makefile.am +++ b/tests/data/Makefile.am @@ -65,7 +65,7 @@ EXTRA_DIST = test1 test108 test117 test127 test20 test27 test34 test46 \ test564 test1101 test1102 test1103 test1104 test299 test310 test311 \ test312 test1105 test565 test800 test1106 test801 test566 test802 test803 \ test1107 test1108 test1109 test1110 test1111 test1112 test129 test567 \ - test568 test569 test570 test571 + test568 test569 test570 test571 test804 filecheck: @mkdir test-place; \ diff --git a/tests/data/test804 b/tests/data/test804 new file mode 100644 index 000000000..e1fc419e9 --- /dev/null +++ b/tests/data/test804 @@ -0,0 +1,59 @@ + + + +SMTP +SMTP HELO +RFC821 + + + +# +# Server-side + + +REPLY EHLO 500 Command unrecognized +REPLY HELO 250 Already old but still servicing... + + + +# +# Client-side + + +smtp + + +RFC821-only SMTP server (EHLO not supported) + + +From: different +To: another + +body + + +smtp://%HOSTIP:%SMTPPORT/user --mail-rcpt 804@foo --mail-from 804@from -T - + + + +# +# Verify data after the test has been "shot" + + +EHLO user +HELO user +MAIL FROM:804@from +RCPT TO:<804@foo> +DATA +QUIT + + +From: different +To: another + +body + +. + + + -- cgit v1.2.3