tidying
[exim.git] / src / src / tls-openssl.c
index 298d8d4e176d9984ecd1822ed9aa43ec2add1867..eabe34f3159584a7801c238fd8edd3680e143805 100644 (file)
@@ -2,9 +2,10 @@
 *     Exim - an Internet mail transport agent    *
 *************************************************/
 
 *     Exim - an Internet mail transport agent    *
 *************************************************/
 
+/* Copyright (c) The Exim Maintainers 2020 - 2022 */
 /* Copyright (c) University of Cambridge 1995 - 2019 */
 /* Copyright (c) University of Cambridge 1995 - 2019 */
-/* Copyright (c) The Exim Maintainers 2020 */
 /* See the file NOTICE for conditions of use and distribution. */
 /* 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 */
 
 
 /* Portions Copyright (c) The OpenSSL Project 1999 */
 
@@ -48,6 +49,7 @@ functions from the OpenSSL library. */
 #if OPENSSL_VERSION_NUMBER >= 0x10100000L
 # define EXIM_HAVE_OCSP_RESP_COUNT
 # define OPENSSL_AUTO_SHA256
 #if OPENSSL_VERSION_NUMBER >= 0x10100000L
 # define EXIM_HAVE_OCSP_RESP_COUNT
 # define OPENSSL_AUTO_SHA256
+# define OPENSSL_MIN_PROTO_VERSION
 #else
 # define EXIM_HAVE_EPHEM_RSA_KEX
 # define EXIM_HAVE_RAND_PSEUDO
 #else
 # define EXIM_HAVE_EPHEM_RSA_KEX
 # define EXIM_HAVE_RAND_PSEUDO
@@ -77,6 +79,8 @@ change this guard and punt the issue for a while longer. */
 #  define EXIM_HAVE_SESSION_TICKET
 #  define EXIM_HAVE_OPESSL_TRACE
 #  define EXIM_HAVE_OPESSL_GET0_SERIAL
 #  define EXIM_HAVE_SESSION_TICKET
 #  define EXIM_HAVE_OPESSL_TRACE
 #  define EXIM_HAVE_OPESSL_GET0_SERIAL
+#  define EXIM_HAVE_OPESSL_OCSP_RESP_GET0_CERTS
+#  define EXIM_HAVE_SSL_GET0_VERIFIED_CHAIN
 #  ifndef DISABLE_OCSP
 #   define EXIM_HAVE_OCSP
 #  endif
 #  ifndef DISABLE_OCSP
 #   define EXIM_HAVE_OCSP
 #  endif
@@ -90,6 +94,15 @@ change this guard and punt the issue for a while longer. */
 # endif
 #endif
 
 # endif
 #endif
 
+#if LIBRESSL_VERSION_NUMBER >= 0x3040000fL
+# define EXIM_HAVE_OPENSSL_CIPHER_GET_ID
+#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) \
     || LIBRESSL_VERSION_NUMBER >= 0x20010000L
 # if !defined(OPENSSL_NO_ECDH)
 #if !defined(LIBRESSL_VERSION_NUMBER) \
     || LIBRESSL_VERSION_NUMBER >= 0x20010000L
 # if !defined(OPENSSL_NO_ECDH)
@@ -107,11 +120,17 @@ change this guard and punt the issue for a while longer. */
 #  define OPENSSL_HAVE_KEYLOG_CB
 #  define OPENSSL_HAVE_NUM_TICKETS
 #  define EXIM_HAVE_OPENSSL_CIPHER_STD_NAME
 #  define OPENSSL_HAVE_KEYLOG_CB
 #  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
 # else
 #  define OPENSSL_BAD_SRVR_OURCERT
 # endif
 #endif
 
 # else
 #  define OPENSSL_BAD_SRVR_OURCERT
 # endif
 #endif
 
+#if !defined(LIBRESSL_VERSION_NUMBER) && (OPENSSL_VERSION_NUMBER >= 0x010002000L)
+# define EXIM_HAVE_EXPORT_CHNL_BNGNG
+#endif
+
 #if !defined(EXIM_HAVE_OPENSSL_TLSEXT) && !defined(DISABLE_OCSP)
 # warning "OpenSSL library version too old; define DISABLE_OCSP in Makefile"
 # define DISABLE_OCSP
 #if !defined(EXIM_HAVE_OPENSSL_TLSEXT) && !defined(DISABLE_OCSP)
 # warning "OpenSSL library version too old; define DISABLE_OCSP in Makefile"
 # define DISABLE_OCSP
@@ -228,10 +247,14 @@ static exim_openssl_option exim_openssl_options[] = {
   { US"no_tlsv1", SSL_OP_NO_TLSv1 },
 #endif
 #ifdef SSL_OP_NO_TLSv1_1
   { US"no_tlsv1", SSL_OP_NO_TLSv1 },
 #endif
 #ifdef SSL_OP_NO_TLSv1_1
-# if SSL_OP_NO_TLSv1_1 == 0x00000400L
+# if OPENSSL_VERSION_NUMBER < 0x30000000L
+#  if SSL_OP_NO_TLSv1_1 == 0x00000400L
   /* Error in chosen value in 1.0.1a; see first item in CHANGES for 1.0.1b */
   /* Error in chosen value in 1.0.1a; see first item in CHANGES for 1.0.1b */
-#  warning OpenSSL 1.0.1a uses a bad value for SSL_OP_NO_TLSv1_1, ignoring
-# else
+#   warning OpenSSL 1.0.1a uses a bad value for SSL_OP_NO_TLSv1_1, ignoring
+#   define NO_SSL_OP_NO_TLSv1_1
+#  endif
+# endif
+# ifndef NO_SSL_OP_NO_TLSv1_1
   { US"no_tlsv1_1", SSL_OP_NO_TLSv1_1 },
 # endif
 #endif
   { US"no_tlsv1_1", SSL_OP_NO_TLSv1_1 },
 # endif
 #endif
@@ -390,15 +413,16 @@ typedef struct exim_openssl_state {
   uschar *     privatekey;
   BOOL         is_server;
 #ifndef DISABLE_OCSP
   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;
   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 */
     } server;
     struct {
       X509_STORE    *verify_store;     /* non-null if status requested */
+      uschar       *verify_errstr;     /* only if _required */
       BOOL         verify_required;
     } client;
   } u_ocsp;
       BOOL         verify_required;
     } client;
   } u_ocsp;
@@ -421,12 +445,14 @@ exim_openssl_state_st *client_static_state = NULL;        /*XXX should not use static;
 exim_openssl_state_st state_server = {.is_server = TRUE};
 
 static int
 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 );
+setup_certs(SSL_CTX * sctx, uschar ** certs, uschar * crl, host_item * host,
+    uschar ** errstr);
 
 /* Callbacks */
 #ifndef DISABLE_OCSP
 static int tls_server_stapling_cb(SSL *s, void *arg);
 
 /* Callbacks */
 #ifndef DISABLE_OCSP
 static int tls_server_stapling_cb(SSL *s, void *arg);
+static void x509_stack_dump_cert_s_names(const STACK_OF(X509) * sk);
+static void x509_store_dump_cert_s_names(X509_STORE * store);
 #endif
 
 
 #endif
 
 
@@ -541,23 +567,27 @@ EVP_add_digest(EVP_sha256());
 *************************************************/
 
 /* If dhparam is set, expand it, and load up the parameters for DH encryption.
 *************************************************/
 
 /* If dhparam is set, expand it, and load up the parameters for DH encryption.
+Server only.
 
 Arguments:
   sctx      The current SSL CTX (inbound or outbound)
   dhparam   DH parameter file or fixed parameter identity string
 
 Arguments:
   sctx      The current SSL CTX (inbound or outbound)
   dhparam   DH parameter file or fixed parameter identity string
-  host      connected host, if client; NULL if server
   errstr    error string pointer
 
 Returns:    TRUE if OK (nothing to set up, or setup worked)
 */
 
 static BOOL
   errstr    error string pointer
 
 Returns:    TRUE if OK (nothing to set up, or setup worked)
 */
 
 static BOOL
-init_dh(SSL_CTX *sctx, uschar *dhparam, const host_item *host, uschar ** errstr)
+init_dh(SSL_CTX * sctx, uschar * dhparam, uschar ** errstr)
 {
 {
-BIO *bio;
-DH *dh;
-uschar *dhexpanded;
-const char *pem;
+BIO * bio;
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
+DH * dh;
+#else
+EVP_PKEY * pkey;
+#endif
+uschar * dhexpanded;
+const char * pem;
 int dh_bitsize;
 
 if (!expand_check(dhparam, US"tls_dhparam", &dhexpanded, errstr))
 int dh_bitsize;
 
 if (!expand_check(dhparam, US"tls_dhparam", &dhexpanded, errstr))
@@ -570,7 +600,7 @@ else if (dhexpanded[0] == '/')
   if (!(bio = BIO_new_file(CS dhexpanded, "r")))
     {
     tls_error(string_sprintf("could not read dhparams file %s", dhexpanded),
   if (!(bio = BIO_new_file(CS dhexpanded, "r")))
     {
     tls_error(string_sprintf("could not read dhparams file %s", dhexpanded),
-          host, US strerror(errno), errstr);
+          NULL, US strerror(errno), errstr);
     return FALSE;
     }
   }
     return FALSE;
     }
   }
@@ -585,53 +615,80 @@ else
   if (!(pem = std_dh_prime_named(dhexpanded)))
     {
     tls_error(string_sprintf("Unknown standard DH prime \"%s\"", dhexpanded),
   if (!(pem = std_dh_prime_named(dhexpanded)))
     {
     tls_error(string_sprintf("Unknown standard DH prime \"%s\"", dhexpanded),
-        host, US strerror(errno), errstr);
+        NULL, US strerror(errno), errstr);
     return FALSE;
     }
   bio = BIO_new_mem_buf(CS pem, -1);
   }
 
     return FALSE;
     }
   bio = BIO_new_mem_buf(CS pem, -1);
   }
 
-if (!(dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL)))
+if (!(
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
+      dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL)
+#else
+      pkey = PEM_read_bio_Parameters_ex(bio, NULL, NULL, NULL)
+#endif
+   ) )
   {
   BIO_free(bio);
   tls_error(string_sprintf("Could not read tls_dhparams \"%s\"", dhexpanded),
   {
   BIO_free(bio);
   tls_error(string_sprintf("Could not read tls_dhparams \"%s\"", dhexpanded),
-      host, NULL, errstr);
+      NULL, NULL, errstr);
   return FALSE;
   }
 
 /* note: our default limit of 2236 is not a multiple of 8; the limit comes from
   return FALSE;
   }
 
 /* note: our default limit of 2236 is not a multiple of 8; the limit comes from
- * an NSS limit, and the GnuTLS APIs handle bit-sizes fine, so we went with
- * 2236.  But older OpenSSL can only report in bytes (octets), not bits.
- * If someone wants to dance at the edge, then they can raise the limit or use
- * current libraries. */
-#ifdef EXIM_HAVE_OPENSSL_DH_BITS
+an NSS limit, and the GnuTLS APIs handle bit-sizes fine, so we went with 2236.
+But older OpenSSL can only report in bytes (octets), not bits.  If someone wants
+to dance at the edge, then they can raise the limit or use current libraries. */
+
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
+# ifdef EXIM_HAVE_OPENSSL_DH_BITS
 /* Added in commit 26c79d5641d; `git describe --contains` says OpenSSL_1_1_0-pre1~1022
 /* Added in commit 26c79d5641d; `git describe --contains` says OpenSSL_1_1_0-pre1~1022
- * This predates OpenSSL_1_1_0 (before a, b, ...) so is in all 1.1.0 */
+This predates OpenSSL_1_1_0 (before a, b, ...) so is in all 1.1.0 */
 dh_bitsize = DH_bits(dh);
 dh_bitsize = DH_bits(dh);
-#else
+# else
 dh_bitsize = 8 * DH_size(dh);
 dh_bitsize = 8 * DH_size(dh);
+# endif
+#else  /* 3.0.0 + */
+dh_bitsize = EVP_PKEY_get_bits(pkey);
 #endif
 
 #endif
 
-/* Even if it is larger, we silently return success rather than cause things
- * to fail out, so that a too-large DH will not knock out all TLS; it's a
- * debatable choice. */
-if (dh_bitsize > tls_dh_max_bits)
+/* Even if it is larger, we silently return success rather than cause things to
+fail out, so that a too-large DH will not knock out all TLS; it's a debatable
+choice.  Likewise for a failing attempt to set one. */
+
+if (dh_bitsize <= tls_dh_max_bits)
   {
   {
-  DEBUG(D_tls)
-    debug_printf("dhparams file %d bits, is > tls_dh_max_bits limit of %d\n",
-        dh_bitsize, tls_dh_max_bits);
+  if (
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
+      SSL_CTX_set_tmp_dh(sctx, dh)
+#else
+      SSL_CTX_set0_tmp_dh_pkey(sctx, pkey)
+#endif
+      == 0)
+    {
+    ERR_error_string_n(ERR_get_error(), ssl_errstring, sizeof(ssl_errstring));
+    log_write(0, LOG_MAIN|LOG_PANIC, "TLS error (D-H param setting '%s'): %s",
+       dhexpanded ? dhexpanded : US"default", ssl_errstring);
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+    /* EVP_PKEY_free(pkey);  crashes */
+#endif
+    }
+  else
+    DEBUG(D_tls)
+      debug_printf("Diffie-Hellman initialized from %s with %d-bit prime\n",
+       dhexpanded ? dhexpanded : US"default", dh_bitsize);
   }
 else
   }
 else
-  {
-  SSL_CTX_set_tmp_dh(sctx, dh);
   DEBUG(D_tls)
   DEBUG(D_tls)
-    debug_printf("Diffie-Hellman initialized from %s with %d-bit prime\n",
-      dhexpanded ? dhexpanded : US"default", dh_bitsize);
-  }
+    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
 DH_free(dh);
 DH_free(dh);
-BIO_free(bio);
+#endif
+/* The EVP_PKEY ownership stays with the ctx; do not free it */
 
 
+BIO_free(bio);
 return TRUE;
 }
 
 return TRUE;
 }
 
@@ -642,7 +699,7 @@ return TRUE;
 *               Initialize for ECDH              *
 *************************************************/
 
 *               Initialize for ECDH              *
 *************************************************/
 
-/* Load parameters for ECDH encryption.
+/* Load parameters for ECDH encryption.  Server only.
 
 For now, we stick to NIST P-256 because: it's simple and easy to configure;
 it avoids any patent issues that might bite redistributors; despite events in
 
 For now, we stick to NIST P-256 because: it's simple and easy to configure;
 it avoids any patent issues that might bite redistributors; despite events in
@@ -660,27 +717,22 @@ Patches welcome.
 
 Arguments:
   sctx      The current SSL CTX (inbound or outbound)
 
 Arguments:
   sctx      The current SSL CTX (inbound or outbound)
-  host      connected host, if client; NULL if server
   errstr    error string pointer
 
 Returns:    TRUE if OK (nothing to set up, or setup worked)
 */
 
 static BOOL
   errstr    error string pointer
 
 Returns:    TRUE if OK (nothing to set up, or setup worked)
 */
 
 static BOOL
-init_ecdh(SSL_CTX * sctx, host_item * host, uschar ** errstr)
+init_ecdh(SSL_CTX * sctx, uschar ** errstr)
 {
 #ifdef OPENSSL_NO_ECDH
 return TRUE;
 #else
 
 {
 #ifdef OPENSSL_NO_ECDH
 return TRUE;
 #else
 
-EC_KEY * ecdh;
 uschar * exp_curve;
 int nid;
 BOOL rv;
 
 uschar * exp_curve;
 int nid;
 BOOL rv;
 
-if (host)      /* No ECDH setup for clients, only for servers */
-  return TRUE;
-
 # ifndef EXIM_HAVE_ECDH
 DEBUG(D_tls)
   debug_printf("No OpenSSL API to define ECDH parameters, skipping\n");
 # ifndef EXIM_HAVE_ECDH
 DEBUG(D_tls)
   debug_printf("No OpenSSL API to define ECDH parameters, skipping\n");
@@ -727,25 +779,38 @@ if (  (nid = OBJ_sn2nid       (CCS exp_curve)) == NID_undef
    )
   {
   tls_error(string_sprintf("Unknown curve name tls_eccurve '%s'", exp_curve),
    )
   {
   tls_error(string_sprintf("Unknown curve name tls_eccurve '%s'", exp_curve),
-    host, NULL, errstr);
+    NULL, NULL, errstr);
   return FALSE;
   }
 
   return FALSE;
   }
 
-if (!(ecdh = EC_KEY_new_by_curve_name(nid)))
-  {
-  tls_error(US"Unable to create ec curve", host, NULL, errstr);
-  return FALSE;
-  }
+# if OPENSSL_VERSION_NUMBER < 0x30000000L
+ {
+  EC_KEY * ecdh;
+  if (!(ecdh = EC_KEY_new_by_curve_name(nid)))
+    {
+    tls_error(US"Unable to create ec curve", NULL, NULL, errstr);
+    return FALSE;
+    }
 
 
-/* The "tmp" in the name here refers to setting a temporary key
-not to the stability of the interface. */
+  /* 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))
-  tls_error(string_sprintf("Error enabling '%s' curve", exp_curve), host, NULL, errstr);
+  if ((rv = 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);
+  EC_KEY_free(ecdh);
+ }
+
+#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
 else
-  DEBUG(D_tls) debug_printf("ECDH: enabled '%s' curve\n", exp_curve);
+  DEBUG(D_tls) debug_printf("ECDH: enabled '%s' group\n", exp_curve);
+
+#endif
 
 
-EC_KEY_free(ecdh);
 return !rv;
 
 # endif        /*EXIM_HAVE_ECDH*/
 return !rv;
 
 # endif        /*EXIM_HAVE_ECDH*/
@@ -758,6 +823,7 @@ return !rv;
 *        Expand key and cert file specs          *
 *************************************************/
 
 *        Expand key and cert file specs          *
 *************************************************/
 
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
 /*
 Arguments:
   s          SSL connection (not used)
 /*
 Arguments:
   s          SSL connection (not used)
@@ -777,14 +843,14 @@ BIGNUM *bn = BN_new();
 
 DEBUG(D_tls) debug_printf("Generating %d bit RSA key...\n", keylength);
 
 
 DEBUG(D_tls) debug_printf("Generating %d bit RSA key...\n", keylength);
 
-#ifdef EXIM_HAVE_RSA_GENKEY_EX
+# ifdef EXIM_HAVE_RSA_GENKEY_EX
 if (  !BN_set_word(bn, (unsigned long)RSA_F4)
    || !(rsa_key = RSA_new())
    || !RSA_generate_key_ex(rsa_key, keylength, bn, NULL)
    )
 if (  !BN_set_word(bn, (unsigned long)RSA_F4)
    || !(rsa_key = RSA_new())
    || !RSA_generate_key_ex(rsa_key, keylength, bn, NULL)
    )
-#else
+# else
 if (!(rsa_key = RSA_generate_key(keylength, RSA_F4, NULL, NULL)))
 if (!(rsa_key = RSA_generate_key(keylength, RSA_F4, NULL, NULL)))
-#endif
+# endif
 
   {
   ERR_error_string_n(ERR_get_error(), ssl_errstring, sizeof(ssl_errstring));
 
   {
   ERR_error_string_n(ERR_get_error(), ssl_errstring, sizeof(ssl_errstring));
@@ -794,6 +860,7 @@ if (!(rsa_key = RSA_generate_key(keylength, RSA_F4, NULL, NULL)))
   }
 return rsa_key;
 }
   }
 return rsa_key;
 }
+#endif /* pre-3.0.0 */
 
 
 
 
 
 
@@ -807,7 +874,6 @@ tls_install_selfsign(SSL_CTX * sctx, uschar ** errstr)
 {
 X509 * x509 = NULL;
 EVP_PKEY * pkey;
 {
 X509 * x509 = NULL;
 EVP_PKEY * pkey;
-RSA * rsa;
 X509_NAME * name;
 uschar * where;
 
 X509_NAME * name;
 uschar * where;
 
@@ -821,12 +887,19 @@ if (!(x509 = X509_new()))
   goto err;
 
 where = US"generating pkey";
   goto err;
 
 where = US"generating pkey";
-if (!(rsa = rsa_callback(NULL, 0, 2048)))
-  goto err;
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
+ {
+  RSA * rsa;
+  if (!(rsa = rsa_callback(NULL, 0, 2048)))
+    goto err;
 
 
-where = US"assigning pkey";
-if (!EVP_PKEY_assign_RSA(pkey, rsa))
-  goto err;
+  where = US"assigning pkey";
+  if (!EVP_PKEY_assign_RSA(pkey, rsa))
+    goto err;
+ }
+#else
+pkey = EVP_RSA_gen(2048);
+#endif
 
 X509_set_version(x509, 2);                             /* N+1 - version 3 */
 ASN1_INTEGER_set(X509_get_serialNumber(x509), 1);
 
 X509_set_version(x509, 2);                             /* N+1 - version 3 */
 ASN1_INTEGER_set(X509_get_serialNumber(x509), 1);
@@ -887,36 +960,35 @@ Returns:    nothing
 */
 
 static void
 */
 
 static void
-info_callback(SSL *s, int where, int ret)
+info_callback(SSL * s, int where, int ret)
 {
 DEBUG(D_tls)
   {
 {
 DEBUG(D_tls)
   {
-  const uschar * str;
-
-  if (where & SSL_ST_CONNECT)
-     str = US"SSL_connect";
-  else if (where & SSL_ST_ACCEPT)
-     str = US"SSL_accept";
-  else
-     str = US"SSL info (undefined)";
+  gstring * g = NULL;
+
+  if (where & SSL_ST_CONNECT) g = string_append_listele(g, ',', US"SSL_connect");
+  if (where & SSL_ST_ACCEPT)  g = string_append_listele(g, ',', US"SSL_accept");
+  if (where & SSL_CB_LOOP)    g = string_append_listele(g, ',', US"state_chg");
+  if (where & SSL_CB_EXIT)    g = string_append_listele(g, ',', US"hshake_exit");
+  if (where & SSL_CB_READ)    g = string_append_listele(g, ',', US"read");
+  if (where & SSL_CB_WRITE)   g = string_append_listele(g, ',', US"write");
+  if (where & SSL_CB_ALERT)   g = string_append_listele(g, ',', US"alert");
+  if (where & SSL_CB_HANDSHAKE_START) g = string_append_listele(g, ',', US"hshake_start");
+  if (where & SSL_CB_HANDSHAKE_DONE)  g = string_append_listele(g, ',', US"hshake_done");
 
   if (where & SSL_CB_LOOP)
 
   if (where & SSL_CB_LOOP)
-     debug_printf("%s: %s\n", str, SSL_state_string_long(s));
+     debug_printf("SSL %s: %s\n", g->s, SSL_state_string_long(s));
   else if (where & SSL_CB_ALERT)
   else if (where & SSL_CB_ALERT)
-    debug_printf("SSL3 alert %s:%s:%s\n",
-         str = where & SSL_CB_READ ? US"read" : US"write",
+    debug_printf("SSL %s %s:%s\n", g->s,
          SSL_alert_type_string_long(ret), SSL_alert_desc_string_long(ret));
   else if (where & SSL_CB_EXIT)
     {
          SSL_alert_type_string_long(ret), SSL_alert_desc_string_long(ret));
   else if (where & SSL_CB_EXIT)
     {
-    if (ret == 0)
-      debug_printf("%s: failed in %s\n", str, SSL_state_string_long(s));
-    else if (ret < 0)
-      debug_printf("%s: error in %s\n", str, SSL_state_string_long(s));
+    if (ret <= 0)
+      debug_printf("SSL %s: %s in %s\n", g->s,
+       ret == 0 ? "failed" : "error", SSL_state_string_long(s));
     }
     }
-  else if (where & SSL_CB_HANDSHAKE_START)
-     debug_printf("%s: hshake start: %s\n", str, SSL_state_string_long(s));
-  else if (where & SSL_CB_HANDSHAKE_DONE)
-     debug_printf("%s: hshake done: %s\n", str, SSL_state_string_long(s));
+  else if (where & (SSL_CB_HANDSHAKE_START | SSL_CB_HANDSHAKE_DONE))
+     debug_printf("SSL %s: %s\n", g->s, SSL_state_string_long(s));
   }
 }
 
   }
 }
 
@@ -954,7 +1026,7 @@ if (ev)
   old_cert = tlsp->peercert;
   tlsp->peercert = X509_dup(cert);
   /* NB we do not bother setting peerdn */
   old_cert = tlsp->peercert;
   tlsp->peercert = X509_dup(cert);
   /* NB we do not bother setting peerdn */
-  if ((yield = event_raise(ev, US"tls:cert", string_sprintf("%d", depth))))
+  if ((yield = event_raise(ev, US"tls:cert", string_sprintf("%d", depth), &errno)))
     {
     log_write(0, LOG_MAIN, "[%s] %s verify denied by event-action: "
                "depth=%d cert=%s: %s",
     {
     log_write(0, LOG_MAIN, "[%s] %s verify denied by event-action: "
                "depth=%d cert=%s: %s",
@@ -1053,18 +1125,6 @@ if (preverify_ok == 0)
 else if (depth != 0)
   {
   DEBUG(D_tls) debug_printf("SSL verify ok: depth=%d SN=%s\n", depth, dn);
 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 */
 #ifndef DISABLE_EVENT
     if (verify_event(tlsp, cert, depth, dn, calledp, optionalp, US"SSL"))
       return 0;                                /* reject, with peercert set */
@@ -1192,21 +1252,7 @@ DEBUG(D_tls) debug_printf("verify_callback_client_dane: %s depth %d %s\n",
 #endif
 
 if (preverify_ok == 1)
 #endif
 
 if (preverify_ok == 1)
-  {
   tls_out.dane_verified = TRUE;
   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);
 else
   {
   int err = X509_STORE_CTX_get_error(x509ctx);
@@ -1222,6 +1268,14 @@ return preverify_ok;
 
 
 #ifndef DISABLE_OCSP
 
 
 #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         *
 *************************************************/
 /*************************************************
 *       Load OCSP information into state         *
 *************************************************/
@@ -1247,7 +1301,6 @@ OCSP_BASICRESP * basic_response;
 OCSP_SINGLERESP * single_response;
 ASN1_GENERALIZEDTIME * rev, * thisupd, * nextupd;
 STACK_OF(X509) * sk;
 OCSP_SINGLERESP * single_response;
 ASN1_GENERALIZEDTIME * rev, * thisupd, * nextupd;
 STACK_OF(X509) * sk;
-unsigned long verify_flags;
 int status, reason, i;
 
 DEBUG(D_tls)
 int status, reason, i;
 
 DEBUG(D_tls)
@@ -1312,20 +1365,20 @@ if (!(basic_response = OCSP_response_get1_basic(resp)))
   goto bad;
   }
 
   goto bad;
   }
 
-sk = state->verify_stack;
-verify_flags = OCSP_NOVERIFY; /* check sigs, but not purpose */
+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
 OCSP_TRUSTOTHER OCSP_NOINTERN */
 
 
 /* May need to expose ability to adjust those flags?
 OCSP_NOSIGS OCSP_NOVERIFY OCSP_NOCHAIN OCSP_NOCHECKS OCSP_NOEXPLICIT
 OCSP_TRUSTOTHER OCSP_NOINTERN */
 
-/* This does a full verify on the OCSP proof before we load it for serving
-up; possibly overkill - just date-checks might be nice enough.
+/* This does a partial verify (only the signer link, not the whole chain-to-CA)
+on the OCSP proof before we load it for serving up; possibly overkill -
+just date-checks might be nice enough.
 
 OCSP_basic_verify takes a "store" arg, but does not
 
 OCSP_basic_verify takes a "store" arg, but does not
-use it for the chain verification, which is all we do
-when OCSP_NOVERIFY is set.  The content from the wire
-"basic_response" and a cert-stack "sk" are all that is used.
+use it for the chain verification, when OCSP_NOVERIFY is set.
+The content from the wire "basic_response" and a cert-stack "sk" are all
+that is used.
 
 We have a stack, loaded in setup_certs() if tls_verify_certificates
 was a file (not a directory, or "system").  It is unfortunate we
 
 We have a stack, loaded in setup_certs() if tls_verify_certificates
 was a file (not a directory, or "system").  It is unfortunate we
@@ -1333,21 +1386,23 @@ 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) ? ]
 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) -
 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 + is 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?  */
 
 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?  */
 
-if ((i = OCSP_basic_verify(basic_response, sk, NULL, verify_flags)) < 0)
+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(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;
   }
     }
   goto bad;
   }
@@ -1381,7 +1436,16 @@ if (status != V_OCSP_CERTSTATUS_GOOD)
 
 if (!OCSP_check_validity(thisupd, nextupd, EXIM_OCSP_SKEW_SECONDS, EXIM_OCSP_MAX_AGE))
   {
 
 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;
   }
 
   goto bad;
   }
 
@@ -1391,7 +1455,7 @@ supply_response:
   ocsp_resplist ** op = &state->u_ocsp.server.olist, * oentry;
   while (oentry = *op)
     op = &oentry->next;
   ocsp_resplist ** op = &state->u_ocsp.server.olist, * oentry;
   while (oentry = *op)
     op = &oentry->next;
-  *op = oentry = store_get(sizeof(ocsp_resplist), FALSE);
+  *op = oentry = store_get(sizeof(ocsp_resplist), GET_UNTAINTED);
   oentry->next = NULL;
   oentry->resp = resp;
   }
   oentry->next = NULL;
   oentry->resp = resp;
   }
@@ -1413,12 +1477,12 @@ return;
 
 
 static void
 
 
 static void
-ocsp_free_response_list(exim_openssl_state_st * cbinfo)
+ocsp_free_response_list(exim_openssl_state_st * state)
 {
 {
-for (ocsp_resplist * olist = cbinfo->u_ocsp.server.olist; olist;
+for (ocsp_resplist * olist = state->u_ocsp.server.olist; olist;
      olist = olist->next)
   OCSP_RESPONSE_free(olist->resp);
      olist = olist->next)
   OCSP_RESPONSE_free(olist->resp);
-cbinfo->u_ocsp.server.olist = NULL;
+state->u_ocsp.server.olist = NULL;
 }
 #endif /*!DISABLE_OCSP*/
 
 }
 #endif /*!DISABLE_OCSP*/
 
@@ -1510,6 +1574,11 @@ else
       if (olist && !*olist)
        olist = NULL;
 
       if (olist && !*olist)
        olist = NULL;
 
+      /* If doing a re-expand after SNI, avoid reloading the OCSP
+      responses when the list of filenames has not changed.
+      The creds-invali on content change wipes file_expanded, so that
+      always reloads here. */
+
       if (  state->u_ocsp.server.file_expanded && olist
         && (Ustrcmp(olist, state->u_ocsp.server.file_expanded) == 0))
        {
       if (  state->u_ocsp.server.file_expanded && olist
         && (Ustrcmp(olist, state->u_ocsp.server.file_expanded) == 0))
        {
@@ -1587,11 +1656,24 @@ return OK;
 * One-time init credentials for server and client *
 **************************************************/
 
 * One-time init credentials for server and client *
 **************************************************/
 
+static void
+normalise_ciphers(uschar ** ciphers, const uschar * pre_expansion_ciphers)
+{
+uschar * s = *ciphers;
+
+if (!s || !Ustrchr(s, '_')) return;    /* no change needed */
+
+if (s == pre_expansion_ciphers)
+  s = string_copy(s);                  /* get writable copy */
+
+for (uschar * t = s; *t; t++) if (*t == '_') *t = '-';
+*ciphers = s;
+}
+
 static int
 server_load_ciphers(SSL_CTX * ctx, exim_openssl_state_st * state,
   uschar * ciphers, uschar ** errstr)
 {
 static int
 server_load_ciphers(SSL_CTX * ctx, exim_openssl_state_st * state,
   uschar * ciphers, uschar ** errstr)
 {
-for (uschar * s = ciphers; *s; s++ ) if (*s == '_') *s = '-';
 DEBUG(D_tls) debug_printf("required ciphers: %s\n", ciphers);
 if (!SSL_CTX_set_cipher_list(ctx, CS ciphers))
   return tls_error(US"SSL_CTX_set_cipher_list", NULL, NULL, errstr);
 DEBUG(D_tls) debug_printf("required ciphers: %s\n", ciphers);
 if (!SSL_CTX_set_cipher_list(ctx, CS ciphers))
   return tls_error(US"SSL_CTX_set_cipher_list", NULL, NULL, errstr);
@@ -1654,64 +1736,25 @@ 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");
 if (opt_unset_or_noexpand(tls_dhparam))
   {
   DEBUG(D_tls) debug_printf("TLS: preloading DH params for server\n");
-  if (init_dh(ctx, tls_dhparam, NULL, &dummy_errstr))
+  if (init_dh(ctx, tls_dhparam, &dummy_errstr))
     state_server.lib_state.dh = TRUE;
   }
     state_server.lib_state.dh = TRUE;
   }
+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");
 if (opt_unset_or_noexpand(tls_eccurve))
   {
   DEBUG(D_tls) debug_printf("TLS: preloading ECDH curve for server\n");
-  if (init_ecdh(ctx, NULL, &dummy_errstr))
+  if (init_ecdh(ctx, &dummy_errstr))
     state_server.lib_state.ecdh = TRUE;
   }
     state_server.lib_state.ecdh = TRUE;
   }
-
-#if defined(EXIM_HAVE_INOTIFY) || defined(EXIM_HAVE_KEVENT)
-/* If we can, preload the server-side cert, key and ocsp */
-
-if (  opt_set_and_noexpand(tls_certificate)
-# ifndef DISABLE_OCSP
-   && opt_unset_or_noexpand(tls_ocsp_file)
-#endif
-   && opt_unset_or_noexpand(tls_privatekey))
-  {
-  /* Set watches on the filenames.  The implementation does de-duplication
-  so we can just blindly do them all.  */
-
-  if (  tls_set_watch(tls_certificate, TRUE)
-# ifndef DISABLE_OCSP
-     && tls_set_watch(tls_ocsp_file, TRUE)
-#endif
-     && tls_set_watch(tls_privatekey, TRUE))
-    {
-    state_server.certificate = tls_certificate;
-    state_server.privatekey = tls_privatekey;
-#ifndef DISABLE_OCSP
-    state_server.u_ocsp.server.file = tls_ocsp_file;
-#endif
-
-    DEBUG(D_tls) debug_printf("TLS: preloading server certs\n");
-    if (tls_expand_session_files(ctx, &state_server, &dummy_errstr) == OK)
-      state_server.lib_state.conn_certs = TRUE;
-    }
-  }
-else if (  !tls_certificate && !tls_privatekey
-# ifndef DISABLE_OCSP
-       && !tls_ocsp_file
-#endif
-   )
-  {            /* Generate & preload a selfsigned cert. No files to watch. */
-  if (tls_expand_session_files(ctx, &state_server, &dummy_errstr) == OK)
-    {
-    state_server.lib_state.conn_certs = TRUE;
-    lifetime = f.running_in_test_harness ? 2 : 60 * 60;                /* 1 hour */
-    }
-  }
 else
 else
-  DEBUG(D_tls) debug_printf("TLS: not preloading server certs\n");
-
+  DEBUG(D_tls) debug_printf("TLS: not preloading ECDH curve for server\n");
 
 
+#if defined(EXIM_HAVE_INOTIFY) || defined(EXIM_HAVE_KEVENT)
 /* If we can, preload the Authorities for checking client certs against.
 Actual choice to do verify is made (tls_{,try_}verify_hosts)
 /* If we can, preload the Authorities for checking client certs against.
 Actual choice to do verify is made (tls_{,try_}verify_hosts)
-at TLS conn startup */
+at TLS conn startup.
+Do this before the server ocsp so that its info can verify the ocsp. */
 
 if (  opt_set_and_noexpand(tls_verify_certificates)
    && opt_unset_or_noexpand(tls_crl))
 
 if (  opt_set_and_noexpand(tls_verify_certificates)
    && opt_unset_or_noexpand(tls_crl))
@@ -1722,15 +1765,60 @@ if (  opt_set_and_noexpand(tls_verify_certificates)
      && tls_set_watch(tls_verify_certificates, FALSE)
      && tls_set_watch(tls_crl, FALSE))
     {
      && tls_set_watch(tls_verify_certificates, FALSE)
      && tls_set_watch(tls_crl, FALSE))
     {
+    uschar * v_certs = tls_verify_certificates;
     DEBUG(D_tls) debug_printf("TLS: preloading CA bundle for server\n");
 
     DEBUG(D_tls) debug_printf("TLS: preloading CA bundle for server\n");
 
-    if (setup_certs(ctx, tls_verify_certificates, tls_crl, NULL, &dummy_errstr)
-       == OK)
+    if (setup_certs(ctx, &v_certs, tls_crl, NULL, &dummy_errstr) == OK)
       state_server.lib_state.cabundle = TRUE;
       state_server.lib_state.cabundle = TRUE;
-    }
+
+    /* If we can, preload the server-side cert, key and ocsp */
+
+    if (  opt_set_and_noexpand(tls_certificate)
+# ifndef DISABLE_OCSP
+       && opt_unset_or_noexpand(tls_ocsp_file)
+# endif
+       && opt_unset_or_noexpand(tls_privatekey))
+      {
+      /* Set watches on the filenames.  The implementation does de-duplication
+      so we can just blindly do them all.  */
+
+      if (  tls_set_watch(tls_certificate, TRUE)
+# ifndef DISABLE_OCSP
+        && tls_set_watch(tls_ocsp_file, TRUE)
+# endif
+        && tls_set_watch(tls_privatekey, TRUE))
+       {
+       state_server.certificate = tls_certificate;
+       state_server.privatekey = tls_privatekey;
+#ifndef DISABLE_OCSP
+       state_server.u_ocsp.server.file = tls_ocsp_file;
+# endif
+
+       DEBUG(D_tls) debug_printf("TLS: preloading server certs\n");
+       if (tls_expand_session_files(ctx, &state_server, &dummy_errstr) == OK)
+         state_server.lib_state.conn_certs = TRUE;
+       }
+      }
+    else if (  !tls_certificate && !tls_privatekey
+# ifndef DISABLE_OCSP
+           && !tls_ocsp_file
+# endif
+       )
+      {                /* Generate & preload a selfsigned cert. No files to watch. */
+      if (tls_expand_session_files(ctx, &state_server, &dummy_errstr) == OK)
+       {
+       state_server.lib_state.conn_certs = TRUE;
+       lifetime = f.running_in_test_harness ? 2 : 60 * 60;             /* 1 hour */
+       }
+      }
+    else
+      DEBUG(D_tls) debug_printf("TLS: not preloading server certs\n");
+       }
   }
 else
   DEBUG(D_tls) debug_printf("TLS: not preloading CA bundle for server\n");
   }
 else
   DEBUG(D_tls) debug_printf("TLS: not preloading CA bundle for server\n");
+
+
 #endif /* EXIM_HAVE_INOTIFY */
 
 
 #endif /* EXIM_HAVE_INOTIFY */
 
 
@@ -1739,6 +1827,7 @@ else
 if (opt_set_and_noexpand(tls_require_ciphers))
   {
   DEBUG(D_tls) debug_printf("TLS: preloading cipher list for server\n");
 if (opt_set_and_noexpand(tls_require_ciphers))
   {
   DEBUG(D_tls) debug_printf("TLS: preloading cipher list for server\n");
+  normalise_ciphers(&tls_require_ciphers, tls_require_ciphers);
   if (server_load_ciphers(ctx, &state_server, tls_require_ciphers,
                          &dummy_errstr) == OK)
     state_server.lib_state.pri_string = TRUE;
   if (server_load_ciphers(ctx, &state_server, tls_require_ciphers,
                          &dummy_errstr) == OK)
     state_server.lib_state.pri_string = TRUE;
@@ -1774,19 +1863,6 @@ ob->tls_preload.lib_ctx = ctx;
 
 tpt_dummy_state.lib_state = ob->tls_preload;
 
 
 tpt_dummy_state.lib_state = ob->tls_preload;
 
-if (opt_unset_or_noexpand(tls_dhparam))
-  {
-  DEBUG(D_tls) debug_printf("TLS: preloading DH params for transport '%s'\n", t->name);
-  if (init_dh(ctx, tls_dhparam, NULL, &dummy_errstr))
-    ob->tls_preload.dh = TRUE;
-  }
-if (opt_unset_or_noexpand(tls_eccurve))
-  {
-  DEBUG(D_tls) debug_printf("TLS: preloading ECDH curve for transport '%s'\n", t->name);
-  if (init_ecdh(ctx, NULL, &dummy_errstr))
-    ob->tls_preload.ecdh = TRUE;
-  }
-
 #if defined(EXIM_HAVE_INOTIFY) || defined(EXIM_HAVE_KEVENT)
 if (  opt_set_and_noexpand(ob->tls_certificate)
    && opt_unset_or_noexpand(ob->tls_privatekey))
 #if defined(EXIM_HAVE_INOTIFY) || defined(EXIM_HAVE_KEVENT)
 if (  opt_set_and_noexpand(ob->tls_certificate)
    && opt_unset_or_noexpand(ob->tls_privatekey))
@@ -1824,10 +1900,11 @@ if (  opt_set_and_noexpand(ob->tls_verify_certificates)
        && tls_set_watch(ob->tls_crl, FALSE)
      )
     {
        && tls_set_watch(ob->tls_crl, FALSE)
      )
     {
+    uschar * v_certs = ob->tls_verify_certificates;
     DEBUG(D_tls)
       debug_printf("TLS: preloading CA bundle for transport '%s'\n", t->name);
 
     DEBUG(D_tls)
       debug_printf("TLS: preloading CA bundle for transport '%s'\n", t->name);
 
-    if (setup_certs(ctx, ob->tls_verify_certificates,
+    if (setup_certs(ctx, &v_certs,
          ob->tls_crl, dummy_host, &dummy_errstr) == OK)
       ob->tls_preload.cabundle = TRUE;
     }
          ob->tls_crl, dummy_host, &dummy_errstr) == OK)
       ob->tls_preload.cabundle = TRUE;
     }
@@ -1843,12 +1920,15 @@ 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. */
 #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)
 {
 SSL_CTX_free(state_server.lib_state.lib_ctx);
 state_server.lib_state = null_tls_preload;
 static void
 tls_server_creds_invalidate(void)
 {
 SSL_CTX_free(state_server.lib_state.lib_ctx);
 state_server.lib_state = null_tls_preload;
+#ifndef DISABLE_OCSP
+state_server.u_ocsp.server.file_expanded = NULL;
+#endif
 }
 
 
 }
 
 
@@ -1874,28 +1954,51 @@ tls_client_creds_invalidate(transport_instance * t)
 
 
 /* Extreme debug
 
 
 /* Extreme debug
+ * */
 #ifndef DISABLE_OCSP
 #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];
 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*/
+/*
 */
 
 
 */
 
 
@@ -1907,7 +2010,11 @@ typedef struct {                 /* Session ticket encryption key */
 
   const EVP_CIPHER *   aes_cipher;
   uschar               aes_key[32];    /* size needed depends on cipher. aes_128 implies 128/8 = 16? */
 
   const EVP_CIPHER *   aes_cipher;
   uschar               aes_key[32];    /* size needed depends on cipher. aes_128 implies 128/8 = 16? */
+# if OPENSSL_VERSION_NUMBER < 0x30000000L
   const EVP_MD *       hmac_hash;
   const EVP_MD *       hmac_hash;
+# else
+  const uschar *       hmac_hashname;
+# endif
   uschar               hmac_key[16];
   time_t               renew;
   time_t               expire;
   uschar               hmac_key[16];
   time_t               renew;
   time_t               expire;
@@ -1936,7 +2043,11 @@ if (RAND_bytes(exim_tk.name+1, sizeof(exim_tk.name)-1) <= 0) return;
 
 exim_tk.name[0] = 'E';
 exim_tk.aes_cipher = EVP_aes_256_cbc();
 
 exim_tk.name[0] = 'E';
 exim_tk.aes_cipher = EVP_aes_256_cbc();
+# if OPENSSL_VERSION_NUMBER < 0x30000000L
 exim_tk.hmac_hash = EVP_sha256();
 exim_tk.hmac_hash = EVP_sha256();
+# else
+exim_tk.hmac_hashname = US "sha256";
+# endif
 exim_tk.expire = t + ssl_session_timeout;
 exim_tk.renew = t + ssl_session_timeout/2;
 }
 exim_tk.expire = t + ssl_session_timeout;
 exim_tk.renew = t + ssl_session_timeout/2;
 }
@@ -1956,10 +2067,49 @@ return memcmp(name, exim_tk.name, sizeof(exim_tk.name)) == 0 ? &exim_tk
   : NULL;
 }
 
   : NULL;
 }
 
+
+static int
+tk_hmac_init(
+# if OPENSSL_VERSION_NUMBER < 0x30000000L
+  HMAC_CTX * hctx,
+#else
+  EVP_MAC_CTX * hctx,
+#endif
+  exim_stek * key
+  )
+{
+/*XXX will want these dependent on the ssl session strength */
+# if OPENSSL_VERSION_NUMBER < 0x30000000L
+  HMAC_Init_ex(hctx, key->hmac_key, sizeof(key->hmac_key),
+               key->hmac_hash, NULL);
+#else
+ {
+  OSSL_PARAM params[3];
+  uschar * hk = string_copy(key->hmac_hashname);       /* need nonconst */
+  params[0] = OSSL_PARAM_construct_octet_string("key", key->hmac_key, sizeof(key->hmac_key));
+  params[1] = OSSL_PARAM_construct_utf8_string("digest", CS hk, 0);
+  params[2] = OSSL_PARAM_construct_end();
+  if (EVP_MAC_CTX_set_params(hctx, params) == 0)
+    {
+    DEBUG(D_tls) debug_printf("EVP_MAC_CTX_set_params: %s\n",
+      ERR_reason_error_string(ERR_get_error()));
+    return 0; /* error in mac initialisation */
+    }
+}
+#endif
+return 1;
+}
+
 /* Callback for session tickets, on server */
 static int
 ticket_key_callback(SSL * ssl, uschar key_name[16],
 /* Callback for session tickets, on server */
 static int
 ticket_key_callback(SSL * ssl, uschar key_name[16],
-  uschar * iv, EVP_CIPHER_CTX * c_ctx, HMAC_CTX * hctx, int enc)
+  uschar * iv, EVP_CIPHER_CTX * c_ctx,
+# if OPENSSL_VERSION_NUMBER < 0x30000000L
+  HMAC_CTX * hctx,
+#else
+  EVP_MAC_CTX * hctx,
+#endif
+  int enc)
 {
 tls_support * tlsp = state_server.tlsp;
 exim_stek * key;
 {
 tls_support * tlsp = state_server.tlsp;
 exim_stek * key;
@@ -1977,9 +2127,7 @@ if (enc)
   memcpy(key_name, key->name, 16);
   DEBUG(D_tls) debug_printf("STEK expire " TIME_T_FMT "\n", key->expire - time(NULL));
 
   memcpy(key_name, key->name, 16);
   DEBUG(D_tls) debug_printf("STEK expire " TIME_T_FMT "\n", key->expire - time(NULL));
 
-  /*XXX will want these dependent on the ssl session strength */
-  HMAC_Init_ex(hctx, key->hmac_key, sizeof(key->hmac_key),
-               key->hmac_hash, NULL);
+  if (tk_hmac_init(hctx, key) == 0) return 0;
   EVP_EncryptInit_ex(c_ctx, key->aes_cipher, NULL, key->aes_key, iv);
 
   DEBUG(D_tls) debug_printf("ticket created\n");
   EVP_EncryptInit_ex(c_ctx, key->aes_cipher, NULL, key->aes_key, iv);
 
   DEBUG(D_tls) debug_printf("ticket created\n");
@@ -2002,8 +2150,7 @@ else
     return 0;
     }
 
     return 0;
     }
 
-  HMAC_Init_ex(hctx, key->hmac_key, sizeof(key->hmac_key),
-               key->hmac_hash, NULL);
+  if (tk_hmac_init(hctx, key) == 0) return 0;
   EVP_DecryptInit_ex(c_ctx, key->aes_cipher, NULL, key->aes_key, iv);
 
   DEBUG(D_tls) debug_printf("ticket usable, STEK expire " TIME_T_FMT "\n", key->expire - now);
   EVP_DecryptInit_ex(c_ctx, key->aes_cipher, NULL, key->aes_key, iv);
 
   DEBUG(D_tls) debug_printf("ticket usable, STEK expire " TIME_T_FMT "\n", key->expire - now);
@@ -2016,7 +2163,7 @@ else
   return key->renew < now ? 2 : 1;
   }
 }
   return key->renew < now ? 2 : 1;
   }
 }
-#endif
+#endif /* !DISABLE_TLS_RESUME */
 
 
 
 
 
 
@@ -2070,7 +2217,7 @@ DEBUG(D_tls) debug_printf("Received TLS SNI \"%s\"%s\n", servername,
 
 /* Make the extension value available for expansion */
 store_pool = POOL_PERM;
 
 /* Make the extension value available for expansion */
 store_pool = POOL_PERM;
-tls_in.sni = string_copy_taint(US servername, TRUE);
+tls_in.sni = string_copy_taint(US servername, GET_TAINTED);
 store_pool = old_pool;
 
 if (!reexpand_tls_files_for_sni)
 store_pool = old_pool;
 
 if (!reexpand_tls_files_for_sni)
@@ -2086,18 +2233,22 @@ if (lib_ctx_new(&server_sni, NULL, &dummy_errstr) != OK)
 /* Not sure how many of these are actually needed, since SSL object
 already exists.  Might even need this selfsame callback, for reneg? */
 
 /* 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));
   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));
+#ifdef OPENSSL_MIN_PROTO_VERSION
+  SSL_CTX_set_min_proto_version(server_sni, SSL3_VERSION);
+#endif
   SSL_CTX_set_options(server_sni, SSL_CTX_get_options(ctx));
   SSL_CTX_set_options(server_sni, SSL_CTX_get_options(ctx));
+  SSL_CTX_clear_options(server_sni, ~SSL_CTX_get_options(ctx));
   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);
   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, NULL, &dummy_errstr)
-   || !init_ecdh(server_sni, NULL, &dummy_errstr)
+if (  !init_dh(server_sni, state->dhparam, &dummy_errstr)
+   || !init_ecdh(server_sni, &dummy_errstr)
    )
   goto bad;
 
    )
   goto bad;
 
@@ -2114,14 +2265,12 @@ if (state->u_ocsp.server.file)
 #endif
 
   {
 #endif
 
   {
-  uschar * expcerts;
-  if (  !expand_check(tls_verify_certificates, US"tls_verify_certificates",
-                 &expcerts, &dummy_errstr)
-     || (rc = setup_certs(server_sni, expcerts, tls_crl, NULL,
+  uschar * v_certs = tls_verify_certificates;
+  if ((rc = setup_certs(server_sni, &v_certs, tls_crl, NULL,
                        &dummy_errstr)) != OK)
     goto bad;
 
                        &dummy_errstr)) != OK)
     goto bad;
 
-  if (expcerts && *expcerts)
+  if (v_certs && *v_certs)
     setup_cert_verify(server_sni, FALSE, verify_callback_server);
   }
 
     setup_cert_verify(server_sni, FALSE, verify_callback_server);
   }
 
@@ -2178,7 +2327,7 @@ if (  inlen > 1           /* at least one name */
   for (uschar * name; name = string_nextinlist(&list, &sep, NULL, 0); )
     if (Ustrncmp(in+1, name, in[0]) == 0)
       {
   for (uschar * name; name = string_nextinlist(&list, &sep, NULL, 0); )
     if (Ustrncmp(in+1, name, in[0]) == 0)
       {
-      *out = in;                       /* we checked for exactly one, so can just point to it */
+      *out = in+1;                     /* we checked for exactly one, so can just point to it */
       *outlen = inlen;
       return SSL_TLSEXT_ERR_OK;                /* use ALPN */
       }
       *outlen = inlen;
       return SSL_TLSEXT_ERR_OK;                /* use ALPN */
       }
@@ -2231,9 +2380,6 @@ if (!olist)
   const X509 * cert_sent = SSL_get_certificate(s);
   const ASN1_INTEGER * cert_serial = X509_get0_serialNumber(cert_sent);
   const BIGNUM * cert_bn = ASN1_INTEGER_to_BN(cert_serial, NULL);
   const X509 * cert_sent = SSL_get_certificate(s);
   const ASN1_INTEGER * cert_serial = X509_get0_serialNumber(cert_sent);
   const BIGNUM * cert_bn = ASN1_INTEGER_to_BN(cert_serial, NULL);
-  const X509_NAME * cert_issuer = X509_get_issuer_name(cert_sent);
-  uschar * chash;
-  uint chash_len;
 
   for (; olist; olist = olist->next)
     {
 
   for (; olist; olist = olist->next)
     {
@@ -2296,15 +2442,25 @@ return SSL_TLSEXT_ERR_OK;
 
 
 static void
 
 
 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
 }
 
 static int
-tls_client_stapling_cb(SSL *s, void *arg)
+tls_client_stapling_cb(SSL * ssl, void * arg)
 {
 exim_openssl_state_st * cbinfo = arg;
 const unsigned char * p;
 {
 exim_openssl_state_st * cbinfo = arg;
 const unsigned char * p;
@@ -2314,16 +2470,26 @@ OCSP_BASICRESP * bs;
 int i;
 
 DEBUG(D_tls) debug_printf("Received TLS status callback (OCSP stapling):\n");
 int i;
 
 DEBUG(D_tls) debug_printf("Received TLS status callback (OCSP stapling):\n");
-len = SSL_get_tlsext_status_ocsp_resp(s, &p);
+len = SSL_get_tlsext_status_ocsp_resp(ssl, &p);
 if(!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");
   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)))
   {
 
 if (!(rsp = d2i_OCSP_RESPONSE(NULL, &p, len)))
   {
@@ -2355,33 +2521,160 @@ if (!(bs = OCSP_response_get1_basic(rsp)))
 */
   {
     BIO * bp = NULL;
 */
   {
     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
 
 #ifndef EXIM_HAVE_OCSP_RESP_COUNT
     STACK_OF(OCSP_SINGLERESP) * sresp = bs->tbsResponseData->responses;
 #endif
 
-    DEBUG(D_tls) bp = BIO_new_fp(debug_file, BIO_NOCLOSE);
+    DEBUG(D_tls) bp = BIO_new(BIO_s_mem());
+
+    /* 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? */
 
 
-    /*OCSP_RESPONSE_print(bp, rsp, 0);   extreme debug: stapling content */
+   {
+    /* 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. */
+
+#ifdef EXIM_HAVE_OPENSSL_OCSP_RESP_GET0_SIGNER
+    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;
 
 
-    /* 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); */
+      verify_store = X509_STORE_new();
 
 
-    if ((i = OCSP_basic_verify(bs, cbinfo->verify_stack,
-             cbinfo->u_ocsp.client.verify_store, OCSP_NOEXPLICIT)) <= 0)
+      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_OPESSL_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, 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 (ERR_peek_error())
        {
        tls_out.ocsp = OCSP_FAILED;
-       if (LOGGING(tls_cipher)) log_write(0, LOG_MAIN,
-               "Received TLS cert status response, itself unverifiable: %s",
-               ERR_reason_error_string(ERR_peek_error()));
-       BIO_printf(bp, "OCSP response verify failure\n");
-       ERR_print_errors(bp);
-       OCSP_RESPONSE_print(bp, rsp, 0);
+       if (LOGGING(tls_cipher))
+         {
+         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",
+               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;
        }
       else
        DEBUG(D_tls) debug_printf("no explicit trust for OCSP signing"
          " in the root CA certificate; ignoring\n");
        goto failed;
        }
       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");
 
 
     DEBUG(D_tls) debug_printf("OCSP response well-formed and signed OK\n");
 
@@ -2414,14 +2707,19 @@ if (!(bs = OCSP_response_get1_basic(rsp)))
       status = OCSP_single_get0_status(single, &reason, &rev,
                  &thisupd, &nextupd);
 
       status = OCSP_single_get0_status(single, &reason, &rev,
                  &thisupd, &nextupd);
 
-      DEBUG(D_tls) time_print(bp, "This OCSP Update", thisupd);
-      DEBUG(D_tls) if(nextupd) time_print(bp, "Next OCSP Update", nextupd);
+      DEBUG(D_tls)
+       {
+       time_print(bp, "This OCSP Update", thisupd);
+       if (nextupd) time_print(bp, "Next OCSP Update", nextupd);
+       }
       if (!OCSP_check_validity(thisupd, nextupd,
            EXIM_OCSP_SKEW_SECONDS, EXIM_OCSP_MAX_AGE))
        {
        tls_out.ocsp = OCSP_FAILED;
        DEBUG(D_tls) ERR_print_errors(bp);
       if (!OCSP_check_validity(thisupd, nextupd,
            EXIM_OCSP_SKEW_SECONDS, EXIM_OCSP_MAX_AGE))
        {
        tls_out.ocsp = OCSP_FAILED;
        DEBUG(D_tls) ERR_print_errors(bp);
-       log_write(0, LOG_MAIN, "Server OSCP dates invalid");
+       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;
        }
 
        goto failed;
        }
 
@@ -2432,12 +2730,16 @@ if (!(bs = OCSP_response_get1_basic(rsp)))
        case V_OCSP_CERTSTATUS_GOOD:
          continue;     /* the idx loop */
        case V_OCSP_CERTSTATUS_REVOKED:
        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:
          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;
          log_write(0, LOG_MAIN,
              "Server certificate status unknown, in OCSP stapling");
          break;
@@ -2454,6 +2756,11 @@ if (!(bs = OCSP_response_get1_basic(rsp)))
     tls_out.ocsp = OCSP_FAILED;
     i = cbinfo->u_ocsp.client.verify_required ? 0 : 1;
   good:
     tls_out.ocsp = OCSP_FAILED;
     i = cbinfo->u_ocsp.client.verify_required ? 0 : 1;
   good:
+    {
+    uschar * s = NULL;
+    int len = (int) BIO_get_mem_data(bp, CSS &s);
+    if (len > 0) debug_printf("%.*s", len, s);
+    }
     BIO_free(bp);
   }
 
     BIO_free(bp);
   }
 
@@ -2486,8 +2793,7 @@ tls_init(host_item * host, smtp_transport_options_block * ob,
   uschar *ocsp_file,
 #endif
   address_item *addr, exim_openssl_state_st ** caller_state,
   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;
 {
 SSL_CTX * ctx;
 exim_openssl_state_st * state;
@@ -2584,10 +2890,17 @@ if (init_options)
     }
 #endif
 
     }
 #endif
 
-  DEBUG(D_tls) debug_printf("setting SSL CTX options: %#lx\n", init_options);
-  if (!(SSL_CTX_set_options(ctx, init_options)))
-    return tls_error(string_sprintf(
+#ifdef OPENSSL_MIN_PROTO_VERSION
+  SSL_CTX_set_min_proto_version(ctx, SSL3_VERSION);
+#endif
+  DEBUG(D_tls) debug_printf("setting  SSL CTX options: %016lx\n", init_options);
+  SSL_CTX_set_options(ctx, init_options);
+   {
+    uint64_t readback = SSL_CTX_clear_options(ctx, ~init_options);
+    if (readback != init_options)
+      return tls_error(string_sprintf(
           "SSL_CTX_set_option(%#lx)", init_options), host, NULL, errstr);
           "SSL_CTX_set_option(%#lx)", init_options), host, NULL, errstr);
+   }
   }
 else
   DEBUG(D_tls) debug_printf("no SSL CTX options to set\n");
   }
 else
   DEBUG(D_tls) debug_printf("no SSL CTX options to set\n");
@@ -2605,15 +2918,18 @@ will never be used because we use a new context every time. */
 /* Initialize with DH parameters if supplied */
 /* Initialize ECDH temp key parameter selection */
 
 /* Initialize with DH parameters if supplied */
 /* Initialize ECDH temp key parameter selection */
 
-if (state->lib_state.dh)
-  { DEBUG(D_tls) debug_printf("TLS: DH params were preloaded\n"); }
-else
-  if (!init_dh(ctx, state->dhparam, host, errstr)) return DEFER;
+if (!host)
+  {
+  if (state->lib_state.dh)
+    { DEBUG(D_tls) debug_printf("TLS: DH params were preloaded\n"); }
+  else
+    if (!init_dh(ctx, state->dhparam, errstr)) return DEFER;
 
 
-if (state->lib_state.ecdh)
-  { DEBUG(D_tls) debug_printf("TLS: ECDH curve was preloaded\n"); }
-else
-  if (!init_ecdh(ctx, host, errstr)) return DEFER;
+  if (state->lib_state.ecdh)
+    { DEBUG(D_tls) debug_printf("TLS: ECDH curve was preloaded\n"); }
+  else
+    if (!init_ecdh(ctx, errstr)) return DEFER;
+  }
 
 /* Set up certificate and key (and perhaps OCSP info) */
 
 
 /* Set up certificate and key (and perhaps OCSP info) */
 
@@ -2625,7 +2941,7 @@ if (state->lib_state.conn_certs)
 else
   {
 #ifndef DISABLE_OCSP
 else
   {
 #ifndef DISABLE_OCSP
-  if (!host)
+  if (!host)                                   /* server */
     {
     state->u_ocsp.server.file = ocsp_file;
     state->u_ocsp.server.file_expanded = NULL;
     {
     state->u_ocsp.server.file = ocsp_file;
     state->u_ocsp.server.file_expanded = NULL;
@@ -2639,7 +2955,7 @@ else
 
 #ifdef EXIM_HAVE_OPENSSL_TLSEXT
 # ifndef DISABLE_OCSP
 
 #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;
     {
     DEBUG(D_tls) debug_printf("failed to create stack for stapling verify\n");
     return FAIL;
@@ -2688,11 +3004,12 @@ else                    /* client */
       DEBUG(D_tls) debug_printf("failed to create store for stapling verify\n");
       return FAIL;
       }
       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
     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;
 
 
 state->verify_cert_hostnames = NULL;
 
@@ -2831,7 +3148,7 @@ if (tlsp->peercert)
 *************************************************/
 
 #ifndef DISABLE_OCSP
 *************************************************/
 
 #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)
 
 static BOOL
 chain_from_pem_file(const uschar * file, STACK_OF(X509) ** vp)
@@ -2861,7 +3178,7 @@ repeated after a Server Name Indication.
 
 Arguments:
   sctx          SSL_CTX* to initialise
 
 Arguments:
   sctx          SSL_CTX* to initialise
-  certs         certs file, 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
   crl           CRL file or NULL
   host          NULL in a server; the remote host in a client
   errstr       error string pointer
@@ -2870,15 +3187,16 @@ Returns:        OK/DEFER/FAIL
 */
 
 static int
 */
 
 static int
-setup_certs(SSL_CTX *sctx, uschar *certs, uschar *crl, host_item *host,
+setup_certs(SSL_CTX * sctx, uschar ** certsp, uschar * crl, host_item * host,
     uschar ** errstr)
 {
     uschar ** errstr)
 {
-uschar *expcerts, *expcrl;
+uschar * expcerts, * expcrl;
 
 
-if (!expand_check(certs, US"tls_verify_certificates", &expcerts, errstr))
+if (!expand_check(*certsp, US"tls_verify_certificates", &expcerts, errstr))
   return DEFER;
 DEBUG(D_tls) debug_printf("tls_verify_certificates: %s\n", expcerts);
 
   return DEFER;
 DEBUG(D_tls) debug_printf("tls_verify_certificates: %s\n", expcerts);
 
+*certsp = expcerts;
 if (expcerts && *expcerts)
   {
   /* Tell the library to use its compiled-in location for the system default
 if (expcerts && *expcerts)
   {
   /* Tell the library to use its compiled-in location for the system default
@@ -2906,19 +3224,20 @@ if (expcerts && *expcerts)
        {
        STACK_OF(X509) * verify_stack =
 #ifndef DISABLE_OCSP
        {
        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
 #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.
 */
        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
        if (  !host
           && statbuf.st_size > 0
           && state_server.u_ocsp.server.file
@@ -3026,6 +3345,67 @@ return OK;
 
 
 
 
 
 
+static void
+tls_dump_keylog(SSL * ssl)
+{
+#ifdef EXIM_HAVE_OPENSSL_KEYLOG
+  BIO * bp = BIO_new(BIO_s_mem());
+  uschar * s = NULL;
+  int len;
+  SSL_SESSION_print_keylog(bp, SSL_get_session(ssl));
+  len = (int) BIO_get_mem_data(bp, CSS &s);
+  if (len > 0) debug_printf("%.*s", len, s);
+  BIO_free(bp);
+#endif
+}
+
+
+/* Channel-binding info for authenticators
+See description in https://paquier.xyz/postgresql-2/channel-binding-openssl/
+for pre-TLS1.3
+*/
+
+static void
+tls_get_channel_binding(SSL * ssl, tls_support * tlsp, const void * taintval)
+{
+uschar c, * s;
+size_t len;
+
+#ifdef EXIM_HAVE_EXPORT_CHNL_BNGNG
+if (SSL_version(ssl) > TLS1_2_VERSION)
+  {
+  /* It's not documented by OpenSSL how big the output buffer must be.
+  The OpenSSL testcases use 80 bytes but don't say why. The GnuTLS impl only
+  serves out 32B.  RFC 9266 says it is 32B.
+  Interop fails unless we use the same each end. */
+  len = 32;
+
+  tlsp->channelbind_exporter = TRUE;
+  taintval = GET_UNTAINTED;
+  if (SSL_export_keying_material(ssl,
+       s = store_get((int)len, taintval), len,
+       "EXPORTER-Channel-Binding", (size_t) 24,
+       NULL, 0, 0) != 1)
+    len = 0;
+  }
+else
+#endif
+  {
+  len = SSL_get_peer_finished(ssl, &c, 0);
+  len = SSL_get_peer_finished(ssl, s = store_get((int)len, taintval), len);
+  }
+
+if (len > 0)
+  {
+  int old_pool = store_pool;
+  store_pool = POOL_PERM;
+    tlsp->channelbinding = b64encode_taint(CUS s, (int)len, taintval);
+  store_pool = old_pool;
+  DEBUG(D_tls) debug_printf("Have channel bindings cached for possible auth usage %p %p\n", tlsp->channelbinding, tlsp);
+  }
+}
+
+
 /*************************************************
 *       Start a TLS session in a server          *
 *************************************************/
 /*************************************************
 *       Start a TLS session in a server          *
 *************************************************/
@@ -3083,14 +3463,17 @@ 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"); }
 
 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;
 
   {
   if (!expand_check(tls_require_ciphers, US"tls_require_ciphers", &expciphers, errstr))
     return FAIL;
 
-  if (  expciphers
-     && (rc = server_load_ciphers(ctx, &state_server, expciphers, errstr)) != OK)
-    return rc;
+  if (expciphers)
+    {
+    normalise_ciphers(&expciphers, tls_require_ciphers);
+    if ((rc = server_load_ciphers(ctx, &state_server, expciphers, errstr)) != OK)
+      return rc;
+    }
   }
 
 /* If this is a host for which certificate verification is mandatory or
   }
 
 /* If this is a host for which certificate verification is mandatory or
@@ -3109,28 +3492,34 @@ else if (verify_check_host(&tls_try_verify_hosts) == OK)
 else
   goto skip_certs;
 
 else
   goto skip_certs;
 
-  {
-  uschar * expcerts;
-  if (!expand_check(tls_verify_certificates, US"tls_verify_certificates",
-                   &expcerts, errstr))
-    return DEFER;
-  DEBUG(D_tls) debug_printf("tls_verify_certificates: %s\n", expcerts);
+ {
+  uschar * v_certs = tls_verify_certificates;
 
   if (state_server.lib_state.cabundle)
 
   if (state_server.lib_state.cabundle)
-    { DEBUG(D_tls) debug_printf("TLS: CA bundle for server was preloaded\n"); }
+    {
+    DEBUG(D_tls) debug_printf("TLS: CA bundle for server was preloaded\n");
+    setup_cert_verify(ctx, server_verify_optional, verify_callback_server);
+    }
   else
   else
-    if ((rc = setup_certs(ctx, expcerts, tls_crl, NULL, errstr)) != OK)
+    {
+    if ((rc = setup_certs(ctx, &v_certs, tls_crl, NULL, errstr)) != OK)
       return rc;
       return rc;
-
-  if (expcerts && *expcerts)
-    setup_cert_verify(ctx, server_verify_optional, verify_callback_server);
 }
+    if (v_certs && *v_certs)
+      setup_cert_verify(ctx, server_verify_optional, verify_callback_server);
+    }
+ }
 skip_certs: ;
 
 #ifndef DISABLE_TLS_RESUME
 skip_certs: ;
 
 #ifndef DISABLE_TLS_RESUME
+# if OPENSSL_VERSION_NUMBER < 0x30000000L
 SSL_CTX_set_tlsext_ticket_key_cb(ctx, ticket_key_callback);
 /* despite working, appears to always return failure, so ignoring */
 SSL_CTX_set_tlsext_ticket_key_cb(ctx, ticket_key_callback);
 /* despite working, appears to always return failure, so ignoring */
+# else
+SSL_CTX_set_tlsext_ticket_key_evp_cb(ctx, ticket_key_callback);
+/* despite working, appears to always return failure, so ignoring */
+# endif
 #endif
 #endif
+
 #ifdef OPENSSL_HAVE_NUM_TICKETS
 # ifndef DISABLE_TLS_RESUME
 SSL_CTX_set_num_tickets(ctx, tls_in.host_resumable ? 1 : 0);
 #ifdef OPENSSL_HAVE_NUM_TICKETS
 # ifndef DISABLE_TLS_RESUME
 SSL_CTX_set_num_tickets(ctx, tls_in.host_resumable ? 1 : 0);
@@ -3198,9 +3587,11 @@ if (rc <= 0)
     case SSL_ERROR_ZERO_RETURN:
       DEBUG(D_tls) debug_printf("Got SSL_ERROR_ZERO_RETURN\n");
       (void) tls_error(US"SSL_accept", NULL, sigalrm_seen ? US"timed out" : NULL, errstr);
     case SSL_ERROR_ZERO_RETURN:
       DEBUG(D_tls) debug_printf("Got SSL_ERROR_ZERO_RETURN\n");
       (void) tls_error(US"SSL_accept", NULL, sigalrm_seen ? US"timed out" : NULL, errstr);
-
+#ifndef DISABLE_EVENT
+      (void) event_raise(event_action, US"tls:fail:connect", *errstr, NULL);
+#endif
       if (SSL_get_shutdown(ssl) == SSL_RECEIVED_SHUTDOWN)
       if (SSL_get_shutdown(ssl) == SSL_RECEIVED_SHUTDOWN)
-           SSL_shutdown(ssl);
+       SSL_shutdown(ssl);
 
       tls_close(NULL, TLS_NO_SHUTDOWN);
       return FAIL;
 
       tls_close(NULL, TLS_NO_SHUTDOWN);
       return FAIL;
@@ -3215,8 +3606,11 @@ if (rc <= 0)
          || r == SSL_R_VERSION_TOO_LOW
 #endif
          || r == SSL_R_UNKNOWN_PROTOCOL || r == SSL_R_UNSUPPORTED_PROTOCOL)
          || r == SSL_R_VERSION_TOO_LOW
 #endif
          || r == SSL_R_UNKNOWN_PROTOCOL || r == SSL_R_UNSUPPORTED_PROTOCOL)
-       s = string_sprintf("%s (%s)", s, SSL_get_version(ssl));
+       s = string_sprintf("(%s)", SSL_get_version(ssl));
       (void) tls_error(US"SSL_accept", NULL, sigalrm_seen ? US"timed out" : s, errstr);
       (void) tls_error(US"SSL_accept", NULL, sigalrm_seen ? US"timed out" : s, errstr);
+#ifndef DISABLE_EVENT
+      (void) event_raise(event_action, US"tls:fail:connect", *errstr, NULL);
+#endif
       return FAIL;
       }
 
       return FAIL;
       }
 
@@ -3227,6 +3621,9 @@ if (rc <= 0)
        if (!errno)
          {
          *errstr = US"SSL_accept: TCP connection closed by peer";
        if (!errno)
          {
          *errstr = US"SSL_accept: TCP connection closed by peer";
+#ifndef DISABLE_EVENT
+         (void) event_raise(event_action, US"tls:fail:connect", *errstr, NULL);
+#endif
          return FAIL;
          }
        DEBUG(D_tls) debug_printf(" - syscall %s\n", strerror(errno));
          return FAIL;
          }
        DEBUG(D_tls) debug_printf(" - syscall %s\n", strerror(errno));
@@ -3235,6 +3632,9 @@ if (rc <= 0)
                      sigalrm_seen ? US"timed out"
                      : ERR_peek_error() ? NULL : string_sprintf("ret %d", error),
                      errstr);
                      sigalrm_seen ? US"timed out"
                      : ERR_peek_error() ? NULL : string_sprintf("ret %d", error),
                      errstr);
+#ifndef DISABLE_EVENT
+      (void) event_raise(event_action, US"tls:fail:connect", *errstr, NULL);
+#endif
       return FAIL;
     }
   }
       return FAIL;
     }
   }
@@ -3282,6 +3682,7 @@ else DEBUG(D_tls)
 adjust the input functions to read via TLS, and initialize things. */
 
 #ifdef SSL_get_extms_support
 adjust the input functions to read via TLS, and initialize things. */
 
 #ifdef SSL_get_extms_support
+/*XXX what does this return for tls1.3 ? */
 tls_in.ext_master_secret = SSL_get_extms_support(ssl) == 1;
 #endif
 peer_cert(ssl, &tls_in, peerdn, sizeof(peerdn));
 tls_in.ext_master_secret = SSL_get_extms_support(ssl) == 1;
 #endif
 peer_cert(ssl, &tls_in, peerdn, sizeof(peerdn));
@@ -3296,13 +3697,7 @@ DEBUG(D_tls)
   if (SSL_get_shared_ciphers(ssl, CS buf, sizeof(buf)))
     debug_printf("Shared ciphers: %s\n", buf);
 
   if (SSL_get_shared_ciphers(ssl, CS buf, sizeof(buf)))
     debug_printf("Shared ciphers: %s\n", buf);
 
-#ifdef EXIM_HAVE_OPENSSL_KEYLOG
-  {
-  BIO * bp = BIO_new_fp(debug_file, BIO_NOCLOSE);
-  SSL_SESSION_print_keylog(bp, SSL_get_session(ssl));
-  BIO_free(bp);
-  }
-#endif
+  tls_dump_keylog(ssl);
 
 #ifdef EXIM_HAVE_SESSION_TICKET
   {
 
 #ifdef EXIM_HAVE_SESSION_TICKET
   {
@@ -3320,19 +3715,7 @@ DEBUG(D_tls)
   tls_in.ourcert = crt ? X509_dup(crt) : NULL;
   }
 
   tls_in.ourcert = crt ? X509_dup(crt) : NULL;
   }
 
-/* Channel-binding info for authenticators
-See description in https://paquier.xyz/postgresql-2/channel-binding-openssl/ */
-  {
-  uschar c, * s;
-  size_t len = SSL_get_peer_finished(ssl, &c, 0);
-  int old_pool = store_pool;
-
-  SSL_get_peer_finished(ssl, s = store_get((int)len, FALSE), len);
-  store_pool = POOL_PERM;
-    tls_in.channelbinding = b64encode_taint(CUS s, (int)len, FALSE);
-  store_pool = old_pool;
-  DEBUG(D_tls) debug_printf("Have channel bindings cached for possible auth usage %p\n", tls_in.channelbinding);
-  }
+tls_get_channel_binding(ssl, &tls_in, GET_UNTAINTED);
 
 /* Only used by the server-side tls (tls_in), including tls_getc.
    Client-side (tls_out) reads (seem to?) go via
 
 /* Only used by the server-side tls (tls_in), including tls_getc.
    Client-side (tls_out) reads (seem to?) go via
@@ -3346,10 +3729,10 @@ ssl_xfer_eof = ssl_xfer_error = FALSE;
 receive_getc = tls_getc;
 receive_getbuf = tls_getbuf;
 receive_get_cache = tls_get_cache;
 receive_getc = tls_getc;
 receive_getbuf = tls_getbuf;
 receive_get_cache = tls_get_cache;
+receive_hasc = tls_hasc;
 receive_ungetc = tls_ungetc;
 receive_feof = tls_feof;
 receive_ferror = tls_ferror;
 receive_ungetc = tls_ungetc;
 receive_feof = tls_feof;
 receive_ferror = tls_ferror;
-receive_smtp_buffered = tls_smtp_buffered;
 
 tls_in.active.sock = fileno(smtp_out);
 tls_in.active.tls_ctx = NULL;  /* not using explicit ctx for server-side */
 
 tls_in.active.sock = fileno(smtp_out);
 tls_in.active.tls_ctx = NULL;  /* not using explicit ctx for server-side */
@@ -3385,22 +3768,22 @@ else if (verify_check_given_host(CUSS &ob->tls_try_verify_hosts, host) == OK)
 else
   return OK;
 
 else
   return OK;
 
-  {
-  uschar * expcerts;
-  if (!expand_check(ob->tls_verify_certificates, US"tls_verify_certificates",
-                   &expcerts, errstr))
-    return DEFER;
-  DEBUG(D_tls) debug_printf("tls_verify_certificates: %s\n", expcerts);
+ {
+  uschar * v_certs = ob->tls_verify_certificates;
 
   if (state->lib_state.cabundle)
 
   if (state->lib_state.cabundle)
-    { DEBUG(D_tls) debug_printf("TLS: CA bundle was preloaded\n"); }
+    {
+    DEBUG(D_tls) debug_printf("TLS: CA bundle for tpt was preloaded\n");
+    setup_cert_verify(ctx, client_verify_optional, verify_callback_client);
+    }
   else
   else
-    if ((rc = setup_certs(ctx, expcerts, ob->tls_crl, host, errstr)) != OK)
+    {
+    if ((rc = setup_certs(ctx, &v_certs, ob->tls_crl, host, errstr)) != OK)
       return rc;
       return rc;
-
-  if (expcerts && *expcerts)
-    setup_cert_verify(ctx, client_verify_optional, verify_callback_client);
 }
+    if (v_certs && *v_certs)
+      setup_cert_verify(ctx, client_verify_optional, verify_callback_client);
+    }
+ }
 
 if (verify_check_given_host(CUSS &ob->tls_verify_cert_hostnames, host) == OK)
   {
 
 if (verify_check_given_host(CUSS &ob->tls_verify_cert_hostnames, host) == OK)
   {
@@ -3479,9 +3862,8 @@ return DEFER;
 and apply it to the ssl-connection for attempted resumption. */
 
 static void
 and apply it to the ssl-connection for attempted resumption. */
 
 static void
-tls_retrieve_session(tls_support * tlsp, SSL * ssl, const uschar * key)
+tls_retrieve_session(tls_support * tlsp, SSL * ssl)
 {
 {
-tlsp->resumption |= RESUME_SUPPORTED;
 if (tlsp->host_resumable)
   {
   dbdata_tls_session * dt;
 if (tlsp->host_resumable)
   {
   dbdata_tls_session * dt;
@@ -3489,11 +3871,11 @@ if (tlsp->host_resumable)
   open_db dbblock, * dbm_file;
 
   tlsp->resumption |= RESUME_CLIENT_REQUESTED;
   open_db dbblock, * dbm_file;
 
   tlsp->resumption |= RESUME_CLIENT_REQUESTED;
-  DEBUG(D_tls) debug_printf("checking for resumable session for %s\n", key);
+  DEBUG(D_tls)
+    debug_printf("checking for resumable session for %s\n", tlsp->resume_index);
   if ((dbm_file = dbfn_open(US"tls", O_RDWR, &dbblock, FALSE, FALSE)))
     {
   if ((dbm_file = dbfn_open(US"tls", O_RDWR, &dbblock, FALSE, FALSE)))
     {
-    /* key for the db is the IP */
-    if ((dt = dbfn_read_with_length(dbm_file, key, &len)))
+    if ((dt = dbfn_read_with_length(dbm_file, tlsp->resume_index, &len)))
       {
       SSL_SESSION * ss = NULL;
       const uschar * sess_asn1 = dt->session;
       {
       SSL_SESSION * ss = NULL;
       const uschar * sess_asn1 = dt->session;
@@ -3519,24 +3901,21 @@ if (tlsp->host_resumable)
        if (lifetime + dt->time_stamp < time(NULL))
          {
          DEBUG(D_tls) debug_printf("session expired\n");
        if (lifetime + dt->time_stamp < time(NULL))
          {
          DEBUG(D_tls) debug_printf("session expired\n");
-         dbfn_delete(dbm_file, key);
-         }
-       else if (!SSL_set_session(ssl, ss))
-         {
-         DEBUG(D_tls)
-           {
-           ERR_error_string_n(ERR_get_error(),
-             ssl_errstring, sizeof(ssl_errstring));
-           debug_printf("applying session to ssl: %s\n", ssl_errstring);
-           }
+         dbfn_delete(dbm_file, tlsp->resume_index);
          }
          }
-       else
+       else if (SSL_set_session(ssl, ss))
          {
          DEBUG(D_tls) debug_printf("good session\n");
          tlsp->resumption |= RESUME_CLIENT_SUGGESTED;
          tlsp->verify_override = dt->verify_override;
          tlsp->ocsp = dt->ocsp;
          }
          {
          DEBUG(D_tls) debug_printf("good session\n");
          tlsp->resumption |= RESUME_CLIENT_SUGGESTED;
          tlsp->verify_override = dt->verify_override;
          tlsp->ocsp = dt->ocsp;
          }
+       else DEBUG(D_tls)
+         {
+         ERR_error_string_n(ERR_get_error(),
+           ssl_errstring, sizeof(ssl_errstring));
+         debug_printf("applying session to ssl: %s\n", ssl_errstring);
+         }
        }
       }
     else
        }
       }
     else
@@ -3565,7 +3944,7 @@ if (SSL_SESSION_is_resumable(ss))         /* 1.1.1 */
   {
   int len = i2d_SSL_SESSION(ss, NULL);
   int dlen = sizeof(dbdata_tls_session) + len;
   {
   int len = i2d_SSL_SESSION(ss, NULL);
   int dlen = sizeof(dbdata_tls_session) + len;
-  dbdata_tls_session * dt = store_get(dlen, TRUE);
+  dbdata_tls_session * dt = store_get(dlen, GET_TAINTED);
   uschar * s = dt->session;
   open_db dbblock, * dbm_file;
 
   uschar * s = dt->session;
   open_db dbblock, * dbm_file;
 
@@ -3578,9 +3957,7 @@ if (SSL_SESSION_is_resumable(ss))         /* 1.1.1 */
 
   if ((dbm_file = dbfn_open(US"tls", O_RDWR, &dbblock, FALSE, FALSE)))
     {
 
   if ((dbm_file = dbfn_open(US"tls", O_RDWR, &dbblock, FALSE, FALSE)))
     {
-    const uschar * key = cbinfo->host->address;
-    dbfn_delete(dbm_file, key);
-    dbfn_write(dbm_file, key, dt, dlen);
+    dbfn_write(dbm_file, tlsp->resume_index, dt, dlen);
     dbfn_close(dbm_file);
     DEBUG(D_tls) debug_printf("wrote session (len %u) to db\n",
                  (unsigned)dlen);
     dbfn_close(dbm_file);
     DEBUG(D_tls) debug_printf("wrote session (len %u) to db\n",
                  (unsigned)dlen);
@@ -3590,21 +3967,20 @@ return 1;
 }
 
 
 }
 
 
+/* Construct a key for session DB lookup, and setup the SSL_CTX for resumption */
+
 static void
 tls_client_ctx_resume_prehandshake(
 static void
 tls_client_ctx_resume_prehandshake(
-  exim_openssl_client_tls_ctx * exim_client_ctx, tls_support * tlsp,
-  smtp_transport_options_block * ob, host_item * host)
+  exim_openssl_client_tls_ctx * exim_client_ctx, smtp_connect_args * conn_args,
+  tls_support * tlsp, smtp_transport_options_block * ob)
 {
 {
-/* Should the client request a session resumption ticket? */
-if (verify_check_given_host(CUSS &ob->tls_resumption_hosts, host) == OK)
-  {
-  tlsp->host_resumable = TRUE;
+tlsp->host_resumable = TRUE;
+tls_client_resmption_key(tlsp, conn_args, ob);
 
 
-  SSL_CTX_set_session_cache_mode(exim_client_ctx->ctx,
-       SSL_SESS_CACHE_CLIENT
-       | SSL_SESS_CACHE_NO_INTERNAL | SSL_SESS_CACHE_NO_AUTO_CLEAR);
-  SSL_CTX_sess_set_new_cb(exim_client_ctx->ctx, tls_save_session_cb);
-  }
+SSL_CTX_set_session_cache_mode(exim_client_ctx->ctx,
+      SSL_SESS_CACHE_CLIENT
+      | SSL_SESS_CACHE_NO_INTERNAL | SSL_SESS_CACHE_NO_AUTO_CLEAR);
+SSL_CTX_sess_set_new_cb(exim_client_ctx->ctx, tls_save_session_cb);
 }
 
 static BOOL
 }
 
 static BOOL
@@ -3628,7 +4004,7 @@ if (tlsp->host_resumable)
 
 tlsp->resumption = RESUME_SUPPORTED;
 /* Pick up a previous session, saved on an old ticket */
 
 tlsp->resumption = RESUME_SUPPORTED;
 /* Pick up a previous session, saved on an old ticket */
-tls_retrieve_session(tlsp, ssl, host->address);
+tls_retrieve_session(tlsp, ssl);
 return TRUE;
 }
 
 return TRUE;
 }
 
@@ -3648,16 +4024,19 @@ if (SSL_session_reused(exim_client_ctx->ssl))
 #ifdef EXIM_HAVE_ALPN
 /* Expand and convert an Exim list to an ALPN list.  False return for fail.
 NULL plist return for silent no-ALPN.
 #ifdef EXIM_HAVE_ALPN
 /* Expand and convert an Exim list to an ALPN list.  False return for fail.
 NULL plist return for silent no-ALPN.
+
+Overwite the passed-in list with the expanded version.
 */
 
 static BOOL
 */
 
 static BOOL
-tls_alpn_plist(const uschar * tls_alpn, const uschar ** plist, unsigned * plen,
+tls_alpn_plist(uschar ** tls_alpn, const uschar ** plist, unsigned * plen,
   uschar ** errstr)
 {
 uschar * exp_alpn;
 
   uschar ** errstr)
 {
 uschar * exp_alpn;
 
-if (!expand_check(tls_alpn, US"tls_alpn", &exp_alpn, errstr))
+if (!expand_check(*tls_alpn, US"tls_alpn", &exp_alpn, errstr))
   return FALSE;
   return FALSE;
+*tls_alpn = exp_alpn;
 
 if (!exp_alpn)
   {
 
 if (!exp_alpn)
   {
@@ -3670,7 +4049,7 @@ else
   but it's little extra code complexity in the client. */
 
   const uschar * list = exp_alpn;
   but it's little extra code complexity in the client. */
 
   const uschar * list = exp_alpn;
-  uschar * p = store_get(Ustrlen(exp_alpn), is_tainted(exp_alpn)), * s, * t;
+  uschar * p = store_get(Ustrlen(exp_alpn), exp_alpn), * s, * t;
   int sep = 0;
   uschar len;
 
   int sep = 0;
   uschar len;
 
@@ -3724,7 +4103,7 @@ BOOL require_ocsp = FALSE;
 
 rc = store_pool;
 store_pool = POOL_PERM;
 
 rc = store_pool;
 store_pool = POOL_PERM;
-exim_client_ctx = store_get(sizeof(exim_openssl_client_tls_ctx), FALSE);
+exim_client_ctx = store_get(sizeof(exim_openssl_client_tls_ctx), GET_UNTAINTED);
 exim_client_ctx->corked = NULL;
 store_pool = rc;
 
 exim_client_ctx->corked = NULL;
 store_pool = rc;
 
@@ -3735,7 +4114,6 @@ tlsp->tlsa_usage = 0;
 #ifndef DISABLE_OCSP
   {
 # ifdef SUPPORT_DANE
 #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'
   if (  conn_args->dane
      && ob->hosts_request_ocsp[0] == '*'
      && ob->hosts_request_ocsp[1] == '\0'
@@ -3758,6 +4136,15 @@ tlsp->tlsa_usage = 0;
 # endif
       request_ocsp =
        verify_check_given_host(CUSS &ob->hosts_request_ocsp, host) == OK;
 # 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
 
   }
 #endif
 
@@ -3785,21 +4172,25 @@ if (conn_args->dane)
     return FALSE;
   if (expciphers && *expciphers == '\0')
     expciphers = NULL;
     return FALSE;
   if (expciphers && *expciphers == '\0')
     expciphers = NULL;
+
+  normalise_ciphers(&expciphers, ob->dane_require_tls_ciphers);
   }
 #endif
   }
 #endif
-if (!expciphers &&
-    !expand_check(ob->tls_require_ciphers, US"tls_require_ciphers",
+if (!expciphers)
+  {
+  if (!expand_check(ob->tls_require_ciphers, US"tls_require_ciphers",
       &expciphers, errstr))
       &expciphers, errstr))
-  return FALSE;
+    return FALSE;
 
 
-/* In OpenSSL, cipher components are separated by hyphens. In GnuTLS, they
-are separated by underscores. So that I can use either form in my tests, and
-also for general convenience, we turn underscores into hyphens here. */
+  /* In OpenSSL, cipher components are separated by hyphens. In GnuTLS, they
+  are separated by underscores. So that I can use either form in my tests, and
+  also for general convenience, we turn underscores into hyphens here. */
+
+  normalise_ciphers(&expciphers, ob->tls_require_ciphers);
+  }
 
 if (expciphers)
   {
 
 if (expciphers)
   {
-  uschar *s = expciphers;
-  while (*s) { if (*s == '_') *s = '-'; s++; }
   DEBUG(D_tls) debug_printf("required ciphers: %s\n", expciphers);
   if (!SSL_CTX_set_cipher_list(exim_client_ctx->ctx, CS expciphers))
     {
   DEBUG(D_tls) debug_printf("required ciphers: %s\n", expciphers);
   if (!SSL_CTX_set_cipher_list(exim_client_ctx->ctx, CS expciphers))
     {
@@ -3825,6 +4216,7 @@ if (conn_args->dane)
     tls_error(US"context init", host, NULL, errstr);
     return FALSE;
     }
     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
 
   }
 else
 
@@ -3834,39 +4226,20 @@ if (tls_client_basic_ctx_init(exim_client_ctx->ctx, host, ob,
       client_static_state, errstr) != OK)
   return FALSE;
 
       client_static_state, errstr) != OK)
   return FALSE;
 
-#ifndef DISABLE_TLS_RESUME
-tls_client_ctx_resume_prehandshake(exim_client_ctx, tlsp, ob, host);
-#endif
-
-
-if (!(exim_client_ctx->ssl = SSL_new(exim_client_ctx->ctx)))
-  {
-  tls_error(US"SSL_new", host, NULL, errstr);
-  return FALSE;
-  }
-SSL_set_session_id_context(exim_client_ctx->ssl, sid_ctx, Ustrlen(sid_ctx));
-
-SSL_set_fd(exim_client_ctx->ssl, cctx->sock);
-SSL_set_connect_state(exim_client_ctx->ssl);
-
 if (ob->tls_sni)
   {
   if (!expand_check(ob->tls_sni, US"tls_sni", &tlsp->sni, errstr))
     return FALSE;
   if (!tlsp->sni)
 if (ob->tls_sni)
   {
   if (!expand_check(ob->tls_sni, US"tls_sni", &tlsp->sni, errstr))
     return FALSE;
   if (!tlsp->sni)
-    {
-    DEBUG(D_tls) debug_printf("Setting TLS SNI forced to fail, not sending\n");
-    }
+    { DEBUG(D_tls) debug_printf("Setting TLS SNI forced to fail, not sending\n"); }
   else if (!Ustrlen(tlsp->sni))
     tlsp->sni = NULL;
   else
     {
   else if (!Ustrlen(tlsp->sni))
     tlsp->sni = NULL;
   else
     {
-#ifdef EXIM_HAVE_OPENSSL_TLSEXT
-    DEBUG(D_tls) debug_printf("Setting TLS SNI \"%s\"\n", tlsp->sni);
-    SSL_set_tlsext_host_name(exim_client_ctx->ssl, tlsp->sni);
-#else
+#ifndef EXIM_HAVE_OPENSSL_TLSEXT
     log_write(0, LOG_MAIN, "SNI unusable with this OpenSSL library version; ignoring \"%s\"\n",
           tlsp->sni);
     log_write(0, LOG_MAIN, "SNI unusable with this OpenSSL library version; ignoring \"%s\"\n",
           tlsp->sni);
+    tlsp->sni = NULL;
 #endif
     }
   }
 #endif
     }
   }
@@ -3877,10 +4250,10 @@ if (ob->tls_alpn)
   const uschar * plist;
   unsigned plen;
 
   const uschar * plist;
   unsigned plen;
 
-  if (!tls_alpn_plist(ob->tls_alpn, &plist, &plen, errstr))
+  if (!tls_alpn_plist(&ob->tls_alpn, &plist, &plen, errstr))
     return FALSE;
   if (plist)
     return FALSE;
   if (plist)
-    if (SSL_set_alpn_protos(exim_client_ctx->ssl, plist, plen) != 0)
+    if (SSL_CTX_set_alpn_protos(exim_client_ctx->ctx, plist, plen) != 0)
       {
       tls_error(US"alpn init", host, NULL, errstr);
       return FALSE;
       {
       tls_error(US"alpn init", host, NULL, errstr);
       return FALSE;
@@ -3893,6 +4266,34 @@ if (ob->tls_alpn)
           ob->tls_alpn);
 #endif
 
           ob->tls_alpn);
 #endif
 
+#ifndef DISABLE_TLS_RESUME
+/*XXX have_lbserver: another cmdline arg possibly, for continued-conn, but use
+will be very low. */
+
+if (!conn_args->have_lbserver) /* wanted for tls_client_resmption_key() */
+  { DEBUG(D_tls) debug_printf("resumption not supported on continued-connection\n"); }
+else if (verify_check_given_host(CUSS &ob->tls_resumption_hosts, host) == OK)
+  tls_client_ctx_resume_prehandshake(exim_client_ctx, conn_args, tlsp, ob);
+#endif
+
+
+if (!(exim_client_ctx->ssl = SSL_new(exim_client_ctx->ctx)))
+  {
+  tls_error(US"SSL_new", host, NULL, errstr);
+  return FALSE;
+  }
+SSL_set_session_id_context(exim_client_ctx->ssl, sid_ctx, Ustrlen(sid_ctx));
+SSL_set_fd(exim_client_ctx->ssl, cctx->sock);
+SSL_set_connect_state(exim_client_ctx->ssl);
+
+#ifdef EXIM_HAVE_OPENSSL_TLSEXT
+if (tlsp->sni)
+  {
+  DEBUG(D_tls) debug_printf("Setting TLS SNI \"%s\"\n", tlsp->sni);
+  SSL_set_tlsext_host_name(exim_client_ctx->ssl, tlsp->sni);
+  }
+#endif
+
 #ifdef SUPPORT_DANE
 if (conn_args->dane)
   if (dane_tlsa_load(exim_client_ctx->ssl, host, &conn_args->tlsa_dnsa, errstr) != OK)
 #ifdef SUPPORT_DANE
 if (conn_args->dane)
   if (dane_tlsa_load(exim_client_ctx->ssl, host, &conn_args->tlsa_dnsa, errstr) != OK)
@@ -3952,20 +4353,19 @@ if (conn_args->dane)
 
 if (rc <= 0)
   {
 
 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;
   }
 
 DEBUG(D_tls)
   {
   debug_printf("SSL_connect succeeded\n");
   return FALSE;
   }
 
 DEBUG(D_tls)
   {
   debug_printf("SSL_connect succeeded\n");
-#ifdef EXIM_HAVE_OPENSSL_KEYLOG
-  {
-  BIO * bp = BIO_new_fp(debug_file, BIO_NOCLOSE);
-  SSL_SESSION_print_keylog(bp, SSL_get_session(exim_client_ctx->ssl));
-  BIO_free(bp);
-  }
-#endif
+  tls_dump_keylog(exim_client_ctx->ssl);
   }
 
 #ifndef DISABLE_TLS_RESUME
   }
 
 #ifndef DISABLE_TLS_RESUME
@@ -4006,18 +4406,7 @@ tlsp->cipher_stdname = cipher_stdname_ssl(exim_client_ctx->ssl);
   }
 
 /*XXX will this work with continued-TLS? */
   }
 
 /*XXX will this work with continued-TLS? */
-/* Channel-binding info for authenticators */
-  {
-  uschar c, * s;
-  size_t len = SSL_get_finished(exim_client_ctx->ssl, &c, 0);
-  int old_pool = store_pool;
-
-  SSL_get_finished(exim_client_ctx->ssl, s = store_get((int)len, TRUE), len);
-  store_pool = POOL_PERM;
-    tlsp->channelbinding = b64encode_taint(CUS s, (int)len, TRUE);
-  store_pool = old_pool;
-  DEBUG(D_tls) debug_printf("Have channel bindings cached for possible auth usage %p %p\n", tlsp->channelbinding, tlsp);
-  }
+tls_get_channel_binding(exim_client_ctx->ssl, tlsp, GET_TAINTED);
 
 tlsp->active.sock = cctx->sock;
 tlsp->active.tls_ctx = exim_client_ctx;
 
 tlsp->active.sock = cctx->sock;
 tlsp->active.tls_ctx = exim_client_ctx;
@@ -4122,6 +4511,12 @@ if (ssl_xfer_buffer_lwm >= ssl_xfer_buffer_hwm)
 return ssl_xfer_buffer[ssl_xfer_buffer_lwm++];
 }
 
 return ssl_xfer_buffer[ssl_xfer_buffer_lwm++];
 }
 
+BOOL
+tls_hasc(void)
+{
+return ssl_xfer_buffer_lwm < ssl_xfer_buffer_hwm;
+}
+
 uschar *
 tls_getbuf(unsigned * len)
 {
 uschar *
 tls_getbuf(unsigned * len)
 {
@@ -4150,7 +4545,6 @@ tls_get_cache(unsigned lim)
 {
 #ifndef DISABLE_DKIM
 int n = ssl_xfer_buffer_hwm - ssl_xfer_buffer_lwm;
 {
 #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)
 if (n > lim)
   n = lim;
 if (n > 0)
@@ -4160,7 +4554,7 @@ if (n > 0)
 
 
 BOOL
 
 
 BOOL
-tls_could_read(void)
+tls_could_getc(void)
 {
 return ssl_xfer_buffer_lwm < ssl_xfer_buffer_hwm
     || SSL_pending(state_server.lib_state.lib_ssl) > 0;
 {
 return ssl_xfer_buffer_lwm < ssl_xfer_buffer_hwm
     || SSL_pending(state_server.lib_state.lib_ssl) > 0;
@@ -4378,19 +4772,25 @@ int * fdp = o_ctx ? &tls_out.active.sock : &tls_in.active.sock;
 
 if (*fdp < 0) return;  /* TLS was not active */
 
 
 if (*fdp < 0) return;  /* TLS was not active */
 
-if (do_shutdown)
+if (do_shutdown > TLS_NO_SHUTDOWN)
   {
   int rc;
   DEBUG(D_tls) debug_printf("tls_close(): shutting down TLS%s\n",
   {
   int rc;
   DEBUG(D_tls) debug_printf("tls_close(): shutting down TLS%s\n",
-    do_shutdown > 1 ? " (with response-wait)" : "");
+    do_shutdown > TLS_SHUTDOWN_NOWAIT ? " (with response-wait)" : "");
 
   tls_write(ct_ctx, NULL, 0, FALSE);   /* flush write buffer */
 
 
   tls_write(ct_ctx, NULL, 0, FALSE);   /* flush write buffer */
 
-  if (  (rc = SSL_shutdown(*sslp)) == 0        /* send "close notify" alert */
-     && do_shutdown > 1)
+  if (  (  do_shutdown >= TLS_SHUTDOWN_WONLY
+       || (rc = SSL_shutdown(*sslp)) == 0      /* send "close notify" alert */
+       )
+     && do_shutdown > TLS_SHUTDOWN_NOWAIT
+     )
     {
     {
+#ifdef EXIM_TCP_CORK
+    (void) setsockopt(*fdp, IPPROTO_TCP, EXIM_TCP_CORK, US &off, sizeof(off));
+#endif
     ALARM(2);
     ALARM(2);
-    rc = SSL_shutdown(*sslp);          /* wait for response */
+    rc = SSL_shutdown(*sslp);                  /* wait for response */
     ALARM_CLR(0);
     }
 
     ALARM_CLR(0);
     }
 
@@ -4404,17 +4804,17 @@ if (do_shutdown)
 if (!o_ctx)            /* server side */
   {
 #ifndef DISABLE_OCSP
 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;
   receive_getbuf =     smtp_getbuf;
   receive_get_cache =  smtp_get_cache;
 #endif
 
   receive_getc =       smtp_getc;
   receive_getbuf =     smtp_getbuf;
   receive_get_cache =  smtp_get_cache;
+  receive_hasc =       smtp_hasc;
   receive_ungetc =     smtp_ungetc;
   receive_feof =       smtp_feof;
   receive_ferror =     smtp_ferror;
   receive_ungetc =     smtp_ungetc;
   receive_feof =       smtp_feof;
   receive_ferror =     smtp_ferror;
-  receive_smtp_buffered = smtp_buffered;
   tls_in.active.tls_ctx = NULL;
   tls_in.sni = NULL;
   /* Leave bits, peercert, cipher, peerdn, certificate_verified set, for logging */
   tls_in.active.tls_ctx = NULL;
   tls_in.sni = NULL;
   /* Leave bits, peercert, cipher, peerdn, certificate_verified set, for logging */
@@ -4441,8 +4841,8 @@ Returns:     NULL on success, or error message
 uschar *
 tls_validate_require_cipher(void)
 {
 uschar *
 tls_validate_require_cipher(void)
 {
-SSL_CTX *ctx;
-uschar *s, *expciphers, *err;
+SSL_CTX * ctx;
+uschar * expciphers, * err;
 
 tls_openssl_init();
 
 
 tls_openssl_init();
 
@@ -4456,12 +4856,9 @@ if (!expand_check(tls_require_ciphers, US"tls_require_ciphers", &expciphers,
 if (!(expciphers && *expciphers))
   return NULL;
 
 if (!(expciphers && *expciphers))
   return NULL;
 
-/* normalisation ripped from above */
-s = expciphers;
-while (*s != 0) { if (*s == '_') *s = '-'; s++; }
+normalise_ciphers(&expciphers, tls_require_ciphers);
 
 err = NULL;
 
 err = NULL;
-
 if (lib_ctx_new(&ctx, NULL, &err) == OK)
   {
   DEBUG(D_tls)
 if (lib_ctx_new(&ctx, NULL, &err) == OK)
   {
   DEBUG(D_tls)
@@ -4497,21 +4894,22 @@ number/string, and the version date remains unchanged.  The _build_ date
 will change, so we can more usefully assist with version diagnosis by also
 reporting the build date.
 
 will change, so we can more usefully assist with version diagnosis by also
 reporting the build date.
 
-Arguments:   a FILE* to print the results to
-Returns:     nothing
+Arguments:   string to append to
+Returns:     string
 */
 
 */
 
-void
-tls_version_report(FILE *f)
+gstring *
+tls_version_report(gstring * g)
 {
 {
-fprintf(f, "Library version: OpenSSL: Compile: %s\n"
-           "                          Runtime: %s\n"
-           "                                 : %s\n",
-           OPENSSL_VERSION_TEXT,
-           SSLeay_version(SSLEAY_VERSION),
-           SSLeay_version(SSLEAY_BUILT_ON));
-/* third line is 38 characters for the %s and the line is 73 chars long;
-the OpenSSL output includes a "built on: " prefix already. */
+return string_fmt_append(g,
+    "Library version: OpenSSL: Compile: %s\n"
+    "                          Runtime: %s\n"
+    "                                 : %s\n",
+            OPENSSL_VERSION_TEXT,
+            SSLeay_version(SSLEAY_VERSION),
+            SSLeay_version(SSLEAY_BUILT_ON));
+  /* third line is 38 characters for the %s and the line is 73 chars long;
+  the OpenSSL output includes a "built on: " prefix already. */
 }
 
 
 }