blob: 165fd8af478328288f7542f11064e2643acaa863 [file] [log] [blame]
From a91c22a072cbb32e296f1efba3502f1b7775dfaf Mon Sep 17 00:00:00 2001
From: Daniel Stenberg <daniel@haxx.se>
Date: Sun, 26 Jun 2022 11:00:48 +0200
Subject: [PATCH] cookie: apply limits
- Send no more than 150 cookies per request
- Cap the max length used for a cookie: header to 8K
- Cap the max number of received Set-Cookie: headers to 50
Bug: https://curl.se/docs/CVE-2022-32205.html
CVE-2022-32205
Reported-by: Harry Sintonen
Closes #9048
Upstream-Status: Backport [https://github.com/curl/curl/commit/48d7064a49148f0394]
Signed-off-by: Robert Joslyn <robert.joslyn@redrectangle.org>
---
lib/cookie.c | 14 ++++++++++++--
lib/cookie.h | 21 +++++++++++++++++++--
lib/http.c | 13 +++++++++++--
lib/urldata.h | 1 +
4 files changed, 43 insertions(+), 6 deletions(-)
diff --git a/lib/cookie.c b/lib/cookie.c
index 1b8c8f9..8a6aa1a 100644
--- a/lib/cookie.c
+++ b/lib/cookie.c
@@ -477,6 +477,10 @@ Curl_cookie_add(struct Curl_easy *data,
(void)data;
#endif
+ DEBUGASSERT(MAX_SET_COOKIE_AMOUNT <= 255); /* counter is an unsigned char */
+ if(data->req.setcookies >= MAX_SET_COOKIE_AMOUNT)
+ return NULL;
+
/* First, alloc and init a new struct for it */
co = calloc(1, sizeof(struct Cookie));
if(!co)
@@ -816,7 +820,7 @@ Curl_cookie_add(struct Curl_easy *data,
freecookie(co);
return NULL;
}
-
+ data->req.setcookies++;
}
else {
/*
@@ -1354,7 +1358,8 @@ static struct Cookie *dup_cookie(struct Cookie *src)
*
* It shall only return cookies that haven't expired.
*/
-struct Cookie *Curl_cookie_getlist(struct CookieInfo *c,
+struct Cookie *Curl_cookie_getlist(struct Curl_easy *data,
+ struct CookieInfo *c,
const char *host, const char *path,
bool secure)
{
@@ -1409,6 +1414,11 @@ struct Cookie *Curl_cookie_getlist(struct CookieInfo *c,
mainco = newco;
matches++;
+ if(matches >= MAX_COOKIE_SEND_AMOUNT) {
+ infof(data, "Included max number of cookies (%u) in request!",
+ matches);
+ break;
+ }
}
else
goto fail;
diff --git a/lib/cookie.h b/lib/cookie.h
index 0ffe08e..7411980 100644
--- a/lib/cookie.h
+++ b/lib/cookie.h
@@ -81,10 +81,26 @@ struct CookieInfo {
*/
#define MAX_COOKIE_LINE 5000
-/* This is the maximum length of a cookie name or content we deal with: */
+/* Maximum length of an incoming cookie name or content we deal with. Longer
+ cookies are ignored. */
#define MAX_NAME 4096
#define MAX_NAME_TXT "4095"
+/* Maximum size for an outgoing cookie line libcurl will use in an http
+ request. This is the default maximum length used in some versions of Apache
+ httpd. */
+#define MAX_COOKIE_HEADER_LEN 8190
+
+/* Maximum number of cookies libcurl will send in a single request, even if
+ there might be more cookies that match. One reason to cap the number is to
+ keep the maximum HTTP request within the maximum allowed size. */
+#define MAX_COOKIE_SEND_AMOUNT 150
+
+/* Maximum number of Set-Cookie: lines accepted in a single response. If more
+ such header lines are received, they are ignored. This value must be less
+ than 256 since an unsigned char is used to count. */
+#define MAX_SET_COOKIE_AMOUNT 50
+
struct Curl_easy;
/*
* Add a cookie to the internal list of cookies. The domain and path arguments
@@ -97,7 +113,8 @@ struct Cookie *Curl_cookie_add(struct Curl_easy *data,
const char *domain, const char *path,
bool secure);
-struct Cookie *Curl_cookie_getlist(struct CookieInfo *c, const char *host,
+struct Cookie *Curl_cookie_getlist(struct Curl_easy *data,
+ struct CookieInfo *c, const char *host,
const char *path, bool secure);
void Curl_cookie_freelist(struct Cookie *cookies);
void Curl_cookie_clearall(struct CookieInfo *cookies);
diff --git a/lib/http.c b/lib/http.c
index 4433824..2c8b0c4 100644
--- a/lib/http.c
+++ b/lib/http.c
@@ -2709,12 +2709,14 @@ CURLcode Curl_http_bodysend(struct Curl_easy *data, struct connectdata *conn,
}
#if !defined(CURL_DISABLE_COOKIES)
+
CURLcode Curl_http_cookies(struct Curl_easy *data,
struct connectdata *conn,
struct dynbuf *r)
{
CURLcode result = CURLE_OK;
char *addcookies = NULL;
+ bool linecap = FALSE;
if(data->set.str[STRING_COOKIE] &&
!Curl_checkheaders(data, STRCONST("Cookie")))
addcookies = data->set.str[STRING_COOKIE];
@@ -2732,7 +2734,7 @@ CURLcode Curl_http_cookies(struct Curl_easy *data,
!strcmp(host, "127.0.0.1") ||
!strcmp(host, "[::1]") ? TRUE : FALSE;
Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE);
- co = Curl_cookie_getlist(data->cookies, host, data->state.up.path,
+ co = Curl_cookie_getlist(data, data->cookies, host, data->state.up.path,
secure_context);
Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE);
}
@@ -2746,6 +2748,13 @@ CURLcode Curl_http_cookies(struct Curl_easy *data,
if(result)
break;
}
+ if((Curl_dyn_len(r) + strlen(co->name) + strlen(co->value) + 1) >=
+ MAX_COOKIE_HEADER_LEN) {
+ infof(data, "Restricted outgoing cookies due to header size, "
+ "'%s' not sent", co->name);
+ linecap = TRUE;
+ break;
+ }
result = Curl_dyn_addf(r, "%s%s=%s", count?"; ":"",
co->name, co->value);
if(result)
@@ -2756,7 +2765,7 @@ CURLcode Curl_http_cookies(struct Curl_easy *data,
}
Curl_cookie_freelist(store);
}
- if(addcookies && !result) {
+ if(addcookies && !result && !linecap) {
if(!count)
result = Curl_dyn_addn(r, STRCONST("Cookie: "));
if(!result) {
diff --git a/lib/urldata.h b/lib/urldata.h
index e006495..54faf7d 100644
--- a/lib/urldata.h
+++ b/lib/urldata.h
@@ -707,6 +707,7 @@ struct SingleRequest {
#ifndef CURL_DISABLE_DOH
struct dohdata *doh; /* DoH specific data for this request */
#endif
+ unsigned char setcookies;
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