From 34fa034d9a390c4bd65e2d05262755ec8646ac12 Mon Sep 17 00:00:00 2001
From: Daniel Stenberg <daniel@haxx.se>
Date: Thu, 5 Feb 2026 08:34:21 +0100
Subject: [PATCH] url: fix reuse of connections using HTTP Negotiate
Assume Negotiate means connection-based
Follow-up fix: f1a39f221d57354990e3eeeddc3404aede2aff70
url: fix copy and paste url_match_auth_nego mistake
Reported-by: Zhicheng Chen
Closes #20534
Closes #20662
Conflict: adapt for curl-7.78.0 which uses ConnectionExists() with local
variables instead of url_conn_match struct. Variable names adjusted:
needle->scheme->protocol => needle->handler->protocol
m->needle => needle, conn => check, *force_reuse stays as pointer
Reference: https://github.com/curl/curl/commit/34fa034d9a390c4bd65e2d05262755ec8646ac12
Reference: https://github.com/curl/curl/commit/f1a39f221d57354990e3eeeddc3404aede2aff70
lib/url.c | 69 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 68 insertions(+), 1 deletion(-)
@@ -1119,6 +1119,17 @@
bool wantProxyNTLMhttp = FALSE;
#endif
#endif
+#ifdef USE_SPNEGO
+ bool wantNegohttp = ((data->state.authhost.want & CURLAUTH_NEGOTIATE) &&
+ (needle->handler->protocol & PROTO_FAMILY_HTTP));
+#ifndef CURL_DISABLE_PROXY
+ bool wantProxyNegohttp = (needle->bits.proxy_user_passwd &&
+ (data->state.authproxy.want & CURLAUTH_NEGOTIATE) &&
+ (needle->handler->protocol & PROTO_FAMILY_HTTP));
+#else
+ bool wantProxyNegohttp = FALSE;
+#endif /* CURL_DISABLE_PROXY */
+#endif /* USE_SPNEGO */
*force_reuse = FALSE;
*waitpipe = FALSE;
@@ -1471,7 +1482,58 @@
/* Continue look up for a better connection */
continue;
}
-#endif
+#endif /* USE_NTLM */
+#ifdef USE_SPNEGO
+ /* 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(wantNegohttp) {
+ if(Curl_timestrcmp(needle->user, check->user) ||
+ Curl_timestrcmp(needle->passwd, check->passwd)) {
+ /* we prefer a credential match, but this is at least a connection
+ that can be reused and "upgraded" */
+ if(check->http_negotiate_state == GSS_AUTHNONE)
+ chosen = check;
+ continue;
+ }
+ }
+ else if(check->http_negotiate_state != GSS_AUTHNONE) {
+ /* Connection is using Negotiate auth but we don't want Negotiate */
+ continue;
+ }
+#ifndef CURL_DISABLE_PROXY
+ /* Same for Proxy Negotiate authentication */
+ if(wantProxyNegohttp) {
+ if(!check->http_proxy.user || !check->http_proxy.passwd) {
+ continue;
+ }
+ if(Curl_timestrcmp(needle->http_proxy.user,
+ check->http_proxy.user) ||
+ Curl_timestrcmp(needle->http_proxy.passwd,
+ check->http_proxy.passwd)) {
+ continue;
+ }
+ }
+ else if(check->proxy_negotiate_state != GSS_AUTHNONE) {
+ /* Proxy connection is using Negotiate auth but we don't want it */
+ continue;
+ }
+#endif /* CURL_DISABLE_PROXY */
+ if(wantNegohttp || wantProxyNegohttp) {
+ /* Credentials are already checked, we can use this connection */
+ chosen = check;
+ if((wantNegohttp &&
+ (check->http_negotiate_state != GSS_AUTHNONE)) ||
+ (wantProxyNegohttp &&
+ (check->proxy_negotiate_state != GSS_AUTHNONE))) {
+ /* We must use this connection, no other */
+ *force_reuse = TRUE;
+ break;
+ }
+ /* Continue look up for a better connection */
+ continue;
+ }
+#endif /* USE_SPNEGO */
if(canmultiplex) {
/* We can multiplex if we want to. Let's continue looking for
the optimal connection to use. */