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

Reported-by: Zhicheng Chen
Closes #20534

Upstream-Status: Backport [https://github.com/curl/curl/commit/34fa034d9a390c4bd6]
Backported by Ubuntu team https://launchpad.net/ubuntu/+archive/primary/+sourcefiles/curl/7.81.0-1ubuntu1.23/curl_7.81.0-1ubuntu1.23.debian.tar.xz

CVE: CVE-2026-1965
Signed-off-by: Vijay Anusuri <vanusuri@mvista.com>
---
 lib/url.c | 87 +++++++++++++++++++++++++++++++++++++++++++++++++++----
 1 file changed, 82 insertions(+), 5 deletions(-)

--- a/lib/url.c
+++ b/lib/url.c
@@ -1145,6 +1145,18 @@ ConnectionExists(struct Curl_easy *data,
 #endif
 #endif
 
+#if !defined(CURL_DISABLE_HTTP) && defined(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);
+#endif
+#endif
+
   *force_reuse = FALSE;
   *waitpipe = FALSE;
 
@@ -1496,6 +1508,57 @@ ConnectionExists(struct Curl_easy *data,
           continue;
         }
 #endif
+
+#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))
+      continue;
+  }
+  else if(check->http_negotiate_state != GSS_AUTHNONE) {
+    /* Connection is using Negotiate auth but we do not want Negotiate */
+    continue;
+  }
+
+#ifndef CURL_DISABLE_PROXY
+  /* Same for Proxy Negotiate authentication */
+  if(wantProxyNegohttp) {
+    /* Both check->http_proxy.user and check->http_proxy.passwd can be
+     * NULL */
+    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 do not want Negotiate */
+    continue;
+  }
+#endif
+  if(wantNTLMhttp || wantProxyNTLMhttp) {
+    /* 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. */
+    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; /* get another */
+  }
+#endif
+
         if(canmultiplex) {
           /* We can multiplex if we want to. Let's continue looking for
              the optimal connection to use. */
