]> git.ipfire.org Git - thirdparty/curl.git/commitdiff
schannel: improve handshake procedure
authorJay Satiro <raysatiro@yahoo.com>
Wed, 20 Aug 2025 06:10:53 +0000 (02:10 -0400)
committerJay Satiro <raysatiro@yahoo.com>
Fri, 22 Aug 2025 05:50:28 +0000 (01:50 -0400)
- During handshake, do not require reading more data if unprocessed
  encrypted data that may be a complete TLS record is already available.

- During handshake, check that the socket is writeable before processing
  encrypted data that may require an immediate reply to the server.

These two fixes are for issues that were found during renegotiation
testing but could affect any handshake.

Prior to this change it was possible in some abnormal network conditions
for the Schannel TLS handshake procedure to erroneously wait or error.

Ref: https://github.com/curl/curl/pull/18125

Closes https://github.com/curl/curl/pull/18323

lib/vtls/schannel.c

index 0b3ec8cc25c8e2fdf5a1e468fa4aa3e134222612..4fd424a43cf2dc30afcf4cfb52ee4bbcf4d05d6c 100644 (file)
@@ -1190,21 +1190,28 @@ schannel_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data)
                                  backend->encdata_offset,
                                  &nread);
       if(result == CURLE_AGAIN) {
-        connssl->io_need = CURL_SSL_IO_NEED_RECV;
-        DEBUGF(infof(data, "schannel: failed to receive handshake, "
-                     "need more data"));
-        return CURLE_OK;
+        if(!backend->encdata_offset || backend->encdata_is_incomplete) {
+          connssl->io_need = CURL_SSL_IO_NEED_RECV;
+          DEBUGF(infof(data, "schannel: failed to receive handshake, "
+                       "need more data"));
+          return CURLE_OK;
+        }
+        else {
+          DEBUGF(infof(data, "schannel: no new handshake data received, "
+                       "continuing to process existing handshake data"));
+        }
       }
       else if(result || (nread == 0)) {
         failf(data, "schannel: failed to receive handshake, "
               "SSL/TLS connection failed");
         return CURLE_SSL_CONNECT_ERROR;
       }
-
-      /* increase encrypted data buffer offset */
-      backend->encdata_offset += nread;
-      backend->encdata_is_incomplete = FALSE;
-      SCH_DEV(infof(data, "schannel: encrypted data got %zu", nread));
+      else {
+        /* increase encrypted data buffer offset */
+        backend->encdata_offset += nread;
+        backend->encdata_is_incomplete = FALSE;
+        SCH_DEV(infof(data, "schannel: encrypted data got %zu", nread));
+      }
     }
 
     SCH_DEV(infof(data,
@@ -1232,6 +1239,16 @@ schannel_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data)
     memcpy(inbuf[0].pvBuffer, backend->encdata_buffer,
            backend->encdata_offset);
 
+    /* The socket must be writeable (or a poll error occurred) before we call
+       InitializeSecurityContext to continue processing the received TLS
+       records. This is because that function is not idempotent and we don't
+       support partial save/resume sending replies of handshake tokens. */
+    if(!SOCKET_WRITABLE(Curl_conn_cf_get_socket(cf, data), 0)) {
+      SCH_DEV(infof(data, "schannel: handshake waiting for writeable socket"));
+      connssl->io_need = CURL_SSL_IO_NEED_SEND;
+      return CURLE_OK;
+    }
+
     sspi_status = Curl_pSecFn->InitializeSecurityContext(
       &backend->cred->cred_handle, &backend->ctxt->ctxt_handle,
       backend->cred->sni_hostname, backend->req_flags,