summaryrefslogtreecommitdiff
path: root/src/hsts.c
diff options
context:
space:
mode:
authorAdam Ehlers Nyholm Thomsen <adament@adament.net>2012-12-11 00:18:41 +0100
committerAdam Ehlers Nyholm Thomsen <adament@adament.net>2012-12-11 00:18:41 +0100
commit307c558801ba06c3a36f88e2ae9dbe208f6a0059 (patch)
tree793044d3791aec79a782ee5f4ef5aeed672b8771 /src/hsts.c
parent944fc426af70bb926749d0273c082e5feb7b5306 (diff)
downloaddwb-307c558801ba06c3a36f88e2ae9dbe208f6a0059.zip
First version of HSTS implementation.
Diffstat (limited to 'src/hsts.c')
-rw-r--r--src/hsts.c697
1 files changed, 697 insertions, 0 deletions
diff --git a/src/hsts.c b/src/hsts.c
new file mode 100644
index 00000000..f622bb54
--- /dev/null
+++ b/src/hsts.c
@@ -0,0 +1,697 @@
+/*
+ * Copyright (c) 2010-2012 Stefan Bolte <portix@gmx.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+#include <stdio.h>
+#include <string.h>
+#include <glib-object.h>
+#include <glib/gstdio.h>
+#include "dwb.h"
+#include "util.h"
+#include "hsts.h"
+
+/*
+ * This file contains an HSTS (HTTP Strict Transport Security) implementation
+ * for the dwb browser. It works by registering a session interface with soup
+ * and rewriting relevant requests when they are queued, and listening for
+ * hsts headers. The approach was inspired by the HSTS implementation in thje
+ * midori browser.
+ *
+ * Current Features:
+ * + Enforces HSTS as specified in [RFC6797]
+ * + Loading and saving of the cache
+ *
+ * TODO:
+ * + Handle UTF-8 BOM in loading code
+ * + Bootstrap whitelist (steal from chromium)
+ * + Enforce strict ssl verification on known hsts hosts
+ * + Add support for certificate pining a la Chromium
+ * + Periodic saving of database to mitigate loss of information in event of crash
+ *
+ * Problems:
+ * 1. The implementation doesn't consider mixed content, which should be
+ * blocked according to RFC 6797 12.4
+ *
+ * Sites that support HSTS to test the system with:
+ * + crypto.cat
+ */
+
+#define HSTS_HEADER_NAME "Strict-Transport-Security"
+
+/* The HSTSEntry data structure represents a known host in the HSTS database
+ *
+ * Members:
+ * expiry - the expiry of the rule, represented as microseconds since January 1, 1970 UTC.
+ * sub_domains - whether the rule applies to sub_domains
+ */
+typedef struct _HSTSEntry {
+ gint64 expiry;
+ gboolean sub_domains;
+} HSTSEntry;
+
+/* Allocate a new HSTSEntry and initialise it. It is initialised to have
+ * maximum expiry (effectively indefinite life) and not to apply to sub
+ * domains.
+ */
+static HSTSEntry *
+hsts_entry_new()
+{
+ HSTSEntry *entry = dwb_malloc(sizeof(HSTSEntry));
+ entry->expiry = G_MAXINT64;
+ entry->sub_domains = false;
+ return entry;
+}
+
+/* Allocates and initialises a new HSTSEntry to the given values.
+ * Params:
+ * max_age - number of seconds the rule should live.
+ * sub_domains - whether the rule applies to sub_domains
+ */
+static HSTSEntry *
+hsts_entry_new_from_val(gint64 max_age, gboolean sub_domains)
+{
+ HSTSEntry *entry = hsts_entry_new();
+ entry->expiry = g_get_real_time();
+ if(max_age > (G_MAXINT64 - entry->expiry)/G_USEC_PER_SEC)
+ entry->expiry = G_MAXINT64;
+ else
+ entry->expiry += max_age*G_USEC_PER_SEC;
+ entry->sub_domains = sub_domains;
+ return entry;
+}
+
+/* Frees the HSTSEntry
+ */
+static void
+hsts_entry_free(HSTSEntry *entry)
+{
+ g_free(entry);
+}
+
+/*
+ * HSTSProvider works by registering as a SoupSessionFeature and rewriting all
+ * http requests into https requests for known hosts. However this means that
+ * HSTSProvider has to be implement the SoupSessionFeatureInterface and hence
+ * all the boilerplate gobject code in the following.
+ *
+ */
+
+/*
+ * Type macros.
+ */
+#define HSTS_TYPE_PROVIDER (hsts_provider_get_type ())
+#define HSTS_PROVIDER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), HSTS_TYPE_PROVIDER, HSTSProvider))
+#define HSTS_IS_PROVIDER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), HSTS_TYPE_PROVIDER))
+#define HSTS_PROVIDER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), HSTS_TYPE_PROVIDER, HSTSProviderClass))
+#define HSTS_IS_PROVIDER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), HSTS_TYPE_PROVIDER))
+#define HSTS_PROVIDER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), HSTS_TYPE_PROVIDER, HSTSProviderClass))
+#define HSTS_PROVIDER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), HSTS_TYPE_PROVIDER, HSTSProviderPrivate))
+
+/* The HSTSProvider public interface
+ */
+typedef struct _HSTSProvider
+{
+ GObject parent_instance;
+} HSTSProvider;
+
+/* The private members of the HSTSProvider
+ */
+typedef struct _HSTSProviderPrivate
+{
+ GHashTable *domains;
+} HSTSProviderPrivate;
+
+/* The class members of the HSTSProvider
+ */
+typedef struct _HSTSProviderClass
+{
+ GObjectClass parent_class;
+
+ /* The following static variables are used to do case insensitive comparisons
+ * of directive names, as specified in RFC 6797 6.1 2.
+ */
+ gchar *directive_max_age;
+ gchar *directive_sub_domains;
+} HSTSProviderClass;
+
+/* Prototypes of various functions, some are needed for glib magic. This is not an exhaustive
+ * list of the hsts_provider functions.
+ */
+static void hsts_provider_init (HSTSProvider *self);
+static void hsts_provider_class_init (HSTSProviderClass *klass);
+static void hsts_provider_base_class_init (HSTSProviderClass *klass);
+static void hsts_provider_base_class_finalize (HSTSProviderClass *klass);
+static gpointer hsts_provider_parent_class = NULL;
+static void hsts_provider_session_feature_init(SoupSessionFeatureInterface *feature_interface, gpointer interface_data);
+static void hsts_provider_finalize (GObject *object);
+
+/* GLib essential function. This basically declares the existence of the
+ * HSTSProvider class to GLib and gives it various information about it. This
+ * rather cumbersome function is needed to get dynamic class members(ie.
+ * setting the base_* options).
+ */
+GType
+hsts_provider_get_type (void)
+{
+ static volatile gsize g_define_type_id__volatile = 0;
+ if (g_once_init_enter (&g_define_type_id__volatile))
+ {
+ GTypeInfo info;
+ info.class_size = sizeof(HSTSProviderClass);
+ info.base_init = (GBaseInitFunc) hsts_provider_base_class_init;
+ info.base_finalize = (GBaseFinalizeFunc) hsts_provider_base_class_finalize;
+ info.class_init = (GClassInitFunc) hsts_provider_class_init;
+ info.class_finalize = NULL;
+ info.class_data = NULL;
+ info.instance_size = sizeof(HSTSProvider);
+ info.n_preallocs = 0;
+ info.instance_init = (GInstanceInitFunc) hsts_provider_init;
+ info.value_table = NULL;
+
+ GType g_define_type_id = g_type_register_static (G_TYPE_OBJECT, g_intern_static_string ("HSTSProvider"), &info, 0);
+
+ const GInterfaceInfo g_implement_interface_info = {
+ (GInterfaceInitFunc) hsts_provider_session_feature_init, NULL, NULL
+ };
+ g_type_add_interface_static (g_define_type_id, SOUP_TYPE_SESSION_FEATURE, &g_implement_interface_info);
+ g_once_init_leave (&g_define_type_id__volatile, g_define_type_id);
+ }
+ return g_define_type_id__volatile;
+}
+
+/* Initialise the dynamic class members of HSTSProvider
+ */
+static void
+hsts_provider_base_class_init (HSTSProviderClass *klass)
+{
+ klass->directive_max_age = g_utf8_casefold("max-age", -1);
+ klass->directive_sub_domains = g_utf8_casefold("includeSubDomains", -1);
+}
+
+/* Finalise(free) the dynamic class members of HSTSProvider
+ */
+static void
+hsts_provider_base_class_finalize (HSTSProviderClass *klass)
+{
+ g_free(klass->directive_max_age);
+ g_free(klass->directive_sub_domains);
+}
+
+/* Initialise the HSTSProvider class
+ */
+static void
+hsts_provider_class_init (HSTSProviderClass *klass)
+{
+ hsts_provider_parent_class = g_type_class_peek_parent (klass);
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ g_type_class_add_private (klass, sizeof (HSTSProviderPrivate));
+
+ object_class->finalize = hsts_provider_finalize;
+}
+
+/* Initialise an HSTSProvider instance
+ */
+static void
+hsts_provider_init (HSTSProvider *provider)
+{
+ HSTSProviderPrivate *priv = HSTS_PROVIDER_GET_PRIVATE (provider);
+
+ priv->domains = g_hash_table_new_full((GHashFunc)g_str_hash, (GEqualFunc)g_str_equal, (GDestroyNotify)g_free, (GDestroyNotify)hsts_entry_free);
+}
+
+/* Finalise an HSTSProvider instance
+ */
+static void
+hsts_provider_finalize (GObject *object)
+{
+ HSTSProviderPrivate *priv = HSTS_PROVIDER_GET_PRIVATE (object);
+
+ g_hash_table_destroy(priv->domains);
+
+ G_OBJECT_CLASS (hsts_provider_parent_class)->finalize (object);
+}
+
+/* Remove an entry from the known hosts, this doesn't remove superdomains of
+ * host with the includeSubDomains directive. So the host might still be
+ * affected by the HSTS code
+ */
+static void
+hsts_provider_remove_entry(HSTSProvider *provider, const char *host)
+{
+ HSTSProviderPrivate *priv = HSTS_PROVIDER_GET_PRIVATE(provider);
+
+ gchar *canonical = g_hostname_to_unicode(host);
+ g_hash_table_remove(priv->domains, canonical);
+ g_free(canonical);
+}
+
+/* Adds the host to the known host, if it already exists it replaces it with
+ * the information contained in entry. As specified in 8.1 [RFC6797] it won't
+ * add ip addresses as hosts.
+ */
+static void
+hsts_provider_add_entry(HSTSProvider *provider, const char *host, HSTSEntry *entry)
+{
+ if(g_hostname_is_ip_address(host))
+ return;
+
+ HSTSProviderPrivate *priv = HSTS_PROVIDER_GET_PRIVATE(provider);
+
+ g_hash_table_replace(priv->domains, g_hostname_to_unicode(host), entry);
+}
+
+/* Checks whether host is currently a known host or it is a sub domain of a
+ * known host which covers sub domains.
+ *
+ * Beware: An ip address will return false, as specified in 8.3 [RFC6797]
+ */
+static gboolean
+hsts_provider_should_secure_host(HSTSProvider *provider, const char *host)
+{
+ HSTSProviderPrivate *priv = HSTS_PROVIDER_GET_PRIVATE(provider);
+
+ if(g_hostname_is_ip_address(host))
+ return false;
+
+ gchar *canonical = g_hostname_to_unicode(host);
+ gboolean result = false;
+ if(strlen(canonical) > 0) /* Don't match empty strings as per. 8.3 [RFC6797] */
+ {
+ gchar *cur = canonical;
+ gboolean sub_domain = false; /* Indicates whether host is a proper sub domain of cur */
+ gunichar dot = g_utf8_get_char(".");
+ while(cur != NULL)
+ {
+ HSTSEntry *entry = g_hash_table_lookup(priv->domains, cur);
+ if(entry != NULL)
+ {
+ if(g_get_real_time() > entry->expiry) /* Remove expired entries */
+ hsts_provider_remove_entry(provider, cur);
+ else if(!sub_domain || entry->sub_domains)
+ { /* If either host == cur or host is a proper sub domain of
+ cur and the cur entry covers sub domains. */
+ result = true;
+ break;
+ }
+ }
+
+ sub_domain = true;
+ cur = g_utf8_strchr(cur, -1, dot);
+ /* Since canonical is in canonical form, it doesn't end with a .
+ * and hence there's no problem with the following: */
+ if(cur != NULL)
+ cur = g_utf8_next_char(cur);
+ }
+ }
+ g_free(canonical);
+
+ return result;
+}
+
+/* Parse an HSTS header and add it to the known hosts.
+ * Returns whether or not the header was valid.
+ */
+static gboolean
+hsts_provider_parse_header(HSTSProvider *provider, const char *host, const char *header)
+{
+ GHashTable *directives = soup_header_parse_semi_param_list(header);
+
+ HSTSProviderClass *klass = g_type_class_ref(HSTS_TYPE_PROVIDER);
+ gint64 max_age = -1;
+ gboolean sub_domains = false;
+ gboolean success = true;
+
+ GHashTableIter iter;
+ gpointer key, value;
+ g_hash_table_iter_init(&iter, directives);
+ while (g_hash_table_iter_next (&iter, &key, &value))
+ {
+ /* We have to jump through hoops here to be able to do the
+ * comparison in a case-insensitive manner, as specified in
+ * RFC 6797 6.1
+ */
+ gchar *key_ci = g_utf8_casefold(key, -1);
+ if (g_utf8_collate(key_ci, klass->directive_max_age) == 0)
+ {
+ if(value == NULL)
+ {
+ success = false;
+ break;
+ }
+ else
+ {
+ gchar *endptr;
+ max_age = g_ascii_strtoll(value, &endptr, 10);
+ if(endptr == value || max_age < 0)
+ {
+ success = false;
+ break;
+ }
+ }
+ }
+ else if (g_utf8_collate(key_ci, klass->directive_sub_domains) == 0)
+ {
+ if(value != NULL)
+ {
+ success = false;
+ break;
+ }
+ else
+ sub_domains = true;
+ }
+ g_free(key_ci);
+ }
+ g_type_class_unref(klass);
+ if(success)
+ {
+ if(max_age != 0)
+ hsts_provider_add_entry(provider, host, hsts_entry_new_from_val(max_age, sub_domains));
+ else /* max_age = 0 indicates remove header */
+ hsts_provider_remove_entry(provider, host);
+ }
+
+ soup_header_free_param_list(directives);
+ return success;
+}
+
+/* Processes the headers of msg and looks for a valid HSTS, if found it adds it
+ * as a known host according to the information specified in the header.
+ */
+static void
+hsts_process_hsts_header (SoupMessage *msg, gpointer user_data)
+{
+ GTlsCertificate *certificate;
+ GTlsCertificateFlags errors;
+ /* Only read HSTS headers sent over a properly validated https connection
+ * as specified in 8.1 [RFC6797]
+ */
+ SoupURI *uri = soup_message_get_uri(msg);
+ const char *host = soup_uri_get_host(uri);
+ if(!g_hostname_is_ip_address(host) &&
+ soup_message_get_https_status(msg, &certificate, &errors) &&
+ errors == 0){
+ HSTSProvider *provider = user_data;
+
+ SoupMessageHeaders *hdrs;
+ {
+ /* Get the headers from msg */
+ GValue hdrs_val = G_VALUE_INIT;
+ g_value_init(&hdrs_val, SOUP_TYPE_MESSAGE_HEADERS);
+ g_object_get_property(G_OBJECT(msg), SOUP_MESSAGE_RESPONSE_HEADERS, &hdrs_val);
+ hdrs = g_value_get_boxed(&hdrs_val);
+ g_value_unset(&hdrs_val);
+ }
+
+ SoupMessageHeadersIter iter;
+ soup_message_headers_iter_init(&iter, hdrs);
+ const char *name, *value;
+ while(soup_message_headers_iter_next(&iter, &name, &value))
+ {
+ if(strcmp(name, HSTS_HEADER_NAME) == 0)
+ {
+ /* It is not exactly clear to me what the correct behavior is
+ * if multiple headers are present. There seems to be some
+ * relevant information in 8.1 [RFC6797].
+ */
+ if(hsts_provider_parse_header(provider, host, value))
+ break;
+ }
+ }
+ }
+}
+
+/* Contains case folded versions of true and false used for comparisons in
+ * parse_line */
+static char *parser_true, *parser_false;
+
+/* Parses a line from a known hosts file and if it is correctly parsed it is
+ * added to the known hosts in provider */
+static void
+parse_line(HSTSProvider *provider, const char *line, gint64 now)
+{
+ /* Ignore comments */
+ if(g_utf8_get_char(line) == g_utf8_get_char("#"))
+ return;
+
+ char **split = g_strsplit(line, "\t", -1);
+ if(g_strv_length(split) == 3)
+ {
+ char *host = split[0], *sub_domains = split[1], *expires = split[2];
+ HSTSEntry *entry = hsts_entry_new();
+ gboolean success = true;
+
+ if(g_utf8_collate(parser_true, sub_domains) == 0)
+ entry->sub_domains = true;
+ else if(g_utf8_collate(parser_false, sub_domains) == 0)
+ entry->sub_domains = false;
+ else
+ success = false;
+
+ char *end;
+ entry->expiry = g_ascii_strtoll(expires, &end, 10);
+ if(expires == end || entry->expiry < now)
+ success = false;
+
+ if(success)
+ hsts_provider_add_entry(provider, host, entry);
+ else
+ hsts_entry_free(entry);
+ }
+
+ g_strfreev(split);
+}
+
+/* Loads the default database built into dwb
+ */
+static void
+load_default_database(HSTSProvider *provider)
+{
+}
+
+/* Reads a database of known hosts from filename. filename is a utf-8 encoded
+ * file, which on each line contains the following tab separated fields:
+ *
+ * host - is the known host
+ * sub domains - is either true or false compared case-insensitively and
+ * indicates whether the entry applies to sub domains of the
+ * given host
+ * expiry - Expiry time given as the number of microseconds since
+ * January 1, 1970 UTF. Encoded as a decimal.
+ *
+ * Lines which start with a '#' are treated as comments. Only \n and \r are
+ * recognised as line separators.
+ */
+static gboolean
+hsts_provider_load(HSTSProvider *provider, const char *filename)
+{
+
+ load_default_database(provider);
+
+ gchar *contents;
+ gsize length = 0;
+ if(!g_file_get_contents(filename, &contents, &length, NULL))
+ return false;
+
+ gboolean success = false;
+ if(g_utf8_validate(contents, length, NULL))
+ {
+ parser_true = g_utf8_casefold("true", -1);
+ parser_false = g_utf8_casefold("false", -1);
+
+ gint64 now = g_get_real_time();
+ /* TODO: Handle UTF-8 BOM */
+ gchar *line = contents, *p = contents;
+ gunichar r = g_utf8_get_char("\r"), n = g_utf8_get_char("\n");
+ while(*p)
+ {
+ gunichar c = g_utf8_get_char(p);
+ if(c == r || c == n)
+ {
+ /* \r\n is treated as two lines but it doesn't since empty
+ * lines are ignored */
+ gchar *next = g_utf8_next_char(p);
+ *p = '\0'; /* null terminate line */
+ parse_line(provider, line, now);
+ line = next;
+ p = next;
+ }
+ else
+ p = g_utf8_next_char(p);
+ }
+
+ success = true;
+ g_free(parser_true);
+ g_free(parser_false);
+ }
+ g_free(contents);
+ return success;
+}
+
+/* Saves the database of known hosts to filename in the format specified for
+ * hsts_provider_load */
+static void
+hsts_provider_save(HSTSProvider *provider, const char *filename)
+{
+ HSTSProviderPrivate *priv = HSTS_PROVIDER_GET_PRIVATE(provider);
+ FILE *file = g_fopen(filename, "w");
+ fprintf(file, "# dwb hsts database\n");
+
+ GHashTableIter iter;
+ gpointer key, value;
+ g_hash_table_iter_init(&iter, priv->domains);
+ while (g_hash_table_iter_next (&iter, &key, &value))
+ {
+ const char *host = (const char *)key;
+ const HSTSEntry *entry = (HSTSEntry *)value;
+ /* TODO: assert MAX_LONG_LONG > G_MAXINT64 */
+ long long expiry = entry->expiry;
+ fprintf(file, "%s\t%s\t%lld\n", host, entry->sub_domains ? "true" : "false", expiry);
+ }
+ fclose(file);
+}
+
+/* This callback is called when a new message is put on the session queue. It
+ * investigates whether the message is intended for a known host and if so it
+ * switches URI scheme to HTTPS.
+ */
+static void
+hsts_provider_request_queued (SoupSessionFeature *feature,
+ SoupSession *session,
+ SoupMessage *msg)
+{
+ HSTSProvider *provider = HSTS_PROVIDER (feature);
+
+ SoupURI *uri = soup_message_get_uri(msg);
+ if(soup_uri_get_scheme(uri) == SOUP_URI_SCHEME_HTTP &&
+ hsts_provider_should_secure_host(provider, soup_uri_get_host(uri)))
+ {
+ soup_uri_set_scheme(uri, SOUP_URI_SCHEME_HTTPS);
+ /* Only change port if it explicitly references port 80 as specified in
+ * 8.3 [RFC6797]. */
+ if(soup_uri_get_port(uri) == 80)
+ soup_uri_set_port(uri, 443);
+ soup_session_requeue_message(session, msg);
+
+ /* TODO: Ensure strict ssl handling of known HSTS hosts. */
+ }
+
+ /* Only look for HSTS headers sent over https */
+ if(soup_uri_get_scheme(uri) == SOUP_URI_SCHEME_HTTPS)
+ {
+ soup_message_add_header_handler (msg, "got-headers",
+ HSTS_HEADER_NAME,
+ G_CALLBACK (hsts_process_hsts_header),
+ feature);
+ }
+}
+
+static void
+hsts_provider_request_started (SoupSessionFeature *feature,
+ SoupSession *session,
+ SoupMessage *msg,
+ SoupSocket *socket)
+{
+}
+
+/* Removes added callbacks on message unqueue
+ */
+static void
+hsts_provider_request_unqueued (SoupSessionFeature *feature,
+ SoupSession *session,
+ SoupMessage *msg)
+{
+ g_signal_handlers_disconnect_by_func (msg, hsts_process_hsts_header, feature);
+}
+
+/* Initialise the SoupSessionFeature interface.
+ */
+static void
+hsts_provider_session_feature_init (SoupSessionFeatureInterface *feature_interface,
+ gpointer interface_data)
+{
+ feature_interface->request_queued = hsts_provider_request_queued;
+ feature_interface->request_started = hsts_provider_request_started;
+ feature_interface->request_unqueued = hsts_provider_request_unqueued;
+}
+
+/* Indicates whether hsts has been initialised */
+static gboolean s_init = false;
+static HSTSProvider *s_provider;
+
+gboolean
+hsts_running()
+{
+ return s_init && GET_BOOL("hsts");
+}
+
+/* Activates hsts */
+void
+hsts_activate()
+{
+ if(!hsts_init())
+ return;
+ soup_session_add_feature(dwb.misc.soupsession, SOUP_SESSION_FEATURE(s_provider));
+}
+
+/* Deactivates hsts */
+void
+hsts_deactivate()
+{
+ if(!s_init)
+ return;
+ soup_session_remove_feature(dwb.misc.soupsession, SOUP_SESSION_FEATURE(s_provider));
+}
+
+/* Save current hsts lists */
+void
+hsts_save()
+{
+ if(hsts_running())
+ hsts_provider_save(s_provider, dwb.files[FILES_HSTS]);
+}
+
+/* Initialises the hsts implementation */
+gboolean
+hsts_init()
+{
+ if(s_init)
+ return true;
+ if(!GET_BOOL("hsts"))
+ return false;
+
+ s_provider = g_object_new(HSTS_TYPE_PROVIDER, NULL);
+ s_init = true;
+
+ hsts_provider_load(s_provider, dwb.files[FILES_HSTS]);
+ hsts_activate();
+
+ return true;
+}
+
+/* Finalises the hsts implementation */
+void
+hsts_end()
+{
+ hsts_save();
+ hsts_deactivate();
+
+ if(s_init)
+ {
+ g_object_unref(s_provider);
+ s_init = false;
+ }
+}