xref: /openbsd-src/usr.sbin/rpki-client/filemode.c (revision 39b34cbddaf3e100b37f75daf1c9123bc2cc4cd2)
1*39b34cbdStb /*	$OpenBSD: filemode.c,v 1.57 2024/12/16 13:53:37 tb Exp $ */
2c4a9443cSclaudio /*
3c4a9443cSclaudio  * Copyright (c) 2019 Claudio Jeker <claudio@openbsd.org>
4c4a9443cSclaudio  * Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv>
5c4a9443cSclaudio  *
6c4a9443cSclaudio  * Permission to use, copy, modify, and distribute this software for any
7c4a9443cSclaudio  * purpose with or without fee is hereby granted, provided that the above
8c4a9443cSclaudio  * copyright notice and this permission notice appear in all copies.
9c4a9443cSclaudio  *
10c4a9443cSclaudio  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11c4a9443cSclaudio  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12c4a9443cSclaudio  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13c4a9443cSclaudio  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14c4a9443cSclaudio  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15c4a9443cSclaudio  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16c4a9443cSclaudio  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17c4a9443cSclaudio  */
18c4a9443cSclaudio 
19c4a9443cSclaudio #include <sys/queue.h>
20c4a9443cSclaudio #include <sys/tree.h>
21c4a9443cSclaudio #include <sys/types.h>
22c4a9443cSclaudio 
23c4a9443cSclaudio #include <assert.h>
24c4a9443cSclaudio #include <err.h>
25c4a9443cSclaudio #include <fcntl.h>
26c4a9443cSclaudio #include <poll.h>
27c4a9443cSclaudio #include <stdio.h>
28c4a9443cSclaudio #include <stdlib.h>
29c4a9443cSclaudio #include <string.h>
30c4a9443cSclaudio #include <limits.h>
31c4a9443cSclaudio #include <unistd.h>
32c4a9443cSclaudio #include <imsg.h>
33c4a9443cSclaudio 
34c4a9443cSclaudio #include <openssl/asn1.h>
35c4a9443cSclaudio #include <openssl/err.h>
36c4a9443cSclaudio #include <openssl/evp.h>
37d61b9ff1Sjob #include <openssl/pem.h>
38c4a9443cSclaudio #include <openssl/x509.h>
39c4a9443cSclaudio #include <openssl/x509v3.h>
40c4a9443cSclaudio 
41c4a9443cSclaudio #include "extern.h"
427aabcda0Sclaudio #include "json.h"
43c4a9443cSclaudio 
44318f0572Sjob extern BN_CTX		*bn_ctx;
45318f0572Sjob 
46c4a9443cSclaudio static X509_STORE_CTX	*ctx;
47c4a9443cSclaudio static struct auth_tree	 auths = RB_INITIALIZER(&auths);
48c4a9443cSclaudio static struct crl_tree	 crlt = RB_INITIALIZER(&crlt);
49c4a9443cSclaudio 
50c4a9443cSclaudio struct tal		*talobj[TALSZ_MAX];
51c4a9443cSclaudio 
520bc420b9Sclaudio struct uripath {
530bc420b9Sclaudio 	RB_ENTRY(uripath)	 entry;
540bc420b9Sclaudio 	const char		*uri;
550bc420b9Sclaudio 	struct cert		*cert;
560bc420b9Sclaudio };
570bc420b9Sclaudio 
580bc420b9Sclaudio static RB_HEAD(uripath_tree, uripath) uritree;
590bc420b9Sclaudio 
600bc420b9Sclaudio static inline int
610bc420b9Sclaudio uripathcmp(const struct uripath *a, const struct uripath *b)
620bc420b9Sclaudio {
630bc420b9Sclaudio 	return strcmp(a->uri, b->uri);
640bc420b9Sclaudio }
650bc420b9Sclaudio 
660bc420b9Sclaudio RB_PROTOTYPE(uripath_tree, uripath, entry, uripathcmp);
670bc420b9Sclaudio 
680bc420b9Sclaudio static void
690bc420b9Sclaudio uripath_add(const char *uri, struct cert *cert)
700bc420b9Sclaudio {
710bc420b9Sclaudio 	struct uripath *up;
720bc420b9Sclaudio 
730bc420b9Sclaudio 	if ((up = calloc(1, sizeof(*up))) == NULL)
740bc420b9Sclaudio 		err(1, NULL);
750bc420b9Sclaudio 	if ((up->uri = strdup(uri)) == NULL)
760bc420b9Sclaudio 		err(1, NULL);
770bc420b9Sclaudio 	up->cert = cert;
780bc420b9Sclaudio 	if (RB_INSERT(uripath_tree, &uritree, up) != NULL)
790bc420b9Sclaudio 		errx(1, "corrupt AIA lookup tree");
800bc420b9Sclaudio }
810bc420b9Sclaudio 
820bc420b9Sclaudio static struct cert *
830bc420b9Sclaudio uripath_lookup(const char *uri)
840bc420b9Sclaudio {
850bc420b9Sclaudio 	struct uripath needle = { .uri = uri };
860bc420b9Sclaudio 	struct uripath *up;
870bc420b9Sclaudio 
880bc420b9Sclaudio 	up = RB_FIND(uripath_tree, &uritree, &needle);
890bc420b9Sclaudio 	if (up == NULL)
900bc420b9Sclaudio 		return NULL;
910bc420b9Sclaudio 	return up->cert;
920bc420b9Sclaudio }
930bc420b9Sclaudio 
940bc420b9Sclaudio RB_GENERATE(uripath_tree, uripath, entry, uripathcmp);
950bc420b9Sclaudio 
96c4a9443cSclaudio /*
97c4a9443cSclaudio  * Use the X509 CRL Distribution Points to locate the CRL needed for
98c4a9443cSclaudio  * verification.
99c4a9443cSclaudio  */
100c4a9443cSclaudio static void
101c4a9443cSclaudio parse_load_crl(char *uri)
102c4a9443cSclaudio {
103c4a9443cSclaudio 	struct crl *crl;
104c4a9443cSclaudio 	char *f;
105c4a9443cSclaudio 	size_t flen;
106c4a9443cSclaudio 
107c4a9443cSclaudio 	if (uri == NULL)
108c4a9443cSclaudio 		return;
1090610060dSjob 	if (strncmp(uri, RSYNC_PROTO, RSYNC_PROTO_LEN) != 0) {
110c4a9443cSclaudio 		warnx("bad CRL distribution point URI %s", uri);
111c4a9443cSclaudio 		return;
112c4a9443cSclaudio 	}
1130610060dSjob 	uri += RSYNC_PROTO_LEN;
114c4a9443cSclaudio 
115c4a9443cSclaudio 	f = load_file(uri, &flen);
116c4a9443cSclaudio 	if (f == NULL) {
117c4a9443cSclaudio 		warn("parse file %s", uri);
118c4a9443cSclaudio 		return;
119c4a9443cSclaudio 	}
120c4a9443cSclaudio 
121c4a9443cSclaudio 	crl = crl_parse(uri, f, flen);
122c4a9443cSclaudio 	if (crl != NULL && !crl_insert(&crlt, crl))
123c4a9443cSclaudio 		crl_free(crl);
124c4a9443cSclaudio 
125c4a9443cSclaudio 	free(f);
126c4a9443cSclaudio }
127c4a9443cSclaudio 
128c4a9443cSclaudio /*
129c4a9443cSclaudio  * Parse the cert pointed at by the AIA URI while doing that also load
130c4a9443cSclaudio  * the CRL of this cert. While the CRL is validated the returned cert
131c4a9443cSclaudio  * is not. The caller needs to make sure it is validated once all
132c4a9443cSclaudio  * necessary certs were loaded. Returns NULL on failure.
133c4a9443cSclaudio  */
134c4a9443cSclaudio static struct cert *
135c4a9443cSclaudio parse_load_cert(char *uri)
136c4a9443cSclaudio {
137c4a9443cSclaudio 	struct cert *cert = NULL;
138c4a9443cSclaudio 	char *f;
139c4a9443cSclaudio 	size_t flen;
140c4a9443cSclaudio 
141c4a9443cSclaudio 	if (uri == NULL)
142c4a9443cSclaudio 		return NULL;
143c4a9443cSclaudio 
1440610060dSjob 	if (strncmp(uri, RSYNC_PROTO, RSYNC_PROTO_LEN) != 0) {
145c4a9443cSclaudio 		warnx("bad authority information access URI %s", uri);
146c4a9443cSclaudio 		return NULL;
147c4a9443cSclaudio 	}
1480610060dSjob 	uri += RSYNC_PROTO_LEN;
149c4a9443cSclaudio 
150c4a9443cSclaudio 	f = load_file(uri, &flen);
151c4a9443cSclaudio 	if (f == NULL) {
152c4a9443cSclaudio 		warn("parse file %s", uri);
153c4a9443cSclaudio 		goto done;
154c4a9443cSclaudio 	}
155c4a9443cSclaudio 
156c4a9443cSclaudio 	cert = cert_parse_pre(uri, f, flen);
157c4a9443cSclaudio 	free(f);
158c4a9443cSclaudio 
159c4a9443cSclaudio 	if (cert == NULL)
160c4a9443cSclaudio 		goto done;
161c4a9443cSclaudio 	if (cert->purpose != CERT_PURPOSE_CA) {
162eb6f3761Stb 		warnx("AIA reference to %s in %s",
163eb6f3761Stb 		    purpose2str(cert->purpose), uri);
164c4a9443cSclaudio 		goto done;
165c4a9443cSclaudio 	}
166c4a9443cSclaudio 	/* try to load the CRL of this cert */
167c4a9443cSclaudio 	parse_load_crl(cert->crl);
168c4a9443cSclaudio 
169c4a9443cSclaudio 	return cert;
170c4a9443cSclaudio 
171c4a9443cSclaudio  done:
172c4a9443cSclaudio 	cert_free(cert);
173c4a9443cSclaudio 	return NULL;
174c4a9443cSclaudio }
175c4a9443cSclaudio 
176c4a9443cSclaudio /*
177c4a9443cSclaudio  * Build the certificate chain by using the Authority Information Access.
178c4a9443cSclaudio  * This requires that the TA are already validated and added to the auths
179c4a9443cSclaudio  * tree. Once the TA is located in the chain the chain is validated in
180c4a9443cSclaudio  * reverse order.
181c4a9443cSclaudio  */
1820bc420b9Sclaudio static struct auth *
183c4a9443cSclaudio parse_load_certchain(char *uri)
184c4a9443cSclaudio {
185ad462a11Sclaudio 	struct cert *stack[MAX_CERT_DEPTH] = { 0 };
186c4a9443cSclaudio 	char *filestack[MAX_CERT_DEPTH];
187c4a9443cSclaudio 	struct cert *cert;
188ad462a11Sclaudio 	struct crl *crl;
189ad462a11Sclaudio 	struct auth *a;
190fd7a2857Sclaudio 	const char *errstr;
191ad462a11Sclaudio 	int i;
192c4a9443cSclaudio 
193c4a9443cSclaudio 	for (i = 0; i < MAX_CERT_DEPTH; i++) {
1940bc420b9Sclaudio 		if ((cert = uripath_lookup(uri)) != NULL) {
1950bc420b9Sclaudio 			a = auth_find(&auths, cert->certid);
1961d8c6443Stb 			if (a == NULL) {
1971d8c6443Stb 				warnx("failed to find issuer for %s", uri);
1981d8c6443Stb 				goto fail;
1991d8c6443Stb 			}
2000bc420b9Sclaudio 			break;
2010bc420b9Sclaudio 		}
202ad462a11Sclaudio 		filestack[i] = uri;
203ad462a11Sclaudio 		stack[i] = cert = parse_load_cert(uri);
204ad462a11Sclaudio 		if (cert == NULL || cert->purpose != CERT_PURPOSE_CA) {
2050bc420b9Sclaudio 			warnx("failed to build authority chain: %s", uri);
206ad462a11Sclaudio 			goto fail;
207c4a9443cSclaudio 		}
208c4a9443cSclaudio 		uri = cert->aia;
209c4a9443cSclaudio 	}
210c4a9443cSclaudio 
211c4a9443cSclaudio 	if (i >= MAX_CERT_DEPTH) {
212c4a9443cSclaudio 		warnx("authority chain exceeds max depth of %d",
213c4a9443cSclaudio 		    MAX_CERT_DEPTH);
214ad462a11Sclaudio 		goto fail;
215c4a9443cSclaudio 	}
216c4a9443cSclaudio 
217c4a9443cSclaudio 	/* TA found play back the stack and add all certs */
2180bc420b9Sclaudio 	for (; i > 0; i--) {
2190bc420b9Sclaudio 		cert = stack[i - 1];
2200bc420b9Sclaudio 		uri = filestack[i - 1];
221c4a9443cSclaudio 
222ad462a11Sclaudio 		crl = crl_get(&crlt, a);
223fd7a2857Sclaudio 		if (!valid_x509(uri, ctx, cert->x509, a, crl, &errstr) ||
224fd7a2857Sclaudio 		    !valid_cert(uri, a, cert)) {
225fd7a2857Sclaudio 			if (errstr != NULL)
226fd7a2857Sclaudio 				warnx("%s: %s", uri, errstr);
227ad462a11Sclaudio 			goto fail;
228fd7a2857Sclaudio 		}
229ad462a11Sclaudio 		cert->talid = a->cert->talid;
2300bc420b9Sclaudio 		a = auth_insert(uri, &auths, cert, a);
2310bc420b9Sclaudio 		uripath_add(uri, cert);
23268dbd033Stb 		stack[i - 1] = NULL;
233c4a9443cSclaudio 	}
234ad462a11Sclaudio 
2350bc420b9Sclaudio 	return a;
236ad462a11Sclaudio fail:
237ad462a11Sclaudio 	for (i = 0; i < MAX_CERT_DEPTH; i++)
238ad462a11Sclaudio 		cert_free(stack[i]);
2390bc420b9Sclaudio 	return NULL;
240c4a9443cSclaudio }
241c4a9443cSclaudio 
242c4a9443cSclaudio static void
243c4a9443cSclaudio parse_load_ta(struct tal *tal)
244c4a9443cSclaudio {
245ad462a11Sclaudio 	const char *filename;
246ad462a11Sclaudio 	struct cert *cert;
247ad462a11Sclaudio 	unsigned char *f = NULL;
248ad462a11Sclaudio 	char *file;
2490bc420b9Sclaudio 	size_t flen, i;
250c4a9443cSclaudio 
251c4a9443cSclaudio 	/* does not matter which URI, all end with same filename */
252ad462a11Sclaudio 	filename = strrchr(tal->uri[0], '/');
253ad462a11Sclaudio 	assert(filename);
254c4a9443cSclaudio 
255ad462a11Sclaudio 	if (asprintf(&file, "ta/%s%s", tal->descr, filename) == -1)
256c4a9443cSclaudio 		err(1, NULL);
257c4a9443cSclaudio 
258ad462a11Sclaudio 	f = load_file(file, &flen);
259c4a9443cSclaudio 	if (f == NULL) {
260ad462a11Sclaudio 		warn("parse file %s", file);
261ad462a11Sclaudio 		goto out;
262c4a9443cSclaudio 	}
263c4a9443cSclaudio 
264ad462a11Sclaudio 	/* Extract certificate data. */
265ad462a11Sclaudio 	cert = cert_parse_pre(file, f, flen);
266ad462a11Sclaudio 	cert = ta_parse(file, cert, tal->pkey, tal->pkeysz);
267ad462a11Sclaudio 	if (cert == NULL)
268ad462a11Sclaudio 		goto out;
269ad462a11Sclaudio 
270ad462a11Sclaudio 	cert->talid = tal->id;
2710bc420b9Sclaudio 	auth_insert(file, &auths, cert, NULL);
27230a08502Stb 	for (i = 0; i < tal->num_uris; i++) {
2730bc420b9Sclaudio 		if (strncasecmp(tal->uri[i], RSYNC_PROTO, RSYNC_PROTO_LEN) != 0)
2740bc420b9Sclaudio 			continue;
2750bc420b9Sclaudio 		/* Add all rsync uri since any of them could be used as AIA. */
2760bc420b9Sclaudio 		uripath_add(tal->uri[i], cert);
2770bc420b9Sclaudio 	}
278ad462a11Sclaudio 
279ad462a11Sclaudio out:
280ad462a11Sclaudio 	free(file);
281c4a9443cSclaudio 	free(f);
282c4a9443cSclaudio }
283c4a9443cSclaudio 
284c4a9443cSclaudio static struct tal *
285c4a9443cSclaudio find_tal(struct cert *cert)
286c4a9443cSclaudio {
287c4a9443cSclaudio 	EVP_PKEY	*pk, *opk;
288c4a9443cSclaudio 	struct tal	*tal;
289c4a9443cSclaudio 	int		 i;
290c4a9443cSclaudio 
291c4a9443cSclaudio 	if ((opk = X509_get0_pubkey(cert->x509)) == NULL)
292c4a9443cSclaudio 		return NULL;
293c4a9443cSclaudio 
294c4a9443cSclaudio 	for (i = 0; i < TALSZ_MAX; i++) {
295c4a9443cSclaudio 		const unsigned char *pkey;
296c4a9443cSclaudio 
297c4a9443cSclaudio 		if (talobj[i] == NULL)
298c4a9443cSclaudio 			break;
299c4a9443cSclaudio 		tal = talobj[i];
300c4a9443cSclaudio 		pkey = tal->pkey;
301c4a9443cSclaudio 		pk = d2i_PUBKEY(NULL, &pkey, tal->pkeysz);
302c4a9443cSclaudio 		if (pk == NULL)
303c4a9443cSclaudio 			continue;
304c4a9443cSclaudio 		if (EVP_PKEY_cmp(pk, opk) == 1) {
305c4a9443cSclaudio 			EVP_PKEY_free(pk);
306c4a9443cSclaudio 			return tal;
307c4a9443cSclaudio 		}
308c4a9443cSclaudio 		EVP_PKEY_free(pk);
309c4a9443cSclaudio 	}
310c4a9443cSclaudio 	return NULL;
311c4a9443cSclaudio }
312c4a9443cSclaudio 
313bb47adf3Sjob static void
3149c1f5d6bSjob print_signature_path(const char *crl, const char *aia, const struct auth *a)
315bb47adf3Sjob {
316bb47adf3Sjob 	if (crl != NULL)
3179c1f5d6bSjob 		printf("Signature path:           %s\n", crl);
318a6002f6aStb 	if (a != NULL && a->cert != NULL && a->cert->mft != NULL)
31984418253Sjob 		printf("                          %s\n", a->cert->mft);
320bb47adf3Sjob 	if (aia != NULL)
321bb47adf3Sjob 		printf("                          %s\n", aia);
322bb47adf3Sjob 
323335482abStb 	for (; a != NULL; a = a->issuer) {
324bb47adf3Sjob 		if (a->cert->crl != NULL)
325bb47adf3Sjob 			printf("                          %s\n", a->cert->crl);
326335482abStb 		if (a->issuer != NULL && a->issuer->cert != NULL &&
327335482abStb 		    a->issuer->cert->mft != NULL)
32884418253Sjob 			printf("                          %s\n",
329335482abStb 			    a->issuer->cert->mft);
330bb47adf3Sjob 		if (a->cert->aia != NULL)
331bb47adf3Sjob 			printf("                          %s\n", a->cert->aia);
332bb47adf3Sjob 	}
333bb47adf3Sjob }
334bb47adf3Sjob 
335c4a9443cSclaudio /*
336c4a9443cSclaudio  * Parse file passed with -f option.
337c4a9443cSclaudio  */
338c4a9443cSclaudio static void
339c4a9443cSclaudio proc_parser_file(char *file, unsigned char *buf, size_t len)
340c4a9443cSclaudio {
341c4a9443cSclaudio 	static int num;
342c4a9443cSclaudio 	X509 *x509 = NULL;
3439649a735Sjob 	struct aspa *aspa = NULL;
344c4a9443cSclaudio 	struct cert *cert = NULL;
345c4a9443cSclaudio 	struct crl *crl = NULL;
3469649a735Sjob 	struct gbr *gbr = NULL;
3479649a735Sjob 	struct geofeed *geofeed = NULL;
348c4a9443cSclaudio 	struct mft *mft = NULL;
349c4a9443cSclaudio 	struct roa *roa = NULL;
35004834fbdSjob 	struct rsc *rsc = NULL;
351d4be4cdeSjob 	struct spl *spl = NULL;
352ee2a33daSjob 	struct tak *tak = NULL;
3539649a735Sjob 	struct tal *tal = NULL;
3540bc420b9Sclaudio 	char *aia = NULL;
355a6235ec6Sjob 	char *crl_uri = NULL;
356490bf478Stb 	time_t *notbefore = NULL, *expires = NULL, *notafter = NULL;
357490bf478Stb 	time_t now;
358a6002f6aStb 	struct auth *a = NULL;
359a6235ec6Sjob 	struct crl *c;
3607aabcda0Sclaudio 	const char *errstr = NULL, *valid;
361a6235ec6Sjob 	int status = 0;
362d0b565d2Sjob 	char filehash[SHA256_DIGEST_LENGTH];
363d0b565d2Sjob 	char *hash;
364c4a9443cSclaudio 	enum rtype type;
365c4a9443cSclaudio 	int is_ta = 0;
366c4a9443cSclaudio 
367490bf478Stb 	now = get_current_time();
368490bf478Stb 
3697aabcda0Sclaudio 	if (outformats & FORMAT_JSON) {
3707aabcda0Sclaudio 		json_do_start(stdout);
3717aabcda0Sclaudio 	} else {
3727aabcda0Sclaudio 		if (num++ > 0)
373c4a9443cSclaudio 			printf("--\n");
374c4a9443cSclaudio 	}
375c4a9443cSclaudio 
3760610060dSjob 	if (strncmp(file, RSYNC_PROTO, RSYNC_PROTO_LEN) == 0) {
3770610060dSjob 		file += RSYNC_PROTO_LEN;
378c4a9443cSclaudio 		buf = load_file(file, &len);
379c4a9443cSclaudio 		if (buf == NULL) {
380c4a9443cSclaudio 			warn("parse file %s", file);
381c4a9443cSclaudio 			return;
382c4a9443cSclaudio 		}
383c4a9443cSclaudio 	}
384c4a9443cSclaudio 
385d0b565d2Sjob 	if (!EVP_Digest(buf, len, filehash, NULL, EVP_sha256(), NULL))
386d0b565d2Sjob 		errx(1, "EVP_Digest failed in %s", __func__);
387d0b565d2Sjob 
388d0b565d2Sjob 	if (base64_encode(filehash, sizeof(filehash), &hash) == -1)
389d0b565d2Sjob 		errx(1, "base64_encode failed in %s", __func__);
390d0b565d2Sjob 
391d0b565d2Sjob 	if (outformats & FORMAT_JSON) {
3927aabcda0Sclaudio 		json_do_string("file", file);
3937aabcda0Sclaudio 		json_do_string("hash_id", hash);
394d0b565d2Sjob 	} else {
395c4a9443cSclaudio 		printf("File:                     %s\n", file);
396d0b565d2Sjob 		printf("Hash identifier:          %s\n", hash);
397d0b565d2Sjob 	}
398d0b565d2Sjob 
399d0b565d2Sjob 	free(hash);
400c4a9443cSclaudio 
401c4a9443cSclaudio 	type = rtype_from_file_extension(file);
402c4a9443cSclaudio 
403c4a9443cSclaudio 	switch (type) {
4049649a735Sjob 	case RTYPE_ASPA:
4050636c4d0Stb 		aspa = aspa_parse(&x509, file, -1, buf, len);
4069649a735Sjob 		if (aspa == NULL)
4079649a735Sjob 			break;
4089649a735Sjob 		aia = aspa->aia;
4099c1f5d6bSjob 		expires = &aspa->expires;
410490bf478Stb 		notbefore = &aspa->notbefore;
4119c1f5d6bSjob 		notafter = &aspa->notafter;
4129649a735Sjob 		break;
413c4a9443cSclaudio 	case RTYPE_CER:
414c4a9443cSclaudio 		cert = cert_parse_pre(file, buf, len);
415c4a9443cSclaudio 		if (cert == NULL)
416c4a9443cSclaudio 			break;
4177b62aa6fStb 		is_ta = (cert->purpose == CERT_PURPOSE_TA);
418c4a9443cSclaudio 		if (!is_ta)
419c4a9443cSclaudio 			cert = cert_parse(file, cert);
420c4a9443cSclaudio 		if (cert == NULL)
421c4a9443cSclaudio 			break;
422c4a9443cSclaudio 		aia = cert->aia;
423c4a9443cSclaudio 		x509 = cert->x509;
424c4a9443cSclaudio 		if (X509_up_ref(x509) == 0)
425c4a9443cSclaudio 			errx(1, "%s: X509_up_ref failed", __func__);
426894936b4Sjob 		expires = &cert->expires;
427490bf478Stb 		notbefore = &cert->notbefore;
428894936b4Sjob 		notafter = &cert->notafter;
429c4a9443cSclaudio 		break;
430c4a9443cSclaudio 	case RTYPE_CRL:
431c4a9443cSclaudio 		crl = crl_parse(file, buf, len);
432c4a9443cSclaudio 		if (crl == NULL)
433c4a9443cSclaudio 			break;
434c4a9443cSclaudio 		crl_print(crl);
435c4a9443cSclaudio 		break;
436c4a9443cSclaudio 	case RTYPE_MFT:
4370636c4d0Stb 		mft = mft_parse(&x509, file, -1, buf, len);
438c4a9443cSclaudio 		if (mft == NULL)
439c4a9443cSclaudio 			break;
440c4a9443cSclaudio 		aia = mft->aia;
441894936b4Sjob 		expires = &mft->expires;
442490bf478Stb 		notbefore = &mft->thisupdate;
443894936b4Sjob 		notafter = &mft->nextupdate;
444c4a9443cSclaudio 		break;
445c4a9443cSclaudio 	case RTYPE_GBR:
4460636c4d0Stb 		gbr = gbr_parse(&x509, file, -1, buf, len);
447c4a9443cSclaudio 		if (gbr == NULL)
448c4a9443cSclaudio 			break;
449c4a9443cSclaudio 		aia = gbr->aia;
450894936b4Sjob 		expires = &gbr->expires;
451490bf478Stb 		notbefore = &gbr->notbefore;
452894936b4Sjob 		notafter = &gbr->notafter;
453c4a9443cSclaudio 		break;
4549649a735Sjob 	case RTYPE_GEOFEED:
4550636c4d0Stb 		geofeed = geofeed_parse(&x509, file, -1, buf, len);
4569649a735Sjob 		if (geofeed == NULL)
457c4a9443cSclaudio 			break;
4589649a735Sjob 		aia = geofeed->aia;
459894936b4Sjob 		expires = &geofeed->expires;
460490bf478Stb 		notbefore = &geofeed->notbefore;
461894936b4Sjob 		notafter = &geofeed->notafter;
4629649a735Sjob 		break;
4639649a735Sjob 	case RTYPE_ROA:
4640636c4d0Stb 		roa = roa_parse(&x509, file, -1, buf, len);
4659649a735Sjob 		if (roa == NULL)
4669649a735Sjob 			break;
4679649a735Sjob 		aia = roa->aia;
4689c1f5d6bSjob 		expires = &roa->expires;
469490bf478Stb 		notbefore = &roa->notbefore;
4709c1f5d6bSjob 		notafter = &roa->notafter;
471c4a9443cSclaudio 		break;
47204834fbdSjob 	case RTYPE_RSC:
4730636c4d0Stb 		rsc = rsc_parse(&x509, file, -1, buf, len);
47404834fbdSjob 		if (rsc == NULL)
47504834fbdSjob 			break;
47604834fbdSjob 		aia = rsc->aia;
477894936b4Sjob 		expires = &rsc->expires;
478490bf478Stb 		notbefore = &rsc->notbefore;
479894936b4Sjob 		notafter = &rsc->notafter;
48004834fbdSjob 		break;
481d4be4cdeSjob 	case RTYPE_SPL:
482d4be4cdeSjob 		spl = spl_parse(&x509, file, -1, buf, len);
483d4be4cdeSjob 		if (spl == NULL)
484d4be4cdeSjob 			break;
485d4be4cdeSjob 		aia = spl->aia;
486d4be4cdeSjob 		expires = &spl->expires;
487490bf478Stb 		notbefore = &spl->notbefore;
488d4be4cdeSjob 		notafter = &spl->notafter;
489d4be4cdeSjob 		break;
490ee2a33daSjob 	case RTYPE_TAK:
4910636c4d0Stb 		tak = tak_parse(&x509, file, -1, buf, len);
492ee2a33daSjob 		if (tak == NULL)
493ee2a33daSjob 			break;
494ee2a33daSjob 		aia = tak->aia;
495894936b4Sjob 		expires = &tak->expires;
496490bf478Stb 		notbefore = &tak->notbefore;
497894936b4Sjob 		notafter = &tak->notafter;
498ee2a33daSjob 		break;
4999649a735Sjob 	case RTYPE_TAL:
5009649a735Sjob 		tal = tal_parse(file, buf, len);
5019649a735Sjob 		if (tal == NULL)
502ef3f6f56Sjob 			break;
5039649a735Sjob 		tal_print(tal);
504ef3f6f56Sjob 		break;
505c4a9443cSclaudio 	default:
506c4a9443cSclaudio 		printf("%s: unsupported file type\n", file);
507c4a9443cSclaudio 		break;
508c4a9443cSclaudio 	}
509c4a9443cSclaudio 
510c4a9443cSclaudio 	if (aia != NULL) {
511c4a9443cSclaudio 		x509_get_crl(x509, file, &crl_uri);
512c4a9443cSclaudio 		parse_load_crl(crl_uri);
5130bc420b9Sclaudio 		a = parse_load_certchain(aia);
514c4a9443cSclaudio 		c = crl_get(&crlt, a);
515c4a9443cSclaudio 
516fd7a2857Sclaudio 		if ((status = valid_x509(file, ctx, x509, a, c, &errstr))) {
517a29ddfd5Sjob 			switch (type) {
518a29ddfd5Sjob 			case RTYPE_ASPA:
519a29ddfd5Sjob 				status = aspa->valid;
520a29ddfd5Sjob 				break;
521ef3f6f56Sjob 			case RTYPE_GEOFEED:
522ef3f6f56Sjob 				status = geofeed->valid;
523ef3f6f56Sjob 				break;
5249649a735Sjob 			case RTYPE_ROA:
5259649a735Sjob 				status = roa->valid;
5269649a735Sjob 				break;
5279649a735Sjob 			case RTYPE_RSC:
5289649a735Sjob 				status = rsc->valid;
5299649a735Sjob 				break;
530d4be4cdeSjob 			case RTYPE_SPL:
531d4be4cdeSjob 				status = spl->valid;
532a29ddfd5Sjob 			default:
533a29ddfd5Sjob 				break;
534a29ddfd5Sjob 			}
535e6c729cdSjob 		}
536891d6bceSjob 		if (status && cert == NULL) {
537891d6bceSjob 			struct cert *eecert;
538891d6bceSjob 
539891d6bceSjob 			eecert = cert_parse_ee_cert(file, a->cert->talid, x509);
540891d6bceSjob 			if (eecert == NULL)
541891d6bceSjob 				status = 0;
542891d6bceSjob 			cert_free(eecert);
543891d6bceSjob 		} else if (status) {
544891d6bceSjob 			cert->talid = a->cert->talid;
545ce2ba085Sjob 			constraints_validate(file, cert);
546891d6bceSjob 		}
547c4a9443cSclaudio 	} else if (is_ta) {
548c2f5584bStb 		expires = NULL;
549c2f5584bStb 		notafter = NULL;
550c4a9443cSclaudio 		if ((tal = find_tal(cert)) != NULL) {
551c4a9443cSclaudio 			cert = ta_parse(file, cert, tal->pkey, tal->pkeysz);
552a6235ec6Sjob 			status = (cert != NULL);
553c2f5584bStb 			if (status) {
554c2f5584bStb 				expires = &cert->expires;
555c2f5584bStb 				notafter = &cert->notafter;
556c2f5584bStb 			}
557c4a9443cSclaudio 			if (outformats & FORMAT_JSON)
5587aabcda0Sclaudio 				json_do_string("tal", tal->descr);
559c4a9443cSclaudio 			else
560a6235ec6Sjob 				printf("TAL:                      %s\n",
561bb47adf3Sjob 				    tal->descr);
562c4a9443cSclaudio 			tal = NULL;
563c4a9443cSclaudio 		} else {
564c4a9443cSclaudio 			cert_free(cert);
565c4a9443cSclaudio 			cert = NULL;
566a6235ec6Sjob 			status = 0;
567a6235ec6Sjob 		}
568a6235ec6Sjob 	}
569a6235ec6Sjob 
5709c1f5d6bSjob 	if (expires != NULL) {
571*39b34cbdStb 		if ((status && aia != NULL) || is_ta)
5729c1f5d6bSjob 			*expires = x509_find_expires(*notafter, a, &crlt);
5739c1f5d6bSjob 
5749c1f5d6bSjob 		switch (type) {
5759c1f5d6bSjob 		case RTYPE_ASPA:
5769c1f5d6bSjob 			aspa_print(x509, aspa);
5779c1f5d6bSjob 			break;
578f09fce12Sjob 		case RTYPE_CER:
579f09fce12Sjob 			cert_print(cert);
580f09fce12Sjob 			break;
581894936b4Sjob 		case RTYPE_GBR:
582894936b4Sjob 			gbr_print(x509, gbr);
583894936b4Sjob 			break;
584894936b4Sjob 		case RTYPE_GEOFEED:
585894936b4Sjob 			geofeed_print(x509, geofeed);
586894936b4Sjob 			break;
587894936b4Sjob 		case RTYPE_MFT:
588894936b4Sjob 			mft_print(x509, mft);
589894936b4Sjob 			break;
5909c1f5d6bSjob 		case RTYPE_ROA:
5919c1f5d6bSjob 			roa_print(x509, roa);
5929c1f5d6bSjob 			break;
593894936b4Sjob 		case RTYPE_RSC:
594894936b4Sjob 			rsc_print(x509, rsc);
595894936b4Sjob 			break;
596d4be4cdeSjob 		case RTYPE_SPL:
597d4be4cdeSjob 			spl_print(x509, spl);
598d4be4cdeSjob 			break;
599894936b4Sjob 		case RTYPE_TAK:
600894936b4Sjob 			tak_print(x509, tak);
601894936b4Sjob 			break;
6029c1f5d6bSjob 		default:
6039c1f5d6bSjob 			break;
6049c1f5d6bSjob 		}
6059c1f5d6bSjob 	}
6069c1f5d6bSjob 
607490bf478Stb 	if (status) {
608490bf478Stb 		if (notbefore != NULL && *notbefore > now)
609490bf478Stb 			valid = "Not yet valid";
610490bf478Stb 		else if (notafter != NULL && *notafter < now)
611490bf478Stb 			valid = "Expired";
612490bf478Stb 		else if (expires != NULL && *expires < now)
613490bf478Stb 			valid = "Signature path expired";
614490bf478Stb 		else
6157aabcda0Sclaudio 			valid = "OK";
616490bf478Stb 	} else if (aia == NULL)
6177aabcda0Sclaudio 		valid = "N/A";
6187aabcda0Sclaudio 	else
6197aabcda0Sclaudio 		valid = "Failed";
6207aabcda0Sclaudio 
6217aabcda0Sclaudio 	if (outformats & FORMAT_JSON) {
6227aabcda0Sclaudio 		json_do_string("validation", valid);
6237aabcda0Sclaudio 		if (errstr != NULL)
6247aabcda0Sclaudio 			json_do_string("error", errstr);
6257aabcda0Sclaudio 	} else {
6267aabcda0Sclaudio 		printf("Validation:               %s", valid);
627a6235ec6Sjob 		if (errstr != NULL)
628a6235ec6Sjob 			printf(", %s", errstr);
629c4a9443cSclaudio 	}
630c4a9443cSclaudio 
631c4a9443cSclaudio 	if (outformats & FORMAT_JSON)
6327aabcda0Sclaudio 		json_do_finish();
633d61b9ff1Sjob 	else {
6341e618320Sjob 		printf("\n");
6351e618320Sjob 
636a6002f6aStb 		if (aia != NULL && status) {
6379c1f5d6bSjob 			print_signature_path(crl_uri, aia, a);
6389c1f5d6bSjob 			if (expires != NULL)
6399c1f5d6bSjob 				printf("Signature path expires:   %s\n",
6409c1f5d6bSjob 				    time2str(*expires));
6419c1f5d6bSjob 		}
6421e618320Sjob 
643d61b9ff1Sjob 		if (x509 == NULL)
644d61b9ff1Sjob 			goto out;
645d61b9ff1Sjob 		if (type == RTYPE_TAL || type == RTYPE_CRL)
646d61b9ff1Sjob 			goto out;
647d61b9ff1Sjob 
648d61b9ff1Sjob 		if (verbose) {
649d61b9ff1Sjob 			if (!X509_print_fp(stdout, x509))
650d61b9ff1Sjob 				errx(1, "X509_print_fp");
651d61b9ff1Sjob 		}
652d61b9ff1Sjob 
653d53c6c47Sjob 		if (verbose > 1) {
654d61b9ff1Sjob 			if (!PEM_write_X509(stdout, x509))
655d61b9ff1Sjob 				errx(1, "PEM_write_X509");
656d61b9ff1Sjob 		}
657d61b9ff1Sjob 	}
658d61b9ff1Sjob 
659d61b9ff1Sjob  out:
660a6235ec6Sjob 	free(crl_uri);
661c4a9443cSclaudio 	X509_free(x509);
6629649a735Sjob 	aspa_free(aspa);
663c4a9443cSclaudio 	cert_free(cert);
664c4a9443cSclaudio 	crl_free(crl);
6659649a735Sjob 	gbr_free(gbr);
6669649a735Sjob 	geofeed_free(geofeed);
667c4a9443cSclaudio 	mft_free(mft);
668c4a9443cSclaudio 	roa_free(roa);
669827b2096Sjob 	rsc_free(rsc);
670ee2a33daSjob 	tak_free(tak);
6719649a735Sjob 	tal_free(tal);
672c4a9443cSclaudio }
673c4a9443cSclaudio 
674c4a9443cSclaudio /*
675c4a9443cSclaudio  * Process a file request, in general don't send anything back.
676c4a9443cSclaudio  */
677c4a9443cSclaudio static void
678c4a9443cSclaudio parse_file(struct entityq *q, struct msgbuf *msgq)
679c4a9443cSclaudio {
680c4a9443cSclaudio 	struct entity	*entp;
681c4a9443cSclaudio 	struct ibuf	*b;
682c4a9443cSclaudio 	struct tal	*tal;
68308ac1330Sjob 	time_t		 dummy = 0;
684c4a9443cSclaudio 
685c4a9443cSclaudio 	while ((entp = TAILQ_FIRST(q)) != NULL) {
686c4a9443cSclaudio 		TAILQ_REMOVE(q, entp, entries);
687c4a9443cSclaudio 
688c4a9443cSclaudio 		switch (entp->type) {
689c4a9443cSclaudio 		case RTYPE_FILE:
690c4a9443cSclaudio 			proc_parser_file(entp->file, entp->data, entp->datasz);
691c4a9443cSclaudio 			break;
692c4a9443cSclaudio 		case RTYPE_TAL:
693c4a9443cSclaudio 			if ((tal = tal_parse(entp->file, entp->data,
694c4a9443cSclaudio 			    entp->datasz)) == NULL)
695c4a9443cSclaudio 				errx(1, "%s: could not parse tal file",
696c4a9443cSclaudio 				    entp->file);
697c4a9443cSclaudio 			tal->id = entp->talid;
698c4a9443cSclaudio 			talobj[tal->id] = tal;
699c4a9443cSclaudio 			parse_load_ta(tal);
700c4a9443cSclaudio 			break;
701c4a9443cSclaudio 		default:
702c4a9443cSclaudio 			errx(1, "unhandled entity type %d", entp->type);
703c4a9443cSclaudio 		}
704c4a9443cSclaudio 
705c4a9443cSclaudio 		b = io_new_buffer();
706c4a9443cSclaudio 		io_simple_buffer(b, &entp->type, sizeof(entp->type));
70739c0924aSclaudio 		io_simple_buffer(b, &entp->repoid, sizeof(entp->repoid));
7081fc2657fSclaudio 		io_simple_buffer(b, &entp->talid, sizeof(entp->talid));
709c4a9443cSclaudio 		io_str_buffer(b, entp->file);
71008ac1330Sjob 		io_simple_buffer(b, &dummy, sizeof(dummy));
711c4a9443cSclaudio 		io_close_buffer(msgq, b);
712c4a9443cSclaudio 		entity_free(entp);
713c4a9443cSclaudio 	}
714c4a9443cSclaudio }
715c4a9443cSclaudio 
716c4a9443cSclaudio /*
717c4a9443cSclaudio  * Process responsible for parsing and validating content.
718c4a9443cSclaudio  * All this process does is wait to be told about a file to parse, then
719c4a9443cSclaudio  * it parses it and makes sure that the data being returned is fully
720c4a9443cSclaudio  * validated and verified.
721c4a9443cSclaudio  * The process will exit cleanly only when fd is closed.
722c4a9443cSclaudio  */
723c4a9443cSclaudio void
724c4a9443cSclaudio proc_filemode(int fd)
725c4a9443cSclaudio {
726c4a9443cSclaudio 	struct entityq	 q;
727c4a9443cSclaudio 	struct pollfd	 pfd;
72825d36c5cSclaudio 	struct msgbuf	*msgq;
729c4a9443cSclaudio 	struct entity	*entp;
730c4a9443cSclaudio 	struct ibuf	*b, *inbuf = NULL;
731c4a9443cSclaudio 
732c4a9443cSclaudio 	/* Only allow access to the cache directory. */
733c4a9443cSclaudio 	if (unveil(".", "r") == -1)
734c4a9443cSclaudio 		err(1, "unveil cachedir");
735c4a9443cSclaudio 	if (pledge("stdio rpath", NULL) == -1)
736c4a9443cSclaudio 		err(1, "pledge");
737c4a9443cSclaudio 
738c4a9443cSclaudio 	ERR_load_crypto_strings();
739c4a9443cSclaudio 	OpenSSL_add_all_ciphers();
740c4a9443cSclaudio 	OpenSSL_add_all_digests();
741c4a9443cSclaudio 	x509_init_oid();
742891d6bceSjob 	constraints_parse();
743c4a9443cSclaudio 
744c4a9443cSclaudio 	if ((ctx = X509_STORE_CTX_new()) == NULL)
745c0528901Stb 		err(1, "X509_STORE_CTX_new");
746318f0572Sjob 	if ((bn_ctx = BN_CTX_new()) == NULL)
747318f0572Sjob 		err(1, "BN_CTX_new");
748318f0572Sjob 
749c4a9443cSclaudio 	TAILQ_INIT(&q);
750c4a9443cSclaudio 
751b5fa5d51Sclaudio 	if ((msgq = msgbuf_new_reader(sizeof(size_t), io_parse_hdr, NULL)) ==
752b5fa5d51Sclaudio 	    NULL)
75325d36c5cSclaudio 		err(1, NULL);
754c4a9443cSclaudio 	pfd.fd = fd;
755c4a9443cSclaudio 
756c4a9443cSclaudio 	for (;;) {
757c4a9443cSclaudio 		pfd.events = POLLIN;
75825d36c5cSclaudio 		if (msgbuf_queuelen(msgq) > 0)
759c4a9443cSclaudio 			pfd.events |= POLLOUT;
760c4a9443cSclaudio 
761c4a9443cSclaudio 		if (poll(&pfd, 1, INFTIM) == -1) {
762c4a9443cSclaudio 			if (errno == EINTR)
763c4a9443cSclaudio 				continue;
764c4a9443cSclaudio 			err(1, "poll");
765c4a9443cSclaudio 		}
766c4a9443cSclaudio 		if ((pfd.revents & (POLLERR|POLLNVAL)))
767c4a9443cSclaudio 			errx(1, "poll: bad descriptor");
768c4a9443cSclaudio 
769c4a9443cSclaudio 		/* If the parent closes, return immediately. */
770c4a9443cSclaudio 
771c4a9443cSclaudio 		if ((pfd.revents & POLLHUP))
772c4a9443cSclaudio 			break;
773c4a9443cSclaudio 
774c4a9443cSclaudio 		if ((pfd.revents & POLLIN)) {
775b5fa5d51Sclaudio 			switch (ibuf_read(fd, msgq)) {
776b5fa5d51Sclaudio 			case -1:
777b5fa5d51Sclaudio 				err(1, "ibuf_read");
778b5fa5d51Sclaudio 			case 0:
779b5fa5d51Sclaudio 				errx(1, "ibuf_read: connection closed");
780b5fa5d51Sclaudio 			}
781b5fa5d51Sclaudio 			while ((b = io_buf_get(msgq)) != NULL) {
782c4a9443cSclaudio 				entp = calloc(1, sizeof(struct entity));
783c4a9443cSclaudio 				if (entp == NULL)
784c4a9443cSclaudio 					err(1, NULL);
785c4a9443cSclaudio 				entity_read_req(b, entp);
786c4a9443cSclaudio 				TAILQ_INSERT_TAIL(&q, entp, entries);
787c4a9443cSclaudio 				ibuf_free(b);
788c4a9443cSclaudio 			}
789c4a9443cSclaudio 		}
790c4a9443cSclaudio 
791c4a9443cSclaudio 		if (pfd.revents & POLLOUT) {
79225d36c5cSclaudio 			if (msgbuf_write(fd, msgq) == -1) {
7939aadc625Sclaudio 				if (errno == EPIPE)
794c4a9443cSclaudio 					errx(1, "write: connection closed");
7959aadc625Sclaudio 				else
796c4a9443cSclaudio 					err(1, "write");
797c4a9443cSclaudio 			}
798c4a9443cSclaudio 		}
799c4a9443cSclaudio 
80025d36c5cSclaudio 		parse_file(&q, msgq);
801c4a9443cSclaudio 	}
802c4a9443cSclaudio 
80325d36c5cSclaudio 	msgbuf_free(msgq);
804c4a9443cSclaudio 	while ((entp = TAILQ_FIRST(&q)) != NULL) {
805c4a9443cSclaudio 		TAILQ_REMOVE(&q, entp, entries);
806c4a9443cSclaudio 		entity_free(entp);
807c4a9443cSclaudio 	}
808c4a9443cSclaudio 
809b8017900Sjob 	auth_tree_free(&auths);
810b8017900Sjob 	crl_tree_free(&crlt);
811b8017900Sjob 
812c4a9443cSclaudio 	X509_STORE_CTX_free(ctx);
813318f0572Sjob 	BN_CTX_free(bn_ctx);
814318f0572Sjob 
815b8017900Sjob 	ibuf_free(inbuf);
816c4a9443cSclaudio 
817c4a9443cSclaudio 	exit(0);
818c4a9443cSclaudio }
819