From b249592d29ae0a2b3e8e07fdbc01f33b5a5b8420 Mon Sep 17 00:00:00 2001 From: Michael Kaufmann Date: Tue, 18 May 2021 11:34:02 +0200 Subject: [PATCH] ssl: read pending close notify alert before closing the connection This avoids a TCP reset (RST) if the server initiates a connection shutdown by sending an SSL close notify alert and then closes the TCP connection. For SSL connections, usually the server announces that it will close the connection with an SSL close notify alert. curl should read this alert. If curl does not read this alert and just closes the connection, some operating systems close the TCP connection with an RST flag. See RFC 1122, section 4.2.2.13 If curl reads the close notify alert, the TCP connection is closed normally with a FIN flag. The new code is similar to existing code in the "SSL shutdown" function: try to read an alert (non-blocking), and ignore any read errors. Closes #7095 --- lib/vtls/gtls.c | 4 ++++ lib/vtls/mbedtls.c | 7 ++++++- lib/vtls/nss.c | 8 ++++++++ lib/vtls/openssl.c | 6 ++++++ lib/vtls/wolfssl.c | 4 ++++ 5 files changed, 28 insertions(+), 1 deletion(-) diff --git a/lib/vtls/gtls.c b/lib/vtls/gtls.c index ecde5c44d..d9bc5611e 100644 --- a/lib/vtls/gtls.c +++ b/lib/vtls/gtls.c @@ -1438,6 +1438,10 @@ static void close_one(struct ssl_connect_data *connssl) { struct ssl_backend_data *backend = connssl->backend; if(backend->session) { + char buf[32]; + /* Maybe the server has already sent a close notify alert. + Read it to avoid an RST on the TCP connection. */ + (void)gnutls_record_recv(backend->session, buf, sizeof(buf)); gnutls_bye(backend->session, GNUTLS_SHUT_WR); gnutls_deinit(backend->session); backend->session = NULL; diff --git a/lib/vtls/mbedtls.c b/lib/vtls/mbedtls.c index 335b58527..addbff5c2 100644 --- a/lib/vtls/mbedtls.c +++ b/lib/vtls/mbedtls.c @@ -813,8 +813,13 @@ static void mbedtls_close(struct Curl_easy *data, { struct ssl_connect_data *connssl = &conn->ssl[sockindex]; struct ssl_backend_data *backend = connssl->backend; - + char buf[32]; (void) data; + + /* Maybe the server has already sent a close notify alert. + Read it to avoid an RST on the TCP connection. */ + (void)mbedtls_ssl_read(&backend->ssl, (unsigned char *)buf, sizeof(buf)); + mbedtls_pk_free(&backend->pk); mbedtls_x509_crt_free(&backend->clicert); mbedtls_x509_crt_free(&backend->cacert); diff --git a/lib/vtls/nss.c b/lib/vtls/nss.c index 1582b1e58..f7583d504 100644 --- a/lib/vtls/nss.c +++ b/lib/vtls/nss.c @@ -1546,6 +1546,14 @@ static void close_one(struct ssl_connect_data *connssl) const bool client_cert = (backend->client_nickname != NULL) || (backend->obj_clicert != NULL); + if(backend->handle) { + char buf[32]; + /* Maybe the server has already sent a close notify alert. + Read it to avoid an RST on the TCP connection. */ + (void)PR_Recv(backend->handle, buf, (int)sizeof(buf), 0, + PR_INTERVAL_NO_WAIT); + } + free(backend->client_nickname); backend->client_nickname = NULL; diff --git a/lib/vtls/openssl.c b/lib/vtls/openssl.c index ebd7abc3b..c8958e0f6 100644 --- a/lib/vtls/openssl.c +++ b/lib/vtls/openssl.c @@ -1400,7 +1400,13 @@ static void ossl_closeone(struct Curl_easy *data, { struct ssl_backend_data *backend = connssl->backend; if(backend->handle) { + char buf[32]; set_logger(conn, data); + + /* Maybe the server has already sent a close notify alert. + Read it to avoid an RST on the TCP connection. */ + (void)SSL_read(backend->handle, buf, (int)sizeof(buf)); + (void)SSL_shutdown(backend->handle); SSL_set_connect_state(backend->handle); diff --git a/lib/vtls/wolfssl.c b/lib/vtls/wolfssl.c index 60e27e366..8d217ec54 100644 --- a/lib/vtls/wolfssl.c +++ b/lib/vtls/wolfssl.c @@ -810,6 +810,10 @@ static void wolfssl_close(struct Curl_easy *data, struct connectdata *conn, (void) data; if(backend->handle) { + char buf[32]; + /* Maybe the server has already sent a close notify alert. + Read it to avoid an RST on the TCP connection. */ + (void)SSL_read(backend->handle, buf, (int)sizeof(buf)); (void)SSL_shutdown(backend->handle); SSL_free(backend->handle); backend->handle = NULL;