Skip to content

Commit

Permalink
hsts: handle unlimited expiry
Browse files Browse the repository at this point in the history
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.

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
  • Loading branch information
bagder committed Sep 14, 2021
1 parent 352b07f commit 33fd5a1
Show file tree
Hide file tree
Showing 4 changed files with 47 additions and 28 deletions.
40 changes: 25 additions & 15 deletions lib/hsts.c
Expand Up @@ -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
Expand Down Expand Up @@ -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);
Expand All @@ -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;
}

Expand Down Expand Up @@ -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;
Expand Down
4 changes: 3 additions & 1 deletion tests/data/test1660
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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>
Expand Down
6 changes: 3 additions & 3 deletions tests/data/test1915
Expand Up @@ -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>
25 changes: 16 additions & 9 deletions tests/libtest/lib1915.c
Expand Up @@ -25,12 +25,18 @@
#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 {
Expand All @@ -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
Expand Down

0 comments on commit 33fd5a1

Please sign in to comment.