From: Daniel Stenberg Date: Tue, 28 Oct 2025 08:05:25 +0000 (+0100) Subject: schannel: handle Curl_conn_cf_send() errors better X-Git-Tag: rc-8_17_0-3~2 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=875ea98c360df34dd757eb7e1e62a1cb2d177da8;p=thirdparty%2Fcurl.git schannel: handle Curl_conn_cf_send() errors better Avoid a resource leak in the error path. Reported-by: Joshua Rogers Closes #19265 --- diff --git a/lib/vtls/schannel.c b/lib/vtls/schannel.c index 0561d6fbdd..376d1a67ea 100644 --- a/lib/vtls/schannel.c +++ b/lib/vtls/schannel.c @@ -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 */