On Sat, Jul 18, 2015 at 12:14:37AM +0000, Florian Obser wrote: > OK? >
As discussed, I like the implementation this way. Comments below. Reyk > diff --git httpd.conf.5 httpd.conf.5 > index b3eaad8..bfca29f 100644 > --- httpd.conf.5 > +++ httpd.conf.5 > @@ -262,6 +262,18 @@ root directory of > .Xr httpd 8 > and defaults to > .Pa /run/slowcgi.sock . > +.It Ic hsts Oo Ar option Oc > +Enable HTTP Strict Transport Security. > +Valid options are: > +.Bl -tag -width Ds > +.It Ic max-age Ar seconds > +Set the maximum time in seconds a receiving user agent should regard > +this host as a HSTS host. > +The default is one year. > +.It Ic subdomains > +Signal to the receiving user agent that this host and all sub domains > +of the host's domain should be considered HSTS hosts. > +.El > .It Ic listen on Ar address Oo Ic tls Oc Ic port Ar number > Set the listen address and port. > This statement can be specified multiple times. > diff --git httpd.h httpd.h > index 2cb7934..9596000 100644 > --- httpd.h > +++ httpd.h > @@ -68,6 +68,7 @@ > #define SERVER_OUTOF_FD_RETRIES 5 > #define SERVER_MAX_PREFETCH 256 > #define SERVER_MIN_PREFETCHED 32 > +#define SERVER_HSTS_DEFAULT_AGE 31536000 > > #define MEDIATYPE_NAMEMAX 128 /* file name extension */ > #define MEDIATYPE_TYPEMAX 64 /* length of type/subtype */ > @@ -351,13 +352,14 @@ SPLAY_HEAD(client_tree, client); > #define SRVFLAG_NO_BLOCK 0x00080000 > #define SRVFLAG_LOCATION_MATCH 0x00100000 > #define SRVFLAG_SERVER_MATCH 0x00200000 > +#define SRVFLAG_SERVER_HSTS 0x00400000 > > #define SRVFLAG_BITS \ > "\10\01INDEX\02NO_INDEX\03AUTO_INDEX\04NO_AUTO_INDEX" \ > "\05ROOT\06LOCATION\07FCGI\10NO_FCGI\11LOG\12NO_LOG\13SOCKET" \ > "\14SYSLOG\15NO_SYSLOG\16TLS\17ACCESS_LOG\20ERROR_LOG" \ > "\21AUTH\22NO_AUTH\23BLOCK\24NO_BLOCK\25LOCATION_MATCH" \ > - "\26SERVER_MATCH" > + "\26SERVER_MATCH\27SERVER_HSTS" > > #define TCPFLAG_NODELAY 0x01 > #define TCPFLAG_NNODELAY 0x02 > @@ -443,6 +445,9 @@ struct server_config { > char *return_uri; > off_t return_uri_len; > > + int64_t hsts_max_age; Do you really need int64_t instead of int here? How many years do you want to enforce HSTS? Or, in the distant future we either entirely switched to encrypted connections or the opposite and it became illegal in a dystopian future. > + int hsts_subdomains; > + > TAILQ_ENTRY(server_config) entry; > }; > TAILQ_HEAD(serverhosts, server_config); > diff --git parse.y parse.y > index 0870819..8dfad1a 100644 > --- parse.y > +++ parse.y > @@ -133,7 +133,7 @@ typedef struct { > %token COMBINED CONNECTION DHE DIRECTORY ECDHE ERR FCGI INDEX IP KEY > LISTEN > %token LOCATION LOG LOGDIR MATCH MAXIMUM NO NODELAY ON PORT PREFORK > PROTOCOLS > %token REQUEST REQUESTS ROOT SACK SERVER SOCKET STRIP STYLE SYSLOG TCP > TIMEOUT > -%token TLS TYPES > +%token TLS TYPES HSTS MAXAGE SUBDOMAINS > %token ERROR INCLUDE AUTHENTICATE WITH BLOCK DROP RETURN PASS > %token <v.string> STRING > %token <v.number> NUMBER > @@ -256,6 +256,8 @@ server : SERVER optmatch STRING { > HTTPD_TLS_ECDHE_CURVE, > sizeof(s->srv_conf.tls_ecdhe_curve)); > > + s->srv_conf.hsts_max_age = -1; You could just initialize it to SERVER_HSTS_DEFAULT_AGE here. > + > if (last_server_id == INT_MAX) { > yyerror("too many servers defined"); > free(s); > @@ -556,6 +558,30 @@ serveroptsl : LISTEN ON STRING opttls port { > parentsrv = NULL; > } > | include > + | hsts { > + if (parentsrv != NULL) { > + yyerror("hsts inside location"); > + YYERROR; > + } > + srv->srv_conf.flags |= SRVFLAG_SERVER_HSTS; > + } > + ; > + > +hsts : HSTS '{' optnl hstsflags_l '}' > + | HSTS hstsflags > + | HSTS > + ; > + > +hstsflags_l : hstsflags optcommanl hstsflags_l > + | hstsflags optnl > + ; > + > +hstsflags : MAXAGE NUMBER { > + srv_conf->hsts_max_age = $2; > + } > + | SUBDOMAINS { > + srv->srv_conf.hsts_subdomains = 1; > + } > ; > > fastcgi : NO FCGI { > @@ -1115,6 +1141,7 @@ lookup(char *s) > { "ecdhe", ECDHE }, > { "error", ERR }, > { "fastcgi", FCGI }, > + { "hsts", HSTS }, > { "include", INCLUDE }, > { "index", INDEX }, > { "ip", IP }, > @@ -1125,6 +1152,7 @@ lookup(char *s) > { "logdir", LOGDIR }, > { "match", MATCH }, > { "max", MAXIMUM }, > + { "max-age", MAXAGE }, > { "no", NO }, > { "nodelay", NODELAY }, > { "on", ON }, > @@ -1141,6 +1169,7 @@ lookup(char *s) > { "socket", SOCKET }, > { "strip", STRIP }, > { "style", STYLE }, > + { "subdomains", SUBDOMAINS }, > { "syslog", SYSLOG }, > { "tcp", TCP }, > { "timeout", TIMEOUT }, > diff --git server_http.c server_http.c > index 9a6609e..8bdeade 100644 > --- server_http.c > +++ server_http.c > @@ -741,7 +741,7 @@ server_abort_http(struct client *clt, u_int code, const > char *msg) > struct http_descriptor *desc = clt->clt_descreq; > const char *httperr = NULL, *style; > char *httpmsg, *body = NULL, *extraheader = NULL; > - char tmbuf[32], hbuf[128]; > + char tmbuf[32], hbuf[128], *hstsheader = NULL; > char buf[IBUF_READ_SIZE]; > int bodylen; > > @@ -828,6 +828,16 @@ server_abort_http(struct client *clt, u_int code, const > char *msg) > code, httperr, style, code, httperr, HTTPD_SERVERNAME)) == -1) > goto done; > > + if (srv_conf->flags & SRVFLAG_SERVER_HSTS) { > + if (asprintf(&hstsheader, > + "Strict-Transport-Security: max-age=%lld%s\r\n", > + srv_conf->hsts_max_age == -1 ? SERVER_HSTS_DEFAULT_AGE : > + srv_conf->hsts_max_age, And use srv_conf->hsts_max_age here because the default was set above. > + srv_conf->hsts_subdomains == 0 ? "" : > + " ; includeSubDomains") == -1) > + goto done; > + } > + > /* Add basic HTTP headers */ > if (asprintf(&httpmsg, > "HTTP/1.0 %03d %s\r\n" > @@ -837,10 +847,12 @@ server_abort_http(struct client *clt, u_int code, const > char *msg) > "Content-Type: text/html\r\n" > "Content-Length: %d\r\n" > "%s" > + "%s" > "\r\n" > "%s", > code, httperr, tmbuf, HTTPD_SERVERNAME, bodylen, > extraheader == NULL ? "" : extraheader, > + hstsheader == NULL ? "" : hstsheader, > desc->http_method == HTTP_METHOD_HEAD ? "" : body) == -1) > goto done; > > @@ -851,6 +863,7 @@ server_abort_http(struct client *clt, u_int code, const > char *msg) > done: > free(body); > free(extraheader); > + free(hstsheader); > if (msg == NULL) > msg = "\"\""; > if (asprintf(&httpmsg, "%s (%03d %s)", msg, code, httperr) == -1) { > @@ -1210,6 +1223,8 @@ int > server_response_http(struct client *clt, u_int code, > struct media_type *media, off_t size, time_t mtime) > { > + struct server *srv = clt->clt_srv; > + struct server_config *srv_conf = &srv->srv_conf; > struct http_descriptor *desc = clt->clt_descreq; > struct http_descriptor *resp = clt->clt_descresp; > const char *error; > @@ -1257,6 +1272,18 @@ server_response_http(struct client *clt, u_int code, > kv_add(&resp->http_headers, "Last-Modified", tmbuf) == NULL) > return (-1); > > + /* HSTS header */ > + if (srv_conf->flags & SRVFLAG_SERVER_HSTS) { > + if ((cl = > + kv_add(&resp->http_headers, "Strict-Transport-Security", > + NULL)) == NULL || > + kv_set(cl, "max-age=%lld%s", srv_conf->hsts_max_age == -1 ? > + SERVER_HSTS_DEFAULT_AGE : srv_conf->hsts_max_age, > + srv_conf->hsts_subdomains == 0 ? "" : > + " ; includeSubDomains") == -1) > + return (-1); > + } > + > /* Date header is mandatory and should be added as late as possible */ > if (server_http_time(time(NULL), tmbuf, sizeof(tmbuf)) <= 0 || > kv_add(&resp->http_headers, "Date", tmbuf) == NULL) > > -- > I'm not entirely sure you are real. > --