GnuTLS: fix hanging callout connections
[exim.git] / src / src / tls-gnu.c
index eaec862aad8a7ae217ad0dc7942555335731ce2e..2e69c59369fdbd3f2743380d8454b51a1e0b24ff 100644 (file)
@@ -74,6 +74,12 @@ require current GnuTLS, then we'll drop support for the ancient libraries).
 # define GNUTLS_AUTO_GLOBAL_INIT
 # define GNUTLS_AUTO_PKCS11_MANUAL
 #endif
 # 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 >= 0x030500
 # define SUPPORT_GNUTLS_KEYLOG
 #endif
@@ -127,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
 
@@ -155,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 {
@@ -165,10 +177,17 @@ typedef struct exim_gnutls_state {
   enum peer_verify_requirement verify_requirement;
   int                  fd_in;
   int                  fd_out;
   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;
   const struct host_item *host;                /* NULL if server */
   gnutls_x509_crt_t    peercert;
   uschar               *peerdn;
@@ -201,8 +220,6 @@ typedef struct exim_gnutls_state {
   uschar *xfer_buffer;
   int xfer_buffer_lwm;
   int xfer_buffer_hwm;
   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 = {
 } exim_gnutls_state_st;
 
 static const exim_gnutls_state_st exim_gnutls_state_init = {
@@ -467,7 +484,7 @@ 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_ver                   a string
   tls_cipher                a string
   tls_peercert              pointer to library internal
@@ -499,10 +516,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;
@@ -510,11 +527,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
 
@@ -1755,12 +1776,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);
     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 */
@@ -1918,7 +1944,7 @@ else
       const char ** dd;
       int * ddl;
 
       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);
       nrec++;
 
       dd = store_get(nrec * sizeof(uschar *), FALSE);
@@ -2262,17 +2288,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);
@@ -2285,7 +2311,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
 }
 
@@ -2507,6 +2534,11 @@ if (rc != GNUTLS_E_SUCCESS)
   return FAIL;
   }
 
   return FAIL;
   }
 
+#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 EXPERIMENTAL_TLS_RESUME
 tls_server_resume_posthandshake(state);
 #endif
 #ifdef EXPERIMENTAL_TLS_RESUME
 tls_server_resume_posthandshake(state);
 #endif
@@ -2976,6 +3008,11 @@ if (!verify_certificate(state, errstr))
   return FALSE;
   }
 
   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)
   {
 #ifndef DISABLE_OCSP
 if (request_ocsp)
   {
@@ -3088,7 +3125,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);
@@ -3302,6 +3339,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
 */
@@ -3313,9 +3353,8 @@ ssize_t outbytes;
 size_t left = len;
 exim_gnutls_state_st * state = ct_ctx ? ct_ctx : &state_server;
 #ifdef SUPPORT_CORK
 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);
+if (more && !state->corked) gnutls_record_cork(state->session);
 #endif
 
 DEBUG(D_tls) debug_printf("%s(%p, " SIZE_T_FMT "%s)\n", __FUNCTION__,
 #endif
 
 DEBUG(D_tls) debug_printf("%s(%p, " SIZE_T_FMT "%s)\n", __FUNCTION__,
@@ -3323,8 +3362,8 @@ DEBUG(D_tls) debug_printf("%s(%p, " SIZE_T_FMT "%s)\n", __FUNCTION__,
 
 while (left > 0)
   {
 
 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(%p, %p, " SIZE_T_FMT ")\n",
+      state->session, buff, left);
 
   do
     outbytes = gnutls_record_send(state->session, buff, left);
 
   do
     outbytes = gnutls_record_send(state->session, buff, left);
@@ -3356,10 +3395,10 @@ if (len > INT_MAX)
   }
 
 #ifdef SUPPORT_CORK
   }
 
 #ifdef SUPPORT_CORK
-if (more != corked)
+if (more != state->corked)
   {
   if (!more) (void) gnutls_record_uncork(state->session, 0);
   {
   if (!more) (void) gnutls_record_uncork(state->session, 0);
-  corked = more;
+  state->corked = more;
   }
 #endif
 
   }
 #endif