curl/0037-curl-7.61.1-CVE-2022-27776.patch

711 lines
21 KiB
Diff

From 24ff6b126726201cf778038c332b3b921c7f5b2f Mon Sep 17 00:00:00 2001
From: Katsuhiko YOSHIDA <claddvd@gmail.com>
Date: Sun, 30 Dec 2018 09:44:30 +0900
Subject: [PATCH 1/6] cookies: skip custom cookies when redirecting cross-site
Closes #3417
Upstream-commit: 1f30dc886d1a4a6e81599a9f5f5e9f60d97801d4
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
docs/libcurl/opts/CURLOPT_HTTPHEADER.3 | 4 ++
lib/http.c | 3 +-
tests/data/Makefile.inc | 2 +-
tests/data/test330 | 90 ++++++++++++++++++++++++++
4 files changed, 97 insertions(+), 2 deletions(-)
create mode 100644 tests/data/test330
diff --git a/docs/libcurl/opts/CURLOPT_HTTPHEADER.3 b/docs/libcurl/opts/CURLOPT_HTTPHEADER.3
index f5826e1..4af69f4 100644
--- a/docs/libcurl/opts/CURLOPT_HTTPHEADER.3
+++ b/docs/libcurl/opts/CURLOPT_HTTPHEADER.3
@@ -88,6 +88,10 @@ those servers will get all the contents of your custom headers too.
Starting in 7.58.0, libcurl will specifically prevent "Authorization:" headers
from being sent to other hosts than the first used one, unless specifically
permitted with the \fICURLOPT_UNRESTRICTED_AUTH(3)\fP option.
+
+Starting in 7.64.0, libcurl will specifically prevent "Cookie:" headers
+from being sent to other hosts than the first used one, unless specifically
+permitted with the \fICURLOPT_UNRESTRICTED_AUTH(3)\fP option.
.SH DEFAULT
NULL
.SH PROTOCOLS
diff --git a/lib/http.c b/lib/http.c
index bf19077..0b5e476 100644
--- a/lib/http.c
+++ b/lib/http.c
@@ -1774,7 +1774,8 @@ CURLcode Curl_add_custom_headers(struct connectdata *conn,
checkprefix("Transfer-Encoding:", headers->data))
/* HTTP/2 doesn't support chunked requests */
;
- else if(checkprefix("Authorization:", headers->data) &&
+ else if((checkprefix("Authorization:", headers->data) ||
+ checkprefix("Cookie:", headers->data)) &&
/* be careful of sending this potentially sensitive header to
other hosts */
(data->state.this_is_a_follow &&
diff --git a/tests/data/Makefile.inc b/tests/data/Makefile.inc
index e0f1ef4..77e85fd 100644
--- a/tests/data/Makefile.inc
+++ b/tests/data/Makefile.inc
@@ -56,7 +56,7 @@ test289 test290 test291 test292 test293 test294 test295 test296 test297 \
test298 test299 test300 test301 test302 test303 test304 test305 test306 \
test307 test308 test309 test310 test311 test312 test313 test314 test315 \
test316 test317 test318 test319 test320 test321 test322 test323 test324 \
-test325 test326 \
+test325 test326 test330 \
\
test340 \
\
diff --git a/tests/data/test330 b/tests/data/test330
new file mode 100644
index 0000000..74607d5
--- /dev/null
+++ b/tests/data/test330
@@ -0,0 +1,90 @@
+<testcase>
+<info>
+<keywords>
+HTTP
+followlocation
+cookies
+</keywords>
+</info>
+#
+# Server-side
+<reply>
+<data>
+HTTP/1.1 302 OK
+Date: Thu, 09 Nov 2010 14:49:00 GMT
+Server: test-server/fake swsclose
+Content-Type: text/html
+Funny-head: yesyes
+Location: http://goto.second.host.now/3170002
+Content-Length: 8
+Connection: close
+
+contents
+</data>
+<data2>
+HTTP/1.1 200 OK
+Date: Thu, 09 Nov 2010 14:49:00 GMT
+Server: test-server/fake swsclose
+Content-Type: text/html
+Funny-head: yesyes
+Content-Length: 9
+
+contents
+</data2>
+
+<datacheck>
+HTTP/1.1 302 OK
+Date: Thu, 09 Nov 2010 14:49:00 GMT
+Server: test-server/fake swsclose
+Content-Type: text/html
+Funny-head: yesyes
+Location: http://goto.second.host.now/3170002
+Content-Length: 8
+Connection: close
+
+HTTP/1.1 200 OK
+Date: Thu, 09 Nov 2010 14:49:00 GMT
+Server: test-server/fake swsclose
+Content-Type: text/html
+Funny-head: yesyes
+Content-Length: 9
+
+contents
+</datacheck>
+</reply>
+
+#
+# Client-side
+<client>
+<server>
+http
+</server>
+ <name>
+HTTP with custom Cookie: and redirect to new host
+ </name>
+ <command>
+http://first.host.it.is/we/want/that/page/317 -x %HOSTIP:%HTTPPORT -H "Cookie: test=yes" --location
+</command>
+</client>
+
+#
+# Verify data after the test has been "shot"
+<verify>
+<strip>
+^User-Agent:.*
+</strip>
+<protocol>
+GET http://first.host.it.is/we/want/that/page/317 HTTP/1.1
+Host: first.host.it.is
+Accept: */*
+Proxy-Connection: Keep-Alive
+Cookie: test=yes
+
+GET http://goto.second.host.now/3170002 HTTP/1.1
+Host: goto.second.host.now
+Accept: */*
+Proxy-Connection: Keep-Alive
+
+</protocol>
+</verify>
+</testcase>
--
2.34.1
From a3f3855c8bf3a39ef0d86ef04087c200bca765f1 Mon Sep 17 00:00:00 2001
From: Daniel Stenberg <daniel@haxx.se>
Date: Thu, 19 Dec 2019 16:45:53 +0100
Subject: [PATCH 2/6] sws: search for "Testno:" header uncondtionally if no
testno
Even if the initial request line wasn't found. With the fix to 1455, the
test number is now detected correctly.
(Problem found when running tests in random order.)
Closes #4744
Upstream-commit: 25b69c482f45c7acd817920bd8fdf68887be51a2
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
tests/data/test1455 | 3 ++-
tests/server/sws.c | 40 +++++++++++++++++++++++-----------------
2 files changed, 25 insertions(+), 18 deletions(-)
diff --git a/tests/data/test1455 b/tests/data/test1455
index 0b77dc4..25f742e 100644
--- a/tests/data/test1455
+++ b/tests/data/test1455
@@ -35,7 +35,7 @@ http
HTTP GET when PROXY Protocol enabled
</name>
<command>
-http://%HOSTIP:%HTTPPORT/1455 --haproxy-protocol
+http://%HOSTIP:%HTTPPORT/1455 --haproxy-protocol -H "Testno: 1455"
</command>
</client>
@@ -53,6 +53,7 @@ proxy-line
GET /1455 HTTP/1.1
Host: %HOSTIP:%HTTPPORT
Accept: */*
+Testno: 1455
</protocol>
</verify>
diff --git a/tests/server/sws.c b/tests/server/sws.c
index fbe7761..4ece830 100644
--- a/tests/server/sws.c
+++ b/tests/server/sws.c
@@ -367,6 +367,8 @@ static int parse_servercmd(struct httprequest *req)
filename = test2file(req->testno);
req->close = FALSE;
+ req->connmon = FALSE;
+
stream = fopen(filename, "rb");
if(!stream) {
error = errno;
@@ -391,8 +393,6 @@ static int parse_servercmd(struct httprequest *req)
return 1; /* done */
}
- req->connmon = FALSE;
-
cmd = orgcmd;
while(cmd && cmdsize) {
char *check;
@@ -548,12 +548,11 @@ static int ProcessRequest(struct httprequest *req)
snprintf(logbuf, sizeof(logbuf), "Requested test number %ld part %ld",
req->testno, req->partno);
logmsg("%s", logbuf);
-
- /* find and parse <servercmd> for this test */
- parse_servercmd(req);
}
- else
+ else {
+ logmsg("No test number");
req->testno = DOCNUMBER_NOTHING;
+ }
}
@@ -613,14 +612,6 @@ static int ProcessRequest(struct httprequest *req)
}
}
- if(req->testno == DOCNUMBER_NOTHING) {
- /* check for a Testno: header with the test case number */
- char *testno = strstr(line, "\nTestno: ");
- if(testno) {
- req->testno = strtol(&testno[9], NULL, 10);
- logmsg("Found test number %d in Testno: header!", req->testno);
- }
- }
if(req->testno == DOCNUMBER_NOTHING) {
/* Still no test case number. Try to get the the number off the last dot
instead, IE we consider the TLD to be the test number. Test 123 can
@@ -661,8 +652,8 @@ static int ProcessRequest(struct httprequest *req)
}
}
else if((req->offset >= 3) && (req->testno == DOCNUMBER_NOTHING)) {
- logmsg("** Unusual request. Starts with %02x %02x %02x",
- line[0], line[1], line[2]);
+ logmsg("** Unusual request. Starts with %02x %02x %02x (%c%c%c)",
+ line[0], line[1], line[2], line[0], line[1], line[2]);
}
if(!end) {
@@ -670,7 +661,22 @@ static int ProcessRequest(struct httprequest *req)
logmsg("request not complete yet");
return 0; /* not complete yet */
}
- logmsg("- request found to be complete");
+ logmsg("- request found to be complete (%d)", req->testno);
+
+ if(req->testno == DOCNUMBER_NOTHING) {
+ /* check for a Testno: header with the test case number */
+ char *testno = strstr(line, "\nTestno: ");
+ if(testno) {
+ req->testno = strtol(&testno[9], NULL, 10);
+ logmsg("Found test number %d in Testno: header!", req->testno);
+ }
+ else {
+ logmsg("No Testno: header");
+ }
+ }
+
+ /* find and parse <servercmd> for this test */
+ parse_servercmd(req);
if(use_gopher) {
/* when using gopher we cannot check the request until the entire
--
2.34.1
From 3772ea764c05a1cf37b96c091ae266138e8a2867 Mon Sep 17 00:00:00 2001
From: Daniel Stenberg <daniel@haxx.se>
Date: Thu, 16 Apr 2020 14:16:22 +0200
Subject: [PATCH 3/6] runtests: always put test number in servercmd file
Upstream-commit: d1a2816b4128faa8ebc50ce93285c7364652856e
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
tests/runtests.pl | 10 +++-------
1 file changed, 3 insertions(+), 7 deletions(-)
diff --git a/tests/runtests.pl b/tests/runtests.pl
index a0fd991..8d8ed81 100755
--- a/tests/runtests.pl
+++ b/tests/runtests.pl
@@ -3878,10 +3878,9 @@ sub singletest {
unlink($SERVER2IN);
unlink($PROXYIN);
- if(@ftpservercmd) {
- # write the instructions to file
- writearray($FTPDCMD, \@ftpservercmd);
- }
+ push @ftpservercmd, "Testnum $testnum\n";
+ # write the instructions to file
+ writearray($FTPDCMD, \@ftpservercmd);
# get the command line options to use
my @blaha;
@@ -4222,9 +4221,6 @@ sub singletest {
}
}
- # remove the test server commands file after each test
- unlink($FTPDCMD) if(-f $FTPDCMD);
-
# run the postcheck command
my @postcheck= getpart("client", "postcheck");
if(@postcheck) {
--
2.34.1
From ac04f6feaa19c636aa09a1b50643d70a77be4465 Mon Sep 17 00:00:00 2001
From: Daniel Stenberg <daniel@haxx.se>
Date: Thu, 14 May 2020 17:45:40 +0200
Subject: [PATCH 4/6] sws: as last resort, get test number from server cmd file
If it can't be found in the request. Also support --cmdfile to set it to
a custom file name.
runtests.pl always writes this file with the test number in it since a
while back.
Upstream-commit: a3b0699d5c110270f09ac51b5b465ca8753b35a9
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
tests/server/sws.c | 68 ++++++++++++++++++++++++++++++++++------------
1 file changed, 51 insertions(+), 17 deletions(-)
diff --git a/tests/server/sws.c b/tests/server/sws.c
index 4ece830..2696872 100644
--- a/tests/server/sws.c
+++ b/tests/server/sws.c
@@ -155,6 +155,10 @@ const char *serverlogfile = DEFAULT_LOGFILE;
#define REQUEST_PROXY_DUMP "log/proxy.input"
#define RESPONSE_PROXY_DUMP "log/proxy.response"
+/* file in which additional instructions may be found */
+#define DEFAULT_CMDFILE "log/ftpserver.cmd"
+const char *cmdfile = DEFAULT_CMDFILE;
+
/* very-big-path support */
#define MAXDOCNAMELEN 140000
#define MAXDOCNAMELEN_TXT "139999"
@@ -358,6 +362,24 @@ static bool socket_domain_is_ip(void)
}
}
+/* parse the file on disk that might have a test number for us */
+static int parse_cmdfile(struct httprequest *req)
+{
+ int testnum = DOCNUMBER_NOTHING;
+ char buf[256];
+ FILE *f = fopen(cmdfile, FOPEN_READTEXT);
+ if(f) {
+ while(fgets(buf, sizeof(buf), f)) {
+ if(1 == sscanf(buf, "Testnum %d", &testnum)) {
+ logmsg("[%s] cmdfile says testnum %d", cmdfile, testnum);
+ req->testno = testnum;
+ }
+ }
+ fclose(f);
+ }
+ return 0;
+}
+
/* based on the testno, parse the correct server commands */
static int parse_servercmd(struct httprequest *req)
{
@@ -622,34 +644,41 @@ static int ProcessRequest(struct httprequest *req)
/* get the number after it */
if(ptr) {
+ long num;
ptr++; /* skip the dot */
- req->testno = strtol(ptr, &ptr, 10);
+ num = strtol(ptr, &ptr, 10);
- if(req->testno > 10000) {
- req->partno = req->testno % 10000;
- req->testno /= 10000;
+ if(num) {
+ req->testno = num;
+ if(req->testno > 10000) {
+ req->partno = req->testno % 10000;
+ req->testno /= 10000;
- logmsg("found test %d in requested host name", req->testno);
+ logmsg("found test %d in requested host name", req->testno);
+ }
+ else
+ req->partno = 0;
}
- else
- req->partno = 0;
- snprintf(logbuf, sizeof(logbuf),
- "Requested test number %ld part %ld (from host name)",
+ if(req->testno != DOCNUMBER_NOTHING) {
+ logmsg("Requested test number %ld part %ld (from host name)",
req->testno, req->partno);
- logmsg("%s", logbuf);
-
+ }
}
+ }
- if(!req->testno) {
- logmsg("Did not find test number in PATH");
- req->testno = DOCNUMBER_404;
- }
- else
- parse_servercmd(req);
+ if(req->testno == DOCNUMBER_NOTHING)
+ /* might get the test number */
+ parse_cmdfile(req);
+
+ if(req->testno == DOCNUMBER_NOTHING) {
+ logmsg("Did not find test number in PATH");
+ req->testno = DOCNUMBER_404;
}
+ else
+ parse_servercmd(req);
}
else if((req->offset >= 3) && (req->testno == DOCNUMBER_NOTHING)) {
logmsg("** Unusual request. Starts with %02x %02x %02x (%c%c%c)",
@@ -2038,6 +2067,11 @@ int main(int argc, char *argv[])
if(argc>arg)
serverlogfile = argv[arg++];
}
+ else if(!strcmp("--cmdfile", argv[arg])) {
+ arg++;
+ if(argc>arg)
+ cmdfile = argv[arg++];
+ }
else if(!strcmp("--gopher", argv[arg])) {
arg++;
use_gopher = TRUE;
--
2.34.1
From 9fa56a1e3ae7feff14668d8abd892fa028a9f32e Mon Sep 17 00:00:00 2001
From: Daniel Stenberg <daniel@haxx.se>
Date: Mon, 25 Apr 2022 13:05:40 +0200
Subject: [PATCH 5/6] http: avoid auth/cookie on redirects same host diff port
CVE-2022-27776
Reported-by: Harry Sintonen
Bug: https://curl.se/docs/CVE-2022-27776.html
Closes #8749
Upstream-commit: 6e659993952aa5f90f48864be84a1bbb047fc258
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
lib/http.c | 33 +++++++++++++++++++++------------
lib/urldata.h | 16 +++++++++-------
2 files changed, 30 insertions(+), 19 deletions(-)
diff --git a/lib/http.c b/lib/http.c
index 0b5e476..39fc7aa 100644
--- a/lib/http.c
+++ b/lib/http.c
@@ -688,6 +688,21 @@ output_auth_headers(struct connectdata *conn,
return CURLE_OK;
}
+/*
+ * allow_auth_to_host() tells if autentication, cookies or other "sensitive
+ * data" can (still) be sent to this host.
+ */
+static bool allow_auth_to_host(struct connectdata *conn)
+{
+ struct Curl_easy *data = conn->data;
+ return (!data->state.this_is_a_follow ||
+ data->set.allow_auth_to_other_hosts ||
+ (data->state.first_host &&
+ strcasecompare(data->state.first_host, conn->host.name) &&
+ (data->state.first_remote_port == conn->remote_port) &&
+ (data->state.first_remote_protocol == conn->handler->protocol)));
+}
+
/**
* Curl_http_output_auth() setups the authentication headers for the
* host/proxy and the correct authentication
@@ -756,15 +771,11 @@ Curl_http_output_auth(struct connectdata *conn,
with it */
authproxy->done = TRUE;
- /* To prevent the user+password to get sent to other than the original
- host due to a location-follow, we do some weirdo checks here */
- if(!data->state.this_is_a_follow ||
- conn->bits.netrc ||
- !data->state.first_host ||
- data->set.allow_auth_to_other_hosts ||
- strcasecompare(data->state.first_host, conn->host.name)) {
+ /* 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)
+ || conn->bits.netrc)
result = output_auth_headers(conn, authhost, request, path, FALSE);
- }
else
authhost->done = TRUE;
@@ -1778,10 +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 */
- (data->state.this_is_a_follow &&
- data->state.first_host &&
- !data->set.allow_auth_to_other_hosts &&
- !strcasecompare(data->state.first_host, conn->host.name)))
+ !allow_auth_to_host(conn))
;
else {
result = Curl_add_bufferf(req_buffer, "%s\r\n", headers->data);
@@ -1937,6 +1945,7 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
return CURLE_OUT_OF_MEMORY;
data->state.first_remote_port = conn->remote_port;
+ data->state.first_remote_protocol = conn->handler->protocol;
}
http->writebytecount = http->readbytecount = 0;
diff --git a/lib/urldata.h b/lib/urldata.h
index d3b971c..4bb0a84 100644
--- a/lib/urldata.h
+++ b/lib/urldata.h
@@ -1231,13 +1231,15 @@ struct UrlState {
bytes / second */
bool this_is_a_follow; /* this is a followed Location: request */
bool refused_stream; /* this was refused, try again */
- char *first_host; /* host name of the first (not followed) request.
- if set, this should be the host name that we will
- sent authorization to, no else. Used to make Location:
- following not keep sending user+password... This is
- strdup() data.
- */
- int first_remote_port; /* remote port of the first (not followed) request */
+
+ /* host name, port number and protocol of the first (not followed) request.
+ if set, this should be the host name that we will sent authorization to,
+ no else. Used to make Location: following not keep sending user+password.
+ This is strdup()ed data. */
+ char *first_host;
+ int first_remote_port;
+ unsigned int first_remote_protocol;
+
struct curl_ssl_session *session; /* array of 'max_ssl_sessions' size */
long sessionage; /* number of the most recent session */
unsigned int tempcount; /* number of entries in use in tempwrite, 0 - 3 */
--
2.34.1
From a8bb1e37e22788abaca37c59cf447d690fdcdfa4 Mon Sep 17 00:00:00 2001
From: Daniel Stenberg <daniel@haxx.se>
Date: Mon, 25 Apr 2022 13:05:47 +0200
Subject: [PATCH 6/6] test898: verify the fix for CVE-2022-27776
Do not pass on Authorization headers on redirects to another port
Upstream-commit: afe752e0504ab60bf63787ede0b992cbe1065f78
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
tests/data/Makefile.inc | 2 +-
tests/data/test898 | 91 +++++++++++++++++++++++++++++++++++++++++
2 files changed, 92 insertions(+), 1 deletion(-)
create mode 100644 tests/data/test898
diff --git a/tests/data/Makefile.inc b/tests/data/Makefile.inc
index 77e85fd..58c9e31 100644
--- a/tests/data/Makefile.inc
+++ b/tests/data/Makefile.inc
@@ -99,7 +99,7 @@ test850 test851 test852 test853 test854 test855 test856 test857 test858 \
test859 test860 test861 test862 test863 test864 test865 test866 test867 \
test868 test869 test870 test871 test872 test873 test874 test875 test876 \
test877 test878 test879 test880 test881 test882 test883 test884 test885 \
-test886 test887 test888 test889 test890 test891 \
+test886 test887 test888 test889 test890 test891 test898 \
\
test900 test901 test902 test903 test904 test905 test906 test907 test908 \
test909 test910 test911 test912 test913 test914 test915 test916 test917 \
diff --git a/tests/data/test898 b/tests/data/test898
new file mode 100644
index 0000000..e295c26
--- /dev/null
+++ b/tests/data/test898
@@ -0,0 +1,91 @@
+<testcase>
+<info>
+<keywords>
+HTTP
+--location
+Authorization
+Cookie
+</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/8980002
+
+</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/8980002
+
+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>
+<server>
+http
+</server>
+ <name>
+HTTP with custom auth and cookies redirected to HTTP on a diff port
+ </name>
+ <command>
+-x http://%HOSTIP:%HTTPPORT http://firsthost.com -L -H "Authorization: Basic am9lOnNlY3JldA==" -H "Cookie: userpwd=am9lOnNlY3JldA=="
+</command>
+</client>
+
+#
+# Verify data after the test has been "shot"
+<verify>
+<strip>
+^User-Agent:.*
+</strip>
+<protocol>
+GET http://firsthost.com/ HTTP/1.1
+Host: firsthost.com
+Accept: */*
+Proxy-Connection: Keep-Alive
+Authorization: Basic am9lOnNlY3JldA==
+Cookie: userpwd=am9lOnNlY3JldA==
+
+GET http://firsthost.com:9999/a/path/8980002 HTTP/1.1
+Host: firsthost.com:9999
+Accept: */*
+Proxy-Connection: Keep-Alive
+
+</protocol>
+</verify>
+</testcase>
--
2.34.1