NetSurf
strict-transport-security.c
Go to the documentation of this file.
1/*
2 * Copyright 2018 John-Mark Bell <jmb@netsurf-browser.org>
3 *
4 * This file is part of NetSurf, http://www.netsurf-browser.org/
5 *
6 * NetSurf is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; version 2 of the License.
9 *
10 * NetSurf is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19#include <limits.h>
20#include <stdlib.h>
21
22#include "utils/corestrings.h"
23#include "utils/http.h"
24
25#include "utils/http/generics.h"
27
28/**
29 * Representation of a Strict-Transport-Security
30 */
32 uint32_t max_age; /**< Max age (delta seconds) */
33 bool include_sub_domains; /**< Whether subdomains are included */
34};
35
36/**
37 * Representation of a directive
38 */
39typedef struct http_directive {
41
42 lwc_string *name; /**< Parameter name */
43 lwc_string *value; /**< Parameter value (optional) */
45
46
48{
49 lwc_string_unref(self->name);
50 if (self->value != NULL) {
51 lwc_string_unref(self->value);
52 }
53 free(self);
54}
55
56static nserror http__parse_directive(const char **input,
58{
59 const char *pos = *input;
60 lwc_string *name;
61 lwc_string *value = NULL;
62 http_directive *directive;
63 nserror error;
64
65 /* token [ "=" ( token | quoted-string ) ] */
66
67 error = http__parse_token(&pos, &name);
68 if (error != NSERROR_OK)
69 return error;
70
71 http__skip_LWS(&pos);
72
73 if (*pos == '=') {
74 pos++;
75
76 http__skip_LWS(&pos);
77
78 if (*pos == '"')
79 error = http__parse_quoted_string(&pos, &value);
80 else
81 error = http__parse_token(&pos, &value);
82
83 if (error != NSERROR_OK) {
84 lwc_string_unref(name);
85 return error;
86 }
87 }
88
89 directive = malloc(sizeof(*directive));
90 if (directive == NULL) {
91 if (value != NULL) {
92 lwc_string_unref(value);
93 }
94 lwc_string_unref(name);
95 return NSERROR_NOMEM;
96 }
97
99 directive->name = name;
100 directive->value = value;
101
102 *result = directive;
103 *input = pos;
104
105 return NSERROR_OK;
106}
107
109{
111}
112
114 lwc_string *name, lwc_string **value)
115{
116 bool match;
117
118 while (list != NULL) {
119 if (lwc_string_caseless_isequal(name, list->name,
120 &match) == lwc_error_ok && match)
121 break;
122
123 list = (http_directive *) list->base.next;
124 }
125
126 if (list == NULL)
127 return NSERROR_NOT_FOUND;
128
129 if (list->value != NULL) {
130 *value = lwc_string_ref(list->value);
131 } else {
132 *value = NULL;
133 }
134
135 return NSERROR_OK;
136}
137
139 const http_directive *cur,
140 lwc_string **name, lwc_string **value)
141{
142 if (cur == NULL)
143 return NULL;
144
145 *name = lwc_string_ref(cur->name);
146 if (cur->value != NULL) {
147 *value = lwc_string_ref(cur->value);
148 } else {
149 *value = NULL;
150 }
151
152 return (http_directive *) cur->base.next;
153}
154
155static uint32_t count(const http_directive *list, lwc_string *key)
156{
157 uint32_t count = 0;
158 bool match;
159
160 while (list != NULL) {
161 if (lwc_string_caseless_isequal(key, list->name,
162 &match) == lwc_error_ok && match) {
163 count++;
164 }
165
166 list = (http_directive *) list->base.next;
167 }
168
169 return count;
170}
171
172static bool check_duplicates(const http_directive *directives)
173{
174 bool result = true;
175 const http_directive *key = directives;
176
177 if (key == NULL) {
178 /* No directives, so there can't be any duplicates */
179 return true;
180 }
181
182 do {
183 lwc_string *name = NULL, *value = NULL;
184
185 key = http_directive_list_iterate(key, &name, &value);
186
187 result &= (count(directives, name) == 1);
188
189 lwc_string_unref(name);
190 if (value != NULL) {
191 lwc_string_unref(value);
192 }
193 } while (key != NULL);
194
195 return result;
196}
197
198static nserror parse_max_age(lwc_string *value, uint32_t *result)
199{
200 const char *pos = lwc_string_data(value);
201 const char *end = pos + lwc_string_length(value);
202 uint32_t val = 0;
203
204 /* 1*DIGIT */
205
206 if (pos == end) {
207 /* Blank value */
208 return NSERROR_NOT_FOUND;
209 }
210
211 while (pos < end) {
212 if ('0' <= *pos && *pos <= '9') {
213 uint32_t nv = val * 10 + (*pos - '0');
214 if (nv < val) {
215 val = UINT_MAX;
216 } else {
217 val = nv;
218 }
219 } else {
220 /* Non-digit */
221 return NSERROR_NOT_FOUND;
222 }
223
224 pos++;
225 }
226
227 *result = val;
228
229 return NSERROR_OK;
230}
231
232/* See strict-transport-security.h for documentation */
235{
236 const char *pos = header_value;
238 http_directive *first = NULL;
239 http_directive *directives = NULL;
240 lwc_string *max_age_str = NULL, *isd_str = NULL;
241 uint32_t max_age;
242 bool include_sub_domains = false;
243 nserror error;
244
245 /* directive *( ";" directive ) */
246
247 http__skip_LWS(&pos);
248
249 error = http__parse_directive(&pos, &first);
250 if (error != NSERROR_OK) {
251 return error;
252 }
253
254 http__skip_LWS(&pos);
255
256 if (*pos == ';') {
257 error = http__item_list_parse(&pos,
258 http__parse_directive, first, &directives);
259 if (error != NSERROR_OK) {
260 if (directives != NULL) {
261 http_directive_list_destroy(directives);
262 }
263 return error;
264 }
265 } else {
266 directives = first;
267 }
268
269 /* Each directive must only appear once */
270 if (check_duplicates(directives) == false) {
271 http_directive_list_destroy(directives);
272 return NSERROR_NOT_FOUND;
273 }
274
275 /* max-age is required */
276 error = http_directive_list_find_item(directives,
277 corestring_lwc_max_age, &max_age_str);
278 if (error != NSERROR_OK || max_age_str == NULL) {
279 http_directive_list_destroy(directives);
280 return NSERROR_NOT_FOUND;
281 }
282
283 error = parse_max_age(max_age_str, &max_age);
284 if (error != NSERROR_OK) {
285 lwc_string_unref(max_age_str);
286 http_directive_list_destroy(directives);
287 return NSERROR_NOT_FOUND;
288 }
289 lwc_string_unref(max_age_str);
290
291 /* includeSubDomains is optional and valueless */
292 error = http_directive_list_find_item(directives,
293 corestring_lwc_includesubdomains, &isd_str);
294 if (error != NSERROR_OK && error != NSERROR_NOT_FOUND) {
295 http_directive_list_destroy(directives);
296 return NSERROR_NOT_FOUND;
297 } else if (error == NSERROR_OK) {
298 if (isd_str != NULL) {
299 /* Present, but not valueless: invalid */
300 lwc_string_unref(isd_str);
301 http_directive_list_destroy(directives);
302 return NSERROR_NOT_FOUND;
303 }
304 include_sub_domains = true;
305 }
306 http_directive_list_destroy(directives);
307
308 sts = malloc(sizeof(*sts));
309 if (sts == NULL) {
310 return NSERROR_NOMEM;
311 }
312
313 sts->max_age = max_age;
314 sts->include_sub_domains = include_sub_domains;
315
316 *result = sts;
317
318 return NSERROR_OK;
319}
320
321/* See strict-transport-security.h for documentation */
324{
325 free(victim);
326}
327
328/* See strict-transport-security.h for documentation */
331{
332 return sts->max_age;
333}
334
335/* See strict-transport-security.h for documentation */
338{
339 return sts->include_sub_domains;
340}
341
STATIC char result[100]
Definition: arexx.c:77
Useful interned string pointers (interface).
nserror
Enumeration of error codes.
Definition: errors.h:29
@ NSERROR_NOT_FOUND
Requested item not found.
Definition: errors.h:34
@ NSERROR_NOMEM
Memory exhaustion.
Definition: errors.h:32
@ NSERROR_OK
No error.
Definition: errors.h:30
#define http__item_list_destroy(l)
Definition: generics.h:46
#define HTTP__ITEM_INIT(item, n, f)
Definition: generics.h:35
#define http__item_list_parse(i, p, f, r)
Definition: generics.h:52
HTTP header parsing functions.
nserror http__parse_token(const char **input, lwc_string **value)
Parse an HTTP token.
Definition: primitives.c:68
void http__skip_LWS(const char **input)
Skip past linear whitespace in input.
Definition: primitives.c:31
nserror http__parse_quoted_string(const char **input, lwc_string **value)
Parse an HTTP quoted-string.
Definition: primitives.c:102
struct http_directive http_directive
Representation of a directive.
static bool check_duplicates(const http_directive *directives)
nserror http_parse_strict_transport_security(const char *header_value, http_strict_transport_security **result)
Parse an HTTP Strict-Transport-Security header value.
void http_strict_transport_security_destroy(http_strict_transport_security *victim)
Destroy a strict transport security object.
static void http_destroy_directive(http_directive *self)
static nserror parse_max_age(lwc_string *value, uint32_t *result)
static void http_directive_list_destroy(http_directive *list)
static nserror http__parse_directive(const char **input, http_directive **result)
static uint32_t count(const http_directive *list, lwc_string *key)
static const http_directive * http_directive_list_iterate(const http_directive *cur, lwc_string **name, lwc_string **value)
bool http_strict_transport_security_include_subdomains(http_strict_transport_security *sts)
Get the value of a strict transport security's includeSubDomains flag.
uint32_t http_strict_transport_security_max_age(http_strict_transport_security *sts)
Get the value of a strict transport security's max-age.
static nserror http_directive_list_find_item(const http_directive *list, lwc_string *name, lwc_string **value)
Representation of an item.
Definition: generics.h:29
struct http__item * next
Next item in list, or NULL.
Definition: generics.h:30
Representation of a directive.
Definition: cache-control.c:41
lwc_string * value
Parameter value (optional)
Definition: cache-control.c:45
lwc_string * name
Parameter name.
Definition: cache-control.c:44
http__item base
Definition: cache-control.c:42
Representation of a Strict-Transport-Security.
uint32_t max_age
Max age (delta seconds)
bool include_sub_domains
Whether subdomains are included.