TLS SNI support for OpenSSL ($tls_sni)
authorPhil Pennock <pdp@exim.org>
Fri, 4 May 2012 11:39:01 +0000 (04:39 -0700)
committerPhil Pennock <pdp@exim.org>
Fri, 4 May 2012 11:39:01 +0000 (04:39 -0700)
doc/doc-docbook/spec.xfpt
doc/doc-txt/ChangeLog
doc/doc-txt/NewStuff
src/src/daemon.c
src/src/expand.c
src/src/globals.c
src/src/globals.h
src/src/mytypes.h
src/src/spool_in.c
src/src/spool_out.c
src/src/tls-openssl.c

index 016f3f075f57c9bd7cf724d7eadfd50e784211e9..32e24ca8086811f1669b75f8f3c2436f835433f5 100644 (file)
@@ -11888,6 +11888,24 @@ the value of the Distinguished Name of the certificate is made available in the
 value is retained during message delivery, except during outbound SMTP
 deliveries.
 
 value is retained during message delivery, except during outbound SMTP
 deliveries.
 
+.new
+.vitem &$tls_sni$&
+.vindex "&$tls_sni$&"
+.cindex "TLS" "Server Name Indication"
+When a TLS session is being established, if the client sends the Server
+Name Indication extension, the value will be placed in this variable.
+If the variable appears in &%tls_certificate%& then this option and
+&%tls_privatekey%& will be re-expanded early in the TLS session, to permit
+a different certificate to be presented (and optionally a different key to be
+used) to the client, based upon the value of the SNI extension.
+
+The value will be retained for the lifetime of the message, and not changed
+during outbound SMTP.
+
+This is currently only available when using OpenSSL, built with support for
+SNI.
+.wen
+
 .vitem &$tod_bsdinbox$&
 .vindex "&$tod_bsdinbox$&"
 The time of day and the date, in the format required for BSD-style mailbox
 .vitem &$tod_bsdinbox$&
 .vindex "&$tod_bsdinbox$&"
 The time of day and the date, in the format required for BSD-style mailbox
index a491cf9730aae091b097e77ba0676bb57f15df54..4ad79c28e274ea97f8fea730ff1bbb5facac6a1d 100644 (file)
@@ -73,6 +73,9 @@ PP/16 Removed "dont_insert_empty_fragments" fron "openssl_options".
       Removed SSL_clear() after SSL_new() which led to protocol negotiation
       failures.  We appear to now support TLS1.1+ with Exim.
 
       Removed SSL_clear() after SSL_new() which led to protocol negotiation
       failures.  We appear to now support TLS1.1+ with Exim.
 
+PP/17 OpenSSL: new expansion var $tls_sni, which if used in tls_certificate
+      lets Exim select keys and certificates based upon TLS SNI from client.
+
 
 Exim version 4.77
 -----------------
 
 Exim version 4.77
 -----------------
index 0aee33cec83dfc0987eeea199a4e0dc3ee7b1a7f..b788b45dc47f029bb903dc8825ef113ad7f1dd10 100644 (file)
@@ -42,6 +42,13 @@ Version 4.78
     administrators can choose to make the trade-off themselves and restore
     compatibility at the cost of session security.
 
     administrators can choose to make the trade-off themselves and restore
     compatibility at the cost of session security.
 
+ 7. Use of the new expansion variable $tls_sni in the main configuration option
+    tls_certificate will cause Exim to re-expand the option, if the client
+    sends the TLS Server Name Indication extension, to permit choosing a
+    different certificate; tls_privatekey will also be re-expanded.  You must
+    still set these options to expand to valid files when $tls_sni is not set.
+    Currently OpenSSL only.
+
 
 Version 4.77
 ------------
 
 Version 4.77
 ------------
index 4ac34332b84a20b01412cc283e57fa4d52e94022..27b4cb26567045add35870477d9f3f30e21a3714 100644 (file)
@@ -828,8 +828,17 @@ pid_t pid;
 while ((pid = waitpid(-1, &status, WNOHANG)) > 0)
   {
   int i;
 while ((pid = waitpid(-1, &status, WNOHANG)) > 0)
   {
   int i;
-  DEBUG(D_any) debug_printf("child %d ended: status=0x%x\n", (int)pid,
-    status);
+  DEBUG(D_any)
+    {
+    debug_printf("child %d ended: status=0x%x\n", (int)pid, status);
+#ifdef WCOREDUMP
+    if (WIFEXITED(status))
+      debug_printf("  normal exit, %d\n", WEXITSTATUS(status));
+    else if (WIFSIGNALED(status))
+      debug_printf("  signal exit, signal %d%s\n", WTERMSIG(status),
+          WCOREDUMP(status) ? " (core dumped)" : "");
+#endif
+    }
 
   /* If it's a listening daemon for which we are keeping track of individual
   subprocesses, deal with an accepting process that has terminated. */
 
   /* If it's a listening daemon for which we are keeping track of individual
   subprocesses, deal with an accepting process that has terminated. */
index 54501de0b92497984038cefbb9c90b7fb281a183..22f7d9a66ff0c366d7cafc085caf85c3906913df 100644 (file)
@@ -615,6 +615,9 @@ static var_entry var_table[] = {
   { "tls_certificate_verified", vtype_int,    &tls_certificate_verified },
   { "tls_cipher",          vtype_stringptr,   &tls_cipher },
   { "tls_peerdn",          vtype_stringptr,   &tls_peerdn },
   { "tls_certificate_verified", vtype_int,    &tls_certificate_verified },
   { "tls_cipher",          vtype_stringptr,   &tls_cipher },
   { "tls_peerdn",          vtype_stringptr,   &tls_peerdn },
+#if defined(SUPPORT_TLS) && !defined(USE_GNUTLS)
+  { "tls_sni",             vtype_stringptr,   &tls_sni },
+#endif
   { "tod_bsdinbox",        vtype_todbsdin,    NULL },
   { "tod_epoch",           vtype_tode,        NULL },
   { "tod_full",            vtype_todf,        NULL },
   { "tod_bsdinbox",        vtype_todbsdin,    NULL },
   { "tod_epoch",           vtype_tode,        NULL },
   { "tod_full",            vtype_todf,        NULL },
index 6124cb585591972f1dcc17300f8a48c1eb7bb865..7985cd3f066007ba2102e525ddd43ac0cee3a678 100644 (file)
@@ -116,6 +116,9 @@ BOOL    tls_offered            = FALSE;
 uschar *tls_privatekey         = NULL;
 BOOL    tls_remember_esmtp     = FALSE;
 uschar *tls_require_ciphers    = NULL;
 uschar *tls_privatekey         = NULL;
 BOOL    tls_remember_esmtp     = FALSE;
 uschar *tls_require_ciphers    = NULL;
+#ifndef USE_GNUTLS
+uschar *tls_sni                = NULL;
+#endif
 uschar *tls_try_verify_hosts   = NULL;
 uschar *tls_verify_certificates= NULL;
 uschar *tls_verify_hosts       = NULL;
 uschar *tls_try_verify_hosts   = NULL;
 uschar *tls_verify_certificates= NULL;
 uschar *tls_verify_hosts       = NULL;
index a51e3bc50657475312548f3efbd7bf035541798a..f9540785ce52abe2365ddd8b82842aa28ff7c738 100644 (file)
@@ -98,6 +98,9 @@ extern BOOL    tls_offered;            /* Server offered TLS */
 extern uschar *tls_privatekey;         /* Private key file */
 extern BOOL    tls_remember_esmtp;     /* For YAEB */
 extern uschar *tls_require_ciphers;    /* So some can be avoided */
 extern uschar *tls_privatekey;         /* Private key file */
 extern BOOL    tls_remember_esmtp;     /* For YAEB */
 extern uschar *tls_require_ciphers;    /* So some can be avoided */
+#ifndef USE_GNUTLS
+extern uschar *tls_sni;                /* Server Name Indication */
+#endif
 extern uschar *tls_try_verify_hosts;   /* Optional client verification */
 extern uschar *tls_verify_certificates;/* Path for certificates to check */
 extern uschar *tls_verify_hosts;       /* Mandatory client verification */
 extern uschar *tls_try_verify_hosts;   /* Optional client verification */
 extern uschar *tls_verify_certificates;/* Path for certificates to check */
 extern uschar *tls_verify_hosts;       /* Mandatory client verification */
index 5215777f8e369d9409e21e6a12278d5deb133ddf..ade294e5df82688cc5f1d4b1e0b9d85b5dfbf080 100644 (file)
@@ -31,8 +31,10 @@ the arguments of printf-like functions. This is done by a macro. */
 
 #if defined(__GNUC__) || defined(__clang__)
 #define PRINTF_FUNCTION(A,B)  __attribute__((format(printf,A,B)))
 
 #if defined(__GNUC__) || defined(__clang__)
 #define PRINTF_FUNCTION(A,B)  __attribute__((format(printf,A,B)))
+#define ARG_UNUSED  __attribute__((__unused__))
 #else
 #define PRINTF_FUNCTION(A,B)
 #else
 #define PRINTF_FUNCTION(A,B)
+#define ARG_UNUSED  /**/
 #endif
 
 
 #endif
 
 
index e0d7fcffe6a25ae5ca1626834f4d47b438b550c1..bdc3903c048a3201aad2f6812487a369233e7f5b 100644 (file)
@@ -286,6 +286,9 @@ dkim_collect_input = FALSE;
 tls_certificate_verified = FALSE;
 tls_cipher = NULL;
 tls_peerdn = NULL;
 tls_certificate_verified = FALSE;
 tls_cipher = NULL;
 tls_peerdn = NULL;
+#ifndef USE_GNUTLS
+tls_sni = NULL;
+#endif
 #endif
 
 #ifdef WITH_CONTENT_SCAN
 #endif
 
 #ifdef WITH_CONTENT_SCAN
@@ -549,6 +552,10 @@ for (;;)
       tls_cipher = string_copy(big_buffer + 12);
     else if (Ustrncmp(p, "ls_peerdn", 9) == 0)
       tls_peerdn = string_unprinting(string_copy(big_buffer + 12));
       tls_cipher = string_copy(big_buffer + 12);
     else if (Ustrncmp(p, "ls_peerdn", 9) == 0)
       tls_peerdn = string_unprinting(string_copy(big_buffer + 12));
+    #ifndef USE_GNUTLS
+    else if (Ustrncmp(p, "ls_sni", 6) == 0)
+      tls_sni = string_unprinting(string_copy(big_buffer + 9));
+    #endif
     break;
     #endif
 
     break;
     #endif
 
index 7b8229934d63ab688034c865b8a83465a2bc749b..fa4f1b6e2f5a49ed098fd0b7e59f35ed3ffd82fb 100644 (file)
@@ -229,6 +229,9 @@ if (bmi_verdicts != NULL) fprintf(f, "-bmi_verdicts %s\n", bmi_verdicts);
 if (tls_certificate_verified) fprintf(f, "-tls_certificate_verified\n");
 if (tls_cipher != NULL) fprintf(f, "-tls_cipher %s\n", tls_cipher);
 if (tls_peerdn != NULL) fprintf(f, "-tls_peerdn %s\n", string_printing(tls_peerdn));
 if (tls_certificate_verified) fprintf(f, "-tls_certificate_verified\n");
 if (tls_cipher != NULL) fprintf(f, "-tls_cipher %s\n", tls_cipher);
 if (tls_peerdn != NULL) fprintf(f, "-tls_peerdn %s\n", string_printing(tls_peerdn));
+#ifndef USE_GNUTLS
+if (tls_sni != NULL) fprintf(f, "-tls_sni %s\n", string_printing(tls_sni));
+#endif
 #endif
 
 /* To complete the envelope, write out the tree of non-recipients, followed by
 #endif
 
 /* To complete the envelope, write out the tree of non-recipients, followed by
index 5e8c804e5789be729b892f0c6f8e5d7f4389104e..8cc2457e5b6cbe88d6486db9d12813c51ad5b1b6 100644 (file)
@@ -34,6 +34,7 @@ static BOOL verify_callback_called = FALSE;
 static const uschar *sid_ctx = US"exim";
 
 static SSL_CTX *ctx = NULL;
 static const uschar *sid_ctx = US"exim";
 
 static SSL_CTX *ctx = NULL;
+static SSL_CTX *ctx_sni = NULL;
 static SSL *ssl = NULL;
 
 static char ssl_errstring[256];
 static SSL *ssl = NULL;
 
 static char ssl_errstring[256];
@@ -41,8 +42,26 @@ static char ssl_errstring[256];
 static int  ssl_session_timeout = 200;
 static BOOL verify_optional = FALSE;
 
 static int  ssl_session_timeout = 200;
 static BOOL verify_optional = FALSE;
 
+static BOOL    reexpand_tls_files_for_sni = FALSE;
 
 
 
 
+typedef struct tls_ext_ctx_cb {
+  uschar *certificate;
+  uschar *privatekey;
+  uschar *dhparam;
+  /* these are cached from first expand */
+  uschar *server_cipher_list;
+  /* only passed down to tls_error: */
+  host_item *host;
+} tls_ext_ctx_cb;
+
+/* 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 *static_cbinfo = NULL;
+
+static int
+setup_certs(SSL_CTX *sctx, uschar *certs, uschar *crl, host_item *host, BOOL optional);
 
 
 /*************************************************
 
 
 /*************************************************
@@ -201,8 +220,8 @@ return 1;   /* accept */
 *************************************************/
 
 /* The SSL library functions call this from time to time to indicate what they
 *************************************************/
 
 /* 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 the level is high
-enough.
+are doing. We copy the string to the debugging output when TLS debugging has
+been requested.
 
 Arguments:
   s         the SSL connection
 
 Arguments:
   s         the SSL connection
@@ -279,6 +298,145 @@ return yield;
 
 
 
 
 
 
+/*************************************************
+*        Expand key and cert file specs          *
+*************************************************/
+
+/* Called once during tls_init and possibly againt 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 SSL_CTX* to update
+  cbinfo          various parts of session state
+
+Returns:          OK/DEFER/FAIL
+*/
+
+static int
+tls_expand_session_files(SSL_CTX *sctx, const tls_ext_ctx_cb *cbinfo)
+{
+uschar *expanded;
+
+if (cbinfo->certificate == NULL)
+  return OK;
+
+if (Ustrstr(cbinfo->certificate, US"tls_sni"))
+  reexpand_tls_files_for_sni = TRUE;
+
+if (!expand_check(cbinfo->certificate, US"tls_certificate", &expanded))
+  return DEFER;
+
+if (expanded != NULL)
+  {
+  DEBUG(D_tls) debug_printf("tls_certificate file %s\n", expanded);
+  if (!SSL_CTX_use_certificate_chain_file(sctx, CS expanded))
+    return tls_error(string_sprintf(
+      "SSL_CTX_use_certificate_chain_file file=%s", expanded),
+        cbinfo->host, NULL);
+  }
+
+if (cbinfo->privatekey != NULL &&
+    !expand_check(cbinfo->privatekey, US"tls_privatekey", &expanded))
+  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 != NULL && *expanded != 0)
+  {
+  DEBUG(D_tls) debug_printf("tls_privatekey file %s\n", expanded);
+  if (!SSL_CTX_use_PrivateKey_file(sctx, CS expanded, SSL_FILETYPE_PEM))
+    return tls_error(string_sprintf(
+      "SSL_CTX_use_PrivateKey_file file=%s", expanded), cbinfo->host, NULL);
+  }
+
+return OK;
+}
+
+
+
+
+/*************************************************
+*            Callback to handle SNI              *
+*************************************************/
+
+/* Called when acting as server during the TLS session setup if a Server Name
+Indication extension was sent by the client.
+
+API documentation is OpenSSL s_server.c implementation.
+
+Arguments:
+  s               SSL* of the current session
+  ad              unknown (part of OpenSSL API) (unused)
+  arg             Callback of "our" registered data
+
+Returns:          SSL_TLSEXT_ERR_{OK,ALERT_WARNING,ALERT_FATAL,NOACK}
+*/
+
+static int
+tls_servername_cb(SSL *s, int *ad ARG_UNUSED, void *arg);
+/* pre-declared for SSL_CTX_set_tlsext_servername_callback call within func */
+
+static int
+tls_servername_cb(SSL *s, int *ad ARG_UNUSED, void *arg)
+{
+const char *servername = SSL_get_servername(s, TLSEXT_NAMETYPE_host_name);
+const tls_ext_ctx_cb *cbinfo = (tls_ext_ctx_cb *) arg;
+int rc;
+
+if (!servername)
+  return SSL_TLSEXT_ERR_OK;
+
+DEBUG(D_tls) debug_printf("TLS SNI: %s%s\n", servername,
+    reexpand_tls_files_for_sni ? "" : " (unused for certificate selection)");
+
+/* Make the extension value available for expansion */
+tls_sni = servername;
+
+if (!reexpand_tls_files_for_sni)
+  return SSL_TLSEXT_ERR_OK;
+
+/* Can't find an SSL_CTX_clone() or equivalent, so we do it manually;
+not confident that memcpy wouldn't break some internal reference counting.
+Especially since there's a references struct member, which would be off. */
+
+ctx_sni = SSL_CTX_new(SSLv23_server_method());
+if (!ctx_sni)
+  {
+  ERR_error_string(ERR_get_error(), ssl_errstring);
+  DEBUG(D_tls) debug_printf("SSL_CTX_new() failed: %s\n", ssl_errstring);
+  return SSL_TLSEXT_ERR_NOACK;
+  }
+
+/* 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(ctx_sni, SSL_CTX_get_info_callback(ctx));
+SSL_CTX_set_mode(ctx_sni, SSL_CTX_get_mode(ctx));
+SSL_CTX_set_options(ctx_sni, SSL_CTX_get_options(ctx));
+SSL_CTX_set_timeout(ctx_sni, SSL_CTX_get_timeout(ctx));
+SSL_CTX_set_tlsext_servername_callback(ctx_sni, tls_servername_cb);
+SSL_CTX_set_tlsext_servername_arg(ctx_sni, cbinfo);
+if (cbinfo->server_cipher_list)
+  SSL_CTX_set_cipher_list(ctx_sni, CS cbinfo->server_cipher_list);
+
+rc = tls_expand_session_files(ctx_sni, cbinfo);
+if (rc != OK) return SSL_TLSEXT_ERR_NOACK;
+
+rc = setup_certs(ctx_sni, tls_verify_certificates, tls_crl, NULL, FALSE);
+if (rc != OK) return SSL_TLSEXT_ERR_NOACK;
+
+DEBUG(D_tls) debug_printf("Switching SSL context.\n");
+SSL_set_SSL_CTX(s, ctx_sni);
+
+return SSL_TLSEXT_ERR_OK;
+}
+
+
+
+
 /*************************************************
 *            Initialize for TLS                  *
 *************************************************/
 /*************************************************
 *            Initialize for TLS                  *
 *************************************************/
@@ -301,7 +459,15 @@ tls_init(host_item *host, uschar *dhparam, uschar *certificate,
   uschar *privatekey, address_item *addr)
 {
 long init_options;
   uschar *privatekey, address_item *addr)
 {
 long init_options;
+int rc;
 BOOL okay;
 BOOL okay;
+tls_ext_ctx_cb *cbinfo;
+
+cbinfo = store_malloc(sizeof(tls_ext_ctx_cb));
+cbinfo->certificate = certificate;
+cbinfo->privatekey = privatekey;
+cbinfo->dhparam = dhparam;
+cbinfo->host = host;
 
 SSL_load_error_strings();          /* basic set up */
 OpenSSL_add_ssl_algorithms();
 
 SSL_load_error_strings();          /* basic set up */
 OpenSSL_add_ssl_algorithms();
@@ -379,36 +545,16 @@ if (!init_dh(dhparam, host)) return DEFER;
 
 /* Set up certificate and key */
 
 
 /* Set up certificate and key */
 
-if (certificate != NULL)
-  {
-  uschar *expanded;
-  if (!expand_check(certificate, US"tls_certificate", &expanded))
-    return DEFER;
-
-  if (expanded != NULL)
-    {
-    DEBUG(D_tls) debug_printf("tls_certificate file %s\n", expanded);
-    if (!SSL_CTX_use_certificate_chain_file(ctx, CS expanded))
-      return tls_error(string_sprintf(
-        "SSL_CTX_use_certificate_chain_file file=%s", expanded), host, NULL);
-    }
-
-  if (privatekey != NULL &&
-      !expand_check(privatekey, US"tls_privatekey", &expanded))
-    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. */
+rc = tls_expand_session_files(ctx, cbinfo);
+if (rc != OK) return rc;
 
 
-  if (expanded != NULL && *expanded != 0)
-    {
-    DEBUG(D_tls) debug_printf("tls_privatekey file %s\n", expanded);
-    if (!SSL_CTX_use_PrivateKey_file(ctx, CS expanded, SSL_FILETYPE_PEM))
-      return tls_error(string_sprintf(
-        "SSL_CTX_use_PrivateKey_file file=%s", expanded), host, NULL);
-    }
-  }
+/* If we need to handle SNI, do so */
+#if OPENSSL_VERSION_NUMBER >= 0x0090806fL && !defined(OPENSSL_NO_TLSEXT)
+/* 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);
+#endif
 
 /* Set up the RSA callback */
 
 
 /* Set up the RSA callback */
 
@@ -418,6 +564,9 @@ SSL_CTX_set_tmp_rsa_callback(ctx, rsa_callback);
 
 SSL_CTX_set_timeout(ctx, ssl_session_timeout);
 DEBUG(D_tls) debug_printf("Initialized TLS\n");
 
 SSL_CTX_set_timeout(ctx, ssl_session_timeout);
 DEBUG(D_tls) debug_printf("Initialized TLS\n");
+
+static_cbinfo = cbinfo;
+
 return OK;
 }
 
 return OK;
 }
 
@@ -496,6 +645,7 @@ DEBUG(D_tls) debug_printf("Cipher: %s\n", cipherbuf);
 /* Called by both client and server startup
 
 Arguments:
 /* Called by both client and server startup
 
 Arguments:
+  sctx          SSL_CTX* to initialise
   certs         certs file or NULL
   crl           CRL file or NULL
   host          NULL in a server; the remote host in a client
   certs         certs file or NULL
   crl           CRL file or NULL
   host          NULL in a server; the remote host in a client
@@ -506,7 +656,7 @@ Returns:        OK/DEFER/FAIL
 */
 
 static int
 */
 
 static int
-setup_certs(uschar *certs, uschar *crl, host_item *host, BOOL optional)
+setup_certs(SSL_CTX *sctx, uschar *certs, uschar *crl, host_item *host, BOOL optional)
 {
 uschar *expcerts, *expcrl;
 
 {
 uschar *expcerts, *expcrl;
 
@@ -516,7 +666,7 @@ if (!expand_check(certs, US"tls_verify_certificates", &expcerts))
 if (expcerts != NULL)
   {
   struct stat statbuf;
 if (expcerts != NULL)
   {
   struct stat statbuf;
-  if (!SSL_CTX_set_default_verify_paths(ctx))
+  if (!SSL_CTX_set_default_verify_paths(sctx))
     return tls_error(US"SSL_CTX_set_default_verify_paths", host, NULL);
 
   if (Ustat(expcerts, &statbuf) < 0)
     return tls_error(US"SSL_CTX_set_default_verify_paths", host, NULL);
 
   if (Ustat(expcerts, &statbuf) < 0)
@@ -539,12 +689,12 @@ if (expcerts != NULL)
     says no certificate was supplied.) But this is better. */
 
     if ((file == NULL || statbuf.st_size > 0) &&
     says no certificate was supplied.) But this is better. */
 
     if ((file == NULL || statbuf.st_size > 0) &&
-          !SSL_CTX_load_verify_locations(ctx, CS file, CS dir))
+          !SSL_CTX_load_verify_locations(sctx, CS file, CS dir))
       return tls_error(US"SSL_CTX_load_verify_locations", host, NULL);
 
     if (file != NULL)
       {
       return tls_error(US"SSL_CTX_load_verify_locations", host, NULL);
 
     if (file != NULL)
       {
-      SSL_CTX_set_client_CA_list(ctx, SSL_load_client_CA_file(CS file));
+      SSL_CTX_set_client_CA_list(sctx, SSL_load_client_CA_file(CS file));
       }
     }
 
       }
     }
 
@@ -576,7 +726,7 @@ if (expcerts != NULL)
       {
       /* is it a file or directory? */
       uschar *file, *dir;
       {
       /* is it a file or directory? */
       uschar *file, *dir;
-      X509_STORE *cvstore = SSL_CTX_get_cert_store(ctx);
+      X509_STORE *cvstore = SSL_CTX_get_cert_store(sctx);
       if ((statbufcrl.st_mode & S_IFMT) == S_IFDIR)
         {
         file = NULL;
       if ((statbufcrl.st_mode & S_IFMT) == S_IFDIR)
         {
         file = NULL;
@@ -603,7 +753,7 @@ if (expcerts != NULL)
 
   /* If verification is optional, don't fail if no certificate */
 
 
   /* If verification is optional, don't fail if no certificate */
 
-  SSL_CTX_set_verify(ctx,
+  SSL_CTX_set_verify(sctx,
     SSL_VERIFY_PEER | (optional? 0 : SSL_VERIFY_FAIL_IF_NO_PEER_CERT),
     verify_callback);
   }
     SSL_VERIFY_PEER | (optional? 0 : SSL_VERIFY_FAIL_IF_NO_PEER_CERT),
     verify_callback);
   }
@@ -641,6 +791,7 @@ tls_server_start(uschar *require_ciphers, uschar *require_mac,
 {
 int rc;
 uschar *expciphers;
 {
 int rc;
 uschar *expciphers;
+tls_ext_ctx_cb *cbinfo;
 
 /* Check for previous activation */
 
 
 /* Check for previous activation */
 
@@ -656,6 +807,7 @@ the error. */
 
 rc = tls_init(NULL, tls_dhparam, tls_certificate, tls_privatekey, NULL);
 if (rc != OK) return rc;
 
 rc = tls_init(NULL, tls_dhparam, tls_certificate, tls_privatekey, NULL);
 if (rc != OK) return rc;
+cbinfo = static_cbinfo;
 
 if (!expand_check(require_ciphers, US"tls_require_ciphers", &expciphers))
   return FAIL;
 
 if (!expand_check(require_ciphers, US"tls_require_ciphers", &expciphers))
   return FAIL;
@@ -671,6 +823,7 @@ if (expciphers != NULL)
   DEBUG(D_tls) debug_printf("required ciphers: %s\n", expciphers);
   if (!SSL_CTX_set_cipher_list(ctx, CS expciphers))
     return tls_error(US"SSL_CTX_set_cipher_list", NULL, NULL);
   DEBUG(D_tls) debug_printf("required ciphers: %s\n", expciphers);
   if (!SSL_CTX_set_cipher_list(ctx, CS expciphers))
     return tls_error(US"SSL_CTX_set_cipher_list", NULL, NULL);
+  cbinfo->server_cipher_list = expciphers;
   }
 
 /* If this is a host for which certificate verification is mandatory or
   }
 
 /* If this is a host for which certificate verification is mandatory or
@@ -681,13 +834,13 @@ verify_callback_called = FALSE;
 
 if (verify_check_host(&tls_verify_hosts) == OK)
   {
 
 if (verify_check_host(&tls_verify_hosts) == OK)
   {
-  rc = setup_certs(tls_verify_certificates, tls_crl, NULL, FALSE);
+  rc = setup_certs(ctx, tls_verify_certificates, tls_crl, NULL, FALSE);
   if (rc != OK) return rc;
   verify_optional = FALSE;
   }
 else if (verify_check_host(&tls_try_verify_hosts) == OK)
   {
   if (rc != OK) return rc;
   verify_optional = FALSE;
   }
 else if (verify_check_host(&tls_try_verify_hosts) == OK)
   {
-  rc = setup_certs(tls_verify_certificates, tls_crl, NULL, TRUE);
+  rc = setup_certs(ctx, tls_verify_certificates, tls_crl, NULL, TRUE);
   if (rc != OK) return rc;
   verify_optional = TRUE;
   }
   if (rc != OK) return rc;
   verify_optional = TRUE;
   }
@@ -839,7 +992,7 @@ if (expciphers != NULL)
     return tls_error(US"SSL_CTX_set_cipher_list", host, NULL);
   }
 
     return tls_error(US"SSL_CTX_set_cipher_list", host, NULL);
   }
 
-rc = setup_certs(verify_certs, crl, host, FALSE);
+rc = setup_certs(ctx, verify_certs, crl, host, FALSE);
 if (rc != OK) return rc;
 
 if ((ssl = SSL_new(ctx)) == NULL) return tls_error(US"SSL_new", host, NULL);
 if (rc != OK) return rc;
 
 if ((ssl = SSL_new(ctx)) == NULL) return tls_error(US"SSL_new", host, NULL);