url: fix reuse of connections using HTTP Negotiate

Assume Negotiate means connection-based

Reported-by: Zhicheng Chen
Closes #20534
This commit is contained in:
Daniel Stenberg 2026-02-05 08:34:21 +01:00
parent 2d8284e4e8
commit 34fa034d9a
No known key found for this signature in database
GPG Key ID: 5CC908FDB71E12C2

View File

@ -764,6 +764,8 @@ struct url_conn_match {
BIT(may_multiplex);
BIT(want_ntlm_http);
BIT(want_proxy_ntlm_http);
BIT(want_nego_http);
BIT(want_proxy_nego_http);
BIT(wait_pipe);
BIT(force_reuse);
@ -1185,6 +1187,63 @@ static bool url_match_auth_ntlm(struct connectdata *conn,
#define url_match_auth_ntlm(c, m) ((void)c, (void)m, TRUE)
#endif
#ifdef USE_SPNEGO
static bool url_match_auth_nego(struct connectdata *conn,
struct url_conn_match *m)
{
/* If we are looking for an HTTP+Negotiate connection, check if this is
already authenticating with the right credentials. If not, keep looking
so that we can reuse Negotiate connections if possible. */
if(m->want_nego_http) {
if(Curl_timestrcmp(m->needle->user, conn->user) ||
Curl_timestrcmp(m->needle->passwd, conn->passwd))
return FALSE;
}
else if(conn->http_negotiate_state != GSS_AUTHNONE) {
/* Connection is using Negotiate auth but we do not want Negotiate */
return FALSE;
}
#ifndef CURL_DISABLE_PROXY
/* Same for Proxy Negotiate authentication */
if(m->want_proxy_nego_http) {
/* Both conn->http_proxy.user and conn->http_proxy.passwd can be
* NULL */
if(!conn->http_proxy.user || !conn->http_proxy.passwd)
return FALSE;
if(Curl_timestrcmp(m->needle->http_proxy.user,
conn->http_proxy.user) ||
Curl_timestrcmp(m->needle->http_proxy.passwd,
conn->http_proxy.passwd))
return FALSE;
}
else if(conn->proxy_negotiate_state != GSS_AUTHNONE) {
/* Proxy connection is using Negotiate auth but we do not want Negotiate */
return FALSE;
}
#endif
if(m->want_ntlm_http || m->want_proxy_ntlm_http) {
/* Credentials are already checked, we may use this connection. We MUST
* use a connection where it has already been fully negotiated. If it has
* not, we keep on looking for a better one. */
m->found = conn;
if((m->want_nego_http &&
(conn->http_negotiate_state != GSS_AUTHNONE)) ||
(m->want_proxy_nego_http &&
(conn->proxy_negotiate_state != GSS_AUTHNONE))) {
/* We must use this connection, no other */
m->force_reuse = TRUE;
return TRUE;
}
return FALSE; /* get another */
}
return TRUE;
}
#else
#define url_match_auth_nego(c, m) ((void)c, (void)m, TRUE)
#endif
static bool url_match_conn(struct connectdata *conn, void *userdata)
{
struct url_conn_match *m = userdata;
@ -1228,6 +1287,11 @@ static bool url_match_conn(struct connectdata *conn, void *userdata)
else if(m->force_reuse)
return TRUE;
if(!url_match_auth_nego(conn, m))
return FALSE;
else if(m->force_reuse)
return TRUE;
if(!url_match_multiplex_limits(conn, m))
return FALSE;
@ -1290,13 +1354,26 @@ static bool url_attach_existing(struct Curl_easy *data,
match.may_multiplex = xfer_may_multiplex(data, needle);
#ifdef USE_NTLM
match.want_ntlm_http = ((data->state.authhost.want & CURLAUTH_NTLM) &&
(needle->scheme->protocol & PROTO_FAMILY_HTTP));
match.want_ntlm_http =
(data->state.authhost.want & CURLAUTH_NTLM) &&
(needle->scheme->protocol & PROTO_FAMILY_HTTP);
#ifndef CURL_DISABLE_PROXY
match.want_proxy_ntlm_http =
(needle->bits.proxy_user_passwd &&
(data->state.authproxy.want & CURLAUTH_NTLM) &&
(needle->scheme->protocol & PROTO_FAMILY_HTTP));
needle->bits.proxy_user_passwd &&
(data->state.authproxy.want & CURLAUTH_NTLM) &&
(needle->scheme->protocol & PROTO_FAMILY_HTTP);
#endif
#endif
#if !defined(CURL_DISABLE_HTTP) && defined(USE_SPNEGO)
match.want_nego_http =
(data->state.authhost.want & CURLAUTH_NEGOTIATE) &&
(needle->scheme->protocol & PROTO_FAMILY_HTTP);
#ifndef CURL_DISABLE_PROXY
match.want_proxy_nego_http =
needle->bits.proxy_user_passwd &&
(data->state.authproxy.want & CURLAUTH_NEGOTIATE) &&
(needle->scheme->protocol & PROTO_FAMILY_HTTP);
#endif
#endif