TLS resumption: restrict session re-use
[exim.git] / src / src / tls-openssl.c
index e975eae4d73c42841efa089fdcfa0a059bb75e0d..bab02d0560b23868c53906b16f775ba3dbf559c8 100644 (file)
@@ -1009,7 +1009,7 @@ if (ev)
   old_cert = tlsp->peercert;
   tlsp->peercert = X509_dup(cert);
   /* NB we do not bother setting peerdn */
   old_cert = tlsp->peercert;
   tlsp->peercert = X509_dup(cert);
   /* NB we do not bother setting peerdn */
-  if ((yield = event_raise(ev, US"tls:cert", string_sprintf("%d", depth))))
+  if ((yield = event_raise(ev, US"tls:cert", string_sprintf("%d", depth), &errno)))
     {
     log_write(0, LOG_MAIN, "[%s] %s verify denied by event-action: "
                "depth=%d cert=%s: %s",
     {
     log_write(0, LOG_MAIN, "[%s] %s verify denied by event-action: "
                "depth=%d cert=%s: %s",
@@ -1446,7 +1446,7 @@ supply_response:
   ocsp_resplist ** op = &state->u_ocsp.server.olist, * oentry;
   while (oentry = *op)
     op = &oentry->next;
   ocsp_resplist ** op = &state->u_ocsp.server.olist, * oentry;
   while (oentry = *op)
     op = &oentry->next;
-  *op = oentry = store_get(sizeof(ocsp_resplist), FALSE);
+  *op = oentry = store_get(sizeof(ocsp_resplist), GET_UNTAINTED);
   oentry->next = NULL;
   oentry->resp = resp;
   }
   oentry->next = NULL;
   oentry->resp = resp;
   }
@@ -1642,11 +1642,24 @@ return OK;
 * One-time init credentials for server and client *
 **************************************************/
 
 * One-time init credentials for server and client *
 **************************************************/
 
+static void
+normalise_ciphers(uschar ** ciphers, const uschar * pre_expansion_ciphers)
+{
+uschar * s = *ciphers;
+
+if (!s || !Ustrchr(s, '_')) return;    /* no change needed */
+
+if (s == pre_expansion_ciphers)
+  s = string_copy(s);                  /* get writable copy */
+
+for (uschar * t = s; *t; t++) if (*t == '_') *t = '-';
+*ciphers = s;
+}
+
 static int
 server_load_ciphers(SSL_CTX * ctx, exim_openssl_state_st * state,
   uschar * ciphers, uschar ** errstr)
 {
 static int
 server_load_ciphers(SSL_CTX * ctx, exim_openssl_state_st * state,
   uschar * ciphers, uschar ** errstr)
 {
-for (uschar * s = ciphers; *s; s++ ) if (*s == '_') *s = '-';
 DEBUG(D_tls) debug_printf("required ciphers: %s\n", ciphers);
 if (!SSL_CTX_set_cipher_list(ctx, CS ciphers))
   return tls_error(US"SSL_CTX_set_cipher_list", NULL, NULL, errstr);
 DEBUG(D_tls) debug_printf("required ciphers: %s\n", ciphers);
 if (!SSL_CTX_set_cipher_list(ctx, CS ciphers))
   return tls_error(US"SSL_CTX_set_cipher_list", NULL, NULL, errstr);
@@ -1798,6 +1811,7 @@ else
 if (opt_set_and_noexpand(tls_require_ciphers))
   {
   DEBUG(D_tls) debug_printf("TLS: preloading cipher list for server\n");
 if (opt_set_and_noexpand(tls_require_ciphers))
   {
   DEBUG(D_tls) debug_printf("TLS: preloading cipher list for server\n");
+  normalise_ciphers(&tls_require_ciphers, tls_require_ciphers);
   if (server_load_ciphers(ctx, &state_server, tls_require_ciphers,
                          &dummy_errstr) == OK)
     state_server.lib_state.pri_string = TRUE;
   if (server_load_ciphers(ctx, &state_server, tls_require_ciphers,
                          &dummy_errstr) == OK)
     state_server.lib_state.pri_string = TRUE;
@@ -2160,7 +2174,7 @@ DEBUG(D_tls) debug_printf("Received TLS SNI \"%s\"%s\n", servername,
 
 /* Make the extension value available for expansion */
 store_pool = POOL_PERM;
 
 /* Make the extension value available for expansion */
 store_pool = POOL_PERM;
-tls_in.sni = string_copy_taint(US servername, TRUE);
+tls_in.sni = string_copy_taint(US servername, GET_TAINTED);
 store_pool = old_pool;
 
 if (!reexpand_tls_files_for_sni)
 store_pool = old_pool;
 
 if (!reexpand_tls_files_for_sni)
@@ -2403,8 +2417,12 @@ int i;
 DEBUG(D_tls) debug_printf("Received TLS status callback (OCSP stapling):\n");
 len = SSL_get_tlsext_status_ocsp_resp(s, &p);
 if(!p)
 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 */
+ {                             /* Expect this when we requested ocsp but got none */
+  if (SSL_session_reused(s) && tls_out.ocsp == OCSP_VFIED)
+    {
+    DEBUG(D_tls) debug_printf(" null, but resumed; ocsp vfy stored with session is good\n");
+    return 1;
+    }
   if (cbinfo->u_ocsp.client.verify_required && LOGGING(tls_cipher))
     log_write(0, LOG_MAIN, "Required TLS certificate status not received");
   else
   if (cbinfo->u_ocsp.client.verify_required && LOGGING(tls_cipher))
     log_write(0, LOG_MAIN, "Required TLS certificate status not received");
   else
@@ -2446,7 +2464,7 @@ if (!(bs = OCSP_response_get1_basic(rsp)))
     STACK_OF(OCSP_SINGLERESP) * sresp = bs->tbsResponseData->responses;
 #endif
 
     STACK_OF(OCSP_SINGLERESP) * sresp = bs->tbsResponseData->responses;
 #endif
 
-    DEBUG(D_tls) bp = BIO_new_fp(debug_file, BIO_NOCLOSE);
+    DEBUG(D_tls) bp = BIO_new(BIO_s_mem());
 
     /*OCSP_RESPONSE_print(bp, rsp, 0);   extreme debug: stapling content */
 
 
     /*OCSP_RESPONSE_print(bp, rsp, 0);   extreme debug: stapling content */
 
@@ -2461,9 +2479,12 @@ if (!(bs = OCSP_response_get1_basic(rsp)))
        if (LOGGING(tls_cipher)) log_write(0, LOG_MAIN,
                "Received TLS cert status response, itself unverifiable: %s",
                ERR_reason_error_string(ERR_peek_error()));
        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);
+       DEBUG(D_tls)
+         {
+         BIO_printf(bp, "OCSP response verify failure\n");
+         ERR_print_errors(bp);
+         OCSP_RESPONSE_print(bp, rsp, 0);
+         }
        goto failed;
        }
       else
        goto failed;
        }
       else
@@ -2501,14 +2522,17 @@ if (!(bs = OCSP_response_get1_basic(rsp)))
       status = OCSP_single_get0_status(single, &reason, &rev,
                  &thisupd, &nextupd);
 
       status = OCSP_single_get0_status(single, &reason, &rev,
                  &thisupd, &nextupd);
 
-      DEBUG(D_tls) time_print(bp, "This OCSP Update", thisupd);
-      DEBUG(D_tls) if(nextupd) time_print(bp, "Next OCSP Update", nextupd);
+      DEBUG(D_tls)
+       {
+       time_print(bp, "This OCSP Update", thisupd);
+       if (nextupd) time_print(bp, "Next OCSP Update", nextupd);
+       }
       if (!OCSP_check_validity(thisupd, nextupd,
            EXIM_OCSP_SKEW_SECONDS, EXIM_OCSP_MAX_AGE))
        {
        tls_out.ocsp = OCSP_FAILED;
        DEBUG(D_tls) ERR_print_errors(bp);
       if (!OCSP_check_validity(thisupd, nextupd,
            EXIM_OCSP_SKEW_SECONDS, EXIM_OCSP_MAX_AGE))
        {
        tls_out.ocsp = OCSP_FAILED;
        DEBUG(D_tls) ERR_print_errors(bp);
-       log_write(0, LOG_MAIN, "Server OSCP dates invalid");
+       log_write(0, LOG_MAIN, "OCSP dates invalid");
        goto failed;
        }
 
        goto failed;
        }
 
@@ -2541,6 +2565,11 @@ if (!(bs = OCSP_response_get1_basic(rsp)))
     tls_out.ocsp = OCSP_FAILED;
     i = cbinfo->u_ocsp.client.verify_required ? 0 : 1;
   good:
     tls_out.ocsp = OCSP_FAILED;
     i = cbinfo->u_ocsp.client.verify_required ? 0 : 1;
   good:
+    {
+    uschar * s = NULL;
+    int len = (int) BIO_get_mem_data(bp, CSS &s);
+    if (len > 0) debug_printf("%.*s", len, s);
+    }
     BIO_free(bp);
   }
 
     BIO_free(bp);
   }
 
@@ -3116,6 +3145,21 @@ return OK;
 
 
 
 
 
 
+static void
+tls_dump_keylog(SSL * ssl)
+{
+#ifdef EXIM_HAVE_OPENSSL_KEYLOG
+  BIO * bp = BIO_new(BIO_s_mem());
+  uschar * s = NULL;
+  int len;
+  SSL_SESSION_print_keylog(bp, SSL_get_session(ssl));
+  len = (int) BIO_get_mem_data(bp, CSS &s);
+  if (len > 0) debug_printf("%.*s", len, s);
+  BIO_free(bp);
+#endif
+}
+
+
 /*************************************************
 *       Start a TLS session in a server          *
 *************************************************/
 /*************************************************
 *       Start a TLS session in a server          *
 *************************************************/
@@ -3178,9 +3222,12 @@ else
   if (!expand_check(tls_require_ciphers, US"tls_require_ciphers", &expciphers, errstr))
     return FAIL;
 
   if (!expand_check(tls_require_ciphers, US"tls_require_ciphers", &expciphers, errstr))
     return FAIL;
 
-  if (  expciphers
-     && (rc = server_load_ciphers(ctx, &state_server, expciphers, errstr)) != OK)
-    return rc;
+  if (expciphers)
+    {
+    normalise_ciphers(&expciphers, tls_require_ciphers);
+    if ((rc = server_load_ciphers(ctx, &state_server, expciphers, errstr)) != OK)
+      return rc;
+    }
   }
 
 /* If this is a host for which certificate verification is mandatory or
   }
 
 /* If this is a host for which certificate verification is mandatory or
@@ -3294,9 +3341,10 @@ if (rc <= 0)
     case SSL_ERROR_ZERO_RETURN:
       DEBUG(D_tls) debug_printf("Got SSL_ERROR_ZERO_RETURN\n");
       (void) tls_error(US"SSL_accept", NULL, sigalrm_seen ? US"timed out" : NULL, errstr);
     case SSL_ERROR_ZERO_RETURN:
       DEBUG(D_tls) debug_printf("Got SSL_ERROR_ZERO_RETURN\n");
       (void) tls_error(US"SSL_accept", NULL, sigalrm_seen ? US"timed out" : NULL, errstr);
+      (void) event_raise(event_action, US"tls:fail:connect", *errstr, NULL);
 
       if (SSL_get_shutdown(ssl) == SSL_RECEIVED_SHUTDOWN)
 
       if (SSL_get_shutdown(ssl) == SSL_RECEIVED_SHUTDOWN)
-           SSL_shutdown(ssl);
+       SSL_shutdown(ssl);
 
       tls_close(NULL, TLS_NO_SHUTDOWN);
       return FAIL;
 
       tls_close(NULL, TLS_NO_SHUTDOWN);
       return FAIL;
@@ -3311,8 +3359,9 @@ if (rc <= 0)
          || r == SSL_R_VERSION_TOO_LOW
 #endif
          || r == SSL_R_UNKNOWN_PROTOCOL || r == SSL_R_UNSUPPORTED_PROTOCOL)
          || r == SSL_R_VERSION_TOO_LOW
 #endif
          || r == SSL_R_UNKNOWN_PROTOCOL || r == SSL_R_UNSUPPORTED_PROTOCOL)
-       s = string_sprintf("%s (%s)", s, SSL_get_version(ssl));
+       s = string_sprintf("(%s)", SSL_get_version(ssl));
       (void) tls_error(US"SSL_accept", NULL, sigalrm_seen ? US"timed out" : s, errstr);
       (void) tls_error(US"SSL_accept", NULL, sigalrm_seen ? US"timed out" : s, errstr);
+      (void) event_raise(event_action, US"tls:fail:connect", *errstr, NULL);
       return FAIL;
       }
 
       return FAIL;
       }
 
@@ -3323,6 +3372,7 @@ if (rc <= 0)
        if (!errno)
          {
          *errstr = US"SSL_accept: TCP connection closed by peer";
        if (!errno)
          {
          *errstr = US"SSL_accept: TCP connection closed by peer";
+         (void) event_raise(event_action, US"tls:fail:connect", *errstr, NULL);
          return FAIL;
          }
        DEBUG(D_tls) debug_printf(" - syscall %s\n", strerror(errno));
          return FAIL;
          }
        DEBUG(D_tls) debug_printf(" - syscall %s\n", strerror(errno));
@@ -3331,6 +3381,7 @@ if (rc <= 0)
                      sigalrm_seen ? US"timed out"
                      : ERR_peek_error() ? NULL : string_sprintf("ret %d", error),
                      errstr);
                      sigalrm_seen ? US"timed out"
                      : ERR_peek_error() ? NULL : string_sprintf("ret %d", error),
                      errstr);
+      (void) event_raise(event_action, US"tls:fail:connect", *errstr, NULL);
       return FAIL;
     }
   }
       return FAIL;
     }
   }
@@ -3392,13 +3443,7 @@ DEBUG(D_tls)
   if (SSL_get_shared_ciphers(ssl, CS buf, sizeof(buf)))
     debug_printf("Shared ciphers: %s\n", buf);
 
   if (SSL_get_shared_ciphers(ssl, CS buf, sizeof(buf)))
     debug_printf("Shared ciphers: %s\n", buf);
 
-#ifdef EXIM_HAVE_OPENSSL_KEYLOG
-  {
-  BIO * bp = BIO_new_fp(debug_file, BIO_NOCLOSE);
-  SSL_SESSION_print_keylog(bp, SSL_get_session(ssl));
-  BIO_free(bp);
-  }
-#endif
+  tls_dump_keylog(ssl);
 
 #ifdef EXIM_HAVE_SESSION_TICKET
   {
 
 #ifdef EXIM_HAVE_SESSION_TICKET
   {
@@ -3423,9 +3468,9 @@ See description in https://paquier.xyz/postgresql-2/channel-binding-openssl/ */
   size_t len = SSL_get_peer_finished(ssl, &c, 0);
   int old_pool = store_pool;
 
   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);
+  SSL_get_peer_finished(ssl, s = store_get((int)len, GET_UNTAINTED), len);
   store_pool = POOL_PERM;
   store_pool = POOL_PERM;
-    tls_in.channelbinding = b64encode_taint(CUS s, (int)len, FALSE);
+    tls_in.channelbinding = b64encode_taint(CUS s, (int)len, GET_UNTAINTED);
   store_pool = old_pool;
   DEBUG(D_tls) debug_printf("Have channel bindings cached for possible auth usage %p\n", tls_in.channelbinding);
   }
   store_pool = old_pool;
   DEBUG(D_tls) debug_printf("Have channel bindings cached for possible auth usage %p\n", tls_in.channelbinding);
   }
@@ -3575,21 +3620,21 @@ return DEFER;
 and apply it to the ssl-connection for attempted resumption. */
 
 static void
 and apply it to the ssl-connection for attempted resumption. */
 
 static void
-tls_retrieve_session(tls_support * tlsp, SSL * ssl, const uschar * key)
+tls_retrieve_session(tls_support * tlsp, SSL * ssl)
 {
 {
-tlsp->resumption |= RESUME_SUPPORTED;
 if (tlsp->host_resumable)
   {
 if (tlsp->host_resumable)
   {
+  const uschar * key = tlsp->resume_index;
   dbdata_tls_session * dt;
   int len;
   open_db dbblock, * dbm_file;
 
   tlsp->resumption |= RESUME_CLIENT_REQUESTED;
   dbdata_tls_session * dt;
   int len;
   open_db dbblock, * dbm_file;
 
   tlsp->resumption |= RESUME_CLIENT_REQUESTED;
-  DEBUG(D_tls) debug_printf("checking for resumable session for %s\n", key);
+  DEBUG(D_tls)
+    debug_printf("checking for resumable session for %s\n", tlsp->resume_index);
   if ((dbm_file = dbfn_open(US"tls", O_RDWR, &dbblock, FALSE, FALSE)))
     {
   if ((dbm_file = dbfn_open(US"tls", O_RDWR, &dbblock, FALSE, FALSE)))
     {
-    /* key for the db is the IP */
-    if ((dt = dbfn_read_with_length(dbm_file, key, &len)))
+    if ((dt = dbfn_read_with_length(dbm_file, tlsp->resume_index, &len)))
       {
       SSL_SESSION * ss = NULL;
       const uschar * sess_asn1 = dt->session;
       {
       SSL_SESSION * ss = NULL;
       const uschar * sess_asn1 = dt->session;
@@ -3615,24 +3660,21 @@ if (tlsp->host_resumable)
        if (lifetime + dt->time_stamp < time(NULL))
          {
          DEBUG(D_tls) debug_printf("session expired\n");
        if (lifetime + dt->time_stamp < time(NULL))
          {
          DEBUG(D_tls) debug_printf("session expired\n");
-         dbfn_delete(dbm_file, key);
+         dbfn_delete(dbm_file, tlsp->resume_index);
          }
          }
-       else if (!SSL_set_session(ssl, ss))
-         {
-         DEBUG(D_tls)
-           {
-           ERR_error_string_n(ERR_get_error(),
-             ssl_errstring, sizeof(ssl_errstring));
-           debug_printf("applying session to ssl: %s\n", ssl_errstring);
-           }
-         }
-       else
+       else if (SSL_set_session(ssl, ss))
          {
          DEBUG(D_tls) debug_printf("good session\n");
          tlsp->resumption |= RESUME_CLIENT_SUGGESTED;
          tlsp->verify_override = dt->verify_override;
          tlsp->ocsp = dt->ocsp;
          }
          {
          DEBUG(D_tls) debug_printf("good session\n");
          tlsp->resumption |= RESUME_CLIENT_SUGGESTED;
          tlsp->verify_override = dt->verify_override;
          tlsp->ocsp = dt->ocsp;
          }
+       else DEBUG(D_tls)
+         {
+         ERR_error_string_n(ERR_get_error(),
+           ssl_errstring, sizeof(ssl_errstring));
+         debug_printf("applying session to ssl: %s\n", ssl_errstring);
+         }
        }
       }
     else
        }
       }
     else
@@ -3661,7 +3703,7 @@ if (SSL_SESSION_is_resumable(ss))         /* 1.1.1 */
   {
   int len = i2d_SSL_SESSION(ss, NULL);
   int dlen = sizeof(dbdata_tls_session) + len;
   {
   int len = i2d_SSL_SESSION(ss, NULL);
   int dlen = sizeof(dbdata_tls_session) + len;
-  dbdata_tls_session * dt = store_get(dlen, TRUE);
+  dbdata_tls_session * dt = store_get(dlen, GET_TAINTED);
   uschar * s = dt->session;
   open_db dbblock, * dbm_file;
 
   uschar * s = dt->session;
   open_db dbblock, * dbm_file;
 
@@ -3674,9 +3716,7 @@ if (SSL_SESSION_is_resumable(ss))         /* 1.1.1 */
 
   if ((dbm_file = dbfn_open(US"tls", O_RDWR, &dbblock, FALSE, FALSE)))
     {
 
   if ((dbm_file = dbfn_open(US"tls", O_RDWR, &dbblock, FALSE, FALSE)))
     {
-    const uschar * key = cbinfo->host->address;
-    dbfn_delete(dbm_file, key);
-    dbfn_write(dbm_file, key, dt, dlen);
+    dbfn_write(dbm_file, tlsp->resume_index, dt, dlen);
     dbfn_close(dbm_file);
     DEBUG(D_tls) debug_printf("wrote session (len %u) to db\n",
                  (unsigned)dlen);
     dbfn_close(dbm_file);
     DEBUG(D_tls) debug_printf("wrote session (len %u) to db\n",
                  (unsigned)dlen);
@@ -3686,21 +3726,20 @@ return 1;
 }
 
 
 }
 
 
+/* Construct a key for session DB lookup, and setup the SSL_CTX for resumption */
+
 static void
 tls_client_ctx_resume_prehandshake(
 static void
 tls_client_ctx_resume_prehandshake(
-  exim_openssl_client_tls_ctx * exim_client_ctx, tls_support * tlsp,
-  smtp_transport_options_block * ob, host_item * host)
+  exim_openssl_client_tls_ctx * exim_client_ctx, smtp_connect_args * conn_args,
+  tls_support * tlsp, smtp_transport_options_block * ob)
 {
 {
-/* Should the client request a session resumption ticket? */
-if (verify_check_given_host(CUSS &ob->tls_resumption_hosts, host) == OK)
-  {
-  tlsp->host_resumable = TRUE;
+tlsp->host_resumable = TRUE;
+tls_client_resmption_key(tlsp, conn_args, ob);
 
 
-  SSL_CTX_set_session_cache_mode(exim_client_ctx->ctx,
-       SSL_SESS_CACHE_CLIENT
-       | SSL_SESS_CACHE_NO_INTERNAL | SSL_SESS_CACHE_NO_AUTO_CLEAR);
-  SSL_CTX_sess_set_new_cb(exim_client_ctx->ctx, tls_save_session_cb);
-  }
+SSL_CTX_set_session_cache_mode(exim_client_ctx->ctx,
+      SSL_SESS_CACHE_CLIENT
+      | SSL_SESS_CACHE_NO_INTERNAL | SSL_SESS_CACHE_NO_AUTO_CLEAR);
+SSL_CTX_sess_set_new_cb(exim_client_ctx->ctx, tls_save_session_cb);
 }
 
 static BOOL
 }
 
 static BOOL
@@ -3724,7 +3763,7 @@ if (tlsp->host_resumable)
 
 tlsp->resumption = RESUME_SUPPORTED;
 /* Pick up a previous session, saved on an old ticket */
 
 tlsp->resumption = RESUME_SUPPORTED;
 /* Pick up a previous session, saved on an old ticket */
-tls_retrieve_session(tlsp, ssl, host->address);
+tls_retrieve_session(tlsp, ssl);
 return TRUE;
 }
 
 return TRUE;
 }
 
@@ -3744,16 +3783,19 @@ if (SSL_session_reused(exim_client_ctx->ssl))
 #ifdef EXIM_HAVE_ALPN
 /* Expand and convert an Exim list to an ALPN list.  False return for fail.
 NULL plist return for silent no-ALPN.
 #ifdef EXIM_HAVE_ALPN
 /* Expand and convert an Exim list to an ALPN list.  False return for fail.
 NULL plist return for silent no-ALPN.
+
+Overwite the passed-in list with the expanded version.
 */
 
 static BOOL
 */
 
 static BOOL
-tls_alpn_plist(const uschar * tls_alpn, const uschar ** plist, unsigned * plen,
+tls_alpn_plist(uschar ** tls_alpn, const uschar ** plist, unsigned * plen,
   uschar ** errstr)
 {
 uschar * exp_alpn;
 
   uschar ** errstr)
 {
 uschar * exp_alpn;
 
-if (!expand_check(tls_alpn, US"tls_alpn", &exp_alpn, errstr))
+if (!expand_check(*tls_alpn, US"tls_alpn", &exp_alpn, errstr))
   return FALSE;
   return FALSE;
+*tls_alpn = exp_alpn;
 
 if (!exp_alpn)
   {
 
 if (!exp_alpn)
   {
@@ -3766,7 +3808,7 @@ else
   but it's little extra code complexity in the client. */
 
   const uschar * list = exp_alpn;
   but it's little extra code complexity in the client. */
 
   const uschar * list = exp_alpn;
-  uschar * p = store_get(Ustrlen(exp_alpn), is_tainted(exp_alpn)), * s, * t;
+  uschar * p = store_get(Ustrlen(exp_alpn), exp_alpn), * s, * t;
   int sep = 0;
   uschar len;
 
   int sep = 0;
   uschar len;
 
@@ -3820,7 +3862,7 @@ BOOL require_ocsp = FALSE;
 
 rc = store_pool;
 store_pool = POOL_PERM;
 
 rc = store_pool;
 store_pool = POOL_PERM;
-exim_client_ctx = store_get(sizeof(exim_openssl_client_tls_ctx), FALSE);
+exim_client_ctx = store_get(sizeof(exim_openssl_client_tls_ctx), GET_UNTAINTED);
 exim_client_ctx->corked = NULL;
 store_pool = rc;
 
 exim_client_ctx->corked = NULL;
 store_pool = rc;
 
@@ -3881,21 +3923,25 @@ if (conn_args->dane)
     return FALSE;
   if (expciphers && *expciphers == '\0')
     expciphers = NULL;
     return FALSE;
   if (expciphers && *expciphers == '\0')
     expciphers = NULL;
+
+  normalise_ciphers(&expciphers, ob->dane_require_tls_ciphers);
   }
 #endif
   }
 #endif
-if (!expciphers &&
-    !expand_check(ob->tls_require_ciphers, US"tls_require_ciphers",
+if (!expciphers)
+  {
+  if (!expand_check(ob->tls_require_ciphers, US"tls_require_ciphers",
       &expciphers, errstr))
       &expciphers, errstr))
-  return FALSE;
+    return FALSE;
 
 
-/* In OpenSSL, cipher components are separated by hyphens. In GnuTLS, they
-are separated by underscores. So that I can use either form in my tests, and
-also for general convenience, we turn underscores into hyphens here. */
+  /* In OpenSSL, cipher components are separated by hyphens. In GnuTLS, they
+  are separated by underscores. So that I can use either form in my tests, and
+  also for general convenience, we turn underscores into hyphens here. */
+
+  normalise_ciphers(&expciphers, ob->tls_require_ciphers);
+  }
 
 if (expciphers)
   {
 
 if (expciphers)
   {
-  uschar *s = expciphers;
-  while (*s) { if (*s == '_') *s = '-'; s++; }
   DEBUG(D_tls) debug_printf("required ciphers: %s\n", expciphers);
   if (!SSL_CTX_set_cipher_list(exim_client_ctx->ctx, CS expciphers))
     {
   DEBUG(D_tls) debug_printf("required ciphers: %s\n", expciphers);
   if (!SSL_CTX_set_cipher_list(exim_client_ctx->ctx, CS expciphers))
     {
@@ -3930,39 +3976,20 @@ if (tls_client_basic_ctx_init(exim_client_ctx->ctx, host, ob,
       client_static_state, errstr) != OK)
   return FALSE;
 
       client_static_state, errstr) != OK)
   return FALSE;
 
-#ifndef DISABLE_TLS_RESUME
-tls_client_ctx_resume_prehandshake(exim_client_ctx, tlsp, ob, host);
-#endif
-
-
-if (!(exim_client_ctx->ssl = SSL_new(exim_client_ctx->ctx)))
-  {
-  tls_error(US"SSL_new", host, NULL, errstr);
-  return FALSE;
-  }
-SSL_set_session_id_context(exim_client_ctx->ssl, sid_ctx, Ustrlen(sid_ctx));
-
-SSL_set_fd(exim_client_ctx->ssl, cctx->sock);
-SSL_set_connect_state(exim_client_ctx->ssl);
-
 if (ob->tls_sni)
   {
   if (!expand_check(ob->tls_sni, US"tls_sni", &tlsp->sni, errstr))
     return FALSE;
   if (!tlsp->sni)
 if (ob->tls_sni)
   {
   if (!expand_check(ob->tls_sni, US"tls_sni", &tlsp->sni, errstr))
     return FALSE;
   if (!tlsp->sni)
-    {
-    DEBUG(D_tls) debug_printf("Setting TLS SNI forced to fail, not sending\n");
-    }
+    { DEBUG(D_tls) debug_printf("Setting TLS SNI forced to fail, not sending\n"); }
   else if (!Ustrlen(tlsp->sni))
     tlsp->sni = NULL;
   else
     {
   else if (!Ustrlen(tlsp->sni))
     tlsp->sni = NULL;
   else
     {
-#ifdef EXIM_HAVE_OPENSSL_TLSEXT
-    DEBUG(D_tls) debug_printf("Setting TLS SNI \"%s\"\n", tlsp->sni);
-    SSL_set_tlsext_host_name(exim_client_ctx->ssl, tlsp->sni);
-#else
+#ifndef EXIM_HAVE_OPENSSL_TLSEXT
     log_write(0, LOG_MAIN, "SNI unusable with this OpenSSL library version; ignoring \"%s\"\n",
           tlsp->sni);
     log_write(0, LOG_MAIN, "SNI unusable with this OpenSSL library version; ignoring \"%s\"\n",
           tlsp->sni);
+    tlsp->sni = NULL;
 #endif
     }
   }
 #endif
     }
   }
@@ -3973,10 +4000,10 @@ if (ob->tls_alpn)
   const uschar * plist;
   unsigned plen;
 
   const uschar * plist;
   unsigned plen;
 
-  if (!tls_alpn_plist(ob->tls_alpn, &plist, &plen, errstr))
+  if (!tls_alpn_plist(&ob->tls_alpn, &plist, &plen, errstr))
     return FALSE;
   if (plist)
     return FALSE;
   if (plist)
-    if (SSL_set_alpn_protos(exim_client_ctx->ssl, plist, plen) != 0)
+    if (SSL_CTX_set_alpn_protos(exim_client_ctx->ctx, plist, plen) != 0)
       {
       tls_error(US"alpn init", host, NULL, errstr);
       return FALSE;
       {
       tls_error(US"alpn init", host, NULL, errstr);
       return FALSE;
@@ -3989,6 +4016,29 @@ if (ob->tls_alpn)
           ob->tls_alpn);
 #endif
 
           ob->tls_alpn);
 #endif
 
+#ifndef DISABLE_TLS_RESUME
+if (verify_check_given_host(CUSS &ob->tls_resumption_hosts, host) == OK)
+  tls_client_ctx_resume_prehandshake(exim_client_ctx, conn_args, tlsp, ob);
+#endif
+
+
+if (!(exim_client_ctx->ssl = SSL_new(exim_client_ctx->ctx)))
+  {
+  tls_error(US"SSL_new", host, NULL, errstr);
+  return FALSE;
+  }
+SSL_set_session_id_context(exim_client_ctx->ssl, sid_ctx, Ustrlen(sid_ctx));
+SSL_set_fd(exim_client_ctx->ssl, cctx->sock);
+SSL_set_connect_state(exim_client_ctx->ssl);
+
+#ifdef EXIM_HAVE_OPENSSL_TLSEXT
+if (tlsp->sni)
+  {
+  DEBUG(D_tls) debug_printf("Setting TLS SNI \"%s\"\n", tlsp->sni);
+  SSL_set_tlsext_host_name(exim_client_ctx->ssl, tlsp->sni);
+  }
+#endif
+
 #ifdef SUPPORT_DANE
 if (conn_args->dane)
   if (dane_tlsa_load(exim_client_ctx->ssl, host, &conn_args->tlsa_dnsa, errstr) != OK)
 #ifdef SUPPORT_DANE
 if (conn_args->dane)
   if (dane_tlsa_load(exim_client_ctx->ssl, host, &conn_args->tlsa_dnsa, errstr) != OK)
@@ -4055,13 +4105,7 @@ if (rc <= 0)
 DEBUG(D_tls)
   {
   debug_printf("SSL_connect succeeded\n");
 DEBUG(D_tls)
   {
   debug_printf("SSL_connect succeeded\n");
-#ifdef EXIM_HAVE_OPENSSL_KEYLOG
-  {
-  BIO * bp = BIO_new_fp(debug_file, BIO_NOCLOSE);
-  SSL_SESSION_print_keylog(bp, SSL_get_session(exim_client_ctx->ssl));
-  BIO_free(bp);
-  }
-#endif
+  tls_dump_keylog(exim_client_ctx->ssl);
   }
 
 #ifndef DISABLE_TLS_RESUME
   }
 
 #ifndef DISABLE_TLS_RESUME
@@ -4108,9 +4152,9 @@ tlsp->cipher_stdname = cipher_stdname_ssl(exim_client_ctx->ssl);
   size_t len = SSL_get_finished(exim_client_ctx->ssl, &c, 0);
   int old_pool = store_pool;
 
   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);
+  SSL_get_finished(exim_client_ctx->ssl, s = store_get((int)len, GET_TAINTED), len);
   store_pool = POOL_PERM;
   store_pool = POOL_PERM;
-    tlsp->channelbinding = b64encode_taint(CUS s, (int)len, TRUE);
+    tlsp->channelbinding = b64encode_taint(CUS s, (int)len, GET_TAINTED);
   store_pool = old_pool;
   DEBUG(D_tls) debug_printf("Have channel bindings cached for possible auth usage %p %p\n", tlsp->channelbinding, tlsp);
   }
   store_pool = old_pool;
   DEBUG(D_tls) debug_printf("Have channel bindings cached for possible auth usage %p %p\n", tlsp->channelbinding, tlsp);
   }
@@ -4480,19 +4524,25 @@ int * fdp = o_ctx ? &tls_out.active.sock : &tls_in.active.sock;
 
 if (*fdp < 0) return;  /* TLS was not active */
 
 
 if (*fdp < 0) return;  /* TLS was not active */
 
-if (do_shutdown)
+if (do_shutdown > TLS_NO_SHUTDOWN)
   {
   int rc;
   DEBUG(D_tls) debug_printf("tls_close(): shutting down TLS%s\n",
   {
   int rc;
   DEBUG(D_tls) debug_printf("tls_close(): shutting down TLS%s\n",
-    do_shutdown > 1 ? " (with response-wait)" : "");
+    do_shutdown > TLS_SHUTDOWN_NOWAIT ? " (with response-wait)" : "");
 
   tls_write(ct_ctx, NULL, 0, FALSE);   /* flush write buffer */
 
 
   tls_write(ct_ctx, NULL, 0, FALSE);   /* flush write buffer */
 
-  if (  (rc = SSL_shutdown(*sslp)) == 0        /* send "close notify" alert */
-     && do_shutdown > 1)
+  if (  (  do_shutdown >= TLS_SHUTDOWN_WONLY
+       || (rc = SSL_shutdown(*sslp)) == 0      /* send "close notify" alert */
+       )
+     && do_shutdown > TLS_SHUTDOWN_NOWAIT
+     )
     {
     {
+#ifdef EXIM_TCP_CORK
+    (void) setsockopt(*fdp, IPPROTO_TCP, EXIM_TCP_CORK, US &off, sizeof(off));
+#endif
     ALARM(2);
     ALARM(2);
-    rc = SSL_shutdown(*sslp);          /* wait for response */
+    rc = SSL_shutdown(*sslp);                  /* wait for response */
     ALARM_CLR(0);
     }
 
     ALARM_CLR(0);
     }
 
@@ -4543,8 +4593,8 @@ Returns:     NULL on success, or error message
 uschar *
 tls_validate_require_cipher(void)
 {
 uschar *
 tls_validate_require_cipher(void)
 {
-SSL_CTX *ctx;
-uschar *s, *expciphers, *err;
+SSL_CTX * ctx;
+uschar * expciphers, * err;
 
 tls_openssl_init();
 
 
 tls_openssl_init();
 
@@ -4558,12 +4608,9 @@ if (!expand_check(tls_require_ciphers, US"tls_require_ciphers", &expciphers,
 if (!(expciphers && *expciphers))
   return NULL;
 
 if (!(expciphers && *expciphers))
   return NULL;
 
-/* normalisation ripped from above */
-s = expciphers;
-while (*s != 0) { if (*s == '_') *s = '-'; s++; }
+normalise_ciphers(&expciphers, tls_require_ciphers);
 
 err = NULL;
 
 err = NULL;
-
 if (lib_ctx_new(&ctx, NULL, &err) == OK)
   {
   DEBUG(D_tls)
 if (lib_ctx_new(&ctx, NULL, &err) == OK)
   {
   DEBUG(D_tls)
@@ -4599,21 +4646,22 @@ number/string, and the version date remains unchanged.  The _build_ date
 will change, so we can more usefully assist with version diagnosis by also
 reporting the build date.
 
 will change, so we can more usefully assist with version diagnosis by also
 reporting the build date.
 
-Arguments:   a FILE* to print the results to
-Returns:     nothing
+Arguments:   string to append to
+Returns:     string
 */
 
 */
 
-void
-tls_version_report(FILE *f)
+gstring *
+tls_version_report(gstring * g)
 {
 {
-fprintf(f, "Library version: OpenSSL: Compile: %s\n"
-           "                          Runtime: %s\n"
-           "                                 : %s\n",
-           OPENSSL_VERSION_TEXT,
-           SSLeay_version(SSLEAY_VERSION),
-           SSLeay_version(SSLEAY_BUILT_ON));
-/* third line is 38 characters for the %s and the line is 73 chars long;
-the OpenSSL output includes a "built on: " prefix already. */
+return string_fmt_append(g,
+    "Library version: OpenSSL: Compile: %s\n"
+    "                          Runtime: %s\n"
+    "                                 : %s\n",
+            OPENSSL_VERSION_TEXT,
+            SSLeay_version(SSLEAY_VERSION),
+            SSLeay_version(SSLEAY_BUILT_ON));
+  /* third line is 38 characters for the %s and the line is 73 chars long;
+  the OpenSSL output includes a "built on: " prefix already. */
 }
 
 
 }