summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJeremy Harris <jgh146exb@wizmail.org>2014-09-04 22:40:09 +0100
committerJeremy Harris <jgh146exb@wizmail.org>2014-09-04 23:14:55 +0100
commit0e66b3b655cefaf20e3ce7347b45e158c3427831 (patch)
tree6c0e3a3e1be436c79b5d90263e0993411acd5143 /src
parent82dbd376b5de9b9d91e93e91a4e058a80a43de99 (diff)
Enforce TLS under DANE when host has TLSA records
Diffstat (limited to 'src')
-rw-r--r--src/src/functions.h11
-rw-r--r--src/src/tls-gnu.c6
-rw-r--r--src/src/tls-openssl.c84
-rw-r--r--src/src/transports/smtp.c98
-rw-r--r--src/src/verify.c53
5 files changed, 156 insertions, 96 deletions
diff --git a/src/src/functions.h b/src/src/functions.h
index fee4429a5..d10a68a31 100644
--- a/src/src/functions.h
+++ b/src/src/functions.h
@@ -44,7 +44,11 @@ extern uschar * tls_cert_fprt_sha1(void *);
extern uschar * tls_cert_fprt_sha256(void *);
extern int tls_client_start(int, host_item *, address_item *,
- transport_instance *);
+ transport_instance *
+#ifdef EXPERIMENTAL_DANE
+ , dns_answer *
+#endif
+ );
extern void tls_close(BOOL, BOOL);
extern int tls_export_cert(uschar *, size_t, void *);
extern int tls_feof(void);
@@ -66,6 +70,11 @@ extern uschar * tls_field_from_dn(uschar *, uschar *);
# ifdef EXPERIMENTAL_CERTNAMES
extern BOOL tls_is_name_for_cert(uschar *, void *);
# endif
+
+# ifdef EXPERIMENTAL_DANE
+extern int tlsa_lookup(host_item *, dns_answer *, BOOL, BOOL *);
+# endif
+
#endif /*SUPPORT_TLS*/
diff --git a/src/src/tls-gnu.c b/src/src/tls-gnu.c
index b7eae1793..3043e3abc 100644
--- a/src/src/tls-gnu.c
+++ b/src/src/tls-gnu.c
@@ -1761,7 +1761,11 @@ Returns: OK/DEFER/FAIL (because using common functions),
int
tls_client_start(int fd, host_item *host,
address_item *addr ARG_UNUSED,
- transport_instance *tb)
+ transport_instance *tb
+#ifdef EXPERIMENTAL_DANE
+ , dne_answer * unused_tlsa_dnsa
+#endif
+ )
{
smtp_transport_options_block *ob =
(smtp_transport_options_block *)tb->options_block;
diff --git a/src/src/tls-openssl.c b/src/src/tls-openssl.c
index 2e95a467a..5056e6188 100644
--- a/src/src/tls-openssl.c
+++ b/src/src/tls-openssl.c
@@ -1678,44 +1678,6 @@ return OK;
#ifdef EXPERIMENTAL_DANE
static int
-tlsa_lookup(host_item * host, dns_answer * dnsa,
- BOOL dane_required, BOOL * dane)
-{
-/* move this out to host.c given the similarity to dns_lookup() ? */
-uschar buffer[300];
-uschar * fullname = buffer;
-
-/* TLSA lookup string */
-(void)sprintf(CS buffer, "_%d._tcp.%.256s", host->port, host->name);
-
-switch (dns_lookup(dnsa, buffer, T_TLSA, &fullname))
- {
- case DNS_AGAIN:
- return DEFER; /* just defer this TLS'd conn */
-
- default:
- case DNS_FAIL:
- if (dane_required)
- {
- log_write(0, LOG_MAIN, "DANE error: TLSA lookup failed");
- return FAIL;
- }
- break;
-
- case DNS_SUCCEED:
- if (!dns_is_secure(dnsa))
- {
- log_write(0, LOG_MAIN, "DANE error: TLSA lookup not DNSSEC");
- return DEFER;
- }
- *dane = TRUE;
- break;
- }
-return OK;
-}
-
-
-static int
dane_tlsa_load(SSL * ssl, host_item * host, dns_answer * dnsa)
{
dns_record * rr;
@@ -1783,6 +1745,7 @@ Argument:
host connected host (for messages)
addr the first address
tb transport (always smtp)
+ tlsa_dnsa tlsa lookup, if DANE, else null
Returns: OK on success
FAIL otherwise - note that tls_error() will not give DEFER
@@ -1791,7 +1754,11 @@ Returns: OK on success
int
tls_client_start(int fd, host_item *host, address_item *addr,
- transport_instance *tb)
+ transport_instance *tb
+#ifdef EXPERIMENTAL_DANE
+ , dns_answer * tlsa_dnsa
+#endif
+ )
{
smtp_transport_options_block * ob =
(smtp_transport_options_block *)tb->options_block;
@@ -1805,34 +1772,9 @@ static uschar cipherbuf[256];
BOOL request_ocsp = FALSE;
BOOL require_ocsp = FALSE;
#endif
-#ifdef EXPERIMENTAL_DANE
-dns_answer tlsa_dnsa;
-BOOL dane = FALSE;
-BOOL dane_required;
-#endif
#ifdef EXPERIMENTAL_DANE
-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)
- {
- /*XXX a shame we only find this after making tcp & smtp connection */
- /* move the test earlier? */
- log_write(0, LOG_MAIN, "DANE error: previous lookup not DNSSEC");
- return FAIL;
- }
#endif
#ifndef DISABLE_OCSP
@@ -1843,7 +1785,7 @@ else if (dane_required)
else
{
# ifdef EXPERIMENTAL_DANE
- if ( dane
+ if ( tlsa_dnsa
&& ob->hosts_request_ocsp[0] == '*'
&& ob->hosts_request_ocsp[1] == '\0'
)
@@ -1891,7 +1833,7 @@ if (expciphers != NULL)
}
#ifdef EXPERIMENTAL_DANE
-if (dane)
+if (tlsa_dnsa)
{
SSL_CTX_set_verify(client_ctx, SSL_VERIFY_PEER, verify_callback_client_dane);
@@ -1941,8 +1883,8 @@ if (ob->tls_sni)
}
#ifdef EXPERIMENTAL_DANE
-if (dane)
- if ((rc = dane_tlsa_load(client_ssl, host, &tlsa_dnsa)) != OK)
+if (tlsa_dnsa)
+ if ((rc = dane_tlsa_load(client_ssl, host, tlsa_dnsa)) != OK)
return rc;
#endif
@@ -1980,10 +1922,6 @@ if (request_ocsp)
client_static_cbinfo->event_action = tb->tpda_event_action;
#endif
-#ifdef EXPERIMENTAL_TPDA
-client_static_cbinfo->event_action = tb->tpda_event_action;
-#endif
-
/* There doesn't seem to be a built-in timeout on connection. */
DEBUG(D_tls) debug_printf("Calling SSL_connect\n");
@@ -1993,7 +1931,7 @@ rc = SSL_connect(client_ssl);
alarm(0);
#ifdef EXPERIMENTAL_DANE
-if (dane)
+if (tlsa_dnsa)
DANESSL_cleanup(client_ssl);
#endif
diff --git a/src/src/transports/smtp.c b/src/src/transports/smtp.c
index 7b2a7d559..9986e80f4 100644
--- a/src/src/transports/smtp.c
+++ b/src/src/transports/smtp.c
@@ -212,7 +212,7 @@ smtp_transport_options_block smtp_transport_option_defaults = {
NULL, /* hosts_try_prdr */
#endif
#ifndef DISABLE_OCSP
- US"*", /* hosts_request_ocsp (except under DANE) */
+ US"*", /* hosts_request_ocsp (except under DANE; tls_client_start()) */
NULL, /* hosts_require_ocsp */
#endif
NULL, /* hosts_require_tls */
@@ -1148,6 +1148,46 @@ return FALSE;
+#ifdef EXPERIMENTAL_DANE
+int
+tlsa_lookup(host_item * host, dns_answer * dnsa,
+ BOOL dane_required, BOOL * dane)
+{
+/* move this out to host.c given the similarity to dns_lookup() ? */
+uschar buffer[300];
+uschar * fullname = buffer;
+
+/* TLSA lookup string */
+(void)sprintf(CS buffer, "_%d._tcp.%.256s", host->port, host->name);
+
+switch (dns_lookup(dnsa, buffer, T_TLSA, &fullname))
+ {
+ case DNS_AGAIN:
+ return DEFER; /* just defer this TLS'd conn */
+
+ default:
+ case DNS_FAIL:
+ if (dane_required)
+ {
+ log_write(0, LOG_MAIN, "DANE error: TLSA lookup failed");
+ return FAIL;
+ }
+ break;
+
+ case DNS_SUCCEED:
+ if (!dns_is_secure(dnsa))
+ {
+ log_write(0, LOG_MAIN, "DANE error: TLSA lookup not DNSSEC");
+ return DEFER;
+ }
+ *dane = TRUE;
+ break;
+ }
+return OK;
+}
+#endif
+
+
/*************************************************
* Deliver address list to given host *
*************************************************/
@@ -1226,6 +1266,10 @@ BOOL prdr_active;
#ifdef EXPERIMENTAL_DSN
BOOL dsn_all_lasthop = TRUE;
#endif
+#if defined(SUPPORT_TLS) && defined(EXPERIMENTAL_DANE)
+BOOL dane = FALSE;
+dns_answer tlsa_dnsa;
+#endif
smtp_inblock inblock;
smtp_outblock outblock;
int max_rcpt = tblock->max_addresses;
@@ -1307,6 +1351,36 @@ if (continue_hostname == NULL)
return DEFER;
}
+#if defined(SUPPORT_TLS) && defined(EXPERIMENTAL_DANE)
+ {
+ BOOL dane_required;
+
+ 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*/
+
/* Expand the greeting message while waiting for the initial response. (Makes
sense if helo_data contains ${lookup dnsdb ...} stuff). The expansion is
delayed till here so that $sending_interface and $sending_port are set. */
@@ -1505,7 +1579,11 @@ if (tls_offered && !suppress_tls &&
else
TLS_NEGOTIATE:
{
- int rc = tls_client_start(inblock.sock, host, addrlist, tblock);
+ int rc = tls_client_start(inblock.sock, host, addrlist, tblock
+# ifdef EXPERIMENTAL_DANE
+ , dane ? &tlsa_dnsa : NULL
+# endif
+ );
/* TLS negotiation failed; give an error. From outside, this function may
be called again to try in clear on a new connection, if the options permit
@@ -1588,12 +1666,12 @@ if (tls_out.active >= 0)
/* If the host is required to use a secure channel, ensure that we
have one. */
-else if ( verify_check_this_host(&(ob->hosts_require_tls), NULL, host->name,
- host->address, NULL) == OK
-#ifdef EXPERIMENTAL_DANE
- || verify_check_this_host(&(ob->hosts_require_dane), NULL, host->name,
+else if (
+# ifdef EXPERIMENTAL_DANE
+ dane ||
+# endif
+ verify_check_this_host(&(ob->hosts_require_tls), NULL, host->name,
host->address, NULL) == OK
-#endif
)
{
save_errno = ERRNO_TLSREQUIRED;
@@ -1603,7 +1681,7 @@ else if ( verify_check_this_host(&(ob->hosts_require_tls), NULL, host->name,
"the server did not offer TLS support");
goto TLS_FAILED;
}
-#endif
+#endif /*SUPPORT_TLS*/
/* If TLS is active, we have just started it up and re-done the EHLO command,
so its response needs to be analyzed. If TLS is not active and this is a
@@ -3299,10 +3377,6 @@ for (cutoff_retry = 0; expired &&
&& ob->tls_tempfail_tryclear
&& verify_check_this_host(&(ob->hosts_require_tls), NULL, host->name,
host->address, NULL) != OK
-# ifdef EXPERIMENTAL_DANE
- && verify_check_this_host(&(ob->hosts_require_dane), NULL, host->name,
- host->address, NULL) != OK
-# endif
)
{
log_write(0, LOG_MAIN, "TLS session failure: delivering unencrypted "
diff --git a/src/src/verify.c b/src/src/verify.c
index d2ecb9cde..c25e6e257 100644
--- a/src/src/verify.c
+++ b/src/src/verify.c
@@ -426,6 +426,10 @@ else
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];
@@ -478,6 +482,37 @@ else
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;
@@ -654,7 +689,11 @@ else
int rc;
ob->command_timeout = callout;
- rc = tls_client_start(inblock.sock, host, addr, addr->transport);
+ rc = tls_client_start(inblock.sock, host, addr, addr->transport
+#ifdef EXPERIMENTAL_DANE
+ , dane ? &tlsa_dnsa : NULL
+#endif
+ );
ob->command_timeout = oldtimeout;
/* TLS negotiation failed; give an error. Try in clear on a new connection,
@@ -666,10 +705,6 @@ else
&& !smtps
&& verify_check_this_host(&(ob->hosts_require_tls), NULL,
host->name, host->address, NULL) != OK
-#ifdef EXPERIMENTAL_DANE
- && verify_check_this_host(&(ob->hosts_require_dane), NULL,
- host->name, host->address, NULL) != OK
-#endif
)
{
(void)close(inblock.sock);
@@ -704,12 +739,12 @@ else
/* 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
- || verify_check_this_host(&(ob->hosts_require_dane), NULL, host->name,
- host->address, NULL) == OK
+ dane ||
#endif
+ verify_check_this_host(&(ob->hosts_require_tls), NULL, host->name,
+ host->address, NULL) == OK
)
{
/*save_errno = ERRNO_TLSREQUIRED;*/