From b6a5f672597a06bdf4361a2e70bb53c6f097105f Mon Sep 17 00:00:00 2001 From: Jay Satiro Date: Wed, 20 Aug 2025 02:10:53 -0400 Subject: [PATCH] schannel: improve handshake procedure - 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 | 35 ++++++++++++++++++++++++++--------- 1 file changed, 26 insertions(+), 9 deletions(-) diff --git a/lib/vtls/schannel.c b/lib/vtls/schannel.c index 0b3ec8cc25..4fd424a43c 100644 --- a/lib/vtls/schannel.c +++ b/lib/vtls/schannel.c @@ -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, -- 2.47.3