Subject: bug#34102: [staging] Guix fails to download from TLSv1.3-enabled servers

From: Ludovic Courtès <[email-masked]>
Date: Wed, 12 Jun 2019 14:34:44 +0200

Hi Marius,

Marius Bakke <[email-masked]> skribis:

> $ ./pre-inst-env guix download https://data.iana.org
> Starting download of /tmp/guix-file.vJ4v7h
> From https://data.iana.org...
> Throw to key `gnutls-error' with args `(#<gnutls-error-enum Resource temporarily unavailable, try again.> read_from_session_record_port)'.
> failed to download "/tmp/guix-file.vJ4v7h" from "https://data.iana.org"
> guix download: error: https://data.iana.org: download failed
>
> The GnuTLS maintainer have written a blog post about TLS 1.3 porting[0],
> and I suspect the problem is that Guix (or the GnuTLS Guile bindings)
> does not handle the "GNUTLS_E_REAUTH_REQUEST" error code; however my
> attempts at catching it (or any error code) has been unfruitful.
>
> This is an obvious merge blocker, help wanted!  Disabling TLS1.3 in the
> priority string works as a last-resort workaround.
>
> [0] https://nikmav.blogspot.com/2018/05/gnutls-and-tls-13.html

I’ve submitted a bunch of changes upstream to better support
post-handshake re-authentication:

  https://gitlab.com/gnutls/gnutls/merge_requests/1026

In particular, this adds ‘connection-flag/post-handshake-auth’ and
‘connection-flag/auto-reauth’, which can be passed to ‘make-session’.

But as it turns out, there’s one patch that, alone, appears to fix the
issue above:

  https://gitlab.com/civodul/gnutls/commit/7421ca2cfd2d9f4ac89bdec786eb745533430316

Ideally we’d wait for the next GnuTLS release that includes all of this.
However, if that helps, we can apply this patch to the ‘gnutls’ package
in ‘core-updates’ in the meantime.

WDYT?

Ludo’.

commit 7421ca2cfd2d9f4ac89bdec786eb745533430316
Author: Ludovic Courtès <[email-masked]>
Date:   Wed Jun 12 11:32:19 2019 +0200

    guile: Loop upon EAGAIN or EINTR.
    
    * guile/src/core.c (do_fill_port) [USING_GUILE_BEFORE_2_2]: Loop while
    'gnutls_record_recv' returns GNUTLS_E_AGAIN or GNUTLS_E_INTERRUPTED.
    (read_from_session_record_port) [!USING_GUILE_BEFORE_2_2]: Likewise.
    
    Signed-off-by: Ludovic Courtès <[email-masked]>

diff --git a/guile/src/core.c b/guile/src/core.c
index 546d63a1e3..8b9aa62560 100644
--- a/guile/src/core.c
+++ b/guile/src/core.c
@@ -1,5 +1,5 @@
 /* GnuTLS --- Guile bindings for GnuTLS.
-   Copyright (C) 2007-2014, 2016 Free Software Foundation, Inc.
+   Copyright (C) 2007-2014, 2016, 2019 Free Software Foundation, Inc.
 
    GnuTLS is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
@@ -869,8 +869,12 @@ do_fill_port (void *data)
   const fill_port_data_t *args = (fill_port_data_t *) data;
 
   c_port = args->c_port;
-  result = gnutls_record_recv (args->c_session,
-                               c_port->read_buf, c_port->read_buf_size);
+
+  do
+    result = gnutls_record_recv (args->c_session,
+				 c_port->read_buf, c_port->read_buf_size);
+  while (result == GNUTLS_E_AGAIN || result == GNUTLS_E_INTERRUPTED);
+
   if (EXPECT_TRUE (result > 0))
     {
       c_port->read_pos = c_port->read_buf;
@@ -1002,7 +1006,12 @@ read_from_session_record_port (SCM port, SCM dst, size_t start, size_t count)
 
   /* XXX: Leave guile mode when SCM_GNUTLS_SESSION_TRANSPORT_IS_FD is
      true?  */
-  result = gnutls_record_recv (c_session, read_buf, count);
+  /* We can get EAGAIN for example if we received a reauth request, even when
+     GNUTLS_AUTO_REAUTH is set.  In that case, loop again.  */
+  do
+    result = gnutls_record_recv (c_session, read_buf, count);
+  while (result == GNUTLS_E_AGAIN || result == GNUTLS_E_INTERRUPTED);
+
   if (EXPECT_FALSE (result < 0))
     /* FIXME: Silently swallowed! */
     scm_gnutls_error (result, FUNC_NAME);