xref: /freebsd/lib/libsecureboot/brf.c (revision ce6a89e27cd190313be39bb479880aeda4778436)
1 // The functions here are derrived from BearSSL/tools/*.c
2 // When that is refactored suitably we can use them directly.
3 /*
4  * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining
7  * a copy of this software and associated documentation files (the
8  * "Software"), to deal in the Software without restriction, including
9  * without limitation the rights to use, copy, modify, merge, publish,
10  * distribute, sublicense, and/or sell copies of the Software, and to
11  * permit persons to whom the Software is furnished to do so, subject to
12  * the following conditions:
13  *
14  * The above copyright notice and this permission notice shall be
15  * included in all copies or substantial portions of the Software.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
21  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
22  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
23  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24  * SOFTWARE.
25  */
26 #include <sys/cdefs.h>
27 __FBSDID("$FreeBSD$");
28 
29 #define NEED_BRSSL_H
30 #include "libsecureboot-priv.h"
31 #include <brssl.h>
32 
33 
34 static int
35 is_ign(int c)
36 {
37 	if (c == 0) {
38 		return (0);
39 	}
40 	if (c <= 32 || c == '-' || c == '_' || c == '.'
41 		|| c == '/' || c == '+' || c == ':')
42 	{
43 		return (1);
44 	}
45 	return (0);
46 }
47 
48 /*
49  * Get next non-ignored character, normalised:
50  *    ASCII letters are converted to lowercase
51  *    control characters, space, '-', '_', '.', '/', '+' and ':' are ignored
52  * A terminating zero is returned as 0.
53  */
54 static int
55 next_char(const char **ps, const char *limit)
56 {
57 	for (;;) {
58 		int c;
59 
60 		if (*ps == limit) {
61 			return (0);
62 		}
63 		c = *(*ps) ++;
64 		if (c == 0) {
65 			return (0);
66 		}
67 		if (c >= 'A' && c <= 'Z') {
68 			c += 'a' - 'A';
69 		}
70 		if (!is_ign(c)) {
71 			return (c);
72 		}
73 	}
74 }
75 
76 /*
77  * Partial string equality comparison, with normalisation.
78  */
79 static int
80 eqstr_chunk(const char *s1, size_t s1_len, const char *s2, size_t s2_len)
81 {
82 	const char *lim1, *lim2;
83 
84 	lim1 = s1 + s1_len;
85 	lim2 = s2 + s2_len;
86 	for (;;) {
87 		int c1, c2;
88 
89 		c1 = next_char(&s1, lim1);
90 		c2 = next_char(&s2, lim2);
91 		if (c1 != c2) {
92 			return (0);
93 		}
94 		if (c1 == 0) {
95 			return (1);
96 		}
97 	}
98 }
99 
100 /* see brssl.h */
101 int
102 eqstr(const char *s1, const char *s2)
103 {
104 	return (eqstr_chunk(s1, strlen(s1), s2, strlen(s2)));
105 }
106 
107 int
108 looks_like_DER(const unsigned char *buf, size_t len)
109 {
110 	int fb;
111 	size_t dlen;
112 
113 	if (len < 2) {
114 		return (0);
115 	}
116 	if (*buf ++ != 0x30) {
117 		return (0);
118 	}
119 	fb = *buf ++;
120 	len -= 2;
121 	if (fb < 0x80) {
122 		return ((size_t)fb == len);
123 	} else if (fb == 0x80) {
124 		return (0);
125 	} else {
126 		fb -= 0x80;
127 		if (len < (size_t)fb + 2) {
128 			return (0);
129 		}
130 		len -= (size_t)fb;
131 		dlen = 0;
132 		while (fb -- > 0) {
133 			if (dlen > (len >> 8)) {
134 				return (0);
135 			}
136 			dlen = (dlen << 8) + (size_t)*buf ++;
137 		}
138 		return (dlen == len);
139 	}
140 }
141 
142 static void
143 vblob_append(void *cc, const void *data, size_t len)
144 {
145 	bvector *bv;
146 
147 	bv = cc;
148 	VEC_ADDMANY(*bv, data, len);
149 }
150 
151 void
152 free_pem_object_contents(pem_object *po)
153 {
154 	if (po != NULL) {
155 		xfree(po->name);
156 		xfree(po->data);
157 	}
158 }
159 
160 pem_object *
161 decode_pem(const void *src, size_t len, size_t *num)
162 {
163 	VECTOR(pem_object) pem_list = VEC_INIT;
164 	br_pem_decoder_context pc;
165 	pem_object po, *pos;
166 	const unsigned char *buf;
167 	bvector bv = VEC_INIT;
168 	int inobj;
169 	int extra_nl;
170 
171 	*num = 0;
172 	br_pem_decoder_init(&pc);
173 	buf = src;
174 	inobj = 0;
175 	po.name = NULL;
176 	po.data = NULL;
177 	po.data_len = 0;
178 	extra_nl = 1;
179 	while (len > 0) {
180 		size_t tlen;
181 
182 		tlen = br_pem_decoder_push(&pc, buf, len);
183 		buf += tlen;
184 		len -= tlen;
185 		switch (br_pem_decoder_event(&pc)) {
186 
187 		case BR_PEM_BEGIN_OBJ:
188 			po.name = xstrdup(br_pem_decoder_name(&pc));
189 			br_pem_decoder_setdest(&pc, vblob_append, &bv);
190 			inobj = 1;
191 			break;
192 
193 		case BR_PEM_END_OBJ:
194 			if (inobj) {
195 				po.data = VEC_TOARRAY(bv);
196 				po.data_len = VEC_LEN(bv);
197 				VEC_ADD(pem_list, po);
198 				VEC_CLEAR(bv);
199 				po.name = NULL;
200 				po.data = NULL;
201 				po.data_len = 0;
202 				inobj = 0;
203 			}
204 			break;
205 
206 		case BR_PEM_ERROR:
207 			xfree(po.name);
208 			VEC_CLEAR(bv);
209 			ve_error_set("ERROR: invalid PEM encoding");
210 			VEC_CLEAREXT(pem_list, &free_pem_object_contents);
211 			return (NULL);
212 		}
213 
214 		/*
215 		 * We add an extra newline at the end, in order to
216 		 * support PEM files that lack the newline on their last
217 		 * line (this is somwehat invalid, but PEM format is not
218 		 * standardised and such files do exist in the wild, so
219 		 * we'd better accept them).
220 		 */
221 		if (len == 0 && extra_nl) {
222 			extra_nl = 0;
223 			buf = (const unsigned char *)"\n";
224 			len = 1;
225 		}
226 	}
227 	if (inobj) {
228 	    ve_error_set("ERROR: unfinished PEM object");
229 		xfree(po.name);
230 		VEC_CLEAR(bv);
231 		VEC_CLEAREXT(pem_list, &free_pem_object_contents);
232 		return (NULL);
233 	}
234 
235 	*num = VEC_LEN(pem_list);
236 	VEC_ADD(pem_list, po);
237 	pos = VEC_TOARRAY(pem_list);
238 	VEC_CLEAR(pem_list);
239 	return (pos);
240 }
241 
242 br_x509_certificate *
243 parse_certificates(unsigned char *buf, size_t len, size_t *num)
244 {
245 	VECTOR(br_x509_certificate) cert_list = VEC_INIT;
246 	pem_object *pos;
247 	size_t u, num_pos;
248 	br_x509_certificate *xcs;
249 	br_x509_certificate dummy;
250 
251 	*num = 0;
252 
253 	/*
254 	 * Check for a DER-encoded certificate.
255 	 */
256 	if (looks_like_DER(buf, len)) {
257 		xcs = xmalloc(2 * sizeof *xcs);
258 		xcs[0].data = buf;
259 		xcs[0].data_len = len;
260 		xcs[1].data = NULL;
261 		xcs[1].data_len = 0;
262 		*num = 1;
263 		return (xcs);
264 	}
265 
266 	pos = decode_pem(buf, len, &num_pos);
267 	if (pos == NULL) {
268 		return (NULL);
269 	}
270 	for (u = 0; u < num_pos; u ++) {
271 		if (eqstr(pos[u].name, "CERTIFICATE")
272 			|| eqstr(pos[u].name, "X509 CERTIFICATE"))
273 		{
274 			br_x509_certificate xc;
275 
276 			xc.data = pos[u].data;
277 			xc.data_len = pos[u].data_len;
278 			pos[u].data = NULL;
279 			VEC_ADD(cert_list, xc);
280 		}
281 	}
282 	for (u = 0; u < num_pos; u ++) {
283 		free_pem_object_contents(&pos[u]);
284 	}
285 	xfree(pos);
286 
287 	if (VEC_LEN(cert_list) == 0) {
288 		return (NULL);
289 	}
290 	*num = VEC_LEN(cert_list);
291 	dummy.data = NULL;
292 	dummy.data_len = 0;
293 	VEC_ADD(cert_list, dummy);
294 	xcs = VEC_TOARRAY(cert_list);
295 	VEC_CLEAR(cert_list);
296 	return (xcs);
297 }
298 
299 br_x509_certificate *
300 read_certificates(const char *fname, size_t *num)
301 {
302 	br_x509_certificate *xcs;
303 	unsigned char *buf;
304 	size_t len;
305 
306 	*num = 0;
307 
308 	/*
309 	 * TODO: reading the whole file is crude; we could parse them
310 	 * in a streamed fashion. But it does not matter much in practice.
311 	 */
312 	buf = read_file(fname, &len);
313 	if (buf == NULL) {
314 		return (NULL);
315 	}
316 	xcs = parse_certificates(buf, len, num);
317 	if (xcs == NULL) {
318 	    ve_error_set("ERROR: no certificate in file '%s'\n", fname);
319 	}
320 	xfree(buf);
321 	return (xcs);
322 }
323 
324 /* see brssl.h */
325 void
326 free_certificates(br_x509_certificate *certs, size_t num)
327 {
328 	size_t u;
329 
330 	for (u = 0; u < num; u ++) {
331 		xfree(certs[u].data);
332 	}
333 	xfree(certs);
334 }
335 
336 
337 static void
338 dn_append(void *ctx, const void *buf, size_t len)
339 {
340 	VEC_ADDMANY(*(bvector *)ctx, buf, len);
341 }
342 
343 int
344 certificate_to_trust_anchor_inner(br_x509_trust_anchor *ta,
345 	br_x509_certificate *xc)
346 {
347 	br_x509_decoder_context dc;
348 	bvector vdn = VEC_INIT;
349 	br_x509_pkey *pk;
350 
351 	br_x509_decoder_init(&dc, dn_append, &vdn);
352 	br_x509_decoder_push(&dc, xc->data, xc->data_len);
353 	pk = br_x509_decoder_get_pkey(&dc);
354 	if (pk == NULL) {
355 	    ve_error_set("ERROR: CA decoding failed with error %d\n",
356 		      br_x509_decoder_last_error(&dc));
357 	    VEC_CLEAR(vdn);
358 	    return (-1);
359 	}
360 	ta->dn.data = VEC_TOARRAY(vdn);
361 	ta->dn.len = VEC_LEN(vdn);
362 	VEC_CLEAR(vdn);
363 	ta->flags = 0;
364 	if (br_x509_decoder_isCA(&dc)) {
365 		ta->flags |= BR_X509_TA_CA;
366 	}
367 	switch (pk->key_type) {
368 	case BR_KEYTYPE_RSA:
369 		ta->pkey.key_type = BR_KEYTYPE_RSA;
370 		ta->pkey.key.rsa.n = xblobdup(pk->key.rsa.n, pk->key.rsa.nlen);
371 		ta->pkey.key.rsa.nlen = pk->key.rsa.nlen;
372 		ta->pkey.key.rsa.e = xblobdup(pk->key.rsa.e, pk->key.rsa.elen);
373 		ta->pkey.key.rsa.elen = pk->key.rsa.elen;
374 		break;
375 	case BR_KEYTYPE_EC:
376 		ta->pkey.key_type = BR_KEYTYPE_EC;
377 		ta->pkey.key.ec.curve = pk->key.ec.curve;
378 		ta->pkey.key.ec.q = xblobdup(pk->key.ec.q, pk->key.ec.qlen);
379 		ta->pkey.key.ec.qlen = pk->key.ec.qlen;
380 		break;
381 	default:
382 	    ve_error_set("ERROR: unsupported public key type in CA\n");
383 		xfree(ta->dn.data);
384 		return (-1);
385 	}
386 	return (0);
387 }
388 
389 /* see brssl.h */
390 void
391 free_ta_contents(br_x509_trust_anchor *ta)
392 {
393 	xfree(ta->dn.data);
394 	switch (ta->pkey.key_type) {
395 	case BR_KEYTYPE_RSA:
396 		xfree(ta->pkey.key.rsa.n);
397 		xfree(ta->pkey.key.rsa.e);
398 		break;
399 	case BR_KEYTYPE_EC:
400 		xfree(ta->pkey.key.ec.q);
401 		break;
402 	}
403 }
404