CVE-2020-28018: Use-after-free in tls-openssl.c
[exim.git] / src / src / tls-openssl.c
index 5ea4d964e8b02a39d809c4f65a516d1145d7eb26..499384b50e34c6e6159c79a9858abe8bcc4bb133 100644 (file)
@@ -3,6 +3,7 @@
 *************************************************/
 
 /* Copyright (c) University of Cambridge 1995 - 2019 */
+/* Copyright (c) The Exim Maintainers 2020 */
 /* See the file NOTICE for conditions of use and distribution. */
 
 /* Portions Copyright (c) The OpenSSL Project 1999 */
@@ -76,6 +77,9 @@ change this guard and punt the issue for a while longer. */
 #  define EXIM_HAVE_SESSION_TICKET
 #  define EXIM_HAVE_OPESSL_TRACE
 #  define EXIM_HAVE_OPESSL_GET0_SERIAL
+#  ifndef DISABLE_OCSP
+#   define EXIM_HAVE_OCSP
+#  endif
 # else
 #  define EXIM_NEED_OPENSSL_INIT
 # endif
@@ -102,6 +106,8 @@ change this guard and punt the issue for a while longer. */
 #  define OPENSSL_HAVE_KEYLOG_CB
 #  define OPENSSL_HAVE_NUM_TICKETS
 #  define EXIM_HAVE_OPENSSL_CIPHER_STD_NAME
+# else
+#  define OPENSSL_BAD_SRVR_OURCERT
 # endif
 #endif
 
@@ -143,24 +149,35 @@ all options unless explicitly for DTLS, let the administrator choose which
 to apply.
 
 This list is current as of:
-  ==>  1.0.1b  <==
-Plus SSL_OP_SAFARI_ECDHE_ECDSA_BUG from 2013-June patch/discussion on openssl-dev
-Plus SSL_OP_NO_TLSv1_3 for 1.1.2-dev
+  ==>  1.1.1c  <==
+
+XXX could we autobuild this list, as with predefined-macros?
+Seems just parsing ssl.h for SSL_OP_.* would be enough (except to exclude DTLS).
+Also allow a numeric literal?
 */
 static exim_openssl_option exim_openssl_options[] = {
 /* KEEP SORTED ALPHABETICALLY! */
 #ifdef SSL_OP_ALL
   { US"all", (long) SSL_OP_ALL },
 #endif
+#ifdef SSL_OP_ALLOW_NO_DHE_KEX
+  { US"allow_no_dhe_kex", SSL_OP_ALLOW_NO_DHE_KEX },
+#endif
 #ifdef SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION
   { US"allow_unsafe_legacy_renegotiation", SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION },
 #endif
 #ifdef SSL_OP_CIPHER_SERVER_PREFERENCE
   { US"cipher_server_preference", SSL_OP_CIPHER_SERVER_PREFERENCE },
 #endif
+#ifdef SSL_OP_CRYPTOPRO_TLSEXT_BUG
+  { US"cryptopro_tlsext_bug", SSL_OP_CRYPTOPRO_TLSEXT_BUG },
+#endif
 #ifdef SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS
   { US"dont_insert_empty_fragments", SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS },
 #endif
+#ifdef SSL_OP_ENABLE_MIDDLEBOX_COMPAT
+  { US"enable_middlebox_compat", SSL_OP_ENABLE_MIDDLEBOX_COMPAT },
+#endif
 #ifdef SSL_OP_EPHEMERAL_RSA
   { US"ephemeral_rsa", SSL_OP_EPHEMERAL_RSA },
 #endif
@@ -182,9 +199,18 @@ static exim_openssl_option exim_openssl_options[] = {
 #ifdef SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG
   { US"netscape_reuse_cipher_change_bug", SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG },
 #endif
+#ifdef SSL_OP_NO_ANTI_REPLAY
+  { US"no_anti_replay", SSL_OP_NO_ANTI_REPLAY },
+#endif
 #ifdef SSL_OP_NO_COMPRESSION
   { US"no_compression", SSL_OP_NO_COMPRESSION },
 #endif
+#ifdef SSL_OP_NO_ENCRYPT_THEN_MAC
+  { US"no_encrypt_then_mac", SSL_OP_NO_ENCRYPT_THEN_MAC },
+#endif
+#ifdef SSL_OP_NO_RENEGOTIATION
+  { US"no_renegotiation", SSL_OP_NO_RENEGOTIATION },
+#endif
 #ifdef SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION
   { US"no_session_resumption_on_renegotiation", SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION },
 #endif
@@ -214,6 +240,9 @@ static exim_openssl_option exim_openssl_options[] = {
 #ifdef SSL_OP_NO_TLSv1_3
   { US"no_tlsv1_3", SSL_OP_NO_TLSv1_3 },
 #endif
+#ifdef SSL_OP_PRIORITIZE_CHACHA
+  { US"prioritize_chacha", SSL_OP_PRIORITIZE_CHACHA },
+#endif
 #ifdef SSL_OP_SAFARI_ECDHE_ECDSA_BUG
   { US"safari_ecdhe_ecdsa_bug", SSL_OP_SAFARI_ECDHE_ECDSA_BUG },
 #endif
@@ -238,6 +267,9 @@ static exim_openssl_option exim_openssl_options[] = {
 #ifdef SSL_OP_TLS_ROLLBACK_BUG
   { US"tls_rollback_bug", SSL_OP_TLS_ROLLBACK_BUG },
 #endif
+#ifdef SSL_OP_TLSEXT_PADDING
+  { US"tlsext_padding", SSL_OP_TLSEXT_PADDING },
+#endif
 };
 
 #ifndef MACRO_PREDEF
@@ -266,6 +298,13 @@ builtin_macro_create_var(US"_RESUME_DECODE", RESUME_DECODE_STRING );
 # ifdef SSL_OP_NO_TLSv1_3
 builtin_macro_create(US"_HAVE_TLS1_3");
 # endif
+# ifdef OPENSSL_BAD_SRVR_OURCERT
+builtin_macro_create(US"_TLS_BAD_MULTICERT_IN_OURCERT");
+# endif
+# ifdef EXIM_HAVE_OCSP
+builtin_macro_create(US"_HAVE_TLS_OCSP");
+builtin_macro_create(US"_HAVE_TLS_OCSP_LIST");
+# endif
 }
 #else
 
@@ -333,10 +372,10 @@ typedef struct ocsp_resp {
 } ocsp_resplist;
 
 typedef struct tls_ext_ctx_cb {
-  tls_support * tlsp;
-  uschar *certificate;
-  uschar *privatekey;
-  BOOL is_server;
+  tls_support *        tlsp;
+  uschar *     certificate;
+  uschar *     privatekey;
+  BOOL         is_server;
 #ifndef DISABLE_OCSP
   STACK_OF(X509) *verify_stack;                /* chain for verifying the proof */
   union {
@@ -351,14 +390,14 @@ typedef struct tls_ext_ctx_cb {
     } client;
   } u_ocsp;
 #endif
-  uschar *dhparam;
+  uschar *     dhparam;
   /* these are cached from first expand */
-  uschar *server_cipher_list;
+  uschar *     server_cipher_list;
   /* only passed down to tls_error: */
-  host_item *host;
+  host_item *  host;
   const uschar * verify_cert_hostnames;
 #ifndef DISABLE_EVENT
-  uschar * event_action;
+  uschar *     event_action;
 #endif
 } tls_ext_ctx_cb;
 
@@ -841,7 +880,13 @@ DEBUG(D_tls)
 static void
 keylog_callback(const SSL *ssl, const char *line)
 {
+char * filename;
+FILE * fp;
 DEBUG(D_tls) debug_printf("%.200s\n", line);
+if (!(filename = getenv("SSLKEYLOGFILE"))) return;
+if (!(fp = fopen(filename, "a"))) return;
+fprintf(fp, "%s\n", line);
+fclose(fp);
 }
 #endif
 
@@ -1200,12 +1245,13 @@ Arguments:
   sctx            the SSL_CTX* to update
   cbinfo          various parts of session state
   filename        the filename putatively holding an OCSP response
+  is_pem         file is PEM format; otherwise is DER
 
 */
 
 static void
 ocsp_load_response(SSL_CTX * sctx, tls_ext_ctx_cb * cbinfo,
-  const uschar * filename)
+  const uschar * filename, BOOL is_pem)
 {
 BIO * bio;
 OCSP_RESPONSE * resp;
@@ -1216,7 +1262,8 @@ STACK_OF(X509) * sk;
 unsigned long verify_flags;
 int status, reason, i;
 
-DEBUG(D_tls) debug_printf("tls_ocsp_file        '%s'\n", filename);
+DEBUG(D_tls)
+  debug_printf("tls_ocsp_file (%s)  '%s'\n", is_pem ? "PEM" : "DER", filename);
 
 if (!(bio = BIO_new_file(CS filename, "rb")))
   {
@@ -1225,8 +1272,26 @@ if (!(bio = BIO_new_file(CS filename, "rb")))
   return;
   }
 
-resp = d2i_OCSP_RESPONSE_bio(bio, NULL);
+if (is_pem)
+  {
+  uschar * data, * freep;
+  char * dummy;
+  long len;
+  if (!PEM_read_bio(bio, &dummy, &dummy, &data, &len))
+    {
+    DEBUG(D_tls) debug_printf("Failed to read PEM file \"%s\"\n",
+       filename);
+    return;
+    }
+debug_printf("read pem file\n");
+  freep = data;
+  resp = d2i_OCSP_RESPONSE(NULL, CUSS &data, len);
+  OPENSSL_free(freep);
+  }
+else
+  resp = d2i_OCSP_RESPONSE_bio(bio, NULL);
 BIO_free(bio);
+
 if (!resp)
   {
   DEBUG(D_tls) debug_printf("Error reading OCSP response.\n");
@@ -1512,6 +1577,7 @@ else
       const uschar * olist = cbinfo->u_ocsp.server.file;
       int osep = 0;
       uschar * ofile;
+      BOOL fmt_pem = FALSE;
 
       if (olist)
        if (!expand_check(olist, US"tls_ocsp_file", USS &olist, errstr))
@@ -1540,7 +1606,19 @@ else
 #ifndef DISABLE_OCSP
        if (olist)
          if ((ofile = string_nextinlist(&olist, &osep, NULL, 0)))
-           ocsp_load_response(sctx, cbinfo, ofile);
+           {
+           if (Ustrncmp(ofile, US"PEM ", 4) == 0)
+             {
+             fmt_pem = TRUE;
+             ofile += 4;
+             }
+           else if (Ustrncmp(ofile, US"DER ", 4) == 0)
+             {
+             fmt_pem = FALSE;
+             ofile += 4;
+             }
+           ocsp_load_response(sctx, cbinfo, ofile, fmt_pem);
+           }
          else
            DEBUG(D_tls) debug_printf("ran out of ocsp file list\n");
 #endif
@@ -1802,13 +1880,13 @@ OCSP_RESPONSE * rsp;
 OCSP_BASICRESP * bs;
 int i;
 
-DEBUG(D_tls) debug_printf("Received TLS status response (OCSP stapling):");
+DEBUG(D_tls) debug_printf("Received TLS status callback (OCSP stapling):\n");
 len = SSL_get_tlsext_status_ocsp_resp(s, &p);
 if(!p)
  {
   /* Expect this when we requested ocsp but got none */
   if (cbinfo->u_ocsp.client.verify_required && LOGGING(tls_cipher))
-    log_write(0, LOG_MAIN, "Received TLS status callback, null content");
+    log_write(0, LOG_MAIN, "Required TLS certificate status not received");
   else
     DEBUG(D_tls) debug_printf(" null\n");
   return cbinfo->u_ocsp.client.verify_required ? 0 : 1;
@@ -1844,8 +1922,9 @@ if (!(bs = OCSP_response_get1_basic(rsp)))
 */
   {
     BIO * bp = NULL;
-    int status, reason;
-    ASN1_GENERALIZEDTIME *rev, *thisupd, *nextupd;
+#ifndef EXIM_HAVE_OCSP_RESP_COUNT
+    STACK_OF(OCSP_SINGLERESP) * sresp = bs->tbsResponseData->responses;
+#endif
 
     DEBUG(D_tls) bp = BIO_new_fp(debug_file, BIO_NOCLOSE);
 
@@ -1855,19 +1934,23 @@ if (!(bs = OCSP_response_get1_basic(rsp)))
     /* DEBUG(D_tls) x509_store_dump_cert_s_names(cbinfo->u_ocsp.client.verify_store); */
 
     if ((i = OCSP_basic_verify(bs, cbinfo->verify_stack,
-             cbinfo->u_ocsp.client.verify_store, 0)) <= 0)
-      {
-      tls_out.ocsp = OCSP_FAILED;
-      if (LOGGING(tls_cipher)) log_write(0, LOG_MAIN,
-             "Received TLS cert status response, itself unverifiable: %s",
-             ERR_reason_error_string(ERR_peek_error()));
-      BIO_printf(bp, "OCSP response verify failure\n");
-      ERR_print_errors(bp);
-      OCSP_RESPONSE_print(bp, rsp, 0);
-      goto failed;
-      }
+             cbinfo->u_ocsp.client.verify_store, OCSP_NOEXPLICIT)) <= 0)
+      if (ERR_peek_error())
+       {
+       tls_out.ocsp = OCSP_FAILED;
+       if (LOGGING(tls_cipher)) log_write(0, LOG_MAIN,
+               "Received TLS cert status response, itself unverifiable: %s",
+               ERR_reason_error_string(ERR_peek_error()));
+       BIO_printf(bp, "OCSP response verify failure\n");
+       ERR_print_errors(bp);
+       OCSP_RESPONSE_print(bp, rsp, 0);
+       goto failed;
+       }
+      else
+       DEBUG(D_tls) debug_printf("no explicit trust for OCSP signing"
+         " in the root CA certificate; ignoring\n");
 
-    BIO_printf(bp, "OCSP response well-formed and signed OK\n");
+    DEBUG(D_tls) debug_printf("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
@@ -1877,60 +1960,65 @@ if (!(bs = OCSP_response_get1_basic(rsp)))
 
     For now, carry on blindly accepting the resp. */
 
-      {
-      OCSP_SINGLERESP * single;
-
+    for (int idx =
 #ifdef EXIM_HAVE_OCSP_RESP_COUNT
-      if (OCSP_resp_count(bs) != 1)
+           OCSP_resp_count(bs) - 1;
 #else
-      STACK_OF(OCSP_SINGLERESP) * sresp = bs->tbsResponseData->responses;
-      if (sk_OCSP_SINGLERESP_num(sresp) != 1)
+           sk_OCSP_SINGLERESP_num(sresp) - 1;
 #endif
-        {
-       tls_out.ocsp = OCSP_FAILED;
-        log_write(0, LOG_MAIN, "OCSP stapling "
-           "with multiple responses not handled");
-        goto failed;
-        }
-      single = OCSP_resp_get0(bs, 0);
+        idx >= 0; idx--)
+      {
+      OCSP_SINGLERESP * single = OCSP_resp_get0(bs, idx);
+      int status, reason;
+      ASN1_GENERALIZEDTIME * rev, * thisupd, * nextupd;
+
+  /*XXX so I can see putting a loop in here to handle a rsp with >1 singleresp
+  - but what happens with a GnuTLS-style input?
+
+  we could do with a debug label for each singleresp
+  - it has a certID with a serialNumber, but I see no API to get that
+  */
       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");
-      }
-    else
-      {
+      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");
+       goto failed;
+       }
+
       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;
-         goto good;
+         continue;     /* the idx loop */
        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);
          break;
        default:
-         tls_out.ocsp = OCSP_FAILED;
          log_write(0, LOG_MAIN,
              "Server certificate status unknown, in OCSP stapling");
          break;
        }
+
+      goto failed;
       }
+
+    i = 1;
+    tls_out.ocsp = OCSP_VFIED;
+    goto good;
+
   failed:
+    tls_out.ocsp = OCSP_FAILED;
     i = cbinfo->u_ocsp.client.verify_required ? 0 : 1;
   good:
     BIO_free(bp);
@@ -2300,7 +2388,11 @@ if (tlsp->peercert)
     for resumption next to the TLS session, and used here. */
 
     if (!tlsp->verify_override)
-      tlsp->certificate_verified = SSL_get_verify_result(ssl) == X509_V_OK;
+      tlsp->certificate_verified =
+#ifdef SUPPORT_DANE
+       tlsp->dane_verified ||
+#endif
+       SSL_get_verify_result(ssl) == X509_V_OK;
     }
 }
 
@@ -2407,7 +2499,7 @@ This is inconsistent with the need to verify the OCSP proof of the server cert.
 #endif
        }
 
-      /* If a certificate file is empty, the next function fails with an
+      /* If a certificate file is empty, the load function fails with an
       unhelpful error message. If we skip it, we get the correct behaviour (no
       certificates are recognized, but the error message is still misleading (it
       says no certificate was supplied).  But this is better. */
@@ -2416,9 +2508,9 @@ This is inconsistent with the need to verify the OCSP proof of the server cert.
          && !SSL_CTX_load_verify_locations(sctx, CS file, CS dir))
        return tls_error(US"SSL_CTX_load_verify_locations", host, NULL, errstr);
 
-      /* 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.
+      /* On the server load the list of CAs for which we will accept certs, for
+      sending to the client.  This is only for the one-file
+      tls_verify_certificates variant.
       If a list isn't loaded into the server, but some verify locations are set,
       the server end appears to make a wildcard request for client certs.
       Meanwhile, the client library as default behaviour *ignores* the list
@@ -2430,7 +2522,7 @@ This is inconsistent with the need to verify the OCSP proof of the server cert.
        {
        STACK_OF(X509_NAME) * names = SSL_load_client_CA_file(CS file);
 
-       SSL_CTX_set_client_CA_list(sctx, names);
+       if (!host) SSL_CTX_set_client_CA_list(sctx, names);
        DEBUG(D_tls) debug_printf("Added %d certificate authorities.\n",
                                    sk_X509_NAME_num(names));
        }
@@ -2671,8 +2763,18 @@ if (rc <= 0)
 
     /* Handle genuine errors */
     case SSL_ERROR_SSL:
-      (void) tls_error(US"SSL_accept", NULL, sigalrm_seen ? US"timed out" : NULL, errstr);
+      {
+      uschar * s = US"SSL_accept";
+      int r = ERR_GET_REASON(ERR_peek_error());
+      if (  r == SSL_R_WRONG_VERSION_NUMBER
+#ifdef SSL_R_VERSION_TOO_LOW
+         || r == SSL_R_VERSION_TOO_LOW
+#endif
+         || r == SSL_R_UNKNOWN_PROTOCOL || r == SSL_R_UNSUPPORTED_PROTOCOL)
+       s = string_sprintf("%s (%s)", s, SSL_get_version(server_ssl));
+      (void) tls_error(s, NULL, sigalrm_seen ? US"timed out" : NULL, errstr);
       return FAIL;
+      }
 
     default:
       DEBUG(D_tls) debug_printf("Got SSL error %d\n", error);
@@ -2705,6 +2807,9 @@ if (SSL_session_reused(server_ssl))
 /* TLS has been set up. Record data for the connection,
 adjust the input functions to read via TLS, and initialize things. */
 
+#ifdef SSL_get_extms_support
+tls_in.ext_master_secret = SSL_get_extms_support(server_ssl) == 1;
+#endif
 peer_cert(server_ssl, &tls_in, peerdn, sizeof(peerdn));
 
 tls_in.ver = tlsver_name(server_ssl);
@@ -2747,12 +2852,12 @@ See description in https://paquier.xyz/postgresql-2/channel-binding-openssl/ */
   uschar c, * s;
   size_t len = SSL_get_peer_finished(server_ssl, &c, 0);
   int old_pool = store_pool;
-  
+
   SSL_get_peer_finished(server_ssl, s = store_get((int)len, FALSE), len);
   store_pool = POOL_PERM;
     tls_in.channelbinding = b64encode_taint(CUS s, (int)len, FALSE);
   store_pool = old_pool;
-  DEBUG(D_tls) debug_printf("Have channel bindings cached for possible auth usage\n");
+  DEBUG(D_tls) debug_printf("Have channel bindings cached for possible auth usage %p\n", tls_in.channelbinding);
   }
 
 /* Only used by the server-side tls (tls_in), including tls_getc.
@@ -2810,9 +2915,9 @@ if (verify_check_given_host(CUSS &ob->tls_verify_cert_hostnames, host) == OK)
   {
   cbinfo->verify_cert_hostnames =
 #ifdef SUPPORT_I18N
-    string_domain_utf8_to_alabel(host->name, NULL);
+    string_domain_utf8_to_alabel(host->certname, NULL);
 #else
-    host->name;
+    host->certname;
 #endif
   DEBUG(D_tls) debug_printf("Cert hostname to check: \"%s\"\n",
                    cbinfo->verify_cert_hostnames);
@@ -2894,7 +2999,7 @@ if (tlsp->host_resumable)
 
   tlsp->resumption |= RESUME_CLIENT_REQUESTED;
   DEBUG(D_tls) debug_printf("checking for resumable session for %s\n", key);
-  if ((dbm_file = dbfn_open(US"tls", O_RDONLY, &dbblock, FALSE, FALSE)))
+  if ((dbm_file = dbfn_open(US"tls", O_RDWR, &dbblock, FALSE, FALSE)))
     {
     /* key for the db is the IP */
     if ((dt = dbfn_read_with_length(dbm_file, key, &len)))
@@ -3092,6 +3197,7 @@ tlsp->tlsa_usage = 0;
 #ifndef DISABLE_OCSP
   {
 # ifdef SUPPORT_DANE
+  /*XXX this should be moved to caller, to be common across gnutls/openssl */
   if (  conn_args->dane
      && ob->hosts_request_ocsp[0] == '*'
      && ob->hosts_request_ocsp[1] == '\0'
@@ -3305,6 +3411,9 @@ DEBUG(D_tls)
 tls_client_resume_posthandshake(exim_client_ctx, tlsp);
 #endif
 
+#ifdef SSL_get_extms_support
+tlsp->ext_master_secret = SSL_get_extms_support(exim_client_ctx->ssl) == 1;
+#endif
 peer_cert(exim_client_ctx->ssl, tlsp, peerdn, sizeof(peerdn));
 
 tlsp->ver = tlsver_name(exim_client_ctx->ssl);
@@ -3323,12 +3432,12 @@ tlsp->cipher_stdname = cipher_stdname_ssl(exim_client_ctx->ssl);
   uschar c, * s;
   size_t len = SSL_get_finished(exim_client_ctx->ssl, &c, 0);
   int old_pool = store_pool;
-  
+
   SSL_get_finished(exim_client_ctx->ssl, s = store_get((int)len, TRUE), len);
   store_pool = POOL_PERM;
     tlsp->channelbinding = b64encode_taint(CUS s, (int)len, TRUE);
   store_pool = old_pool;
-  DEBUG(D_tls) debug_printf("Have channel bindings cached for possible auth usage\n");
+  DEBUG(D_tls) debug_printf("Have channel bindings cached for possible auth usage %p %p\n", tlsp->channelbinding, tlsp);
   }
 
 tlsp->active.sock = cctx->sock;
@@ -3350,6 +3459,7 @@ int inbytes;
 DEBUG(D_tls) debug_printf("Calling SSL_read(%p, %p, %u)\n", server_ssl,
   ssl_xfer_buffer, ssl_xfer_buffer_size);
 
+ERR_clear_error();
 if (smtp_receive_timeout > 0) ALARM(smtp_receive_timeout);
 inbytes = SSL_read(server_ssl, CS ssl_xfer_buffer,
                  MIN(ssl_xfer_buffer_size, lim));
@@ -3499,6 +3609,7 @@ int error;
 DEBUG(D_tls) debug_printf("Calling SSL_read(%p, %p, %u)\n", ssl,
   buff, (unsigned int)len);
 
+ERR_clear_error();
 inbytes = SSL_read(ssl, CS buff, len);
 error = SSL_get_error(ssl, inbytes);
 
@@ -3531,11 +3642,12 @@ Arguments:
 Returns:    the number of bytes after a successful write,
             -1 after a failed write
 
-Used by both server-side and client-side TLS.
+Used by both server-side and client-side TLS.  Calling with len zero and more unset
+will flush buffered writes; buff can be null for this case.
 */
 
 int
-tls_write(void * ct_ctx, const uschar *buff, size_t len, BOOL more)
+tls_write(void * ct_ctx, const uschar * buff, size_t len, BOOL more)
 {
 size_t olen = len;
 int outbytes, error;
@@ -3561,16 +3673,14 @@ a store reset there, so use POOL_PERM. */
 
 if ((more || corked))
   {
-#ifndef DISABLE_PIPE_CONNECT
+  if (!len) buff = US &error;  /* dummy just so that string_catn is ok */
+
   int save_pool = store_pool;
   store_pool = POOL_PERM;
-#endif
 
   corked = string_catn(corked, buff, len);
 
-#ifndef DISABLE_PIPE_CONNECT
   store_pool = save_pool;
-#endif
 
   if (more)
     {
@@ -3585,21 +3695,22 @@ if ((more || corked))
 for (int left = len; left > 0;)
   {
   DEBUG(D_tls) debug_printf("SSL_write(%p, %p, %d)\n", ssl, buff, left);
+  ERR_clear_error();
   outbytes = SSL_write(ssl, CS buff, left);
   error = SSL_get_error(ssl, outbytes);
   DEBUG(D_tls) debug_printf("outbytes=%d error=%d\n", outbytes, error);
   switch (error)
     {
+    case SSL_ERROR_NONE:       /* the usual case */
+      left -= outbytes;
+      buff += outbytes;
+      break;
+
     case SSL_ERROR_SSL:
       ERR_error_string_n(ERR_get_error(), ssl_errstring, sizeof(ssl_errstring));
       log_write(0, LOG_MAIN, "TLS error (SSL_write): %s", ssl_errstring);
       return -1;
 
-    case SSL_ERROR_NONE:
-      left -= outbytes;
-      buff += outbytes;
-      break;
-
     case SSL_ERROR_ZERO_RETURN:
       log_write(0, LOG_MAIN, "SSL channel closed on write");
       return -1;
@@ -3942,7 +4053,7 @@ BOOL
 tls_openssl_options_parse(uschar *option_spec, long *results)
 {
 long result, item;
-uschar *end;
+uschar * exp, * end;
 uschar keep_c;
 BOOL adding, item_parsed;
 
@@ -3950,7 +4061,7 @@ BOOL adding, item_parsed;
 result = SSL_OP_NO_TICKET;
 
 /* Prior to 4.80 we or'd in SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS; removed
- * from default because it increases BEAST susceptibility. */
+from default because it increases BEAST susceptibility. */
 #ifdef SSL_OP_NO_SSLv2
 result |= SSL_OP_NO_SSLv2;
 #endif
@@ -3960,6 +4071,9 @@ result |= SSL_OP_NO_SSLv3;
 #ifdef SSL_OP_SINGLE_DH_USE
 result |= SSL_OP_SINGLE_DH_USE;
 #endif
+#ifdef SSL_OP_NO_RENEGOTIATION
+result |= SSL_OP_NO_RENEGOTIATION;
+#endif
 
 if (!option_spec)
   {
@@ -3967,7 +4081,10 @@ if (!option_spec)
   return TRUE;
   }
 
-for (uschar * s = option_spec; *s; /**/)
+if (!expand_check(option_spec, US"openssl_options", &exp, &end))
+  return FALSE;
+
+for (uschar * s = exp; *s; /**/)
   {
   while (isspace(*s)) ++s;
   if (*s == '\0')
@@ -3989,7 +4106,7 @@ for (uschar * s = option_spec; *s; /**/)
     DEBUG(D_tls) debug_printf("openssl option setting unrecognised: \"%s\"\n", s);
     return FALSE;
     }
-  DEBUG(D_tls) debug_printf("openssl option, %s %8lx: %lx (%s)\n",
+  DEBUG(D_tls) debug_printf("openssl option, %s %08lx: %08lx (%s)\n",
       adding ? "adding to    " : "removing from", result, item, s);
   if (adding)
     result |= item;