xref: /openbsd-src/usr.sbin/rpki-client/filemode.c (revision 3374c67d44f9b75b98444cbf63020f777792342e)
1 /*	$OpenBSD: filemode.c,v 1.19 2023/01/06 16:06:43 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/pem.h>
38 #include <openssl/x509.h>
39 #include <openssl/x509v3.h>
40 
41 #include "extern.h"
42 
43 extern int		 verbose;
44 
45 static X509_STORE_CTX	*ctx;
46 static struct auth_tree	 auths = RB_INITIALIZER(&auths);
47 static struct crl_tree	 crlt = RB_INITIALIZER(&crlt);
48 
49 struct tal		*talobj[TALSZ_MAX];
50 
51 /*
52  * Use the X509 CRL Distribution Points to locate the CRL needed for
53  * verification.
54  */
55 static void
56 parse_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  */
89 static struct cert *
90 parse_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  */
136 static void
137 parse_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;
187 fail:
188 	for (i = 0; i < MAX_CERT_DEPTH; i++)
189 		cert_free(stack[i]);
190 }
191 
192 static void
193 parse_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);
226 out:
227 	free(file);
228 	free(f);
229 }
230 
231 static struct tal *
232 find_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 
260 /*
261  * Parse file passed with -f option.
262  */
263 static void
264 proc_parser_file(char *file, unsigned char *buf, size_t len)
265 {
266 	static int num;
267 	X509 *x509 = NULL;
268 	struct cert *cert = NULL;
269 	struct crl *crl = NULL;
270 	struct mft *mft = NULL;
271 	struct roa *roa = NULL;
272 	struct gbr *gbr = NULL;
273 	struct tal *tal = NULL;
274 	struct rsc *rsc = NULL;
275 	struct aspa *aspa = NULL;
276 	struct tak *tak = NULL;
277 	struct geofeed *geofeed = NULL;
278 	char *aia = NULL, *aki = NULL;
279 	char filehash[SHA256_DIGEST_LENGTH];
280 	char *hash;
281 	enum rtype type;
282 	int is_ta = 0;
283 
284 	if (num++ > 0) {
285 		if ((outformats & FORMAT_JSON) == 0)
286 			printf("--\n");
287 	}
288 
289 	if (strncmp(file, "rsync://", strlen("rsync://")) == 0) {
290 		file += strlen("rsync://");
291 		buf = load_file(file, &len);
292 		if (buf == NULL) {
293 			warn("parse file %s", file);
294 			return;
295 		}
296 	}
297 
298 	if (!EVP_Digest(buf, len, filehash, NULL, EVP_sha256(), NULL))
299 		errx(1, "EVP_Digest failed in %s", __func__);
300 
301 	if (base64_encode(filehash, sizeof(filehash), &hash) == -1)
302 		errx(1, "base64_encode failed in %s", __func__);
303 
304 	if (outformats & FORMAT_JSON) {
305 		printf("{\n\t\"file\": \"%s\",\n", file);
306 		printf("\t\"hash_id\": \"%s\",\n", hash);
307 	} else {
308 		printf("File:                     %s\n", file);
309 		printf("Hash identifier:          %s\n", hash);
310 	}
311 
312 	free(hash);
313 
314 	type = rtype_from_file_extension(file);
315 
316 	switch (type) {
317 	case RTYPE_CER:
318 		cert = cert_parse_pre(file, buf, len);
319 		if (cert == NULL)
320 			break;
321 		is_ta = X509_get_extension_flags(cert->x509) & EXFLAG_SS;
322 		if (!is_ta)
323 			cert = cert_parse(file, cert);
324 		if (cert == NULL)
325 			break;
326 		cert_print(cert);
327 		aia = cert->aia;
328 		aki = cert->aki;
329 		x509 = cert->x509;
330 		if (X509_up_ref(x509) == 0)
331 			errx(1, "%s: X509_up_ref failed", __func__);
332 		break;
333 	case RTYPE_CRL:
334 		crl = crl_parse(file, buf, len);
335 		if (crl == NULL)
336 			break;
337 		crl_print(crl);
338 		break;
339 	case RTYPE_MFT:
340 		mft = mft_parse(&x509, file, buf, len);
341 		if (mft == NULL)
342 			break;
343 		mft_print(x509, mft);
344 		aia = mft->aia;
345 		aki = mft->aki;
346 		break;
347 	case RTYPE_ROA:
348 		roa = roa_parse(&x509, file, buf, len);
349 		if (roa == NULL)
350 			break;
351 		roa_print(x509, roa);
352 		aia = roa->aia;
353 		aki = roa->aki;
354 		break;
355 	case RTYPE_GBR:
356 		gbr = gbr_parse(&x509, file, buf, len);
357 		if (gbr == NULL)
358 			break;
359 		gbr_print(x509, gbr);
360 		aia = gbr->aia;
361 		aki = gbr->aki;
362 		break;
363 	case RTYPE_TAL:
364 		tal = tal_parse(file, buf, len);
365 		if (tal == NULL)
366 			break;
367 		tal_print(tal);
368 		break;
369 	case RTYPE_RSC:
370 		rsc = rsc_parse(&x509, file, buf, len);
371 		if (rsc == NULL)
372 			break;
373 		rsc_print(x509, rsc);
374 		aia = rsc->aia;
375 		aki = rsc->aki;
376 		break;
377 	case RTYPE_ASPA:
378 		aspa = aspa_parse(&x509, file, buf, len);
379 		if (aspa == NULL)
380 			break;
381 		aspa_print(x509, aspa);
382 		aia = aspa->aia;
383 		aki = aspa->aki;
384 		break;
385 	case RTYPE_TAK:
386 		tak = tak_parse(&x509, file, buf, len);
387 		if (tak == NULL)
388 			break;
389 		tak_print(x509, tak);
390 		aia = tak->aia;
391 		aki = tak->aki;
392 		break;
393 	case RTYPE_GEOFEED:
394 		geofeed = geofeed_parse(&x509, file, buf, len);
395 		if (geofeed == NULL)
396 			break;
397 		geofeed_print(x509, geofeed);
398 		aia = geofeed->aia;
399 		aki = geofeed->aki;
400 		break;
401 	default:
402 		printf("%s: unsupported file type\n", file);
403 		break;
404 	}
405 
406 	if (outformats & FORMAT_JSON)
407 		printf("\t\"validation\": \"");
408 	else
409 		printf("Validation: ");
410 
411 	if (aia != NULL) {
412 		struct auth *a;
413 		struct crl *c;
414 		const char *errstr;
415 		char *crl_uri;
416 		int status;
417 
418 		x509_get_crl(x509, file, &crl_uri);
419 		parse_load_crl(crl_uri);
420 		free(crl_uri);
421 		if (auth_find(&auths, aki) == NULL)
422 			parse_load_certchain(aia);
423 		a = auth_find(&auths, aki);
424 		c = crl_get(&crlt, a);
425 
426 		if ((status = valid_x509(file, ctx, x509, a, c, &errstr))) {
427 			switch (type) {
428 			case RTYPE_ROA:
429 				status = roa->valid;
430 				break;
431 			case RTYPE_RSC:
432 				status = rsc->valid;
433 				break;
434 			case RTYPE_ASPA:
435 				status = aspa->valid;
436 				break;
437 			case RTYPE_GEOFEED:
438 				status = geofeed->valid;
439 				break;
440 			default:
441 				break;
442 			}
443 		}
444 		if (status)
445 			printf("OK");
446 		else {
447 			printf("Failed");
448 			if (errstr != NULL)
449 				printf(", %s", errstr);
450 		}
451 	} else if (is_ta) {
452 		if ((tal = find_tal(cert)) != NULL) {
453 			cert = ta_parse(file, cert, tal->pkey, tal->pkeysz);
454 			if (cert != NULL)
455 				printf("OK");
456 			else
457 				printf("Failed");
458 			if (outformats & FORMAT_JSON)
459 				printf("\",\n\t\"tal\": \"%s", tal->descr);
460 			else
461 				printf("\nTAL: %s", tal->descr);
462 			tal = NULL;
463 		} else {
464 			cert_free(cert);
465 			cert = NULL;
466 			printf("Failed");
467 		}
468 	}
469 
470 	if (outformats & FORMAT_JSON)
471 		printf("\"\n}\n");
472 	else {
473 		printf("\n");
474 
475 		if (x509 == NULL)
476 			goto out;
477 		if (type == RTYPE_TAL || type == RTYPE_CRL)
478 			goto out;
479 
480 		if (verbose) {
481 			if (!X509_print_fp(stdout, x509))
482 				errx(1, "X509_print_fp");
483 		}
484 
485 		if (verbose > 1) {
486 			if (!PEM_write_X509(stdout, x509))
487 				errx(1, "PEM_write_X509");
488 		}
489 	}
490 
491  out:
492 	X509_free(x509);
493 	cert_free(cert);
494 	crl_free(crl);
495 	mft_free(mft);
496 	roa_free(roa);
497 	gbr_free(gbr);
498 	tal_free(tal);
499 	rsc_free(rsc);
500 	aspa_free(aspa);
501 	tak_free(tak);
502 	geofeed_free(geofeed);
503 }
504 
505 /*
506  * Process a file request, in general don't send anything back.
507  */
508 static void
509 parse_file(struct entityq *q, struct msgbuf *msgq)
510 {
511 	struct entity	*entp;
512 	struct ibuf	*b;
513 	struct tal	*tal;
514 
515 	while ((entp = TAILQ_FIRST(q)) != NULL) {
516 		TAILQ_REMOVE(q, entp, entries);
517 
518 		switch (entp->type) {
519 		case RTYPE_FILE:
520 			proc_parser_file(entp->file, entp->data, entp->datasz);
521 			break;
522 		case RTYPE_TAL:
523 			if ((tal = tal_parse(entp->file, entp->data,
524 			    entp->datasz)) == NULL)
525 				errx(1, "%s: could not parse tal file",
526 				    entp->file);
527 			tal->id = entp->talid;
528 			talobj[tal->id] = tal;
529 			parse_load_ta(tal);
530 			break;
531 		default:
532 			errx(1, "unhandled entity type %d", entp->type);
533 		}
534 
535 		b = io_new_buffer();
536 		io_simple_buffer(b, &entp->type, sizeof(entp->type));
537 		io_simple_buffer(b, &entp->repoid, sizeof(entp->repoid));
538 		io_str_buffer(b, entp->file);
539 		io_close_buffer(msgq, b);
540 		entity_free(entp);
541 	}
542 }
543 
544 /*
545  * Process responsible for parsing and validating content.
546  * All this process does is wait to be told about a file to parse, then
547  * it parses it and makes sure that the data being returned is fully
548  * validated and verified.
549  * The process will exit cleanly only when fd is closed.
550  */
551 void
552 proc_filemode(int fd)
553 {
554 	struct entityq	 q;
555 	struct msgbuf	 msgq;
556 	struct pollfd	 pfd;
557 	struct entity	*entp;
558 	struct ibuf	*b, *inbuf = NULL;
559 
560 	/* Only allow access to the cache directory. */
561 	if (unveil(".", "r") == -1)
562 		err(1, "unveil cachedir");
563 	if (pledge("stdio rpath", NULL) == -1)
564 		err(1, "pledge");
565 
566 	ERR_load_crypto_strings();
567 	OpenSSL_add_all_ciphers();
568 	OpenSSL_add_all_digests();
569 	x509_init_oid();
570 
571 	if ((ctx = X509_STORE_CTX_new()) == NULL)
572 		cryptoerrx("X509_STORE_CTX_new");
573 	TAILQ_INIT(&q);
574 
575 	msgbuf_init(&msgq);
576 	msgq.fd = fd;
577 
578 	pfd.fd = fd;
579 
580 	for (;;) {
581 		pfd.events = POLLIN;
582 		if (msgq.queued)
583 			pfd.events |= POLLOUT;
584 
585 		if (poll(&pfd, 1, INFTIM) == -1) {
586 			if (errno == EINTR)
587 				continue;
588 			err(1, "poll");
589 		}
590 		if ((pfd.revents & (POLLERR|POLLNVAL)))
591 			errx(1, "poll: bad descriptor");
592 
593 		/* If the parent closes, return immediately. */
594 
595 		if ((pfd.revents & POLLHUP))
596 			break;
597 
598 		if ((pfd.revents & POLLIN)) {
599 			b = io_buf_read(fd, &inbuf);
600 			if (b != NULL) {
601 				entp = calloc(1, sizeof(struct entity));
602 				if (entp == NULL)
603 					err(1, NULL);
604 				entity_read_req(b, entp);
605 				TAILQ_INSERT_TAIL(&q, entp, entries);
606 				ibuf_free(b);
607 			}
608 		}
609 
610 		if (pfd.revents & POLLOUT) {
611 			switch (msgbuf_write(&msgq)) {
612 			case 0:
613 				errx(1, "write: connection closed");
614 			case -1:
615 				err(1, "write");
616 			}
617 		}
618 
619 		parse_file(&q, &msgq);
620 	}
621 
622 	msgbuf_clear(&msgq);
623 	while ((entp = TAILQ_FIRST(&q)) != NULL) {
624 		TAILQ_REMOVE(&q, entp, entries);
625 		entity_free(entp);
626 	}
627 
628 	auth_tree_free(&auths);
629 	crl_tree_free(&crlt);
630 
631 	X509_STORE_CTX_free(ctx);
632 	ibuf_free(inbuf);
633 
634 	exit(0);
635 }
636