]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - lib/libsecureboot/brf.c
tarfs: Remove unnecessary hack and obsolete comment.
[FreeBSD/FreeBSD.git] / lib / libsecureboot / brf.c
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 #define NEED_BRSSL_H
28 #include "libsecureboot-priv.h"
29 #include <brssl.h>
30
31
32 static int
33 is_ign(int c)
34 {
35         if (c == 0) {
36                 return (0);
37         }
38         if (c <= 32 || c == '-' || c == '_' || c == '.'
39                 || c == '/' || c == '+' || c == ':')
40         {
41                 return (1);
42         }
43         return (0);
44 }
45
46 /*
47  * Get next non-ignored character, normalised:
48  *    ASCII letters are converted to lowercase
49  *    control characters, space, '-', '_', '.', '/', '+' and ':' are ignored
50  * A terminating zero is returned as 0.
51  */
52 static int
53 next_char(const char **ps, const char *limit)
54 {
55         for (;;) {
56                 int c;
57
58                 if (*ps == limit) {
59                         return (0);
60                 }
61                 c = *(*ps) ++;
62                 if (c == 0) {
63                         return (0);
64                 }
65                 if (c >= 'A' && c <= 'Z') {
66                         c += 'a' - 'A';
67                 }
68                 if (!is_ign(c)) {
69                         return (c);
70                 }
71         }
72 }
73
74 /*
75  * Partial string equality comparison, with normalisation.
76  */
77 static int
78 eqstr_chunk(const char *s1, size_t s1_len, const char *s2, size_t s2_len)
79 {
80         const char *lim1, *lim2;
81
82         lim1 = s1 + s1_len;
83         lim2 = s2 + s2_len;
84         for (;;) {
85                 int c1, c2;
86
87                 c1 = next_char(&s1, lim1);
88                 c2 = next_char(&s2, lim2);
89                 if (c1 != c2) {
90                         return (0);
91                 }
92                 if (c1 == 0) {
93                         return (1);
94                 }
95         }
96 }
97
98 /* see brssl.h */
99 int
100 eqstr(const char *s1, const char *s2)
101 {
102         return (eqstr_chunk(s1, strlen(s1), s2, strlen(s2)));
103 }
104
105 int
106 looks_like_DER(const unsigned char *buf, size_t len)
107 {
108         int fb;
109         size_t dlen;
110
111         if (len < 2) {
112                 return (0);
113         }
114         if (*buf ++ != 0x30) {
115                 return (0);
116         }
117         fb = *buf ++;
118         len -= 2;
119         if (fb < 0x80) {
120                 return ((size_t)fb == len);
121         } else if (fb == 0x80) {
122                 return (0);
123         } else {
124                 fb -= 0x80;
125                 if (len < (size_t)fb + 2) {
126                         return (0);
127                 }
128                 len -= (size_t)fb;
129                 dlen = 0;
130                 while (fb -- > 0) {
131                         if (dlen > (len >> 8)) {
132                                 return (0);
133                         }
134                         dlen = (dlen << 8) + (size_t)*buf ++;
135                 }
136                 return (dlen == len);
137         }
138 }
139
140 static void
141 vblob_append(void *cc, const void *data, size_t len)
142 {
143         bvector *bv;
144
145         bv = cc;
146         VEC_ADDMANY(*bv, data, len);
147 }
148
149 void
150 free_pem_object_contents(pem_object *po)
151 {
152         if (po != NULL) {
153                 xfree(po->name);
154                 xfree(po->data);
155         }
156 }
157
158 pem_object *
159 decode_pem(const void *src, size_t len, size_t *num)
160 {
161         VECTOR(pem_object) pem_list = VEC_INIT;
162         br_pem_decoder_context pc;
163         pem_object po, *pos;
164         const unsigned char *buf;
165         bvector bv = VEC_INIT;
166         int inobj;
167         int extra_nl;
168
169         *num = 0;
170         br_pem_decoder_init(&pc);
171         buf = src;
172         inobj = 0;
173         po.name = NULL;
174         po.data = NULL;
175         po.data_len = 0;
176         extra_nl = 1;
177         while (len > 0) {
178                 size_t tlen;
179
180                 tlen = br_pem_decoder_push(&pc, buf, len);
181                 buf += tlen;
182                 len -= tlen;
183                 switch (br_pem_decoder_event(&pc)) {
184
185                 case BR_PEM_BEGIN_OBJ:
186                         po.name = xstrdup(br_pem_decoder_name(&pc));
187                         br_pem_decoder_setdest(&pc, vblob_append, &bv);
188                         inobj = 1;
189                         break;
190
191                 case BR_PEM_END_OBJ:
192                         if (inobj) {
193                                 po.data = VEC_TOARRAY(bv);
194                                 po.data_len = VEC_LEN(bv);
195                                 VEC_ADD(pem_list, po);
196                                 VEC_CLEAR(bv);
197                                 po.name = NULL;
198                                 po.data = NULL;
199                                 po.data_len = 0;
200                                 inobj = 0;
201                         }
202                         break;
203
204                 case BR_PEM_ERROR:
205                         xfree(po.name);
206                         VEC_CLEAR(bv);
207                         ve_error_set("ERROR: invalid PEM encoding");
208                         VEC_CLEAREXT(pem_list, &free_pem_object_contents);
209                         return (NULL);
210                 }
211
212                 /*
213                  * We add an extra newline at the end, in order to
214                  * support PEM files that lack the newline on their last
215                  * line (this is somwehat invalid, but PEM format is not
216                  * standardised and such files do exist in the wild, so
217                  * we'd better accept them).
218                  */
219                 if (len == 0 && extra_nl) {
220                         extra_nl = 0;
221                         buf = (const unsigned char *)"\n";
222                         len = 1;
223                 }
224         }
225         if (inobj) {
226             ve_error_set("ERROR: unfinished PEM object");
227                 xfree(po.name);
228                 VEC_CLEAR(bv);
229                 VEC_CLEAREXT(pem_list, &free_pem_object_contents);
230                 return (NULL);
231         }
232
233         *num = VEC_LEN(pem_list);
234         VEC_ADD(pem_list, po);
235         pos = VEC_TOARRAY(pem_list);
236         VEC_CLEAR(pem_list);
237         return (pos);
238 }
239
240 br_x509_certificate *
241 parse_certificates(unsigned char *buf, size_t len, size_t *num)
242 {
243         VECTOR(br_x509_certificate) cert_list = VEC_INIT;
244         pem_object *pos;
245         size_t u, num_pos;
246         br_x509_certificate *xcs;
247         br_x509_certificate dummy;
248
249         *num = 0;
250
251         /*
252          * Check for a DER-encoded certificate.
253          */
254         if (looks_like_DER(buf, len)) {
255                 xcs = xmalloc(2 * sizeof *xcs);
256                 xcs[0].data = buf;
257                 xcs[0].data_len = len;
258                 xcs[1].data = NULL;
259                 xcs[1].data_len = 0;
260                 *num = 1;
261                 return (xcs);
262         }
263
264         pos = decode_pem(buf, len, &num_pos);
265         if (pos == NULL) {
266                 return (NULL);
267         }
268         for (u = 0; u < num_pos; u ++) {
269                 if (eqstr(pos[u].name, "CERTIFICATE")
270                         || eqstr(pos[u].name, "X509 CERTIFICATE"))
271                 {
272                         br_x509_certificate xc;
273
274                         xc.data = pos[u].data;
275                         xc.data_len = pos[u].data_len;
276                         pos[u].data = NULL;
277                         VEC_ADD(cert_list, xc);
278                 }
279         }
280         for (u = 0; u < num_pos; u ++) {
281                 free_pem_object_contents(&pos[u]);
282         }
283         xfree(pos);
284
285         if (VEC_LEN(cert_list) == 0) {
286                 return (NULL);
287         }
288         *num = VEC_LEN(cert_list);
289         dummy.data = NULL;
290         dummy.data_len = 0;
291         VEC_ADD(cert_list, dummy);
292         xcs = VEC_TOARRAY(cert_list);
293         VEC_CLEAR(cert_list);
294         return (xcs);
295 }
296
297 br_x509_certificate *
298 read_certificates(const char *fname, size_t *num)
299 {
300         br_x509_certificate *xcs;
301         unsigned char *buf;
302         size_t len;
303
304         *num = 0;
305
306         /*
307          * TODO: reading the whole file is crude; we could parse them
308          * in a streamed fashion. But it does not matter much in practice.
309          */
310         buf = read_file(fname, &len);
311         if (buf == NULL) {
312                 return (NULL);
313         }
314         xcs = parse_certificates(buf, len, num);
315         if (xcs == NULL) {
316             ve_error_set("ERROR: no certificate in file '%s'\n", fname);
317         }
318         xfree(buf);
319         return (xcs);
320 }
321
322 /* see brssl.h */
323 void
324 free_certificates(br_x509_certificate *certs, size_t num)
325 {
326         size_t u;
327
328         for (u = 0; u < num; u ++) {
329                 xfree(certs[u].data);
330         }
331         xfree(certs);
332 }
333
334
335 static void
336 dn_append(void *ctx, const void *buf, size_t len)
337 {
338         VEC_ADDMANY(*(bvector *)ctx, buf, len);
339 }
340
341 int
342 certificate_to_trust_anchor_inner(br_x509_trust_anchor *ta,
343         br_x509_certificate *xc)
344 {
345         br_x509_decoder_context dc;
346         bvector vdn = VEC_INIT;
347         br_x509_pkey *pk;
348
349         br_x509_decoder_init(&dc, dn_append, &vdn);
350         br_x509_decoder_push(&dc, xc->data, xc->data_len);
351         pk = br_x509_decoder_get_pkey(&dc);
352         if (pk == NULL) {
353             ve_error_set("ERROR: CA decoding failed with error %d\n",
354                       br_x509_decoder_last_error(&dc));
355             VEC_CLEAR(vdn);
356             return (-1);
357         }
358         ta->dn.data = VEC_TOARRAY(vdn);
359         ta->dn.len = VEC_LEN(vdn);
360         VEC_CLEAR(vdn);
361         ta->flags = 0;
362         if (br_x509_decoder_isCA(&dc)) {
363                 ta->flags |= BR_X509_TA_CA;
364         }
365         switch (pk->key_type) {
366         case BR_KEYTYPE_RSA:
367                 ta->pkey.key_type = BR_KEYTYPE_RSA;
368                 ta->pkey.key.rsa.n = xblobdup(pk->key.rsa.n, pk->key.rsa.nlen);
369                 ta->pkey.key.rsa.nlen = pk->key.rsa.nlen;
370                 ta->pkey.key.rsa.e = xblobdup(pk->key.rsa.e, pk->key.rsa.elen);
371                 ta->pkey.key.rsa.elen = pk->key.rsa.elen;
372                 break;
373         case BR_KEYTYPE_EC:
374                 ta->pkey.key_type = BR_KEYTYPE_EC;
375                 ta->pkey.key.ec.curve = pk->key.ec.curve;
376                 ta->pkey.key.ec.q = xblobdup(pk->key.ec.q, pk->key.ec.qlen);
377                 ta->pkey.key.ec.qlen = pk->key.ec.qlen;
378                 break;
379         default:
380             ve_error_set("ERROR: unsupported public key type in CA\n");
381                 xfree(ta->dn.data);
382                 return (-1);
383         }
384         return (0);
385 }
386
387 /* see brssl.h */
388 void
389 free_ta_contents(br_x509_trust_anchor *ta)
390 {
391         xfree(ta->dn.data);
392         switch (ta->pkey.key_type) {
393         case BR_KEYTYPE_RSA:
394                 xfree(ta->pkey.key.rsa.n);
395                 xfree(ta->pkey.key.rsa.e);
396                 break;
397         case BR_KEYTYPE_EC:
398                 xfree(ta->pkey.key.ec.q);
399                 break;
400         }
401 }