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