From 5ec37a55162e10377e457fe1deef174093f757a8 Mon Sep 17 00:00:00 2001 From: Phil Pennock Date: Wed, 28 Mar 2018 23:01:34 -0400 Subject: [PATCH] Implement dane_require_tls_ciphers (theoretically) It compiles with OpenSSL, on Darwin (if restore Darwin OS). It doesn't crash immediately, but more testing is needed from a place where port 25 is not just blocked. --- doc/doc-docbook/spec.xfpt | 18 ++++++++++++++++++ src/src/tls-gnu.c | 22 ++++++++++++++++++++-- src/src/tls-openssl.c | 21 ++++++++++++++++++--- src/src/transports/smtp.c | 5 +++++ src/src/transports/smtp.h | 1 + 5 files changed, 62 insertions(+), 5 deletions(-) diff --git a/doc/doc-docbook/spec.xfpt b/doc/doc-docbook/spec.xfpt index 9722c0063..16d276ee8 100644 --- a/doc/doc-docbook/spec.xfpt +++ b/doc/doc-docbook/spec.xfpt @@ -23941,6 +23941,24 @@ For testing purposes, this value can be overridden by the &%-oB%& command line option. +.new +.option dane_require_tls_ciphers smtp string&!! unset +.cindex "TLS" "requiring specific ciphers for DANE" +.cindex "cipher" "requiring specific" +.cindex DANE "TLS ciphers" +This option may be used to override &%tls_require_ciphers%& for connections +where DANE has been determined to be in effect. +If not set, then &%tls_require_ciphers%& will be used. +Normal SMTP delivery is not able to make strong demands of TLS cipher +configuration, because delivery will fall back to plaintext. Once DANE has +been determined to be in effect, there is no plaintext fallback and making the +TLS cipherlist configuration stronger will increase security, rather than +counter-intuitively decreasing it. +If the option expands to be empty or is forced to fail, then it will +be treated as unset and &%tls_require_ciphers%& will be used instead. +.wen + + .option data_timeout smtp time 5m This sets a timeout for the transmission of each block in the data portion of the message. As a result, the overall timeout for a message depends on the size diff --git a/src/src/tls-gnu.c b/src/src/tls-gnu.c index eed8c06dc..0d20fea34 100644 --- a/src/src/tls-gnu.c +++ b/src/src/tls-gnu.c @@ -1681,7 +1681,7 @@ else 1, 0)) || (rc = dane_verify_crt_raw(s, certlist, lsize, gnutls_certificate_type_get(state->session), - r, 0, + r, 0, # ifdef GNUTLS_BROKEN_DANE_VALIDATION usage == (1 << DANESSL_USAGE_DANE_EE) ? DANE_VFLAG_ONLY_CHECK_EE_USAGE : 0, @@ -2260,6 +2260,7 @@ smtp_transport_options_block *ob = (smtp_transport_options_block *)tb->options_block; int rc; exim_gnutls_state_st * state = NULL; +uschar *cipher_list = NULL; #ifndef DISABLE_OCSP BOOL require_ocsp = verify_check_given_host(&ob->hosts_require_ocsp, host) == OK; @@ -2269,9 +2270,26 @@ BOOL request_ocsp = require_ocsp ? TRUE DEBUG(D_tls) debug_printf("initialising GnuTLS as a client on fd %d\n", fd); +#ifdef SUPPORT_DANE +if (ob->dane_require_tls_ciphers) + { + /* not using expand_check_tlsvar because not yet in state */ + if (!expand_check(ob->dane_require_tls_ciphers, US"dane_require_tls_ciphers", + &cipher_list, errstr)) + return DEFER; + if (cipher_list && *cipher_list) + cipher_list = ob->dane_require_tls_ciphers; + else + cipher_list = ob->tls_require_ciphers; + } +#endif + +if (!cipher_list) + cipher_list = ob->tls_require_ciphers; + if ((rc = tls_init(host, ob->tls_certificate, ob->tls_privatekey, ob->tls_sni, ob->tls_verify_certificates, ob->tls_crl, - ob->tls_require_ciphers, &state, errstr)) != OK) + cipher_list, &state, errstr)) != OK) return rc; { diff --git a/src/src/tls-openssl.c b/src/src/tls-openssl.c index 969a99d99..c142bd059 100644 --- a/src/src/tls-openssl.c +++ b/src/src/tls-openssl.c @@ -904,7 +904,7 @@ We do not free the stack since it could be needed a second time for SNI handling. Separately we might try to replace using OCSP_basic_verify() - which seems to not -be a public interface into the OpenSSL library (there's no manual entry) - +be a public interface into the OpenSSL library (there's no manual entry) - But what with? We also use OCSP_basic_verify in the client stapling callback. And there we NEED it; we must verify that status... unless the library does it for us anyway? */ @@ -2300,8 +2300,23 @@ if (rc != OK) return rc; tls_out.certificate_verified = FALSE; client_verify_callback_called = FALSE; -if (!expand_check(ob->tls_require_ciphers, US"tls_require_ciphers", - &expciphers, errstr)) +expciphers = NULL; +#ifdef SUPPORT_DANE +if (tlsa_dnsa) + { + /* We fall back to tls_require_ciphers if unset, empty or forced failure, but + other failures should be treated as problems. */ + if (ob->dane_require_tls_ciphers && + !expand_check(ob->dane_require_tls_ciphers, US"dane_require_tls_ciphers", + &expciphers, errstr)) + return FAIL; + if (expciphers && *expciphers == '\0') + expciphers = NULL; + } +#endif +if (!expciphers && + !expand_check(ob->tls_require_ciphers, US"tls_require_ciphers", + &expciphers, errstr)) return FAIL; /* In OpenSSL, cipher components are separated by hyphens. In GnuTLS, they diff --git a/src/src/transports/smtp.c b/src/src/transports/smtp.c index 23083f5d8..2dfb5b73a 100644 --- a/src/src/transports/smtp.c +++ b/src/src/transports/smtp.c @@ -38,6 +38,10 @@ optionlist smtp_transport_options[] = { (void *)offsetof(smtp_transport_options_block, connect_timeout) }, { "connection_max_messages", opt_int | opt_public, (void *)offsetof(transport_instance, connection_max_messages) }, +# ifdef SUPPORT_DANE + { "dane_require_tls_ciphers", opt_stringptr, + (void *)offsetof(smtp_transport_options_block, dane_require_tls_ciphers) }, +# endif { "data_timeout", opt_time, (void *)offsetof(smtp_transport_options_block, data_timeout) }, { "delay_after_cutoff", opt_bool, @@ -225,6 +229,7 @@ smtp_transport_options_block smtp_transport_option_defaults = { #ifdef SUPPORT_DANE .hosts_try_dane = NULL, .hosts_require_dane = NULL, + .dane_require_tls_ciphers = NULL, #endif .hosts_try_fastopen = NULL, #ifndef DISABLE_PRDR diff --git a/src/src/transports/smtp.h b/src/src/transports/smtp.h index 749c6f778..7727c0c6d 100644 --- a/src/src/transports/smtp.h +++ b/src/src/transports/smtp.h @@ -32,6 +32,7 @@ typedef struct { #ifdef SUPPORT_DANE uschar *hosts_try_dane; uschar *hosts_require_dane; + uschar *dane_require_tls_ciphers; #endif uschar *hosts_try_fastopen; #ifndef DISABLE_PRDR -- 2.25.1