{
HDEBUG(D_verify) debug_printf("cannot callout via null transport\n");
}
+else if (Ustrcmp(addr->transport->driver_name, "smtp") != 0)
+ log_write(0, LOG_MAIN|LOG_PANIC|LOG_CONFIG_FOR, "callout transport '%s': %s is non-smtp",
+ addr->transport->name, addr->transport->driver_name);
else
{
smtp_transport_options_block *ob =
- (smtp_transport_options_block *)(addr->transport->options_block);
+ (smtp_transport_options_block *)addr->transport->options_block;
/* The information wasn't available in the cache, so we have to do a real
callout and save the result in the cache for next time, unless no_cache is set,
BOOL esmtp;
BOOL suppress_tls = FALSE;
uschar *interface = NULL; /* Outgoing interface to use; NULL => any */
+#if defined(SUPPORT_TLS) && defined(EXPERIMENTAL_DANE)
+ BOOL dane = FALSE;
+ dns_answer tlsa_dnsa;
+#endif
uschar inbuffer[4096];
uschar outbuffer[1024];
uschar responsebuffer[4096];
deliver_host = host->name;
deliver_host_address = host->address;
+ deliver_host_port = host->port;
deliver_domain = addr->domain;
+ transport_name = addr->transport->name;
if (!smtp_get_interface(tf->interface, host_af, addr, NULL, &interface,
US"callout") ||
HDEBUG(D_verify) debug_printf("interface=%s port=%d\n", interface, port);
+#if defined(SUPPORT_TLS) && defined(EXPERIMENTAL_DANE)
+ {
+ BOOL dane_required;
+ int rc;
+
+ tls_out.dane_verified = FALSE;
+ tls_out.tlsa_usage = 0;
+
+ dane_required = verify_check_this_host(&ob->hosts_require_dane, NULL,
+ host->name, host->address, NULL) == OK;
+
+ if (host->dnssec == DS_YES)
+ {
+ if( dane_required
+ || verify_check_this_host(&ob->hosts_try_dane, NULL,
+ host->name, host->address, NULL) == OK
+ )
+ if ((rc = tlsa_lookup(host, &tlsa_dnsa, dane_required, &dane)) != OK)
+ return rc;
+ }
+ else if (dane_required)
+ {
+ log_write(0, LOG_MAIN, "DANE error: %s lookup not DNSSEC", host->name);
+ return FAIL;
+ }
+
+ if (dane)
+ ob->tls_tempfail_tryclear = FALSE;
+ }
+#endif /*DANE*/
+
/* Set up the buffer for reading SMTP response packets. */
inblock.buffer = inbuffer;
tls_retry_connection:
inblock.sock = outblock.sock =
- smtp_connect(host, host_af, port, interface, callout_connect, TRUE, NULL);
+ smtp_connect(host, host_af, port, interface, callout_connect, TRUE, NULL
+#ifdef EXPERIMENTAL_EVENT
+ /*XXX event action? NULL for now. */
+ , NULL
+#endif
+ );
/* reconsider DSCP here */
if (inblock.sock < 0)
{
addr->message = string_sprintf("could not connect to %s [%s]: %s",
host->name, host->address, strerror(errno));
+ transport_name = NULL;
deliver_host = deliver_host_address = NULL;
deliver_domain = save_deliver_domain;
continue;
else active_hostname = s;
}
- deliver_host = deliver_host_address = NULL;
- deliver_domain = save_deliver_domain;
-
/* Wait for initial response, and send HELO. The smtp_write_command()
function leaves its command in big_buffer. This is used in error responses.
Initialize it in case the connection is rejected. */
/* Unless ssl-on-connect, wait for the initial greeting */
smtps_redo_greeting:
- #ifdef SUPPORT_TLS
+#ifdef SUPPORT_TLS
if (!smtps || (smtps && tls_out.active >= 0))
- #endif
+#endif
+ {
if (!(done= smtp_read_response(&inblock, responsebuffer, sizeof(responsebuffer), '2', callout)))
goto RESPONSE_FAILED;
+#ifdef EXPERIMENTAL_EVENT
+ if (event_raise(addr->transport->event_action,
+ US"smtp:connect", responsebuffer) == DEFER)
+ {
+ /* Logging? Debug? */
+ goto RESPONSE_FAILED;
+ }
+#endif
+ }
+
/* Not worth checking greeting line for ESMTP support */
if (!(esmtp = verify_check_this_host(&(ob->hosts_avoid_esmtp), NULL,
host->name, host->address, NULL) != OK))
tls_redo_helo:
- #ifdef SUPPORT_TLS
+#ifdef SUPPORT_TLS
if (smtps && tls_out.active < 0) /* ssl-on-connect, first pass */
{
tls_offered = TRUE;
ob->tls_tempfail_tryclear = FALSE;
}
- else /* all other cases */
- #endif
+ else /* all other cases */
+#endif
{ esmtp_retry:
done= FALSE;
goto RESPONSE_FAILED;
}
- #ifdef SUPPORT_TLS
+#ifdef SUPPORT_TLS
tls_offered = FALSE;
- #endif
+#endif
esmtp = FALSE;
goto esmtp_retry; /* fallback to HELO */
}
/* Set tls_offered if the response to EHLO specifies support for STARTTLS. */
- #ifdef SUPPORT_TLS
+#ifdef SUPPORT_TLS
if (esmtp && !suppress_tls && tls_out.active < 0)
- {
- if (regex_STARTTLS == NULL) regex_STARTTLS =
- regex_must_compile(US"\\n250[\\s\\-]STARTTLS(\\s|\\n|$)", FALSE, TRUE);
+ {
+ if (regex_STARTTLS == NULL) regex_STARTTLS =
+ regex_must_compile(US"\\n250[\\s\\-]STARTTLS(\\s|\\n|$)", FALSE, TRUE);
- tls_offered = pcre_exec(regex_STARTTLS, NULL, CS responsebuffer,
- Ustrlen(responsebuffer), 0, PCRE_EOPT, NULL, 0) >= 0;
+ tls_offered = pcre_exec(regex_STARTTLS, NULL, CS responsebuffer,
+ Ustrlen(responsebuffer), 0, PCRE_EOPT, NULL, 0) >= 0;
}
else
tls_offered = FALSE;
- #endif
+#endif
}
/* If TLS is available on this connection attempt to
the client not be required to use TLS. If the response is bad, copy the buffer
for error analysis. */
- #ifdef SUPPORT_TLS
+#ifdef SUPPORT_TLS
if (tls_offered &&
verify_check_this_host(&(ob->hosts_avoid_tls), NULL, host->name,
host->address, NULL) != OK &&
{
if (errno != 0 || buffer2[0] == 0 ||
(buffer2[0] == '4' && !ob->tls_tempfail_tryclear))
- {
- Ustrncpy(responsebuffer, buffer2, sizeof(responsebuffer));
- done= FALSE;
- goto RESPONSE_FAILED;
- }
+ {
+ Ustrncpy(responsebuffer, buffer2, sizeof(responsebuffer));
+ done= FALSE;
+ goto RESPONSE_FAILED;
+ }
}
/* STARTTLS accepted or ssl-on-connect: try to negotiate a TLS session. */
else
{
- int rc = tls_client_start(inblock.sock, host, addr,
- ob->tls_certificate, ob->tls_privatekey,
- ob->tls_sni,
- ob->tls_verify_certificates, ob->tls_crl,
- ob->tls_require_ciphers,
-#ifdef EXPERIMENTAL_OCSP
- ob->hosts_require_ocsp,
+ int oldtimeout = ob->command_timeout;
+ int rc;
+
+ ob->command_timeout = callout;
+ rc = tls_client_start(inblock.sock, host, addr, addr->transport
+#ifdef EXPERIMENTAL_DANE
+ , dane ? &tlsa_dnsa : NULL
#endif
- ob->tls_dh_min_bits, callout,
- ob->tls_verify_hosts, ob->tls_try_verify_hosts);
+ );
+ ob->command_timeout = oldtimeout;
/* TLS negotiation failed; give an error. Try in clear on a new connection,
if the options permit it for this host. */
if (rc != OK)
{
- if (rc == DEFER && ob->tls_tempfail_tryclear && !smtps &&
- verify_check_this_host(&(ob->hosts_require_tls), NULL, host->name,
- host->address, NULL) != OK)
- {
- (void)close(inblock.sock);
- log_write(0, LOG_MAIN, "TLS session failure: delivering unencrypted "
- "to %s [%s] (not in hosts_require_tls)", host->name, host->address);
- suppress_tls = TRUE;
- goto tls_retry_connection;
- }
- /*save_errno = ERRNO_TLSFAILURE;*/
- /*message = US"failure while setting up TLS session";*/
- send_quit = FALSE;
- done= FALSE;
- goto TLS_FAILED;
- }
+ if ( rc == DEFER
+ && ob->tls_tempfail_tryclear
+ && !smtps
+ && verify_check_this_host(&(ob->hosts_require_tls), NULL,
+ host->name, host->address, NULL) != OK
+ )
+ {
+ (void)close(inblock.sock);
+#ifdef EXPERIMENTAL_EVENT
+ (void) event_raise(addr->transport->event_action,
+ US"tcp:close", NULL);
+#endif
+ log_write(0, LOG_MAIN, "TLS session failure: delivering unencrypted "
+ "to %s [%s] (not in hosts_require_tls)", host->name, host->address);
+ suppress_tls = TRUE;
+ goto tls_retry_connection;
+ }
+ /*save_errno = ERRNO_TLSFAILURE;*/
+ /*message = US"failure while setting up TLS session";*/
+ send_quit = FALSE;
+ done= FALSE;
+ goto TLS_FAILED;
+ }
/* TLS session is set up. Copy info for logging. */
addr->cipher = tls_out.cipher;
/* For SMTPS we need to wait for the initial OK response, then do HELO. */
if (smtps)
- goto smtps_redo_greeting;
+ goto smtps_redo_greeting;
/* For STARTTLS we need to redo EHLO */
goto tls_redo_helo;
/* If the host is required to use a secure channel, ensure that we have one. */
if (tls_out.active < 0)
- if (verify_check_this_host(&(ob->hosts_require_tls), NULL, host->name,
- host->address, NULL) == OK)
+ if (
+#ifdef EXPERIMENTAL_DANE
+ dane ||
+#endif
+ verify_check_this_host(&(ob->hosts_require_tls), NULL, host->name,
+ host->address, NULL) == OK
+ )
{
/*save_errno = ERRNO_TLSREQUIRED;*/
- log_write(0, LOG_MAIN, "a TLS session is required for %s [%s], but %s",
+ log_write(0, LOG_MAIN,
+ "H=%s [%s]: a TLS session is required for this host, but %s",
host->name, host->address,
- tls_offered? "an attempt to start TLS failed" : "the server did not offer TLS support");
+ tls_offered ? "an attempt to start TLS failed"
+ : "the server did not offer TLS support");
done= FALSE;
goto TLS_FAILED;
}
cutthrough_delivery= FALSE;
HDEBUG(D_acl|D_v) debug_printf("Cutthrough cancelled by presence of transport filter\n");
}
- #ifndef DISABLE_DKIM
+#ifndef DISABLE_DKIM
if (ob->dkim_domain)
{
cutthrough_delivery= FALSE;
HDEBUG(D_acl|D_v) debug_printf("Cutthrough cancelled by presence of DKIM signing\n");
}
- #endif
+#endif
}
SEND_FAILED:
;
/* Clear down of the TLS, SMTP and TCP layers on error is handled below. */
-
/* Failure to accept HELO is cached; this blocks the whole domain for all
senders. I/O errors and defer responses are not cached. */
smtp_read_response(&inblock, responsebuffer, sizeof(responsebuffer),
'2', callout);
+ deliver_host = deliver_host_address = NULL;
+ deliver_domain = save_deliver_domain;
+
/* If the host does not accept MAIL FROM:<>, arrange to cache this
information, but again, don't record anything for an I/O error or a defer. Do
not cache rejections of MAIL when a non-empty sender has been used, because
/* If accepted, we aren't going to do any further tests below. */
if (random_ok)
- {
new_domain_record.random_result = ccache_accept;
- }
/* Otherwise, cache a real negative response, and get back to the right
state to send RCPT. Unless there's some problem such as a dropped
cutthrough_addr = *addr; /* Save the address_item for later logging */
cutthrough_addr.next = NULL;
cutthrough_addr.host_used = store_get(sizeof(host_item));
- cutthrough_addr.host_used->name = host->name;
- cutthrough_addr.host_used->address = host->address;
- cutthrough_addr.host_used->port = port;
+ *(cutthrough_addr.host_used) = *host;
if (addr->parent)
*(cutthrough_addr.parent = store_get(sizeof(address_item)))= *addr->parent;
ctblock.buffer = ctbuffer;
cancel_cutthrough_connection("multiple verify calls");
if (send_quit) (void)smtp_write_command(&outblock, FALSE, "QUIT\r\n");
- #ifdef SUPPORT_TLS
+#ifdef SUPPORT_TLS
tls_close(FALSE, TRUE);
- #endif
+#endif
(void)close(inblock.sock);
+#ifdef EXPERIMENTAL_EVENT
+ (void) event_raise(addr->transport->event_action,
+ US"tcp:close", NULL);
+#endif
}
} /* Loop through all hosts, while !done */
they're used in the context of a transport used by verification. Reset them
at exit from this routine. */
-modify_variable(US"tls_bits", &tls_out.bits);
-modify_variable(US"tls_certificate_verified", &tls_out.certificate_verified);
-modify_variable(US"tls_cipher", &tls_out.cipher);
-modify_variable(US"tls_peerdn", &tls_out.peerdn);
-#if defined(SUPPORT_TLS) && !defined(USE_GNUTLS)
-modify_variable(US"tls_sni", &tls_out.sni);
-#endif
+tls_modify_variables(&tls_out);
/* Save a copy of the sender address for re-instating if we change it to <>
while verifying a sender address (a nice bit of self-reference there). */
string_is_ip_address(host->name, NULL) != 0)
(void)host_find_byname(host, NULL, flags, &canonical_name, TRUE);
else
+ {
+ uschar * d_request = NULL, * d_require = NULL;
+ if (Ustrcmp(addr->transport->driver_name, "smtp") == 0)
+ {
+ smtp_transport_options_block * ob =
+ (smtp_transport_options_block *)
+ addr->transport->options_block;
+ d_request = ob->dnssec_request_domains;
+ d_require = ob->dnssec_require_domains;
+ }
+
(void)host_find_bydns(host, NULL, flags, NULL, NULL, NULL,
- &canonical_name, NULL);
+ d_request, d_require, &canonical_name, NULL);
+ }
}
}
}
#ifdef SUPPORT_TLS
deliver_set_expansions(addr);
#endif
+ verify_mode = is_recipient ? US"R" : US"S";
rc = do_callout(addr, host_list, &tf, callout, callout_overall,
callout_connect, options, se_mailfrom, pm_mailfrom);
+ verify_mode = NULL;
}
}
else
the -bv or -bt case). */
out:
-
-modify_variable(US"tls_bits", &tls_in.bits);
-modify_variable(US"tls_certificate_verified", &tls_in.certificate_verified);
-modify_variable(US"tls_cipher", &tls_in.cipher);
-modify_variable(US"tls_peerdn", &tls_in.peerdn);
-#if defined(SUPPORT_TLS) && !defined(USE_GNUTLS)
-modify_variable(US"tls_sni", &tls_in.sni);
-#endif
+tls_modify_variables(&tls_in);
return yield;
}
/* In case this is the first time the DNS resolver is being used. */
-dns_init(FALSE, FALSE);
+dns_init(FALSE, FALSE, FALSE); /*XXX dnssec? */
/* Loop through all the domains supplied, until something matches */