GSASL channel-binding: TLS resumption checks
[exim.git] / src / src / tls-gnu.c
index 22f7fe5489434b3980812e4fa363500a4d7179d5..69a8bd6f41fb510106e42c60b57b7f2c202d21ef 100644 (file)
@@ -70,12 +70,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 >= 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 >= 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
 #if GNUTLS_VERSION_NUMBER >= 0x030603
 # define EXIM_HAVE_TLS1_3
 # define SUPPORT_GNUTLS_EXT_RAW_PARSE
@@ -120,6 +133,12 @@ builtin_macro_create_var(US"_RESUME_DECODE", RESUME_DECODE_STRING );
 # ifdef EXIM_HAVE_TLS1_3
 builtin_macro_create(US"_HAVE_TLS1_3");
 # 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
 
 }
 #else
 
@@ -148,7 +167,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.
 
 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 {
 */
 
 typedef struct exim_gnutls_state {
@@ -217,11 +236,13 @@ XXX But see gnutls_session_get_ptr()
 
 static exim_gnutls_state_st state_server;
 
 
 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;
 /* 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 */
 
 
 static int ssl_session_timeout = 7200; /* Two hours */
 
@@ -458,7 +479,8 @@ Sets:
   tls_active                fd
   tls_bits                  strength indicator
   tls_certificate_verified  bool indicator
   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
   tls_cipher                a string
   tls_peercert              pointer to library internal
   tls_peerdn                a string
@@ -489,10 +511,10 @@ tlsp->certificate_verified = state->peer_cert_verified;
 tlsp->dane_verified = state->peer_dane_verified;
 #endif
 
 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. */
 
 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;
 #ifdef HAVE_GNUTLS_SESSION_CHANNEL_BINDING
 channel.data = NULL;
 channel.size = 0;
@@ -500,11 +522,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
   {
   { 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;
   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;
   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
 
   }
 #endif
 
@@ -524,6 +550,7 @@ tlsp->sni =    state->received_sni;
 
 
 
 
 
 
+#ifndef GNUTLS_AUTO_DHPARAMS
 /*************************************************
 *            Setup up DH parameters              *
 *************************************************/
 /*************************************************
 *            Setup up DH parameters              *
 *************************************************/
@@ -546,7 +573,7 @@ init_server_dh(uschar ** errstr)
 {
 int fd, rc;
 unsigned int dh_bits;
 {
 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;
 uschar filename_buf[PATH_MAX];
 uschar *filename = NULL;
 size_t sz;
@@ -559,9 +586,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);
 
 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;
 
 if (!expand_check(tls_dhparam, US"tls_dhparam", &exp_tls_dhparam, errstr))
   return DEFER;
 
@@ -711,14 +735,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 */
 
     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;
   if (dh_bits >= EXIM_CLIENT_DH_MIN_BITS + 10)
     {
     dh_bits_gen = dh_bits - 10;
@@ -781,6 +803,7 @@ if (rc < 0)
 DEBUG(D_tls) debug_printf("initialized server D-H parameters\n");
 return OK;
 }
 DEBUG(D_tls) debug_printf("initialized server D-H parameters\n");
 return OK;
 }
+#endif
 
 
 
 
 
 
@@ -1395,6 +1418,7 @@ tls_set_remaining_x509(exim_gnutls_state_st *state, uschar ** errstr)
 int rc;
 const host_item *host = state->host;  /* macro should be reconsidered? */
 
 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. */
 /* 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 +1427,11 @@ if (!state->host)
   {
   if (!dh_server_params)
     if ((rc = init_server_dh(errstr)) != OK) return rc;
   {
   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);
   }
   gnutls_certificate_set_dh_params(state->x509_cred, dh_server_params);
   }
+#endif
 
 /* Link the credentials to the session. */
 
 
 /* Link the credentials to the session. */
 
@@ -1491,7 +1518,7 @@ if (!exim_gnutls_base_init_done)
   {
   DEBUG(D_tls) debug_printf("GnuTLS global init required.\n");
 
   {
   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
   /* 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 +1529,10 @@ if (!exim_gnutls_base_init_done)
       return tls_error_gnu(US"gnutls_pkcs11_init", rc, host, errstr);
 #endif
 
       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);
   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)
 
 #if EXIM_GNUTLS_LIBRARY_LOG_LEVEL >= 0
   DEBUG(D_tls)
@@ -1742,11 +1771,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);
     /* 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))
       {
     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 */
       /* now on ) closing group */
       if ((c = *s) && *++s == '-') g = string_catn(g, US"__", 2);
       /* now on _ between groups */
@@ -1766,6 +1801,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 = '-';
   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); */
 #endif
 
 /* debug_printf("peer_status: ciphersuite %s\n", state->ciphersuite); */
@@ -2246,17 +2283,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_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)
 # ifdef EXIM_HAVE_TLS1_3
 if (gnutls_protocol_get_version(state->session) < GNUTLS_TLS1_3)
-#else
+# else
 if (TRUE)
 if (TRUE)
-#endif
+# endif
   {
   gnutls_datum_t c, s;
   gstring * gc, * gs;
   {
   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);
   gnutls_session_get_random(state->session, &c, &s);
   gnutls_session_get_master_secret(state->session, &s);
   gc = ddump(&c);
@@ -2269,7 +2306,8 @@ else
     " 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"
     " 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");
+    " (works for TLS1.2 also, and saves cut-paste into file)"
+    " Trying to use add_environment for this will not work\n");
 #endif
 }
 
 #endif
 }
 
@@ -2373,9 +2411,20 @@ and sent an SMTP response. */
 
 DEBUG(D_tls) debug_printf("initialising GnuTLS as a server\n");
 
 
 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 MEASURE_TIMING
+  report_time_since(&t0, US"server tls_init (delta)");
+#endif
+  }
 
 #ifdef EXPERIMENTAL_TLS_RESUME
 tls_server_resume_prehandshake(state);
 
 #ifdef EXPERIMENTAL_TLS_RESUME
 tls_server_resume_prehandshake(state);
@@ -2480,6 +2529,9 @@ if (rc != GNUTLS_E_SUCCESS)
   return FAIL;
   }
 
   return FAIL;
   }
 
+if (gnutls_session_get_flags(state->session) & GNUTLS_SFLAGS_EXT_MASTER_SECRET)
+  tls_in.ext_master_secret = TRUE;
+
 #ifdef EXPERIMENTAL_TLS_RESUME
 tls_server_resume_posthandshake(state);
 #endif
 #ifdef EXPERIMENTAL_TLS_RESUME
 tls_server_resume_posthandshake(state);
 #endif
@@ -2809,10 +2861,21 @@ if (conn_args->dane && ob->dane_require_tls_ciphers)
 if (!cipher_list)
   cipher_list = ob->tls_require_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;
 
   {
   int dh_min_bits = ob->tls_dh_min_bits;
@@ -2938,6 +3001,9 @@ if (!verify_certificate(state, errstr))
   return FALSE;
   }
 
   return FALSE;
   }
 
+if (gnutls_session_get_flags(state->session) & GNUTLS_SFLAGS_EXT_MASTER_SECRET)
+  tlsp->ext_master_secret = TRUE;
+
 #ifndef DISABLE_OCSP
 if (request_ocsp)
   {
 #ifndef DISABLE_OCSP
 if (request_ocsp)
   {
@@ -3050,7 +3116,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 */
 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);
 
 
 if (state->xfer_buffer) store_free(state->xfer_buffer);
@@ -3264,6 +3330,9 @@ Arguments:
   len       number of bytes
   more     more data expected soon
 
   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
 */
 Returns:    the number of bytes after a successful write,
             -1 after a failed write
 */
@@ -3411,24 +3480,33 @@ gnutls_priority_t priority_cache;
 const char *errpos;
 uschar * dummy_errstr;
 
 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(); \
   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");
 
 
 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
 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()");
 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))
 exim_gnutls_base_init_done = TRUE;
 
 if (!(tls_require_ciphers && *tls_require_ciphers))
@@ -3451,7 +3529,9 @@ validate_check_rc(string_sprintf(
 
 #undef return_deinit
 #undef validate_check_rc
 
 #undef return_deinit
 #undef validate_check_rc
+#ifndef GNUTLS_AUTO_GLOBAL_INIT
 gnutls_global_deinit();
 gnutls_global_deinit();
+#endif
 
 return NULL;
 }
 
 return NULL;
 }