From 311c9219c3c270e939ce7d72f3071dc2ab829f86 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Tue, 27 Dec 2022 11:50:23 +0100 Subject: [PATCH 1/7] hsts: handle adding the same host name again It will then use the largest expire time of the two entries. CVE-2023-23914, CVE-2023-23915 Signed-off-by: zhouhaifeng --- lib/hsts.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/lib/hsts.c b/lib/hsts.c index 1d0263cdf..525d2f2b1 100644 --- a/lib/hsts.c +++ b/lib/hsts.c @@ -394,14 +394,23 @@ static CURLcode hsts_add(struct hsts *h, char *line) host, date); if(2 == rc) { time_t expires = Curl_getdate_capped(date); - CURLcode result; + CURLcode result = CURLE_OK; char *p = host; bool subdomain = FALSE; + struct stsentry *e; if(p[0] == '.') { p++; subdomain = TRUE; } - result = hsts_create(h, p, subdomain, expires); + /* only add it if not already present */ + e = Curl_hsts(h, p, subdomain); + if(!e) + result = hsts_create(h, p, subdomain, expires); + else { + /* the same host name, use the largest expire time */ + if(expires > e->expires) + e->expires = expires; + } if(result) return result; } -- Gitee From 918776a3cca34d8e7e0041aa8d103948d30a7671 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Tue, 27 Dec 2022 11:50:23 +0100 Subject: [PATCH 2/7] test446: verify hsts with two URLs CVE-2023-23914, CVE-2023-23915 Signed-off-by: zhouhaifeng --- tests/data/Makefile.inc | 2 +- tests/data/test446 | 84 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 85 insertions(+), 1 deletion(-) create mode 100644 tests/data/test446 diff --git a/tests/data/Makefile.inc b/tests/data/Makefile.inc index 4368ddf3d..bce33d1ce 100644 --- a/tests/data/Makefile.inc +++ b/tests/data/Makefile.inc @@ -69,7 +69,7 @@ test409 test410 \ \ test430 test431 test432 test433 test434 \ \ -test442 test443 test444 test445 \ +test442 test443 test444 test445 test446 \ \ test490 test491 test492 test493 test494 \ \ diff --git a/tests/data/test446 b/tests/data/test446 new file mode 100644 index 000000000..eda302256 --- /dev/null +++ b/tests/data/test446 @@ -0,0 +1,84 @@ + + + + +HTTP +HTTP proxy +HSTS +trailing-dot + + + + + +# we use this as response to a CONNECT + +HTTP/1.1 200 OK + + + +HTTP/1.1 200 OK +Content-Length: 6 +Strict-Transport-Security: max-age=604800 + +-foo- + + +HTTP/1.1 200 OK +Content-Length: 6 +Strict-Transport-Security: max-age=6048000 + +-baa- + + + + + +https +http-proxy + + +HSTS +proxy +https +debug + + +CURL_HSTS_HTTP=yes +CURL_TIME=2000000000 + + + +HSTS with two URLs + + +-x http://%HOSTIP:%PROXYPORT --hsts log/hsts%TESTNUMBER http://this.hsts.example./%TESTNUMBER http://another.example.com/%TESTNUMBER0002 + + + + +# we let it CONNECT to the server to confirm HSTS but deny from there + +GET http://this.hsts.example./%TESTNUMBER HTTP/1.1 +Host: this.hsts.example. +User-Agent: curl/%VERSION +Accept: */* +Proxy-Connection: Keep-Alive + +GET http://another.example.com/%TESTNUMBER0002 HTTP/1.1 +Host: another.example.com +User-Agent: curl/%VERSION +Accept: */* +Proxy-Connection: Keep-Alive + + + + +# Your HSTS cache. https://curl.se/docs/hsts.html +# This file was generated by libcurl! Edit at your own risk. +this.hsts.example "20330525 03:33:20" +another.example.com "20330727 03:33:20" + + + + -- Gitee From 66d72320ff7e3ad7427fa75d1bbdf1b605d3ef10 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Tue, 27 Dec 2022 11:50:23 +0100 Subject: [PATCH 3/7] runtests: support crlf="yes" for verify/proxy CVE-2023-23914, CVE-2023-23915 Signed-off-by: zhouhaifeng --- tests/FILEFORMAT.md | 4 ++-- tests/runtests.pl | 5 +++++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/tests/FILEFORMAT.md b/tests/FILEFORMAT.md index 7a9c48204..e17605beb 100644 --- a/tests/FILEFORMAT.md +++ b/tests/FILEFORMAT.md @@ -527,14 +527,14 @@ changing protocol data such as port numbers or user-agent strings. One perl op per line that operates on the protocol dump. This is pretty advanced. Example: `s/^EPRT .*/EPRT stripped/`. -### `` +### `` the protocol dump curl should transmit, if 'nonewline' is set, we will cut off the trailing newline of this given data before comparing with the one actually sent by the client The `` and `` rules are applied before comparisons are made. -### `` +### `` The protocol dump curl should transmit to a HTTP proxy (when the http-proxy server is used), if 'nonewline' is set, we will cut off the trailing newline diff --git a/tests/runtests.pl b/tests/runtests.pl index 9afc97df0..ecb5f93da 100755 --- a/tests/runtests.pl +++ b/tests/runtests.pl @@ -4470,6 +4470,11 @@ sub singletest { } } + if($hash{'crlf'} || + ($has_hyper && ($keywords{"HTTP"} || $keywords{"HTTPS"}))) { + map subNewlines(0, \$_), @protstrip; + } + $res = compare($testnum, $testname, "proxy", \@out, \@protstrip); if($res) { return $errorreturncode; -- Gitee From 0e6924c5e9c1c2be492475596d37261d0866769b Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Tue, 27 Dec 2022 11:50:23 +0100 Subject: [PATCH 4/7] tool_operate: share HSTS between handles CVE-2023-23914, CVE-2023-23915 Signed-off-by: zhouhaifeng --- src/tool_operate.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/tool_operate.c b/src/tool_operate.c index e6ca575bf..c70259533 100644 --- a/src/tool_operate.c +++ b/src/tool_operate.c @@ -2540,6 +2540,7 @@ CURLcode operate(struct GlobalConfig *global, int argc, argv_item_t argv[]) curl_share_setopt(share, CURLSHOPT_SHARE, CURL_LOCK_DATA_SSL_SESSION); curl_share_setopt(share, CURLSHOPT_SHARE, CURL_LOCK_DATA_CONNECT); curl_share_setopt(share, CURLSHOPT_SHARE, CURL_LOCK_DATA_PSL); + curl_share_setopt(share, CURLSHOPT_SHARE, CURL_LOCK_DATA_HSTS); /* Get the required arguments for each operation */ do { -- Gitee From 3a703a5402d30abdce2ded7de8008ca393d38fa0 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Tue, 27 Dec 2022 11:50:20 +0100 Subject: [PATCH 5/7] share: add sharing of HSTS cache among handles Closes #10138 CVE-2023-23914, CVE-2023-23915 Signed-off-by: zhouhaifeng --- docs/libcurl/symbols-in-versions | 1 + include/curl/curl.h | 1 + lib/hsts.c | 15 ++++++++++ lib/hsts.h | 2 ++ lib/setopt.c | 48 +++++++++++++++++++++++++++----- lib/share.c | 32 +++++++++++++++++++-- lib/share.h | 6 +++- lib/transfer.c | 3 ++ lib/url.c | 6 +++- lib/urldata.h | 2 ++ 10 files changed, 105 insertions(+), 11 deletions(-) diff --git a/docs/libcurl/symbols-in-versions b/docs/libcurl/symbols-in-versions index 2dc0af2c9..abae02d55 100644 --- a/docs/libcurl/symbols-in-versions +++ b/docs/libcurl/symbols-in-versions @@ -943,6 +943,7 @@ CURL_LOCK_ACCESS_SINGLE 7.10.3 CURL_LOCK_DATA_CONNECT 7.10.3 CURL_LOCK_DATA_COOKIE 7.10.3 CURL_LOCK_DATA_DNS 7.10.3 +CURL_LOCK_DATA_HSTS 7.88.0 CURL_LOCK_DATA_NONE 7.10.3 CURL_LOCK_DATA_PSL 7.61.0 CURL_LOCK_DATA_SHARE 7.10.4 diff --git a/include/curl/curl.h b/include/curl/curl.h index 521c254e7..f3bf8f41c 100644 --- a/include/curl/curl.h +++ b/include/curl/curl.h @@ -2824,6 +2824,7 @@ typedef enum { CURL_LOCK_DATA_SSL_SESSION, CURL_LOCK_DATA_CONNECT, CURL_LOCK_DATA_PSL, + CURL_LOCK_DATA_HSTS, CURL_LOCK_DATA_LAST } curl_lock_data; diff --git a/lib/hsts.c b/lib/hsts.c index 525d2f2b1..4de55a91c 100644 --- a/lib/hsts.c +++ b/lib/hsts.c @@ -38,6 +38,7 @@ #include "fopen.h" #include "rename.h" #include "strtoofft.h" +#include "share.h" /* The last 3 #include files should be in this order */ #include "curl_printf.h" @@ -528,4 +529,18 @@ CURLcode Curl_hsts_loadcb(struct Curl_easy *data, struct hsts *h) return hsts_pull(data, h); } +void Curl_hsts_loadfiles(struct Curl_easy *data) +{ + struct curl_slist *l = data->set.hstslist; + if(l) { + Curl_share_lock(data, CURL_LOCK_DATA_HSTS, CURL_LOCK_ACCESS_SINGLE); + + while(l) { + (void)Curl_hsts_loadfile(data, data->hsts, l->data); + l = l->next; + } + Curl_share_unlock(data, CURL_LOCK_DATA_HSTS); + } +} + #endif /* CURL_DISABLE_HTTP || CURL_DISABLE_HSTS */ diff --git a/lib/hsts.h b/lib/hsts.h index baa582864..3c9d2dad1 100644 --- a/lib/hsts.h +++ b/lib/hsts.h @@ -57,9 +57,11 @@ CURLcode Curl_hsts_loadfile(struct Curl_easy *data, struct hsts *h, const char *file); CURLcode Curl_hsts_loadcb(struct Curl_easy *data, struct hsts *h); +void Curl_hsts_loadfiles(struct Curl_easy *data); #else #define Curl_hsts_cleanup(x) #define Curl_hsts_loadcb(x,y) #define Curl_hsts_save(x,y,z) +#define Curl_hsts_loadfiles(x) #endif /* CURL_DISABLE_HTTP || CURL_DISABLE_HSTS */ #endif /* HEADER_CURL_HSTS_H */ diff --git a/lib/setopt.c b/lib/setopt.c index 6edd187ac..c6bece9ab 100644 --- a/lib/setopt.c +++ b/lib/setopt.c @@ -2230,9 +2230,14 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) data->cookies = NULL; #endif +#ifndef CURL_DISABLE_HSTS + if(data->share->hsts == data->hsts) + data->hsts = NULL; +#endif +#ifdef USE_SSL if(data->share->sslsession == data->state.session) data->state.session = NULL; - +#endif #ifdef USE_LIBPSL if(data->psl == &data->share->psl) data->psl = data->multi? &data->multi->psl: NULL; @@ -2266,10 +2271,19 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) data->cookies = data->share->cookies; } #endif /* CURL_DISABLE_HTTP */ +#ifndef CURL_DISABLE_HSTS + if(data->share->hsts) { + /* first free the private one if any */ + Curl_hsts_cleanup(&data->hsts); + data->hsts = data->share->hsts; + } +#endif /* CURL_DISABLE_HTTP */ +#ifdef USE_SSL if(data->share->sslsession) { data->set.general_ssl.max_ssl_sessions = data->share->max_ssl_sessions; data->state.session = data->share->sslsession; } +#endif #ifdef USE_LIBPSL if(data->share->specifier & (1 << CURL_LOCK_DATA_PSL)) data->psl = &data->share->psl; @@ -2961,19 +2975,39 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) case CURLOPT_HSTSWRITEDATA: data->set.hsts_write_userp = va_arg(param, void *); break; - case CURLOPT_HSTS: + case CURLOPT_HSTS: { + struct curl_slist *h; if(!data->hsts) { data->hsts = Curl_hsts_init(); if(!data->hsts) return CURLE_OUT_OF_MEMORY; } argptr = va_arg(param, char *); - result = Curl_setstropt(&data->set.str[STRING_HSTS], argptr); - if(result) - return result; - if(argptr) - (void)Curl_hsts_loadfile(data, data->hsts, argptr); + if(argptr) { + result = Curl_setstropt(&data->set.str[STRING_HSTS], argptr); + if(result) + return result; + /* this needs to build a list of file names to read from, so that it can + read them later, as we might get a shared HSTS handle to load them + into */ + h = curl_slist_append(data->set.hstslist, argptr); + if(!h) { + curl_slist_free_all(data->set.hstslist); + data->set.hstslist = NULL; + return CURLE_OUT_OF_MEMORY; + } + data->set.hstslist = h; /* store the list for later use */ + } + else { + /* clear the list of HSTS files */ + curl_slist_free_all(data->set.hstslist); + data->set.hstslist = NULL; + if(!data->share || !data->share->hsts) + /* throw away the HSTS cache unless shared */ + Curl_hsts_cleanup(&data->hsts); + } break; + } case CURLOPT_HSTS_CTRL: arg = va_arg(param, long); if(arg & CURLHSTS_ENABLE) { diff --git a/lib/share.c b/lib/share.c index 9c43c8f70..8681ac2c2 100644 --- a/lib/share.c +++ b/lib/share.c @@ -27,9 +27,11 @@ #include "share.h" #include "psl.h" #include "vtls/vtls.h" -#include "curl_memory.h" +#include "hsts.h" -/* The last #include file should be: */ +/* The last 3 #include files should be in this order */ +#include "curl_printf.h" +#include "curl_memory.h" #include "memdebug.h" struct Curl_share * @@ -91,6 +93,18 @@ curl_share_setopt(struct Curl_share *share, CURLSHoption option, ...) #endif break; + case CURL_LOCK_DATA_HSTS: +#ifndef CURL_DISABLE_HSTS + if(!share->hsts) { + share->hsts = Curl_hsts_init(); + if(!share->hsts) + res = CURLSHE_NOMEM; + } +#else /* CURL_DISABLE_HSTS */ + res = CURLSHE_NOT_BUILT_IN; +#endif + break; + case CURL_LOCK_DATA_SSL_SESSION: #ifdef USE_SSL if(!share->sslsession) { @@ -143,6 +157,16 @@ curl_share_setopt(struct Curl_share *share, CURLSHoption option, ...) #endif break; + case CURL_LOCK_DATA_HSTS: +#ifndef CURL_DISABLE_HSTS + if(share->hsts) { + Curl_hsts_cleanup(&share->hsts); + } +#else /* CURL_DISABLE_HSTS */ + res = CURLSHE_NOT_BUILT_IN; +#endif + break; + case CURL_LOCK_DATA_SSL_SESSION: #ifdef USE_SSL Curl_safefree(share->sslsession); @@ -209,6 +233,10 @@ curl_share_cleanup(struct Curl_share *share) Curl_cookie_cleanup(share->cookies); #endif +#ifndef CURL_DISABLE_HSTS + Curl_hsts_cleanup(&share->hsts); +#endif + #ifdef USE_SSL if(share->sslsession) { size_t i; diff --git a/lib/share.h b/lib/share.h index 222e34ba6..97b820b0f 100644 --- a/lib/share.h +++ b/lib/share.h @@ -57,10 +57,14 @@ struct Curl_share { #ifdef USE_LIBPSL struct PslCache psl; #endif - +#ifndef CURL_DISABLE_HSTS + struct hsts *hsts; +#endif +#ifdef USE_SSL struct Curl_ssl_session *sslsession; size_t max_ssl_sessions; long sessionage; +#endif }; CURLSHcode Curl_share_lock(struct Curl_easy *, curl_lock_data, diff --git a/lib/transfer.c b/lib/transfer.c index 78bf349fc..0d3736447 100644 --- a/lib/transfer.c +++ b/lib/transfer.c @@ -1468,6 +1468,9 @@ CURLcode Curl_pretransfer(struct Curl_easy *data) if(data->state.resolve) result = Curl_loadhostpairs(data); + /* If there is a list of hsts files to read */ + Curl_hsts_loadfiles(data); + if(!result) { /* Allow data->set.use_port to set which port to use. This needs to be * disabled for example when we follow Location: headers to URLs using diff --git a/lib/url.c b/lib/url.c index 10d6ade8c..2569f6d11 100644 --- a/lib/url.c +++ b/lib/url.c @@ -427,7 +427,11 @@ CURLcode Curl_close(struct Curl_easy **datap) Curl_altsvc_save(data, data->asi, data->set.str[STRING_ALTSVC]); Curl_altsvc_cleanup(&data->asi); Curl_hsts_save(data, data->hsts, data->set.str[STRING_HSTS]); - Curl_hsts_cleanup(&data->hsts); +#ifndef CURL_DISABLE_HSTS + if(!data->share || !data->share->hsts) + Curl_hsts_cleanup(&data->hsts); + curl_slist_free_all(data->set.hstslist); /* clean up list */ +#endif #if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_CRYPTO_AUTH) Curl_http_auth_cleanup_digest(data); #endif diff --git a/lib/urldata.h b/lib/urldata.h index 0430ac4e5..5d4db19a6 100644 --- a/lib/urldata.h +++ b/lib/urldata.h @@ -1669,6 +1669,8 @@ struct UserDefined { /* function to convert from UTF-8 encoding: */ curl_conv_callback convfromutf8; #ifndef CURL_DISABLE_HSTS + struct curl_slist *hstslist; /* list of HSTS files set by + curl_easy_setopt(HSTS) calls */ curl_hstsread_callback hsts_read; void *hsts_read_userp; curl_hstswrite_callback hsts_write; -- Gitee From 8fe96be7f5319b0b514e62e55044bdb782dddd8b Mon Sep 17 00:00:00 2001 From: Josh Brobst Date: Sat, 31 Dec 2022 14:41:44 -0500 Subject: [PATCH 6/7] http: decode transfer encoding first The unencoding stack is added to as Transfer-Encoding and Content-Encoding fields are encountered with no distinction between the two, meaning the stack will be incorrect if, e.g., the message has both fields and a non-chunked Transfer-Encoding comes first. This commit fixes this by ordering the stack with transfer encodings first. Reviewed-by: Patrick Monnerat Closes #10187 CVE-2023-23916 Signed-off-by: zhouhaifeng --- lib/content_encoding.c | 39 +++++--- lib/content_encoding.h | 5 +- tests/data/Makefile.inc | 4 +- tests/data/test1277 | 206 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 239 insertions(+), 15 deletions(-) create mode 100644 tests/data/test1277 diff --git a/lib/content_encoding.c b/lib/content_encoding.c index c4b95be45..24470bfe0 100644 --- a/lib/content_encoding.c +++ b/lib/content_encoding.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2021, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2023, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -968,7 +968,8 @@ static const struct content_encoding error_encoding = { static struct contenc_writer * new_unencoding_writer(struct Curl_easy *data, const struct content_encoding *handler, - struct contenc_writer *downstream) + struct contenc_writer *downstream, + int order) { size_t sz = offsetof(struct contenc_writer, params) + handler->paramsize; struct contenc_writer *writer = (struct contenc_writer *)calloc(1, sz); @@ -976,6 +977,7 @@ new_unencoding_writer(struct Curl_easy *data, if(writer) { writer->handler = handler; writer->downstream = downstream; + writer->order = order; if(handler->init_writer(data, writer)) { free(writer); writer = NULL; @@ -1031,10 +1033,11 @@ static const struct content_encoding *find_encoding(const char *name, /* Set-up the unencoding stack from the Content-Encoding header value. * See RFC 7231 section 3.1.2.2. */ CURLcode Curl_build_unencoding_stack(struct Curl_easy *data, - const char *enclist, int maybechunked) + const char *enclist, int is_transfer) { struct SingleRequest *k = &data->req; int counter = 0; + unsigned int order = is_transfer? 2: 1; do { const char *name; @@ -1051,7 +1054,7 @@ CURLcode Curl_build_unencoding_stack(struct Curl_easy *data, namelen = enclist - name + 1; /* Special case: chunked encoding is handled at the reader level. */ - if(maybechunked && namelen == 7 && strncasecompare(name, "chunked", 7)) { + if(is_transfer && namelen == 7 && strncasecompare(name, "chunked", 7)) { k->chunk = TRUE; /* chunks coming our way. */ Curl_httpchunk_init(data); /* init our chunky engine. */ } @@ -1060,7 +1063,8 @@ CURLcode Curl_build_unencoding_stack(struct Curl_easy *data, struct contenc_writer *writer; if(!k->writer_stack) { - k->writer_stack = new_unencoding_writer(data, &client_encoding, NULL); + k->writer_stack = new_unencoding_writer(data, &client_encoding, + NULL, 0); if(!k->writer_stack) return CURLE_OUT_OF_MEMORY; @@ -1076,10 +1080,23 @@ CURLcode Curl_build_unencoding_stack(struct Curl_easy *data, } /* Stack the unencoding stage. */ - writer = new_unencoding_writer(data, encoding, k->writer_stack); - if(!writer) - return CURLE_OUT_OF_MEMORY; - k->writer_stack = writer; + if(order >= k->writer_stack->order) { + writer = new_unencoding_writer(data, encoding, + k->writer_stack, order); + if(!writer) + return CURLE_OUT_OF_MEMORY; + k->writer_stack = writer; + } + else { + struct contenc_writer *w = k->writer_stack; + while(w->downstream && order < w->downstream->order) + w = w->downstream; + writer = new_unencoding_writer(data, encoding, + w->downstream, order); + if(!writer) + return CURLE_OUT_OF_MEMORY; + w->downstream = writer; + } } } while(*enclist); @@ -1089,11 +1106,11 @@ CURLcode Curl_build_unencoding_stack(struct Curl_easy *data, #else /* Stubs for builds without HTTP. */ CURLcode Curl_build_unencoding_stack(struct Curl_easy *data, - const char *enclist, int maybechunked) + const char *enclist, int is_transfer) { (void) data; (void) enclist; - (void) maybechunked; + (void) is_transfer; return CURLE_NOT_BUILT_IN; } diff --git a/lib/content_encoding.h b/lib/content_encoding.h index acfd0c27b..010297ca4 100644 --- a/lib/content_encoding.h +++ b/lib/content_encoding.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2021, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2023, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -27,6 +27,7 @@ struct contenc_writer { const struct content_encoding *handler; /* Encoding handler. */ struct contenc_writer *downstream; /* Downstream writer. */ void *params; /* Encoding-specific storage (variable length). */ + unsigned int order; /* Ordering within writer stack. */ }; /* Content encoding writer. */ @@ -45,7 +46,7 @@ struct content_encoding { CURLcode Curl_build_unencoding_stack(struct Curl_easy *data, - const char *enclist, int maybechunked); + const char *enclist, int is_transfer); CURLcode Curl_unencode_write(struct Curl_easy *data, struct contenc_writer *writer, const char *buf, size_t nbytes); diff --git a/tests/data/Makefile.inc b/tests/data/Makefile.inc index bce33d1ce..bb85506b0 100644 --- a/tests/data/Makefile.inc +++ b/tests/data/Makefile.inc @@ -5,7 +5,7 @@ # | (__| |_| | _ <| |___ # \___|\___/|_| \_\_____| # -# Copyright (C) 1998 - 2021, Daniel Stenberg, , et al. +# Copyright (C) 1998 - 2023, Daniel Stenberg, , et al. # # This software is licensed as described in the file COPYING, which # you should have received as part of this distribution. The terms @@ -159,7 +159,7 @@ test1240 test1241 test1242 test1243 test1244 test1245 test1246 test1247 \ test1248 test1249 test1250 test1251 test1252 test1253 test1254 test1255 \ test1256 test1257 test1258 test1259 test1260 test1261 test1262 test1263 \ test1264 test1265 test1266 test1267 test1268 test1269 test1270 test1271 \ -test1272 test1273 \ +test1272 test1273 test1277 \ \ test1280 test1281 test1282 test1283 test1284 test1285 test1286 test1287 \ test1288 test1289 test1290 test1291 test1292 test1293 test1294 test1295 \ diff --git a/tests/data/test1277 b/tests/data/test1277 new file mode 100644 index 000000000..df7402038 --- /dev/null +++ b/tests/data/test1277 @@ -0,0 +1,206 @@ + + + +HTTP +HTTP GET +compressed +Transfer-Encoding +Content-Encoding + + +# +# Server-side + + +SFRUUC8xLjEgMjAwIE9LDQpEYXRlOiBNb24sIDI5IE5vdiAyMDA0IDIxOjU2OjUzIEdNVA0KU2Vy +dmVyOiBBcGFjaGUvMS4zLjMxIChEZWJpYW4gR05VL0xpbnV4KSBtb2RfZ3ppcC8xLjMuMjYuMWEg +UEhQLzQuMy45LTEgbW9kX3NzbC8yLjguMjAgT3BlblNTTC8wLjkuN2QgbW9kX3BlcmwvMS4yOQ0K +VmFyeTogQWNjZXB0LUVuY29kaW5nDQpDb250ZW50LVR5cGU6IHRleHQvaHRtbDsgY2hhcnNldD1J +U08tODg1OS0xDQpUcmFuc2Zlci1FbmNvZGluZzogZ3ppcCwgY2h1bmtlZA0KQ29udGVudC1FbmNv +ZGluZzogZGVmbGF0ZQ0KDQo1MjINCh+LCAAHgbBjAv8BCwX0+nja3VjbbuM2EH03kH9g/dQCtm62 +EzlwtChy2QTNNsHaBbpPBi3RNhtJFEjKuTz02zuUKImxHK93g74ESGJy5pzD4QxJk5l8ekpitCFc +UJaedV3L6SKShiyi6eqsezO96/v+aNx3u5+CzuSXi7vz2bf7S5Rx9g8JZT+mQgIQTb9NZ5dfUHct +ZXZq20tOxDohWFopkfYChw8kjexl0tc80XesoRXJqAuiW1pBB6HKptpNb06jwD12vYltGEpEhCWZ +4ygigBiP/b7j9z0XOcNTxz31xhPb8BuEPFMfUeA5zrDvOn3XR4536sHPQFMqxKs4UpyQuVgzLoMw +53EdjmFv45d5HBdwhNMIxXTRphYQHR4RoZY6Z0lSUlKCJGO1AMf8GS0ZR2FMSSr7gkYE/fX1FkmO +U7GEilowi0bIUN4ZDKIC4XIEpVqpcFXfJY2JOOrkQnXUGOI5lfjJQjcSiTzLQF+g69nsvlf8nfbQ +FbSPOldF++LmfNZDs8vbPy/h8/bid4Bd3dxe9orhP9/dX19+hTYM8EhiiKmU6mdMyJ5u5tAKGXug +RBTaKM9ihiMYApZanpComXUPZViIR8Yj1YTI0jxZFPbp9PaoExIu6ZKGUFcw/UE4+JgoQ4FqPMEI +xbQgGxl7JJxER53Fc5WkHpJr8mbGq7JYR53ZGqgIw++CpmozCUhtnWqVYAabDnnOUSfG6SrHKyKK +GEi6oZylCQwgQEeX0FgbGypxTOXzXISMk2B04g+t0cnE3rJvgTPCQ5AMBpZ7bGAr8xYa5vMAe21s +IAuTXtcsy2PMm7GOR2OIYQjLedvTIlQDugPLd18RXodiOMpgnNErtBEOx8W54VsjZ2LrjumZhywH +Xc+tvNrwClMO4g9qjDGAyBci5DSTcESKYKBOh9emEgalT8P1XO3l4IIscR7LiW0aSxjUf14dYBmU +Pdh5aFYnpa3Wiz2xW6xGbM0S8rYQLGDKSxV1eNomQ+vWAo2mXL0cLgdgraRojcjixTtcBMBaRNEa +kReaHS4CYC2iaI1IuIY9RmK2OlyqpmjBRqKR5VlyuCCAtZSiNSIRWRwuAmAtomiNCBNPh4sAWIso +mlEvEanz8gdqVhKqumm6scxzDmkTJNDrt+oahdmIHyjJRlTF2AhDRN0cDldRaC1TEBudhHLO+OFK +JV5rabJZ14QFVa2gWXpiCoccpODLzcz+G53D6QEpo3mCbkvHxK4QGq++p+Sck5jUmWuZ5/r+FpxY +rmfBBekN9xtsda86OXb8UYtYXbDaHHUv2n1x2gUs52LvmswEzlECE14zOZdr+KqGnLVMJRLnEm4y +og6p7Ffd2qBPYJxSEqOpJCl8w68mtulsUaBKu+v+7wKvIqKKbCBbdM5iEtw9poTXuMJURWqboVY9 +YVzJOE1oiqU5O8nhhlDUBgpad1pezzve5x7s5e5zur6/l+vuHReWxv6o97rd4+E+93gv2R/v9Tp7 +3e7oO3F/J7D9kQ1aY5f3u+3yw5rI4MkEDzG4jtY6tfEZyeeMnHU5CeF1oGzqJVUvygZnbmRHDbXL +sZOo7y3gPhmqYux07WQaj7MWdfvh9hZXUqk2FNjhzo5+1bep33Zqldh6pzWIt/PGinsbjv+/pDk/ +nbHh4J0ZU++rj58o1xuM3Hdk6gWeYh8/SwPPGZ68I0vXhCYRjj9+oobD0cB7R6LCPjz1xc/kybDo +s77+r1DQqZvNf8f+A904jtHT7+gYCwUAAA0KMA0KDQo= + + + + +HTTP/1.1 200 OK +Date: Mon, 29 Nov 2004 21:56:53 GMT +Server: Apache/1.3.31 (Debian GNU/Linux) mod_gzip/1.3.26.1a PHP/4.3.9-1 mod_ssl/2.8.20 OpenSSL/0.9.7d mod_perl/1.29 +Vary: Accept-Encoding +Content-Type: text/html; charset=ISO-8859-1 +Transfer-Encoding: gzip, chunked +Content-Encoding: deflate + + + + + + 1612 + 1998-08-21 04:01:29 + 2004-10-18 02:22:23 + curl + curl and libcurl + Command line tool and library for client-side URL transfers. + curl and libcurl is a tool for transferring files +using URL syntax. It supports HTTP, HTTPS, FTP, +FTPS, DICT, TELNET, LDAP, FILE, and GOPHER, as +well as HTTP-post, HTTP-put, cookies, FTP upload, +resumed transfers, passwords, portnumbers, SSL +certificates, Kerberos, and proxies. It is powered +by libcurl, the client-side URL transfer library. +There are bindings to libcurl for over 20 +languages and environments. + + 5784.57 + 3.16 + 169 + 6594.54 + 13.81 + 105 + 8.50 + 21 + 183 + 323 + Default + http://freshmeat.net/projects/curl/ + http://freshmeat.net/redir/curl/1612/url_homepage/ + http://freshmeat.net/redir/curl/1612/url_tgz/ + http://freshmeat.net/redir/curl/1612/url_bz2/ + http://freshmeat.net/redir/curl/1612/url_zip/ + http://freshmeat.net/redir/curl/1612/url_changelog/ + http://freshmeat.net/redir/curl/1612/url_rpm/ + http://freshmeat.net/redir/curl/1612/url_deb/ + http://freshmeat.net/redir/curl/1612/url_osx/ + http://freshmeat.net/redir/curl/1612/url_bsdport/ + + http://freshmeat.net/redir/curl/1612/url_cvs/ + http://freshmeat.net/redir/curl/1612/url_list/ + http://freshmeat.net/redir/curl/1612/url_mirror/ + + MIT/X Consortium License + + 7.12.2 + 176085 + 2004-10-18 02:22:23 + + + + + Daniel Stenberg + http://freshmeat.net/~bagder/ + Owner + + + + 12 + 226 + 3 + 2 + 188 + 216 + 200 + 220 + 164 + 90 + 89 + 809 + 150 + 224 + 900 + 839 + + + + 0 + 7464 + 7464 + OpenSSL (Default) + + + 0 + 0 + 7443 + OpenLDAP + + + 0 + 0 + 12351 + zlib + + + 0 + 0 + 32047 + Heimdal + + + 0 + 0 + 44532 + c-ares + + + + + + + + +# +# Client-side + + +libz + + +http + + +HTTP GET with both content and transfer encoding + + +http://%HOSTIP:%HTTPPORT/%TESTNUMBER --tr-encoding --compressed + + + +# +# Verify data after the test has been "shot" + + +s/^Accept-Encoding: [a-zA-Z, ]*/Accept-Encoding: xxx/ + + +GET /%TESTNUMBER HTTP/1.1 +Host: %HOSTIP:%HTTPPORT +User-Agent: curl/%VERSION +Accept: */* +Connection: TE +TE: gzip +Accept-Encoding: xxx + + + + -- Gitee From 5bfe2e3c0a457d6edb966de79b96098ea1878bb0 Mon Sep 17 00:00:00 2001 From: Patrick Monnerat Date: Mon, 13 Feb 2023 08:33:09 +0100 Subject: [PATCH 7/7] content_encoding: do not reset stage counter for each header Test 418 verifies Closes #10492 CVE-2023-23916 Signed-off-by: zhouhaifeng --- lib/content_encoding.c | 7 +- lib/urldata.h | 3 +- tests/data/Makefile.inc | 2 +- tests/data/test387 | 2 +- tests/data/test418 | 152 ++++++++++++++++++++++++++++++++++++++++ 5 files changed, 159 insertions(+), 7 deletions(-) create mode 100644 tests/data/test418 diff --git a/lib/content_encoding.c b/lib/content_encoding.c index 24470bfe0..4809a3b20 100644 --- a/lib/content_encoding.c +++ b/lib/content_encoding.c @@ -1036,7 +1036,6 @@ CURLcode Curl_build_unencoding_stack(struct Curl_easy *data, const char *enclist, int is_transfer) { struct SingleRequest *k = &data->req; - int counter = 0; unsigned int order = is_transfer? 2: 1; do { @@ -1073,9 +1072,9 @@ CURLcode Curl_build_unencoding_stack(struct Curl_easy *data, if(!encoding) encoding = &error_encoding; /* Defer error at stack use. */ - if(++counter >= MAX_ENCODE_STACK) { - failf(data, "Reject response due to %u content encodings", - counter); + if(k->writer_stack_depth++ >= MAX_ENCODE_STACK) { + failf(data, "Reject response due to more than %u content encodings", + MAX_ENCODE_STACK); return CURLE_BAD_CONTENT_ENCODING; } diff --git a/lib/urldata.h b/lib/urldata.h index 5d4db19a6..ba76fc794 100644 --- a/lib/urldata.h +++ b/lib/urldata.h @@ -706,7 +706,8 @@ struct SingleRequest { struct dohdata *doh; /* DoH specific data for this request */ #endif unsigned char setcookies; - BIT(header); /* incoming data has HTTP header */ + unsigned char writer_stack_depth; /* Unencoding stack depth. */ + BIT(header); /* incoming data has HTTP header */ BIT(content_range); /* set TRUE if Content-Range: was found */ BIT(upload_done); /* set to TRUE when doing chunked transfer-encoding upload and we're uploading the last chunk */ diff --git a/tests/data/Makefile.inc b/tests/data/Makefile.inc index bb85506b0..7dc553696 100644 --- a/tests/data/Makefile.inc +++ b/tests/data/Makefile.inc @@ -65,7 +65,7 @@ test387 \ test393 test394 test395 test396 test397 \ \ test400 test401 test402 test403 test404 test405 test406 test407 test408 \ -test409 test410 \ +test409 test410 test418 \ \ test430 test431 test432 test433 test434 \ \ diff --git a/tests/data/test387 b/tests/data/test387 index 545c5752e..979ff733a 100644 --- a/tests/data/test387 +++ b/tests/data/test387 @@ -47,7 +47,7 @@ Accept: */* 61 -curl: (61) Reject response due to 5 content encodings +curl: (61) Reject response due to more than 5 content encodings diff --git a/tests/data/test418 b/tests/data/test418 new file mode 100644 index 000000000..50e974e60 --- /dev/null +++ b/tests/data/test418 @@ -0,0 +1,152 @@ + + + +HTTP +gzip + + + +# +# Server-side + + +HTTP/1.1 200 OK +Transfer-Encoding: gzip +Transfer-Encoding: gzip +Transfer-Encoding: gzip +Transfer-Encoding: gzip +Transfer-Encoding: gzip +Transfer-Encoding: gzip +Transfer-Encoding: gzip +Transfer-Encoding: gzip +Transfer-Encoding: gzip +Transfer-Encoding: gzip +Transfer-Encoding: gzip +Transfer-Encoding: gzip +Transfer-Encoding: gzip +Transfer-Encoding: gzip +Transfer-Encoding: gzip +Transfer-Encoding: gzip +Transfer-Encoding: gzip +Transfer-Encoding: gzip +Transfer-Encoding: gzip +Transfer-Encoding: gzip +Transfer-Encoding: gzip +Transfer-Encoding: gzip +Transfer-Encoding: gzip +Transfer-Encoding: gzip +Transfer-Encoding: gzip +Transfer-Encoding: gzip +Transfer-Encoding: gzip +Transfer-Encoding: gzip +Transfer-Encoding: gzip +Transfer-Encoding: gzip +Transfer-Encoding: gzip +Transfer-Encoding: gzip +Transfer-Encoding: gzip +Transfer-Encoding: gzip +Transfer-Encoding: gzip +Transfer-Encoding: gzip +Transfer-Encoding: gzip +Transfer-Encoding: gzip +Transfer-Encoding: gzip +Transfer-Encoding: gzip +Transfer-Encoding: gzip +Transfer-Encoding: gzip +Transfer-Encoding: gzip +Transfer-Encoding: gzip +Transfer-Encoding: gzip +Transfer-Encoding: gzip +Transfer-Encoding: gzip +Transfer-Encoding: gzip +Transfer-Encoding: gzip +Transfer-Encoding: gzip +Transfer-Encoding: gzip +Transfer-Encoding: gzip +Transfer-Encoding: gzip +Transfer-Encoding: gzip +Transfer-Encoding: gzip +Transfer-Encoding: gzip +Transfer-Encoding: gzip +Transfer-Encoding: gzip +Transfer-Encoding: gzip +Transfer-Encoding: gzip +Transfer-Encoding: gzip +Transfer-Encoding: gzip +Transfer-Encoding: gzip +Transfer-Encoding: gzip +Transfer-Encoding: gzip +Transfer-Encoding: gzip +Transfer-Encoding: gzip +Transfer-Encoding: gzip +Transfer-Encoding: gzip +Transfer-Encoding: gzip +Transfer-Encoding: gzip +Transfer-Encoding: gzip +Transfer-Encoding: gzip +Transfer-Encoding: gzip +Transfer-Encoding: gzip +Transfer-Encoding: gzip +Transfer-Encoding: gzip +Transfer-Encoding: gzip +Transfer-Encoding: gzip +Transfer-Encoding: gzip +Transfer-Encoding: gzip +Transfer-Encoding: gzip +Transfer-Encoding: gzip +Transfer-Encoding: gzip +Transfer-Encoding: gzip +Transfer-Encoding: gzip +Transfer-Encoding: gzip +Transfer-Encoding: gzip +Transfer-Encoding: gzip +Transfer-Encoding: gzip +Transfer-Encoding: gzip +Transfer-Encoding: gzip +Transfer-Encoding: gzip +Transfer-Encoding: gzip +Transfer-Encoding: gzip +Transfer-Encoding: gzip +Transfer-Encoding: gzip +Transfer-Encoding: gzip +Transfer-Encoding: gzip +Transfer-Encoding: gzip + +-foo- + + + +# +# Client-side + + +http + + +Response with multiple Transfer-Encoding headers + + +http://%HOSTIP:%HTTPPORT/%TESTNUMBER -sS + + + +# +# Verify data after the test has been "shot" + + +GET /%TESTNUMBER HTTP/1.1 +Host: %HOSTIP:%HTTPPORT +User-Agent: curl/%VERSION +Accept: */* + + + +# CURLE_BAD_CONTENT_ENCODING is 61 + +61 + + +curl: (61) Reject response due to more than 5 content encodings + + + -- Gitee