]> git.ipfire.org Git - thirdparty/curl.git/commitdiff
wolfssl: improve shutdown handling
authorStefan Eissing <stefan@eissing.org>
Sun, 4 Aug 2024 11:22:03 +0000 (13:22 +0200)
committerDaniel Stenberg <daniel@haxx.se>
Mon, 5 Aug 2024 06:49:20 +0000 (08:49 +0200)
Improve handling of shutdown when sending gets blocked.

Add workaround for <https://github.com/wolfSSL/wolfssl/issues/7784>
where wolfSSL keeps on adding close notify messages to its outgoing
buffer on ever attempt.

Closes #14376

lib/vtls/wolfssl.c
lib/vtls/wolfssl.h

index ed5005c881ef313cc203d9cc151b7624d80f9ab4..4f777dbe6f4dea3874ce339709cd5b74c39d7441 100644 (file)
@@ -287,17 +287,32 @@ static int wolfssl_bio_cf_out_write(WOLFSSL_BIO *bio,
   struct wolfssl_ctx *backend =
     (struct wolfssl_ctx *)connssl->backend;
   struct Curl_easy *data = CF_DATA_CURRENT(cf);
-  ssize_t nwritten;
+  ssize_t nwritten, skiplen = 0;
   CURLcode result = CURLE_OK;
 
   DEBUGASSERT(data);
+  if(backend->shutting_down && backend->io_send_blocked_len &&
+     (backend->io_send_blocked_len < blen)) {
+    /* bug in wolfSSL: <https://github.com/wolfSSL/wolfssl/issues/7784>
+     * It adds the close notify message again every time we retry
+     * sending during shutdown. */
+    CURL_TRC_CF(data, cf, "bio_write, shutdown restrict send of %d"
+                " to %d bytes", blen, backend->io_send_blocked_len);
+    skiplen = (ssize_t)(blen - backend->io_send_blocked_len);
+    blen = backend->io_send_blocked_len;
+  }
   nwritten = Curl_conn_cf_send(cf->next, data, buf, blen, FALSE, &result);
   backend->io_result = result;
   CURL_TRC_CF(data, cf, "bio_write(len=%d) -> %zd, %d",
               blen, nwritten, result);
   wolfSSL_BIO_clear_retry_flags(bio);
-  if(nwritten < 0 && CURLE_AGAIN == result)
+  if(nwritten < 0 && CURLE_AGAIN == result) {
     BIO_set_retry_write(bio);
+    if(backend->shutting_down && !backend->io_send_blocked_len)
+      backend->io_send_blocked_len = blen;
+  }
+  else if(!result && skiplen)
+    nwritten += skiplen;
   return (int)nwritten;
 }
 
@@ -1428,7 +1443,10 @@ static CURLcode wolfssl_shutdown(struct Curl_cfilter *cf,
   struct wolfssl_ctx *wctx = (struct wolfssl_ctx *)connssl->backend;
   CURLcode result = CURLE_OK;
   char buf[1024];
-  int nread, err;
+  char error_buffer[256];
+  int nread = -1, err;
+  size_t i;
+  int detail;
 
   DEBUGASSERT(wctx);
   if(!wctx->handle || cf->shutdown) {
@@ -1436,6 +1454,7 @@ static CURLcode wolfssl_shutdown(struct Curl_cfilter *cf,
     goto out;
   }
 
+  wctx->shutting_down = TRUE;
   connssl->io_need = CURL_SSL_IO_NEED_NONE;
   *done = FALSE;
   if(!(wolfSSL_get_shutdown(wctx->handle) & SSL_SENT_SHUTDOWN)) {
@@ -1444,6 +1463,7 @@ static CURLcode wolfssl_shutdown(struct Curl_cfilter *cf,
     ERR_clear_error();
     nread = wolfSSL_read(wctx->handle, buf, (int)sizeof(buf));
     err = wolfSSL_get_error(wctx->handle, nread);
+    CURL_TRC_CF(data, cf, "wolfSSL_read, nread=%d, err=%d", nread, err);
     if(!nread && err == SSL_ERROR_ZERO_RETURN) {
       bool input_pending;
       /* Yes, it did. */
@@ -1464,49 +1484,55 @@ static CURLcode wolfssl_shutdown(struct Curl_cfilter *cf,
     }
   }
 
-  if(send_shutdown && wolfSSL_shutdown(wctx->handle) == 1) {
-    CURL_TRC_CF(data, cf, "SSL shutdown finished");
-    *done = TRUE;
-    goto out;
-  }
-  else {
-    size_t i;
-    /* SSL should now have started the shutdown from our side. Since it
-     * was not complete, we are lacking the close notify from the server. */
-    for(i = 0; i < 10; ++i) {
-      ERR_clear_error();
-      nread = wolfSSL_read(wctx->handle, buf, (int)sizeof(buf));
-      if(nread <= 0)
-        break;
-    }
-    err = wolfSSL_get_error(wctx->handle, nread);
-    switch(err) {
-    case SSL_ERROR_ZERO_RETURN: /* no more data */
-      CURL_TRC_CF(data, cf, "SSL shutdown received");
+  /* SSL should now have started the shutdown from our side. Since it
+   * was not complete, we are lacking the close notify from the server. */
+  if(send_shutdown) {
+    ERR_clear_error();
+    if(wolfSSL_shutdown(wctx->handle) == 1) {
+      CURL_TRC_CF(data, cf, "SSL shutdown finished");
       *done = TRUE;
-      break;
-    case SSL_ERROR_NONE: /* just did not get anything */
-    case SSL_ERROR_WANT_READ:
-      /* SSL has send its notify and now wants to read the reply
-       * from the server. We are not really interested in that. */
-      CURL_TRC_CF(data, cf, "SSL shutdown sent, want receive");
-      connssl->io_need = CURL_SSL_IO_NEED_RECV;
-      break;
-    case SSL_ERROR_WANT_WRITE:
-      CURL_TRC_CF(data, cf, "SSL shutdown send blocked");
-      connssl->io_need = CURL_SSL_IO_NEED_SEND;
-      break;
-    default: {
-      char error_buffer[256];
-      int detail = wolfSSL_get_error(wctx->handle, err);
-      CURL_TRC_CF(data, cf, "SSL shutdown, error: '%s'(%d)",
-                  wolfssl_strerror((unsigned long)err, error_buffer,
-                                   sizeof(error_buffer)),
-                  detail);
-      result = CURLE_RECV_ERROR;
-      break;
+      goto out;
     }
+    if(SSL_ERROR_WANT_WRITE == wolfSSL_get_error(wctx->handle, nread)) {
+      CURL_TRC_CF(data, cf, "SSL shutdown still wants to send");
+      connssl->io_need = CURL_SSL_IO_NEED_SEND;
+      goto out;
     }
+    /* Having sent the close notify, we use wolfSSL_read() to get the
+     * missing close notify from the server. */
+  }
+
+  for(i = 0; i < 10; ++i) {
+    ERR_clear_error();
+    nread = wolfSSL_read(wctx->handle, buf, (int)sizeof(buf));
+    if(nread <= 0)
+      break;
+  }
+  err = wolfSSL_get_error(wctx->handle, nread);
+  switch(err) {
+  case SSL_ERROR_ZERO_RETURN: /* no more data */
+    CURL_TRC_CF(data, cf, "SSL shutdown received");
+    *done = TRUE;
+    break;
+  case SSL_ERROR_NONE: /* just did not get anything */
+  case SSL_ERROR_WANT_READ:
+    /* SSL has send its notify and now wants to read the reply
+     * from the server. We are not really interested in that. */
+    CURL_TRC_CF(data, cf, "SSL shutdown sent, want receive");
+    connssl->io_need = CURL_SSL_IO_NEED_RECV;
+    break;
+  case SSL_ERROR_WANT_WRITE:
+    CURL_TRC_CF(data, cf, "SSL shutdown send blocked");
+    connssl->io_need = CURL_SSL_IO_NEED_SEND;
+    break;
+  default:
+    detail = wolfSSL_get_error(wctx->handle, err);
+    CURL_TRC_CF(data, cf, "SSL shutdown, error: '%s'(%d)",
+                wolfssl_strerror((unsigned long)err, error_buffer,
+                                 sizeof(error_buffer)),
+                detail);
+    result = CURLE_RECV_ERROR;
+    break;
   }
 
 out:
index d75bdaa1e00e10f6aaa5ca2ef987baa872d12d3b..318d8b4ab3308b4f5fd32530db9cded9319945da 100644 (file)
@@ -39,7 +39,9 @@ struct wolfssl_ctx {
   WOLFSSL_CTX *ctx;
   WOLFSSL     *handle;
   CURLcode    io_result;   /* result of last BIO cfilter operation */
+  int io_send_blocked_len; /* length of last BIO write that EAGAINed */
   BIT(x509_store_setup);   /* x509 store has been set up */
+  BIT(shutting_down);      /* TLS is being shut down */
 };
 
 CURLcode Curl_wssl_setup_x509_store(struct Curl_cfilter *cf,