* Exim - an Internet mail transport agent *
*************************************************/
-/* Copyright (c) University of Cambridge 1995 - 2009 */
+/* 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, 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,
{ "gethostbyname", opt_bool,
(void *)offsetof(smtp_transport_options_block, gethostbyname) },
#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,
#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, 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,
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 */
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
#ifndef DISABLE_DKIM
/* 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
-/* 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
-an authenticated_sender for this message, the original value will be used.
-Other expansion failures are serious. An empty result is ignored, but there is
-otherwise no check - this feature is expected to be used with LMTP and other
-cases where non-standard addresses (e.g. without domains) might be required. */
+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 (ob->authenticated_sender != NULL)
+#ifndef SUPPORT_TLS
+if (smtps)
{
- uschar *new = expand_string(ob->authenticated_sender);
- if (new == NULL)
- {
- if (!expand_string_forcedfail)
- {
- uschar *message = string_sprintf("failed to expand "
- "authenticated_sender: %s", expand_string_message);
- set_errno(addrlist, 0, message, DEFER, FALSE);
- return ERROR;
- }
- }
- else if (new[0] != 0) local_authenticated_sender = new;
+ 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
{
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",
/* 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
)
{
{
case OK:
smtp_authenticated = TRUE; /* stops the outer loop */
+ client_authenticator = au->name;
+ if (au->set_client_id != NULL)
+ client_authenticated_id = expand_string(au->set_client_id);
break;
/* Failure after writing a command */
while (*p) p++;
}
+/* 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
+an authenticated_sender for this message, the original value will be used.
+Other expansion failures are serious. An empty result is ignored, but there is
+otherwise no check - this feature is expected to be used with LMTP and other
+cases where non-standard addresses (e.g. without domains) might be required. */
+
+if (ob->authenticated_sender != NULL)
+ {
+ uschar *new = expand_string(ob->authenticated_sender);
+ if (new == NULL)
+ {
+ if (!expand_string_forcedfail)
+ {
+ uschar *message = string_sprintf("failed to expand "
+ "authenticated_sender: %s", expand_string_message);
+ set_errno(addrlist, 0, message, DEFER, FALSE);
+ return ERROR;
+ }
+ }
+ else if (new[0] != 0) local_authenticated_sender = new;
+ }
+
/* Add the authenticated sender address if present */
if ((smtp_authenticated || ob->authenticated_sender_force) &&
string_format(p, sizeof(buffer) - (p-buffer), " AUTH=%s",
auth_xtextencode(local_authenticated_sender,
Ustrlen(local_authenticated_sender)));
+ client_authenticated_sender = string_copy(local_authenticated_sender);
}
+else
+ client_authenticated_sender = NULL;
/* From here until we send the DATA command, we can make use of PIPELINING
if the server host supports it. The code has to be able to check the responses
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
/* Update the database which keeps information about which messages are waiting
for which hosts to become available. For some message-specific errors, the
update_waiting flag is turned off because we don't want follow-on deliveries in
-those cases. */
+those cases. If this transport instance is explicitly limited to one message
+per connection then follow-on deliveries are not possible and there's no need
+to create/update the per-transport wait-<transport_name> database. */
-if (update_waiting) transport_update_waiting(hostlist, tblock->name);
+if (update_waiting && tblock->connection_max_messages != 1)
+ transport_update_waiting(hostlist, tblock->name);
END_TRANSPORT: