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