From: Daniel Stenberg Date: Thu, 5 Feb 2026 07:34:21 +0000 (+0100) Subject: url: fix reuse of connections using HTTP Negotiate X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=34fa034d9a390c4bd65e2d05262755ec8646ac12;p=thirdparty%2Fcurl.git url: fix reuse of connections using HTTP Negotiate Assume Negotiate means connection-based Reported-by: Zhicheng Chen Closes #20534 --- diff --git a/lib/url.c b/lib/url.c index fac8cea732..cfe398de8b 100644 --- a/lib/url.c +++ b/lib/url.c @@ -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