Fix cert-try-verify when denied by event action
[exim.git] / src / src / tls-openssl.c
index 5e8c804e5789be729b892f0c6f8e5d7f4389104e..fe1b208ac5b2e4d708b913e2f466e11733ad0450 100644 (file)
@@ -2,9 +2,11 @@
 *     Exim - an Internet mail transport agent    *
 *************************************************/
 
-/* Copyright (c) University of Cambridge 1995 - 2009 */
+/* Copyright (c) University of Cambridge 1995 - 2014 */
 /* See the file NOTICE for conditions of use and distribution. */
 
+/* Portions Copyright (c) The OpenSSL Project 1999 */
+
 /* This module provides the TLS (aka SSL) support for Exim using the OpenSSL
 library. It is #included into the tls.c file when that library is used. The
 code herein is based on a patch that was originally contributed by Steve
@@ -20,6 +22,34 @@ functions from the OpenSSL library. */
 #include <openssl/ssl.h>
 #include <openssl/err.h>
 #include <openssl/rand.h>
+#ifndef DISABLE_OCSP
+# include <openssl/ocsp.h>
+#endif
+#ifdef EXPERIMENTAL_DANE
+# include <danessl.h>
+#endif
+
+
+#ifndef DISABLE_OCSP
+# define EXIM_OCSP_SKEW_SECONDS (300L)
+# define EXIM_OCSP_MAX_AGE (-1L)
+#endif
+
+#if OPENSSL_VERSION_NUMBER >= 0x0090806fL && !defined(OPENSSL_NO_TLSEXT)
+# define EXIM_HAVE_OPENSSL_TLSEXT
+#endif
+#if OPENSSL_VERSION_NUMBER >= 0x010100000L
+# define EXIM_HAVE_OPENSSL_CHECKHOST
+#endif
+#if OPENSSL_VERSION_NUMBER >= 0x010000000L \
+    && (OPENSSL_VERSION_NUMBER & 0x0000ff000L) >= 0x000002000L
+# define EXIM_HAVE_OPENSSL_CHECKHOST
+#endif
+
+#if !defined(EXIM_HAVE_OPENSSL_TLSEXT) && !defined(DISABLE_OCSP)
+# warning "OpenSSL library version too old; define DISABLE_OCSP in Makefile"
+# define DISABLE_OCSP
+#endif
 
 /* Structure for collecting random data for seeding. */
 
@@ -30,19 +60,95 @@ typedef struct randstuff {
 
 /* Local static variables */
 
-static BOOL verify_callback_called = FALSE;
+static BOOL client_verify_callback_called = FALSE;
+static BOOL server_verify_callback_called = FALSE;
 static const uschar *sid_ctx = US"exim";
 
-static SSL_CTX *ctx = NULL;
-static SSL *ssl = NULL;
+/* We have three different contexts to care about.
+
+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.
+
+Server:
+ There are two cases: with and without ServerNameIndication from the client.
+ Given TLS SNI, we can be using different keys, certs and various other
+ configuration settings, because they're re-expanded with $tls_sni set.  This
+ allows vhosting with TLS.  This SNI is sent in the handshake.
+ A client might not send SNI, so we need a fallback, and an initial setup too.
+ So as a server, we start out using `server_ctx`.
+ If SNI is sent by the client, then we as server, mid-negotiation, try to clone
+ `server_sni` from `server_ctx` and then initialise settings by re-expanding
+ configuration.
+*/
+
+static SSL_CTX *client_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_CTX *server_sni = NULL;
+#endif
 
 static char ssl_errstring[256];
 
 static int  ssl_session_timeout = 200;
-static BOOL verify_optional = FALSE;
+static BOOL client_verify_optional = FALSE;
+static BOOL server_verify_optional = FALSE;
+
+static BOOL reexpand_tls_files_for_sni = FALSE;
+
+
+typedef struct tls_ext_ctx_cb {
+  uschar *certificate;
+  uschar *privatekey;
+#ifndef DISABLE_OCSP
+  BOOL is_server;
+  union {
+    struct {
+      uschar        *file;
+      uschar        *file_expanded;
+      OCSP_RESPONSE *response;
+    } server;
+    struct {
+      X509_STORE    *verify_store;     /* non-null if status requested */
+      BOOL         verify_required;
+    } client;
+  } u_ocsp;
+#endif
+  uschar *dhparam;
+  /* these are cached from first expand */
+  uschar *server_cipher_list;
+  /* only passed down to tls_error: */
+  host_item *host;
+
+#ifdef EXPERIMENTAL_CERTNAMES
+  uschar * verify_cert_hostnames;
+#endif
+#ifdef EXPERIMENTAL_EVENT
+  uschar * event_action;
+#endif
+} 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 *client_static_cbinfo = NULL;
+tls_ext_ctx_cb *server_static_cbinfo = NULL;
 
+static int
+setup_certs(SSL_CTX *sctx, uschar *certs, uschar *crl, host_item *host, BOOL optional,
+    int (*cert_vfy_cb)(int, X509_STORE_CTX *) );
 
+/* Callbacks */
+#ifdef EXIM_HAVE_OPENSSL_TLSEXT
+static int tls_servername_cb(SSL *s, int *ad ARG_UNUSED, void *arg);
+#endif
+#ifndef DISABLE_OCSP
+static int tls_server_stapling_cb(SSL *s, void *arg);
+#endif
 
 
 /*************************************************
@@ -68,27 +174,28 @@ Returns:    OK/DEFER/FAIL
 static int
 tls_error(uschar *prefix, host_item *host, uschar *msg)
 {
-if (msg == NULL)
+if (!msg)
   {
   ERR_error_string(ERR_get_error(), ssl_errstring);
   msg = (uschar *)ssl_errstring;
   }
 
-if (host == NULL)
+if (host)
+  {
+  log_write(0, LOG_MAIN, "H=%s [%s] TLS error on connection (%s): %s",
+    host->name, host->address, prefix, msg);
+  return FAIL;
+  }
+else
   {
   uschar *conn_info = smtp_get_connection_info();
   if (Ustrncmp(conn_info, US"SMTP ", 5) == 0)
     conn_info += 5;
+  /* I'd like to get separated H= here, but too hard for now */
   log_write(0, LOG_MAIN, "TLS error on %s (%s): %s",
     conn_info, prefix, msg);
   return DEFER;
   }
-else
-  {
-  log_write(0, LOG_MAIN, "TLS error on connection to %s [%s] (%s): %s",
-    host->name, host->address, prefix, msg);
-  return FAIL;
-  }
 }
 
 
@@ -125,6 +232,29 @@ return rsa_key;
 
 
 
+/* Extreme debug
+#ifndef DISABLE_OCSP
+void
+x509_store_dump_cert_s_names(X509_STORE * store)
+{
+STACK_OF(X509_OBJECT) * roots= store->objs;
+int i;
+static uschar name[256];
+
+for(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 * current_cert= tmp_obj->data.x509;
+    X509_NAME_oneline(X509_get_subject_name(current_cert), CS name, sizeof(name));
+    debug_printf(" %s\n", name);
+    }
+  }
+}
+#endif
+*/
+
 
 /*************************************************
 *        Callback for verification               *
@@ -147,53 +277,224 @@ when asked. We get here only if a certificate has been received. Handling of
 optional verification for this case is done when requesting SSL to verify, by
 setting SSL_VERIFY_FAIL_IF_NO_PEER_CERT in the non-optional case.
 
+May be called multiple times for different issues with a certificate, even
+for a given "depth" in the certificate chain.
+
 Arguments:
   state      current yes/no state as 1/0
   x509ctx    certificate information.
+  client     TRUE for client startup, FALSE for server startup
 
 Returns:     1 if verified, 0 if not
 */
 
 static int
-verify_callback(int state, X509_STORE_CTX *x509ctx)
+verify_callback(int state, 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);
 static uschar txt[256];
+#ifdef EXPERIMENTAL_EVENT
+uschar * ev;
+uschar * yield;
+#endif
 
-X509_NAME_oneline(X509_get_subject_name(x509ctx->current_cert),
-  CS txt, sizeof(txt));
+X509_NAME_oneline(X509_get_subject_name(cert), CS txt, sizeof(txt));
 
 if (state == 0)
   {
   log_write(0, LOG_MAIN, "SSL verify error: depth=%d error=%s cert=%s",
-    x509ctx->error_depth,
-    X509_verify_cert_error_string(x509ctx->error),
+    depth,
+    X509_verify_cert_error_string(X509_STORE_CTX_get_error(x509ctx)),
     txt);
-  tls_certificate_verified = FALSE;
-  verify_callback_called = TRUE;
-  if (!verify_optional) return 0;    /* reject */
+  *calledp = TRUE;
+  if (!*optionalp)
+    {
+    tlsp->peercert = X509_dup(cert);
+    return 0;                      /* reject */
+    }
   DEBUG(D_tls) debug_printf("SSL verify failure overridden (host in "
     "tls_try_verify_hosts)\n");
-  return 1;                          /* accept */
   }
 
-if (x509ctx->error_depth != 0)
+else if (depth != 0)
   {
-  DEBUG(D_tls) debug_printf("SSL verify ok: depth=%d cert=%s\n",
-     x509ctx->error_depth, txt);
+  DEBUG(D_tls) debug_printf("SSL verify ok: depth=%d SN=%s\n", depth, txt);
+#ifndef DISABLE_OCSP
+  if (tlsp == &tls_out && client_static_cbinfo->u_ocsp.client.verify_store)
+    {  /* client, wanting stapling  */
+    /* Add the server cert's signing chain as the one
+    for the verification of the OCSP stapled information. */
+  
+    if (!X509_STORE_add_cert(client_static_cbinfo->u_ocsp.client.verify_store,
+                             cert))
+      ERR_clear_error();
+    }
+#endif
+#ifdef EXPERIMENTAL_EVENT
+  ev = tlsp == &tls_out ? client_static_cbinfo->event_action : event_action;
+  if (ev)
+    {
+    tlsp->peercert = X509_dup(cert);
+    if ((yield = event_raise(ev, US"tls:cert", string_sprintf("%d", depth))))
+      {
+      log_write(0, LOG_MAIN, "SSL verify denied by event-action: "
+                             "depth=%d cert=%s: %s", depth, txt, yield);
+      *calledp = TRUE;
+      if (!*optionalp)
+       return 0;                           /* reject */
+      DEBUG(D_tls) debug_printf("Event-action verify failure overridden "
+       "(host in tls_try_verify_hosts)\n");
+      }
+    X509_free(tlsp->peercert);
+    tlsp->peercert = NULL;
+    }
+#endif
   }
 else
   {
-  DEBUG(D_tls) debug_printf("SSL%s peer: %s\n",
-    verify_callback_called? "" : " authenticated", txt);
-  tls_peerdn = txt;
+#ifdef EXPERIMENTAL_CERTNAMES
+  uschar * verify_cert_hostnames;
+#endif
+
+  tlsp->peerdn = txt;
+  tlsp->peercert = X509_dup(cert);
+
+#ifdef EXPERIMENTAL_CERTNAMES
+  if (  tlsp == &tls_out
+     && ((verify_cert_hostnames = client_static_cbinfo->verify_cert_hostnames)))
+       /* client, wanting hostname check */
+
+# if EXIM_HAVE_OPENSSL_CHECKHOST
+#  ifndef X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS
+#   define X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS 0
+#  endif
+    {
+    int sep = 0;
+    uschar * list = verify_cert_hostnames;
+    uschar * name;
+    int rc;
+    while ((name = string_nextinlist(&list, &sep, NULL, 0)))
+      if ((rc = X509_check_host(cert, name, 0,
+                 X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS)))
+       {
+       if (rc < 0)
+         {
+         log_write(0, LOG_MAIN, "SSL verify error: internal error\n");
+         name = NULL;
+         }
+       break;
+       }
+    if (!name)
+      {
+      log_write(0, LOG_MAIN,
+       "SSL verify error: certificate name mismatch: \"%s\"\n", txt);
+      *calledp = TRUE;
+      if (!*optionalp)
+       return 0;                           /* reject */
+      DEBUG(D_tls) debug_printf("SSL verify failure overridden (host in "
+       "tls_try_verify_hosts)\n");
+      }
+    }
+# else
+    if (!tls_is_name_for_cert(verify_cert_hostnames, cert))
+      {
+      log_write(0, LOG_MAIN,
+       "SSL verify error: certificate name mismatch: \"%s\"\n", txt);
+      *calledp = TRUE;
+      if (!*optionalp)
+       return 0;                           /* reject */
+      DEBUG(D_tls) debug_printf("SSL verify failure overridden (host in "
+       "tls_try_verify_hosts)\n");
+      }
+# endif
+#endif /*EXPERIMENTAL_CERTNAMES*/
+
+#ifdef EXPERIMENTAL_EVENT
+  ev = tlsp == &tls_out ? client_static_cbinfo->event_action : event_action;
+  if (ev)
+    if ((yield = event_raise(ev, US"tls:cert", US"0")))
+      {
+      log_write(0, LOG_MAIN, "SSL verify denied by event-action: "
+                             "depth=0 cert=%s: %s", txt, yield);
+      *calledp = TRUE;
+      if (!*optionalp)
+       return 0;                           /* reject */
+      DEBUG(D_tls) debug_printf("Event-action verify failure overridden "
+       "(host in tls_try_verify_hosts)\n");
+      }
+#endif
+
+  DEBUG(D_tls) debug_printf("SSL%s verify ok: depth=0 SN=%s\n",
+    *calledp ? "" : " authenticated", txt);
+  if (!*calledp) tlsp->certificate_verified = TRUE;
+  *calledp = TRUE;
   }
 
-if (!verify_callback_called) tls_certificate_verified = TRUE;
-verify_callback_called = TRUE;
+return 1;   /* accept, at least for this level */
+}
+
+static int
+verify_callback_client(int state, X509_STORE_CTX *x509ctx)
+{
+return verify_callback(state, x509ctx, &tls_out, &client_verify_callback_called, &client_verify_optional);
+}
+
+static int
+verify_callback_server(int state, X509_STORE_CTX *x509ctx)
+{
+return verify_callback(state, x509ctx, &tls_in, &server_verify_callback_called, &server_verify_optional);
+}
+
+
+#ifdef EXPERIMENTAL_DANE
+
+/* This gets called *by* the dane library verify callback, which interposes
+itself.
+*/
+static int
+verify_callback_client_dane(int state, X509_STORE_CTX * x509ctx)
+{
+X509 * cert = X509_STORE_CTX_get_current_cert(x509ctx);
+static uschar txt[256];
+#ifdef EXPERIMENTAL_EVENT
+int depth = X509_STORE_CTX_get_error_depth(x509ctx);
+uschar * yield;
+#endif
+
+X509_NAME_oneline(X509_get_subject_name(cert), CS txt, sizeof(txt));
+
+DEBUG(D_tls) debug_printf("verify_callback_client_dane: %s\n", txt);
+tls_out.peerdn = txt;
+tls_out.peercert = X509_dup(cert);
+
+#ifdef EXPERIMENTAL_EVENT
+  if (client_static_cbinfo->event_action)
+    {
+    if ((yield = event_raise(client_static_cbinfo->event_action,
+                   US"tls:cert", string_sprintf("%d", depth))))
+      {
+      log_write(0, LOG_MAIN, "DANE verify denied by event-action: "
+                             "depth=%d cert=%s: %s", depth, txt, yield);
+      tls_out.certificate_verified = FALSE;
+      return 0;                            /* reject */
+      }
+    if (depth != 0)
+      {
+      X509_free(tls_out.peercert);
+      tls_out.peercert = NULL;
+      }
+    }
+#endif
 
-return 1;   /* accept */
+if (state == 1)
+  tls_out.dane_verified =
+  tls_out.certificate_verified = TRUE;
+return 1;
 }
 
+#endif /*EXPERIMENTAL_DANE*/
 
 
 /*************************************************
@@ -201,8 +502,8 @@ return 1;   /* accept */
 *************************************************/
 
 /* 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
@@ -229,79 +530,640 @@ DEBUG(D_tls) debug_printf("SSL info: %s\n", SSL_state_string_long(s));
 /* If dhparam is set, expand it, and load up the parameters for DH encryption.
 
 Arguments:
-  dhparam   DH parameter file
+  dhparam   DH parameter file or fixed parameter identity string
   host      connected host, if client; NULL if server
 
 Returns:    TRUE if OK (nothing to set up, or setup worked)
 */
 
 static BOOL
-init_dh(uschar *dhparam, host_item *host)
+init_dh(SSL_CTX *sctx, uschar *dhparam, host_item *host)
 {
-BOOL yield = TRUE;
 BIO *bio;
 DH *dh;
 uschar *dhexpanded;
+const char *pem;
 
 if (!expand_check(dhparam, US"tls_dhparam", &dhexpanded))
   return FALSE;
 
-if (dhexpanded == NULL) return TRUE;
-
-if ((bio = BIO_new_file(CS dhexpanded, "r")) == NULL)
+if (!dhexpanded || !*dhexpanded)
+  bio = BIO_new_mem_buf(CS std_dh_prime_default(), -1);
+else if (dhexpanded[0] == '/')
   {
-  tls_error(string_sprintf("could not read dhparams file %s", dhexpanded),
-    host, (uschar *)strerror(errno));
-  yield = FALSE;
+  if (!(bio = BIO_new_file(CS dhexpanded, "r")))
+    {
+    tls_error(string_sprintf("could not read dhparams file %s", dhexpanded),
+          host, US strerror(errno));
+    return FALSE;
+    }
   }
 else
   {
-  if ((dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL)) == NULL)
+  if (Ustrcmp(dhexpanded, "none") == 0)
     {
-    tls_error(string_sprintf("could not read dhparams file %s", dhexpanded),
-      host, NULL);
-    yield = FALSE;
+    DEBUG(D_tls) debug_printf("Requested no DH parameters.\n");
+    return TRUE;
     }
-  else
+
+  if (!(pem = std_dh_prime_named(dhexpanded)))
     {
-    SSL_CTX_set_tmp_dh(ctx, dh);
-    DEBUG(D_tls)
-      debug_printf("Diffie-Hellman initialized from %s with %d-bit key\n",
-        dhexpanded, 8*DH_size(dh));
-    DH_free(dh);
+    tls_error(string_sprintf("Unknown standard DH prime \"%s\"", dhexpanded),
+        host, US strerror(errno));
+    return FALSE;
     }
+  bio = BIO_new_mem_buf(CS pem, -1);
+  }
+
+if (!(dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL)))
+  {
   BIO_free(bio);
+  tls_error(string_sprintf("Could not read tls_dhparams \"%s\"", dhexpanded),
+      host, NULL);
+  return FALSE;
+  }
+
+/* Even if it is larger, we silently return success rather than cause things
+ * to fail out, so that a too-large DH will not knock out all TLS; it's a
+ * debatable choice. */
+if ((8*DH_size(dh)) > tls_dh_max_bits)
+  {
+  DEBUG(D_tls)
+    debug_printf("dhparams file %d bits, is > tls_dh_max_bits limit of %d",
+        8*DH_size(dh), tls_dh_max_bits);
   }
+else
+  {
+  SSL_CTX_set_tmp_dh(sctx, dh);
+  DEBUG(D_tls)
+    debug_printf("Diffie-Hellman initialized from %s with %d-bit prime\n",
+      dhexpanded ? dhexpanded : US"default", 8*DH_size(dh));
+  }
+
+DH_free(dh);
+BIO_free(bio);
 
-return yield;
+return TRUE;
 }
 
 
 
 
+#ifndef DISABLE_OCSP
+/*************************************************
+*       Load OCSP information into state         *
+*************************************************/
+
+/* Called to load the server OCSP response from the given file into memory, once
+caller has determined this is needed.  Checks validity.  Debugs a message
+if invalid.
+
+ASSUMES: single response, for single cert.
+
+Arguments:
+  sctx            the SSL_CTX* to update
+  cbinfo          various parts of session state
+  expanded        the filename putatively holding an OCSP response
+
+*/
+
+static void
+ocsp_load_response(SSL_CTX *sctx, tls_ext_ctx_cb *cbinfo, const uschar *expanded)
+{
+BIO *bio;
+OCSP_RESPONSE *resp;
+OCSP_BASICRESP *basic_response;
+OCSP_SINGLERESP *single_response;
+ASN1_GENERALIZEDTIME *rev, *thisupd, *nextupd;
+X509_STORE *store;
+unsigned long verify_flags;
+int status, reason, i;
+
+cbinfo->u_ocsp.server.file_expanded = string_copy(expanded);
+if (cbinfo->u_ocsp.server.response)
+  {
+  OCSP_RESPONSE_free(cbinfo->u_ocsp.server.response);
+  cbinfo->u_ocsp.server.response = NULL;
+  }
+
+bio = BIO_new_file(CS cbinfo->u_ocsp.server.file_expanded, "rb");
+if (!bio)
+  {
+  DEBUG(D_tls) debug_printf("Failed to open OCSP response file \"%s\"\n",
+      cbinfo->u_ocsp.server.file_expanded);
+  return;
+  }
+
+resp = d2i_OCSP_RESPONSE_bio(bio, NULL);
+BIO_free(bio);
+if (!resp)
+  {
+  DEBUG(D_tls) debug_printf("Error reading OCSP response.\n");
+  return;
+  }
+
+status = OCSP_response_status(resp);
+if (status != OCSP_RESPONSE_STATUS_SUCCESSFUL)
+  {
+  DEBUG(D_tls) debug_printf("OCSP response not valid: %s (%d)\n",
+      OCSP_response_status_str(status), status);
+  goto bad;
+  }
+
+basic_response = OCSP_response_get1_basic(resp);
+if (!basic_response)
+  {
+  DEBUG(D_tls)
+    debug_printf("OCSP response parse error: unable to extract basic response.\n");
+  goto bad;
+  }
+
+store = SSL_CTX_get_cert_store(sctx);
+verify_flags = OCSP_NOVERIFY; /* check sigs, but not purpose */
+
+/* May need to expose ability to adjust those flags?
+OCSP_NOSIGS OCSP_NOVERIFY OCSP_NOCHAIN OCSP_NOCHECKS OCSP_NOEXPLICIT
+OCSP_TRUSTOTHER OCSP_NOINTERN */
+
+i = OCSP_basic_verify(basic_response, NULL, store, verify_flags);
+if (i <= 0)
+  {
+  DEBUG(D_tls) {
+    ERR_error_string(ERR_get_error(), ssl_errstring);
+    debug_printf("OCSP response verify failure: %s\n", US ssl_errstring);
+    }
+  goto bad;
+  }
+
+/* Here's the simplifying assumption: there's only one response, for the
+one certificate we use, and nothing for anything else in a chain.  If this
+proves false, we need to extract a cert id from our issued cert
+(tls_certificate) and use that for OCSP_resp_find_status() (which finds the
+right cert in the stack and then calls OCSP_single_get0_status()).
+
+I'm hoping to avoid reworking a bunch more of how we handle state here. */
+single_response = OCSP_resp_get0(basic_response, 0);
+if (!single_response)
+  {
+  DEBUG(D_tls)
+    debug_printf("Unable to get first response from OCSP basic response.\n");
+  goto bad;
+  }
+
+status = OCSP_single_get0_status(single_response, &reason, &rev, &thisupd, &nextupd);
+if (status != V_OCSP_CERTSTATUS_GOOD)
+  {
+  DEBUG(D_tls) debug_printf("OCSP response bad cert status: %s (%d) %s (%d)\n",
+      OCSP_cert_status_str(status), status,
+      OCSP_crl_reason_str(reason), reason);
+  goto bad;
+  }
+
+if (!OCSP_check_validity(thisupd, nextupd, EXIM_OCSP_SKEW_SECONDS, EXIM_OCSP_MAX_AGE))
+  {
+  DEBUG(D_tls) debug_printf("OCSP status invalid times.\n");
+  goto bad;
+  }
+
+supply_response:
+  cbinfo->u_ocsp.server.response = resp;
+return;
+
+bad:
+  if (running_in_test_harness)
+    {
+    extern char ** environ;
+    uschar ** p;
+    for (p = USS environ; *p != NULL; p++)
+      if (Ustrncmp(*p, "EXIM_TESTHARNESS_DISABLE_OCSPVALIDITYCHECK", 42) == 0)
+       {
+       DEBUG(D_tls) debug_printf("Supplying known bad OCSP response\n");
+       goto supply_response;
+       }
+    }
+return;
+}
+#endif /*!DISABLE_OCSP*/
+
+
+
+
+/*************************************************
+*        Expand key and cert file specs          *
+*************************************************/
+
+/* Called once during tls_init and possibly again during TLS setup, for a
+new context, if Server Name Indication was used and tls_sni was seen in
+the certificate string.
+
+Arguments:
+  sctx            the SSL_CTX* to update
+  cbinfo          various parts of session state
+
+Returns:          OK/DEFER/FAIL
+*/
+
+static int
+tls_expand_session_files(SSL_CTX *sctx, tls_ext_ctx_cb *cbinfo)
+{
+uschar *expanded;
+
+if (cbinfo->certificate == NULL)
+  return OK;
+
+if (Ustrstr(cbinfo->certificate, US"tls_sni") ||
+    Ustrstr(cbinfo->certificate, US"tls_in_sni") ||
+    Ustrstr(cbinfo->certificate, US"tls_out_sni")
+   )
+  reexpand_tls_files_for_sni = TRUE;
+
+if (!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);
+  }
+
+#ifndef DISABLE_OCSP
+if (cbinfo->is_server &&  cbinfo->u_ocsp.server.file != NULL)
+  {
+  if (!expand_check(cbinfo->u_ocsp.server.file, US"tls_ocsp_file", &expanded))
+    return DEFER;
+
+  if (expanded != NULL && *expanded != 0)
+    {
+    DEBUG(D_tls) debug_printf("tls_ocsp_file %s\n", expanded);
+    if (cbinfo->u_ocsp.server.file_expanded &&
+        (Ustrcmp(expanded, cbinfo->u_ocsp.server.file_expanded) == 0))
+      {
+      DEBUG(D_tls)
+        debug_printf("tls_ocsp_file value unchanged, using existing values.\n");
+      } else {
+        ocsp_load_response(sctx, cbinfo, expanded);
+      }
+    }
+  }
+#endif
+
+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}
+*/
+
+#ifdef EXIM_HAVE_OPENSSL_TLSEXT
+static int
+tls_servername_cb(SSL *s, int *ad ARG_UNUSED, void *arg)
+{
+const char *servername = SSL_get_servername(s, TLSEXT_NAMETYPE_host_name);
+tls_ext_ctx_cb *cbinfo = (tls_ext_ctx_cb *) arg;
+int rc;
+int old_pool = store_pool;
+
+if (!servername)
+  return SSL_TLSEXT_ERR_OK;
+
+DEBUG(D_tls) debug_printf("Received TLS SNI \"%s\"%s\n", servername,
+    reexpand_tls_files_for_sni ? "" : " (unused for certificate selection)");
+
+/* Make the extension value available for expansion */
+store_pool = POOL_PERM;
+tls_in.sni = string_copy(US servername);
+store_pool = old_pool;
+
+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. */
+
+if (!(server_sni = SSL_CTX_new(SSLv23_server_method())))
+  {
+  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(server_sni, SSL_CTX_get_info_callback(server_ctx));
+SSL_CTX_set_mode(server_sni, SSL_CTX_get_mode(server_ctx));
+SSL_CTX_set_options(server_sni, SSL_CTX_get_options(server_ctx));
+SSL_CTX_set_timeout(server_sni, SSL_CTX_get_timeout(server_ctx));
+SSL_CTX_set_tlsext_servername_callback(server_sni, tls_servername_cb);
+SSL_CTX_set_tlsext_servername_arg(server_sni, cbinfo);
+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)
+  {
+  SSL_CTX_set_tlsext_status_cb(server_sni, tls_server_stapling_cb);
+  SSL_CTX_set_tlsext_status_arg(server_sni, cbinfo);
+  }
+#endif
+
+rc = setup_certs(server_sni, tls_verify_certificates, tls_crl, NULL, FALSE, verify_callback_server);
+if (rc != OK) return SSL_TLSEXT_ERR_NOACK;
+
+/* do this after setup_certs, because this can require the certs for verifying
+OCSP information. */
+rc = tls_expand_session_files(server_sni, cbinfo);
+if (rc != OK) return SSL_TLSEXT_ERR_NOACK;
+
+if (!init_dh(server_sni, cbinfo->dhparam, NULL))
+  return SSL_TLSEXT_ERR_NOACK;
+
+DEBUG(D_tls) debug_printf("Switching SSL context.\n");
+SSL_set_SSL_CTX(s, server_sni);
+
+return SSL_TLSEXT_ERR_OK;
+}
+#endif /* EXIM_HAVE_OPENSSL_TLSEXT */
+
+
+
+
+#ifndef DISABLE_OCSP
+
+/*************************************************
+*        Callback to handle OCSP Stapling        *
+*************************************************/
+
+/* Called when acting as server during the TLS session setup if the client
+requests OCSP information with a Certificate Status Request.
+
+Documentation via openssl s_server.c and the Apache patch from the OpenSSL
+project.
+
+*/
+
+static int
+tls_server_stapling_cb(SSL *s, void *arg)
+{
+const tls_ext_ctx_cb *cbinfo = (tls_ext_ctx_cb *) arg;
+uschar *response_der;
+int response_der_len;
+
+DEBUG(D_tls)
+  debug_printf("Received TLS status request (OCSP stapling); %s response.",
+    cbinfo->u_ocsp.server.response ? "have" : "lack");
+
+tls_in.ocsp = OCSP_NOT_RESP;
+if (!cbinfo->u_ocsp.server.response)
+  return SSL_TLSEXT_ERR_NOACK;
+
+response_der = NULL;
+response_der_len = i2d_OCSP_RESPONSE(cbinfo->u_ocsp.server.response,
+                     &response_der);
+if (response_der_len <= 0)
+  return SSL_TLSEXT_ERR_NOACK;
+
+SSL_set_tlsext_status_ocsp_resp(server_ssl, response_der, response_der_len);
+tls_in.ocsp = OCSP_VFIED;
+return SSL_TLSEXT_ERR_OK;
+}
+
+
+static void
+time_print(BIO * bp, const char * str, ASN1_GENERALIZEDTIME * time)
+{
+BIO_printf(bp, "\t%s: ", str);
+ASN1_GENERALIZEDTIME_print(bp, time);
+BIO_puts(bp, "\n");
+}
+
+static int
+tls_client_stapling_cb(SSL *s, void *arg)
+{
+tls_ext_ctx_cb * cbinfo = arg;
+const unsigned char * p;
+int len;
+OCSP_RESPONSE * rsp;
+OCSP_BASICRESP * bs;
+int i;
+
+DEBUG(D_tls) debug_printf("Received TLS status response (OCSP stapling):");
+len = SSL_get_tlsext_status_ocsp_resp(s, &p);
+if(!p)
+ {
+  /* Expect this when we requested ocsp but got none */
+  if (  cbinfo->u_ocsp.client.verify_required
+     && log_extra_selector & LX_tls_cipher)
+    log_write(0, LOG_MAIN, "Received TLS status callback, null content");
+  else
+    DEBUG(D_tls) debug_printf(" null\n");
+  return cbinfo->u_ocsp.client.verify_required ? 0 : 1;
+ }
+
+if(!(rsp = d2i_OCSP_RESPONSE(NULL, &p, len)))
+ {
+  tls_out.ocsp = OCSP_FAILED;
+  if (log_extra_selector & LX_tls_cipher)
+    log_write(0, LOG_MAIN, "Received TLS cert status response, parse error");
+  else
+    DEBUG(D_tls) debug_printf(" parse error\n");
+  return 0;
+ }
+
+if(!(bs = OCSP_response_get1_basic(rsp)))
+  {
+  tls_out.ocsp = OCSP_FAILED;
+  if (log_extra_selector & LX_tls_cipher)
+    log_write(0, LOG_MAIN, "Received TLS cert status response, error parsing response");
+  else
+    DEBUG(D_tls) debug_printf(" error parsing response\n");
+  OCSP_RESPONSE_free(rsp);
+  return 0;
+  }
+
+/* We'd check the nonce here if we'd put one in the request. */
+/* However that would defeat cacheability on the server so we don't. */
+
+/* This section of code reworked from OpenSSL apps source;
+   The OpenSSL Project retains copyright:
+   Copyright (c) 1999 The OpenSSL Project.  All rights reserved.
+*/
+  {
+    BIO * bp = NULL;
+    int status, reason;
+    ASN1_GENERALIZEDTIME *rev, *thisupd, *nextupd;
+
+    DEBUG(D_tls) bp = BIO_new_fp(stderr, BIO_NOCLOSE);
+
+    /*OCSP_RESPONSE_print(bp, rsp, 0);   extreme debug: stapling content */
+
+    /* Use the chain that verified the server cert to verify the stapled info */
+    /* DEBUG(D_tls) x509_store_dump_cert_s_names(cbinfo->u_ocsp.client.verify_store); */
+
+    if ((i = OCSP_basic_verify(bs, NULL,
+             cbinfo->u_ocsp.client.verify_store, 0)) <= 0)
+      {
+      tls_out.ocsp = OCSP_FAILED;
+      if (log_extra_selector & LX_tls_cipher)
+       log_write(0, LOG_MAIN, "Received TLS cert status response, itself unverifiable");
+      BIO_printf(bp, "OCSP response verify failure\n");
+      ERR_print_errors(bp);
+      i = cbinfo->u_ocsp.client.verify_required ? 0 : 1;
+      goto out;
+      }
+
+    BIO_printf(bp, "OCSP response well-formed and signed OK\n");
+
+      {
+      STACK_OF(OCSP_SINGLERESP) * sresp = bs->tbsResponseData->responses;
+      OCSP_SINGLERESP * single;
+
+      if (sk_OCSP_SINGLERESP_num(sresp) != 1)
+        {
+       tls_out.ocsp = OCSP_FAILED;
+        log_write(0, LOG_MAIN, "OCSP stapling "
+           "with multiple responses not handled");
+       i = cbinfo->u_ocsp.client.verify_required ? 0 : 1;
+        goto out;
+        }
+      single = OCSP_resp_get0(bs, 0);
+      status = OCSP_single_get0_status(single, &reason, &rev,
+                 &thisupd, &nextupd);
+      }
+
+    DEBUG(D_tls) time_print(bp, "This OCSP Update", thisupd);
+    DEBUG(D_tls) if(nextupd) time_print(bp, "Next OCSP Update", nextupd);
+    if (!OCSP_check_validity(thisupd, nextupd,
+         EXIM_OCSP_SKEW_SECONDS, EXIM_OCSP_MAX_AGE))
+      {
+      tls_out.ocsp = OCSP_FAILED;
+      DEBUG(D_tls) ERR_print_errors(bp);
+      log_write(0, LOG_MAIN, "Server OSCP dates invalid");
+      i = cbinfo->u_ocsp.client.verify_required ? 0 : 1;
+      }
+    else
+      {
+      DEBUG(D_tls) BIO_printf(bp, "Certificate status: %s\n",
+                   OCSP_cert_status_str(status));
+      switch(status)
+       {
+       case V_OCSP_CERTSTATUS_GOOD:
+         tls_out.ocsp = OCSP_VFIED;
+         i = 1;
+         break;
+       case V_OCSP_CERTSTATUS_REVOKED:
+         tls_out.ocsp = OCSP_FAILED;
+         log_write(0, LOG_MAIN, "Server certificate revoked%s%s",
+             reason != -1 ? "; reason: " : "",
+             reason != -1 ? OCSP_crl_reason_str(reason) : "");
+         DEBUG(D_tls) time_print(bp, "Revocation Time", rev);
+         i = cbinfo->u_ocsp.client.verify_required ? 0 : 1;
+         break;
+       default:
+         tls_out.ocsp = OCSP_FAILED;
+         log_write(0, LOG_MAIN,
+             "Server certificate status unknown, in OCSP stapling");
+         i = cbinfo->u_ocsp.client.verify_required ? 0 : 1;
+         break;
+       }
+      }
+  out:
+    BIO_free(bp);
+  }
+
+OCSP_RESPONSE_free(rsp);
+return i;
+}
+#endif /*!DISABLE_OCSP*/
+
+
 /*************************************************
 *            Initialize for TLS                  *
 *************************************************/
 
-/* Called from both server and client code, to do preliminary initialization of
-the library.
+/* Called from both server and client code, to do preliminary initialization
+of the library.  We allocate and return a context structure.
 
 Arguments:
+  ctxp            returned SSL context
   host            connected host, if client; NULL if server
   dhparam         DH parameter file
   certificate     certificate file
   privatekey      private key
+  ocsp_file       file of stapling info (server); flag for require ocsp (client)
   addr            address if client; NULL if server (for some randomness)
+  cbp             place to put allocated callback context
 
 Returns:          OK/DEFER/FAIL
 */
 
 static int
-tls_init(host_item *host, uschar *dhparam, uschar *certificate,
-  uschar *privatekey, address_item *addr)
+tls_init(SSL_CTX **ctxp, host_item *host, uschar *dhparam, uschar *certificate,
+  uschar *privatekey,
+#ifndef DISABLE_OCSP
+  uschar *ocsp_file,
+#endif
+  address_item *addr, tls_ext_ctx_cb ** cbp)
 {
 long init_options;
+int rc;
 BOOL okay;
+tls_ext_ctx_cb * cbinfo;
+
+cbinfo = store_malloc(sizeof(tls_ext_ctx_cb));
+cbinfo->certificate = certificate;
+cbinfo->privatekey = privatekey;
+#ifndef DISABLE_OCSP
+if ((cbinfo->is_server = host==NULL))
+  {
+  cbinfo->u_ocsp.server.file = ocsp_file;
+  cbinfo->u_ocsp.server.file_expanded = NULL;
+  cbinfo->u_ocsp.server.response = NULL;
+  }
+else
+  cbinfo->u_ocsp.client.verify_store = NULL;
+#endif
+cbinfo->dhparam = dhparam;
+cbinfo->server_cipher_list = NULL;
+cbinfo->host = host;
+#ifdef EXPERIMENTAL_EVENT
+cbinfo->event_action = NULL;
+#endif
 
 SSL_load_error_strings();          /* basic set up */
 OpenSSL_add_ssl_algorithms();
@@ -312,12 +1174,18 @@ list of available digests. */
 EVP_add_digest(EVP_sha256());
 #endif
 
-/* Create a context */
+/* Create a context.
+The OpenSSL docs in 1.0.1b have not been updated to clarify TLS variant
+negotiation in the different methods; as far as I can tell, the only
+*_{server,client}_method which allows negotiation is SSLv23, which exists even
+when OpenSSL is built without SSLv2 support.
+By disabling with openssl_options, we can let admins re-enable with the
+existing knob. */
 
-ctx = SSL_CTX_new((host == NULL)?
+*ctxp = SSL_CTX_new((host == NULL)?
   SSLv23_server_method() : SSLv23_client_method());
 
-if (ctx == NULL) return tls_error(US"SSL_CTX_new", host, NULL);
+if (*ctxp == NULL) return tls_error(US"SSL_CTX_new", host, NULL);
 
 /* It turns out that we need to seed the random number generator this early in
 order to get the full complement of ciphers to work. It took me roughly a day
@@ -345,10 +1213,10 @@ if (!RAND_status())
 /* Set up the information callback, which outputs if debugging is at a suitable
 level. */
 
-SSL_CTX_set_info_callback(ctx, (void (*)())info_callback);
+SSL_CTX_set_info_callback(*ctxp, (void (*)())info_callback);
 
 /* Automatically re-try reads/writes after renegotiation. */
-(void) SSL_CTX_set_mode(ctx, SSL_MODE_AUTO_RETRY);
+(void) SSL_CTX_set_mode(*ctxp, SSL_MODE_AUTO_RETRY);
 
 /* Apply administrator-supplied work-arounds.
 Historically we applied just one requested option,
@@ -366,7 +1234,7 @@ if (!okay)
 if (init_options)
   {
   DEBUG(D_tls) debug_printf("setting SSL CTX options: %#lx\n", init_options);
-  if (!(SSL_CTX_set_options(ctx, init_options)))
+  if (!(SSL_CTX_set_options(*ctxp, init_options)))
     return tls_error(string_sprintf(
           "SSL_CTX_set_option(%#lx)", init_options), host, NULL);
   }
@@ -375,49 +1243,63 @@ else
 
 /* Initialize with DH parameters if supplied */
 
-if (!init_dh(dhparam, host)) return DEFER;
+if (!init_dh(*ctxp, dhparam, host)) return DEFER;
 
-/* Set up certificate and key */
+/* Set up certificate and key (and perhaps OCSP info) */
 
-if (certificate != NULL)
-  {
-  uschar *expanded;
-  if (!expand_check(certificate, US"tls_certificate", &expanded))
-    return DEFER;
+rc = tls_expand_session_files(*ctxp, cbinfo);
+if (rc != OK) return rc;
 
-  if (expanded != NULL)
+/* If we need to handle SNI, do so */
+#ifdef EXIM_HAVE_OPENSSL_TLSEXT
+if (host == NULL)              /* server */
+  {
+# ifndef DISABLE_OCSP
+  /* We check u_ocsp.server.file, not server.response, because we care about if
+  the option exists, not what the current expansion might be, as SNI might
+  change the certificate and OCSP file in use between now and the time the
+  callback is invoked. */
+  if (cbinfo->u_ocsp.server.file)
     {
-    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);
+    SSL_CTX_set_tlsext_status_cb(server_ctx, tls_server_stapling_cb);
+    SSL_CTX_set_tlsext_status_arg(server_ctx, cbinfo);
     }
-
-  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. */
-
-  if (expanded != NULL && *expanded != 0)
+# endif
+  /* We always do this, so that $tls_sni is available even if not used in
+  tls_certificate */
+  SSL_CTX_set_tlsext_servername_callback(*ctxp, tls_servername_cb);
+  SSL_CTX_set_tlsext_servername_arg(*ctxp, cbinfo);
+  }
+# ifndef DISABLE_OCSP
+else                   /* client */
+  if(ocsp_file)                /* wanting stapling */
     {
-    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 (!(cbinfo->u_ocsp.client.verify_store = X509_STORE_new()))
+      {
+      DEBUG(D_tls) debug_printf("failed to create store for stapling verify\n");
+      return FAIL;
+      }
+    SSL_CTX_set_tlsext_status_cb(*ctxp, tls_client_stapling_cb);
+    SSL_CTX_set_tlsext_status_arg(*ctxp, cbinfo);
     }
-  }
+# endif
+#endif
+
+#ifdef EXPERIMENTAL_CERTNAMES
+cbinfo->verify_cert_hostnames = NULL;
+#endif
 
 /* Set up the RSA callback */
 
-SSL_CTX_set_tmp_rsa_callback(ctx, rsa_callback);
+SSL_CTX_set_tmp_rsa_callback(*ctxp, rsa_callback);
 
 /* Finally, set the timeout, and we are done */
 
-SSL_CTX_set_timeout(ctx, ssl_session_timeout);
+SSL_CTX_set_timeout(*ctxp, ssl_session_timeout);
 DEBUG(D_tls) debug_printf("Initialized TLS\n");
+
+*cbp = cbinfo;
+
 return OK;
 }
 
@@ -428,59 +1310,30 @@ return OK;
 *           Get name of cipher in use            *
 *************************************************/
 
-/* The answer is left in a static buffer, and tls_cipher is set to point
-to it.
-
+/*
 Argument:   pointer to an SSL structure for the connection
+            buffer to use for answer
+            size of buffer
+           pointer to number of bits for cipher
 Returns:    nothing
 */
 
 static void
-construct_cipher_name(SSL *ssl)
+construct_cipher_name(SSL *ssl, uschar *cipherbuf, int bsize, int *bits)
 {
-static uschar cipherbuf[256];
 /* With OpenSSL 1.0.0a, this needs to be const but the documentation doesn't
 yet reflect that.  It should be a safe change anyway, even 0.9.8 versions have
 the accessor functions use const in the prototype. */
 const SSL_CIPHER *c;
-uschar *ver;
-
-switch (ssl->session->ssl_version)
-  {
-  case SSL2_VERSION:
-  ver = US"SSLv2";
-  break;
+const uschar *ver;
 
-  case SSL3_VERSION:
-  ver = US"SSLv3";
-  break;
-
-  case TLS1_VERSION:
-  ver = US"TLSv1";
-  break;
-
-#ifdef TLS1_1_VERSION
-  case TLS1_1_VERSION:
-  ver = US"TLSv1.1";
-  break;
-#endif
-
-#ifdef TLS1_2_VERSION
-  case TLS1_2_VERSION:
-  ver = US"TLSv1.2";
-  break;
-#endif
-
-  default:
-  ver = US"UNKNOWN";
-  }
+ver = (const uschar *)SSL_get_version(ssl);
 
 c = (const SSL_CIPHER *) SSL_get_current_cipher(ssl);
-SSL_CIPHER_get_bits(c, &tls_bits);
+SSL_CIPHER_get_bits(c, bits);
 
-string_format(cipherbuf, sizeof(cipherbuf), "%s:%s:%u", ver,
-  SSL_CIPHER_get_name(c), tls_bits);
-tls_cipher = cipherbuf;
+string_format(cipherbuf, bsize, "%s:%s:%u", ver,
+  SSL_CIPHER_get_name(c), *bits);
 
 DEBUG(D_tls) debug_printf("Cipher: %s\n", cipherbuf);
 }
@@ -496,27 +1349,30 @@ DEBUG(D_tls) debug_printf("Cipher: %s\n", cipherbuf);
 /* 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
   optional      TRUE if called from a server for a host in tls_try_verify_hosts;
                 otherwise passed as FALSE
+  cert_vfy_cb  Callback function for certificate verification
 
 Returns:        OK/DEFER/FAIL
 */
 
 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,
+    int (*cert_vfy_cb)(int, X509_STORE_CTX *) )
 {
 uschar *expcerts, *expcrl;
 
 if (!expand_check(certs, US"tls_verify_certificates", &expcerts))
   return DEFER;
 
-if (expcerts != NULL)
+if (expcerts != NULL && *expcerts != '\0')
   {
   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)
@@ -539,12 +1395,26 @@ if (expcerts != NULL)
     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);
 
+    /* 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 reqest for client certs.
+    Meanwhile, the client library as deafult 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.
+    */
     if (file != NULL)
       {
-      SSL_CTX_set_client_CA_list(ctx, SSL_load_client_CA_file(CS file));
+      STACK_OF(X509_NAME) * names = SSL_load_client_CA_file(CS file);
+DEBUG(D_tls) debug_printf("Added %d certificate authorities.\n",
+                                 sk_X509_NAME_num(names));
+      SSL_CTX_set_client_CA_list(sctx, names);
       }
     }
 
@@ -576,7 +1446,7 @@ if (expcerts != NULL)
       {
       /* 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;
@@ -603,9 +1473,9 @@ if (expcerts != NULL)
 
   /* 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);
+    cert_vfy_cb);
   }
 
 return OK;
@@ -623,11 +1493,6 @@ a TLS session.
 
 Arguments:
   require_ciphers   allowed ciphers
-  ------------------------------------------------------
-  require_mac      list of allowed MACs                 ) Not used
-  require_kx       list of allowed key_exchange methods )   for
-  require_proto    list of allowed protocols            ) OpenSSL
-  ------------------------------------------------------
 
 Returns:            OK on success
                     DEFER for errors before the start of the negotiation
@@ -636,15 +1501,16 @@ Returns:            OK on success
 */
 
 int
-tls_server_start(uschar *require_ciphers, uschar *require_mac,
-  uschar *require_kx, uschar *require_proto)
+tls_server_start(const uschar *require_ciphers)
 {
 int rc;
 uschar *expciphers;
+tls_ext_ctx_cb *cbinfo;
+static uschar cipherbuf[256];
 
 /* Check for previous activation */
 
-if (tls_active >= 0)
+if (tls_in.active >= 0)
   {
   tls_error(US"STARTTLS received after TLS started", NULL, US"");
   smtp_printf("554 Already in TLS\r\n");
@@ -654,47 +1520,59 @@ if (tls_active >= 0)
 /* Initialize the SSL library. If it fails, it will already have logged
 the error. */
 
-rc = tls_init(NULL, tls_dhparam, tls_certificate, tls_privatekey, NULL);
+rc = tls_init(&server_ctx, NULL, tls_dhparam, tls_certificate, tls_privatekey,
+#ifndef DISABLE_OCSP
+    tls_ocsp_file,
+#endif
+    NULL, &server_static_cbinfo);
 if (rc != OK) return rc;
+cbinfo = server_static_cbinfo;
 
 if (!expand_check(require_ciphers, US"tls_require_ciphers", &expciphers))
   return FAIL;
 
 /* In OpenSSL, cipher components are separated by hyphens. In GnuTLS, they
-are separated by underscores. So that I can use either form in my tests, and
-also for general convenience, we turn underscores into hyphens here. */
+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.
+*/
 
 if (expciphers != NULL)
   {
   uschar *s = expciphers;
   while (*s != 0) { if (*s == '_') *s = '-'; s++; }
   DEBUG(D_tls) debug_printf("required ciphers: %s\n", expciphers);
-  if (!SSL_CTX_set_cipher_list(ctx, CS expciphers))
+  if (!SSL_CTX_set_cipher_list(server_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
 optional, set up appropriately. */
 
-tls_certificate_verified = FALSE;
-verify_callback_called = FALSE;
+tls_in.certificate_verified = FALSE;
+#ifdef EXPERIMENTAL_DANE
+tls_in.dane_verified = FALSE;
+#endif
+server_verify_callback_called = FALSE;
 
 if (verify_check_host(&tls_verify_hosts) == OK)
   {
-  rc = setup_certs(tls_verify_certificates, tls_crl, NULL, FALSE);
+  rc = setup_certs(server_ctx, tls_verify_certificates, tls_crl, NULL,
+                       FALSE, verify_callback_server);
   if (rc != OK) return rc;
-  verify_optional = FALSE;
+  server_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(server_ctx, tls_verify_certificates, tls_crl, NULL,
+                       TRUE, verify_callback_server);
   if (rc != OK) return rc;
-  verify_optional = TRUE;
+  server_verify_optional = TRUE;
   }
 
 /* Prepare for new connection */
 
-if ((ssl = SSL_new(ctx)) == NULL) return tls_error(US"SSL_new", NULL, NULL);
+if ((server_ssl = SSL_new(server_ctx)) == NULL) return tls_error(US"SSL_new", NULL, NULL);
 
 /* Warning: we used to SSL_clear(ssl) here, it was removed.
  *
@@ -715,8 +1593,8 @@ make them disconnect. We need to have an explicit fflush() here, to force out
 the response. Other smtp_printf() calls do not need it, because in non-TLS
 mode, the fflush() happens when smtp_getc() is called. */
 
-SSL_set_session_id_context(ssl, sid_ctx, Ustrlen(sid_ctx));
-if (!tls_on_connect)
+SSL_set_session_id_context(server_ssl, sid_ctx, Ustrlen(sid_ctx));
+if (!tls_in.on_connect)
   {
   smtp_printf("220 TLS go ahead\r\n");
   fflush(smtp_out);
@@ -725,15 +1603,15 @@ if (!tls_on_connect)
 /* Now negotiate the TLS session. We put our own timer on it, since it seems
 that the OpenSSL library doesn't. */
 
-SSL_set_wfd(ssl, fileno(smtp_out));
-SSL_set_rfd(ssl, fileno(smtp_in));
-SSL_set_accept_state(ssl);
+SSL_set_wfd(server_ssl, fileno(smtp_out));
+SSL_set_rfd(server_ssl, fileno(smtp_in));
+SSL_set_accept_state(server_ssl);
 
 DEBUG(D_tls) debug_printf("Calling SSL_accept\n");
 
 sigalrm_seen = FALSE;
 if (smtp_receive_timeout > 0) alarm(smtp_receive_timeout);
-rc = SSL_accept(ssl);
+rc = SSL_accept(server_ssl);
 alarm(0);
 
 if (rc <= 0)
@@ -750,16 +1628,27 @@ DEBUG(D_tls) debug_printf("SSL_accept was successful\n");
 /* TLS has been set up. Adjust the input functions to read via TLS,
 and initialize things. */
 
-construct_cipher_name(ssl);
+construct_cipher_name(server_ssl, cipherbuf, sizeof(cipherbuf), &tls_in.bits);
+tls_in.cipher = cipherbuf;
 
 DEBUG(D_tls)
   {
   uschar buf[2048];
-  if (SSL_get_shared_ciphers(ssl, CS buf, sizeof(buf)) != NULL)
+  if (SSL_get_shared_ciphers(server_ssl, CS buf, sizeof(buf)) != NULL)
     debug_printf("Shared ciphers: %s\n", buf);
   }
 
+/* Record the certificate we presented */
+  {
+  X509 * crt = SSL_get_certificate(server_ssl);
+  tls_in.ourcert = crt ? X509_dup(crt) : NULL;
+  }
 
+/* Only used by the server-side tls (tls_in), including tls_getc.
+   Client-side (tls_out) reads (seem to?) go via
+   smtp_read_response()/ip_recv().
+   Hence no need to duplicate for _in and _out.
+ */
 ssl_xfer_buffer = store_malloc(ssl_xfer_buffer_size);
 ssl_xfer_buffer_lwm = ssl_xfer_buffer_hwm = 0;
 ssl_xfer_eof = ssl_xfer_error = 0;
@@ -770,13 +1659,117 @@ receive_feof = tls_feof;
 receive_ferror = tls_ferror;
 receive_smtp_buffered = tls_smtp_buffered;
 
-tls_active = fileno(smtp_out);
+tls_in.active = fileno(smtp_out);
 return OK;
 }
 
 
 
 
+static int
+tls_client_basic_ctx_init(SSL_CTX * ctx,
+    host_item * host, smtp_transport_options_block * ob
+#ifdef EXPERIMENTAL_CERTNAMES
+    , tls_ext_ctx_cb * cbinfo
+#endif
+                         )
+{
+int rc;
+/* stick to the old behaviour for compatibility if tls_verify_certificates is 
+   set but both tls_verify_hosts and tls_try_verify_hosts is not set. Check only
+   the specified host patterns if one of them is defined */
+
+if ((!ob->tls_verify_hosts && !ob->tls_try_verify_hosts) ||
+    (verify_check_host(&ob->tls_verify_hosts) == OK))
+  {
+  if ((rc = setup_certs(ctx, ob->tls_verify_certificates,
+       ob->tls_crl, host, FALSE, verify_callback_client)) != OK)
+    return rc;
+  client_verify_optional = FALSE;
+
+#ifdef EXPERIMENTAL_CERTNAMES
+  if (ob->tls_verify_cert_hostnames)
+    {
+    if (!expand_check(ob->tls_verify_cert_hostnames,
+                     US"tls_verify_cert_hostnames",
+                     &cbinfo->verify_cert_hostnames))
+      return FAIL;
+    if (cbinfo->verify_cert_hostnames)
+      DEBUG(D_tls) debug_printf("Cert hostname to check: \"%s\"\n",
+                     cbinfo->verify_cert_hostnames);
+    }
+#endif
+  }
+else if (verify_check_host(&ob->tls_try_verify_hosts) == OK)
+  {
+  if ((rc = setup_certs(ctx, ob->tls_verify_certificates,
+       ob->tls_crl, host, TRUE, verify_callback_client)) != OK)
+    return rc;
+  client_verify_optional = TRUE;
+  }
+
+return OK;
+}
+
+
+#ifdef EXPERIMENTAL_DANE
+static int
+dane_tlsa_load(SSL * ssl, host_item * host, dns_answer * dnsa)
+{
+dns_record * rr;
+dns_scan dnss;
+const char * hostnames[2] = { CS host->name, NULL };
+int found = 0;
+
+if (DANESSL_init(ssl, NULL, hostnames) != 1)
+  return tls_error(US"hostnames load", host, NULL);
+
+for (rr = dns_next_rr(dnsa, &dnss, RESET_ANSWERS);
+     rr;
+     rr = dns_next_rr(dnsa, &dnss, RESET_NEXT)
+    ) if (rr->type == T_TLSA)
+  {
+  uschar * p = rr->data;
+  uint8_t usage, selector, mtype;
+  const char * mdname;
+
+  usage = *p++;
+
+  /* Only DANE-TA(2) and DANE-EE(3) are supported */
+  if (usage != 2 && usage != 3) continue;
+
+  selector = *p++;
+  mtype = *p++;
+
+  switch (mtype)
+    {
+    default: continue; /* Only match-types 0, 1, 2 are supported */
+    case 0:  mdname = NULL; break;
+    case 1:  mdname = "sha256"; break;
+    case 2:  mdname = "sha512"; break;
+    }
+
+  found++;
+  switch (DANESSL_add_tlsa(ssl, usage, selector, mdname, p, rr->size - 3))
+    {
+    default:
+    case 0:    /* action not taken */
+      return tls_error(US"tlsa load", host, NULL);
+    case 1:    break;
+    }
+
+  tls_out.tlsa_usage |= 1<<usage;
+  }
+
+if (found)
+  return OK;
+
+log_write(0, LOG_MAIN, "DANE error: No usable TLSA records");
+return FAIL;
+}
+#endif /*EXPERIMENTAL_DANE*/
+
+
 
 /*************************************************
 *    Start a TLS session in a client             *
@@ -788,18 +1781,8 @@ Argument:
   fd               the fd of the connection
   host             connected host (for messages)
   addr             the first address
-  dhparam          DH parameter file
-  certificate      certificate file
-  privatekey       private key file
-  verify_certs     file for certificate verify
-  crl              file containing CRL
-  require_ciphers  list of allowed ciphers
-  ------------------------------------------------------
-  require_mac      list of allowed MACs                 ) Not used
-  require_kx       list of allowed key_exchange methods )   for
-  require_proto    list of allowed protocols            ) OpenSSL
-  ------------------------------------------------------
-  timeout          startup timeout
+  tb               transport (always smtp)
+  tlsa_dnsa        tlsa lookup, if DANE, else null
 
 Returns:           OK on success
                    FAIL otherwise - note that tls_error() will not give DEFER
@@ -807,23 +1790,71 @@ Returns:           OK on success
 */
 
 int
-tls_client_start(int fd, host_item *host, address_item *addr, uschar *dhparam,
-  uschar *certificate, uschar *privatekey, uschar *verify_certs, uschar *crl,
-  uschar *require_ciphers, uschar *require_mac, uschar *require_kx,
-  uschar *require_proto, int timeout)
+tls_client_start(int fd, host_item *host, address_item *addr,
+  transport_instance *tb
+#ifdef EXPERIMENTAL_DANE
+  , dns_answer * tlsa_dnsa
+#endif
+  )
 {
+smtp_transport_options_block * ob =
+  (smtp_transport_options_block *)tb->options_block;
 static uschar txt[256];
-uschar *expciphers;
-X509* server_cert;
+uschar * expciphers;
+X509 * server_cert;
 int rc;
+static uschar cipherbuf[256];
+
+#ifndef DISABLE_OCSP
+BOOL request_ocsp = FALSE;
+BOOL require_ocsp = FALSE;
+#endif
+
+#ifdef EXPERIMENTAL_DANE
+tls_out.tlsa_usage = 0;
+#endif
+
+#ifndef DISABLE_OCSP
+  {
+# ifdef EXPERIMENTAL_DANE
+  if (  tlsa_dnsa
+     && ob->hosts_request_ocsp[0] == '*'
+     && ob->hosts_request_ocsp[1] == '\0'
+     )
+    {
+    /* Unchanged from default.  Use a safer one under DANE */
+    request_ocsp = TRUE;
+    ob->hosts_request_ocsp = US"${if or { {= {0}{$tls_out_tlsa_usage}} "
+                                     "   {= {4}{$tls_out_tlsa_usage}} } "
+                                " {*}{}}";
+    }
+# endif
+
+  if ((require_ocsp = verify_check_this_host(&ob->hosts_require_ocsp,
+    NULL, host->name, host->address, NULL) == OK))
+    request_ocsp = TRUE;
+  else
+# ifdef EXPERIMENTAL_DANE
+    if (!request_ocsp)
+# endif
+      request_ocsp = verify_check_this_host(&ob->hosts_request_ocsp,
+         NULL, host->name, host->address, NULL) == OK;
+  }
+#endif
 
-rc = tls_init(host, dhparam, certificate, privatekey, addr);
+rc = tls_init(&client_ctx, host, NULL,
+    ob->tls_certificate, ob->tls_privatekey,
+#ifndef DISABLE_OCSP
+    (void *)(long)request_ocsp,
+#endif
+    addr, &client_static_cbinfo);
 if (rc != OK) return rc;
 
-tls_certificate_verified = FALSE;
-verify_callback_called = FALSE;
+tls_out.certificate_verified = FALSE;
+client_verify_callback_called = FALSE;
 
-if (!expand_check(require_ciphers, US"tls_require_ciphers", &expciphers))
+if (!expand_check(ob->tls_require_ciphers, US"tls_require_ciphers",
+    &expciphers))
   return FAIL;
 
 /* In OpenSSL, cipher components are separated by hyphens. In GnuTLS, they
@@ -835,45 +1866,140 @@ if (expciphers != NULL)
   uschar *s = expciphers;
   while (*s != 0) { if (*s == '_') *s = '-'; s++; }
   DEBUG(D_tls) debug_printf("required ciphers: %s\n", expciphers);
-  if (!SSL_CTX_set_cipher_list(ctx, CS expciphers))
+  if (!SSL_CTX_set_cipher_list(client_ctx, CS expciphers))
     return tls_error(US"SSL_CTX_set_cipher_list", host, NULL);
   }
 
-rc = setup_certs(verify_certs, crl, host, FALSE);
-if (rc != OK) return rc;
+#ifdef EXPERIMENTAL_DANE
+if (tlsa_dnsa)
+  {
+  SSL_CTX_set_verify(client_ctx, SSL_VERIFY_PEER, verify_callback_client_dane);
+
+  if (!DANESSL_library_init())
+    return tls_error(US"library init", host, NULL);
+  if (DANESSL_CTX_init(client_ctx) <= 0)
+    return tls_error(US"context init", host, NULL);
+  }
+else
+
+#endif
+
+  if ((rc = tls_client_basic_ctx_init(client_ctx, host, ob
+#ifdef EXPERIMENTAL_CERTNAMES
+                               , client_static_cbinfo
+#endif
+                               )) != OK)
+    return rc;
+
+if ((client_ssl = SSL_new(client_ctx)) == NULL)
+  return tls_error(US"SSL_new", host, NULL);
+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 (ob->tls_sni)
+  {
+  if (!expand_check(ob->tls_sni, US"tls_sni", &tls_out.sni))
+    return FAIL;
+  if (tls_out.sni == NULL)
+    {
+    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
+    {
+#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);
+#else
+    DEBUG(D_tls)
+      debug_printf("OpenSSL at build-time lacked SNI support, ignoring \"%s\"\n",
+          tls_out.sni);
+#endif
+    }
+  }
+
+#ifdef EXPERIMENTAL_DANE
+if (tlsa_dnsa)
+  if ((rc = dane_tlsa_load(client_ssl, host, tlsa_dnsa)) != OK)
+    return rc;
+#endif
+
+#ifndef DISABLE_OCSP
+/* Request certificate status at connection-time.  If the server
+does OCSP stapling we will get the callback (set in tls_init()) */
+# ifdef EXPERIMENTAL_DANE
+if (request_ocsp)
+  {
+  const uschar * s;
+  if (  ((s = ob->hosts_require_ocsp) && Ustrstr(s, US"tls_out_tlsa_usage"))
+     || ((s = ob->hosts_request_ocsp) && Ustrstr(s, US"tls_out_tlsa_usage"))
+     )
+    {  /* 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_this_host(&ob->hosts_require_ocsp,
+      NULL, host->name, host->address, NULL) == OK;
+    request_ocsp = require_ocsp ? TRUE
+      : verify_check_this_host(&ob->hosts_request_ocsp,
+         NULL, host->name, host->address, NULL) == OK;
+    }
+  }
+# endif
+
+if (request_ocsp)
+  {
+  SSL_set_tlsext_status_type(client_ssl, TLSEXT_STATUSTYPE_ocsp);
+  client_static_cbinfo->u_ocsp.client.verify_required = require_ocsp;
+  tls_out.ocsp = OCSP_NOT_RESP;
+  }
+#endif
 
-if ((ssl = SSL_new(ctx)) == NULL) return tls_error(US"SSL_new", host, NULL);
-SSL_set_session_id_context(ssl, sid_ctx, Ustrlen(sid_ctx));
-SSL_set_fd(ssl, fd);
-SSL_set_connect_state(ssl);
+#ifdef EXPERIMENTAL_EVENT
+client_static_cbinfo->event_action = tb->event_action;
+#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(timeout);
-rc = SSL_connect(ssl);
+alarm(ob->command_timeout);
+rc = SSL_connect(client_ssl);
 alarm(0);
 
+#ifdef EXPERIMENTAL_DANE
+if (tlsa_dnsa)
+  DANESSL_cleanup(client_ssl);
+#endif
+
 if (rc <= 0)
   return tls_error(US"SSL_connect", host, sigalrm_seen ? US"timed out" : NULL);
 
 DEBUG(D_tls) debug_printf("SSL_connect succeeded\n");
 
 /* Beware anonymous ciphers which lead to server_cert being NULL */
-server_cert = SSL_get_peer_certificate (ssl);
+/*XXX server_cert is never freed... use X509_free() */
+server_cert = SSL_get_peer_certificate (client_ssl);
 if (server_cert)
   {
-  tls_peerdn = US X509_NAME_oneline(X509_get_subject_name(server_cert),
+  tls_out.peerdn = US X509_NAME_oneline(X509_get_subject_name(server_cert),
     CS txt, sizeof(txt));
-  tls_peerdn = txt;
+  tls_out.peerdn = txt;                /*XXX a static buffer... */
   }
 else
-  tls_peerdn = NULL;
+  tls_out.peerdn = NULL;
+
+construct_cipher_name(client_ssl, cipherbuf, sizeof(cipherbuf), &tls_out.bits);
+tls_out.cipher = cipherbuf;
 
-construct_cipher_name(ssl);   /* Sets tls_cipher */
+/* Record the certificate we presented */
+  {
+  X509 * crt = SSL_get_certificate(client_ssl);
+  tls_out.ourcert = crt ? X509_dup(crt) : NULL;
+  }
 
-tls_active = fd;
+tls_out.active = fd;
 return OK;
 }
 
@@ -890,6 +2016,8 @@ it refills the buffer via the SSL reading function.
 
 Arguments:  none
 Returns:    the next character or EOF
+
+Only used by the server-side TLS.
 */
 
 int
@@ -900,12 +2028,12 @@ if (ssl_xfer_buffer_lwm >= ssl_xfer_buffer_hwm)
   int error;
   int inbytes;
 
-  DEBUG(D_tls) debug_printf("Calling SSL_read(%p, %p, %u)\n", ssl,
+  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);
-  inbytes = SSL_read(ssl, CS ssl_xfer_buffer, ssl_xfer_buffer_size);
-  error = SSL_get_error(ssl, inbytes);
+  inbytes = SSL_read(server_ssl, CS ssl_xfer_buffer, ssl_xfer_buffer_size);
+  error = SSL_get_error(server_ssl, inbytes);
   alarm(0);
 
   /* SSL_ERROR_ZERO_RETURN appears to mean that the SSL session has been
@@ -922,11 +2050,13 @@ if (ssl_xfer_buffer_lwm >= ssl_xfer_buffer_hwm)
     receive_ferror = smtp_ferror;
     receive_smtp_buffered = smtp_buffered;
 
-    SSL_free(ssl);
-    ssl = NULL;
-    tls_active = -1;
-    tls_cipher = NULL;
-    tls_peerdn = NULL;
+    SSL_free(server_ssl);
+    server_ssl = NULL;
+    tls_in.active = -1;
+    tls_in.bits = 0;
+    tls_in.cipher = NULL;
+    tls_in.peerdn = NULL;
+    tls_in.sni = NULL;
 
     return smtp_getc();
     }
@@ -973,11 +2103,14 @@ Arguments:
 
 Returns:    the number of bytes read
             -1 after a failed read
+
+Only used by the client-side TLS.
 */
 
 int
-tls_read(uschar *buff, size_t len)
+tls_read(BOOL is_server, uschar *buff, size_t len)
 {
+SSL *ssl = is_server ? server_ssl : client_ssl;
 int inbytes;
 int error;
 
@@ -1010,19 +2143,23 @@ return inbytes;
 
 /*
 Arguments:
+  is_server channel specifier
   buff      buffer of data
   len       number of bytes
 
 Returns:    the number of bytes after a successful write,
             -1 after a failed write
+
+Used by both server-side and client-side TLS.
 */
 
 int
-tls_write(const uschar *buff, size_t len)
+tls_write(BOOL is_server, const uschar *buff, size_t len)
 {
 int outbytes;
 int error;
 int left = len;
+SSL *ssl = is_server ? server_ssl : client_ssl;
 
 DEBUG(D_tls) debug_printf("tls_do_write(%p, %d)\n", buff, left);
 while (left > 0)
@@ -1047,6 +2184,11 @@ while (left > 0)
     log_write(0, LOG_MAIN, "SSL channel closed on write");
     return -1;
 
+    case SSL_ERROR_SYSCALL:
+    log_write(0, LOG_MAIN, "SSL_write: (from %s) syscall: %s",
+      sender_fullhost ? sender_fullhost : US"<unknown>",
+      strerror(errno));
+
     default:
     log_write(0, LOG_MAIN, "SSL_write error %d", error);
     return -1;
@@ -1067,23 +2209,94 @@ would tamper with the SSL session in the parent process).
 
 Arguments:   TRUE if SSL_shutdown is to be called
 Returns:     nothing
+
+Used by both server-side and client-side TLS.
 */
 
 void
-tls_close(BOOL shutdown)
+tls_close(BOOL is_server, BOOL shutdown)
 {
-if (tls_active < 0) return;  /* TLS was not active */
+SSL **sslp = is_server ? &server_ssl : &client_ssl;
+int *fdp = is_server ? &tls_in.active : &tls_out.active;
+
+if (*fdp < 0) return;  /* TLS was not active */
 
 if (shutdown)
   {
   DEBUG(D_tls) debug_printf("tls_close(): shutting down SSL\n");
-  SSL_shutdown(ssl);
+  SSL_shutdown(*sslp);
   }
 
-SSL_free(ssl);
-ssl = NULL;
+SSL_free(*sslp);
+*sslp = NULL;
 
-tls_active = -1;
+*fdp = -1;
+}
+
+
+
+
+/*************************************************
+*  Let tls_require_ciphers be checked at startup *
+*************************************************/
+
+/* The tls_require_ciphers option, if set, must be something which the
+library can parse.
+
+Returns:     NULL on success, or error message
+*/
+
+uschar *
+tls_validate_require_cipher(void)
+{
+SSL_CTX *ctx;
+uschar *s, *expciphers, *err;
+
+/* this duplicates from tls_init(), we need a better "init just global
+state, for no specific purpose" singleton function of our own */
+
+SSL_load_error_strings();
+OpenSSL_add_ssl_algorithms();
+#if (OPENSSL_VERSION_NUMBER >= 0x0090800fL) && !defined(OPENSSL_NO_SHA256)
+/* SHA256 is becoming ever more popular. This makes sure it gets added to the
+list of available digests. */
+EVP_add_digest(EVP_sha256());
+#endif
+
+if (!(tls_require_ciphers && *tls_require_ciphers))
+  return NULL;
+
+if (!expand_check(tls_require_ciphers, US"tls_require_ciphers", &expciphers))
+  return US"failed to expand tls_require_ciphers";
+
+if (!(expciphers && *expciphers))
+  return NULL;
+
+/* normalisation ripped from above */
+s = expciphers;
+while (*s != 0) { if (*s == '_') *s = '-'; s++; }
+
+err = NULL;
+
+ctx = SSL_CTX_new(SSLv23_server_method());
+if (!ctx)
+  {
+  ERR_error_string(ERR_get_error(), ssl_errstring);
+  return string_sprintf("SSL_CTX_new() failed: %s", ssl_errstring);
+  }
+
+DEBUG(D_tls)
+  debug_printf("tls_require_ciphers expands to \"%s\"\n", expciphers);
+
+if (!SSL_CTX_set_cipher_list(ctx, CS expciphers))
+  {
+  ERR_error_string(ERR_get_error(), ssl_errstring);
+  err = string_sprintf("SSL_CTX_set_cipher_list(%s) failed", expciphers);
+  }
+
+SSL_CTX_free(ctx);
+
+return err;
 }
 
 
@@ -1099,6 +2312,11 @@ one version of OpenSSL but the run-time linker picks up another version,
 it can result in serious failures, including crashing with a SIGSEGV.  So
 report the version found by the compiler and the run-time version.
 
+Note: some OS vendors backport security fixes without changing the version
+number/string, and the version date remains unchanged.  The _build_ date
+will change, so we can more usefully assist with version diagnosis by also
+reporting the build date.
+
 Arguments:   a FILE* to print the results to
 Returns:     nothing
 */
@@ -1107,16 +2325,20 @@ void
 tls_version_report(FILE *f)
 {
 fprintf(f, "Library version: OpenSSL: Compile: %s\n"
-           "                          Runtime: %s\n",
+           "                          Runtime: %s\n"
+           "                                 : %s\n",
            OPENSSL_VERSION_TEXT,
-           SSLeay_version(SSLEAY_VERSION));
+           SSLeay_version(SSLEAY_VERSION),
+           SSLeay_version(SSLEAY_BUILT_ON));
+/* third line is 38 characters for the %s and the line is 73 chars long;
+the OpenSSL output includes a "built on: " prefix already. */
 }
 
 
 
 
 /*************************************************
-*        Pseudo-random number generation         *
+*            Random number generation            *
 *************************************************/
 
 /* Pseudo-random number generation.  The result is not expected to be
@@ -1131,16 +2353,30 @@ Returns     a random number in range [0, max-1]
 */
 
 int
-pseudo_random_number(int max)
+vaguely_random_number(int max)
 {
 unsigned int r;
 int i, needed_len;
+static pid_t pidlast = 0;
+pid_t pidnow;
 uschar *p;
 uschar smallbuf[sizeof(r)];
 
 if (max <= 1)
   return 0;
 
+pidnow = getpid();
+if (pidnow != pidlast)
+  {
+  /* Although OpenSSL documents that "OpenSSL makes sure that the PRNG state
+  is unique for each thread", this doesn't apparently apply across processes,
+  so our own warning from vaguely_random_number_fallback() applies here too.
+  Fix per PostgreSQL. */
+  if (pidlast != 0)
+    RAND_cleanup();
+  pidlast = pidnow;
+  }
+
 /* OpenSSL auto-seeds from /dev/random, etc, but this a double-check. */
 if (!RAND_status())
   {
@@ -1167,7 +2403,14 @@ if (i < needed_len)
   needed_len = i;
 
 /* We do not care if crypto-strong */
-(void) RAND_pseudo_bytes(smallbuf, needed_len);
+i = RAND_pseudo_bytes(smallbuf, needed_len);
+if (i < 0)
+  {
+  DEBUG(D_all)
+    debug_printf("OpenSSL RAND_pseudo_bytes() not supported by RAND method, using fallback.\n");
+  return vaguely_random_number_fallback(max);
+  }
+
 r = 0;
 for (p = smallbuf; needed_len; --needed_len, ++p)
   {
@@ -1205,7 +2448,9 @@ all options unless explicitly for DTLS, let the administrator choose which
 to apply.
 
 This list is current as of:
-  ==>  1.0.1b  <==  */
+  ==>  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
@@ -1270,6 +2515,9 @@ static struct exim_openssl_option exim_openssl_options[] = {
 #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
@@ -1344,8 +2592,11 @@ uschar keep_c;
 BOOL adding, item_parsed;
 
 result = 0L;
-/* Prior to 4.78 we or'd in SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS; removed
+/* Prior to 4.80 we or'd in SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS; removed
  * from default because it increases BEAST susceptibility. */
+#ifdef SSL_OP_NO_SSLv2
+result |= SSL_OP_NO_SSLv2;
+#endif
 
 if (option_spec == NULL)
   {
@@ -1388,4 +2639,6 @@ for (s=option_spec; *s != '\0'; /**/)
 return TRUE;
 }
 
+/* vi: aw ai sw=2
+*/
 /* End of tls-openssl.c */