Harden plaintext authenticator
[users/heiko/exim.git] / src / src / tls-openssl.c
index fb59217da87031de1a4ebabe0cf4f6baaeaeff2f..5a5e1464ba5665d7518e251c320764fb99f35505 100644 (file)
@@ -51,25 +51,29 @@ functions from the OpenSSL library. */
 # define EXIM_HAVE_RAND_PSEUDO
 #endif
 #if (OPENSSL_VERSION_NUMBER >= 0x0090800fL) && !defined(OPENSSL_NO_SHA256)
 # define EXIM_HAVE_RAND_PSEUDO
 #endif
 #if (OPENSSL_VERSION_NUMBER >= 0x0090800fL) && !defined(OPENSSL_NO_SHA256)
-# define EXIM_HAVE_SHA256      /*MMMM*/
+# define EXIM_HAVE_SHA256
 #endif
 
 #endif
 
-/*
- * X509_check_host provides sane certificate hostname checking, but was added
- * to OpenSSL late, after other projects forked off the code-base.  So in
- * addition to guarding against the base version number, beware that LibreSSL
- * does not (at this time) support this function.
- *
- * If LibreSSL gains a different API, perhaps via libtls, then we'll probably
- * opt to disentangle and ask a LibreSSL user to provide glue for a third
- * crypto provider for libtls instead of continuing to tie the OpenSSL glue
- * into even twistier knots.  If LibreSSL gains the same API, we can just
- * change this guard and punt the issue for a while longer.
- */
+/* X509_check_host provides sane certificate hostname checking, but was added
+to OpenSSL late, after other projects forked off the code-base.  So in
+addition to guarding against the base version number, beware that LibreSSL
+does not (at this time) support this function.
+
+If LibreSSL gains a different API, perhaps via libtls, then we'll probably
+opt to disentangle and ask a LibreSSL user to provide glue for a third
+crypto provider for libtls instead of continuing to tie the OpenSSL glue
+into even twistier knots.  If LibreSSL gains the same API, we can just
+change this guard and punt the issue for a while longer. */
+
 #ifndef LIBRESSL_VERSION_NUMBER
 # if OPENSSL_VERSION_NUMBER >= 0x010100000L
 #  define EXIM_HAVE_OPENSSL_CHECKHOST
 #  define EXIM_HAVE_OPENSSL_DH_BITS
 #ifndef LIBRESSL_VERSION_NUMBER
 # if OPENSSL_VERSION_NUMBER >= 0x010100000L
 #  define EXIM_HAVE_OPENSSL_CHECKHOST
 #  define EXIM_HAVE_OPENSSL_DH_BITS
+#  define EXIM_HAVE_OPENSSL_TLS_METHOD
+#  define EXIM_HAVE_OPENSSL_KEYLOG
+#  define EXIM_HAVE_OPENSSL_CIPHER_GET_ID
+# else
+#  define EXIM_NEED_OPENSSL_INIT
 # endif
 # if OPENSSL_VERSION_NUMBER >= 0x010000000L \
     && (OPENSSL_VERSION_NUMBER & 0x0000ff000L) >= 0x000002000L
 # endif
 # if OPENSSL_VERSION_NUMBER >= 0x010000000L \
     && (OPENSSL_VERSION_NUMBER & 0x0000ff000L) >= 0x000002000L
@@ -81,7 +85,7 @@ functions from the OpenSSL library. */
     || LIBRESSL_VERSION_NUMBER >= 0x20010000L
 # if !defined(OPENSSL_NO_ECDH)
 #  if OPENSSL_VERSION_NUMBER >= 0x0090800fL
     || LIBRESSL_VERSION_NUMBER >= 0x20010000L
 # if !defined(OPENSSL_NO_ECDH)
 #  if OPENSSL_VERSION_NUMBER >= 0x0090800fL
-#   define EXIM_HAVE_ECDH      /*MMMM*/
+#   define EXIM_HAVE_ECDH
 #  endif
 #  if OPENSSL_VERSION_NUMBER >= 0x10002000L
 #   define EXIM_HAVE_OPENSSL_EC_NIST2NID
 #  endif
 #  if OPENSSL_VERSION_NUMBER >= 0x10002000L
 #   define EXIM_HAVE_OPENSSL_EC_NIST2NID
@@ -89,6 +93,14 @@ functions from the OpenSSL library. */
 # endif
 #endif
 
 # endif
 #endif
 
+#ifndef LIBRESSL_VERSION_NUMBER
+# if OPENSSL_VERSION_NUMBER >= 0x010101000L
+#  define OPENSSL_HAVE_KEYLOG_CB
+#  define OPENSSL_HAVE_NUM_TICKETS
+#  define EXIM_HAVE_OPENSSL_CIPHER_STD_NAME
+# endif
+#endif
+
 #if !defined(EXIM_HAVE_OPENSSL_TLSEXT) && !defined(DISABLE_OCSP)
 # warning "OpenSSL library version too old; define DISABLE_OCSP in Makefile"
 # define DISABLE_OCSP
 #if !defined(EXIM_HAVE_OPENSSL_TLSEXT) && !defined(DISABLE_OCSP)
 # warning "OpenSSL library version too old; define DISABLE_OCSP in Makefile"
 # define DISABLE_OCSP
@@ -98,6 +110,148 @@ functions from the OpenSSL library. */
 # include <openssl/x509v3.h>
 #endif
 
 # include <openssl/x509v3.h>
 #endif
 
+#ifndef EXIM_HAVE_OPENSSL_CIPHER_STD_NAME
+# ifndef EXIM_HAVE_OPENSSL_CIPHER_GET_ID
+#  define SSL_CIPHER_get_id(c) (c->id)
+# endif
+# include "tls-cipher-stdname.c"
+#endif
+
+/*************************************************
+*        OpenSSL option parse                    *
+*************************************************/
+
+typedef struct exim_openssl_option {
+  uschar *name;
+  long    value;
+} exim_openssl_option;
+/* We could use a macro to expand, but we need the ifdef and not all the
+options document which version they were introduced in.  Policylet: include
+all options unless explicitly for DTLS, let the administrator choose which
+to apply.
+
+This list is current as of:
+  ==>  1.0.1b  <==
+Plus SSL_OP_SAFARI_ECDHE_ECDSA_BUG from 2013-June patch/discussion on openssl-dev
+Plus SSL_OP_NO_TLSv1_3 for 1.1.2-dev
+*/
+static exim_openssl_option exim_openssl_options[] = {
+/* KEEP SORTED ALPHABETICALLY! */
+#ifdef SSL_OP_ALL
+  { US"all", 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_CIPHER_SERVER_PREFERENCE
+  { US"cipher_server_preference", SSL_OP_CIPHER_SERVER_PREFERENCE },
+#endif
+#ifdef SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS
+  { US"dont_insert_empty_fragments", SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS },
+#endif
+#ifdef SSL_OP_EPHEMERAL_RSA
+  { US"ephemeral_rsa", SSL_OP_EPHEMERAL_RSA },
+#endif
+#ifdef SSL_OP_LEGACY_SERVER_CONNECT
+  { US"legacy_server_connect", SSL_OP_LEGACY_SERVER_CONNECT },
+#endif
+#ifdef SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER
+  { US"microsoft_big_sslv3_buffer", SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER },
+#endif
+#ifdef SSL_OP_MICROSOFT_SESS_ID_BUG
+  { US"microsoft_sess_id_bug", SSL_OP_MICROSOFT_SESS_ID_BUG },
+#endif
+#ifdef SSL_OP_MSIE_SSLV2_RSA_PADDING
+  { US"msie_sslv2_rsa_padding", SSL_OP_MSIE_SSLV2_RSA_PADDING },
+#endif
+#ifdef SSL_OP_NETSCAPE_CHALLENGE_BUG
+  { US"netscape_challenge_bug", SSL_OP_NETSCAPE_CHALLENGE_BUG },
+#endif
+#ifdef SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG
+  { US"netscape_reuse_cipher_change_bug", SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG },
+#endif
+#ifdef SSL_OP_NO_COMPRESSION
+  { US"no_compression", SSL_OP_NO_COMPRESSION },
+#endif
+#ifdef SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION
+  { US"no_session_resumption_on_renegotiation", SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION },
+#endif
+#ifdef SSL_OP_NO_SSLv2
+  { US"no_sslv2", SSL_OP_NO_SSLv2 },
+#endif
+#ifdef SSL_OP_NO_SSLv3
+  { US"no_sslv3", SSL_OP_NO_SSLv3 },
+#endif
+#ifdef SSL_OP_NO_TICKET
+  { US"no_ticket", SSL_OP_NO_TICKET },
+#endif
+#ifdef SSL_OP_NO_TLSv1
+  { US"no_tlsv1", SSL_OP_NO_TLSv1 },
+#endif
+#ifdef SSL_OP_NO_TLSv1_1
+#if SSL_OP_NO_TLSv1_1 == 0x00000400L
+  /* Error in chosen value in 1.0.1a; see first item in CHANGES for 1.0.1b */
+#warning OpenSSL 1.0.1a uses a bad value for SSL_OP_NO_TLSv1_1, ignoring
+#else
+  { US"no_tlsv1_1", SSL_OP_NO_TLSv1_1 },
+#endif
+#endif
+#ifdef SSL_OP_NO_TLSv1_2
+  { US"no_tlsv1_2", SSL_OP_NO_TLSv1_2 },
+#endif
+#ifdef SSL_OP_NO_TLSv1_3
+  { US"no_tlsv1_3", SSL_OP_NO_TLSv1_3 },
+#endif
+#ifdef SSL_OP_SAFARI_ECDHE_ECDSA_BUG
+  { US"safari_ecdhe_ecdsa_bug", SSL_OP_SAFARI_ECDHE_ECDSA_BUG },
+#endif
+#ifdef SSL_OP_SINGLE_DH_USE
+  { US"single_dh_use", SSL_OP_SINGLE_DH_USE },
+#endif
+#ifdef SSL_OP_SINGLE_ECDH_USE
+  { US"single_ecdh_use", SSL_OP_SINGLE_ECDH_USE },
+#endif
+#ifdef SSL_OP_SSLEAY_080_CLIENT_DH_BUG
+  { US"ssleay_080_client_dh_bug", SSL_OP_SSLEAY_080_CLIENT_DH_BUG },
+#endif
+#ifdef SSL_OP_SSLREF2_REUSE_CERT_TYPE_BUG
+  { US"sslref2_reuse_cert_type_bug", SSL_OP_SSLREF2_REUSE_CERT_TYPE_BUG },
+#endif
+#ifdef SSL_OP_TLS_BLOCK_PADDING_BUG
+  { US"tls_block_padding_bug", SSL_OP_TLS_BLOCK_PADDING_BUG },
+#endif
+#ifdef SSL_OP_TLS_D5_BUG
+  { US"tls_d5_bug", SSL_OP_TLS_D5_BUG },
+#endif
+#ifdef SSL_OP_TLS_ROLLBACK_BUG
+  { US"tls_rollback_bug", SSL_OP_TLS_ROLLBACK_BUG },
+#endif
+};
+
+#ifndef MACRO_PREDEF
+static int exim_openssl_options_size = nelem(exim_openssl_options);
+#endif
+
+#ifdef MACRO_PREDEF
+void
+options_tls(void)
+{
+uschar buf[64];
+
+for (struct exim_openssl_option * o = exim_openssl_options;
+     o < exim_openssl_options + nelem(exim_openssl_options); o++)
+  {
+  /* Trailing X is workaround for problem with _OPT_OPENSSL_NO_TLSV1
+  being a ".ifdef _OPT_OPENSSL_NO_TLSV1_3" match */
+
+  spf(buf, sizeof(buf), US"_OPT_OPENSSL_%T_X", o->name);
+  builtin_macro_create(buf);
+  }
+}
+#else
+
+/******************************************************************************/
+
 /* Structure for collecting random data for seeding. */
 
 typedef struct randstuff {
 /* Structure for collecting random data for seeding. */
 
 typedef struct randstuff {
@@ -116,7 +270,9 @@ static const uschar *sid_ctx = US"exim";
 Simple case: client, `client_ctx`
  As a client, we can be doing a callout or cut-through delivery while receiving
  a message.  So we have a client context, which should have options initialised
 Simple case: client, `client_ctx`
  As a client, we can be doing a callout or cut-through delivery while receiving
  a message.  So we have a client context, which should have options initialised
- from the SMTP Transport.
+ from the SMTP Transport.  We may also concurrently want to make TLS connections
+ to utility daemons, so client-contexts are allocated and passed around in call
+ args rather than using a gobal.
 
 Server:
  There are two cases: with and without ServerNameIndication from the client.
 
 Server:
  There are two cases: with and without ServerNameIndication from the client.
@@ -130,9 +286,13 @@ Server:
  configuration.
 */
 
  configuration.
 */
 
-static SSL_CTX *client_ctx = NULL;
+typedef struct {
+  SSL_CTX *    ctx;
+  SSL *                ssl;
+  gstring *    corked;
+} exim_openssl_client_tls_ctx;
+
 static SSL_CTX *server_ctx = NULL;
 static SSL_CTX *server_ctx = NULL;
-static SSL     *client_ssl = NULL;
 static SSL     *server_ssl = NULL;
 
 #ifdef EXIM_HAVE_OPENSSL_TLSEXT
 static SSL     *server_ssl = NULL;
 
 #ifdef EXIM_HAVE_OPENSSL_TLSEXT
@@ -222,11 +382,13 @@ tls_error(uschar * prefix, const host_item * host, uschar * msg, uschar ** errst
 {
 if (!msg)
   {
 {
 if (!msg)
   {
-  ERR_error_string(ERR_get_error(), ssl_errstring);
+  ERR_error_string_n(ERR_get_error(), ssl_errstring, sizeof(ssl_errstring));
   msg = US ssl_errstring;
   }
 
   msg = US ssl_errstring;
   }
 
-if (errstr) *errstr = string_sprintf("(%s): %s", prefix, msg);
+msg = string_sprintf("(%s): %s", prefix, msg);
+DEBUG(D_tls) debug_printf("TLS error '%s'\n", msg);
+if (errstr) *errstr = msg;
 return host ? FAIL : DEFER;
 }
 
 return host ? FAIL : DEFER;
 }
 
@@ -266,7 +428,7 @@ if (!(rsa_key = RSA_generate_key(keylength, RSA_F4, NULL, NULL)))
 #endif
 
   {
 #endif
 
   {
-  ERR_error_string(ERR_get_error(), ssl_errstring);
+  ERR_error_string_n(ERR_get_error(), ssl_errstring, sizeof(ssl_errstring));
   log_write(0, LOG_MAIN|LOG_PANIC, "TLS error (RSA_generate_key): %s",
     ssl_errstring);
   return NULL;
   log_write(0, LOG_MAIN|LOG_PANIC, "TLS error (RSA_generate_key): %s",
     ssl_errstring);
   return NULL;
@@ -282,18 +444,19 @@ void
 x509_store_dump_cert_s_names(X509_STORE * store)
 {
 STACK_OF(X509_OBJECT) * roots= store->objs;
 x509_store_dump_cert_s_names(X509_STORE * store)
 {
 STACK_OF(X509_OBJECT) * roots= store->objs;
-int i;
 static uschar name[256];
 
 static uschar name[256];
 
-for(i= 0; i<sk_X509_OBJECT_num(roots); i++)
+for (int i= 0; i < sk_X509_OBJECT_num(roots); i++)
   {
   X509_OBJECT * tmp_obj= sk_X509_OBJECT_value(roots, i);
   if(tmp_obj->type == X509_LU_X509)
     {
   {
   X509_OBJECT * tmp_obj= sk_X509_OBJECT_value(roots, i);
   if(tmp_obj->type == X509_LU_X509)
     {
-    X509 * current_cert= tmp_obj->data.x509;
-    X509_NAME_oneline(X509_get_subject_name(current_cert), CS name, sizeof(name));
-       name[sizeof(name)-1] = '\0';
-    debug_printf(" %s\n", name);
+    X509_NAME * sn = X509_get_subject_name(tmp_obj->data.x509);
+    if (X509_NAME_oneline(sn, CS name, sizeof(name)))
+      {
+      name[sizeof(name)-1] = '\0';
+      debug_printf(" %s\n", name);
+      }
     }
   }
 }
     }
   }
 }
@@ -374,14 +537,20 @@ Returns:     0 if verification should fail, otherwise 1
 */
 
 static int
 */
 
 static int
-verify_callback(int preverify_ok, X509_STORE_CTX *x509ctx,
-  tls_support *tlsp, BOOL *calledp, BOOL *optionalp)
+verify_callback(int preverify_ok, X509_STORE_CTX * x509ctx,
+  tls_support * tlsp, BOOL * calledp, BOOL * optionalp)
 {
 X509 * cert = X509_STORE_CTX_get_current_cert(x509ctx);
 int depth = X509_STORE_CTX_get_error_depth(x509ctx);
 uschar dn[256];
 
 {
 X509 * cert = X509_STORE_CTX_get_current_cert(x509ctx);
 int depth = X509_STORE_CTX_get_error_depth(x509ctx);
 uschar dn[256];
 
-X509_NAME_oneline(X509_get_subject_name(cert), CS dn, sizeof(dn));
+if (!X509_NAME_oneline(X509_get_subject_name(cert), CS dn, sizeof(dn)))
+  {
+  DEBUG(D_tls) debug_printf("X509_NAME_oneline() error\n");
+  log_write(0, LOG_MAIN, "[%s] SSL verify error: internal error",
+    tlsp == &tls_out ? deliver_host_address : sender_host_address);
+  return 0;
+  }
 dn[sizeof(dn)-1] = '\0';
 
 if (preverify_ok == 0)
 dn[sizeof(dn)-1] = '\0';
 
 if (preverify_ok == 0)
@@ -430,7 +599,7 @@ else
 
   if (  tlsp == &tls_out
      && ((verify_cert_hostnames = client_static_cbinfo->verify_cert_hostnames)))
 
   if (  tlsp == &tls_out
      && ((verify_cert_hostnames = client_static_cbinfo->verify_cert_hostnames)))
-       /* client, wanting hostname check */
+       /* client, wanting hostname check */
     {
 
 #ifdef EXIM_HAVE_OPENSSL_CHECKHOST
     {
 
 #ifdef EXIM_HAVE_OPENSSL_CHECKHOST
@@ -527,7 +696,13 @@ int depth = X509_STORE_CTX_get_error_depth(x509ctx);
 BOOL dummy_called, optional = FALSE;
 #endif
 
 BOOL dummy_called, optional = FALSE;
 #endif
 
-X509_NAME_oneline(X509_get_subject_name(cert), CS dn, sizeof(dn));
+if (!X509_NAME_oneline(X509_get_subject_name(cert), CS dn, sizeof(dn)))
+  {
+  DEBUG(D_tls) debug_printf("X509_NAME_oneline() error\n");
+  log_write(0, LOG_MAIN, "[%s] SSL verify error: internal error",
+    deliver_host_address);
+  return 0;
+  }
 dn[sizeof(dn)-1] = '\0';
 
 DEBUG(D_tls) debug_printf("verify_callback_client_dane: %s depth %d %s\n",
 dn[sizeof(dn)-1] = '\0';
 
 DEBUG(D_tls) debug_printf("verify_callback_client_dane: %s depth %d %s\n",
@@ -588,10 +763,42 @@ Returns:    nothing
 static void
 info_callback(SSL *s, int where, int ret)
 {
 static void
 info_callback(SSL *s, int where, int ret)
 {
-where = where;
-ret = ret;
-DEBUG(D_tls) debug_printf("SSL info: %s\n", SSL_state_string_long(s));
+DEBUG(D_tls)
+  {
+  const uschar * str;
+
+  if (where & SSL_ST_CONNECT)
+     str = US"SSL_connect";
+  else if (where & SSL_ST_ACCEPT)
+     str = US"SSL_accept";
+  else
+     str = US"SSL info (undefined)";
+
+  if (where & SSL_CB_LOOP)
+     debug_printf("%s: %s\n", str, SSL_state_string_long(s));
+  else if (where & SSL_CB_ALERT)
+    debug_printf("SSL3 alert %s:%s:%s\n",
+         str = where & SSL_CB_READ ? US"read" : US"write",
+         SSL_alert_type_string_long(ret), SSL_alert_desc_string_long(ret));
+  else if (where & SSL_CB_EXIT)
+     if (ret == 0)
+       debug_printf("%s: failed in %s\n", str, SSL_state_string_long(s));
+     else if (ret < 0)
+       debug_printf("%s: error in %s\n", str, SSL_state_string_long(s));
+  else if (where & SSL_CB_HANDSHAKE_START)
+     debug_printf("%s: hshake start: %s\n", str, SSL_state_string_long(s));
+  else if (where & SSL_CB_HANDSHAKE_DONE)
+     debug_printf("%s: hshake done: %s\n", str, SSL_state_string_long(s));
+  }
+}
+
+#ifdef OPENSSL_HAVE_KEYLOG_CB
+static void
+keylog_callback(const SSL *ssl, const char *line)
+{
+DEBUG(D_tls) debug_printf("%.200s\n", line);
 }
 }
+#endif
 
 
 
 
 
 
@@ -913,7 +1120,7 @@ if ((i = OCSP_basic_verify(basic_response, sk, NULL, verify_flags)) < 0)
   {
   DEBUG(D_tls)
     {
   {
   DEBUG(D_tls)
     {
-    ERR_error_string(ERR_get_error(), ssl_errstring);
+    ERR_error_string_n(ERR_get_error(), ssl_errstring, sizeof(ssl_errstring));
     debug_printf("OCSP response verify failure: %s\n", US ssl_errstring);
     }
   goto bad;
     debug_printf("OCSP response verify failure: %s\n", US ssl_errstring);
     }
   goto bad;
@@ -954,11 +1161,10 @@ supply_response:
 return;
 
 bad:
 return;
 
 bad:
-  if (running_in_test_harness)
+  if (f.running_in_test_harness)
     {
     extern char ** environ;
     {
     extern char ** environ;
-    uschar ** p;
-    if (environ) for (p = USS environ; *p; p++)
+    if (environ) for (uschar ** p = USS environ; *p; p++)
       if (Ustrncmp(*p, "EXIM_TESTHARNESS_DISABLE_OCSPVALIDITYCHECK", 42) == 0)
        {
        DEBUG(D_tls) debug_printf("Supplying known bad OCSP response\n");
       if (Ustrncmp(*p, "EXIM_TESTHARNESS_DISABLE_OCSPVALIDITYCHECK", 42) == 0)
        {
        DEBUG(D_tls) debug_printf("Supplying known bad OCSP response\n");
@@ -992,7 +1198,7 @@ if (!(x509 = X509_new()))
   goto err;
 
 where = US"generating pkey";
   goto err;
 
 where = US"generating pkey";
-if (!(rsa = rsa_callback(NULL, 0, 1024)))
+if (!(rsa = rsa_callback(NULL, 0, 2048)))
   goto err;
 
 where = US"assigning pkey";
   goto err;
 
 where = US"assigning pkey";
@@ -1000,7 +1206,7 @@ if (!EVP_PKEY_assign_RSA(pkey, rsa))
   goto err;
 
 X509_set_version(x509, 2);                             /* N+1 - version 3 */
   goto err;
 
 X509_set_version(x509, 2);                             /* N+1 - version 3 */
-ASN1_INTEGER_set(X509_get_serialNumber(x509), 0);
+ASN1_INTEGER_set(X509_get_serialNumber(x509), 1);
 X509_gmtime_adj(X509_get_notBefore(x509), 0);
 X509_gmtime_adj(X509_get_notAfter(x509), (long)60 * 60);       /* 1 hour */
 X509_set_pubkey(x509, pkey);
 X509_gmtime_adj(X509_get_notBefore(x509), 0);
 X509_gmtime_adj(X509_get_notAfter(x509), (long)60 * 60);       /* 1 hour */
 X509_set_pubkey(x509, pkey);
@@ -1088,7 +1294,7 @@ if (!cbinfo->certificate)
   {
   if (!cbinfo->is_server)              /* client */
     return OK;
   {
   if (!cbinfo->is_server)              /* client */
     return OK;
-                                       /* server */
+                                       /* server */
   if (tls_install_selfsign(sctx, errstr) != OK)
     return DEFER;
   }
   if (tls_install_selfsign(sctx, errstr) != OK)
     return DEFER;
   }
@@ -1120,8 +1326,8 @@ else
       if ((err = tls_add_certfile(sctx, cbinfo, expanded, errstr)))
        return err;
 
       if ((err = tls_add_certfile(sctx, cbinfo, expanded, errstr)))
        return err;
 
-  if (cbinfo->privatekey != NULL &&
-      !expand_check(cbinfo->privatekey, US"tls_privatekey", &expanded, errstr))
+  if (  cbinfo->privatekey
+     && !expand_check(cbinfo->privatekey, US"tls_privatekey", &expanded, errstr))
     return DEFER;
 
   /* If expansion was forced to fail, key_expanded will be NULL. If the result
     return DEFER;
 
   /* If expansion was forced to fail, key_expanded will be NULL. If the result
@@ -1216,11 +1422,15 @@ if (!reexpand_tls_files_for_sni)
 not confident that memcpy wouldn't break some internal reference counting.
 Especially since there's a references struct member, which would be off. */
 
 not confident that memcpy wouldn't break some internal reference counting.
 Especially since there's a references struct member, which would be off. */
 
+#ifdef EXIM_HAVE_OPENSSL_TLS_METHOD
+if (!(server_sni = SSL_CTX_new(TLS_server_method())))
+#else
 if (!(server_sni = SSL_CTX_new(SSLv23_server_method())))
 if (!(server_sni = SSL_CTX_new(SSLv23_server_method())))
+#endif
   {
   {
-  ERR_error_string(ERR_get_error(), ssl_errstring);
+  ERR_error_string_n(ERR_get_error(), ssl_errstring, sizeof(ssl_errstring));
   DEBUG(D_tls) debug_printf("SSL_CTX_new() failed: %s\n", ssl_errstring);
   DEBUG(D_tls) debug_printf("SSL_CTX_new() failed: %s\n", ssl_errstring);
-  return SSL_TLSEXT_ERR_NOACK;
+  goto bad;
   }
 
 /* Not sure how many of these are actually needed, since SSL object
   }
 
 /* Not sure how many of these are actually needed, since SSL object
@@ -1236,10 +1446,12 @@ SSL_CTX_set_tlsext_servername_arg(server_sni, cbinfo);
 if (  !init_dh(server_sni, cbinfo->dhparam, NULL, &dummy_errstr)
    || !init_ecdh(server_sni, NULL, &dummy_errstr)
    )
 if (  !init_dh(server_sni, cbinfo->dhparam, NULL, &dummy_errstr)
    || !init_ecdh(server_sni, NULL, &dummy_errstr)
    )
-  return SSL_TLSEXT_ERR_NOACK;
+  goto bad;
+
+if (  cbinfo->server_cipher_list
+   && !SSL_CTX_set_cipher_list(server_sni, CS cbinfo->server_cipher_list))
+  goto bad;
 
 
-if (cbinfo->server_cipher_list)
-  SSL_CTX_set_cipher_list(server_sni, CS cbinfo->server_cipher_list);
 #ifndef DISABLE_OCSP
 if (cbinfo->u_ocsp.server.file)
   {
 #ifndef DISABLE_OCSP
 if (cbinfo->u_ocsp.server.file)
   {
@@ -1250,17 +1462,18 @@ if (cbinfo->u_ocsp.server.file)
 
 if ((rc = setup_certs(server_sni, tls_verify_certificates, tls_crl, NULL, FALSE,
                      verify_callback_server, &dummy_errstr)) != OK)
 
 if ((rc = setup_certs(server_sni, tls_verify_certificates, tls_crl, NULL, FALSE,
                      verify_callback_server, &dummy_errstr)) != OK)
-  return SSL_TLSEXT_ERR_NOACK;
+  goto bad;
 
 /* do this after setup_certs, because this can require the certs for verifying
 OCSP information. */
 if ((rc = tls_expand_session_files(server_sni, cbinfo, &dummy_errstr)) != OK)
 
 /* do this after setup_certs, because this can require the certs for verifying
 OCSP information. */
 if ((rc = tls_expand_session_files(server_sni, cbinfo, &dummy_errstr)) != OK)
-  return SSL_TLSEXT_ERR_NOACK;
+  goto bad;
 
 DEBUG(D_tls) debug_printf("Switching SSL context.\n");
 SSL_set_SSL_CTX(s, server_sni);
 
 DEBUG(D_tls) debug_printf("Switching SSL context.\n");
 SSL_set_SSL_CTX(s, server_sni);
-
 return SSL_TLSEXT_ERR_OK;
 return SSL_TLSEXT_ERR_OK;
+
+bad: return SSL_TLSEXT_ERR_ALERT_FATAL;
 }
 #endif /* EXIM_HAVE_OPENSSL_TLSEXT */
 
 }
 #endif /* EXIM_HAVE_OPENSSL_TLSEXT */
 
@@ -1528,8 +1741,10 @@ 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();
 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
 
 #ifdef EXIM_HAVE_SHA256
 /* SHA256 is becoming ever more popular. This makes sure it gets added to the
@@ -1545,7 +1760,11 @@ when OpenSSL is built without SSLv2 support.
 By disabling with openssl_options, we can let admins re-enable with the
 existing knob. */
 
 By disabling with openssl_options, we can let admins re-enable with the
 existing knob. */
 
+#ifdef EXIM_HAVE_OPENSSL_TLS_METHOD
+if (!(ctx = SSL_CTX_new(host ? TLS_client_method() : TLS_server_method())))
+#else
 if (!(ctx = SSL_CTX_new(host ? SSLv23_client_method() : SSLv23_server_method())))
 if (!(ctx = SSL_CTX_new(host ? SSLv23_client_method() : SSLv23_server_method())))
+#endif
   return tls_error(US"SSL_CTX_new", host, NULL, errstr);
 
 /* It turns out that we need to seed the random number generator this early in
   return tls_error(US"SSL_CTX_new", host, NULL, errstr);
 
 /* It turns out that we need to seed the random number generator this early in
@@ -1575,6 +1794,9 @@ if (!RAND_status())
 level. */
 
 DEBUG(D_tls) SSL_CTX_set_info_callback(ctx, (void (*)())info_callback);
 level. */
 
 DEBUG(D_tls) SSL_CTX_set_info_callback(ctx, (void (*)())info_callback);
+#ifdef OPENSSL_HAVE_KEYLOG_CB
+DEBUG(D_tls) SSL_CTX_set_keylog_callback(ctx, (void (*)())keylog_callback);
+#endif
 
 /* Automatically re-try reads/writes after renegotiation. */
 (void) SSL_CTX_set_mode(ctx, SSL_MODE_AUTO_RETRY);
 
 /* Automatically re-try reads/writes after renegotiation. */
 (void) SSL_CTX_set_mode(ctx, SSL_MODE_AUTO_RETRY);
@@ -1601,6 +1823,10 @@ if (init_options)
 else
   DEBUG(D_tls) debug_printf("no SSL CTX options to set\n");
 
 else
   DEBUG(D_tls) debug_printf("no SSL CTX options to set\n");
 
+#ifdef OPENSSL_HAVE_NUM_TICKETS
+SSL_CTX_set_num_tickets(ctx, 0);       /* send no TLS1.3 stateful-tickets */
+#endif
+
 /* We'd like to disable session cache unconditionally, but foolish Outlook
 Express clients then give up the first TLS connection and make a second one
 (which works).  Only when there is an IMAP service on the same machine.
 /* We'd like to disable session cache unconditionally, but foolish Outlook
 Express clients then give up the first TLS connection and make a second one
 (which works).  Only when there is an IMAP service on the same machine.
@@ -1635,7 +1861,7 @@ if ((rc = tls_expand_session_files(ctx, cbinfo, errstr)) != OK)
     }
 # endif
 
     }
 # endif
 
-if (host == NULL)              /* server */
+if (!host)             /* server */
   {
 # ifndef DISABLE_OCSP
   /* We check u_ocsp.server.file, not server.response, because we care about if
   {
 # ifndef DISABLE_OCSP
   /* We check u_ocsp.server.file, not server.response, because we care about if
@@ -1695,53 +1921,71 @@ return OK;
 
 /*
 Argument:   pointer to an SSL structure for the connection
 
 /*
 Argument:   pointer to an SSL structure for the connection
-            buffer to use for answer
-            size of buffer
            pointer to number of bits for cipher
            pointer to number of bits for cipher
-Returns:    nothing
+Returns:    pointer to allocated string in perm-pool
 */
 
 */
 
-static void
-construct_cipher_name(SSL *ssl, uschar *cipherbuf, int bsize, int *bits)
+static uschar *
+construct_cipher_name(SSL * ssl, int * bits)
 {
 {
-/* With OpenSSL 1.0.0a, this needs to be const but the documentation doesn't
+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. */
 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 SSL_CIPHER *c;
-const uschar *ver;
 
 
-ver = (const uschar *)SSL_get_version(ssl);
+const uschar * ver = CUS SSL_get_version(ssl);
+const SSL_CIPHER * c = (const SSL_CIPHER *) SSL_get_current_cipher(ssl);
+uschar * s;
 
 
-c = (const SSL_CIPHER *) SSL_get_current_cipher(ssl);
 SSL_CIPHER_get_bits(c, bits);
 
 SSL_CIPHER_get_bits(c, bits);
 
-string_format(cipherbuf, bsize, "%s:%s:%u", ver,
-  SSL_CIPHER_get_name(c), *bits);
+store_pool = POOL_PERM;
+s = string_sprintf("%s:%s:%u", ver, SSL_CIPHER_get_name(c), *bits);
+store_pool = pool;
+DEBUG(D_tls) debug_printf("Cipher: %s\n", s);
+return s;
+}
+
 
 
-DEBUG(D_tls) debug_printf("Cipher: %s\n", cipherbuf);
+/* Get IETF-standard name for ciphersuite.
+Argument:   pointer to an SSL structure for the connection
+Returns:    pointer to string
+*/
+
+static const uschar *
+cipher_stdname_ssl(SSL * ssl)
+{
+#ifdef EXIM_HAVE_OPENSSL_CIPHER_STD_NAME
+return CUS SSL_CIPHER_standard_name(SSL_get_current_cipher(ssl));
+#else
+ushort id = 0xffff & SSL_CIPHER_get_id(SSL_get_current_cipher(ssl));
+return cipher_stdname(id >> 8, id & 0xff);
+#endif
 }
 
 
 static void
 }
 
 
 static void
-peer_cert(SSL * ssl, tls_support * tlsp, uschar * peerdn, unsigned bsize)
+peer_cert(SSL * ssl, tls_support * tlsp, uschar * peerdn, unsigned siz)
 {
 /*XXX we might consider a list-of-certs variable for the cert chain.
 SSL_get_peer_cert_chain(SSL*).  We'd need a new variable type and support
 in list-handling functions, also consider the difference between the entire
 chain and the elements sent by the peer. */
 
 {
 /*XXX we might consider a list-of-certs variable for the cert chain.
 SSL_get_peer_cert_chain(SSL*).  We'd need a new variable type and support
 in list-handling functions, also consider the difference between the entire
 chain and the elements sent by the peer. */
 
+tlsp->peerdn = NULL;
+
 /* Will have already noted peercert on a verify fail; possibly not the leaf */
 if (!tlsp->peercert)
   tlsp->peercert = SSL_get_peer_certificate(ssl);
 /* Beware anonymous ciphers which lead to server_cert being NULL */
 if (tlsp->peercert)
 /* Will have already noted peercert on a verify fail; possibly not the leaf */
 if (!tlsp->peercert)
   tlsp->peercert = SSL_get_peer_certificate(ssl);
 /* Beware anonymous ciphers which lead to server_cert being NULL */
 if (tlsp->peercert)
-  {
-  X509_NAME_oneline(X509_get_subject_name(tlsp->peercert), CS peerdn, bsize);
-  peerdn[bsize-1] = '\0';
-  tlsp->peerdn = peerdn;               /*XXX a static buffer... */
-  }
-else
-  tlsp->peerdn = NULL;
+  if (!X509_NAME_oneline(X509_get_subject_name(tlsp->peercert), CS peerdn, siz))
+    { DEBUG(D_tls) debug_printf("X509_NAME_oneline() error\n"); }
+  else
+    {
+    peerdn[siz-1] = '\0';
+    tlsp->peerdn = peerdn;             /*XXX a static buffer... */
+    }
 }
 
 
 }
 
 
@@ -1752,6 +1996,7 @@ else
 *        Set up for verifying certificates       *
 *************************************************/
 
 *        Set up for verifying certificates       *
 *************************************************/
 
+#ifndef DISABLE_OCSP
 /* Load certs from file, return TRUE on success */
 
 static BOOL
 /* Load certs from file, return TRUE on success */
 
 static BOOL
@@ -1769,6 +2014,7 @@ while ((x = PEM_read_bio_X509(bp, NULL, 0, NULL)))
 BIO_free(bp);
 return TRUE;
 }
 BIO_free(bp);
 return TRUE;
 }
+#endif
 
 
 
 
 
 
@@ -1853,14 +2099,13 @@ if (expcerts && *expcerts)
       /* Load the list of CAs for which we will accept certs, for sending
       to the client.  This is only for the one-file tls_verify_certificates
       variant.
       /* Load the list of CAs for which we will accept certs, for sending
       to the client.  This is only for the one-file tls_verify_certificates
       variant.
-      If a list isn't loaded into the server, but
-      some verify locations are set, the server end appears to make
-      a wildcard request for client certs.
+      If a list isn't loaded into the server, but some verify locations are set,
+      the server end appears to make a wildcard request for client certs.
       Meanwhile, the client library as default behaviour *ignores* the list
       we send over the wire - see man SSL_CTX_set_client_cert_cb.
       Because of this, and that the dir variant is likely only used for
       Meanwhile, the client library as default behaviour *ignores* the list
       we send over the wire - see man SSL_CTX_set_client_cert_cb.
       Because of this, and that the dir variant is likely only used for
-      the public-CA bundle (not for a private CA), not worth fixing.
-      */
+      the public-CA bundle (not for a private CA), not worth fixing.  */
+
       if (file)
        {
        STACK_OF(X509_NAME) * names = SSL_load_client_CA_file(CS file);
       if (file)
        {
        STACK_OF(X509_NAME) * names = SSL_load_client_CA_file(CS file);
@@ -1928,7 +2173,7 @@ if (expcerts && *expcerts)
   /* If verification is optional, don't fail if no certificate */
 
   SSL_CTX_set_verify(sctx,
   /* If verification is optional, don't fail if no certificate */
 
   SSL_CTX_set_verify(sctx,
-    SSL_VERIFY_PEER | (optional? 0 : SSL_VERIFY_FAIL_IF_NO_PEER_CERT),
+    SSL_VERIFY_PEER | (optional ? 0 : SSL_VERIFY_FAIL_IF_NO_PEER_CERT),
     cert_vfy_cb);
   }
 
     cert_vfy_cb);
   }
 
@@ -1962,11 +2207,10 @@ int rc;
 uschar * expciphers;
 tls_ext_ctx_cb * cbinfo;
 static uschar peerdn[256];
 uschar * expciphers;
 tls_ext_ctx_cb * cbinfo;
 static uschar peerdn[256];
-static uschar cipherbuf[256];
 
 /* Check for previous activation */
 
 
 /* Check for previous activation */
 
-if (tls_in.active >= 0)
+if (tls_in.active.sock >= 0)
   {
   tls_error(US"STARTTLS received after TLS started", NULL, US"", errstr);
   smtp_printf("554 Already in TLS\r\n", FALSE);
   {
   tls_error(US"STARTTLS received after TLS started", NULL, US"", errstr);
   smtp_printf("554 Already in TLS\r\n", FALSE);
@@ -1990,6 +2234,10 @@ if (!expand_check(require_ciphers, US"tls_require_ciphers", &expciphers, errstr)
 /* In OpenSSL, cipher components are separated by hyphens. In GnuTLS, they
 were historically separated by underscores. So that I can use either form in my
 tests, and also for general convenience, we turn underscores into hyphens here.
 /* In OpenSSL, cipher components are separated by hyphens. In GnuTLS, they
 were historically separated by underscores. So that I can use either form in my
 tests, and also for general convenience, we turn underscores into hyphens here.
+
+XXX SSL_CTX_set_cipher_list() is replaced by SSL_CTX_set_ciphersuites()
+for TLS 1.3 .  Since we do not call it at present we get the default list:
+TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256
 */
 
 if (expciphers)
 */
 
 if (expciphers)
@@ -2014,14 +2262,14 @@ server_verify_callback_called = FALSE;
 if (verify_check_host(&tls_verify_hosts) == OK)
   {
   rc = setup_certs(server_ctx, tls_verify_certificates, tls_crl, NULL,
 if (verify_check_host(&tls_verify_hosts) == OK)
   {
   rc = setup_certs(server_ctx, tls_verify_certificates, tls_crl, NULL,
-                       FALSE, verify_callback_server, errstr);
+                       FALSE, verify_callback_server, errstr);
   if (rc != OK) return rc;
   server_verify_optional = FALSE;
   }
 else if (verify_check_host(&tls_try_verify_hosts) == OK)
   {
   rc = setup_certs(server_ctx, tls_verify_certificates, tls_crl, NULL,
   if (rc != OK) return rc;
   server_verify_optional = FALSE;
   }
 else if (verify_check_host(&tls_try_verify_hosts) == OK)
   {
   rc = setup_certs(server_ctx, tls_verify_certificates, tls_crl, NULL,
-                       TRUE, verify_callback_server, errstr);
+                       TRUE, verify_callback_server, errstr);
   if (rc != OK) return rc;
   server_verify_optional = TRUE;
   }
   if (rc != OK) return rc;
   server_verify_optional = TRUE;
   }
@@ -2067,9 +2315,9 @@ SSL_set_accept_state(server_ssl);
 DEBUG(D_tls) debug_printf("Calling SSL_accept\n");
 
 sigalrm_seen = FALSE;
 DEBUG(D_tls) debug_printf("Calling SSL_accept\n");
 
 sigalrm_seen = FALSE;
-if (smtp_receive_timeout > 0) alarm(smtp_receive_timeout);
+if (smtp_receive_timeout > 0) ALARM(smtp_receive_timeout);
 rc = SSL_accept(server_ssl);
 rc = SSL_accept(server_ssl);
-alarm(0);
+ALARM_CLR(0);
 
 if (rc <= 0)
   {
 
 if (rc <= 0)
   {
@@ -2078,20 +2326,34 @@ if (rc <= 0)
   }
 
 DEBUG(D_tls) debug_printf("SSL_accept was successful\n");
   }
 
 DEBUG(D_tls) debug_printf("SSL_accept was successful\n");
+ERR_clear_error();     /* Even success can leave errors in the stack. Seen with
+                       anon-authentication ciphersuite negociated. */
 
 /* TLS has been set up. Adjust the input functions to read via TLS,
 and initialize things. */
 
 peer_cert(server_ssl, &tls_in, peerdn, sizeof(peerdn));
 
 
 /* TLS has been set up. Adjust the input functions to read via TLS,
 and initialize things. */
 
 peer_cert(server_ssl, &tls_in, peerdn, sizeof(peerdn));
 
-construct_cipher_name(server_ssl, cipherbuf, sizeof(cipherbuf), &tls_in.bits);
-tls_in.cipher = cipherbuf;
+tls_in.cipher = construct_cipher_name(server_ssl, &tls_in.bits);
+tls_in.cipher_stdname = cipher_stdname_ssl(server_ssl);
 
 DEBUG(D_tls)
   {
   uschar buf[2048];
 
 DEBUG(D_tls)
   {
   uschar buf[2048];
-  if (SSL_get_shared_ciphers(server_ssl, CS buf, sizeof(buf)) != NULL)
+  if (SSL_get_shared_ciphers(server_ssl, CS buf, sizeof(buf)))
     debug_printf("Shared ciphers: %s\n", buf);
     debug_printf("Shared ciphers: %s\n", buf);
+
+#ifdef EXIM_HAVE_OPENSSL_KEYLOG
+  {
+  BIO * bp = BIO_new(BIO_s_mem());
+  uschar * s;
+  int len;
+  SSL_SESSION_print_keylog(bp, SSL_get_session(server_ssl));
+  len = (int) BIO_get_mem_data(bp, CSS &s);
+  debug_printf("%.*s", len, s);
+  BIO_free(bp);
+  }
+#endif
   }
 
 /* Record the certificate we presented */
   }
 
 /* Record the certificate we presented */
@@ -2117,7 +2379,8 @@ receive_feof = tls_feof;
 receive_ferror = tls_ferror;
 receive_smtp_buffered = tls_smtp_buffered;
 
 receive_ferror = tls_ferror;
 receive_smtp_buffered = tls_smtp_buffered;
 
-tls_in.active = fileno(smtp_out);
+tls_in.active.sock = fileno(smtp_out);
+tls_in.active.tls_ctx = NULL;  /* not using explicit ctx for server-side */
 return OK;
 }
 
 return OK;
 }
 
@@ -2137,10 +2400,10 @@ int rc;
 if (  (  !ob->tls_verify_hosts
       && (!ob->tls_try_verify_hosts || !*ob->tls_try_verify_hosts)
       )
 if (  (  !ob->tls_verify_hosts
       && (!ob->tls_try_verify_hosts || !*ob->tls_try_verify_hosts)
       )
-   || (verify_check_given_host(&ob->tls_verify_hosts, host) == OK)
+   || verify_check_given_host(CUSS &ob->tls_verify_hosts, host) == OK
    )
   client_verify_optional = FALSE;
    )
   client_verify_optional = FALSE;
-else if (verify_check_given_host(&ob->tls_try_verify_hosts, host) == OK)
+else if (verify_check_given_host(CUSS &ob->tls_try_verify_hosts, host) == OK)
   client_verify_optional = TRUE;
 else
   return OK;
   client_verify_optional = TRUE;
 else
   return OK;
@@ -2150,7 +2413,7 @@ if ((rc = setup_certs(ctx, ob->tls_verify_certificates,
       errstr)) != OK)
   return rc;
 
       errstr)) != OK)
   return rc;
 
-if (verify_check_given_host(&ob->tls_verify_cert_hostnames, host) == OK)
+if (verify_check_given_host(CUSS &ob->tls_verify_cert_hostnames, host) == OK)
   {
   cbinfo->verify_cert_hostnames =
 #ifdef SUPPORT_I18N
   {
   cbinfo->verify_cert_hostnames =
 #ifdef SUPPORT_I18N
@@ -2169,7 +2432,6 @@ return OK;
 static int
 dane_tlsa_load(SSL * ssl, host_item * host, dns_answer * dnsa, uschar ** errstr)
 {
 static int
 dane_tlsa_load(SSL * ssl, host_item * host, dns_answer * dnsa, uschar ** errstr)
 {
-dns_record * rr;
 dns_scan dnss;
 const char * hostnames[2] = { CS host->name, NULL };
 int found = 0;
 dns_scan dnss;
 const char * hostnames[2] = { CS host->name, NULL };
 int found = 0;
@@ -2177,10 +2439,9 @@ int found = 0;
 if (DANESSL_init(ssl, NULL, hostnames) != 1)
   return tls_error(US"hostnames load", host, NULL, errstr);
 
 if (DANESSL_init(ssl, NULL, hostnames) != 1)
   return tls_error(US"hostnames load", host, NULL, errstr);
 
-for (rr = dns_next_rr(dnsa, &dnss, RESET_ANSWERS);
-     rr;
+for (dns_record * rr = dns_next_rr(dnsa, &dnss, RESET_ANSWERS); rr;
      rr = dns_next_rr(dnsa, &dnss, RESET_NEXT)
      rr = dns_next_rr(dnsa, &dnss, RESET_NEXT)
-    ) if (rr->type == T_TLSA)
+    ) if (rr->type == T_TLSA && rr->size > 3)
   {
   const uschar * p = rr->data;
   uint8_t usage, selector, mtype;
   {
   const uschar * p = rr->data;
   uint8_t usage, selector, mtype;
@@ -2230,47 +2491,50 @@ return DEFER;
 
 /* Called from the smtp transport after STARTTLS has been accepted.
 
 
 /* Called from the smtp transport after STARTTLS has been accepted.
 
-Argument:
-  fd               the fd of the connection
-  host             connected host (for messages)
-  addr             the first address
-  tb               transport (always smtp)
-  tlsa_dnsa        tlsa lookup, if DANE, else null
-  errstr          error string pointer
-
-Returns:           OK on success
-                   FAIL otherwise - note that tls_error() will not give DEFER
-                     because this is not a server
+Arguments:
+  cctx         connection context
+  conn_args    connection details
+  cookie       datum for randomness; can be NULL
+  tlsp         record details of TLS channel configuration here; must be non-NULL
+  errstr       error string pointer
+
+Returns:       TRUE for success with TLS session context set in connection context,
+               FALSE on error
 */
 
 */
 
-int
-tls_client_start(int fd, host_item *host, address_item *addr,
-  transport_instance * tb,
-#ifdef SUPPORT_DANE
-  dns_answer * tlsa_dnsa,
-#endif
-  uschar ** errstr)
+BOOL
+tls_client_start(client_conn_ctx * cctx, smtp_connect_args * conn_args,
+  void * cookie, tls_support * tlsp, uschar ** errstr)
 {
 {
-smtp_transport_options_block * ob =
-  (smtp_transport_options_block *)tb->options_block;
-static uschar peerdn[256];
+host_item * host = conn_args->host;            /* for msgs and option-tests */
+transport_instance * tb = conn_args->tblock;   /* always smtp or NULL */
+smtp_transport_options_block * ob = tb
+  ? (smtp_transport_options_block *)tb->options_block
+  : &smtp_transport_option_defaults;
+exim_openssl_client_tls_ctx * exim_client_ctx;
 uschar * expciphers;
 int rc;
 uschar * expciphers;
 int rc;
-static uschar cipherbuf[256];
+static uschar peerdn[256];
 
 #ifndef DISABLE_OCSP
 BOOL request_ocsp = FALSE;
 BOOL require_ocsp = FALSE;
 #endif
 
 
 #ifndef DISABLE_OCSP
 BOOL request_ocsp = FALSE;
 BOOL require_ocsp = FALSE;
 #endif
 
+rc = store_pool;
+store_pool = POOL_PERM;
+exim_client_ctx = store_get(sizeof(exim_openssl_client_tls_ctx));
+exim_client_ctx->corked = NULL;
+store_pool = rc;
+
 #ifdef SUPPORT_DANE
 #ifdef SUPPORT_DANE
-tls_out.tlsa_usage = 0;
+tlsp->tlsa_usage = 0;
 #endif
 
 #ifndef DISABLE_OCSP
   {
 # ifdef SUPPORT_DANE
 #endif
 
 #ifndef DISABLE_OCSP
   {
 # ifdef SUPPORT_DANE
-  if (  tlsa_dnsa
+  if (  conn_args->dane
      && ob->hosts_request_ocsp[0] == '*'
      && ob->hosts_request_ocsp[1] == '\0'
      )
      && ob->hosts_request_ocsp[0] == '*'
      && ob->hosts_request_ocsp[1] == '\0'
      )
@@ -2284,38 +2548,38 @@ tls_out.tlsa_usage = 0;
 # endif
 
   if ((require_ocsp =
 # endif
 
   if ((require_ocsp =
-       verify_check_given_host(&ob->hosts_require_ocsp, host) == OK))
+       verify_check_given_host(CUSS &ob->hosts_require_ocsp, host) == OK))
     request_ocsp = TRUE;
   else
 # ifdef SUPPORT_DANE
     if (!request_ocsp)
 # endif
       request_ocsp =
     request_ocsp = TRUE;
   else
 # ifdef SUPPORT_DANE
     if (!request_ocsp)
 # endif
       request_ocsp =
-       verify_check_given_host(&ob->hosts_request_ocsp, host) == OK;
+       verify_check_given_host(CUSS &ob->hosts_request_ocsp, host) == OK;
   }
 #endif
 
   }
 #endif
 
-rc = tls_init(&client_ctx, host, NULL,
+rc = tls_init(&exim_client_ctx->ctx, host, NULL,
     ob->tls_certificate, ob->tls_privatekey,
 #ifndef DISABLE_OCSP
     (void *)(long)request_ocsp,
 #endif
     ob->tls_certificate, ob->tls_privatekey,
 #ifndef DISABLE_OCSP
     (void *)(long)request_ocsp,
 #endif
-    addr, &client_static_cbinfo, errstr);
-if (rc != OK) return rc;
+    cookie, &client_static_cbinfo, errstr);
+if (rc != OK) return FALSE;
 
 
-tls_out.certificate_verified = FALSE;
+tlsp->certificate_verified = FALSE;
 client_verify_callback_called = FALSE;
 
 expciphers = NULL;
 #ifdef SUPPORT_DANE
 client_verify_callback_called = FALSE;
 
 expciphers = NULL;
 #ifdef SUPPORT_DANE
-if (tlsa_dnsa)
+if (conn_args->dane)
   {
   /* We fall back to tls_require_ciphers if unset, empty or forced failure, but
   other failures should be treated as problems. */
   if (ob->dane_require_tls_ciphers &&
       !expand_check(ob->dane_require_tls_ciphers, US"dane_require_tls_ciphers",
         &expciphers, errstr))
   {
   /* We fall back to tls_require_ciphers if unset, empty or forced failure, but
   other failures should be treated as problems. */
   if (ob->dane_require_tls_ciphers &&
       !expand_check(ob->dane_require_tls_ciphers, US"dane_require_tls_ciphers",
         &expciphers, errstr))
-    return FAIL;
+    return FALSE;
   if (expciphers && *expciphers == '\0')
     expciphers = NULL;
   }
   if (expciphers && *expciphers == '\0')
     expciphers = NULL;
   }
@@ -2323,7 +2587,7 @@ if (tlsa_dnsa)
 if (!expciphers &&
     !expand_check(ob->tls_require_ciphers, US"tls_require_ciphers",
       &expciphers, errstr))
 if (!expciphers &&
     !expand_check(ob->tls_require_ciphers, US"tls_require_ciphers",
       &expciphers, errstr))
-  return FAIL;
+  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
 
 /* 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
@@ -2334,62 +2598,74 @@ if (expciphers)
   uschar *s = expciphers;
   while (*s) { if (*s == '_') *s = '-'; s++; }
   DEBUG(D_tls) debug_printf("required ciphers: %s\n", 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(client_ctx, CS expciphers))
-    return tls_error(US"SSL_CTX_set_cipher_list", host, NULL, errstr);
+  if (!SSL_CTX_set_cipher_list(exim_client_ctx->ctx, CS expciphers))
+    {
+    tls_error(US"SSL_CTX_set_cipher_list", host, NULL, errstr);
+    return FALSE;
+    }
   }
 
 #ifdef SUPPORT_DANE
   }
 
 #ifdef SUPPORT_DANE
-if (tlsa_dnsa)
+if (conn_args->dane)
   {
   {
-  SSL_CTX_set_verify(client_ctx,
+  SSL_CTX_set_verify(exim_client_ctx->ctx,
     SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT,
     verify_callback_client_dane);
 
   if (!DANESSL_library_init())
     SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT,
     verify_callback_client_dane);
 
   if (!DANESSL_library_init())
-    return tls_error(US"library init", host, NULL, errstr);
-  if (DANESSL_CTX_init(client_ctx) <= 0)
-    return tls_error(US"context init", host, NULL, errstr);
+    {
+    tls_error(US"library init", host, NULL, errstr);
+    return FALSE;
+    }
+  if (DANESSL_CTX_init(exim_client_ctx->ctx) <= 0)
+    {
+    tls_error(US"context init", host, NULL, errstr);
+    return FALSE;
+    }
   }
 else
 
 #endif
 
   }
 else
 
 #endif
 
-  if ((rc = tls_client_basic_ctx_init(client_ctx, host, ob,
-      client_static_cbinfo, errstr)) != OK)
-    return rc;
+  if (tls_client_basic_ctx_init(exim_client_ctx->ctx, host, ob,
+       client_static_cbinfo, errstr) != OK)
+    return FALSE;
 
 
-if ((client_ssl = SSL_new(client_ctx)) == NULL)
-  return tls_error(US"SSL_new", host, NULL, errstr);
-SSL_set_session_id_context(client_ssl, sid_ctx, Ustrlen(sid_ctx));
-SSL_set_fd(client_ssl, fd);
-SSL_set_connect_state(client_ssl);
+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 (ob->tls_sni)
   {
-  if (!expand_check(ob->tls_sni, US"tls_sni", &tls_out.sni, errstr))
-    return FAIL;
-  if (!tls_out.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(tls_out.sni))
-    tls_out.sni = NULL;
+  else if (!Ustrlen(tlsp->sni))
+    tlsp->sni = NULL;
   else
     {
 #ifdef EXIM_HAVE_OPENSSL_TLSEXT
   else
     {
 #ifdef EXIM_HAVE_OPENSSL_TLSEXT
-    DEBUG(D_tls) debug_printf("Setting TLS SNI \"%s\"\n", tls_out.sni);
-    SSL_set_tlsext_host_name(client_ssl, tls_out.sni);
+    DEBUG(D_tls) debug_printf("Setting TLS SNI \"%s\"\n", tlsp->sni);
+    SSL_set_tlsext_host_name(exim_client_ctx->ssl, tlsp->sni);
 #else
     log_write(0, LOG_MAIN, "SNI unusable with this OpenSSL library version; ignoring \"%s\"\n",
 #else
     log_write(0, LOG_MAIN, "SNI unusable with this OpenSSL library version; ignoring \"%s\"\n",
-          tls_out.sni);
+          tlsp->sni);
 #endif
     }
   }
 
 #ifdef SUPPORT_DANE
 #endif
     }
   }
 
 #ifdef SUPPORT_DANE
-if (tlsa_dnsa)
-  if ((rc = dane_tlsa_load(client_ssl, host, tlsa_dnsa, errstr)) != OK)
-    return rc;
+if (conn_args->dane)
+  if (dane_tlsa_load(exim_client_ctx->ssl, host, &conn_args->tlsa_dnsa, errstr) != OK)
+    return FALSE;
 #endif
 
 #ifndef DISABLE_OCSP
 #endif
 
 #ifndef DISABLE_OCSP
@@ -2405,57 +2681,80 @@ if (request_ocsp)
     {  /* Re-eval now $tls_out_tlsa_usage is populated.  If
        this means we avoid the OCSP request, we wasted the setup
        cost in tls_init(). */
     {  /* Re-eval now $tls_out_tlsa_usage is populated.  If
        this means we avoid the OCSP request, we wasted the setup
        cost in tls_init(). */
-    require_ocsp = verify_check_given_host(&ob->hosts_require_ocsp, host) == OK;
+    require_ocsp = verify_check_given_host(CUSS &ob->hosts_require_ocsp, host) == OK;
     request_ocsp = require_ocsp
     request_ocsp = require_ocsp
-      || verify_check_given_host(&ob->hosts_request_ocsp, host) == OK;
+      || verify_check_given_host(CUSS &ob->hosts_request_ocsp, host) == OK;
     }
   }
 # endif
 
 if (request_ocsp)
   {
     }
   }
 # endif
 
 if (request_ocsp)
   {
-  SSL_set_tlsext_status_type(client_ssl, TLSEXT_STATUSTYPE_ocsp);
+  SSL_set_tlsext_status_type(exim_client_ctx->ssl, TLSEXT_STATUSTYPE_ocsp);
   client_static_cbinfo->u_ocsp.client.verify_required = require_ocsp;
   client_static_cbinfo->u_ocsp.client.verify_required = require_ocsp;
-  tls_out.ocsp = OCSP_NOT_RESP;
+  tlsp->ocsp = OCSP_NOT_RESP;
   }
 #endif
 
 #ifndef DISABLE_EVENT
   }
 #endif
 
 #ifndef DISABLE_EVENT
-client_static_cbinfo->event_action = tb->event_action;
+client_static_cbinfo->event_action = tb ? tb->event_action : NULL;
 #endif
 
 /* There doesn't seem to be a built-in timeout on connection. */
 
 DEBUG(D_tls) debug_printf("Calling SSL_connect\n");
 sigalrm_seen = FALSE;
 #endif
 
 /* There doesn't seem to be a built-in timeout on connection. */
 
 DEBUG(D_tls) debug_printf("Calling SSL_connect\n");
 sigalrm_seen = FALSE;
-alarm(ob->command_timeout);
-rc = SSL_connect(client_ssl);
-alarm(0);
+ALARM(ob->command_timeout);
+rc = SSL_connect(exim_client_ctx->ssl);
+ALARM_CLR(0);
 
 #ifdef SUPPORT_DANE
 
 #ifdef SUPPORT_DANE
-if (tlsa_dnsa)
-  DANESSL_cleanup(client_ssl);
+if (conn_args->dane)
+  DANESSL_cleanup(exim_client_ctx->ssl);
 #endif
 
 if (rc <= 0)
 #endif
 
 if (rc <= 0)
-  return tls_error(US"SSL_connect", host, sigalrm_seen ? US"timed out" : NULL,
-    errstr);
+  {
+  tls_error(US"SSL_connect", host, sigalrm_seen ? US"timed out" : NULL, errstr);
+  return FALSE;
+  }
 
 
-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(BIO_s_mem());
+  if (bp)
+    {
+    uschar * s;
+    int len;
+    SSL_SESSION_print_keylog(bp, SSL_get_session(exim_client_ctx->ssl));
+    len = (int) BIO_get_mem_data(bp, CSS &s);
+    debug_printf("%.*s", len, s);
+    BIO_free(bp);
+    }
+  else
+    debug_printf("(alloc failure for keylog)\n");
+  }
+#endif
+  }
 
 
-peer_cert(client_ssl, &tls_out, peerdn, sizeof(peerdn));
+peer_cert(exim_client_ctx->ssl, tlsp, peerdn, sizeof(peerdn));
 
 
-construct_cipher_name(client_ssl, cipherbuf, sizeof(cipherbuf), &tls_out.bits);
-tls_out.cipher = cipherbuf;
+tlsp->cipher = construct_cipher_name(exim_client_ctx->ssl, &tlsp->bits);
+tlsp->cipher_stdname = cipher_stdname_ssl(exim_client_ctx->ssl);
 
 /* Record the certificate we presented */
   {
 
 /* Record the certificate we presented */
   {
-  X509 * crt = SSL_get_certificate(client_ssl);
-  tls_out.ourcert = crt ? X509_dup(crt) : NULL;
+  X509 * crt = SSL_get_certificate(exim_client_ctx->ssl);
+  tlsp->ourcert = crt ? X509_dup(crt) : NULL;
   }
 
   }
 
-tls_out.active = fd;
-return OK;
+tlsp->active.sock = cctx->sock;
+tlsp->active.tls_ctx = exim_client_ctx;
+cctx->tls_ctx = exim_client_ctx;
+return TRUE;
 }
 
 
 }
 
 
@@ -2471,11 +2770,11 @@ int inbytes;
 DEBUG(D_tls) debug_printf("Calling SSL_read(%p, %p, %u)\n", server_ssl,
   ssl_xfer_buffer, ssl_xfer_buffer_size);
 
 DEBUG(D_tls) debug_printf("Calling SSL_read(%p, %p, %u)\n", server_ssl,
   ssl_xfer_buffer, ssl_xfer_buffer_size);
 
-if (smtp_receive_timeout > 0) alarm(smtp_receive_timeout);
+if (smtp_receive_timeout > 0) ALARM(smtp_receive_timeout);
 inbytes = SSL_read(server_ssl, CS ssl_xfer_buffer,
                  MIN(ssl_xfer_buffer_size, lim));
 error = SSL_get_error(server_ssl, inbytes);
 inbytes = SSL_read(server_ssl, CS ssl_xfer_buffer,
                  MIN(ssl_xfer_buffer_size, lim));
 error = SSL_get_error(server_ssl, inbytes);
-if (smtp_receive_timeout > 0) alarm(0);
+if (smtp_receive_timeout > 0) ALARM_CLR(0);
 
 if (had_command_timeout)               /* set by signal handler */
   smtp_command_timeout_exit();         /* does not return */
 
 if (had_command_timeout)               /* set by signal handler */
   smtp_command_timeout_exit();         /* does not return */
@@ -2490,51 +2789,55 @@ if (had_data_sigint)
 closed down, not that the socket itself has been closed down. Revert to
 non-SSL handling. */
 
 closed down, not that the socket itself has been closed down. Revert to
 non-SSL handling. */
 
-if (error == SSL_ERROR_ZERO_RETURN)
+switch(error)
   {
   {
-  DEBUG(D_tls) debug_printf("Got SSL_ERROR_ZERO_RETURN\n");
+  case SSL_ERROR_NONE:
+    break;
 
 
-  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;
+  case SSL_ERROR_ZERO_RETURN:
+    DEBUG(D_tls) debug_printf("Got SSL_ERROR_ZERO_RETURN\n");
 
 
-  if (SSL_get_shutdown(server_ssl) == SSL_RECEIVED_SHUTDOWN)
-       SSL_shutdown(server_ssl);
+    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;
 
 
-  sk_X509_pop_free(server_static_cbinfo->verify_stack, X509_free);
-  SSL_free(server_ssl);
-  SSL_CTX_free(server_ctx);
-  server_static_cbinfo->verify_stack = NULL;
-  server_ctx = NULL;
-  server_ssl = NULL;
-  tls_in.active = -1;
-  tls_in.bits = 0;
-  tls_in.cipher = NULL;
-  tls_in.peerdn = NULL;
-  tls_in.sni = NULL;
+    if (SSL_get_shutdown(server_ssl) == SSL_RECEIVED_SHUTDOWN)
+         SSL_shutdown(server_ssl);
 
 
-  return FALSE;
-  }
+#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;
 
 
-/* Handle genuine errors */
+    return FALSE;
 
 
-else if (error == SSL_ERROR_SSL)
-  {
-  ERR_error_string(ERR_get_error(), ssl_errstring);
-  log_write(0, LOG_MAIN, "TLS error (SSL_read): %s", ssl_errstring);
-  ssl_xfer_error = TRUE;
-  return FALSE;
-  }
+  /* Handle genuine errors */
+  case SSL_ERROR_SSL:
+    ERR_error_string_n(ERR_get_error(), ssl_errstring, sizeof(ssl_errstring));
+    log_write(0, LOG_MAIN, "TLS error (SSL_read): %s", ssl_errstring);
+    ssl_xfer_error = TRUE;
+    return FALSE;
 
 
-else if (error != SSL_ERROR_NONE)
-  {
-  DEBUG(D_tls) debug_printf("Got SSL error %d\n", error);
-  ssl_xfer_error = TRUE;
-  return FALSE;
+  default:
+    DEBUG(D_tls) debug_printf("Got SSL error %d\n", error);
+    DEBUG(D_tls) if (error == SSL_ERROR_SYSCALL)
+      debug_printf(" - syscall %s\n", strerror(errno));
+    ssl_xfer_error = TRUE;
+    return FALSE;
   }
 
 #ifndef DISABLE_DKIM
   }
 
 #ifndef DISABLE_DKIM
@@ -2618,19 +2921,20 @@ return ssl_xfer_buffer_lwm < ssl_xfer_buffer_hwm || SSL_pending(server_ssl) > 0;
 
 /*
 Arguments:
 
 /*
 Arguments:
+  ct_ctx    client context pointer, or NULL for the one global server context
   buff      buffer of data
   len       size of buffer
 
 Returns:    the number of bytes read
   buff      buffer of data
   len       size of buffer
 
 Returns:    the number of bytes read
-            -1 after a failed read
+            -1 after a failed read, including EOF
 
 Only used by the client-side TLS.
 */
 
 int
 
 Only used by the client-side TLS.
 */
 
 int
-tls_read(BOOL is_server, uschar *buff, size_t len)
+tls_read(void * ct_ctx, uschar *buff, size_t len)
 {
 {
-SSL *ssl = is_server ? server_ssl : client_ssl;
+SSL * ssl = ct_ctx ? ((exim_openssl_client_tls_ctx *)ct_ctx)->ssl : server_ssl;
 int inbytes;
 int error;
 
 int inbytes;
 int error;
 
@@ -2661,7 +2965,7 @@ return inbytes;
 
 /*
 Arguments:
 
 /*
 Arguments:
-  is_server channel specifier
+  ct_ctx    client context pointer, or NULL for the one global server context
   buff      buffer of data
   len       number of bytes
   more     further data expected soon
   buff      buffer of data
   len       number of bytes
   more     further data expected soon
@@ -2673,11 +2977,16 @@ Used by both server-side and client-side TLS.
 */
 
 int
 */
 
 int
-tls_write(BOOL is_server, const uschar *buff, size_t len, BOOL more)
+tls_write(void * ct_ctx, const uschar *buff, size_t len, BOOL more)
 {
 {
-int outbytes, error, left;
-SSL *ssl = is_server ? server_ssl : client_ssl;
-static gstring * corked = NULL;
+size_t olen = len;
+int outbytes, error;
+SSL * ssl = ct_ctx
+  ? ((exim_openssl_client_tls_ctx *)ct_ctx)->ssl : server_ssl;
+static gstring * server_corked = NULL;
+gstring ** corkedp = ct_ctx
+  ? &((exim_openssl_client_tls_ctx *)ct_ctx)->corked : &server_corked;
+gstring * corked = *corkedp;
 
 DEBUG(D_tls) debug_printf("%s(%p, %lu%s)\n", __FUNCTION__,
   buff, (unsigned long)len, more ? ", more" : "");
 
 DEBUG(D_tls) debug_printf("%s(%p, %lu%s)\n", __FUNCTION__,
   buff, (unsigned long)len, more ? ", more" : "");
@@ -2685,28 +2994,46 @@ DEBUG(D_tls) debug_printf("%s(%p, %lu%s)\n", __FUNCTION__,
 /* Lacking a CORK or MSG_MORE facility (such as GnuTLS has) we copy data when
 "more" is notified.  This hack is only ok if small amounts are involved AND only
 one stream does it, in one context (i.e. no store reset).  Currently it is used
 /* Lacking a CORK or MSG_MORE facility (such as GnuTLS has) we copy data when
 "more" is notified.  This hack is only ok if small amounts are involved AND only
 one stream does it, in one context (i.e. no store reset).  Currently it is used
-for the responses to the received SMTP MAIL , RCPT, DATA sequence, only. */
+for the responses to the received SMTP MAIL , RCPT, DATA sequence, only.
+We support callouts done by the server process by using a separate client
+context for the stashed information. */
+/* + if PIPE_COMMAND, banner & ehlo-resp for smmtp-on-connect. Suspect there's
+a store reset there, so use POOL_PERM. */
+/* + if CHUNKING, cmds EHLO,MAIL,RCPT(s),BDAT */
 
 
-if (is_server && (more || corked))
+if ((more || corked))
   {
   {
+#ifdef EXPERIMENTAL_PIPE_CONNECT
+  int save_pool = store_pool;
+  store_pool = POOL_PERM;
+#endif
+
   corked = string_catn(corked, buff, len);
   corked = string_catn(corked, buff, len);
+
+#ifdef EXPERIMENTAL_PIPE_CONNECT
+  store_pool = save_pool;
+#endif
+
   if (more)
   if (more)
+    {
+    *corkedp = corked;
     return len;
     return len;
+    }
   buff = CUS corked->s;
   len = corked->ptr;
   buff = CUS corked->s;
   len = corked->ptr;
-  corked = NULL;
+  *corkedp = NULL;
   }
 
   }
 
-for (left = len; left > 0;)
+for (int left = len; left > 0;)
   {
   {
-  DEBUG(D_tls) debug_printf("SSL_write(SSL, %p, %d)\n", buff, left);
+  DEBUG(D_tls) debug_printf("SSL_write(%p, %p, %d)\n", ssl, buff, left);
   outbytes = SSL_write(ssl, CS buff, left);
   error = SSL_get_error(ssl, outbytes);
   DEBUG(D_tls) debug_printf("outbytes=%d error=%d\n", outbytes, error);
   switch (error)
     {
     case SSL_ERROR_SSL:
   outbytes = SSL_write(ssl, CS buff, left);
   error = SSL_get_error(ssl, outbytes);
   DEBUG(D_tls) debug_printf("outbytes=%d error=%d\n", outbytes, error);
   switch (error)
     {
     case SSL_ERROR_SSL:
-      ERR_error_string(ERR_get_error(), ssl_errstring);
+      ERR_error_string_n(ERR_get_error(), ssl_errstring, sizeof(ssl_errstring));
       log_write(0, LOG_MAIN, "TLS error (SSL_write): %s", ssl_errstring);
       return -1;
 
       log_write(0, LOG_MAIN, "TLS error (SSL_write): %s", ssl_errstring);
       return -1;
 
@@ -2730,7 +3057,7 @@ for (left = len; left > 0;)
       return -1;
     }
   }
       return -1;
     }
   }
-return len;
+return olen;
 }
 
 
 }
 
 
@@ -2744,6 +3071,7 @@ daemon, to shut down the TLS library, without actually doing a shutdown (which
 would tamper with the SSL session in the parent process).
 
 Arguments:
 would tamper with the SSL session in the parent process).
 
 Arguments:
+  ct_ctx       client TLS context pointer, or NULL for the one global server context
   shutdown     1 if TLS close-alert is to be sent,
                2 if also response to be waited for
 
   shutdown     1 if TLS close-alert is to be sent,
                2 if also response to be waited for
 
@@ -2753,11 +3081,12 @@ Used by both server-side and client-side TLS.
 */
 
 void
 */
 
 void
-tls_close(BOOL is_server, int shutdown)
+tls_close(void * ct_ctx, int shutdown)
 {
 {
-SSL_CTX **ctxp = is_server ? &server_ctx : &client_ctx;
-SSL **sslp = is_server ? &server_ssl : &client_ssl;
-int *fdp = is_server ? &tls_in.active : &tls_out.active;
+exim_openssl_client_tls_ctx * o_ctx = ct_ctx;
+SSL_CTX **ctxp = o_ctx ? &o_ctx->ctx : &server_ctx;
+SSL **sslp =     o_ctx ? &o_ctx->ssl : &server_ssl;
+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 */
 
@@ -2770,23 +3099,25 @@ if (shutdown)
   if (  (rc = SSL_shutdown(*sslp)) == 0        /* send "close notify" alert */
      && shutdown > 1)
     {
   if (  (rc = SSL_shutdown(*sslp)) == 0        /* send "close notify" alert */
      && shutdown > 1)
     {
-    alarm(2);
+    ALARM(2);
     rc = SSL_shutdown(*sslp);          /* wait for response */
     rc = SSL_shutdown(*sslp);          /* wait for response */
-    alarm(0);
+    ALARM_CLR(0);
     }
 
   if (rc < 0) DEBUG(D_tls)
     {
     }
 
   if (rc < 0) DEBUG(D_tls)
     {
-    ERR_error_string(ERR_get_error(), ssl_errstring);
+    ERR_error_string_n(ERR_get_error(), ssl_errstring, sizeof(ssl_errstring));
     debug_printf("SSL_shutdown: %s\n", ssl_errstring);
     }
   }
 
     debug_printf("SSL_shutdown: %s\n", ssl_errstring);
     }
   }
 
-if (is_server)
+#ifndef DISABLE_OCSP
+if (!o_ctx)            /* server side */
   {
   sk_X509_pop_free(server_static_cbinfo->verify_stack, X509_free);
   server_static_cbinfo->verify_stack = NULL;
   }
   {
   sk_X509_pop_free(server_static_cbinfo->verify_stack, X509_free);
   server_static_cbinfo->verify_stack = NULL;
   }
+#endif
 
 SSL_CTX_free(*ctxp);
 SSL_free(*sslp);
 
 SSL_CTX_free(*ctxp);
 SSL_free(*sslp);
@@ -2817,8 +3148,10 @@ 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 */
 
 /* 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();
 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. */
 #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. */
@@ -2841,10 +3174,13 @@ while (*s != 0) { if (*s == '_') *s = '-'; s++; }
 
 err = NULL;
 
 
 err = NULL;
 
-ctx = SSL_CTX_new(SSLv23_server_method());
-if (!ctx)
+#ifdef EXIM_HAVE_OPENSSL_TLS_METHOD
+if (!(ctx = SSL_CTX_new(TLS_server_method())))
+#else
+if (!(ctx = SSL_CTX_new(SSLv23_server_method())))
+#endif
   {
   {
-  ERR_error_string(ERR_get_error(), ssl_errstring);
+  ERR_error_string_n(ERR_get_error(), ssl_errstring, sizeof(ssl_errstring));
   return string_sprintf("SSL_CTX_new() failed: %s", ssl_errstring);
   }
 
   return string_sprintf("SSL_CTX_new() failed: %s", ssl_errstring);
   }
 
@@ -2853,7 +3189,7 @@ DEBUG(D_tls)
 
 if (!SSL_CTX_set_cipher_list(ctx, CS expciphers))
   {
 
 if (!SSL_CTX_set_cipher_list(ctx, CS expciphers))
   {
-  ERR_error_string(ERR_get_error(), ssl_errstring);
+  ERR_error_string_n(ERR_get_error(), ssl_errstring, sizeof(ssl_errstring));
   err = string_sprintf("SSL_CTX_set_cipher_list(%s) failed: %s",
                      expciphers, ssl_errstring);
   }
   err = string_sprintf("SSL_CTX_set_cipher_list(%s) failed: %s",
                      expciphers, ssl_errstring);
   }
@@ -2923,7 +3259,6 @@ unsigned int r;
 int i, needed_len;
 static pid_t pidlast = 0;
 pid_t pidnow;
 int i, needed_len;
 static pid_t pidlast = 0;
 pid_t pidnow;
-uschar *p;
 uschar smallbuf[sizeof(r)];
 
 if (max <= 1)
 uschar smallbuf[sizeof(r)];
 
 if (max <= 1)
@@ -2981,11 +3316,8 @@ if (i < 0)
   }
 
 r = 0;
   }
 
 r = 0;
-for (p = smallbuf; needed_len; --needed_len, ++p)
-  {
-  r *= 256;
-  r += *p;
-  }
+for (uschar * p = smallbuf; needed_len; --needed_len, ++p)
+  r = 256 * r + *p;
 
 /* We don't particularly care about weighted results; if someone wants
 smooth distribution and cares enough then they should submit a patch then. */
 
 /* We don't particularly care about weighted results; if someone wants
 smooth distribution and cares enough then they should submit a patch then. */
@@ -3007,110 +3339,6 @@ Arguments:
 Returns   success or failure in parsing
 */
 
 Returns   success or failure in parsing
 */
 
-struct exim_openssl_option {
-  uschar *name;
-  long    value;
-};
-/* We could use a macro to expand, but we need the ifdef and not all the
-options document which version they were introduced in.  Policylet: include
-all options unless explicitly for DTLS, let the administrator choose which
-to apply.
-
-This list is current as of:
-  ==>  1.0.1b  <==
-Plus SSL_OP_SAFARI_ECDHE_ECDSA_BUG from 2013-June patch/discussion on openssl-dev
-*/
-static struct exim_openssl_option exim_openssl_options[] = {
-/* KEEP SORTED ALPHABETICALLY! */
-#ifdef SSL_OP_ALL
-  { US"all", 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_CIPHER_SERVER_PREFERENCE
-  { US"cipher_server_preference", SSL_OP_CIPHER_SERVER_PREFERENCE },
-#endif
-#ifdef SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS
-  { US"dont_insert_empty_fragments", SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS },
-#endif
-#ifdef SSL_OP_EPHEMERAL_RSA
-  { US"ephemeral_rsa", SSL_OP_EPHEMERAL_RSA },
-#endif
-#ifdef SSL_OP_LEGACY_SERVER_CONNECT
-  { US"legacy_server_connect", SSL_OP_LEGACY_SERVER_CONNECT },
-#endif
-#ifdef SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER
-  { US"microsoft_big_sslv3_buffer", SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER },
-#endif
-#ifdef SSL_OP_MICROSOFT_SESS_ID_BUG
-  { US"microsoft_sess_id_bug", SSL_OP_MICROSOFT_SESS_ID_BUG },
-#endif
-#ifdef SSL_OP_MSIE_SSLV2_RSA_PADDING
-  { US"msie_sslv2_rsa_padding", SSL_OP_MSIE_SSLV2_RSA_PADDING },
-#endif
-#ifdef SSL_OP_NETSCAPE_CHALLENGE_BUG
-  { US"netscape_challenge_bug", SSL_OP_NETSCAPE_CHALLENGE_BUG },
-#endif
-#ifdef SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG
-  { US"netscape_reuse_cipher_change_bug", SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG },
-#endif
-#ifdef SSL_OP_NO_COMPRESSION
-  { US"no_compression", SSL_OP_NO_COMPRESSION },
-#endif
-#ifdef SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION
-  { US"no_session_resumption_on_renegotiation", SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION },
-#endif
-#ifdef SSL_OP_NO_SSLv2
-  { US"no_sslv2", SSL_OP_NO_SSLv2 },
-#endif
-#ifdef SSL_OP_NO_SSLv3
-  { US"no_sslv3", SSL_OP_NO_SSLv3 },
-#endif
-#ifdef SSL_OP_NO_TICKET
-  { US"no_ticket", SSL_OP_NO_TICKET },
-#endif
-#ifdef SSL_OP_NO_TLSv1
-  { US"no_tlsv1", SSL_OP_NO_TLSv1 },
-#endif
-#ifdef SSL_OP_NO_TLSv1_1
-#if SSL_OP_NO_TLSv1_1 == 0x00000400L
-  /* Error in chosen value in 1.0.1a; see first item in CHANGES for 1.0.1b */
-#warning OpenSSL 1.0.1a uses a bad value for SSL_OP_NO_TLSv1_1, ignoring
-#else
-  { US"no_tlsv1_1", SSL_OP_NO_TLSv1_1 },
-#endif
-#endif
-#ifdef SSL_OP_NO_TLSv1_2
-  { US"no_tlsv1_2", SSL_OP_NO_TLSv1_2 },
-#endif
-#ifdef SSL_OP_SAFARI_ECDHE_ECDSA_BUG
-  { US"safari_ecdhe_ecdsa_bug", SSL_OP_SAFARI_ECDHE_ECDSA_BUG },
-#endif
-#ifdef SSL_OP_SINGLE_DH_USE
-  { US"single_dh_use", SSL_OP_SINGLE_DH_USE },
-#endif
-#ifdef SSL_OP_SINGLE_ECDH_USE
-  { US"single_ecdh_use", SSL_OP_SINGLE_ECDH_USE },
-#endif
-#ifdef SSL_OP_SSLEAY_080_CLIENT_DH_BUG
-  { US"ssleay_080_client_dh_bug", SSL_OP_SSLEAY_080_CLIENT_DH_BUG },
-#endif
-#ifdef SSL_OP_SSLREF2_REUSE_CERT_TYPE_BUG
-  { US"sslref2_reuse_cert_type_bug", SSL_OP_SSLREF2_REUSE_CERT_TYPE_BUG },
-#endif
-#ifdef SSL_OP_TLS_BLOCK_PADDING_BUG
-  { US"tls_block_padding_bug", SSL_OP_TLS_BLOCK_PADDING_BUG },
-#endif
-#ifdef SSL_OP_TLS_D5_BUG
-  { US"tls_d5_bug", SSL_OP_TLS_D5_BUG },
-#endif
-#ifdef SSL_OP_TLS_ROLLBACK_BUG
-  { US"tls_rollback_bug", SSL_OP_TLS_ROLLBACK_BUG },
-#endif
-};
-static int exim_openssl_options_size =
-  sizeof(exim_openssl_options)/sizeof(struct exim_openssl_option);
 
 
 static BOOL
 
 
 static BOOL
@@ -3156,7 +3384,7 @@ BOOL
 tls_openssl_options_parse(uschar *option_spec, long *results)
 {
 long result, item;
 tls_openssl_options_parse(uschar *option_spec, long *results)
 {
 long result, item;
-uschar *s, *end;
+uschar *end;
 uschar keep_c;
 BOOL adding, item_parsed;
 
 uschar keep_c;
 BOOL adding, item_parsed;
 
@@ -3176,7 +3404,7 @@ if (!option_spec)
   return TRUE;
   }
 
   return TRUE;
   }
 
-for (s=option_spec; *s != '\0'; /**/)
+for (uschar * s = option_spec; *s != '\0'; /**/)
   {
   while (isspace(*s)) ++s;
   if (*s == '\0')
   {
   while (isspace(*s)) ++s;
   if (*s == '\0')
@@ -3211,6 +3439,7 @@ for (s=option_spec; *s != '\0'; /**/)
 return TRUE;
 }
 
 return TRUE;
 }
 
+#endif /*!MACRO_PREDEF*/
 /* vi: aw ai sw=2
 */
 /* End of tls-openssl.c */
 /* vi: aw ai sw=2
 */
 /* End of tls-openssl.c */