filemode.c revision 1.21
1/*	$OpenBSD: filemode.c,v 1.21 2023/03/07 14:49:32 job Exp $ */
2/*
3 * Copyright (c) 2019 Claudio Jeker <claudio@openbsd.org>
4 * Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv>
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19#include <sys/queue.h>
20#include <sys/tree.h>
21#include <sys/types.h>
22
23#include <assert.h>
24#include <err.h>
25#include <fcntl.h>
26#include <poll.h>
27#include <stdio.h>
28#include <stdlib.h>
29#include <string.h>
30#include <limits.h>
31#include <unistd.h>
32#include <imsg.h>
33
34#include <openssl/asn1.h>
35#include <openssl/err.h>
36#include <openssl/evp.h>
37#include <openssl/pem.h>
38#include <openssl/x509.h>
39#include <openssl/x509v3.h>
40
41#include "extern.h"
42
43extern int		 verbose;
44
45static X509_STORE_CTX	*ctx;
46static struct auth_tree	 auths = RB_INITIALIZER(&auths);
47static struct crl_tree	 crlt = RB_INITIALIZER(&crlt);
48
49struct tal		*talobj[TALSZ_MAX];
50
51/*
52 * Use the X509 CRL Distribution Points to locate the CRL needed for
53 * verification.
54 */
55static void
56parse_load_crl(char *uri)
57{
58	struct crl *crl;
59	char *f;
60	size_t flen;
61
62	if (uri == NULL)
63		return;
64	if (strncmp(uri, "rsync://", strlen("rsync://")) != 0) {
65		warnx("bad CRL distribution point URI %s", uri);
66		return;
67	}
68	uri += strlen("rsync://");
69
70	f = load_file(uri, &flen);
71	if (f == NULL) {
72		warn("parse file %s", uri);
73		return;
74	}
75
76	crl = crl_parse(uri, f, flen);
77	if (crl != NULL && !crl_insert(&crlt, crl))
78		crl_free(crl);
79
80	free(f);
81}
82
83/*
84 * Parse the cert pointed at by the AIA URI while doing that also load
85 * the CRL of this cert. While the CRL is validated the returned cert
86 * is not. The caller needs to make sure it is validated once all
87 * necessary certs were loaded. Returns NULL on failure.
88 */
89static struct cert *
90parse_load_cert(char *uri)
91{
92	struct cert *cert = NULL;
93	char *f;
94	size_t flen;
95
96	if (uri == NULL)
97		return NULL;
98
99	if (strncmp(uri, "rsync://", strlen("rsync://")) != 0) {
100		warnx("bad authority information access URI %s", uri);
101		return NULL;
102	}
103	uri += strlen("rsync://");
104
105	f = load_file(uri, &flen);
106	if (f == NULL) {
107		warn("parse file %s", uri);
108		goto done;
109	}
110
111	cert = cert_parse_pre(uri, f, flen);
112	free(f);
113
114	if (cert == NULL)
115		goto done;
116	if (cert->purpose != CERT_PURPOSE_CA) {
117		warnx("AIA reference to bgpsec cert %s", uri);
118		goto done;
119	}
120	/* try to load the CRL of this cert */
121	parse_load_crl(cert->crl);
122
123	return cert;
124
125 done:
126	cert_free(cert);
127	return NULL;
128}
129
130/*
131 * Build the certificate chain by using the Authority Information Access.
132 * This requires that the TA are already validated and added to the auths
133 * tree. Once the TA is located in the chain the chain is validated in
134 * reverse order.
135 */
136static void
137parse_load_certchain(char *uri)
138{
139	struct cert *stack[MAX_CERT_DEPTH] = { 0 };
140	char *filestack[MAX_CERT_DEPTH];
141	struct cert *cert;
142	struct crl *crl;
143	struct auth *a;
144	const char *errstr;
145	int i;
146
147	for (i = 0; i < MAX_CERT_DEPTH; i++) {
148		filestack[i] = uri;
149		stack[i] = cert = parse_load_cert(uri);
150		if (cert == NULL || cert->purpose != CERT_PURPOSE_CA) {
151			warnx("failed to build authority chain");
152			goto fail;
153		}
154		if (auth_find(&auths, cert->ski) != NULL) {
155			assert(i == 0);
156			goto fail;
157		}
158		if ((a = auth_find(&auths, cert->aki)) != NULL)
159			break;	/* found chain to TA */
160		uri = cert->aia;
161	}
162
163	if (i >= MAX_CERT_DEPTH) {
164		warnx("authority chain exceeds max depth of %d",
165		    MAX_CERT_DEPTH);
166		goto fail;
167	}
168
169	/* TA found play back the stack and add all certs */
170	for (; i >= 0; i--) {
171		cert = stack[i];
172		uri = filestack[i];
173
174		crl = crl_get(&crlt, a);
175		if (!valid_x509(uri, ctx, cert->x509, a, crl, &errstr) ||
176		    !valid_cert(uri, a, cert)) {
177			if (errstr != NULL)
178				warnx("%s: %s", uri, errstr);
179			goto fail;
180		}
181		cert->talid = a->cert->talid;
182		a = auth_insert(&auths, cert, a);
183		stack[i] = NULL;
184	}
185
186	return;
187fail:
188	for (i = 0; i < MAX_CERT_DEPTH; i++)
189		cert_free(stack[i]);
190}
191
192static void
193parse_load_ta(struct tal *tal)
194{
195	const char *filename;
196	struct cert *cert;
197	unsigned char *f = NULL;
198	char *file;
199	size_t flen;
200
201	/* does not matter which URI, all end with same filename */
202	filename = strrchr(tal->uri[0], '/');
203	assert(filename);
204
205	if (asprintf(&file, "ta/%s%s", tal->descr, filename) == -1)
206		err(1, NULL);
207
208	f = load_file(file, &flen);
209	if (f == NULL) {
210		warn("parse file %s", file);
211		goto out;
212	}
213
214	/* Extract certificate data. */
215	cert = cert_parse_pre(file, f, flen);
216	cert = ta_parse(file, cert, tal->pkey, tal->pkeysz);
217	if (cert == NULL)
218		goto out;
219
220	cert->talid = tal->id;
221
222	if (!valid_ta(file, &auths, cert))
223		cert_free(cert);
224	else
225		auth_insert(&auths, cert, NULL);
226out:
227	free(file);
228	free(f);
229}
230
231static struct tal *
232find_tal(struct cert *cert)
233{
234	EVP_PKEY	*pk, *opk;
235	struct tal	*tal;
236	int		 i;
237
238	if ((opk = X509_get0_pubkey(cert->x509)) == NULL)
239		return NULL;
240
241	for (i = 0; i < TALSZ_MAX; i++) {
242		const unsigned char *pkey;
243
244		if (talobj[i] == NULL)
245			break;
246		tal = talobj[i];
247		pkey = tal->pkey;
248		pk = d2i_PUBKEY(NULL, &pkey, tal->pkeysz);
249		if (pk == NULL)
250			continue;
251		if (EVP_PKEY_cmp(pk, opk) == 1) {
252			EVP_PKEY_free(pk);
253			return tal;
254		}
255		EVP_PKEY_free(pk);
256	}
257	return NULL;
258}
259
260static void
261print_certification_path(const char *crl, const char *aia, const struct auth *a)
262{
263	if (crl != NULL)
264		printf("Certification path:       %s\n", crl);
265	if (aia != NULL)
266		printf("                          %s\n", aia);
267
268	for (; a != NULL; a = a->parent) {
269		if (a->cert->crl != NULL)
270			printf("                          %s\n", a->cert->crl);
271		if (a->cert->aia != NULL)
272			printf("                          %s\n", a->cert->aia);
273	}
274}
275
276/*
277 * Parse file passed with -f option.
278 */
279static void
280proc_parser_file(char *file, unsigned char *buf, size_t len)
281{
282	static int num;
283	X509 *x509 = NULL;
284	struct cert *cert = NULL;
285	struct crl *crl = NULL;
286	struct mft *mft = NULL;
287	struct roa *roa = NULL;
288	struct gbr *gbr = NULL;
289	struct tal *tal = NULL;
290	struct rsc *rsc = NULL;
291	struct aspa *aspa = NULL;
292	struct tak *tak = NULL;
293	struct geofeed *geofeed = NULL;
294	char *aia = NULL, *aki = NULL;
295	char filehash[SHA256_DIGEST_LENGTH];
296	char *hash;
297	enum rtype type;
298	int is_ta = 0;
299
300	if (num++ > 0) {
301		if ((outformats & FORMAT_JSON) == 0)
302			printf("--\n");
303	}
304
305	if (strncmp(file, "rsync://", strlen("rsync://")) == 0) {
306		file += strlen("rsync://");
307		buf = load_file(file, &len);
308		if (buf == NULL) {
309			warn("parse file %s", file);
310			return;
311		}
312	}
313
314	if (!EVP_Digest(buf, len, filehash, NULL, EVP_sha256(), NULL))
315		errx(1, "EVP_Digest failed in %s", __func__);
316
317	if (base64_encode(filehash, sizeof(filehash), &hash) == -1)
318		errx(1, "base64_encode failed in %s", __func__);
319
320	if (outformats & FORMAT_JSON) {
321		printf("{\n\t\"file\": \"%s\",\n", file);
322		printf("\t\"hash_id\": \"%s\",\n", hash);
323	} else {
324		printf("File:                     %s\n", file);
325		printf("Hash identifier:          %s\n", hash);
326	}
327
328	free(hash);
329
330	type = rtype_from_file_extension(file);
331
332	switch (type) {
333	case RTYPE_CER:
334		cert = cert_parse_pre(file, buf, len);
335		if (cert == NULL)
336			break;
337		is_ta = X509_get_extension_flags(cert->x509) & EXFLAG_SS;
338		if (!is_ta)
339			cert = cert_parse(file, cert);
340		if (cert == NULL)
341			break;
342		cert_print(cert);
343		aia = cert->aia;
344		aki = cert->aki;
345		x509 = cert->x509;
346		if (X509_up_ref(x509) == 0)
347			errx(1, "%s: X509_up_ref failed", __func__);
348		break;
349	case RTYPE_CRL:
350		crl = crl_parse(file, buf, len);
351		if (crl == NULL)
352			break;
353		crl_print(crl);
354		break;
355	case RTYPE_MFT:
356		mft = mft_parse(&x509, file, buf, len);
357		if (mft == NULL)
358			break;
359		mft_print(x509, mft);
360		aia = mft->aia;
361		aki = mft->aki;
362		break;
363	case RTYPE_ROA:
364		roa = roa_parse(&x509, file, buf, len);
365		if (roa == NULL)
366			break;
367		roa_print(x509, roa);
368		aia = roa->aia;
369		aki = roa->aki;
370		break;
371	case RTYPE_GBR:
372		gbr = gbr_parse(&x509, file, buf, len);
373		if (gbr == NULL)
374			break;
375		gbr_print(x509, gbr);
376		aia = gbr->aia;
377		aki = gbr->aki;
378		break;
379	case RTYPE_TAL:
380		tal = tal_parse(file, buf, len);
381		if (tal == NULL)
382			break;
383		tal_print(tal);
384		break;
385	case RTYPE_RSC:
386		rsc = rsc_parse(&x509, file, buf, len);
387		if (rsc == NULL)
388			break;
389		rsc_print(x509, rsc);
390		aia = rsc->aia;
391		aki = rsc->aki;
392		break;
393	case RTYPE_ASPA:
394		aspa = aspa_parse(&x509, file, buf, len);
395		if (aspa == NULL)
396			break;
397		aspa_print(x509, aspa);
398		aia = aspa->aia;
399		aki = aspa->aki;
400		break;
401	case RTYPE_TAK:
402		tak = tak_parse(&x509, file, buf, len);
403		if (tak == NULL)
404			break;
405		tak_print(x509, tak);
406		aia = tak->aia;
407		aki = tak->aki;
408		break;
409	case RTYPE_GEOFEED:
410		geofeed = geofeed_parse(&x509, file, buf, len);
411		if (geofeed == NULL)
412			break;
413		geofeed_print(x509, geofeed);
414		aia = geofeed->aia;
415		aki = geofeed->aki;
416		break;
417	default:
418		printf("%s: unsupported file type\n", file);
419		break;
420	}
421
422	if (outformats & FORMAT_JSON)
423		printf("\t\"validation\": \"");
424	else {
425		if (type == RTYPE_CRL)
426			printf("Validation:               N/A\n");
427		else
428			printf("Validation: ");
429	}
430
431	if (aia != NULL) {
432		struct auth *a;
433		struct crl *c;
434		const char *errstr;
435		char *crl_uri;
436		int status;
437
438		x509_get_crl(x509, file, &crl_uri);
439		parse_load_crl(crl_uri);
440		if (auth_find(&auths, aki) == NULL)
441			parse_load_certchain(aia);
442		a = auth_find(&auths, aki);
443		c = crl_get(&crlt, a);
444
445		if ((status = valid_x509(file, ctx, x509, a, c, &errstr))) {
446			switch (type) {
447			case RTYPE_ROA:
448				status = roa->valid;
449				break;
450			case RTYPE_RSC:
451				status = rsc->valid;
452				break;
453			case RTYPE_ASPA:
454				status = aspa->valid;
455				break;
456			case RTYPE_GEOFEED:
457				status = geofeed->valid;
458				break;
459			default:
460				break;
461			}
462		}
463		if (status) {
464			if ((outformats & FORMAT_JSON) == 0)
465				printf("              ");
466			printf("OK");
467			if ((outformats & FORMAT_JSON) == 0) {
468				printf("\n");
469				print_certification_path(crl_uri, aia, a);
470			}
471		} else {
472			if ((outformats & FORMAT_JSON) == 0)
473				printf("              ");
474			printf("Failed");
475			if (errstr != NULL)
476				printf(", %s", errstr);
477			if ((outformats & FORMAT_JSON) == 0)
478				printf("\n");
479		}
480		free(crl_uri);
481	} else if (is_ta) {
482		if ((tal = find_tal(cert)) != NULL) {
483			cert = ta_parse(file, cert, tal->pkey, tal->pkeysz);
484			if ((outformats & FORMAT_JSON) == 0)
485				printf("              ");
486			if (cert != NULL)
487				printf("OK");
488			else
489				printf("Failed");
490			if (outformats & FORMAT_JSON)
491				printf("\",\n\t\"tal\": \"%s", tal->descr);
492			else
493				printf("\nTAL:                      %s\n",
494				    tal->descr);
495			tal = NULL;
496		} else {
497			cert_free(cert);
498			cert = NULL;
499			printf("Failed");
500		}
501	}
502
503	if (outformats & FORMAT_JSON)
504		printf("\"\n}\n");
505	else {
506		if (x509 == NULL)
507			goto out;
508		if (type == RTYPE_TAL || type == RTYPE_CRL)
509			goto out;
510
511		if (verbose) {
512			if (!X509_print_fp(stdout, x509))
513				errx(1, "X509_print_fp");
514		}
515
516		if (verbose > 1) {
517			if (!PEM_write_X509(stdout, x509))
518				errx(1, "PEM_write_X509");
519		}
520	}
521
522 out:
523	X509_free(x509);
524	cert_free(cert);
525	crl_free(crl);
526	mft_free(mft);
527	roa_free(roa);
528	gbr_free(gbr);
529	tal_free(tal);
530	rsc_free(rsc);
531	aspa_free(aspa);
532	tak_free(tak);
533	geofeed_free(geofeed);
534}
535
536/*
537 * Process a file request, in general don't send anything back.
538 */
539static void
540parse_file(struct entityq *q, struct msgbuf *msgq)
541{
542	struct entity	*entp;
543	struct ibuf	*b;
544	struct tal	*tal;
545
546	while ((entp = TAILQ_FIRST(q)) != NULL) {
547		TAILQ_REMOVE(q, entp, entries);
548
549		switch (entp->type) {
550		case RTYPE_FILE:
551			proc_parser_file(entp->file, entp->data, entp->datasz);
552			break;
553		case RTYPE_TAL:
554			if ((tal = tal_parse(entp->file, entp->data,
555			    entp->datasz)) == NULL)
556				errx(1, "%s: could not parse tal file",
557				    entp->file);
558			tal->id = entp->talid;
559			talobj[tal->id] = tal;
560			parse_load_ta(tal);
561			break;
562		default:
563			errx(1, "unhandled entity type %d", entp->type);
564		}
565
566		b = io_new_buffer();
567		io_simple_buffer(b, &entp->type, sizeof(entp->type));
568		io_simple_buffer(b, &entp->repoid, sizeof(entp->repoid));
569		io_str_buffer(b, entp->file);
570		io_close_buffer(msgq, b);
571		entity_free(entp);
572	}
573}
574
575/*
576 * Process responsible for parsing and validating content.
577 * All this process does is wait to be told about a file to parse, then
578 * it parses it and makes sure that the data being returned is fully
579 * validated and verified.
580 * The process will exit cleanly only when fd is closed.
581 */
582void
583proc_filemode(int fd)
584{
585	struct entityq	 q;
586	struct msgbuf	 msgq;
587	struct pollfd	 pfd;
588	struct entity	*entp;
589	struct ibuf	*b, *inbuf = NULL;
590
591	/* Only allow access to the cache directory. */
592	if (unveil(".", "r") == -1)
593		err(1, "unveil cachedir");
594	if (pledge("stdio rpath", NULL) == -1)
595		err(1, "pledge");
596
597	ERR_load_crypto_strings();
598	OpenSSL_add_all_ciphers();
599	OpenSSL_add_all_digests();
600	x509_init_oid();
601
602	if ((ctx = X509_STORE_CTX_new()) == NULL)
603		cryptoerrx("X509_STORE_CTX_new");
604	TAILQ_INIT(&q);
605
606	msgbuf_init(&msgq);
607	msgq.fd = fd;
608
609	pfd.fd = fd;
610
611	for (;;) {
612		pfd.events = POLLIN;
613		if (msgq.queued)
614			pfd.events |= POLLOUT;
615
616		if (poll(&pfd, 1, INFTIM) == -1) {
617			if (errno == EINTR)
618				continue;
619			err(1, "poll");
620		}
621		if ((pfd.revents & (POLLERR|POLLNVAL)))
622			errx(1, "poll: bad descriptor");
623
624		/* If the parent closes, return immediately. */
625
626		if ((pfd.revents & POLLHUP))
627			break;
628
629		if ((pfd.revents & POLLIN)) {
630			b = io_buf_read(fd, &inbuf);
631			if (b != NULL) {
632				entp = calloc(1, sizeof(struct entity));
633				if (entp == NULL)
634					err(1, NULL);
635				entity_read_req(b, entp);
636				TAILQ_INSERT_TAIL(&q, entp, entries);
637				ibuf_free(b);
638			}
639		}
640
641		if (pfd.revents & POLLOUT) {
642			switch (msgbuf_write(&msgq)) {
643			case 0:
644				errx(1, "write: connection closed");
645			case -1:
646				err(1, "write");
647			}
648		}
649
650		parse_file(&q, &msgq);
651	}
652
653	msgbuf_clear(&msgq);
654	while ((entp = TAILQ_FIRST(&q)) != NULL) {
655		TAILQ_REMOVE(&q, entp, entries);
656		entity_free(entp);
657	}
658
659	auth_tree_free(&auths);
660	crl_tree_free(&crlt);
661
662	X509_STORE_CTX_free(ctx);
663	ibuf_free(inbuf);
664
665	exit(0);
666}
667