Merge branch 'CHUNKING'
[exim.git] / src / src / tls-openssl.c
index 96ac72c3c16cabdea5a1b4b5764835164a89b222..242394f95ccc72be30c404888ce7106e64d2f1f2 100644 (file)
@@ -2,7 +2,7 @@
 *     Exim - an Internet mail transport agent    *
 *************************************************/
 
-/* Copyright (c) University of Cambridge 1995 - 2014 */
+/* Copyright (c) University of Cambridge 1995 - 2016 */
 /* See the file NOTICE for conditions of use and distribution. */
 
 /* Portions Copyright (c) The OpenSSL Project 1999 */
@@ -22,6 +22,9 @@ functions from the OpenSSL library. */
 #include <openssl/ssl.h>
 #include <openssl/err.h>
 #include <openssl/rand.h>
+#ifndef OPENSSL_NO_ECDH
+# include <openssl/ec.h>
+#endif
 #ifndef DISABLE_OCSP
 # include <openssl/ocsp.h>
 #endif
@@ -38,12 +41,54 @@ functions from the OpenSSL library. */
 #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
+#if OPENSSL_VERSION_NUMBER >= 0x00908000L
+# define EXIM_HAVE_RSA_GENKEY_EX
+#endif
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L
+# define EXIM_HAVE_OCSP_RESP_COUNT
+#else
+# define EXIM_HAVE_EPHEM_RSA_KEX
+# define EXIM_HAVE_RAND_PSEUDO
+#endif
+#if (OPENSSL_VERSION_NUMBER >= 0x0090800fL) && !defined(OPENSSL_NO_SHA256)
+# define EXIM_HAVE_SHA256
 #endif
-#if OPENSSL_VERSION_NUMBER >= 0x010000000L \
+
+/*
+ * 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
+# endif
+# if OPENSSL_VERSION_NUMBER >= 0x010000000L \
     && (OPENSSL_VERSION_NUMBER & 0x0000ff000L) >= 0x000002000L
-# define EXIM_HAVE_OPENSSL_CHECKHOST
+#  define EXIM_HAVE_OPENSSL_CHECKHOST
+# endif
+#endif
+
+#if !defined(LIBRESSL_VERSION_NUMBER) \
+    || LIBRESSL_VERSION_NUMBER >= 0x20010000L
+# if !defined(OPENSSL_NO_ECDH)
+#  if OPENSSL_VERSION_NUMBER >= 0x0090800fL
+#   define EXIM_HAVE_ECDH
+#  endif
+#  if OPENSSL_VERSION_NUMBER >= 0x10002000L
+#   if OPENSSL_VERSION_NUMBER < 0x10100000L
+#    define EXIM_HAVE_OPENSSL_ECDH_AUTO
+#   endif
+#   define EXIM_HAVE_OPENSSL_EC_NIST2NID
+#  endif
+# endif
 #endif
 
 #if !defined(EXIM_HAVE_OPENSSL_TLSEXT) && !defined(DISABLE_OCSP)
@@ -124,7 +169,7 @@ typedef struct tls_ext_ctx_cb {
   /* only passed down to tls_error: */
   host_item *host;
   const uschar * verify_cert_hostnames;
-#ifdef EXPERIMENTAL_EVENT
+#ifndef DISABLE_EVENT
   uschar * event_action;
 #endif
 } tls_ext_ctx_cb;
@@ -169,7 +214,7 @@ Returns:    OK/DEFER/FAIL
 */
 
 static int
-tls_error(uschar * prefix, const host_item * host, uschar *  msg)
+tls_error(uschar * prefix, const host_item * host, uschar * msg)
 {
 if (!msg)
   {
@@ -197,6 +242,7 @@ else
 
 
 
+#ifdef EXIM_HAVE_EPHEM_RSA_KEX
 /*************************************************
 *        Callback to generate RSA key            *
 *************************************************/
@@ -214,10 +260,22 @@ static RSA *
 rsa_callback(SSL *s, int export, int keylength)
 {
 RSA *rsa_key;
+#ifdef EXIM_HAVE_RSA_GENKEY_EX
+BIGNUM *bn = BN_new();
+#endif
+
 export = export;     /* Shut picky compilers up */
 DEBUG(D_tls) debug_printf("Generating %d bit RSA key...\n", keylength);
-rsa_key = RSA_generate_key(keylength, RSA_F4, NULL, NULL);
-if (rsa_key == NULL)
+
+#ifdef EXIM_HAVE_RSA_GENKEY_EX
+if (  !BN_set_word(bn, (unsigned long)RSA_F4)
+   || !(rsa_key = RSA_new())
+   || !RSA_generate_key_ex(rsa_key, keylength, bn, NULL)
+   )
+#else
+if (!(rsa_key = RSA_generate_key(keylength, RSA_F4, NULL, NULL)))
+#endif
+
   {
   ERR_error_string(ERR_get_error(), ssl_errstring);
   log_write(0, LOG_MAIN|LOG_PANIC, "TLS error (RSA_generate_key): %s",
@@ -226,6 +284,7 @@ if (rsa_key == NULL)
   }
 return rsa_key;
 }
+#endif
 
 
 
@@ -254,7 +313,7 @@ for(i= 0; i<sk_X509_OBJECT_num(roots); i++)
 */
 
 
-#ifdef EXPERIMENTAL_EVENT
+#ifndef DISABLE_EVENT
 static int
 verify_event(tls_support * tlsp, X509 * cert, int depth, const uschar * dn,
   BOOL *calledp, const BOOL *optionalp, const uschar * what)
@@ -266,6 +325,7 @@ X509 * old_cert;
 ev = tlsp == &tls_out ? client_static_cbinfo->event_action : event_action;
 if (ev)
   {
+  DEBUG(D_tls) debug_printf("verify_event: %s %d\n", what, depth);
   old_cert = tlsp->peercert;
   tlsp->peercert = X509_dup(cert);
   /* NB we do not bother setting peerdn */
@@ -316,15 +376,17 @@ 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
+  preverify_ok current yes/no state as 1/0
+  x509ctx      certificate information.
+  tlsp         per-direction (client vs. server) support data
+  calledp      has-been-called flag
+  optionalp    verification-is-optional flag
 
-Returns:     1 if verified, 0 if not
+Returns:     0 if verification should fail, otherwise 1
 */
 
 static int
-verify_callback(int state, X509_STORE_CTX *x509ctx,
+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);
@@ -334,7 +396,7 @@ uschar dn[256];
 X509_NAME_oneline(X509_get_subject_name(cert), CS dn, sizeof(dn));
 dn[sizeof(dn)-1] = '\0';
 
-if (state == 0)
+if (preverify_ok == 0)
   {
   log_write(0, LOG_MAIN, "[%s] SSL verify error: depth=%d error=%s cert=%s",
        tlsp == &tls_out ? deliver_host_address : sender_host_address,
@@ -360,13 +422,13 @@ else if (depth != 0)
     {  /* 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
+#ifndef DISABLE_EVENT
     if (verify_event(tlsp, cert, depth, dn, calledp, optionalp, US"SSL"))
       return 0;                                /* reject, with peercert set */
 #endif
@@ -380,7 +442,7 @@ else
        /* client, wanting hostname check */
     {
 
-#if EXIM_HAVE_OPENSSL_CHECKHOST
+#ifdef EXIM_HAVE_OPENSSL_CHECKHOST
 # ifndef X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS
 #  define X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS 0
 # endif
@@ -392,9 +454,10 @@ else
     uschar * name;
     int rc;
     while ((name = string_nextinlist(&list, &sep, NULL, 0)))
-      if ((rc = X509_check_host(cert, name, 0,
+      if ((rc = X509_check_host(cert, CCS name, 0,
                  X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS
-                 | X509_CHECK_FLAG_SINGLE_LABEL_SUBDOMAINS)))
+                 | X509_CHECK_FLAG_SINGLE_LABEL_SUBDOMAINS,
+                 NULL)))
        {
        if (rc < 0)
          {
@@ -425,7 +488,7 @@ else
       }
     }
 
-#ifdef EXPERIMENTAL_EVENT
+#ifndef DISABLE_EVENT
   if (verify_event(tlsp, cert, depth, dn, calledp, optionalp, US"SSL"))
     return 0;                          /* reject, with peercert set */
 #endif
@@ -440,15 +503,17 @@ return 1;   /* accept, at least for this level */
 }
 
 static int
-verify_callback_client(int state, X509_STORE_CTX *x509ctx)
+verify_callback_client(int preverify_ok, X509_STORE_CTX *x509ctx)
 {
-return verify_callback(state, x509ctx, &tls_out, &client_verify_callback_called, &client_verify_optional);
+return verify_callback(preverify_ok, x509ctx, &tls_out,
+  &client_verify_callback_called, &client_verify_optional);
 }
 
 static int
-verify_callback_server(int state, X509_STORE_CTX *x509ctx)
+verify_callback_server(int preverify_ok, X509_STORE_CTX *x509ctx)
 {
-return verify_callback(state, x509ctx, &tls_in, &server_verify_callback_called, &server_verify_optional);
+return verify_callback(preverify_ok, x509ctx, &tls_in,
+  &server_verify_callback_called, &server_verify_optional);
 }
 
 
@@ -458,31 +523,39 @@ return verify_callback(state, x509ctx, &tls_in, &server_verify_callback_called,
 itself.
 */
 static int
-verify_callback_client_dane(int state, X509_STORE_CTX * x509ctx)
+verify_callback_client_dane(int preverify_ok, X509_STORE_CTX * x509ctx)
 {
 X509 * cert = X509_STORE_CTX_get_current_cert(x509ctx);
 uschar dn[256];
-#ifdef EXPERIMENTAL_EVENT
+#ifndef DISABLE_EVENT
 int depth = X509_STORE_CTX_get_error_depth(x509ctx);
-uschar * yield;
 BOOL dummy_called, optional = FALSE;
 #endif
 
 X509_NAME_oneline(X509_get_subject_name(cert), CS dn, sizeof(dn));
 dn[sizeof(dn)-1] = '\0';
 
-DEBUG(D_tls) debug_printf("verify_callback_client_dane: %s\n", dn);
+DEBUG(D_tls) debug_printf("verify_callback_client_dane: %s depth %d %s\n",
+  preverify_ok ? "ok":"BAD", depth, dn);
 
-#ifdef EXPERIMENTAL_EVENT
+#ifndef DISABLE_EVENT
   if (verify_event(&tls_out, cert, depth, dn,
          &dummy_called, &optional, US"DANE"))
     return 0;                          /* reject, with peercert set */
 #endif
 
-if (state == 1)
+if (preverify_ok == 1)
   tls_out.dane_verified =
   tls_out.certificate_verified = TRUE;
-return 1;
+else
+  {
+  int err = X509_STORE_CTX_get_error(x509ctx);
+  DEBUG(D_tls)
+    debug_printf(" - err %d '%s'\n", err, X509_verify_cert_error_string(err));
+  if (err == X509_V_ERR_APPLICATION_VERIFICATION)
+    preverify_ok = 1;
+  }
+return preverify_ok;
 }
 
 #endif /*EXPERIMENTAL_DANE*/
@@ -521,6 +594,7 @@ 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:
+  sctx      The current SSL CTX (inbound or outbound)
   dhparam   DH parameter file or fixed parameter identity string
   host      connected host, if client; NULL if server
 
@@ -600,6 +674,107 @@ return TRUE;
 
 
 
+/*************************************************
+*               Initialize for ECDH              *
+*************************************************/
+
+/* Load parameters for ECDH encryption.
+
+For now, we stick to NIST P-256 because: it's simple and easy to configure;
+it avoids any patent issues that might bite redistributors; despite events in
+the news and concerns over curve choices, we're not cryptographers, we're not
+pretending to be, and this is "good enough" to be better than no support,
+protecting against most adversaries.  Given another year or two, there might
+be sufficient clarity about a "right" way forward to let us make an informed
+decision, instead of a knee-jerk reaction.
+
+Longer-term, we should look at supporting both various named curves and
+external files generated with "openssl ecparam", much as we do for init_dh().
+We should also support "none" as a value, to explicitly avoid initialisation.
+
+Patches welcome.
+
+Arguments:
+  sctx      The current SSL CTX (inbound or outbound)
+  host      connected host, if client; NULL if server
+
+Returns:    TRUE if OK (nothing to set up, or setup worked)
+*/
+
+static BOOL
+init_ecdh(SSL_CTX * sctx, host_item * host)
+{
+#ifdef OPENSSL_NO_ECDH
+return TRUE;
+#else
+
+EC_KEY * ecdh;
+uschar * exp_curve;
+int nid;
+BOOL rv;
+
+if (host)      /* No ECDH setup for clients, only for servers */
+  return TRUE;
+
+# ifndef EXIM_HAVE_ECDH
+DEBUG(D_tls)
+  debug_printf("No OpenSSL API to define ECDH parameters, skipping\n");
+return TRUE;
+# else
+
+if (!expand_check(tls_eccurve, US"tls_eccurve", &exp_curve))
+  return FALSE;
+if (!exp_curve || !*exp_curve)
+  return TRUE;
+
+#  ifdef EXIM_HAVE_OPENSSL_ECDH_AUTO
+/* check if new enough library to support auto ECDH temp key parameter selection */
+if (Ustrcmp(exp_curve, "auto") == 0)
+  {
+  DEBUG(D_tls) debug_printf(
+    "ECDH temp key parameter settings: OpenSSL 1.2+ autoselection\n");
+  SSL_CTX_set_ecdh_auto(sctx, 1);
+  return TRUE;
+  }
+#  endif
+
+DEBUG(D_tls) debug_printf("ECDH: curve '%s'\n", exp_curve);
+if (  (nid = OBJ_sn2nid       (CCS exp_curve)) == NID_undef
+#   ifdef EXIM_HAVE_OPENSSL_EC_NIST2NID
+   && (nid = EC_curve_nist2nid(CCS exp_curve)) == NID_undef
+#   endif
+   )
+  {
+  tls_error(string_sprintf("Unknown curve name tls_eccurve '%s'",
+      exp_curve),
+    host, NULL);
+  return FALSE;
+  }
+
+if (!(ecdh = EC_KEY_new_by_curve_name(nid)))
+  {
+  tls_error(US"Unable to create ec curve", host, NULL);
+  return FALSE;
+  }
+
+/* The "tmp" in the name here refers to setting a temporary key
+not to the stability of the interface. */
+
+if ((rv = SSL_CTX_set_tmp_ecdh(sctx, ecdh) == 0))
+  tls_error(string_sprintf("Error enabling '%s' curve", exp_curve), host, NULL);
+else
+  DEBUG(D_tls) debug_printf("ECDH: enabled '%s' curve\n", exp_curve);
+
+EC_KEY_free(ecdh);
+return !rv;
+
+# endif        /*EXIM_HAVE_ECDH*/
+#endif /*OPENSSL_NO_ECDH*/
+}
+
+
+
+
 #ifndef DISABLE_OCSP
 /*************************************************
 *       Load OCSP information into state         *
@@ -725,7 +900,7 @@ bad:
     {
     extern char ** environ;
     uschar ** p;
-    for (p = USS environ; *p != NULL; p++)
+    if (environ) 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");
@@ -739,6 +914,73 @@ return;
 
 
 
+/* Create and install a selfsigned certificate, for use in server mode */
+
+static int
+tls_install_selfsign(SSL_CTX * sctx)
+{
+X509 * x509 = NULL;
+EVP_PKEY * pkey;
+RSA * rsa;
+X509_NAME * name;
+uschar * where;
+
+where = US"allocating pkey";
+if (!(pkey = EVP_PKEY_new()))
+  goto err;
+
+where = US"allocating cert";
+if (!(x509 = X509_new()))
+  goto err;
+
+where = US"generating pkey";
+               /* deprecated, use RSA_generate_key_ex() */
+if (!(rsa = RSA_generate_key(1024, RSA_F4, NULL, NULL)))
+  goto err;
+
+where = US"assiging pkey";
+if (!EVP_PKEY_assign_RSA(pkey, rsa))
+  goto err;
+
+X509_set_version(x509, 2);                             /* N+1 - version 3 */
+ASN1_INTEGER_set(X509_get_serialNumber(x509), 0);
+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);
+
+name = X509_get_subject_name(x509);
+X509_NAME_add_entry_by_txt(name, "C",
+                         MBSTRING_ASC, "UK", -1, -1, 0);
+X509_NAME_add_entry_by_txt(name, "O",
+                         MBSTRING_ASC, "Exim Developers", -1, -1, 0);
+X509_NAME_add_entry_by_txt(name, "CN",
+                         MBSTRING_ASC, CS smtp_active_hostname, -1, -1, 0);
+X509_set_issuer_name(x509, name);
+
+where = US"signing cert";
+if (!X509_sign(x509, pkey, EVP_md5()))
+  goto err;
+
+where = US"installing selfsign cert";
+if (!SSL_CTX_use_certificate(sctx, x509))
+  goto err;
+
+where = US"installing selfsign key";
+if (!SSL_CTX_use_PrivateKey(sctx, pkey))
+  goto err;
+
+return OK;
+
+err:
+  (void) tls_error(where, NULL, NULL);
+  if (x509) X509_free(x509);
+  if (pkey) EVP_PKEY_free(pkey);
+  return DEFER;
+}
+
+
+
+
 /*************************************************
 *        Expand key and cert file specs          *
 *************************************************/
@@ -759,59 +1001,68 @@ 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)
+if (!cbinfo->certificate)
   {
-  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->host)                    /* client */
+    return OK;
+                                       /* server */
+  if (tls_install_selfsign(sctx) != OK)
+    return DEFER;
   }
+else
+  {
+  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 (cbinfo->privatekey != NULL &&
-    !expand_check(cbinfo->privatekey, US"tls_privatekey", &expanded))
-  return DEFER;
+  if (!expand_check(cbinfo->certificate, US"tls_certificate", &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)
+    {
+    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 (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);
+  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 && *expanded)
+    {
+    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 (cbinfo->is_server && cbinfo->u_ocsp.server.file)
   {
   if (!expand_check(cbinfo->u_ocsp.server.file, US"tls_ocsp_file", &expanded))
     return DEFER;
 
-  if (expanded != NULL && *expanded != 0)
+  if (expanded && *expanded)
     {
     DEBUG(D_tls) debug_printf("tls_ocsp_file %s\n", expanded);
-    if (cbinfo->u_ocsp.server.file_expanded &&
-        (Ustrcmp(expanded, cbinfo->u_ocsp.server.file_expanded) == 0))
+    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);
+      DEBUG(D_tls) debug_printf(" - value unchanged, using existing values\n");
+      }
+    else
+      {
+      ocsp_load_response(sctx, cbinfo, expanded);
       }
     }
   }
@@ -883,6 +1134,12 @@ 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 (  !init_dh(server_sni, cbinfo->dhparam, NULL)
+   || !init_ecdh(server_sni, NULL)
+   )
+  return SSL_TLSEXT_ERR_NOACK;
+
 if (cbinfo->server_cipher_list)
   SSL_CTX_set_cipher_list(server_sni, CS cbinfo->server_cipher_list);
 #ifndef DISABLE_OCSP
@@ -898,10 +1155,7 @@ 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))
+if ((rc = tls_expand_session_files(server_sni, cbinfo)) != OK)
   return SSL_TLSEXT_ERR_NOACK;
 
 DEBUG(D_tls) debug_printf("Switching SSL context.\n");
@@ -936,7 +1190,7 @@ uschar *response_der;
 int response_der_len;
 
 DEBUG(D_tls)
-  debug_printf("Received TLS status request (OCSP stapling); %s response.",
+  debug_printf("Received TLS status request (OCSP stapling); %s response\n",
     cbinfo->u_ocsp.server.response ? "have" : "lack");
 
 tls_in.ocsp = OCSP_NOT_RESP;
@@ -978,8 +1232,7 @@ 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)
+  if (cbinfo->u_ocsp.client.verify_required && LOGGING(tls_cipher))
     log_write(0, LOG_MAIN, "Received TLS status callback, null content");
   else
     DEBUG(D_tls) debug_printf(" null\n");
@@ -989,7 +1242,7 @@ if(!p)
 if(!(rsp = d2i_OCSP_RESPONSE(NULL, &p, len)))
  {
   tls_out.ocsp = OCSP_FAILED;
-  if (log_extra_selector & LX_tls_cipher)
+  if (LOGGING(tls_cipher))
     log_write(0, LOG_MAIN, "Received TLS cert status response, parse error");
   else
     DEBUG(D_tls) debug_printf(" parse error\n");
@@ -999,7 +1252,7 @@ if(!(rsp = d2i_OCSP_RESPONSE(NULL, &p, len)))
 if(!(bs = OCSP_response_get1_basic(rsp)))
   {
   tls_out.ocsp = OCSP_FAILED;
-  if (log_extra_selector & LX_tls_cipher)
+  if (LOGGING(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");
@@ -1030,27 +1283,37 @@ if(!(bs = OCSP_response_get1_basic(rsp)))
              cbinfo->u_ocsp.client.verify_store, 0)) <= 0)
       {
       tls_out.ocsp = OCSP_FAILED;
-      if (log_extra_selector & LX_tls_cipher)
+      if (LOGGING(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;
+      goto failed;
       }
 
     BIO_printf(bp, "OCSP response well-formed and signed OK\n");
 
+    /*XXX So we have a good stapled OCSP status.  How do we know
+    it is for the cert of interest?  OpenSSL 1.1.0 has a routine
+    OCSP_resp_find_status() which matches on a cert id, which presumably
+    we should use. Making an id needs OCSP_cert_id_new(), which takes
+    issuerName, issuerKey, serialNumber.  Are they all in the cert?
+
+    For now, carry on blindly accepting the resp. */
+
       {
-      STACK_OF(OCSP_SINGLERESP) * sresp = bs->tbsResponseData->responses;
       OCSP_SINGLERESP * single;
 
+#ifdef EXIM_HAVE_OCSP_RESP_COUNT
+      if (OCSP_resp_count(bs) != 1)
+#else
+      STACK_OF(OCSP_SINGLERESP) * sresp = bs->tbsResponseData->responses;
       if (sk_OCSP_SINGLERESP_num(sresp) != 1)
+#endif
         {
        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;
+        goto failed;
         }
       single = OCSP_resp_get0(bs, 0);
       status = OCSP_single_get0_status(single, &reason, &rev,
@@ -1065,7 +1328,6 @@ if(!(bs = OCSP_response_get1_basic(rsp)))
       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
       {
@@ -1076,24 +1338,24 @@ if(!(bs = OCSP_response_get1_basic(rsp)))
        case V_OCSP_CERTSTATUS_GOOD:
          tls_out.ocsp = OCSP_VFIED;
          i = 1;
-         break;
+         goto good;
        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:
+  failed:
+    i = cbinfo->u_ocsp.client.verify_required ? 0 : 1;
+  good:
     BIO_free(bp);
   }
 
@@ -1152,14 +1414,14 @@ else
 cbinfo->dhparam = dhparam;
 cbinfo->server_cipher_list = NULL;
 cbinfo->host = host;
-#ifdef EXPERIMENTAL_EVENT
+#ifndef DISABLE_EVENT
 cbinfo->event_action = NULL;
 #endif
 
 SSL_load_error_strings();          /* basic set up */
 OpenSSL_add_ssl_algorithms();
 
-#if (OPENSSL_VERSION_NUMBER >= 0x0090800fL) && !defined(OPENSSL_NO_SHA256)
+#ifdef EXIM_HAVE_SHA256
 /* SHA256 is becoming ever more popular. This makes sure it gets added to the
 list of available digests. */
 EVP_add_digest(EVP_sha256());
@@ -1173,10 +1435,9 @@ when OpenSSL is built without SSLv2 support.
 By disabling with openssl_options, we can let admins re-enable with the
 existing knob. */
 
-*ctxp = SSL_CTX_new((host == NULL)?
-  SSLv23_server_method() : SSLv23_client_method());
+*ctxp = SSL_CTX_new(host ? SSLv23_client_method() : SSLv23_server_method());
 
-if (*ctxp == NULL) return tls_error(US"SSL_CTX_new", host, NULL);
+if (!*ctxp) 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
@@ -1233,13 +1494,17 @@ else
   DEBUG(D_tls) debug_printf("no SSL CTX options to set\n");
 
 /* Initialize with DH parameters if supplied */
+/* Initialize ECDH temp key parameter selection */
 
-if (!init_dh(*ctxp, dhparam, host)) return DEFER;
+if (  !init_dh(*ctxp, dhparam, host)
+   || !init_ecdh(*ctxp, host)
+   )
+  return DEFER;
 
 /* Set up certificate and key (and perhaps OCSP info) */
 
-rc = tls_expand_session_files(*ctxp, cbinfo);
-if (rc != OK) return rc;
+if ((rc = tls_expand_session_files(*ctxp, cbinfo)) != OK)
+  return rc;
 
 /* If we need to handle SNI, do so */
 #ifdef EXIM_HAVE_OPENSSL_TLSEXT
@@ -1278,9 +1543,10 @@ else                     /* client */
 
 cbinfo->verify_cert_hostnames = NULL;
 
+#ifdef EXIM_HAVE_EPHEM_RSA_KEX
 /* Set up the RSA callback */
-
 SSL_CTX_set_tmp_rsa_callback(*ctxp, rsa_callback);
+#endif
 
 /* Finally, set the timeout, and we are done */
 
@@ -1381,26 +1647,18 @@ uschar *expcerts, *expcrl;
 if (!expand_check(certs, US"tls_verify_certificates", &expcerts))
   return DEFER;
 
-if (expcerts != NULL && *expcerts != '\0')
+if (expcerts && *expcerts)
   {
-  if (Ustrcmp(expcerts, "system") == 0)
-    {
-    /* Tell the library to use its compiled-in location for the system default
-    CA bundle, only */
+  /* Tell the library to use its compiled-in location for the system default
+  CA bundle. Then add the ones specified in the config, if any. */
 
-    if (!SSL_CTX_set_default_verify_paths(sctx))
-      return tls_error(US"SSL_CTX_set_default_verify_paths", host, NULL);
-    }
-  else
+  if (!SSL_CTX_set_default_verify_paths(sctx))
+    return tls_error(US"SSL_CTX_set_default_verify_paths", host, NULL);
+
+  if (Ustrcmp(expcerts, "system") != 0)
     {
     struct stat statbuf;
 
-    /* Tell the library to use its compiled-in location for the system default
-    CA bundle. Those given by the exim config are additional to these */
-
-    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)
       {
       log_write(0, LOG_MAIN|LOG_PANIC,
@@ -1420,8 +1678,8 @@ if (expcerts != NULL && *expcerts != '\0')
       certificates are recognized, but the error message is still misleading (it
       says no certificate was supplied.) But this is better. */
 
-      if ((file == NULL || statbuf.st_size > 0) &&
-           !SSL_CTX_load_verify_locations(sctx, CS file, CS dir))
+      if (  (!file || statbuf.st_size > 0)
+         && !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
@@ -1430,15 +1688,16 @@ if (expcerts != NULL && *expcerts != '\0')
       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
+      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.
       */
-      if (file != NULL)
+      if (file)
        {
        STACK_OF(X509_NAME) * names = SSL_load_client_CA_file(CS file);
-  DEBUG(D_tls) debug_printf("Added %d certificate authorities.\n",
+
+       DEBUG(D_tls) debug_printf("Added %d certificate authorities.\n",
                                    sk_X509_NAME_num(names));
        SSL_CTX_set_client_CA_list(sctx, names);
        }
@@ -1447,20 +1706,20 @@ if (expcerts != NULL && *expcerts != '\0')
 
   /* Handle a certificate revocation list. */
 
-  #if OPENSSL_VERSION_NUMBER > 0x00907000L
+#if OPENSSL_VERSION_NUMBER > 0x00907000L
 
   /* This bit of code is now the version supplied by Lars Mainka. (I have
-   * merely reformatted it into the Exim code style.)
+  merely reformatted it into the Exim code style.)
 
-   * "From here I changed the code to add support for multiple crl's
-   * in pem format in one file or to support hashed directory entries in
-   * pem format instead of a file. This method now uses the library function
-   * X509_STORE_load_locations to add the CRL location to the SSL context.
-   * OpenSSL will then handle the verify against CA certs and CRLs by
-   * itself in the verify callback." */
+  "From here I changed the code to add support for multiple crl's
+  in pem format in one file or to support hashed directory entries in
+  pem format instead of a file. This method now uses the library function
+  X509_STORE_load_locations to add the CRL location to the SSL context.
+  OpenSSL will then handle the verify against CA certs and CRLs by
+  itself in the verify callback." */
 
   if (!expand_check(crl, US"tls_crl", &expcrl)) return DEFER;
-  if (expcrl != NULL && *expcrl != 0)
+  if (expcrl && *expcrl)
     {
     struct stat statbufcrl;
     if (Ustat(expcrl, &statbufcrl) < 0)
@@ -1496,7 +1755,7 @@ if (expcerts != NULL && *expcerts != '\0')
       }
     }
 
-  #endif  /* OPENSSL_VERSION_NUMBER > 0x00907000L */
+#endif  /* OPENSSL_VERSION_NUMBER > 0x00907000L */
 
   /* If verification is optional, don't fail if no certificate */
 
@@ -1533,7 +1792,6 @@ tls_server_start(const uschar *require_ciphers)
 int rc;
 uschar *expciphers;
 tls_ext_ctx_cb *cbinfo;
-X509 * peercert;
 static uschar peerdn[256];
 static uschar cipherbuf[256];
 
@@ -1685,6 +1943,7 @@ ssl_xfer_buffer_lwm = ssl_xfer_buffer_hwm = 0;
 ssl_xfer_eof = ssl_xfer_error = 0;
 
 receive_getc = tls_getc;
+receive_get_cache = tls_get_cache;
 receive_ungetc = tls_ungetc;
 receive_feof = tls_feof;
 receive_ferror = tls_ferror;
@@ -1703,7 +1962,7 @@ tls_client_basic_ctx_init(SSL_CTX * ctx,
                          )
 {
 int rc;
-/* stick to the old behaviour for compatibility if tls_verify_certificates is 
+/* 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 */
 
@@ -1724,7 +1983,12 @@ if ((rc = setup_certs(ctx, ob->tls_verify_certificates,
 
 if (verify_check_given_host(&ob->tls_verify_cert_hostnames, host) == OK)
   {
-  cbinfo->verify_cert_hostnames = host->name;
+  cbinfo->verify_cert_hostnames =
+#ifdef SUPPORT_I18N
+    string_domain_utf8_to_alabel(host->name, NULL);
+#else
+    host->name;
+#endif
   DEBUG(D_tls) debug_printf("Cert hostname to check: \"%s\"\n",
                    cbinfo->verify_cert_hostnames);
   }
@@ -1773,8 +2037,8 @@ for (rr = dns_next_rr(dnsa, &dnss, RESET_ANSWERS);
   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 0:    /* action not taken */
     case 1:    break;
     }
 
@@ -1931,8 +2195,7 @@ if (ob->tls_sni)
     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",
+    log_write(0, LOG_MAIN, "SNI unusable with this OpenSSL library version; ignoring \"%s\"\n",
           tls_out.sni);
 #endif
     }
@@ -1972,7 +2235,7 @@ if (request_ocsp)
   }
 #endif
 
-#ifdef EXPERIMENTAL_EVENT
+#ifndef DISABLE_EVENT
 client_static_cbinfo->event_action = tb->event_action;
 #endif
 
@@ -2051,6 +2314,7 @@ if (ssl_xfer_buffer_lwm >= ssl_xfer_buffer_hwm)
     DEBUG(D_tls) debug_printf("Got SSL_ERROR_ZERO_RETURN\n");
 
     receive_getc = smtp_getc;
+    receive_get_cache = smtp_get_cache;
     receive_ungetc = smtp_ungetc;
     receive_feof = smtp_feof;
     receive_ferror = smtp_ferror;
@@ -2096,6 +2360,16 @@ if (ssl_xfer_buffer_lwm >= ssl_xfer_buffer_hwm)
 return ssl_xfer_buffer[ssl_xfer_buffer_lwm++];
 }
 
+#ifndef DISABLE_DKIM
+void
+tls_get_cache()
+{
+int n = ssl_xfer_buffer_hwm - ssl_xfer_buffer_lwm;
+if (n > 0)
+  dkim_exim_verify_feed(ssl_xfer_buffer+ssl_xfer_buffer_lwm, n);
+}
+#endif
+
 
 
 /*************************************************
@@ -2177,27 +2451,28 @@ while (left > 0)
   switch (error)
     {
     case SSL_ERROR_SSL:
-    ERR_error_string(ERR_get_error(), ssl_errstring);
-    log_write(0, LOG_MAIN, "TLS error (SSL_write): %s", ssl_errstring);
-    return -1;
+      ERR_error_string(ERR_get_error(), ssl_errstring);
+      log_write(0, LOG_MAIN, "TLS error (SSL_write): %s", ssl_errstring);
+      return -1;
 
     case SSL_ERROR_NONE:
-    left -= outbytes;
-    buff += outbytes;
-    break;
+      left -= outbytes;
+      buff += outbytes;
+      break;
 
     case SSL_ERROR_ZERO_RETURN:
-    log_write(0, LOG_MAIN, "SSL channel closed on write");
-    return -1;
+      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));
+      log_write(0, LOG_MAIN, "SSL_write: (from %s) syscall: %s",
+       sender_fullhost ? sender_fullhost : US"<unknown>",
+       strerror(errno));
+      return -1;
 
     default:
-    log_write(0, LOG_MAIN, "SSL_write error %d", error);
-    return -1;
+      log_write(0, LOG_MAIN, "SSL_write error %d", error);
+      return -1;
     }
   }
 return len;
@@ -2408,8 +2683,13 @@ i = (i + 7) / 8;
 if (i < needed_len)
   needed_len = i;
 
+#ifdef EXIM_HAVE_RAND_PSEUDO
 /* We do not care if crypto-strong */
 i = RAND_pseudo_bytes(smallbuf, needed_len);
+#else
+i = RAND_bytes(smallbuf, needed_len);
+#endif
+
 if (i < 0)
   {
   DEBUG(D_all)
@@ -2603,6 +2883,9 @@ result = 0L;
 #ifdef SSL_OP_NO_SSLv2
 result |= SSL_OP_NO_SSLv2;
 #endif
+#ifdef SSL_OP_SINGLE_DH_USE
+result |= SSL_OP_SINGLE_DH_USE;
+#endif
 
 if (option_spec == NULL)
   {
@@ -2626,6 +2909,7 @@ for (s=option_spec; *s != '\0'; /**/)
   keep_c = *end;
   *end = '\0';
   item_parsed = tls_openssl_one_option_parse(s, &item);
+  *end = keep_c;
   if (!item_parsed)
     {
     DEBUG(D_tls) debug_printf("openssl option setting unrecognised: \"%s\"\n", s);
@@ -2637,7 +2921,6 @@ for (s=option_spec; *s != '\0'; /**/)
     result |= item;
   else
     result &= ~item;
-  *end = keep_c;
   s = end;
   }