xref: /openbsd-src/usr.sbin/rpki-client/parser.c (revision b5fa5d51bd4733ed62a748e18f6a838408441343)
1*b5fa5d51Sclaudio /*	$OpenBSD: parser.c,v 1.148 2024/11/21 13:32:27 claudio Exp $ */
2eae58378Sclaudio /*
3eae58378Sclaudio  * Copyright (c) 2019 Claudio Jeker <claudio@openbsd.org>
4eae58378Sclaudio  * Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv>
5eae58378Sclaudio  *
6eae58378Sclaudio  * Permission to use, copy, modify, and distribute this software for any
7eae58378Sclaudio  * purpose with or without fee is hereby granted, provided that the above
8eae58378Sclaudio  * copyright notice and this permission notice appear in all copies.
9eae58378Sclaudio  *
10eae58378Sclaudio  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11eae58378Sclaudio  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12eae58378Sclaudio  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13eae58378Sclaudio  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14eae58378Sclaudio  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15eae58378Sclaudio  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16eae58378Sclaudio  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17eae58378Sclaudio  */
18eae58378Sclaudio 
19eae58378Sclaudio #include <sys/queue.h>
20eae58378Sclaudio #include <sys/tree.h>
21eae58378Sclaudio #include <sys/types.h>
22eae58378Sclaudio 
23eae58378Sclaudio #include <err.h>
2487c7c78dSclaudio #include <fcntl.h>
25eae58378Sclaudio #include <poll.h>
26eae58378Sclaudio #include <stdio.h>
27eae58378Sclaudio #include <stdlib.h>
28eae58378Sclaudio #include <string.h>
29eae58378Sclaudio #include <limits.h>
30eae58378Sclaudio #include <unistd.h>
31eae58378Sclaudio #include <imsg.h>
32eae58378Sclaudio 
33c631bb87Sclaudio #include <openssl/asn1.h>
34eae58378Sclaudio #include <openssl/err.h>
35a1753de6Sclaudio #include <openssl/evp.h>
36c631bb87Sclaudio #include <openssl/x509.h>
37cac2b49eStb #include <openssl/x509v3.h>
38eae58378Sclaudio 
39eae58378Sclaudio #include "extern.h"
40eae58378Sclaudio 
411d8c6443Stb extern int certid;
421d8c6443Stb 
43318f0572Sjob extern BN_CTX		*bn_ctx;
44318f0572Sjob 
456feb6ad0Sclaudio static X509_STORE_CTX	*ctx;
466feb6ad0Sclaudio static struct auth_tree	 auths = RB_INITIALIZER(&auths);
476feb6ad0Sclaudio static struct crl_tree	 crlt = RB_INITIALIZER(&crlt);
486feb6ad0Sclaudio 
49100ded9eSclaudio struct parse_repo {
50100ded9eSclaudio 	RB_ENTRY(parse_repo)	 entry;
51100ded9eSclaudio 	char			*path;
524bccd3c1Sclaudio 	char			*validpath;
53100ded9eSclaudio 	unsigned int		 id;
54100ded9eSclaudio };
55100ded9eSclaudio 
56100ded9eSclaudio static RB_HEAD(repo_tree, parse_repo)	repos = RB_INITIALIZER(&repos);
57100ded9eSclaudio 
58100ded9eSclaudio static inline int
59100ded9eSclaudio repocmp(struct parse_repo *a, struct parse_repo *b)
60100ded9eSclaudio {
61100ded9eSclaudio 	return a->id - b->id;
62100ded9eSclaudio }
63100ded9eSclaudio 
64100ded9eSclaudio RB_GENERATE_STATIC(repo_tree, parse_repo, entry, repocmp);
65100ded9eSclaudio 
66100ded9eSclaudio static struct parse_repo *
67100ded9eSclaudio repo_get(unsigned int id)
68100ded9eSclaudio {
69100ded9eSclaudio 	struct parse_repo needle = { .id = id };
70100ded9eSclaudio 
71100ded9eSclaudio 	return RB_FIND(repo_tree, &repos, &needle);
72100ded9eSclaudio }
73100ded9eSclaudio 
74100ded9eSclaudio static void
754bccd3c1Sclaudio repo_add(unsigned int id, char *path, char *validpath)
76100ded9eSclaudio {
77100ded9eSclaudio 	struct parse_repo *rp;
78100ded9eSclaudio 
794bccd3c1Sclaudio 	if ((rp = calloc(1, sizeof(*rp))) == NULL)
80100ded9eSclaudio 		err(1, NULL);
81100ded9eSclaudio 	rp->id = id;
824bccd3c1Sclaudio 	if (path != NULL)
83100ded9eSclaudio 		if ((rp->path = strdup(path)) == NULL)
84100ded9eSclaudio 			err(1, NULL);
854bccd3c1Sclaudio 	if (validpath != NULL)
864bccd3c1Sclaudio 		if ((rp->validpath = strdup(validpath)) == NULL)
874bccd3c1Sclaudio 			err(1, NULL);
88100ded9eSclaudio 
89100ded9eSclaudio 	if (RB_INSERT(repo_tree, &repos, rp) != NULL)
90100ded9eSclaudio 		errx(1, "repository already added: id %d, %s", id, path);
91100ded9eSclaudio }
92100ded9eSclaudio 
934bccd3c1Sclaudio /*
940bc420b9Sclaudio  * Return the issuer by its certificate id, or NULL on failure.
950bc420b9Sclaudio  * Make sure the AKI is the same as the AKI listed on the Manifest,
960bc420b9Sclaudio  * and that the SKI of the cert matches with the AKI.
970bc420b9Sclaudio  */
980bc420b9Sclaudio static struct auth *
990bc420b9Sclaudio find_issuer(const char *fn, int id, const char *aki, const char *mftaki)
1000bc420b9Sclaudio {
1010bc420b9Sclaudio 	struct auth *a;
1020bc420b9Sclaudio 
1030bc420b9Sclaudio 	a = auth_find(&auths, id);
1040bc420b9Sclaudio 	if (a == NULL) {
1051d8c6443Stb 		if (certid <= CERTID_MAX)
1061d8c6443Stb 			warnx("%s: RFC 6487: unknown cert with SKI %s", fn,
1071d8c6443Stb 			    aki);
1080bc420b9Sclaudio 		return NULL;
1090bc420b9Sclaudio 	}
1100bc420b9Sclaudio 
1110bc420b9Sclaudio 	if (mftaki != NULL) {
1120bc420b9Sclaudio 		if (strcmp(aki, mftaki) != 0) {
1130bc420b9Sclaudio 			warnx("%s: AKI %s doesn't match Manifest AKI %s", fn,
1140bc420b9Sclaudio 			    aki, mftaki);
1150bc420b9Sclaudio 			return NULL;
1160bc420b9Sclaudio 		}
1170bc420b9Sclaudio 	}
1180bc420b9Sclaudio 
1190bc420b9Sclaudio 	if (strcmp(aki, a->cert->ski) != 0) {
1200bc420b9Sclaudio 		warnx("%s: AKI %s doesn't match issuer SKI %s", fn,
1210bc420b9Sclaudio 		    aki, a->cert->ski);
1220bc420b9Sclaudio 		return NULL;
1230bc420b9Sclaudio 	}
1240bc420b9Sclaudio 
1250bc420b9Sclaudio 	return a;
1260bc420b9Sclaudio }
1270bc420b9Sclaudio 
1280bc420b9Sclaudio /*
129df512fbcSclaudio  * Build access path to file based on repoid, path, location and file values.
1304bccd3c1Sclaudio  */
1314bccd3c1Sclaudio static char *
1324bccd3c1Sclaudio parse_filepath(unsigned int repoid, const char *path, const char *file,
133df512fbcSclaudio     enum location loc)
1344bccd3c1Sclaudio {
1354bccd3c1Sclaudio 	struct parse_repo	*rp;
1364bccd3c1Sclaudio 	char			*fn, *repopath;
1374bccd3c1Sclaudio 
1384bccd3c1Sclaudio 	/* build file path based on repoid, entity path and filename */
1394bccd3c1Sclaudio 	rp = repo_get(repoid);
140df512fbcSclaudio 	if (rp == NULL)
141441ac56aSclaudio 		errx(1, "build file path: repository %u missing", repoid);
1424bccd3c1Sclaudio 
143df512fbcSclaudio 	if (loc == DIR_VALID)
1444bccd3c1Sclaudio 		repopath = rp->validpath;
1454bccd3c1Sclaudio 	else
1464bccd3c1Sclaudio 		repopath = rp->path;
1474bccd3c1Sclaudio 
1484bccd3c1Sclaudio 	if (repopath == NULL)
1494bccd3c1Sclaudio 		return NULL;
1504bccd3c1Sclaudio 
1514bccd3c1Sclaudio 	if (path == NULL) {
1524bccd3c1Sclaudio 		if (asprintf(&fn, "%s/%s", repopath, file) == -1)
1534bccd3c1Sclaudio 			err(1, NULL);
1544bccd3c1Sclaudio 	} else {
155df512fbcSclaudio 		if (asprintf(&fn, "%s/%s/%s", repopath, path, file) == -1)
1564bccd3c1Sclaudio 			err(1, NULL);
1574bccd3c1Sclaudio 	}
1584bccd3c1Sclaudio 	return fn;
1594bccd3c1Sclaudio }
1604bccd3c1Sclaudio 
1614bccd3c1Sclaudio /*
162eae58378Sclaudio  * Parse and validate a ROA.
163eae58378Sclaudio  * This is standard stuff.
164eae58378Sclaudio  * Returns the roa on success, NULL on failure.
165eae58378Sclaudio  */
166eae58378Sclaudio static struct roa *
16732c8d2feSjob proc_parser_roa(char *file, const unsigned char *der, size_t len,
1680636c4d0Stb     const struct entity *entp)
169eae58378Sclaudio {
170eae58378Sclaudio 	struct roa		*roa;
171aec10a2aStb 	X509			*x509 = NULL;
1724bccd3c1Sclaudio 	struct auth		*a;
173d7e95037Stb 	struct crl		*crl;
174fd7a2857Sclaudio 	const char		*errstr;
175eae58378Sclaudio 
1760636c4d0Stb 	if ((roa = roa_parse(&x509, file, entp->talid, der, len)) == NULL)
177aec10a2aStb 		goto out;
178eae58378Sclaudio 
1790bc420b9Sclaudio 	a = find_issuer(file, entp->certid, roa->aki, entp->mftaki);
180aec10a2aStb 	if (a == NULL)
181aec10a2aStb 		goto out;
182c4a9443cSclaudio 	crl = crl_get(&crlt, a);
183eae58378Sclaudio 
184fd7a2857Sclaudio 	if (!valid_x509(file, ctx, x509, a, crl, &errstr)) {
185fd7a2857Sclaudio 		warnx("%s: %s", file, errstr);
186aec10a2aStb 		goto out;
187eae58378Sclaudio 	}
1884bccd3c1Sclaudio 	X509_free(x509);
189aec10a2aStb 	x509 = NULL;
190a66158d7Sjob 
19124069af1Sclaudio 	roa->talid = a->cert->talid;
19224069af1Sclaudio 
193534b6674Sjob 	roa->expires = x509_find_expires(roa->notafter, a, &crlt);
194eae58378Sclaudio 
195eae58378Sclaudio 	return roa;
196aec10a2aStb 
197aec10a2aStb  out:
198aec10a2aStb 	roa_free(roa);
199aec10a2aStb 	X509_free(x509);
200aec10a2aStb 
201aec10a2aStb 	return NULL;
202eae58378Sclaudio }
203eae58378Sclaudio 
204eae58378Sclaudio /*
205d4be4cdeSjob  * Parse and validate a draft-ietf-sidrops-rpki-prefixlist SPL.
206d4be4cdeSjob  * Returns the spl on success, NULL on failure.
207d4be4cdeSjob  */
208d4be4cdeSjob static struct spl *
209d4be4cdeSjob proc_parser_spl(char *file, const unsigned char *der, size_t len,
210d4be4cdeSjob     const struct entity *entp)
211d4be4cdeSjob {
212d4be4cdeSjob 	struct spl		*spl;
213aec10a2aStb 	X509			*x509 = NULL;
214d4be4cdeSjob 	struct auth		*a;
215d4be4cdeSjob 	struct crl		*crl;
216d4be4cdeSjob 	const char		*errstr;
217d4be4cdeSjob 
218d4be4cdeSjob 	if ((spl = spl_parse(&x509, file, entp->talid, der, len)) == NULL)
219aec10a2aStb 		goto out;
220d4be4cdeSjob 
2210bc420b9Sclaudio 	a = find_issuer(file, entp->certid, spl->aki, entp->mftaki);
222aec10a2aStb 	if (a == NULL)
223aec10a2aStb 		goto out;
224d4be4cdeSjob 	crl = crl_get(&crlt, a);
225d4be4cdeSjob 
226d4be4cdeSjob 	if (!valid_x509(file, ctx, x509, a, crl, &errstr)) {
227d4be4cdeSjob 		warnx("%s: %s", file, errstr);
228aec10a2aStb 		goto out;
229d4be4cdeSjob 	}
230d4be4cdeSjob 	X509_free(x509);
231aec10a2aStb 	x509 = NULL;
232d4be4cdeSjob 
233d4be4cdeSjob 	spl->talid = a->cert->talid;
234d4be4cdeSjob 
235d4be4cdeSjob 	spl->expires = x509_find_expires(spl->notafter, a, &crlt);
236d4be4cdeSjob 
237d4be4cdeSjob 	return spl;
238aec10a2aStb 
239aec10a2aStb  out:
240aec10a2aStb 	spl_free(spl);
241aec10a2aStb 	X509_free(x509);
242aec10a2aStb 
243aec10a2aStb 	return NULL;
244d4be4cdeSjob }
245d4be4cdeSjob 
246d4be4cdeSjob /*
24787c7c78dSclaudio  * Check all files and their hashes in a MFT structure.
24887c7c78dSclaudio  * Return zero on failure, non-zero on success.
24987c7c78dSclaudio  */
250487aa6cbSclaudio static int
251487aa6cbSclaudio proc_parser_mft_check(const char *fn, struct mft *p)
25287c7c78dSclaudio {
253df512fbcSclaudio 	const enum location loc[2] = { DIR_TEMP, DIR_VALID };
25487c7c78dSclaudio 	size_t	 i;
255685326f5Sclaudio 	int	 rc = 1;
256685326f5Sclaudio 	char	*path;
25787c7c78dSclaudio 
25894674fa3Stb 	if (p == NULL)
25994674fa3Stb 		return 0;
26094674fa3Stb 
26187c7c78dSclaudio 	for (i = 0; i < p->filesz; i++) {
262df512fbcSclaudio 		struct mftfile *m = &p->files[i];
263df512fbcSclaudio 		int try, fd = -1, noent = 0, valid = 0;
264df512fbcSclaudio 		for (try = 0; try < 2 && !valid; try++) {
2654bccd3c1Sclaudio 			if ((path = parse_filepath(p->repoid, p->path, m->file,
266df512fbcSclaudio 			    loc[try])) == NULL)
267df512fbcSclaudio 				continue;
26887c7c78dSclaudio 			fd = open(path, O_RDONLY);
269df512fbcSclaudio 			if (fd == -1 && errno == ENOENT)
270df512fbcSclaudio 				noent++;
2714bccd3c1Sclaudio 			free(path);
2724bccd3c1Sclaudio 
273df512fbcSclaudio 			/* remember which path was checked */
274df512fbcSclaudio 			m->location = loc[try];
275df512fbcSclaudio 			valid = valid_filehash(fd, m->hash, sizeof(m->hash));
276df512fbcSclaudio 		}
277df512fbcSclaudio 
278df512fbcSclaudio 		if (!valid) {
279df512fbcSclaudio 			/* silently skip not-existing unknown files */
280df512fbcSclaudio 			if (m->type == RTYPE_INVALID && noent == 2)
281df512fbcSclaudio 				continue;
2827822b8bfSjob 			warnx("%s#%s: bad message digest for %s", fn,
2837822b8bfSjob 			    p->seqnum, m->file);
28487c7c78dSclaudio 			rc = 0;
285df512fbcSclaudio 			continue;
28687c7c78dSclaudio 		}
28787c7c78dSclaudio 	}
28887c7c78dSclaudio 
28987c7c78dSclaudio 	return rc;
29087c7c78dSclaudio }
29187c7c78dSclaudio 
29287c7c78dSclaudio /*
2936fd959f8Stb  * Load the CRL from loc using the info from the MFT.
29423bc08f8Sclaudio  */
29523bc08f8Sclaudio static struct crl *
2966fd959f8Stb parse_load_crl_from_mft(struct entity *entp, struct mft *mft, enum location loc,
2976fd959f8Stb     char **crlfile)
29823bc08f8Sclaudio {
29923bc08f8Sclaudio 	struct crl	*crl = NULL;
30023bc08f8Sclaudio 	unsigned char	*f = NULL;
30123bc08f8Sclaudio 	char		*fn = NULL;
30223bc08f8Sclaudio 	size_t		 flen;
30323bc08f8Sclaudio 
3046fd959f8Stb 	*crlfile = NULL;
3056fd959f8Stb 
30623bc08f8Sclaudio 	fn = parse_filepath(entp->repoid, entp->path, mft->crl, loc);
30723bc08f8Sclaudio 	if (fn == NULL)
3086fd959f8Stb 		goto out;
30923bc08f8Sclaudio 
31023bc08f8Sclaudio 	f = load_file(fn, &flen);
3116fd959f8Stb 	if (f == NULL) {
3126fd959f8Stb 		if (errno != ENOENT)
31323bc08f8Sclaudio 			warn("parse file %s", fn);
3146fd959f8Stb 		goto out;
3156fd959f8Stb 	}
3166fd959f8Stb 
31723bc08f8Sclaudio 	if (!valid_hash(f, flen, mft->crlhash, sizeof(mft->crlhash)))
3186fd959f8Stb 		goto out;
3196fd959f8Stb 
32023bc08f8Sclaudio 	crl = crl_parse(fn, f, flen);
3216fd959f8Stb 	if (crl == NULL)
3226fd959f8Stb 		goto out;
323fd7a2857Sclaudio 
32432c8d2feSjob 	if (strcmp(crl->aki, mft->aki) != 0) {
32532c8d2feSjob 		warnx("%s: AKI doesn't match Manifest AKI", fn);
32632c8d2feSjob 		goto out;
32732c8d2feSjob 	}
32832c8d2feSjob 
329c207abadSjob 	if ((crl->mftpath = strdup(mft->sia)) == NULL)
330c207abadSjob 		err(1, NULL);
331c207abadSjob 
332e038f1a1Sclaudio 	*crlfile = fn;
3336fd959f8Stb 	free(f);
3346fd959f8Stb 
33523bc08f8Sclaudio 	return crl;
3366fd959f8Stb 
3376fd959f8Stb  out:
33832c8d2feSjob 	crl_free(crl);
3396fd959f8Stb 	free(f);
340e038f1a1Sclaudio 	free(fn);
3416fd959f8Stb 
34223bc08f8Sclaudio 	return NULL;
34323bc08f8Sclaudio }
34423bc08f8Sclaudio 
34523bc08f8Sclaudio /*
34605c37678Sjob  * Parse and validate a manifest file.
34705c37678Sjob  * Don't check the fileandhash, this is done later on.
34805c37678Sjob  * Return the mft on success, or NULL on failure.
349eae58378Sclaudio  */
350eae58378Sclaudio static struct mft *
35152b4e3adSjob proc_parser_mft_pre(struct entity *entp, char *file, struct crl **crl,
35252b4e3adSjob     char **crlfile, struct mft *cached_mft, const char **errstr)
353eae58378Sclaudio {
354eae58378Sclaudio 	struct mft	*mft;
355eae58378Sclaudio 	X509		*x509;
356eae58378Sclaudio 	struct auth	*a;
357a4968bd9Stb 	unsigned char	*der;
358a4968bd9Stb 	size_t		 len;
3598cad1871Sjob 	time_t		 now;
3601039ba60Stb 	int		 issued_cmp, seqnum_cmp;
361eae58378Sclaudio 
36223bc08f8Sclaudio 	*crl = NULL;
363e038f1a1Sclaudio 	*crlfile = NULL;
364fd7a2857Sclaudio 	*errstr = NULL;
365eae58378Sclaudio 
36609ad02f4Sjob 	if (file == NULL)
367a4968bd9Stb 		return NULL;
368a4968bd9Stb 
36909ad02f4Sjob 	der = load_file(file, &len);
370a4968bd9Stb 	if (der == NULL && errno != ENOENT)
37109ad02f4Sjob 		warn("parse file %s", file);
372a4968bd9Stb 
37309ad02f4Sjob 	if ((mft = mft_parse(&x509, file, entp->talid, der, len)) == NULL) {
374a4968bd9Stb 		free(der);
375a4968bd9Stb 		return NULL;
376a4968bd9Stb 	}
377c5b5cf9aSjob 
378b1d56901Sjob 	if (entp->path != NULL) {
379b1d56901Sjob 		if ((mft->path = strdup(entp->path)) == NULL)
380b1d56901Sjob 			err(1, NULL);
381b1d56901Sjob 	}
382b1d56901Sjob 
383c5b5cf9aSjob 	if (!EVP_Digest(der, len, mft->mfthash, NULL, EVP_sha256(), NULL))
384c5b5cf9aSjob 		errx(1, "EVP_Digest failed");
385c5b5cf9aSjob 
386a4968bd9Stb 	free(der);
387a4968bd9Stb 
3886fd959f8Stb 	*crl = parse_load_crl_from_mft(entp, mft, DIR_TEMP, crlfile);
3896fd959f8Stb 	if (*crl == NULL)
3906fd959f8Stb 		*crl = parse_load_crl_from_mft(entp, mft, DIR_VALID, crlfile);
391a4968bd9Stb 
3920bc420b9Sclaudio 	a = find_issuer(file, entp->certid, mft->aki, NULL);
3931d8c6443Stb 	if (a == NULL)
3941d8c6443Stb 		goto err;
39509ad02f4Sjob 	if (!valid_x509(file, ctx, x509, a, *crl, errstr))
396690a1eadStb 		goto err;
397690a1eadStb 	X509_free(x509);
398690a1eadStb 	x509 = NULL;
399690a1eadStb 
400690a1eadStb 	mft->repoid = entp->repoid;
401690a1eadStb 	mft->talid = a->cert->talid;
4020bc420b9Sclaudio 	mft->certid = entp->certid;
403690a1eadStb 
4048cad1871Sjob 	now = get_current_time();
4058cad1871Sjob 	/* check that now is not before from */
4068cad1871Sjob 	if (now < mft->thisupdate) {
40709ad02f4Sjob 		warnx("%s: manifest not yet valid %s", file,
4088cad1871Sjob 		    time2str(mft->thisupdate));
409c6be5ad7Sjob 		goto err;
4108cad1871Sjob 	}
4118cad1871Sjob 	/* check that now is not after until */
4128cad1871Sjob 	if (now > mft->nextupdate) {
41309ad02f4Sjob 		warnx("%s: manifest expired on %s", file,
4148cad1871Sjob 		    time2str(mft->nextupdate));
415c6be5ad7Sjob 		goto err;
4168cad1871Sjob 	}
4178cad1871Sjob 
4188cad1871Sjob 	/* if there is nothing to compare to, return now */
41986eea840Stb 	if (cached_mft == NULL)
42086eea840Stb 		return mft;
42186eea840Stb 
42286eea840Stb 	/*
4231039ba60Stb 	 * Check that the cached manifest is older in the sense that it was
4241039ba60Stb 	 * issued earlier and that it has a smaller sequence number.
42586eea840Stb 	 */
42686eea840Stb 
4271039ba60Stb 	if ((issued_cmp = mft_compare_issued(mft, cached_mft)) < 0) {
42862c6bfd2Sjob 		warnx("%s: unexpected manifest issuance date (want >= %lld, "
42909ad02f4Sjob 		    "got %lld)", file, (long long)cached_mft->thisupdate,
4301039ba60Stb 		    (long long)mft->thisupdate);
4311039ba60Stb 		goto err;
4321039ba60Stb 	}
433e73085f8Stb 	if ((seqnum_cmp = mft_compare_seqnum(mft, cached_mft)) < 0) {
43486eea840Stb 		warnx("%s: unexpected manifest number (want >= #%s, got #%s)",
43509ad02f4Sjob 		    file, cached_mft->seqnum, mft->seqnum);
43686eea840Stb 		goto err;
43786eea840Stb 	}
4381039ba60Stb 	if (issued_cmp > 0 && seqnum_cmp == 0) {
43962c6bfd2Sjob 		warnx("%s: manifest issued at %lld and %lld with same "
44009ad02f4Sjob 		    "manifest number #%s", file, (long long)mft->thisupdate,
44162c6bfd2Sjob 		    (long long)cached_mft->thisupdate, cached_mft->seqnum);
4421039ba60Stb 		goto err;
4431039ba60Stb 	}
4441039ba60Stb 	if (issued_cmp == 0 && seqnum_cmp > 0) {
44562c6bfd2Sjob 		warnx("%s: #%s and #%s were issued at same issuance date %lld",
44609ad02f4Sjob 		    file, mft->seqnum, cached_mft->seqnum,
44762c6bfd2Sjob 		    (long long)mft->thisupdate);
4481039ba60Stb 		goto err;
4491039ba60Stb 	}
4501039ba60Stb 	if (issued_cmp == 0 && seqnum_cmp == 0 && memcmp(mft->mfthash,
45186eea840Stb 	    cached_mft->mfthash, SHA256_DIGEST_LENGTH) != 0) {
45262c6bfd2Sjob 		warnx("%s: misissuance, issuance date %lld and manifest number "
45309ad02f4Sjob 		    "#%s were recycled", file, (long long)mft->thisupdate,
45462c6bfd2Sjob 		    mft->seqnum);
45586eea840Stb 		goto err;
45686eea840Stb 	}
45786eea840Stb 
458318f0572Sjob 	if (seqnum_cmp > 0) {
459318f0572Sjob 		if (mft_seqnum_gap_present(mft, cached_mft)) {
460318f0572Sjob 			mft->seqnum_gap = 1;
461318f0572Sjob 			warnx("%s: seqnum gap detected #%s -> #%s", file,
462318f0572Sjob 			    cached_mft->seqnum, mft->seqnum);
463318f0572Sjob 		}
464318f0572Sjob 	}
465318f0572Sjob 
466690a1eadStb 	return mft;
467690a1eadStb 
468690a1eadStb  err:
469f999fe57Sclaudio 	X509_free(x509);
47023bc08f8Sclaudio 	mft_free(mft);
47123bc08f8Sclaudio 	crl_free(*crl);
47223bc08f8Sclaudio 	*crl = NULL;
473e038f1a1Sclaudio 	free(*crlfile);
474e038f1a1Sclaudio 	*crlfile = NULL;
475f999fe57Sclaudio 	return NULL;
476f999fe57Sclaudio }
477df512fbcSclaudio 
478df512fbcSclaudio /*
47923bc08f8Sclaudio  * Load the most recent MFT by opening both options and comparing the two.
48023bc08f8Sclaudio  */
48123bc08f8Sclaudio static char *
48208ac1330Sjob proc_parser_mft(struct entity *entp, struct mft **mp, char **crlfile,
48308ac1330Sjob     time_t *crlmtime)
48423bc08f8Sclaudio {
48523bc08f8Sclaudio 	struct mft	*mft1 = NULL, *mft2 = NULL;
48652b4e3adSjob 	struct crl	*crl, *crl1 = NULL, *crl2 = NULL;
48752b4e3adSjob 	char		*file, *file1 = NULL, *file2 = NULL;
48852b4e3adSjob 	char		*crl1file = NULL, *crl2file = NULL;
48952b4e3adSjob 	const char	*err1 = NULL, *err2 = NULL;
49023bc08f8Sclaudio 
49123bc08f8Sclaudio 	*mp = NULL;
49208ac1330Sjob 	*crlmtime = 0;
49323bc08f8Sclaudio 
49409ad02f4Sjob 	file2 = parse_filepath(entp->repoid, entp->path, entp->file, DIR_VALID);
49552b4e3adSjob 	mft2 = proc_parser_mft_pre(entp, file2, &crl2, &crl2file, NULL, &err2);
49609ad02f4Sjob 
49752b4e3adSjob 	if (!noop) {
49852b4e3adSjob 		file1 = parse_filepath(entp->repoid, entp->path, entp->file,
49952b4e3adSjob 		    DIR_TEMP);
50052b4e3adSjob 		mft1 = proc_parser_mft_pre(entp, file1, &crl1, &crl1file, mft2,
50152b4e3adSjob 		    &err1);
50252b4e3adSjob 	}
50323bc08f8Sclaudio 
5044dfb12e7Sjob 	if (proc_parser_mft_check(file1, mft1)) {
50523bc08f8Sclaudio 		mft_free(mft2);
50623bc08f8Sclaudio 		crl_free(crl2);
507e038f1a1Sclaudio 		free(crl2file);
50823bc08f8Sclaudio 		free(file2);
509102443c7Sjob 
51094674fa3Stb 		*mp = mft1;
51123bc08f8Sclaudio 		crl = crl1;
51223bc08f8Sclaudio 		file = file1;
513e038f1a1Sclaudio 		*crlfile = crl1file;
51423bc08f8Sclaudio 	} else {
51594674fa3Stb 		if (mft1 != NULL && mft2 != NULL)
51694674fa3Stb 			warnx("%s: failed fetch, continuing with #%s "
51794674fa3Stb 			    "from cache", file2, mft2->seqnum);
51894674fa3Stb 
5196aaa3c8dStb 		if (!proc_parser_mft_check(file2, mft2)) {
520a0d2775aStb 			mft_free(mft2);
521a0d2775aStb 			mft2 = NULL;
52294674fa3Stb 
523102443c7Sjob 			if (err2 == NULL)
524102443c7Sjob 				err2 = err1;
525a0d2775aStb 			if (err2 == NULL)
52694674fa3Stb 				err2 = "no valid manifest available";
5271d8c6443Stb 			if (certid <= CERTID_MAX)
528a0d2775aStb 				warnx("%s: %s", file2, err2);
529a0d2775aStb 		}
530102443c7Sjob 
53123bc08f8Sclaudio 		mft_free(mft1);
53223bc08f8Sclaudio 		crl_free(crl1);
533e038f1a1Sclaudio 		free(crl1file);
53423bc08f8Sclaudio 		free(file1);
535102443c7Sjob 
5366aaa3c8dStb 		*mp = mft2;
53723bc08f8Sclaudio 		crl = crl2;
53823bc08f8Sclaudio 		file = file2;
539e038f1a1Sclaudio 		*crlfile = crl2file;
54023bc08f8Sclaudio 	}
54123bc08f8Sclaudio 
54223bc08f8Sclaudio 	if (*mp != NULL) {
543c527cc7aSjob 		*crlmtime = crl->thisupdate;
544fc437288Stb 		if (crl_insert(&crlt, crl))
545fc437288Stb 			crl = NULL;
54623bc08f8Sclaudio 	}
54723bc08f8Sclaudio 	crl_free(crl);
548fc437288Stb 
54923bc08f8Sclaudio 	return file;
55023bc08f8Sclaudio }
55123bc08f8Sclaudio 
55223bc08f8Sclaudio /*
553f43c4d92Sclaudio  * Certificates are from manifests (has a digest and is signed with
554f43c4d92Sclaudio  * another certificate) Parse the certificate, make sure its
555f43c4d92Sclaudio  * signatures are valid (with CRLs), then validate the RPKI content.
556f43c4d92Sclaudio  * This returns a certificate (which must not be freed) or NULL on
557f43c4d92Sclaudio  * parse failure.
558f43c4d92Sclaudio  */
559f43c4d92Sclaudio static struct cert *
56032c8d2feSjob proc_parser_cert(char *file, const unsigned char *der, size_t len,
5610bc420b9Sclaudio     const struct entity *entp)
562f43c4d92Sclaudio {
563f43c4d92Sclaudio 	struct cert	*cert;
564ad462a11Sclaudio 	struct crl	*crl;
565ad462a11Sclaudio 	struct auth	*a;
566fd7a2857Sclaudio 	const char	*errstr = NULL;
567f43c4d92Sclaudio 
568f43c4d92Sclaudio 	/* Extract certificate data. */
569f43c4d92Sclaudio 
570ba153bd8Sclaudio 	cert = cert_parse_pre(file, der, len);
571ba153bd8Sclaudio 	cert = cert_parse(file, cert);
572f43c4d92Sclaudio 	if (cert == NULL)
573aec10a2aStb 		goto out;
574f43c4d92Sclaudio 
5750bc420b9Sclaudio 	a = find_issuer(file, entp->certid, cert->aki, entp->mftaki);
576aec10a2aStb 	if (a == NULL)
577aec10a2aStb 		goto out;
578ad462a11Sclaudio 	crl = crl_get(&crlt, a);
579ad462a11Sclaudio 
580fd7a2857Sclaudio 	if (!valid_x509(file, ctx, cert->x509, a, crl, &errstr) ||
581ad462a11Sclaudio 	    !valid_cert(file, a, cert)) {
582fd7a2857Sclaudio 		if (errstr != NULL)
583fd7a2857Sclaudio 			warnx("%s: %s", file, errstr);
584aec10a2aStb 		goto out;
585ad462a11Sclaudio 	}
586ad462a11Sclaudio 
587ad462a11Sclaudio 	cert->talid = a->cert->talid;
588ad462a11Sclaudio 
589891d6bceSjob 	if (cert->purpose == CERT_PURPOSE_BGPSEC_ROUTER) {
590aec10a2aStb 		if (!constraints_validate(file, cert))
591aec10a2aStb 			goto out;
592891d6bceSjob 	}
593891d6bceSjob 
594ad462a11Sclaudio 	/*
595ad462a11Sclaudio 	 * Add validated CA certs to the RPKI auth tree.
596ad462a11Sclaudio 	 */
597ad462a11Sclaudio 	if (cert->purpose == CERT_PURPOSE_CA)
5980bc420b9Sclaudio 		auth_insert(file, &auths, cert, a);
599ad462a11Sclaudio 
600f43c4d92Sclaudio 	return cert;
601aec10a2aStb 
602aec10a2aStb  out:
603aec10a2aStb 	cert_free(cert);
604aec10a2aStb 
605aec10a2aStb 	return NULL;
606f43c4d92Sclaudio }
607f43c4d92Sclaudio 
608b12001c0Stb static int
609b12001c0Stb proc_parser_ta_cmp(const struct cert *cert1, const struct cert *cert2)
610eae58378Sclaudio {
611b12001c0Stb 	if (cert1 == NULL)
612b12001c0Stb 		return -1;
613b12001c0Stb 	if (cert2 == NULL)
614b12001c0Stb 		return 1;
615eae58378Sclaudio 
616198a0520Sclaudio 	/*
61793ab8db6Sjob 	 * The standards don't specify tiebreakers. While RFC 6487 and other
61893ab8db6Sjob 	 * sources advise against backdating, it's explicitly allowed and some
61993ab8db6Sjob 	 * TAs do. Some TAs have also re-issued with new dates and old
62093ab8db6Sjob 	 * serialNumber.
62193ab8db6Sjob 	 * Our tiebreaker logic: a more recent notBefore is taken to mean a
62293ab8db6Sjob 	 * more recent issuance, and thus preferable. Given equal notBefore
62393ab8db6Sjob 	 * values, prefer the TA cert with the narrower validity window. This
62493ab8db6Sjob 	 * hopefully encourages TA operators to reduce egregiously long TA
62593ab8db6Sjob 	 * validity periods.
626198a0520Sclaudio 	 */
627eae58378Sclaudio 
628b12001c0Stb 	if (cert1->notbefore < cert2->notbefore)
629b12001c0Stb 		return -1;
630b12001c0Stb 	if (cert1->notbefore > cert2->notbefore)
631b12001c0Stb 		return 1;
632b12001c0Stb 
63393ab8db6Sjob 	if (cert1->notafter > cert2->notafter)
63493ab8db6Sjob 		return -1;
63593ab8db6Sjob 	if (cert1->notafter < cert2->notafter)
63693ab8db6Sjob 		return 1;
63793ab8db6Sjob 
638b12001c0Stb 	/*
63918165bc1Stb 	 * Both certs are valid from our perspective. If anything changed,
64018165bc1Stb 	 * prefer the freshly-fetched one. We rely on cert_parse_pre() having
64118165bc1Stb 	 * cached the extensions and thus libcrypto has already computed the
64218165bc1Stb 	 * certs' hashes (SHA-1 for OpenSSL, SHA-512 for LibreSSL). The below
64318165bc1Stb 	 * compares them.
644b12001c0Stb 	 */
645b12001c0Stb 
64618165bc1Stb 	return X509_cmp(cert1->x509, cert2->x509) != 0;
647b12001c0Stb }
648b12001c0Stb 
649b12001c0Stb /*
650b12001c0Stb  * Root certificates come from TALs. Inspect and validate both options and
651b12001c0Stb  * compare the two. The cert in out_cert must not be freed. Returns the file
652b12001c0Stb  * name of the chosen TA.
653b12001c0Stb  */
654b12001c0Stb static char *
655b12001c0Stb proc_parser_root_cert(struct entity *entp, struct cert **out_cert)
656b12001c0Stb {
657b12001c0Stb 	struct cert		*cert1 = NULL, *cert2 = NULL;
658b12001c0Stb 	char			*file1 = NULL, *file2 = NULL;
659b12001c0Stb 	unsigned char		*der = NULL, *pkey = entp->data;
660b12001c0Stb 	size_t			 der_len = 0, pkeysz = entp->datasz;
661b12001c0Stb 	int			 cmp;
662b12001c0Stb 
663b12001c0Stb 	*out_cert = NULL;
664b12001c0Stb 
665b12001c0Stb 	file2 = parse_filepath(entp->repoid, entp->path, entp->file, DIR_VALID);
666b12001c0Stb 	der = load_file(file2, &der_len);
667b12001c0Stb 	cert2 = cert_parse_pre(file2, der, der_len);
668b12001c0Stb 	free(der);
669b12001c0Stb 	cert2 = ta_parse(file2, cert2, pkey, pkeysz);
670b12001c0Stb 
671b12001c0Stb 	if (!noop) {
672b12001c0Stb 		file1 = parse_filepath(entp->repoid, entp->path, entp->file,
673b12001c0Stb 		    DIR_TEMP);
674b12001c0Stb 		der = load_file(file1, &der_len);
675b12001c0Stb 		cert1 = cert_parse_pre(file1, der, der_len);
676b12001c0Stb 		free(der);
677b12001c0Stb 		cert1 = ta_parse(file1, cert1, pkey, pkeysz);
678b12001c0Stb 	}
679b12001c0Stb 
680b12001c0Stb 	if ((cmp = proc_parser_ta_cmp(cert1, cert2)) > 0) {
681b12001c0Stb 		cert_free(cert2);
682b12001c0Stb 		free(file2);
683b12001c0Stb 
684b12001c0Stb 		cert1->talid = entp->talid;
685b12001c0Stb 		auth_insert(file1, &auths, cert1, NULL);
686b12001c0Stb 
687b12001c0Stb 		*out_cert = cert1;
688b12001c0Stb 		return file1;
689b12001c0Stb 	} else {
690b12001c0Stb 		if (cmp < 0 && cert1 != NULL && cert2 != NULL)
691b12001c0Stb 			warnx("%s: cached TA is newer", entp->file);
692b12001c0Stb 		cert_free(cert1);
693b12001c0Stb 		free(file1);
694b12001c0Stb 
695b12001c0Stb 		if (cert2 != 0) {
696b12001c0Stb 			cert2->talid = entp->talid;
697b12001c0Stb 			auth_insert(file2, &auths, cert2, NULL);
698b12001c0Stb 		}
699b12001c0Stb 
700b12001c0Stb 		*out_cert = cert2;
701b12001c0Stb 		return file2;
702b12001c0Stb 	}
703eae58378Sclaudio }
704eae58378Sclaudio 
705eae58378Sclaudio /*
706eae58378Sclaudio  * Parse a ghostbuster record
707eae58378Sclaudio  */
70808ac1330Sjob static struct gbr *
70932c8d2feSjob proc_parser_gbr(char *file, const unsigned char *der, size_t len,
7100636c4d0Stb     const struct entity *entp)
711eae58378Sclaudio {
712eae58378Sclaudio 	struct gbr	*gbr;
713aec10a2aStb 	X509		*x509 = NULL;
7144bccd3c1Sclaudio 	struct crl	*crl;
715d7e95037Stb 	struct auth	*a;
716fd7a2857Sclaudio 	const char	*errstr;
717eae58378Sclaudio 
7180636c4d0Stb 	if ((gbr = gbr_parse(&x509, file, entp->talid, der, len)) == NULL)
719aec10a2aStb 		goto out;
720eae58378Sclaudio 
7210bc420b9Sclaudio 	a = find_issuer(file, entp->certid, gbr->aki, entp->mftaki);
722aec10a2aStb 	if (a == NULL)
723aec10a2aStb 		goto out;
724c4a9443cSclaudio 	crl = crl_get(&crlt, a);
725eae58378Sclaudio 
72608ac1330Sjob 	if (!valid_x509(file, ctx, x509, a, crl, &errstr)) {
727fd7a2857Sclaudio 		warnx("%s: %s", file, errstr);
728aec10a2aStb 		goto out;
72908ac1330Sjob 	}
73008ac1330Sjob 	X509_free(x509);
731aec10a2aStb 	x509 = NULL;
73208ac1330Sjob 
73308ac1330Sjob 	gbr->talid = a->cert->talid;
73408ac1330Sjob 
73508ac1330Sjob 	return gbr;
736aec10a2aStb 
737aec10a2aStb  out:
738aec10a2aStb 	gbr_free(gbr);
739aec10a2aStb 	X509_free(x509);
740aec10a2aStb 
741aec10a2aStb 	return NULL;
742eae58378Sclaudio }
743eae58378Sclaudio 
74411e5e285Sclaudio /*
745a29ddfd5Sjob  * Parse an ASPA object
746a29ddfd5Sjob  */
747a29ddfd5Sjob static struct aspa *
74832c8d2feSjob proc_parser_aspa(char *file, const unsigned char *der, size_t len,
7490636c4d0Stb     const struct entity *entp)
750a29ddfd5Sjob {
751a29ddfd5Sjob 	struct aspa	*aspa;
752aec10a2aStb 	X509		*x509 = NULL;
753a29ddfd5Sjob 	struct auth	*a;
754a29ddfd5Sjob 	struct crl	*crl;
755fd7a2857Sclaudio 	const char	*errstr;
756a29ddfd5Sjob 
7570636c4d0Stb 	if ((aspa = aspa_parse(&x509, file, entp->talid, der, len)) == NULL)
758aec10a2aStb 		goto out;
759a29ddfd5Sjob 
7600bc420b9Sclaudio 	a = find_issuer(file, entp->certid, aspa->aki, entp->mftaki);
761aec10a2aStb 	if (a == NULL)
762aec10a2aStb 		goto out;
763a29ddfd5Sjob 	crl = crl_get(&crlt, a);
764a29ddfd5Sjob 
765fd7a2857Sclaudio 	if (!valid_x509(file, ctx, x509, a, crl, &errstr)) {
766fd7a2857Sclaudio 		warnx("%s: %s", file, errstr);
767aec10a2aStb 		goto out;
768a29ddfd5Sjob 	}
769a29ddfd5Sjob 	X509_free(x509);
770aec10a2aStb 	x509 = NULL;
771a29ddfd5Sjob 
772a29ddfd5Sjob 	aspa->talid = a->cert->talid;
773a29ddfd5Sjob 
774534b6674Sjob 	aspa->expires = x509_find_expires(aspa->notafter, a, &crlt);
775a29ddfd5Sjob 
776a29ddfd5Sjob 	return aspa;
777aec10a2aStb 
778aec10a2aStb  out:
779aec10a2aStb 	aspa_free(aspa);
780aec10a2aStb 	X509_free(x509);
781aec10a2aStb 
782aec10a2aStb 	return NULL;
783a29ddfd5Sjob }
784a29ddfd5Sjob 
785a29ddfd5Sjob /*
786ee2a33daSjob  * Parse a TAK object.
787ee2a33daSjob  */
788ee2a33daSjob static struct tak *
78932c8d2feSjob proc_parser_tak(char *file, const unsigned char *der, size_t len,
7900636c4d0Stb     const struct entity *entp)
791ee2a33daSjob {
792ee2a33daSjob 	struct tak	*tak;
793aec10a2aStb 	X509		*x509 = NULL;
794ee2a33daSjob 	struct crl	*crl;
795ee2a33daSjob 	struct auth	*a;
796fd7a2857Sclaudio 	const char	*errstr;
797ee2a33daSjob 
7980636c4d0Stb 	if ((tak = tak_parse(&x509, file, entp->talid, der, len)) == NULL)
799aec10a2aStb 		goto out;
800ee2a33daSjob 
8010bc420b9Sclaudio 	a = find_issuer(file, entp->certid, tak->aki, entp->mftaki);
8021d8c6443Stb 	if (a == NULL)
8031d8c6443Stb 		goto out;
804ee2a33daSjob 	crl = crl_get(&crlt, a);
805ee2a33daSjob 
806fd7a2857Sclaudio 	if (!valid_x509(file, ctx, x509, a, crl, &errstr)) {
807fd7a2857Sclaudio 		warnx("%s: %s", file, errstr);
808ee2a33daSjob 		goto out;
809fd7a2857Sclaudio 	}
810aec10a2aStb 	X509_free(x509);
811aec10a2aStb 	x509 = NULL;
812ee2a33daSjob 
813ee2a33daSjob 	/* TAK EE must be signed by self-signed CA */
814335482abStb 	if (a->issuer != NULL)
815ee2a33daSjob 		goto out;
816ee2a33daSjob 
817ee2a33daSjob 	tak->talid = a->cert->talid;
818aec10a2aStb 
819ee2a33daSjob 	return tak;
820aec10a2aStb 
821aec10a2aStb  out:
822aec10a2aStb 	tak_free(tak);
823aec10a2aStb 	X509_free(x509);
824aec10a2aStb 
825aec10a2aStb 	return NULL;
826ee2a33daSjob }
827ee2a33daSjob 
828ee2a33daSjob /*
829df512fbcSclaudio  * Load the file specified by the entity information.
830df512fbcSclaudio  */
831100ded9eSclaudio static char *
8324bccd3c1Sclaudio parse_load_file(struct entity *entp, unsigned char **f, size_t *flen)
833100ded9eSclaudio {
834df512fbcSclaudio 	char *file;
835100ded9eSclaudio 
836df512fbcSclaudio 	file = parse_filepath(entp->repoid, entp->path, entp->file,
837df512fbcSclaudio 	    entp->location);
838df512fbcSclaudio 	if (file == NULL)
839df512fbcSclaudio 		errx(1, "no path to file");
8404bccd3c1Sclaudio 
841df512fbcSclaudio 	*f = load_file(file, flen);
842df512fbcSclaudio 	if (*f == NULL)
843df512fbcSclaudio 		warn("parse file %s", file);
844df512fbcSclaudio 
8454bccd3c1Sclaudio 	return file;
846100ded9eSclaudio }
8474bccd3c1Sclaudio 
848d7e95037Stb /*
8493b77ffafSjob  * Process an entity and respond to parent process.
850f43c4d92Sclaudio  */
8516feb6ad0Sclaudio static void
8526feb6ad0Sclaudio parse_entity(struct entityq *q, struct msgbuf *msgq)
8536feb6ad0Sclaudio {
8546feb6ad0Sclaudio 	struct entity	*entp;
8556feb6ad0Sclaudio 	struct tal	*tal;
8566feb6ad0Sclaudio 	struct cert	*cert;
857d3c7e816Sclaudio 	struct mft	*mft;
8586feb6ad0Sclaudio 	struct roa	*roa;
859a29ddfd5Sjob 	struct aspa	*aspa;
86008ac1330Sjob 	struct gbr	*gbr;
86108ac1330Sjob 	struct tak	*tak;
862d4be4cdeSjob 	struct spl	*spl;
8636feb6ad0Sclaudio 	struct ibuf	*b;
864cabf3a3bSclaudio 	unsigned char	*f;
86508ac1330Sjob 	time_t		 mtime, crlmtime;
866cabf3a3bSclaudio 	size_t		 flen;
867e038f1a1Sclaudio 	char		*file, *crlfile;
8686feb6ad0Sclaudio 	int		 c;
8696feb6ad0Sclaudio 
8706feb6ad0Sclaudio 	while ((entp = TAILQ_FIRST(q)) != NULL) {
8716feb6ad0Sclaudio 		TAILQ_REMOVE(q, entp, entries);
8726feb6ad0Sclaudio 
873100ded9eSclaudio 		/* handle RTYPE_REPO first */
874100ded9eSclaudio 		if (entp->type == RTYPE_REPO) {
8754bccd3c1Sclaudio 			repo_add(entp->repoid, entp->path, entp->file);
876100ded9eSclaudio 			entity_free(entp);
877100ded9eSclaudio 			continue;
878100ded9eSclaudio 		}
8796feb6ad0Sclaudio 
8804bccd3c1Sclaudio 		/* pass back at least type, repoid and filename */
881100ded9eSclaudio 		b = io_new_buffer();
882de22d368Sclaudio 		io_simple_buffer(b, &entp->type, sizeof(entp->type));
88339c0924aSclaudio 		io_simple_buffer(b, &entp->repoid, sizeof(entp->repoid));
8841fc2657fSclaudio 		io_simple_buffer(b, &entp->talid, sizeof(entp->talid));
885de22d368Sclaudio 
886df512fbcSclaudio 		file = NULL;
887df512fbcSclaudio 		f = NULL;
88808ac1330Sjob 		mtime = 0;
88908ac1330Sjob 		crlmtime = 0;
89008ac1330Sjob 
8916feb6ad0Sclaudio 		switch (entp->type) {
8926feb6ad0Sclaudio 		case RTYPE_TAL:
893df512fbcSclaudio 			io_str_buffer(b, entp->file);
89408ac1330Sjob 			io_simple_buffer(b, &mtime, sizeof(mtime));
89541edc670Sclaudio 			if ((tal = tal_parse(entp->file, entp->data,
89641edc670Sclaudio 			    entp->datasz)) == NULL)
8976feb6ad0Sclaudio 				errx(1, "%s: could not parse tal file",
8986feb6ad0Sclaudio 				    entp->file);
899dc508150Sclaudio 			tal->id = entp->talid;
9006feb6ad0Sclaudio 			tal_buffer(b, tal);
9016feb6ad0Sclaudio 			tal_free(tal);
9026feb6ad0Sclaudio 			break;
9036feb6ad0Sclaudio 		case RTYPE_CER:
904b12001c0Stb 			if (entp->data != NULL) {
905b12001c0Stb 				file = proc_parser_root_cert(entp, &cert);
906b12001c0Stb 			} else {
907df512fbcSclaudio 				file = parse_load_file(entp, &f, &flen);
9080bc420b9Sclaudio 				cert = proc_parser_cert(file, f, flen, entp);
909b12001c0Stb 			}
910b12001c0Stb 			io_str_buffer(b, file);
91108ac1330Sjob 			if (cert != NULL)
91208ac1330Sjob 				mtime = cert->notbefore;
91308ac1330Sjob 			io_simple_buffer(b, &mtime, sizeof(mtime));
9146feb6ad0Sclaudio 			c = (cert != NULL);
9156feb6ad0Sclaudio 			io_simple_buffer(b, &c, sizeof(int));
916c94cf448Sclaudio 			if (cert != NULL) {
917c94cf448Sclaudio 				cert->repoid = entp->repoid;
9186feb6ad0Sclaudio 				cert_buffer(b, cert);
919c94cf448Sclaudio 			}
9206feb6ad0Sclaudio 			/*
9216feb6ad0Sclaudio 			 * The parsed certificate data "cert" is now
9226feb6ad0Sclaudio 			 * managed in the "auths" table, so don't free
923df512fbcSclaudio 			 * it here.
9246feb6ad0Sclaudio 			 */
9256feb6ad0Sclaudio 			break;
9266feb6ad0Sclaudio 		case RTYPE_MFT:
92708ac1330Sjob 			file = proc_parser_mft(entp, &mft, &crlfile, &crlmtime);
928df512fbcSclaudio 			io_str_buffer(b, file);
92908ac1330Sjob 			if (mft != NULL)
93008ac1330Sjob 				mtime = mft->signtime;
93108ac1330Sjob 			io_simple_buffer(b, &mtime, sizeof(mtime));
9326feb6ad0Sclaudio 			c = (mft != NULL);
9336feb6ad0Sclaudio 			io_simple_buffer(b, &c, sizeof(int));
9346feb6ad0Sclaudio 			if (mft != NULL)
9356feb6ad0Sclaudio 				mft_buffer(b, mft);
936e038f1a1Sclaudio 
937e038f1a1Sclaudio 			/* Push valid CRL together with the MFT. */
938e038f1a1Sclaudio 			if (crlfile != NULL) {
939e038f1a1Sclaudio 				enum rtype type;
940e038f1a1Sclaudio 				struct ibuf *b2;
941e038f1a1Sclaudio 
942e038f1a1Sclaudio 				b2 = io_new_buffer();
943e038f1a1Sclaudio 				type = RTYPE_CRL;
944e038f1a1Sclaudio 				io_simple_buffer(b2, &type, sizeof(type));
945e038f1a1Sclaudio 				io_simple_buffer(b2, &entp->repoid,
946e038f1a1Sclaudio 				    sizeof(entp->repoid));
9471fc2657fSclaudio 				io_simple_buffer(b2, &entp->talid,
9481fc2657fSclaudio 				    sizeof(entp->talid));
949e038f1a1Sclaudio 				io_str_buffer(b2, crlfile);
95008ac1330Sjob 				io_simple_buffer(b2, &crlmtime,
95108ac1330Sjob 				    sizeof(crlmtime));
952e038f1a1Sclaudio 				free(crlfile);
953e038f1a1Sclaudio 
954e038f1a1Sclaudio 				io_close_buffer(msgq, b2);
955e038f1a1Sclaudio 			}
9566feb6ad0Sclaudio 			mft_free(mft);
9576feb6ad0Sclaudio 			break;
9586feb6ad0Sclaudio 		case RTYPE_ROA:
959df512fbcSclaudio 			file = parse_load_file(entp, &f, &flen);
960df512fbcSclaudio 			io_str_buffer(b, file);
9610636c4d0Stb 			roa = proc_parser_roa(file, f, flen, entp);
96208ac1330Sjob 			if (roa != NULL)
96308ac1330Sjob 				mtime = roa->signtime;
96408ac1330Sjob 			io_simple_buffer(b, &mtime, sizeof(mtime));
9656feb6ad0Sclaudio 			c = (roa != NULL);
9666feb6ad0Sclaudio 			io_simple_buffer(b, &c, sizeof(int));
9676feb6ad0Sclaudio 			if (roa != NULL)
9686feb6ad0Sclaudio 				roa_buffer(b, roa);
9696feb6ad0Sclaudio 			roa_free(roa);
9706feb6ad0Sclaudio 			break;
9716feb6ad0Sclaudio 		case RTYPE_GBR:
972df512fbcSclaudio 			file = parse_load_file(entp, &f, &flen);
973df512fbcSclaudio 			io_str_buffer(b, file);
9740636c4d0Stb 			gbr = proc_parser_gbr(file, f, flen, entp);
97508ac1330Sjob 			if (gbr != NULL)
97608ac1330Sjob 				mtime = gbr->signtime;
97708ac1330Sjob 			io_simple_buffer(b, &mtime, sizeof(mtime));
97808ac1330Sjob 			gbr_free(gbr);
9796feb6ad0Sclaudio 			break;
980a29ddfd5Sjob 		case RTYPE_ASPA:
981a29ddfd5Sjob 			file = parse_load_file(entp, &f, &flen);
982a29ddfd5Sjob 			io_str_buffer(b, file);
9830636c4d0Stb 			aspa = proc_parser_aspa(file, f, flen, entp);
98408ac1330Sjob 			if (aspa != NULL)
98508ac1330Sjob 				mtime = aspa->signtime;
98608ac1330Sjob 			io_simple_buffer(b, &mtime, sizeof(mtime));
987a29ddfd5Sjob 			c = (aspa != NULL);
988a29ddfd5Sjob 			io_simple_buffer(b, &c, sizeof(int));
989a29ddfd5Sjob 			if (aspa != NULL)
990a29ddfd5Sjob 				aspa_buffer(b, aspa);
991a29ddfd5Sjob 			aspa_free(aspa);
992a29ddfd5Sjob 			break;
993ee2a33daSjob 		case RTYPE_TAK:
994ee2a33daSjob 			file = parse_load_file(entp, &f, &flen);
995ee2a33daSjob 			io_str_buffer(b, file);
9960636c4d0Stb 			tak = proc_parser_tak(file, f, flen, entp);
99708ac1330Sjob 			if (tak != NULL)
99808ac1330Sjob 				mtime = tak->signtime;
99908ac1330Sjob 			io_simple_buffer(b, &mtime, sizeof(mtime));
100008ac1330Sjob 			tak_free(tak);
1001ee2a33daSjob 			break;
1002d4be4cdeSjob 		case RTYPE_SPL:
1003d4be4cdeSjob 			file = parse_load_file(entp, &f, &flen);
1004d4be4cdeSjob 			io_str_buffer(b, file);
10059463abd5Stb 			if (experimental) {
1006d4be4cdeSjob 				spl = proc_parser_spl(file, f, flen, entp);
1007d4be4cdeSjob 				if (spl != NULL)
1008d4be4cdeSjob 					mtime = spl->signtime;
10099463abd5Stb 			} else {
10109463abd5Stb 				if (verbose > 0)
10119463abd5Stb 					warnx("%s: skipped", file);
10129463abd5Stb 				spl = NULL;
10139463abd5Stb 			}
1014d4be4cdeSjob 			io_simple_buffer(b, &mtime, sizeof(mtime));
1015d4be4cdeSjob 			c = (spl != NULL);
1016d4be4cdeSjob 			io_simple_buffer(b, &c, sizeof(int));
1017d4be4cdeSjob 			if (spl != NULL)
1018d4be4cdeSjob 				spl_buffer(b, spl);
1019d4be4cdeSjob 			spl_free(spl);
1020d4be4cdeSjob 			break;
1021e038f1a1Sclaudio 		case RTYPE_CRL:
10226feb6ad0Sclaudio 		default:
102339c0924aSclaudio 			file = parse_filepath(entp->repoid, entp->path,
102439c0924aSclaudio 			    entp->file, entp->location);
102539c0924aSclaudio 			io_str_buffer(b, file);
102608ac1330Sjob 			io_simple_buffer(b, &mtime, sizeof(mtime));
102739c0924aSclaudio 			warnx("%s: unhandled type %d", file, entp->type);
102839c0924aSclaudio 			break;
10296feb6ad0Sclaudio 		}
10306feb6ad0Sclaudio 
1031cabf3a3bSclaudio 		free(f);
1032100ded9eSclaudio 		free(file);
10336feb6ad0Sclaudio 		io_close_buffer(msgq, b);
10346feb6ad0Sclaudio 		entity_free(entp);
10356feb6ad0Sclaudio 	}
10366feb6ad0Sclaudio }
10376feb6ad0Sclaudio 
1038eae58378Sclaudio /*
1039eae58378Sclaudio  * Process responsible for parsing and validating content.
1040eae58378Sclaudio  * All this process does is wait to be told about a file to parse, then
1041eae58378Sclaudio  * it parses it and makes sure that the data being returned is fully
1042eae58378Sclaudio  * validated and verified.
1043eae58378Sclaudio  * The process will exit cleanly only when fd is closed.
1044eae58378Sclaudio  */
1045eae58378Sclaudio void
1046eae58378Sclaudio proc_parser(int fd)
1047eae58378Sclaudio {
1048eae58378Sclaudio 	struct entityq	 q;
104925d36c5cSclaudio 	struct msgbuf	*msgq;
1050eae58378Sclaudio 	struct pollfd	 pfd;
10516feb6ad0Sclaudio 	struct entity	*entp;
10527eb79a4aSclaudio 	struct ibuf	*b, *inbuf = NULL;
1053eae58378Sclaudio 
10541db5fd2bSclaudio 	/* Only allow access to the cache directory. */
10551db5fd2bSclaudio 	if (unveil(".", "r") == -1)
10561db5fd2bSclaudio 		err(1, "unveil cachedir");
10571db5fd2bSclaudio 	if (pledge("stdio rpath", NULL) == -1)
10581db5fd2bSclaudio 		err(1, "pledge");
10591db5fd2bSclaudio 
1060eae58378Sclaudio 	ERR_load_crypto_strings();
1061eae58378Sclaudio 	OpenSSL_add_all_ciphers();
1062eae58378Sclaudio 	OpenSSL_add_all_digests();
10634bccd3c1Sclaudio 	x509_init_oid();
1064891d6bceSjob 	constraints_parse();
1065eae58378Sclaudio 
1066eae58378Sclaudio 	if ((ctx = X509_STORE_CTX_new()) == NULL)
1067c0528901Stb 		err(1, "X509_STORE_CTX_new");
1068318f0572Sjob 	if ((bn_ctx = BN_CTX_new()) == NULL)
1069318f0572Sjob 		err(1, "BN_CTX_new");
1070eae58378Sclaudio 
1071eae58378Sclaudio 	TAILQ_INIT(&q);
1072eae58378Sclaudio 
1073*b5fa5d51Sclaudio 	if ((msgq = msgbuf_new_reader(sizeof(size_t), io_parse_hdr, NULL)) ==
1074*b5fa5d51Sclaudio 	    NULL)
107525d36c5cSclaudio 		err(1, NULL);
1076eae58378Sclaudio 
1077eae58378Sclaudio 	pfd.fd = fd;
1078eae58378Sclaudio 
1079eae58378Sclaudio 	for (;;) {
1080eae58378Sclaudio 		pfd.events = POLLIN;
108125d36c5cSclaudio 		if (msgbuf_queuelen(msgq) > 0)
1082eae58378Sclaudio 			pfd.events |= POLLOUT;
1083eae58378Sclaudio 
10847ba5db23Sclaudio 		if (poll(&pfd, 1, INFTIM) == -1) {
10857ba5db23Sclaudio 			if (errno == EINTR)
10867ba5db23Sclaudio 				continue;
1087eae58378Sclaudio 			err(1, "poll");
10887ba5db23Sclaudio 		}
1089eae58378Sclaudio 		if ((pfd.revents & (POLLERR|POLLNVAL)))
1090eae58378Sclaudio 			errx(1, "poll: bad descriptor");
1091eae58378Sclaudio 
1092eae58378Sclaudio 		/* If the parent closes, return immediately. */
1093eae58378Sclaudio 
1094eae58378Sclaudio 		if ((pfd.revents & POLLHUP))
1095eae58378Sclaudio 			break;
1096eae58378Sclaudio 
1097eae58378Sclaudio 		if ((pfd.revents & POLLIN)) {
1098*b5fa5d51Sclaudio 			switch (ibuf_read(fd, msgq)) {
1099*b5fa5d51Sclaudio 			case -1:
1100*b5fa5d51Sclaudio 				err(1, "ibuf_read");
1101*b5fa5d51Sclaudio 			case 0:
1102*b5fa5d51Sclaudio 				errx(1, "ibuf_read: connection closed");
1103*b5fa5d51Sclaudio 			}
1104*b5fa5d51Sclaudio 			while ((b = io_buf_get(msgq)) != NULL) {
1105eae58378Sclaudio 				entp = calloc(1, sizeof(struct entity));
1106eae58378Sclaudio 				if (entp == NULL)
1107eae58378Sclaudio 					err(1, NULL);
11087eb79a4aSclaudio 				entity_read_req(b, entp);
1109eae58378Sclaudio 				TAILQ_INSERT_TAIL(&q, entp, entries);
11107eb79a4aSclaudio 				ibuf_free(b);
11117eb79a4aSclaudio 			}
1112eae58378Sclaudio 		}
1113eae58378Sclaudio 
1114eae58378Sclaudio 		if (pfd.revents & POLLOUT) {
111525d36c5cSclaudio 			if (msgbuf_write(fd, msgq) == -1) {
11169aadc625Sclaudio 				if (errno == EPIPE)
1117eae58378Sclaudio 					errx(1, "write: connection closed");
11189aadc625Sclaudio 				else
1119eae58378Sclaudio 					err(1, "write");
1120eae58378Sclaudio 			}
1121eae58378Sclaudio 		}
1122eae58378Sclaudio 
112325d36c5cSclaudio 		parse_entity(&q, msgq);
1124eae58378Sclaudio 	}
1125eae58378Sclaudio 
1126eae58378Sclaudio 	while ((entp = TAILQ_FIRST(&q)) != NULL) {
1127eae58378Sclaudio 		TAILQ_REMOVE(&q, entp, entries);
1128eae58378Sclaudio 		entity_free(entp);
1129eae58378Sclaudio 	}
1130eae58378Sclaudio 
113191176c18Sjob 	auth_tree_free(&auths);
113291176c18Sjob 	crl_tree_free(&crlt);
1133eae58378Sclaudio 
1134eae58378Sclaudio 	X509_STORE_CTX_free(ctx);
1135318f0572Sjob 	BN_CTX_free(bn_ctx);
1136318f0572Sjob 
113725d36c5cSclaudio 	msgbuf_free(msgq);
113891176c18Sjob 	ibuf_free(inbuf);
113991176c18Sjob 
11401d8c6443Stb 	if (certid > CERTID_MAX)
11411d8c6443Stb 		errx(1, "processing incomplete: too many certificates");
11421d8c6443Stb 
11436feb6ad0Sclaudio 	exit(0);
1144eae58378Sclaudio }
1145