Fix non-OCSP build
[exim.git] / src / src / tls-openssl.c
index 3092dce2ed9d66c7af8cad36a52030914fa2e4ca..b8466ee22317b8c066b8863a06d65ea90279be65 100644 (file)
@@ -3,6 +3,7 @@
 *************************************************/
 
 /* Copyright (c) University of Cambridge 1995 - 2019 */
+/* Copyright (c) The Exim Maintainers 2020 */
 /* See the file NOTICE for conditions of use and distribution. */
 
 /* Portions Copyright (c) The OpenSSL Project 1999 */
@@ -46,6 +47,7 @@ functions from the OpenSSL library. */
 #endif
 #if OPENSSL_VERSION_NUMBER >= 0x10100000L
 # define EXIM_HAVE_OCSP_RESP_COUNT
+# define OPENSSL_AUTO_SHA256
 #else
 # define EXIM_HAVE_EPHEM_RSA_KEX
 # define EXIM_HAVE_RAND_PSEUDO
@@ -74,6 +76,10 @@ change this guard and punt the issue for a while longer. */
 #  define EXIM_HAVE_OPENSSL_CIPHER_GET_ID
 #  define EXIM_HAVE_SESSION_TICKET
 #  define EXIM_HAVE_OPESSL_TRACE
+#  define EXIM_HAVE_OPESSL_GET0_SERIAL
+#  ifndef DISABLE_OCSP
+#   define EXIM_HAVE_OCSP
+#  endif
 # else
 #  define EXIM_NEED_OPENSSL_INIT
 # endif
@@ -100,6 +106,8 @@ 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
+# else
+#  define OPENSSL_BAD_SRVR_OURCERT
 # endif
 #endif
 
@@ -108,7 +116,7 @@ change this guard and punt the issue for a while longer. */
 # define DISABLE_OCSP
 #endif
 
-#ifdef EXPERIMENTAL_TLS_RESUME
+#ifndef DISABLE_TLS_RESUME
 # if OPENSSL_VERSION_NUMBER < 0x0101010L
 #  error OpenSSL version too old for session-resumption
 # endif
@@ -141,14 +149,19 @@ all options unless explicitly for DTLS, let the administrator choose which
 to apply.
 
 This list is current as of:
-  ==>  1.0.1b  <==
-Plus SSL_OP_SAFARI_ECDHE_ECDSA_BUG from 2013-June patch/discussion on openssl-dev
-Plus SSL_OP_NO_TLSv1_3 for 1.1.2-dev
+  ==>  1.1.1c  <==
+
+XXX could we autobuild this list, as with predefined-macros?
+Seems just parsing ssl.h for SSL_OP_.* would be enough (except to exclude DTLS).
+Also allow a numeric literal?
 */
 static exim_openssl_option exim_openssl_options[] = {
 /* KEEP SORTED ALPHABETICALLY! */
 #ifdef SSL_OP_ALL
-  { US"all", SSL_OP_ALL },
+  { US"all", (long) SSL_OP_ALL },
+#endif
+#ifdef SSL_OP_ALLOW_NO_DHE_KEX
+  { US"allow_no_dhe_kex", SSL_OP_ALLOW_NO_DHE_KEX },
 #endif
 #ifdef SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION
   { US"allow_unsafe_legacy_renegotiation", SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION },
@@ -156,9 +169,15 @@ static exim_openssl_option exim_openssl_options[] = {
 #ifdef SSL_OP_CIPHER_SERVER_PREFERENCE
   { US"cipher_server_preference", SSL_OP_CIPHER_SERVER_PREFERENCE },
 #endif
+#ifdef SSL_OP_CRYPTOPRO_TLSEXT_BUG
+  { US"cryptopro_tlsext_bug", SSL_OP_CRYPTOPRO_TLSEXT_BUG },
+#endif
 #ifdef SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS
   { US"dont_insert_empty_fragments", SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS },
 #endif
+#ifdef SSL_OP_ENABLE_MIDDLEBOX_COMPAT
+  { US"enable_middlebox_compat", SSL_OP_ENABLE_MIDDLEBOX_COMPAT },
+#endif
 #ifdef SSL_OP_EPHEMERAL_RSA
   { US"ephemeral_rsa", SSL_OP_EPHEMERAL_RSA },
 #endif
@@ -180,9 +199,18 @@ static exim_openssl_option exim_openssl_options[] = {
 #ifdef SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG
   { US"netscape_reuse_cipher_change_bug", SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG },
 #endif
+#ifdef SSL_OP_NO_ANTI_REPLAY
+  { US"no_anti_replay", SSL_OP_NO_ANTI_REPLAY },
+#endif
 #ifdef SSL_OP_NO_COMPRESSION
   { US"no_compression", SSL_OP_NO_COMPRESSION },
 #endif
+#ifdef SSL_OP_NO_ENCRYPT_THEN_MAC
+  { US"no_encrypt_then_mac", SSL_OP_NO_ENCRYPT_THEN_MAC },
+#endif
+#ifdef SSL_OP_NO_RENEGOTIATION
+  { US"no_renegotiation", SSL_OP_NO_RENEGOTIATION },
+#endif
 #ifdef SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION
   { US"no_session_resumption_on_renegotiation", SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION },
 #endif
@@ -212,6 +240,9 @@ static exim_openssl_option exim_openssl_options[] = {
 #ifdef SSL_OP_NO_TLSv1_3
   { US"no_tlsv1_3", SSL_OP_NO_TLSv1_3 },
 #endif
+#ifdef SSL_OP_PRIORITIZE_CHACHA
+  { US"prioritize_chacha", SSL_OP_PRIORITIZE_CHACHA },
+#endif
 #ifdef SSL_OP_SAFARI_ECDHE_ECDSA_BUG
   { US"safari_ecdhe_ecdsa_bug", SSL_OP_SAFARI_ECDHE_ECDSA_BUG },
 #endif
@@ -236,10 +267,14 @@ static exim_openssl_option exim_openssl_options[] = {
 #ifdef SSL_OP_TLS_ROLLBACK_BUG
   { US"tls_rollback_bug", SSL_OP_TLS_ROLLBACK_BUG },
 #endif
+#ifdef SSL_OP_TLSEXT_PADDING
+  { US"tlsext_padding", SSL_OP_TLSEXT_PADDING },
+#endif
 };
 
 #ifndef MACRO_PREDEF
 static int exim_openssl_options_size = nelem(exim_openssl_options);
+static long init_options = 0;
 #endif
 
 #ifdef MACRO_PREDEF
@@ -258,9 +293,19 @@ for (struct exim_openssl_option * o = exim_openssl_options;
   builtin_macro_create(buf);
   }
 
-# ifdef EXPERIMENTAL_TLS_RESUME
+# ifndef DISABLE_TLS_RESUME
 builtin_macro_create_var(US"_RESUME_DECODE", RESUME_DECODE_STRING );
 # endif
+# ifdef SSL_OP_NO_TLSv1_3
+builtin_macro_create(US"_HAVE_TLS1_3");
+# endif
+# ifdef OPENSSL_BAD_SRVR_OURCERT
+builtin_macro_create(US"_TLS_BAD_MULTICERT_IN_OURCERT");
+# endif
+# ifdef EXIM_HAVE_OCSP
+builtin_macro_create(US"_HAVE_TLS_OCSP");
+builtin_macro_create(US"_HAVE_TLS_OCSP_LIST");
+# endif
 }
 #else
 
@@ -306,8 +351,9 @@ typedef struct {
   gstring *    corked;
 } exim_openssl_client_tls_ctx;
 
-static SSL_CTX *server_ctx = NULL;
-static SSL     *server_ssl = NULL;
+
+/* static SSL_CTX *server_ctx = NULL; */
+/* static SSL     *server_ssl = NULL; */
 
 #ifdef EXIM_HAVE_OPENSSL_TLSEXT
 static SSL_CTX *server_sni = NULL;
@@ -322,18 +368,27 @@ static BOOL server_verify_optional = FALSE;
 static BOOL reexpand_tls_files_for_sni = FALSE;
 
 
-typedef struct tls_ext_ctx_cb {
-  tls_support * tlsp;
-  uschar *certificate;
-  uschar *privatekey;
-  BOOL is_server;
+typedef struct ocsp_resp {
+  struct ocsp_resp *   next;
+  OCSP_RESPONSE *      resp;
+} ocsp_resplist;
+
+typedef struct exim_openssl_state {
+  exim_tlslib_state    lib_state;
+#define lib_ctx                        libdata0
+#define lib_ssl                        libdata1
+
+  tls_support *        tlsp;
+  uschar *     certificate;
+  uschar *     privatekey;
+  BOOL         is_server;
 #ifndef DISABLE_OCSP
   STACK_OF(X509) *verify_stack;                /* chain for verifying the proof */
   union {
     struct {
       uschar        *file;
-      uschar        *file_expanded;
-      OCSP_RESPONSE *response;
+      const uschar  *file_expanded;
+      ocsp_resplist *olist;
     } server;
     struct {
       X509_STORE    *verify_store;     /* non-null if status requested */
@@ -341,26 +396,26 @@ typedef struct tls_ext_ctx_cb {
     } client;
   } u_ocsp;
 #endif
-  uschar *dhparam;
+  uschar *     dhparam;
   /* these are cached from first expand */
-  uschar *server_cipher_list;
+  uschar *     server_cipher_list;
   /* only passed down to tls_error: */
-  host_item *host;
+  host_item *  host;
   const uschar * verify_cert_hostnames;
 #ifndef DISABLE_EVENT
-  uschar * event_action;
+  uschar *     event_action;
 #endif
-} tls_ext_ctx_cb;
+} exim_openssl_state_st;
 
 /* should figure out a cleanup of API to handle state preserved per
 implementation, for various reasons, which can be void * in the APIs.
 For now, we hack around it. */
-tls_ext_ctx_cb *client_static_cbinfo = NULL;   /*XXX should not use static; multiple concurrent clients! */
-tls_ext_ctx_cb *server_static_cbinfo = NULL;
+exim_openssl_state_st *client_static_state = NULL;     /*XXX should not use static; multiple concurrent clients! */
+exim_openssl_state_st state_server = {.is_server = TRUE};
 
 static int
-setup_certs(SSL_CTX *sctx, uschar *certs, uschar *crl, host_item *host, BOOL optional,
-    int (*cert_vfy_cb)(int, X509_STORE_CTX *), uschar ** errstr );
+setup_certs(SSL_CTX *sctx, uschar *certs, uschar *crl, host_item *host,
+    uschar ** errstr );
 
 /* Callbacks */
 #ifdef EXIM_HAVE_OPENSSL_TLSEXT
@@ -373,18 +428,25 @@ static int tls_server_stapling_cb(SSL *s, void *arg);
 
 
 /* Daemon-called, before every connection, key create/rotate */
-#ifdef EXPERIMENTAL_TLS_RESUME
+#ifndef DISABLE_TLS_RESUME
 static void tk_init(void);
 static int tls_exdata_idx = -1;
 #endif
 
-void
-tls_daemon_init(void)
+static void
+tls_per_lib_daemon_tick(void)
 {
-#ifdef EXPERIMENTAL_TLS_RESUME
+#ifndef DISABLE_TLS_RESUME
 tk_init();
 #endif
-return;
+}
+
+/* Called once at daemon startup */
+
+static void
+tls_per_lib_daemon_init(void)
+{
+tls_daemon_creds_reload();
 }
 
 
@@ -426,156 +488,526 @@ return host ? FAIL : DEFER;
 
 
 
-/*************************************************
-*        Callback to generate RSA key            *
-*************************************************/
+/**************************************************
+* General library initalisation                   *
+**************************************************/
 
-/*
-Arguments:
-  s          SSL connection (not used)
-  export     not used
-  keylength  keylength
+static BOOL
+lib_rand_init(void * addr)
+{
+randstuff r;
+if (!RAND_status()) return TRUE;
 
-Returns:     pointer to generated key
-*/
+gettimeofday(&r.tv, NULL);
+r.p = getpid();
+RAND_seed(US (&r), sizeof(r));
+RAND_seed(US big_buffer, big_buffer_size);
+if (addr) RAND_seed(US addr, sizeof(addr));
 
-static RSA *
-rsa_callback(SSL *s, int export, int keylength)
+return RAND_status();
+}
+
+
+static void
+tls_openssl_init(void)
 {
-RSA *rsa_key;
-#ifdef EXIM_HAVE_RSA_GENKEY_EX
-BIGNUM *bn = BN_new();
-#endif
+static BOOL once = FALSE;
+if (once) return;
+once = TRUE;
 
-export = export;     /* Shut picky compilers up */
-DEBUG(D_tls) debug_printf("Generating %d bit RSA key...\n", keylength);
+#ifdef EXIM_NEED_OPENSSL_INIT
+SSL_load_error_strings();          /* basic set up */
+OpenSSL_add_ssl_algorithms();
+#endif
 
-#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
-if (!(rsa_key = RSA_generate_key(keylength, RSA_F4, NULL, NULL)))
+#if defined(EXIM_HAVE_SHA256) && !defined(OPENSSL_AUTO_SHA256)
+/* SHA256 is becoming ever more popular. This makes sure it gets added to the
+list of available digests. */
+EVP_add_digest(EVP_sha256());
 #endif
 
-  {
-  ERR_error_string_n(ERR_get_error(), ssl_errstring, sizeof(ssl_errstring));
-  log_write(0, LOG_MAIN|LOG_PANIC, "TLS error (RSA_generate_key): %s",
-    ssl_errstring);
-  return NULL;
-  }
-return rsa_key;
+(void) lib_rand_init(NULL);
+(void) tls_openssl_options_parse(openssl_options, &init_options);
 }
 
 
 
-/* Extreme debug
-#ifndef DISABLE_OCSP
-void
-x509_store_dump_cert_s_names(X509_STORE * store)
+/*************************************************
+*                Initialize for DH               *
+*************************************************/
+
+/* If dhparam is set, expand it, and load up the parameters for DH encryption.
+
+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)
 {
-STACK_OF(X509_OBJECT) * roots= store->objs;
-static uschar name[256];
+BIO *bio;
+DH *dh;
+uschar *dhexpanded;
+const char *pem;
+int dh_bitsize;
 
-for (int i= 0; i < sk_X509_OBJECT_num(roots); i++)
+if (!expand_check(dhparam, US"tls_dhparam", &dhexpanded, errstr))
+  return FALSE;
+
+if (!dhexpanded || !*dhexpanded)
+  bio = BIO_new_mem_buf(CS std_dh_prime_default(), -1);
+else if (dhexpanded[0] == '/')
   {
-  X509_OBJECT * tmp_obj= sk_X509_OBJECT_value(roots, i);
-  if(tmp_obj->type == X509_LU_X509)
+  if (!(bio = BIO_new_file(CS dhexpanded, "r")))
     {
-    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);
-      }
+    tls_error(string_sprintf("could not read dhparams file %s", dhexpanded),
+          host, US strerror(errno), errstr);
+    return FALSE;
     }
   }
-}
-#endif
-*/
+else
+  {
+  if (Ustrcmp(dhexpanded, "none") == 0)
+    {
+    DEBUG(D_tls) debug_printf("Requested no DH parameters.\n");
+    return TRUE;
+    }
+
+  if (!(pem = std_dh_prime_named(dhexpanded)))
+    {
+    tls_error(string_sprintf("Unknown standard DH prime \"%s\"", dhexpanded),
+        host, US strerror(errno), errstr);
+    return FALSE;
+    }
+  bio = BIO_new_mem_buf(CS pem, -1);
+  }
 
+if (!(dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL)))
+  {
+  BIO_free(bio);
+  tls_error(string_sprintf("Could not read tls_dhparams \"%s\"", dhexpanded),
+      host, NULL, errstr);
+  return FALSE;
+  }
 
-#ifndef DISABLE_EVENT
-static int
-verify_event(tls_support * tlsp, X509 * cert, int depth, const uschar * dn,
-  BOOL *calledp, const BOOL *optionalp, const uschar * what)
-{
-uschar * ev;
-uschar * yield;
-X509 * old_cert;
+/* 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
+/* 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 */
+dh_bitsize = DH_bits(dh);
+#else
+dh_bitsize = 8 * DH_size(dh);
+#endif
 
-ev = tlsp == &tls_out ? client_static_cbinfo->event_action : event_action;
-if (ev)
+/* 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)
   {
-  DEBUG(D_tls) debug_printf("verify_event: %s %d\n", what, depth);
-  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))))
-    {
-    log_write(0, LOG_MAIN, "[%s] %s verify denied by event-action: "
-               "depth=%d cert=%s: %s",
-             tlsp == &tls_out ? deliver_host_address : sender_host_address,
-             what, depth, dn, yield);
-    *calledp = TRUE;
-    if (!*optionalp)
-      {
-      if (old_cert) tlsp->peercert = old_cert; /* restore 1st failing cert */
-      return 1;                            /* reject (leaving peercert set) */
-      }
-    DEBUG(D_tls) debug_printf("Event-action verify failure overridden "
-      "(host in tls_try_verify_hosts)\n");
-    tlsp->verify_override = TRUE;
-    }
-  X509_free(tlsp->peercert);
-  tlsp->peercert = old_cert;
+  DEBUG(D_tls)
+    debug_printf("dhparams file %d bits, is > tls_dh_max_bits limit of %d\n",
+        dh_bitsize, tls_dh_max_bits);
   }
-return 0;
+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);
+  }
+
+DH_free(dh);
+BIO_free(bio);
+
+return TRUE;
 }
-#endif
+
+
+
 
 /*************************************************
-*        Callback for verification               *
+*               Initialize for ECDH              *
 *************************************************/
 
-/* The SSL library does certificate verification if set up to do so. This
-callback has the current yes/no state is in "state". If verification succeeded,
-we set the certificate-verified flag. If verification failed, what happens
-depends on whether the client is required to present a verifiable certificate
-or not.
+/* Load parameters for ECDH encryption.
 
-If verification is optional, we change the state to yes, but still log the
-verification error. For some reason (it really would help to have proper
-documentation of OpenSSL), this callback function then gets called again, this
-time with state = 1.  We must take care not to set the private verified flag on
-the second time through.
+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
+the news and concerns over curve choices, we're not cryptographers, we're not
+pretending to be, and this is "good enough" to be better than no support,
+protecting against most adversaries.  Given another year or two, there might
+be sufficient clarity about a "right" way forward to let us make an informed
+decision, instead of a knee-jerk reaction.
 
-Note: this function is not called if the client fails to present a certificate
-when asked. We get here only if a certificate has been received. Handling of
-optional verification for this case is done when requesting SSL to verify, by
-setting SSL_VERIFY_FAIL_IF_NO_PEER_CERT in the non-optional case.
+Longer-term, we should look at supporting both various named curves and
+external files generated with "openssl ecparam", much as we do for init_dh().
+We should also support "none" as a value, to explicitly avoid initialisation.
 
-May be called multiple times for different issues with a certificate, even
-for a given "depth" in the certificate chain.
+Patches welcome.
 
 Arguments:
-  preverify_ok current yes/no state as 1/0
-  x509ctx      certificate information.
-  tlsp         per-direction (client vs. server) support data
-  calledp      has-been-called flag
-  optionalp    verification-is-optional flag
+  sctx      The current SSL CTX (inbound or outbound)
+  host      connected host, if client; NULL if server
+  errstr    error string pointer
 
-Returns:     0 if verification should fail, otherwise 1
+Returns:    TRUE if OK (nothing to set up, or setup worked)
 */
 
-static int
-verify_callback(int preverify_ok, X509_STORE_CTX * x509ctx,
-  tls_support * tlsp, BOOL * calledp, BOOL * optionalp)
+static BOOL
+init_ecdh(SSL_CTX * sctx, host_item * host, uschar ** errstr)
 {
-X509 * cert = X509_STORE_CTX_get_current_cert(x509ctx);
-int depth = X509_STORE_CTX_get_error_depth(x509ctx);
-uschar dn[256];
+#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");
+return TRUE;
+# else
+
+if (!expand_check(tls_eccurve, US"tls_eccurve", &exp_curve, errstr))
+  return FALSE;
+if (!exp_curve || !*exp_curve)
+  return TRUE;
+
+/* "auto" needs to be handled carefully.
+ * OpenSSL <  1.0.2: we do not select anything, but fallback to prime256v1
+ * OpenSSL <  1.1.0: we have to call SSL_CTX_set_ecdh_auto
+ *                   (openssl/ssl.h defines SSL_CTRL_SET_ECDH_AUTO)
+ * OpenSSL >= 1.1.0: we do not set anything, the libray does autoselection
+ *                   https://github.com/openssl/openssl/commit/fe6ef2472db933f01b59cad82aa925736935984b
+ */
+if (Ustrcmp(exp_curve, "auto") == 0)
+  {
+#if OPENSSL_VERSION_NUMBER < 0x10002000L
+  DEBUG(D_tls) debug_printf(
+    "ECDH OpenSSL < 1.0.2: temp key parameter settings: overriding \"auto\" with \"prime256v1\"\n");
+  exp_curve = US"prime256v1";
+#else
+# if defined SSL_CTRL_SET_ECDH_AUTO
+  DEBUG(D_tls) debug_printf(
+    "ECDH OpenSSL 1.0.2+ temp key parameter settings: autoselection\n");
+  SSL_CTX_set_ecdh_auto(sctx, 1);
+  return TRUE;
+# else
+  DEBUG(D_tls) debug_printf(
+    "ECDH OpenSSL 1.1.0+ temp key parameter settings: default selection\n");
+  return TRUE;
+# endif
+#endif
+  }
+
+DEBUG(D_tls) debug_printf("ECDH: curve '%s'\n", exp_curve);
+if (  (nid = OBJ_sn2nid       (CCS exp_curve)) == NID_undef
+#   ifdef EXIM_HAVE_OPENSSL_EC_NIST2NID
+   && (nid = EC_curve_nist2nid(CCS exp_curve)) == NID_undef
+#   endif
+   )
+  {
+  tls_error(string_sprintf("Unknown curve name tls_eccurve '%s'", exp_curve),
+    host, 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;
+  }
+
+/* The "tmp" in the name here refers to setting a temporary key
+not to the stability of the interface. */
+
+if ((rv = SSL_CTX_set_tmp_ecdh(sctx, ecdh) == 0))
+  tls_error(string_sprintf("Error enabling '%s' curve", exp_curve), host, NULL, errstr);
+else
+  DEBUG(D_tls) debug_printf("ECDH: enabled '%s' curve\n", exp_curve);
+
+EC_KEY_free(ecdh);
+return !rv;
+
+# endif        /*EXIM_HAVE_ECDH*/
+#endif /*OPENSSL_NO_ECDH*/
+}
+
+
+
+/*************************************************
+*        Expand key and cert file specs          *
+*************************************************/
+
+/*
+Arguments:
+  s          SSL connection (not used)
+  export     not used
+  keylength  keylength
+
+Returns:     pointer to generated key
+*/
+
+static RSA *
+rsa_callback(SSL *s, int export, int keylength)
+{
+RSA *rsa_key;
+#ifdef EXIM_HAVE_RSA_GENKEY_EX
+BIGNUM *bn = BN_new();
+#endif
+
+DEBUG(D_tls) debug_printf("Generating %d bit RSA key...\n", keylength);
+
+#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
+if (!(rsa_key = RSA_generate_key(keylength, RSA_F4, NULL, NULL)))
+#endif
+
+  {
+  ERR_error_string_n(ERR_get_error(), ssl_errstring, sizeof(ssl_errstring));
+  log_write(0, LOG_MAIN|LOG_PANIC, "TLS error (RSA_generate_key): %s",
+    ssl_errstring);
+  return NULL;
+  }
+return rsa_key;
+}
+
+
+
+/* Create and install a selfsigned certificate, for use in server mode */
+
+static int
+tls_install_selfsign(SSL_CTX * sctx, uschar ** errstr)
+{
+X509 * x509 = NULL;
+EVP_PKEY * pkey;
+RSA * rsa;
+X509_NAME * name;
+uschar * where;
+
+where = US"allocating pkey";
+if (!(pkey = EVP_PKEY_new()))
+  goto err;
+
+where = US"allocating cert";
+if (!(x509 = X509_new()))
+  goto err;
+
+where = US"generating pkey";
+if (!(rsa = rsa_callback(NULL, 0, 2048)))
+  goto err;
+
+where = US"assigning pkey";
+if (!EVP_PKEY_assign_RSA(pkey, rsa))
+  goto err;
+
+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_set_pubkey(x509, pkey);
+
+name = X509_get_subject_name(x509);
+X509_NAME_add_entry_by_txt(name, "C",
+                         MBSTRING_ASC, CUS "UK", -1, -1, 0);
+X509_NAME_add_entry_by_txt(name, "O",
+                         MBSTRING_ASC, CUS "Exim Developers", -1, -1, 0);
+X509_NAME_add_entry_by_txt(name, "CN",
+                         MBSTRING_ASC, CUS smtp_active_hostname, -1, -1, 0);
+X509_set_issuer_name(x509, name);
+
+where = US"signing cert";
+if (!X509_sign(x509, pkey, EVP_md5()))
+  goto err;
+
+where = US"installing selfsign cert";
+if (!SSL_CTX_use_certificate(sctx, x509))
+  goto err;
+
+where = US"installing selfsign key";
+if (!SSL_CTX_use_PrivateKey(sctx, pkey))
+  goto err;
+
+return OK;
+
+err:
+  (void) tls_error(where, NULL, NULL, errstr);
+  if (x509) X509_free(x509);
+  if (pkey) EVP_PKEY_free(pkey);
+  return DEFER;
+}
+
+
+
+
+
+
+
+/*************************************************
+*           Information callback                 *
+*************************************************/
+
+/* The SSL library functions call this from time to time to indicate what they
+are doing. We copy the string to the debugging output when TLS debugging has
+been requested.
+
+Arguments:
+  s         the SSL connection
+  where
+  ret
+
+Returns:    nothing
+*/
+
+static void
+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)";
+
+  if (where & SSL_CB_LOOP)
+     debug_printf("%s: %s\n", str, 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",
+         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));
+  }
+}
+
+#ifdef OPENSSL_HAVE_KEYLOG_CB
+static void
+keylog_callback(const SSL *ssl, const char *line)
+{
+char * filename;
+FILE * fp;
+DEBUG(D_tls) debug_printf("%.200s\n", line);
+if (!(filename = getenv("SSLKEYLOGFILE"))) return;
+if (!(fp = fopen(filename, "a"))) return;
+fprintf(fp, "%s\n", line);
+fclose(fp);
+}
+#endif
+
+
+
+
+
+#ifndef DISABLE_EVENT
+static int
+verify_event(tls_support * tlsp, X509 * cert, int depth, const uschar * dn,
+  BOOL *calledp, const BOOL *optionalp, const uschar * what)
+{
+uschar * ev;
+uschar * yield;
+X509 * old_cert;
+
+ev = tlsp == &tls_out ? client_static_state->event_action : event_action;
+if (ev)
+  {
+  DEBUG(D_tls) debug_printf("verify_event: %s %d\n", what, depth);
+  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))))
+    {
+    log_write(0, LOG_MAIN, "[%s] %s verify denied by event-action: "
+               "depth=%d cert=%s: %s",
+             tlsp == &tls_out ? deliver_host_address : sender_host_address,
+             what, depth, dn, yield);
+    *calledp = TRUE;
+    if (!*optionalp)
+      {
+      if (old_cert) tlsp->peercert = old_cert; /* restore 1st failing cert */
+      return 1;                            /* reject (leaving peercert set) */
+      }
+    DEBUG(D_tls) debug_printf("Event-action verify failure overridden "
+      "(host in tls_try_verify_hosts)\n");
+    tlsp->verify_override = TRUE;
+    }
+  X509_free(tlsp->peercert);
+  tlsp->peercert = old_cert;
+  }
+return 0;
+}
+#endif
+
+/*************************************************
+*        Callback for verification               *
+*************************************************/
+
+/* The SSL library does certificate verification if set up to do so. This
+callback has the current yes/no state is in "state". If verification succeeded,
+we set the certificate-verified flag. If verification failed, what happens
+depends on whether the client is required to present a verifiable certificate
+or not.
+
+If verification is optional, we change the state to yes, but still log the
+verification error. For some reason (it really would help to have proper
+documentation of OpenSSL), this callback function then gets called again, this
+time with state = 1.  We must take care not to set the private verified flag on
+the second time through.
+
+Note: this function is not called if the client fails to present a certificate
+when asked. We get here only if a certificate has been received. Handling of
+optional verification for this case is done when requesting SSL to verify, by
+setting SSL_VERIFY_FAIL_IF_NO_PEER_CERT in the non-optional case.
+
+May be called multiple times for different issues with a certificate, even
+for a given "depth" in the certificate chain.
+
+Arguments:
+  preverify_ok current yes/no state as 1/0
+  x509ctx      certificate information.
+  tlsp         per-direction (client vs. server) support data
+  calledp      has-been-called flag
+  optionalp    verification-is-optional flag
+
+Returns:     0 if verification should fail, otherwise 1
+*/
+
+static int
+verify_callback(int preverify_ok, X509_STORE_CTX * x509ctx,
+  tls_support * tlsp, BOOL * calledp, BOOL * optionalp)
+{
+X509 * cert = X509_STORE_CTX_get_current_cert(x509ctx);
+int depth = X509_STORE_CTX_get_error_depth(x509ctx);
+uschar dn[256];
 
 if (!X509_NAME_oneline(X509_get_subject_name(cert), CS dn, sizeof(dn)))
   {
@@ -586,6 +1018,7 @@ if (!X509_NAME_oneline(X509_get_subject_name(cert), CS dn, sizeof(dn)))
   }
 dn[sizeof(dn)-1] = '\0';
 
+tlsp->verify_override = FALSE;
 if (preverify_ok == 0)
   {
   uschar * extra = verify_mode ? string_sprintf(" (during %c-verify for [%s])",
@@ -611,15 +1044,15 @@ 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_cbinfo->u_ocsp.client.verify_store)
+  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_cbinfo->u_ocsp.client.verify_store,
+    if (!X509_STORE_add_cert(client_static_state->u_ocsp.client.verify_store,
                              cert))
       ERR_clear_error();
-    sk_X509_push(client_static_cbinfo->verify_stack, cert);
+    sk_X509_push(client_static_state->verify_stack, cert);
     }
 #endif
 #ifndef DISABLE_EVENT
@@ -632,7 +1065,7 @@ else
   const uschar * verify_cert_hostnames;
 
   if (  tlsp == &tls_out
-     && ((verify_cert_hostnames = client_static_cbinfo->verify_cert_hostnames)))
+     && ((verify_cert_hostnames = client_static_state->verify_cert_hostnames)))
        /* client, wanting hostname check */
     {
 
@@ -752,15 +1185,15 @@ if (preverify_ok == 1)
   {
   tls_out.dane_verified = TRUE;
 #ifndef DISABLE_OCSP
-  if (client_static_cbinfo->u_ocsp.client.verify_store)
+  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_cbinfo->u_ocsp.client.verify_store,
+    if (!X509_STORE_add_cert(client_static_state->u_ocsp.client.verify_store,
                              cert))
       ERR_clear_error();
-    sk_X509_push(client_static_cbinfo->verify_stack, cert);
+    sk_X509_push(client_static_state->verify_stack, cert);
     }
 #endif
   }
@@ -778,757 +1211,1000 @@ return preverify_ok;
 #endif /*SUPPORT_DANE*/
 
 
+#ifndef DISABLE_OCSP
 /*************************************************
-*           Information callback                 *
+*       Load OCSP information into state         *
 *************************************************/
+/* Called to load the server OCSP response from the given file into memory, once
+caller has determined this is needed.  Checks validity.  Debugs a message
+if invalid.
 
-/* The SSL library functions call this from time to time to indicate what they
-are doing. We copy the string to the debugging output when TLS debugging has
-been requested.
+ASSUMES: single response, for single cert.
 
 Arguments:
-  s         the SSL connection
-  where
-  ret
-
-Returns:    nothing
+  state           various parts of session state
+  filename        the filename putatively holding an OCSP response
+  is_pem         file is PEM format; otherwise is DER
 */
 
 static void
-info_callback(SSL *s, int where, int ret)
+ocsp_load_response(exim_openssl_state_st * state, const uschar * filename,
+  BOOL is_pem)
 {
+BIO * bio;
+OCSP_RESPONSE * resp;
+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 (!(bio = BIO_new_file(CS filename, "rb")))
   {
-  const uschar * str;
+  DEBUG(D_tls) debug_printf("Failed to open OCSP response file \"%s\"\n",
+      filename);
+  return;
+  }
 
-  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)";
+if (is_pem)
+  {
+  uschar * data, * freep;
+  char * dummy;
+  long len;
+  if (!PEM_read_bio(bio, &dummy, &dummy, &data, &len))
+    {
+    DEBUG(D_tls) debug_printf("Failed to read PEM file \"%s\"\n",
+       filename);
+    return;
+    }
+  freep = data;
+  resp = d2i_OCSP_RESPONSE(NULL, CUSS &data, len);
+  OPENSSL_free(freep);
+  }
+else
+  resp = d2i_OCSP_RESPONSE_bio(bio, NULL);
+BIO_free(bio);
 
-  if (where & SSL_CB_LOOP)
-     debug_printf("%s: %s\n", str, 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",
-         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 (!resp)
+  {
+  DEBUG(D_tls) debug_printf("Error reading OCSP response.\n");
+  return;
   }
-}
 
-#ifdef OPENSSL_HAVE_KEYLOG_CB
-static void
-keylog_callback(const SSL *ssl, const char *line)
-{
-DEBUG(D_tls) debug_printf("%.200s\n", line);
-}
+if ((status = OCSP_response_status(resp)) != OCSP_RESPONSE_STATUS_SUCCESSFUL)
+  {
+  DEBUG(D_tls) debug_printf("OCSP response not valid: %s (%d)\n",
+      OCSP_response_status_str(status), status);
+  goto bad;
+  }
+
+#ifdef notdef
+  {
+  BIO * bp = BIO_new_fp(debug_file, BIO_NOCLOSE);
+  OCSP_RESPONSE_print(bp, resp, 0);  /* extreme debug: stapling content */
+  BIO_free(bp);
+  }
 #endif
 
+if (!(basic_response = OCSP_response_get1_basic(resp)))
+  {
+  DEBUG(D_tls)
+    debug_printf("OCSP response parse error: unable to extract basic response.\n");
+  goto bad;
+  }
 
-#ifdef EXPERIMENTAL_TLS_RESUME
-/* Manage the keysets used for encrypting the session tickets, on the server. */
+sk = state->verify_stack;
+verify_flags = OCSP_NOVERIFY; /* check sigs, but not purpose */
 
-typedef struct {                       /* Session ticket encryption key */
-  uschar       name[16];
+/* May need to expose ability to adjust those flags?
+OCSP_NOSIGS OCSP_NOVERIFY OCSP_NOCHAIN OCSP_NOCHECKS OCSP_NOEXPLICIT
+OCSP_TRUSTOTHER OCSP_NOINTERN */
 
-  const EVP_CIPHER *   aes_cipher;
-  uschar               aes_key[32];    /* size needed depends on cipher. aes_128 implies 128/8 = 16? */
-  const EVP_MD *       hmac_hash;
-  uschar               hmac_key[16];
-  time_t               renew;
-  time_t               expire;
-} exim_stek;
+/* 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.
 
-static exim_stek exim_tk;      /* current key */
-static exim_stek exim_tk_old;  /* previous key */
+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.
 
-static void
-tk_init(void)
-{
-time_t t = time(NULL);
+We have a stack, loaded in setup_certs() if tls_verify_certificates
+was a file (not a directory, or "system").  It is unfortunate we
+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) ? ]
+We do not free the stack since it could be needed a second time for
+SNI handling.
 
-if (exim_tk.name[0])
+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) -
+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 (exim_tk.renew >= t) return;
-  exim_tk_old = exim_tk;
+  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);
+    }
+  goto bad;
   }
 
-if (f.running_in_test_harness) ssl_session_timeout = 6;
-
-DEBUG(D_tls) debug_printf("OpenSSL: %s STEK\n", exim_tk.name[0] ? "rotating" : "creating");
-if (RAND_bytes(exim_tk.aes_key, sizeof(exim_tk.aes_key)) <= 0) return;
-if (RAND_bytes(exim_tk.hmac_key, sizeof(exim_tk.hmac_key)) <= 0) return;
-if (RAND_bytes(exim_tk.name+1, sizeof(exim_tk.name)-1) <= 0) return;
+/* Here's the simplifying assumption: there's only one response, for the
+one certificate we use, and nothing for anything else in a chain.  If this
+proves false, we need to extract a cert id from our issued cert
+(tls_certificate) and use that for OCSP_resp_find_status() (which finds the
+right cert in the stack and then calls OCSP_single_get0_status()).
 
-exim_tk.name[0] = 'E';
-exim_tk.aes_cipher = EVP_aes_256_cbc();
-exim_tk.hmac_hash = EVP_sha256();
-exim_tk.expire = t + ssl_session_timeout;
-exim_tk.renew = t + ssl_session_timeout/2;
-}
+I'm hoping to avoid reworking a bunch more of how we handle state here.
 
-static exim_stek *
-tk_current(void)
-{
-if (!exim_tk.name[0]) return NULL;
-return &exim_tk;
-}
+XXX that will change when we add support for (TLS1.3) whole-chain stapling
+*/
 
-static exim_stek *
-tk_find(const uschar * name)
-{
-return memcmp(name, exim_tk.name, sizeof(exim_tk.name)) == 0 ? &exim_tk
-  : memcmp(name, exim_tk_old.name, sizeof(exim_tk_old.name)) == 0 ? &exim_tk_old
-  : NULL;
-}
+if (!(single_response = OCSP_resp_get0(basic_response, 0)))
+  {
+  DEBUG(D_tls)
+    debug_printf("Unable to get first response from OCSP basic response.\n");
+  goto bad;
+  }
 
-/* Callback for session tickets, on server */
-static int
-ticket_key_callback(SSL * ssl, uschar key_name[16],
-  uschar * iv, EVP_CIPHER_CTX * ctx, HMAC_CTX * hctx, int enc)
-{
-tls_support * tlsp = server_static_cbinfo->tlsp;
-exim_stek * key;
+status = OCSP_single_get0_status(single_response, &reason, &rev, &thisupd, &nextupd);
+if (status != V_OCSP_CERTSTATUS_GOOD)
+  {
+  DEBUG(D_tls) debug_printf("OCSP response bad cert status: %s (%d) %s (%d)\n",
+      OCSP_cert_status_str(status), status,
+      OCSP_crl_reason_str(reason), reason);
+  goto bad;
+  }
 
-if (enc)
+if (!OCSP_check_validity(thisupd, nextupd, EXIM_OCSP_SKEW_SECONDS, EXIM_OCSP_MAX_AGE))
   {
-  DEBUG(D_tls) debug_printf("ticket_key_callback: create new session\n");
-  tlsp->resumption |= RESUME_CLIENT_REQUESTED;
+  DEBUG(D_tls) debug_printf("OCSP status invalid times.\n");
+  goto bad;
+  }
 
-  if (RAND_bytes(iv, EVP_MAX_IV_LENGTH) <= 0)
-    return -1; /* insufficient random */
+supply_response:
+  /* Add the resp to the list used by tls_server_stapling_cb() */
+  {
+  ocsp_resplist ** op = &state->u_ocsp.server.olist, * oentry;
+  while (oentry = *op)
+    op = &oentry->next;
+  *op = oentry = store_get(sizeof(ocsp_resplist), FALSE);
+  oentry->next = NULL;
+  oentry->resp = resp;
+  }
+return;
 
-  if (!(key = tk_current()))   /* current key doesn't exist or isn't valid */
-     return 0;                 /* key couldn't be created */
-  memcpy(key_name, key->name, 16);
-  DEBUG(D_tls) debug_printf("STEK expire %ld\n", key->expire - time(NULL));
+bad:
+  if (f.running_in_test_harness)
+    {
+    extern char ** environ;
+    if (environ) for (uschar ** p = USS environ; *p; p++)
+      if (Ustrncmp(*p, "EXIM_TESTHARNESS_DISABLE_OCSPVALIDITYCHECK", 42) == 0)
+       {
+       DEBUG(D_tls) debug_printf("Supplying known bad OCSP response\n");
+       goto supply_response;
+       }
+    }
+return;
+}
 
-  /*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);
-  EVP_EncryptInit_ex(ctx, key->aes_cipher, NULL, key->aes_key, iv);
 
-  DEBUG(D_tls) debug_printf("ticket created\n");
-  return 1;
-  }
-else
-  {
-  time_t now = time(NULL);
+static void
+ocsp_free_response_list(exim_openssl_state_st * cbinfo)
+{
+for (ocsp_resplist * olist = cbinfo->u_ocsp.server.olist; olist;
+     olist = olist->next)
+  OCSP_RESPONSE_free(olist->resp);
+cbinfo->u_ocsp.server.olist = NULL;
+}
+#endif /*!DISABLE_OCSP*/
 
-  DEBUG(D_tls) debug_printf("ticket_key_callback: retrieve session\n");
-  tlsp->resumption |= RESUME_CLIENT_SUGGESTED;
 
-  if (!(key = tk_find(key_name)) || key->expire < now)
-    {
-    DEBUG(D_tls)
-      {
-      debug_printf("ticket not usable (%s)\n", key ? "expired" : "not found");
-      if (key) debug_printf("STEK expire %ld\n", key->expire - now);
-      }
-    return 0;
-    }
 
-  HMAC_Init_ex(hctx, key->hmac_key, sizeof(key->hmac_key),
-               key->hmac_hash, NULL);
-  EVP_DecryptInit_ex(ctx, key->aes_cipher, NULL, key->aes_key, iv);
 
-  DEBUG(D_tls) debug_printf("ticket usable, STEK expire %ld\n", key->expire - now);
 
-  /* The ticket lifetime and renewal are the same as the STEK lifetime and
-  renewal, which is overenthusiastic.  A factor of, say, 3x longer STEK would
-  be better.  To do that we'd have to encode ticket lifetime in the name as
-  we don't yet see the restored session.  Could check posthandshake for TLS1.3
-  and trigger a new ticket then, but cannot do that for TLS1.2 */
-  return key->renew < now ? 2 : 1;
-  }
+static int
+tls_add_certfile(SSL_CTX * sctx, exim_openssl_state_st * cbinfo, uschar * file,
+  uschar ** errstr)
+{
+DEBUG(D_tls) debug_printf("tls_certificate file '%s'\n", file);
+if (!SSL_CTX_use_certificate_chain_file(sctx, CS file))
+  return tls_error(string_sprintf(
+    "SSL_CTX_use_certificate_chain_file file=%s", file),
+      cbinfo->host, NULL, errstr);
+return 0;
+}
+
+static int
+tls_add_pkeyfile(SSL_CTX * sctx, exim_openssl_state_st * cbinfo, uschar * file,
+  uschar ** errstr)
+{
+DEBUG(D_tls) debug_printf("tls_privatekey file  '%s'\n", file);
+if (!SSL_CTX_use_PrivateKey_file(sctx, CS file, SSL_FILETYPE_PEM))
+  return tls_error(string_sprintf(
+    "SSL_CTX_use_PrivateKey_file file=%s", file), cbinfo->host, NULL, errstr);
+return 0;
 }
-#endif
 
 
 
-/*************************************************
-*                Initialize for DH               *
-*************************************************/
 
-/* If dhparam is set, expand it, and load up the parameters for DH encryption.
+/* Called once during tls_init and possibly again during TLS setup, for a
+new context, if Server Name Indication was used and tls_sni was seen in
+the certificate string.
 
 Arguments:
-  sctx      The current SSL CTX (inbound or outbound)
-  dhparam   DH parameter file or fixed parameter identity string
-  host      connected host, if client; NULL if server
-  errstr    error string pointer
+  sctx            the SSL_CTX* to update
+  state           various parts of session state
+  errstr         error string pointer
 
-Returns:    TRUE if OK (nothing to set up, or setup worked)
+Returns:          OK/DEFER/FAIL
 */
 
-static BOOL
-init_dh(SSL_CTX *sctx, uschar *dhparam, const host_item *host, uschar ** errstr)
+static int
+tls_expand_session_files(SSL_CTX * sctx, exim_openssl_state_st * state,
+  uschar ** errstr)
 {
-BIO *bio;
-DH *dh;
-uschar *dhexpanded;
-const char *pem;
-int dh_bitsize;
+uschar * expanded;
 
-if (!expand_check(dhparam, US"tls_dhparam", &dhexpanded, errstr))
-  return FALSE;
-
-if (!dhexpanded || !*dhexpanded)
-  bio = BIO_new_mem_buf(CS std_dh_prime_default(), -1);
-else if (dhexpanded[0] == '/')
+if (!state->certificate)
   {
-  if (!(bio = BIO_new_file(CS dhexpanded, "r")))
-    {
-    tls_error(string_sprintf("could not read dhparams file %s", dhexpanded),
-          host, US strerror(errno), errstr);
-    return FALSE;
-    }
+  if (!state->is_server)               /* client */
+    return OK;
+                                       /* server */
+  if (tls_install_selfsign(sctx, errstr) != OK)
+    return DEFER;
   }
 else
   {
-  if (Ustrcmp(dhexpanded, "none") == 0)
-    {
-    DEBUG(D_tls) debug_printf("Requested no DH parameters.\n");
-    return TRUE;
-    }
+  int err;
 
-  if (!(pem = std_dh_prime_named(dhexpanded)))
-    {
-    tls_error(string_sprintf("Unknown standard DH prime \"%s\"", dhexpanded),
-        host, US strerror(errno), errstr);
-    return FALSE;
-    }
-  bio = BIO_new_mem_buf(CS pem, -1);
-  }
+  if ( !reexpand_tls_files_for_sni
+     && (  Ustrstr(state->certificate, US"tls_sni")
+       || Ustrstr(state->certificate, US"tls_in_sni")
+       || Ustrstr(state->certificate, US"tls_out_sni")
+     )  )
+    reexpand_tls_files_for_sni = TRUE;
 
-if (!(dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL)))
-  {
-  BIO_free(bio);
-  tls_error(string_sprintf("Could not read tls_dhparams \"%s\"", dhexpanded),
-      host, NULL, errstr);
-  return FALSE;
-  }
+  if (!expand_check(state->certificate, US"tls_certificate", &expanded, errstr))
+    return DEFER;
 
-/* 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
-/* 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 */
-dh_bitsize = DH_bits(dh);
-#else
-dh_bitsize = 8 * DH_size(dh);
+  if (expanded)
+    if (state->is_server)
+      {
+      const uschar * file_list = expanded;
+      int sep = 0;
+      uschar * file;
+#ifndef DISABLE_OCSP
+      const uschar * olist = state->u_ocsp.server.file;
+      int osep = 0;
+      uschar * ofile;
+      BOOL fmt_pem = FALSE;
+
+      if (olist)
+       if (!expand_check(olist, US"tls_ocsp_file", USS &olist, errstr))
+         return DEFER;
+      if (olist && !*olist)
+       olist = NULL;
+
+      if (  state->u_ocsp.server.file_expanded && olist
+        && (Ustrcmp(olist, state->u_ocsp.server.file_expanded) == 0))
+       {
+       DEBUG(D_tls) debug_printf(" - value unchanged, using existing values\n");
+       olist = NULL;
+       }
+      else
+       {
+       ocsp_free_response_list(state);
+       state->u_ocsp.server.file_expanded = olist;
+       }
 #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)
-  {
-  DEBUG(D_tls)
-    debug_printf("dhparams file %d bits, is > tls_dh_max_bits limit of %d\n",
-        dh_bitsize, tls_dh_max_bits);
-  }
-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);
-  }
+      while (file = string_nextinlist(&file_list, &sep, NULL, 0))
+       {
+       if ((err = tls_add_certfile(sctx, state, file, errstr)))
+         return err;
 
-DH_free(dh);
-BIO_free(bio);
+#ifndef DISABLE_OCSP
+       if (olist)
+         if ((ofile = string_nextinlist(&olist, &osep, NULL, 0)))
+           {
+           if (Ustrncmp(ofile, US"PEM ", 4) == 0)
+             {
+             fmt_pem = TRUE;
+             ofile += 4;
+             }
+           else if (Ustrncmp(ofile, US"DER ", 4) == 0)
+             {
+             fmt_pem = FALSE;
+             ofile += 4;
+             }
+           ocsp_load_response(state, ofile, fmt_pem);
+           }
+         else
+           DEBUG(D_tls) debug_printf("ran out of ocsp file list\n");
+#endif
+       }
+      }
+    else       /* would there ever be a need for multiple client certs? */
+      if ((err = tls_add_certfile(sctx, state, expanded, errstr)))
+       return err;
 
-return TRUE;
-}
+  if (  state->privatekey
+     && !expand_check(state->privatekey, US"tls_privatekey", &expanded, errstr))
+    return DEFER;
 
+  /* If expansion was forced to fail, key_expanded will be NULL. If the result
+  of the expansion is an empty string, ignore it also, and assume the private
+  key is in the same file as the certificate. */
 
+  if (expanded && *expanded)
+    if (state->is_server)
+      {
+      const uschar * file_list = expanded;
+      int sep = 0;
+      uschar * file;
 
+      while (file = string_nextinlist(&file_list, &sep, NULL, 0))
+       if ((err = tls_add_pkeyfile(sctx, state, file, errstr)))
+         return err;
+      }
+    else       /* would there ever be a need for multiple client certs? */
+      if ((err = tls_add_pkeyfile(sctx, state, expanded, errstr)))
+       return err;
+  }
 
-/*************************************************
-*               Initialize for ECDH              *
-*************************************************/
+return OK;
+}
 
-/* Load parameters for ECDH encryption.
 
-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
-the news and concerns over curve choices, we're not cryptographers, we're not
-pretending to be, and this is "good enough" to be better than no support,
-protecting against most adversaries.  Given another year or two, there might
-be sufficient clarity about a "right" way forward to let us make an informed
-decision, instead of a knee-jerk reaction.
 
-Longer-term, we should look at supporting both various named curves and
-external files generated with "openssl ecparam", much as we do for init_dh().
-We should also support "none" as a value, to explicitly avoid initialisation.
 
-Patches welcome.
+/**************************************************
+* One-time init credentials for server and client *
+**************************************************/
 
-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)
-*/
+#ifdef gnutls
+static void
+creds_basic_init(gnutls_certificate_credentials_t x509_cred, BOOL server)
+{
+}
+#endif
 
-static BOOL
-init_ecdh(SSL_CTX * sctx, host_item * host, uschar ** errstr)
+static int
+creds_load_server_certs(/*exim_gnutls_state_st * state,*/ const uschar * cert,
+  const uschar * pkey, const uschar * ocsp, uschar ** errstr)
 {
-#ifdef OPENSSL_NO_ECDH
-return TRUE;
-#else
+#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
 
-EC_KEY * ecdh;
-uschar * exp_curve;
-int nid;
-BOOL rv;
+if (!expand_check(ocsp, US"tls_ocsp_file", &ofile, errstr))
+  return DEFER;
+olist = ofile;
+#endif
 
-if (host)      /* No ECDH setup for clients, only for servers */
-  return TRUE;
+while (cfile = string_nextinlist(&clist, &csep, NULL, 0))
 
-# ifndef EXIM_HAVE_ECDH
-DEBUG(D_tls)
-  debug_printf("No OpenSSL API to define ECDH parameters, skipping\n");
-return TRUE;
-# else
+  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;
 
-if (!expand_check(tls_eccurve, US"tls_eccurve", &exp_curve, errstr))
-  return FALSE;
-if (!exp_curve || !*exp_curve)
-  return TRUE;
 
-/* "auto" needs to be handled carefully.
- * OpenSSL <  1.0.2: we do not select anything, but fallback to prime256v1
- * OpenSSL <  1.1.0: we have to call SSL_CTX_set_ecdh_auto
- *                   (openssl/ssl.h defines SSL_CTRL_SET_ECDH_AUTO)
- * OpenSSL >= 1.1.0: we do not set anything, the libray does autoselection
- *                   https://github.com/openssl/openssl/commit/fe6ef2472db933f01b59cad82aa925736935984b
- */
-if (Ustrcmp(exp_curve, "auto") == 0)
-  {
-#if OPENSSL_VERSION_NUMBER < 0x10002000L
-  DEBUG(D_tls) debug_printf(
-    "ECDH OpenSSL < 1.0.2: temp key parameter settings: overriding \"auto\" with \"prime256v1\"\n");
-  exp_curve = US"prime256v1";
-#else
-# if defined SSL_CTRL_SET_ECDH_AUTO
-  DEBUG(D_tls) debug_printf(
-    "ECDH OpenSSL 1.0.2+ temp key parameter settings: autoselection\n");
-  SSL_CTX_set_ecdh_auto(sctx, 1);
-  return TRUE;
 # else
-  DEBUG(D_tls) debug_printf(
-    "ECDH OpenSSL 1.1.0+ temp key parameter settings: default selection\n");
-  return TRUE;
-# endif
+#  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)
+{
+#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("ECDH: curve '%s'\n", exp_curve);
-if (  (nid = OBJ_sn2nid       (CCS exp_curve)) == NID_undef
-#   ifdef EXIM_HAVE_OPENSSL_EC_NIST2NID
-   && (nid = EC_curve_nist2nid(CCS exp_curve)) == NID_undef
-#   endif
-   )
-  {
-  tls_error(string_sprintf("Unknown curve name tls_eccurve '%s'", exp_curve),
-    host, NULL, errstr);
-  return FALSE;
-  }
+  DEBUG(D_tls) debug_printf("verify certificates = %s size=" OFF_T_FMT "\n",
+         bundle, statbuf.st_size);
 
-if (!(ecdh = EC_KEY_new_by_curve_name(nid)))
-  {
-  tls_error(US"Unable to create ec curve", host, NULL, errstr);
-  return FALSE;
-  }
+  if (statbuf.st_size == 0)
+    {
+    DEBUG(D_tls)
+      debug_printf("cert file empty, no certs, no verification, ignoring any CRL\n");
+    return OK;
+    }
 
-/* The "tmp" in the name here refers to setting a temporary key
-not to the stability of the interface. */
+  cert_count =
 
-if ((rv = SSL_CTX_set_tmp_ecdh(sctx, ecdh) == 0))
-  tls_error(string_sprintf("Error enabling '%s' curve", exp_curve), host, NULL, errstr);
-else
-  DEBUG(D_tls) debug_printf("ECDH: enabled '%s' curve\n", exp_curve);
+#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);
 
-EC_KEY_free(ecdh);
-return !rv;
+#ifdef SUPPORT_CA_DIR
+  /* Mimic the behaviour with OpenSSL of not advertising a usable-cert list
+  when using the directory-of-certs config model. */
 
-# endif        /*EXIM_HAVE_ECDH*/
-#endif /*OPENSSL_NO_ECDH*/
+  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;
+}
 
 
-#ifndef DISABLE_OCSP
-/*************************************************
-*       Load OCSP information into state         *
-*************************************************/
-/* Called to load the server OCSP response from the given file into memory, once
-caller has determined this is needed.  Checks validity.  Debugs a message
-if invalid.
+static int
+creds_load_pristring(/*exim_gnutls_state_st * state,*/ const uschar * p,
+  const char ** errpos)
+{
+return FAIL;
+}
 
-ASSUMES: single response, for single cert.
+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);
+state->server_cipher_list = ciphers;
+return OK;
+}
 
-Arguments:
-  sctx            the SSL_CTX* to update
-  cbinfo          various parts of session state
-  expanded        the filename putatively holding an OCSP response
 
-*/
 
-static void
-ocsp_load_response(SSL_CTX *sctx, tls_ext_ctx_cb *cbinfo, const uschar *expanded)
+static int
+lib_ctx_new(SSL_CTX ** ctxp, host_item * host, uschar ** errstr)
 {
-BIO * bio;
-OCSP_RESPONSE * resp;
-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;
+SSL_CTX * ctx;
+#ifdef EXIM_HAVE_OPENSSL_TLS_METHOD
+if (!(ctx = SSL_CTX_new(host ? TLS_client_method() : TLS_server_method())))
+#else
+if (!(ctx = SSL_CTX_new(host ? SSLv23_client_method() : SSLv23_server_method())))
+#endif
+  return tls_error(US"SSL_CTX_new", host, NULL, errstr);
 
-cbinfo->u_ocsp.server.file_expanded = string_copy(expanded);
-if (cbinfo->u_ocsp.server.response)
-  {
-  OCSP_RESPONSE_free(cbinfo->u_ocsp.server.response);
-  cbinfo->u_ocsp.server.response = NULL;
-  }
+/* Set up the information callback, which outputs if debugging is at a suitable
+level. */
 
-if (!(bio = BIO_new_file(CS cbinfo->u_ocsp.server.file_expanded, "rb")))
+DEBUG(D_tls)
   {
-  DEBUG(D_tls) debug_printf("Failed to open OCSP response file \"%s\"\n",
-      cbinfo->u_ocsp.server.file_expanded);
-  return;
+  SSL_CTX_set_info_callback(ctx, (void (*)())info_callback);
+#if defined(EXIM_HAVE_OPESSL_TRACE) && !defined(OPENSSL_NO_SSL_TRACE)
+  /* this needs a debug build of OpenSSL */
+  SSL_CTX_set_msg_callback(ctx, (void (*)())SSL_trace);
+#endif
+#ifdef OPENSSL_HAVE_KEYLOG_CB
+  SSL_CTX_set_keylog_callback(ctx, (void (*)())keylog_callback);
+#endif
   }
 
-resp = d2i_OCSP_RESPONSE_bio(bio, NULL);
-BIO_free(bio);
-if (!resp)
-  {
-  DEBUG(D_tls) debug_printf("Error reading OCSP response.\n");
-  return;
-  }
+/* Automatically re-try reads/writes after renegotiation. */
+(void) SSL_CTX_set_mode(ctx, SSL_MODE_AUTO_RETRY);
+*ctxp = ctx;
+return OK;
+}
 
-if ((status = OCSP_response_status(resp)) != OCSP_RESPONSE_STATUS_SUCCESSFUL)
-  {
-  DEBUG(D_tls) debug_printf("OCSP response not valid: %s (%d)\n",
-      OCSP_response_status_str(status), status);
-  goto bad;
-  }
 
-if (!(basic_response = OCSP_response_get1_basic(resp)))
-  {
-  DEBUG(D_tls)
-    debug_printf("OCSP response parse error: unable to extract basic response.\n");
-  goto bad;
-  }
+static void
+tls_server_creds_init(void)
+{
+SSL_CTX * ctx;
+uschar * dummy_errstr;
 
-sk = cbinfo->verify_stack;
-verify_flags = OCSP_NOVERIFY; /* check sigs, but not purpose */
+tls_openssl_init();
 
-/* May need to expose ability to adjust those flags?
-OCSP_NOSIGS OCSP_NOVERIFY OCSP_NOCHAIN OCSP_NOCHECKS OCSP_NOEXPLICIT
-OCSP_TRUSTOTHER OCSP_NOINTERN */
+state_server.lib_state = null_tls_preload;
 
-/* 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.
+if (lib_ctx_new(&ctx, NULL, &dummy_errstr) != OK)
+  return;
+state_server.lib_state.lib_ctx = ctx;
 
-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.
+/* Preload DH params and EC curve */
 
-We have a stack, loaded in setup_certs() if tls_verify_certificates
-was a file (not a directory, or "system").  It is unfortunate we
-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) ? ]
-We do not free the stack since it could be needed a second time for
-SNI handling.
+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))
+    state_server.lib_state.dh = TRUE;
+  }
+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))
+    state_server.lib_state.ecdh = TRUE;
+  }
 
-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) -
-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?  */
+#ifdef EXIM_HAVE_INOTIFY
+/* If we can, preload the server-side cert, key and ocsp */
 
-if ((i = OCSP_basic_verify(basic_response, sk, NULL, verify_flags)) < 0)
+if (  opt_set_and_noexpand(tls_certificate)
+# ifndef DISABLE_OCSP
+   && opt_unset_or_noexpand(tls_ocsp_file)
+#endif
+   && opt_unset_or_noexpand(tls_privatekey))
   {
-  DEBUG(D_tls)
+  /* 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))
     {
-    ERR_error_string_n(ERR_get_error(), ssl_errstring, sizeof(ssl_errstring));
-    debug_printf("OCSP response verify failure: %s\n", US ssl_errstring);
+    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;
     }
-  goto bad;
   }
+else
+  DEBUG(D_tls) debug_printf("TLS: not preloading server certs\n");
 
-/* Here's the simplifying assumption: there's only one response, for the
-one certificate we use, and nothing for anything else in a chain.  If this
-proves false, we need to extract a cert id from our issued cert
-(tls_certificate) and use that for OCSP_resp_find_status() (which finds the
-right cert in the stack and then calls OCSP_single_get0_status()).
 
-I'm hoping to avoid reworking a bunch more of how we handle state here. */
+/* 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 */
 
-if (!(single_response = OCSP_resp_get0(basic_response, 0)))
+if (  opt_set_and_noexpand(tls_verify_certificates)
+   && opt_unset_or_noexpand(tls_crl))
   {
-  DEBUG(D_tls)
-    debug_printf("Unable to get first response from OCSP basic response.\n");
-  goto bad;
-  }
+  /* Watch the default dir also as they are always included */
 
-status = OCSP_single_get0_status(single_response, &reason, &rev, &thisupd, &nextupd);
-if (status != V_OCSP_CERTSTATUS_GOOD)
-  {
-  DEBUG(D_tls) debug_printf("OCSP response bad cert status: %s (%d) %s (%d)\n",
-      OCSP_cert_status_str(status), status,
-      OCSP_crl_reason_str(reason), reason);
-  goto bad;
-  }
+  if (  tls_set_watch(CUS X509_get_default_cert_file(), FALSE)
+     && tls_set_watch(tls_verify_certificates, FALSE)
+     && tls_set_watch(tls_crl, FALSE))
+    {
+    DEBUG(D_tls) debug_printf("TLS: preloading CA bundle for server\n");
 
-if (!OCSP_check_validity(thisupd, nextupd, EXIM_OCSP_SKEW_SECONDS, EXIM_OCSP_MAX_AGE))
-  {
-  DEBUG(D_tls) debug_printf("OCSP status invalid times.\n");
-  goto bad;
+    if (setup_certs(ctx, tls_verify_certificates, tls_crl, NULL, &dummy_errstr)
+       == OK)
+      state_server.lib_state.cabundle = TRUE;
+    }
   }
+else
+  DEBUG(D_tls) debug_printf("TLS: not preloading CA bundle for server\n");
+#endif /* EXIM_HAVE_INOTIFY */
 
-supply_response:
-  cbinfo->u_ocsp.server.response = resp;       /*XXX stack?*/
-return;
 
-bad:
-  if (f.running_in_test_harness)
-    {
-    extern char ** environ;
-    if (environ) for (uschar ** p = USS environ; *p; p++)
-      if (Ustrncmp(*p, "EXIM_TESTHARNESS_DISABLE_OCSPVALIDITYCHECK", 42) == 0)
-       {
-       DEBUG(D_tls) debug_printf("Supplying known bad OCSP response\n");
-       goto supply_response;
-       }
-    }
-return;
+/* If we can, preload the ciphers control string */
+
+if (opt_set_and_noexpand(tls_require_ciphers))
+  {
+  DEBUG(D_tls) debug_printf("TLS: preloading cipher list for server\n");
+  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");
 }
-#endif /*!DISABLE_OCSP*/
 
 
 
 
-/* Create and install a selfsigned certificate, for use in server mode */
+/* Preload whatever creds are static, onto a transport.  The client can then
+just copy the pointer as it starts up.
+Called from the daemon after a cache-invalidate with watch set; called from
+a queue-run startup with watch clear. */
 
-static int
-tls_install_selfsign(SSL_CTX * sctx, uschar ** errstr)
+static void
+tls_client_creds_init(transport_instance * t, BOOL watch)
 {
-X509 * x509 = NULL;
-EVP_PKEY * pkey;
-RSA * rsa;
-X509_NAME * name;
-uschar * where;
-
-where = US"allocating pkey";
-if (!(pkey = EVP_PKEY_new()))
-  goto err;
+smtp_transport_options_block * ob = t->options_block;
+exim_openssl_state_st tpt_dummy_state;
+host_item * dummy_host = (host_item *)1;
+uschar * dummy_errstr;
+SSL_CTX * ctx;
 
-where = US"allocating cert";
-if (!(x509 = X509_new()))
-  goto err;
+tls_openssl_init();
 
-where = US"generating pkey";
-if (!(rsa = rsa_callback(NULL, 0, 2048)))
-  goto err;
+ob->tls_preload = null_tls_preload;
+if (lib_ctx_new(&ctx, dummy_host, &dummy_errstr) != OK)
+  return;
+ob->tls_preload.lib_ctx = ctx;
 
-where = US"assigning pkey";
-if (!EVP_PKEY_assign_RSA(pkey, rsa))
-  goto err;
+tpt_dummy_state.lib_state = ob->tls_preload;
 
-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_set_pubkey(x509, pkey);
+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;
+  }
 
-name = X509_get_subject_name(x509);
-X509_NAME_add_entry_by_txt(name, "C",
-                         MBSTRING_ASC, CUS "UK", -1, -1, 0);
-X509_NAME_add_entry_by_txt(name, "O",
-                         MBSTRING_ASC, CUS "Exim Developers", -1, -1, 0);
-X509_NAME_add_entry_by_txt(name, "CN",
-                         MBSTRING_ASC, CUS smtp_active_hostname, -1, -1, 0);
-X509_set_issuer_name(x509, name);
+#ifdef EXIM_HAVE_INOTIFY
+if (  opt_set_and_noexpand(ob->tls_certificate)
+   && opt_unset_or_noexpand(ob->tls_privatekey))
+  {
+  if (  !watch
+     || (  tls_set_watch(ob->tls_certificate, FALSE)
+       && tls_set_watch(ob->tls_privatekey, FALSE)
+     )  )
+    {
+    uschar * pkey = ob->tls_privatekey;
 
-where = US"signing cert";
-if (!X509_sign(x509, pkey, EVP_md5()))
-  goto err;
+    DEBUG(D_tls)
+      debug_printf("TLS: preloading client certs for transport '%s'\n",t->name);
+
+    if (  tls_add_certfile(ctx, &tpt_dummy_state, ob->tls_certificate,
+                                   &dummy_errstr) == 0
+       && tls_add_pkeyfile(ctx, &tpt_dummy_state,
+                                   pkey ? pkey : ob->tls_certificate,
+                                   &dummy_errstr) == 0
+       )
+      ob->tls_preload.conn_certs = TRUE;
+    }
+  }
+else
+  DEBUG(D_tls)
+    debug_printf("TLS: not preloading client certs, for transport '%s'\n", t->name);
 
-where = US"installing selfsign cert";
-if (!SSL_CTX_use_certificate(sctx, x509))
-  goto err;
 
-where = US"installing selfsign key";
-if (!SSL_CTX_use_PrivateKey(sctx, pkey))
-  goto err;
+if (  opt_set_and_noexpand(ob->tls_verify_certificates)
+   && opt_unset_or_noexpand(ob->tls_crl))
+  {
+  if (  !watch
+     ||    tls_set_watch(CUS X509_get_default_cert_file(), FALSE)
+        && tls_set_watch(ob->tls_verify_certificates, FALSE)
+       && tls_set_watch(ob->tls_crl, FALSE)
+     )
+    {
+    DEBUG(D_tls)
+      debug_printf("TLS: preloading CA bundle for transport '%s'\n", t->name);
 
-return OK;
+    if (setup_certs(ctx, ob->tls_verify_certificates,
+         ob->tls_crl, dummy_host, &dummy_errstr) == OK)
+      ob->tls_preload.cabundle = TRUE;
+    }
+  }
+else
+  DEBUG(D_tls)
+      debug_printf("TLS: not preloading CA bundle, for transport '%s'\n", t->name);
 
-err:
-  (void) tls_error(where, NULL, NULL, errstr);
-  if (x509) X509_free(x509);
-  if (pkey) EVP_PKEY_free(pkey);
-  return DEFER;
+#endif /*EXIM_HAVE_INOTIFY*/
 }
 
 
+#ifdef EXIM_HAVE_INOTIFY
+/* Invalidate the creds cached, by dropping the current ones.
+Call when we notice one of the source files has changed. */
+static void
+tls_server_creds_invalidate(void)
+{
+SSL_CTX_free(state_server.lib_state.lib_ctx);
+state_server.lib_state = null_tls_preload;
+}
 
 
-static int
-tls_add_certfile(SSL_CTX * sctx, tls_ext_ctx_cb * cbinfo, uschar * file,
-  uschar ** errstr)
+static void
+tls_client_creds_invalidate(transport_instance * t)
 {
-DEBUG(D_tls) debug_printf("tls_certificate file %s\n", file);
-if (!SSL_CTX_use_certificate_chain_file(sctx, CS file))
-  return tls_error(string_sprintf(
-    "SSL_CTX_use_certificate_chain_file file=%s", file),
-      cbinfo->host, NULL, errstr);
-return 0;
+smtp_transport_options_block * ob = t->options_block;
+SSL_CTX_free(ob->tls_preload.lib_ctx);
+ob->tls_preload = null_tls_preload;
 }
+#endif /*EXIM_HAVE_INOTIFY*/
 
-static int
-tls_add_pkeyfile(SSL_CTX * sctx, tls_ext_ctx_cb * cbinfo, uschar * file,
-  uschar ** errstr)
+
+/* Extreme debug
+#ifndef DISABLE_OCSP
+void
+x509_store_dump_cert_s_names(X509_STORE * store)
 {
-DEBUG(D_tls) debug_printf("tls_privatekey file %s\n", file);
-if (!SSL_CTX_use_PrivateKey_file(sctx, CS file, SSL_FILETYPE_PEM))
-  return tls_error(string_sprintf(
-    "SSL_CTX_use_PrivateKey_file file=%s", file), cbinfo->host, NULL, errstr);
-return 0;
+STACK_OF(X509_OBJECT) * roots= store->objs;
+static uschar name[256];
+
+for (int i= 0; i < sk_X509_OBJECT_num(roots); i++)
+  {
+  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);
+      }
+    }
+  }
 }
+#endif
+*/
 
 
-/*************************************************
-*        Expand key and cert file specs          *
-*************************************************/
+#ifndef DISABLE_TLS_RESUME
+/* Manage the keysets used for encrypting the session tickets, on the server. */
 
-/* Called once during tls_init and possibly again during TLS setup, for a
-new context, if Server Name Indication was used and tls_sni was seen in
-the certificate string.
+typedef struct {                       /* Session ticket encryption key */
+  uschar       name[16];
 
-Arguments:
-  sctx            the SSL_CTX* to update
-  cbinfo          various parts of session state
-  errstr         error string pointer
+  const EVP_CIPHER *   aes_cipher;
+  uschar               aes_key[32];    /* size needed depends on cipher. aes_128 implies 128/8 = 16? */
+  const EVP_MD *       hmac_hash;
+  uschar               hmac_key[16];
+  time_t               renew;
+  time_t               expire;
+} exim_stek;
 
-Returns:          OK/DEFER/FAIL
-*/
+static exim_stek exim_tk;      /* current key */
+static exim_stek exim_tk_old;  /* previous key */
 
-static int
-tls_expand_session_files(SSL_CTX *sctx, tls_ext_ctx_cb *cbinfo,
-  uschar ** errstr)
+static void
+tk_init(void)
 {
-uschar *expanded;
+time_t t = time(NULL);
 
-if (!cbinfo->certificate)
+if (exim_tk.name[0])
   {
-  if (!cbinfo->is_server)              /* client */
-    return OK;
-                                       /* server */
-  if (tls_install_selfsign(sctx, errstr) != OK)
-    return DEFER;
+  if (exim_tk.renew >= t) return;
+  exim_tk_old = exim_tk;
   }
-else
-  {
-  int err;
 
-  if (Ustrstr(cbinfo->certificate, US"tls_sni") ||
-      Ustrstr(cbinfo->certificate, US"tls_in_sni") ||
-      Ustrstr(cbinfo->certificate, US"tls_out_sni")
-     )
-    reexpand_tls_files_for_sni = TRUE;
+if (f.running_in_test_harness) ssl_session_timeout = 6;
 
-  if (!expand_check(cbinfo->certificate, US"tls_certificate", &expanded, errstr))
-    return DEFER;
+DEBUG(D_tls) debug_printf("OpenSSL: %s STEK\n", exim_tk.name[0] ? "rotating" : "creating");
+if (RAND_bytes(exim_tk.aes_key, sizeof(exim_tk.aes_key)) <= 0) return;
+if (RAND_bytes(exim_tk.hmac_key, sizeof(exim_tk.hmac_key)) <= 0) return;
+if (RAND_bytes(exim_tk.name+1, sizeof(exim_tk.name)-1) <= 0) return;
 
-  if (expanded)
-    if (cbinfo->is_server)
-      {
-      const uschar * file_list = expanded;
-      int sep = 0;
-      uschar * file;
+exim_tk.name[0] = 'E';
+exim_tk.aes_cipher = EVP_aes_256_cbc();
+exim_tk.hmac_hash = EVP_sha256();
+exim_tk.expire = t + ssl_session_timeout;
+exim_tk.renew = t + ssl_session_timeout/2;
+}
 
-      while (file = string_nextinlist(&file_list, &sep, NULL, 0))
-       if ((err = tls_add_certfile(sctx, cbinfo, file, errstr)))
-         return err;
-      }
-    else       /* would there ever be a need for multiple client certs? */
-      if ((err = tls_add_certfile(sctx, cbinfo, expanded, errstr)))
-       return err;
+static exim_stek *
+tk_current(void)
+{
+if (!exim_tk.name[0]) return NULL;
+return &exim_tk;
+}
 
-  if (  cbinfo->privatekey
-     && !expand_check(cbinfo->privatekey, US"tls_privatekey", &expanded, errstr))
-    return DEFER;
+static exim_stek *
+tk_find(const uschar * name)
+{
+return memcmp(name, exim_tk.name, sizeof(exim_tk.name)) == 0 ? &exim_tk
+  : memcmp(name, exim_tk_old.name, sizeof(exim_tk_old.name)) == 0 ? &exim_tk_old
+  : NULL;
+}
 
-  /* If expansion was forced to fail, key_expanded will be NULL. If the result
-  of the expansion is an empty string, ignore it also, and assume the private
-  key is in the same file as the certificate. */
+/* 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)
+{
+tls_support * tlsp = state_server.tlsp;
+exim_stek * key;
 
-  if (expanded && *expanded)
-    if (cbinfo->is_server)
-      {
-      const uschar * file_list = expanded;
-      int sep = 0;
-      uschar * file;
+if (enc)
+  {
+  DEBUG(D_tls) debug_printf("ticket_key_callback: create new session\n");
+  tlsp->resumption |= RESUME_CLIENT_REQUESTED;
 
-      while (file = string_nextinlist(&file_list, &sep, NULL, 0))
-       if ((err = tls_add_pkeyfile(sctx, cbinfo, file, errstr)))
-         return err;
-      }
-    else       /* would there ever be a need for multiple client certs? */
-      if ((err = tls_add_pkeyfile(sctx, cbinfo, expanded, errstr)))
-       return err;
-  }
+  if (RAND_bytes(iv, EVP_MAX_IV_LENGTH) <= 0)
+    return -1; /* insufficient random */
 
-#ifndef DISABLE_OCSP
-if (cbinfo->is_server && cbinfo->u_ocsp.server.file)
+  if (!(key = tk_current()))   /* current key doesn't exist or isn't valid */
+     return 0;                 /* key couldn't be created */
+  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);
+  EVP_EncryptInit_ex(c_ctx, key->aes_cipher, NULL, key->aes_key, iv);
+
+  DEBUG(D_tls) debug_printf("ticket created\n");
+  return 1;
+  }
+else
   {
-  /*XXX stack*/
-  if (!expand_check(cbinfo->u_ocsp.server.file, US"tls_ocsp_file", &expanded, errstr))
-    return DEFER;
+  time_t now = time(NULL);
 
-  if (expanded && *expanded)
+  DEBUG(D_tls) debug_printf("ticket_key_callback: retrieve session\n");
+  tlsp->resumption |= RESUME_CLIENT_SUGGESTED;
+
+  if (!(key = tk_find(key_name)) || key->expire < now)
     {
-    DEBUG(D_tls) debug_printf("tls_ocsp_file %s\n", expanded);
-    if (  cbinfo->u_ocsp.server.file_expanded
-       && (Ustrcmp(expanded, cbinfo->u_ocsp.server.file_expanded) == 0))
+    DEBUG(D_tls)
       {
-      DEBUG(D_tls) debug_printf(" - value unchanged, using existing values\n");
+      debug_printf("ticket not usable (%s)\n", key ? "expired" : "not found");
+      if (key) debug_printf("STEK expire " TIME_T_FMT "\n", key->expire - now);
       }
-    else
-      ocsp_load_response(sctx, cbinfo, expanded);
+    return 0;
     }
+
+  HMAC_Init_ex(hctx, key->hmac_key, sizeof(key->hmac_key),
+               key->hmac_hash, NULL);
+  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);
+
+  /* The ticket lifetime and renewal are the same as the STEK lifetime and
+  renewal, which is overenthusiastic.  A factor of, say, 3x longer STEK would
+  be better.  To do that we'd have to encode ticket lifetime in the name as
+  we don't yet see the restored session.  Could check posthandshake for TLS1.3
+  and trigger a new ticket then, but cannot do that for TLS1.2 */
+  return key->renew < now ? 2 : 1;
   }
+}
 #endif
 
-return OK;
-}
 
 
+static void
+setup_cert_verify(SSL_CTX * ctx, BOOL optional,
+    int (*cert_vfy_cb)(int, X509_STORE_CTX *))
+{
+/* If verification is optional, don't fail if no certificate */
+
+SSL_CTX_set_verify(ctx,
+    SSL_VERIFY_PEER | (optional ? 0 : SSL_VERIFY_FAIL_IF_NO_PEER_CERT),
+    cert_vfy_cb);
+}
 
 
 /*************************************************
@@ -1556,7 +2232,7 @@ static int
 tls_servername_cb(SSL *s, int *ad ARG_UNUSED, void *arg)
 {
 const char *servername = SSL_get_servername(s, TLSEXT_NAMETYPE_host_name);
-tls_ext_ctx_cb *cbinfo = (tls_ext_ctx_cb *) arg;
+exim_openssl_state_st *state = (exim_openssl_state_st *) arg;
 int rc;
 int old_pool = store_pool;
 uschar * dummy_errstr;
@@ -1569,7 +2245,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(US servername);
+tls_in.sni = string_copy_taint(US servername, TRUE);
 store_pool = old_pool;
 
 if (!reexpand_tls_files_for_sni)
@@ -1579,51 +2255,54 @@ if (!reexpand_tls_files_for_sni)
 not confident that memcpy wouldn't break some internal reference counting.
 Especially since there's a references struct member, which would be off. */
 
-#ifdef EXIM_HAVE_OPENSSL_TLS_METHOD
-if (!(server_sni = SSL_CTX_new(TLS_server_method())))
-#else
-if (!(server_sni = SSL_CTX_new(SSLv23_server_method())))
-#endif
-  {
-  ERR_error_string_n(ERR_get_error(), ssl_errstring, sizeof(ssl_errstring));
-  DEBUG(D_tls) debug_printf("SSL_CTX_new() failed: %s\n", ssl_errstring);
+if (lib_ctx_new(&server_sni, NULL, &dummy_errstr) != OK)
   goto bad;
-  }
 
 /* Not sure how many of these are actually needed, since SSL object
 already exists.  Might even need this selfsame callback, for reneg? */
 
-SSL_CTX_set_info_callback(server_sni, SSL_CTX_get_info_callback(server_ctx));
-SSL_CTX_set_mode(server_sni, SSL_CTX_get_mode(server_ctx));
-SSL_CTX_set_options(server_sni, SSL_CTX_get_options(server_ctx));
-SSL_CTX_set_timeout(server_sni, SSL_CTX_get_timeout(server_ctx));
-SSL_CTX_set_tlsext_servername_callback(server_sni, tls_servername_cb);
-SSL_CTX_set_tlsext_servername_arg(server_sni, cbinfo);
+  {
+  SSL_CTX * ctx = state_server.lib_state.lib_ctx;
+  SSL_CTX_set_info_callback(server_sni, SSL_CTX_get_info_callback(ctx));
+  SSL_CTX_set_mode(server_sni, SSL_CTX_get_mode(ctx));
+  SSL_CTX_set_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, cbinfo->dhparam, NULL, &dummy_errstr)
+if (  !init_dh(server_sni, state->dhparam, NULL, &dummy_errstr)
    || !init_ecdh(server_sni, NULL, &dummy_errstr)
    )
   goto bad;
 
-if (  cbinfo->server_cipher_list
-   && !SSL_CTX_set_cipher_list(server_sni, CS cbinfo->server_cipher_list))
+if (  state->server_cipher_list
+   && !SSL_CTX_set_cipher_list(server_sni, CS state->server_cipher_list))
   goto bad;
 
 #ifndef DISABLE_OCSP
-if (cbinfo->u_ocsp.server.file)
+if (state->u_ocsp.server.file)
   {
   SSL_CTX_set_tlsext_status_cb(server_sni, tls_server_stapling_cb);
-  SSL_CTX_set_tlsext_status_arg(server_sni, cbinfo);
+  SSL_CTX_set_tlsext_status_arg(server_sni, state);
   }
 #endif
 
-if ((rc = setup_certs(server_sni, tls_verify_certificates, tls_crl, NULL, FALSE,
-                     verify_callback_server, &dummy_errstr)) != OK)
-  goto bad;
+  {
+  uschar * expcerts;
+  if (  !expand_check(tls_verify_certificates, US"tls_verify_certificates",
+                 &expcerts, &dummy_errstr)
+     || (rc = setup_certs(server_sni, expcerts, tls_crl, NULL,
+                       &dummy_errstr)) != OK)
+    goto bad;
+
+  if (expcerts && *expcerts)
+    setup_cert_verify(server_sni, FALSE, verify_callback_server);
+  }
 
 /* do this after setup_certs, because this can require the certs for verifying
 OCSP information. */
-if ((rc = tls_expand_session_files(server_sni, cbinfo, &dummy_errstr)) != OK)
+if ((rc = tls_expand_session_files(server_sni, state, &dummy_errstr)) != OK)
   goto bad;
 
 DEBUG(D_tls) debug_printf("Switching SSL context.\n");
@@ -1654,31 +2333,83 @@ project.
 static int
 tls_server_stapling_cb(SSL *s, void *arg)
 {
-const tls_ext_ctx_cb *cbinfo = (tls_ext_ctx_cb *) arg;
-uschar *response_der;  /*XXX blob */
+const exim_openssl_state_st * state = arg;
+ocsp_resplist * olist = state->u_ocsp.server.olist;
+uschar * response_der; /*XXX blob */
 int response_der_len;
 
-/*XXX stack: use SSL_get_certificate() to see which cert; from that work
-out which ocsp blob to send.  Unfortunately, SSL_get_certificate is known
-buggy in current OpenSSL; it returns the last cert loaded always rather than
-the one actually presented.  So we can't support a stack of OCSP proofs at
-this time. */
-
 DEBUG(D_tls)
-  debug_printf("Received TLS status request (OCSP stapling); %s response\n",
-    cbinfo->u_ocsp.server.response ? "have" : "lack");
+  debug_printf("Received TLS status request (OCSP stapling); %s response list\n",
+    olist ? "have" : "lack");
 
 tls_in.ocsp = OCSP_NOT_RESP;
-if (!cbinfo->u_ocsp.server.response)
+if (!olist)
+  return SSL_TLSEXT_ERR_NOACK;
+
+#ifdef EXIM_HAVE_OPESSL_GET0_SERIAL
+ {
+  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)
+    {
+    OCSP_BASICRESP * bs = OCSP_response_get1_basic(olist->resp);
+    const OCSP_SINGLERESP * single = OCSP_resp_get0(bs, 0);
+    const OCSP_CERTID * cid = OCSP_SINGLERESP_get0_id(single);
+    ASN1_INTEGER * res_cert_serial;
+    const BIGNUM * resp_bn;
+    ASN1_OCTET_STRING * res_cert_iNameHash;
+
+
+    (void) OCSP_id_get0_info(&res_cert_iNameHash, NULL, NULL, &res_cert_serial,
+      (OCSP_CERTID *) cid);
+    resp_bn = ASN1_INTEGER_to_BN(res_cert_serial, NULL);
+
+    DEBUG(D_tls)
+      {
+      debug_printf("cert serial: %s\n", BN_bn2hex(cert_bn));
+      debug_printf("resp serial: %s\n", BN_bn2hex(resp_bn));
+      }
+
+    if (BN_cmp(cert_bn, resp_bn) == 0)
+      {
+      DEBUG(D_tls) debug_printf("matched serial for ocsp\n");
+
+      /*XXX TODO: check the rest of the list for duplicate matches.
+      If any, need to also check the Issuer Name hash.
+      Without this, we will provide the wrong status in the case of
+      duplicate id. */
+
+      break;
+      }
+    DEBUG(D_tls) debug_printf("not match serial for ocsp\n");
+    }
+  if (!olist)
+    {
+    DEBUG(D_tls) debug_printf("failed to find match for ocsp\n");
+    return SSL_TLSEXT_ERR_NOACK;
+    }
+ }
+#else
+if (olist->next)
+  {
+  DEBUG(D_tls) debug_printf("OpenSSL version too early to support multi-leaf OCSP\n");
   return SSL_TLSEXT_ERR_NOACK;
+  }
+#endif
 
+/*XXX could we do the i2d earlier, rather than during the callback? */
 response_der = NULL;
-response_der_len = i2d_OCSP_RESPONSE(cbinfo->u_ocsp.server.response,   /*XXX stack*/
-                     &response_der);
+response_der_len = i2d_OCSP_RESPONSE(olist->resp, &response_der);
 if (response_der_len <= 0)
   return SSL_TLSEXT_ERR_NOACK;
 
-SSL_set_tlsext_status_ocsp_resp(server_ssl, response_der, response_der_len);
+SSL_set_tlsext_status_ocsp_resp(state_server.lib_state.lib_ssl,
+                               response_der, response_der_len);
 tls_in.ocsp = OCSP_VFIED;
 return SSL_TLSEXT_ERR_OK;
 }
@@ -1695,36 +2426,36 @@ BIO_puts(bp, "\n");
 static int
 tls_client_stapling_cb(SSL *s, void *arg)
 {
-tls_ext_ctx_cb * cbinfo = arg;
+exim_openssl_state_st * cbinfo = arg;
 const unsigned char * p;
 int len;
 OCSP_RESPONSE * rsp;
 OCSP_BASICRESP * bs;
 int i;
 
-DEBUG(D_tls) debug_printf("Received TLS status response (OCSP stapling):");
+DEBUG(D_tls) debug_printf("Received TLS status callback (OCSP stapling):\n");
 len = SSL_get_tlsext_status_ocsp_resp(s, &p);
 if(!p)
  {
   /* Expect this when we requested ocsp but got none */
   if (cbinfo->u_ocsp.client.verify_required && LOGGING(tls_cipher))
-    log_write(0, LOG_MAIN, "Received TLS status callback, null content");
+    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(!(rsp = d2i_OCSP_RESPONSE(NULL, &p, len)))
- {
-  tls_out.ocsp = OCSP_FAILED;
+if (!(rsp = d2i_OCSP_RESPONSE(NULL, &p, len)))
 {
+  tls_out.ocsp = OCSP_FAILED;  /*XXX should use tlsp-> to permit concurrent outbound */
   if (LOGGING(tls_cipher))
     log_write(0, LOG_MAIN, "Received TLS cert status response, parse error");
   else
     DEBUG(D_tls) debug_printf(" parse error\n");
   return 0;
- }
 }
 
-if(!(bs = OCSP_response_get1_basic(rsp)))
+if (!(bs = OCSP_response_get1_basic(rsp)))
   {
   tls_out.ocsp = OCSP_FAILED;
   if (LOGGING(tls_cipher))
@@ -1744,8 +2475,9 @@ if(!(bs = OCSP_response_get1_basic(rsp)))
 */
   {
     BIO * bp = NULL;
-    int status, reason;
-    ASN1_GENERALIZEDTIME *rev, *thisupd, *nextupd;
+#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);
 
@@ -1755,19 +2487,23 @@ if(!(bs = OCSP_response_get1_basic(rsp)))
     /* DEBUG(D_tls) x509_store_dump_cert_s_names(cbinfo->u_ocsp.client.verify_store); */
 
     if ((i = OCSP_basic_verify(bs, cbinfo->verify_stack,
-             cbinfo->u_ocsp.client.verify_store, 0)) <= 0)
-      {
-      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);
-      goto failed;
-      }
+             cbinfo->u_ocsp.client.verify_store, OCSP_NOEXPLICIT)) <= 0)
+      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);
+       goto failed;
+       }
+      else
+       DEBUG(D_tls) debug_printf("no explicit trust for OCSP signing"
+         " in the root CA certificate; ignoring\n");
 
-    BIO_printf(bp, "OCSP response well-formed and signed OK\n");
+    DEBUG(D_tls) debug_printf("OCSP response well-formed and signed OK\n");
 
     /*XXX So we have a good stapled OCSP status.  How do we know
     it is for the cert of interest?  OpenSSL 1.1.0 has a routine
@@ -1777,60 +2513,65 @@ if(!(bs = OCSP_response_get1_basic(rsp)))
 
     For now, carry on blindly accepting the resp. */
 
-      {
-      OCSP_SINGLERESP * single;
-
+    for (int idx =
 #ifdef EXIM_HAVE_OCSP_RESP_COUNT
-      if (OCSP_resp_count(bs) != 1)
+           OCSP_resp_count(bs) - 1;
 #else
-      STACK_OF(OCSP_SINGLERESP) * sresp = bs->tbsResponseData->responses;
-      if (sk_OCSP_SINGLERESP_num(sresp) != 1)
+           sk_OCSP_SINGLERESP_num(sresp) - 1;
 #endif
-        {
-       tls_out.ocsp = OCSP_FAILED;
-        log_write(0, LOG_MAIN, "OCSP stapling "
-           "with multiple responses not handled");
-        goto failed;
-        }
-      single = OCSP_resp_get0(bs, 0);
+        idx >= 0; idx--)
+      {
+      OCSP_SINGLERESP * single = OCSP_resp_get0(bs, idx);
+      int status, reason;
+      ASN1_GENERALIZEDTIME * rev, * thisupd, * nextupd;
+
+  /*XXX so I can see putting a loop in here to handle a rsp with >1 singleresp
+  - but what happens with a GnuTLS-style input?
+
+  we could do with a debug label for each singleresp
+  - it has a certID with a serialNumber, but I see no API to get that
+  */
       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);
-    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");
-      }
-    else
-      {
+      DEBUG(D_tls) time_print(bp, "This OCSP Update", thisupd);
+      DEBUG(D_tls) 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");
+       goto failed;
+       }
+
       DEBUG(D_tls) BIO_printf(bp, "Certificate status: %s\n",
                    OCSP_cert_status_str(status));
       switch(status)
        {
        case V_OCSP_CERTSTATUS_GOOD:
-         tls_out.ocsp = OCSP_VFIED;
-         i = 1;
-         goto good;
+         continue;     /* the idx loop */
        case V_OCSP_CERTSTATUS_REVOKED:
-         tls_out.ocsp = OCSP_FAILED;
          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:
-         tls_out.ocsp = OCSP_FAILED;
          log_write(0, LOG_MAIN,
              "Server certificate status unknown, in OCSP stapling");
          break;
        }
+
+      goto failed;
       }
+
+    i = 1;
+    tls_out.ocsp = OCSP_VFIED;
+    goto good;
+
   failed:
+    tls_out.ocsp = OCSP_FAILED;
     i = cbinfo->u_ocsp.client.verify_required ? 0 : 1;
   good:
     BIO_free(bp);
@@ -1845,87 +2586,64 @@ return i;
 /*************************************************
 *            Initialize for TLS                  *
 *************************************************/
-
 /* Called from both server and client code, to do preliminary initialization
 of the library.  We allocate and return a context structure.
 
 Arguments:
-  ctxp            returned SSL context
   host            connected host, if client; NULL if server
-  dhparam         DH parameter file
-  certificate     certificate file
-  privatekey      private key
+  ob             transport options block, if client; NULL if server
   ocsp_file       file of stapling info (server); flag for require ocsp (client)
   addr            address if client; NULL if server (for some randomness)
-  cbp             place to put allocated callback context
+  caller_state    place to put pointer to allocated state-struct
   errstr         error string pointer
 
 Returns:          OK/DEFER/FAIL
 */
 
 static int
-tls_init(SSL_CTX **ctxp, host_item *host, uschar *dhparam, uschar *certificate,
-  uschar *privatekey,
+tls_init(host_item * host, smtp_transport_options_block * ob,
 #ifndef DISABLE_OCSP
-  uschar *ocsp_file,   /*XXX stack, in server*/
+  uschar *ocsp_file,
 #endif
-  address_item *addr, tls_ext_ctx_cb ** cbp,
+  address_item *addr, exim_openssl_state_st ** caller_state,
   tls_support * tlsp,
   uschar ** errstr)
 {
 SSL_CTX * ctx;
-long init_options;
+exim_openssl_state_st * state;
 int rc;
-tls_ext_ctx_cb * cbinfo;
 
-cbinfo = store_malloc(sizeof(tls_ext_ctx_cb));
-cbinfo->tlsp = tlsp;
-cbinfo->certificate = certificate;
-cbinfo->privatekey = privatekey;
-cbinfo->is_server = host==NULL;
-#ifndef DISABLE_OCSP
-cbinfo->verify_stack = NULL;
-if (!host)
+if (host)                      /* client */
   {
-  cbinfo->u_ocsp.server.file = ocsp_file;
-  cbinfo->u_ocsp.server.file_expanded = NULL;
-  cbinfo->u_ocsp.server.response = NULL;
+  state = store_malloc(sizeof(exim_openssl_state_st));
+  memset(state, 0, sizeof(*state));
+  state->certificate = ob->tls_certificate;
+  state->privatekey =  ob->tls_privatekey;
+  state->is_server = FALSE;
+  state->dhparam = NULL;
+  state->lib_state = ob->tls_preload;
+  }
+else                           /* server */
+  {
+  state = &state_server;
+  state->certificate = tls_certificate;
+  state->privatekey =  tls_privatekey;
+  state->is_server = TRUE;
+  state->dhparam = tls_dhparam;
+  state->lib_state = state_server.lib_state;
   }
-else
-  cbinfo->u_ocsp.client.verify_store = NULL;
-#endif
-cbinfo->dhparam = dhparam;
-cbinfo->server_cipher_list = NULL;
-cbinfo->host = host;
-#ifndef DISABLE_EVENT
-cbinfo->event_action = NULL;
-#endif
-
-#ifdef EXIM_NEED_OPENSSL_INIT
-SSL_load_error_strings();          /* basic set up */
-OpenSSL_add_ssl_algorithms();
-#endif
 
-#ifdef EXIM_HAVE_SHA256
-/* SHA256 is becoming ever more popular. This makes sure it gets added to the
-list of available digests. */
-EVP_add_digest(EVP_sha256());
-#endif
+state->tlsp = tlsp;
+state->host = host;
 
-/* Create a context.
-The OpenSSL docs in 1.0.1b have not been updated to clarify TLS variant
-negotiation in the different methods; as far as I can tell, the only
-*_{server,client}_method which allows negotiation is SSLv23, which exists even
-when OpenSSL is built without SSLv2 support.
-By disabling with openssl_options, we can let admins re-enable with the
-existing knob. */
+if (!state->lib_state.pri_string)
+  state->server_cipher_list = NULL;
 
-#ifdef EXIM_HAVE_OPENSSL_TLS_METHOD
-if (!(ctx = SSL_CTX_new(host ? TLS_client_method() : TLS_server_method())))
-#else
-if (!(ctx = SSL_CTX_new(host ? SSLv23_client_method() : SSLv23_server_method())))
+#ifndef DISABLE_EVENT
+state->event_action = NULL;
 #endif
-  return tls_error(US"SSL_CTX_new", host, NULL, errstr);
+
+tls_openssl_init();
 
 /* It turns out that we need to seed the random number generator this early in
 order to get the full complement of ciphers to work. It took me roughly a day
@@ -1933,40 +2651,14 @@ of work to discover this by experiment.
 
 On systems that have /dev/urandom, SSL may automatically seed itself from
 there. Otherwise, we have to make something up as best we can. Double check
-afterwards. */
-
-if (!RAND_status())
-  {
-  randstuff r;
-  gettimeofday(&r.tv, NULL);
-  r.p = getpid();
-
-  RAND_seed(US (&r), sizeof(r));
-  RAND_seed(US big_buffer, big_buffer_size);
-  if (addr != NULL) RAND_seed(US addr, sizeof(addr));
-
-  if (!RAND_status())
-    return tls_error(US"RAND_status", host,
-      US"unable to seed random number generator", errstr);
-  }
-
-/* Set up the information callback, which outputs if debugging is at a suitable
-level. */
+afterwards.
 
-DEBUG(D_tls)
-  {
-  SSL_CTX_set_info_callback(ctx, (void (*)())info_callback);
-#if defined(EXIM_HAVE_OPESSL_TRACE) && !defined(OPENSSL_NO_SSL_TRACE)
-  /* this needs a debug build of OpenSSL */
-  SSL_CTX_set_msg_callback(ctx, (void (*)())SSL_trace);
-#endif
-#ifdef OPENSSL_HAVE_KEYLOG_CB
-  SSL_CTX_set_keylog_callback(ctx, (void (*)())keylog_callback);
-#endif
-  }
+Although we likely called this before, at daemon startup, this is a chance
+to mix in further variable info (time, pid) if needed. */
 
-/* Automatically re-try reads/writes after renegotiation. */
-(void) SSL_CTX_set_mode(ctx, SSL_MODE_AUTO_RETRY);
+if (!lib_rand_init(addr))
+  return tls_error(US"RAND_status", host,
+    US"unable to seed random number generator", errstr);
 
 /* Apply administrator-supplied work-arounds.
 Historically we applied just one requested option,
@@ -1977,15 +2669,31 @@ grandfathered in the first one as the default value for "openssl_options".
 No OpenSSL version number checks: the options we accept depend upon the
 availability of the option value macros from OpenSSL.  */
 
-if (!tls_openssl_options_parse(openssl_options, &init_options))
-  return tls_error(US"openssl_options parsing failed", host, NULL, errstr);
+if (!init_options)
+  if (!tls_openssl_options_parse(openssl_options, &init_options))
+    return tls_error(US"openssl_options parsing failed", host, NULL, errstr);
+
+/* Create a context.
+The OpenSSL docs in 1.0.1b have not been updated to clarify TLS variant
+negotiation in the different methods; as far as I can tell, the only
+*_{server,client}_method which allows negotiation is SSLv23, which exists even
+when OpenSSL is built without SSLv2 support.
+By disabling with openssl_options, we can let admins re-enable with the
+existing knob. */
+
+if (!(ctx = state->lib_state.lib_ctx))
+  {
+  if ((rc = lib_ctx_new(&ctx, host, errstr)) != OK)
+    return rc;
+  state->lib_state.lib_ctx = ctx;
+  }
 
-#ifdef EXPERIMENTAL_TLS_RESUME
+#ifndef DISABLE_TLS_RESUME
 tlsp->resumption = RESUME_SUPPORTED;
 #endif
 if (init_options)
   {
-#ifdef EXPERIMENTAL_TLS_RESUME
+#ifndef DISABLE_TLS_RESUME
   /* Should the server offer session resumption? */
   if (!host && verify_check_host(&tls_resumption_hosts) == OK)
     {
@@ -2017,21 +2725,41 @@ 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 (  !init_dh(ctx, dhparam, host, errstr)
-   || !init_ecdh(ctx, host, errstr)
-   )
-  return DEFER;
+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 (state->lib_state.ecdh)
+  { DEBUG(D_tls) debug_printf("TLS: ECDH curve was preloaded\n"); }
+else
+  if (!init_ecdh(ctx, host, errstr)) return DEFER;
 
 /* Set up certificate and key (and perhaps OCSP info) */
 
-if ((rc = tls_expand_session_files(ctx, cbinfo, errstr)) != OK)
-  return rc;
+if (state->lib_state.conn_certs)
+  {
+  DEBUG(D_tls)
+    debug_printf("TLS: %s certs were preloaded\n", host ? "client":"server");
+  }
+else
+  {
+#ifndef DISABLE_OCSP
+  if (!host)
+    {
+    state->u_ocsp.server.file = ocsp_file;
+    state->u_ocsp.server.file_expanded = NULL;
+    state->u_ocsp.server.olist = NULL;
+    }
+#endif
+  if ((rc = tls_expand_session_files(ctx, state, errstr)) != OK) return rc;
+  }
 
 /* If we need to handle SNI or OCSP, do so */
 
 #ifdef EXIM_HAVE_OPENSSL_TLSEXT
 # ifndef DISABLE_OCSP
-  if (!(cbinfo->verify_stack = sk_X509_new_null()))
+  if (!(state->verify_stack = sk_X509_new_null()))
     {
     DEBUG(D_tls) debug_printf("failed to create stack for stapling verify\n");
     return FAIL;
@@ -2041,37 +2769,37 @@ if ((rc = tls_expand_session_files(ctx, cbinfo, errstr)) != OK)
 if (!host)             /* server */
   {
 # ifndef DISABLE_OCSP
-  /* We check u_ocsp.server.file, not server.response, because we care about if
+  /* We check u_ocsp.server.file, not server.olist, because we care about if
   the option exists, not what the current expansion might be, as SNI might
   change the certificate and OCSP file in use between now and the time the
   callback is invoked. */
-  if (cbinfo->u_ocsp.server.file)
+  if (state->u_ocsp.server.file)
     {
     SSL_CTX_set_tlsext_status_cb(ctx, tls_server_stapling_cb);
-    SSL_CTX_set_tlsext_status_arg(ctx, cbinfo);
+    SSL_CTX_set_tlsext_status_arg(ctx, state);
     }
 # endif
   /* We always do this, so that $tls_sni is available even if not used in
   tls_certificate */
   SSL_CTX_set_tlsext_servername_callback(ctx, tls_servername_cb);
-  SSL_CTX_set_tlsext_servername_arg(ctx, cbinfo);
+  SSL_CTX_set_tlsext_servername_arg(ctx, state);
   }
 # ifndef DISABLE_OCSP
 else                   /* client */
   if(ocsp_file)                /* wanting stapling */
     {
-    if (!(cbinfo->u_ocsp.client.verify_store = X509_STORE_new()))
+    if (!(state->u_ocsp.client.verify_store = X509_STORE_new()))
       {
       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, cbinfo);
+    SSL_CTX_set_tlsext_status_arg(ctx, state);
     }
 # endif
 #endif
 
-cbinfo->verify_cert_hostnames = NULL;
+state->verify_cert_hostnames = NULL;
 
 #ifdef EXIM_HAVE_EPHEM_RSA_KEX
 /* Set up the RSA callback */
@@ -2084,8 +2812,7 @@ The period appears to be also used for (server-generated) session tickets */
 SSL_CTX_set_timeout(ctx, ssl_session_timeout);
 DEBUG(D_tls) debug_printf("Initialized TLS\n");
 
-*cbp = cbinfo;
-*ctxp = ctx;
+*caller_state = state;
 
 return OK;
 }
@@ -2104,14 +2831,13 @@ Returns:    pointer to allocated string in perm-pool
 */
 
 static uschar *
-construct_cipher_name(SSL * ssl, int * bits)
+construct_cipher_name(SSL * ssl, const uschar * ver, int * bits)
 {
 int pool = store_pool;
 /* With OpenSSL 1.0.0a, 'c' needs to be const but the documentation doesn't
 yet reflect that.  It should be a safe change anyway, even 0.9.8 versions have
 the accessor functions use const in the prototype. */
 
-const uschar * ver = CUS SSL_get_version(ssl);
 const SSL_CIPHER * c = (const SSL_CIPHER *) SSL_get_current_cipher(ssl);
 uschar * s;
 
@@ -2142,6 +2868,21 @@ return cipher_stdname(id >> 8, id & 0xff);
 }
 
 
+static const uschar *
+tlsver_name(SSL * ssl)
+{
+uschar * s, * p;
+int pool = store_pool;
+
+store_pool = POOL_PERM;
+s = string_copy(US SSL_get_version(ssl));
+store_pool = pool;
+if ((p = Ustrchr(s, 'v')))     /* TLSv1.2 -> TLS1.2 */
+  for (;; p++) if (!(*p = p[1])) break;
+return CUS s;
+}
+
+
 static void
 peer_cert(SSL * ssl, tls_support * tlsp, uschar * peerdn, unsigned siz)
 {
@@ -2174,10 +2915,15 @@ if (tlsp->peercert)
     when it actually failed but we're in try-verify mode, due to us wanting the
     knowlege that it failed so needing to have the callback and forcing a
     permissive return.  If we don't force it, the TLS startup is failed.
-    Hence the verify_override bodge - though still a problem for resumption. */
+    The extra bit of information is set in verify_override in the cb, stashed
+    for resumption next to the TLS session, and used here. */
 
     if (!tlsp->verify_override)
-      tlsp->certificate_verified = SSL_get_verify_result(ssl) == X509_V_OK;
+      tlsp->certificate_verified =
+#ifdef SUPPORT_DANE
+       tlsp->dane_verified ||
+#endif
+       SSL_get_verify_result(ssl) == X509_V_OK;
     }
 }
 
@@ -2216,20 +2962,17 @@ repeated after a Server Name Indication.
 
 Arguments:
   sctx          SSL_CTX* to initialise
-  certs         certs file or NULL
+  certs         certs file, expanded
   crl           CRL file or NULL
   host          NULL in a server; the remote host in a client
-  optional      TRUE if called from a server for a host in tls_try_verify_hosts;
-                otherwise passed as FALSE
-  cert_vfy_cb  Callback function for certificate verification
   errstr       error string pointer
 
 Returns:        OK/DEFER/FAIL
 */
 
 static int
-setup_certs(SSL_CTX *sctx, uschar *certs, uschar *crl, host_item *host, BOOL optional,
-    int (*cert_vfy_cb)(int, X509_STORE_CTX *), uschar ** errstr)
+setup_certs(SSL_CTX *sctx, uschar *certs, uschar *crl, host_item *host,
+    uschar ** errstr)
 {
 uschar *expcerts, *expcrl;
 
@@ -2245,7 +2988,7 @@ if (expcerts && *expcerts)
   if (!SSL_CTX_set_default_verify_paths(sctx))
     return tls_error(US"SSL_CTX_set_default_verify_paths", host, NULL, errstr);
 
-  if (Ustrcmp(expcerts, "system") != 0)
+  if (Ustrcmp(expcerts, "system") != 0 && Ustrncmp(expcerts, "system,", 7) != 0)
     {
     struct stat statbuf;
 
@@ -2267,10 +3010,14 @@ if (expcerts && *expcerts)
        /* 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.
+*/
+
        if (  !host
           && statbuf.st_size > 0
-          && server_static_cbinfo->u_ocsp.server.file
-          && !chain_from_pem_file(file, server_static_cbinfo->verify_stack)
+          && state_server.u_ocsp.server.file
+          && !chain_from_pem_file(file, state_server.verify_stack)
           )
          {
          log_write(0, LOG_MAIN|LOG_PANIC,
@@ -2280,18 +3027,19 @@ if (expcerts && *expcerts)
 #endif
        }
 
-      /* If a certificate file is empty, the next function fails with an
+      /* If a certificate file is empty, the load function fails with an
       unhelpful error message. If we skip it, we get the correct behaviour (no
       certificates are recognized, but the error message is still misleading (it
       says no certificate was supplied).  But this is better. */
 
       if (  (!file || statbuf.st_size > 0)
          && !SSL_CTX_load_verify_locations(sctx, CS file, CS dir))
-       return tls_error(US"SSL_CTX_load_verify_locations", host, NULL, errstr);
+         return tls_error(US"SSL_CTX_load_verify_locations",
+                           host, NULL, errstr);
 
-      /* Load the list of CAs for which we will accept certs, for sending
-      to the client.  This is only for the one-file tls_verify_certificates
-      variant.
+      /* On the server load the list of CAs for which we will accept certs, for
+      sending to the client.  This is only for the one-file
+      tls_verify_certificates variant.
       If a list isn't loaded into the server, but some verify locations are set,
       the server end appears to make a wildcard request for client certs.
       Meanwhile, the client library as default behaviour *ignores* the list
@@ -2302,11 +3050,15 @@ if (expcerts && *expcerts)
       if (file)
        {
        STACK_OF(X509_NAME) * names = SSL_load_client_CA_file(CS file);
+       int i = sk_X509_NAME_num(names);
 
-       SSL_CTX_set_client_CA_list(sctx, names);
-       DEBUG(D_tls) debug_printf("Added %d certificate authorities.\n",
-                                   sk_X509_NAME_num(names));
+       if (!host) SSL_CTX_set_client_CA_list(sctx, names);
+       DEBUG(D_tls) debug_printf("Added %d additional certificate authorit%s\n",
+                                   i, i>1 ? "ies":"y");
        }
+      else
+       DEBUG(D_tls)
+         debug_printf("Added dir for additional certificate authorities\n");
       }
     }
 
@@ -2362,12 +3114,6 @@ if (expcerts && *expcerts)
     }
 
 #endif  /* OPENSSL_VERSION_NUMBER > 0x00907000L */
-
-  /* If verification is optional, don't fail if no certificate */
-
-  SSL_CTX_set_verify(sctx,
-    SSL_VERIFY_PEER | (optional ? 0 : SSL_VERIFY_FAIL_IF_NO_PEER_CERT),
-    cert_vfy_cb);
   }
 
 return OK;
@@ -2378,13 +3124,11 @@ return OK;
 /*************************************************
 *       Start a TLS session in a server          *
 *************************************************/
-
 /* This is called when Exim is running as a server, after having received
 the STARTTLS command. It must respond to that command, and then negotiate
 a TLS session.
 
 Arguments:
-  require_ciphers   allowed ciphers
   errstr           pointer to error message
 
 Returns:            OK on success
@@ -2394,11 +3138,13 @@ Returns:            OK on success
 */
 
 int
-tls_server_start(const uschar * require_ciphers, uschar ** errstr)
+tls_server_start(uschar ** errstr)
 {
 int rc;
 uschar * expciphers;
-tls_ext_ctx_cb * cbinfo;
+exim_openssl_state_st * dummy_statep;
+SSL_CTX * ctx;
+SSL * ssl;
 static uschar peerdn[256];
 
 /* Check for previous activation */
@@ -2413,16 +3159,13 @@ if (tls_in.active.sock >= 0)
 /* Initialize the SSL library. If it fails, it will already have logged
 the error. */
 
-rc = tls_init(&server_ctx, NULL, tls_dhparam, tls_certificate, tls_privatekey,
+rc = tls_init(NULL, NULL,
 #ifndef DISABLE_OCSP
-    tls_ocsp_file,     /*XXX stack*/
+    tls_ocsp_file,
 #endif
-    NULL, &server_static_cbinfo, &tls_in, errstr);
+    NULL, &dummy_statep, &tls_in, errstr);
 if (rc != OK) return rc;
-cbinfo = server_static_cbinfo;
-
-if (!expand_check(require_ciphers, US"tls_require_ciphers", &expciphers, errstr))
-  return FAIL;
+ctx = state_server.lib_state.lib_ctx;
 
 /* In OpenSSL, cipher components are separated by hyphens. In GnuTLS, they
 were historically separated by underscores. So that I can use either form in my
@@ -2433,13 +3176,16 @@ for TLS 1.3 .  Since we do not call it at present we get the default list:
 TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256
 */
 
-if (expciphers)
+if (state_server.lib_state.pri_string)
+  { DEBUG(D_tls) debug_printf("TLS: cipher list was preloaded\n"); }
+else 
   {
-  for (uschar * s = expciphers; *s; s++ ) if (*s == '_') *s = '-';
-  DEBUG(D_tls) debug_printf("required ciphers: %s\n", expciphers);
-  if (!SSL_CTX_set_cipher_list(server_ctx, CS expciphers))
-    return tls_error(US"SSL_CTX_set_cipher_list", NULL, NULL, errstr);
-  cbinfo->server_cipher_list = expciphers;
+  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 this is a host for which certificate verification is mandatory or
@@ -2452,37 +3198,48 @@ tls_in.dane_verified = FALSE;
 server_verify_callback_called = FALSE;
 
 if (verify_check_host(&tls_verify_hosts) == OK)
-  {
-  rc = setup_certs(server_ctx, tls_verify_certificates, tls_crl, NULL,
-                       FALSE, verify_callback_server, errstr);
-  if (rc != OK) return rc;
   server_verify_optional = FALSE;
-  }
 else if (verify_check_host(&tls_try_verify_hosts) == OK)
-  {
-  rc = setup_certs(server_ctx, tls_verify_certificates, tls_crl, NULL,
-                       TRUE, verify_callback_server, errstr);
-  if (rc != OK) return rc;
   server_verify_optional = TRUE;
+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);
+
+  if (state_server.lib_state.cabundle)
+    { DEBUG(D_tls) debug_printf("TLS: CA bundle for server was preloaded\n"); }
+  else
+    if ((rc = setup_certs(ctx, expcerts, tls_crl, NULL, errstr)) != OK)
+      return rc;
+
+  if (expcerts && *expcerts)
+    setup_cert_verify(ctx, server_verify_optional, verify_callback_server);
   }
+skip_certs: ;
 
-#ifdef EXPERIMENTAL_TLS_RESUME
-SSL_CTX_set_tlsext_ticket_key_cb(server_ctx, ticket_key_callback);
+#ifndef DISABLE_TLS_RESUME
+SSL_CTX_set_tlsext_ticket_key_cb(ctx, ticket_key_callback);
 /* despite working, appears to always return failure, so ignoring */
 #endif
 #ifdef OPENSSL_HAVE_NUM_TICKETS
-# ifdef EXPERIMENTAL_TLS_RESUME
-SSL_CTX_set_num_tickets(server_ctx, tls_in.host_resumable ? 1 : 0);
+# ifndef DISABLE_TLS_RESUME
+SSL_CTX_set_num_tickets(ctx, tls_in.host_resumable ? 1 : 0);
 # else
-SSL_CTX_set_num_tickets(server_ctx, 0);        /* send no TLS1.3 stateful-tickets */
+SSL_CTX_set_num_tickets(ctx, 0);       /* send no TLS1.3 stateful-tickets */
 # endif
 #endif
 
 
 /* Prepare for new connection */
 
-if (!(server_ssl = SSL_new(server_ctx)))
+if (!(ssl = SSL_new(ctx)))
   return tls_error(US"SSL_new", NULL, NULL, errstr);
+state_server.lib_state.lib_ssl = ssl;
 
 /* Warning: we used to SSL_clear(ssl) here, it was removed.
  *
@@ -2503,7 +3260,7 @@ make them disconnect. We need to have an explicit fflush() here, to force out
 the response. Other smtp_printf() calls do not need it, because in non-TLS
 mode, the fflush() happens when smtp_getc() is called. */
 
-SSL_set_session_id_context(server_ssl, sid_ctx, Ustrlen(sid_ctx));
+SSL_set_session_id_context(ssl, sid_ctx, Ustrlen(sid_ctx));
 if (!tls_in.on_connect)
   {
   smtp_printf("220 TLS go ahead\r\n", FALSE);
@@ -2513,60 +3270,111 @@ if (!tls_in.on_connect)
 /* Now negotiate the TLS session. We put our own timer on it, since it seems
 that the OpenSSL library doesn't. */
 
-SSL_set_wfd(server_ssl, fileno(smtp_out));
-SSL_set_rfd(server_ssl, fileno(smtp_in));
-SSL_set_accept_state(server_ssl);
+SSL_set_wfd(ssl, fileno(smtp_out));
+SSL_set_rfd(ssl, fileno(smtp_in));
+SSL_set_accept_state(ssl);
 
 DEBUG(D_tls) debug_printf("Calling SSL_accept\n");
 
+ERR_clear_error();
 sigalrm_seen = FALSE;
 if (smtp_receive_timeout > 0) ALARM(smtp_receive_timeout);
-rc = SSL_accept(server_ssl);
+rc = SSL_accept(ssl);
 ALARM_CLR(0);
 
 if (rc <= 0)
   {
-  (void) tls_error(US"SSL_accept", NULL, sigalrm_seen ? US"timed out" : NULL, errstr);
-  return FAIL;
+  int error = SSL_get_error(ssl, rc);
+  switch(error)
+    {
+    case SSL_ERROR_NONE:
+      break;
+
+    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);
+
+      if (SSL_get_shutdown(ssl) == SSL_RECEIVED_SHUTDOWN)
+           SSL_shutdown(ssl);
+
+      tls_close(NULL, TLS_NO_SHUTDOWN);
+      return FAIL;
+
+    /* Handle genuine errors */
+    case SSL_ERROR_SSL:
+      {
+      uschar * s = US"SSL_accept";
+      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);
+      return FAIL;
+      }
+
+    default:
+      DEBUG(D_tls) debug_printf("Got SSL error %d\n", error);
+      if (error == SSL_ERROR_SYSCALL)
+       {
+       if (!errno)
+         {
+         *errstr = US"SSL_accept: TCP connection closed by peer";
+         return FAIL;
+         }
+       DEBUG(D_tls) debug_printf(" - syscall %s\n", strerror(errno));
+       }
+      (void) tls_error(US"SSL_accept", NULL,
+                     sigalrm_seen ? US"timed out"
+                     : ERR_peek_error() ? NULL : string_sprintf("ret %d", error),
+                     errstr);
+      return FAIL;
+    }
   }
 
 DEBUG(D_tls) debug_printf("SSL_accept was successful\n");
 ERR_clear_error();     /* Even success can leave errors in the stack. Seen with
                        anon-authentication ciphersuite negotiated. */
 
-#ifdef EXPERIMENTAL_TLS_RESUME
-if (SSL_session_reused(server_ssl))
+#ifndef DISABLE_TLS_RESUME
+if (SSL_session_reused(ssl))
   {
   tls_in.resumption |= RESUME_USED;
   DEBUG(D_tls) debug_printf("Session reused\n");
   }
 #endif
 
-/* TLS has been set up. Adjust the input functions to read via TLS,
-and initialize things. */
+/* TLS has been set up. Record data for the connection,
+adjust the input functions to read via TLS, and initialize things. */
 
-peer_cert(server_ssl, &tls_in, peerdn, sizeof(peerdn));
+#ifdef SSL_get_extms_support
+tls_in.ext_master_secret = SSL_get_extms_support(ssl) == 1;
+#endif
+peer_cert(ssl, &tls_in, peerdn, sizeof(peerdn));
 
-tls_in.cipher = construct_cipher_name(server_ssl, &tls_in.bits);
-tls_in.cipher_stdname = cipher_stdname_ssl(server_ssl);
+tls_in.ver = tlsver_name(ssl);
+tls_in.cipher = construct_cipher_name(ssl, tls_in.ver, &tls_in.bits);
+tls_in.cipher_stdname = cipher_stdname_ssl(ssl);
 
 DEBUG(D_tls)
   {
   uschar buf[2048];
-  if (SSL_get_shared_ciphers(server_ssl, CS buf, sizeof(buf)))
+  if (SSL_get_shared_ciphers(ssl, CS buf, sizeof(buf)))
     debug_printf("Shared ciphers: %s\n", buf);
 
 #ifdef EXIM_HAVE_OPENSSL_KEYLOG
   {
   BIO * bp = BIO_new_fp(debug_file, BIO_NOCLOSE);
-  SSL_SESSION_print_keylog(bp, SSL_get_session(server_ssl));
+  SSL_SESSION_print_keylog(bp, SSL_get_session(ssl));
   BIO_free(bp);
   }
 #endif
 
 #ifdef EXIM_HAVE_SESSION_TICKET
   {
-  SSL_SESSION * ss = SSL_get_session(server_ssl);
+  SSL_SESSION * ss = SSL_get_session(ssl);
   if (SSL_SESSION_has_ticket(ss))      /* 1.1.0 */
     debug_printf("The session has a ticket, life %lu seconds\n",
       SSL_SESSION_get_ticket_lifetime_hint(ss));
@@ -2576,10 +3384,24 @@ DEBUG(D_tls)
 
 /* Record the certificate we presented */
   {
-  X509 * crt = SSL_get_certificate(server_ssl);
+  X509 * crt = SSL_get_certificate(ssl);
   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);
+  }
+
 /* Only used by the server-side tls (tls_in), including tls_getc.
    Client-side (tls_out) reads (seem to?) go via
    smtp_read_response()/ip_recv().
@@ -2607,16 +3429,21 @@ return OK;
 
 static int
 tls_client_basic_ctx_init(SSL_CTX * ctx,
-    host_item * host, smtp_transport_options_block * ob, tls_ext_ctx_cb * cbinfo,
+    host_item * host, smtp_transport_options_block * ob, exim_openssl_state_st * state,
     uschar ** errstr)
 {
 int rc;
-/* stick to the old behaviour for compatibility if tls_verify_certificates is
-   set but both tls_verify_hosts and tls_try_verify_hosts is not set. Check only
-   the specified host patterns if one of them is defined */
 
-if (  (  !ob->tls_verify_hosts
-      && (!ob->tls_try_verify_hosts || !*ob->tls_try_verify_hosts)
+/* Back-compatible old behaviour if tls_verify_certificates is set but both
+tls_verify_hosts and tls_try_verify_hosts are not set. Check only the specified
+host patterns if one of them is set with content. */
+
+if (  (  (  !ob->tls_verify_hosts || !ob->tls_verify_hosts
+        || Ustrcmp(ob->tls_try_verify_hosts, ":") == 0
+        )
+      && (  !ob->tls_try_verify_hosts || !*ob->tls_try_verify_hosts
+        || Ustrcmp(ob->tls_try_verify_hosts, ":") == 0
+         )
       )
    || verify_check_given_host(CUSS &ob->tls_verify_hosts, host) == OK
    )
@@ -2626,21 +3453,33 @@ else if (verify_check_given_host(CUSS &ob->tls_try_verify_hosts, host) == OK)
 else
   return OK;
 
-if ((rc = setup_certs(ctx, ob->tls_verify_certificates,
-      ob->tls_crl, host, client_verify_optional, verify_callback_client,
-      errstr)) != OK)
-  return rc;
+  {
+  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);
+
+  if (state->lib_state.cabundle)
+    { DEBUG(D_tls) debug_printf("TLS: CA bundle was preloaded\n"); }
+  else
+    if ((rc = setup_certs(ctx, expcerts, ob->tls_crl, host, errstr)) != OK)
+      return rc;
+
+  if (expcerts && *expcerts)
+    setup_cert_verify(ctx, client_verify_optional, verify_callback_client);
+  }
 
 if (verify_check_given_host(CUSS &ob->tls_verify_cert_hostnames, host) == OK)
   {
-  cbinfo->verify_cert_hostnames =
+  state->verify_cert_hostnames =
 #ifdef SUPPORT_I18N
-    string_domain_utf8_to_alabel(host->name, NULL);
+    string_domain_utf8_to_alabel(host->certname, NULL);
 #else
-    host->name;
+    host->certname;
 #endif
   DEBUG(D_tls) debug_printf("Cert hostname to check: \"%s\"\n",
-                   cbinfo->verify_cert_hostnames);
+                   state->verify_cert_hostnames);
   }
 return OK;
 }
@@ -2703,7 +3542,7 @@ return DEFER;
 
 
 
-#ifdef EXPERIMENTAL_TLS_RESUME
+#ifndef DISABLE_TLS_RESUME
 /* On the client, get any stashed session for the given IP from hints db
 and apply it to the ssl-connection for attempted resumption. */
 
@@ -2719,7 +3558,7 @@ if (tlsp->host_resumable)
 
   tlsp->resumption |= RESUME_CLIENT_REQUESTED;
   DEBUG(D_tls) debug_printf("checking for resumable session for %s\n", key);
-  if ((dbm_file = dbfn_open(US"tls", O_RDONLY, &dbblock, FALSE, FALSE)))
+  if ((dbm_file = dbfn_open(US"tls", O_RDWR, &dbblock, FALSE, FALSE)))
     {
     /* key for the db is the IP */
     if ((dt = dbfn_read_with_length(dbm_file, key, &len)))
@@ -2737,12 +3576,14 @@ 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))
        {
        DEBUG(D_tls) debug_printf("session expired\n");
        dbfn_delete(dbm_file, key);
        }
+#endif
       else if (!SSL_set_session(ssl, ss))
        {
        DEBUG(D_tls)
@@ -2756,6 +3597,8 @@ if (tlsp->host_resumable)
        {
        DEBUG(D_tls) debug_printf("good session\n");
        tlsp->resumption |= RESUME_CLIENT_SUGGESTED;
+       tlsp->verify_override = dt->verify_override;
+       tlsp->ocsp = dt->ocsp;
        }
       }
     else
@@ -2771,7 +3614,7 @@ if (tlsp->host_resumable)
 static int
 tls_save_session_cb(SSL * ssl, SSL_SESSION * ss)
 {
-tls_ext_ctx_cb * cbinfo = SSL_get_ex_data(ssl, tls_exdata_idx);
+exim_openssl_state_st * cbinfo = SSL_get_ex_data(ssl, tls_exdata_idx);
 tls_support * tlsp;
 
 DEBUG(D_tls) debug_printf("tls_save_session_cb\n");
@@ -2784,14 +3627,16 @@ 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);
+  dbdata_tls_session * dt = store_get(dlen, TRUE);
   uschar * s = dt->session;
   open_db dbblock, * dbm_file;
 
   DEBUG(D_tls) debug_printf("session is resumable\n");
   tlsp->resumption |= RESUME_SERVER_TICKET;    /* server gave us a ticket */
 
-  len = i2d_SSL_SESSION(ss, &s);       /* s gets bumped to end */
+  dt->verify_override = tlsp->verify_override;
+  dt->ocsp = tlsp->ocsp;
+  (void) i2d_SSL_SESSION(ss, &s);              /* s gets bumped to end */
 
   if ((dbm_file = dbfn_open(US"tls", O_RDWR, &dbblock, FALSE, FALSE)))
     {
@@ -2835,12 +3680,12 @@ if (tlsp->host_resumable)
   SSL_clear_options(ssl, SSL_OP_NO_TICKET);
 
   tls_exdata_idx = SSL_get_ex_new_index(0, 0, 0, 0, 0);
-  if (!SSL_set_ex_data(ssl, tls_exdata_idx, client_static_cbinfo))
+  if (!SSL_set_ex_data(ssl, tls_exdata_idx, client_static_state))
     {
     tls_error(US"set ex_data", host, NULL, errstr);
     return FALSE;
     }
-  debug_printf("tls_exdata_idx %d cbinfo %p\n", tls_exdata_idx, client_static_cbinfo);
+  debug_printf("tls_exdata_idx %d cbinfo %p\n", tls_exdata_idx, client_static_state);
   }
 
 tlsp->resumption = RESUME_SUPPORTED;
@@ -2859,7 +3704,7 @@ if (SSL_session_reused(exim_client_ctx->ssl))
   tlsp->resumption |= RESUME_USED;
   }
 }
-#endif /* EXPERIMENTAL_TLS_RESUME */
+#endif /* !DISABLE_TLS_RESUME */
 
 
 /*************************************************
@@ -2900,7 +3745,7 @@ BOOL require_ocsp = FALSE;
 
 rc = store_pool;
 store_pool = POOL_PERM;
-exim_client_ctx = store_get(sizeof(exim_openssl_client_tls_ctx));
+exim_client_ctx = store_get(sizeof(exim_openssl_client_tls_ctx), FALSE);
 exim_client_ctx->corked = NULL;
 store_pool = rc;
 
@@ -2911,6 +3756,7 @@ 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'
@@ -2936,14 +3782,15 @@ tlsp->tlsa_usage = 0;
   }
 #endif
 
-rc = tls_init(&exim_client_ctx->ctx, host, NULL,
-    ob->tls_certificate, ob->tls_privatekey,
+rc = tls_init(host, ob,
 #ifndef DISABLE_OCSP
     (void *)(long)request_ocsp,
 #endif
-    cookie, &client_static_cbinfo, tlsp, errstr);
+    cookie, &client_static_state, tlsp, errstr);
 if (rc != OK) return FALSE;
 
+exim_client_ctx->ctx = client_static_state->lib_state.lib_ctx;
+
 tlsp->certificate_verified = FALSE;
 client_verify_callback_called = FALSE;
 
@@ -3004,11 +3851,11 @@ else
 
 #endif
 
-  if (tls_client_basic_ctx_init(exim_client_ctx->ctx, host, ob,
-       client_static_cbinfo, errstr) != OK)
-    return FALSE;
+if (tls_client_basic_ctx_init(exim_client_ctx->ctx, host, ob,
+      client_static_state, errstr) != OK)
+  return FALSE;
 
-#ifdef EXPERIMENTAL_TLS_RESUME
+#ifndef DISABLE_TLS_RESUME
 tls_client_ctx_resume_prehandshake(exim_client_ctx, tlsp, ob, host);
 #endif
 
@@ -3020,12 +3867,6 @@ if (!(exim_client_ctx->ssl = SSL_new(exim_client_ctx->ctx)))
   }
 SSL_set_session_id_context(exim_client_ctx->ssl, sid_ctx, Ustrlen(sid_ctx));
 
-#ifdef EXPERIMENTAL_TLS_RESUME
-if (!tls_client_ssl_resume_prehandshake(exim_client_ctx->ssl, tlsp, host,
-      errstr))
-  return FALSE;
-#endif
-
 SSL_set_fd(exim_client_ctx->ssl, cctx->sock);
 SSL_set_connect_state(exim_client_ctx->ssl);
 
@@ -3080,13 +3921,19 @@ if (request_ocsp)
 if (request_ocsp)
   {
   SSL_set_tlsext_status_type(exim_client_ctx->ssl, TLSEXT_STATUSTYPE_ocsp);
-  client_static_cbinfo->u_ocsp.client.verify_required = require_ocsp;
+  client_static_state->u_ocsp.client.verify_required = require_ocsp;
   tlsp->ocsp = OCSP_NOT_RESP;
   }
 #endif
 
+#ifndef DISABLE_TLS_RESUME
+if (!tls_client_ssl_resume_prehandshake(exim_client_ctx->ssl, tlsp, host,
+      errstr))
+  return FALSE;
+#endif
+
 #ifndef DISABLE_EVENT
-client_static_cbinfo->event_action = tb ? tb->event_action : NULL;
+client_static_state->event_action = tb ? tb->event_action : NULL;
 #endif
 
 /* There doesn't seem to be a built-in timeout on connection. */
@@ -3120,13 +3967,17 @@ DEBUG(D_tls)
 #endif
   }
 
-#ifdef EXPERIMENTAL_TLS_RESUME
+#ifndef DISABLE_TLS_RESUME
 tls_client_resume_posthandshake(exim_client_ctx, tlsp);
 #endif
 
+#ifdef SSL_get_extms_support
+tlsp->ext_master_secret = SSL_get_extms_support(exim_client_ctx->ssl) == 1;
+#endif
 peer_cert(exim_client_ctx->ssl, tlsp, peerdn, sizeof(peerdn));
 
-tlsp->cipher = construct_cipher_name(exim_client_ctx->ssl, &tlsp->bits);
+tlsp->ver = tlsver_name(exim_client_ctx->ssl);
+tlsp->cipher = construct_cipher_name(exim_client_ctx->ssl, tlsp->ver, &tlsp->bits);
 tlsp->cipher_stdname = cipher_stdname_ssl(exim_client_ctx->ssl);
 
 /* Record the certificate we presented */
@@ -3135,6 +3986,20 @@ tlsp->cipher_stdname = cipher_stdname_ssl(exim_client_ctx->ssl);
   tlsp->ourcert = crt ? X509_dup(crt) : NULL;
   }
 
+/*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);
+  }
+
 tlsp->active.sock = cctx->sock;
 tlsp->active.tls_ctx = exim_client_ctx;
 cctx->tls_ctx = exim_client_ctx;
@@ -3148,16 +4013,18 @@ return TRUE;
 static BOOL
 tls_refill(unsigned lim)
 {
+SSL * ssl = state_server.lib_state.lib_ssl;
 int error;
 int inbytes;
 
-DEBUG(D_tls) debug_printf("Calling SSL_read(%p, %p, %u)\n", server_ssl,
+DEBUG(D_tls) debug_printf("Calling SSL_read(%p, %p, %u)\n", ssl,
   ssl_xfer_buffer, ssl_xfer_buffer_size);
 
+ERR_clear_error();
 if (smtp_receive_timeout > 0) ALARM(smtp_receive_timeout);
-inbytes = SSL_read(server_ssl, CS ssl_xfer_buffer,
+inbytes = SSL_read(ssl, CS ssl_xfer_buffer,
                  MIN(ssl_xfer_buffer_size, lim));
-error = SSL_get_error(server_ssl, inbytes);
+error = SSL_get_error(ssl, inbytes);
 if (smtp_receive_timeout > 0) ALARM_CLR(0);
 
 if (had_command_timeout)               /* set by signal handler */
@@ -3181,32 +4048,10 @@ switch(error)
   case SSL_ERROR_ZERO_RETURN:
     DEBUG(D_tls) debug_printf("Got SSL_ERROR_ZERO_RETURN\n");
 
-    receive_getc = smtp_getc;
-    receive_getbuf = smtp_getbuf;
-    receive_get_cache = smtp_get_cache;
-    receive_ungetc = smtp_ungetc;
-    receive_feof = smtp_feof;
-    receive_ferror = smtp_ferror;
-    receive_smtp_buffered = smtp_buffered;
-
-    if (SSL_get_shutdown(server_ssl) == SSL_RECEIVED_SHUTDOWN)
-         SSL_shutdown(server_ssl);
-
-#ifndef DISABLE_OCSP
-    sk_X509_pop_free(server_static_cbinfo->verify_stack, X509_free);
-    server_static_cbinfo->verify_stack = NULL;
-#endif
-    SSL_free(server_ssl);
-    SSL_CTX_free(server_ctx);
-    server_ctx = NULL;
-    server_ssl = NULL;
-    tls_in.active.sock = -1;
-    tls_in.active.tls_ctx = NULL;
-    tls_in.bits = 0;
-    tls_in.cipher = NULL;
-    tls_in.peerdn = NULL;
-    tls_in.sni = NULL;
+    if (SSL_get_shutdown(ssl) == SSL_RECEIVED_SHUTDOWN)
+         SSL_shutdown(ssl);
 
+    tls_close(NULL, TLS_NO_SHUTDOWN);
     return FALSE;
 
   /* Handle genuine errors */
@@ -3295,7 +4140,8 @@ if (n > 0)
 BOOL
 tls_could_read(void)
 {
-return ssl_xfer_buffer_lwm < ssl_xfer_buffer_hwm || SSL_pending(server_ssl) > 0;
+return ssl_xfer_buffer_lwm < ssl_xfer_buffer_hwm
+      || SSL_pending(state_server.lib_state.lib_ssl) > 0;
 }
 
 
@@ -3318,13 +4164,15 @@ Only used by the client-side TLS.
 int
 tls_read(void * ct_ctx, uschar *buff, size_t len)
 {
-SSL * ssl = ct_ctx ? ((exim_openssl_client_tls_ctx *)ct_ctx)->ssl : server_ssl;
+SSL * ssl = ct_ctx ? ((exim_openssl_client_tls_ctx *)ct_ctx)->ssl
+                 : state_server.lib_state.lib_ssl;
 int inbytes;
 int error;
 
 DEBUG(D_tls) debug_printf("Calling SSL_read(%p, %p, %u)\n", ssl,
   buff, (unsigned int)len);
 
+ERR_clear_error();
 inbytes = SSL_read(ssl, CS buff, len);
 error = SSL_get_error(ssl, inbytes);
 
@@ -3357,16 +4205,18 @@ Arguments:
 Returns:    the number of bytes after a successful write,
             -1 after a failed write
 
-Used by both server-side and client-side TLS.
+Used by both server-side and client-side TLS.  Calling with len zero and more unset
+will flush buffered writes; buff can be null for this case.
 */
 
 int
-tls_write(void * ct_ctx, const uschar *buff, size_t len, BOOL more)
+tls_write(void * ct_ctx, const uschar * buff, size_t len, BOOL more)
 {
 size_t olen = len;
 int outbytes, error;
 SSL * ssl = ct_ctx
-  ? ((exim_openssl_client_tls_ctx *)ct_ctx)->ssl : server_ssl;
+  ? ((exim_openssl_client_tls_ctx *)ct_ctx)->ssl
+  : state_server.lib_state.lib_ssl;
 static gstring * server_corked = NULL;
 gstring ** corkedp = ct_ctx
   ? &((exim_openssl_client_tls_ctx *)ct_ctx)->corked : &server_corked;
@@ -3385,16 +4235,18 @@ context for the stashed information. */
 a store reset there, so use POOL_PERM. */
 /* + if CHUNKING, cmds EHLO,MAIL,RCPT(s),BDAT */
 
-if ((more || corked))
+if (more || corked)
   {
-#ifdef EXPERIMENTAL_PIPE_CONNECT
+  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);
 
-#ifdef EXPERIMENTAL_PIPE_CONNECT
+#ifndef DISABLE_PIPE_CONNECT
   store_pool = save_pool;
 #endif
 
@@ -3411,29 +4263,37 @@ if ((more || corked))
 for (int left = len; left > 0;)
   {
   DEBUG(D_tls) debug_printf("SSL_write(%p, %p, %d)\n", ssl, buff, left);
+  ERR_clear_error();
   outbytes = SSL_write(ssl, CS buff, left);
   error = SSL_get_error(ssl, outbytes);
   DEBUG(D_tls) debug_printf("outbytes=%d error=%d\n", outbytes, error);
   switch (error)
     {
+    case SSL_ERROR_NONE:       /* the usual case */
+      left -= outbytes;
+      buff += outbytes;
+      break;
+
     case SSL_ERROR_SSL:
       ERR_error_string_n(ERR_get_error(), ssl_errstring, sizeof(ssl_errstring));
       log_write(0, LOG_MAIN, "TLS error (SSL_write): %s", ssl_errstring);
       return -1;
 
-    case SSL_ERROR_NONE:
-      left -= outbytes;
-      buff += outbytes;
-      break;
-
     case SSL_ERROR_ZERO_RETURN:
       log_write(0, LOG_MAIN, "SSL channel closed on write");
       return -1;
 
     case SSL_ERROR_SYSCALL:
-      log_write(0, LOG_MAIN, "SSL_write: (from %s) syscall: %s",
-       sender_fullhost ? sender_fullhost : US"<unknown>",
-       strerror(errno));
+      if (ct_ctx || errno != ECONNRESET || !f.smtp_in_quit)
+       log_write(0, LOG_MAIN, "SSL_write: (from %s) syscall: %s",
+         sender_fullhost ? sender_fullhost : US"<unknown>",
+         strerror(errno));
+      else if (LOGGING(protocol_detail))
+       log_write(0, LOG_MAIN, "[%s] after QUIT, client reset TCP before"
+         " SMTP response and TLS close\n", sender_host_address);
+      else
+       DEBUG(D_tls) debug_printf("[%s] SSL_write: after QUIT,"
+         " client reset TCP before TLS close\n", sender_host_address);
       return -1;
 
     default:
@@ -3468,8 +4328,7 @@ void
 tls_close(void * ct_ctx, int shutdown)
 {
 exim_openssl_client_tls_ctx * o_ctx = ct_ctx;
-SSL_CTX **ctxp = o_ctx ? &o_ctx->ctx : &server_ctx;
-SSL **sslp =     o_ctx ? &o_ctx->ssl : &server_ssl;
+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 */
@@ -3495,17 +4354,26 @@ if (shutdown)
     }
   }
 
-#ifndef DISABLE_OCSP
 if (!o_ctx)            /* server side */
   {
-  sk_X509_pop_free(server_static_cbinfo->verify_stack, X509_free);
-  server_static_cbinfo->verify_stack = NULL;
-  }
+#ifndef DISABLE_OCSP
+  sk_X509_pop_free(state_server.verify_stack, X509_free);
+  state_server.verify_stack = NULL;
 #endif
 
-SSL_CTX_free(*ctxp);
+  receive_getc =       smtp_getc;
+  receive_getbuf =     smtp_getbuf;
+  receive_get_cache =  smtp_get_cache;
+  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 */
+  }
+
 SSL_free(*sslp);
-*ctxp = NULL;
 *sslp = NULL;
 *fdp = -1;
 }
@@ -3529,18 +4397,7 @@ tls_validate_require_cipher(void)
 SSL_CTX *ctx;
 uschar *s, *expciphers, *err;
 
-/* this duplicates from tls_init(), we need a better "init just global
-state, for no specific purpose" singleton function of our own */
-
-#ifdef EXIM_NEED_OPENSSL_INIT
-SSL_load_error_strings();
-OpenSSL_add_ssl_algorithms();
-#endif
-#if (OPENSSL_VERSION_NUMBER >= 0x0090800fL) && !defined(OPENSSL_NO_SHA256)
-/* SHA256 is becoming ever more popular. This makes sure it gets added to the
-list of available digests. */
-EVP_add_digest(EVP_sha256());
-#endif
+tls_openssl_init();
 
 if (!(tls_require_ciphers && *tls_require_ciphers))
   return NULL;
@@ -3558,28 +4415,20 @@ while (*s != 0) { if (*s == '_') *s = '-'; s++; }
 
 err = NULL;
 
-#ifdef EXIM_HAVE_OPENSSL_TLS_METHOD
-if (!(ctx = SSL_CTX_new(TLS_server_method())))
-#else
-if (!(ctx = SSL_CTX_new(SSLv23_server_method())))
-#endif
+if (lib_ctx_new(&ctx, NULL, &err) == OK)
   {
-  ERR_error_string_n(ERR_get_error(), ssl_errstring, sizeof(ssl_errstring));
-  return string_sprintf("SSL_CTX_new() failed: %s", ssl_errstring);
-  }
+  DEBUG(D_tls)
+    debug_printf("tls_require_ciphers expands to \"%s\"\n", expciphers);
 
-DEBUG(D_tls)
-  debug_printf("tls_require_ciphers expands to \"%s\"\n", expciphers);
+  if (!SSL_CTX_set_cipher_list(ctx, CS expciphers))
+    {
+    ERR_error_string_n(ERR_get_error(), ssl_errstring, sizeof(ssl_errstring));
+    err = string_sprintf("SSL_CTX_set_cipher_list(%s) failed: %s",
+                       expciphers, ssl_errstring);
+    }
 
-if (!SSL_CTX_set_cipher_list(ctx, CS expciphers))
-  {
-  ERR_error_string_n(ERR_get_error(), ssl_errstring, sizeof(ssl_errstring));
-  err = string_sprintf("SSL_CTX_set_cipher_list(%s) failed: %s",
-                     expciphers, ssl_errstring);
+  SSL_CTX_free(ctx);
   }
-
-SSL_CTX_free(ctx);
-
 return err;
 }
 
@@ -3768,7 +4617,7 @@ BOOL
 tls_openssl_options_parse(uschar *option_spec, long *results)
 {
 long result, item;
-uschar *end;
+uschar * exp, * end;
 uschar keep_c;
 BOOL adding, item_parsed;
 
@@ -3776,7 +4625,7 @@ BOOL adding, item_parsed;
 result = SSL_OP_NO_TICKET;
 
 /* Prior to 4.80 we or'd in SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS; removed
- * from default because it increases BEAST susceptibility. */
+from default because it increases BEAST susceptibility. */
 #ifdef SSL_OP_NO_SSLv2
 result |= SSL_OP_NO_SSLv2;
 #endif
@@ -3786,6 +4635,9 @@ result |= SSL_OP_NO_SSLv3;
 #ifdef SSL_OP_SINGLE_DH_USE
 result |= SSL_OP_SINGLE_DH_USE;
 #endif
+#ifdef SSL_OP_NO_RENEGOTIATION
+result |= SSL_OP_NO_RENEGOTIATION;
+#endif
 
 if (!option_spec)
   {
@@ -3793,7 +4645,10 @@ if (!option_spec)
   return TRUE;
   }
 
-for (uschar * s = option_spec; *s; /**/)
+if (!expand_check(option_spec, US"openssl_options", &exp, &end))
+  return FALSE;
+
+for (uschar * s = exp; *s; /**/)
   {
   while (isspace(*s)) ++s;
   if (*s == '\0')
@@ -3815,7 +4670,7 @@ for (uschar * s = option_spec; *s; /**/)
     DEBUG(D_tls) debug_printf("openssl option setting unrecognised: \"%s\"\n", s);
     return FALSE;
     }
-  DEBUG(D_tls) debug_printf("openssl option, %s %8lx: %lx (%s)\n",
+  DEBUG(D_tls) debug_printf("openssl option, %s %08lx: %08lx (%s)\n",
       adding ? "adding to    " : "removing from", result, item, s);
   if (adding)
     result |= item;