*************************************************/
/* Copyright (c) University of Cambridge 1995 - 2018 */
+/* Copyright (c) The Exim Maintainers 2020 */
/* See the file NOTICE for conditions of use and distribution. */
#include "../exim.h"
.tls_verify_cert_hostnames = US"*",
#endif
#ifdef SUPPORT_I18N
- .utf8_downconvert = NULL,
+ .utf8_downconvert = US"-1",
#endif
#ifndef DISABLE_DKIM
.dkim =
for (au = auths, authnum = 0; au; au = au->next, authnum++) if (au->client)
{
const uschar * list = names;
- int sep = ' ';
- uschar name[32];
-
- while (string_nextinlist(&list, &sep, name, sizeof(name)))
- if (strcmpic(au->public_name, name) == 0)
+ uschar * s;
+ for (int sep = ' '; s = string_nextinlist(&list, &sep, NULL, 0); )
+ if (strcmpic(au->public_name, s) == 0)
{ authbits |= BIT(authnum); break; }
}
{
DEBUG(D_transport) debug_printf("%s expect mail\n", __FUNCTION__);
count--;
- sx->pending_MAIL = FALSE;
+ sx->pending_MAIL = sx->RCPT_452 = FALSE;
if (!smtp_read_response(sx, sx->buffer, sizeof(sx->buffer),
'2', ob->command_timeout))
{
if (addr->more_errno >> 8 == 52 && yield & 3)
{
- if (!sx->RCPT_452)
+ if (!sx->RCPT_452) /* initialised at MAIL-ack above */
{
DEBUG(D_transport)
debug_printf("%s: seen first 452 too-many-rcpts\n", __FUNCTION__);
}
}
}
+ if (count && !(addr = addr->next))
+ return -2;
} /* Loop for next RCPT response */
/* Update where to start at for the next block of responses, unless we
if (require_auth == OK && !f.smtp_authenticated)
{
+ invalidate_ehlo_cache_entry(sx);
set_errno_nohost(sx->addrlist, ERRNO_AUTHFAIL,
string_sprintf("authentication required but %s", fail_reason), DEFER,
FALSE, &sx->delivery_start);
typedef struct smtp_compare_s
{
- uschar *current_sender_address;
- struct transport_instance *tblock;
+ uschar * current_sender_address;
+ struct transport_instance * tblock;
} smtp_compare_t;
smtp_local_identity(s_compare->current_sender_address, s_compare->tblock);
if (!(new_sender_address = deliver_get_sender_address(message_id)))
- return 0;
+ return FALSE;
message_local_identity =
smtp_local_identity(new_sender_address, s_compare->tblock);
DEFER, FALSE, &sx->delivery_start);
return ERROR;
}
-#endif
+#else
+
+/* If we have a proxied TLS connection, check usability for this message */
+
+if (continue_hostname && continue_proxy_cipher)
+ {
+ int rc;
+ const uschar * sni = US"";
+
+# ifdef SUPPORT_DANE
+ /* Check if the message will be DANE-verified; if so force its SNI */
+
+ tls_out.dane_verified = FALSE;
+ smtp_port_for_connect(sx->conn_args.host, sx->port);
+ if ( sx->conn_args.host->dnssec == DS_YES
+ && ( sx->dane_required
+ || verify_check_given_host(CUSS &ob->hosts_try_dane, sx->conn_args.host) == OK
+ ) )
+ switch (rc = tlsa_lookup(sx->conn_args.host, &sx->conn_args.tlsa_dnsa, sx->dane_required))
+ {
+ case OK: sx->conn_args.dane = TRUE;
+ ob->tls_tempfail_tryclear = FALSE; /* force TLS */
+ ob->tls_sni = sx->first_addr->domain; /* force SNI */
+ break;
+ case FAIL_FORCED: break;
+ default: set_errno_nohost(sx->addrlist, ERRNO_DNSDEFER,
+ string_sprintf("DANE error: tlsa lookup %s",
+ rc_to_string(rc)),
+ rc, FALSE, &sx->delivery_start);
+# ifndef DISABLE_EVENT
+ (void) event_raise(sx->conn_args.tblock->event_action,
+ US"dane:fail", sx->dane_required
+ ? US"dane-required" : US"dnssec-invalid");
+# endif
+ return rc;
+ }
+# endif
+
+ /* If the SNI or the DANE status required for the new message differs from the
+ existing conn drop the connection to force a new one. */
+
+ if (ob->tls_sni && !(sni = expand_cstring(ob->tls_sni)))
+ log_write(0, LOG_MAIN|LOG_PANIC,
+ "<%s>: failed to expand transport's tls_sni value: %s",
+ sx->addrlist->address, expand_string_message);
+
+# ifdef SUPPORT_DANE
+ if ( (continue_proxy_sni ? (Ustrcmp(continue_proxy_sni, sni) == 0) : !*sni)
+ && continue_proxy_dane == sx->conn_args.dane)
+ {
+ tls_out.sni = US sni;
+ if ((tls_out.dane_verified = continue_proxy_dane))
+ sx->conn_args.host->dnssec = DS_YES;
+ }
+# else
+ if ((continue_proxy_sni ? (Ustrcmp(continue_proxy_sni, sni) == 0) : !*sni))
+ tls_out.sni = US sni;
+# endif
+ else
+ {
+ DEBUG(D_transport)
+ debug_printf("Closing proxied-TLS connection due to SNI mismatch\n");
+
+ HDEBUG(D_transport|D_acl|D_v) debug_printf_indent(" SMTP>> QUIT\n");
+ write(0, "QUIT\r\n", 6);
+ close(0);
+ continue_hostname = continue_proxy_cipher = NULL;
+ f.continue_more = FALSE;
+ continue_sequence = 1; /* Unfortunately, this process cannot affect success log
+ which is done by delivery proc. Would have to pass this
+ back through reporting pipe. */
+ }
+ }
+#endif /*!DISABLE_TLS*/
/* 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
switch (rc = tlsa_lookup(sx->conn_args.host, &sx->conn_args.tlsa_dnsa, sx->dane_required))
{
case OK: sx->conn_args.dane = TRUE;
- ob->tls_tempfail_tryclear = FALSE;
+ ob->tls_tempfail_tryclear = FALSE; /* force TLS */
+ ob->tls_sni = sx->first_addr->domain; /* force SNI */
break;
case FAIL_FORCED: break;
default: set_errno_nohost(sx->addrlist, ERRNO_DNSDEFER,
else DEBUG(D_transport)
debug_printf("helo needs $sending_ip_address\n");
+PIPE_CONNECT_RETRY:
if (sx->early_pipe_active)
sx->outblock.conn_args = &sx->conn_args;
else
{
HDEBUG(D_transport)
debug_printf("failed reaping pipelined cmd responses\n");
- goto RESPONSE_FAILED;
+ close(sx->cctx.sock);
+ sx->cctx.sock = -1;
+ sx->early_pipe_active = FALSE;
+ goto PIPE_CONNECT_RETRY;
}
#endif
int rc, i;
close(pfd[1]);
-if ((rc = fork()))
- {
- DEBUG(D_transport) debug_printf("proxy-proc final-pid %d\n", rc);
+if ((rc = exim_fork(US"tls-proxy")))
_exit(rc < 0 ? EXIT_FAILURE : EXIT_SUCCESS);
- }
-testharness_pause_ms(100); /* let parent debug out */
set_process_info("proxying TLS connection for continued transport");
FD_ZERO(&rfds);
FD_SET(tls_out.active.sock, &rfds);
done:
testharness_pause_ms(100); /* let logging complete */
- exim_exit(0, US"TLS proxy");
+ exim_exit(EXIT_SUCCESS);
}
#endif
/* If continue_hostname is not null, we get here only when continuing to
deliver down an existing channel. The channel was passed as the standard
-input. TLS is never active on a passed channel; the previous process always
-closes it down before passing the connection on.
+input. TLS is never active on a passed channel; the previous process either
+closes it down before passing the connection on, or inserts a TLS-proxy
+process and passes on a cleartext conection.
Otherwise, we have to make a connection to the remote host, and do the
initial protocol exchange.
uschar *message = NULL;
uschar new_message_id[MESSAGE_ID_LENGTH + 1];
smtp_context * sx = store_get(sizeof(*sx), TRUE); /* tainted, for the data buffers */
+#ifdef SUPPORT_DANE
+BOOL dane_held;
+#endif
suppress_tls = suppress_tls; /* stop compiler warning when no TLS support */
*message_defer = FALSE;
gettimeofday(&sx->delivery_start, NULL);
sx->sync_addr = sx->first_addr = addrlist;
-/* Get the channel set up ready for a message (MAIL FROM being the next
-SMTP command to send */
+#ifdef SUPPORT_DANE
+DANE_DOMAINS:
+dane_held = FALSE;
+#endif
+
+/* Get the channel set up ready for a message, MAIL FROM being the next
+SMTP command to send. */
if ((rc = smtp_setup_conn(sx, suppress_tls)) != OK)
{
timesince(&addrlist->delivery_time, &sx->delivery_start);
- return rc;
+ yield = rc;
+ goto TIDYUP;
+ }
+
+#ifdef SUPPORT_DANE
+/* If the connection used DANE, ignore for now any addresses with incompatible
+domains. The SNI has to be the domain. Arrange a whole new TCP conn later,
+just in case only TLS isn't enough. */
+
+if (sx->conn_args.dane)
+ {
+ const uschar * dane_domain = sx->first_addr->domain;
+
+ for (address_item * a = sx->first_addr->next; a; a = a->next)
+ if ( a->transport_return == PENDING_DEFER
+ && Ustrcmp(dane_domain, a->domain) != 0)
+ {
+ DEBUG(D_transport) debug_printf("DANE: holding %s for later\n", a->domain);
+ dane_held = TRUE;
+ a->transport_return = DANE;
+ }
}
+#endif
/* If there is a filter command specified for this transport, we can now
-set it up. This cannot be done until the identify of the host is known. */
+set it up. This cannot be done until the identity of the host is known. */
if (tblock->filter_command)
{
}
/* Process all transported addresses - for LMTP or PRDR, read a status for
- each one. */
+ each one. We used to drop out at first_addr, until someone returned a 452
+ followed by a 250... and we screwed up the accepted addresses. */
- for (address_item * addr = addrlist; addr != sx->first_addr; addr = addr->next)
+ for (address_item * addr = addrlist; addr; addr = addr->next)
{
if (addr->transport_return != PENDING_OK) continue;
t_compare.tblock = tblock;
t_compare.current_sender_address = sender_address;
- if ( sx->first_addr != NULL
- || f.continue_more
+ if ( sx->first_addr != NULL /* more addrs for this message */
+ || f.continue_more /* more addrs for coninued-host */
|| (
#ifndef DISABLE_TLS
( tls_out.active.sock < 0 && !continue_proxy_cipher
if (sx->first_addr != NULL) /* More addresses still to be sent */
- { /* in this run of the transport */
+ { /* for this message */
continue_sequence++; /* Causes * in logging */
pipelining_active = sx->pipelining_used; /* was cleared at DATA */
goto SEND_MESSAGE;
tls_close(sx->cctx.tls_ctx, TLS_SHUTDOWN_WAIT);
sx->cctx.tls_ctx = NULL;
+ tls_out.active.sock = -1;
smtp_peer_options = smtp_peer_options_wrap;
sx->ok = !sx->smtps
&& smtp_write_command(sx, SCMD_FLUSH, "EHLO %s\r\n", sx->helo_data)
'2', ob->command_timeout);
if (sx->ok && f.continue_more)
- return yield; /* More addresses for another run */
+ goto TIDYUP; /* More addresses for another run */
}
else
{
else
#endif
if (f.continue_more)
- return yield; /* More addresses for another run */
+ goto TIDYUP; /* More addresses for another run */
/* If the socket is successfully passed, we mustn't send QUIT (or
indeed anything!) from here. */
#ifndef DISABLE_TLS
if (tls_out.active.sock >= 0)
{
- int pid = fork();
+ int pid = exim_fork(US"tls-proxy-interproc");
if (pid == 0) /* child; fork again to disconnect totally */
{
- testharness_pause_ms(100); /* let parent debug out */
/* does not return */
smtp_proxy_tls(sx->cctx.tls_ctx, sx->buffer, sizeof(sx->buffer), pfd,
ob->command_timeout);
if (pid > 0) /* parent */
{
- DEBUG(D_transport) debug_printf("proxy-proc inter-pid %d\n", pid);
close(pfd[0]);
/* tidy the inter-proc to disconn the proxy proc */
waitpid(pid, NULL, 0);
sx->cctx.sock = -1;
continue_transport = NULL;
continue_hostname = NULL;
- return yield;
+ goto TIDYUP;
}
log_write(0, LOG_PANIC_DIE, "fork failed");
}
(void) event_raise(tblock->event_action, US"tcp:close", NULL);
#endif
+#ifdef SUPPORT_DANE
+if (dane_held)
+ {
+ sx->first_addr = NULL;
+ for (address_item * a = sx->addrlist->next; a; a = a->next)
+ if (a->transport_return == DANE)
+ {
+ a->transport_return = PENDING_DEFER;
+ if (!sx->first_addr)
+ {
+ /* Remember the new start-point in the addrlist, for smtp_setup_conn()
+ to get the domain string for SNI */
+
+ sx->first_addr = a;
+ DEBUG(D_transport) debug_printf("DANE: go-around for %s\n", a->domain);
+ }
+ }
+ goto DANE_DOMAINS;
+ }
+#endif
+
continue_transport = NULL;
continue_hostname = NULL;
return yield;
+
+TIDYUP:
+#ifdef SUPPORT_DANE
+if (dane_held) for (address_item * a = sx->addrlist->next; a; a = a->next)
+ if (a->transport_return == DANE)
+ a->transport_return = PENDING_DEFER;
+#endif
+return yield;
}
#ifndef DISABLE_EVENT
/* If the last host gave a defer raise a per-message event */
- if (!nexthost && (message_defer || rc == DEFER))
+ if ( !( nexthost
+ && unexpired_hosts_tried < ob->hosts_max_try
+ && total_hosts_tried < ob->hosts_max_try_hardlimit
+ )
+ && (message_defer || rc == DEFER)
+ )
deferred_event_raise(first_addr, host, US"msg:defer");
#endif
}