]> git.ipfire.org Git - thirdparty/curl.git/commitdiff
url: fix reuse of connections using HTTP Negotiate
authorDaniel Stenberg <daniel@haxx.se>
Thu, 5 Feb 2026 07:34:21 +0000 (08:34 +0100)
committerDaniel Stenberg <daniel@haxx.se>
Sat, 7 Feb 2026 21:16:53 +0000 (22:16 +0100)
Assume Negotiate means connection-based

Reported-by: Zhicheng Chen
Closes #20534

lib/url.c

index fac8cea732b04896e737473148facb8d161d33c5..cfe398de8b58a0e8aa6a1ebec63bbf39f903ff00 100644 (file)
--- 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