]> git.ipfire.org Git - thirdparty/curl.git/commitdiff
url: connection credentials origin
authorStefan Eissing <stefan@eissing.org>
Tue, 16 Jun 2026 08:12:24 +0000 (10:12 +0200)
committerDaniel Stenberg <daniel@haxx.se>
Tue, 16 Jun 2026 13:57:10 +0000 (15:57 +0200)
When tying credentials to a connection (NTLM, Negotiate) also link the
origin the credentials are for. This prevents a connection reuse with
the same credentials, but intended for another origin.

The mis-reuse could happen for a forwarding proxy and NTLM (although, in
the mind of the person writing this, it is an insane setup).

Closes #22040

lib/http_negotiate.c
lib/http_ntlm.c
lib/url.c
lib/urldata.h

index 4aa67f01d5b6ddd933e5dfd0bfa9124b87b654c8..891369b5bc2b6b514d376e6220ddc2dfb77ae6b2 100644 (file)
@@ -42,6 +42,7 @@ static void http_auth_nego_reset(struct connectdata *conn,
     conn->proxy_negotiate_state = GSS_AUTHNONE;
   else {
     conn->http_negotiate_state = GSS_AUTHNONE;
+    Curl_peer_unlink(&conn->creds_origin);
     Curl_creds_unlink(&conn->creds);
   }
   if(neg_ctx)
@@ -132,13 +133,19 @@ CURLcode Curl_input_negotiate(struct Curl_easy *data, struct connectdata *conn,
   if(result)
     http_auth_nego_reset(conn, neg_ctx, proxy);
 
-  if(!proxy) {
+  if(!result && !proxy) {
     /* Start it up. From this time onwards, the connection is tied
      * tp the credentials used. */
+    if(conn->creds_origin &&
+       !Curl_peer_equal(conn->creds_origin, data->state.origin)) {
+      DEBUGASSERT(0); /* should not happen. */
+      return CURLE_FAILED_INIT;
+    }
     if(conn->creds && !Curl_creds_same(creds, conn->creds)) {
       DEBUGASSERT(0); /* should not happen. */
       return CURLE_FAILED_INIT;
     }
+    Curl_peer_link(&conn->creds_origin, data->state.origin);
     Curl_creds_link(&conn->creds, creds);
   }
 
index 1442fd6f7a7d027dbbe856884a8dc8d51eb5d110..dc9911fdacb2cab0cce30b2bbc9227e55c466825 100644 (file)
@@ -93,6 +93,8 @@ CURLcode Curl_input_ntlm(struct Curl_easy *data,
       else if(*state == NTLMSTATE_TYPE3) {
         infof(data, "NTLM handshake rejected");
         Curl_auth_ntlm_remove(conn, proxy);
+        Curl_peer_unlink(&conn->creds_origin);
+        Curl_creds_unlink(&conn->creds);
         *state = NTLMSTATE_NONE;
         return CURLE_REMOTE_ACCESS_DENIED;
       }
@@ -184,10 +186,16 @@ CURLcode Curl_output_ntlm(struct Curl_easy *data, bool proxy)
     if(!proxy) {
       /* Start it up. From this time onwards, the connection is tied
        * tp the credentials used. */
+      if(conn->creds_origin &&
+         !Curl_peer_equal(conn->creds_origin, data->state.origin)) {
+        DEBUGASSERT(0); /* should not happen. */
+        return CURLE_FAILED_INIT;
+      }
       if(conn->creds && !Curl_creds_same(creds, conn->creds)) {
         DEBUGASSERT(0); /* should not happen. */
         return CURLE_FAILED_INIT;
       }
+      Curl_peer_link(&conn->creds_origin, data->state.origin);
       Curl_creds_link(&conn->creds, creds);
     }
     result = Curl_auth_create_ntlm_type1_message(data, creds, "HTTP",
index 8da2aca924326532fe6b984fab2147e63ac20724..3018dc438b8ae0c5baf5e819e96e78578f9c9251 100644 (file)
--- a/lib/url.c
+++ b/lib/url.c
@@ -515,6 +515,7 @@ void Curl_conn_free(struct Curl_easy *data, struct connectdata *conn)
   Curl_creds_unlink(&conn->socks_proxy.creds);
 #endif
   Curl_creds_unlink(&conn->creds);
+  Curl_peer_unlink(&conn->creds_origin);
   curlx_safefree(conn->options);
   curlx_safefree(conn->localdev);
   Curl_ssl_conn_config_cleanup(conn);
@@ -1011,18 +1012,11 @@ static bool url_match_auth_ntlm(struct connectdata *conn,
      possible. (Especially we must not reuse the same connection if
      partway through a handshake!) */
   if(m->want_ntlm_http) {
-    if(!Curl_creds_same(m->data->state.creds, conn->creds)) {
-      /* we prefer a credential match, but this is at least a connection
-         that can be reused and "upgraded" to NTLM if it does
-         not have any auth ongoing. */
-#ifdef USE_SPNEGO
-      if((conn->http_ntlm_state == NTLMSTATE_NONE) &&
-         (conn->http_negotiate_state == GSS_AUTHNONE)) {
-#else
-      if(conn->http_ntlm_state == NTLMSTATE_NONE) {
-#endif
-        m->found = conn;
-      }
+    if(conn->creds &&
+       (!Curl_creds_same(conn->creds, m->data->state.creds) ||
+        !Curl_peer_equal(conn->creds_origin, m->data->state.origin))) {
+      /* connection credentials in play and not the same or not for the
+       * same origin. */
       return FALSE;
     }
   }
@@ -1079,7 +1073,9 @@ static bool url_match_auth_nego(struct connectdata *conn,
      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_creds_same(m->needle->creds, conn->creds))
+    if(conn->creds &&
+       (!Curl_creds_same(conn->creds, m->data->state.creds) ||
+        !Curl_peer_equal(conn->creds_origin, m->data->state.origin)))
       return FALSE;
   }
   else if(conn->http_negotiate_state != GSS_AUTHNONE) {
@@ -1816,6 +1812,7 @@ static CURLcode url_set_conn_login(struct Curl_easy *data,
 {
   /* If our protocol needs a password and we have none, use the defaults */
   if((conn->scheme->flags & PROTOPT_NEEDSPWD) && !conn->creds) {
+    Curl_peer_link(&conn->creds_origin, data->state.origin);
     if(data->state.creds)
       Curl_creds_link(&conn->creds, data->state.creds);
     else
@@ -1825,6 +1822,7 @@ static CURLcode url_set_conn_login(struct Curl_easy *data,
   else if(!(conn->scheme->flags & PROTOPT_CREDSPERREQUEST)) {
     /* for protocols that do not handle credentials per request,
      * the connection credentials are set by the initial transfer. */
+    Curl_peer_link(&conn->creds_origin, data->state.origin);
     Curl_creds_link(&conn->creds, data->state.creds);
   }
 
index a0d7fa421df8cf0ef7b65738cdef01a45f969f91..232364fcf3a7ba4362ec46172bc8d12a94814374 100644 (file)
@@ -301,6 +301,7 @@ struct connectdata {
   struct proxy_info http_proxy;
 #endif
   struct Curl_creds *creds; /* When connection itself is tied to credentials */
+  struct Curl_peer *creds_origin; /* origin tied credentials are for */
   char *options; /* options string, allocated */
   struct curltime created; /* creation time */
   struct curltime lastused; /* when returned to the connection pool as idle */