1/*	$OpenBSD: filemode.c,v 1.47 2024/06/17 18:54:36 tb 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#include "json.h"
43
44static X509_STORE_CTX	*ctx;
45static struct auth_tree	 auths = RB_INITIALIZER(&auths);
46static struct crl_tree	 crlt = RB_INITIALIZER(&crlt);
47
48struct tal		*talobj[TALSZ_MAX];
49
50struct uripath {
51	RB_ENTRY(uripath)	 entry;
52	const char		*uri;
53	struct cert		*cert;
54};
55
56static RB_HEAD(uripath_tree, uripath) uritree;
57
58static inline int
59uripathcmp(const struct uripath *a, const struct uripath *b)
60{
61	return strcmp(a->uri, b->uri);
62}
63
64RB_PROTOTYPE(uripath_tree, uripath, entry, uripathcmp);
65
66static void
67uripath_add(const char *uri, struct cert *cert)
68{
69	struct uripath *up;
70
71	if ((up = calloc(1, sizeof(*up))) == NULL)
72		err(1, NULL);
73	if ((up->uri = strdup(uri)) == NULL)
74		err(1, NULL);
75	up->cert = cert;
76	if (RB_INSERT(uripath_tree, &uritree, up) != NULL)
77		errx(1, "corrupt AIA lookup tree");
78}
79
80static struct cert *
81uripath_lookup(const char *uri)
82{
83	struct uripath needle = { .uri = uri };
84	struct uripath *up;
85
86	up = RB_FIND(uripath_tree, &uritree, &needle);
87	if (up == NULL)
88		return NULL;
89	return up->cert;
90}
91
92RB_GENERATE(uripath_tree, uripath, entry, uripathcmp);
93
94/*
95 * Use the X509 CRL Distribution Points to locate the CRL needed for
96 * verification.
97 */
98static void
99parse_load_crl(char *uri)
100{
101	struct crl *crl;
102	char *f;
103	size_t flen;
104
105	if (uri == NULL)
106		return;
107	if (strncmp(uri, RSYNC_PROTO, RSYNC_PROTO_LEN) != 0) {
108		warnx("bad CRL distribution point URI %s", uri);
109		return;
110	}
111	uri += RSYNC_PROTO_LEN;
112
113	f = load_file(uri, &flen);
114	if (f == NULL) {
115		warn("parse file %s", uri);
116		return;
117	}
118
119	crl = crl_parse(uri, f, flen);
120	if (crl != NULL && !crl_insert(&crlt, crl))
121		crl_free(crl);
122
123	free(f);
124}
125
126/*
127 * Parse the cert pointed at by the AIA URI while doing that also load
128 * the CRL of this cert. While the CRL is validated the returned cert
129 * is not. The caller needs to make sure it is validated once all
130 * necessary certs were loaded. Returns NULL on failure.
131 */
132static struct cert *
133parse_load_cert(char *uri)
134{
135	struct cert *cert = NULL;
136	char *f;
137	size_t flen;
138
139	if (uri == NULL)
140		return NULL;
141
142	if (strncmp(uri, RSYNC_PROTO, RSYNC_PROTO_LEN) != 0) {
143		warnx("bad authority information access URI %s", uri);
144		return NULL;
145	}
146	uri += RSYNC_PROTO_LEN;
147
148	f = load_file(uri, &flen);
149	if (f == NULL) {
150		warn("parse file %s", uri);
151		goto done;
152	}
153
154	cert = cert_parse_pre(uri, f, flen);
155	free(f);
156
157	if (cert == NULL)
158		goto done;
159	if (cert->purpose != CERT_PURPOSE_CA) {
160		warnx("AIA reference to %s in %s",
161		    purpose2str(cert->purpose), uri);
162		goto done;
163	}
164	/* try to load the CRL of this cert */
165	parse_load_crl(cert->crl);
166
167	return cert;
168
169 done:
170	cert_free(cert);
171	return NULL;
172}
173
174/*
175 * Build the certificate chain by using the Authority Information Access.
176 * This requires that the TA are already validated and added to the auths
177 * tree. Once the TA is located in the chain the chain is validated in
178 * reverse order.
179 */
180static struct auth *
181parse_load_certchain(char *uri)
182{
183	struct cert *stack[MAX_CERT_DEPTH] = { 0 };
184	char *filestack[MAX_CERT_DEPTH];
185	struct cert *cert;
186	struct crl *crl;
187	struct auth *a;
188	const char *errstr;
189	int i;
190
191	for (i = 0; i < MAX_CERT_DEPTH; i++) {
192		if ((cert = uripath_lookup(uri)) != NULL) {
193			a = auth_find(&auths, cert->certid);
194			if (a == NULL) {
195				warnx("failed to find issuer for %s", uri);
196				goto fail;
197			}
198			break;
199		}
200		filestack[i] = uri;
201		stack[i] = cert = parse_load_cert(uri);
202		if (cert == NULL || cert->purpose != CERT_PURPOSE_CA) {
203			warnx("failed to build authority chain: %s", uri);
204			goto fail;
205		}
206		uri = cert->aia;
207	}
208
209	if (i >= MAX_CERT_DEPTH) {
210		warnx("authority chain exceeds max depth of %d",
211		    MAX_CERT_DEPTH);
212		goto fail;
213	}
214
215	/* TA found play back the stack and add all certs */
216	for (; i > 0; i--) {
217		cert = stack[i - 1];
218		uri = filestack[i - 1];
219
220		crl = crl_get(&crlt, a);
221		if (!valid_x509(uri, ctx, cert->x509, a, crl, &errstr) ||
222		    !valid_cert(uri, a, cert)) {
223			if (errstr != NULL)
224				warnx("%s: %s", uri, errstr);
225			goto fail;
226		}
227		cert->talid = a->cert->talid;
228		a = auth_insert(uri, &auths, cert, a);
229		uripath_add(uri, cert);
230		stack[i - 1] = NULL;
231	}
232
233	return a;
234fail:
235	for (i = 0; i < MAX_CERT_DEPTH; i++)
236		cert_free(stack[i]);
237	return NULL;
238}
239
240static void
241parse_load_ta(struct tal *tal)
242{
243	const char *filename;
244	struct cert *cert;
245	unsigned char *f = NULL;
246	char *file;
247	size_t flen, i;
248
249	/* does not matter which URI, all end with same filename */
250	filename = strrchr(tal->uri[0], '/');
251	assert(filename);
252
253	if (asprintf(&file, "ta/%s%s", tal->descr, filename) == -1)
254		err(1, NULL);
255
256	f = load_file(file, &flen);
257	if (f == NULL) {
258		warn("parse file %s", file);
259		goto out;
260	}
261
262	/* Extract certificate data. */
263	cert = cert_parse_pre(file, f, flen);
264	cert = ta_parse(file, cert, tal->pkey, tal->pkeysz);
265	if (cert == NULL)
266		goto out;
267
268	cert->talid = tal->id;
269	auth_insert(file, &auths, cert, NULL);
270	for (i = 0; i < tal->urisz; i++) {
271		if (strncasecmp(tal->uri[i], RSYNC_PROTO, RSYNC_PROTO_LEN) != 0)
272			continue;
273		/* Add all rsync uri since any of them could be used as AIA. */
274		uripath_add(tal->uri[i], cert);
275	}
276
277out:
278	free(file);
279	free(f);
280}
281
282static struct tal *
283find_tal(struct cert *cert)
284{
285	EVP_PKEY	*pk, *opk;
286	struct tal	*tal;
287	int		 i;
288
289	if ((opk = X509_get0_pubkey(cert->x509)) == NULL)
290		return NULL;
291
292	for (i = 0; i < TALSZ_MAX; i++) {
293		const unsigned char *pkey;
294
295		if (talobj[i] == NULL)
296			break;
297		tal = talobj[i];
298		pkey = tal->pkey;
299		pk = d2i_PUBKEY(NULL, &pkey, tal->pkeysz);
300		if (pk == NULL)
301			continue;
302		if (EVP_PKEY_cmp(pk, opk) == 1) {
303			EVP_PKEY_free(pk);
304			return tal;
305		}
306		EVP_PKEY_free(pk);
307	}
308	return NULL;
309}
310
311static void
312print_signature_path(const char *crl, const char *aia, const struct auth *a)
313{
314	if (crl != NULL)
315		printf("Signature path:           %s\n", crl);
316	if (a->cert->mft != NULL)
317		printf("                          %s\n", a->cert->mft);
318	if (aia != NULL)
319		printf("                          %s\n", aia);
320
321	for (; a != NULL; a = a->issuer) {
322		if (a->cert->crl != NULL)
323			printf("                          %s\n", a->cert->crl);
324		if (a->issuer != NULL && a->issuer->cert != NULL &&
325		    a->issuer->cert->mft != NULL)
326			printf("                          %s\n",
327			    a->issuer->cert->mft);
328		if (a->cert->aia != NULL)
329			printf("                          %s\n", a->cert->aia);
330	}
331}
332
333/*
334 * Parse file passed with -f option.
335 */
336static void
337proc_parser_file(char *file, unsigned char *buf, size_t len)
338{
339	static int num;
340	X509 *x509 = NULL;
341	struct aspa *aspa = NULL;
342	struct cert *cert = NULL;
343	struct crl *crl = NULL;
344	struct gbr *gbr = NULL;
345	struct geofeed *geofeed = NULL;
346	struct mft *mft = NULL;
347	struct roa *roa = NULL;
348	struct rsc *rsc = NULL;
349	struct spl *spl = NULL;
350	struct tak *tak = NULL;
351	struct tal *tal = NULL;
352	char *aia = NULL;
353	char *crl_uri = NULL;
354	time_t *expires = NULL, *notafter = NULL;
355	struct auth *a;
356	struct crl *c;
357	const char *errstr = NULL, *valid;
358	int status = 0;
359	char filehash[SHA256_DIGEST_LENGTH];
360	char *hash;
361	enum rtype type;
362	int is_ta = 0;
363
364	if (outformats & FORMAT_JSON) {
365		json_do_start(stdout);
366	} else {
367		if (num++ > 0)
368			printf("--\n");
369	}
370
371	if (strncmp(file, RSYNC_PROTO, RSYNC_PROTO_LEN) == 0) {
372		file += RSYNC_PROTO_LEN;
373		buf = load_file(file, &len);
374		if (buf == NULL) {
375			warn("parse file %s", file);
376			return;
377		}
378	}
379
380	if (!EVP_Digest(buf, len, filehash, NULL, EVP_sha256(), NULL))
381		errx(1, "EVP_Digest failed in %s", __func__);
382
383	if (base64_encode(filehash, sizeof(filehash), &hash) == -1)
384		errx(1, "base64_encode failed in %s", __func__);
385
386	if (outformats & FORMAT_JSON) {
387		json_do_string("file", file);
388		json_do_string("hash_id", hash);
389	} else {
390		printf("File:                     %s\n", file);
391		printf("Hash identifier:          %s\n", hash);
392	}
393
394	free(hash);
395
396	type = rtype_from_file_extension(file);
397
398	switch (type) {
399	case RTYPE_ASPA:
400		aspa = aspa_parse(&x509, file, -1, buf, len);
401		if (aspa == NULL)
402			break;
403		aia = aspa->aia;
404		expires = &aspa->expires;
405		notafter = &aspa->notafter;
406		break;
407	case RTYPE_CER:
408		cert = cert_parse_pre(file, buf, len);
409		if (cert == NULL)
410			break;
411		is_ta = (cert->purpose == CERT_PURPOSE_TA);
412		if (!is_ta)
413			cert = cert_parse(file, cert);
414		if (cert == NULL)
415			break;
416		aia = cert->aia;
417		x509 = cert->x509;
418		if (X509_up_ref(x509) == 0)
419			errx(1, "%s: X509_up_ref failed", __func__);
420		expires = &cert->expires;
421		notafter = &cert->notafter;
422		break;
423	case RTYPE_CRL:
424		crl = crl_parse(file, buf, len);
425		if (crl == NULL)
426			break;
427		crl_print(crl);
428		break;
429	case RTYPE_MFT:
430		mft = mft_parse(&x509, file, -1, buf, len);
431		if (mft == NULL)
432			break;
433		aia = mft->aia;
434		expires = &mft->expires;
435		notafter = &mft->nextupdate;
436		break;
437	case RTYPE_GBR:
438		gbr = gbr_parse(&x509, file, -1, buf, len);
439		if (gbr == NULL)
440			break;
441		aia = gbr->aia;
442		expires = &gbr->expires;
443		notafter = &gbr->notafter;
444		break;
445	case RTYPE_GEOFEED:
446		geofeed = geofeed_parse(&x509, file, -1, buf, len);
447		if (geofeed == NULL)
448			break;
449		aia = geofeed->aia;
450		expires = &geofeed->expires;
451		notafter = &geofeed->notafter;
452		break;
453	case RTYPE_ROA:
454		roa = roa_parse(&x509, file, -1, buf, len);
455		if (roa == NULL)
456			break;
457		aia = roa->aia;
458		expires = &roa->expires;
459		notafter = &roa->notafter;
460		break;
461	case RTYPE_RSC:
462		rsc = rsc_parse(&x509, file, -1, buf, len);
463		if (rsc == NULL)
464			break;
465		aia = rsc->aia;
466		expires = &rsc->expires;
467		notafter = &rsc->notafter;
468		break;
469	case RTYPE_SPL:
470		spl = spl_parse(&x509, file, -1, buf, len);
471		if (spl == NULL)
472			break;
473		aia = spl->aia;
474		expires = &spl->expires;
475		notafter = &spl->notafter;
476		break;
477	case RTYPE_TAK:
478		tak = tak_parse(&x509, file, -1, buf, len);
479		if (tak == NULL)
480			break;
481		aia = tak->aia;
482		expires = &tak->expires;
483		notafter = &tak->notafter;
484		break;
485	case RTYPE_TAL:
486		tal = tal_parse(file, buf, len);
487		if (tal == NULL)
488			break;
489		tal_print(tal);
490		break;
491	default:
492		printf("%s: unsupported file type\n", file);
493		break;
494	}
495
496	if (aia != NULL) {
497		x509_get_crl(x509, file, &crl_uri);
498		parse_load_crl(crl_uri);
499		a = parse_load_certchain(aia);
500		c = crl_get(&crlt, a);
501
502		if ((status = valid_x509(file, ctx, x509, a, c, &errstr))) {
503			switch (type) {
504			case RTYPE_ASPA:
505				status = aspa->valid;
506				break;
507			case RTYPE_GEOFEED:
508				status = geofeed->valid;
509				break;
510			case RTYPE_ROA:
511				status = roa->valid;
512				break;
513			case RTYPE_RSC:
514				status = rsc->valid;
515				break;
516			case RTYPE_SPL:
517				status = spl->valid;
518			default:
519				break;
520			}
521		}
522		if (status && cert == NULL) {
523			struct cert *eecert;
524
525			eecert = cert_parse_ee_cert(file, a->cert->talid, x509);
526			if (eecert == NULL)
527				status = 0;
528			cert_free(eecert);
529		} else if (status) {
530			cert->talid = a->cert->talid;
531			constraints_validate(file, cert);
532		}
533	} else if (is_ta) {
534		expires = NULL;
535		notafter = NULL;
536		if ((tal = find_tal(cert)) != NULL) {
537			cert = ta_parse(file, cert, tal->pkey, tal->pkeysz);
538			status = (cert != NULL);
539			if (status) {
540				expires = &cert->expires;
541				notafter = &cert->notafter;
542			}
543			if (outformats & FORMAT_JSON)
544				json_do_string("tal", tal->descr);
545			else
546				printf("TAL:                      %s\n",
547				    tal->descr);
548			tal = NULL;
549		} else {
550			cert_free(cert);
551			cert = NULL;
552			status = 0;
553		}
554	}
555
556	if (expires != NULL) {
557		if (status && aia != NULL)
558			*expires = x509_find_expires(*notafter, a, &crlt);
559
560		switch (type) {
561		case RTYPE_ASPA:
562			aspa_print(x509, aspa);
563			break;
564		case RTYPE_CER:
565			cert_print(cert);
566			break;
567		case RTYPE_GBR:
568			gbr_print(x509, gbr);
569			break;
570		case RTYPE_GEOFEED:
571			geofeed_print(x509, geofeed);
572			break;
573		case RTYPE_MFT:
574			mft_print(x509, mft);
575			break;
576		case RTYPE_ROA:
577			roa_print(x509, roa);
578			break;
579		case RTYPE_RSC:
580			rsc_print(x509, rsc);
581			break;
582		case RTYPE_SPL:
583			spl_print(x509, spl);
584			break;
585		case RTYPE_TAK:
586			tak_print(x509, tak);
587			break;
588		default:
589			break;
590		}
591	}
592
593	if (status)
594		valid = "OK";
595	else if (aia == NULL)
596		valid = "N/A";
597	else
598		valid = "Failed";
599
600	if (outformats & FORMAT_JSON) {
601		json_do_string("validation", valid);
602		if (errstr != NULL)
603			json_do_string("error", errstr);
604	} else {
605		printf("Validation:               %s", valid);
606		if (errstr != NULL)
607			printf(", %s", errstr);
608	}
609
610	if (outformats & FORMAT_JSON)
611		json_do_finish();
612	else {
613		printf("\n");
614
615		if (status && aia != NULL) {
616			print_signature_path(crl_uri, aia, a);
617			if (expires != NULL)
618				printf("Signature path expires:   %s\n",
619				    time2str(*expires));
620		}
621
622		if (x509 == NULL)
623			goto out;
624		if (type == RTYPE_TAL || type == RTYPE_CRL)
625			goto out;
626
627		if (verbose) {
628			if (!X509_print_fp(stdout, x509))
629				errx(1, "X509_print_fp");
630		}
631
632		if (verbose > 1) {
633			if (!PEM_write_X509(stdout, x509))
634				errx(1, "PEM_write_X509");
635		}
636	}
637
638 out:
639	free(crl_uri);
640	X509_free(x509);
641	aspa_free(aspa);
642	cert_free(cert);
643	crl_free(crl);
644	gbr_free(gbr);
645	geofeed_free(geofeed);
646	mft_free(mft);
647	roa_free(roa);
648	rsc_free(rsc);
649	tak_free(tak);
650	tal_free(tal);
651}
652
653/*
654 * Process a file request, in general don't send anything back.
655 */
656static void
657parse_file(struct entityq *q, struct msgbuf *msgq)
658{
659	struct entity	*entp;
660	struct ibuf	*b;
661	struct tal	*tal;
662	time_t		 dummy = 0;
663
664	while ((entp = TAILQ_FIRST(q)) != NULL) {
665		TAILQ_REMOVE(q, entp, entries);
666
667		switch (entp->type) {
668		case RTYPE_FILE:
669			proc_parser_file(entp->file, entp->data, entp->datasz);
670			break;
671		case RTYPE_TAL:
672			if ((tal = tal_parse(entp->file, entp->data,
673			    entp->datasz)) == NULL)
674				errx(1, "%s: could not parse tal file",
675				    entp->file);
676			tal->id = entp->talid;
677			talobj[tal->id] = tal;
678			parse_load_ta(tal);
679			break;
680		default:
681			errx(1, "unhandled entity type %d", entp->type);
682		}
683
684		b = io_new_buffer();
685		io_simple_buffer(b, &entp->type, sizeof(entp->type));
686		io_simple_buffer(b, &entp->repoid, sizeof(entp->repoid));
687		io_simple_buffer(b, &entp->talid, sizeof(entp->talid));
688		io_str_buffer(b, entp->file);
689		io_simple_buffer(b, &dummy, sizeof(dummy));
690		io_close_buffer(msgq, b);
691		entity_free(entp);
692	}
693}
694
695/*
696 * Process responsible for parsing and validating content.
697 * All this process does is wait to be told about a file to parse, then
698 * it parses it and makes sure that the data being returned is fully
699 * validated and verified.
700 * The process will exit cleanly only when fd is closed.
701 */
702void
703proc_filemode(int fd)
704{
705	struct entityq	 q;
706	struct msgbuf	 msgq;
707	struct pollfd	 pfd;
708	struct entity	*entp;
709	struct ibuf	*b, *inbuf = NULL;
710
711	/* Only allow access to the cache directory. */
712	if (unveil(".", "r") == -1)
713		err(1, "unveil cachedir");
714	if (pledge("stdio rpath", NULL) == -1)
715		err(1, "pledge");
716
717	ERR_load_crypto_strings();
718	OpenSSL_add_all_ciphers();
719	OpenSSL_add_all_digests();
720	x509_init_oid();
721	constraints_parse();
722
723	if ((ctx = X509_STORE_CTX_new()) == NULL)
724		err(1, "X509_STORE_CTX_new");
725	TAILQ_INIT(&q);
726
727	msgbuf_init(&msgq);
728	msgq.fd = fd;
729
730	pfd.fd = fd;
731
732	for (;;) {
733		pfd.events = POLLIN;
734		if (msgq.queued)
735			pfd.events |= POLLOUT;
736
737		if (poll(&pfd, 1, INFTIM) == -1) {
738			if (errno == EINTR)
739				continue;
740			err(1, "poll");
741		}
742		if ((pfd.revents & (POLLERR|POLLNVAL)))
743			errx(1, "poll: bad descriptor");
744
745		/* If the parent closes, return immediately. */
746
747		if ((pfd.revents & POLLHUP))
748			break;
749
750		if ((pfd.revents & POLLIN)) {
751			b = io_buf_read(fd, &inbuf);
752			if (b != NULL) {
753				entp = calloc(1, sizeof(struct entity));
754				if (entp == NULL)
755					err(1, NULL);
756				entity_read_req(b, entp);
757				TAILQ_INSERT_TAIL(&q, entp, entries);
758				ibuf_free(b);
759			}
760		}
761
762		if (pfd.revents & POLLOUT) {
763			switch (msgbuf_write(&msgq)) {
764			case 0:
765				errx(1, "write: connection closed");
766			case -1:
767				err(1, "write");
768			}
769		}
770
771		parse_file(&q, &msgq);
772	}
773
774	msgbuf_clear(&msgq);
775	while ((entp = TAILQ_FIRST(&q)) != NULL) {
776		TAILQ_REMOVE(&q, entp, entries);
777		entity_free(entp);
778	}
779
780	auth_tree_free(&auths);
781	crl_tree_free(&crlt);
782
783	X509_STORE_CTX_free(ctx);
784	ibuf_free(inbuf);
785
786	exit(0);
787}
788