HSTS: support policy in urldb

This commit is contained in:
John-Mark Bell 2018-04-22 02:25:43 +00:00
parent 1c05280b5c
commit a6014cae15
2 changed files with 196 additions and 10 deletions

View File

@ -108,6 +108,7 @@
#include "utils/time.h"
#include "utils/nsurl.h"
#include "utils/ascii.h"
#include "utils/http.h"
#include "netsurf/bitmap.h"
#include "desktop/cookie_manager.h"
#include "desktop/gui_internal.h"
@ -222,6 +223,11 @@ struct path_data {
struct path_data *last; /**< Last child */
};
struct hsts_data {
time_t expires; /**< Expiry time */
bool include_sub_domains; /**< Whether to include subdomains */
};
struct host_part {
/**
* Known paths on this host. This _must_ be first so that
@ -233,6 +239,8 @@ struct host_part {
* without verifying certificate authenticity
*/
bool permit_invalid_certs;
/* HSTS data */
struct hsts_data hsts;
/**
* Part of host string
@ -290,7 +298,7 @@ static int loaded_cookie_file_version;
/** Minimum URL database file version */
#define MIN_URL_FILE_VERSION 106
/** Current URL database file version */
#define URL_FILE_VERSION 106
#define URL_FILE_VERSION 107
/**
* filter for url presence in database
@ -511,7 +519,8 @@ static void urldb_save_search_tree(struct search_node *parent, FILE *fp)
unsigned int path_count = 0;
char *path, *p, *end;
int path_alloc = 64, path_used = 1;
time_t expiry;
time_t expiry, hsts_expiry = 0;
int hsts_include_subdomains = 0;
expiry = time(NULL) - ((60 * 60 * 24) * nsoption_int(expire_url));
@ -537,13 +546,25 @@ static void urldb_save_search_tree(struct search_node *parent, FILE *fp)
p += written;
}
h = parent->data;
if (h && h->hsts.expires > expiry) {
hsts_expiry = h->hsts.expires;
hsts_include_subdomains = h->hsts.include_sub_domains;
}
urldb_count_urls(&parent->data->paths, expiry, &path_count);
if (path_count > 0) {
fprintf(fp, "%s\n%i\n", host, path_count);
fprintf(fp, "%s %i ", host, hsts_include_subdomains);
urldb_write_timet(fp, hsts_expiry);
fprintf(fp, "%i\n", path_count);
urldb_write_paths(&parent->data->paths, host, fp,
&path, &path_alloc, &path_used, expiry);
} else if (hsts_expiry) {
fprintf(fp, "%s %i ", host, hsts_include_subdomains);
urldb_write_timet(fp, hsts_expiry);
fprintf(fp, "0\n");
}
free(path);
@ -2894,6 +2915,9 @@ nserror urldb_load(const char *filename)
}
while (fgets(host, sizeof host, fp)) {
time_t hsts_expiry = 0;
int hsts_include_sub_domains = 0;
/* get the hostname */
length = strlen(host) - 1;
host[length] = '\0';
@ -2911,6 +2935,25 @@ nserror urldb_load(const char *filename)
continue;
}
if (version >= 107) {
char *p = host;
while (*p && *p != ' ') p++;
while (*p && *p == ' ') { *p = '\0'; p++; }
hsts_include_sub_domains = (*p == '1');
while (*p && *p != ' ') p++;
while (*p && *p == ' ') p++;
nsc_snptimet(p, strlen(p), &hsts_expiry);
}
h = urldb_add_host(host);
if (!h) {
NSLOG(netsurf, INFO, "Failed adding host: '%s'", host);
fclose(fp);
return NSERROR_NOMEM;
}
h->hsts.expires = hsts_expiry;
h->hsts.include_sub_domains = hsts_include_sub_domains;
/* read number of URLs */
if (!fgets(s, MAXIMUM_URL_LENGTH, fp))
break;
@ -2922,13 +2965,6 @@ nserror urldb_load(const char *filename)
continue;
}
h = urldb_add_host(host);
if (!h) {
NSLOG(netsurf, INFO, "Failed adding host: '%s'", host);
fclose(fp);
return NSERROR_NOMEM;
}
/* load the non-corrupt data */
for (i = 0; i < urls; i++) {
struct path_data *p = NULL;
@ -3460,6 +3496,138 @@ bool urldb_get_cert_permissions(nsurl *url)
}
/* exported interface documented in content/urldb.h */
bool urldb_set_hsts_policy(struct nsurl *url, const char *header)
{
struct path_data *p;
struct host_part *h;
lwc_string *host;
time_t now = time(NULL);
http_strict_transport_security *sts;
uint32_t max_age = 0;
nserror error;
assert(url);
host = nsurl_get_component(url, NSURL_HOST);
if (host != NULL) {
if (urldb__host_is_ip_address(lwc_string_data(host))) {
/* Host is IP: ignore */
lwc_string_unref(host);
return true;
} else if (lwc_string_length(host) == 0) {
/* Host is blank: ignore */
lwc_string_unref(host);
return true;
}
lwc_string_unref(host);
} else {
/* No host part: ignore */
return true;
}
/* add url, in case it's missing */
urldb_add_url(url);
p = urldb_find_url(url);
if (!p)
return false;
for (; p && p->parent; p = p->parent)
/* do nothing */;
assert(p);
h = (struct host_part *)p;
if (h->permit_invalid_certs) {
/* Transport is tainted: ignore */
return true;
}
error = http_parse_strict_transport_security(header, &sts);
if (error != NSERROR_OK) {
/* Parse failed: ignore */
return true;
}
h->hsts.include_sub_domains =
http_strict_transport_security_include_subdomains(sts);
max_age = http_strict_transport_security_max_age(sts);
if (max_age == 0) {
h->hsts.expires = 0;
h->hsts.include_sub_domains = false;
} else if (now + max_age > h->hsts.expires) {
h->hsts.expires = now + max_age;
}
http_strict_transport_security_destroy(sts);
return true;
}
/* exported interface documented in content/urldb.h */
bool urldb_get_hsts_enabled(struct nsurl *url)
{
struct path_data *p;
const struct host_part *h;
lwc_string *host;
time_t now = time(NULL);
assert(url);
host = nsurl_get_component(url, NSURL_HOST);
if (host != NULL) {
if (urldb__host_is_ip_address(lwc_string_data(host))) {
/* Host is IP: not enabled */
lwc_string_unref(host);
return false;
} else if (lwc_string_length(host) == 0) {
/* Host is blank: not enabled */
lwc_string_unref(host);
return false;
}
lwc_string_unref(host);
} else {
/* No host part: not enabled */
return false;
}
/* The URL must exist in the db in order to find HSTS policy, since
* we search up the tree from the URL node, and policy from further
* up may also apply. */
urldb_add_url(url);
p = urldb_find_url(url);
if (!p)
return false;
for (; p && p->parent; p = p->parent)
/* do nothing */;
assert(p);
h = (const struct host_part *)p;
/* Consult record for this host */
if (h->hsts.expires > now) {
/* Not expired */
return true;
}
/* Consult parent domains */
for (h = h->parent; h && h != &db_root; h = h->parent) {
if (h->hsts.expires > now && h->hsts.include_sub_domains) {
/* Not expired and subdomains included */
return true;
}
}
return false;
}
/* exported interface documented in netsurf/url_db.h */
void
urldb_iterate_partial(const char *prefix,

View File

@ -131,4 +131,22 @@ bool urldb_set_cookie(const char *header, struct nsurl *url, struct nsurl *refer
char *urldb_get_cookie(struct nsurl *url, bool include_http_only);
/**
* Set HSTS policy for an URL
*
* \param url URL being fetched
* \param header Strict-Transport-Security header value
* \return true on success, false otherwise
*/
bool urldb_set_hsts_policy(struct nsurl *url, const char *header);
/**
* Determine if HSTS policy is enabled for an URL
*
* \param url URL being fetched
* \return true if HSTS policy is enabled, false otherwise
*/
bool urldb_get_hsts_enabled(struct nsurl *url);
#endif