GnuTLS: remove GNUTLS_E_AGAIN handling
[exim.git] / src / src / tls-gnu.c
index 7b0f2f6adb01248dd0b4c273d2c6ba0a2104d459..cf899454f69b512f1ccfe142dec71b5b5d715651 100644 (file)
@@ -53,6 +53,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
@@ -74,6 +77,12 @@ require current GnuTLS, then we'll drop support for the ancient libraries).
 # 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
@@ -127,6 +136,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_OCSP
+builtin_macro_create(US"_HAVE_TLS_OCSP");
+# endif
+# ifdef SUPPORT_SRV_OCSP_STACK
+builtin_macro_create(US"_HAVE_TLS_OCSP_LIST");
+# endif
 }
 #else
 
@@ -165,10 +180,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;
@@ -201,8 +223,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 = {
@@ -807,13 +827,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
@@ -1927,7 +1953,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);
@@ -2271,17 +2297,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);
@@ -2290,11 +2316,13 @@ 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
 }
 
@@ -2485,7 +2513,7 @@ sigalrm_seen = FALSE;
 if (smtp_receive_timeout > 0) ALARM(smtp_receive_timeout);
 do
   rc = gnutls_handshake(state->session);
-while (rc == GNUTLS_E_AGAIN ||  rc == GNUTLS_E_INTERRUPTED && !sigalrm_seen);
+while (rc == GNUTLS_E_INTERRUPTED && !sigalrm_seen);
 ALARM_CLR(0);
 
 if (rc != GNUTLS_E_SUCCESS)
@@ -2516,6 +2544,11 @@ if (rc != GNUTLS_E_SUCCESS)
   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
@@ -2960,7 +2993,7 @@ sigalrm_seen = FALSE;
 ALARM(ob->command_timeout);
 do
   rc = gnutls_handshake(state->session);
-while (rc == GNUTLS_E_AGAIN || rc == GNUTLS_E_INTERRUPTED && !sigalrm_seen);
+while (rc == GNUTLS_E_INTERRUPTED && !sigalrm_seen);
 ALARM_CLR(0);
 
 if (rc != GNUTLS_E_SUCCESS)
@@ -2985,6 +3018,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)
   {
@@ -3113,16 +3151,13 @@ 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;
 if (smtp_receive_timeout > 0) ALARM(smtp_receive_timeout);
 
-do
-  inbytes = gnutls_record_recv(state->session, state->xfer_buffer,
-    MIN(ssl_xfer_buffer_size, lim));
-while (inbytes == GNUTLS_E_AGAIN);
+inbytes = gnutls_record_recv(state->session, state->xfer_buffer, MIN(ssl_xfer_buffer_size, lim));
 
 if (smtp_receive_timeout > 0) ALARM_CLR(0);
 
@@ -3276,12 +3311,10 @@ 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
-  inbytes = gnutls_record_recv(state->session, buff, len);
-while (inbytes == GNUTLS_E_AGAIN);
+inbytes = gnutls_record_recv(state->session, buff, len);
 
 if (inbytes > 0) return inbytes;
 if (inbytes == 0)
@@ -3324,10 +3357,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__,
@@ -3335,14 +3372,13 @@ 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);
+  outbytes = gnutls_record_send(state->session, buff, left);
 
   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__);
@@ -3368,10 +3404,18 @@ if (len > INT_MAX)
   }
 
 #ifdef SUPPORT_CORK
-if (more != corked)
-  {
-  if (!more) (void) gnutls_record_uncork(state->session, 0);
-  corked = more;
+if (!more && state->corked)
+  {
+  DEBUG(D_tls) debug_printf("gnutls_record_uncork(session=%p)\n", state->session);
+  do {
+      outbytes = gnutls_record_uncork(state->session, 0);
+     if (outbytes < 0)
+       {
+       record_io_error(state, len, US"uncork", NULL);
+       return -1;
+       }
+  } while (gnutls_record_check_corked(state->session) > 0);
+  state->corked = FALSE;
   }
 #endif