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 */
34unsigned char *
35read_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 */
73int
74write_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 */
110int
111looks_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
145static void
146vblob_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 */
155void
156free_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 */
165pem_object *
166decode_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 */
249br_x509_certificate *
250read_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 */
320void
321free_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}
330