-/* $Cambridge: exim/src/src/transports/smtp.c,v 1.41 2009/01/02 17:22:12 nm4 Exp $ */
-
/*************************************************
* Exim - an Internet mail transport agent *
*************************************************/
-/* Copyright (c) University of Cambridge 1995 - 2007 */
+/* Copyright (c) University of Cambridge 1995 - 2012 */
/* See the file NOTICE for conditions of use and distribution. */
#include "../exim.h"
(void *)offsetof(smtp_transport_options_block, data_timeout) },
{ "delay_after_cutoff", opt_bool,
(void *)offsetof(smtp_transport_options_block, delay_after_cutoff) },
- #if (defined EXPERIMENTAL_DOMAINKEYS) || (defined EXPERIMENTAL_DKIM)
- { "dk_canon", opt_stringptr,
- (void *)offsetof(smtp_transport_options_block, dk_canon) },
- { "dk_domain", opt_stringptr,
- (void *)offsetof(smtp_transport_options_block, dk_domain) },
- { "dk_headers", opt_stringptr,
- (void *)offsetof(smtp_transport_options_block, dk_headers) },
- { "dk_private_key", opt_stringptr,
- (void *)offsetof(smtp_transport_options_block, dk_private_key) },
- { "dk_selector", opt_stringptr,
- (void *)offsetof(smtp_transport_options_block, dk_selector) },
- { "dk_strict", opt_stringptr,
- (void *)offsetof(smtp_transport_options_block, dk_strict) },
+#ifndef DISABLE_DKIM
{ "dkim_canon", opt_stringptr,
(void *)offsetof(smtp_transport_options_block, dkim_canon) },
{ "dkim_domain", opt_stringptr,
(void *)offsetof(smtp_transport_options_block, dkim_sign_headers) },
{ "dkim_strict", opt_stringptr,
(void *)offsetof(smtp_transport_options_block, dkim_strict) },
- #endif
+#endif
{ "dns_qualify_single", opt_bool,
(void *)offsetof(smtp_transport_options_block, dns_qualify_single) },
{ "dns_search_parents", opt_bool,
(void *)offsetof(smtp_transport_options_block, dns_search_parents) },
+ { "dscp", opt_stringptr,
+ (void *)offsetof(smtp_transport_options_block, dscp) },
{ "fallback_hosts", opt_stringptr,
(void *)offsetof(smtp_transport_options_block, fallback_hosts) },
{ "final_timeout", opt_time,
(void *)offsetof(smtp_transport_options_block, final_timeout) },
{ "gethostbyname", opt_bool,
(void *)offsetof(smtp_transport_options_block, gethostbyname) },
- #ifdef SUPPORT_TLS
+#ifdef SUPPORT_TLS
+ /* These are no longer honoured, as of Exim 4.80; for now, we silently
+ ignore; a later release will warn, and a later-still release will remove
+ these options, so that using them becomes an error. */
{ "gnutls_require_kx", opt_stringptr,
(void *)offsetof(smtp_transport_options_block, gnutls_require_kx) },
{ "gnutls_require_mac", opt_stringptr,
(void *)offsetof(smtp_transport_options_block, gnutls_require_mac) },
{ "gnutls_require_protocols", opt_stringptr,
(void *)offsetof(smtp_transport_options_block, gnutls_require_proto) },
- #endif
+#endif
{ "helo_data", opt_stringptr,
(void *)offsetof(smtp_transport_options_block, helo_data) },
{ "hosts", opt_stringptr,
(void *)offsetof(smtp_transport_options_block, hosts_avoid_esmtp) },
{ "hosts_avoid_pipelining", opt_stringptr,
(void *)offsetof(smtp_transport_options_block, hosts_avoid_pipelining) },
- #ifdef SUPPORT_TLS
+#ifdef SUPPORT_TLS
{ "hosts_avoid_tls", opt_stringptr,
(void *)offsetof(smtp_transport_options_block, hosts_avoid_tls) },
- #endif
+#endif
{ "hosts_max_try", opt_int,
(void *)offsetof(smtp_transport_options_block, hosts_max_try) },
{ "hosts_max_try_hardlimit", opt_int,
(void *)offsetof(smtp_transport_options_block, hosts_max_try_hardlimit) },
- #ifdef SUPPORT_TLS
+#ifdef SUPPORT_TLS
{ "hosts_nopass_tls", opt_stringptr,
(void *)offsetof(smtp_transport_options_block, hosts_nopass_tls) },
- #endif
+#endif
{ "hosts_override", opt_bool,
(void *)offsetof(smtp_transport_options_block, hosts_override) },
{ "hosts_randomize", opt_bool,
(void *)offsetof(smtp_transport_options_block, hosts_randomize) },
{ "hosts_require_auth", opt_stringptr,
(void *)offsetof(smtp_transport_options_block, hosts_require_auth) },
- #ifdef SUPPORT_TLS
+#ifdef SUPPORT_TLS
{ "hosts_require_tls", opt_stringptr,
(void *)offsetof(smtp_transport_options_block, hosts_require_tls) },
- #endif
+#endif
{ "hosts_try_auth", opt_stringptr,
(void *)offsetof(smtp_transport_options_block, hosts_try_auth) },
+#ifdef SUPPORT_TLS
+ { "hosts_verify_avoid_tls", opt_stringptr,
+ (void *)offsetof(smtp_transport_options_block, hosts_verify_avoid_tls) },
+#endif
{ "interface", opt_stringptr,
(void *)offsetof(smtp_transport_options_block, interface) },
{ "keepalive", opt_bool,
(void *)offsetof(smtp_transport_options_block, serialize_hosts) },
{ "size_addition", opt_int,
(void *)offsetof(smtp_transport_options_block, size_addition) }
- #ifdef SUPPORT_TLS
+#ifdef SUPPORT_TLS
,{ "tls_certificate", opt_stringptr,
(void *)offsetof(smtp_transport_options_block, tls_certificate) },
{ "tls_crl", opt_stringptr,
(void *)offsetof(smtp_transport_options_block, tls_crl) },
+ { "tls_dh_min_bits", opt_int,
+ (void *)offsetof(smtp_transport_options_block, tls_dh_min_bits) },
{ "tls_privatekey", opt_stringptr,
(void *)offsetof(smtp_transport_options_block, tls_privatekey) },
- { "tls_require_ciphers", opt_stringptr,
+ { "tls_require_ciphers", opt_stringptr,
(void *)offsetof(smtp_transport_options_block, tls_require_ciphers) },
+ { "tls_sni", opt_stringptr,
+ (void *)offsetof(smtp_transport_options_block, tls_sni) },
{ "tls_tempfail_tryclear", opt_bool,
(void *)offsetof(smtp_transport_options_block, tls_tempfail_tryclear) },
{ "tls_verify_certificates", opt_stringptr,
(void *)offsetof(smtp_transport_options_block, tls_verify_certificates) }
- #endif
+#endif
};
/* Size of the options list. An extern variable has to be used so that its
NULL, /* interface */
NULL, /* port */
US"smtp", /* protocol */
+ NULL, /* DSCP */
NULL, /* serialize_hosts */
NULL, /* hosts_try_auth */
NULL, /* hosts_require_auth */
NULL, /* hosts_require_tls */
NULL, /* hosts_avoid_tls */
+ US"*", /* hosts_verify_avoid_tls */
NULL, /* hosts_avoid_pipelining */
NULL, /* hosts_avoid_esmtp */
NULL, /* hosts_nopass_tls */
TRUE, /* keepalive */
FALSE, /* lmtp_ignore_quota */
TRUE /* retry_include_ip_address */
- #ifdef SUPPORT_TLS
+#ifdef SUPPORT_TLS
,NULL, /* tls_certificate */
NULL, /* tls_crl */
NULL, /* tls_privatekey */
NULL, /* gnutls_require_kx */
NULL, /* gnutls_require_mac */
NULL, /* gnutls_require_proto */
+ NULL, /* tls_sni */
NULL, /* tls_verify_certificates */
+ EXIM_CLIENT_DH_DEFAULT_MIN_BITS,
+ /* tls_dh_min_bits */
TRUE /* tls_tempfail_tryclear */
- #endif
- #if (defined EXPERIMENTAL_DOMAINKEYS) || (defined EXPERIMENTAL_DKIM)
- ,NULL, /* dk_canon */
- NULL, /* dk_domain */
- NULL, /* dk_headers */
- NULL, /* dk_private_key */
- NULL, /* dk_selector */
- NULL /* dk_strict */
+#endif
+#ifndef DISABLE_DKIM
,NULL, /* dkim_canon */
NULL, /* dkim_domain */
NULL, /* dkim_private_key */
NULL, /* dkim_selector */
NULL, /* dkim_sign_headers */
NULL /* dkim_strict */
- #endif
+#endif
};
/* Set the default port according to the protocol */
if (ob->port == NULL)
- ob->port = (strcmpic(ob->protocol, US"lmtp") == 0)? US"lmtp" : US"smtp";
+ ob->port = (strcmpic(ob->protocol, US"lmtp") == 0)? US"lmtp" :
+ (strcmpic(ob->protocol, US"smtps") == 0)? US"smtps" : US"smtp";
/* Set up the setup entry point, to be called before subprocesses for this
transport. */
smtp_transport_options_block *ob =
(smtp_transport_options_block *)(tblock->options_block);
BOOL lmtp = strcmpic(ob->protocol, US"lmtp") == 0;
+BOOL smtps = strcmpic(ob->protocol, US"smtps") == 0;
BOOL ok = FALSE;
BOOL send_rset = TRUE;
BOOL send_quit = TRUE;
/* Reset the parameters of a TLS session. */
-tls_cipher = NULL;
-tls_peerdn = NULL;
+tls_in.bits = 0;
+tls_in.cipher = NULL; /* for back-compatible behaviour */
+tls_in.peerdn = NULL;
+#if defined(SUPPORT_TLS) && !defined(USE_GNUTLS)
+tls_in.sni = NULL;
+#endif
+
+tls_out.bits = 0;
+tls_out.cipher = NULL; /* the one we may use for this transport */
+tls_out.peerdn = NULL;
+#if defined(SUPPORT_TLS) && !defined(USE_GNUTLS)
+tls_out.sni = NULL;
+#endif
/* If an authenticated_sender override has been specified for this transport
instance, expand it. If the expansion is forced to fail, and there was already
else if (new[0] != 0) local_authenticated_sender = new;
}
+#ifndef SUPPORT_TLS
+if (smtps)
+ {
+ set_errno(addrlist, 0, US"TLS support not available", DEFER, FALSE);
+ return ERROR;
+ }
+#endif
+
/* Make a connection to the host if this isn't a continued delivery, and handle
the initial interaction and HELO/EHLO/LHLO. Connect timeout errors are handled
specially so they can be identified for retries. */
{
inblock.sock = outblock.sock =
smtp_connect(host, host_af, port, interface, ob->connect_timeout,
- ob->keepalive); /* This puts port into host->port */
+ ob->keepalive, ob->dscp); /* This puts port into host->port */
if (inblock.sock < 0)
{
is nevertheless a reasonably clean way of programming this kind of logic,
where you want to escape on any error. */
- if (!smtp_read_response(&inblock, buffer, sizeof(buffer), '2',
- ob->command_timeout)) goto RESPONSE_FAILED;
+ if (!smtps)
+ {
+ if (!smtp_read_response(&inblock, buffer, sizeof(buffer), '2',
+ ob->command_timeout)) goto RESPONSE_FAILED;
- /* Now check if the helo_data expansion went well, and sign off cleanly if it
- didn't. */
+ /* Now check if the helo_data expansion went well, and sign off cleanly if
+ it didn't. */
- if (helo_data == NULL)
- {
- uschar *message = string_sprintf("failed to expand helo_data: %s",
- expand_string_message);
- set_errno(addrlist, 0, message, DEFER, FALSE);
- yield = DEFER;
- goto SEND_QUIT;
+ if (helo_data == NULL)
+ {
+ uschar *message = string_sprintf("failed to expand helo_data: %s",
+ expand_string_message);
+ set_errno(addrlist, 0, message, DEFER, FALSE);
+ yield = DEFER;
+ goto SEND_QUIT;
+ }
}
/** Debugging without sending a message
esmtp = verify_check_this_host(&(ob->hosts_avoid_esmtp), NULL,
host->name, host->address, NULL) != OK;
+ /* Alas; be careful, since this goto is not an error-out, so conceivably
+ we might set data between here and the target which we assume to exist
+ and be usable. I can see this coming back to bite us. */
+ #ifdef SUPPORT_TLS
+ if (smtps)
+ {
+ tls_offered = TRUE;
+ suppress_tls = FALSE;
+ ob->tls_tempfail_tryclear = FALSE;
+ smtp_command = US"SSL-on-connect";
+ goto TLS_NEGOTIATE;
+ }
+ #endif
+
if (esmtp)
{
if (smtp_write_command(&outblock, FALSE, "%s %s\r\n",
if (!smtp_read_response(&inblock, buffer2, sizeof(buffer2), '2',
ob->command_timeout))
{
- Ustrncpy(buffer, buffer2, sizeof(buffer));
if (errno != 0 || buffer2[0] == 0 ||
(buffer2[0] == '4' && !ob->tls_tempfail_tryclear))
+ {
+ Ustrncpy(buffer, buffer2, sizeof(buffer));
goto RESPONSE_FAILED;
+ }
}
/* STARTTLS accepted: try to negotiate a TLS session. */
else
+ TLS_NEGOTIATE:
{
int rc = tls_client_start(inblock.sock,
host,
NULL, /* No DH param */
ob->tls_certificate,
ob->tls_privatekey,
+ ob->tls_sni,
ob->tls_verify_certificates,
ob->tls_crl,
ob->tls_require_ciphers,
- ob->gnutls_require_mac,
- ob->gnutls_require_kx,
- ob->gnutls_require_proto,
+ ob->tls_dh_min_bits,
ob->command_timeout);
/* TLS negotiation failed; give an error. From outside, this function may
{
if (addr->transport_return == PENDING_DEFER)
{
- addr->cipher = tls_cipher;
- addr->peerdn = tls_peerdn;
+ addr->cipher = tls_out.cipher;
+ addr->peerdn = tls_out.peerdn;
}
}
}
}
+/* if smtps, we'll have smtp_command set to something else; always safe to
+reset it here. */
+smtp_command = big_buffer;
+
/* If we started TLS, redo the EHLO/LHLO exchange over the secure channel. If
helo_data is null, we are dealing with a connection that was passed from
another process, and so we won't have expanded helo_data above. We have to
expand it here. $sending_ip_address and $sending_port are set up right at the
start of the Exim process (in exim.c). */
-if (tls_active >= 0)
+if (tls_out.active >= 0)
{
+ char *greeting_cmd;
if (helo_data == NULL)
{
helo_data = expand_string(ob->helo_data);
}
}
- if (smtp_write_command(&outblock, FALSE, "%s %s\r\n", lmtp? "LHLO" : "EHLO",
- helo_data) < 0)
+ /* For SMTPS we need to wait for the initial OK response. */
+ if (smtps)
+ {
+ if (!smtp_read_response(&inblock, buffer, sizeof(buffer), '2',
+ ob->command_timeout)) goto RESPONSE_FAILED;
+ }
+
+ if (esmtp)
+ greeting_cmd = "EHLO";
+ else
+ {
+ greeting_cmd = "HELO";
+ DEBUG(D_transport)
+ debug_printf("not sending EHLO (host matches hosts_avoid_esmtp)\n");
+ }
+
+ if (smtp_write_command(&outblock, FALSE, "%s %s\r\n",
+ lmtp? "LHLO" : greeting_cmd, helo_data) < 0)
goto SEND_FAILED;
if (!smtp_read_response(&inblock, buffer, sizeof(buffer), '2',
ob->command_timeout))
if (continue_hostname == NULL
#ifdef SUPPORT_TLS
- || tls_active >= 0
+ || tls_out.active >= 0
#endif
)
{
DEBUG(D_transport|D_v)
debug_printf(" SMTP>> writing message and terminating \".\"\n");
transport_count = 0;
-#if (defined EXPERIMENTAL_DOMAINKEYS) || (defined EXPERIMENTAL_DKIM)
+#ifndef DISABLE_DKIM
ok = dkim_transport_write_message(addrlist, inblock.sock,
topt_use_crlf | topt_end_dot | topt_escape_headers |
(tblock->body_only? topt_no_headers : 0) |
US".", US"..", /* Escaping strings */
tblock->rewrite_rules, tblock->rewrite_existflags,
ob->dkim_private_key, ob->dkim_domain, ob->dkim_selector,
- ob->dkim_canon, ob->dkim_strict, ob->dkim_sign_headers,
- ob->dk_private_key, ob->dk_domain, ob->dk_selector,
- ob->dk_canon, ob->dk_headers, ob->dk_strict
+ ob->dkim_canon, ob->dkim_strict, ob->dkim_sign_headers
);
#else
ok = transport_write_message(addrlist, inblock.sock,
continue;
}
completed_address = TRUE; /* NOW we can set this flag */
+ if ((log_extra_selector & LX_smtp_confirmation) != 0)
+ {
+ uschar *s = string_printing(buffer);
+ conf = (s == buffer)? (uschar *)string_copy(s) : s;
+ }
}
/* SMTP, or success return from LMTP for this address. Pass back the
BOOL more;
if (first_addr != NULL || continue_more ||
(
- (tls_active < 0 ||
+ (tls_out.active < 0 ||
verify_check_this_host(&(ob->hosts_nopass_tls), NULL, host->name,
host->address, NULL) != OK)
&&
don't get a good response, we don't attempt to pass the socket on. */
#ifdef SUPPORT_TLS
- if (tls_active >= 0)
+ if (tls_out.active >= 0)
{
- tls_close(TRUE);
- ok = smtp_write_command(&outblock,FALSE,"EHLO %s\r\n",helo_data) >= 0 &&
- smtp_read_response(&inblock, buffer, sizeof(buffer), '2',
- ob->command_timeout);
+ tls_close(FALSE, TRUE);
+ if (smtps)
+ ok = FALSE;
+ else
+ ok = smtp_write_command(&outblock,FALSE,"EHLO %s\r\n",helo_data) >= 0 &&
+ smtp_read_response(&inblock, buffer, sizeof(buffer), '2',
+ ob->command_timeout);
}
#endif
END_OFF:
#ifdef SUPPORT_TLS
-tls_close(TRUE);
+tls_close(FALSE, TRUE);
#endif
/* Close the socket, and return the appropriate value, first setting