]> git.ipfire.org Git - thirdparty/curl.git/commitdiff
cookie: apply limits
authorDaniel Stenberg <daniel@haxx.se>
Sun, 26 Jun 2022 09:00:48 +0000 (11:00 +0200)
committerDaniel Stenberg <daniel@haxx.se>
Sun, 26 Jun 2022 09:00:48 +0000 (11:00 +0200)
- 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

lib/cookie.c
lib/cookie.h
lib/http.c
lib/urldata.h

index a308346a777bc3411d6c5d884ac6982c400e271b..a1ab89532033b35c372fee7db4ec2d3ab51531ee 100644 (file)
@@ -482,6 +482,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)
@@ -821,7 +825,7 @@ Curl_cookie_add(struct Curl_easy *data,
       freecookie(co);
       return NULL;
     }
-
+    data->req.setcookies++;
   }
   else {
     /*
@@ -1375,7 +1379,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)
 {
@@ -1430,6 +1435,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;
index 453dfced8a342d70ceb253e513af27c912e7cd75..abc0a2e8a01ad1ff6529304689d44728aefc6943 100644 (file)
@@ -83,10 +83,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
@@ -99,7 +115,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);
index 5284475ba92c435eb9723006b29dccf12efebca5..258722a602e409f741ad8fcd5fcbf7bbd6d28ff8 100644 (file)
@@ -2711,12 +2711,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];
@@ -2734,7 +2736,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);
     }
@@ -2748,6 +2750,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)
@@ -2758,7 +2767,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) {
index 17fe25720be3377261e0188f3dbea7ca5e50236b..bcb4d460c2fe6d2108683d83506a54c2bcd4bbb2 100644 (file)
@@ -698,6 +698,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