LibreSSL: fix build for 3.5.0+ Bug 3074
[exim.git] / src / src / tls-openssl.c
index 2e09882d25b6dffe512a1b72e538334eb0de221f..e6a1c6fa4383868e9a85c4a249d2d1ae26c4eb62 100644 (file)
@@ -2,9 +2,10 @@
 *     Exim - an Internet mail transport agent    *
 *************************************************/
 
-/* Copyright (c) The Exim Maintainers 2020 - 2022 */
+/* Copyright (c) The Exim Maintainers 2020 - 2023 */
 /* Copyright (c) University of Cambridge 1995 - 2019 */
 /* See the file NOTICE for conditions of use and distribution. */
+/* SPDX-License-Identifier: GPL-2.0-or-later */
 
 /* Portions Copyright (c) The OpenSSL Project 1999 */
 
@@ -76,8 +77,10 @@ change this guard and punt the issue for a while longer. */
 #  define EXIM_HAVE_OPENSSL_KEYLOG
 #  define EXIM_HAVE_OPENSSL_CIPHER_GET_ID
 #  define EXIM_HAVE_SESSION_TICKET
-#  define EXIM_HAVE_OPESSL_TRACE
-#  define EXIM_HAVE_OPESSL_GET0_SERIAL
+#  define EXIM_HAVE_OPENSSL_TRACE
+#  define EXIM_HAVE_OPENSSL_GET0_SERIAL
+#  define EXIM_HAVE_OPENSSL_OCSP_RESP_GET0_CERTS
+#  define EXIM_HAVE_SSL_GET0_VERIFIED_CHAIN
 #  ifndef DISABLE_OCSP
 #   define EXIM_HAVE_OCSP
 #  endif
@@ -94,9 +97,13 @@ change this guard and punt the issue for a while longer. */
 #if LIBRESSL_VERSION_NUMBER >= 0x3040000fL
 # define EXIM_HAVE_OPENSSL_CIPHER_GET_ID
 #endif
+#if LIBRESSL_VERSION_NUMBER >= 0x3050000fL
+# define EXIM_HAVE_OPENSSL_OCSP_RESP_GET0_CERTS
+#endif
 
 #if !defined(LIBRESSL_VERSION_NUMBER) && (OPENSSL_VERSION_NUMBER >= 0x030000000L)
 # define EXIM_HAVE_EXPORT_CHNL_BNGNG
+# define EXIM_HAVE_OPENSSL_X509_STORE_GET1_ALL_CERTS
 #endif
 
 #if !defined(LIBRESSL_VERSION_NUMBER) \
@@ -117,6 +124,8 @@ change this guard and punt the issue for a while longer. */
 #  define OPENSSL_HAVE_NUM_TICKETS
 #  define EXIM_HAVE_OPENSSL_CIPHER_STD_NAME
 #  define EXIM_HAVE_EXP_CHNL_BNGNG
+#  define EXIM_HAVE_OPENSSL_OCSP_RESP_GET0_SIGNER
+#  define EXIM_HAVE_OPENSSL_SET1_GROUPS
 # else
 #  define OPENSSL_BAD_SRVR_OURCERT
 # endif
@@ -150,6 +159,7 @@ change this guard and punt the issue for a while longer. */
 # endif
 #endif
 
+#define TESTSUITE_TICKET_LIFE 10       /* seconds */
 /*************************************************
 *        OpenSSL option parse                    *
 *************************************************/
@@ -408,15 +418,16 @@ typedef struct exim_openssl_state {
   uschar *     privatekey;
   BOOL         is_server;
 #ifndef DISABLE_OCSP
-  STACK_OF(X509) *verify_stack;                /* chain for verifying the proof */
   union {
     struct {
       uschar        *file;
       const uschar  *file_expanded;
       ocsp_resplist *olist;
+      STACK_OF(X509) *verify_stack;    /* chain for verifying the proof */
     } server;
     struct {
       X509_STORE    *verify_store;     /* non-null if status requested */
+      uschar       *verify_errstr;     /* only if _required */
       BOOL         verify_required;
     } client;
   } u_ocsp;
@@ -440,7 +451,7 @@ exim_openssl_state_st state_server = {.is_server = TRUE};
 
 static int
 setup_certs(SSL_CTX * sctx, uschar ** certs, uschar * crl, host_item * host,
-    uschar ** errstr );
+    uschar ** errstr);
 
 /* Callbacks */
 #ifndef DISABLE_OCSP
@@ -669,12 +680,12 @@ if (dh_bitsize <= tls_dh_max_bits)
     }
   else
     DEBUG(D_tls)
-      debug_printf("Diffie-Hellman initialized from %s with %d-bit prime\n",
+      debug_printf(" Diffie-Hellman initialized from %s with %d-bit prime\n",
        dhexpanded ? dhexpanded : US"default", dh_bitsize);
   }
 else
   DEBUG(D_tls)
-    debug_printf("dhparams '%s' %d bits, is > tls_dh_max_bits limit of %d\n",
+    debug_printf(" dhparams '%s' %d bits, is > tls_dh_max_bits limit of %d\n",
        dhexpanded ? dhexpanded : US"default", dh_bitsize, tls_dh_max_bits);
 
 #if OPENSSL_VERSION_NUMBER < 0x30000000L
@@ -693,6 +704,41 @@ return TRUE;
 *               Initialize for ECDH              *
 *************************************************/
 
+/* "auto" needs to be handled carefully.
+OpenSSL <  1.0.2: we do not select anything, but fallback to prime256v1
+OpenSSL <  1.1.0: we have to call SSL_CTX_set_ecdh_auto
+                  (openssl/ssl.h defines SSL_CTRL_SET_ECDH_AUTO)
+OpenSSL >= 1.1.0: we do not set anything, the libray does autoselection
+                  https://github.com/openssl/openssl/commit/fe6ef2472db933f01b59cad82aa925736935984b
+
+*/
+
+static uschar *
+init_ecdh_auto(SSL_CTX * sctx)
+{
+#if OPENSSL_VERSION_NUMBER < 0x10002000L
+DEBUG(D_tls) debug_printf(
+  " ECDH OpenSSL < 1.0.2: temp key parameter settings: overriding \"auto\" with \"prime256v1\"\n");
+return US"prime256v1";
+
+#else
+# if defined SSL_CTRL_SET_ECDH_AUTO
+
+DEBUG(D_tls) debug_printf(
+  " ECDH OpenSSL 1.0.2+: temp key parameter settings: autoselection\n");
+SSL_CTX_set_ecdh_auto(sctx, 1);
+return NULL;
+
+# else
+
+DEBUG(D_tls) debug_printf(
+  " ECDH OpenSSL 1.1.0+: temp key parameter settings: library default selection\n");
+return NULL;
+
+# endif
+#endif
+}
+
 /* Load parameters for ECDH encryption.  Server only.
 
 For now, we stick to NIST P-256 because: it's simple and easy to configure;
@@ -723,64 +769,76 @@ init_ecdh(SSL_CTX * sctx, uschar ** errstr)
 return TRUE;
 #else
 
-uschar * exp_curve;
-int nid;
-BOOL rv;
-
 # ifndef EXIM_HAVE_ECDH
 DEBUG(D_tls)
-  debug_printf("No OpenSSL API to define ECDH parameters, skipping\n");
+  debug_printf(" No OpenSSL API to define ECDH parameters, skipping\n");
 return TRUE;
 # else
 
+uschar * exp_curve;
+int ngroups, rc, sep;
+const uschar * curves_list,  * curve;
+#  ifdef EXIM_HAVE_OPENSSL_SET1_GROUPS
+int nids[16];
+#  else
+int nids[1];
+#  endif
+
 if (!expand_check(tls_eccurve, US"tls_eccurve", &exp_curve, errstr))
   return FALSE;
+
+/* Is the option deliberately empty? */
+
 if (!exp_curve || !*exp_curve)
   return TRUE;
 
-/* "auto" needs to be handled carefully.
- * OpenSSL <  1.0.2: we do not select anything, but fallback to prime256v1
- * OpenSSL <  1.1.0: we have to call SSL_CTX_set_ecdh_auto
- *                   (openssl/ssl.h defines SSL_CTRL_SET_ECDH_AUTO)
- * OpenSSL >= 1.1.0: we do not set anything, the libray does autoselection
- *                   https://github.com/openssl/openssl/commit/fe6ef2472db933f01b59cad82aa925736935984b
- */
-if (Ustrcmp(exp_curve, "auto") == 0)
-  {
-#if OPENSSL_VERSION_NUMBER < 0x10002000L
-  DEBUG(D_tls) debug_printf(
-    "ECDH OpenSSL < 1.0.2: temp key parameter settings: overriding \"auto\" with \"prime256v1\"\n");
-  exp_curve = US"prime256v1";
-#else
-# if defined SSL_CTRL_SET_ECDH_AUTO
-  DEBUG(D_tls) debug_printf(
-    "ECDH OpenSSL 1.0.2+: temp key parameter settings: autoselection\n");
-  SSL_CTX_set_ecdh_auto(sctx, 1);
-  return TRUE;
-# else
-  DEBUG(D_tls) debug_printf(
-    "ECDH OpenSSL 1.1.0+: temp key parameter settings: default selection\n");
-  return TRUE;
-# endif
-#endif
-  }
+/* Limit the list to hardwired array size. Drop out if any element is "suto". */
 
-DEBUG(D_tls) debug_printf("ECDH: curve '%s'\n", exp_curve);
-if (  (nid = OBJ_sn2nid       (CCS exp_curve)) == NID_undef
-#   ifdef EXIM_HAVE_OPENSSL_EC_NIST2NID
-   && (nid = EC_curve_nist2nid(CCS exp_curve)) == NID_undef
-#   endif
-   )
-  {
-  tls_error(string_sprintf("Unknown curve name tls_eccurve '%s'", exp_curve),
-    NULL, NULL, errstr);
-  return FALSE;
-  }
+curves_list = exp_curve;
+sep = 0;
+for (ngroups = 0;
+       ngroups < nelem(nids)
+    && (curve = string_nextinlist(&curves_list, &sep, NULL, 0));
+    )
+  if (Ustrcmp(curve, "auto") == 0)
+    {
+    DEBUG(D_tls) if (ngroups > 0)
+      debug_printf(" tls_eccurve 'auto' item takes precedence\n");
+    if ((exp_curve = init_ecdh_auto(sctx))) break; /* have a curve name to set */
+    return TRUE;                                  /* all done */
+    }
+  else
+    ngroups++;
 
-# if OPENSSL_VERSION_NUMBER < 0x30000000L
+/* Translate to NIDs */
+
+curves_list = exp_curve;
+for (ngroups = 0; curve = string_nextinlist(&curves_list, &sep, NULL, 0);
+     ngroups++)
+  if (  (nids[ngroups] = OBJ_sn2nid       (CCS curve)) == NID_undef
+#  ifdef EXIM_HAVE_OPENSSL_EC_NIST2NID
+     && (nids[ngroups] = EC_curve_nist2nid(CCS curve)) == NID_undef
+#  endif
+     )
+    {
+    uschar * s = string_sprintf("Unknown curve name in tls_eccurve '%s'", curve);
+    DEBUG(D_tls) debug_printf("TLS error: %s\n", s);
+    if (errstr) *errstr = s;
+    return FALSE;
+    }
+
+#  ifdef EXIM_HAVE_OPENSSL_SET1_GROUPS
+/* Set the groups */
+
+if ((rc = SSL_CTX_set1_groups(sctx, nids, ngroups)) == 0)
+  tls_error(string_sprintf("Error enabling '%s' group(s)", exp_curve), NULL, NULL, errstr);
+else
+  DEBUG(D_tls) debug_printf(" ECDH: enabled '%s' group(s)\n", exp_curve);
+
+#  else                /* Cannot handle a list; only 1 element nids array */
  {
   EC_KEY * ecdh;
-  if (!(ecdh = EC_KEY_new_by_curve_name(nid)))
+  if (!(ecdh = EC_KEY_new_by_curve_name(nids[0])))
     {
     tls_error(US"Unable to create ec curve", NULL, NULL, errstr);
     return FALSE;
@@ -789,23 +847,15 @@ if (  (nid = OBJ_sn2nid       (CCS exp_curve)) == NID_undef
   /* The "tmp" in the name here refers to setting a temporary key
   not to the stability of the interface. */
 
-  if ((rv = SSL_CTX_set_tmp_ecdh(sctx, ecdh) == 0))
+  if ((rc = SSL_CTX_set_tmp_ecdh(sctx, ecdh)) == 0)
     tls_error(string_sprintf("Error enabling '%s' curve", exp_curve), NULL, NULL, errstr);
   else
-    DEBUG(D_tls) debug_printf("ECDH: enabled '%s' curve\n", exp_curve);
+    DEBUG(D_tls) debug_printf(" ECDH: enabled '%s' curve\n", exp_curve);
   EC_KEY_free(ecdh);
  }
+#  endif       /*!EXIM_HAVE_OPENSSL_SET1_GROUPS*/
 
-#else  /* v 3.0.0 + */
-
-if ((rv = SSL_CTX_set1_groups(sctx, &nid, 1)) == 0)
-  tls_error(string_sprintf("Error enabling '%s' group", exp_curve), NULL, NULL, errstr);
-else
-  DEBUG(D_tls) debug_printf("ECDH: enabled '%s' group\n", exp_curve);
-
-#endif
-
-return !rv;
+return !!rc;
 
 # endif        /*EXIM_HAVE_ECDH*/
 #endif /*OPENSSL_NO_ECDH*/
@@ -954,7 +1004,7 @@ Returns:    nothing
 */
 
 static void
-info_callback(SSL * s, int where, int ret)
+info_callback(const SSL * s, int where, int ret)
 {
 DEBUG(D_tls)
   {
@@ -1119,18 +1169,6 @@ if (preverify_ok == 0)
 else if (depth != 0)
   {
   DEBUG(D_tls) debug_printf("SSL verify ok: depth=%d SN=%s\n", depth, dn);
-#ifndef DISABLE_OCSP
-  if (tlsp == &tls_out && client_static_state->u_ocsp.client.verify_store)
-    {  /* client, wanting stapling  */
-    /* Add the server cert's signing chain as the one
-    for the verification of the OCSP stapled information. */
-
-    if (!X509_STORE_add_cert(client_static_state->u_ocsp.client.verify_store,
-                             cert))
-      ERR_clear_error();
-    sk_X509_push(client_static_state->verify_stack, cert);
-    }
-#endif
 #ifndef DISABLE_EVENT
     if (verify_event(tlsp, cert, depth, dn, calledp, optionalp, US"SSL"))
       return 0;                                /* reject, with peercert set */
@@ -1157,6 +1195,8 @@ else
     uschar * name;
     int rc;
     while ((name = string_nextinlist(&list, &sep, NULL, 0)))
+      {
+      DEBUG(D_tls|D_lookup) debug_printf_indent("%s suitable for cert, per OpenSSL?", name);
       if ((rc = X509_check_host(cert, CCS name, 0,
                  X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS
                  | X509_CHECK_FLAG_SINGLE_LABEL_SUBDOMAINS,
@@ -1168,8 +1208,11 @@ else
            tlsp == &tls_out ? deliver_host_address : sender_host_address);
          name = NULL;
          }
+       DEBUG(D_tls|D_lookup) debug_printf_indent("  yes\n");
        break;
        }
+      else DEBUG(D_tls|D_lookup) debug_printf_indent("  no\n");
+      }
     if (!name)
 #else
     if (!tls_is_name_for_cert(verify_cert_hostnames, cert))
@@ -1258,21 +1301,7 @@ DEBUG(D_tls) debug_printf("verify_callback_client_dane: %s depth %d %s\n",
 #endif
 
 if (preverify_ok == 1)
-  {
   tls_out.dane_verified = TRUE;
-#ifndef DISABLE_OCSP
-  if (client_static_state->u_ocsp.client.verify_store)
-    {  /* client, wanting stapling  */
-    /* Add the server cert's signing chain as the one
-    for the verification of the OCSP stapled information. */
-
-    if (!X509_STORE_add_cert(client_static_state->u_ocsp.client.verify_store,
-                             cert))
-      ERR_clear_error();
-    sk_X509_push(client_static_state->verify_stack, cert);
-    }
-#endif
-  }
 else
   {
   int err = X509_STORE_CTX_get_error(x509ctx);
@@ -1288,6 +1317,14 @@ return preverify_ok;
 
 
 #ifndef DISABLE_OCSP
+static void
+time_print(BIO * bp, const char * str, ASN1_GENERALIZEDTIME * time)
+{
+BIO_printf(bp, "\t%s: ", str);
+ASN1_GENERALIZEDTIME_print(bp, time);
+BIO_puts(bp, "\n");
+}
+
 /*************************************************
 *       Load OCSP information into state         *
 *************************************************/
@@ -1377,7 +1414,7 @@ if (!(basic_response = OCSP_response_get1_basic(resp)))
   goto bad;
   }
 
-sk = state->verify_stack;      /* set by setup_certs() / chain_from_pem_file() */
+sk = state->u_ocsp.server.verify_stack;        /* set by setup_certs() / chain_from_pem_file() */
 
 /* May need to expose ability to adjust those flags?
 OCSP_NOSIGS OCSP_NOVERIFY OCSP_NOCHAIN OCSP_NOCHECKS OCSP_NOEXPLICIT
@@ -1398,11 +1435,13 @@ cannot used the connection context store, as that would neatly
 handle the "system" case too, but there seems to be no library
 function for getting a stack from a store.
 [ In OpenSSL 1.1 - ?  X509_STORE_CTX_get0_chain(ctx) ? ]
+[ 3.0.0 - sk = X509_STORE_get1_all_certs(store) ]
 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) -
+(in 3.0.0 + it is public)
 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?  */
@@ -1412,7 +1451,7 @@ if ((i = OCSP_basic_verify(basic_response, sk, NULL, OCSP_NOVERIFY)) < 0)
   DEBUG(D_tls)
     {
     ERR_error_string_n(ERR_get_error(), ssl_errstring, sizeof(ssl_errstring));
-    debug_printf("OCSP response verify failure: %s\n", US ssl_errstring);
+    debug_printf("OCSP response has bad signature: %s\n", US ssl_errstring);
     }
   goto bad;
   }
@@ -1446,7 +1485,16 @@ if (status != V_OCSP_CERTSTATUS_GOOD)
 
 if (!OCSP_check_validity(thisupd, nextupd, EXIM_OCSP_SKEW_SECONDS, EXIM_OCSP_MAX_AGE))
   {
-  DEBUG(D_tls) debug_printf("OCSP status invalid times.\n");
+  DEBUG(D_tls)
+    {
+    BIO * bp = BIO_new(BIO_s_mem());
+    uschar * s = NULL;
+    int len;
+    time_print(bp, "This OCSP Update", thisupd);
+    if (nextupd) time_print(bp, "Next OCSP Update", nextupd);
+    if ((len = (int) BIO_get_mem_data(bp, CSS &s)) > 0) debug_printf("%.*s", len, s);
+    debug_printf("OCSP status invalid times.\n");
+    }
   goto bad;
   }
 
@@ -1554,8 +1602,13 @@ else
      )  )
     reexpand_tls_files_for_sni = TRUE;
 
-  if (!expand_check(state->certificate, US"tls_certificate", &expanded, errstr))
+  if (  !expand_check(state->certificate, US"tls_certificate", &expanded, errstr)
+     || f.expand_string_forcedfail)
+    {
+    if (f.expand_string_forcedfail)
+      *errstr = US"expansion of tls_certificate failed";
     return DEFER;
+    }
 
   if (expanded)
     if (state->is_server)
@@ -1623,9 +1676,14 @@ else
       if ((err = tls_add_certfile(sctx, state, expanded, errstr)))
        return err;
 
-  if (  state->privatekey
-     && !expand_check(state->privatekey, US"tls_privatekey", &expanded, errstr))
+  if (     state->privatekey
+        && !expand_check(state->privatekey, US"tls_privatekey", &expanded, errstr)
+     || f.expand_string_forcedfail)
+    {
+    if (f.expand_string_forcedfail)
+      *errstr = US"expansion of tls_privatekey failed";
     return DEFER;
+    }
 
   /* If expansion was forced to fail, key_expanded will be NULL. If the result
   of the expansion is an empty string, ignore it also, and assume the private
@@ -1700,13 +1758,13 @@ level. */
 
 DEBUG(D_tls)
   {
-  SSL_CTX_set_info_callback(ctx, (void (*)())info_callback);
-#if defined(EXIM_HAVE_OPESSL_TRACE) && !defined(OPENSSL_NO_SSL_TRACE)
+  SSL_CTX_set_info_callback(ctx, info_callback);
+#if defined(EXIM_HAVE_OPENSSL_TRACE) && !defined(OPENSSL_NO_SSL_TRACE)
   /* this needs a debug build of OpenSSL */
-  SSL_CTX_set_msg_callback(ctx, (void (*)())SSL_trace);
+  SSL_CTX_set_msg_callback(ctx, SSL_trace);
 #endif
 #ifdef OPENSSL_HAVE_KEYLOG_CB
-  SSL_CTX_set_keylog_callback(ctx, (void (*)())keylog_callback);
+  SSL_CTX_set_keylog_callback(ctx, keylog_callback);
 #endif
   }
 
@@ -1736,7 +1794,7 @@ state_server.lib_state.lib_ctx = ctx;
 
 if (opt_unset_or_noexpand(tls_dhparam))
   {
-  DEBUG(D_tls) debug_printf("TLS: preloading DH params for server\n");
+  DEBUG(D_tls) debug_printf("TLS: preloading DH params '%s' for server\n", tls_dhparam);
   if (init_dh(ctx, tls_dhparam, &dummy_errstr))
     state_server.lib_state.dh = TRUE;
   }
@@ -1744,7 +1802,7 @@ else
   DEBUG(D_tls) debug_printf("TLS: not preloading DH params for server\n");
 if (opt_unset_or_noexpand(tls_eccurve))
   {
-  DEBUG(D_tls) debug_printf("TLS: preloading ECDH curve for server\n");
+  DEBUG(D_tls) debug_printf("TLS: preloading ECDH curve '%s' for server\n", tls_eccurve);
   if (init_ecdh(ctx, &dummy_errstr))
     state_server.lib_state.ecdh = TRUE;
   }
@@ -1921,7 +1979,7 @@ else
 #if defined(EXIM_HAVE_INOTIFY) || defined(EXIM_HAVE_KEVENT)
 /* Invalidate the creds cached, by dropping the current ones.
 Call when we notice one of the source files has changed. */
+
 static void
 tls_server_creds_invalidate(void)
 {
@@ -1955,28 +2013,51 @@ tls_client_creds_invalidate(transport_instance * t)
 
 
 /* Extreme debug
+ * */
 #ifndef DISABLE_OCSP
-void
-x509_store_dump_cert_s_names(X509_STORE * store)
+static void
+debug_print_sn(const X509 * cert)
 {
-STACK_OF(X509_OBJECT) * roots= store->objs;
+X509_NAME * sn = X509_get_subject_name((X509 *)cert);
 static uschar name[256];
+if (X509_NAME_oneline(sn, CS name, sizeof(name)))
+  {
+  name[sizeof(name)-1] = '\0';
+  debug_printf(" %s\n", name);
+  }
+}
 
-for (int i= 0; i < sk_X509_OBJECT_num(roots); i++)
+static void
+x509_stack_dump_cert_s_names(const STACK_OF(X509) * sk)
+{
+if (!sk)
+  debug_printf(" (null)\n");
+else
   {
-  X509_OBJECT * tmp_obj= sk_X509_OBJECT_value(roots, i);
-  if(tmp_obj->type == X509_LU_X509)
-    {
-    X509_NAME * sn = X509_get_subject_name(tmp_obj->data.x509);
-    if (X509_NAME_oneline(sn, CS name, sizeof(name)))
-      {
-      name[sizeof(name)-1] = '\0';
-      debug_printf(" %s\n", name);
-      }
-    }
+  int idx = sk_X509_num(sk);
+  if (!idx)
+    debug_printf(" (empty)\n");
+  else
+    while (--idx >= 0) debug_print_sn(sk_X509_value(sk, idx));
   }
 }
-#endif
+
+static void
+x509_store_dump_cert_s_names(X509_STORE * store)
+{
+# ifdef EXIM_HAVE_OPENSSL_X509_STORE_GET1_ALL_CERTS
+if (!store)
+  debug_printf(" (no store)\n");
+else
+  {
+  STACK_OF(X509) * sk = X509_STORE_get1_all_certs(store);
+  x509_stack_dump_cert_s_names(sk);
+  sk_X509_pop_free(sk, X509_free);
+  }
+# endif
+}
+#endif /*!DISABLE_OCSP*/
+/*
 */
 
 
@@ -2012,7 +2093,7 @@ if (exim_tk.name[0])
   exim_tk_old = exim_tk;
   }
 
-if (f.running_in_test_harness) ssl_session_timeout = 6;
+if (f.running_in_test_harness) ssl_session_timeout = TESTSUITE_TICKET_LIFE;
 
 DEBUG(D_tls) debug_printf("OpenSSL: %s STEK\n", exim_tk.name[0] ? "rotating" : "creating");
 if (RAND_bytes(exim_tk.aes_key, sizeof(exim_tk.aes_key)) <= 0) return;
@@ -2179,13 +2260,13 @@ per https://www.openssl.org/docs/manmaster/man3/SSL_client_hello_cb_fn.html
 
 #ifdef EXIM_HAVE_OPENSSL_TLSEXT
 static int
-tls_servername_cb(SSL *s, int *ad ARG_UNUSED, void *arg)
+tls_servername_cb(SSL * s, int * ad ARG_UNUSED, void * arg)
 {
-const char *servername = SSL_get_servername(s, TLSEXT_NAMETYPE_host_name);
-exim_openssl_state_st *state = (exim_openssl_state_st *) arg;
+const char * servername = SSL_get_servername(s, TLSEXT_NAMETYPE_host_name);
+exim_openssl_state_st * state = (exim_openssl_state_st *) arg;
 int rc;
 int old_pool = store_pool;
-uschar * dummy_errstr;
+uschar * errstr;
 
 if (!servername)
   return SSL_TLSEXT_ERR_OK;
@@ -2205,13 +2286,13 @@ if (!reexpand_tls_files_for_sni)
 not confident that memcpy wouldn't break some internal reference counting.
 Especially since there's a references struct member, which would be off. */
 
-if (lib_ctx_new(&server_sni, NULL, &dummy_errstr) != OK)
+if (lib_ctx_new(&server_sni, NULL, &errstr) != OK)
   goto bad;
 
 /* Not sure how many of these are actually needed, since SSL object
 already exists.  Might even need this selfsame callback, for reneg? */
 
 {
+ {
   SSL_CTX * ctx = state_server.lib_state.lib_ctx;
   SSL_CTX_set_info_callback(server_sni, SSL_CTX_get_info_callback(ctx));
   SSL_CTX_set_mode(server_sni, SSL_CTX_get_mode(ctx));
@@ -2223,10 +2304,10 @@ already exists.  Might even need this selfsame callback, for reneg? */
   SSL_CTX_set_timeout(server_sni, SSL_CTX_get_timeout(ctx));
   SSL_CTX_set_tlsext_servername_callback(server_sni, tls_servername_cb);
   SSL_CTX_set_tlsext_servername_arg(server_sni, state);
 }
+ }
 
-if (  !init_dh(server_sni, state->dhparam, &dummy_errstr)
-   || !init_ecdh(server_sni, &dummy_errstr)
+if (  !init_dh(server_sni, state->dhparam, &errstr)
+   || !init_ecdh(server_sni, &errstr)
    )
   goto bad;
 
@@ -2245,7 +2326,7 @@ if (state->u_ocsp.server.file)
   {
   uschar * v_certs = tls_verify_certificates;
   if ((rc = setup_certs(server_sni, &v_certs, tls_crl, NULL,
-                       &dummy_errstr)) != OK)
+                       &errstr)) != OK)
     goto bad;
 
   if (v_certs && *v_certs)
@@ -2254,14 +2335,16 @@ if (state->u_ocsp.server.file)
 
 /* do this after setup_certs, because this can require the certs for verifying
 OCSP information. */
-if ((rc = tls_expand_session_files(server_sni, state, &dummy_errstr)) != OK)
+if ((rc = tls_expand_session_files(server_sni, state, &errstr)) != OK)
   goto bad;
 
 DEBUG(D_tls) debug_printf("Switching SSL context.\n");
 SSL_set_SSL_CTX(s, server_sni);
 return SSL_TLSEXT_ERR_OK;
 
-bad: return SSL_TLSEXT_ERR_ALERT_FATAL;
+bad:
+  log_write(0, LOG_MAIN|LOG_PANIC, "%s", errstr);
+  return SSL_TLSEXT_ERR_ALERT_FATAL;
 }
 #endif /* EXIM_HAVE_OPENSSL_TLSEXT */
 
@@ -2281,6 +2364,8 @@ static int
 tls_server_alpn_cb(SSL *ssl, const uschar ** out, uschar * outlen,
   const uschar * in, unsigned int inlen, void * arg)
 {
+gstring * g = NULL;
+
 server_seen_alpn = TRUE;
 DEBUG(D_tls)
   {
@@ -2311,12 +2396,19 @@ if (  inlen > 1         /* at least one name */
       }
   }
 
-/* More than one name from clilent, or name did not match our list. */
+/* More than one name from client, or name did not match our list. */
 
 /* This will be fatal to the TLS conn; would be nice to kill TCP also.
 Maybe as an option in future; for now leave control to the config (must-tls). */
 
-DEBUG(D_tls) debug_printf("TLS ALPN rejected\n");
+for (int pos = 0, siz; pos < inlen; pos += siz+1)
+  {
+  siz = in[pos];
+  if (pos + 1 + siz > inlen) siz = inlen - pos - 1;
+  g = string_append_listele_n(g, ':', in + pos + 1, siz);
+  }
+log_write(0, LOG_MAIN, "TLS ALPN (%Y) rejected", g);
+gstring_release_unused(g);
 return SSL_TLSEXT_ERR_ALERT_FATAL;
 }
 #endif /* EXIM_HAVE_ALPN */
@@ -2353,7 +2445,7 @@ tls_in.ocsp = OCSP_NOT_RESP;
 if (!olist)
   return SSL_TLSEXT_ERR_NOACK;
 
-#ifdef EXIM_HAVE_OPESSL_GET0_SERIAL
+#ifdef EXIM_HAVE_OPENSSL_GET0_SERIAL
  {
   const X509 * cert_sent = SSL_get_certificate(s);
   const ASN1_INTEGER * cert_serial = X509_get0_serialNumber(cert_sent);
@@ -2420,11 +2512,21 @@ return SSL_TLSEXT_ERR_OK;
 
 
 static void
-time_print(BIO * bp, const char * str, ASN1_GENERALIZEDTIME * time)
+add_chain_to_store(X509_STORE * store, STACK_OF(X509) * sk,
+  const char * debug_text)
 {
-BIO_printf(bp, "\t%s: ", str);
-ASN1_GENERALIZEDTIME_print(bp, time);
-BIO_puts(bp, "\n");
+int idx;
+
+DEBUG(D_tls)
+  {
+  debug_printf("chain for %s:\n", debug_text);
+  x509_stack_dump_cert_s_names(sk);
+  }
+if (sk)
+  if ((idx = sk_X509_num(sk)) > 0)
+    while (--idx >= 0)
+      X509_STORE_add_cert(store, sk_X509_value(sk, idx));
+
 }
 
 static int
@@ -2440,18 +2542,24 @@ int i;
 DEBUG(D_tls) debug_printf("Received TLS status callback (OCSP stapling):\n");
 len = SSL_get_tlsext_status_ocsp_resp(ssl, &p);
 if(!p)
                            /* Expect this when we requested ocsp but got none */
 {                            /* Expect this when we requested ocsp but got none */
   if (SSL_session_reused(ssl) && tls_out.ocsp == OCSP_VFIED)
     {
     DEBUG(D_tls) debug_printf(" null, but resumed; ocsp vfy stored with session is good\n");
     return 1;
     }
+
   if (cbinfo->u_ocsp.client.verify_required && LOGGING(tls_cipher))
     log_write(0, LOG_MAIN, "Required TLS certificate status not received");
   else
     DEBUG(D_tls) debug_printf(" null\n");
-  return cbinfo->u_ocsp.client.verify_required ? 0 : 1;
- }
+
+  if (!cbinfo->u_ocsp.client.verify_required)
+    return 1;
+  cbinfo->u_ocsp.client.verify_errstr =
+                       US"(SSL_connect) Required TLS certificate status not received";
+  return 0;
+  }
 
 if (!(rsp = d2i_OCSP_RESPONSE(NULL, &p, len)))
   {
@@ -2483,39 +2591,152 @@ if (!(bs = OCSP_response_get1_basic(rsp)))
 */
   {
     BIO * bp = NULL;
+    X509_STORE * verify_store = NULL;
+    BOOL have_verified_OCSP_signer = FALSE;
 #ifndef EXIM_HAVE_OCSP_RESP_COUNT
     STACK_OF(OCSP_SINGLERESP) * sresp = bs->tbsResponseData->responses;
 #endif
 
     DEBUG(D_tls) bp = BIO_new(BIO_s_mem());
 
-    /*OCSP_RESPONSE_print(bp, rsp, 0);   extreme debug: stapling content */
+    /* Use the CA & chain that verified the server cert to verify the stapled info */
+    /*XXX could we do an event here, for observability of ocsp?  What reasonable data could we give access to? */
+    /* Dates would be a start. Do we need another opaque variable type, as for certs, plus an extract expansion? */
+
+   {
+    /* If this routine is not available, we've avoided [in tls_client_start()]
+    asking for certificate-status under DANE, so this callback won't run for
+    that combination. It still will for non-DANE. */
+
+#if defined(EXIM_HAVE_OPENSSL_OCSP_RESP_GET0_SIGNER) && defined(SUPPORT_DANE)
+    X509 * signer;
+
+    if (  tls_out.dane_verified
+       && (have_verified_OCSP_signer =
+       OCSP_resp_get0_signer(bs, &signer, SSL_get0_verified_chain(ssl)) == 1))
+      {
+      DEBUG(D_tls)
+       debug_printf("signer for OCSP basicres is in the verified chain;"
+                     " shortcut its verification\n");
+      }
+    else
+#endif
+      {
+      STACK_OF(X509) * verified_chain;
+
+      verify_store = X509_STORE_new();
 
-    /* Use the chain that verified the server cert to verify the stapled info */
-    /* DEBUG(D_tls) x509_store_dump_cert_s_names(cbinfo->u_ocsp.client.verify_store); */
+      SSL_get0_chain_certs(ssl, &verified_chain);
+      add_chain_to_store(verify_store, verified_chain,
+                             "'current cert' per SSL_get0_chain_certs()");
+#ifdef EXIM_HAVE_SSL_GET0_VERIFIED_CHAIN
+      verified_chain = SSL_get0_verified_chain(ssl);
+      add_chain_to_store(verify_store, verified_chain,
+                             "SSL_get0_verified_chain()");
+#endif
+      }
+   }
+
+    DEBUG(D_tls)
+      {
+      debug_printf("Untrusted intermediate cert stack (from SSL_get_peer_cert_chain()):\n");
+      x509_stack_dump_cert_s_names(SSL_get_peer_cert_chain(ssl));
+
+      debug_printf("will use this CA store for verifying basicresp:\n");
+      x509_store_dump_cert_s_names(verify_store);
+
+      /* OCSP_RESPONSE_print(bp, rsp, 0);   extreme debug: stapling content */
+
+      debug_printf("certs contained in basicresp:\n");
+      x509_stack_dump_cert_s_names(
+#ifdef EXIM_HAVE_OPENSSL_OCSP_RESP_GET0_CERTS
+       OCSP_resp_get0_certs(bs)
+#else
+       bs->certs
+#endif
+       );
+
+#ifdef EXIM_HAVE_OPENSSL_X509_STORE_GET1_ALL_CERTS
+/* could do via X509_STORE_get0_objects(); not worth it just for debug info */
+       {
+       X509 * signer;
+       if (OCSP_resp_get0_signer(bs, &signer, X509_STORE_get1_all_certs(verify_store)) == 1)
+         {
+         debug_printf("found signer for basicres:\n");
+         debug_print_sn(signer);
+         }
+       else
+         {
+         debug_printf("failed to find signer for basicres:\n");
+         ERR_print_errors(bp);
+         }
+       }
+#endif
+
+      }
+
+    ERR_clear_error();
+
+    /* Under DANE the trust-anchor (at least in TA mode) is indicated by the TLSA
+    record in DNS, and probably is not the root of the chain of certificates. So
+    accept a partial chain for that case (and hope that anchor is visible for
+    verifying the OCSP stapling).
+    XXX for EE mode it won't even be that.  Does that make OCSP useless for EE?
+
+    Worse, for LetsEncrypt-mode (ocsp signer is leaf-signer) under DANE, the
+    data used within OpenSSL for the signer has nil pointers for signing
+    algorithms - and a crash results.  Avoid this by shortcutting verification,
+    having determined that the OCSP signer is in the (DANE-)validated set.
+    */
+
+#ifndef OCSP_PARTIAL_CHAIN     /* defined for 3.0.0 onwards */
+# define OCSP_PARTIAL_CHAIN 0
+#endif
 
-    if ((i = OCSP_basic_verify(bs, cbinfo->verify_stack,
-             cbinfo->u_ocsp.client.verify_store, OCSP_NOEXPLICIT)) <= 0)
+    if ((i = OCSP_basic_verify(bs, SSL_get_peer_cert_chain(ssl),
+               verify_store,
+#ifdef SUPPORT_DANE
+               tls_out.dane_verified
+               ? have_verified_OCSP_signer
+                 ? OCSP_NOVERIFY | OCSP_NOEXPLICIT
+                 : OCSP_PARTIAL_CHAIN | OCSP_NOEXPLICIT
+               :
+#endif
+               OCSP_NOEXPLICIT)) <= 0)
+      {
+      DEBUG(D_tls) debug_printf("OCSP_basic_verify() fail: returned %d\n", i);
       if (ERR_peek_error())
        {
        tls_out.ocsp = OCSP_FAILED;
        if (LOGGING(tls_cipher))
          {
-         const uschar * errstr = CUS ERR_reason_error_string(ERR_peek_error());
          static uschar peerdn[256];
+         const uschar * errstr;;
+
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+         ERR_peek_error_all(NULL, NULL, NULL, CCSS &errstr, NULL);
+         if (!errstr)
+#endif
+           errstr = CUS ERR_reason_error_string(ERR_peek_error());
+
          X509_NAME_oneline(X509_get_subject_name(SSL_get_peer_certificate(ssl)),
                                                  CS peerdn, sizeof(peerdn));
          log_write(0, LOG_MAIN,
                "[%s] %s Received TLS cert (DN: '%.*s') status response, "
                "itself unverifiable: %s",
-               sender_host_address, sender_host_name,
-               (int)sizeof(peerdn), peerdn,
-               errstr);
+               deliver_host_address, deliver_host,
+               (int)sizeof(peerdn), peerdn, errstr);
          }
        DEBUG(D_tls)
          {
          BIO_printf(bp, "OCSP response verify failure\n");
          ERR_print_errors(bp);
+  {
+  uschar * s = NULL;
+  int len = (int) BIO_get_mem_data(bp, CSS &s);
+  if (len > 0) debug_printf("%.*s", len, s);
+  BIO_reset(bp);
+  }
          OCSP_RESPONSE_print(bp, rsp, 0);
          }
        goto failed;
@@ -2523,6 +2744,7 @@ if (!(bs = OCSP_response_get1_basic(rsp)))
       else
        DEBUG(D_tls) debug_printf("no explicit trust for OCSP signing"
          " in the root CA certificate; ignoring\n");
+      }
 
     DEBUG(D_tls) debug_printf("OCSP response well-formed and signed OK\n");
 
@@ -2565,6 +2787,8 @@ if (!(bs = OCSP_response_get1_basic(rsp)))
        {
        tls_out.ocsp = OCSP_FAILED;
        DEBUG(D_tls) ERR_print_errors(bp);
+       cbinfo->u_ocsp.client.verify_errstr =
+                   US"(SSL_connect) Server certificate status is out-of-date";
        log_write(0, LOG_MAIN, "OCSP dates invalid");
        goto failed;
        }
@@ -2576,12 +2800,16 @@ if (!(bs = OCSP_response_get1_basic(rsp)))
        case V_OCSP_CERTSTATUS_GOOD:
          continue;     /* the idx loop */
        case V_OCSP_CERTSTATUS_REVOKED:
+         cbinfo->u_ocsp.client.verify_errstr =
+                       US"(SSL_connect) Server certificate revoked";
          log_write(0, LOG_MAIN, "Server certificate revoked%s%s",
              reason != -1 ? "; reason: " : "",
              reason != -1 ? OCSP_crl_reason_str(reason) : "");
          DEBUG(D_tls) time_print(bp, "Revocation Time", rev);
          break;
        default:
+         cbinfo->u_ocsp.client.verify_errstr =
+                       US"(SSL_connect) Server certificate has unknown status";
          log_write(0, LOG_MAIN,
              "Server certificate status unknown, in OCSP stapling");
          break;
@@ -2635,8 +2863,7 @@ tls_init(host_item * host, smtp_transport_options_block * ob,
   uschar *ocsp_file,
 #endif
   address_item *addr, exim_openssl_state_st ** caller_state,
-  tls_support * tlsp,
-  uschar ** errstr)
+  tls_support * tlsp, uschar ** errstr)
 {
 SSL_CTX * ctx;
 exim_openssl_state_st * state;
@@ -2798,7 +3025,7 @@ else
 
 #ifdef EXIM_HAVE_OPENSSL_TLSEXT
 # ifndef DISABLE_OCSP
-  if (!(state->verify_stack = sk_X509_new_null()))
+  if (!host && !(state->u_ocsp.server.verify_stack = sk_X509_new_null()))
     {
     DEBUG(D_tls) debug_printf("failed to create stack for stapling verify\n");
     return FAIL;
@@ -2847,11 +3074,12 @@ else                    /* client */
       DEBUG(D_tls) debug_printf("failed to create store for stapling verify\n");
       return FAIL;
       }
+
     SSL_CTX_set_tlsext_status_cb(ctx, tls_client_stapling_cb);
     SSL_CTX_set_tlsext_status_arg(ctx, state);
     }
 # endif
-#endif
+#endif /*EXIM_HAVE_OPENSSL_TLSEXT*/
 
 state->verify_cert_hostnames = NULL;
 
@@ -2990,7 +3218,7 @@ if (tlsp->peercert)
 *************************************************/
 
 #ifndef DISABLE_OCSP
-/* Load certs from file, return TRUE on success */
+/* In the server, load certs from file, return TRUE on success */
 
 static BOOL
 chain_from_pem_file(const uschar * file, STACK_OF(X509) ** vp)
@@ -3020,7 +3248,7 @@ repeated after a Server Name Indication.
 
 Arguments:
   sctx          SSL_CTX* to initialise
-  certs         certs file, returned expanded
+  certsp        certs file, returned expanded
   crl           CRL file or NULL
   host          NULL in a server; the remote host in a client
   errstr       error string pointer
@@ -3066,19 +3294,20 @@ if (expcerts && *expcerts)
        {
        STACK_OF(X509) * verify_stack =
 #ifndef DISABLE_OCSP
-         !host ? state_server.verify_stack :
+         !host ? state_server.u_ocsp.server.verify_stack :
 #endif
          NULL;
        STACK_OF(X509) ** vp = &verify_stack;
 
        file = expcerts; dir = NULL;
 #ifndef DISABLE_OCSP
-       /* In the server if we will be offering an OCSP proof, load chain from
+       /* In the server if we will be offering an OCSP proof; load chain from
        file for verifying the OCSP proof at load time. */
 
 /*XXX Glitch!   The file here is tls_verify_certs: the chain for verifying the client cert.
 This is inconsistent with the need to verify the OCSP proof of the server cert.
 */
+/* *debug_printf("file for checking server ocsp stapling is: %s\n", file); */
        if (  !host
           && statbuf.st_size > 0
           && state_server.u_ocsp.server.file
@@ -3278,7 +3507,7 @@ static uschar peerdn[256];
 if (tls_in.active.sock >= 0)
   {
   tls_error(US"STARTTLS received after TLS started", NULL, US"", errstr);
-  smtp_printf("554 Already in TLS\r\n", FALSE);
+  smtp_printf("554 Already in TLS\r\n", SP_NO_MORE);
   return FAIL;
   }
 
@@ -3304,7 +3533,7 @@ TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256
 
 if (state_server.lib_state.pri_string)
   { DEBUG(D_tls) debug_printf("TLS: cipher list was preloaded\n"); }
-else 
+else
   {
   if (!expand_check(tls_require_ciphers, US"tls_require_ciphers", &expciphers, errstr))
     return FAIL;
@@ -3398,7 +3627,7 @@ mode, the fflush() happens when smtp_getc() is called. */
 SSL_set_session_id_context(ssl, sid_ctx, Ustrlen(sid_ctx));
 if (!tls_in.on_connect)
   {
-  smtp_printf("220 TLS go ahead\r\n", FALSE);
+  smtp_printf("220 TLS go ahead\r\n", SP_NO_MORE);
   fflush(smtp_out);
   }
 
@@ -3707,7 +3936,6 @@ tls_retrieve_session(tls_support * tlsp, SSL * ssl)
 {
 if (tlsp->host_resumable)
   {
-  const uschar * key = tlsp->resume_index;
   dbdata_tls_session * dt;
   int len;
   open_db dbblock, * dbm_file;
@@ -3738,16 +3966,17 @@ if (tlsp->host_resumable)
 #ifdef EXIM_HAVE_SESSION_TICKET
          SSL_SESSION_get_ticket_lifetime_hint(ss);
 #else                  /* Use, fairly arbitrilarily, what we as server would */
-         f.running_in_test_harness ? 6 : ssl_session_timeout;
+         f.running_in_test_harness ? TESTSUITE_TICKET_LIFE : ssl_session_timeout;
 #endif
-       if (lifetime + dt->time_stamp < time(NULL))
+       time_t now = time(NULL), expires = lifetime + dt->time_stamp;
+       if (expires < now)
          {
-         DEBUG(D_tls) debug_printf("session expired\n");
+         DEBUG(D_tls) debug_printf("session expired (by " TIME_T_FMT "s from %lus)\n", now - expires, lifetime);
          dbfn_delete(dbm_file, tlsp->resume_index);
          }
        else if (SSL_set_session(ssl, ss))
          {
-         DEBUG(D_tls) debug_printf("good session\n");
+         DEBUG(D_tls) debug_printf("good session (" TIME_T_FMT "s left of %lus)\n", expires - now, lifetime);
          tlsp->resumption |= RESUME_CLIENT_SUGGESTED;
          tlsp->verify_override = dt->verify_override;
          tlsp->ocsp = dt->ocsp;
@@ -3841,7 +4070,7 @@ if (tlsp->host_resumable)
     tls_error(US"set ex_data", host, NULL, errstr);
     return FALSE;
     }
-  debug_printf("tls_exdata_idx %d cbinfo %p\n", tls_exdata_idx, client_static_state);
+  /* debug_printf("tls_exdata_idx %d cbinfo %p\n", tls_exdata_idx, client_static_state); */
   }
 
 tlsp->resumption = RESUME_SUPPORTED;
@@ -3956,7 +4185,6 @@ tlsp->tlsa_usage = 0;
 #ifndef DISABLE_OCSP
   {
 # ifdef SUPPORT_DANE
-  /*XXX this should be moved to caller, to be common across gnutls/openssl */
   if (  conn_args->dane
      && ob->hosts_request_ocsp[0] == '*'
      && ob->hosts_request_ocsp[1] == '\0'
@@ -3979,6 +4207,15 @@ tlsp->tlsa_usage = 0;
 # endif
       request_ocsp =
        verify_check_given_host(CUSS &ob->hosts_request_ocsp, host) == OK;
+
+# if defined(SUPPORT_DANE) && !defined(EXIM_HAVE_OPENSSL_OCSP_RESP_GET0_SIGNER)
+  if (conn_args->dane && (require_ocsp || request_ocsp))
+    {
+    DEBUG(D_tls) debug_printf("OpenSSL version to early to combine OCSP"
+                             " and DANE; disabling OCSP\n");
+    require_ocsp = request_ocsp = FALSE;
+    }
+# endif
   }
 #endif
 
@@ -4050,6 +4287,7 @@ if (conn_args->dane)
     tls_error(US"context init", host, NULL, errstr);
     return FALSE;
     }
+  DEBUG(D_tls) debug_printf("since dane-mode conn, not loading the usual CA bundle\n");
   }
 else
 
@@ -4186,7 +4424,12 @@ if (conn_args->dane)
 
 if (rc <= 0)
   {
-  tls_error(US"SSL_connect", host, sigalrm_seen ? US"timed out" : NULL, errstr);
+#ifndef DISABLE_OCSP
+  if (client_static_state->u_ocsp.client.verify_errstr)
+    { if (errstr) *errstr = client_static_state->u_ocsp.client.verify_errstr; }
+  else
+#endif
+    tls_error(US"SSL_connect", host, sigalrm_seen ? US"timed out" : NULL, errstr);
   return FALSE;
   }
 
@@ -4292,10 +4535,15 @@ switch(error)
 
   /* Handle genuine errors */
   case SSL_ERROR_SSL:
+    {
+    uschar * conn_info = smtp_get_connection_info();
+    if (Ustrncmp(conn_info, US"SMTP ", 5) == 0) conn_info += 5;
+    /* I'd like to get separated H= here, but too hard for now */
     ERR_error_string_n(ERR_get_error(), ssl_errstring, sizeof(ssl_errstring));
-    log_write(0, LOG_MAIN, "TLS error (SSL_read): %s", ssl_errstring);
+    log_write(0, LOG_MAIN, "TLS error (SSL_read): on %s %s", conn_info, ssl_errstring);
     ssl_xfer_error = TRUE;
     return FALSE;
+    }
 
   default:
     DEBUG(D_tls) debug_printf("Got SSL error %d\n", error);
@@ -4373,7 +4621,6 @@ tls_get_cache(unsigned lim)
 {
 #ifndef DISABLE_DKIM
 int n = ssl_xfer_buffer_hwm - ssl_xfer_buffer_lwm;
-debug_printf("tls_get_cache\n");
 if (n > lim)
   n = lim;
 if (n > 0)
@@ -4633,8 +4880,8 @@ if (do_shutdown > TLS_NO_SHUTDOWN)
 if (!o_ctx)            /* server side */
   {
 #ifndef DISABLE_OCSP
-  sk_X509_pop_free(state_server.verify_stack, X509_free);
-  state_server.verify_stack = NULL;
+  sk_X509_pop_free(state_server.u_ocsp.server.verify_stack, X509_free);
+  state_server.u_ocsp.server.verify_stack = NULL;
 #endif
 
   receive_getc =       smtp_getc;