Hi,
 It seems that wget does not enable/use post-handshake authentication
with gnutls when running under TLS1.3.

The enabling of TLS1.3 although transparent for all uses cases, is not
for the use case where the server allows a client to connect without
certificate but requests authentication later after the location of
access is known. Under TLS1.2 this was working via a re-handshake, but
under TLS1.3 a client must enable and perform post-handshake
authentication instead.

A quick and dirty patch to demonstrate how to enable it, is attached.
If you wait until gnutls 3.6.5, there may be a simpler way to enable
it:
https://gitlab.com/gnutls/gnutls/merge_requests/766


More info at:
https://nikmav.blogspot.com/2018/05/gnutls-and-tls-13.html

regards,
Nikos

From 04f89c1ae3c169b20bca77e69a1cb7333f18cb2c Mon Sep 17 00:00:00 2001
From: Nikos Mavrogiannopoulos <[email protected]>
Date: Mon, 8 Oct 2018 10:42:22 +0200
Subject: [PATCH] Enable post-handshake auth under gnutls on TLS1.3

---
 src/gnutls.c | 95 +++++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 94 insertions(+), 1 deletion(-)

diff --git a/src/gnutls.c b/src/gnutls.c
index 206d0b09..3c3cd381 100644
--- a/src/gnutls.c
+++ b/src/gnutls.c
@@ -60,6 +60,9 @@ as that of the covered work.  */
 static int
 _do_handshake (gnutls_session_t session, int fd, double timeout);
 
+static int
+_do_reauth (gnutls_session_t session, int fd, double timeout);
+
 static int
 key_type_to_gnutls_type (enum keyfile_type type)
 {
@@ -287,6 +290,14 @@ wgnutls_read_timeout (int fd, char *buf, int bufsize, void *arg, double timeout)
               if ((ret = _do_handshake (ctx->session, fd, timeout)) == 0)
                 ret = GNUTLS_E_AGAIN; /* restart reading */
             }
+#if GNUTLS_VERSION_NUMBER >= 0x030604
+          if (!timed_out && ret == GNUTLS_E_REAUTH_REQUEST)
+            {
+              DEBUGP (("GnuTLS: *** re-authentication while reading\n"));
+              if ((ret = _do_reauth (ctx->session, fd, timeout)) == 0)
+                ret = GNUTLS_E_AGAIN; /* restart reading */
+            }
+#endif
         }
     }
   while (ret == GNUTLS_E_INTERRUPTED || (ret == GNUTLS_E_AGAIN && !timed_out));
@@ -519,6 +530,82 @@ _do_handshake (gnutls_session_t session, int fd, double timeout)
   return err;
 }
 
+static int
+_do_reauth (gnutls_session_t session, int fd, double timeout)
+{
+#ifdef F_GETFL
+  int flags = 0;
+#endif
+  int err;
+
+  if (timeout)
+    {
+#ifdef F_GETFL
+      flags = fcntl (fd, F_GETFL, 0);
+      if (flags < 0)
+        return flags;
+      if (fcntl (fd, F_SETFL, flags | O_NONBLOCK))
+        return -1;
+#else
+      /* XXX: Assume it was blocking before.  */
+      const int one = 1;
+      if (ioctl (fd, FIONBIO, &one) < 0)
+        return -1;
+#endif
+    }
+
+  /* We don't stop the handshake process for non-fatal errors */
+  do
+    {
+      err = gnutls_reauth (session);
+
+      if (timeout && err == GNUTLS_E_AGAIN)
+        {
+          if (gnutls_record_get_direction (session))
+            {
+              /* wait for writeability */
+              err = select_fd (fd, timeout, WAIT_FOR_WRITE);
+            }
+          else
+            {
+              /* wait for readability */
+              err = select_fd (fd, timeout, WAIT_FOR_READ);
+            }
+
+          if (err <= 0)
+            {
+              if (err == 0)
+                {
+                  errno = ETIMEDOUT;
+                  err = -1;
+                }
+              break;
+            }
+
+           err = GNUTLS_E_AGAIN;
+        }
+      else if (err < 0)
+        {
+          logprintf (LOG_NOTQUIET, "GnuTLS: %s\n", gnutls_strerror (err));
+        }
+    }
+  while (err && gnutls_error_is_fatal (err) == 0);
+
+  if (timeout)
+    {
+#ifdef F_GETFL
+      if (fcntl (fd, F_SETFL, flags) < 0)
+        return -1;
+#else
+      const int zero = 0;
+      if (ioctl (fd, FIONBIO, &zero) < 0)
+        return -1;
+#endif
+    }
+
+  return err;
+}
+
 static const char *
 _sni_hostname(const char *hostname)
 {
@@ -648,6 +735,12 @@ set_prio_default (gnutls_session_t session)
   return err;
 }
 
+#if GNUTLS_VERSION_NUMBER >= 0x030604
+#define DEFAULT_FLAGS (GNUTLS_CLIENT|GNUTLS_POST_HANDSHAKE_AUTH)
+#else
+#define DEFAULT_FLAGS GNUTLS_CLIENT
+#endif
+
 bool
 ssl_connect_wget (int fd, const char *hostname, int *continue_session)
 {
@@ -655,7 +748,7 @@ ssl_connect_wget (int fd, const char *hostname, int *continue_session)
   gnutls_session_t session;
   int err;
 
-  gnutls_init (&session, GNUTLS_CLIENT);
+  gnutls_init (&session, DEFAULT_FLAGS);
 
   /* We set the server name but only if it's not an IP address. */
   if (! is_valid_ip_address (hostname))
-- 
2.17.1

Reply via email to