schannel: ban server ALPN change during recv renegotiation
By the time schannel_recv is renegotiating the connection, libcurl has already decided on a protocol and it is too late for the server to select a protocol via ALPN except for the originally selected protocol. Ref: https://github.com/curl/curl/issues/9451 Closes https://github.com/curl/curl/pull/9463
This commit is contained in:
Родитель
1a87a1efba
Коммит
5c0d02b7a7
|
@ -1314,6 +1314,7 @@ schannel_connect_step1(struct Curl_easy *data, struct connectdata *conn,
|
||||||
backend->recv_unrecoverable_err = CURLE_OK;
|
backend->recv_unrecoverable_err = CURLE_OK;
|
||||||
backend->recv_sspi_close_notify = false;
|
backend->recv_sspi_close_notify = false;
|
||||||
backend->recv_connection_closed = false;
|
backend->recv_connection_closed = false;
|
||||||
|
backend->recv_renegotiating = false;
|
||||||
backend->encdata_is_incomplete = false;
|
backend->encdata_is_incomplete = false;
|
||||||
|
|
||||||
/* continue to second handshake step */
|
/* continue to second handshake step */
|
||||||
|
@ -1713,6 +1714,7 @@ schannel_connect_step3(struct Curl_easy *data, struct connectdata *conn,
|
||||||
|
|
||||||
if(alpn_result.ProtoNegoStatus ==
|
if(alpn_result.ProtoNegoStatus ==
|
||||||
SecApplicationProtocolNegotiationStatus_Success) {
|
SecApplicationProtocolNegotiationStatus_Success) {
|
||||||
|
unsigned char alpn = 0;
|
||||||
|
|
||||||
infof(data, VTLS_INFOF_ALPN_ACCEPTED_LEN_1STR,
|
infof(data, VTLS_INFOF_ALPN_ACCEPTED_LEN_1STR,
|
||||||
alpn_result.ProtocolIdSize, alpn_result.ProtocolId);
|
alpn_result.ProtocolIdSize, alpn_result.ProtocolId);
|
||||||
|
@ -1720,21 +1722,34 @@ schannel_connect_step3(struct Curl_easy *data, struct connectdata *conn,
|
||||||
#ifdef USE_HTTP2
|
#ifdef USE_HTTP2
|
||||||
if(alpn_result.ProtocolIdSize == ALPN_H2_LENGTH &&
|
if(alpn_result.ProtocolIdSize == ALPN_H2_LENGTH &&
|
||||||
!memcmp(ALPN_H2, alpn_result.ProtocolId, ALPN_H2_LENGTH)) {
|
!memcmp(ALPN_H2, alpn_result.ProtocolId, ALPN_H2_LENGTH)) {
|
||||||
conn->alpn = CURL_HTTP_VERSION_2;
|
alpn = CURL_HTTP_VERSION_2;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
#endif
|
#endif
|
||||||
if(alpn_result.ProtocolIdSize == ALPN_HTTP_1_1_LENGTH &&
|
if(alpn_result.ProtocolIdSize == ALPN_HTTP_1_1_LENGTH &&
|
||||||
!memcmp(ALPN_HTTP_1_1, alpn_result.ProtocolId,
|
!memcmp(ALPN_HTTP_1_1, alpn_result.ProtocolId,
|
||||||
ALPN_HTTP_1_1_LENGTH)) {
|
ALPN_HTTP_1_1_LENGTH)) {
|
||||||
conn->alpn = CURL_HTTP_VERSION_1_1;
|
alpn = CURL_HTTP_VERSION_1_1;
|
||||||
|
}
|
||||||
|
if(backend->recv_renegotiating) {
|
||||||
|
if(alpn != conn->alpn) {
|
||||||
|
failf(data, "schannel: server selected an ALPN protocol too late");
|
||||||
|
return CURLE_SSL_CONNECT_ERROR;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
conn->alpn = alpn;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if(!backend->recv_renegotiating)
|
||||||
infof(data, VTLS_INFOF_NO_ALPN);
|
infof(data, VTLS_INFOF_NO_ALPN);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!backend->recv_renegotiating) {
|
||||||
Curl_multiuse_state(data, conn->alpn == CURL_HTTP_VERSION_2 ?
|
Curl_multiuse_state(data, conn->alpn == CURL_HTTP_VERSION_2 ?
|
||||||
BUNDLE_MULTIPLEX : BUNDLE_NO_MULTIUSE);
|
BUNDLE_MULTIPLEX : BUNDLE_NO_MULTIUSE);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* save the current session data for possible re-use */
|
/* save the current session data for possible re-use */
|
||||||
|
@ -2293,7 +2308,9 @@ schannel_recv(struct Curl_easy *data, int sockindex,
|
||||||
infof(data, "schannel: renegotiating SSL/TLS connection");
|
infof(data, "schannel: renegotiating SSL/TLS connection");
|
||||||
connssl->state = ssl_connection_negotiating;
|
connssl->state = ssl_connection_negotiating;
|
||||||
connssl->connecting_state = ssl_connect_2_writing;
|
connssl->connecting_state = ssl_connect_2_writing;
|
||||||
|
backend->recv_renegotiating = true;
|
||||||
*err = schannel_connect_common(data, conn, sockindex, FALSE, &done);
|
*err = schannel_connect_common(data, conn, sockindex, FALSE, &done);
|
||||||
|
backend->recv_renegotiating = false;
|
||||||
if(*err) {
|
if(*err) {
|
||||||
infof(data, "schannel: renegotiation failed");
|
infof(data, "schannel: renegotiation failed");
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
|
|
|
@ -179,6 +179,7 @@ struct ssl_backend_data {
|
||||||
CURLcode recv_unrecoverable_err; /* schannel_recv had an unrecoverable err */
|
CURLcode recv_unrecoverable_err; /* schannel_recv had an unrecoverable err */
|
||||||
bool recv_sspi_close_notify; /* true if connection closed by close_notify */
|
bool recv_sspi_close_notify; /* true if connection closed by close_notify */
|
||||||
bool recv_connection_closed; /* true if connection closed, regardless how */
|
bool recv_connection_closed; /* true if connection closed, regardless how */
|
||||||
|
bool recv_renegotiating; /* true if recv is doing renegotiation */
|
||||||
bool use_alpn; /* true if ALPN is used for this connection */
|
bool use_alpn; /* true if ALPN is used for this connection */
|
||||||
#ifdef HAS_MANUAL_VERIFY_API
|
#ifdef HAS_MANUAL_VERIFY_API
|
||||||
bool use_manual_cred_validation; /* true if manual cred validation is used */
|
bool use_manual_cred_validation; /* true if manual cred validation is used */
|
||||||
|
|
Загрузка…
Ссылка в новой задаче