/ tools / files.c
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  }