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 lwc_string_unref(self->value);
51 free(self);
52}
53
54static nserror http__parse_directive(const char **input,
56{
57 const char *pos = *input;
58 lwc_string *name;
59 lwc_string *value = NULL;
60 http_directive *directive;
61 nserror error;
62
63 /* token [ "=" ( token | quoted-string ) ] */
64
65 error = http__parse_token(&pos, &name);
66 if (error != NSERROR_OK)
67 return error;
68
69 http__skip_LWS(&pos);
70
71 if (*pos == '=') {
72 pos++;
73
74 http__skip_LWS(&pos);
75
76 if (*pos == '"')
77 error = http__parse_quoted_string(&pos, &value);
78 else
79 error = http__parse_token(&pos, &value);
80
81 if (error != NSERROR_OK) {
82 lwc_string_unref(name);
83 return error;
84 }
85 }
86
87 directive = malloc(sizeof(*directive));
88 if (directive == NULL) {
89 lwc_string_unref(value);
90 lwc_string_unref(name);
91 return NSERROR_NOMEM;
92 }
93
95 directive->name = name;
96 directive->value = value;
97
98 *result = directive;
99 *input = pos;
100
101 return NSERROR_OK;
102}
103
105{
107}
108
110 lwc_string *name, lwc_string **value)
111{
112 bool match;
113
114 while (list != NULL) {
115 if (lwc_string_caseless_isequal(name, list->name,
116 &match) == lwc_error_ok && match)
117 break;
118
119 list = (http_directive *) list->base.next;
120 }
121
122 if (list == NULL)
123 return NSERROR_NOT_FOUND;
124
125 if (list->value != NULL) {
126 *value = lwc_string_ref(list->value);
127 } else {
128 *value = NULL;
129 }
130
131 return NSERROR_OK;
132}
133
135 const http_directive *cur,
136 lwc_string **name, lwc_string **value)
137{
138 if (cur == NULL)
139 return NULL;
140
141 *name = lwc_string_ref(cur->name);
142 if (cur->value != NULL) {
143 *value = lwc_string_ref(cur->value);
144 } else {
145 *value = NULL;
146 }
147
148 return (http_directive *) cur->base.next;
149}
150
151static uint32_t count(const http_directive *list, lwc_string *key)
152{
153 uint32_t count = 0;
154 bool match;
155
156 while (list != NULL) {
157 if (lwc_string_caseless_isequal(key, list->name,
158 &match) == lwc_error_ok && match) {
159 count++;
160 }
161
162 list = (http_directive *) list->base.next;
163 }
164
165 return count;
166}
167
168static bool check_duplicates(const http_directive *directives)
169{
170 bool result = true;
171 const http_directive *key = directives;
172
173 if (key == NULL) {
174 /* No directives, so there can't be any duplicates */
175 return true;
176 }
177
178 do {
179 lwc_string *name = NULL, *value = NULL;
180
181 key = http_directive_list_iterate(key, &name, &value);
182
183 result &= (count(directives, name) == 1);
184
185 lwc_string_unref(name);
186 lwc_string_unref(value);
187 } while (key != NULL);
188
189 return result;
190}
191
192static nserror parse_max_age(lwc_string *value, uint32_t *result)
193{
194 const char *pos = lwc_string_data(value);
195 const char *end = pos + lwc_string_length(value);
196 uint32_t val = 0;
197
198 /* 1*DIGIT */
199
200 if (pos == end) {
201 /* Blank value */
202 return NSERROR_NOT_FOUND;
203 }
204
205 while (pos < end) {
206 if ('0' <= *pos && *pos <= '9') {
207 uint32_t nv = val * 10 + (*pos - '0');
208 if (nv < val) {
209 val = UINT_MAX;
210 } else {
211 val = nv;
212 }
213 } else {
214 /* Non-digit */
215 return NSERROR_NOT_FOUND;
216 }
217
218 pos++;
219 }
220
221 *result = val;
222
223 return NSERROR_OK;
224}
225
226/* See strict-transport-security.h for documentation */
229{
230 const char *pos = header_value;
232 http_directive *first = NULL;
233 http_directive *directives = NULL;
234 lwc_string *max_age_str = NULL, *isd_str = NULL;
235 uint32_t max_age;
236 bool include_sub_domains = false;
237 nserror error;
238
239 /* directive *( ";" directive ) */
240
241 http__skip_LWS(&pos);
242
243 error = http__parse_directive(&pos, &first);
244 if (error != NSERROR_OK) {
245 return error;
246 }
247
248 http__skip_LWS(&pos);
249
250 if (*pos == ';') {
251 error = http__item_list_parse(&pos,
252 http__parse_directive, first, &directives);
253 if (error != NSERROR_OK) {
254 if (directives != NULL) {
255 http_directive_list_destroy(directives);
256 }
257 return error;
258 }
259 } else {
260 directives = first;
261 }
262
263 /* Each directive must only appear once */
264 if (check_duplicates(directives) == false) {
265 http_directive_list_destroy(directives);
266 return NSERROR_NOT_FOUND;
267 }
268
269 /* max-age is required */
270 error = http_directive_list_find_item(directives,
271 corestring_lwc_max_age, &max_age_str);
272 if (error != NSERROR_OK || max_age_str == NULL) {
273 http_directive_list_destroy(directives);
274 return NSERROR_NOT_FOUND;
275 }
276
277 error = parse_max_age(max_age_str, &max_age);
278 if (error != NSERROR_OK) {
279 lwc_string_unref(max_age_str);
280 http_directive_list_destroy(directives);
281 return NSERROR_NOT_FOUND;
282 }
283 lwc_string_unref(max_age_str);
284
285 /* includeSubDomains is optional and valueless */
286 error = http_directive_list_find_item(directives,
287 corestring_lwc_includesubdomains, &isd_str);
288 if (error != NSERROR_OK && error != NSERROR_NOT_FOUND) {
289 http_directive_list_destroy(directives);
290 return NSERROR_NOT_FOUND;
291 } else if (error == NSERROR_OK) {
292 if (isd_str != NULL) {
293 /* Present, but not valueless: invalid */
294 lwc_string_unref(isd_str);
295 http_directive_list_destroy(directives);
296 return NSERROR_NOT_FOUND;
297 }
298 include_sub_domains = true;
299 }
300 http_directive_list_destroy(directives);
301
302 sts = malloc(sizeof(*sts));
303 if (sts == NULL) {
304 return NSERROR_NOMEM;
305 }
306
307 sts->max_age = max_age;
308 sts->include_sub_domains = include_sub_domains;
309
310 *result = sts;
311
312 return NSERROR_OK;
313}
314
315/* See strict-transport-security.h for documentation */
318{
319 free(victim);
320}
321
322/* See strict-transport-security.h for documentation */
325{
326 return sts->max_age;
327}
328
329/* See strict-transport-security.h for documentation */
332{
333 return sts->include_sub_domains;
334}
335
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.