TLS: variables $tls_(in,out)_tlsver
[exim.git] / src / src / tls-openssl.c
index c40af65c743198e38243ef3423aa3a3de809fcf3..7a82e1d554176ca1f8adb03c4c15348aefdd208f 100644 (file)
@@ -46,6 +46,7 @@ functions from the OpenSSL library. */
 #endif
 #if OPENSSL_VERSION_NUMBER >= 0x10100000L
 # define EXIM_HAVE_OCSP_RESP_COUNT
 #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
 #else
 # define EXIM_HAVE_EPHEM_RSA_KEX
 # define EXIM_HAVE_RAND_PSEUDO
@@ -74,6 +75,7 @@ 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_OPENSSL_CIPHER_GET_ID
 #  define EXIM_HAVE_SESSION_TICKET
 #  define EXIM_HAVE_OPESSL_TRACE
+#  define EXIM_HAVE_OPESSL_GET0_SERIAL
 # else
 #  define EXIM_NEED_OPENSSL_INIT
 # endif
 # else
 #  define EXIM_NEED_OPENSSL_INIT
 # endif
@@ -148,7 +150,7 @@ Plus SSL_OP_NO_TLSv1_3 for 1.1.2-dev
 static exim_openssl_option exim_openssl_options[] = {
 /* KEEP SORTED ALPHABETICALLY! */
 #ifdef SSL_OP_ALL
 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_UNSAFE_LEGACY_RENEGOTIATION
   { US"allow_unsafe_legacy_renegotiation", SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION },
 #endif
 #ifdef SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION
   { US"allow_unsafe_legacy_renegotiation", SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION },
@@ -261,6 +263,9 @@ for (struct exim_openssl_option * o = exim_openssl_options;
 # ifdef EXPERIMENTAL_TLS_RESUME
 builtin_macro_create_var(US"_RESUME_DECODE", RESUME_DECODE_STRING );
 # endif
 # ifdef EXPERIMENTAL_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
 }
 #else
 
 }
 #else
 
@@ -315,13 +320,18 @@ static SSL_CTX *server_sni = NULL;
 
 static char ssl_errstring[256];
 
 
 static char ssl_errstring[256];
 
-static int  ssl_session_timeout = 3600;
+static int  ssl_session_timeout = 7200;                /* Two hours */
 static BOOL client_verify_optional = FALSE;
 static BOOL server_verify_optional = FALSE;
 
 static BOOL reexpand_tls_files_for_sni = FALSE;
 
 
 static BOOL client_verify_optional = FALSE;
 static BOOL server_verify_optional = FALSE;
 
 static BOOL reexpand_tls_files_for_sni = FALSE;
 
 
+typedef struct ocsp_resp {
+  struct ocsp_resp *   next;
+  OCSP_RESPONSE *      resp;
+} ocsp_resplist;
+
 typedef struct tls_ext_ctx_cb {
   tls_support * tlsp;
   uschar *certificate;
 typedef struct tls_ext_ctx_cb {
   tls_support * tlsp;
   uschar *certificate;
@@ -332,8 +342,8 @@ typedef struct tls_ext_ctx_cb {
   union {
     struct {
       uschar        *file;
   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 */
     } server;
     struct {
       X509_STORE    *verify_store;     /* non-null if status requested */
@@ -526,6 +536,7 @@ if (ev)
       }
     DEBUG(D_tls) debug_printf("Event-action verify failure overridden "
       "(host in tls_try_verify_hosts)\n");
       }
     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;
     }
   X509_free(tlsp->peercert);
   tlsp->peercert = old_cert;
@@ -585,6 +596,7 @@ if (!X509_NAME_oneline(X509_get_subject_name(cert), CS dn, sizeof(dn)))
   }
 dn[sizeof(dn)-1] = '\0';
 
   }
 dn[sizeof(dn)-1] = '\0';
 
+tlsp->verify_override = FALSE;
 if (preverify_ok == 0)
   {
   uschar * extra = verify_mode ? string_sprintf(" (during %c-verify for [%s])",
 if (preverify_ok == 0)
   {
   uschar * extra = verify_mode ? string_sprintf(" (during %c-verify for [%s])",
@@ -603,6 +615,7 @@ if (preverify_ok == 0)
     }
   DEBUG(D_tls) debug_printf("SSL verify failure overridden (host in "
     "tls_try_verify_hosts)\n");
     }
   DEBUG(D_tls) debug_printf("SSL verify failure overridden (host in "
     "tls_try_verify_hosts)\n");
+  tlsp->verify_override = TRUE;
   }
 
 else if (depth != 0)
   }
 
 else if (depth != 0)
@@ -679,8 +692,9 @@ else
          tlsp->peercert = X509_dup(cert);      /* record failing cert */
        return 0;                               /* reject */
        }
          tlsp->peercert = X509_dup(cert);      /* record failing cert */
        return 0;                               /* reject */
        }
-      DEBUG(D_tls) debug_printf("SSL verify failure overridden (host in "
+      DEBUG(D_tls) debug_printf("SSL verify name failure overridden (host in "
        "tls_try_verify_hosts)\n");
        "tls_try_verify_hosts)\n");
+      tlsp->verify_override = TRUE;
       }
     }
 
       }
     }
 
@@ -691,7 +705,6 @@ else
 
   DEBUG(D_tls) debug_printf("SSL%s verify ok: depth=0 SN=%s\n",
     *calledp ? "" : " authenticated", dn);
 
   DEBUG(D_tls) debug_printf("SSL%s verify ok: depth=0 SN=%s\n",
     *calledp ? "" : " authenticated", dn);
-  if (!*calledp) tlsp->certificate_verified = TRUE;
   *calledp = TRUE;
   }
 
   *calledp = TRUE;
   }
 
@@ -748,7 +761,7 @@ DEBUG(D_tls) debug_printf("verify_callback_client_dane: %s depth %d %s\n",
 
 if (preverify_ok == 1)
   {
 
 if (preverify_ok == 1)
   {
-  tls_out.dane_verified = tls_out.certificate_verified = TRUE;
+  tls_out.dane_verified = TRUE;
 #ifndef DISABLE_OCSP
   if (client_static_cbinfo->u_ocsp.client.verify_store)
     {  /* client, wanting stapling  */
 #ifndef DISABLE_OCSP
   if (client_static_cbinfo->u_ocsp.client.verify_store)
     {  /* client, wanting stapling  */
@@ -909,7 +922,7 @@ if (enc)
   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);
   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));
+  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),
 
   /*XXX will want these dependent on the ssl session strength */
   HMAC_Init_ex(hctx, key->hmac_key, sizeof(key->hmac_key),
@@ -931,7 +944,7 @@ else
     DEBUG(D_tls)
       {
       debug_printf("ticket not usable (%s)\n", key ? "expired" : "not found");
     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);
+      if (key) debug_printf("STEK expire " TIME_T_FMT "\n", key->expire - now);
       }
     return 0;
     }
       }
     return 0;
     }
@@ -940,7 +953,13 @@ else
                key->hmac_hash, NULL);
   EVP_DecryptInit_ex(ctx, key->aes_cipher, NULL, key->aes_key, iv);
 
                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);
+  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;
   }
 }
   return key->renew < now ? 2 : 1;
   }
 }
@@ -1180,12 +1199,13 @@ ASSUMES: single response, for single cert.
 Arguments:
   sctx            the SSL_CTX* to update
   cbinfo          various parts of session state
 Arguments:
   sctx            the SSL_CTX* to update
   cbinfo          various parts of session state
-  expanded        the filename putatively holding an OCSP response
+  filename        the filename putatively holding an OCSP response
 
 */
 
 static void
 
 */
 
 static void
-ocsp_load_response(SSL_CTX *sctx, tls_ext_ctx_cb *cbinfo, const uschar *expanded)
+ocsp_load_response(SSL_CTX * sctx, tls_ext_ctx_cb * cbinfo,
+  const uschar * filename)
 {
 BIO * bio;
 OCSP_RESPONSE * resp;
 {
 BIO * bio;
 OCSP_RESPONSE * resp;
@@ -1196,17 +1216,12 @@ STACK_OF(X509) * sk;
 unsigned long verify_flags;
 int status, reason, i;
 
 unsigned long verify_flags;
 int status, reason, i;
 
-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;
-  }
+DEBUG(D_tls) debug_printf("tls_ocsp_file        '%s'\n", filename);
 
 
-if (!(bio = BIO_new_file(CS cbinfo->u_ocsp.server.file_expanded, "rb")))
+if (!(bio = BIO_new_file(CS filename, "rb")))
   {
   DEBUG(D_tls) debug_printf("Failed to open OCSP response file \"%s\"\n",
   {
   DEBUG(D_tls) debug_printf("Failed to open OCSP response file \"%s\"\n",
-      cbinfo->u_ocsp.server.file_expanded);
+      filename);
   return;
   }
 
   return;
   }
 
@@ -1225,6 +1240,14 @@ if ((status = OCSP_response_status(resp)) != OCSP_RESPONSE_STATUS_SUCCESSFUL)
   goto bad;
   }
 
   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)
 if (!(basic_response = OCSP_response_get1_basic(resp)))
   {
   DEBUG(D_tls)
@@ -1278,7 +1301,10 @@ 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()).
 
 (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. */
+I'm hoping to avoid reworking a bunch more of how we handle state here.
+
+XXX that will change when we add support for (TLS1.3) whole-chain stapling
+*/
 
 if (!(single_response = OCSP_resp_get0(basic_response, 0)))
   {
 
 if (!(single_response = OCSP_resp_get0(basic_response, 0)))
   {
@@ -1303,7 +1329,15 @@ if (!OCSP_check_validity(thisupd, nextupd, EXIM_OCSP_SKEW_SECONDS, EXIM_OCSP_MAX
   }
 
 supply_response:
   }
 
 supply_response:
-  cbinfo->u_ocsp.server.response = resp;       /*XXX stack?*/
+  /* Add the resp to the list used by tls_server_stapling_cb() */
+  {
+  ocsp_resplist ** op = &cbinfo->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;
 
 bad:
 return;
 
 bad:
@@ -1319,6 +1353,16 @@ bad:
     }
 return;
 }
     }
 return;
 }
+
+
+static void
+ocsp_free_response_list(tls_ext_ctx_cb * 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*/
 
 
 #endif /*!DISABLE_OCSP*/
 
 
@@ -1394,7 +1438,7 @@ static int
 tls_add_certfile(SSL_CTX * sctx, tls_ext_ctx_cb * cbinfo, uschar * file,
   uschar ** errstr)
 {
 tls_add_certfile(SSL_CTX * sctx, tls_ext_ctx_cb * cbinfo, uschar * file,
   uschar ** errstr)
 {
-DEBUG(D_tls) debug_printf("tls_certificate file %s\n", file);
+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),
 if (!SSL_CTX_use_certificate_chain_file(sctx, CS file))
   return tls_error(string_sprintf(
     "SSL_CTX_use_certificate_chain_file file=%s", file),
@@ -1406,7 +1450,7 @@ static int
 tls_add_pkeyfile(SSL_CTX * sctx, tls_ext_ctx_cb * cbinfo, uschar * file,
   uschar ** errstr)
 {
 tls_add_pkeyfile(SSL_CTX * sctx, tls_ext_ctx_cb * cbinfo, uschar * file,
   uschar ** errstr)
 {
-DEBUG(D_tls) debug_printf("tls_privatekey file %s\n", file);
+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);
 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);
@@ -1431,10 +1475,10 @@ Returns:          OK/DEFER/FAIL
 */
 
 static int
 */
 
 static int
-tls_expand_session_files(SSL_CTX *sctx, tls_ext_ctx_cb *cbinfo,
+tls_expand_session_files(SSL_CTX * sctx, tls_ext_ctx_cb * cbinfo,
   uschar ** errstr)
 {
   uschar ** errstr)
 {
-uschar *expanded;
+uschar * expanded;
 
 if (!cbinfo->certificate)
   {
 
 if (!cbinfo->certificate)
   {
@@ -1448,10 +1492,11 @@ else
   {
   int err;
 
   {
   int err;
 
-  if (Ustrstr(cbinfo->certificate, US"tls_sni") ||
-      Ustrstr(cbinfo->certificate, US"tls_in_sni") ||
-      Ustrstr(cbinfo->certificate, US"tls_out_sni")
-     )
+  if ( !reexpand_tls_files_for_sni
+     && (  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 (!expand_check(cbinfo->certificate, US"tls_certificate", &expanded, errstr))
     reexpand_tls_files_for_sni = TRUE;
 
   if (!expand_check(cbinfo->certificate, US"tls_certificate", &expanded, errstr))
@@ -1463,10 +1508,43 @@ else
       const uschar * file_list = expanded;
       int sep = 0;
       uschar * file;
       const uschar * file_list = expanded;
       int sep = 0;
       uschar * file;
+#ifndef DISABLE_OCSP
+      const uschar * olist = cbinfo->u_ocsp.server.file;
+      int osep = 0;
+      uschar * ofile;
+
+      if (olist)
+       if (!expand_check(olist, US"tls_ocsp_file", USS &olist, errstr))
+         return DEFER;
+      if (olist && !*olist)
+       olist = NULL;
+
+      if (  cbinfo->u_ocsp.server.file_expanded && olist
+        && (Ustrcmp(olist, cbinfo->u_ocsp.server.file_expanded) == 0))
+       {
+       DEBUG(D_tls) debug_printf(" - value unchanged, using existing values\n");
+       olist = NULL;
+       }
+      else
+       {
+       ocsp_free_response_list(cbinfo);
+       cbinfo->u_ocsp.server.file_expanded = olist;
+       }
+#endif
 
       while (file = string_nextinlist(&file_list, &sep, NULL, 0))
 
       while (file = string_nextinlist(&file_list, &sep, NULL, 0))
+       {
        if ((err = tls_add_certfile(sctx, cbinfo, file, errstr)))
          return err;
        if ((err = tls_add_certfile(sctx, cbinfo, file, errstr)))
          return err;
+
+#ifndef DISABLE_OCSP
+       if (olist)
+         if ((ofile = string_nextinlist(&olist, &osep, NULL, 0)))
+           ocsp_load_response(sctx, cbinfo, ofile);
+         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, cbinfo, expanded, errstr)))
       }
     else       /* would there ever be a need for multiple client certs? */
       if ((err = tls_add_certfile(sctx, cbinfo, expanded, errstr)))
@@ -1496,27 +1574,6 @@ else
        return err;
   }
 
        return err;
   }
 
-#ifndef DISABLE_OCSP
-if (cbinfo->is_server && cbinfo->u_ocsp.server.file)
-  {
-  /*XXX stack*/
-  if (!expand_check(cbinfo->u_ocsp.server.file, US"tls_ocsp_file", &expanded, errstr))
-    return DEFER;
-
-  if (expanded && *expanded)
-    {
-    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_printf(" - value unchanged, using existing values\n");
-      }
-    else
-      ocsp_load_response(sctx, cbinfo, expanded);
-    }
-  }
-#endif
-
 return OK;
 }
 
 return OK;
 }
 
@@ -1561,7 +1618,7 @@ DEBUG(D_tls) debug_printf("Received TLS SNI \"%s\"%s\n", servername,
 
 /* Make the extension value available for expansion */
 store_pool = POOL_PERM;
 
 /* Make the extension value available for expansion */
 store_pool = POOL_PERM;
-tls_in.sni = string_copy(US servername);
+tls_in.sni = string_copy_taint(US servername, TRUE);
 store_pool = old_pool;
 
 if (!reexpand_tls_files_for_sni)
 store_pool = old_pool;
 
 if (!reexpand_tls_files_for_sni)
@@ -1646,27 +1703,78 @@ project.
 static int
 tls_server_stapling_cb(SSL *s, void *arg)
 {
 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 tls_ext_ctx_cb * cbinfo = (tls_ext_ctx_cb *) arg;
+ocsp_resplist * olist = cbinfo->u_ocsp.server.olist;
+uschar * response_der; /*XXX blob */
 int response_der_len;
 
 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(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;
 
 tls_in.ocsp = OCSP_NOT_RESP;
-if (!cbinfo->u_ocsp.server.response)
+if (!olist)
   return SSL_TLSEXT_ERR_NOACK;
 
   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 = 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;
 
 if (response_der_len <= 0)
   return SSL_TLSEXT_ERR_NOACK;
 
@@ -1706,17 +1814,17 @@ if(!p)
   return cbinfo->u_ocsp.client.verify_required ? 0 : 1;
  }
 
   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 (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))
   {
   tls_out.ocsp = OCSP_FAILED;
   if (LOGGING(tls_cipher))
@@ -1838,6 +1946,23 @@ return i;
 *            Initialize for TLS                  *
 *************************************************/
 
 *            Initialize for TLS                  *
 *************************************************/
 
+static void
+tls_openssl_init(void)
+{
+#ifdef EXIM_NEED_OPENSSL_INIT
+SSL_load_error_strings();          /* basic set up */
+OpenSSL_add_ssl_algorithms();
+#endif
+
+#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
+}
+
+
+
 /* Called from both server and client code, to do preliminary initialization
 of the library.  We allocate and return a context structure.
 
 /* Called from both server and client code, to do preliminary initialization
 of the library.  We allocate and return a context structure.
 
@@ -1859,7 +1984,7 @@ static int
 tls_init(SSL_CTX **ctxp, host_item *host, uschar *dhparam, uschar *certificate,
   uschar *privatekey,
 #ifndef DISABLE_OCSP
 tls_init(SSL_CTX **ctxp, host_item *host, uschar *dhparam, uschar *certificate,
   uschar *privatekey,
 #ifndef DISABLE_OCSP
-  uschar *ocsp_file,   /*XXX stack, in server*/
+  uschar *ocsp_file,
 #endif
   address_item *addr, tls_ext_ctx_cb ** cbp,
   tls_support * tlsp,
 #endif
   address_item *addr, tls_ext_ctx_cb ** cbp,
   tls_support * tlsp,
@@ -1881,7 +2006,7 @@ if (!host)
   {
   cbinfo->u_ocsp.server.file = ocsp_file;
   cbinfo->u_ocsp.server.file_expanded = NULL;
   {
   cbinfo->u_ocsp.server.file = ocsp_file;
   cbinfo->u_ocsp.server.file_expanded = NULL;
-  cbinfo->u_ocsp.server.response = NULL;
+  cbinfo->u_ocsp.server.olist = NULL;
   }
 else
   cbinfo->u_ocsp.client.verify_store = NULL;
   }
 else
   cbinfo->u_ocsp.client.verify_store = NULL;
@@ -1893,16 +2018,7 @@ cbinfo->host = host;
 cbinfo->event_action = NULL;
 #endif
 
 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
+tls_openssl_init();
 
 /* Create a context.
 The OpenSSL docs in 1.0.1b have not been updated to clarify TLS variant
 
 /* Create a context.
 The OpenSSL docs in 1.0.1b have not been updated to clarify TLS variant
@@ -2033,7 +2149,7 @@ if ((rc = tls_expand_session_files(ctx, cbinfo, errstr)) != OK)
 if (!host)             /* server */
   {
 # ifndef DISABLE_OCSP
 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. */
   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. */
@@ -2096,14 +2212,13 @@ Returns:    pointer to allocated string in perm-pool
 */
 
 static uschar *
 */
 
 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. */
 
 {
 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;
 
 const SSL_CIPHER * c = (const SSL_CIPHER *) SSL_get_current_cipher(ssl);
 uschar * s;
 
@@ -2134,6 +2249,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)
 {
 static void
 peer_cert(SSL * ssl, tls_support * tlsp, uschar * peerdn, unsigned siz)
 {
@@ -2153,8 +2283,24 @@ if (tlsp->peercert)
     { DEBUG(D_tls) debug_printf("X509_NAME_oneline() error\n"); }
   else
     {
     { DEBUG(D_tls) debug_printf("X509_NAME_oneline() error\n"); }
   else
     {
-    peerdn[siz-1] = '\0';
-    tlsp->peerdn = peerdn;             /*XXX a static buffer... */
+    int oldpool = store_pool;
+
+    peerdn[siz-1] = '\0';              /* paranoia */
+    store_pool = POOL_PERM;
+    tlsp->peerdn = string_copy(peerdn);
+    store_pool = oldpool;
+
+    /* We used to set CV in the cert-verify callbacks (either plain or dane)
+    but they don't get called on session-resumption.  So use the official
+    interface, which uses the resumed value.  Unfortunately this claims verified
+    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.
+    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;
     }
 }
 
     }
 }
 
@@ -2244,6 +2390,10 @@ 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. */
 
        /* 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
        if (  !host
           && statbuf.st_size > 0
           && server_static_cbinfo->u_ocsp.server.file
@@ -2392,7 +2542,7 @@ the error. */
 
 rc = tls_init(&server_ctx, NULL, tls_dhparam, tls_certificate, tls_privatekey,
 #ifndef DISABLE_OCSP
 
 rc = tls_init(&server_ctx, NULL, tls_dhparam, tls_certificate, tls_privatekey,
 #ifndef DISABLE_OCSP
-    tls_ocsp_file,     /*XXX stack*/
+    tls_ocsp_file,
 #endif
     NULL, &server_static_cbinfo, &tls_in, errstr);
 if (rc != OK) return rc;
 #endif
     NULL, &server_static_cbinfo, &tls_in, errstr);
 if (rc != OK) return rc;
@@ -2503,8 +2653,41 @@ ALARM_CLR(0);
 
 if (rc <= 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(server_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(server_ssl) == SSL_RECEIVED_SHUTDOWN)
+           SSL_shutdown(server_ssl);
+
+      tls_close(NULL, TLS_NO_SHUTDOWN);
+      return FAIL;
+
+    /* Handle genuine errors */
+    case SSL_ERROR_SSL:
+      (void) tls_error(US"SSL_accept", 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" : NULL, errstr);
+      return FAIL;
+    }
   }
 
 DEBUG(D_tls) debug_printf("SSL_accept was successful\n");
   }
 
 DEBUG(D_tls) debug_printf("SSL_accept was successful\n");
@@ -2519,12 +2702,13 @@ if (SSL_session_reused(server_ssl))
   }
 #endif
 
   }
 #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));
 
 
 peer_cert(server_ssl, &tls_in, peerdn, sizeof(peerdn));
 
-tls_in.cipher = construct_cipher_name(server_ssl, &tls_in.bits);
+tls_in.ver = tlsver_name(server_ssl);
+tls_in.cipher = construct_cipher_name(server_ssl, tls_in.ver, &tls_in.bits);
 tls_in.cipher_stdname = cipher_stdname_ssl(server_ssl);
 
 DEBUG(D_tls)
 tls_in.cipher_stdname = cipher_stdname_ssl(server_ssl);
 
 DEBUG(D_tls)
@@ -2544,7 +2728,7 @@ DEBUG(D_tls)
 #ifdef EXIM_HAVE_SESSION_TICKET
   {
   SSL_SESSION * ss = SSL_get_session(server_ssl);
 #ifdef EXIM_HAVE_SESSION_TICKET
   {
   SSL_SESSION * ss = SSL_get_session(server_ssl);
-  if (SSL_SESSION_has_ticket(ss))
+  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));
   }
     debug_printf("The session has a ticket, life %lu seconds\n",
       SSL_SESSION_get_ticket_lifetime_hint(ss));
   }
@@ -2714,6 +2898,14 @@ if (tlsp->host_resumable)
          debug_printf("decoding session: %s\n", ssl_errstring);
          }
        }
          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)
       else if (!SSL_set_session(ssl, ss))
        {
        DEBUG(D_tls)
@@ -2727,6 +2919,8 @@ if (tlsp->host_resumable)
        {
        DEBUG(D_tls) debug_printf("good session\n");
        tlsp->resumption |= RESUME_CLIENT_SUGGESTED;
        {
        DEBUG(D_tls) debug_printf("good session\n");
        tlsp->resumption |= RESUME_CLIENT_SUGGESTED;
+       tlsp->verify_override = dt->verify_override;
+       tlsp->ocsp = dt->ocsp;
        }
       }
     else
        }
       }
     else
@@ -2749,20 +2943,22 @@ DEBUG(D_tls) debug_printf("tls_save_session_cb\n");
 
 if (!cbinfo || !(tlsp = cbinfo->tlsp)->host_resumable) return 0;
 
 
 if (!cbinfo || !(tlsp = cbinfo->tlsp)->host_resumable) return 0;
 
-# ifdef EXIM_HAVE_SESSION_TICKET
-
-if (SSL_SESSION_is_resumable(ss)) 
+# ifdef OPENSSL_HAVE_NUM_TICKETS
+if (SSL_SESSION_is_resumable(ss))      /* 1.1.1 */
+# endif
   {
   int len = i2d_SSL_SESSION(ss, NULL);
   int dlen = sizeof(dbdata_tls_session) + len;
   {
   int len = i2d_SSL_SESSION(ss, NULL);
   int dlen = sizeof(dbdata_tls_session) + len;
-  dbdata_tls_session * dt = store_get(dlen);
+  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 */
 
   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)))
     {
 
   if ((dbm_file = dbfn_open(US"tls", O_RDWR, &dbblock, FALSE, FALSE)))
     {
@@ -2774,7 +2970,6 @@ if (SSL_SESSION_is_resumable(ss))
                  (unsigned)dlen);
     }
   }
                  (unsigned)dlen);
     }
   }
-# endif
 return 1;
 }
 
 return 1;
 }
 
@@ -2872,7 +3067,7 @@ BOOL require_ocsp = FALSE;
 
 rc = store_pool;
 store_pool = POOL_PERM;
 
 rc = store_pool;
 store_pool = POOL_PERM;
-exim_client_ctx = store_get(sizeof(exim_openssl_client_tls_ctx));
+exim_client_ctx = store_get(sizeof(exim_openssl_client_tls_ctx), FALSE);
 exim_client_ctx->corked = NULL;
 store_pool = rc;
 
 exim_client_ctx->corked = NULL;
 store_pool = rc;
 
@@ -2992,12 +3187,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));
 
   }
 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);
 
 SSL_set_fd(exim_client_ctx->ssl, cctx->sock);
 SSL_set_connect_state(exim_client_ctx->ssl);
 
@@ -3057,6 +3246,12 @@ if (request_ocsp)
   }
 #endif
 
   }
 #endif
 
+#ifdef EXPERIMENTAL_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;
 #endif
 #ifndef DISABLE_EVENT
 client_static_cbinfo->event_action = tb ? tb->event_action : NULL;
 #endif
@@ -3098,7 +3293,8 @@ tls_client_resume_posthandshake(exim_client_ctx, tlsp);
 
 peer_cert(exim_client_ctx->ssl, tlsp, peerdn, sizeof(peerdn));
 
 
 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 */
 tlsp->cipher_stdname = cipher_stdname_ssl(exim_client_ctx->ssl);
 
 /* Record the certificate we presented */
@@ -3153,32 +3349,10 @@ switch(error)
   case SSL_ERROR_ZERO_RETURN:
     DEBUG(D_tls) debug_printf("Got SSL_ERROR_ZERO_RETURN\n");
 
   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);
 
     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;
-
+    tls_close(NULL, TLS_NO_SHUTDOWN);
     return FALSE;
 
   /* Handle genuine errors */
     return FALSE;
 
   /* Handle genuine errors */
@@ -3359,14 +3533,14 @@ a store reset there, so use POOL_PERM. */
 
 if ((more || corked))
   {
 
 if ((more || corked))
   {
-#ifdef EXPERIMENTAL_PIPE_CONNECT
+#ifndef DISABLE_PIPE_CONNECT
   int save_pool = store_pool;
   store_pool = POOL_PERM;
 #endif
 
   corked = string_catn(corked, buff, len);
 
   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
 
   store_pool = save_pool;
 #endif
 
@@ -3467,14 +3641,25 @@ if (shutdown)
     }
   }
 
     }
   }
 
-#ifndef DISABLE_OCSP
 if (!o_ctx)            /* server side */
   {
 if (!o_ctx)            /* server side */
   {
+#ifndef DISABLE_OCSP
   sk_X509_pop_free(server_static_cbinfo->verify_stack, X509_free);
   server_static_cbinfo->verify_stack = NULL;
   sk_X509_pop_free(server_static_cbinfo->verify_stack, X509_free);
   server_static_cbinfo->verify_stack = NULL;
-  }
 #endif
 
 #endif
 
+  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_CTX_free(*ctxp);
 SSL_free(*sslp);
 *ctxp = NULL;
 SSL_CTX_free(*ctxp);
 SSL_free(*sslp);
 *ctxp = NULL;
@@ -3501,18 +3686,7 @@ tls_validate_require_cipher(void)
 SSL_CTX *ctx;
 uschar *s, *expciphers, *err;
 
 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;
 
 if (!(tls_require_ciphers && *tls_require_ciphers))
   return NULL;