From 9bee39bfed2c413b4cc4eb306a57ac92a1854907 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Sat, 12 Oct 2024 23:54:39 +0200 Subject: [PATCH] url: use same credentials on redirect Previously it could lose the username and only use the password. Added test 998 and 999 to verify. Reported-by: Tobias Bora Fixes #15262 Closes #15282 CVE: CVE-2024-11053 Upstream-Status: Backport [https://github.com/curl/curl/commit/9bee39bfed2c413b4cc4eb306a57ac92a1854907] Changes: - Refresh patch context. - Small change in the Makefile to add a new test. Signed-off-by: Yogita Urade --- lib/transfer.c | 3 ++ lib/url.c | 18 ++++---- lib/urldata.h | 8 ++++ tests/data/Makefile.inc | 2 +- tests/data/test998 | 92 +++++++++++++++++++++++++++++++++++++++++ tests/data/test999 | 81 ++++++++++++++++++++++++++++++++++++ 6 files changed, 194 insertions(+), 10 deletions(-) create mode 100644 tests/data/test998 create mode 100644 tests/data/test999 diff --git a/lib/transfer.c b/lib/transfer.c index d567c4b..cd7365b 100644 --- a/lib/transfer.c +++ b/lib/transfer.c @@ -1479,6 +1479,9 @@ CURLcode Curl_pretransfer(struct Curl_easy *data) return CURLE_OUT_OF_MEMORY; } + if(data->set.str[STRING_USERNAME] || + data->set.str[STRING_PASSWORD]) + data->state.creds_from = CREDS_OPTION; if(!result) result = Curl_setstropt(&data->state.aptr.user, data->set.str[STRING_USERNAME]); diff --git a/lib/url.c b/lib/url.c index 9406cca..99d1082 100644 --- a/lib/url.c +++ b/lib/url.c @@ -2098,10 +2098,10 @@ static CURLcode parseurlandfillconn(struct Curl_easy *data, return result; /* - * User name and password set with their own options override the - * credentials possibly set in the URL. + * username and password set with their own options override the credentials + * possibly set in the URL, but netrc does not. */ - if(!data->state.aptr.passwd) { + if(!data->state.aptr.passwd || (data->state.creds_from != CREDS_OPTION)) { uc = curl_url_get(uh, CURLUPART_PASSWORD, &data->state.up.password, 0); if(!uc) { char *decoded; @@ -2112,6 +2112,7 @@ static CURLcode parseurlandfillconn(struct Curl_easy *data, return result; conn->passwd = decoded; result = Curl_setstropt(&data->state.aptr.passwd, decoded); + data->state.creds_from = CREDS_URL; if(result) return result; } @@ -2119,7 +2120,7 @@ static CURLcode parseurlandfillconn(struct Curl_easy *data, return Curl_uc_to_curlcode(uc); } - if(!data->state.aptr.user) { + if(!data->state.aptr.user || (data->state.creds_from != CREDS_OPTION)) { /* we don't use the URL API's URL decoder option here since it rejects control codes and we want to allow them for some schemes in the user and password fields */ @@ -2133,13 +2134,10 @@ static CURLcode parseurlandfillconn(struct Curl_easy *data, return result; conn->user = decoded; result = Curl_setstropt(&data->state.aptr.user, decoded); + data->state.creds_from = CREDS_URL; } else if(uc != CURLUE_NO_USER) return Curl_uc_to_curlcode(uc); - else if(data->state.aptr.passwd) { - /* no user was set but a password, set a blank user */ - result = Curl_setstropt(&data->state.aptr.user, ""); - } if(result) return result; } @@ -3032,7 +3030,8 @@ static CURLcode override_login(struct Curl_easy *data, if(result) return result; } - if(data->state.aptr.user) { + if(data->state.aptr.user && + (data->state.creds_from != CREDS_NETRC)) { uc = curl_url_set(data->state.uh, CURLUPART_USER, data->state.aptr.user, CURLU_URLENCODE); if(uc) @@ -3048,6 +3047,7 @@ static CURLcode override_login(struct Curl_easy *data, CURLcode result = Curl_setstropt(&data->state.aptr.passwd, *passwdp); if(result) return result; + data->state.creds_from = CREDS_NETRC; } if(data->state.aptr.passwd) { uc = curl_url_set(data->state.uh, CURLUPART_PASSWORD, diff --git a/lib/urldata.h b/lib/urldata.h index e78a7e8..d252e73 100644 --- a/lib/urldata.h +++ b/lib/urldata.h @@ -1324,6 +1324,11 @@ struct urlpieces { char *query; }; +#define CREDS_NONE 0 +#define CREDS_URL 1 /* from URL */ +#define CREDS_OPTION 2 /* set with a CURLOPT_ */ +#define CREDS_NETRC 3 /* found in netrc */ + struct UrlState { /* Points to the connection cache */ struct conncache *conn_cache; @@ -1454,6 +1459,9 @@ struct UrlState { char *proxypasswd; } aptr; + unsigned int creds_from:2; /* where is the server credentials originating + from, see the CREDS_* defines above */ + #ifdef CURLDEBUG BIT(conncache_lock); #endif diff --git a/tests/data/Makefile.inc b/tests/data/Makefile.inc index 5415f37..00cdfb8 100644 --- a/tests/data/Makefile.inc +++ b/tests/data/Makefile.inc @@ -123,7 +123,7 @@ test954 test955 test956 test957 test958 test959 test960 test961 test962 \ test963 test964 test965 test966 test967 test968 test969 test970 test971 \ test972 \ \ -test980 test981 test982 test983 test984 test985 test986 \ +test980 test981 test982 test983 test984 test985 test986 test998 test999 \ \ test1000 test1001 test1002 test1003 test1004 test1005 test1006 test1007 \ test1008 test1009 test1010 test1011 test1012 test1013 test1014 test1015 \ diff --git a/tests/data/test998 b/tests/data/test998 new file mode 100644 index 0000000..6dcd95f --- /dev/null +++ b/tests/data/test998 @@ -0,0 +1,92 @@ + + + +HTTP +--location-trusted + + + +# +# Server-side + + +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://somewhere.else.example/a/path/%TESTNUMBER0002 + + + +HTTP/1.1 200 OK +Date: Tue, 09 Nov 2010 14:49:00 GMT +Content-Length: 6 +Content-Type: text/html +Funny-head: yesyes + +-foo- + + + +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://somewhere.else.example/a/path/%TESTNUMBER0002 + +HTTP/1.1 200 OK +Date: Tue, 09 Nov 2010 14:49:00 GMT +Content-Length: 6 +Content-Type: text/html +Funny-head: yesyes + +-foo- + + + + +# +# Client-side + + +proxy + + +http + + +HTTP with auth in URL redirected to another host + + +-x %HOSTIP:%HTTPPORT http://alberto:einstein@somwhere.example/%TESTNUMBER --location-trusted + + + +# +# Verify data after the test has been "shot" + + +QUIT + + +GET http://somwhere.example/998 HTTP/1.1 +Host: somwhere.example +Authorization: Basic YWxiZXJ0bzplaW5zdGVpbg== +User-Agent: curl/%VERSION +Accept: */* +Proxy-Connection: Keep-Alive + +GET http://somewhere.else.example/a/path/9980002 HTTP/1.1 +Host: somewhere.else.example +Authorization: Basic YWxiZXJ0bzplaW5zdGVpbg== +User-Agent: curl/%VERSION +Accept: */* +Proxy-Connection: Keep-Alive + + + + diff --git a/tests/data/test999 b/tests/data/test999 new file mode 100644 index 0000000..e805cde --- /dev/null +++ b/tests/data/test999 @@ -0,0 +1,81 @@ + + + +HTTP +--location-trusted + + + +# +# Server-side + + +HTTP/1.1 200 OK +Date: Tue, 09 Nov 2010 14:49:00 GMT +Content-Length: 6 +Content-Type: text/html +Funny-head: yesyes + +-foo- + + + +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://somewhere.else.example/a/path/%TESTNUMBER0002 + +HTTP/1.1 200 OK +Date: Tue, 09 Nov 2010 14:49:00 GMT +Content-Length: 6 +Content-Type: text/html +Funny-head: yesyes + +-foo- + + + + +# +# Client-side + + +proxy + + +http + + +HTTP with auth in first URL but not second + + +-x %HOSTIP:%HTTPPORT http://alberto:einstein@somwhere.example/%TESTNUMBER http://somewhere.else.example/%TESTNUMBER + + + +# +# Verify data after the test has been "shot" + + +QUIT + + +GET http://somwhere.example/%TESTNUMBER HTTP/1.1 +Host: somwhere.example +Authorization: Basic YWxiZXJ0bzplaW5zdGVpbg== +User-Agent: curl/%VERSION +Accept: */* +Proxy-Connection: Keep-Alive + +GET http://somewhere.else.example/%TESTNUMBER HTTP/1.1 +Host: somewhere.else.example +User-Agent: curl/%VERSION +Accept: */* +Proxy-Connection: Keep-Alive + + + + -- 2.40.0