NetSurf
ssl_certs.c
Go to the documentation of this file.
1/*
2 * Copyright 2020 Vincent Sanders <vince@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/**
20 * \file
21 * helpers for X509 certificate chains
22 */
23
24#include <stdlib.h>
25#include <stdint.h>
26#include <string.h>
27#include <nsutils/base64.h>
28
29#include "utils/errors.h"
30#include "utils/log.h"
31#include "utils/nsurl.h"
32
33#include "netsurf/ssl_certs.h"
34
35/*
36 * create new certificate chain
37 *
38 * exported interface documented in netsurf/ssl_certs.h
39 */
41cert_chain_alloc(size_t depth, struct cert_chain **chain_out)
42{
43 struct cert_chain* chain;
44
45 chain = calloc(1, sizeof(struct cert_chain));
46 if (chain == NULL) {
47 return NSERROR_NOMEM;
48 }
49
50 chain->depth = depth;
51
52 *chain_out = chain;
53
54 return NSERROR_OK;
55}
56
57
58/*
59 * duplicate certificate chain into existing chain
60 *
61 * exported interface documented in netsurf/ssl_certs.h
62 */
64cert_chain_dup_into(const struct cert_chain *src, struct cert_chain *dst)
65{
66 size_t depth;
67 for (depth = 0; depth < dst->depth; depth++) {
68 if (dst->certs[depth].der != NULL) {
69 free(dst->certs[depth].der);
70 dst->certs[depth].der = NULL;
71 }
72 }
73
74 dst->depth = src->depth;
75
76 for (depth = 0; depth < src->depth; depth++) {
77 dst->certs[depth].err = src->certs[depth].err;
79 if (src->certs[depth].der != NULL) {
80 dst->certs[depth].der = malloc(src->certs[depth].der_length);
81 if (dst->certs[depth].der == NULL) {
82 return NSERROR_NOMEM;
83 }
84 memcpy(dst->certs[depth].der,
85 src->certs[depth].der,
86 src->certs[depth].der_length);
87 }
88
89 }
90
91 return NSERROR_OK;
92}
93
94
95/*
96 * duplicate certificate chain
97 *
98 * exported interface documented in netsurf/ssl_certs.h
99 */
101cert_chain_dup(const struct cert_chain *src, struct cert_chain **dst_out)
102{
103 struct cert_chain* dst;
104 size_t depth;
105 nserror res;
106
107 res = cert_chain_alloc(src->depth, &dst);
108 if (res != NSERROR_OK) {
109 return res;
110 }
111
112 for (depth = 0; depth < src->depth; depth++) {
113 dst->certs[depth].err = src->certs[depth].err;
115 if (src->certs[depth].der != NULL) {
116 dst->certs[depth].der = malloc(src->certs[depth].der_length);
117 if (dst->certs[depth].der == NULL) {
118 cert_chain_free(dst);
119 return NSERROR_NOMEM;
120 }
121 memcpy(dst->certs[depth].der,
122 src->certs[depth].der,
123 src->certs[depth].der_length);
124 }
125
126 }
127
128 *dst_out = dst;
129 return NSERROR_OK;
130}
131
132
133#define MIN_CERT_LEN 64
134
135/**
136 * process a part of a query extracting the certificate of an error code
137 */
138static nserror
139process_query_section(const char *str, size_t len, struct cert_chain* chain)
140{
141 nsuerror nsures;
142
143 if ((len > (5 + MIN_CERT_LEN)) &&
144 (strncmp(str, "cert=", 5) == 0)) {
145 /* possible certificate entry */
146 nsures = nsu_base64_decode_alloc_url(
147 (const uint8_t *)str + 5,
148 len - 5,
149 &chain->certs[chain->depth].der,
150 &chain->certs[chain->depth].der_length);
151 if (nsures == NSUERROR_OK) {
152 chain->depth++;
153 }
154 } else if ((len > 8) &&
155 (strncmp(str, "certerr=", 8) == 0)) {
156 /* certificate entry error code */
157 if (chain->depth > 0) {
158 chain->certs[chain->depth - 1].err = strtoul(str + 8, NULL, 10);
159 }
160 }
161 return NSERROR_OK;
162}
163
164/*
165 * create a certificate chain from a fetch query string
166 *
167 * exported interface documented in netsurf/ssl_certs.h
168 */
169nserror cert_chain_from_query(struct nsurl *url, struct cert_chain **chain_out)
170{
171 struct cert_chain* chain;
172 nserror res;
173 char *querystr;
174 size_t querylen;
175 size_t kvstart;
176 size_t kvlen;
177
178 res = nsurl_get(url, NSURL_QUERY, &querystr, &querylen);
179 if (res != NSERROR_OK) {
180 return res;
181 }
182
183 if (querylen < MIN_CERT_LEN) {
184 free(querystr);
185 return NSERROR_NEED_DATA;
186 }
187
188 res = cert_chain_alloc(0, &chain);
189 if (res != NSERROR_OK) {
190 free(querystr);
191 return res;
192 }
193
194 for (kvlen = 0, kvstart = 0; kvstart < querylen; kvstart += kvlen) {
195 /* get query section length */
196 kvlen = 0;
197 while (((kvstart + kvlen) < querylen) &&
198 (querystr[kvstart + kvlen] != '&')) {
199 kvlen++;
200 }
201
202 res = process_query_section(querystr + kvstart, kvlen, chain);
203 if (res != NSERROR_OK) {
204 break;
205 }
206 kvlen++; /* account for & separator */
207 }
208 free(querystr);
209
210 if (chain->depth > 0) {
211 *chain_out = chain;
212 } else {
213 free(chain);
214 return NSERROR_INVALID;
215 }
216
217 return NSERROR_OK;
218}
219
220
221/*
222 * create a fetch query string from a certificate chain
223 *
224 * exported interface documented in netsurf/ssl_certs.h
225 */
226nserror cert_chain_to_query(struct cert_chain *chain, struct nsurl **url_out )
227{
228 nserror res;
229 nsurl *url;
230 size_t allocsize;
231 size_t urlstrlen;
232 uint8_t *urlstr;
233 size_t depth;
234
235 allocsize = 20;
236 for (depth = 0; depth < chain->depth; depth++) {
237 allocsize += 7; /* allow for &cert= */
238 allocsize += 4 * ((chain->certs[depth].der_length + 2) / 3);
239 if (chain->certs[depth].err != SSL_CERT_ERR_OK) {
240 allocsize += 20; /* allow for &certerr=4000000000 */
241 }
242 }
243
244 urlstr = malloc(allocsize);
245 if (urlstr == NULL) {
246 return NSERROR_NOMEM;
247 }
248
249 urlstrlen = snprintf((char *)urlstr, allocsize, "about:certificate");
250 for (depth = 0; depth < chain->depth; depth++) {
251 int written;
252 nsuerror nsures;
253 size_t output_length;
254
255 written = snprintf((char *)urlstr + urlstrlen,
256 allocsize - urlstrlen,
257 "&cert=");
258 if (written < 0) {
259 free(urlstr);
260 return NSERROR_UNKNOWN;
261 }
262 if ((size_t)written >= allocsize - urlstrlen) {
263 free(urlstr);
264 return NSERROR_UNKNOWN;
265 }
266
267 urlstrlen += (size_t)written;
268
269 output_length = allocsize - urlstrlen;
270 nsures = nsu_base64_encode_url(
271 chain->certs[depth].der,
272 chain->certs[depth].der_length,
273 (uint8_t *)urlstr + urlstrlen,
274 &output_length);
275 if (nsures != NSUERROR_OK) {
276 free(urlstr);
277 return (nserror)nsures;
278 }
279 urlstrlen += output_length;
280
281 if (chain->certs[depth].err != SSL_CERT_ERR_OK) {
282 written = snprintf((char *)urlstr + urlstrlen,
283 allocsize - urlstrlen,
284 "&certerr=%d",
285 chain->certs[depth].err);
286 if (written < 0) {
287 free(urlstr);
288 return NSERROR_UNKNOWN;
289 }
290 if ((size_t)written >= allocsize - urlstrlen) {
291 free(urlstr);
292 return NSERROR_UNKNOWN;
293 }
294
295 urlstrlen += (size_t)written;
296 }
297
298 }
299 urlstr[17] = '?';
300 urlstr[urlstrlen] = 0;
301
302 res = nsurl_create((const char *)urlstr, &url);
303 free(urlstr);
304
305 if (res == NSERROR_OK) {
306 *url_out = url;
307 }
308
309 return res;
310}
311
312/*
313 * free certificate chain
314 *
315 * exported interface documented in netsurf/ssl_certs.h
316 */
318{
319 size_t depth;
320
321 if (chain != NULL) {
322 for (depth = 0; depth < chain->depth; depth++) {
323 if (chain->certs[depth].der != NULL) {
324 free(chain->certs[depth].der);
325 }
326 }
327
328 free(chain);
329 }
330
331 return NSERROR_OK;
332}
333
334
335/*
336 * calculate storage used of certificate chain
337 *
338 * exported interface documented in netsurf/ssl_certs.h
339 */
340size_t cert_chain_size(const struct cert_chain *chain)
341{
342 size_t size = 0;
343 size_t depth;
344
345 if (chain != NULL) {
346 size += sizeof(struct cert_chain);
347
348 for (depth = 0; depth < chain->depth; depth++) {
349 if (chain->certs[depth].der != NULL) {
350 size += chain->certs[depth].der_length;
351 }
352 }
353 }
354
355 return size;
356}
Error codes.
nserror
Enumeration of error codes.
Definition: errors.h:29
@ NSERROR_NEED_DATA
More data needed.
Definition: errors.h:46
@ NSERROR_UNKNOWN
Unknown error - DO NOT USE.
Definition: errors.h:31
@ NSERROR_INVALID
Invalid data.
Definition: errors.h:49
@ NSERROR_NOMEM
Memory exhaustion.
Definition: errors.h:32
@ NSERROR_OK
No error.
Definition: errors.h:30
NetSurf URL handling (interface).
nserror nsurl_create(const char *const url_s, nsurl **url)
Create a NetSurf URL object from a URL string.
nserror nsurl_get(const nsurl *url, nsurl_component parts, char **url_s, size_t *url_l)
Get URL (section) as a string, from a NetSurf URL object.
@ NSURL_QUERY
Definition: nsurl.h:53
struct nsurl nsurl
NetSurf URL object.
Definition: nsurl.h:31
nserror cert_chain_from_query(struct nsurl *url, struct cert_chain **chain_out)
create a certificate chain from a fetch query string
Definition: ssl_certs.c:169
nserror cert_chain_alloc(size_t depth, struct cert_chain **chain_out)
create new certificate chain
Definition: ssl_certs.c:41
nserror cert_chain_dup_into(const struct cert_chain *src, struct cert_chain *dst)
duplicate a certificate chain into an existing chain
Definition: ssl_certs.c:64
static nserror process_query_section(const char *str, size_t len, struct cert_chain *chain)
process a part of a query extracting the certificate of an error code
Definition: ssl_certs.c:139
nserror cert_chain_dup(const struct cert_chain *src, struct cert_chain **dst_out)
duplicate a certificate chain
Definition: ssl_certs.c:101
nserror cert_chain_free(struct cert_chain *chain)
free a certificate chain
Definition: ssl_certs.c:317
#define MIN_CERT_LEN
Definition: ssl_certs.c:133
size_t cert_chain_size(const struct cert_chain *chain)
total number of data bytes in a chain
Definition: ssl_certs.c:340
nserror cert_chain_to_query(struct cert_chain *chain, struct nsurl **url_out)
create a fetch query string from a certificate chain
Definition: ssl_certs.c:226
SSL related types and values.
@ SSL_CERT_ERR_OK
Nothing wrong with this certificate.
Definition: ssl_certs.h:37
Interface to utility string handling.
X509 certificate chain.
Definition: ssl_certs.h:59
struct cert_chain::@57 certs[MAX_CERT_DEPTH]
size_t depth
the number of certificates in the chain
Definition: ssl_certs.h:63
uint8_t * der
data in Distinguished Encoding Rules (DER) format
Definition: ssl_certs.h:73
size_t der_length
DER length.
Definition: ssl_certs.h:78
ssl_cert_err err
Whatever is wrong with this certificate.
Definition: ssl_certs.h:68