xref: /openbsd-src/usr.sbin/rpki-client/crl.c (revision 904d9c60a494ce97b94afaf3fd42c67d7804546d)
1*904d9c60Stb /*	$OpenBSD: crl.c,v 1.43 2024/09/12 10:33:25 tb Exp $ */
29a7e9e7fSjob /*
3c79b8c8bStb  * Copyright (c) 2024 Theo Buehler <tb@openbsd.org>
49a7e9e7fSjob  * Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv>
59a7e9e7fSjob  *
69a7e9e7fSjob  * Permission to use, copy, modify, and distribute this software for any
79a7e9e7fSjob  * purpose with or without fee is hereby granted, provided that the above
89a7e9e7fSjob  * copyright notice and this permission notice appear in all copies.
99a7e9e7fSjob  *
109a7e9e7fSjob  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
119a7e9e7fSjob  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
129a7e9e7fSjob  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
139a7e9e7fSjob  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
149a7e9e7fSjob  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
159a7e9e7fSjob  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
169a7e9e7fSjob  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
179a7e9e7fSjob  */
189a7e9e7fSjob 
199a7e9e7fSjob #include <err.h>
209a7e9e7fSjob #include <stdlib.h>
219a7e9e7fSjob #include <string.h>
229a7e9e7fSjob #include <unistd.h>
239a7e9e7fSjob 
247c9af4b7Sjob #include <openssl/x509.h>
257c9af4b7Sjob 
269a7e9e7fSjob #include "extern.h"
279a7e9e7fSjob 
2809383accStb /*
29*904d9c60Stb  * Check CRL Number is present, non-critical and in [0, 2^159-1].
3009383accStb  * Otherwise ignore it per draft-spaghetti-sidrops-rpki-crl-numbers.
3109383accStb  */
3209383accStb static int
33*904d9c60Stb crl_check_crl_number(const char *fn, const X509_CRL *x509_crl)
3409383accStb {
35*904d9c60Stb 	ASN1_INTEGER		*aint = NULL;
36*904d9c60Stb 	int			 crit;
37*904d9c60Stb 	int			 ret = 0;
3809383accStb 
39*904d9c60Stb 	aint = X509_CRL_get_ext_d2i(x509_crl, NID_crl_number, &crit, NULL);
40*904d9c60Stb 	if (aint == NULL) {
41*904d9c60Stb 		if (crit != -1)
42*904d9c60Stb 			warnx("%s: RFC 6487, section 5: "
43*904d9c60Stb 			    "failed to parse CRL number", fn);
44*904d9c60Stb 		else
45*904d9c60Stb 			warnx("%s: RFC 6487, section 5: missing CRL number",
46*904d9c60Stb 			    fn);
47*904d9c60Stb 		goto out;
4809383accStb 	}
49*904d9c60Stb 	if (crit != 0) {
5009383accStb 		warnx("%s: RFC 6487, section 5: CRL number not non-critical",
5109383accStb 		    fn);
52*904d9c60Stb 		goto out;
5309383accStb 	}
5409383accStb 
55*904d9c60Stb 	ret = x509_valid_seqnum(fn, "CRL number", aint);
56*904d9c60Stb 
57*904d9c60Stb  out:
58*904d9c60Stb 	ASN1_INTEGER_free(aint);
59*904d9c60Stb 	return ret;
6009383accStb }
6109383accStb 
6209383accStb /*
6309383accStb  * Parse X509v3 authority key identifier (AKI) from the CRL.
6409383accStb  * Returns the AKI or NULL if it could not be parsed.
6509383accStb  * The AKI is formatted as a hex string.
6609383accStb  */
6709383accStb static char *
6809383accStb crl_get_aki(const char *fn, X509_CRL *x509_crl)
6909383accStb {
7009383accStb 	AUTHORITY_KEYID		*akid = NULL;
7109383accStb 	ASN1_OCTET_STRING	*os;
7209383accStb 	const unsigned char	*d;
7309383accStb 	int			 dsz, crit;
7409383accStb 	char			*res = NULL;
7509383accStb 
7609383accStb 	if ((akid = X509_CRL_get_ext_d2i(x509_crl, NID_authority_key_identifier,
7709383accStb 	    &crit, NULL)) == NULL) {
7809383accStb 		if (crit != -1)
7909383accStb 			warnx("%s: RFC 6487 section 4.8.3: AKI: "
8009383accStb 			    "failed to parse CRL extension", fn);
8109383accStb 		else
8209383accStb 			warnx("%s: RFC 6487 section 4.8.3: AKI: "
8309383accStb 			    "CRL extension missing", fn);
8409383accStb 		goto out;
8509383accStb 	}
8609383accStb 	if (crit != 0) {
8709383accStb 		warnx("%s: RFC 6487 section 4.8.3: "
8809383accStb 		    "AKI: extension not non-critical", fn);
8909383accStb 		goto out;
9009383accStb 	}
9109383accStb 	if (akid->issuer != NULL || akid->serial != NULL) {
9209383accStb 		warnx("%s: RFC 6487 section 4.8.3: AKI: "
9309383accStb 		    "authorityCertIssuer or authorityCertSerialNumber present",
9409383accStb 		    fn);
9509383accStb 		goto out;
9609383accStb 	}
9709383accStb 
9809383accStb 	os = akid->keyid;
9909383accStb 	if (os == NULL) {
10009383accStb 		warnx("%s: RFC 6487 section 4.8.3: AKI: "
10109383accStb 		    "Key Identifier missing", fn);
10209383accStb 		goto out;
10309383accStb 	}
10409383accStb 
10509383accStb 	d = os->data;
10609383accStb 	dsz = os->length;
10709383accStb 
10809383accStb 	if (dsz != SHA_DIGEST_LENGTH) {
10909383accStb 		warnx("%s: RFC 6487 section 4.8.3: AKI: "
11009383accStb 		    "want %d bytes SHA1 hash, have %d bytes",
11109383accStb 		    fn, SHA_DIGEST_LENGTH, dsz);
11209383accStb 		goto out;
11309383accStb 	}
11409383accStb 
11509383accStb 	res = hex_encode(d, dsz);
11609383accStb  out:
11709383accStb 	AUTHORITY_KEYID_free(akid);
11809383accStb 	return res;
11909383accStb }
12009383accStb 
12109383accStb /*
12209383accStb  * Check that the list of revoked certificates contains only the specified
12309383accStb  * two fields, Serial Number and Revocation Date, and that no extensions are
12409383accStb  * present.
12509383accStb  */
12609383accStb static int
12709383accStb crl_check_revoked(const char *fn, X509_CRL *x509_crl)
12809383accStb {
12909383accStb 	STACK_OF(X509_REVOKED)	*list;
13009383accStb 	X509_REVOKED		*revoked;
13109383accStb 	int			 count, i;
13209383accStb 
13309383accStb 	/* If there are no revoked certificates, there's nothing to check. */
13409383accStb 	if ((list = X509_CRL_get_REVOKED(x509_crl)) == NULL)
13509383accStb 		return 1;
13609383accStb 
13709383accStb 	if ((count = sk_X509_REVOKED_num(list)) <= 0) {
13809383accStb 		/*
13909383accStb 		 * XXX - as of May 2024, ~15% of RPKI CRLs fail this check due
14009383accStb 		 * to a bug in rpki-rs/Krill. So silently accept this for now.
14109383accStb 		 * https://github.com/NLnetLabs/krill/issues/1197
142e5e2c7b1Stb 		 * https://github.com/NLnetLabs/rpki-rs/pull/295
14309383accStb 		 */
144741b0d5fStb 		if (verbose > 1)
14509383accStb 			warnx("%s: RFC 5280, section 5.1.2.6: revoked "
14609383accStb 			    "certificate list without entries disallowed", fn);
14709383accStb 		return 1;
14809383accStb 	}
14909383accStb 
15009383accStb 	for (i = 0; i < count; i++) {
15109383accStb 		revoked = sk_X509_REVOKED_value(list, i);
15209383accStb 
15309383accStb 		/*
15409383accStb 		 * serialNumber and revocationDate are mandatory in the ASN.1
15509383accStb 		 * template, so no need to check their presence.
15609383accStb 		 *
15709383accStb 		 * XXX - due to an old bug in Krill, we can't enforce that
15809383accStb 		 * revocationDate is in the past until at least mid-2025:
15909383accStb 		 * https://github.com/NLnetLabs/krill/issues/788.
16009383accStb 		 */
16109383accStb 
16209383accStb 		if (X509_REVOKED_get0_extensions(revoked) != NULL) {
16309383accStb 			warnx("%s: RFC 6487, section 5: CRL entry extensions "
16409383accStb 			    "disallowed", fn);
16509383accStb 			return 0;
16609383accStb 		}
16709383accStb 	}
16809383accStb 
16909383accStb 	return 1;
17009383accStb }
17109383accStb 
1724bd8ba3aStb struct crl *
173cabf3a3bSclaudio crl_parse(const char *fn, const unsigned char *der, size_t len)
1749a7e9e7fSjob {
175797cceeeStb 	const unsigned char	*oder;
1764bd8ba3aStb 	struct crl		*crl;
1770466b83fStb 	const X509_NAME		*name;
1784bd8ba3aStb 	const ASN1_TIME		*at;
1794b65a085Stb 	int			 count, nid, rc = 0;
1809a7e9e7fSjob 
181cabf3a3bSclaudio 	/* just fail for empty buffers, the warning was printed elsewhere */
182cabf3a3bSclaudio 	if (der == NULL)
1835ff8d7beSclaudio 		return NULL;
1845ff8d7beSclaudio 
1854bd8ba3aStb 	if ((crl = calloc(1, sizeof(*crl))) == NULL)
1864bd8ba3aStb 		err(1, NULL);
1874bd8ba3aStb 
188797cceeeStb 	oder = der;
1894bd8ba3aStb 	if ((crl->x509_crl = d2i_X509_CRL(NULL, &der, len)) == NULL) {
190c0528901Stb 		warnx("%s: d2i_X509_CRL", fn);
1919a7e9e7fSjob 		goto out;
1929a7e9e7fSjob 	}
193797cceeeStb 	if (der != oder + len) {
194797cceeeStb 		warnx("%s: %td bytes trailing garbage", fn, oder + len - der);
195797cceeeStb 		goto out;
196797cceeeStb 	}
1979a7e9e7fSjob 
198660fcdefSjob 	if (X509_CRL_get_version(crl->x509_crl) != 1) {
199660fcdefSjob 		warnx("%s: RFC 6487 section 5: version 2 expected", fn);
200660fcdefSjob 		goto out;
201660fcdefSjob 	}
202660fcdefSjob 
2030466b83fStb 	if ((name = X509_CRL_get_issuer(crl->x509_crl)) == NULL) {
2040466b83fStb 		warnx("%s: X509_CRL_get_issuer", fn);
2050466b83fStb 		goto out;
2060466b83fStb 	}
2070466b83fStb 	if (!x509_valid_name(fn, "issuer", name))
2080466b83fStb 		goto out;
2090466b83fStb 
210073e107aStb 	if ((nid = X509_CRL_get_signature_nid(crl->x509_crl)) == NID_undef) {
211073e107aStb 		warnx("%s: unknown signature type", fn);
2127c9af4b7Sjob 		goto out;
2137c9af4b7Sjob 	}
21481a06611Sclaudio 	if (experimental && nid == NID_ecdsa_with_SHA256) {
215ec1cc732Sjob 		if (verbose)
2168fcc9cc2Sjob 			warnx("%s: P-256 support is experimental", fn);
217ec1cc732Sjob 	} else if (nid != NID_sha256WithRSAEncryption) {
2187c9af4b7Sjob 		warnx("%s: RFC 7935: wrong signature algorithm %s, want %s",
21978de3577Stb 		    fn, nid2str(nid), LN_sha256WithRSAEncryption);
2207c9af4b7Sjob 		goto out;
2217c9af4b7Sjob 	}
2227c9af4b7Sjob 
2234b65a085Stb 	/*
2244b65a085Stb 	 * RFC 6487, section 5: AKI and crlNumber MUST be present, no other
2254b65a085Stb 	 * CRL extensions are allowed.
2264b65a085Stb 	 */
2274b65a085Stb 	if ((count = X509_CRL_get_ext_count(crl->x509_crl)) != 2) {
2284b65a085Stb 		warnx("%s: RFC 6487 section 5: unexpected number of extensions "
2294b65a085Stb 		    "%d != 2", fn, count);
2304bd8ba3aStb 		goto out;
2314bd8ba3aStb 	}
232*904d9c60Stb 	if (!crl_check_crl_number(fn, crl->x509_crl))
23309383accStb 		goto out;
23409383accStb 	if ((crl->aki = crl_get_aki(fn, crl->x509_crl)) == NULL)
23509383accStb 		goto out;
2364bd8ba3aStb 
237d7e95037Stb 	at = X509_CRL_get0_lastUpdate(crl->x509_crl);
238d7e95037Stb 	if (at == NULL) {
239d7e95037Stb 		warnx("%s: X509_CRL_get0_lastUpdate failed", fn);
240d7e95037Stb 		goto out;
241d7e95037Stb 	}
242c527cc7aSjob 	if (!x509_get_time(at, &crl->thisupdate)) {
2435abefff6Stb 		warnx("%s: ASN1_TIME_to_tm failed", fn);
244d7e95037Stb 		goto out;
245d7e95037Stb 	}
246d7e95037Stb 
2474bd8ba3aStb 	at = X509_CRL_get0_nextUpdate(crl->x509_crl);
2484bd8ba3aStb 	if (at == NULL) {
2494bd8ba3aStb 		warnx("%s: X509_CRL_get0_nextUpdate failed", fn);
2504bd8ba3aStb 		goto out;
2514bd8ba3aStb 	}
2529f544822Sjob 	if (!x509_get_time(at, &crl->nextupdate)) {
2535abefff6Stb 		warnx("%s: ASN1_TIME_to_tm failed", fn);
2544bd8ba3aStb 		goto out;
2554bd8ba3aStb 	}
2564bd8ba3aStb 
25709383accStb 	if (!crl_check_revoked(fn, crl->x509_crl))
25809383accStb 		goto out;
25909383accStb 
2609a7e9e7fSjob 	rc = 1;
2619a7e9e7fSjob  out:
2629a7e9e7fSjob 	if (rc == 0) {
2634bd8ba3aStb 		crl_free(crl);
2644bd8ba3aStb 		crl = NULL;
2659a7e9e7fSjob 	}
2664bd8ba3aStb 	return crl;
2679a7e9e7fSjob }
2689a7e9e7fSjob 
26951b3988bSbenno static inline int
27051b3988bSbenno crlcmp(struct crl *a, struct crl *b)
27151b3988bSbenno {
272c207abadSjob 	int	 cmp;
273c207abadSjob 
274c207abadSjob 	cmp = strcmp(a->aki, b->aki);
275c207abadSjob 	if (cmp > 0)
276c207abadSjob 		return 1;
277c207abadSjob 	if (cmp < 0)
278c207abadSjob 		return -1;
279c207abadSjob 
280c207abadSjob 	/*
281c207abadSjob 	 * In filemode the mftpath cannot be determined easily,
282c207abadSjob 	 * but it is always set in normal top-down validation.
283c207abadSjob 	 */
284c207abadSjob 	if (a->mftpath == NULL || b->mftpath == NULL)
285c207abadSjob 		return 0;
286c207abadSjob 
287c207abadSjob 	cmp = strcmp(a->mftpath, b->mftpath);
288c207abadSjob 	if (cmp > 0)
289c207abadSjob 		return 1;
290c207abadSjob 	if (cmp < 0)
291c207abadSjob 		return -1;
292c207abadSjob 
293c207abadSjob 	return 0;
29451b3988bSbenno }
29551b3988bSbenno 
296c4a9443cSclaudio RB_GENERATE_STATIC(crl_tree, crl, entry, crlcmp);
297c4a9443cSclaudio 
298c4a9443cSclaudio /*
299c4a9443cSclaudio  * Find a CRL based on the auth SKI value.
300c4a9443cSclaudio  */
301c4a9443cSclaudio struct crl *
302c4a9443cSclaudio crl_get(struct crl_tree *crlt, const struct auth *a)
303c4a9443cSclaudio {
304c4a9443cSclaudio 	struct crl	find;
305c4a9443cSclaudio 
306f8e924bcStb 	/* XXX - this should be removed, but filemode relies on it. */
307f8e924bcStb 	if (a == NULL)
308f8e924bcStb 		return NULL;
309f8e924bcStb 
310c4a9443cSclaudio 	find.aki = a->cert->ski;
311c207abadSjob 	find.mftpath = a->cert->mft;
312c207abadSjob 
313c4a9443cSclaudio 	return RB_FIND(crl_tree, crlt, &find);
314c4a9443cSclaudio }
315c4a9443cSclaudio 
316c4a9443cSclaudio int
317c4a9443cSclaudio crl_insert(struct crl_tree *crlt, struct crl *crl)
318c4a9443cSclaudio {
319c4a9443cSclaudio 	return RB_INSERT(crl_tree, crlt, crl) == NULL;
320c4a9443cSclaudio }
32151b3988bSbenno 
32251b3988bSbenno void
3234bd8ba3aStb crl_free(struct crl *crl)
32451b3988bSbenno {
3254bd8ba3aStb 	if (crl == NULL)
3264bd8ba3aStb 		return;
327e669621fSclaudio 	free(crl->aki);
328c207abadSjob 	free(crl->mftpath);
32951b3988bSbenno 	X509_CRL_free(crl->x509_crl);
33051b3988bSbenno 	free(crl);
33151b3988bSbenno }
33291176c18Sjob 
33391176c18Sjob void
33491176c18Sjob crl_tree_free(struct crl_tree *crlt)
33591176c18Sjob {
33691176c18Sjob 	struct crl	*crl, *tcrl;
33791176c18Sjob 
33891176c18Sjob 	RB_FOREACH_SAFE(crl, crl_tree, crlt, tcrl) {
33991176c18Sjob 		RB_REMOVE(crl_tree, crlt, crl);
34091176c18Sjob 		crl_free(crl);
34191176c18Sjob 	}
34291176c18Sjob }
343