1/*	$OpenBSD: x509.c,v 1.99 2024/06/10 12:44:06 tb Exp $ */
2/*
3 * Copyright (c) 2022 Theo Buehler <tb@openbsd.org>
4 * Copyright (c) 2021 Claudio Jeker <claudio@openbsd.org>
5 * Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv>
6 *
7 * Permission to use, copy, modify, and distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
10 *
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 */
19
20#include <assert.h>
21#include <err.h>
22#include <stdlib.h>
23#include <string.h>
24#include <unistd.h>
25
26#include <openssl/evp.h>
27#include <openssl/x509v3.h>
28
29#include "extern.h"
30
31ASN1_OBJECT	*certpol_oid;	/* id-cp-ipAddr-asNumber cert policy */
32ASN1_OBJECT	*carepo_oid;	/* 1.3.6.1.5.5.7.48.5 (caRepository) */
33ASN1_OBJECT	*manifest_oid;	/* 1.3.6.1.5.5.7.48.10 (rpkiManifest) */
34ASN1_OBJECT	*signedobj_oid;	/* 1.3.6.1.5.5.7.48.11 (signedObject) */
35ASN1_OBJECT	*notify_oid;	/* 1.3.6.1.5.5.7.48.13 (rpkiNotify) */
36ASN1_OBJECT	*roa_oid;	/* id-ct-routeOriginAuthz CMS content type */
37ASN1_OBJECT	*mft_oid;	/* id-ct-rpkiManifest CMS content type */
38ASN1_OBJECT	*gbr_oid;	/* id-ct-rpkiGhostbusters CMS content type */
39ASN1_OBJECT	*bgpsec_oid;	/* id-kp-bgpsec-router Key Purpose */
40ASN1_OBJECT	*cnt_type_oid;	/* pkcs-9 id-contentType */
41ASN1_OBJECT	*msg_dgst_oid;	/* pkcs-9 id-messageDigest */
42ASN1_OBJECT	*sign_time_oid;	/* pkcs-9 id-signingTime */
43ASN1_OBJECT	*rsc_oid;	/* id-ct-signedChecklist */
44ASN1_OBJECT	*aspa_oid;	/* id-ct-ASPA */
45ASN1_OBJECT	*tak_oid;	/* id-ct-SignedTAL */
46ASN1_OBJECT	*geofeed_oid;	/* id-ct-geofeedCSVwithCRLF */
47ASN1_OBJECT	*spl_oid;	/* id-ct-signedPrefixList */
48
49static const struct {
50	const char	 *oid;
51	ASN1_OBJECT	**ptr;
52} oid_table[] = {
53	{
54		.oid = "1.3.6.1.5.5.7.14.2",
55		.ptr = &certpol_oid,
56	},
57	{
58		.oid = "1.3.6.1.5.5.7.48.5",
59		.ptr = &carepo_oid,
60	},
61	{
62		.oid = "1.3.6.1.5.5.7.48.10",
63		.ptr = &manifest_oid,
64	},
65	{
66		.oid = "1.3.6.1.5.5.7.48.11",
67		.ptr = &signedobj_oid,
68	},
69	{
70		.oid = "1.3.6.1.5.5.7.48.13",
71		.ptr = &notify_oid,
72	},
73	{
74		.oid = "1.2.840.113549.1.9.16.1.24",
75		.ptr = &roa_oid,
76	},
77	{
78		.oid = "1.2.840.113549.1.9.16.1.26",
79		.ptr = &mft_oid,
80	},
81	{
82		.oid = "1.2.840.113549.1.9.16.1.35",
83		.ptr = &gbr_oid,
84	},
85	{
86		.oid = "1.3.6.1.5.5.7.3.30",
87		.ptr = &bgpsec_oid,
88	},
89	{
90		.oid = "1.2.840.113549.1.9.3",
91		.ptr = &cnt_type_oid,
92	},
93	{
94		.oid = "1.2.840.113549.1.9.4",
95		.ptr = &msg_dgst_oid,
96	},
97	{
98		.oid = "1.2.840.113549.1.9.5",
99		.ptr = &sign_time_oid,
100	},
101	{
102		.oid = "1.2.840.113549.1.9.16.1.47",
103		.ptr = &geofeed_oid,
104	},
105	{
106		.oid = "1.2.840.113549.1.9.16.1.48",
107		.ptr = &rsc_oid,
108	},
109	{
110		.oid = "1.2.840.113549.1.9.16.1.49",
111		.ptr = &aspa_oid,
112	},
113	{
114		.oid = "1.2.840.113549.1.9.16.1.50",
115		.ptr = &tak_oid,
116	},
117	{
118		.oid = "1.2.840.113549.1.9.16.1.51",
119		.ptr = &spl_oid,
120	},
121};
122
123void
124x509_init_oid(void)
125{
126	size_t	i;
127
128	for (i = 0; i < sizeof(oid_table) / sizeof(oid_table[0]); i++) {
129		*oid_table[i].ptr = OBJ_txt2obj(oid_table[i].oid, 1);
130		if (*oid_table[i].ptr == NULL)
131			errx(1, "OBJ_txt2obj for %s failed", oid_table[i].oid);
132	}
133}
134
135/*
136 * A number of critical OpenSSL API functions can't properly indicate failure
137 * and are unreliable if the extensions aren't already cached. An old trick is
138 * to cache the extensions using an error-checked call to X509_check_purpose()
139 * with a purpose of -1. This way functions such as X509_check_ca(), X509_cmp(),
140 * X509_get_key_usage(), X509_get_extended_key_usage() won't lie.
141 *
142 * Should be called right after deserialization and is essentially free to call
143 * multiple times.
144 */
145int
146x509_cache_extensions(X509 *x509, const char *fn)
147{
148	if (X509_check_purpose(x509, -1, 0) <= 0) {
149		warnx("%s: could not cache X509v3 extensions", fn);
150		return 0;
151	}
152	return 1;
153}
154
155/*
156 * Parse X509v3 authority key identifier (AKI), RFC 6487 sec. 4.8.3.
157 * Returns the AKI or NULL if it could not be parsed.
158 * The AKI is formatted as a hex string.
159 */
160int
161x509_get_aki(X509 *x, const char *fn, char **aki)
162{
163	const unsigned char	*d;
164	AUTHORITY_KEYID		*akid;
165	ASN1_OCTET_STRING	*os;
166	int			 dsz, crit, rc = 0;
167
168	*aki = NULL;
169	akid = X509_get_ext_d2i(x, NID_authority_key_identifier, &crit, NULL);
170	if (akid == NULL) {
171		if (crit != -1) {
172			warnx("%s: RFC 6487 section 4.8.3: error parsing AKI",
173			    fn);
174			return 0;
175		}
176		return 1;
177	}
178	if (crit != 0) {
179		warnx("%s: RFC 6487 section 4.8.3: "
180		    "AKI: extension not non-critical", fn);
181		goto out;
182	}
183	if (akid->issuer != NULL || akid->serial != NULL) {
184		warnx("%s: RFC 6487 section 4.8.3: AKI: "
185		    "authorityCertIssuer or authorityCertSerialNumber present",
186		    fn);
187		goto out;
188	}
189
190	os = akid->keyid;
191	if (os == NULL) {
192		warnx("%s: RFC 6487 section 4.8.3: AKI: "
193		    "Key Identifier missing", fn);
194		goto out;
195	}
196
197	d = os->data;
198	dsz = os->length;
199
200	if (dsz != SHA_DIGEST_LENGTH) {
201		warnx("%s: RFC 6487 section 4.8.2: AKI: "
202		    "want %d bytes SHA1 hash, have %d bytes",
203		    fn, SHA_DIGEST_LENGTH, dsz);
204		goto out;
205	}
206
207	*aki = hex_encode(d, dsz);
208	rc = 1;
209out:
210	AUTHORITY_KEYID_free(akid);
211	return rc;
212}
213
214/*
215 * Validate the X509v3 subject key identifier (SKI), RFC 6487 section 4.8.2:
216 * "The SKI is a SHA-1 hash of the value of the DER-encoded ASN.1 BIT STRING of
217 * the Subject Public Key, as described in Section 4.2.1.2 of RFC 5280."
218 * Returns the SKI formatted as hex string, or NULL if it couldn't be parsed.
219 */
220int
221x509_get_ski(X509 *x, const char *fn, char **ski)
222{
223	ASN1_OCTET_STRING	*os;
224	unsigned char		 md[EVP_MAX_MD_SIZE];
225	unsigned int		 md_len = EVP_MAX_MD_SIZE;
226	int			 crit, rc = 0;
227
228	*ski = NULL;
229	os = X509_get_ext_d2i(x, NID_subject_key_identifier, &crit, NULL);
230	if (os == NULL) {
231		if (crit != -1) {
232			warnx("%s: RFC 6487 section 4.8.2: error parsing SKI",
233			    fn);
234			return 0;
235		}
236		return 1;
237	}
238	if (crit != 0) {
239		warnx("%s: RFC 6487 section 4.8.2: "
240		    "SKI: extension not non-critical", fn);
241		goto out;
242	}
243
244	if (!X509_pubkey_digest(x, EVP_sha1(), md, &md_len)) {
245		warnx("%s: X509_pubkey_digest", fn);
246		goto out;
247	}
248
249	if (os->length < 0 || md_len != (size_t)os->length) {
250		warnx("%s: RFC 6487 section 4.8.2: SKI: "
251		    "want %u bytes SHA1 hash, have %d bytes",
252		    fn, md_len, os->length);
253		goto out;
254	}
255
256	if (memcmp(os->data, md, md_len) != 0) {
257		warnx("%s: SKI does not match SHA1 hash of SPK", fn);
258		goto out;
259	}
260
261	*ski = hex_encode(md, md_len);
262	rc = 1;
263 out:
264	ASN1_OCTET_STRING_free(os);
265	return rc;
266}
267
268/*
269 * Check the cert's purpose: the cA bit in basic constraints distinguishes
270 * between TA/CA and EE/BGPsec router and the key usage bits must match.
271 * TAs are self-signed, CAs not self-issued, EEs have no extended key usage,
272 * BGPsec router have id-kp-bgpsec-router OID.
273 */
274enum cert_purpose
275x509_get_purpose(X509 *x, const char *fn)
276{
277	BASIC_CONSTRAINTS		*bc = NULL;
278	EXTENDED_KEY_USAGE		*eku = NULL;
279	const X509_EXTENSION		*ku;
280	int				 crit, ext_flags, i, is_ca, ku_idx;
281	enum cert_purpose		 purpose = CERT_PURPOSE_INVALID;
282
283	if (!x509_cache_extensions(x, fn))
284		goto out;
285
286	ext_flags = X509_get_extension_flags(x);
287
288	/* Key usage must be present and critical. KU bits are checked below. */
289	if ((ku_idx = X509_get_ext_by_NID(x, NID_key_usage, -1)) < 0) {
290		warnx("%s: RFC 6487, section 4.8.4: missing KeyUsage", fn);
291		goto out;
292	}
293	if ((ku = X509_get_ext(x, ku_idx)) == NULL) {
294		warnx("%s: RFC 6487, section 4.8.4: missing KeyUsage", fn);
295		goto out;
296	}
297	if (!X509_EXTENSION_get_critical(ku)) {
298		warnx("%s: RFC 6487, section 4.8.4: KeyUsage not critical", fn);
299		goto out;
300	}
301
302	/* This weird API can return 0, 1, 2, 4, 5 but can't error... */
303	if ((is_ca = X509_check_ca(x)) > 1) {
304		if (is_ca == 4)
305			warnx("%s: RFC 6487: sections 4.8.1 and 4.8.4: "
306			    "no basic constraints, but keyCertSign set", fn);
307		else
308			warnx("%s: unexpected legacy certificate", fn);
309		goto out;
310	}
311
312	if (is_ca) {
313		bc = X509_get_ext_d2i(x, NID_basic_constraints, &crit, NULL);
314		if (bc == NULL) {
315			if (crit != -1)
316				warnx("%s: RFC 6487 section 4.8.1: "
317				    "error parsing basic constraints", fn);
318			else
319				warnx("%s: RFC 6487 section 4.8.1: "
320				    "missing basic constraints", fn);
321			goto out;
322		}
323		if (crit != 1) {
324			warnx("%s: RFC 6487 section 4.8.1: Basic Constraints "
325			    "must be marked critical", fn);
326			goto out;
327		}
328		if (bc->pathlen != NULL) {
329			warnx("%s: RFC 6487 section 4.8.1: Path Length "
330			    "Constraint must be absent", fn);
331			goto out;
332		}
333
334		if (X509_get_key_usage(x) != (KU_KEY_CERT_SIGN | KU_CRL_SIGN)) {
335			warnx("%s: RFC 6487 section 4.8.4: key usage violation",
336			    fn);
337			goto out;
338		}
339
340		if (X509_get_extended_key_usage(x) != UINT32_MAX) {
341			warnx("%s: RFC 6487 section 4.8.5: EKU not allowed",
342			    fn);
343			goto out;
344		}
345
346		/*
347		 * EXFLAG_SI means that issuer and subject are identical.
348		 * EXFLAG_SS is SI plus the AKI is absent or matches the SKI.
349		 * Thus, exactly the trust anchors should have EXFLAG_SS set
350		 * and we should never see EXFLAG_SI without EXFLAG_SS.
351		 */
352		if ((ext_flags & EXFLAG_SS) != 0)
353			purpose = CERT_PURPOSE_TA;
354		else if ((ext_flags & EXFLAG_SI) == 0)
355			purpose = CERT_PURPOSE_CA;
356		else
357			warnx("%s: RFC 6487, section 4.8.3: "
358			    "self-issued cert with AKI-SKI mismatch", fn);
359		goto out;
360	}
361
362	if ((ext_flags & EXFLAG_BCONS) != 0) {
363		warnx("%s: Basic Constraints ext in non-CA cert", fn);
364		goto out;
365	}
366
367	if (X509_get_key_usage(x) != KU_DIGITAL_SIGNATURE) {
368		warnx("%s: RFC 6487 section 4.8.4: KU must be digitalSignature",
369		    fn);
370		goto out;
371	}
372
373	/*
374	 * EKU is only defined for BGPsec Router certs and must be absent from
375	 * EE certs.
376	 */
377	eku = X509_get_ext_d2i(x, NID_ext_key_usage, &crit, NULL);
378	if (eku == NULL) {
379		if (crit != -1)
380			warnx("%s: error parsing EKU", fn);
381		else
382			purpose = CERT_PURPOSE_EE; /* EKU absent */
383		goto out;
384	}
385	if (crit != 0) {
386		warnx("%s: EKU: extension must not be marked critical", fn);
387		goto out;
388	}
389
390	/*
391	 * Per RFC 8209, section 3.1.3.2 the id-kp-bgpsec-router OID must be
392	 * present and others are allowed, which we don't need to recognize.
393	 * This matches RFC 5280, section 4.2.1.12.
394	 */
395	for (i = 0; i < sk_ASN1_OBJECT_num(eku); i++) {
396		if (OBJ_cmp(bgpsec_oid, sk_ASN1_OBJECT_value(eku, i)) == 0) {
397			purpose = CERT_PURPOSE_BGPSEC_ROUTER;
398			break;
399		}
400	}
401
402 out:
403	BASIC_CONSTRAINTS_free(bc);
404	EXTENDED_KEY_USAGE_free(eku);
405	return purpose;
406}
407
408/*
409 * Extract Subject Public Key Info (SPKI) from BGPsec X.509 Certificate.
410 * Returns NULL on failure, on success return the SPKI as base64 encoded pubkey
411 */
412char *
413x509_get_pubkey(X509 *x, const char *fn)
414{
415	EVP_PKEY	*pkey;
416	EC_KEY		*eckey;
417	int		 nid;
418	const char	*cname;
419	uint8_t		*pubkey = NULL;
420	char		*res = NULL;
421	int		 len;
422
423	pkey = X509_get0_pubkey(x);
424	if (pkey == NULL) {
425		warnx("%s: X509_get0_pubkey failed in %s", fn, __func__);
426		goto out;
427	}
428	if (EVP_PKEY_base_id(pkey) != EVP_PKEY_EC) {
429		warnx("%s: Expected EVP_PKEY_EC, got %d", fn,
430		    EVP_PKEY_base_id(pkey));
431		goto out;
432	}
433
434	eckey = EVP_PKEY_get0_EC_KEY(pkey);
435	if (eckey == NULL) {
436		warnx("%s: Incorrect key type", fn);
437		goto out;
438	}
439
440	nid = EC_GROUP_get_curve_name(EC_KEY_get0_group(eckey));
441	if (nid != NID_X9_62_prime256v1) {
442		if ((cname = EC_curve_nid2nist(nid)) == NULL)
443			cname = nid2str(nid);
444		warnx("%s: Expected P-256, got %s", fn, cname);
445		goto out;
446	}
447
448	if (!EC_KEY_check_key(eckey)) {
449		warnx("%s: EC_KEY_check_key failed in %s", fn, __func__);
450		goto out;
451	}
452
453	len = i2d_PUBKEY(pkey, &pubkey);
454	if (len <= 0) {
455		warnx("%s: i2d_PUBKEY failed in %s", fn, __func__);
456		goto out;
457	}
458
459	if (base64_encode(pubkey, len, &res) == -1)
460		errx(1, "base64_encode failed in %s", __func__);
461
462 out:
463	free(pubkey);
464	return res;
465}
466
467/*
468 * Compute the SKI of an RSA public key in an X509_PUBKEY using SHA-1.
469 * Returns allocated hex-encoded SKI on success, NULL on failure.
470 */
471char *
472x509_pubkey_get_ski(X509_PUBKEY *pubkey, const char *fn)
473{
474	ASN1_OBJECT		*obj;
475	const unsigned char	*der;
476	int			 der_len, nid;
477	unsigned char		 md[EVP_MAX_MD_SIZE];
478	unsigned int		 md_len = EVP_MAX_MD_SIZE;
479
480	if (!X509_PUBKEY_get0_param(&obj, &der, &der_len, NULL, pubkey)) {
481		warnx("%s: X509_PUBKEY_get0_param failed", fn);
482		return NULL;
483	}
484
485	if ((nid = OBJ_obj2nid(obj)) != NID_rsaEncryption) {
486		warnx("%s: RFC 7935: wrong signature algorithm %s, want %s",
487		    fn, nid2str(nid), LN_rsaEncryption);
488		return NULL;
489	}
490
491	if (!EVP_Digest(der, der_len, md, &md_len, EVP_sha1(), NULL)) {
492		warnx("%s: EVP_Digest failed", fn);
493		return NULL;
494	}
495
496	return hex_encode(md, md_len);
497}
498
499/*
500 * Parse the Authority Information Access (AIA) extension
501 * See RFC 6487, section 4.8.7 for details.
502 * Returns NULL on failure, on success returns the AIA URI
503 * (which has to be freed after use).
504 */
505int
506x509_get_aia(X509 *x, const char *fn, char **out_aia)
507{
508	ACCESS_DESCRIPTION		*ad;
509	AUTHORITY_INFO_ACCESS		*info;
510	int				 crit, rc = 0;
511
512	assert(*out_aia == NULL);
513
514	info = X509_get_ext_d2i(x, NID_info_access, &crit, NULL);
515	if (info == NULL) {
516		if (crit != -1) {
517			warnx("%s: RFC 6487 section 4.8.7: error parsing AIA",
518			    fn);
519			return 0;
520		}
521		return 1;
522	}
523
524	if (crit != 0) {
525		warnx("%s: RFC 6487 section 4.8.7: "
526		    "AIA: extension not non-critical", fn);
527		goto out;
528	}
529
530	if ((X509_get_extension_flags(x) & EXFLAG_SS) != 0) {
531		warnx("%s: RFC 6487 section 4.8.7: AIA must be absent from "
532		    "a self-signed certificate", fn);
533		goto out;
534	}
535
536	if (sk_ACCESS_DESCRIPTION_num(info) != 1) {
537		warnx("%s: RFC 6487 section 4.8.7: AIA: "
538		    "want 1 element, have %d", fn,
539		    sk_ACCESS_DESCRIPTION_num(info));
540		goto out;
541	}
542
543	ad = sk_ACCESS_DESCRIPTION_value(info, 0);
544	if (OBJ_obj2nid(ad->method) != NID_ad_ca_issuers) {
545		warnx("%s: RFC 6487 section 4.8.7: AIA: "
546		    "expected caIssuers, have %d", fn, OBJ_obj2nid(ad->method));
547		goto out;
548	}
549
550	if (!x509_location(fn, "AIA: caIssuers", ad->location, out_aia))
551		goto out;
552
553	rc = 1;
554
555 out:
556	AUTHORITY_INFO_ACCESS_free(info);
557	return rc;
558}
559
560/*
561 * Parse the Subject Information Access (SIA) extension for an EE cert.
562 * See RFC 6487, section 4.8.8.2 for details.
563 * Returns NULL on failure, on success returns the SIA signedObject URI
564 * (which has to be freed after use).
565 */
566int
567x509_get_sia(X509 *x, const char *fn, char **out_sia)
568{
569	ACCESS_DESCRIPTION		*ad;
570	AUTHORITY_INFO_ACCESS		*info;
571	ASN1_OBJECT			*oid;
572	int				 i, crit, rc = 0;
573
574	assert(*out_sia == NULL);
575
576	info = X509_get_ext_d2i(x, NID_sinfo_access, &crit, NULL);
577	if (info == NULL) {
578		if (crit != -1) {
579			warnx("%s: error parsing SIA", fn);
580			return 0;
581		}
582		return 1;
583	}
584
585	if (crit != 0) {
586		warnx("%s: RFC 6487 section 4.8.8: "
587		    "SIA: extension not non-critical", fn);
588		goto out;
589	}
590
591	for (i = 0; i < sk_ACCESS_DESCRIPTION_num(info); i++) {
592		char	*sia;
593
594		ad = sk_ACCESS_DESCRIPTION_value(info, i);
595		oid = ad->method;
596
597		/*
598		 * XXX: RFC 6487 4.8.8.2 states that the accessMethod MUST be
599		 * signedObject. However, rpkiNotify accessMethods currently
600		 * exist in the wild. Consider removing this special case.
601		 * See also https://www.rfc-editor.org/errata/eid7239.
602		 */
603		if (OBJ_cmp(oid, notify_oid) == 0) {
604			if (verbose > 1)
605				warnx("%s: RFC 6487 section 4.8.8.2: SIA should"
606				    " not contain rpkiNotify accessMethod", fn);
607			continue;
608		}
609		if (OBJ_cmp(oid, signedobj_oid) != 0) {
610			char buf[128];
611
612			OBJ_obj2txt(buf, sizeof(buf), oid, 0);
613			warnx("%s: RFC 6487 section 4.8.8.2: unexpected"
614			    " accessMethod: %s", fn, buf);
615			goto out;
616		}
617
618		sia = NULL;
619		if (!x509_location(fn, "SIA: signedObject", ad->location, &sia))
620			goto out;
621
622		if (*out_sia == NULL && strncasecmp(sia, RSYNC_PROTO,
623		    RSYNC_PROTO_LEN) == 0) {
624			const char *p = sia + RSYNC_PROTO_LEN;
625			size_t fnlen, plen;
626
627			if (filemode) {
628				*out_sia = sia;
629				continue;
630			}
631
632			fnlen = strlen(fn);
633			plen = strlen(p);
634
635			if (fnlen < plen || strcmp(p, fn + fnlen - plen) != 0) {
636				warnx("%s: mismatch between pathname and SIA "
637				    "(%s)", fn, sia);
638				free(sia);
639				goto out;
640			}
641
642			*out_sia = sia;
643			continue;
644		}
645		if (verbose)
646			warnx("%s: RFC 6487 section 4.8.8: SIA: "
647			    "ignoring location %s", fn, sia);
648		free(sia);
649	}
650
651	if (*out_sia == NULL) {
652		warnx("%s: RFC 6487 section 4.8.8.2: "
653		    "SIA without rsync accessLocation", fn);
654		goto out;
655	}
656
657	rc = 1;
658
659 out:
660	AUTHORITY_INFO_ACCESS_free(info);
661	return rc;
662}
663
664/*
665 * Extract the notBefore of a certificate.
666 */
667int
668x509_get_notbefore(X509 *x, const char *fn, time_t *tt)
669{
670	const ASN1_TIME	*at;
671
672	at = X509_get0_notBefore(x);
673	if (at == NULL) {
674		warnx("%s: X509_get0_notBefore failed", fn);
675		return 0;
676	}
677	if (!x509_get_time(at, tt)) {
678		warnx("%s: ASN1_TIME_to_tm failed", fn);
679		return 0;
680	}
681	return 1;
682}
683
684/*
685 * Extract the notAfter from a certificate.
686 */
687int
688x509_get_notafter(X509 *x, const char *fn, time_t *tt)
689{
690	const ASN1_TIME	*at;
691
692	at = X509_get0_notAfter(x);
693	if (at == NULL) {
694		warnx("%s: X509_get0_notafter failed", fn);
695		return 0;
696	}
697	if (!x509_get_time(at, tt)) {
698		warnx("%s: ASN1_TIME_to_tm failed", fn);
699		return 0;
700	}
701	return 1;
702}
703
704/*
705 * Check whether all RFC 3779 extensions are set to inherit.
706 * Return 1 if both AS & IP are set to inherit.
707 * Return 0 on failure (such as missing extensions or no inheritance).
708 */
709int
710x509_inherits(X509 *x)
711{
712	STACK_OF(IPAddressFamily)	*addrblk = NULL;
713	ASIdentifiers			*asidentifiers = NULL;
714	const IPAddressFamily		*af;
715	int				 crit, i, rc = 0;
716
717	addrblk = X509_get_ext_d2i(x, NID_sbgp_ipAddrBlock, &crit, NULL);
718	if (addrblk == NULL) {
719		if (crit != -1)
720			warnx("error parsing ipAddrBlock");
721		goto out;
722	}
723
724	/*
725	 * Check by hand, since X509v3_addr_inherits() success only means that
726	 * at least one address family inherits, not all of them.
727	 */
728	for (i = 0; i < sk_IPAddressFamily_num(addrblk); i++) {
729		af = sk_IPAddressFamily_value(addrblk, i);
730		if (af->ipAddressChoice->type != IPAddressChoice_inherit)
731			goto out;
732	}
733
734	asidentifiers = X509_get_ext_d2i(x, NID_sbgp_autonomousSysNum, NULL,
735	    NULL);
736	if (asidentifiers == NULL) {
737		if (crit != -1)
738			warnx("error parsing asIdentifiers");
739		goto out;
740	}
741
742	/* We need to have AS numbers and don't want RDIs. */
743	if (asidentifiers->asnum == NULL || asidentifiers->rdi != NULL)
744		goto out;
745	if (!X509v3_asid_inherits(asidentifiers))
746		goto out;
747
748	rc = 1;
749 out:
750	ASIdentifiers_free(asidentifiers);
751	sk_IPAddressFamily_pop_free(addrblk, IPAddressFamily_free);
752	return rc;
753}
754
755/*
756 * Check whether at least one RFC 3779 extension is set to inherit.
757 * Return 1 if an inherit element is encountered in AS or IP.
758 * Return 0 otherwise.
759 */
760int
761x509_any_inherits(X509 *x)
762{
763	STACK_OF(IPAddressFamily)	*addrblk = NULL;
764	ASIdentifiers			*asidentifiers = NULL;
765	int				 crit, rc = 0;
766
767	addrblk = X509_get_ext_d2i(x, NID_sbgp_ipAddrBlock, &crit, NULL);
768	if (addrblk == NULL && crit != -1)
769		warnx("error parsing ipAddrBlock");
770	if (X509v3_addr_inherits(addrblk))
771		rc = 1;
772
773	asidentifiers = X509_get_ext_d2i(x, NID_sbgp_autonomousSysNum, &crit,
774	    NULL);
775	if (asidentifiers == NULL && crit != -1)
776		warnx("error parsing asIdentifiers");
777	if (X509v3_asid_inherits(asidentifiers))
778		rc = 1;
779
780	ASIdentifiers_free(asidentifiers);
781	sk_IPAddressFamily_pop_free(addrblk, IPAddressFamily_free);
782	return rc;
783}
784
785/*
786 * Parse the very specific subset of information in the CRL distribution
787 * point extension.
788 * See RFC 6487, section 4.8.6 for details.
789 * Returns NULL on failure, the crl URI on success which has to be freed
790 * after use.
791 */
792int
793x509_get_crl(X509 *x, const char *fn, char **out_crl)
794{
795	CRL_DIST_POINTS		*crldp;
796	DIST_POINT		*dp;
797	GENERAL_NAMES		*names;
798	GENERAL_NAME		*name;
799	int			 i, crit, rc = 0;
800
801	assert(*out_crl == NULL);
802
803	crldp = X509_get_ext_d2i(x, NID_crl_distribution_points, &crit, NULL);
804	if (crldp == NULL) {
805		if (crit != -1) {
806			warnx("%s: RFC 6487 section 4.8.6: failed to parse "
807			    "CRL distribution points", fn);
808			return 0;
809		}
810		return 1;
811	}
812
813	if (crit != 0) {
814		warnx("%s: RFC 6487 section 4.8.6: "
815		    "CRL distribution point: extension not non-critical", fn);
816		goto out;
817	}
818
819	if (sk_DIST_POINT_num(crldp) != 1) {
820		warnx("%s: RFC 6487 section 4.8.6: CRL: "
821		    "want 1 element, have %d", fn,
822		    sk_DIST_POINT_num(crldp));
823		goto out;
824	}
825
826	dp = sk_DIST_POINT_value(crldp, 0);
827	if (dp->CRLissuer != NULL) {
828		warnx("%s: RFC 6487 section 4.8.6: CRL CRLIssuer field"
829		    " disallowed", fn);
830		goto out;
831	}
832	if (dp->reasons != NULL) {
833		warnx("%s: RFC 6487 section 4.8.6: CRL Reasons field"
834		    " disallowed", fn);
835		goto out;
836	}
837	if (dp->distpoint == NULL) {
838		warnx("%s: RFC 6487 section 4.8.6: CRL: "
839		    "no distribution point name", fn);
840		goto out;
841	}
842	if (dp->distpoint->dpname != NULL) {
843		warnx("%s: RFC 6487 section 4.8.6: nameRelativeToCRLIssuer"
844		    " disallowed", fn);
845		goto out;
846	}
847	/* Need to hardcode the alternative 0 due to missing macros or enum. */
848	if (dp->distpoint->type != 0) {
849		warnx("%s: RFC 6487 section 4.8.6: CRL DistributionPointName:"
850		    " expected fullName, have %d", fn, dp->distpoint->type);
851		goto out;
852	}
853
854	names = dp->distpoint->name.fullname;
855	for (i = 0; i < sk_GENERAL_NAME_num(names); i++) {
856		char	*crl = NULL;
857
858		name = sk_GENERAL_NAME_value(names, i);
859
860		if (!x509_location(fn, "CRL distribution point", name, &crl))
861			goto out;
862
863		if (*out_crl == NULL && strncasecmp(crl, RSYNC_PROTO,
864		    RSYNC_PROTO_LEN) == 0) {
865			*out_crl = crl;
866			continue;
867		}
868		if (verbose)
869			warnx("%s: ignoring CRL distribution point %s",
870			    fn, crl);
871		free(crl);
872	}
873
874	if (*out_crl == NULL) {
875		warnx("%s: RFC 6487 section 4.8.6: no rsync URI "
876		    "in CRL distributionPoint", fn);
877		goto out;
878	}
879
880	rc = 1;
881
882 out:
883	CRL_DIST_POINTS_free(crldp);
884	return rc;
885}
886
887/*
888 * Convert passed ASN1_TIME to time_t *t.
889 * Returns 1 on success and 0 on failure.
890 */
891int
892x509_get_time(const ASN1_TIME *at, time_t *t)
893{
894	struct tm	 tm;
895
896	*t = 0;
897	memset(&tm, 0, sizeof(tm));
898	/* Fail instead of silently falling back to the current time. */
899	if (at == NULL)
900		return 0;
901	if (!ASN1_TIME_to_tm(at, &tm))
902		return 0;
903	if ((*t = timegm(&tm)) == -1)
904		errx(1, "timegm failed");
905	return 1;
906}
907
908/*
909 * Extract and validate an accessLocation, RFC 6487, 4.8 and RFC 8182, 3.2.
910 * Returns 0 on failure and 1 on success.
911 */
912int
913x509_location(const char *fn, const char *descr, GENERAL_NAME *location,
914    char **out)
915{
916	ASN1_IA5STRING	*uri;
917
918	assert(*out == NULL);
919
920	if (location->type != GEN_URI) {
921		warnx("%s: RFC 6487 section 4.8: %s not URI", fn, descr);
922		return 0;
923	}
924
925	uri = location->d.uniformResourceIdentifier;
926
927	if (!valid_uri(uri->data, uri->length, NULL)) {
928		warnx("%s: RFC 6487 section 4.8: %s bad location", fn, descr);
929		return 0;
930	}
931
932	if ((*out = strndup(uri->data, uri->length)) == NULL)
933		err(1, NULL);
934
935	return 1;
936}
937
938/*
939 * Check that subject or issuer only contain commonName and serialNumber.
940 * Return 0 on failure.
941 */
942int
943x509_valid_name(const char *fn, const char *descr, const X509_NAME *xn)
944{
945	const X509_NAME_ENTRY *ne;
946	const ASN1_OBJECT *ao;
947	const ASN1_STRING *as;
948	int cn = 0, sn = 0;
949	int i, nid;
950
951	for (i = 0; i < X509_NAME_entry_count(xn); i++) {
952		if ((ne = X509_NAME_get_entry(xn, i)) == NULL) {
953			warnx("%s: X509_NAME_get_entry", fn);
954			return 0;
955		}
956		if ((ao = X509_NAME_ENTRY_get_object(ne)) == NULL) {
957			warnx("%s: X509_NAME_ENTRY_get_object", fn);
958			return 0;
959		}
960
961		nid = OBJ_obj2nid(ao);
962		switch (nid) {
963		case NID_commonName:
964			if (cn++ > 0) {
965				warnx("%s: duplicate commonName in %s",
966				    fn, descr);
967				return 0;
968			}
969			if ((as = X509_NAME_ENTRY_get_data(ne)) == NULL) {
970				warnx("%s: X509_NAME_ENTRY_get_data failed",
971				    fn);
972				return 0;
973			}
974/*
975 * The following check can be enabled after AFRINIC re-issues CA certs.
976 * https://lists.afrinic.net/pipermail/dbwg/2023-March/000436.html
977 */
978#if 0
979			/*
980			 * XXX - For some reason RFC 8209, section 3.1.1 decided
981			 * to allow UTF8String for BGPsec Router Certificates.
982			 */
983			if (ASN1_STRING_type(as) != V_ASN1_PRINTABLESTRING) {
984				warnx("%s: RFC 6487 section 4.5: commonName is"
985				    " not PrintableString", fn);
986				return 0;
987			}
988#endif
989			break;
990		case NID_serialNumber:
991			if (sn++ > 0) {
992				warnx("%s: duplicate serialNumber in %s",
993				    fn, descr);
994				return 0;
995			}
996			break;
997		case NID_undef:
998			warnx("%s: OBJ_obj2nid failed", fn);
999			return 0;
1000		default:
1001			warnx("%s: RFC 6487 section 4.5: unexpected attribute"
1002			    " %s in %s", fn, nid2str(nid), descr);
1003			return 0;
1004		}
1005	}
1006
1007	if (cn == 0) {
1008		warnx("%s: RFC 6487 section 4.5: %s missing commonName",
1009		    fn, descr);
1010		return 0;
1011	}
1012
1013	return 1;
1014}
1015
1016/*
1017 * Convert an ASN1_INTEGER into a hexstring, enforcing that it is non-negative
1018 * and representable by at most 20 octets (RFC 5280, section 4.1.2.2).
1019 * Returned string needs to be freed by the caller.
1020 */
1021char *
1022x509_convert_seqnum(const char *fn, const ASN1_INTEGER *i)
1023{
1024	BIGNUM	*seqnum = NULL;
1025	char	*s = NULL;
1026
1027	if (i == NULL)
1028		goto out;
1029
1030	if (ASN1_STRING_length(i) > 20) {
1031		warnx("%s: %s: want 20 octets or fewer, have more.",
1032		    __func__, fn);
1033		goto out;
1034	}
1035
1036	seqnum = ASN1_INTEGER_to_BN(i, NULL);
1037	if (seqnum == NULL) {
1038		warnx("%s: ASN1_INTEGER_to_BN error", fn);
1039		goto out;
1040	}
1041
1042	if (BN_is_negative(seqnum)) {
1043		warnx("%s: %s: want positive integer, have negative.",
1044		    __func__, fn);
1045		goto out;
1046	}
1047
1048	s = BN_bn2hex(seqnum);
1049	if (s == NULL)
1050		warnx("%s: BN_bn2hex error", fn);
1051
1052 out:
1053	BN_free(seqnum);
1054	return s;
1055}
1056
1057/*
1058 * Find the closest expiry moment by walking the chain of authorities.
1059 */
1060time_t
1061x509_find_expires(time_t notafter, struct auth *a, struct crl_tree *crlt)
1062{
1063	struct crl	*crl;
1064	time_t		 expires;
1065
1066	expires = notafter;
1067
1068	for (; a != NULL; a = a->issuer) {
1069		if (expires > a->cert->notafter)
1070			expires = a->cert->notafter;
1071		crl = crl_get(crlt, a);
1072		if (crl != NULL && expires > crl->nextupdate)
1073			expires = crl->nextupdate;
1074	}
1075
1076	return expires;
1077}
1078