From 48f126157d36962e458bf12f90b50cfcef26eee9 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Mon, 25 Apr 2022 16:24:33 +0200 Subject: [PATCH 1/4] connect: store "conn_remote_port" in the info struct To make it available after the connection ended. Upstream-commit: 08b8ef4e726ba10f45081ecda5b3cea788d3c839 Signed-off-by: Kamil Dudka --- lib/connect.c | 1 + lib/urldata.h | 6 +++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/connect.c b/lib/connect.c index f724646..12a8aae 100644 --- a/lib/connect.c +++ b/lib/connect.c @@ -614,6 +614,7 @@ void Curl_persistconninfo(struct connectdata *conn) conn->data->info.conn_scheme = conn->handler->scheme; conn->data->info.conn_protocol = conn->handler->protocol; conn->data->info.conn_primary_port = conn->primary_port; + conn->data->info.conn_remote_port = conn->remote_port; conn->data->info.conn_local_port = conn->local_port; } diff --git a/lib/urldata.h b/lib/urldata.h index 4bb0a84..cadf0e5 100644 --- a/lib/urldata.h +++ b/lib/urldata.h @@ -1050,7 +1050,11 @@ struct PureInfo { reused, in the connection cache. */ char conn_primary_ip[MAX_IPADR_LEN]; - long conn_primary_port; + long conn_primary_port;/* this is the destination port to the connection, + which might have been a proxy */ + int conn_remote_port; /* this is the "remote port", which is the port + number of the used URL, independent of proxy or + not */ char conn_local_ip[MAX_IPADR_LEN]; long conn_local_port; -- 2.34.1 From 6307fa6f9784402ba58697f46ba04354225391b7 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Mon, 25 Apr 2022 16:24:33 +0200 Subject: [PATCH 2/4] transfer: redirects to other protocols or ports clear auth ... unless explicitly permitted. Bug: https://curl.se/docs/CVE-2022-27774.html Reported-by: Harry Sintonen Closes #8748 Upstream-commit: 620ea21410030a9977396b4661806bc187231b79 Signed-off-by: Kamil Dudka --- lib/transfer.c | 63 ++++++++++++++++++++++++++++++++++++++++++++++++++ lib/url.c | 27 ++++++++++++++-------- lib/urldata.h | 1 + 3 files changed, 81 insertions(+), 10 deletions(-) diff --git a/lib/transfer.c b/lib/transfer.c index ad5a7ba..2022cba 100644 --- a/lib/transfer.c +++ b/lib/transfer.c @@ -1370,6 +1370,7 @@ CURLcode Curl_pretransfer(struct Curl_easy *data) data->state.wildcardmatch = data->set.wildcard_enabled; data->set.followlocation = 0; /* reset the location-follow counter */ data->state.this_is_a_follow = FALSE; /* reset this */ + data->state.this_is_a_follow_without_auth = FALSE; data->state.errorbuf = FALSE; /* no error has occurred */ data->state.httpversion = 0; /* don't assume any particular server version */ @@ -1554,6 +1555,68 @@ CURLcode Curl_follow(struct Curl_easy *data, } + /* Clear auth if this redirects to a different port number or protocol, + unless permitted */ + if(!data->set.allow_auth_to_other_hosts && (type != FOLLOW_FAKE)) { + int port; + bool clear = FALSE; + + CURLU *u = curl_url(); + if(!u) + return CURLE_OUT_OF_MEMORY; + + uc = curl_url_set(u, CURLUPART_URL, newurl, + ((type == FOLLOW_REDIR) ? CURLU_URLENCODE : 0)); + if(uc) { + infof(data, "Clear auth, curl_url_set() failed\n"); + clear = TRUE; + } + + if(!clear) { + if(data->set.use_port && data->state.allow_port) + /* a custom port is used */ + port = (int)data->set.use_port; + else { + char *portnum; + uc = curl_url_get(u, CURLUPART_PORT, &portnum, CURLU_DEFAULT_PORT); + if(uc) { + infof(data, "Clear auth, failed to parse port number\n"); + clear = TRUE; + } + else { + port = atoi(portnum); + free(portnum); + } + } + } + if(!clear && port != data->info.conn_remote_port) { + infof(data, "Clear auth, redirects to port from %u to %u\n", + data->info.conn_remote_port, port); + clear = TRUE; + } + if(!clear) { + char *scheme; + const struct Curl_handler *p; + uc = curl_url_get(u, CURLUPART_SCHEME, &scheme, 0); + if(uc) { + infof(data, "Clear auth, failed to parse scheme\n"); + clear = TRUE; + } + else { + p = Curl_builtin_scheme(scheme); + if(p && (p->protocol != data->info.conn_protocol)) { + infof(data, "Clear auth, redirects scheme from %s to %s\n", + data->info.conn_scheme, scheme); + clear = TRUE; + } + free(scheme); + } + } + if(clear) + data->state.this_is_a_follow_without_auth = TRUE; + curl_url_cleanup(u); + } + if(type == FOLLOW_FAKE) { /* we're only figuring out the new url if we would've followed locations but now we're done so we can get out! */ diff --git a/lib/url.c b/lib/url.c index ed3c933..7dd5267 100644 --- a/lib/url.c +++ b/lib/url.c @@ -3483,18 +3483,25 @@ static CURLcode override_login(struct Curl_easy *data, struct connectdata *conn, char **userp, char **passwdp, char **optionsp) { - if(data->set.str[STRING_USERNAME]) { - free(*userp); - *userp = strdup(data->set.str[STRING_USERNAME]); - if(!*userp) - return CURLE_OUT_OF_MEMORY; + if(data->state.this_is_a_follow + && data->state.this_is_a_follow_without_auth) + { + conn->bits.user_passwd = FALSE; } + else { + if(data->set.str[STRING_USERNAME]) { + free(*userp); + *userp = strdup(data->set.str[STRING_USERNAME]); + if(!*userp) + return CURLE_OUT_OF_MEMORY; + } - if(data->set.str[STRING_PASSWORD]) { - free(*passwdp); - *passwdp = strdup(data->set.str[STRING_PASSWORD]); - if(!*passwdp) - return CURLE_OUT_OF_MEMORY; + if(data->set.str[STRING_PASSWORD]) { + free(*passwdp); + *passwdp = strdup(data->set.str[STRING_PASSWORD]); + if(!*passwdp) + return CURLE_OUT_OF_MEMORY; + } } if(data->set.str[STRING_OPTIONS]) { diff --git a/lib/urldata.h b/lib/urldata.h index cadf0e5..026684b 100644 --- a/lib/urldata.h +++ b/lib/urldata.h @@ -1234,6 +1234,7 @@ struct UrlState { curl_off_t current_speed; /* the ProgressShow() function sets this, bytes / second */ bool this_is_a_follow; /* this is a followed Location: request */ + bool this_is_a_follow_without_auth; bool refused_stream; /* this was refused, try again */ /* host name, port number and protocol of the first (not followed) request. -- 2.34.1 From b142f97840dfb033a1776d5a2986385da7753224 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Mon, 25 Apr 2022 16:24:33 +0200 Subject: [PATCH 3/4] tests: verify the fix for CVE-2022-27774 - Test 973 redirects from HTTP to FTP, clear auth - Test 974 redirects from HTTP to HTTP different port, clear auth - Test 975 redirects from HTTP to FTP, permitted to keep auth - Test 976 redirects from HTTP to HTTP different port, permitted to keep auth Upstream-commit: 5295e8d64ac6949ecb3f9e564317a608f51b90d8 Signed-off-by: Kamil Dudka --- tests/data/Makefile.inc | 1 + tests/data/test973 | 90 +++++++++++++++++++++++++++++++++++++++++ tests/data/test974 | 88 ++++++++++++++++++++++++++++++++++++++++ tests/data/test975 | 90 +++++++++++++++++++++++++++++++++++++++++ tests/data/test976 | 89 ++++++++++++++++++++++++++++++++++++++++ 5 files changed, 358 insertions(+) create mode 100644 tests/data/test973 create mode 100644 tests/data/test974 create mode 100644 tests/data/test975 create mode 100644 tests/data/test976 diff --git a/tests/data/Makefile.inc b/tests/data/Makefile.inc index 58c9e31..6c920ff 100644 --- a/tests/data/Makefile.inc +++ b/tests/data/Makefile.inc @@ -108,6 +108,7 @@ test927 test928 test929 test930 test931 test932 test933 test934 test935 \ test936 test937 test938 test939 test940 test941 test942 test943 test944 \ test945 test946 test947 test948 test949 test950 test951 test952 \ \ +test973 test974 test975 test976 \ test980 test981 test982 test983 test984 test985 test986 \ \ test1000 test1001 test1002 test1003 test1004 test1005 test1006 test1007 \ diff --git a/tests/data/test973 b/tests/data/test973 new file mode 100644 index 0000000..6fe6ce0 --- /dev/null +++ b/tests/data/test973 @@ -0,0 +1,90 @@ + + + +HTTP +FTP +--location + + + +# +# 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: ftp://127.0.0.1:8992/a/path/9730002 + + + +data + to + see +that FTP +works + so does it? + + + +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: ftp://127.0.0.1:8992/a/path/9730002 + +data + to + see +that FTP +works + so does it? + + + + +# +# Client-side + + +http +ftp + + +HTTP with auth redirected to FTP w/o auth + + +http://%HOSTIP:%HTTPPORT/973 -L -u joe:secret + + + +# +# Verify data after the test has been "shot" + + +^User-Agent:.* + + +GET /973 HTTP/1.1 +Host: %HOSTIP:%HTTPPORT +Authorization: Basic am9lOnNlY3JldA== +Accept: */* + +USER anonymous +PASS ftp@example.com +PWD +CWD a +CWD path +EPSV +TYPE I +SIZE 9730002 +RETR 9730002 +QUIT + + + diff --git a/tests/data/test974 b/tests/data/test974 new file mode 100644 index 0000000..de02d89 --- /dev/null +++ b/tests/data/test974 @@ -0,0 +1,88 @@ + + + +HTTP +--location + + + +# +# 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://firsthost.com:9999/a/path/9740002 + + + +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 + + + +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/9740002 + +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 + + + + +# +# Client-side + + +http + + +HTTP with auth redirected to HTTP on a diff port w/o auth + + +-x http://%HOSTIP:%HTTPPORT http://firsthost.com -L -u joe:secret + + + +# +# Verify data after the test has been "shot" + + +^User-Agent:.* + + +GET http://firsthost.com/ HTTP/1.1 +Host: firsthost.com +Authorization: Basic am9lOnNlY3JldA== +Accept: */* +Proxy-Connection: Keep-Alive + +GET http://firsthost.com:9999/a/path/9740002 HTTP/1.1 +Host: firsthost.com:9999 +Accept: */* +Proxy-Connection: Keep-Alive + + + + diff --git a/tests/data/test975 b/tests/data/test975 new file mode 100644 index 0000000..3a4eccf --- /dev/null +++ b/tests/data/test975 @@ -0,0 +1,90 @@ + + + +HTTP +FTP +--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: ftp://127.0.0.1:8992/a/path/9750002 + + + +data + to + see +that FTP +works + so does it? + + + +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: ftp://127.0.0.1:8992/a/path/9750002 + +data + to + see +that FTP +works + so does it? + + + + +# +# Client-side + + +http +ftp + + +HTTP with auth redirected to FTP allowing auth to continue + + +http://%HOSTIP:%HTTPPORT/975 --location-trusted -u joe:secret + + + +# +# Verify data after the test has been "shot" + + +^User-Agent:.* + + +GET /975 HTTP/1.1 +Host: %HOSTIP:%HTTPPORT +Authorization: Basic am9lOnNlY3JldA== +Accept: */* + +USER joe +PASS secret +PWD +CWD a +CWD path +EPSV +TYPE I +SIZE 9750002 +RETR 9750002 +QUIT + + + diff --git a/tests/data/test976 b/tests/data/test976 new file mode 100644 index 0000000..3b6fac7 --- /dev/null +++ b/tests/data/test976 @@ -0,0 +1,89 @@ + + + +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://firsthost.com:9999/a/path/9760002 + + + +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 + + + +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/9760002 + +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 + + + + +# +# Client-side + + +http + + +HTTP with auth redirected to HTTP on a diff port --location-trusted + + +-x http://%HOSTIP:%HTTPPORT http://firsthost.com --location-trusted -u joe:secret + + + +# +# Verify data after the test has been "shot" + + +^User-Agent:.* + + +GET http://firsthost.com/ HTTP/1.1 +Host: firsthost.com +Authorization: Basic am9lOnNlY3JldA== +Accept: */* +Proxy-Connection: Keep-Alive + +GET http://firsthost.com:9999/a/path/9760002 HTTP/1.1 +Host: firsthost.com:9999 +Authorization: Basic am9lOnNlY3JldA== +Accept: */* +Proxy-Connection: Keep-Alive + + + + -- 2.34.1 From cf98bd64b9949c50d4726eb26745c2f7fdf3a075 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Mon, 25 Apr 2022 17:59:15 +0200 Subject: [PATCH 4/4] openssl: don't leak the SRP credentials in redirects either Follow-up to 620ea21410030 Reported-by: Harry Sintonen Closes #8751 Upstream-commit: 139a54ed0a172adaaf1a78d6f4fff50b2c3f9e08 Signed-off-by: Kamil Dudka --- lib/http.c | 10 +++++----- lib/http.h | 6 ++++++ lib/vtls/openssl.c | 3 ++- 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/lib/http.c b/lib/http.c index 39fc7aa..d413738 100644 --- a/lib/http.c +++ b/lib/http.c @@ -689,10 +689,10 @@ output_auth_headers(struct connectdata *conn, } /* - * allow_auth_to_host() tells if autentication, cookies or other "sensitive - * data" can (still) be sent to this host. + * Curl_allow_auth_to_host() tells if authentication, cookies or other + * "sensitive data" can (still) be sent to this host. */ -static bool allow_auth_to_host(struct connectdata *conn) +bool Curl_allow_auth_to_host(struct connectdata *conn) { struct Curl_easy *data = conn->data; return (!data->state.this_is_a_follow || @@ -773,7 +773,7 @@ Curl_http_output_auth(struct connectdata *conn, /* To prevent the user+password to get sent to other than the original host due to a location-follow */ - if(allow_auth_to_host(conn) + if(Curl_allow_auth_to_host(conn) || conn->bits.netrc) result = output_auth_headers(conn, authhost, request, path, FALSE); else @@ -1789,7 +1789,7 @@ CURLcode Curl_add_custom_headers(struct connectdata *conn, checkprefix("Cookie:", headers->data)) && /* be careful of sending this potentially sensitive header to other hosts */ - !allow_auth_to_host(conn)) + !Curl_allow_auth_to_host(conn)) ; else { result = Curl_add_bufferf(req_buffer, "%s\r\n", headers->data); diff --git a/lib/http.h b/lib/http.h index 1d373e8..56a6061 100644 --- a/lib/http.h +++ b/lib/http.h @@ -252,5 +252,11 @@ Curl_http_output_auth(struct connectdata *conn, bool proxytunnel); /* TRUE if this is the request setting up the proxy tunnel */ +/* + * Curl_allow_auth_to_host() tells if authentication, cookies or other + * "sensitive data" can (still) be sent to this host. + */ +bool Curl_allow_auth_to_host(struct connectdata *conn); + #endif /* HEADER_CURL_HTTP_H */ diff --git a/lib/vtls/openssl.c b/lib/vtls/openssl.c index 28eaa6d..6c8faa2 100644 --- a/lib/vtls/openssl.c +++ b/lib/vtls/openssl.c @@ -2499,7 +2499,8 @@ static CURLcode ossl_connect_step1(struct connectdata *conn, int sockindex) #endif #ifdef USE_TLS_SRP - if(ssl_authtype == CURL_TLSAUTH_SRP) { + if((ssl_authtype == CURL_TLSAUTH_SRP) && + Curl_allow_auth_to_host(conn)) { char * const ssl_username = SSL_SET_OPTION(username); infof(data, "Using TLS-SRP username: %s\n", ssl_username); -- 2.34.1