]> git.netwichtig.de Git - user/henk/code/exim.git/blobdiff - src/src/tls-gnu.c
TLS: Fix handling for server cert/key file SNI re-expansion forced-fail
[user/henk/code/exim.git] / src / src / tls-gnu.c
index 5b48980145fe7be620755d014fcc19fe9404143f..f8cc344061be0c9d910316f7d4fa298c1900d8bd 100644 (file)
@@ -6,6 +6,7 @@
 /* Copyright (c) University of Cambridge 1995 - 2018 */
 /* Copyright (c) Phil Pennock 2012 */
 /* See the file NOTICE for conditions of use and distribution. */
+/* SPDX-License-Identifier: GPL-2.0-or-later */
 
 /* This file provides TLS/SSL support for Exim using the GnuTLS library,
 one of the available supported implementations.  This file is #included into
@@ -121,6 +122,10 @@ require current GnuTLS, then we'll drop support for the ancient libraries).
 # endif
 #endif
 
+#if GNUTLS_VERSION_NUMBER >= 0x030702
+# define HAVE_GNUTLS_EXPORTER
+#endif
+
 #ifndef DISABLE_OCSP
 # include <gnutls/ocsp.h>
 #endif
@@ -393,7 +398,8 @@ tls_error_gnu(exim_gnutls_state_st * state, const uschar *prefix, int err,
 {
 return tls_error(prefix,
   state && err == GNUTLS_E_FATAL_ALERT_RECEIVED
-  ? US gnutls_alert_get_name(gnutls_alert_get(state->session))
+  ? string_sprintf("rxd alert: %s",
+                 US gnutls_alert_get_name(gnutls_alert_get(state->session)))
   : US gnutls_strerror(err),
   state ? state->host : NULL,
   errstr);
@@ -646,14 +652,20 @@ tlsp->channelbinding = NULL;
 #ifdef HAVE_GNUTLS_SESSION_CHANNEL_BINDING
   {
   gnutls_datum_t channel = {.data = NULL, .size = 0};
-  uschar * buf;
   int rc;
 
-# ifdef HAVE_GNUTLS_PRF_RFC5705
+# ifdef HAVE_GNUTLS_EXPORTER
+  if (gnutls_protocol_get_version(state->session) >= GNUTLS_TLS1_3)
+    {
+    rc = gnutls_session_channel_binding(state->session, GNUTLS_CB_TLS_EXPORTER, &channel);
+    tlsp->channelbind_exporter = TRUE;
+    }
+  else
+# elif defined(HAVE_GNUTLS_PRF_RFC5705)
   /* Older libraries may not have GNUTLS_TLS1_3 defined! */
   if (gnutls_protocol_get_version(state->session) > GNUTLS_TLS1_2)
     {
-    buf = store_get(32, state->host ? GET_TAINTED : GET_UNTAINTED);
+    uschar * buf = store_get(32, state->host ? GET_TAINTED : GET_UNTAINTED);
     rc = gnutls_prf_rfc5705(state->session,
                                (size_t)24,  "EXPORTER-Channel-Binding", (size_t)0, "",
                                32, CS buf);
@@ -670,11 +682,11 @@ tlsp->channelbinding = NULL;
     {
     int old_pool = store_pool;
     /* Declare the taintedness of the binding info.  On server, untainted; on
-    client, tainted - being the Finish msg from the server. */
+    client, tainted if we used the Finish msg from the server. */
 
     store_pool = POOL_PERM;
     tlsp->channelbinding = b64encode_taint(CUS channel.data, (int)channel.size,
-                                           state->host ? GET_TAINTED : GET_UNTAINTED);
+               !tlsp->channelbind_exporter && state->host ? GET_TAINTED : GET_UNTAINTED);
     store_pool = old_pool;
     DEBUG(D_tls) debug_printf("Have channel bindings cached for possible auth usage\n");
     }
@@ -1132,8 +1144,9 @@ tls_server_clienthello_cb(gnutls_session_t session, unsigned int htype,
   unsigned when, unsigned int incoming, const gnutls_datum_t * msg)
 {
 /* Call fn for each extension seen.  3.6.3 onwards */
-return gnutls_ext_raw_parse(NULL, tls_server_clienthello_ext, msg,
+int rc = gnutls_ext_raw_parse(NULL, tls_server_clienthello_ext, msg,
                           GNUTLS_EXT_RAW_FLAG_TLS_CLIENT_HELLO);
+return rc == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE ? 0 : rc;
 }
 
 
@@ -1281,7 +1294,7 @@ while (cfile = string_nextinlist(&clist, &csep, NULL, 0))
 
   if (!(kfile = string_nextinlist(&klist, &ksep, NULL, 0)))
     return tls_error(US"cert/key setup: out of keys", NULL, NULL, errstr);
-  else if ((rc = tls_add_certfile(state, NULL, cfile, kfile, errstr)) > 0)
+  else if ((rc = tls_add_certfile(state, NULL, cfile, kfile, errstr)) != OK)
     return rc;
   else
     {
@@ -1798,8 +1811,13 @@ D-H generation. */
 
 if (!state->lib_state.conn_certs)
   {
-  if (!Expand_check_tlsvar(tls_certificate, errstr))
+  if (  !Expand_check_tlsvar(tls_certificate, errstr)
+     || f.expand_string_forcedfail)
+    {
+    if (f.expand_string_forcedfail)
+      *errstr = US"expansion of tls_certificate failed";
     return DEFER;
+    }
 
   /* certificate is mandatory in server, optional in client */
 
@@ -1811,8 +1829,14 @@ if (!state->lib_state.conn_certs)
     else
       DEBUG(D_tls) debug_printf("TLS: no client certificate specified; okay\n");
 
-  if (state->tls_privatekey && !Expand_check_tlsvar(tls_privatekey, errstr))
+  if (  state->tls_privatekey && !Expand_check_tlsvar(tls_privatekey, errstr)
+     || f.expand_string_forcedfail
+     )
+    {
+    if (f.expand_string_forcedfail)
+      *errstr = US"expansion of tls_privatekey failed";
     return DEFER;
+    }
 
   /* tls_privatekey is optional, defaulting to same file as certificate */
 
@@ -1854,7 +1878,11 @@ if (!state->lib_state.conn_certs)
                              tls_ocsp_file,
 #endif
                              errstr)
-       )  ) return rc;
+       )  )
+      {
+      DEBUG(D_tls) debug_printf("load-cert: '%s'\n", *errstr);
+      return rc;
+      }
     }
   }
 else
@@ -2698,11 +2726,12 @@ if ((rc = tls_expand_session_files(state, &dummy_errstr)) != OK)
   {
   /* If the setup of certs/etc failed before handshake, TLS would not have
   been offered.  The best we can do now is abort. */
-  return GNUTLS_E_APPLICATION_ERROR_MIN;
+  DEBUG(D_tls) debug_printf("expansion for SNI-dependent session files failed\n");
+  return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
   }
 
 rc = tls_set_remaining_x509(state, &dummy_errstr);
-if (rc != OK) return GNUTLS_E_APPLICATION_ERROR_MIN;
+if (rc != OK) return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
 
 return 0;
 }
@@ -2731,25 +2760,25 @@ exim_gnutls_state_st * state = gnutls_session_get_ptr(session);
 
 if ((cert_list = gnutls_certificate_get_peers(session, &cert_list_size)))
   while (cert_list_size--)
-  {
-  if ((rc = import_cert(&cert_list[cert_list_size], &crt)) != GNUTLS_E_SUCCESS)
     {
-    DEBUG(D_tls) debug_printf("TLS: peer cert problem: depth %d: %s\n",
-      cert_list_size, gnutls_strerror(rc));
-    break;
-    }
+    if ((rc = import_cert(&cert_list[cert_list_size], &crt)) != GNUTLS_E_SUCCESS)
+      {
+      DEBUG(D_tls) debug_printf("TLS: peer cert problem: depth %d: %s\n",
+       cert_list_size, gnutls_strerror(rc));
+      break;
+      }
 
-  state->tlsp->peercert = crt;
-  if ((yield = event_raise(state->event_action,
-             US"tls:cert", string_sprintf("%d", cert_list_size), &errno)))
-    {
-    log_write(0, LOG_MAIN,
-             "SSL verify denied by event-action: depth=%d: %s",
-             cert_list_size, yield);
-    return 1;                     /* reject */
+    state->tlsp->peercert = crt;
+    if ((yield = event_raise(state->event_action,
+               US"tls:cert", string_sprintf("%d", cert_list_size), &errno)))
+      {
+      log_write(0, LOG_MAIN,
+               "SSL verify denied by event-action: depth=%d: %s",
+               cert_list_size, yield);
+      return 1;                     /* reject */
+      }
+    state->tlsp->peercert = NULL;
     }
-  state->tlsp->peercert = NULL;
-  }
 
 return 0;
 }
@@ -3278,7 +3307,7 @@ tls_retrieve_session(tls_support * tlsp, gnutls_session_t session,
 {
 tlsp->resumption = RESUME_SUPPORTED;
 
-if (continue_hostname) /* no host_lbserver available for tls_client_resmption_key() */
+if (!conn_args->have_lbserver)
   { DEBUG(D_tls) debug_printf("resumption not supported on continued-connection\n"); }
 else if (verify_check_given_host(CUSS &ob->tls_resumption_hosts, conn_args->host) == OK)
   {