schannel: handle Curl_conn_cf_send() errors better

Avoid a resource leak in the error path.

Reported-by: Joshua Rogers

Closes #19265
This commit is contained in:
Daniel Stenberg 2025-10-28 09:05:25 +01:00
parent 9308ea22e8
commit 875ea98c36
No known key found for this signature in database
GPG key ID: 5CC908FDB71E12C2

View file

@ -1113,6 +1113,41 @@ schannel_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
return CURLE_OK;
}
static CURLcode schannel_error(struct Curl_easy *data,
SECURITY_STATUS sspi_status)
{
char buffer[STRERROR_LEN];
switch(sspi_status) {
case SEC_E_INSUFFICIENT_MEMORY:
failf(data, "schannel: next InitializeSecurityContext failed: %s",
Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer)));
return CURLE_OUT_OF_MEMORY;
case SEC_E_WRONG_PRINCIPAL:
failf(data, "schannel: SNI or certificate check failed: %s",
Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer)));
return CURLE_PEER_FAILED_VERIFICATION;
case SEC_E_UNTRUSTED_ROOT:
failf(data, "schannel: %s",
Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer)));
return CURLE_PEER_FAILED_VERIFICATION;
#if 0
case SEC_E_INVALID_HANDLE:
case SEC_E_INVALID_TOKEN:
case SEC_E_LOGON_DENIED:
case SEC_E_TARGET_UNKNOWN:
case SEC_E_NO_AUTHENTICATING_AUTHORITY:
case SEC_E_INTERNAL_ERROR:
case SEC_E_NO_CREDENTIALS:
case SEC_E_UNSUPPORTED_FUNCTION:
case SEC_E_APPLICATION_PROTOCOL_MISMATCH:
#endif
default:
failf(data, "schannel: next InitializeSecurityContext failed: %s",
Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer)));
return CURLE_SSL_CONNECT_ERROR;
}
}
static CURLcode
schannel_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data)
{
@ -1266,28 +1301,18 @@ schannel_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data)
Curl_safefree(inbuf[0].pvBuffer);
/* check if the handshake was incomplete */
if(sspi_status == SEC_E_INCOMPLETE_MESSAGE) {
switch(sspi_status) {
case SEC_E_INCOMPLETE_MESSAGE:
backend->encdata_is_incomplete = TRUE;
connssl->io_need = CURL_SSL_IO_NEED_RECV;
DEBUGF(infof(data,
"schannel: received incomplete message, need more data"));
return CURLE_OK;
}
/* If the server has requested a client certificate, attempt to continue
the handshake without one. This will allow connections to servers which
request a client certificate but do not require it. */
if(sspi_status == SEC_I_INCOMPLETE_CREDENTIALS &&
!(backend->req_flags & ISC_REQ_USE_SUPPLIED_CREDS)) {
backend->req_flags |= ISC_REQ_USE_SUPPLIED_CREDS;
connssl->io_need = CURL_SSL_IO_NEED_SEND;
DEBUGF(infof(data,
"schannel: a client certificate has been requested"));
return CURLE_OK;
}
/* check if the handshake needs to be continued */
if(sspi_status == SEC_I_CONTINUE_NEEDED || sspi_status == SEC_E_OK) {
case SEC_I_CONTINUE_NEEDED:
case SEC_E_OK:
/* check if the handshake needs to be continued */
result = CURLE_OK;
for(i = 0; i < 3; i++) {
/* search for handshake tokens that need to be send */
if(outbuf[i].BufferType == SECBUFFER_TOKEN && outbuf[i].cbBuffer > 0) {
@ -1301,47 +1326,35 @@ schannel_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data)
if(result || (outbuf[i].cbBuffer != written)) {
failf(data, "schannel: failed to send next handshake data: "
"sent %zu of %lu bytes", written, outbuf[i].cbBuffer);
return CURLE_SSL_CONNECT_ERROR;
result = CURLE_SSL_CONNECT_ERROR;
}
}
}
for(i = 0; i < 3; i++) {
/* free obsolete buffer */
if(outbuf[i].pvBuffer) {
if(outbuf[i].pvBuffer)
Curl_pSecFn->FreeContextBuffer(outbuf[i].pvBuffer);
}
}
}
else {
char buffer[STRERROR_LEN];
switch(sspi_status) {
case SEC_E_INSUFFICIENT_MEMORY:
failf(data, "schannel: next InitializeSecurityContext failed: %s",
Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer)));
return CURLE_OUT_OF_MEMORY;
case SEC_E_WRONG_PRINCIPAL:
failf(data, "schannel: SNI or certificate check failed: %s",
Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer)));
return CURLE_PEER_FAILED_VERIFICATION;
case SEC_E_UNTRUSTED_ROOT:
failf(data, "schannel: %s",
Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer)));
return CURLE_PEER_FAILED_VERIFICATION;
#if 0
case SEC_E_INVALID_HANDLE:
case SEC_E_INVALID_TOKEN:
case SEC_E_LOGON_DENIED:
case SEC_E_TARGET_UNKNOWN:
case SEC_E_NO_AUTHENTICATING_AUTHORITY:
case SEC_E_INTERNAL_ERROR:
case SEC_E_NO_CREDENTIALS:
case SEC_E_UNSUPPORTED_FUNCTION:
case SEC_E_APPLICATION_PROTOCOL_MISMATCH:
#endif
default:
failf(data, "schannel: next InitializeSecurityContext failed: %s",
Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer)));
return CURLE_SSL_CONNECT_ERROR;
if(result)
return result;
break;
case SEC_I_INCOMPLETE_CREDENTIALS:
if(!(backend->req_flags & ISC_REQ_USE_SUPPLIED_CREDS)) {
/* If the server has requested a client certificate, attempt to
continue the handshake without one. This will allow connections to
servers which request a client certificate but do not require
it. */
backend->req_flags |= ISC_REQ_USE_SUPPLIED_CREDS;
connssl->io_need = CURL_SSL_IO_NEED_SEND;
DEBUGF(infof(data,
"schannel: a client certificate has been requested"));
return CURLE_OK;
}
FALLTHROUGH();
default:
return schannel_error(data, sspi_status);
}
/* check if there was additional remaining encrypted data */