]> git.ipfire.org Git - thirdparty/curl.git/commitdiff
ssl: read pending close notify alert before closing the connection
authorMichael Kaufmann <mail@michael-kaufmann.ch>
Tue, 18 May 2021 09:34:02 +0000 (11:34 +0200)
committerMichael Kaufmann <mail@michael-kaufmann.ch>
Tue, 1 Jun 2021 07:40:40 +0000 (09:40 +0200)
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
lib/vtls/mbedtls.c
lib/vtls/nss.c
lib/vtls/openssl.c
lib/vtls/wolfssl.c

index ecde5c44deeb5572a03e00452cf0f592feccaaf3..d9bc5611e8f98c310014ad2e4edcc55e800e9974 100644 (file)
@@ -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;
index 335b585270082b7909baafe558a83d787578e6ab..addbff5c24188053917a25d6765c9af50af2e4b8 100644 (file)
@@ -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);
index 1582b1e580a9094624b1f7186f10c1e286ea08a6..f7583d5043917fe2505d6b49ac571e8a4ba62840 100644 (file)
@@ -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;
 
index ebd7abc3b4ac016dc6506165f9c765273beb4a1b..c8958e0f69349d856191b02a813a3cc771564caa 100644 (file)
@@ -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);
 
index 60e27e366252f1bc84b8610ab8f0e1f7bd187419..8d217ec54e6ab74e42a27e26b666334e9c4520b8 100644 (file)
@@ -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;