Implement dane_require_tls_ciphers (theoretically)
authorPhil Pennock <pdp@exim.org>
Thu, 29 Mar 2018 03:01:34 +0000 (23:01 -0400)
committerPhil Pennock <pdp@exim.org>
Thu, 29 Mar 2018 03:01:34 +0000 (23:01 -0400)
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
src/src/tls-gnu.c
src/src/tls-openssl.c
src/src/transports/smtp.c
src/src/transports/smtp.h

index 9722c006333d3c695a6a0911ac5a457ce958edfb..16d276ee8503f11b84997eccb1266c3e41c65436 100644 (file)
@@ -23941,6 +23941,24 @@ For testing purposes, this value can be overridden by the &%-oB%& command line
 option.
 
 
 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
 .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
index eed8c06dc9d01bbdabcb7cbbf8ed456fe651b110..0d20fea34ba3df75c876c4a940cbeb54e610dc21 100644 (file)
@@ -1681,7 +1681,7 @@ else
                        1, 0))
         || (rc = dane_verify_crt_raw(s, certlist, lsize,
                        gnutls_certificate_type_get(state->session),
                        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,
 # 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;
   (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;
 #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);
 
 
 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,
 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;
 
   {
   return rc;
 
   {
index 969a99d997a4658328e25e29948ba5dc29de4a55..c142bd05926dcab759a61484eeba75886c570187 100644 (file)
@@ -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
 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?  */
 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;
 
 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
   return FAIL;
 
 /* In OpenSSL, cipher components are separated by hyphens. In GnuTLS, they
index 23083f5d879c829a4ed5d7d0ff2a851fa696fb5d..2dfb5b73af4ad31e9f192db1d82d648207f70100 100644 (file)
@@ -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) },
       (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,
   { "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,
 #ifdef SUPPORT_DANE
   .hosts_try_dane =            NULL,
   .hosts_require_dane =                NULL,
+  .dane_require_tls_ciphers =  NULL,
 #endif
   .hosts_try_fastopen =                NULL,
 #ifndef DISABLE_PRDR
 #endif
   .hosts_try_fastopen =                NULL,
 #ifndef DISABLE_PRDR
index 749c6f778713e65fd8c5913cb73292edf21f30c3..7727c0c6db5d20cb108c276f92b5ecd1db9205d3 100644 (file)
@@ -32,6 +32,7 @@ typedef struct {
 #ifdef SUPPORT_DANE
   uschar *hosts_try_dane;
   uschar *hosts_require_dane;
 #ifdef SUPPORT_DANE
   uschar *hosts_try_dane;
   uschar *hosts_require_dane;
+  uschar *dane_require_tls_ciphers;
 #endif
   uschar *hosts_try_fastopen;
 #ifndef DISABLE_PRDR
 #endif
   uschar *hosts_try_fastopen;
 #ifndef DISABLE_PRDR