xref: /openbsd-src/usr.sbin/rpki-client/parser.c (revision c1a45aed656e7d5627c30c92421893a76f370ccb)
1 /*	$OpenBSD: parser.c,v 1.73 2022/04/21 12:59:03 claudio 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/x509.h>
38 #include <openssl/x509v3.h>
39 
40 #include "extern.h"
41 
42 static X509_STORE_CTX	*ctx;
43 static struct auth_tree	 auths = RB_INITIALIZER(&auths);
44 static struct crl_tree	 crlt = RB_INITIALIZER(&crlt);
45 
46 struct parse_repo {
47 	RB_ENTRY(parse_repo)	 entry;
48 	char			*path;
49 	char			*validpath;
50 	unsigned int		 id;
51 };
52 
53 static RB_HEAD(repo_tree, parse_repo)	repos = RB_INITIALIZER(&repos);
54 
55 static inline int
56 repocmp(struct parse_repo *a, struct parse_repo *b)
57 {
58 	return a->id - b->id;
59 }
60 
61 RB_GENERATE_STATIC(repo_tree, parse_repo, entry, repocmp);
62 
63 static struct parse_repo *
64 repo_get(unsigned int id)
65 {
66 	struct parse_repo needle = { .id = id };
67 
68 	return RB_FIND(repo_tree, &repos, &needle);
69 }
70 
71 static void
72 repo_add(unsigned int id, char *path, char *validpath)
73 {
74 	struct parse_repo *rp;
75 
76 	if ((rp = calloc(1, sizeof(*rp))) == NULL)
77 		err(1, NULL);
78 	rp->id = id;
79 	if (path != NULL)
80 		if ((rp->path = strdup(path)) == NULL)
81 			err(1, NULL);
82 	if (validpath != NULL)
83 		if ((rp->validpath = strdup(validpath)) == NULL)
84 			err(1, NULL);
85 
86 	if (RB_INSERT(repo_tree, &repos, rp) != NULL)
87 		errx(1, "repository already added: id %d, %s", id, path);
88 }
89 
90 /*
91  * Build access path to file based on repoid, path, location and file values.
92  */
93 static char *
94 parse_filepath(unsigned int repoid, const char *path, const char *file,
95     enum location loc)
96 {
97 	struct parse_repo	*rp;
98 	char			*fn, *repopath;
99 
100 	/* build file path based on repoid, entity path and filename */
101 	rp = repo_get(repoid);
102 	if (rp == NULL)
103 		errx(1, "build file path: repository %u missing", repoid);
104 
105 	if (loc == DIR_VALID)
106 		repopath = rp->validpath;
107 	else
108 		repopath = rp->path;
109 
110 	if (repopath == NULL)
111 		return NULL;
112 
113 	if (path == NULL) {
114 		if (asprintf(&fn, "%s/%s", repopath, file) == -1)
115 			err(1, NULL);
116 	} else {
117 		if (asprintf(&fn, "%s/%s/%s", repopath, path, file) == -1)
118 			err(1, NULL);
119 	}
120 	return fn;
121 }
122 
123 /*
124  * Parse and validate a ROA.
125  * This is standard stuff.
126  * Returns the roa on success, NULL on failure.
127  */
128 static struct roa *
129 proc_parser_roa(char *file, const unsigned char *der, size_t len)
130 {
131 	struct roa		*roa;
132 	struct auth		*a;
133 	struct crl		*crl;
134 	X509			*x509;
135 
136 	if ((roa = roa_parse(&x509, file, der, len)) == NULL)
137 		return NULL;
138 
139 	a = valid_ski_aki(file, &auths, roa->ski, roa->aki);
140 	crl = crl_get(&crlt, a);
141 
142 	if (!valid_x509(file, ctx, x509, a, crl, 0)) {
143 		X509_free(x509);
144 		roa_free(roa);
145 		return NULL;
146 	}
147 	X509_free(x509);
148 
149 	roa->talid = a->cert->talid;
150 
151 	/*
152 	 * If the ROA isn't valid, we accept it anyway and depend upon
153 	 * the code around roa_read() to check the "valid" field itself.
154 	 */
155 
156 	if (valid_roa(file, a, roa))
157 		roa->valid = 1;
158 
159 	/*
160 	 * Check CRL to figure out the soonest transitive expiry moment
161 	 */
162 	if (crl != NULL && roa->expires > crl->expires)
163 		roa->expires = crl->expires;
164 
165 	/*
166 	 * Scan the cert tree to figure out the soonest transitive
167 	 * expiry moment
168 	 */
169 	for (; a != NULL; a = a->parent) {
170 		if (roa->expires > a->cert->expires)
171 			roa->expires = a->cert->expires;
172 	}
173 
174 	return roa;
175 }
176 
177 /*
178  * Check all files and their hashes in a MFT structure.
179  * Return zero on failure, non-zero on success.
180  */
181 static int
182 proc_parser_mft_check(const char *fn, struct mft *p)
183 {
184 	const enum location loc[2] = { DIR_TEMP, DIR_VALID };
185 	size_t	 i;
186 	int	 rc = 1;
187 	char	*path;
188 
189 	for (i = 0; i < p->filesz; i++) {
190 		struct mftfile *m = &p->files[i];
191 		int try, fd = -1, noent = 0, valid = 0;
192 		for (try = 0; try < 2 && !valid; try++) {
193 			if ((path = parse_filepath(p->repoid, p->path, m->file,
194 			    loc[try])) == NULL)
195 				continue;
196 			fd = open(path, O_RDONLY);
197 			if (fd == -1 && errno == ENOENT)
198 				noent++;
199 			free(path);
200 
201 			/* remember which path was checked */
202 			m->location = loc[try];
203 			valid = valid_filehash(fd, m->hash, sizeof(m->hash));
204 		}
205 
206 		if (!valid) {
207 			/* silently skip not-existing unknown files */
208 			if (m->type == RTYPE_INVALID && noent == 2)
209 				continue;
210 			warnx("%s: bad message digest for %s", fn, m->file);
211 			rc = 0;
212 			continue;
213 		}
214 	}
215 
216 	return rc;
217 }
218 
219 /*
220  * Load the correct CRL using the info from the MFT.
221  */
222 static struct crl *
223 parse_load_crl_from_mft(struct entity *entp, struct mft *mft, enum location loc)
224 {
225 	struct crl	*crl = NULL;
226 	unsigned char	*f = NULL;
227 	char		*fn = NULL;
228 	size_t		 flen;
229 
230 	while (1) {
231 		fn = parse_filepath(entp->repoid, entp->path, mft->crl, loc);
232 		if (fn == NULL)
233 			goto next;
234 
235 		f = load_file(fn, &flen);
236 		if (f == NULL && errno != ENOENT)
237 			warn("parse file %s", fn);
238 		if (f == NULL)
239 			goto next;
240 		if (!valid_hash(f, flen, mft->crlhash, sizeof(mft->crlhash)))
241 			goto next;
242 		crl = crl_parse(fn, f, flen);
243 next:
244 		free(f);
245 		free(fn);
246 		f = NULL;
247 		fn = NULL;
248 
249 		if (crl != NULL)
250 			return crl;
251 		if (loc == DIR_TEMP)
252 			loc = DIR_VALID;
253 		else
254 			return NULL;
255 	}
256 }
257 
258 /*
259  * Parse and validate a manifest file. Skip checking the fileandhash
260  * this is done in the post check. After this step we know the mft is
261  * valid and can be compared.
262  * Return the mft on success or NULL on failure.
263  */
264 static struct mft *
265 proc_parser_mft_pre(char *file, const unsigned char *der, size_t len,
266     struct entity *entp, enum location loc, struct crl **crl)
267 {
268 	struct mft	*mft;
269 	X509		*x509;
270 	struct auth	*a;
271 
272 	*crl = NULL;
273 	if ((mft = mft_parse(&x509, file, der, len)) == NULL)
274 		return NULL;
275 	*crl = parse_load_crl_from_mft(entp, mft, loc);
276 
277 	a = valid_ski_aki(file, &auths, mft->ski, mft->aki);
278 	if (!valid_x509(file, ctx, x509, a, *crl, 1)) {
279 		X509_free(x509);
280 		mft_free(mft);
281 		crl_free(*crl);
282 		*crl = NULL;
283 		return NULL;
284 	}
285 	X509_free(x509);
286 
287 	mft->repoid = entp->repoid;
288 	return mft;
289 }
290 
291 /*
292  * Do the end of manifest validation.
293  * Return the mft on success or NULL on failure.
294  */
295 static struct mft *
296 proc_parser_mft_post(char *file, struct mft *mft, const char *path)
297 {
298 	/* check that now is not before from */
299 	time_t now = time(NULL);
300 
301 	if (mft == NULL) {
302 		warnx("%s: no valid mft available", file);
303 		return NULL;
304 	}
305 
306 	/* check that now is not before from */
307 	if (now < mft->valid_since) {
308 		warnx("%s: mft not yet valid %s", file,
309 		    time2str(mft->valid_since));
310 		mft->stale = 1;
311 	}
312 	/* check that now is not after until */
313 	if (now > mft->valid_until) {
314 		warnx("%s: mft expired on %s", file,
315 		    time2str(mft->valid_until));
316 		mft->stale = 1;
317 	}
318 
319 	if (path != NULL)
320 		if ((mft->path = strdup(path)) == NULL)
321 			err(1, NULL);
322 
323 	if (!mft->stale)
324 		if (!proc_parser_mft_check(file, mft)) {
325 			mft_free(mft);
326 			return NULL;
327 		}
328 
329 	return mft;
330 }
331 
332 /*
333  * Load the most recent MFT by opening both options and comparing the two.
334  */
335 static char *
336 proc_parser_mft(struct entity *entp, struct mft **mp)
337 {
338 	struct mft	*mft1 = NULL, *mft2 = NULL;
339 	struct crl	*crl, *crl1 = NULL, *crl2 = NULL;
340 	char		*f, *file, *file1, *file2;
341 	size_t		 flen;
342 
343 	*mp = NULL;
344 	file1 = parse_filepath(entp->repoid, entp->path, entp->file, DIR_VALID);
345 	file2 = parse_filepath(entp->repoid, entp->path, entp->file, DIR_TEMP);
346 
347 	if (file1 != NULL) {
348 		f = load_file(file1, &flen);
349 		if (f == NULL && errno != ENOENT)
350 			warn("parse file %s", file1);
351 		mft1 = proc_parser_mft_pre(file1, f, flen, entp, DIR_VALID,
352 		    &crl1);
353 		free(f);
354 	}
355 	if (file2 != NULL) {
356 		f = load_file(file2, &flen);
357 		if (f == NULL && errno != ENOENT)
358 			warn("parse file %s", file2);
359 		mft2 = proc_parser_mft_pre(file2, f, flen, entp, DIR_TEMP,
360 		    &crl2);
361 		free(f);
362 	}
363 
364 	if (mft_compare(mft1, mft2) == 1) {
365 		mft_free(mft2);
366 		crl_free(crl2);
367 		free(file2);
368 		*mp = proc_parser_mft_post(file1, mft1, entp->path);
369 		crl = crl1;
370 		file = file1;
371 	} else {
372 		mft_free(mft1);
373 		crl_free(crl1);
374 		free(file1);
375 		*mp = proc_parser_mft_post(file2, mft2, entp->path);
376 		crl = crl2;
377 		file = file2;
378 	}
379 
380 	if (*mp != NULL) {
381 		if (!crl_insert(&crlt, crl)) {
382 			warnx("%s: duplicate AKI %s", file, crl->aki);
383 			crl_free(crl);
384 		}
385 	} else {
386 		crl_free(crl);
387 	}
388 	return file;
389 }
390 
391 /*
392  * Certificates are from manifests (has a digest and is signed with
393  * another certificate) Parse the certificate, make sure its
394  * signatures are valid (with CRLs), then validate the RPKI content.
395  * This returns a certificate (which must not be freed) or NULL on
396  * parse failure.
397  */
398 static struct cert *
399 proc_parser_cert(char *file, const unsigned char *der, size_t len)
400 {
401 	struct cert	*cert;
402 	struct crl	*crl;
403 	struct auth	*a;
404 
405 	/* Extract certificate data. */
406 
407 	cert = cert_parse_pre(file, der, len);
408 	cert = cert_parse(file, cert);
409 	if (cert == NULL)
410 		return NULL;
411 
412 	a = valid_ski_aki(file, &auths, cert->ski, cert->aki);
413 	crl = crl_get(&crlt, a);
414 
415 	if (!valid_x509(file, ctx, cert->x509, a, crl, 0) ||
416 	    !valid_cert(file, a, cert)) {
417 		cert_free(cert);
418 		return NULL;
419 	}
420 
421 	cert->talid = a->cert->talid;
422 
423 	/*
424 	 * Add validated CA certs to the RPKI auth tree.
425 	 */
426 	if (cert->purpose == CERT_PURPOSE_CA)
427 		auth_insert(&auths, cert, a);
428 
429 	return cert;
430 }
431 
432 /*
433  * Root certificates come from TALs (has a pkey and is self-signed).
434  * Parse the certificate, ensure that its public key matches the
435  * known public key from the TAL, and then validate the RPKI
436  * content.
437  *
438  * This returns a certificate (which must not be freed) or NULL on
439  * parse failure.
440  */
441 static struct cert *
442 proc_parser_root_cert(char *file, const unsigned char *der, size_t len,
443     unsigned char *pkey, size_t pkeysz, int talid)
444 {
445 	struct cert		*cert;
446 
447 	/* Extract certificate data. */
448 
449 	cert = cert_parse_pre(file, der, len);
450 	cert = ta_parse(file, cert, pkey, pkeysz);
451 	if (cert == NULL)
452 		return NULL;
453 
454 	if (!valid_ta(file, &auths, cert)) {
455 		warnx("%s: certificate not a valid ta", file);
456 		cert_free(cert);
457 		return NULL;
458 	}
459 
460 	cert->talid = talid;
461 
462 	/*
463 	 * Add valid roots to the RPKI auth tree.
464 	 */
465 	auth_insert(&auths, cert, NULL);
466 
467 	return cert;
468 }
469 
470 /*
471  * Parse a ghostbuster record
472  */
473 static void
474 proc_parser_gbr(char *file, const unsigned char *der, size_t len)
475 {
476 	struct gbr		*gbr;
477 	X509			*x509;
478 	struct crl		*crl;
479 	struct auth		*a;
480 
481 	if ((gbr = gbr_parse(&x509, file, der, len)) == NULL)
482 		return;
483 
484 	a = valid_ski_aki(file, &auths, gbr->ski, gbr->aki);
485 	crl = crl_get(&crlt, a);
486 
487 	/* return value can be ignored since nothing happens here */
488 	valid_x509(file, ctx, x509, a, crl, 0);
489 
490 	X509_free(x509);
491 	gbr_free(gbr);
492 }
493 
494 /*
495  * Load the file specified by the entity information.
496  */
497 static char *
498 parse_load_file(struct entity *entp, unsigned char **f, size_t *flen)
499 {
500 	char *file;
501 
502 	file = parse_filepath(entp->repoid, entp->path, entp->file,
503 	    entp->location);
504 	if (file == NULL)
505 		errx(1, "no path to file");
506 
507 	*f = load_file(file, flen);
508 	if (*f == NULL)
509 		warn("parse file %s", file);
510 
511 	return file;
512 }
513 
514 /*
515  * Process an entity and responing to parent process.
516  */
517 static void
518 parse_entity(struct entityq *q, struct msgbuf *msgq)
519 {
520 	struct entity	*entp;
521 	struct tal	*tal;
522 	struct cert	*cert;
523 	struct mft	*mft;
524 	struct roa	*roa;
525 	struct ibuf	*b;
526 	unsigned char	*f;
527 	size_t		 flen;
528 	char		*file;
529 	int		 c;
530 
531 	while ((entp = TAILQ_FIRST(q)) != NULL) {
532 		TAILQ_REMOVE(q, entp, entries);
533 
534 		/* handle RTYPE_REPO first */
535 		if (entp->type == RTYPE_REPO) {
536 			repo_add(entp->repoid, entp->path, entp->file);
537 			entity_free(entp);
538 			continue;
539 		}
540 
541 		/* pass back at least type, repoid and filename */
542 		b = io_new_buffer();
543 		io_simple_buffer(b, &entp->type, sizeof(entp->type));
544 
545 		file = NULL;
546 		f = NULL;
547 		switch (entp->type) {
548 		case RTYPE_TAL:
549 			io_str_buffer(b, entp->file);
550 			if ((tal = tal_parse(entp->file, entp->data,
551 			    entp->datasz)) == NULL)
552 				errx(1, "%s: could not parse tal file",
553 				    entp->file);
554 			tal->id = entp->talid;
555 			tal_buffer(b, tal);
556 			tal_free(tal);
557 			break;
558 		case RTYPE_CER:
559 			file = parse_load_file(entp, &f, &flen);
560 			io_str_buffer(b, file);
561 			if (entp->data != NULL)
562 				cert = proc_parser_root_cert(file,
563 				    f, flen, entp->data, entp->datasz,
564 				    entp->talid);
565 			else
566 				cert = proc_parser_cert(file, f, flen);
567 			c = (cert != NULL);
568 			io_simple_buffer(b, &c, sizeof(int));
569 			if (cert != NULL)
570 				cert_buffer(b, cert);
571 			/*
572 			 * The parsed certificate data "cert" is now
573 			 * managed in the "auths" table, so don't free
574 			 * it here.
575 			 */
576 			break;
577 		case RTYPE_CRL:
578 			/*
579 			 * CRLs are already loaded with the MFT so nothing
580 			 * really needs to be done here.
581 			 */
582 			file = parse_filepath(entp->repoid, entp->path,
583 			    entp->file, entp->location);
584 			io_str_buffer(b, file);
585 			break;
586 		case RTYPE_MFT:
587 			file = proc_parser_mft(entp, &mft);
588 			io_str_buffer(b, file);
589 			c = (mft != NULL);
590 			io_simple_buffer(b, &c, sizeof(int));
591 			if (mft != NULL)
592 				mft_buffer(b, mft);
593 			mft_free(mft);
594 			break;
595 		case RTYPE_ROA:
596 			file = parse_load_file(entp, &f, &flen);
597 			io_str_buffer(b, file);
598 			roa = proc_parser_roa(file, f, flen);
599 			c = (roa != NULL);
600 			io_simple_buffer(b, &c, sizeof(int));
601 			if (roa != NULL)
602 				roa_buffer(b, roa);
603 			roa_free(roa);
604 			break;
605 		case RTYPE_GBR:
606 			file = parse_load_file(entp, &f, &flen);
607 			io_str_buffer(b, file);
608 			proc_parser_gbr(file, f, flen);
609 			break;
610 		default:
611 			errx(1, "unhandled entity type %d", entp->type);
612 		}
613 
614 		free(f);
615 		free(file);
616 		io_close_buffer(msgq, b);
617 		entity_free(entp);
618 	}
619 }
620 
621 /*
622  * Process responsible for parsing and validating content.
623  * All this process does is wait to be told about a file to parse, then
624  * it parses it and makes sure that the data being returned is fully
625  * validated and verified.
626  * The process will exit cleanly only when fd is closed.
627  */
628 void
629 proc_parser(int fd)
630 {
631 	struct entityq	 q;
632 	struct msgbuf	 msgq;
633 	struct pollfd	 pfd;
634 	struct entity	*entp;
635 	struct ibuf	*b, *inbuf = NULL;
636 
637 	/* Only allow access to the cache directory. */
638 	if (unveil(".", "r") == -1)
639 		err(1, "unveil cachedir");
640 	if (pledge("stdio rpath", NULL) == -1)
641 		err(1, "pledge");
642 
643 	ERR_load_crypto_strings();
644 	OpenSSL_add_all_ciphers();
645 	OpenSSL_add_all_digests();
646 	x509_init_oid();
647 
648 	if ((ctx = X509_STORE_CTX_new()) == NULL)
649 		cryptoerrx("X509_STORE_CTX_new");
650 
651 	TAILQ_INIT(&q);
652 
653 	msgbuf_init(&msgq);
654 	msgq.fd = fd;
655 
656 	pfd.fd = fd;
657 
658 	for (;;) {
659 		pfd.events = POLLIN;
660 		if (msgq.queued)
661 			pfd.events |= POLLOUT;
662 
663 		if (poll(&pfd, 1, INFTIM) == -1) {
664 			if (errno == EINTR)
665 				continue;
666 			err(1, "poll");
667 		}
668 		if ((pfd.revents & (POLLERR|POLLNVAL)))
669 			errx(1, "poll: bad descriptor");
670 
671 		/* If the parent closes, return immediately. */
672 
673 		if ((pfd.revents & POLLHUP))
674 			break;
675 
676 		if ((pfd.revents & POLLIN)) {
677 			b = io_buf_read(fd, &inbuf);
678 			if (b != NULL) {
679 				entp = calloc(1, sizeof(struct entity));
680 				if (entp == NULL)
681 					err(1, NULL);
682 				entity_read_req(b, entp);
683 				TAILQ_INSERT_TAIL(&q, entp, entries);
684 				ibuf_free(b);
685 			}
686 		}
687 
688 		if (pfd.revents & POLLOUT) {
689 			switch (msgbuf_write(&msgq)) {
690 			case 0:
691 				errx(1, "write: connection closed");
692 			case -1:
693 				err(1, "write");
694 			}
695 		}
696 
697 		parse_entity(&q, &msgq);
698 	}
699 
700 	while ((entp = TAILQ_FIRST(&q)) != NULL) {
701 		TAILQ_REMOVE(&q, entp, entries);
702 		entity_free(entp);
703 	}
704 
705 	/* XXX free auths and crl tree */
706 
707 	X509_STORE_CTX_free(ctx);
708 	msgbuf_clear(&msgq);
709 
710 	exit(0);
711 }
712