113498266Sopenharmony_ci/*************************************************************************** 213498266Sopenharmony_ci * _ _ ____ _ 313498266Sopenharmony_ci * Project ___| | | | _ \| | 413498266Sopenharmony_ci * / __| | | | |_) | | 513498266Sopenharmony_ci * | (__| |_| | _ <| |___ 613498266Sopenharmony_ci * \___|\___/|_| \_\_____| 713498266Sopenharmony_ci * 813498266Sopenharmony_ci * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. 913498266Sopenharmony_ci * 1013498266Sopenharmony_ci * This software is licensed as described in the file COPYING, which 1113498266Sopenharmony_ci * you should have received as part of this distribution. The terms 1213498266Sopenharmony_ci * are also available at https://curl.se/docs/copyright.html. 1313498266Sopenharmony_ci * 1413498266Sopenharmony_ci * You may opt to use, copy, modify, merge, publish, distribute and/or sell 1513498266Sopenharmony_ci * copies of the Software, and permit persons to whom the Software is 1613498266Sopenharmony_ci * furnished to do so, under the terms of the COPYING file. 1713498266Sopenharmony_ci * 1813498266Sopenharmony_ci * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY 1913498266Sopenharmony_ci * KIND, either express or implied. 2013498266Sopenharmony_ci * 2113498266Sopenharmony_ci * SPDX-License-Identifier: curl 2213498266Sopenharmony_ci * 2313498266Sopenharmony_ci ***************************************************************************/ 2413498266Sopenharmony_ci/* 2513498266Sopenharmony_ci * The Strict-Transport-Security header is defined in RFC 6797: 2613498266Sopenharmony_ci * https://datatracker.ietf.org/doc/html/rfc6797 2713498266Sopenharmony_ci */ 2813498266Sopenharmony_ci#include "curl_setup.h" 2913498266Sopenharmony_ci 3013498266Sopenharmony_ci#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_HSTS) 3113498266Sopenharmony_ci#include <curl/curl.h> 3213498266Sopenharmony_ci#include "urldata.h" 3313498266Sopenharmony_ci#include "llist.h" 3413498266Sopenharmony_ci#include "hsts.h" 3513498266Sopenharmony_ci#include "curl_get_line.h" 3613498266Sopenharmony_ci#include "strcase.h" 3713498266Sopenharmony_ci#include "sendf.h" 3813498266Sopenharmony_ci#include "strtoofft.h" 3913498266Sopenharmony_ci#include "parsedate.h" 4013498266Sopenharmony_ci#include "fopen.h" 4113498266Sopenharmony_ci#include "rename.h" 4213498266Sopenharmony_ci#include "share.h" 4313498266Sopenharmony_ci#include "strdup.h" 4413498266Sopenharmony_ci 4513498266Sopenharmony_ci/* The last 3 #include files should be in this order */ 4613498266Sopenharmony_ci#include "curl_printf.h" 4713498266Sopenharmony_ci#include "curl_memory.h" 4813498266Sopenharmony_ci#include "memdebug.h" 4913498266Sopenharmony_ci 5013498266Sopenharmony_ci#define MAX_HSTS_LINE 4095 5113498266Sopenharmony_ci#define MAX_HSTS_HOSTLEN 256 5213498266Sopenharmony_ci#define MAX_HSTS_HOSTLENSTR "256" 5313498266Sopenharmony_ci#define MAX_HSTS_DATELEN 64 5413498266Sopenharmony_ci#define MAX_HSTS_DATELENSTR "64" 5513498266Sopenharmony_ci#define UNLIMITED "unlimited" 5613498266Sopenharmony_ci 5713498266Sopenharmony_ci#ifdef DEBUGBUILD 5813498266Sopenharmony_ci/* to play well with debug builds, we can *set* a fixed time this will 5913498266Sopenharmony_ci return */ 6013498266Sopenharmony_citime_t deltatime; /* allow for "adjustments" for unit test purposes */ 6113498266Sopenharmony_cistatic time_t hsts_debugtime(void *unused) 6213498266Sopenharmony_ci{ 6313498266Sopenharmony_ci char *timestr = getenv("CURL_TIME"); 6413498266Sopenharmony_ci (void)unused; 6513498266Sopenharmony_ci if(timestr) { 6613498266Sopenharmony_ci curl_off_t val; 6713498266Sopenharmony_ci (void)curlx_strtoofft(timestr, NULL, 10, &val); 6813498266Sopenharmony_ci 6913498266Sopenharmony_ci val += (curl_off_t)deltatime; 7013498266Sopenharmony_ci return (time_t)val; 7113498266Sopenharmony_ci } 7213498266Sopenharmony_ci return time(NULL); 7313498266Sopenharmony_ci} 7413498266Sopenharmony_ci#undef time 7513498266Sopenharmony_ci#define time(x) hsts_debugtime(x) 7613498266Sopenharmony_ci#endif 7713498266Sopenharmony_ci 7813498266Sopenharmony_cistruct hsts *Curl_hsts_init(void) 7913498266Sopenharmony_ci{ 8013498266Sopenharmony_ci struct hsts *h = calloc(1, sizeof(struct hsts)); 8113498266Sopenharmony_ci if(h) { 8213498266Sopenharmony_ci Curl_llist_init(&h->list, NULL); 8313498266Sopenharmony_ci } 8413498266Sopenharmony_ci return h; 8513498266Sopenharmony_ci} 8613498266Sopenharmony_ci 8713498266Sopenharmony_cistatic void hsts_free(struct stsentry *e) 8813498266Sopenharmony_ci{ 8913498266Sopenharmony_ci free((char *)e->host); 9013498266Sopenharmony_ci free(e); 9113498266Sopenharmony_ci} 9213498266Sopenharmony_ci 9313498266Sopenharmony_civoid Curl_hsts_cleanup(struct hsts **hp) 9413498266Sopenharmony_ci{ 9513498266Sopenharmony_ci struct hsts *h = *hp; 9613498266Sopenharmony_ci if(h) { 9713498266Sopenharmony_ci struct Curl_llist_element *e; 9813498266Sopenharmony_ci struct Curl_llist_element *n; 9913498266Sopenharmony_ci for(e = h->list.head; e; e = n) { 10013498266Sopenharmony_ci struct stsentry *sts = e->ptr; 10113498266Sopenharmony_ci n = e->next; 10213498266Sopenharmony_ci hsts_free(sts); 10313498266Sopenharmony_ci } 10413498266Sopenharmony_ci free(h->filename); 10513498266Sopenharmony_ci free(h); 10613498266Sopenharmony_ci *hp = NULL; 10713498266Sopenharmony_ci } 10813498266Sopenharmony_ci} 10913498266Sopenharmony_ci 11013498266Sopenharmony_cistatic struct stsentry *hsts_entry(void) 11113498266Sopenharmony_ci{ 11213498266Sopenharmony_ci return calloc(1, sizeof(struct stsentry)); 11313498266Sopenharmony_ci} 11413498266Sopenharmony_ci 11513498266Sopenharmony_cistatic CURLcode hsts_create(struct hsts *h, 11613498266Sopenharmony_ci const char *hostname, 11713498266Sopenharmony_ci bool subdomains, 11813498266Sopenharmony_ci curl_off_t expires) 11913498266Sopenharmony_ci{ 12013498266Sopenharmony_ci size_t hlen; 12113498266Sopenharmony_ci DEBUGASSERT(h); 12213498266Sopenharmony_ci DEBUGASSERT(hostname); 12313498266Sopenharmony_ci 12413498266Sopenharmony_ci hlen = strlen(hostname); 12513498266Sopenharmony_ci if(hlen && (hostname[hlen - 1] == '.')) 12613498266Sopenharmony_ci /* strip off any trailing dot */ 12713498266Sopenharmony_ci --hlen; 12813498266Sopenharmony_ci if(hlen) { 12913498266Sopenharmony_ci char *duphost; 13013498266Sopenharmony_ci struct stsentry *sts = hsts_entry(); 13113498266Sopenharmony_ci if(!sts) 13213498266Sopenharmony_ci return CURLE_OUT_OF_MEMORY; 13313498266Sopenharmony_ci 13413498266Sopenharmony_ci duphost = Curl_memdup0(hostname, hlen); 13513498266Sopenharmony_ci if(!duphost) { 13613498266Sopenharmony_ci free(sts); 13713498266Sopenharmony_ci return CURLE_OUT_OF_MEMORY; 13813498266Sopenharmony_ci } 13913498266Sopenharmony_ci 14013498266Sopenharmony_ci sts->host = duphost; 14113498266Sopenharmony_ci sts->expires = expires; 14213498266Sopenharmony_ci sts->includeSubDomains = subdomains; 14313498266Sopenharmony_ci Curl_llist_insert_next(&h->list, h->list.tail, sts, &sts->node); 14413498266Sopenharmony_ci } 14513498266Sopenharmony_ci return CURLE_OK; 14613498266Sopenharmony_ci} 14713498266Sopenharmony_ci 14813498266Sopenharmony_ciCURLcode Curl_hsts_parse(struct hsts *h, const char *hostname, 14913498266Sopenharmony_ci const char *header) 15013498266Sopenharmony_ci{ 15113498266Sopenharmony_ci const char *p = header; 15213498266Sopenharmony_ci curl_off_t expires = 0; 15313498266Sopenharmony_ci bool gotma = FALSE; 15413498266Sopenharmony_ci bool gotinc = FALSE; 15513498266Sopenharmony_ci bool subdomains = FALSE; 15613498266Sopenharmony_ci struct stsentry *sts; 15713498266Sopenharmony_ci time_t now = time(NULL); 15813498266Sopenharmony_ci 15913498266Sopenharmony_ci if(Curl_host_is_ipnum(hostname)) 16013498266Sopenharmony_ci /* "explicit IP address identification of all forms is excluded." 16113498266Sopenharmony_ci / RFC 6797 */ 16213498266Sopenharmony_ci return CURLE_OK; 16313498266Sopenharmony_ci 16413498266Sopenharmony_ci do { 16513498266Sopenharmony_ci while(*p && ISBLANK(*p)) 16613498266Sopenharmony_ci p++; 16713498266Sopenharmony_ci if(strncasecompare("max-age=", p, 8)) { 16813498266Sopenharmony_ci bool quoted = FALSE; 16913498266Sopenharmony_ci CURLofft offt; 17013498266Sopenharmony_ci char *endp; 17113498266Sopenharmony_ci 17213498266Sopenharmony_ci if(gotma) 17313498266Sopenharmony_ci return CURLE_BAD_FUNCTION_ARGUMENT; 17413498266Sopenharmony_ci 17513498266Sopenharmony_ci p += 8; 17613498266Sopenharmony_ci while(*p && ISBLANK(*p)) 17713498266Sopenharmony_ci p++; 17813498266Sopenharmony_ci if(*p == '\"') { 17913498266Sopenharmony_ci p++; 18013498266Sopenharmony_ci quoted = TRUE; 18113498266Sopenharmony_ci } 18213498266Sopenharmony_ci offt = curlx_strtoofft(p, &endp, 10, &expires); 18313498266Sopenharmony_ci if(offt == CURL_OFFT_FLOW) 18413498266Sopenharmony_ci expires = CURL_OFF_T_MAX; 18513498266Sopenharmony_ci else if(offt) 18613498266Sopenharmony_ci /* invalid max-age */ 18713498266Sopenharmony_ci return CURLE_BAD_FUNCTION_ARGUMENT; 18813498266Sopenharmony_ci p = endp; 18913498266Sopenharmony_ci if(quoted) { 19013498266Sopenharmony_ci if(*p != '\"') 19113498266Sopenharmony_ci return CURLE_BAD_FUNCTION_ARGUMENT; 19213498266Sopenharmony_ci p++; 19313498266Sopenharmony_ci } 19413498266Sopenharmony_ci gotma = TRUE; 19513498266Sopenharmony_ci } 19613498266Sopenharmony_ci else if(strncasecompare("includesubdomains", p, 17)) { 19713498266Sopenharmony_ci if(gotinc) 19813498266Sopenharmony_ci return CURLE_BAD_FUNCTION_ARGUMENT; 19913498266Sopenharmony_ci subdomains = TRUE; 20013498266Sopenharmony_ci p += 17; 20113498266Sopenharmony_ci gotinc = TRUE; 20213498266Sopenharmony_ci } 20313498266Sopenharmony_ci else { 20413498266Sopenharmony_ci /* unknown directive, do a lame attempt to skip */ 20513498266Sopenharmony_ci while(*p && (*p != ';')) 20613498266Sopenharmony_ci p++; 20713498266Sopenharmony_ci } 20813498266Sopenharmony_ci 20913498266Sopenharmony_ci while(*p && ISBLANK(*p)) 21013498266Sopenharmony_ci p++; 21113498266Sopenharmony_ci if(*p == ';') 21213498266Sopenharmony_ci p++; 21313498266Sopenharmony_ci } while(*p); 21413498266Sopenharmony_ci 21513498266Sopenharmony_ci if(!gotma) 21613498266Sopenharmony_ci /* max-age is mandatory */ 21713498266Sopenharmony_ci return CURLE_BAD_FUNCTION_ARGUMENT; 21813498266Sopenharmony_ci 21913498266Sopenharmony_ci if(!expires) { 22013498266Sopenharmony_ci /* remove the entry if present verbatim (without subdomain match) */ 22113498266Sopenharmony_ci sts = Curl_hsts(h, hostname, FALSE); 22213498266Sopenharmony_ci if(sts) { 22313498266Sopenharmony_ci Curl_llist_remove(&h->list, &sts->node, NULL); 22413498266Sopenharmony_ci hsts_free(sts); 22513498266Sopenharmony_ci } 22613498266Sopenharmony_ci return CURLE_OK; 22713498266Sopenharmony_ci } 22813498266Sopenharmony_ci 22913498266Sopenharmony_ci if(CURL_OFF_T_MAX - now < expires) 23013498266Sopenharmony_ci /* would overflow, use maximum value */ 23113498266Sopenharmony_ci expires = CURL_OFF_T_MAX; 23213498266Sopenharmony_ci else 23313498266Sopenharmony_ci expires += now; 23413498266Sopenharmony_ci 23513498266Sopenharmony_ci /* check if it already exists */ 23613498266Sopenharmony_ci sts = Curl_hsts(h, hostname, FALSE); 23713498266Sopenharmony_ci if(sts) { 23813498266Sopenharmony_ci /* just update these fields */ 23913498266Sopenharmony_ci sts->expires = expires; 24013498266Sopenharmony_ci sts->includeSubDomains = subdomains; 24113498266Sopenharmony_ci } 24213498266Sopenharmony_ci else 24313498266Sopenharmony_ci return hsts_create(h, hostname, subdomains, expires); 24413498266Sopenharmony_ci 24513498266Sopenharmony_ci return CURLE_OK; 24613498266Sopenharmony_ci} 24713498266Sopenharmony_ci 24813498266Sopenharmony_ci/* 24913498266Sopenharmony_ci * Return TRUE if the given host name is currently an HSTS one. 25013498266Sopenharmony_ci * 25113498266Sopenharmony_ci * The 'subdomain' argument tells the function if subdomain matching should be 25213498266Sopenharmony_ci * attempted. 25313498266Sopenharmony_ci */ 25413498266Sopenharmony_cistruct stsentry *Curl_hsts(struct hsts *h, const char *hostname, 25513498266Sopenharmony_ci bool subdomain) 25613498266Sopenharmony_ci{ 25713498266Sopenharmony_ci if(h) { 25813498266Sopenharmony_ci char buffer[MAX_HSTS_HOSTLEN + 1]; 25913498266Sopenharmony_ci time_t now = time(NULL); 26013498266Sopenharmony_ci size_t hlen = strlen(hostname); 26113498266Sopenharmony_ci struct Curl_llist_element *e; 26213498266Sopenharmony_ci struct Curl_llist_element *n; 26313498266Sopenharmony_ci 26413498266Sopenharmony_ci if((hlen > MAX_HSTS_HOSTLEN) || !hlen) 26513498266Sopenharmony_ci return NULL; 26613498266Sopenharmony_ci memcpy(buffer, hostname, hlen); 26713498266Sopenharmony_ci if(hostname[hlen-1] == '.') 26813498266Sopenharmony_ci /* remove the trailing dot */ 26913498266Sopenharmony_ci --hlen; 27013498266Sopenharmony_ci buffer[hlen] = 0; 27113498266Sopenharmony_ci hostname = buffer; 27213498266Sopenharmony_ci 27313498266Sopenharmony_ci for(e = h->list.head; e; e = n) { 27413498266Sopenharmony_ci struct stsentry *sts = e->ptr; 27513498266Sopenharmony_ci n = e->next; 27613498266Sopenharmony_ci if(sts->expires <= now) { 27713498266Sopenharmony_ci /* remove expired entries */ 27813498266Sopenharmony_ci Curl_llist_remove(&h->list, &sts->node, NULL); 27913498266Sopenharmony_ci hsts_free(sts); 28013498266Sopenharmony_ci continue; 28113498266Sopenharmony_ci } 28213498266Sopenharmony_ci if(subdomain && sts->includeSubDomains) { 28313498266Sopenharmony_ci size_t ntail = strlen(sts->host); 28413498266Sopenharmony_ci if(ntail < hlen) { 28513498266Sopenharmony_ci size_t offs = hlen - ntail; 28613498266Sopenharmony_ci if((hostname[offs-1] == '.') && 28713498266Sopenharmony_ci strncasecompare(&hostname[offs], sts->host, ntail)) 28813498266Sopenharmony_ci return sts; 28913498266Sopenharmony_ci } 29013498266Sopenharmony_ci } 29113498266Sopenharmony_ci if(strcasecompare(hostname, sts->host)) 29213498266Sopenharmony_ci return sts; 29313498266Sopenharmony_ci } 29413498266Sopenharmony_ci } 29513498266Sopenharmony_ci return NULL; /* no match */ 29613498266Sopenharmony_ci} 29713498266Sopenharmony_ci 29813498266Sopenharmony_ci/* 29913498266Sopenharmony_ci * Send this HSTS entry to the write callback. 30013498266Sopenharmony_ci */ 30113498266Sopenharmony_cistatic CURLcode hsts_push(struct Curl_easy *data, 30213498266Sopenharmony_ci struct curl_index *i, 30313498266Sopenharmony_ci struct stsentry *sts, 30413498266Sopenharmony_ci bool *stop) 30513498266Sopenharmony_ci{ 30613498266Sopenharmony_ci struct curl_hstsentry e; 30713498266Sopenharmony_ci CURLSTScode sc; 30813498266Sopenharmony_ci struct tm stamp; 30913498266Sopenharmony_ci CURLcode result; 31013498266Sopenharmony_ci 31113498266Sopenharmony_ci e.name = (char *)sts->host; 31213498266Sopenharmony_ci e.namelen = strlen(sts->host); 31313498266Sopenharmony_ci e.includeSubDomains = sts->includeSubDomains; 31413498266Sopenharmony_ci 31513498266Sopenharmony_ci if(sts->expires != TIME_T_MAX) { 31613498266Sopenharmony_ci result = Curl_gmtime((time_t)sts->expires, &stamp); 31713498266Sopenharmony_ci if(result) 31813498266Sopenharmony_ci return result; 31913498266Sopenharmony_ci 32013498266Sopenharmony_ci msnprintf(e.expire, sizeof(e.expire), "%d%02d%02d %02d:%02d:%02d", 32113498266Sopenharmony_ci stamp.tm_year + 1900, stamp.tm_mon + 1, stamp.tm_mday, 32213498266Sopenharmony_ci stamp.tm_hour, stamp.tm_min, stamp.tm_sec); 32313498266Sopenharmony_ci } 32413498266Sopenharmony_ci else 32513498266Sopenharmony_ci strcpy(e.expire, UNLIMITED); 32613498266Sopenharmony_ci 32713498266Sopenharmony_ci sc = data->set.hsts_write(data, &e, i, 32813498266Sopenharmony_ci data->set.hsts_write_userp); 32913498266Sopenharmony_ci *stop = (sc != CURLSTS_OK); 33013498266Sopenharmony_ci return sc == CURLSTS_FAIL ? CURLE_BAD_FUNCTION_ARGUMENT : CURLE_OK; 33113498266Sopenharmony_ci} 33213498266Sopenharmony_ci 33313498266Sopenharmony_ci/* 33413498266Sopenharmony_ci * Write this single hsts entry to a single output line 33513498266Sopenharmony_ci */ 33613498266Sopenharmony_cistatic CURLcode hsts_out(struct stsentry *sts, FILE *fp) 33713498266Sopenharmony_ci{ 33813498266Sopenharmony_ci struct tm stamp; 33913498266Sopenharmony_ci if(sts->expires != TIME_T_MAX) { 34013498266Sopenharmony_ci CURLcode result = Curl_gmtime((time_t)sts->expires, &stamp); 34113498266Sopenharmony_ci if(result) 34213498266Sopenharmony_ci return result; 34313498266Sopenharmony_ci fprintf(fp, "%s%s \"%d%02d%02d %02d:%02d:%02d\"\n", 34413498266Sopenharmony_ci sts->includeSubDomains ? ".": "", sts->host, 34513498266Sopenharmony_ci stamp.tm_year + 1900, stamp.tm_mon + 1, stamp.tm_mday, 34613498266Sopenharmony_ci stamp.tm_hour, stamp.tm_min, stamp.tm_sec); 34713498266Sopenharmony_ci } 34813498266Sopenharmony_ci else 34913498266Sopenharmony_ci fprintf(fp, "%s%s \"%s\"\n", 35013498266Sopenharmony_ci sts->includeSubDomains ? ".": "", sts->host, UNLIMITED); 35113498266Sopenharmony_ci return CURLE_OK; 35213498266Sopenharmony_ci} 35313498266Sopenharmony_ci 35413498266Sopenharmony_ci 35513498266Sopenharmony_ci/* 35613498266Sopenharmony_ci * Curl_https_save() writes the HSTS cache to file and callback. 35713498266Sopenharmony_ci */ 35813498266Sopenharmony_ciCURLcode Curl_hsts_save(struct Curl_easy *data, struct hsts *h, 35913498266Sopenharmony_ci const char *file) 36013498266Sopenharmony_ci{ 36113498266Sopenharmony_ci struct Curl_llist_element *e; 36213498266Sopenharmony_ci struct Curl_llist_element *n; 36313498266Sopenharmony_ci CURLcode result = CURLE_OK; 36413498266Sopenharmony_ci FILE *out; 36513498266Sopenharmony_ci char *tempstore = NULL; 36613498266Sopenharmony_ci 36713498266Sopenharmony_ci if(!h) 36813498266Sopenharmony_ci /* no cache activated */ 36913498266Sopenharmony_ci return CURLE_OK; 37013498266Sopenharmony_ci 37113498266Sopenharmony_ci /* if no new name is given, use the one we stored from the load */ 37213498266Sopenharmony_ci if(!file && h->filename) 37313498266Sopenharmony_ci file = h->filename; 37413498266Sopenharmony_ci 37513498266Sopenharmony_ci if((h->flags & CURLHSTS_READONLYFILE) || !file || !file[0]) 37613498266Sopenharmony_ci /* marked as read-only, no file or zero length file name */ 37713498266Sopenharmony_ci goto skipsave; 37813498266Sopenharmony_ci 37913498266Sopenharmony_ci result = Curl_fopen(data, file, &out, &tempstore); 38013498266Sopenharmony_ci if(!result) { 38113498266Sopenharmony_ci fputs("# Your HSTS cache. https://curl.se/docs/hsts.html\n" 38213498266Sopenharmony_ci "# This file was generated by libcurl! Edit at your own risk.\n", 38313498266Sopenharmony_ci out); 38413498266Sopenharmony_ci for(e = h->list.head; e; e = n) { 38513498266Sopenharmony_ci struct stsentry *sts = e->ptr; 38613498266Sopenharmony_ci n = e->next; 38713498266Sopenharmony_ci result = hsts_out(sts, out); 38813498266Sopenharmony_ci if(result) 38913498266Sopenharmony_ci break; 39013498266Sopenharmony_ci } 39113498266Sopenharmony_ci fclose(out); 39213498266Sopenharmony_ci if(!result && tempstore && Curl_rename(tempstore, file)) 39313498266Sopenharmony_ci result = CURLE_WRITE_ERROR; 39413498266Sopenharmony_ci 39513498266Sopenharmony_ci if(result && tempstore) 39613498266Sopenharmony_ci unlink(tempstore); 39713498266Sopenharmony_ci } 39813498266Sopenharmony_ci free(tempstore); 39913498266Sopenharmony_ciskipsave: 40013498266Sopenharmony_ci if(data->set.hsts_write) { 40113498266Sopenharmony_ci /* if there's a write callback */ 40213498266Sopenharmony_ci struct curl_index i; /* count */ 40313498266Sopenharmony_ci i.total = h->list.size; 40413498266Sopenharmony_ci i.index = 0; 40513498266Sopenharmony_ci for(e = h->list.head; e; e = n) { 40613498266Sopenharmony_ci struct stsentry *sts = e->ptr; 40713498266Sopenharmony_ci bool stop; 40813498266Sopenharmony_ci n = e->next; 40913498266Sopenharmony_ci result = hsts_push(data, &i, sts, &stop); 41013498266Sopenharmony_ci if(result || stop) 41113498266Sopenharmony_ci break; 41213498266Sopenharmony_ci i.index++; 41313498266Sopenharmony_ci } 41413498266Sopenharmony_ci } 41513498266Sopenharmony_ci return result; 41613498266Sopenharmony_ci} 41713498266Sopenharmony_ci 41813498266Sopenharmony_ci/* only returns SERIOUS errors */ 41913498266Sopenharmony_cistatic CURLcode hsts_add(struct hsts *h, char *line) 42013498266Sopenharmony_ci{ 42113498266Sopenharmony_ci /* Example lines: 42213498266Sopenharmony_ci example.com "20191231 10:00:00" 42313498266Sopenharmony_ci .example.net "20191231 10:00:00" 42413498266Sopenharmony_ci */ 42513498266Sopenharmony_ci char host[MAX_HSTS_HOSTLEN + 1]; 42613498266Sopenharmony_ci char date[MAX_HSTS_DATELEN + 1]; 42713498266Sopenharmony_ci int rc; 42813498266Sopenharmony_ci 42913498266Sopenharmony_ci rc = sscanf(line, 43013498266Sopenharmony_ci "%" MAX_HSTS_HOSTLENSTR "s \"%" MAX_HSTS_DATELENSTR "[^\"]\"", 43113498266Sopenharmony_ci host, date); 43213498266Sopenharmony_ci if(2 == rc) { 43313498266Sopenharmony_ci time_t expires = strcmp(date, UNLIMITED) ? Curl_getdate_capped(date) : 43413498266Sopenharmony_ci TIME_T_MAX; 43513498266Sopenharmony_ci CURLcode result = CURLE_OK; 43613498266Sopenharmony_ci char *p = host; 43713498266Sopenharmony_ci bool subdomain = FALSE; 43813498266Sopenharmony_ci struct stsentry *e; 43913498266Sopenharmony_ci if(p[0] == '.') { 44013498266Sopenharmony_ci p++; 44113498266Sopenharmony_ci subdomain = TRUE; 44213498266Sopenharmony_ci } 44313498266Sopenharmony_ci /* only add it if not already present */ 44413498266Sopenharmony_ci e = Curl_hsts(h, p, subdomain); 44513498266Sopenharmony_ci if(!e) 44613498266Sopenharmony_ci result = hsts_create(h, p, subdomain, expires); 44713498266Sopenharmony_ci else { 44813498266Sopenharmony_ci /* the same host name, use the largest expire time */ 44913498266Sopenharmony_ci if(expires > e->expires) 45013498266Sopenharmony_ci e->expires = expires; 45113498266Sopenharmony_ci } 45213498266Sopenharmony_ci if(result) 45313498266Sopenharmony_ci return result; 45413498266Sopenharmony_ci } 45513498266Sopenharmony_ci 45613498266Sopenharmony_ci return CURLE_OK; 45713498266Sopenharmony_ci} 45813498266Sopenharmony_ci 45913498266Sopenharmony_ci/* 46013498266Sopenharmony_ci * Load HSTS data from callback. 46113498266Sopenharmony_ci * 46213498266Sopenharmony_ci */ 46313498266Sopenharmony_cistatic CURLcode hsts_pull(struct Curl_easy *data, struct hsts *h) 46413498266Sopenharmony_ci{ 46513498266Sopenharmony_ci /* if the HSTS read callback is set, use it */ 46613498266Sopenharmony_ci if(data->set.hsts_read) { 46713498266Sopenharmony_ci CURLSTScode sc; 46813498266Sopenharmony_ci DEBUGASSERT(h); 46913498266Sopenharmony_ci do { 47013498266Sopenharmony_ci char buffer[MAX_HSTS_HOSTLEN + 1]; 47113498266Sopenharmony_ci struct curl_hstsentry e; 47213498266Sopenharmony_ci e.name = buffer; 47313498266Sopenharmony_ci e.namelen = sizeof(buffer)-1; 47413498266Sopenharmony_ci e.includeSubDomains = FALSE; /* default */ 47513498266Sopenharmony_ci e.expire[0] = 0; 47613498266Sopenharmony_ci e.name[0] = 0; /* just to make it clean */ 47713498266Sopenharmony_ci sc = data->set.hsts_read(data, &e, data->set.hsts_read_userp); 47813498266Sopenharmony_ci if(sc == CURLSTS_OK) { 47913498266Sopenharmony_ci time_t expires; 48013498266Sopenharmony_ci CURLcode result; 48113498266Sopenharmony_ci DEBUGASSERT(e.name[0]); 48213498266Sopenharmony_ci if(!e.name[0]) 48313498266Sopenharmony_ci /* bail out if no name was stored */ 48413498266Sopenharmony_ci return CURLE_BAD_FUNCTION_ARGUMENT; 48513498266Sopenharmony_ci if(e.expire[0]) 48613498266Sopenharmony_ci expires = Curl_getdate_capped(e.expire); 48713498266Sopenharmony_ci else 48813498266Sopenharmony_ci expires = TIME_T_MAX; /* the end of time */ 48913498266Sopenharmony_ci result = hsts_create(h, e.name, 49013498266Sopenharmony_ci /* bitfield to bool conversion: */ 49113498266Sopenharmony_ci e.includeSubDomains ? TRUE : FALSE, 49213498266Sopenharmony_ci expires); 49313498266Sopenharmony_ci if(result) 49413498266Sopenharmony_ci return result; 49513498266Sopenharmony_ci } 49613498266Sopenharmony_ci else if(sc == CURLSTS_FAIL) 49713498266Sopenharmony_ci return CURLE_ABORTED_BY_CALLBACK; 49813498266Sopenharmony_ci } while(sc == CURLSTS_OK); 49913498266Sopenharmony_ci } 50013498266Sopenharmony_ci return CURLE_OK; 50113498266Sopenharmony_ci} 50213498266Sopenharmony_ci 50313498266Sopenharmony_ci/* 50413498266Sopenharmony_ci * Load the HSTS cache from the given file. The text based line-oriented file 50513498266Sopenharmony_ci * format is documented here: https://curl.se/docs/hsts.html 50613498266Sopenharmony_ci * 50713498266Sopenharmony_ci * This function only returns error on major problems that prevent hsts 50813498266Sopenharmony_ci * handling to work completely. It will ignore individual syntactical errors 50913498266Sopenharmony_ci * etc. 51013498266Sopenharmony_ci */ 51113498266Sopenharmony_cistatic CURLcode hsts_load(struct hsts *h, const char *file) 51213498266Sopenharmony_ci{ 51313498266Sopenharmony_ci CURLcode result = CURLE_OK; 51413498266Sopenharmony_ci char *line = NULL; 51513498266Sopenharmony_ci FILE *fp; 51613498266Sopenharmony_ci 51713498266Sopenharmony_ci /* we need a private copy of the file name so that the hsts cache file 51813498266Sopenharmony_ci name survives an easy handle reset */ 51913498266Sopenharmony_ci free(h->filename); 52013498266Sopenharmony_ci h->filename = strdup(file); 52113498266Sopenharmony_ci if(!h->filename) 52213498266Sopenharmony_ci return CURLE_OUT_OF_MEMORY; 52313498266Sopenharmony_ci 52413498266Sopenharmony_ci fp = fopen(file, FOPEN_READTEXT); 52513498266Sopenharmony_ci if(fp) { 52613498266Sopenharmony_ci line = malloc(MAX_HSTS_LINE); 52713498266Sopenharmony_ci if(!line) 52813498266Sopenharmony_ci goto fail; 52913498266Sopenharmony_ci while(Curl_get_line(line, MAX_HSTS_LINE, fp)) { 53013498266Sopenharmony_ci char *lineptr = line; 53113498266Sopenharmony_ci while(*lineptr && ISBLANK(*lineptr)) 53213498266Sopenharmony_ci lineptr++; 53313498266Sopenharmony_ci if(*lineptr == '#') 53413498266Sopenharmony_ci /* skip commented lines */ 53513498266Sopenharmony_ci continue; 53613498266Sopenharmony_ci 53713498266Sopenharmony_ci hsts_add(h, lineptr); 53813498266Sopenharmony_ci } 53913498266Sopenharmony_ci free(line); /* free the line buffer */ 54013498266Sopenharmony_ci fclose(fp); 54113498266Sopenharmony_ci } 54213498266Sopenharmony_ci return result; 54313498266Sopenharmony_ci 54413498266Sopenharmony_cifail: 54513498266Sopenharmony_ci Curl_safefree(h->filename); 54613498266Sopenharmony_ci fclose(fp); 54713498266Sopenharmony_ci return CURLE_OUT_OF_MEMORY; 54813498266Sopenharmony_ci} 54913498266Sopenharmony_ci 55013498266Sopenharmony_ci/* 55113498266Sopenharmony_ci * Curl_hsts_loadfile() loads HSTS from file 55213498266Sopenharmony_ci */ 55313498266Sopenharmony_ciCURLcode Curl_hsts_loadfile(struct Curl_easy *data, 55413498266Sopenharmony_ci struct hsts *h, const char *file) 55513498266Sopenharmony_ci{ 55613498266Sopenharmony_ci DEBUGASSERT(h); 55713498266Sopenharmony_ci (void)data; 55813498266Sopenharmony_ci return hsts_load(h, file); 55913498266Sopenharmony_ci} 56013498266Sopenharmony_ci 56113498266Sopenharmony_ci/* 56213498266Sopenharmony_ci * Curl_hsts_loadcb() loads HSTS from callback 56313498266Sopenharmony_ci */ 56413498266Sopenharmony_ciCURLcode Curl_hsts_loadcb(struct Curl_easy *data, struct hsts *h) 56513498266Sopenharmony_ci{ 56613498266Sopenharmony_ci if(h) 56713498266Sopenharmony_ci return hsts_pull(data, h); 56813498266Sopenharmony_ci return CURLE_OK; 56913498266Sopenharmony_ci} 57013498266Sopenharmony_ci 57113498266Sopenharmony_civoid Curl_hsts_loadfiles(struct Curl_easy *data) 57213498266Sopenharmony_ci{ 57313498266Sopenharmony_ci struct curl_slist *l = data->state.hstslist; 57413498266Sopenharmony_ci if(l) { 57513498266Sopenharmony_ci Curl_share_lock(data, CURL_LOCK_DATA_HSTS, CURL_LOCK_ACCESS_SINGLE); 57613498266Sopenharmony_ci 57713498266Sopenharmony_ci while(l) { 57813498266Sopenharmony_ci (void)Curl_hsts_loadfile(data, data->hsts, l->data); 57913498266Sopenharmony_ci l = l->next; 58013498266Sopenharmony_ci } 58113498266Sopenharmony_ci Curl_share_unlock(data, CURL_LOCK_DATA_HSTS); 58213498266Sopenharmony_ci } 58313498266Sopenharmony_ci} 58413498266Sopenharmony_ci 58513498266Sopenharmony_ci#endif /* CURL_DISABLE_HTTP || CURL_DISABLE_HSTS */ 586