From ed305ee2c9f5b16e53c0233a34191dcfb89fda04 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Sat, 12 Oct 2024 23:54:39 +0200 Subject: [PATCH] [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 Backported by: Matheus Polkorny . Changes: - Refresh patch context - Small change in the Makefile to add a new test Gbp-Pq: Name url-use-same-credentials-on-redirect.patch --- lib/transfer.c | 3 ++ lib/url.c | 19 +++++---- lib/urldata.h | 8 ++++ tests/data/Makefile.inc | 2 + tests/data/test998 | 92 +++++++++++++++++++++++++++++++++++++++++ tests/data/test999 | 81 ++++++++++++++++++++++++++++++++++++ 6 files changed, 196 insertions(+), 9 deletions(-) create mode 100644 tests/data/test998 create mode 100644 tests/data/test999 diff --git a/lib/transfer.c b/lib/transfer.c index 6da96a09..9a3c7cd2 100644 --- a/lib/transfer.c +++ b/lib/transfer.c @@ -1419,6 +1419,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 6900f11c..e6155dc0 100644 --- a/lib/url.c +++ b/lib/url.c @@ -1880,10 +1880,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; @@ -1896,12 +1896,13 @@ static CURLcode parseurlandfillconn(struct Curl_easy *data, result = Curl_setstropt(&data->state.aptr.passwd, decoded); if(result) return result; + data->state.creds_from = CREDS_URL; } else if(uc != CURLUE_NO_PASSWORD) return Curl_uc_to_curlcode(uc); } - if(!data->set.str[STRING_USERNAME]) { + 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 */ @@ -1915,13 +1916,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; } @@ -2733,7 +2731,8 @@ static CURLcode override_login(struct Curl_easy *data, int ret; bool url_provided = FALSE; - if(data->state.aptr.user) { + if(data->state.aptr.user && + (data->state.creds_from != CREDS_NETRC)) { /* there was a user name in the URL. Use the URL decoded version */ userp = &data->state.aptr.user; url_provided = TRUE; @@ -2779,6 +2778,7 @@ static CURLcode override_login(struct Curl_easy *data, result = Curl_setstropt(&data->state.aptr.user, *userp); if(result) return result; + data->state.creds_from = CREDS_NETRC; } } if(data->state.aptr.user) { @@ -2796,6 +2796,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 68d42f80..a222f870 100644 --- a/lib/urldata.h +++ b/lib/urldata.h @@ -1270,6 +1270,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; @@ -1405,6 +1410,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 379e6e0f..d69262e3 100644 --- a/tests/data/Makefile.inc +++ b/tests/data/Makefile.inc @@ -128,6 +128,8 @@ test972 test973 test974 test975 test976 test977 \ \ 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 \ test1016 test1017 test1018 test1019 test1020 test1021 test1022 test1023 \ diff --git a/tests/data/test998 b/tests/data/test998 new file mode 100644 index 00000000..c3a8f516 --- /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 00000000..990a8d09 --- /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.30.2