xref: /openbsd-src/usr.sbin/rpki-client/cert.c (revision a56972eb38867ce9ead4c1820b27a8a4ecf0c408)
1*a56972ebStb /*	$OpenBSD: cert.c,v 1.155 2024/12/18 21:12:26 tb Exp $ */
29a7e9e7fSjob /*
3740e9a54Stb  * Copyright (c) 2022 Theo Buehler <tb@openbsd.org>
46b83d8e3Sjob  * Copyright (c) 2021 Job Snijders <job@openbsd.org>
59a7e9e7fSjob  * Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv>
69a7e9e7fSjob  *
79a7e9e7fSjob  * Permission to use, copy, modify, and distribute this software for any
89a7e9e7fSjob  * purpose with or without fee is hereby granted, provided that the above
99a7e9e7fSjob  * copyright notice and this permission notice appear in all copies.
109a7e9e7fSjob  *
119a7e9e7fSjob  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
129a7e9e7fSjob  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
139a7e9e7fSjob  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
149a7e9e7fSjob  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
159a7e9e7fSjob  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
169a7e9e7fSjob  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
179a7e9e7fSjob  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
189a7e9e7fSjob  */
199a7e9e7fSjob 
209a7e9e7fSjob #include <assert.h>
219a7e9e7fSjob #include <err.h>
229a7e9e7fSjob #include <stdlib.h>
239a7e9e7fSjob #include <string.h>
249a7e9e7fSjob #include <unistd.h>
259a7e9e7fSjob 
26a1753de6Sclaudio #include <openssl/asn1.h>
27a1753de6Sclaudio #include <openssl/x509.h>
28f9f55a97Stb #include <openssl/x509v3.h>
299a7e9e7fSjob 
309a7e9e7fSjob #include "extern.h"
319a7e9e7fSjob 
32f9f55a97Stb extern ASN1_OBJECT	*certpol_oid;	/* id-cp-ipAddr-asNumber cert policy */
33de9b6f5dSclaudio extern ASN1_OBJECT	*carepo_oid;	/* 1.3.6.1.5.5.7.48.5 (caRepository) */
34de9b6f5dSclaudio extern ASN1_OBJECT	*manifest_oid;	/* 1.3.6.1.5.5.7.48.10 (rpkiManifest) */
35de9b6f5dSclaudio extern ASN1_OBJECT	*notify_oid;	/* 1.3.6.1.5.5.7.48.13 (rpkiNotify) */
36d2e465bbSclaudio 
371d8c6443Stb int certid = TALSZ_MAX;
380bc420b9Sclaudio 
399a7e9e7fSjob /*
409a7e9e7fSjob  * Append an IP address structure to our list of results.
416822deefStb  * This will also constrain us to having at most one inheritance
426822deefStb  * statement per AFI and also not have overlapping ranges (as prohibited
439a7e9e7fSjob  * in section 2.2.3.6).
449a7e9e7fSjob  * It does not make sure that ranges can't coalesce, that is, that any
459a7e9e7fSjob  * two ranges abut each other.
469a7e9e7fSjob  * This is warned against in section 2.2.3.6, but doesn't change the
479a7e9e7fSjob  * semantics of the system.
4895404126Stb  * Returns zero on failure (IP overlap) non-zero on success.
499a7e9e7fSjob  */
509a7e9e7fSjob static int
51381ee599Stb append_ip(const char *fn, struct cert_ip *ips, size_t *num_ips,
52565a9191Stb     const struct cert_ip *ip)
539a7e9e7fSjob {
54381ee599Stb 	if (!ip_addr_check_overlap(ip, fn, ips, *num_ips, 0))
559a7e9e7fSjob 		return 0;
56381ee599Stb 	ips[(*num_ips)++] = *ip;
579a7e9e7fSjob 	return 1;
589a7e9e7fSjob }
599a7e9e7fSjob 
609a7e9e7fSjob /*
619a7e9e7fSjob  * Append an AS identifier structure to our list of results.
629a7e9e7fSjob  * Makes sure that the identifiers do not overlap or improperly inherit
639a7e9e7fSjob  * as defined by RFC 3779 section 3.3.
649a7e9e7fSjob  */
659a7e9e7fSjob static int
66381ee599Stb append_as(const char *fn, struct cert_as *ases, size_t *num_ases,
67565a9191Stb     const struct cert_as *as)
689a7e9e7fSjob {
69381ee599Stb 	if (!as_check_overlap(as, fn, ases, *num_ases, 0))
709a7e9e7fSjob 		return 0;
71381ee599Stb 	ases[(*num_ases)++] = *as;
729a7e9e7fSjob 	return 1;
739a7e9e7fSjob }
749a7e9e7fSjob 
759a7e9e7fSjob /*
761356ae1fStb  * Parse a range of AS identifiers as in 3.2.3.8.
779a7e9e7fSjob  * Returns zero on failure, non-zero on success.
789a7e9e7fSjob  */
79565a9191Stb int
80381ee599Stb sbgp_as_range(const char *fn, struct cert_as *ases, size_t *num_ases,
81565a9191Stb     const ASRange *range)
829a7e9e7fSjob {
839a7e9e7fSjob 	struct cert_as		 as;
849a7e9e7fSjob 
859a7e9e7fSjob 	memset(&as, 0, sizeof(struct cert_as));
869a7e9e7fSjob 	as.type = CERT_AS_RANGE;
879a7e9e7fSjob 
88230ecba3Stb 	if (!as_id_parse(range->min, &as.range.min)) {
89dcb56be1Sjob 		warnx("%s: RFC 3779 section 3.2.3.8 (via RFC 1930): "
90565a9191Stb 		    "malformed AS identifier", fn);
91230ecba3Stb 		return 0;
929a7e9e7fSjob 	}
939a7e9e7fSjob 
94230ecba3Stb 	if (!as_id_parse(range->max, &as.range.max)) {
95dcb56be1Sjob 		warnx("%s: RFC 3779 section 3.2.3.8 (via RFC 1930): "
96565a9191Stb 		    "malformed AS identifier", fn);
97230ecba3Stb 		return 0;
989a7e9e7fSjob 	}
999a7e9e7fSjob 
1009a7e9e7fSjob 	if (as.range.max == as.range.min) {
1019a7e9e7fSjob 		warnx("%s: RFC 3379 section 3.2.3.8: ASRange: "
102565a9191Stb 		    "range is singular", fn);
103230ecba3Stb 		return 0;
1049a7e9e7fSjob 	} else if (as.range.max < as.range.min) {
1059a7e9e7fSjob 		warnx("%s: RFC 3379 section 3.2.3.8: ASRange: "
106565a9191Stb 		    "range is out of order", fn);
107230ecba3Stb 		return 0;
108c1baef53Sclaudio 	}
1099a7e9e7fSjob 
110381ee599Stb 	return append_as(fn, ases, num_ases, &as);
1119a7e9e7fSjob }
1129a7e9e7fSjob 
1139a7e9e7fSjob /*
1149a7e9e7fSjob  * Parse an entire 3.2.3.10 integer type.
1159a7e9e7fSjob  */
116565a9191Stb int
117381ee599Stb sbgp_as_id(const char *fn, struct cert_as *ases, size_t *num_ases,
118565a9191Stb     const ASN1_INTEGER *i)
1199a7e9e7fSjob {
1209a7e9e7fSjob 	struct cert_as	 as;
1219a7e9e7fSjob 
1229a7e9e7fSjob 	memset(&as, 0, sizeof(struct cert_as));
1239a7e9e7fSjob 	as.type = CERT_AS_ID;
1249a7e9e7fSjob 
1259a7e9e7fSjob 	if (!as_id_parse(i, &as.id)) {
126dcb56be1Sjob 		warnx("%s: RFC 3779 section 3.2.3.10 (via RFC 1930): "
127565a9191Stb 		    "malformed AS identifier", fn);
1289a7e9e7fSjob 		return 0;
129c1baef53Sclaudio 	}
130c1baef53Sclaudio 	if (as.id == 0) {
131dcb56be1Sjob 		warnx("%s: RFC 3779 section 3.2.3.10 (via RFC 1930): "
132565a9191Stb 		    "AS identifier zero is reserved", fn);
1339a7e9e7fSjob 		return 0;
1349a7e9e7fSjob 	}
1359a7e9e7fSjob 
136381ee599Stb 	return append_as(fn, ases, num_ases, &as);
1379a7e9e7fSjob }
1389a7e9e7fSjob 
1399a7e9e7fSjob static int
140381ee599Stb sbgp_as_inherit(const char *fn, struct cert_as *ases, size_t *num_ases)
1419a7e9e7fSjob {
1429a7e9e7fSjob 	struct cert_as as;
1439a7e9e7fSjob 
1449a7e9e7fSjob 	memset(&as, 0, sizeof(struct cert_as));
1459a7e9e7fSjob 	as.type = CERT_AS_INHERIT;
146dd7dce3fSclaudio 
147381ee599Stb 	return append_as(fn, ases, num_ases, &as);
1489a7e9e7fSjob }
1499a7e9e7fSjob 
15018c42b30Stb int
15118c42b30Stb sbgp_parse_assysnum(const char *fn, const ASIdentifiers *asidentifiers,
152381ee599Stb     struct cert_as **out_as, size_t *out_num_ases)
1539a7e9e7fSjob {
154dd7dce3fSclaudio 	const ASIdOrRanges	*aors = NULL;
15518c42b30Stb 	struct cert_as		*as = NULL;
156381ee599Stb 	size_t			 num_ases = 0, num;
15718c42b30Stb 	int			 i;
1589a7e9e7fSjob 
159381ee599Stb 	assert(*out_as == NULL && *out_num_ases == 0);
1609a7e9e7fSjob 
161230ecba3Stb 	if (asidentifiers->rdi != NULL) {
1629a7e9e7fSjob 		warnx("%s: RFC 6487 section 4.8.11: autonomousSysNum: "
16318c42b30Stb 		    "should not have RDI values", fn);
1649a7e9e7fSjob 		goto out;
1659a7e9e7fSjob 	}
1669a7e9e7fSjob 
167230ecba3Stb 	if (asidentifiers->asnum == NULL) {
16880272c49Sderaadt 		warnx("%s: RFC 6487 section 4.8.11: autonomousSysNum: "
16918c42b30Stb 		    "no AS number resource set", fn);
1709a7e9e7fSjob 		goto out;
1719a7e9e7fSjob 	}
1729a7e9e7fSjob 
173dd7dce3fSclaudio 	switch (asidentifiers->asnum->type) {
174dd7dce3fSclaudio 	case ASIdentifierChoice_inherit:
175381ee599Stb 		num = 1;
176dd7dce3fSclaudio 		break;
177dd7dce3fSclaudio 	case ASIdentifierChoice_asIdsOrRanges:
178dd7dce3fSclaudio 		aors = asidentifiers->asnum->u.asIdsOrRanges;
179381ee599Stb 		num = sk_ASIdOrRange_num(aors);
180dd7dce3fSclaudio 		break;
181dd7dce3fSclaudio 	default:
182dd7dce3fSclaudio 		warnx("%s: RFC 3779 section 3.2.3.2: ASIdentifierChoice: "
18318c42b30Stb 		    "unknown type %d", fn, asidentifiers->asnum->type);
1849a7e9e7fSjob 		goto out;
185dd7dce3fSclaudio 	}
186dd7dce3fSclaudio 
187381ee599Stb 	if (num == 0) {
18818c42b30Stb 		warnx("%s: RFC 6487 section 4.8.11: empty asIdsOrRanges", fn);
18926660650Stb 		goto out;
19026660650Stb 	}
191381ee599Stb 	if (num >= MAX_AS_SIZE) {
192dd7dce3fSclaudio 		warnx("%s: too many AS number entries: limit %d",
19318c42b30Stb 		    fn, MAX_AS_SIZE);
194dd7dce3fSclaudio 		goto out;
195dd7dce3fSclaudio 	}
196381ee599Stb 	as = calloc(num, sizeof(struct cert_as));
19718c42b30Stb 	if (as == NULL)
198dd7dce3fSclaudio 		err(1, NULL);
199dd7dce3fSclaudio 
200dd7dce3fSclaudio 	if (aors == NULL) {
201381ee599Stb 		if (!sbgp_as_inherit(fn, as, &num_ases))
202dd7dce3fSclaudio 			goto out;
203dd7dce3fSclaudio 	}
204dd7dce3fSclaudio 
205dd7dce3fSclaudio 	for (i = 0; i < sk_ASIdOrRange_num(aors); i++) {
206dd7dce3fSclaudio 		const ASIdOrRange *aor;
207dd7dce3fSclaudio 
208dd7dce3fSclaudio 		aor = sk_ASIdOrRange_value(aors, i);
209dd7dce3fSclaudio 		switch (aor->type) {
210dd7dce3fSclaudio 		case ASIdOrRange_id:
211381ee599Stb 			if (!sbgp_as_id(fn, as, &num_ases, aor->u.id))
212dd7dce3fSclaudio 				goto out;
213dd7dce3fSclaudio 			break;
214dd7dce3fSclaudio 		case ASIdOrRange_range:
215381ee599Stb 			if (!sbgp_as_range(fn, as, &num_ases, aor->u.range))
216dd7dce3fSclaudio 				goto out;
217dd7dce3fSclaudio 			break;
218dd7dce3fSclaudio 		default:
219dd7dce3fSclaudio 			warnx("%s: RFC 3779 section 3.2.3.5: ASIdOrRange: "
22018c42b30Stb 			    "unknown type %d", fn, aor->type);
221dd7dce3fSclaudio 			goto out;
222dd7dce3fSclaudio 		}
223dd7dce3fSclaudio 	}
2249a7e9e7fSjob 
22518c42b30Stb 	*out_as = as;
226381ee599Stb 	*out_num_ases = num_ases;
22718c42b30Stb 
22818c42b30Stb 	return 1;
22918c42b30Stb 
23018c42b30Stb  out:
23118c42b30Stb 	free(as);
23218c42b30Stb 
23318c42b30Stb 	return 0;
23418c42b30Stb }
23518c42b30Stb 
23618c42b30Stb /*
23718c42b30Stb  * Parse RFC 6487 4.8.11 X509v3 extension, with syntax documented in RFC
23818c42b30Stb  * 3779 starting in section 3.2.
23918c42b30Stb  * Returns zero on failure, non-zero on success.
24018c42b30Stb  */
24118c42b30Stb static int
2422b470c61Stb sbgp_assysnum(const char *fn, struct cert *cert, X509_EXTENSION *ext)
24318c42b30Stb {
24418c42b30Stb 	ASIdentifiers		*asidentifiers = NULL;
24518c42b30Stb 	int			 rc = 0;
24618c42b30Stb 
24718c42b30Stb 	if (!X509_EXTENSION_get_critical(ext)) {
24818c42b30Stb 		warnx("%s: RFC 6487 section 4.8.11: autonomousSysNum: "
2492b470c61Stb 		    "extension not critical", fn);
25018c42b30Stb 		goto out;
25118c42b30Stb 	}
25218c42b30Stb 
25318c42b30Stb 	if ((asidentifiers = X509V3_EXT_d2i(ext)) == NULL) {
25418c42b30Stb 		warnx("%s: RFC 6487 section 4.8.11: autonomousSysNum: "
2552b470c61Stb 		    "failed extension parse", fn);
25618c42b30Stb 		goto out;
25718c42b30Stb 	}
25818c42b30Stb 
259381ee599Stb 	if (!sbgp_parse_assysnum(fn, asidentifiers, &cert->ases,
260381ee599Stb 	    &cert->num_ases))
26118c42b30Stb 		goto out;
26218c42b30Stb 
2639a7e9e7fSjob 	rc = 1;
2649a7e9e7fSjob  out:
265230ecba3Stb 	ASIdentifiers_free(asidentifiers);
2669a7e9e7fSjob 	return rc;
2679a7e9e7fSjob }
2689a7e9e7fSjob 
2699a7e9e7fSjob /*
2706de5d2cdStb  * Construct a RFC 3779 2.2.3.8 range from its bit string.
2716de5d2cdStb  * Returns zero on failure, non-zero on success.
2726de5d2cdStb  */
273565a9191Stb int
274381ee599Stb sbgp_addr(const char *fn, struct cert_ip *ips, size_t *num_ips, enum afi afi,
275565a9191Stb     const ASN1_BIT_STRING *bs)
2766de5d2cdStb {
277e212aee8Stb 	struct cert_ip	ip;
278e212aee8Stb 
279e212aee8Stb 	memset(&ip, 0, sizeof(struct cert_ip));
280e212aee8Stb 
281e212aee8Stb 	ip.afi = afi;
282e212aee8Stb 	ip.type = CERT_IP_ADDR;
283e212aee8Stb 
284565a9191Stb 	if (!ip_addr_parse(bs, afi, fn, &ip.ip)) {
2856de5d2cdStb 		warnx("%s: RFC 3779 section 2.2.3.8: IPAddress: "
286565a9191Stb 		    "invalid IP address", fn);
2876de5d2cdStb 		return 0;
2886de5d2cdStb 	}
2896de5d2cdStb 
290e212aee8Stb 	if (!ip_cert_compose_ranges(&ip)) {
2916de5d2cdStb 		warnx("%s: RFC 3779 section 2.2.3.8: IPAddress: "
292565a9191Stb 		    "IP address range reversed", fn);
2936de5d2cdStb 		return 0;
2946de5d2cdStb 	}
2956de5d2cdStb 
296381ee599Stb 	return append_ip(fn, ips, num_ips, &ip);
2976de5d2cdStb }
2986de5d2cdStb 
2996de5d2cdStb /*
3009a7e9e7fSjob  * Parse RFC 3779 2.2.3.9 range of addresses.
30195404126Stb  * Returns zero on failure, non-zero on success.
3029a7e9e7fSjob  */
303565a9191Stb int
304381ee599Stb sbgp_addr_range(const char *fn, struct cert_ip *ips, size_t *num_ips,
305565a9191Stb     enum afi afi, const IPAddressRange *range)
3069a7e9e7fSjob {
307e212aee8Stb 	struct cert_ip	ip;
308e212aee8Stb 
309e212aee8Stb 	memset(&ip, 0, sizeof(struct cert_ip));
310e212aee8Stb 
311e212aee8Stb 	ip.afi = afi;
312e212aee8Stb 	ip.type = CERT_IP_RANGE;
313e212aee8Stb 
314565a9191Stb 	if (!ip_addr_parse(range->min, afi, fn, &ip.range.min)) {
3159a7e9e7fSjob 		warnx("%s: RFC 3779 section 2.2.3.9: IPAddressRange: "
316565a9191Stb 		    "invalid IP address", fn);
31783093aedStb 		return 0;
3189a7e9e7fSjob 	}
3199a7e9e7fSjob 
320565a9191Stb 	if (!ip_addr_parse(range->max, afi, fn, &ip.range.max)) {
3219a7e9e7fSjob 		warnx("%s: RFC 3779 section 2.2.3.9: IPAddressRange: "
322565a9191Stb 		    "invalid IP address", fn);
32383093aedStb 		return 0;
3249a7e9e7fSjob 	}
3259a7e9e7fSjob 
326e212aee8Stb 	if (!ip_cert_compose_ranges(&ip)) {
3279a7e9e7fSjob 		warnx("%s: RFC 3779 section 2.2.3.9: IPAddressRange: "
328565a9191Stb 		    "IP address range reversed", fn);
32983093aedStb 		return 0;
3309a7e9e7fSjob 	}
3319a7e9e7fSjob 
332381ee599Stb 	return append_ip(fn, ips, num_ips, &ip);
333e212aee8Stb }
334e212aee8Stb 
335e212aee8Stb static int
336381ee599Stb sbgp_addr_inherit(const char *fn, struct cert_ip *ips, size_t *num_ips,
337565a9191Stb     enum afi afi)
338e212aee8Stb {
339e212aee8Stb 	struct cert_ip	ip;
340e212aee8Stb 
341e212aee8Stb 	memset(&ip, 0, sizeof(struct cert_ip));
342e212aee8Stb 
343e212aee8Stb 	ip.afi = afi;
344e212aee8Stb 	ip.type = CERT_IP_INHERIT;
345e212aee8Stb 
346381ee599Stb 	return append_ip(fn, ips, num_ips, &ip);
3479a7e9e7fSjob }
3489a7e9e7fSjob 
34918c42b30Stb int
35018c42b30Stb sbgp_parse_ipaddrblk(const char *fn, const IPAddrBlocks *addrblk,
351381ee599Stb     struct cert_ip **out_ips, size_t *out_num_ips)
35218c42b30Stb {
35318c42b30Stb 	const IPAddressFamily	*af;
35418c42b30Stb 	const IPAddressOrRanges	*aors;
35518c42b30Stb 	const IPAddressOrRange	*aor;
35618c42b30Stb 	enum afi		 afi;
35718c42b30Stb 	struct cert_ip		*ips = NULL;
358381ee599Stb 	size_t			 num_ips = 0, num;
359de494ec3Stb 	int			 ipv4_seen = 0, ipv6_seen = 0;
360de494ec3Stb 	int			 i, j, ipaddrblocksz;
36118c42b30Stb 
362381ee599Stb 	assert(*out_ips == NULL && *out_num_ips == 0);
36318c42b30Stb 
364de494ec3Stb 	ipaddrblocksz = sk_IPAddressFamily_num(addrblk);
365de494ec3Stb 	if (ipaddrblocksz != 1 && ipaddrblocksz != 2) {
366de494ec3Stb 		warnx("%s: RFC 6487 section 4.8.10: unexpected number of "
367de494ec3Stb 		    "ipAddrBlocks (got %d, expected 1 or 2)",
368de494ec3Stb 		    fn, ipaddrblocksz);
369de494ec3Stb 		goto out;
370de494ec3Stb 	}
371de494ec3Stb 
372de494ec3Stb 	for (i = 0; i < ipaddrblocksz; i++) {
37318c42b30Stb 		af = sk_IPAddressFamily_value(addrblk, i);
37418c42b30Stb 
37518c42b30Stb 		switch (af->ipAddressChoice->type) {
37618c42b30Stb 		case IPAddressChoice_inherit:
37718c42b30Stb 			aors = NULL;
378381ee599Stb 			num = num_ips + 1;
37918c42b30Stb 			break;
38018c42b30Stb 		case IPAddressChoice_addressesOrRanges:
38118c42b30Stb 			aors = af->ipAddressChoice->u.addressesOrRanges;
382381ee599Stb 			num = num_ips + sk_IPAddressOrRange_num(aors);
38318c42b30Stb 			break;
38418c42b30Stb 		default:
38518c42b30Stb 			warnx("%s: RFC 3779: IPAddressChoice: unknown type %d",
38618c42b30Stb 			    fn, af->ipAddressChoice->type);
38718c42b30Stb 			goto out;
38818c42b30Stb 		}
389381ee599Stb 		if (num == num_ips) {
39018c42b30Stb 			warnx("%s: RFC 6487 section 4.8.10: "
39118c42b30Stb 			    "empty ipAddressesOrRanges", fn);
39218c42b30Stb 			goto out;
39318c42b30Stb 		}
39418c42b30Stb 
395381ee599Stb 		if (num >= MAX_IP_SIZE)
39618c42b30Stb 			goto out;
397381ee599Stb 		ips = recallocarray(ips, num_ips, num, sizeof(struct cert_ip));
39818c42b30Stb 		if (ips == NULL)
39918c42b30Stb 			err(1, NULL);
40018c42b30Stb 
40118c42b30Stb 		if (!ip_addr_afi_parse(fn, af->addressFamily, &afi)) {
40218c42b30Stb 			warnx("%s: RFC 3779: invalid AFI", fn);
40318c42b30Stb 			goto out;
40418c42b30Stb 		}
40518c42b30Stb 
406de494ec3Stb 		switch (afi) {
407de494ec3Stb 		case AFI_IPV4:
408de494ec3Stb 			if (ipv4_seen++ > 0) {
409de494ec3Stb 				warnx("%s: RFC 6487 section 4.8.10: "
410de494ec3Stb 				    "IPv4 appears twice", fn);
411de494ec3Stb 				goto out;
412de494ec3Stb 			}
413de494ec3Stb 			break;
414de494ec3Stb 		case AFI_IPV6:
415de494ec3Stb 			if (ipv6_seen++ > 0) {
416de494ec3Stb 				warnx("%s: RFC 6487 section 4.8.10: "
417de494ec3Stb 				    "IPv6 appears twice", fn);
418de494ec3Stb 				goto out;
419de494ec3Stb 			}
420de494ec3Stb 			break;
421de494ec3Stb 		}
422de494ec3Stb 
42318c42b30Stb 		if (aors == NULL) {
424381ee599Stb 			if (!sbgp_addr_inherit(fn, ips, &num_ips, afi))
42518c42b30Stb 				goto out;
42618c42b30Stb 			continue;
42718c42b30Stb 		}
42818c42b30Stb 
42918c42b30Stb 		for (j = 0; j < sk_IPAddressOrRange_num(aors); j++) {
43018c42b30Stb 			aor = sk_IPAddressOrRange_value(aors, j);
43118c42b30Stb 			switch (aor->type) {
43218c42b30Stb 			case IPAddressOrRange_addressPrefix:
433381ee599Stb 				if (!sbgp_addr(fn, ips, &num_ips, afi,
43418c42b30Stb 				    aor->u.addressPrefix))
43518c42b30Stb 					goto out;
43618c42b30Stb 				break;
43718c42b30Stb 			case IPAddressOrRange_addressRange:
438381ee599Stb 				if (!sbgp_addr_range(fn, ips, &num_ips, afi,
43918c42b30Stb 				    aor->u.addressRange))
44018c42b30Stb 					goto out;
44118c42b30Stb 				break;
44218c42b30Stb 			default:
44318c42b30Stb 				warnx("%s: RFC 3779: IPAddressOrRange: "
44418c42b30Stb 				    "unknown type %d", fn, aor->type);
44518c42b30Stb 				goto out;
44618c42b30Stb 			}
44718c42b30Stb 		}
44818c42b30Stb 	}
44918c42b30Stb 
45018c42b30Stb 	*out_ips = ips;
451381ee599Stb 	*out_num_ips = num_ips;
45218c42b30Stb 
45318c42b30Stb 	return 1;
45418c42b30Stb 
45518c42b30Stb  out:
45618c42b30Stb 	free(ips);
45718c42b30Stb 
45818c42b30Stb 	return 0;
45918c42b30Stb }
46018c42b30Stb 
4619a7e9e7fSjob /*
4629a7e9e7fSjob  * Parse an sbgp-ipAddrBlock X509 extension, RFC 6487 4.8.10, with
4639a7e9e7fSjob  * syntax documented in RFC 3779 starting in section 2.2.
4649a7e9e7fSjob  * Returns zero on failure, non-zero on success.
4659a7e9e7fSjob  */
4669a7e9e7fSjob static int
4672b470c61Stb sbgp_ipaddrblk(const char *fn, struct cert *cert, X509_EXTENSION *ext)
4689a7e9e7fSjob {
469891d6bceSjob 	IPAddrBlocks	*addrblk = NULL;
47018c42b30Stb 	int		 rc = 0;
4719a7e9e7fSjob 
4720bb66b69Stb 	if (!X509_EXTENSION_get_critical(ext)) {
473c0528901Stb 		warnx("%s: RFC 6487 section 4.8.10: sbgp-ipAddrBlock: "
4742b470c61Stb 		    "extension not critical", fn);
4750bb66b69Stb 		goto out;
4760bb66b69Stb 	}
4770bb66b69Stb 
47883093aedStb 	if ((addrblk = X509V3_EXT_d2i(ext)) == NULL) {
479c0528901Stb 		warnx("%s: RFC 6487 section 4.8.10: sbgp-ipAddrBlock: "
4802b470c61Stb 		    "failed extension parse", fn);
4819a7e9e7fSjob 		goto out;
4829a7e9e7fSjob 	}
4839a7e9e7fSjob 
484381ee599Stb 	if (!sbgp_parse_ipaddrblk(fn, addrblk, &cert->ips, &cert->num_ips))
4859a7e9e7fSjob 		goto out;
486b0a85f64Stb 
487381ee599Stb 	if (cert->num_ips == 0) {
4882b470c61Stb 		warnx("%s: RFC 6487 section 4.8.10: empty ipAddrBlock", fn);
48926660650Stb 		goto out;
49026660650Stb 	}
49126660650Stb 
4929a7e9e7fSjob 	rc = 1;
4939a7e9e7fSjob  out:
494891d6bceSjob 	IPAddrBlocks_free(addrblk);
4959a7e9e7fSjob 	return rc;
4969a7e9e7fSjob }
4979a7e9e7fSjob 
498d23e87a2Stb /*
499c5305b1dStb  * Parse "Subject Information Access" extension for a CA cert,
500c5305b1dStb  * RFC 6487, section 4.8.8.1 and RFC 8182, section 3.2.
5012edf3539Stb  * Returns zero on failure, non-zero on success.
5022edf3539Stb  */
5032edf3539Stb static int
5042b470c61Stb sbgp_sia(const char *fn, struct cert *cert, X509_EXTENSION *ext)
5052edf3539Stb {
5062edf3539Stb 	AUTHORITY_INFO_ACCESS	*sia = NULL;
5072edf3539Stb 	ACCESS_DESCRIPTION	*ad;
5082edf3539Stb 	ASN1_OBJECT		*oid;
509c180fb4aSjob 	const char		*mftfilename;
51024ad82f4Stb 	char			*carepo = NULL, *rpkimft = NULL, *notify = NULL;
5112edf3539Stb 	int			 i, rc = 0;
5122edf3539Stb 
513c5305b1dStb 	assert(cert->repo == NULL && cert->mft == NULL && cert->notify == NULL);
514c5305b1dStb 
5152edf3539Stb 	if (X509_EXTENSION_get_critical(ext)) {
5162edf3539Stb 		warnx("%s: RFC 6487 section 4.8.8: SIA: "
5172b470c61Stb 		    "extension not non-critical", fn);
5182edf3539Stb 		goto out;
5192edf3539Stb 	}
5202edf3539Stb 
5212edf3539Stb 	if ((sia = X509V3_EXT_d2i(ext)) == NULL) {
522c0528901Stb 		warnx("%s: RFC 6487 section 4.8.8: SIA: failed extension parse",
5232b470c61Stb 		    fn);
5242edf3539Stb 		goto out;
5252edf3539Stb 	}
5262edf3539Stb 
5272edf3539Stb 	for (i = 0; i < sk_ACCESS_DESCRIPTION_num(sia); i++) {
5282edf3539Stb 		ad = sk_ACCESS_DESCRIPTION_value(sia, i);
5292edf3539Stb 
5302edf3539Stb 		oid = ad->method;
5312edf3539Stb 
5322edf3539Stb 		if (OBJ_cmp(oid, carepo_oid) == 0) {
5332b872fe6Stb 			if (!x509_location(fn, "SIA: caRepository",
534c5305b1dStb 			    ad->location, &carepo))
5352edf3539Stb 				goto out;
536c5305b1dStb 			if (cert->repo == NULL && strncasecmp(carepo,
537c5305b1dStb 			    RSYNC_PROTO, RSYNC_PROTO_LEN) == 0) {
53839ab19dfStb 				if (carepo[strlen(carepo) - 1] != '/') {
53939ab19dfStb 					char *carepo_tmp;
54039ab19dfStb 
54139ab19dfStb 					if (asprintf(&carepo_tmp, "%s/",
54239ab19dfStb 					    carepo) == -1)
54339ab19dfStb 						errx(1, NULL);
54439ab19dfStb 					free(carepo);
54539ab19dfStb 					carepo = carepo_tmp;
54639ab19dfStb 				}
54739ab19dfStb 
548c5305b1dStb 				cert->repo = carepo;
549c5305b1dStb 				carepo = NULL;
550c5305b1dStb 				continue;
551c5305b1dStb 			}
552c5305b1dStb 			if (verbose)
553c5305b1dStb 				warnx("%s: RFC 6487 section 4.8.8: SIA: "
554c5305b1dStb 				    "ignoring location %s", fn, carepo);
555c5305b1dStb 			free(carepo);
556c5305b1dStb 			carepo = NULL;
5572edf3539Stb 		} else if (OBJ_cmp(oid, manifest_oid) == 0) {
5582b872fe6Stb 			if (!x509_location(fn, "SIA: rpkiManifest",
559c5305b1dStb 			    ad->location, &rpkimft))
5602edf3539Stb 				goto out;
561c5305b1dStb 			if (cert->mft == NULL && strncasecmp(rpkimft,
562c5305b1dStb 			    RSYNC_PROTO, RSYNC_PROTO_LEN) == 0) {
563c5305b1dStb 				cert->mft = rpkimft;
564c5305b1dStb 				rpkimft = NULL;
565c5305b1dStb 				continue;
566c5305b1dStb 			}
567c5305b1dStb 			if (verbose)
568c5305b1dStb 				warnx("%s: RFC 6487 section 4.8.8: SIA: "
569c5305b1dStb 				    "ignoring location %s", fn, rpkimft);
570c5305b1dStb 			free(rpkimft);
571c5305b1dStb 			rpkimft = NULL;
5722edf3539Stb 		} else if (OBJ_cmp(oid, notify_oid) == 0) {
5732b470c61Stb 			if (!x509_location(fn, "SIA: rpkiNotify",
57424ad82f4Stb 			    ad->location, &notify))
5752edf3539Stb 				goto out;
57624ad82f4Stb 			if (strncasecmp(notify, HTTPS_PROTO,
5772b872fe6Stb 			    HTTPS_PROTO_LEN) != 0) {
5782b872fe6Stb 				warnx("%s: non-https uri in rpkiNotify: %s",
5792b872fe6Stb 				    fn, cert->notify);
58024ad82f4Stb 				free(notify);
5812b872fe6Stb 				goto out;
5822b872fe6Stb 			}
58324ad82f4Stb 			if (cert->notify != NULL) {
58424ad82f4Stb 				warnx("%s: unexpected rpkiNotify accessMethod",
58524ad82f4Stb 				    fn);
58624ad82f4Stb 				free(notify);
58724ad82f4Stb 				goto out;
58824ad82f4Stb 			}
58924ad82f4Stb 			cert->notify = notify;
59024ad82f4Stb 			notify = NULL;
59178268cf8Stb 		} else {
59278268cf8Stb 			char buf[128];
59378268cf8Stb 
59478268cf8Stb 			OBJ_obj2txt(buf, sizeof(buf), oid, 0);
59578268cf8Stb 			warnx("%s: RFC 6487 section 4.8.8.1: unexpected"
59678268cf8Stb 			    " accessMethod: %s", fn, buf);
59778268cf8Stb 			goto out;
5982edf3539Stb 		}
5992edf3539Stb 	}
6002edf3539Stb 
6012b470c61Stb 	if (cert->mft == NULL || cert->repo == NULL) {
602572cd5a8Stb 		warnx("%s: RFC 6487 section 4.8.8: SIA: missing caRepository "
6032b470c61Stb 		    "or rpkiManifest", fn);
6042edf3539Stb 		goto out;
6052edf3539Stb 	}
6062edf3539Stb 
6072b470c61Stb 	mftfilename = strrchr(cert->mft, '/');
6086747684dSjob 	if (mftfilename == NULL) {
6092b470c61Stb 		warnx("%s: SIA: invalid rpkiManifest entry", fn);
6106747684dSjob 		goto out;
6116747684dSjob 	}
6126747684dSjob 	mftfilename++;
6136747684dSjob 	if (!valid_filename(mftfilename, strlen(mftfilename))) {
614c180fb4aSjob 		warnx("%s: SIA: rpkiManifest filename contains invalid "
6152b470c61Stb 		    "characters", fn);
616c180fb4aSjob 		goto out;
617c180fb4aSjob 	}
618c180fb4aSjob 
6192e5628f8Stb 	if (strstr(cert->mft, cert->repo) != cert->mft ||
6202e5628f8Stb 	    cert->mft + strlen(cert->repo) != mftfilename) {
6212edf3539Stb 		warnx("%s: RFC 6487 section 4.8.8: SIA: "
6222b470c61Stb 		    "conflicting URIs for caRepository and rpkiManifest", fn);
6232edf3539Stb 		goto out;
6242edf3539Stb 	}
6252edf3539Stb 
6262b470c61Stb 	if (rtype_from_file_extension(cert->mft) != RTYPE_MFT) {
6272b470c61Stb 		warnx("%s: RFC 6487 section 4.8.8: SIA: not an MFT file", fn);
6282edf3539Stb 		goto out;
6292edf3539Stb 	}
6302edf3539Stb 
6312edf3539Stb 	rc = 1;
6322edf3539Stb  out:
6332edf3539Stb 	AUTHORITY_INFO_ACCESS_free(sia);
6342edf3539Stb 	return rc;
6352edf3539Stb }
6362edf3539Stb 
6372edf3539Stb /*
638d23e87a2Stb  * Parse the certificate policies extension and check that it follows RFC 7318.
639d23e87a2Stb  * Returns zero on failure, non-zero on success.
640d23e87a2Stb  */
641f9f55a97Stb static int
6422b470c61Stb certificate_policies(const char *fn, struct cert *cert, X509_EXTENSION *ext)
643f9f55a97Stb {
644f9f55a97Stb 	STACK_OF(POLICYINFO)		*policies = NULL;
645f9f55a97Stb 	POLICYINFO			*policy;
646f9f55a97Stb 	STACK_OF(POLICYQUALINFO)	*qualifiers;
647f9f55a97Stb 	POLICYQUALINFO			*qualifier;
648f9f55a97Stb 	int				 nid;
649f9f55a97Stb 	int				 rc = 0;
650f9f55a97Stb 
651f9f55a97Stb 	if (!X509_EXTENSION_get_critical(ext)) {
652c0528901Stb 		warnx("%s: RFC 6487 section 4.8.9: certificatePolicies: "
6532b470c61Stb 		    "extension not critical", fn);
654f9f55a97Stb 		goto out;
655f9f55a97Stb 	}
656f9f55a97Stb 
657f9f55a97Stb 	if ((policies = X509V3_EXT_d2i(ext)) == NULL) {
658c0528901Stb 		warnx("%s: RFC 6487 section 4.8.9: certificatePolicies: "
6592b470c61Stb 		    "failed extension parse", fn);
660f9f55a97Stb 		goto out;
661f9f55a97Stb 	}
662f9f55a97Stb 
663f9f55a97Stb 	if (sk_POLICYINFO_num(policies) != 1) {
664f9f55a97Stb 		warnx("%s: RFC 6487 section 4.8.9: certificatePolicies: "
6652b470c61Stb 		    "want 1 policy, got %d", fn, sk_POLICYINFO_num(policies));
666f9f55a97Stb 		goto out;
667f9f55a97Stb 	}
668f9f55a97Stb 
669f9f55a97Stb 	policy = sk_POLICYINFO_value(policies, 0);
670f9f55a97Stb 	assert(policy != NULL && policy->policyid != NULL);
671f9f55a97Stb 
672f9f55a97Stb 	if (OBJ_cmp(policy->policyid, certpol_oid) != 0) {
673f9f55a97Stb 		char pbuf[128], cbuf[128];
674f9f55a97Stb 
675f9f55a97Stb 		OBJ_obj2txt(pbuf, sizeof(pbuf), policy->policyid, 1);
676f9f55a97Stb 		OBJ_obj2txt(cbuf, sizeof(cbuf), certpol_oid, 1);
677f9f55a97Stb 		warnx("%s: RFC 7318 section 2: certificatePolicies: "
6782b470c61Stb 		    "unexpected OID: %s, want %s", fn, pbuf, cbuf);
679f9f55a97Stb 		goto out;
680f9f55a97Stb 	}
681f9f55a97Stb 
682f9f55a97Stb 	/* Policy qualifiers are optional. If they're absent, we're done. */
683f9f55a97Stb 	if ((qualifiers = policy->qualifiers) == NULL) {
684f9f55a97Stb 		rc = 1;
685f9f55a97Stb 		goto out;
686f9f55a97Stb 	}
687f9f55a97Stb 
688f9f55a97Stb 	if (sk_POLICYQUALINFO_num(qualifiers) != 1) {
689f9f55a97Stb 		warnx("%s: RFC 7318 section 2: certificatePolicies: "
6902b470c61Stb 		    "want 1 policy qualifier, got %d", fn,
691f9f55a97Stb 		    sk_POLICYQUALINFO_num(qualifiers));
692f9f55a97Stb 		goto out;
693f9f55a97Stb 	}
694f9f55a97Stb 
695f9f55a97Stb 	qualifier = sk_POLICYQUALINFO_value(qualifiers, 0);
696f9f55a97Stb 	assert(qualifier != NULL && qualifier->pqualid != NULL);
697f9f55a97Stb 
698f9f55a97Stb 	if ((nid = OBJ_obj2nid(qualifier->pqualid)) != NID_id_qt_cps) {
699f9f55a97Stb 		warnx("%s: RFC 7318 section 2: certificatePolicies: "
7002b470c61Stb 		    "want CPS, got %s", fn, nid2str(nid));
701f9f55a97Stb 		goto out;
702f9f55a97Stb 	}
703f9f55a97Stb 
7048eb56b7eSjob 	if (verbose > 1 && !filemode)
7052b470c61Stb 		warnx("%s: CPS %.*s", fn, qualifier->d.cpsuri->length,
706f9f55a97Stb 		    qualifier->d.cpsuri->data);
707f9f55a97Stb 
708f9f55a97Stb 	rc = 1;
709f9f55a97Stb  out:
710f9f55a97Stb 	sk_POLICYINFO_pop_free(policies, POLICYINFO_free);
711f9f55a97Stb 	return rc;
712f9f55a97Stb }
713f9f55a97Stb 
7140466b83fStb static int
7150466b83fStb cert_check_subject_and_issuer(const char *fn, const X509 *x)
7160466b83fStb {
7170466b83fStb 	const X509_NAME *name;
7180466b83fStb 
7190466b83fStb 	if ((name = X509_get_subject_name(x)) == NULL) {
7200466b83fStb 		warnx("%s: X509_get_subject_name", fn);
7210466b83fStb 		return 0;
7220466b83fStb 	}
7230466b83fStb 	if (!x509_valid_name(fn, "subject", name))
7240466b83fStb 		return 0;
7250466b83fStb 
7260466b83fStb 	if ((name = X509_get_issuer_name(x)) == NULL) {
7270466b83fStb 		warnx("%s: X509_get_issuer_name", fn);
7280466b83fStb 		return 0;
7290466b83fStb 	}
7300466b83fStb 	if (!x509_valid_name(fn, "issuer", name))
7310466b83fStb 		return 0;
7320466b83fStb 
7330466b83fStb 	return 1;
7340466b83fStb }
7350466b83fStb 
7369a7e9e7fSjob /*
7377cc1142dSjob  * Lightweight version of cert_parse_pre() for EE certs.
7387cc1142dSjob  * Parses the two RFC 3779 extensions, and performs some sanity checks.
73999dbdb7fStb  * Returns cert on success and NULL on failure.
74099dbdb7fStb  */
74199dbdb7fStb struct cert *
742891d6bceSjob cert_parse_ee_cert(const char *fn, int talid, X509 *x)
74399dbdb7fStb {
7442b470c61Stb 	struct cert		*cert;
74599dbdb7fStb 	X509_EXTENSION		*ext;
74699dbdb7fStb 	int			 index;
74799dbdb7fStb 
7482b470c61Stb 	if ((cert = calloc(1, sizeof(struct cert))) == NULL)
74999dbdb7fStb 		err(1, NULL);
75099dbdb7fStb 
751138af1ebSjob 	if (X509_get_version(x) != 2) {
752138af1ebSjob 		warnx("%s: RFC 6487 4.1: X.509 version must be v3", fn);
753138af1ebSjob 		goto out;
754138af1ebSjob 	}
755138af1ebSjob 
7560466b83fStb 	if (!cert_check_subject_and_issuer(fn, x))
7577cc1142dSjob 		goto out;
7587cc1142dSjob 
759e891962dStb 	if (!x509_cache_extensions(x, fn))
760e891962dStb 		goto out;
761e891962dStb 
762a5826434Stb 	if ((cert->purpose = x509_get_purpose(x, fn)) != CERT_PURPOSE_EE) {
763a5826434Stb 		warnx("%s: expected EE cert, got %s", fn,
764a5826434Stb 		    purpose2str(cert->purpose));
765a5826434Stb 		goto out;
766a5826434Stb 	}
767a5826434Stb 
76899dbdb7fStb 	index = X509_get_ext_by_NID(x, NID_sbgp_ipAddrBlock, -1);
76999dbdb7fStb 	if ((ext = X509_get_ext(x, index)) != NULL) {
7702b470c61Stb 		if (!sbgp_ipaddrblk(fn, cert, ext))
77199dbdb7fStb 			goto out;
77299dbdb7fStb 	}
77399dbdb7fStb 
77499dbdb7fStb 	index = X509_get_ext_by_NID(x, NID_sbgp_autonomousSysNum, -1);
77599dbdb7fStb 	if ((ext = X509_get_ext(x, index)) != NULL) {
7762b470c61Stb 		if (!sbgp_assysnum(fn, cert, ext))
77799dbdb7fStb 			goto out;
77899dbdb7fStb 	}
77999dbdb7fStb 
78099dbdb7fStb 	if (!X509_up_ref(x)) {
781c0528901Stb 		warnx("%s: X509_up_ref failed", fn);
78299dbdb7fStb 		goto out;
78399dbdb7fStb 	}
78499dbdb7fStb 
7852b470c61Stb 	cert->x509 = x;
7862b470c61Stb 	cert->talid = talid;
787891d6bceSjob 
7882b470c61Stb 	if (!constraints_validate(fn, cert))
789891d6bceSjob 		goto out;
790891d6bceSjob 
7912b470c61Stb 	return cert;
79299dbdb7fStb 
79399dbdb7fStb  out:
7942b470c61Stb 	cert_free(cert);
79599dbdb7fStb 	return NULL;
79699dbdb7fStb }
79799dbdb7fStb 
79899dbdb7fStb /*
7999a7e9e7fSjob  * Parse and partially validate an RPKI X509 certificate (either a trust
8009a7e9e7fSjob  * anchor or a certificate) as defined in RFC 6487.
801f999fe57Sclaudio  * Returns the parse results or NULL on failure.
8029a7e9e7fSjob  */
803ba153bd8Sclaudio struct cert *
804ba153bd8Sclaudio cert_parse_pre(const char *fn, const unsigned char *der, size_t len)
8059a7e9e7fSjob {
8062b470c61Stb 	struct cert		*cert;
807797cceeeStb 	const unsigned char	*oder;
80828d6404fStb 	size_t			 j;
80928d6404fStb 	int			 i, extsz;
8109a7e9e7fSjob 	X509			*x = NULL;
8119a7e9e7fSjob 	X509_EXTENSION		*ext = NULL;
8127a3e7ef3Stb 	const ASN1_BIT_STRING	*issuer_uid = NULL, *subject_uid = NULL;
8139a7e9e7fSjob 	ASN1_OBJECT		*obj;
814ae36eebeSjob 	EVP_PKEY		*pkey;
81576281e49Stb 	int			 nid, ip, as, sia, cp, crldp, aia, aki, ski,
81676281e49Stb 				 eku, bc, ku;
81776281e49Stb 
81876281e49Stb 	nid = ip = as = sia = cp = crldp = aia = aki = ski = eku = bc = ku = 0;
8199a7e9e7fSjob 
820803d3b9aSclaudio 	/* just fail for empty buffers, the warning was printed elsewhere */
821803d3b9aSclaudio 	if (der == NULL)
8225ff8d7beSclaudio 		return NULL;
8239a7e9e7fSjob 
8242b470c61Stb 	if ((cert = calloc(1, sizeof(struct cert))) == NULL)
825ab4d4874Sbenno 		err(1, NULL);
8269a7e9e7fSjob 
827797cceeeStb 	oder = der;
8289551aa73Sclaudio 	if ((x = d2i_X509(NULL, &der, len)) == NULL) {
8292b470c61Stb 		warnx("%s: d2i_X509", fn);
8309a7e9e7fSjob 		goto out;
8319a7e9e7fSjob 	}
832797cceeeStb 	if (der != oder + len) {
833797cceeeStb 		warnx("%s: %td bytes trailing garbage", fn, oder + len - der);
834797cceeeStb 		goto out;
835797cceeeStb 	}
8369a7e9e7fSjob 
837e891962dStb 	if (!x509_cache_extensions(x, fn))
838b2d6bcdcStb 		goto out;
839b2d6bcdcStb 
840138af1ebSjob 	if (X509_get_version(x) != 2) {
841138af1ebSjob 		warnx("%s: RFC 6487 4.1: X.509 version must be v3", fn);
842138af1ebSjob 		goto out;
843138af1ebSjob 	}
844138af1ebSjob 
845073e107aStb 	if ((nid = X509_get_signature_nid(x)) == NID_undef) {
846073e107aStb 		warnx("%s: unknown signature type", fn);
8477c9af4b7Sjob 		goto out;
8487c9af4b7Sjob 	}
84981a06611Sclaudio 	if (experimental && nid == NID_ecdsa_with_SHA256) {
850ec1cc732Sjob 		if (verbose)
8518fcc9cc2Sjob 			warnx("%s: P-256 support is experimental", fn);
852ec1cc732Sjob 	} else if (nid != NID_sha256WithRSAEncryption) {
8537c9af4b7Sjob 		warnx("%s: RFC 7935: wrong signature algorithm %s, want %s",
85478de3577Stb 		    fn, nid2str(nid), LN_sha256WithRSAEncryption);
8557c9af4b7Sjob 		goto out;
8567c9af4b7Sjob 	}
8577c9af4b7Sjob 
8587a3e7ef3Stb 	X509_get0_uids(x, &issuer_uid, &subject_uid);
8597a3e7ef3Stb 	if (issuer_uid != NULL || subject_uid != NULL) {
8604cf8d64cSjob 		warnx("%s: issuer or subject unique identifiers not allowed",
8614cf8d64cSjob 		    fn);
8624cf8d64cSjob 		goto out;
8634cf8d64cSjob 	}
8644cf8d64cSjob 
8650466b83fStb 	if (!cert_check_subject_and_issuer(fn, x))
8667cc1142dSjob 		goto out;
8677cc1142dSjob 
8689a7e9e7fSjob 	/* Look for X509v3 extensions. */
86928d6404fStb 	if ((extsz = X509_get_ext_count(x)) <= 0) {
87028d6404fStb 		warnx("%s: certificate without X.509v3 extensions", fn);
87128d6404fStb 		goto out;
87228d6404fStb 	}
8739a7e9e7fSjob 
87428d6404fStb 	for (i = 0; i < extsz; i++) {
8759a7e9e7fSjob 		ext = X509_get_ext(x, i);
8769a7e9e7fSjob 		assert(ext != NULL);
8779a7e9e7fSjob 		obj = X509_EXTENSION_get_object(ext);
8789a7e9e7fSjob 		assert(obj != NULL);
8799a7e9e7fSjob 
880de9327feStb 		switch (nid = OBJ_obj2nid(obj)) {
8819a7e9e7fSjob 		case NID_sbgp_ipAddrBlock:
882de9327feStb 			if (ip++ > 0)
88376281e49Stb 				goto dup;
8842b470c61Stb 			if (!sbgp_ipaddrblk(fn, cert, ext))
8852dc0b9b1Stb 				goto out;
8869a7e9e7fSjob 			break;
8879a7e9e7fSjob 		case NID_sbgp_autonomousSysNum:
88876281e49Stb 			if (as++ > 0)
88976281e49Stb 				goto dup;
8902b470c61Stb 			if (!sbgp_assysnum(fn, cert, ext))
8912dc0b9b1Stb 				goto out;
8929a7e9e7fSjob 			break;
8939a7e9e7fSjob 		case NID_sinfo_access:
89476281e49Stb 			if (sia++ > 0)
89576281e49Stb 				goto dup;
896c5305b1dStb 			/*
897c5305b1dStb 			 * This will fail for BGPsec certs, but they must omit
898c5305b1dStb 			 * this extension anyway (RFC 8209, section 3.1.3.3).
899c5305b1dStb 			 */
9002b470c61Stb 			if (!sbgp_sia(fn, cert, ext))
9012dc0b9b1Stb 				goto out;
9029a7e9e7fSjob 			break;
903f9f55a97Stb 		case NID_certificate_policies:
90476281e49Stb 			if (cp++ > 0)
90576281e49Stb 				goto dup;
9062b470c61Stb 			if (!certificate_policies(fn, cert, ext))
9072dc0b9b1Stb 				goto out;
908f9f55a97Stb 			break;
9099a7e9e7fSjob 		case NID_crl_distribution_points:
91076281e49Stb 			if (crldp++ > 0)
91176281e49Stb 				goto dup;
9129a7e9e7fSjob 			break;
913ebd55816Sjob 		case NID_info_access:
91476281e49Stb 			if (aia++ > 0)
91576281e49Stb 				goto dup;
916ebd55816Sjob 			break;
9179a7e9e7fSjob 		case NID_authority_key_identifier:
91876281e49Stb 			if (aki++ > 0)
91976281e49Stb 				goto dup;
9209a7e9e7fSjob 			break;
9219a7e9e7fSjob 		case NID_subject_key_identifier:
92276281e49Stb 			if (ski++ > 0)
92376281e49Stb 				goto dup;
9249a7e9e7fSjob 			break;
925fdfddccfSjob 		case NID_ext_key_usage:
92676281e49Stb 			if (eku++ > 0)
92776281e49Stb 				goto dup;
928fdfddccfSjob 			break;
9296c43a88aSjob 		case NID_basic_constraints:
93076281e49Stb 			if (bc++ > 0)
93176281e49Stb 				goto dup;
9326c43a88aSjob 			break;
9336c43a88aSjob 		case NID_key_usage:
93476281e49Stb 			if (ku++ > 0)
93576281e49Stb 				goto dup;
9366c43a88aSjob 			break;
9379a7e9e7fSjob 		default:
9386c43a88aSjob 			/* unexpected extensions warrant investigation */
9396c43a88aSjob 			{
9409a7e9e7fSjob 				char objn[64];
9419a7e9e7fSjob 				OBJ_obj2txt(objn, sizeof(objn), obj, 0);
9429a7e9e7fSjob 				warnx("%s: ignoring %s (NID %d)",
9432b470c61Stb 				    fn, objn, OBJ_obj2nid(obj));
9446c43a88aSjob 			}
9459a7e9e7fSjob 			break;
9469a7e9e7fSjob 		}
9479a7e9e7fSjob 	}
9489a7e9e7fSjob 
9492b470c61Stb 	if (!x509_get_aki(x, fn, &cert->aki))
950f999fe57Sclaudio 		goto out;
9512b470c61Stb 	if (!x509_get_ski(x, fn, &cert->ski))
952f999fe57Sclaudio 		goto out;
9532b470c61Stb 	if (!x509_get_aia(x, fn, &cert->aia))
954f999fe57Sclaudio 		goto out;
9552b470c61Stb 	if (!x509_get_crl(x, fn, &cert->crl))
956f999fe57Sclaudio 		goto out;
9572b470c61Stb 	if (!x509_get_notbefore(x, fn, &cert->notbefore))
958f5999ddfSjob 		goto out;
9592b470c61Stb 	if (!x509_get_notafter(x, fn, &cert->notafter))
96061c641a8Sbeck 		goto out;
961041d165fSclaudio 
9629a7e9e7fSjob 	/* Validation on required fields. */
963eb6f3761Stb 	cert->purpose = x509_get_purpose(x, fn);
9642b470c61Stb 	switch (cert->purpose) {
965eb6f3761Stb 	case CERT_PURPOSE_TA:
966eb6f3761Stb 		/* XXX - caller should indicate if it expects TA or CA cert */
9676b83d8e3Sjob 	case CERT_PURPOSE_CA:
968ae36eebeSjob 		if ((pkey = X509_get0_pubkey(x)) == NULL) {
9692b470c61Stb 			warnx("%s: X509_get0_pubkey failed", fn);
970ae36eebeSjob 			goto out;
971ae36eebeSjob 		}
9722b470c61Stb 		if (!valid_ca_pkey(fn, pkey))
973ae36eebeSjob 			goto out;
974ae36eebeSjob 
9752b470c61Stb 		if (cert->mft == NULL) {
9762b470c61Stb 			warnx("%s: RFC 6487 section 4.8.8: missing SIA", fn);
9776b83d8e3Sjob 			goto out;
9786b83d8e3Sjob 		}
979381ee599Stb 		if (cert->num_ases == 0 && cert->num_ips == 0) {
9802b470c61Stb 			warnx("%s: missing IP or AS resources", fn);
9816b83d8e3Sjob 			goto out;
9826b83d8e3Sjob 		}
9836b83d8e3Sjob 		break;
9846b83d8e3Sjob 	case CERT_PURPOSE_BGPSEC_ROUTER:
9852b470c61Stb 		cert->pubkey = x509_get_pubkey(x, fn);
9862b470c61Stb 		if (cert->pubkey == NULL) {
9872b470c61Stb 			warnx("%s: x509_get_pubkey failed", fn);
9886b83d8e3Sjob 			goto out;
9896b83d8e3Sjob 		}
990381ee599Stb 		if (cert->num_ips > 0) {
9912b470c61Stb 			warnx("%s: unexpected IP resources in BGPsec cert", fn);
9926b83d8e3Sjob 			goto out;
9936b83d8e3Sjob 		}
994381ee599Stb 		for (j = 0; j < cert->num_ases; j++) {
995381ee599Stb 			if (cert->ases[j].type == CERT_AS_INHERIT) {
996ef8bdb37Sjob 				warnx("%s: inherit elements not allowed in EE"
9972b470c61Stb 				    " cert", fn);
998bc1fb2e9Stb 				goto out;
999bc1fb2e9Stb 			}
1000bc1fb2e9Stb 		}
100176281e49Stb 		if (sia) {
10022dadf524Sclaudio 			warnx("%s: unexpected SIA extension in BGPsec cert",
10032b470c61Stb 			    fn);
10042dadf524Sclaudio 			goto out;
10052dadf524Sclaudio 		}
10066b83d8e3Sjob 		break;
1007eb6f3761Stb 	case CERT_PURPOSE_EE:
1008eb6f3761Stb 		warn("%s: unexpected EE cert", fn);
1009eb6f3761Stb 		goto out;
10106b83d8e3Sjob 	default:
10112b470c61Stb 		warnx("%s: x509_get_purpose failed in %s", fn, __func__);
10126b83d8e3Sjob 		goto out;
10136b83d8e3Sjob 	}
10146b83d8e3Sjob 
10152b470c61Stb 	if (cert->ski == NULL) {
10162b470c61Stb 		warnx("%s: RFC 6487 section 8.4.2: missing SKI", fn);
10179a7e9e7fSjob 		goto out;
10189a7e9e7fSjob 	}
10199a7e9e7fSjob 
10202b470c61Stb 	cert->x509 = x;
10212b470c61Stb 	return cert;
10229a7e9e7fSjob 
102376281e49Stb  dup:
102478de3577Stb 	warnx("%s: RFC 5280 section 4.2: duplicate extension: %s", fn,
102578de3577Stb 	    nid2str(nid));
10269a7e9e7fSjob  out:
10272b470c61Stb 	cert_free(cert);
10289a7e9e7fSjob 	X509_free(x);
10292dc0b9b1Stb 	return NULL;
10309a7e9e7fSjob }
10319a7e9e7fSjob 
10329a7e9e7fSjob struct cert *
1033ba153bd8Sclaudio cert_parse(const char *fn, struct cert *p)
10349a7e9e7fSjob {
1035ad462a11Sclaudio 	if (p == NULL)
1036ad462a11Sclaudio 		return NULL;
1037ad462a11Sclaudio 
10380a039610Sclaudio 	if (p->aki == NULL) {
10390a039610Sclaudio 		warnx("%s: RFC 6487 section 8.4.2: "
10400a039610Sclaudio 		    "non-trust anchor missing AKI", fn);
10410a039610Sclaudio 		goto badcert;
10420a039610Sclaudio 	}
10430a039610Sclaudio 	if (strcmp(p->aki, p->ski) == 0) {
10440a039610Sclaudio 		warnx("%s: RFC 6487 section 8.4.2: "
10450a039610Sclaudio 		    "non-trust anchor AKI may not match SKI", fn);
10460a039610Sclaudio 		goto badcert;
10470a039610Sclaudio 	}
10480a039610Sclaudio 	if (p->aia == NULL) {
1049f999fe57Sclaudio 		warnx("%s: RFC 6487 section 8.4.7: AIA: extension missing", fn);
1050f999fe57Sclaudio 		goto badcert;
1051f999fe57Sclaudio 	}
1052f999fe57Sclaudio 	if (p->crl == NULL) {
1053f999fe57Sclaudio 		warnx("%s: RFC 6487 section 4.8.6: CRL: "
1054f999fe57Sclaudio 		    "no CRL distribution point extension", fn);
10550a039610Sclaudio 		goto badcert;
10560a039610Sclaudio 	}
10570a039610Sclaudio 	return p;
10580a039610Sclaudio 
10590a039610Sclaudio badcert:
10600a039610Sclaudio 	cert_free(p);
10610a039610Sclaudio 	return NULL;
10629a7e9e7fSjob }
10639a7e9e7fSjob 
1064e489b848Sjob /*
1065e489b848Sjob  * Reject TA certificates with an overly long validity period.
1066e489b848Sjob  *
1067e489b848Sjob  * The schedule is as follows:
1068e489b848Sjob  * Before February 2nd, 2026, warn on TA certs valid for longer than 15 years.
1069e489b848Sjob  * After February 2nd, 2026, reject TA certs valid for longer than 15 years.
1070e489b848Sjob  * Before March 3rd, 2027, warn on TA certs valid for longer than 3 years.
1071e489b848Sjob  * After March 3rd, 2027, reject TA certs valid for longer than 3 years.
1072e489b848Sjob  *
1073e489b848Sjob  * Return 1 if the validity period is acceptable and 0 otherwise.
1074e489b848Sjob  */
1075e489b848Sjob static int
1076e489b848Sjob ta_check_validity(const char *fn, const struct cert *p, time_t now)
1077e489b848Sjob {
1078e489b848Sjob 	time_t validity = p->notafter - p->notbefore;
1079e489b848Sjob 	time_t cutoff_15y = 1769990400; /* 2026-02-02T00:00:00Z */
1080e489b848Sjob 	time_t cutoff_3y = 1804032000; /* 2027-03-03T00:00:00Z */
1081e489b848Sjob 	time_t cutoff = cutoff_3y;
1082e489b848Sjob 	int warn_years = 3;
1083e489b848Sjob 	int exceeds_15y = 0, exceeds_3y = 0;
1084e489b848Sjob 	int complain = 0, acceptable = 1;
1085e489b848Sjob 
1086e489b848Sjob 	if (validity >= 15 * 365 * 86400)
1087e489b848Sjob 		exceeds_15y = 1;
1088e489b848Sjob 	if (validity >= 3 * 365 * 86400)
1089e489b848Sjob 		exceeds_3y = 1;
1090e489b848Sjob 
1091e489b848Sjob 	if (now < cutoff_15y) {
1092e489b848Sjob 		warn_years = 15;
1093e489b848Sjob 		cutoff = cutoff_15y;
1094e489b848Sjob 		if (exceeds_15y)
1095e489b848Sjob 			complain = 1;
1096e489b848Sjob 	} else if (now < cutoff_3y) {
1097e489b848Sjob 		if (exceeds_15y)
1098e489b848Sjob 			acceptable = 0;
1099e489b848Sjob 		if (exceeds_3y)
1100e489b848Sjob 			complain = 1;
1101e489b848Sjob 	} else if (exceeds_3y) {
1102e489b848Sjob 		acceptable = 0;
1103e489b848Sjob 		complain = 1;
1104e489b848Sjob 	}
1105e489b848Sjob 
1106e489b848Sjob 	/*
1107*a56972ebStb 	 * Suppress warnings for previously fetched TA certs.
1108e489b848Sjob 	 */
1109*a56972ebStb 	if (verbose == 0 && strncmp(fn, "ta/", strlen("ta/")) == 0)
1110e489b848Sjob 		goto out;
1111e489b848Sjob 
1112e489b848Sjob 	if (!acceptable) {
1113e489b848Sjob 		warnx("%s: TA cert rejected: validity period exceeds %d years. "
1114e489b848Sjob 		    "Ask the TA operator to reissue their TA cert with a "
1115e489b848Sjob 		    "shorter validity period.", fn, warn_years);
1116e489b848Sjob 		goto out;
1117e489b848Sjob 	}
1118e489b848Sjob 
1119e489b848Sjob 	if (complain) {
1120e489b848Sjob 		warnx("%s: TA validity period exceeds %d years. After %s this "
1121e489b848Sjob 		    "certificate will be rejected.", fn, warn_years,
1122e489b848Sjob 		    time2str(cutoff));
1123e489b848Sjob 		goto out;
1124e489b848Sjob 	}
1125e489b848Sjob 
1126e489b848Sjob  out:
1127e489b848Sjob 	return acceptable;
1128e489b848Sjob }
1129e489b848Sjob 
11309a7e9e7fSjob struct cert *
1131ba153bd8Sclaudio ta_parse(const char *fn, struct cert *p, const unsigned char *pkey,
1132ba153bd8Sclaudio     size_t pkeysz)
11339a7e9e7fSjob {
1134ba153bd8Sclaudio 	EVP_PKEY	*pk, *opk;
1135e9d2d4cdSjob 	time_t		 now = get_current_time();
11369a7e9e7fSjob 
1137ad462a11Sclaudio 	if (p == NULL)
1138ad462a11Sclaudio 		return NULL;
1139ad462a11Sclaudio 
11404fc676bcSclaudio 	/* first check pubkey against the one from the TAL */
11419a7e9e7fSjob 	pk = d2i_PUBKEY(NULL, &pkey, pkeysz);
11424fc676bcSclaudio 	if (pk == NULL) {
1143c0528901Stb 		warnx("%s: RFC 6487 (trust anchor): bad TAL pubkey", fn);
11444fc676bcSclaudio 		goto badcert;
11454fc676bcSclaudio 	}
11464fc676bcSclaudio 	if ((opk = X509_get0_pubkey(p->x509)) == NULL) {
1147c0528901Stb 		warnx("%s: RFC 6487 (trust anchor): missing pubkey", fn);
11484fc676bcSclaudio 		goto badcert;
1149ba153bd8Sclaudio 	}
1150ba153bd8Sclaudio 	if (EVP_PKEY_cmp(pk, opk) != 1) {
1151c0528901Stb 		warnx("%s: RFC 6487 (trust anchor): "
11529a7e9e7fSjob 		    "pubkey does not match TAL pubkey", fn);
11534fc676bcSclaudio 		goto badcert;
1154f929d3afStobhe 	}
1155e489b848Sjob 
115681fe2f6dStb 	if (p->notbefore > now) {
11574fc676bcSclaudio 		warnx("%s: certificate not yet valid", fn);
11584fc676bcSclaudio 		goto badcert;
11594fc676bcSclaudio 	}
116081fe2f6dStb 	if (p->notafter < now) {
11614fc676bcSclaudio 		warnx("%s: certificate has expired", fn);
11624fc676bcSclaudio 		goto badcert;
11634fc676bcSclaudio 	}
1164e489b848Sjob 	if (!ta_check_validity(fn, p, now))
1165e489b848Sjob 		goto badcert;
1166e489b848Sjob 
11670a039610Sclaudio 	if (p->aki != NULL && strcmp(p->aki, p->ski)) {
11684fdd5861Stb 		warnx("%s: RFC 6487 section 4.8.3: "
11690a039610Sclaudio 		    "trust anchor AKI, if specified, must match SKI", fn);
11700a039610Sclaudio 		goto badcert;
11710a039610Sclaudio 	}
11720a039610Sclaudio 	if (p->aia != NULL) {
11734fdd5861Stb 		warnx("%s: RFC 6487 section 4.8.7: "
11740a039610Sclaudio 		    "trust anchor must not have AIA", fn);
11750a039610Sclaudio 		goto badcert;
11760a039610Sclaudio 	}
11770a039610Sclaudio 	if (p->crl != NULL) {
11784fdd5861Stb 		warnx("%s: RFC 6487 section 4.8.6: "
11790a039610Sclaudio 		    "trust anchor may not specify CRL resource", fn);
11800a039610Sclaudio 		goto badcert;
11810a039610Sclaudio 	}
1182a5826434Stb 	if (p->purpose != CERT_PURPOSE_TA) {
1183a5826434Stb 		warnx("%s: expected trust anchor purpose, got %s", fn,
1184a5826434Stb 		    purpose2str(p->purpose));
11850a039610Sclaudio 		goto badcert;
11860a039610Sclaudio 	}
118760aa04efStb 	/*
118860aa04efStb 	 * Do not replace with a <= 0 check since OpenSSL 3 broke that:
118960aa04efStb 	 * https://github.com/openssl/openssl/issues/24575
119060aa04efStb 	 */
1191b6a9c519Stb 	if (X509_verify(p->x509, pk) != 1) {
1192b6a9c519Stb 		warnx("%s: failed to verify signature", fn);
1193b6a9c519Stb 		goto badcert;
1194b6a9c519Stb 	}
1195c9e39c95Sjob 	if (x509_any_inherits(p->x509)) {
1196c9e39c95Sjob 		warnx("%s: Trust anchor IP/AS resources may not inherit", fn);
1197c9e39c95Sjob 		goto badcert;
1198c9e39c95Sjob 	}
11994fc676bcSclaudio 
12000a039610Sclaudio 	EVP_PKEY_free(pk);
12010a039610Sclaudio 	return p;
12024fc676bcSclaudio 
12034fc676bcSclaudio  badcert:
12044fc676bcSclaudio 	EVP_PKEY_free(pk);
12059a7e9e7fSjob 	cert_free(p);
12060a039610Sclaudio 	return NULL;
12079a7e9e7fSjob }
12089a7e9e7fSjob 
12099a7e9e7fSjob /*
12109a7e9e7fSjob  * Free parsed certificate contents.
12119a7e9e7fSjob  * Passing NULL is a noop.
12129a7e9e7fSjob  */
12139a7e9e7fSjob void
12149a7e9e7fSjob cert_free(struct cert *p)
12159a7e9e7fSjob {
12167ea57ab8Sderaadt 	if (p == NULL)
12179a7e9e7fSjob 		return;
12189a7e9e7fSjob 
12199a7e9e7fSjob 	free(p->crl);
122084c20e47Sclaudio 	free(p->repo);
12219a7e9e7fSjob 	free(p->mft);
12223d81c3dfSclaudio 	free(p->notify);
12239a7e9e7fSjob 	free(p->ips);
1224381ee599Stb 	free(p->ases);
1225ebd55816Sjob 	free(p->aia);
12269a7e9e7fSjob 	free(p->aki);
12279a7e9e7fSjob 	free(p->ski);
122817304ed1Sjob 	free(p->pubkey);
122973072160Sbenno 	X509_free(p->x509);
12309a7e9e7fSjob 	free(p);
12319a7e9e7fSjob }
12329a7e9e7fSjob 
12339a7e9e7fSjob /*
12349a7e9e7fSjob  * Write certificate parsed content into buffer.
12359a7e9e7fSjob  * See cert_read() for the other side of the pipe.
12369a7e9e7fSjob  */
12379a7e9e7fSjob void
123808db1177Sclaudio cert_buffer(struct ibuf *b, const struct cert *p)
12399a7e9e7fSjob {
12409f544822Sjob 	io_simple_buffer(b, &p->notafter, sizeof(p->notafter));
1241dc508150Sclaudio 	io_simple_buffer(b, &p->purpose, sizeof(p->purpose));
1242dc508150Sclaudio 	io_simple_buffer(b, &p->talid, sizeof(p->talid));
12430bc420b9Sclaudio 	io_simple_buffer(b, &p->certid, sizeof(p->certid));
1244c94cf448Sclaudio 	io_simple_buffer(b, &p->repoid, sizeof(p->repoid));
1245381ee599Stb 	io_simple_buffer(b, &p->num_ips, sizeof(p->num_ips));
1246381ee599Stb 	io_simple_buffer(b, &p->num_ases, sizeof(p->num_ases));
12474ef6f693Sclaudio 
1248381ee599Stb 	io_simple_buffer(b, p->ips, p->num_ips * sizeof(p->ips[0]));
1249381ee599Stb 	io_simple_buffer(b, p->ases, p->num_ases * sizeof(p->ases[0]));
12504ef6f693Sclaudio 
125108db1177Sclaudio 	io_str_buffer(b, p->mft);
125208db1177Sclaudio 	io_str_buffer(b, p->notify);
125384c20e47Sclaudio 	io_str_buffer(b, p->repo);
125408db1177Sclaudio 	io_str_buffer(b, p->crl);
1255ebd55816Sjob 	io_str_buffer(b, p->aia);
125608db1177Sclaudio 	io_str_buffer(b, p->aki);
125708db1177Sclaudio 	io_str_buffer(b, p->ski);
125817304ed1Sjob 	io_str_buffer(b, p->pubkey);
12599a7e9e7fSjob }
12609a7e9e7fSjob 
12619a7e9e7fSjob /*
12629a7e9e7fSjob  * Allocate and read parsed certificate content from descriptor.
12639a7e9e7fSjob  * The pointer must be freed with cert_free().
12649a7e9e7fSjob  * Always returns a valid pointer.
12659a7e9e7fSjob  */
12669a7e9e7fSjob struct cert *
12677eb79a4aSclaudio cert_read(struct ibuf *b)
12689a7e9e7fSjob {
12699a7e9e7fSjob 	struct cert	*p;
12709a7e9e7fSjob 
12719a7e9e7fSjob 	if ((p = calloc(1, sizeof(struct cert))) == NULL)
1272ab4d4874Sbenno 		err(1, NULL);
12739a7e9e7fSjob 
12749f544822Sjob 	io_read_buf(b, &p->notafter, sizeof(p->notafter));
1275dc508150Sclaudio 	io_read_buf(b, &p->purpose, sizeof(p->purpose));
1276dc508150Sclaudio 	io_read_buf(b, &p->talid, sizeof(p->talid));
12770bc420b9Sclaudio 	io_read_buf(b, &p->certid, sizeof(p->certid));
1278c94cf448Sclaudio 	io_read_buf(b, &p->repoid, sizeof(p->repoid));
1279381ee599Stb 	io_read_buf(b, &p->num_ips, sizeof(p->num_ips));
1280381ee599Stb 	io_read_buf(b, &p->num_ases, sizeof(p->num_ases));
12817eb79a4aSclaudio 
1282381ee599Stb 	if (p->num_ips > 0) {
1283381ee599Stb 		if ((p->ips = calloc(p->num_ips, sizeof(p->ips[0]))) == NULL)
1284ab4d4874Sbenno 			err(1, NULL);
1285381ee599Stb 		io_read_buf(b, p->ips, p->num_ips * sizeof(p->ips[0]));
1286f814cda1Stb 	}
12879a7e9e7fSjob 
1288381ee599Stb 	if (p->num_ases > 0) {
1289381ee599Stb 		if ((p->ases = calloc(p->num_ases, sizeof(p->ases[0]))) == NULL)
1290ab4d4874Sbenno 			err(1, NULL);
1291381ee599Stb 		io_read_buf(b, p->ases, p->num_ases * sizeof(p->ases[0]));
1292f814cda1Stb 	}
12939a7e9e7fSjob 
12947eb79a4aSclaudio 	io_read_str(b, &p->mft);
12957eb79a4aSclaudio 	io_read_str(b, &p->notify);
12967eb79a4aSclaudio 	io_read_str(b, &p->repo);
12977eb79a4aSclaudio 	io_read_str(b, &p->crl);
12987eb79a4aSclaudio 	io_read_str(b, &p->aia);
12997eb79a4aSclaudio 	io_read_str(b, &p->aki);
13007eb79a4aSclaudio 	io_read_str(b, &p->ski);
13017eb79a4aSclaudio 	io_read_str(b, &p->pubkey);
13027eb79a4aSclaudio 
1303fdfddccfSjob 	assert(p->mft != NULL || p->purpose == CERT_PURPOSE_BGPSEC_ROUTER);
130452c8fec2Sclaudio 	assert(p->ski);
13059a7e9e7fSjob 	return p;
13069a7e9e7fSjob }
13079a7e9e7fSjob 
1308c4a9443cSclaudio static inline int
1309c4a9443cSclaudio authcmp(struct auth *a, struct auth *b)
1310c4a9443cSclaudio {
13110bc420b9Sclaudio 	if (a->cert->certid > b->cert->certid)
13120bc420b9Sclaudio 		return 1;
13130bc420b9Sclaudio 	if (a->cert->certid < b->cert->certid)
13140bc420b9Sclaudio 		return -1;
13150bc420b9Sclaudio 	return 0;
1316c4a9443cSclaudio }
1317c4a9443cSclaudio 
1318c4a9443cSclaudio RB_GENERATE_STATIC(auth_tree, auth, entry, authcmp);
1319c4a9443cSclaudio 
132091176c18Sjob void
132191176c18Sjob auth_tree_free(struct auth_tree *auths)
132291176c18Sjob {
132391176c18Sjob 	struct auth	*auth, *tauth;
132491176c18Sjob 
132591176c18Sjob 	RB_FOREACH_SAFE(auth, auth_tree, auths, tauth) {
132691176c18Sjob 		RB_REMOVE(auth_tree, auths, auth);
132791176c18Sjob 		cert_free(auth->cert);
132891176c18Sjob 		free(auth);
132991176c18Sjob 	}
133091176c18Sjob }
133191176c18Sjob 
1332a079bbf8Sclaudio struct auth *
13330bc420b9Sclaudio auth_find(struct auth_tree *auths, int id)
1334a079bbf8Sclaudio {
1335a079bbf8Sclaudio 	struct auth a;
1336a079bbf8Sclaudio 	struct cert c;
1337a079bbf8Sclaudio 
13380bc420b9Sclaudio 	c.certid = id;
1339a079bbf8Sclaudio 	a.cert = &c;
1340a079bbf8Sclaudio 
1341a079bbf8Sclaudio 	return RB_FIND(auth_tree, auths, &a);
1342a079bbf8Sclaudio }
1343a079bbf8Sclaudio 
1344ad462a11Sclaudio struct auth *
13450bc420b9Sclaudio auth_insert(const char *fn, struct auth_tree *auths, struct cert *cert,
13460bc420b9Sclaudio     struct auth *issuer)
1347198a0520Sclaudio {
1348198a0520Sclaudio 	struct auth *na;
1349198a0520Sclaudio 
13500bc420b9Sclaudio 	na = calloc(1, sizeof(*na));
1351198a0520Sclaudio 	if (na == NULL)
1352198a0520Sclaudio 		err(1, NULL);
1353198a0520Sclaudio 
13540bc420b9Sclaudio 	if (issuer == NULL) {
13550bc420b9Sclaudio 		cert->certid = cert->talid;
13560bc420b9Sclaudio 	} else {
13570bc420b9Sclaudio 		cert->certid = ++certid;
13581d8c6443Stb 		if (certid > CERTID_MAX) {
13591d8c6443Stb 			if (certid == CERTID_MAX + 1)
13601d8c6443Stb 				warnx("%s: too many certificates in store", fn);
13611d8c6443Stb 			free(na);
13621d8c6443Stb 			return NULL;
13631d8c6443Stb 		}
13640bc420b9Sclaudio 		na->depth = issuer->depth + 1;
13650bc420b9Sclaudio 	}
13660bc420b9Sclaudio 
13670bc420b9Sclaudio 	if (na->depth >= MAX_CERT_DEPTH) {
13680bc420b9Sclaudio 		warnx("%s: maximum certificate chain depth exhausted", fn);
13690bc420b9Sclaudio 		free(na);
13700bc420b9Sclaudio 		return NULL;
13710bc420b9Sclaudio 	}
13720bc420b9Sclaudio 
1373335482abStb 	na->issuer = issuer;
1374198a0520Sclaudio 	na->cert = cert;
1375967224c8Stb 	na->any_inherits = x509_any_inherits(cert->x509);
1376198a0520Sclaudio 
1377198a0520Sclaudio 	if (RB_INSERT(auth_tree, auths, na) != NULL)
13780bc420b9Sclaudio 		errx(1, "auth tree corrupted");
1379ad462a11Sclaudio 
1380ad462a11Sclaudio 	return na;
1381198a0520Sclaudio }
1382198a0520Sclaudio 
13836b83d8e3Sjob static void
13846b83d8e3Sjob insert_brk(struct brk_tree *tree, struct cert *cert, int asid)
13856b83d8e3Sjob {
13866b83d8e3Sjob 	struct brk	*b, *found;
13876b83d8e3Sjob 
13886b83d8e3Sjob 	if ((b = calloc(1, sizeof(*b))) == NULL)
13896b83d8e3Sjob 		err(1, NULL);
13906b83d8e3Sjob 
13916b83d8e3Sjob 	b->asid = asid;
13929f544822Sjob 	b->expires = cert->notafter;
1393dc508150Sclaudio 	b->talid = cert->talid;
139417304ed1Sjob 	if ((b->ski = strdup(cert->ski)) == NULL)
139517304ed1Sjob 		err(1, NULL);
139617304ed1Sjob 	if ((b->pubkey = strdup(cert->pubkey)) == NULL)
139717304ed1Sjob 		err(1, NULL);
13986b83d8e3Sjob 
13996b83d8e3Sjob 	/*
14006b83d8e3Sjob 	 * Check if a similar BRK already exists in the tree. If the found BRK
14016b83d8e3Sjob 	 * expires sooner, update it to this BRK's later expiry moment.
14026b83d8e3Sjob 	 */
14036b83d8e3Sjob 	if ((found = RB_INSERT(brk_tree, tree, b)) != NULL) {
14046b83d8e3Sjob 		if (found->expires < b->expires) {
14056b83d8e3Sjob 			found->expires = b->expires;
1406dc508150Sclaudio 			found->talid = b->talid;
14076b83d8e3Sjob 		}
140817304ed1Sjob 		free(b->ski);
140917304ed1Sjob 		free(b->pubkey);
14106b83d8e3Sjob 		free(b);
14116b83d8e3Sjob 	}
14126b83d8e3Sjob }
14136b83d8e3Sjob 
14146b83d8e3Sjob /*
14156b83d8e3Sjob  * Add each BGPsec Router Key into the BRK tree.
14166b83d8e3Sjob  */
14176b83d8e3Sjob void
14186b83d8e3Sjob cert_insert_brks(struct brk_tree *tree, struct cert *cert)
14196b83d8e3Sjob {
14206b83d8e3Sjob 	size_t		 i, asid;
14216b83d8e3Sjob 
1422381ee599Stb 	for (i = 0; i < cert->num_ases; i++) {
1423381ee599Stb 		switch (cert->ases[i].type) {
14246b83d8e3Sjob 		case CERT_AS_ID:
1425381ee599Stb 			insert_brk(tree, cert, cert->ases[i].id);
14266b83d8e3Sjob 			break;
14276b83d8e3Sjob 		case CERT_AS_RANGE:
1428381ee599Stb 			for (asid = cert->ases[i].range.min;
1429381ee599Stb 			    asid <= cert->ases[i].range.max; asid++)
14306b83d8e3Sjob 				insert_brk(tree, cert, asid);
14316b83d8e3Sjob 			break;
14326b83d8e3Sjob 		default:
14336b83d8e3Sjob 			warnx("invalid AS identifier type");
14346b83d8e3Sjob 			continue;
14356b83d8e3Sjob 		}
14366b83d8e3Sjob 	}
14376b83d8e3Sjob }
14386b83d8e3Sjob 
14396b83d8e3Sjob static inline int
14406b83d8e3Sjob brkcmp(struct brk *a, struct brk *b)
14416b83d8e3Sjob {
144217304ed1Sjob 	int rv;
144317304ed1Sjob 
14446b83d8e3Sjob 	if (a->asid > b->asid)
14456b83d8e3Sjob 		return 1;
14466b83d8e3Sjob 	if (a->asid < b->asid)
14476b83d8e3Sjob 		return -1;
14486b83d8e3Sjob 
144917304ed1Sjob 	rv = strcmp(a->ski, b->ski);
145017304ed1Sjob 	if (rv > 0)
145117304ed1Sjob 		return 1;
145217304ed1Sjob 	if (rv < 0)
145317304ed1Sjob 		return -1;
145417304ed1Sjob 
145517304ed1Sjob 	return strcmp(a->pubkey, b->pubkey);
14566b83d8e3Sjob }
14576b83d8e3Sjob 
14586b83d8e3Sjob RB_GENERATE(brk_tree, brk, entry, brkcmp);
1459