xref: /openbsd-src/usr.sbin/rpki-client/filemode.c (revision c1a45aed656e7d5627c30c92421893a76f370ccb)
1 /*	$OpenBSD: filemode.c,v 1.2 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 tal		*talobj[TALSZ_MAX];
47 
48 /*
49  * Use the X509 CRL Distribution Points to locate the CRL needed for
50  * verification.
51  */
52 static void
53 parse_load_crl(char *uri)
54 {
55 	struct crl *crl;
56 	char *f;
57 	size_t flen;
58 
59 	if (uri == NULL)
60 		return;
61 	if (strncmp(uri, "rsync://", strlen("rsync://")) != 0) {
62 		warnx("bad CRL distribution point URI %s", uri);
63 		return;
64 	}
65 	uri += strlen("rsync://");
66 
67 	f = load_file(uri, &flen);
68 	if (f == NULL) {
69 		warn("parse file %s", uri);
70 		return;
71 	}
72 
73 	crl = crl_parse(uri, f, flen);
74 	if (crl != NULL && !crl_insert(&crlt, crl))
75 		crl_free(crl);
76 
77 	free(f);
78 }
79 
80 /*
81  * Parse the cert pointed at by the AIA URI while doing that also load
82  * the CRL of this cert. While the CRL is validated the returned cert
83  * is not. The caller needs to make sure it is validated once all
84  * necessary certs were loaded. Returns NULL on failure.
85  */
86 static struct cert *
87 parse_load_cert(char *uri)
88 {
89 	struct cert *cert = NULL;
90 	char *f;
91 	size_t flen;
92 
93 	if (uri == NULL)
94 		return NULL;
95 
96 	if (strncmp(uri, "rsync://", strlen("rsync://")) != 0) {
97 		warnx("bad authority information access URI %s", uri);
98 		return NULL;
99 	}
100 	uri += strlen("rsync://");
101 
102 	f = load_file(uri, &flen);
103 	if (f == NULL) {
104 		warn("parse file %s", uri);
105 		goto done;
106 	}
107 
108 	cert = cert_parse_pre(uri, f, flen);
109 	free(f);
110 
111 	if (cert == NULL)
112 		goto done;
113 	if (cert->purpose != CERT_PURPOSE_CA) {
114 		warnx("AIA reference to bgpsec cert %s", uri);
115 		goto done;
116 	}
117 	/* try to load the CRL of this cert */
118 	parse_load_crl(cert->crl);
119 
120 	return cert;
121 
122  done:
123 	cert_free(cert);
124 	return NULL;
125 }
126 
127 /*
128  * Build the certificate chain by using the Authority Information Access.
129  * This requires that the TA are already validated and added to the auths
130  * tree. Once the TA is located in the chain the chain is validated in
131  * reverse order.
132  */
133 static void
134 parse_load_certchain(char *uri)
135 {
136 	struct cert *stack[MAX_CERT_DEPTH] = { 0 };
137 	char *filestack[MAX_CERT_DEPTH];
138 	struct cert *cert;
139 	struct crl *crl;
140 	struct auth *a;
141 	int i;
142 
143 	for (i = 0; i < MAX_CERT_DEPTH; i++) {
144 		filestack[i] = uri;
145 		stack[i] = cert = parse_load_cert(uri);
146 		if (cert == NULL || cert->purpose != CERT_PURPOSE_CA) {
147 			warnx("failed to build authority chain");
148 			goto fail;
149 		}
150 		if (auth_find(&auths, cert->ski) != NULL) {
151 			assert(i == 0);
152 			goto fail;
153 		}
154 		if ((a = auth_find(&auths, cert->aki)) != NULL)
155 			break;	/* found chain to TA */
156 		uri = cert->aia;
157 	}
158 
159 	if (i >= MAX_CERT_DEPTH) {
160 		warnx("authority chain exceeds max depth of %d",
161 		    MAX_CERT_DEPTH);
162 		goto fail;
163 	}
164 
165 	/* TA found play back the stack and add all certs */
166 	for (; i >= 0; i--) {
167 		cert = stack[i];
168 		uri = filestack[i];
169 
170 		crl = crl_get(&crlt, a);
171 		if (!valid_x509(uri, ctx, cert->x509, a, crl, 0) ||
172 		    !valid_cert(uri, a, cert))
173 			goto fail;
174 		cert->talid = a->cert->talid;
175 		a = auth_insert(&auths, cert, a);
176 		stack[i] = NULL;
177 	}
178 
179 	return;
180 fail:
181 	for (i = 0; i < MAX_CERT_DEPTH; i++)
182 		cert_free(stack[i]);
183 }
184 
185 static void
186 parse_load_ta(struct tal *tal)
187 {
188 	const char *filename;
189 	struct cert *cert;
190 	unsigned char *f = NULL;
191 	char *file;
192 	size_t flen;
193 
194 	/* does not matter which URI, all end with same filename */
195 	filename = strrchr(tal->uri[0], '/');
196 	assert(filename);
197 
198 	if (asprintf(&file, "ta/%s%s", tal->descr, filename) == -1)
199 		err(1, NULL);
200 
201 	f = load_file(file, &flen);
202 	if (f == NULL) {
203 		warn("parse file %s", file);
204 		goto out;
205 	}
206 
207 	/* Extract certificate data. */
208 	cert = cert_parse_pre(file, f, flen);
209 	cert = ta_parse(file, cert, tal->pkey, tal->pkeysz);
210 	if (cert == NULL)
211 		goto out;
212 
213 	cert->talid = tal->id;
214 
215 	if (!valid_ta(file, &auths, cert))
216 		cert_free(cert);
217 	else
218 		auth_insert(&auths, cert, NULL);
219 out:
220 	free(file);
221 	free(f);
222 }
223 
224 static struct tal *
225 find_tal(struct cert *cert)
226 {
227 	EVP_PKEY	*pk, *opk;
228 	struct tal	*tal;
229 	int		 i;
230 
231 	if ((opk = X509_get0_pubkey(cert->x509)) == NULL)
232 		return NULL;
233 
234 	for (i = 0; i < TALSZ_MAX; i++) {
235 		const unsigned char *pkey;
236 
237 		if (talobj[i] == NULL)
238 			break;
239 		tal = talobj[i];
240 		pkey = tal->pkey;
241 		pk = d2i_PUBKEY(NULL, &pkey, tal->pkeysz);
242 		if (pk == NULL)
243 			continue;
244 		if (EVP_PKEY_cmp(pk, opk) == 1) {
245 			EVP_PKEY_free(pk);
246 			return tal;
247 		}
248 		EVP_PKEY_free(pk);
249 	}
250 	return NULL;
251 }
252 
253 /*
254  * Parse file passed with -f option.
255  */
256 static void
257 proc_parser_file(char *file, unsigned char *buf, size_t len)
258 {
259 	static int num;
260 	X509 *x509 = NULL;
261 	struct cert *cert = NULL;
262 	struct crl *crl = NULL;
263 	struct mft *mft = NULL;
264 	struct roa *roa = NULL;
265 	struct gbr *gbr = NULL;
266 	struct tal *tal = NULL;
267 	char *aia = NULL, *aki = NULL;
268 	enum rtype type;
269 	int is_ta = 0;
270 
271 	if (num++ > 0) {
272 		if (outformats & FORMAT_JSON)
273 			printf("\n");
274 		else
275 			printf("--\n");
276 	}
277 
278 	if (strncmp(file, "rsync://", strlen("rsync://")) == 0) {
279 		file += strlen("rsync://");
280 		buf = load_file(file, &len);
281 		if (buf == NULL) {
282 			warn("parse file %s", file);
283 			return;
284 		}
285 	}
286 
287 	if (outformats & FORMAT_JSON)
288 		printf("{\n\t\"file\": \"%s\",\n", file);
289 	else
290 		printf("File: %s\n", file);
291 
292 	type = rtype_from_file_extension(file);
293 
294 	switch (type) {
295 	case RTYPE_CER:
296 		cert = cert_parse_pre(file, buf, len);
297 		if (cert == NULL)
298 			break;
299 		is_ta = X509_get_extension_flags(cert->x509) & EXFLAG_SS;
300 		if (!is_ta)
301 			cert = cert_parse(file, cert);
302 		if (cert == NULL)
303 			break;
304 		cert_print(cert);
305 		aia = cert->aia;
306 		aki = cert->aki;
307 		x509 = cert->x509;
308 		if (X509_up_ref(x509) == 0)
309 			errx(1, "%s: X509_up_ref failed", __func__);
310 		break;
311 	case RTYPE_CRL:
312 		crl = crl_parse(file, buf, len);
313 		if (crl == NULL)
314 			break;
315 		crl_print(crl);
316 		break;
317 	case RTYPE_MFT:
318 		mft = mft_parse(&x509, file, buf, len);
319 		if (mft == NULL)
320 			break;
321 		mft_print(x509, mft);
322 		aia = mft->aia;
323 		aki = mft->aki;
324 		break;
325 	case RTYPE_ROA:
326 		roa = roa_parse(&x509, file, buf, len);
327 		if (roa == NULL)
328 			break;
329 		roa_print(x509, roa);
330 		aia = roa->aia;
331 		aki = roa->aki;
332 		break;
333 	case RTYPE_GBR:
334 		gbr = gbr_parse(&x509, file, buf, len);
335 		if (gbr == NULL)
336 			break;
337 		gbr_print(x509, gbr);
338 		aia = gbr->aia;
339 		aki = gbr->aki;
340 		break;
341 	case RTYPE_TAL:
342 		tal = tal_parse(file, buf, len);
343 		if (tal == NULL)
344 			break;
345 		tal_print(tal);
346 		break;
347 	default:
348 		printf("%s: unsupported file type\n", file);
349 		break;
350 	}
351 
352 	if (outformats & FORMAT_JSON)
353 		printf("\t\"validation\": \"");
354 	else
355 		printf("Validation: ");
356 
357 	if (aia != NULL) {
358 		struct auth *a;
359 		struct crl *c;
360 		char *crl_uri;
361 
362 		x509_get_crl(x509, file, &crl_uri);
363 		parse_load_crl(crl_uri);
364 		free(crl_uri);
365 		if (auth_find(&auths, aki) == NULL)
366 			parse_load_certchain(aia);
367 		a = auth_find(&auths, aki);
368 		c = crl_get(&crlt, a);
369 
370 		if (valid_x509(file, ctx, x509, a, c, 0))
371 			printf("OK");
372 		else
373 			printf("Failed");
374 	} else if (is_ta) {
375 		if ((tal = find_tal(cert)) != NULL) {
376 			cert = ta_parse(file, cert, tal->pkey, tal->pkeysz);
377 			if (cert != NULL)
378 				printf("OK");
379 			else
380 				printf("Failed");
381 			if (outformats & FORMAT_JSON)
382 				printf("\",\n\t\"tal\": \"%s", tal->descr);
383 			else
384 				printf("\nTAL: %s", tal->descr);
385 			tal = NULL;
386 		} else {
387 			cert_free(cert);
388 			cert = NULL;
389 			printf("Failed");
390 		}
391 	}
392 
393 	if (outformats & FORMAT_JSON)
394 		printf("\"\n}");
395 	else
396 		printf("\n");
397 
398 	X509_free(x509);
399 	cert_free(cert);
400 	crl_free(crl);
401 	mft_free(mft);
402 	roa_free(roa);
403 	gbr_free(gbr);
404 	tal_free(tal);
405 }
406 
407 /*
408  * Process a file request, in general don't send anything back.
409  */
410 static void
411 parse_file(struct entityq *q, struct msgbuf *msgq)
412 {
413 	struct entity	*entp;
414 	struct ibuf	*b;
415 	struct tal	*tal;
416 
417 	while ((entp = TAILQ_FIRST(q)) != NULL) {
418 		TAILQ_REMOVE(q, entp, entries);
419 
420 		switch (entp->type) {
421 		case RTYPE_FILE:
422 			proc_parser_file(entp->file, entp->data, entp->datasz);
423 			break;
424 		case RTYPE_TAL:
425 			if ((tal = tal_parse(entp->file, entp->data,
426 			    entp->datasz)) == NULL)
427 				errx(1, "%s: could not parse tal file",
428 				    entp->file);
429 			tal->id = entp->talid;
430 			talobj[tal->id] = tal;
431 			parse_load_ta(tal);
432 			break;
433 		default:
434 			errx(1, "unhandled entity type %d", entp->type);
435 		}
436 
437 		b = io_new_buffer();
438 		io_simple_buffer(b, &entp->type, sizeof(entp->type));
439 		io_str_buffer(b, entp->file);
440 		io_close_buffer(msgq, b);
441 		entity_free(entp);
442 	}
443 }
444 
445 /*
446  * Process responsible for parsing and validating content.
447  * All this process does is wait to be told about a file to parse, then
448  * it parses it and makes sure that the data being returned is fully
449  * validated and verified.
450  * The process will exit cleanly only when fd is closed.
451  */
452 void
453 proc_filemode(int fd)
454 {
455 	struct entityq	 q;
456 	struct msgbuf	 msgq;
457 	struct pollfd	 pfd;
458 	struct entity	*entp;
459 	struct ibuf	*b, *inbuf = NULL;
460 
461 	/* Only allow access to the cache directory. */
462 	if (unveil(".", "r") == -1)
463 		err(1, "unveil cachedir");
464 	if (pledge("stdio rpath", NULL) == -1)
465 		err(1, "pledge");
466 
467 	ERR_load_crypto_strings();
468 	OpenSSL_add_all_ciphers();
469 	OpenSSL_add_all_digests();
470 	x509_init_oid();
471 
472 	if ((ctx = X509_STORE_CTX_new()) == NULL)
473 		cryptoerrx("X509_STORE_CTX_new");
474 	TAILQ_INIT(&q);
475 
476 	msgbuf_init(&msgq);
477 	msgq.fd = fd;
478 
479 	pfd.fd = fd;
480 
481 	for (;;) {
482 		pfd.events = POLLIN;
483 		if (msgq.queued)
484 			pfd.events |= POLLOUT;
485 
486 		if (poll(&pfd, 1, INFTIM) == -1) {
487 			if (errno == EINTR)
488 				continue;
489 			err(1, "poll");
490 		}
491 		if ((pfd.revents & (POLLERR|POLLNVAL)))
492 			errx(1, "poll: bad descriptor");
493 
494 		/* If the parent closes, return immediately. */
495 
496 		if ((pfd.revents & POLLHUP))
497 			break;
498 
499 		if ((pfd.revents & POLLIN)) {
500 			b = io_buf_read(fd, &inbuf);
501 			if (b != NULL) {
502 				entp = calloc(1, sizeof(struct entity));
503 				if (entp == NULL)
504 					err(1, NULL);
505 				entity_read_req(b, entp);
506 				TAILQ_INSERT_TAIL(&q, entp, entries);
507 				ibuf_free(b);
508 			}
509 		}
510 
511 		if (pfd.revents & POLLOUT) {
512 			switch (msgbuf_write(&msgq)) {
513 			case 0:
514 				errx(1, "write: connection closed");
515 			case -1:
516 				err(1, "write");
517 			}
518 		}
519 
520 		parse_file(&q, &msgq);
521 	}
522 
523 	msgbuf_clear(&msgq);
524 	while ((entp = TAILQ_FIRST(&q)) != NULL) {
525 		TAILQ_REMOVE(&q, entp, entries);
526 		entity_free(entp);
527 	}
528 
529 	/* XXX free auths and crl tree */
530 	X509_STORE_CTX_free(ctx);
531 
532 	exit(0);
533 }
534