]> git.ipfire.org Git - thirdparty/curl.git/commitdiff
hsts: handle unlimited expiry
authorDaniel Stenberg <daniel@haxx.se>
Tue, 14 Sep 2021 14:20:47 +0000 (16:20 +0200)
committerDaniel Stenberg <daniel@haxx.se>
Wed, 15 Sep 2021 12:37:36 +0000 (14:37 +0200)
When setting a blank expire string, meaning unlimited, curl would pass
TIME_T_MAX to getime_r() when creating the output, while on 64 bit
systems such a large value cannot be convetered to a tm struct making
curl to exit the loop with an error instead. It can't be converted
because the year it would represent doesn't fit in the 'int tm_year'
field!

Starting now, unlimited expiry is instead handled differently by using a
human readable expiry date spelled out as "unlimited" instead of trying
to use a distant actual date.

Test 1660 and 1915 have been updated to help verify this change.

Reported-by: Jonathan Cardoso
Fixes #7720
Closes #7721

lib/hsts.c
tests/data/test1660
tests/data/test1915
tests/libtest/lib1915.c

index 853c7dfea5ab0709c030fd07d0e9f056ea461979..fa91cf63f57aa0014dde7546b09ed7c7bd0d1ffe 100644 (file)
@@ -49,6 +49,7 @@
 #define MAX_HSTS_HOSTLENSTR "256"
 #define MAX_HSTS_DATELEN 64
 #define MAX_HSTS_DATELENSTR "64"
+#define UNLIMITED "unlimited"
 
 #ifdef DEBUGBUILD
 /* to play well with debug builds, we can *set* a fixed time this will
@@ -283,13 +284,17 @@ static CURLcode hsts_push(struct Curl_easy *data,
   e.namelen = strlen(sts->host);
   e.includeSubDomains = sts->includeSubDomains;
 
-  result = Curl_gmtime((time_t)sts->expires, &stamp);
-  if(result)
-    return result;
+  if(sts->expires != TIME_T_MAX) {
+    result = Curl_gmtime((time_t)sts->expires, &stamp);
+    if(result)
+      return result;
 
-  msnprintf(e.expire, sizeof(e.expire), "%d%02d%02d %02d:%02d:%02d",
-            stamp.tm_year + 1900, stamp.tm_mon + 1, stamp.tm_mday,
-            stamp.tm_hour, stamp.tm_min, stamp.tm_sec);
+    msnprintf(e.expire, sizeof(e.expire), "%d%02d%02d %02d:%02d:%02d",
+              stamp.tm_year + 1900, stamp.tm_mon + 1, stamp.tm_mday,
+              stamp.tm_hour, stamp.tm_min, stamp.tm_sec);
+  }
+  else
+    strcpy(e.expire, UNLIMITED);
 
   sc = data->set.hsts_write(data, &e, i,
                             data->set.hsts_write_userp);
@@ -303,14 +308,18 @@ static CURLcode hsts_push(struct Curl_easy *data,
 static CURLcode hsts_out(struct stsentry *sts, FILE *fp)
 {
   struct tm stamp;
-  CURLcode result = Curl_gmtime((time_t)sts->expires, &stamp);
-  if(result)
-    return result;
-
-  fprintf(fp, "%s%s \"%d%02d%02d %02d:%02d:%02d\"\n",
-          sts->includeSubDomains ? ".": "", sts->host,
-          stamp.tm_year + 1900, stamp.tm_mon + 1, stamp.tm_mday,
-          stamp.tm_hour, stamp.tm_min, stamp.tm_sec);
+  if(sts->expires != TIME_T_MAX) {
+    CURLcode result = Curl_gmtime((time_t)sts->expires, &stamp);
+    if(result)
+      return result;
+    fprintf(fp, "%s%s \"%d%02d%02d %02d:%02d:%02d\"\n",
+            sts->includeSubDomains ? ".": "", sts->host,
+            stamp.tm_year + 1900, stamp.tm_mon + 1, stamp.tm_mday,
+            stamp.tm_hour, stamp.tm_min, stamp.tm_sec);
+  }
+  else
+    fprintf(fp, "%s%s \"%s\"\n",
+            sts->includeSubDomains ? ".": "", sts->host, UNLIMITED);
   return CURLE_OK;
 }
 
@@ -403,7 +412,8 @@ static CURLcode hsts_add(struct hsts *h, char *line)
               "%" MAX_HSTS_HOSTLENSTR "s \"%" MAX_HSTS_DATELENSTR "[^\"]\"",
               host, date);
   if(2 == rc) {
-    time_t expires = Curl_getdate_capped(date);
+    time_t expires = strcmp(date, UNLIMITED) ? Curl_getdate_capped(date) :
+      TIME_T_MAX;
     CURLcode result;
     char *p = host;
     bool subdomain = FALSE;
index 3ddd8d4275d2ee391c079062cea28ea9af58f258..cbbcf7587ce621f3493fa9d9409d3318615aa667 100644 (file)
@@ -20,6 +20,7 @@ HSTS
 # This file was generated by libcurl! Edit at your own risk.
 .readfrom.example "20211001 04:47:41"
 .old.example "20161001 04:47:41"
+.new.example "unlimited"
 </file>
 
 # This date is exactly "20190124 22:34:21" UTC
@@ -59,7 +60,7 @@ foo.example.com [example.com]: 1569905261 includeSubDomains
 'forexample.net' is not HSTS
 'example.net' is not HSTS
 expire.example [expire.example]: 1548369268
-Number of entries: 3
+Number of entries: 4
 expire.example [expire.example]: 1548369268
 expire.example [expire.example]: 1548369268
 expire.example [expire.example]: 1548369268
@@ -74,6 +75,7 @@ expire.example [expire.example]: 1548369268
 <file name="log/hsts%TESTNUMBER" mode="text">
 # Your HSTS cache. https://curl.se/docs/hsts.html
 # This file was generated by libcurl! Edit at your own risk.
+.new.example "unlimited"
 .example.com "20191001 04:47:41"
 example.org "20200124 22:34:21"
 </file>
index 047a16e2e531fb7ec52bbcee3898c066ca93875b..d0b2862f9a9938bf319b8e68f945014e64ca9003 100644 (file)
@@ -42,9 +42,9 @@ http://%HOSTIP:%NOLISTENPORT/not-there/%TESTNUMBER
 </errorcode>
 <stdout>
 [0/4] 1.example.com 20370320 01:02:03
-[1/4] 2.example.com 20370320 01:02:03
-[2/4] 3.example.com 20370320 01:02:03
-[3/4] 4.example.com 20370320 01:02:03
+[1/4] 2.example.com 20370320 03:02:01
+[2/4] 3.example.com 20370319 01:02:03
+[3/4] 4.example.com unlimited
 </stdout>
 </verify>
 </testcase>
index 7b4d91d3c53472757b2cf5aab54c874f077b2863..2bd6ffce822faecefcdc6fbf1470cb93c114252b 100644 (file)
 #include "warnless.h"
 #include "memdebug.h"
 
-static const char *preload_hosts[] = {
-  "1.example.com",
-  "2.example.com",
-  "3.example.com",
-  "4.example.com",
-  NULL /* end of list marker */
+struct entry {
+  const char *name;
+  const char *exp;
+};
+
+static struct entry preload_hosts[] = {
+  /* curl turns 39 that day just before 31-bit time_t overflow */
+  { "1.example.com", "20370320 01:02:03" },
+  { "2.example.com", "20370320 03:02:01" },
+  { "3.example.com", "20370319 01:02:03" },
+  { "4.example.com", "" },
+  { NULL, NULL } /* end of list marker */
 };
 
 struct state {
@@ -42,15 +48,16 @@ static CURLSTScode hstsread(CURL *easy, struct curl_hstsentry *e,
                             void *userp)
 {
   const char *host;
+  const char *expire;
   struct state *s = (struct state *)userp;
   (void)easy;
-  host = preload_hosts[s->index++];
+  host = preload_hosts[s->index].name;
+  expire = preload_hosts[s->index++].exp;
 
   if(host && (strlen(host) < e->namelen)) {
     strcpy(e->name, host);
     e->includeSubDomains = FALSE;
-    strcpy(e->expire, "20370320 01:02:03"); /* curl turns 39 that day
-                                   just before 31-bit time_t overflow */
+    strcpy(e->expire, expire);
     fprintf(stderr, "add '%s'\n", host);
   }
   else