From 04f89c1ae3c169b20bca77e69a1cb7333f18cb2c Mon Sep 17 00:00:00 2001 From: Nikos Mavrogiannopoulos 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