xref: /openbsd-src/usr.sbin/rpki-client/filemode.c (revision ff0e7be1ebbcc809ea8ad2b6dafe215824da9e46)
1 /*	$OpenBSD: filemode.c,v 1.33 2023/05/30 16:02:28 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, 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, 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, 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, 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, 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, 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, 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 	} else if (is_ta) {
472 		if ((tal = find_tal(cert)) != NULL) {
473 			cert = ta_parse(file, cert, tal->pkey, tal->pkeysz);
474 			status = (cert != NULL);
475 			if (outformats & FORMAT_JSON)
476 				json_do_string("tal", tal->descr);
477 			else
478 				printf("TAL:                      %s\n",
479 				    tal->descr);
480 			tal = NULL;
481 		} else {
482 			cert_free(cert);
483 			cert = NULL;
484 			expires = NULL;
485 			status = 0;
486 		}
487 	}
488 
489 	if (expires != NULL) {
490 		if (status && aia != NULL)
491 			*expires = x509_find_expires(*notafter, a, &crlt);
492 
493 		switch (type) {
494 		case RTYPE_ASPA:
495 			aspa_print(x509, aspa);
496 			break;
497 		case RTYPE_CER:
498 			cert_print(cert);
499 			break;
500 		case RTYPE_GBR:
501 			gbr_print(x509, gbr);
502 			break;
503 		case RTYPE_GEOFEED:
504 			geofeed_print(x509, geofeed);
505 			break;
506 		case RTYPE_MFT:
507 			mft_print(x509, mft);
508 			break;
509 		case RTYPE_ROA:
510 			roa_print(x509, roa);
511 			break;
512 		case RTYPE_RSC:
513 			rsc_print(x509, rsc);
514 			break;
515 		case RTYPE_TAK:
516 			tak_print(x509, tak);
517 			break;
518 		default:
519 			break;
520 		}
521 	}
522 
523 	if (status)
524 		valid = "OK";
525 	else if (aia == NULL)
526 		valid = "N/A";
527 	else
528 		valid = "Failed";
529 
530 	if (outformats & FORMAT_JSON) {
531 		json_do_string("validation", valid);
532 		if (errstr != NULL)
533 			json_do_string("error", errstr);
534 	} else {
535 		printf("Validation:               %s", valid);
536 		if (errstr != NULL)
537 			printf(", %s", errstr);
538 	}
539 
540 	if (outformats & FORMAT_JSON)
541 		json_do_finish();
542 	else {
543 		printf("\n");
544 
545 		if (status && aia != NULL) {
546 			print_signature_path(crl_uri, aia, a);
547 			if (expires != NULL)
548 				printf("Signature path expires:   %s\n",
549 				    time2str(*expires));
550 		}
551 
552 		if (x509 == NULL)
553 			goto out;
554 		if (type == RTYPE_TAL || type == RTYPE_CRL)
555 			goto out;
556 
557 		if (verbose) {
558 			if (!X509_print_fp(stdout, x509))
559 				errx(1, "X509_print_fp");
560 		}
561 
562 		if (verbose > 1) {
563 			if (!PEM_write_X509(stdout, x509))
564 				errx(1, "PEM_write_X509");
565 		}
566 	}
567 
568  out:
569 	free(crl_uri);
570 	X509_free(x509);
571 	aspa_free(aspa);
572 	cert_free(cert);
573 	crl_free(crl);
574 	gbr_free(gbr);
575 	geofeed_free(geofeed);
576 	mft_free(mft);
577 	roa_free(roa);
578 	rsc_free(rsc);
579 	tak_free(tak);
580 	tal_free(tal);
581 }
582 
583 /*
584  * Process a file request, in general don't send anything back.
585  */
586 static void
587 parse_file(struct entityq *q, struct msgbuf *msgq)
588 {
589 	struct entity	*entp;
590 	struct ibuf	*b;
591 	struct tal	*tal;
592 	time_t		 dummy = 0;
593 
594 	while ((entp = TAILQ_FIRST(q)) != NULL) {
595 		TAILQ_REMOVE(q, entp, entries);
596 
597 		switch (entp->type) {
598 		case RTYPE_FILE:
599 			proc_parser_file(entp->file, entp->data, entp->datasz);
600 			break;
601 		case RTYPE_TAL:
602 			if ((tal = tal_parse(entp->file, entp->data,
603 			    entp->datasz)) == NULL)
604 				errx(1, "%s: could not parse tal file",
605 				    entp->file);
606 			tal->id = entp->talid;
607 			talobj[tal->id] = tal;
608 			parse_load_ta(tal);
609 			break;
610 		default:
611 			errx(1, "unhandled entity type %d", entp->type);
612 		}
613 
614 		b = io_new_buffer();
615 		io_simple_buffer(b, &entp->type, sizeof(entp->type));
616 		io_simple_buffer(b, &entp->repoid, sizeof(entp->repoid));
617 		io_simple_buffer(b, &entp->talid, sizeof(entp->talid));
618 		io_str_buffer(b, entp->file);
619 		io_simple_buffer(b, &dummy, sizeof(dummy));
620 		io_close_buffer(msgq, b);
621 		entity_free(entp);
622 	}
623 }
624 
625 /*
626  * Process responsible for parsing and validating content.
627  * All this process does is wait to be told about a file to parse, then
628  * it parses it and makes sure that the data being returned is fully
629  * validated and verified.
630  * The process will exit cleanly only when fd is closed.
631  */
632 void
633 proc_filemode(int fd)
634 {
635 	struct entityq	 q;
636 	struct msgbuf	 msgq;
637 	struct pollfd	 pfd;
638 	struct entity	*entp;
639 	struct ibuf	*b, *inbuf = NULL;
640 
641 	/* Only allow access to the cache directory. */
642 	if (unveil(".", "r") == -1)
643 		err(1, "unveil cachedir");
644 	if (pledge("stdio rpath", NULL) == -1)
645 		err(1, "pledge");
646 
647 	ERR_load_crypto_strings();
648 	OpenSSL_add_all_ciphers();
649 	OpenSSL_add_all_digests();
650 	x509_init_oid();
651 
652 	if ((ctx = X509_STORE_CTX_new()) == NULL)
653 		cryptoerrx("X509_STORE_CTX_new");
654 	TAILQ_INIT(&q);
655 
656 	msgbuf_init(&msgq);
657 	msgq.fd = fd;
658 
659 	pfd.fd = fd;
660 
661 	for (;;) {
662 		pfd.events = POLLIN;
663 		if (msgq.queued)
664 			pfd.events |= POLLOUT;
665 
666 		if (poll(&pfd, 1, INFTIM) == -1) {
667 			if (errno == EINTR)
668 				continue;
669 			err(1, "poll");
670 		}
671 		if ((pfd.revents & (POLLERR|POLLNVAL)))
672 			errx(1, "poll: bad descriptor");
673 
674 		/* If the parent closes, return immediately. */
675 
676 		if ((pfd.revents & POLLHUP))
677 			break;
678 
679 		if ((pfd.revents & POLLIN)) {
680 			b = io_buf_read(fd, &inbuf);
681 			if (b != NULL) {
682 				entp = calloc(1, sizeof(struct entity));
683 				if (entp == NULL)
684 					err(1, NULL);
685 				entity_read_req(b, entp);
686 				TAILQ_INSERT_TAIL(&q, entp, entries);
687 				ibuf_free(b);
688 			}
689 		}
690 
691 		if (pfd.revents & POLLOUT) {
692 			switch (msgbuf_write(&msgq)) {
693 			case 0:
694 				errx(1, "write: connection closed");
695 			case -1:
696 				err(1, "write");
697 			}
698 		}
699 
700 		parse_file(&q, &msgq);
701 	}
702 
703 	msgbuf_clear(&msgq);
704 	while ((entp = TAILQ_FIRST(&q)) != NULL) {
705 		TAILQ_REMOVE(&q, entp, entries);
706 		entity_free(entp);
707 	}
708 
709 	auth_tree_free(&auths);
710 	crl_tree_free(&crlt);
711 
712 	X509_STORE_CTX_free(ctx);
713 	ibuf_free(inbuf);
714 
715 	exit(0);
716 }
717