TLS: use RFC 6125 rules for certifucate name checks when CNAMES are present. Bug...
[exim.git] / src / src / tls-gnu.c
index 22f7fe5489434b3980812e4fa363500a4d7179d5..fa489902a923229e33d48cdb1f892215a95be526 100644 (file)
@@ -3,6 +3,7 @@
 *************************************************/
 
 /* Copyright (c) University of Cambridge 1995 - 2018 */
+/* Copyright (c) The Exim Maintainers 2020 */
 /* See the file NOTICE for conditions of use and distribution. */
 
 /* Copyright (c) Phil Pennock 2012 */
@@ -53,6 +54,9 @@ require current GnuTLS, then we'll drop support for the ancient libraries).
 # warning "GnuTLS library version too old; tls:cert event unsupported"
 # define DISABLE_EVENT
 #endif
+#if GNUTLS_VERSION_NUMBER >= 0x030000
+# define SUPPORT_SELFSIGN      /* Uncertain what version is first usable but 2.12.23 is not */
+#endif
 #if GNUTLS_VERSION_NUMBER >= 0x030306
 # define SUPPORT_CA_DIR
 #else
@@ -70,12 +74,25 @@ require current GnuTLS, then we'll drop support for the ancient libraries).
 #if GNUTLS_VERSION_NUMBER >= 0x03010a
 # define SUPPORT_GNUTLS_SESS_DESC
 #endif
+#if GNUTLS_VERSION_NUMBER >= 0x030300
+# define GNUTLS_AUTO_GLOBAL_INIT
+# define GNUTLS_AUTO_PKCS11_MANUAL
+#endif
+#if (GNUTLS_VERSION_NUMBER >= 0x030404) \
+  || (GNUTLS_VERSION_NUMBER >= 0x030311) && (GNUTLS_VERSION_NUMBER & 0xffff00 == 0x030300)
+# ifndef DISABLE_OCSP
+#  define EXIM_HAVE_OCSP
+# endif
+#endif
 #if GNUTLS_VERSION_NUMBER >= 0x030500
 # define SUPPORT_GNUTLS_KEYLOG
 #endif
 #if GNUTLS_VERSION_NUMBER >= 0x030506 && !defined(DISABLE_OCSP)
 # define SUPPORT_SRV_OCSP_STACK
 #endif
+#if GNUTLS_VERSION_NUMBER >= 0x030600
+# define GNUTLS_AUTO_DHPARAMS
+#endif
 #if GNUTLS_VERSION_NUMBER >= 0x030603
 # define EXIM_HAVE_TLS1_3
 # define SUPPORT_GNUTLS_EXT_RAW_PARSE
@@ -94,9 +111,11 @@ require current GnuTLS, then we'll drop support for the ancient libraries).
 # endif
 #endif
 
-#ifdef EXPERIMENTAL_TLS_RESUME
-# if GNUTLS_VERSION_NUMBER < 0x030603
-#  error GNUTLS version too early for session-resumption
+#ifndef DISABLE_TLS_RESUME
+# if GNUTLS_VERSION_NUMBER >= 0x030603
+#  define EXIM_HAVE_TLS_RESUME
+# else
+#  warning "GnuTLS library version too old; resumption unsupported"
 # endif
 #endif
 
@@ -114,12 +133,18 @@ require current GnuTLS, then we'll drop support for the ancient libraries).
 void
 options_tls(void)
 {
-# ifdef EXPERIMENTAL_TLS_RESUME
+# ifndef DISABLE_TLS_RESUME
 builtin_macro_create_var(US"_RESUME_DECODE", RESUME_DECODE_STRING );
 # endif
 # ifdef EXIM_HAVE_TLS1_3
 builtin_macro_create(US"_HAVE_TLS1_3");
 # endif
+# ifdef EXIM_HAVE_OCSP
+builtin_macro_create(US"_HAVE_TLS_OCSP");
+# endif
+# ifdef SUPPORT_SRV_OCSP_STACK
+builtin_macro_create(US"_HAVE_TLS_OCSP_LIST");
+# endif
 }
 #else
 
@@ -148,7 +173,7 @@ Some of these correspond to variables in globals.c; those variables will
 be set to point to content in one of these instances, as appropriate for
 the stage of the process lifetime.
 
-Not handled here: global tls_channelbinding_b64.
+Not handled here: global tlsp->tls_channelbinding.
 */
 
 typedef struct exim_gnutls_state {
@@ -158,10 +183,17 @@ typedef struct exim_gnutls_state {
   enum peer_verify_requirement verify_requirement;
   int                  fd_in;
   int                  fd_out;
-  BOOL                 peer_cert_verified;
-  BOOL                 peer_dane_verified;
-  BOOL                 trigger_sni_changes;
-  BOOL                 have_set_peerdn;
+
+  BOOL                 peer_cert_verified:1;
+  BOOL                 peer_dane_verified:1;
+  BOOL                 trigger_sni_changes:1;
+  BOOL                 have_set_peerdn:1;
+  BOOL                 xfer_eof:1;     /*XXX never gets set! */
+  BOOL                 xfer_error:1;
+#ifdef SUPPORT_CORK
+  BOOL                 corked:1;
+#endif
+
   const struct host_item *host;                /* NULL if server */
   gnutls_x509_crt_t    peercert;
   uschar               *peerdn;
@@ -194,8 +226,6 @@ typedef struct exim_gnutls_state {
   uschar *xfer_buffer;
   int xfer_buffer_lwm;
   int xfer_buffer_hwm;
-  BOOL xfer_eof;       /*XXX never gets set! */
-  BOOL xfer_error;
 } exim_gnutls_state_st;
 
 static const exim_gnutls_state_st exim_gnutls_state_init = {
@@ -217,11 +247,13 @@ XXX But see gnutls_session_get_ptr()
 
 static exim_gnutls_state_st state_server;
 
+#ifndef GNUTLS_AUTO_DHPARAMS
 /* dh_params are initialised once within the lifetime of a process using TLS;
 if we used TLS in a long-lived daemon, we'd have to reconsider this.  But we
 don't want to repeat this. */
 
 static gnutls_dh_params_t dh_server_params = NULL;
+#endif
 
 static int ssl_session_timeout = 7200; /* Two hours */
 
@@ -236,7 +268,7 @@ static BOOL gnutls_buggy_ocsp = FALSE;
 static BOOL exim_testharness_disable_ocsp_validity_check = FALSE;
 #endif
 
-#ifdef EXPERIMENTAL_TLS_RESUME
+#ifdef EXIM_HAVE_TLS_RESUME
 static gnutls_datum_t server_sessticket_key;
 #endif
 
@@ -296,7 +328,7 @@ static void exim_gnutls_logger_cb(int level, const char *message);
 
 static int exim_sni_handling_cb(gnutls_session_t session);
 
-#ifdef EXPERIMENTAL_TLS_RESUME
+#ifdef EXIM_HAVE_TLS_RESUME
 static int
 tls_server_ticket_cb(gnutls_session_t sess, u_int htype, unsigned when,
   unsigned incoming, const gnutls_datum_t * msg);
@@ -307,7 +339,7 @@ tls_server_ticket_cb(gnutls_session_t sess, u_int htype, unsigned when,
 void
 tls_daemon_init(void)
 {
-#ifdef EXPERIMENTAL_TLS_RESUME
+#ifdef EXIM_HAVE_TLS_RESUME
 /* We are dependent on the GnuTLS implementation of the Session Ticket
 encryption; both the strength and the key rotation period.  We hope that
 the strength at least matches that of the ciphersuite (but GnuTLS does not
@@ -458,7 +490,8 @@ Sets:
   tls_active                fd
   tls_bits                  strength indicator
   tls_certificate_verified  bool indicator
-  tls_channelbinding_b64    for some SASL mechanisms
+  tls_channelbinding        for some SASL mechanisms
+  tls_ver                   a string
   tls_cipher                a string
   tls_peercert              pointer to library internal
   tls_peerdn                a string
@@ -489,10 +522,10 @@ tlsp->certificate_verified = state->peer_cert_verified;
 tlsp->dane_verified = state->peer_dane_verified;
 #endif
 
-/* note that tls_channelbinding_b64 is not saved to the spool file, since it's
+/* note that tls_channelbinding is not saved to the spool file, since it's
 only available for use for authenticators while this TLS session is running. */
 
-tls_channelbinding_b64 = NULL;
+tlsp->channelbinding = NULL;
 #ifdef HAVE_GNUTLS_SESSION_CHANNEL_BINDING
 channel.data = NULL;
 channel.size = 0;
@@ -500,11 +533,15 @@ if ((rc = gnutls_session_channel_binding(state->session, GNUTLS_CB_TLS_UNIQUE, &
   { DEBUG(D_tls) debug_printf("Channel binding error: %s\n", gnutls_strerror(rc)); }
 else
   {
+  /* Declare the taintedness of the binding info.  On server, untainted; on
+  client, tainted - being the Finish msg from the server. */
+
   old_pool = store_pool;
   store_pool = POOL_PERM;
-  tls_channelbinding_b64 = b64encode(CUS channel.data, (int)channel.size);
+  tlsp->channelbinding = b64encode_taint(CUS channel.data, (int)channel.size,
+                                         !!state->host);
   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\n");
   }
 #endif
 
@@ -524,6 +561,7 @@ tlsp->sni =    state->received_sni;
 
 
 
+#ifndef GNUTLS_AUTO_DHPARAMS
 /*************************************************
 *            Setup up DH parameters              *
 *************************************************/
@@ -546,7 +584,7 @@ init_server_dh(uschar ** errstr)
 {
 int fd, rc;
 unsigned int dh_bits;
-gnutls_datum_t m;
+gnutls_datum_t m = {.data = NULL, .size = 0};
 uschar filename_buf[PATH_MAX];
 uschar *filename = NULL;
 size_t sz;
@@ -559,9 +597,6 @@ DEBUG(D_tls) debug_printf("Initialising GnuTLS server params.\n");
 if ((rc = gnutls_dh_params_init(&dh_server_params)))
   return tls_error_gnu(US"gnutls_dh_params_init", rc, host, errstr);
 
-m.data = NULL;
-m.size = 0;
-
 if (!expand_check(tls_dhparam, US"tls_dhparam", &exp_tls_dhparam, errstr))
   return DEFER;
 
@@ -711,14 +746,12 @@ if (rc < 0)
     return tls_error_sys(US"Unable to open temp file", errno, NULL, errstr);
   (void)exim_chown(temp_fn, exim_uid, exim_gid);   /* Probably not necessary */
 
-  /* GnuTLS overshoots!
-   * If we ask for 2236, we might get 2237 or more.
-   * But there's no way to ask GnuTLS how many bits there really are.
-   * We can ask how many bits were used in a TLS session, but that's it!
-   * The prime itself is hidden behind too much abstraction.
-   * So we ask for less, and proceed on a wing and a prayer.
-   * First attempt, subtracted 3 for 2233 and got 2240.
-   */
+  /* GnuTLS overshoots!  If we ask for 2236, we might get 2237 or more.  But
+  there's no way to ask GnuTLS how many bits there really are.  We can ask
+  how many bits were used in a TLS session, but that's it!  The prime itself
+  is hidden behind too much abstraction.  So we ask for less, and proceed on
+  a wing and a prayer.  First attempt, subtracted 3 for 2233 and got 2240.  */
+
   if (dh_bits >= EXIM_CLIENT_DH_MIN_BITS + 10)
     {
     dh_bits_gen = dh_bits - 10;
@@ -781,6 +814,7 @@ if (rc < 0)
 DEBUG(D_tls) debug_printf("initialized server D-H parameters\n");
 return OK;
 }
+#endif
 
 
 
@@ -796,13 +830,19 @@ gnutls_x509_privkey_t pkey = NULL;
 const uschar * where;
 int rc;
 
+#ifndef SUPPORT_SELFSIGN
+where = US"library too old";
+rc = GNUTLS_E_NO_CERTIFICATE_FOUND;
+if (TRUE) goto err;
+#endif
+
 where = US"initialising pkey";
 if ((rc = gnutls_x509_privkey_init(&pkey))) goto err;
 
 where = US"initialising cert";
 if ((rc = gnutls_x509_crt_init(&cert))) goto err;
 
-where = US"generating pkey";
+where = US"generating pkey";   /* Hangs on 2.12.23 */
 if ((rc = gnutls_x509_privkey_generate(pkey, GNUTLS_PK_RSA,
 #ifdef SUPPORT_PARAM_TO_PK_BITS
 # ifndef GNUTLS_SEC_PARAM_MEDIUM
@@ -962,10 +1002,10 @@ return gnutls_ext_raw_parse(NULL, tls_server_servercerts_ext, msg, 0);
  "Handshake Protocol: Certificate" record.
 So we need to spot the Certificate handshake message, parse it and spot any status_request extension(s)
 
-This is different to tls1.2 - where it is a separate record (wireshake term) / handshake message (gnutls term).
+This is different to tls1.2 - where it is a separate record (wireshark term) / handshake message (gnutls term).
 */
 
-#if defined(EXPERIMENTAL_TLS_RESUME) || defined(SUPPORT_GNUTLS_EXT_RAW_PARSE)
+#if defined(EXIM_HAVE_TLS_RESUME) || defined(SUPPORT_GNUTLS_EXT_RAW_PARSE)
 /* Callback for certificate-status, on server. We sent stapled OCSP. */
 static int
 tls_server_certstatus_cb(gnutls_session_t session, unsigned int htype,
@@ -997,7 +1037,7 @@ switch (htype)
 # endif
   case GNUTLS_HANDSHAKE_CERTIFICATE_STATUS:
     return tls_server_certstatus_cb(sess, htype, when, incoming, msg);
-# ifdef EXPERIMENTAL_TLS_RESUME
+# ifdef EXIM_HAVE_TLS_RESUME
   case GNUTLS_HANDSHAKE_NEW_SESSION_TICKET:
     return tls_server_ticket_cb(sess, htype, when, incoming, msg);
 # endif
@@ -1395,6 +1435,7 @@ tls_set_remaining_x509(exim_gnutls_state_st *state, uschar ** errstr)
 int rc;
 const host_item *host = state->host;  /* macro should be reconsidered? */
 
+#ifndef GNUTLS_AUTO_DHPARAMS
 /* Create D-H parameters, or read them from the cache file. This function does
 its own SMTP error messaging. This only happens for the server, TLS D-H ignores
 client-side params. */
@@ -1403,8 +1444,11 @@ if (!state->host)
   {
   if (!dh_server_params)
     if ((rc = init_server_dh(errstr)) != OK) return rc;
+
+  /* Unnecessary & discouraged with 3.6.0 or later */
   gnutls_certificate_set_dh_params(state->x509_cred, dh_server_params);
   }
+#endif
 
 /* Link the credentials to the session. */
 
@@ -1491,7 +1535,7 @@ if (!exim_gnutls_base_init_done)
   {
   DEBUG(D_tls) debug_printf("GnuTLS global init required.\n");
 
-#ifdef HAVE_GNUTLS_PKCS11
+#if defined(HAVE_GNUTLS_PKCS11) && !defined(GNUTLS_AUTO_PKCS11_MANUAL)
   /* By default, gnutls_global_init will init PKCS11 support in auto mode,
   which loads modules from a config file, which sounds good and may be wanted
   by some sysadmin, but also means in common configurations that GNOME keyring
@@ -1502,8 +1546,10 @@ if (!exim_gnutls_base_init_done)
       return tls_error_gnu(US"gnutls_pkcs11_init", rc, host, errstr);
 #endif
 
+#ifndef GNUTLS_AUTO_GLOBAL_INIT
   if ((rc = gnutls_global_init()))
     return tls_error_gnu(US"gnutls_global_init", rc, host, errstr);
+#endif
 
 #if EXIM_GNUTLS_LIBRARY_LOG_LEVEL >= 0
   DEBUG(D_tls)
@@ -1742,11 +1788,17 @@ old_pool = store_pool;
     /* debug_printf("peer_status: gnutls_session_get_desc %s\n", s); */
 
     for (s++; (c = *s) && c != ')'; s++) g = string_catn(g, s, 1);
+
+    tlsp->ver = string_copyn(g->s, g->ptr);
+    for (uschar * p = US tlsp->ver; *p; p++)
+      if (*p == '-') { *p = '\0'; break; }     /* TLS1.0-PKIX -> TLS1.0 */
+
     g = string_catn(g, US":", 1);
     if (*s) s++;               /* now on _ between groups */
     while ((c = *s))
       {
-      for (*++s && ++s; (c = *s) && c != ')'; s++) g = string_catn(g, c == '-' ? US"_" : s, 1);
+      for (*++s && ++s; (c = *s) && c != ')'; s++)
+       g = string_catn(g, c == '-' ? US"_" : s, 1);
       /* now on ) closing group */
       if ((c = *s) && *++s == '-') g = string_catn(g, US"__", 2);
       /* now on _ between groups */
@@ -1766,6 +1818,8 @@ old_pool = store_pool;
   releases did return "TLS 1.0"; play it safe, just in case. */
 
   for (uschar * p = state->ciphersuite; *p; p++) if (isspace(*p)) *p = '-';
+  tlsp->ver = string_copyn(state->ciphersuite,
+                       Ustrchr(state->ciphersuite, ':') - state->ciphersuite);
 #endif
 
 /* debug_printf("peer_status: ciphersuite %s\n", state->ciphersuite); */
@@ -1902,7 +1956,7 @@ else
       const char ** dd;
       int * ddl;
 
-      for(nrec = 0; state->dane_data_len[nrec]; ) nrec++;
+      for (nrec = 0; state->dane_data_len[nrec]; ) nrec++;
       nrec++;
 
       dd = store_get(nrec * sizeof(uschar *), FALSE);
@@ -2246,17 +2300,17 @@ post_handshake_debug(exim_gnutls_state_st * state)
 #ifdef SUPPORT_GNUTLS_SESS_DESC
 debug_printf("%s\n", gnutls_session_get_desc(state->session));
 #endif
-#ifdef SUPPORT_GNUTLS_KEYLOG
 
+#ifdef SUPPORT_GNUTLS_KEYLOG
 # ifdef EXIM_HAVE_TLS1_3
 if (gnutls_protocol_get_version(state->session) < GNUTLS_TLS1_3)
-#else
+# else
 if (TRUE)
-#endif
+# endif
   {
   gnutls_datum_t c, s;
   gstring * gc, * gs;
-  /* we only want the client random and the master secret */
+  /* For TLS1.2 we only want the client random and the master secret */
   gnutls_session_get_random(state->session, &c, &s);
   gnutls_session_get_master_secret(state->session, &s);
   gc = ddump(&c);
@@ -2265,16 +2319,18 @@ if (TRUE)
   }
 else
   debug_printf("To get keying info for TLS1.3 is hard:\n"
-    " set environment variable SSLKEYLOGFILE to a filename writable by uid exim\n"
-    " add SSLKEYLOGFILE to keep_environment in the exim config\n"
-    " run exim as root\n"
-    " if using sudo, add SSLKEYLOGFILE to env_keep in /etc/sudoers\n"
-    " (works for TLS1.2 also, and saves cut-paste into file)\n");
+    " Set environment variable SSLKEYLOGFILE to a filename relative to the spool directory,\n"
+    " and make sure it is writable by the Exim runtime user.\n"
+    " Add SSLKEYLOGFILE to keep_environment in the exim config.\n"
+    " Start Exim as root.\n"
+    " If using sudo, add SSLKEYLOGFILE to env_keep in /etc/sudoers\n"
+    " (works for TLS1.2 also, and saves cut-paste into file).\n"
+    " Trying to use add_environment for this will not work\n");
 #endif
 }
 
 
-#ifdef EXPERIMENTAL_TLS_RESUME
+#ifdef EXIM_HAVE_TLS_RESUME
 static int
 tls_server_ticket_cb(gnutls_session_t sess, u_int htype, unsigned when,
   unsigned incoming, const gnutls_datum_t * msg)
@@ -2373,11 +2429,22 @@ and sent an SMTP response. */
 
 DEBUG(D_tls) debug_printf("initialising GnuTLS as a server\n");
 
-if ((rc = tls_init(NULL, tls_certificate, tls_privatekey,
-    NULL, tls_verify_certificates, tls_crl,
-    require_ciphers, &state, &tls_in, errstr)) != OK) return rc;
+  {
+#ifdef MEASURE_TIMING
+  struct timeval t0;
+  gettimeofday(&t0, NULL);
+#endif
+
+  if ((rc = tls_init(NULL, tls_certificate, tls_privatekey,
+      NULL, tls_verify_certificates, tls_crl,
+      require_ciphers, &state, &tls_in, errstr)) != OK) return rc;
 
-#ifdef EXPERIMENTAL_TLS_RESUME
+#ifdef MEASURE_TIMING
+  report_time_since(&t0, US"server tls_init (delta)");
+#endif
+  }
+
+#ifdef EXIM_HAVE_TLS_RESUME
 tls_server_resume_prehandshake(state);
 #endif
 
@@ -2480,7 +2547,12 @@ if (rc != GNUTLS_E_SUCCESS)
   return FAIL;
   }
 
-#ifdef EXPERIMENTAL_TLS_RESUME
+#ifdef GNUTLS_SFLAGS_EXT_MASTER_SECRET
+if (gnutls_session_get_flags(state->session) & GNUTLS_SFLAGS_EXT_MASTER_SECRET)
+  tls_in.ext_master_secret = TRUE;
+#endif
+
+#ifdef EXIM_HAVE_TLS_RESUME
 tls_server_resume_posthandshake(state);
 #endif
 
@@ -2531,9 +2603,9 @@ if (verify_check_given_host(CUSS &ob->tls_verify_cert_hostnames, host) == OK)
   {
   state->exp_tls_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("TLS: server cert verification includes hostname: \"%s\".\n",
@@ -2613,7 +2685,7 @@ return TRUE;
 
 
 
-#ifdef EXPERIMENTAL_TLS_RESUME
+#ifdef EXIM_HAVE_TLS_RESUME
 /* On the client, get any stashed session for the given IP from hints db
 and apply it to the ssl-connection for attempted resumption.  Although
 there is a gnutls_session_ticket_enable_client() interface it is
@@ -2746,7 +2818,7 @@ if (gnutls_session_is_resumed(state->session))
 
 tls_save_session(tlsp, state->session, host);
 }
-#endif /* EXPERIMENTAL_TLS_RESUME */
+#endif /* !DISABLE_TLS_RESUME */
 
 
 /*************************************************
@@ -2809,10 +2881,21 @@ if (conn_args->dane && ob->dane_require_tls_ciphers)
 if (!cipher_list)
   cipher_list = ob->tls_require_ciphers;
 
-if (tls_init(host, ob->tls_certificate, ob->tls_privatekey,
-    ob->tls_sni, ob->tls_verify_certificates, ob->tls_crl,
-    cipher_list, &state, tlsp, errstr) != OK)
-  return FALSE;
+  {
+#ifdef MEASURE_TIMING
+  struct timeval t0;
+  gettimeofday(&t0, NULL);
+#endif
+
+  if (tls_init(host, ob->tls_certificate, ob->tls_privatekey,
+      ob->tls_sni, ob->tls_verify_certificates, ob->tls_crl,
+      cipher_list, &state, tlsp, errstr) != OK)
+    return FALSE;
+
+#ifdef MEASURE_TIMING
+  report_time_since(&t0, US"client tls_init (delta)");
+#endif
+  }
 
   {
   int dh_min_bits = ob->tls_dh_min_bits;
@@ -2889,7 +2972,7 @@ if (request_ocsp)
   }
 #endif
 
-#ifdef EXPERIMENTAL_TLS_RESUME
+#ifdef EXIM_HAVE_TLS_RESUME
 tls_client_resume_prehandshake(state, tlsp, host, ob);
 #endif
 
@@ -2938,6 +3021,11 @@ if (!verify_certificate(state, errstr))
   return FALSE;
   }
 
+#ifdef GNUTLS_SFLAGS_EXT_MASTER_SECRET
+if (gnutls_session_get_flags(state->session) & GNUTLS_SFLAGS_EXT_MASTER_SECRET)
+  tlsp->ext_master_secret = TRUE;
+#endif
+
 #ifndef DISABLE_OCSP
 if (request_ocsp)
   {
@@ -2984,7 +3072,7 @@ if (request_ocsp)
   }
 #endif
 
-#ifdef EXPERIMENTAL_TLS_RESUME
+#ifdef EXIM_HAVE_TLS_RESUME
 tls_client_resume_posthandshake(state, tlsp, host);
 #endif
 
@@ -3050,7 +3138,7 @@ gnutls_certificate_free_credentials(state->x509_cred);
 tlsp->active.sock = -1;
 tlsp->active.tls_ctx = NULL;
 /* Leave bits, peercert, cipher, peerdn, certificate_verified set, for logging */
-tls_channelbinding_b64 = NULL;
+tlsp->channelbinding = NULL;
 
 
 if (state->xfer_buffer) store_free(state->xfer_buffer);
@@ -3066,7 +3154,7 @@ tls_refill(unsigned lim)
 exim_gnutls_state_st * state = &state_server;
 ssize_t inbytes;
 
-DEBUG(D_tls) debug_printf("Calling gnutls_record_recv(%p, %p, %u)\n",
+DEBUG(D_tls) debug_printf("Calling gnutls_record_recv(session=%p, buffer=%p, buffersize=%u)\n",
   state->session, state->xfer_buffer, ssl_xfer_buffer_size);
 
 sigalrm_seen = FALSE;
@@ -3229,7 +3317,7 @@ if (state->xfer_buffer_lwm < state->xfer_buffer_hwm)
         state->xfer_buffer_hwm - state->xfer_buffer_lwm);
 
 DEBUG(D_tls)
-  debug_printf("Calling gnutls_record_recv(%p, %p, " SIZE_T_FMT ")\n",
+  debug_printf("Calling gnutls_record_recv(session=%p, buffer=%p, len=" SIZE_T_FMT ")\n",
       state->session, buff, len);
 
 do
@@ -3264,6 +3352,9 @@ Arguments:
   len       number of bytes
   more     more data expected soon
 
+Calling with len zero and more unset will flush buffered writes.  The buff
+argument can be null for that case.
+
 Returns:    the number of bytes after a successful write,
             -1 after a failed write
 */
@@ -3274,10 +3365,14 @@ tls_write(void * ct_ctx, const uschar * buff, size_t len, BOOL more)
 ssize_t outbytes;
 size_t left = len;
 exim_gnutls_state_st * state = ct_ctx ? ct_ctx : &state_server;
-#ifdef SUPPORT_CORK
-static BOOL corked = FALSE;
 
-if (more && !corked) gnutls_record_cork(state->session);
+#ifdef SUPPORT_CORK
+if (more && !state->corked)
+  {
+  DEBUG(D_tls) debug_printf("gnutls_record_cork(session=%p)\n", state->session);
+  gnutls_record_cork(state->session);
+  state->corked = TRUE;
+  }
 #endif
 
 DEBUG(D_tls) debug_printf("%s(%p, " SIZE_T_FMT "%s)\n", __FUNCTION__,
@@ -3285,14 +3380,15 @@ DEBUG(D_tls) debug_printf("%s(%p, " SIZE_T_FMT "%s)\n", __FUNCTION__,
 
 while (left > 0)
   {
-  DEBUG(D_tls) debug_printf("gnutls_record_send(SSL, %p, " SIZE_T_FMT ")\n",
-      buff, left);
+  DEBUG(D_tls) debug_printf("gnutls_record_send(session=%p, buffer=%p, left=" SIZE_T_FMT ")\n",
+      state->session, buff, left);
 
   do
     outbytes = gnutls_record_send(state->session, buff, left);
   while (outbytes == GNUTLS_E_AGAIN);
 
   DEBUG(D_tls) debug_printf("outbytes=" SSIZE_T_FMT "\n", outbytes);
+
   if (outbytes < 0)
     {
     DEBUG(D_tls) debug_printf("%s: gnutls_record_send err\n", __FUNCTION__);
@@ -3318,10 +3414,25 @@ if (len > INT_MAX)
   }
 
 #ifdef SUPPORT_CORK
-if (more != corked)
+if (!more && state->corked)
   {
-  if (!more) (void) gnutls_record_uncork(state->session, 0);
-  corked = more;
+  DEBUG(D_tls) debug_printf("gnutls_record_uncork(session=%p)\n", state->session);
+  do
+    /* We can't use GNUTLS_RECORD_WAIT here, as it retries on
+    GNUTLS_E_AGAIN || GNUTLS_E_INTR, which would break our timeout set by alarm().
+    The GNUTLS_E_AGAIN should not happen ever, as our sockets are blocking anyway.
+    But who knows. (That all relies on the fact that GNUTLS_E_INTR and GNUTLS_E_AGAIN
+    match the EINTR and EAGAIN errno values.) */
+    outbytes = gnutls_record_uncork(state->session, 0);
+  while (outbytes == GNUTLS_E_AGAIN);
+
+  if (outbytes < 0)
+    {
+    record_io_error(state, len, US"uncork", NULL);
+    return -1;
+    }
+
+  state->corked = FALSE;
   }
 #endif
 
@@ -3411,24 +3522,33 @@ gnutls_priority_t priority_cache;
 const char *errpos;
 uschar * dummy_errstr;
 
-#define validate_check_rc(Label) do { \
+#ifdef GNUTLS_AUTO_GLOBAL_INIT
+# define validate_check_rc(Label) do { \
+  if (rc != GNUTLS_E_SUCCESS) { if (exim_gnutls_base_init_done) \
+    return string_sprintf("%s failed: %s", (Label), gnutls_strerror(rc)); } } while (0)
+# define return_deinit(Label) do { return (Label); } while (0)
+#else
+# define validate_check_rc(Label) do { \
   if (rc != GNUTLS_E_SUCCESS) { if (exim_gnutls_base_init_done) gnutls_global_deinit(); \
-  return string_sprintf("%s failed: %s", (Label), gnutls_strerror(rc)); } } while (0)
-#define return_deinit(Label) do { gnutls_global_deinit(); return (Label); } while (0)
+    return string_sprintf("%s failed: %s", (Label), gnutls_strerror(rc)); } } while (0)
+# define return_deinit(Label) do { gnutls_global_deinit(); return (Label); } while (0)
+#endif
 
 if (exim_gnutls_base_init_done)
   log_write(0, LOG_MAIN|LOG_PANIC,
       "already initialised GnuTLS, Exim developer bug");
 
-#ifdef HAVE_GNUTLS_PKCS11
+#if defined(HAVE_GNUTLS_PKCS11) && !defined(GNUTLS_AUTO_PKCS11_MANUAL)
 if (!gnutls_allow_auto_pkcs11)
   {
   rc = gnutls_pkcs11_init(GNUTLS_PKCS11_FLAG_MANUAL, NULL);
   validate_check_rc(US"gnutls_pkcs11_init");
   }
 #endif
+#ifndef GNUTLS_AUTO_GLOBAL_INIT
 rc = gnutls_global_init();
 validate_check_rc(US"gnutls_global_init()");
+#endif
 exim_gnutls_base_init_done = TRUE;
 
 if (!(tls_require_ciphers && *tls_require_ciphers))
@@ -3451,7 +3571,9 @@ validate_check_rc(string_sprintf(
 
 #undef return_deinit
 #undef validate_check_rc
+#ifndef GNUTLS_AUTO_GLOBAL_INIT
 gnutls_global_deinit();
+#endif
 
 return NULL;
 }