]> git.ipfire.org Git - thirdparty/curl.git/commitdiff
http: for basic+digest auth, do not engage on empty user+passwd
authorStefan Eissing <stefan@eissing.org>
Wed, 17 Jun 2026 10:06:29 +0000 (12:06 +0200)
committerDaniel Stenberg <daniel@haxx.se>
Wed, 17 Jun 2026 12:05:03 +0000 (14:05 +0200)
Since we have the quirky of empty credentials (the empty string for
username and password) for Negotiate reactivated, we need to check for
this when considering Basic and Digest auth.

Verify a redirect to blank user+password in test 2208

Closes #22060

lib/creds.h
lib/http.c
tests/data/Makefile.am
tests/data/test2208 [new file with mode: 0644]

index 36deff323e96abdf778ed0b559dd847ab1e56a92..0664d08a4f4fb3af2e91c263c187d9e9869c1441 100644 (file)
@@ -73,6 +73,8 @@ bool Curl_creds_equal(struct Curl_creds *c1, struct Curl_creds *c2);
 /* Provides properties for creds or, if creds is NULL, the empty string */
 #define Curl_creds_has_user(c)           ((c) && (c)->user[0])
 #define Curl_creds_has_passwd(c)         ((c) && (c)->passwd[0])
+#define Curl_creds_has_user_or_pass(c)   \
+                             ((c) && ((c)->user[0] || (c)->passwd[0]))
 #define Curl_creds_has_oauth_bearer(c)   ((c) && (c)->oauth_bearer[0])
 #define Curl_creds_has_sasl_service(c)   ((c) && (c)->sasl_service[0])
 #define Curl_creds_user(c)               ((c) ? (c)->user : "")
index e3697b1fa9f0d96cdd4656e7a85f9bbcc9d081a8..83d0e011528ef2729d9d507327a5d740dcf39c12 100644 (file)
@@ -347,8 +347,10 @@ fail:
  *
  * return TRUE if one was picked
  */
-static bool pickoneauth(struct auth *pick, unsigned long mask)
+static bool pickoneauth(struct auth *pick, unsigned long mask,
+                        struct Curl_creds *creds)
 {
+  bool have_user_pass = Curl_creds_has_user_or_pass(creds);
   bool picked;
   /* only deal with authentication we want */
   unsigned long avail = pick->avail & pick->want & mask;
@@ -356,20 +358,20 @@ static bool pickoneauth(struct auth *pick, unsigned long mask)
 
   /* The order of these checks is highly relevant, as this will be the order
      of preference in case of the existence of multiple accepted types. */
-  if(avail & CURLAUTH_NEGOTIATE)
+  if(avail & CURLAUTH_NEGOTIATE)  /* available on empty creds */
     pick->picked = CURLAUTH_NEGOTIATE;
 #ifndef CURL_DISABLE_BEARER_AUTH
-  else if(avail & CURLAUTH_BEARER)
+  else if((avail & CURLAUTH_BEARER) && Curl_creds_has_oauth_bearer(creds))
     pick->picked = CURLAUTH_BEARER;
 #endif
 #ifndef CURL_DISABLE_DIGEST_AUTH
-  else if(avail & CURLAUTH_DIGEST)
+  else if((avail & CURLAUTH_DIGEST) && have_user_pass)
     pick->picked = CURLAUTH_DIGEST;
 #endif
   else if(avail & CURLAUTH_NTLM)
     pick->picked = CURLAUTH_NTLM;
 #ifndef CURL_DISABLE_BASIC_AUTH
-  else if(avail & CURLAUTH_BASIC)
+  else if((avail & CURLAUTH_BASIC) && have_user_pass)
     pick->picked = CURLAUTH_BASIC;
 #endif
 #ifndef CURL_DISABLE_AWS
@@ -568,7 +570,7 @@ CURLcode Curl_http_auth_act(struct Curl_easy *data)
   if(data->state.creds &&
      ((data->req.httpcode == 401) ||
       (data->req.authneg && data->req.httpcode < 300))) {
-    pickhost = pickoneauth(&data->state.authhost, authmask);
+    pickhost = pickoneauth(&data->state.authhost, authmask, data->state.creds);
     if(!pickhost)
       data->state.authproblem = TRUE;
     else
@@ -586,7 +588,8 @@ CURLcode Curl_http_auth_act(struct Curl_easy *data)
      ((data->req.httpcode == 407) ||
       (data->req.authneg && data->req.httpcode < 300))) {
     pickproxy = pickoneauth(&data->state.authproxy,
-                            authmask & ~CURLAUTH_BEARER);
+                            authmask & ~CURLAUTH_BEARER,
+                            conn->http_proxy.creds);
     if(!pickproxy)
       data->state.authproblem = TRUE;
     else
@@ -699,10 +702,12 @@ static CURLcode output_auth_headers(struct Curl_easy *data,
     if(
 #ifndef CURL_DISABLE_PROXY
        (proxy && conn->http_proxy.creds &&
+        Curl_creds_has_user_or_pass(conn->http_proxy.creds) &&
         !Curl_checkProxyheaders(data, conn,
                                 STRCONST("Proxy-authorization"))) ||
 #endif
        (!proxy && data->state.creds &&
+        Curl_creds_has_user_or_pass(data->state.creds) &&
         !Curl_checkheaders(data, STRCONST("Authorization")))) {
       auth = "Basic";
       result = http_output_basic(data, conn, proxy);
index 0d5277d55bceb34ed057ad0c206d74dd7e29b12c..a1d75ab3051fdb635004af86f96b6e882a20da0a 100644 (file)
@@ -256,6 +256,7 @@ test2088 test2089 test2090 test2091 test2092 \
 test2100 test2101 test2102 test2103 test2104 test2105 test2106 test2107 \
 \
 test2200 test2201 test2202 test2203 test2204 test2205 test2206 test2207 \
+test2208 \
 \
 test2300 test2301 test2302 test2303 test2304 test2306 test2307 test2308 \
 test2309 test2310 \
diff --git a/tests/data/test2208 b/tests/data/test2208
new file mode 100644 (file)
index 0000000..354d1a2
--- /dev/null
@@ -0,0 +1,90 @@
+<?xml version="1.0" encoding="US-ASCII"?>
+<testcase>
+<info>
+<keywords>
+HTTP
+HTTP proxy
+--location
+HTTP Basic auth
+</keywords>
+</info>
+
+# Server-side
+<reply>
+<data>
+HTTP/1.1 301 redirect
+Date: Tue, 09 Nov 2010 14:49:00 GMT
+Server: test-server/fake
+Content-Length: 0
+Connection: close
+Content-Type: text/html
+Location: http://@firsthost.com:9999/a/path/%TESTNUMBER0002
+
+</data>
+<data2>
+HTTP/1.1 200 OK
+Date: Tue, 09 Nov 2010 14:49:00 GMT
+Server: test-server/fake
+Content-Length: 4
+Connection: close
+Content-Type: text/html
+
+hey
+</data2>
+
+<datacheck>
+HTTP/1.1 301 redirect
+Date: Tue, 09 Nov 2010 14:49:00 GMT
+Server: test-server/fake
+Content-Length: 0
+Connection: close
+Content-Type: text/html
+Location: http://@firsthost.com:9999/a/path/%TESTNUMBER0002
+
+HTTP/1.1 200 OK
+Date: Tue, 09 Nov 2010 14:49:00 GMT
+Server: test-server/fake
+Content-Length: 4
+Connection: close
+Content-Type: text/html
+
+hey
+</datacheck>
+
+</reply>
+
+# Client-side
+<client>
+<features>
+proxy
+</features>
+<server>
+http
+</server>
+<name>
+HTTP auth on redirect with empty URL userinfo
+</name>
+<command>
+-x http://%HOSTIP:%HTTPPORT http://firsthost.com -L -u joe:secret
+</command>
+</client>
+
+# Verify data after the test has been "shot"
+<verify>
+<protocol crlf="headers">
+GET http://firsthost.com/ HTTP/1.1
+Host: firsthost.com
+Authorization: Basic %b64[joe:secret]b64%
+User-Agent: curl/%VERSION
+Accept: */*
+Proxy-Connection: Keep-Alive
+
+GET http://firsthost.com:9999/a/path/%TESTNUMBER0002 HTTP/1.1
+Host: firsthost.com:9999
+User-Agent: curl/%VERSION
+Accept: */*
+Proxy-Connection: Keep-Alive
+
+</protocol>
+</verify>
+</testcase>