]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/bearssl/tools/files.c
Add support for loader veriexec
[FreeBSD/FreeBSD.git] / contrib / bearssl / tools / files.c
1 /*
2  * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining 
5  * a copy of this software and associated documentation files (the
6  * "Software"), to deal in the Software without restriction, including
7  * without limitation the rights to use, copy, modify, merge, publish,
8  * distribute, sublicense, and/or sell copies of the Software, and to
9  * permit persons to whom the Software is furnished to do so, subject to
10  * the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be 
13  * included in all copies or substantial portions of the Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
16  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
18  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
19  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
20  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
21  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22  * SOFTWARE.
23  */
24
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <stdint.h>
29 #include <errno.h>
30
31 #include "brssl.h"
32
33 /* see brssl.h */
34 unsigned char *
35 read_file(const char *fname, size_t *len)
36 {
37         bvector vbuf = VEC_INIT;
38         FILE *f;
39
40         *len = 0;
41         f = fopen(fname, "rb");
42         if (f == NULL) {
43                 fprintf(stderr,
44                         "ERROR: could not open file '%s' for reading\n", fname);
45                 return NULL;
46         }
47         for (;;) {
48                 unsigned char tmp[1024];
49                 size_t rlen;
50
51                 rlen = fread(tmp, 1, sizeof tmp, f);
52                 if (rlen == 0) {
53                         unsigned char *buf;
54
55                         if (ferror(f)) {
56                                 fprintf(stderr,
57                                         "ERROR: read error on file '%s'\n",
58                                         fname);
59                                 fclose(f);
60                                 return NULL;
61                         }
62                         buf = VEC_TOARRAY(vbuf);
63                         *len = VEC_LEN(vbuf);
64                         VEC_CLEAR(vbuf);
65                         fclose(f);
66                         return buf;
67                 }
68                 VEC_ADDMANY(vbuf, tmp, rlen);
69         }
70 }
71
72 /* see brssl.h */
73 int
74 write_file(const char *fname, const void *data, size_t len)
75 {
76         FILE *f;
77         const unsigned char *buf;
78
79         f = fopen(fname, "wb");
80         if (f == NULL) {
81                 fprintf(stderr,
82                         "ERROR: could not open file '%s' for reading\n", fname);
83                 return -1;
84         }
85         buf = data;
86         while (len > 0) {
87                 size_t wlen;
88
89                 wlen = fwrite(buf, 1, len, f);
90                 if (wlen == 0) {
91                         fprintf(stderr,
92                                 "ERROR: could not write all bytes to '%s'\n",
93                                 fname);
94                         fclose(f);
95                         return -1;
96                 }
97                 buf += wlen;
98                 len -= wlen;
99         }
100         if (ferror(f)) {
101                 fprintf(stderr, "ERROR: write error on file '%s'\n", fname);
102                 fclose(f);
103                 return -1;
104         }
105         fclose(f);
106         return 0;
107 }
108
109 /* see brssl.h */
110 int
111 looks_like_DER(const unsigned char *buf, size_t len)
112 {
113         int fb;
114         size_t dlen;
115
116         if (len < 2) {
117                 return 0;
118         }
119         if (*buf ++ != 0x30) {
120                 return 0;
121         }
122         fb = *buf ++;
123         len -= 2;
124         if (fb < 0x80) {
125                 return (size_t)fb == len;
126         } else if (fb == 0x80) {
127                 return 0;
128         } else {
129                 fb -= 0x80;
130                 if (len < (size_t)fb + 2) {
131                         return 0;
132                 }
133                 len -= (size_t)fb;
134                 dlen = 0;
135                 while (fb -- > 0) {
136                         if (dlen > (len >> 8)) {
137                                 return 0;
138                         }
139                         dlen = (dlen << 8) + (size_t)*buf ++;
140                 }
141                 return dlen == len;
142         }
143 }
144
145 static void
146 vblob_append(void *cc, const void *data, size_t len)
147 {
148         bvector *bv;
149
150         bv = cc;
151         VEC_ADDMANY(*bv, data, len);
152 }
153
154 /* see brssl.h */
155 void
156 free_pem_object_contents(pem_object *po)
157 {
158         if (po != NULL) {
159                 xfree(po->name);
160                 xfree(po->data);
161         }
162 }
163
164 /* see brssl.h */
165 pem_object *
166 decode_pem(const void *src, size_t len, size_t *num)
167 {
168         VECTOR(pem_object) pem_list = VEC_INIT;
169         br_pem_decoder_context pc;
170         pem_object po, *pos;
171         const unsigned char *buf;
172         bvector bv = VEC_INIT;
173         int inobj;
174         int extra_nl;
175
176         *num = 0;
177         br_pem_decoder_init(&pc);
178         buf = src;
179         inobj = 0;
180         po.name = NULL;
181         po.data = NULL;
182         po.data_len = 0;
183         extra_nl = 1;
184         while (len > 0) {
185                 size_t tlen;
186
187                 tlen = br_pem_decoder_push(&pc, buf, len);
188                 buf += tlen;
189                 len -= tlen;
190                 switch (br_pem_decoder_event(&pc)) {
191
192                 case BR_PEM_BEGIN_OBJ:
193                         po.name = xstrdup(br_pem_decoder_name(&pc));
194                         br_pem_decoder_setdest(&pc, vblob_append, &bv);
195                         inobj = 1;
196                         break;
197
198                 case BR_PEM_END_OBJ:
199                         if (inobj) {
200                                 po.data = VEC_TOARRAY(bv);
201                                 po.data_len = VEC_LEN(bv);
202                                 VEC_ADD(pem_list, po);
203                                 VEC_CLEAR(bv);
204                                 po.name = NULL;
205                                 po.data = NULL;
206                                 po.data_len = 0;
207                                 inobj = 0;
208                         }
209                         break;
210
211                 case BR_PEM_ERROR:
212                         xfree(po.name);
213                         VEC_CLEAR(bv);
214                         fprintf(stderr,
215                                 "ERROR: invalid PEM encoding\n");
216                         VEC_CLEAREXT(pem_list, &free_pem_object_contents);
217                         return NULL;
218                 }
219
220                 /*
221                  * We add an extra newline at the end, in order to
222                  * support PEM files that lack the newline on their last
223                  * line (this is somwehat invalid, but PEM format is not
224                  * standardised and such files do exist in the wild, so
225                  * we'd better accept them).
226                  */
227                 if (len == 0 && extra_nl) {
228                         extra_nl = 0;
229                         buf = (const unsigned char *)"\n";
230                         len = 1;
231                 }
232         }
233         if (inobj) {
234                 fprintf(stderr, "ERROR: unfinished PEM object\n");
235                 xfree(po.name);
236                 VEC_CLEAR(bv);
237                 VEC_CLEAREXT(pem_list, &free_pem_object_contents);
238                 return NULL;
239         }
240
241         *num = VEC_LEN(pem_list);
242         VEC_ADD(pem_list, po);
243         pos = VEC_TOARRAY(pem_list);
244         VEC_CLEAR(pem_list);
245         return pos;
246 }
247
248 /* see brssl.h */
249 br_x509_certificate *
250 read_certificates(const char *fname, size_t *num)
251 {
252         VECTOR(br_x509_certificate) cert_list = VEC_INIT;
253         unsigned char *buf;
254         size_t len;
255         pem_object *pos;
256         size_t u, num_pos;
257         br_x509_certificate *xcs;
258         br_x509_certificate dummy;
259
260         *num = 0;
261
262         /*
263          * TODO: reading the whole file is crude; we could parse them
264          * in a streamed fashion. But it does not matter much in practice.
265          */
266         buf = read_file(fname, &len);
267         if (buf == NULL) {
268                 return NULL;
269         }
270
271         /*
272          * Check for a DER-encoded certificate.
273          */
274         if (looks_like_DER(buf, len)) {
275                 xcs = xmalloc(2 * sizeof *xcs);
276                 xcs[0].data = buf;
277                 xcs[0].data_len = len;
278                 xcs[1].data = NULL;
279                 xcs[1].data_len = 0;
280                 *num = 1;
281                 return xcs;
282         }
283
284         pos = decode_pem(buf, len, &num_pos);
285         xfree(buf);
286         if (pos == NULL) {
287                 return NULL;
288         }
289         for (u = 0; u < num_pos; u ++) {
290                 if (eqstr(pos[u].name, "CERTIFICATE")
291                         || eqstr(pos[u].name, "X509 CERTIFICATE"))
292                 {
293                         br_x509_certificate xc;
294
295                         xc.data = pos[u].data;
296                         xc.data_len = pos[u].data_len;
297                         pos[u].data = NULL;
298                         VEC_ADD(cert_list, xc);
299                 }
300         }
301         for (u = 0; u < num_pos; u ++) {
302                 free_pem_object_contents(&pos[u]);
303         }
304         xfree(pos);
305
306         if (VEC_LEN(cert_list) == 0) {
307                 fprintf(stderr, "ERROR: no certificate in file '%s'\n", fname);
308                 return NULL;
309         }
310         *num = VEC_LEN(cert_list);
311         dummy.data = NULL;
312         dummy.data_len = 0;
313         VEC_ADD(cert_list, dummy);
314         xcs = VEC_TOARRAY(cert_list);
315         VEC_CLEAR(cert_list);
316         return xcs;
317 }
318
319 /* see brssl.h */
320 void
321 free_certificates(br_x509_certificate *certs, size_t num)
322 {
323         size_t u;
324
325         for (u = 0; u < num; u ++) {
326                 xfree(certs[u].data);
327         }
328         xfree(certs);
329 }