tidying
[exim.git] / src / src / tls-openssl.c
index b8466ee22317b8c066b8863a06d65ea90279be65..eabe34f3159584a7801c238fd8edd3680e143805 100644 (file)
@@ -2,9 +2,10 @@
 *     Exim - an Internet mail transport agent    *
 *************************************************/
 
+/* Copyright (c) The Exim Maintainers 2020 - 2022 */
 /* Copyright (c) University of Cambridge 1995 - 2019 */
-/* Copyright (c) The Exim Maintainers 2020 */
 /* 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 */
 
@@ -48,6 +49,7 @@ functions from the OpenSSL library. */
 #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
@@ -77,9 +79,12 @@ 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_OPESSL_OCSP_RESP_GET0_CERTS
+#  define EXIM_HAVE_SSL_GET0_VERIFIED_CHAIN
 #  ifndef DISABLE_OCSP
 #   define EXIM_HAVE_OCSP
 #  endif
+#  define EXIM_HAVE_ALPN /* fail ret from hshake-cb is ignored by LibreSSL */
 # else
 #  define EXIM_NEED_OPENSSL_INIT
 # endif
@@ -89,6 +94,15 @@ change this guard and punt the issue for a while longer. */
 # 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)
@@ -106,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 EXIM_HAVE_EXP_CHNL_BNGNG
+#  define EXIM_HAVE_OPENSSL_OCSP_RESP_GET0_SIGNER
 # 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
@@ -227,12 +247,16 @@ static exim_openssl_option exim_openssl_options[] = {
   { 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 */
-#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
 #endif
 #ifdef SSL_OP_NO_TLSv1_2
   { US"no_tlsv1_2", SSL_OP_NO_TLSv1_2 },
@@ -306,6 +330,9 @@ builtin_macro_create(US"_TLS_BAD_MULTICERT_IN_OURCERT");
 builtin_macro_create(US"_HAVE_TLS_OCSP");
 builtin_macro_create(US"_HAVE_TLS_OCSP_LIST");
 # endif
+# ifdef EXIM_HAVE_ALPN
+builtin_macro_create(US"_HAVE_TLS_ALPN");
+# endif
 }
 #else
 
@@ -358,6 +385,9 @@ typedef struct {
 #ifdef EXIM_HAVE_OPENSSL_TLSEXT
 static SSL_CTX *server_sni = NULL;
 #endif
+#ifdef EXIM_HAVE_ALPN
+static BOOL server_seen_alpn = FALSE;
+#endif
 
 static char ssl_errstring[256];
 
@@ -383,15 +413,16 @@ typedef struct exim_openssl_state {
   uschar *     privatekey;
   BOOL         is_server;
 #ifndef DISABLE_OCSP
-  STACK_OF(X509) *verify_stack;                /* chain for verifying the proof */
   union {
     struct {
       uschar        *file;
       const uschar  *file_expanded;
       ocsp_resplist *olist;
+      STACK_OF(X509) *verify_stack;    /* chain for verifying the proof */
     } server;
     struct {
       X509_STORE    *verify_store;     /* non-null if status requested */
+      uschar       *verify_errstr;     /* only if _required */
       BOOL         verify_required;
     } client;
   } u_ocsp;
@@ -414,15 +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
-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 */
-#ifdef EXIM_HAVE_OPENSSL_TLSEXT
-static int tls_servername_cb(SSL *s, int *ad ARG_UNUSED, void *arg);
-#endif
 #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
 
 
@@ -537,23 +567,27 @@ EVP_add_digest(EVP_sha256());
 *************************************************/
 
 /* 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
-  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
-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))
@@ -566,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),
-          host, US strerror(errno), errstr);
+          NULL, US strerror(errno), errstr);
     return FALSE;
     }
   }
@@ -581,53 +615,80 @@ else
   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);
   }
 
-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),
-      host, NULL, errstr);
+      NULL, NULL, errstr);
   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
- * 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);
-#else
+# else
 dh_bitsize = 8 * DH_size(dh);
+# endif
+#else  /* 3.0.0 + */
+dh_bitsize = EVP_PKEY_get_bits(pkey);
 #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
-  {
-  SSL_CTX_set_tmp_dh(sctx, dh);
   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);
-BIO_free(bio);
+#endif
+/* The EVP_PKEY ownership stays with the ctx; do not free it */
 
+BIO_free(bio);
 return TRUE;
 }
 
@@ -638,7 +699,7 @@ return TRUE;
 *               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
@@ -656,27 +717,22 @@ Patches welcome.
 
 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
-init_ecdh(SSL_CTX * sctx, host_item * host, uschar ** errstr)
+init_ecdh(SSL_CTX * sctx, uschar ** errstr)
 {
 #ifdef OPENSSL_NO_ECDH
 return TRUE;
 #else
 
-EC_KEY * ecdh;
 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");
@@ -704,12 +760,12 @@ if (Ustrcmp(exp_curve, "auto") == 0)
 #else
 # if defined SSL_CTRL_SET_ECDH_AUTO
   DEBUG(D_tls) debug_printf(
-    "ECDH OpenSSL 1.0.2+ temp key parameter settings: autoselection\n");
+    "ECDH OpenSSL 1.0.2+: temp key parameter settings: autoselection\n");
   SSL_CTX_set_ecdh_auto(sctx, 1);
   return TRUE;
 # else
   DEBUG(D_tls) debug_printf(
-    "ECDH OpenSSL 1.1.0+ temp key parameter settings: default selection\n");
+    "ECDH OpenSSL 1.1.0+: temp key parameter settings: default selection\n");
   return TRUE;
 # endif
 #endif
@@ -723,25 +779,38 @@ if (  (nid = OBJ_sn2nid       (CCS exp_curve)) == NID_undef
    )
   {
   tls_error(string_sprintf("Unknown curve name tls_eccurve '%s'", exp_curve),
-    host, NULL, errstr);
+    NULL, NULL, errstr);
   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. */
+
+  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);
+ }
 
-/* The "tmp" in the name here refers to setting a temporary key
-not to the stability of the interface. */
+#else  /* v 3.0.0 + */
 
-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_set1_groups(sctx, &nid, 1)) == 0)
+  tls_error(string_sprintf("Error enabling '%s' group", exp_curve), NULL, NULL, errstr);
 else
-  DEBUG(D_tls) debug_printf("ECDH: enabled '%s' 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*/
@@ -754,6 +823,7 @@ return !rv;
 *        Expand key and cert file specs          *
 *************************************************/
 
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
 /*
 Arguments:
   s          SSL connection (not used)
@@ -773,14 +843,14 @@ BIGNUM *bn = BN_new();
 
 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)
    )
-#else
+# else
 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));
@@ -790,20 +860,24 @@ if (!(rsa_key = RSA_generate_key(keylength, RSA_F4, NULL, NULL)))
   }
 return rsa_key;
 }
+#endif /* pre-3.0.0 */
 
 
 
 /* Create and install a selfsigned certificate, for use in server mode */
+/*XXX we could arrange to call this during prelo for a null tls_certificate option.
+The normal cache inval + relo will suffice.
+Just need a timer for inval. */
 
 static int
 tls_install_selfsign(SSL_CTX * sctx, uschar ** errstr)
 {
 X509 * x509 = NULL;
 EVP_PKEY * pkey;
-RSA * rsa;
 X509_NAME * name;
 uschar * where;
 
+DEBUG(D_tls) debug_printf("TLS: generating selfsigned server cert\n");
 where = US"allocating pkey";
 if (!(pkey = EVP_PKEY_new()))
   goto err;
@@ -813,17 +887,24 @@ if (!(x509 = X509_new()))
   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_gmtime_adj(X509_get_notBefore(x509), 0);
-X509_gmtime_adj(X509_get_notAfter(x509), (long)60 * 60);       /* 1 hour */
+X509_gmtime_adj(X509_get_notAfter(x509), (long)2 * 60 * 60);   /* 2 hour */
 X509_set_pubkey(x509, pkey);
 
 name = X509_get_subject_name(x509);
@@ -879,34 +960,35 @@ Returns:    nothing
 */
 
 static void
-info_callback(SSL *s, int where, int ret)
+info_callback(SSL * s, int where, int ret)
 {
 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)
-     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)
-    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)
-     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));
-  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));
+    {
+    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 | SSL_CB_HANDSHAKE_DONE))
+     debug_printf("SSL %s: %s\n", g->s, SSL_state_string_long(s));
   }
 }
 
@@ -944,7 +1026,7 @@ if (ev)
   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",
@@ -1043,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);
-#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 */
@@ -1182,21 +1252,7 @@ DEBUG(D_tls) debug_printf("verify_callback_client_dane: %s depth %d %s\n",
 #endif
 
 if (preverify_ok == 1)
-  {
   tls_out.dane_verified = TRUE;
-#ifndef DISABLE_OCSP
-  if (client_static_state->u_ocsp.client.verify_store)
-    {  /* client, wanting stapling  */
-    /* Add the server cert's signing chain as the one
-    for the verification of the OCSP stapled information. */
-
-    if (!X509_STORE_add_cert(client_static_state->u_ocsp.client.verify_store,
-                             cert))
-      ERR_clear_error();
-    sk_X509_push(client_static_state->verify_stack, cert);
-    }
-#endif
-  }
 else
   {
   int err = X509_STORE_CTX_get_error(x509ctx);
@@ -1212,6 +1268,14 @@ return preverify_ok;
 
 
 #ifndef DISABLE_OCSP
+static void
+time_print(BIO * bp, const char * str, ASN1_GENERALIZEDTIME * time)
+{
+BIO_printf(bp, "\t%s: ", str);
+ASN1_GENERALIZEDTIME_print(bp, time);
+BIO_puts(bp, "\n");
+}
+
 /*************************************************
 *       Load OCSP information into state         *
 *************************************************/
@@ -1237,16 +1301,19 @@ OCSP_BASICRESP * basic_response;
 OCSP_SINGLERESP * single_response;
 ASN1_GENERALIZEDTIME * rev, * thisupd, * nextupd;
 STACK_OF(X509) * sk;
-unsigned long verify_flags;
 int status, reason, i;
 
 DEBUG(D_tls)
   debug_printf("tls_ocsp_file (%s)  '%s'\n", is_pem ? "PEM" : "DER", filename);
 
+if (!filename || !*filename) return;
+
+ERR_clear_error();
 if (!(bio = BIO_new_file(CS filename, "rb")))
   {
-  DEBUG(D_tls) debug_printf("Failed to open OCSP response file \"%s\"\n",
-      filename);
+  log_write(0, LOG_MAIN|LOG_PANIC,
+    "Failed to open OCSP response file \"%s\": %.100s",
+    filename, ERR_reason_error_string(ERR_get_error()));
   return;
   }
 
@@ -1257,8 +1324,8 @@ if (is_pem)
   long len;
   if (!PEM_read_bio(bio, &dummy, &dummy, &data, &len))
     {
-    DEBUG(D_tls) debug_printf("Failed to read PEM file \"%s\"\n",
-       filename);
+    log_write(0, LOG_MAIN|LOG_PANIC, "Failed to read PEM file \"%s\": %.100s",
+      filename, ERR_reason_error_string(ERR_get_error()));
     return;
     }
   freep = data;
@@ -1271,7 +1338,8 @@ BIO_free(bio);
 
 if (!resp)
   {
-  DEBUG(D_tls) debug_printf("Error reading OCSP response.\n");
+  log_write(0, LOG_MAIN|LOG_PANIC, "Error reading OCSP response from \"%s\": %s",
+      filename, ERR_reason_error_string(ERR_get_error()));
   return;
   }
 
@@ -1297,20 +1365,20 @@ if (!(basic_response = OCSP_response_get1_basic(resp)))
   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 */
 
-/* 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
-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
@@ -1318,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) ? ]
+[ 3.0.0 - sk = X509_STORE_get1_all_certs(store) ]
 We do not free the stack since it could be needed a second time for
 SNI handling.
 
 Separately we might try to replace using OCSP_basic_verify() - which seems to not
 be a public interface into the OpenSSL library (there's no manual entry) -
+(in 3.0.0 + 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?  */
 
-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_printf("OCSP response verify failure: %s\n", US ssl_errstring);
+    debug_printf("OCSP response has bad signature: %s\n", US ssl_errstring);
     }
   goto bad;
   }
@@ -1366,7 +1436,16 @@ if (status != V_OCSP_CERTSTATUS_GOOD)
 
 if (!OCSP_check_validity(thisupd, nextupd, EXIM_OCSP_SKEW_SECONDS, EXIM_OCSP_MAX_AGE))
   {
-  DEBUG(D_tls) debug_printf("OCSP status invalid times.\n");
+  DEBUG(D_tls)
+    {
+    BIO * bp = BIO_new(BIO_s_mem());
+    uschar * s = NULL;
+    int len;
+    time_print(bp, "This OCSP Update", thisupd);
+    if (nextupd) time_print(bp, "Next OCSP Update", nextupd);
+    if ((len = (int) BIO_get_mem_data(bp, CSS &s)) > 0) debug_printf("%.*s", len, s);
+    debug_printf("OCSP status invalid times.\n");
+    }
   goto bad;
   }
 
@@ -1376,7 +1455,7 @@ supply_response:
   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;
   }
@@ -1398,12 +1477,12 @@ return;
 
 
 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);
-cbinfo->u_ocsp.server.olist = NULL;
+state->u_ocsp.server.olist = NULL;
 }
 #endif /*!DISABLE_OCSP*/
 
@@ -1495,6 +1574,11 @@ else
       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))
        {
@@ -1572,225 +1656,24 @@ return OK;
 * One-time init credentials for server and client *
 **************************************************/
 
-
-#ifdef gnutls
 static void
-creds_basic_init(gnutls_certificate_credentials_t x509_cred, BOOL server)
-{
-}
-#endif
-
-static int
-creds_load_server_certs(/*exim_gnutls_state_st * state,*/ const uschar * cert,
-  const uschar * pkey, const uschar * ocsp, uschar ** errstr)
-{
-#ifdef gnutls
-const uschar * clist = cert;
-const uschar * klist = pkey;
-const uschar * olist;
-int csep = 0, ksep = 0, osep = 0, cnt = 0, rc;
-uschar * cfile, * kfile, * ofile;
-#ifndef DISABLE_OCSP
-# ifdef SUPPORT_GNUTLS_EXT_RAW_PARSE
-gnutls_x509_crt_fmt_t ocsp_fmt = GNUTLS_X509_FMT_DER;
-# endif
-
-if (!expand_check(ocsp, US"tls_ocsp_file", &ofile, errstr))
-  return DEFER;
-olist = ofile;
-#endif
-
-while (cfile = string_nextinlist(&clist, &csep, NULL, 0))
-
-  if (!(kfile = string_nextinlist(&klist, &ksep, NULL, 0)))
-    return tls_error(US"cert/key setup: out of keys", NULL, NULL, errstr);
-  else if ((rc = tls_add_certfile(state, NULL, cfile, kfile, errstr)) > 0)
-    return rc;
-  else
-    {
-    int gnutls_cert_index = -rc;
-    DEBUG(D_tls) debug_printf("TLS: cert/key %d %s registered\n",
-                             gnutls_cert_index, cfile);
-
-#ifndef DISABLE_OCSP
-    if (ocsp)
-      {
-      /* Set the OCSP stapling server info */
-      if (gnutls_buggy_ocsp)
-       {
-       DEBUG(D_tls)
-         debug_printf("GnuTLS library is buggy for OCSP; avoiding\n");
-       }
-      else if ((ofile = string_nextinlist(&olist, &osep, NULL, 0)))
-       {
-       DEBUG(D_tls) debug_printf("OCSP response file %d  = %s\n",
-                                 gnutls_cert_index, ofile);
-# ifdef SUPPORT_GNUTLS_EXT_RAW_PARSE
-       if (Ustrncmp(ofile, US"PEM ", 4) == 0)
-         {
-         ocsp_fmt = GNUTLS_X509_FMT_PEM;
-         ofile += 4;
-         }
-       else if (Ustrncmp(ofile, US"DER ", 4) == 0)
-         {
-         ocsp_fmt = GNUTLS_X509_FMT_DER;
-         ofile += 4;
-         }
-
-       if  ((rc = gnutls_certificate_set_ocsp_status_request_file2(
-                 state->lib_state.x509_cred, CCS ofile, gnutls_cert_index,
-                 ocsp_fmt)) < 0)
-         return tls_error_gnu(
-                 US"gnutls_certificate_set_ocsp_status_request_file2",
-                 rc, NULL, errstr);
-       DEBUG(D_tls)
-         debug_printf(" %d response%s loaded\n", rc, rc>1 ? "s":"");
-
-       /* Arrange callbacks for OCSP request observability */
-
-       if (state->session)
-         gnutls_handshake_set_hook_function(state->session,
-           GNUTLS_HANDSHAKE_ANY, GNUTLS_HOOK_POST, tls_server_hook_cb);
-       else
-         state->lib_state.ocsp_hook = TRUE;
-
-
-# else
-#  if defined(SUPPORT_SRV_OCSP_STACK)
-       if ((rc = gnutls_certificate_set_ocsp_status_request_function2(
-                    state->lib_state.x509_cred, gnutls_cert_index,
-                    server_ocsp_stapling_cb, ofile)))
-           return tls_error_gnu(
-                 US"gnutls_certificate_set_ocsp_status_request_function2",
-                 rc, NULL, errstr);
-       else
-#  endif
-         {
-         if (cnt++ > 0)
-           {
-           DEBUG(D_tls)
-             debug_printf("oops; multiple OCSP files not supported\n");
-           break;
-           }
-         gnutls_certificate_set_ocsp_status_request_function(
-           state->lib_state.x509_cred, server_ocsp_stapling_cb, ofile);
-         }
-# endif        /* SUPPORT_GNUTLS_EXT_RAW_PARSE */
-       }
-      else
-       DEBUG(D_tls) debug_printf("ran out of OCSP response files in list\n");
-      }
-#endif /* DISABLE_OCSP */
-    }
-return 0;
-#endif /*gnutls*/
-}
-
-static int
-creds_load_client_certs(/*exim_gnutls_state_st * state,*/ const host_item * host,
-  const uschar * cert, const uschar * pkey, uschar ** errstr)
-{
-return 0;
-}
-
-static int
-creds_load_cabundle(/*exim_gnutls_state_st * state,*/ const uschar * bundle,
-  const host_item * host, uschar ** errstr)
+normalise_ciphers(uschar ** ciphers, const uschar * pre_expansion_ciphers)
 {
-#ifdef gnutls
-int cert_count;
-struct stat statbuf;
-
-#ifdef SUPPORT_SYSDEFAULT_CABUNDLE
-if (Ustrcmp(bundle, "system") == 0 || Ustrncmp(bundle, "system,", 7) == 0)
-  cert_count = gnutls_certificate_set_x509_system_trust(state->lib_state.x509_cred);
-else
-#endif
-  {
-  if (Ustat(bundle, &statbuf) < 0)
-    {
-    log_write(0, LOG_MAIN|LOG_PANIC, "could not stat '%s' "
-       "(tls_verify_certificates): %s", bundle, strerror(errno));
-    return DEFER;
-    }
-
-#ifndef SUPPORT_CA_DIR
-  /* The test suite passes in /dev/null; we could check for that path explicitly,
-  but who knows if someone has some weird FIFO which always dumps some certs, or
-  other weirdness.  The thing we really want to check is that it's not a
-  directory, since while OpenSSL supports that, GnuTLS does not.
-  So s/!S_ISREG/S_ISDIR/ and change some messaging ... */
-  if (S_ISDIR(statbuf.st_mode))
-    {
-    log_write(0, LOG_MAIN|LOG_PANIC,
-       "tls_verify_certificates \"%s\" is a directory", bundle);
-    return DEFER;
-    }
-#endif
-
-  DEBUG(D_tls) debug_printf("verify certificates = %s size=" OFF_T_FMT "\n",
-         bundle, statbuf.st_size);
+uschar * s = *ciphers;
 
-  if (statbuf.st_size == 0)
-    {
-    DEBUG(D_tls)
-      debug_printf("cert file empty, no certs, no verification, ignoring any CRL\n");
-    return OK;
-    }
-
-  cert_count =
-
-#ifdef SUPPORT_CA_DIR
-    (statbuf.st_mode & S_IFMT) == S_IFDIR
-    ?
-    gnutls_certificate_set_x509_trust_dir(state->lib_state.x509_cred,
-      CS bundle, GNUTLS_X509_FMT_PEM)
-    :
-#endif
-    gnutls_certificate_set_x509_trust_file(state->lib_state.x509_cred,
-      CS bundle, GNUTLS_X509_FMT_PEM);
-
-#ifdef SUPPORT_CA_DIR
-  /* Mimic the behaviour with OpenSSL of not advertising a usable-cert list
-  when using the directory-of-certs config model. */
-
-  if ((statbuf.st_mode & S_IFMT) == S_IFDIR)
-    if (state->session)
-      gnutls_certificate_send_x509_rdn_sequence(state->session, 1);
-    else
-      state->lib_state.ca_rdn_emulate = TRUE;
-#endif
-  }
-
-if (cert_count < 0)
-  return tls_error_gnu(US"setting certificate trust", cert_count, host, errstr);
-DEBUG(D_tls)
-  debug_printf("Added %d certificate authorities\n", cert_count);
-
-#endif /*gnutls*/
-return OK;
-}
-
-
-static int
-creds_load_crl(/*exim_gnutls_state_st * state,*/ const uschar * crl, uschar ** errstr)
-{
-return FAIL;
-}
+if (!s || !Ustrchr(s, '_')) return;    /* no change needed */
 
+if (s == pre_expansion_ciphers)
+  s = string_copy(s);                  /* get writable copy */
 
-static int
-creds_load_pristring(/*exim_gnutls_state_st * state,*/ const uschar * p,
-  const char ** errpos)
-{
-return FAIL;
+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)
 {
-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);
@@ -1833,18 +1716,19 @@ return OK;
 }
 
 
-static void
+static unsigned
 tls_server_creds_init(void)
 {
 SSL_CTX * ctx;
 uschar * dummy_errstr;
+unsigned lifetime = 0;
 
 tls_openssl_init();
 
 state_server.lib_state = null_tls_preload;
 
 if (lib_ctx_new(&ctx, NULL, &dummy_errstr) != OK)
-  return;
+  return 0;
 state_server.lib_state.lib_ctx = ctx;
 
 /* Preload DH params and EC curve */
@@ -1852,53 +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 (init_dh(ctx, tls_dhparam, NULL, &dummy_errstr))
+  if (init_dh(ctx, tls_dhparam, &dummy_errstr))
     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 (init_ecdh(ctx, NULL, &dummy_errstr))
+  if (init_ecdh(ctx, &dummy_errstr))
     state_server.lib_state.ecdh = TRUE;
   }
-
-#ifdef EXIM_HAVE_INOTIFY
-/* 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
-  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)
-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))
@@ -1909,15 +1765,60 @@ if (  opt_set_and_noexpand(tls_verify_certificates)
      && 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");
 
-    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;
-    }
+
+    /* 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");
+
+
 #endif /* EXIM_HAVE_INOTIFY */
 
 
@@ -1926,12 +1827,14 @@ else
 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;
   }
 else
   DEBUG(D_tls) debug_printf("TLS: not preloading cipher list for server\n");
+return lifetime;
 }
 
 
@@ -1960,20 +1863,7 @@ ob->tls_preload.lib_ctx = ctx;
 
 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;
-  }
-
-#ifdef EXIM_HAVE_INOTIFY
+#if defined(EXIM_HAVE_INOTIFY) || defined(EXIM_HAVE_KEVENT)
 if (  opt_set_and_noexpand(ob->tls_certificate)
    && opt_unset_or_noexpand(ob->tls_privatekey))
   {
@@ -2010,10 +1900,11 @@ if (  opt_set_and_noexpand(ob->tls_verify_certificates)
        && 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);
 
-    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;
     }
@@ -2026,15 +1917,18 @@ else
 }
 
 
-#ifdef EXIM_HAVE_INOTIFY
+#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;
+#ifndef DISABLE_OCSP
+state_server.u_ocsp.server.file_expanded = NULL;
+#endif
 }
 
 
@@ -2045,32 +1939,66 @@ smtp_transport_options_block * ob = t->options_block;
 SSL_CTX_free(ob->tls_preload.lib_ctx);
 ob->tls_preload = null_tls_preload;
 }
+
+#else
+
+static void
+tls_server_creds_invalidate(void)
+{ return; }
+
+static void
+tls_client_creds_invalidate(transport_instance * t)
+{ return; }
+
 #endif /*EXIM_HAVE_INOTIFY*/
 
 
 /* Extreme debug
+ * */
 #ifndef DISABLE_OCSP
-void
-x509_store_dump_cert_s_names(X509_STORE * store)
+static void
+debug_print_sn(const X509 * cert)
 {
-STACK_OF(X509_OBJECT) * roots= store->objs;
+X509_NAME * sn = X509_get_subject_name((X509 *)cert);
 static uschar name[256];
-
-for (int i= 0; i < sk_X509_OBJECT_num(roots); i++)
+if (X509_NAME_oneline(sn, CS name, sizeof(name)))
   {
-  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);
-      }
-    }
+  name[sizeof(name)-1] = '\0';
+  debug_printf(" %s\n", name);
   }
 }
-#endif
+
+static void
+x509_stack_dump_cert_s_names(const STACK_OF(X509) * sk)
+{
+if (!sk)
+  debug_printf(" (null)\n");
+else
+  {
+  int idx = sk_X509_num(sk);
+  if (!idx)
+    debug_printf(" (empty)\n");
+  else
+    while (--idx >= 0) debug_print_sn(sk_X509_value(sk, idx));
+  }
+}
+
+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*/
+/*
 */
 
 
@@ -2082,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? */
+# if OPENSSL_VERSION_NUMBER < 0x30000000L
   const EVP_MD *       hmac_hash;
+# else
+  const uschar *       hmac_hashname;
+# endif
   uschar               hmac_key[16];
   time_t               renew;
   time_t               expire;
@@ -2111,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();
+# if OPENSSL_VERSION_NUMBER < 0x30000000L
 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;
 }
@@ -2131,10 +2067,49 @@ return memcmp(name, exim_tk.name, sizeof(exim_tk.name)) == 0 ? &exim_tk
   : 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],
-  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;
@@ -2152,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));
 
-  /*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");
@@ -2177,8 +2150,7 @@ else
     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);
@@ -2191,7 +2163,7 @@ else
   return key->renew < now ? 2 : 1;
   }
 }
-#endif
+#endif /* !DISABLE_TLS_RESUME */
 
 
 
@@ -2245,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;
-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)
@@ -2261,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? */
 
 {
+ {
   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_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);
 }
+ }
 
-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;
 
@@ -2289,14 +2265,12 @@ if (state->u_ocsp.server.file)
 #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;
 
-  if (expcerts && *expcerts)
+  if (v_certs && *v_certs)
     setup_cert_verify(server_sni, FALSE, verify_callback_server);
   }
 
@@ -2316,6 +2290,61 @@ bad: return SSL_TLSEXT_ERR_ALERT_FATAL;
 
 
 
+#ifdef EXIM_HAVE_ALPN
+/*************************************************
+*        Callback to handle ALPN                 *
+*************************************************/
+
+/* Called on server if tls_alpn nonblank after expansion,
+when client offers ALPN, after the SNI callback.
+If set and not matching the list then we dump the connection */
+
+static int
+tls_server_alpn_cb(SSL *ssl, const uschar ** out, uschar * outlen,
+  const uschar * in, unsigned int inlen, void * arg)
+{
+server_seen_alpn = TRUE;
+DEBUG(D_tls)
+  {
+  debug_printf("Received TLS ALPN offer:");
+  for (int pos = 0, siz; pos < inlen; pos += siz+1)
+    {
+    siz = in[pos];
+    if (pos + 1 + siz > inlen) siz = inlen - pos - 1;
+    debug_printf(" '%.*s'", siz, in + pos + 1);
+    }
+  debug_printf(".  Our list: '%s'\n", tls_alpn);
+  }
+
+/* Look for an acceptable ALPN */
+
+if (  inlen > 1                /* at least one name */
+   && in[0]+1 == inlen /* filling the vector, so exactly one name */
+   )
+  {
+  const uschar * list = tls_alpn;
+  int sep = 0;
+  for (uschar * name; name = string_nextinlist(&list, &sep, NULL, 0); )
+    if (Ustrncmp(in+1, name, in[0]) == 0)
+      {
+      *out = in+1;                     /* we checked for exactly one, so can just point to it */
+      *outlen = inlen;
+      return SSL_TLSEXT_ERR_OK;                /* use ALPN */
+      }
+  }
+
+/* More than one name from clilent, or name did not match our list. */
+
+/* This will be fatal to the TLS conn; would be nice to kill TCP also.
+Maybe as an option in future; for now leave control to the config (must-tls). */
+
+DEBUG(D_tls) debug_printf("TLS ALPN rejected\n");
+return SSL_TLSEXT_ERR_ALERT_FATAL;
+}
+#endif /* EXIM_HAVE_ALPN */
+
+
+
 #ifndef DISABLE_OCSP
 
 /*************************************************
@@ -2351,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_NAME * cert_issuer = X509_get_issuer_name(cert_sent);
-  uschar * chash;
-  uint chash_len;
 
   for (; olist; olist = olist->next)
     {
@@ -2416,15 +2442,25 @@ return SSL_TLSEXT_ERR_OK;
 
 
 static void
-time_print(BIO * bp, const char * str, ASN1_GENERALIZEDTIME * time)
+add_chain_to_store(X509_STORE * store, STACK_OF(X509) * sk,
+  const char * debug_text)
 {
-BIO_printf(bp, "\t%s: ", str);
-ASN1_GENERALIZEDTIME_print(bp, time);
-BIO_puts(bp, "\n");
+int idx;
+
+DEBUG(D_tls)
+  {
+  debug_printf("chain for %s:\n", debug_text);
+  x509_stack_dump_cert_s_names(sk);
+  }
+if (sk)
+  if ((idx = sk_X509_num(sk)) > 0)
+    while (--idx >= 0)
+      X509_STORE_add_cert(store, sk_X509_value(sk, idx));
+
 }
 
 static int
-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;
@@ -2434,16 +2470,26 @@ OCSP_BASICRESP * bs;
 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)
- {
-  /* Expect this when we requested ocsp but got none */
+  {                            /* Expect this when we requested ocsp but got none */
+  if (SSL_session_reused(ssl) && tls_out.ocsp == OCSP_VFIED)
+    {
+    DEBUG(D_tls) debug_printf(" null, but resumed; ocsp vfy stored with session is good\n");
+    return 1;
+    }
+
   if (cbinfo->u_ocsp.client.verify_required && LOGGING(tls_cipher))
     log_write(0, LOG_MAIN, "Required TLS certificate status not received");
   else
     DEBUG(D_tls) debug_printf(" null\n");
-  return cbinfo->u_ocsp.client.verify_required ? 0 : 1;
- }
+
+  if (!cbinfo->u_ocsp.client.verify_required)
+    return 1;
+  cbinfo->u_ocsp.client.verify_errstr =
+                       US"(SSL_connect) Required TLS certificate status not received";
+  return 0;
+  }
 
 if (!(rsp = d2i_OCSP_RESPONSE(NULL, &p, len)))
   {
@@ -2475,33 +2521,160 @@ if (!(bs = OCSP_response_get1_basic(rsp)))
 */
   {
     BIO * bp = NULL;
+    X509_STORE * verify_store = NULL;
+    BOOL have_verified_OCSP_signer = FALSE;
 #ifndef EXIM_HAVE_OCSP_RESP_COUNT
     STACK_OF(OCSP_SINGLERESP) * sresp = bs->tbsResponseData->responses;
 #endif
 
-    DEBUG(D_tls) bp = BIO_new_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. */
 
-    /* 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); */
+#ifdef EXIM_HAVE_OPENSSL_OCSP_RESP_GET0_SIGNER
+    X509 * signer;
 
-    if ((i = OCSP_basic_verify(bs, cbinfo->verify_stack,
-             cbinfo->u_ocsp.client.verify_store, OCSP_NOEXPLICIT)) <= 0)
+    if (  tls_out.dane_verified
+       && (have_verified_OCSP_signer =
+       OCSP_resp_get0_signer(bs, &signer, SSL_get0_verified_chain(ssl)) == 1))
+      {
+      DEBUG(D_tls)
+       debug_printf("signer for OCSP basicres is in the verified chain;"
+                     " shortcut its verification\n");
+      }
+    else
+#endif
+      {
+      STACK_OF(X509) * verified_chain;
+
+      verify_store = X509_STORE_new();
+
+      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 (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");
+      }
 
     DEBUG(D_tls) debug_printf("OCSP response well-formed and signed OK\n");
 
@@ -2534,14 +2707,19 @@ if (!(bs = OCSP_response_get1_basic(rsp)))
       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);
-       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;
        }
 
@@ -2552,12 +2730,16 @@ if (!(bs = OCSP_response_get1_basic(rsp)))
        case V_OCSP_CERTSTATUS_GOOD:
          continue;     /* the idx loop */
        case V_OCSP_CERTSTATUS_REVOKED:
+         cbinfo->u_ocsp.client.verify_errstr =
+                       US"(SSL_connect) Server certificate revoked";
          log_write(0, LOG_MAIN, "Server certificate revoked%s%s",
              reason != -1 ? "; reason: " : "",
              reason != -1 ? OCSP_crl_reason_str(reason) : "");
          DEBUG(D_tls) time_print(bp, "Revocation Time", rev);
          break;
        default:
+         cbinfo->u_ocsp.client.verify_errstr =
+                       US"(SSL_connect) Server certificate has unknown status";
          log_write(0, LOG_MAIN,
              "Server certificate status unknown, in OCSP stapling");
          break;
@@ -2574,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:
+    {
+    uschar * s = NULL;
+    int len = (int) BIO_get_mem_data(bp, CSS &s);
+    if (len > 0) debug_printf("%.*s", len, s);
+    }
     BIO_free(bp);
   }
 
@@ -2606,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,
-  tls_support * tlsp,
-  uschar ** errstr)
+  tls_support * tlsp, uschar ** errstr)
 {
 SSL_CTX * ctx;
 exim_openssl_state_st * state;
@@ -2704,10 +2890,17 @@ if (init_options)
     }
 #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);
+   }
   }
 else
   DEBUG(D_tls) debug_printf("no SSL CTX options to set\n");
@@ -2725,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 */
 
-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) */
 
@@ -2745,7 +2941,7 @@ if (state->lib_state.conn_certs)
 else
   {
 #ifndef DISABLE_OCSP
-  if (!host)
+  if (!host)                                   /* server */
     {
     state->u_ocsp.server.file = ocsp_file;
     state->u_ocsp.server.file_expanded = NULL;
@@ -2759,7 +2955,7 @@ else
 
 #ifdef EXIM_HAVE_OPENSSL_TLSEXT
 # ifndef DISABLE_OCSP
-  if (!(state->verify_stack = sk_X509_new_null()))
+  if (!host && !(state->u_ocsp.server.verify_stack = sk_X509_new_null()))
     {
     DEBUG(D_tls) debug_printf("failed to create stack for stapling verify\n");
     return FAIL;
@@ -2783,6 +2979,21 @@ if (!host)               /* server */
   tls_certificate */
   SSL_CTX_set_tlsext_servername_callback(ctx, tls_servername_cb);
   SSL_CTX_set_tlsext_servername_arg(ctx, state);
+
+# ifdef EXIM_HAVE_ALPN
+  if (tls_alpn && *tls_alpn)
+    {
+    uschar * exp_alpn;
+    if (  expand_check(tls_alpn, US"tls_alpn", &exp_alpn, errstr)
+       && *exp_alpn && !isblank(*exp_alpn))
+      {
+      tls_alpn = exp_alpn;     /* subprocess so ok to overwrite */
+      SSL_CTX_set_alpn_select_cb(ctx, tls_server_alpn_cb, state);
+      }
+    else
+      tls_alpn = NULL;
+    }
+# endif
   }
 # ifndef DISABLE_OCSP
 else                   /* client */
@@ -2793,11 +3004,12 @@ else                    /* client */
       DEBUG(D_tls) debug_printf("failed to create store for stapling verify\n");
       return FAIL;
       }
+
     SSL_CTX_set_tlsext_status_cb(ctx, tls_client_stapling_cb);
     SSL_CTX_set_tlsext_status_arg(ctx, state);
     }
 # endif
-#endif
+#endif /*EXIM_HAVE_OPENSSL_TLSEXT*/
 
 state->verify_cert_hostnames = NULL;
 
@@ -2936,21 +3148,25 @@ if (tlsp->peercert)
 *************************************************/
 
 #ifndef DISABLE_OCSP
-/* Load certs from file, return TRUE on success */
+/* In the server, load certs from file, return TRUE on success */
 
 static BOOL
-chain_from_pem_file(const uschar * file, STACK_OF(X509) * verify_stack)
+chain_from_pem_file(const uschar * file, STACK_OF(X509) ** vp)
 {
 BIO * bp;
-X509 * x;
+STACK_OF(X509) * verify_stack = *vp;
 
-while (sk_X509_num(verify_stack) > 0)
-  X509_free(sk_X509_pop(verify_stack));
+if (verify_stack)
+  while (sk_X509_num(verify_stack) > 0)
+    X509_free(sk_X509_pop(verify_stack));
+else
+  verify_stack = sk_X509_new_null();
 
 if (!(bp = BIO_new_file(CS file, "r"))) return FALSE;
-while ((x = PEM_read_bio_X509(bp, NULL, 0, NULL)))
+for (X509 * x; x = PEM_read_bio_X509(bp, NULL, 0, NULL); )
   sk_X509_push(verify_stack, x);
 BIO_free(bp);
+*vp = verify_stack;
 return TRUE;
 }
 #endif
@@ -2962,7 +3178,7 @@ repeated after a Server Name Indication.
 
 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
@@ -2971,15 +3187,16 @@ Returns:        OK/DEFER/FAIL
 */
 
 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 *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);
 
+*certsp = expcerts;
 if (expcerts && *expcerts)
   {
   /* Tell the library to use its compiled-in location for the system default
@@ -3005,19 +3222,26 @@ if (expcerts && *expcerts)
        { file = NULL; dir = expcerts; }
       else
        {
+       STACK_OF(X509) * verify_stack =
+#ifndef DISABLE_OCSP
+         !host ? state_server.u_ocsp.server.verify_stack :
+#endif
+         NULL;
+       STACK_OF(X509) ** vp = &verify_stack;
+
        file = expcerts; dir = NULL;
 #ifndef DISABLE_OCSP
-       /* In the server if we will be offering an OCSP proof, load chain from
+       /* In the server if we will be offering an OCSP proof; load chain from
        file for verifying the OCSP proof at load time. */
 
 /*XXX Glitch!   The file here is tls_verify_certs: the chain for verifying the client cert.
 This is inconsistent with the need to verify the OCSP proof of the server cert.
 */
-
+/* *debug_printf("file for checking server ocsp stapling is: %s\n", file); */
        if (  !host
           && statbuf.st_size > 0
           && state_server.u_ocsp.server.file
-          && !chain_from_pem_file(file, state_server.verify_stack)
+          && !chain_from_pem_file(file, vp)
           )
          {
          log_write(0, LOG_MAIN|LOG_PANIC,
@@ -3121,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          *
 *************************************************/
@@ -3178,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"); }
-else 
+else
   {
   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
@@ -3204,28 +3492,34 @@ else if (verify_check_host(&tls_try_verify_hosts) == OK)
 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)
-    { 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
-    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;
-
-  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
+# 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 */
+# else
+SSL_CTX_set_tlsext_ticket_key_evp_cb(ctx, ticket_key_callback);
+/* despite working, appears to always return failure, so ignoring */
+# endif
 #endif
+
 #ifdef OPENSSL_HAVE_NUM_TICKETS
 # ifndef DISABLE_TLS_RESUME
 SSL_CTX_set_num_tickets(ctx, tls_in.host_resumable ? 1 : 0);
@@ -3293,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);
-
+#ifndef DISABLE_EVENT
+      (void) event_raise(event_action, US"tls:fail:connect", *errstr, NULL);
+#endif
       if (SSL_get_shutdown(ssl) == SSL_RECEIVED_SHUTDOWN)
-           SSL_shutdown(ssl);
+       SSL_shutdown(ssl);
 
       tls_close(NULL, TLS_NO_SHUTDOWN);
       return FAIL;
@@ -3303,15 +3599,18 @@ if (rc <= 0)
     /* Handle genuine errors */
     case SSL_ERROR_SSL:
       {
-      uschar * s = US"SSL_accept";
+      uschar * s = NULL;
       int r = ERR_GET_REASON(ERR_peek_error());
       if (  r == SSL_R_WRONG_VERSION_NUMBER
 #ifdef SSL_R_VERSION_TOO_LOW
          || 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));
-      (void) tls_error(s, NULL, sigalrm_seen ? US"timed out" : NULL, errstr);
+       s = string_sprintf("(%s)", SSL_get_version(ssl));
+      (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;
       }
 
@@ -3322,6 +3621,9 @@ if (rc <= 0)
        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));
@@ -3330,6 +3632,9 @@ if (rc <= 0)
                      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;
     }
   }
@@ -3346,10 +3651,38 @@ if (SSL_session_reused(ssl))
   }
 #endif
 
+#ifdef EXIM_HAVE_ALPN
+/* If require-alpn, check server_seen_alpn here.  Else abort TLS */
+if (!tls_alpn || !*tls_alpn)
+  { DEBUG(D_tls) debug_printf("TLS: was not watching for ALPN\n"); }
+else if (!server_seen_alpn)
+  if (verify_check_host(&hosts_require_alpn) == OK)
+    {
+    /* We'd like to send a definitive Alert but OpenSSL provides no facility */
+    SSL_shutdown(ssl);
+    tls_error(US"handshake", NULL, US"ALPN required but not negotiated", errstr);
+    return FAIL;
+    }
+  else
+    { DEBUG(D_tls) debug_printf("TLS: no ALPN presented in handshake\n"); }
+else DEBUG(D_tls)
+  {
+  const uschar * name;
+  unsigned len;
+  SSL_get0_alpn_selected(ssl, &name, &len);
+  if (len && name)
+    debug_printf("ALPN negotiated: '%.*s'\n", (int)*name, name+1);
+  else
+    debug_printf("ALPN: no protocol negotiated\n");
+  }
+#endif
+
+
 /* TLS has been set up. Record data for the connection,
 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));
@@ -3364,13 +3697,7 @@ DEBUG(D_tls)
   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
   {
@@ -3388,19 +3715,7 @@ DEBUG(D_tls)
   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
@@ -3414,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_hasc = tls_hasc;
 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 */
@@ -3453,22 +3768,22 @@ else if (verify_check_given_host(CUSS &ob->tls_try_verify_hosts, host) == 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)
-    { 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
-    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;
-
-  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)
   {
@@ -3547,9 +3862,8 @@ return DEFER;
 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;
@@ -3557,11 +3871,11 @@ if (tlsp->host_resumable)
   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)))
     {
-    /* 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;
@@ -3576,30 +3890,33 @@ if (tlsp->host_resumable)
          debug_printf("decoding session: %s\n", ssl_errstring);
          }
        }
-#ifdef EXIM_HAVE_SESSION_TICKET
-      else if ( SSL_SESSION_get_ticket_lifetime_hint(ss) + dt->time_stamp
-              < time(NULL))
+      else
        {
-       DEBUG(D_tls) debug_printf("session expired\n");
-       dbfn_delete(dbm_file, key);
-       }
+       unsigned long lifetime =
+#ifdef EXIM_HAVE_SESSION_TICKET
+         SSL_SESSION_get_ticket_lifetime_hint(ss);
+#else                  /* Use, fairly arbitrilarily, what we as server would */
+         f.running_in_test_harness ? 6 : ssl_session_timeout;
 #endif
-      else if (!SSL_set_session(ssl, ss))
-       {
-       DEBUG(D_tls)
+       if (lifetime + dt->time_stamp < time(NULL))
+         {
+         DEBUG(D_tls) debug_printf("session expired\n");
+         dbfn_delete(dbm_file, tlsp->resume_index);
+         }
+       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;
+         }
+       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
-       {
-       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) debug_printf("no session record\n");
@@ -3627,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;
-  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;
 
@@ -3640,9 +3957,7 @@ if (SSL_SESSION_is_resumable(ss))         /* 1.1.1 */
 
   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);
@@ -3652,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(
-  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
@@ -3690,7 +4004,7 @@ if (tlsp->host_resumable)
 
 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;
 }
 
@@ -3707,6 +4021,50 @@ if (SSL_session_reused(exim_client_ctx->ssl))
 #endif /* !DISABLE_TLS_RESUME */
 
 
+#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
+tls_alpn_plist(uschar ** tls_alpn, const uschar ** plist, unsigned * plen,
+  uschar ** errstr)
+{
+uschar * exp_alpn;
+
+if (!expand_check(*tls_alpn, US"tls_alpn", &exp_alpn, errstr))
+  return FALSE;
+*tls_alpn = exp_alpn;
+
+if (!exp_alpn)
+  {
+  DEBUG(D_tls) debug_printf("Setting TLS ALPN forced to fail, not sending\n");
+  *plist = NULL;
+  }
+else
+  {
+  /* The server implementation only accepts exactly one protocol name
+  but it's little extra code complexity in the client. */
+
+  const uschar * list = exp_alpn;
+  uschar * p = store_get(Ustrlen(exp_alpn), exp_alpn), * s, * t;
+  int sep = 0;
+  uschar len;
+
+  for (t = p; s = string_nextinlist(&list, &sep, NULL, 0); t += len)
+    {
+    *t++ = len = (uschar) Ustrlen(s);
+    memcpy(t, s, len);
+    }
+  *plist = (*plen = t - p) ? p : NULL;
+  }
+return TRUE;
+}
+#endif /* EXIM_HAVE_ALPN */
+
+
 /*************************************************
 *    Start a TLS session in a client             *
 *************************************************/
@@ -3745,7 +4103,7 @@ BOOL require_ocsp = FALSE;
 
 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;
 
@@ -3756,7 +4114,6 @@ tlsp->tlsa_usage = 0;
 #ifndef DISABLE_OCSP
   {
 # ifdef SUPPORT_DANE
-  /*XXX this should be moved to caller, to be common across gnutls/openssl */
   if (  conn_args->dane
      && ob->hosts_request_ocsp[0] == '*'
      && ob->hosts_request_ocsp[1] == '\0'
@@ -3779,6 +4136,15 @@ tlsp->tlsa_usage = 0;
 # endif
       request_ocsp =
        verify_check_given_host(CUSS &ob->hosts_request_ocsp, host) == OK;
+
+# if defined(SUPPORT_DANE) && !defined(EXIM_HAVE_OPENSSL_OCSP_RESP_GET0_SIGNER)
+  if (conn_args->dane && (require_ocsp || request_ocsp))
+    {
+    DEBUG(D_tls) debug_printf("OpenSSL version to early to combine OCSP"
+                             " and DANE; disabling OCSP\n");
+    require_ocsp = request_ocsp = FALSE;
+    }
+# endif
   }
 #endif
 
@@ -3806,21 +4172,25 @@ if (conn_args->dane)
     return FALSE;
   if (expciphers && *expciphers == '\0')
     expciphers = NULL;
+
+  normalise_ciphers(&expciphers, ob->dane_require_tls_ciphers);
   }
 #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))
-  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)
   {
-  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))
     {
@@ -3846,6 +4216,7 @@ if (conn_args->dane)
     tls_error(US"context init", host, NULL, errstr);
     return FALSE;
     }
+  DEBUG(D_tls) debug_printf("since dane-mode conn, not loading the usual CA bundle\n");
   }
 else
 
@@ -3855,8 +4226,54 @@ if (tls_client_basic_ctx_init(exim_client_ctx->ctx, host, ob,
       client_static_state, errstr) != OK)
   return FALSE;
 
+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"); }
+  else if (!Ustrlen(tlsp->sni))
+    tlsp->sni = NULL;
+  else
+    {
+#ifndef EXIM_HAVE_OPENSSL_TLSEXT
+    log_write(0, LOG_MAIN, "SNI unusable with this OpenSSL library version; ignoring \"%s\"\n",
+          tlsp->sni);
+    tlsp->sni = NULL;
+#endif
+    }
+  }
+
+if (ob->tls_alpn)
+#ifdef EXIM_HAVE_ALPN
+  {
+  const uschar * plist;
+  unsigned plen;
+
+  if (!tls_alpn_plist(&ob->tls_alpn, &plist, &plen, errstr))
+    return FALSE;
+  if (plist)
+    if (SSL_CTX_set_alpn_protos(exim_client_ctx->ctx, plist, plen) != 0)
+      {
+      tls_error(US"alpn init", host, NULL, errstr);
+      return FALSE;
+      }
+    else
+      DEBUG(D_tls) debug_printf("Setting TLS ALPN '%s'\n", ob->tls_alpn);
+  }
+#else
+  log_write(0, LOG_MAIN, "ALPN unusable with this OpenSSL library version; ignoring \"%s\"\n",
+          ob->tls_alpn);
+#endif
+
 #ifndef DISABLE_TLS_RESUME
-tls_client_ctx_resume_prehandshake(exim_client_ctx, tlsp, ob, host);
+/*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
 
 
@@ -3866,31 +4283,16 @@ if (!(exim_client_ctx->ssl = SSL_new(exim_client_ctx->ctx)))
   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)
-    {
-    DEBUG(D_tls) debug_printf("Setting TLS SNI forced to fail, not sending\n");
-    }
-  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
-    log_write(0, LOG_MAIN, "SNI unusable with this OpenSSL library version; ignoring \"%s\"\n",
-          tlsp->sni);
-#endif
-    }
+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)
@@ -3951,26 +4353,43 @@ if (conn_args->dane)
 
 if (rc <= 0)
   {
-  tls_error(US"SSL_connect", host, sigalrm_seen ? US"timed out" : NULL, errstr);
+#ifndef DISABLE_OCSP
+  if (client_static_state->u_ocsp.client.verify_errstr)
+    { if (errstr) *errstr = client_static_state->u_ocsp.client.verify_errstr; }
+  else
+#endif
+    tls_error(US"SSL_connect", host, sigalrm_seen ? US"timed out" : NULL, errstr);
   return FALSE;
   }
 
 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
 tls_client_resume_posthandshake(exim_client_ctx, tlsp);
 #endif
 
+#ifdef EXIM_HAVE_ALPN
+if (ob->tls_alpn)      /* We requested. See what was negotiated. */
+  {
+  const uschar * name;
+  unsigned len;
+
+  SSL_get0_alpn_selected(exim_client_ctx->ssl, &name, &len);
+  if (len > 0)
+    { DEBUG(D_tls) debug_printf("ALPN negotiated %u: '%.*s'\n", len, (int)*name, name+1); }
+  else if (verify_check_given_host(CUSS &ob->hosts_require_alpn, host) == OK)
+    {
+    /* Would like to send a relevant fatal Alert, but OpenSSL has no API */
+    tls_error(US"handshake", host, US"ALPN required but not negotiated", errstr);
+    return FALSE;
+    }
+  }
+#endif
+
 #ifdef SSL_get_extms_support
 tlsp->ext_master_secret = SSL_get_extms_support(exim_client_ctx->ssl) == 1;
 #endif
@@ -3987,18 +4406,7 @@ tlsp->cipher_stdname = cipher_stdname_ssl(exim_client_ctx->ssl);
   }
 
 /*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;
@@ -4103,6 +4511,12 @@ if (ssl_xfer_buffer_lwm >= ssl_xfer_buffer_hwm)
 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)
 {
@@ -4127,10 +4541,12 @@ return buf;
 
 
 void
-tls_get_cache()
+tls_get_cache(unsigned lim)
 {
 #ifndef DISABLE_DKIM
 int n = ssl_xfer_buffer_hwm - ssl_xfer_buffer_lwm;
+if (n > lim)
+  n = lim;
 if (n > 0)
   dkim_exim_verify_feed(ssl_xfer_buffer+ssl_xfer_buffer_lwm, n);
 #endif
@@ -4138,10 +4554,10 @@ if (n > 0)
 
 
 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;
+    || SSL_pending(state_server.lib_state.lib_ssl) > 0;
 }
 
 
@@ -4239,16 +4655,12 @@ if (more || corked)
   {
   if (!len) buff = US &error;  /* dummy just so that string_catn is ok */
 
-#ifndef DISABLE_PIPE_CONNECT
   int save_pool = store_pool;
   store_pool = POOL_PERM;
-#endif
 
   corked = string_catn(corked, buff, len);
 
-#ifndef DISABLE_PIPE_CONNECT
   store_pool = save_pool;
-#endif
 
   if (more)
     {
@@ -4306,6 +4718,32 @@ return olen;
 
 
 
+/*
+Arguments:
+  ct_ctx       client TLS context pointer, or NULL for the one global server context
+*/
+
+void
+tls_shutdown_wr(void * ct_ctx)
+{
+exim_openssl_client_tls_ctx * o_ctx = ct_ctx;
+SSL ** sslp = o_ctx ? &o_ctx->ssl : (SSL **) &state_server.lib_state.lib_ssl;
+int * fdp = o_ctx ? &tls_out.active.sock : &tls_in.active.sock;
+int rc;
+
+if (*fdp < 0) return;  /* TLS was not active */
+
+tls_write(ct_ctx, NULL, 0, FALSE);     /* flush write buffer */
+
+HDEBUG(D_transport|D_tls|D_acl|D_v) debug_printf_indent("  SMTP(TLS shutdown)>>\n");
+rc = SSL_shutdown(*sslp);
+if (rc < 0) DEBUG(D_tls)
+  {
+  ERR_error_string_n(ERR_get_error(), ssl_errstring, sizeof(ssl_errstring));
+  debug_printf("SSL_shutdown: %s\n", ssl_errstring);
+  }
+}
+
 /*************************************************
 *         Close down a TLS session               *
 *************************************************/
@@ -4316,7 +4754,8 @@ would tamper with the SSL session in the parent process).
 
 Arguments:
   ct_ctx       client TLS context pointer, or NULL for the one global server context
-  shutdown     1 if TLS close-alert is to be sent,
+  do_shutdown  0 no data-flush or TLS close-alert
+               1 if TLS close-alert is to be sent,
                2 if also response to be waited for
 
 Returns:     nothing
@@ -4325,25 +4764,33 @@ Used by both server-side and client-side TLS.
 */
 
 void
-tls_close(void * ct_ctx, int shutdown)
+tls_close(void * ct_ctx, int do_shutdown)
 {
 exim_openssl_client_tls_ctx * o_ctx = ct_ctx;
-SSL **sslp = o_ctx ? &o_ctx->ssl : (SSL **) &state_server.lib_state.lib_ssl;
-int *fdp = o_ctx ? &tls_out.active.sock : &tls_in.active.sock;
+SSL ** sslp = o_ctx ? &o_ctx->ssl : (SSL **) &state_server.lib_state.lib_ssl;
+int * fdp = o_ctx ? &tls_out.active.sock : &tls_in.active.sock;
 
 if (*fdp < 0) return;  /* TLS was not active */
 
-if (shutdown)
+if (do_shutdown > TLS_NO_SHUTDOWN)
   {
   int rc;
   DEBUG(D_tls) debug_printf("tls_close(): shutting down TLS%s\n",
-    shutdown > 1 ? " (with response-wait)" : "");
+    do_shutdown > TLS_SHUTDOWN_NOWAIT ? " (with response-wait)" : "");
+
+  tls_write(ct_ctx, NULL, 0, FALSE);   /* flush write buffer */
 
-  if (  (rc = SSL_shutdown(*sslp)) == 0        /* send "close notify" alert */
-     && 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);
-    rc = SSL_shutdown(*sslp);          /* wait for response */
+    rc = SSL_shutdown(*sslp);                  /* wait for response */
     ALARM_CLR(0);
     }
 
@@ -4357,17 +4804,17 @@ if (shutdown)
 if (!o_ctx)            /* server side */
   {
 #ifndef DISABLE_OCSP
-  sk_X509_pop_free(state_server.verify_stack, X509_free);
-  state_server.verify_stack = NULL;
+  sk_X509_pop_free(state_server.u_ocsp.server.verify_stack, X509_free);
+  state_server.u_ocsp.server.verify_stack = NULL;
 #endif
 
   receive_getc =       smtp_getc;
   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_smtp_buffered = smtp_buffered;
   tls_in.active.tls_ctx = NULL;
   tls_in.sni = NULL;
   /* Leave bits, peercert, cipher, peerdn, certificate_verified set, for logging */
@@ -4394,8 +4841,8 @@ Returns:     NULL on success, or error message
 uschar *
 tls_validate_require_cipher(void)
 {
-SSL_CTX *ctx;
-uschar *s, *expciphers, *err;
+SSL_CTX * ctx;
+uschar * expciphers, * err;
 
 tls_openssl_init();
 
@@ -4409,12 +4856,9 @@ if (!expand_check(tls_require_ciphers, US"tls_require_ciphers", &expciphers,
 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;
-
 if (lib_ctx_new(&ctx, NULL, &err) == OK)
   {
   DEBUG(D_tls)
@@ -4450,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.
 
-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. */
 }
 
 
@@ -4618,7 +5063,6 @@ tls_openssl_options_parse(uschar *option_spec, long *results)
 {
 long result, item;
 uschar * exp, * end;
-uschar keep_c;
 BOOL adding, item_parsed;
 
 /* Server: send no (<= TLS1.2) session tickets */
@@ -4660,11 +5104,8 @@ for (uschar * s = exp; *s; /**/)
     return FALSE;
     }
   adding = *s++ == '+';
-  for (end = s; (*end != '\0') && !isspace(*end); ++end) /**/ ;
-  keep_c = *end;
-  *end = '\0';
-  item_parsed = tls_openssl_one_option_parse(s, &item);
-  *end = keep_c;
+  for (end = s; *end && !isspace(*end); ) end++;
+  item_parsed = tls_openssl_one_option_parse(string_copyn(s, end-s), &item);
   if (!item_parsed)
     {
     DEBUG(D_tls) debug_printf("openssl option setting unrecognised: \"%s\"\n", s);