From a33ec92f2ecf02765cb3b8f64dc863bb9260413a Mon Sep 17 00:00:00 2001 From: joe Date: Tue, 25 Sep 2018 15:19:29 +0000 Subject: * src/ne_socket.c (error_ossl): Return NE_SOCK_RETRY for SSL_ERROR_WANT_READ. (read_ossl, readable_ossl): Update for OpenSSL 1.1.1, block/timeout properly even if handshake data is received. git-svn-id: http://svn.webdav.org/repos/projects/neon/trunk@2025 61a7d7f5-40b7-0310-9c16-bb0ea8cb1845 --- src/ne_socket.c | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 57 insertions(+), 7 deletions(-) diff --git a/src/ne_socket.c b/src/ne_socket.c index 406ed0b..999b63a 100644 --- a/src/ne_socket.c +++ b/src/ne_socket.c @@ -179,6 +179,9 @@ typedef struct in_addr ne_inet_addr; /* Socket read timeout */ #define SOCKET_READ_TIMEOUT 120 +/* Internal read retry value */ +#define NE_SOCK_RETRY (-6) + /* Critical I/O functions on a socket: useful abstraction for easily * handling SSL I/O alongside raw socket I/O. */ struct iofns { @@ -599,13 +602,50 @@ static ssize_t writev_dummy(ne_socket *sock, const struct ne_iovec *vector, int static const struct iofns iofns_raw = { read_raw, write_raw, readable_raw, writev_raw }; +static int error_ossl(ne_socket *sock, int sret); + #ifdef HAVE_OPENSSL /* OpenSSL I/O function implementations. */ static int readable_ossl(ne_socket *sock, int secs) { +#if OPENSSL_VERSION_NUMBER < 0x10101000L + /* Sufficient for TLSv1.2 and earlier. */ if (SSL_pending(sock->ssl)) return 0; return readable_raw(sock, secs); +#else + /* TLSv1.3 sends a lot more handshake data so the presence of data + * on the socket - i.e. poll() returning 1, is an insufficient + * test for app-data readability. */ + char pending; + int ret; + size_t bytes; + + /* Loop while no app data is pending, each time attempting a one + * byte peek, and retrying the poll if that fails due to absence + * of app data. */ + while (!SSL_pending(sock->ssl)) { + ret = readable_raw(sock, secs); + if (ret == NE_SOCK_TIMEOUT) { + return ret; + } + + ret = SSL_peek_ex(sock->ssl, &pending, 1, &bytes); + if (ret) { + /* App data definitely available. */ + break; + } + else { + /* If this gave SSL_ERROR_WANT_READ, loop and probably + * block again, else some other error happened. */ + ret = error_ossl(sock, ret); + if (ret != NE_SOCK_RETRY) + return ret; + } + } + + return 0; +#endif /* OPENSSL_VERSION_NUMBER < 1.1.1 */ } /* SSL error handling, according to SSL_get_error(3). */ @@ -618,6 +658,10 @@ static int error_ossl(ne_socket *sock, int sret) set_error(sock, _("Connection closed")); return NE_SOCK_CLOSED; } + else if (errnum == SSL_ERROR_WANT_READ) { + set_error(sock, _("Retry operation")); + return NE_SOCK_RETRY; + } /* for all other errors, look at the OpenSSL error stack */ err = ERR_get_error(); @@ -656,13 +700,15 @@ static ssize_t read_ossl(ne_socket *sock, char *buffer, size_t len) { int ret; - ret = readable_ossl(sock, sock->rdtimeout); - if (ret) return ret; - - ret = SSL_read(sock->ssl, buffer, CAST2INT(len)); - if (ret <= 0) - ret = error_ossl(sock, ret); - + do { + ret = readable_ossl(sock, sock->rdtimeout); + if (ret) return ret; + + ret = SSL_read(sock->ssl, buffer, CAST2INT(len)); + if (ret <= 0) + ret = error_ossl(sock, ret); + } while (ret == NE_SOCK_RETRY); + return ret; } @@ -1761,7 +1807,11 @@ int ne_sock_connect_ssl(ne_socket *sock, ne_ssl_context *ctx, void *userdata) } SSL_set_app_data(ssl, userdata); +#if OPENSSL_VERSION_NUMBER < 0x10101000L SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY); +#else + SSL_clear_mode(ssl, SSL_MODE_AUTO_RETRY); +#endif SSL_set_fd(ssl, sock->fd); sock->ops = &iofns_ssl; -- cgit v1.2.1