Mailing List Archive

[6885] cherokee/trunk: Implements "HTTP Strict Transport Security" (HSTS).
Revision: 6885
http://svn.cherokee-project.com/changeset/6885
Author: alo
Date: 2011-10-06 22:46:57 +0200 (Thu, 06 Oct 2011)
Log Message:
-----------
Implements "HTTP Strict Transport Security" (HSTS).

Modified Paths:
--------------
cherokee/trunk/admin/PageVServer.py
cherokee/trunk/cherokee/connection-protected.h
cherokee/trunk/cherokee/connection.c
cherokee/trunk/cherokee/handler_error.c
cherokee/trunk/cherokee/thread.c
cherokee/trunk/cherokee/virtual_server.c
cherokee/trunk/cherokee/virtual_server.h
cherokee/trunk/qa/Makefile.am

Added Paths:
-----------
cherokee/trunk/qa/292-HSTS1.py

Modified: cherokee/trunk/admin/PageVServer.py
===================================================================
--- cherokee/trunk/admin/PageVServer.py 2011-10-06 14:23:36 UTC (rev 6884)
+++ cherokee/trunk/admin/PageVServer.py 2011-10-06 20:46:57 UTC (rev 6885)
@@ -73,6 +73,9 @@
NOTE_UTC_TIME = N_('Time standard to use in the log file entries.')
NOTE_INDEX_USAGE = N_('Remember that only "File Exists" rules and "List & Send" handlers use the Directory Indexes setting.')
NOTE_MATCH_NICK = N_('Use this nickname as an additional host name for this virtual server (Default: yes)')
+NOTE_HSTS = N_('Enforce HTTPS by using the HTTP Strict Transport Security.')
+NOTE_HSTS_MAXAGE = N_("How long the client's browser should remember the forced HTTPS (in seconds).")
+NOTE_HSTS_SUBDOMAINS = N_("Should HSTS be used in all the subdomains of this virtual verser (Default: yes).")

DEFAULT_HOST_NOTE = N_("<p>The 'default' virtual server matches all the domain names.</p>")

@@ -677,7 +680,22 @@
self += CTK.RawHTML ('<h2>%s</h2>' % (_('Advanced Options')))
self += CTK.Indenter (submit)

+ # HSTS
+ table = CTK.PropsTable()
+ table.Add (_('Enable HSTS'), CTK.CheckCfgText ('%s!hsts'%(pre), False, _('Accept')), _(NOTE_HSTS))

+ if int(CTK.cfg.get_val('%s!hsts' %(pre), "0")):
+ table.Add (_('HSTS Max-Age'), CTK.TextCfg ('%s!hsts!max_age'%(pre), True, {'optional_string':_("One year")}), _(NOTE_HSTS_MAXAGE))
+ table.Add (_('Include Subdomains'), CTK.CheckCfgText ('%s!subdomains'%(pre), True, _('Include all')), _(NOTE_HSTS_SUBDOMAINS))
+
+ submit = CTK.Submitter (url_apply)
+ submit.bind ('submit_success', refreshable.JS_to_refresh())
+ submit += table
+
+ self += CTK.RawHTML ('<h2>%s</h2>' % (_('HTTP Strict Transport Security (HSTS)')))
+ self += CTK.Indenter (submit)
+
+
class SecurityWidget (CTK.Container):
def __init__ (self, vsrv_num):
CTK.Container.__init__ (self)

Modified: cherokee/trunk/cherokee/connection-protected.h
===================================================================
--- cherokee/trunk/cherokee/connection-protected.h 2011-10-06 14:23:36 UTC (rev 6884)
+++ cherokee/trunk/cherokee/connection-protected.h 2011-10-06 20:46:57 UTC (rev 6885)
@@ -274,6 +274,7 @@
ret_t cherokee_connection_create_handler (cherokee_connection_t *conn, cherokee_config_entry_t *config_entry);
ret_t cherokee_connection_create_encoder (cherokee_connection_t *conn, cherokee_avl_t *accept_enc);
ret_t cherokee_connection_setup_error_handler (cherokee_connection_t *conn);
+ret_t cherokee_connection_setup_hsts_handler (cherokee_connection_t *conn);
ret_t cherokee_connection_check_authentication (cherokee_connection_t *conn, cherokee_config_entry_t *config_entry);
ret_t cherokee_connection_check_ip_validation (cherokee_connection_t *conn, cherokee_config_entry_t *config_entry);
ret_t cherokee_connection_check_only_secure (cherokee_connection_t *conn, cherokee_config_entry_t *config_entry);

Modified: cherokee/trunk/cherokee/connection.c
===================================================================
--- cherokee/trunk/cherokee/connection.c 2011-10-06 14:23:36 UTC (rev 6884)
+++ cherokee/trunk/cherokee/connection.c 2011-10-06 20:46:57 UTC (rev 6885)
@@ -555,6 +555,43 @@
}


+ret_t
+cherokee_connection_setup_hsts_handler (cherokee_connection_t *conn)
+{
+ ret_t ret;
+
+ /* Redirect to:
+ * "https://" + host + request + query_string
+ */
+ cherokee_buffer_clean (&conn->redirect);
+ cherokee_buffer_add_str (&conn->redirect, "https://");
+
+ cherokee_connection_build_host_port_string (conn, &conn->redirect);
+ cherokee_buffer_add_buffer (&conn->redirect, &conn->request);
+
+ if (conn->query_string.len > 0) {
+ cherokee_buffer_add_char (&conn->redirect, '?');
+ cherokee_buffer_add_buffer (&conn->redirect, &conn->query_string);
+ }
+
+ /* 301 response: Move Permanetly
+ */
+ conn->error_code = http_moved_permanently;
+
+ /* Instance the handler object
+ */
+ ret = cherokee_handler_error_new (&conn->handler, conn, NULL);
+ if (unlikely (ret != ret_ok)) {
+ return ret_error;
+ }
+
+ TRACE (ENTRIES, "HSTS redirection handler set. Phase is '%s' now.\n", "init");
+ conn->phase = phase_init;
+
+ return ret_ok;
+}
+
+
static void
build_response_header_authentication (cherokee_connection_t *conn, cherokee_buffer_t *buffer)
{

Modified: cherokee/trunk/cherokee/handler_error.c
===================================================================
--- cherokee/trunk/cherokee/handler_error.c 2011-10-06 14:23:36 UTC (rev 6884)
+++ cherokee/trunk/cherokee/handler_error.c 2011-10-06 20:46:57 UTC (rev 6885)
@@ -275,6 +275,23 @@
cherokee_buffer_add_str (buffer, CRLF);
}

+ /* HSTS support
+ */
+ if ((conn->socket.is_tls != TLS) &&
+ (CONN_VSRV(conn)->hsts.enabled) &&
+ (conn->error_code == http_moved_permanently))
+ {
+ cherokee_buffer_add_str (buffer, "Strict-Transport-Security: ");
+ cherokee_buffer_add_str (buffer, "max-age=");
+ cherokee_buffer_add_ulong10 (buffer, (culong_t) CONN_VSRV(conn)->hsts.max_age);
+
+ if (CONN_VSRV(conn)->hsts.subdomains) {
+ cherokee_buffer_add_str (buffer, "; includeSubdomains");
+ }
+
+ cherokee_buffer_add_str (buffer, CRLF);
+ }
+
/* Usual headers
*/
cherokee_buffer_add_str (buffer, "Content-Type: text/html"CRLF);

Modified: cherokee/trunk/cherokee/thread.c
===================================================================
--- cherokee/trunk/cherokee/thread.c 2011-10-06 14:23:36 UTC (rev 6884)
+++ cherokee/trunk/cherokee/thread.c 2011-10-06 20:46:57 UTC (rev 6885)
@@ -944,7 +944,6 @@
cherokee_collector_log_request (THREAD_SRV(thd)->collector);
}

-
conn->phase = phase_setup_connection;

/* fall down */
@@ -957,6 +956,15 @@
*/
conn_set_mode (thd, conn, socket_writing);

+ /* HSTS support
+ */
+ if ((conn->socket.is_tls != TLS) &&
+ (CONN_VSRV(conn)->hsts.enabled))
+ {
+ cherokee_connection_setup_hsts_handler (conn);
+ continue;
+ }
+
/* Is it already an error response?
*/
if (http_type_300 (conn->error_code) ||

Modified: cherokee/trunk/cherokee/virtual_server.c
===================================================================
--- cherokee/trunk/cherokee/virtual_server.c 2011-10-06 14:23:36 UTC (rev 6884)
+++ cherokee/trunk/cherokee/virtual_server.c 2011-10-06 20:46:57 UTC (rev 6885)
@@ -65,6 +65,10 @@
n->match_nick = true;
n->flcache = NULL;

+ n->hsts.enabled = false;
+ n->hsts.subdomains = true;
+ n->hsts.max_age = 365 * 24 * 60 * 60;
+
/* Virtual entries
*/
ret = cherokee_rule_list_init (&n->rules);
@@ -948,6 +952,22 @@


static ret_t
+add_hsts (cherokee_config_node_t *config,
+ cherokee_virtual_server_t *vserver)
+{
+ ret_t ret;
+
+ ret = cherokee_atob (config->val.buf, &vserver->hsts.enabled);
+ if (ret != ret_ok) return ret_error;
+
+ cherokee_config_node_read_int (config, "max_age", &vserver->hsts.max_age);
+ cherokee_config_node_read_bool (config, "subdomains", &vserver->hsts.subdomains);
+
+ return ret_ok;
+}
+
+
+static ret_t
add_logger (cherokee_config_node_t *config,
cherokee_virtual_server_t *vserver)
{
@@ -1093,6 +1113,11 @@
if (ret != ret_ok)
return ret;

+ } else if (equal_buf_str (&conf->key, "hsts")) {
+ ret = add_hsts (conf, vserver);
+ if (ret != ret_ok)
+ return ret;
+
} else if (equal_buf_str (&conf->key, "directory_index")) {
cherokee_config_node_read_list (conf, NULL, add_directory_index, vserver);


Modified: cherokee/trunk/cherokee/virtual_server.h
===================================================================
--- cherokee/trunk/cherokee/virtual_server.h 2011-10-06 14:23:36 UTC (rev 6884)
+++ cherokee/trunk/cherokee/virtual_server.h 2011-10-06 20:46:57 UTC (rev 6885)
@@ -77,6 +77,12 @@
cherokee_buffer_t ciphers;
cherokee_cryptor_vserver_t *cryptor;

+ struct {
+ cherokee_boolean_t enabled;
+ cherokee_boolean_t subdomains;
+ cuint_t max_age;
+ } hsts;
+
} cherokee_virtual_server_t;

#define VSERVER(v) ((cherokee_virtual_server_t *)(v))

Added: cherokee/trunk/qa/292-HSTS1.py
===================================================================
--- cherokee/trunk/qa/292-HSTS1.py (rev 0)
+++ cherokee/trunk/qa/292-HSTS1.py 2011-10-06 20:46:57 UTC (rev 6885)
@@ -0,0 +1,29 @@
+from base import *
+
+NICK = "test-2920"
+MAX_AGE = 123456
+
+CONF = """
+vserver!2920!nick = %(NICK)s
+vserver!2920!document_root = %(droot)s
+vserver!2920!hsts = 1
+vserver!2920!hsts!max_age = %(MAX_AGE)s
+vserver!2920!rule!1!match = default
+vserver!2920!rule!1!handler = dirlist
+"""
+
+class Test (TestBase):
+ def __init__ (self):
+ TestBase.__init__ (self, __file__)
+ self.name = "HSTS: Error code and Header"
+ self.request = "HTTP / HTTP/1.0\r\n" + \
+ "Host: %s\r\n" %(NICK)
+ self.expected_error = 301
+ self.expected_content = ["Strict-Transport-Security:", "max-age=%d"%(MAX_AGE)]
+
+ def Prepare (self, www):
+ droot = self.Mkdir (www, "%s_droot"%(NICK))
+
+ vars = globals()
+ vars.update(locals())
+ self.conf = CONF %(vars)

Modified: cherokee/trunk/qa/Makefile.am
===================================================================
--- cherokee/trunk/qa/Makefile.am 2011-10-06 14:23:36 UTC (rev 6884)
+++ cherokee/trunk/qa/Makefile.am 2011-10-06 20:46:57 UTC (rev 6885)
@@ -312,7 +312,8 @@
288-GZip-IE16.py \
289-Connection_TE.py \
290-Question-mark-in-name.py \
-291-Redir-keepalive.py
+291-Redir-keepalive.py \
+292-HSTS1.py

test:
python -m compileall .