]> git.ipfire.org Git - thirdparty/curl.git/commitdiff
cookie: cap expire times to 400 days
authorDaniel Stenberg <daniel@haxx.se>
Wed, 8 Jan 2025 09:19:26 +0000 (10:19 +0100)
committerDaniel Stenberg <daniel@haxx.se>
Fri, 10 Jan 2025 07:20:03 +0000 (08:20 +0100)
The pending cookie RFC update (currently known as 6265bis draft-19) says

  Let cookie-age-limit be the maximum age of the cookie (which name of
  Max-Age and an attribute-value of expiry-time. SHOULD be 400 days or
  less.

This change makes received cookies over the wire get capped to 400 days.

It does not cap the expiry date of cookies loaded from file.

It does this by rounding the expire time to a even minute. This, to
allow the test suite to do the same and have a chance to get the same
number for stable testing without requiring a debug build.

The test script generates TWO numbers in the output file for each
%days[] used in the input test file, and the function that subsequently
compares and verifies output is fine with *either* of the two numbers.

This is done so that if the test case is generated the second
immediately before curl runs, that updated expiry number is also deemed
okay. It still checks for an exact match of either number.

Closes #15937

lib/cookie.c
tests/FILEFORMAT.md
tests/data/test1415
tests/data/test31
tests/data/test46
tests/data/test483
tests/data/test506
tests/data/test61
tests/getpart.pm
tests/testutil.pm

index c56a7c2ee45304a110f0c4a01c464121f032c80c..48a200dc6daff07326d534708bc5d24f6c5a2e78 100644 (file)
@@ -97,6 +97,26 @@ Example set of cookies:
 
 static void strstore(char **str, const char *newstr, size_t len);
 
+/* number of seconds in 400 days */
+#define COOKIES_MAXAGE (400*24*3600)
+
+/* Make sure cookies never expire further away in time than 400 days into the
+   future. (from RFC6265bis draft-19)
+
+   For the sake of easier testing, align the capped time to an even 60 second
+   boundary.
+*/
+static void cap_expires(time_t now, struct Cookie *co)
+{
+  if((TIME_T_MAX - COOKIES_MAXAGE - 30) > now) {
+    timediff_t cap = now + COOKIES_MAXAGE;
+    if(co->expires > cap) {
+      cap += 30;
+      co->expires = (cap/60)*60;
+    }
+  }
+}
+
 static void freecookie(struct Cookie *co)
 {
   free(co->domain);
@@ -714,6 +734,7 @@ parse_cookie_header(struct Curl_easy *data,
             co->expires += now;
           break;
         }
+        cap_expires(now, co);
       }
       else if((nlen == 7) && strncasecompare("expires", namep, 7)) {
         if(!co->expires && (vlen < MAX_DATE_LENGTH)) {
@@ -737,6 +758,7 @@ parse_cookie_header(struct Curl_easy *data,
             co->expires = 1;
           else if(co->expires < 0)
             co->expires = 0;
+          cap_expires(now, co);
         }
       }
 
index f336f772eb331b7d59e7375fead6ae48bc29bfc3..f2b0507f69ceb2150e685ac374c3f4116ace618e 100644 (file)
@@ -74,6 +74,13 @@ For example, to insert the word hello 100 times:
 
     %repeat[100 x hello]%
 
+## Insert capped epoch days
+
+Mostly to test capped cookie expire dates: `%days[NUM]` inserts the number of
+seconds for the given number of days into the future, aligned to the nearest
+minute. That is the same calculation the cookie engine uses to cap expiration
+dates.
+
 ## Include file
 
 This instruction allows a test case to include another file. It is helpful to
index ae07d76f42b517d6090cb8f5e38d828b3bff851a..1e7090092f11c9b3f83d6cb30691dd21e332b2be 100644 (file)
@@ -78,15 +78,9 @@ Proxy-Connection: Keep-Alive
 # https://curl.se/docs/http-cookies.html
 # This file was generated by libcurl! Edit at your own risk.
 
-%if large-time
-.example.com   TRUE    /       FALSE   17545593600     test7value      test7
-.example.com   TRUE    /       FALSE   17545593600     test4value      test4
-.example.com   TRUE    /       FALSE   17545593600     test2value      test2
-%else
-.example.com   TRUE    /       FALSE   2145830400      test7value      test7
-.example.com   TRUE    /       FALSE   2145830400      test4value      test4
-.example.com   TRUE    /       FALSE   2145830400      test2value      test2
-%endif
+.example.com   TRUE    /       FALSE   %days[400]      test7value      test7
+.example.com   TRUE    /       FALSE   %days[400]      test4value      test4
+.example.com   TRUE    /       FALSE   %days[400]      test2value      test2
 .example.com   TRUE    /       FALSE   0       test1value      test1
 </file>
 </verify>
index 45170548615f8c6c09ab387e432bf49da320525d..7412b4bde8b146d4fb1310b245ebf99a81f4bb3a 100644 (file)
@@ -115,11 +115,7 @@ test31.curl        FALSE   /we/want/       FALSE   0       withspaces2     before equals
 test31.curl    FALSE   /we/want/       FALSE   0       withspaces      yes  within and around
 .test31.curl   TRUE    /we/want/       FALSE   0       blexp   yesyes
 #HttpOnly_test31.curl  FALSE   /silly/ FALSE   0       magic   yessir
-%if large-time
-test31.curl    FALSE   /we/want/       FALSE   17517902187     nodomain        value
-%else
-test31.curl    FALSE   /we/want/       FALSE   2118138987      nodomain        value
-%endif
+test31.curl    FALSE   /we/want/       FALSE   %days[400]      nodomain        value
 .test31.curl   TRUE    /       FALSE   0       partmatch       present
 #HttpOnly_.test31.curl TRUE    /p4/    FALSE   0       httponly        myvalue1
 #HttpOnly_.test31.curl TRUE    /p4/    FALSE   0       httpo4  value4
index 042af7e6e671e24ba40ec3eaa81c6169149e9622..0a0e84914ff3d71d8365e07bf52111dfb7e0a0f7 100644 (file)
@@ -91,15 +91,14 @@ domain..tld FALSE   /want/  FALSE   0       simplyhuge      zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz
 domain..tld    FALSE   /       FALSE   0       justaname       
 domain..tld    FALSE   /       FALSE   0       ASPSESSIONIDQGGQQSJJ    GKNBDIFAAOFDPDAIEAKDIBKE
 domain..tld    FALSE   /       FALSE   0       ckySession      temporary
+domain..tld    FALSE   /       FALSE   %days[400]      ckyPersistent   permanent
 %if large-time
-domain..tld    FALSE   /       FALSE   17517902187     ckyPersistent   permanent
 domain..tld    FALSE   /want   FALSE   0       empty   
 #HttpOnly_domain..tld  FALSE   /want   FALSE   22139150993     mooo2   indeed2
 domain..tld    FALSE   /       FALSE   22139150993     mooo    indeed
 www.loser.com  FALSE   /       FALSE   22139150993     UID     99
 www.fake.come  FALSE   /       FALSE   22147483647     cookiecliente   si
 %else
-domain..tld    FALSE   /       FALSE   2118138987      ckyPersistent   permanent
 domain..tld    FALSE   /want   FALSE   0       empty   
 #HttpOnly_domain..tld  FALSE   /want   FALSE   2139150993      mooo2   indeed2
 domain..tld    FALSE   /       FALSE   2139150993      mooo    indeed
index 13fa4f157fc5d7c3a32053c87c6f1839bb57e3cd..5d86f467d0d174d7b5a50744ed6dbd5cfe7eac68 100644 (file)
@@ -58,9 +58,9 @@ Accept: */*
 # This file was generated by libcurl! Edit at your own risk.
 
 127.0.0.1      FALSE   /       FALSE   0       name4   value
-127.0.0.1      FALSE   /       FALSE   5115959787      name3   value
+127.0.0.1      FALSE   /       FALSE   %days[400]      name3   value
 127.0.0.1      FALSE   /       FALSE   0       name2   value
-127.0.0.1      FALSE   /       FALSE   5115959787      name    value
+127.0.0.1      FALSE   /       FALSE   %days[400]      name    value
 </file>
 </verify>
 </testcase>
index 30d4c6215411b230bba20aaeccd472a4c16c1457..02165275b6914999b6b908a2ba4cf710a842cafc 100644 (file)
@@ -210,14 +210,14 @@ lock:   cookie [Pigs in space]: 90
 unlock: cookie [Pigs in space]: 91
 loaded cookies:
 -----------------
-  www.host.foo.com     FALSE   /       FALSE   1993463787      test6   six_more
-  .www.host.foo.com    TRUE    /       FALSE   1993463787      test6   six
-  .host.foo.com        TRUE    /       FALSE   1896263787      test5   five
-  .host.foo.com        TRUE    /       FALSE   2061978987      test4   overwritten4
-  .foo.com     TRUE    /       FALSE   1896263787      test3   three
-  .host.foo.com        TRUE    /       FALSE   1896263787      test2   two
-  .foo.com     TRUE    /       FALSE   1993463787      test1   overwritten1
-  .host.foo.com        TRUE    /       FALSE   1896263787      injected        yes
+  www.host.foo.com     FALSE   /       FALSE   %days[400]      test6   six_more
+  .www.host.foo.com    TRUE    /       FALSE   %days[400]      test6   six
+  .host.foo.com        TRUE    /       FALSE   %days[400]      test5   five
+  .host.foo.com        TRUE    /       FALSE   %days[400]      test4   overwritten4
+  .foo.com     TRUE    /       FALSE   %days[400]      test3   three
+  .host.foo.com        TRUE    /       FALSE   %days[400]      test2   two
+  .foo.com     TRUE    /       FALSE   %days[400]      test1   overwritten1
+  .host.foo.com        TRUE    /       FALSE   %days[400]      injected        yes
 -----------------
 try SHARE_CLEANUP...
 lock:   share  [Pigs in space]: 92
@@ -238,14 +238,14 @@ GLOBAL_CLEANUP
 # https://curl.se/docs/http-cookies.html
 # This file was generated by libcurl! Edit at your own risk.
 
-www.host.foo.com       FALSE   /       FALSE   1993463787      test6   six_more
-.www.host.foo.com      TRUE    /       FALSE   1993463787      test6   six
-.host.foo.com  TRUE    /       FALSE   1896263787      test5   five
-.host.foo.com  TRUE    /       FALSE   2061978987      test4   overwritten4
-.foo.com       TRUE    /       FALSE   1896263787      test3   three
-.host.foo.com  TRUE    /       FALSE   1896263787      test2   two
-.foo.com       TRUE    /       FALSE   1993463787      test1   overwritten1
-.host.foo.com  TRUE    /       FALSE   1896263787      injected        yes
+www.host.foo.com       FALSE   /       FALSE   %days[400]      test6   six_more
+.www.host.foo.com      TRUE    /       FALSE   %days[400]      test6   six
+.host.foo.com  TRUE    /       FALSE   %days[400]      test5   five
+.host.foo.com  TRUE    /       FALSE   %days[400]      test4   overwritten4
+.foo.com       TRUE    /       FALSE   %days[400]      test3   three
+.host.foo.com  TRUE    /       FALSE   %days[400]      test2   two
+.foo.com       TRUE    /       FALSE   %days[400]      test1   overwritten1
+.host.foo.com  TRUE    /       FALSE   %days[400]      injected        yes
 </file>
 </verify>
 </testcase>
index 43a8598babf4a279d10eb1d6a673d014e3ecf53a..2cb5963ff465dc39247db552aacb40ef3b889f43 100644 (file)
@@ -71,13 +71,8 @@ Accept: */*
 # https://curl.se/docs/http-cookies.html
 # This file was generated by libcurl! Edit at your own risk.
 
-%if large-time
-.host.foo.com  TRUE    /we/want/       FALSE   17517902187     test2   yes
-#HttpOnly_.foo.com     TRUE    /we/want/       FALSE   17517902187     test    yes
-%else
-.host.foo.com  TRUE    /we/want/       FALSE   2118138987      test2   yes
-#HttpOnly_.foo.com     TRUE    /we/want/       FALSE   2118138987      test    yes
-%endif
+.host.foo.com  TRUE    /we/want/       FALSE   %days[400]      test2   yes
+#HttpOnly_.foo.com     TRUE    /we/want/       FALSE   %days[400]      test    yes
 </file>
 </verify>
 </testcase>
index afc1f06214eaa27754f0ab8d863c40d268f950f5..8f7204f9f95ab331ab251b411f62f8eeb73fc2c6 100644 (file)
@@ -308,15 +308,48 @@ sub striparray {
 sub compareparts {
  my ($firstref, $secondref)=@_;
 
+ # we cannot compare arrays index per index since with the base64 chunks,
+ # they may not be "evenly" distributed
  my $first = join("", @$firstref);
  my $second = join("", @$secondref);
 
- # we cannot compare arrays index per index since with the base64 chunks,
- # they may not be "evenly" distributed
+ if($first =~ /%alternatives\[/) {
+     die "bad use of compareparts\n";
+ }
 
- # NOTE: this no longer strips off carriage returns from the arrays. Is that
- # really necessary? It ruins the testing of newlines. I believe it was once
- # added to enable tests on Windows.
+ if($second =~ /%alternatives\[([^,]*),([^\]]*)\]/) {
+     # there can be many %alternatives in this chunk, so we call
+     # this function recursively
+     my $alt = $second;
+     $alt =~ s/%alternatives\[([^,]*),([^\]]*)\]/$1/;
+
+     # check first alternative
+     {
+         my @f;
+         my @s;
+         push @f, $first;
+         push @s, $alt;
+         if(!compareparts(\@f, \@s)) {
+             return 0;
+         }
+     }
+
+     $alt = $second;
+     $alt =~ s/%alternatives\[([^,]*),([^\]]*)\]/$2/;
+     # check second alternative
+     {
+         my @f;
+         my @s;
+         push @f, $first;
+         push @s, $alt;
+         if(!compareparts(\@f, \@s)) {
+             return 0;
+         }
+     }
+
+     # neither matched
+     return 1;
+ }
 
  if($first ne $second) {
      return 1;
index 71b1495bc349f325436d188e0f62cc8d3722aede..113c90a2b0aea9f3ead4e9da3bb2a11f7492caf7 100644 (file)
@@ -136,6 +136,17 @@ sub subbase64 {
         $$thing =~ s/%%REPEAT%%/$all/;
     }
 
+    # days
+    while($$thing =~ s/%days\[(.*?)\]/%%DAYS%%/i) {
+        # convert to now + given days in epoch seconds, align to a 60 second
+        # boundary. Then provide two alternatives.
+        my $now = time();
+        my $d = ($1 * 24 * 3600) + $now + 30;
+        $d = int($d/60) * 60;
+        my $d2 = $d + 60;
+        $$thing =~ s/%%DAYS%%/%alternatives[$d,$d2]/;
+    }
+
     # include a file
     $$thing =~ s/%include ([^%]*)%[\n\r]+/includefile($1)/ge;
 }