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(-)

--- curl-7.78.0/lib/url.c	2021-07-19 15:19:57.000000000 +0800
+++ curl-7.78.0-patched/lib/url.c	2026-03-14 00:00:00.000000000 +0800
@@ -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. */