xref: /openbsd/usr.sbin/rpki-client/x509.c (revision dab7a176)
1 /*	$OpenBSD: x509.c,v 1.105 2024/12/03 14:51:09 job 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 
31 ASN1_OBJECT	*certpol_oid;	/* id-cp-ipAddr-asNumber cert policy */
32 ASN1_OBJECT	*carepo_oid;	/* 1.3.6.1.5.5.7.48.5 (caRepository) */
33 ASN1_OBJECT	*manifest_oid;	/* 1.3.6.1.5.5.7.48.10 (rpkiManifest) */
34 ASN1_OBJECT	*signedobj_oid;	/* 1.3.6.1.5.5.7.48.11 (signedObject) */
35 ASN1_OBJECT	*notify_oid;	/* 1.3.6.1.5.5.7.48.13 (rpkiNotify) */
36 ASN1_OBJECT	*roa_oid;	/* id-ct-routeOriginAuthz CMS content type */
37 ASN1_OBJECT	*mft_oid;	/* id-ct-rpkiManifest CMS content type */
38 ASN1_OBJECT	*gbr_oid;	/* id-ct-rpkiGhostbusters CMS content type */
39 ASN1_OBJECT	*bgpsec_oid;	/* id-kp-bgpsec-router Key Purpose */
40 ASN1_OBJECT	*cnt_type_oid;	/* pkcs-9 id-contentType */
41 ASN1_OBJECT	*msg_dgst_oid;	/* pkcs-9 id-messageDigest */
42 ASN1_OBJECT	*sign_time_oid;	/* pkcs-9 id-signingTime */
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 ASN1_OBJECT	*spl_oid;	/* id-ct-signedPrefixList */
48 
49 static 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 
123 void
x509_init_oid(void)124 x509_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  */
145 int
x509_cache_extensions(X509 * x509,const char * fn)146 x509_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  */
160 int
x509_get_aki(X509 * x,const char * fn,char ** aki)161 x509_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;
209 out:
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  */
220 int
x509_get_ski(X509 * x,const char * fn,char ** ski)221 x509_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  */
274 enum cert_purpose
x509_get_purpose(X509 * x,const char * fn)275 x509_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  */
412 char *
x509_get_pubkey(X509 * x,const char * fn)413 x509_get_pubkey(X509 *x, const char *fn)
414 {
415 	EVP_PKEY	*pkey;
416 	const EC_KEY	*eckey;
417 	const EC_GROUP	*ecg;
418 	int		 nid;
419 	const char	*cname;
420 	uint8_t		*pubkey = NULL;
421 	char		*res = NULL;
422 	int		 len;
423 
424 	pkey = X509_get0_pubkey(x);
425 	if (pkey == NULL) {
426 		warnx("%s: X509_get0_pubkey failed in %s", fn, __func__);
427 		goto out;
428 	}
429 	if (EVP_PKEY_base_id(pkey) != EVP_PKEY_EC) {
430 		warnx("%s: Expected EVP_PKEY_EC, got %d", fn,
431 		    EVP_PKEY_base_id(pkey));
432 		goto out;
433 	}
434 
435 	eckey = EVP_PKEY_get0_EC_KEY(pkey);
436 	if (eckey == NULL) {
437 		warnx("%s: Incorrect key type", fn);
438 		goto out;
439 	}
440 
441 	if ((ecg = EC_KEY_get0_group(eckey)) == NULL) {
442 		warnx("%s: EC_KEY_get0_group failed", fn);
443 		goto out;
444 	}
445 
446 	if (EC_GROUP_get_asn1_flag(ecg) != OPENSSL_EC_NAMED_CURVE) {
447 		warnx("%s: curve encoding issue", fn);
448 		goto out;
449 	}
450 
451 	if (EC_GROUP_get_point_conversion_form(ecg) !=
452 	    POINT_CONVERSION_UNCOMPRESSED)
453 		warnx("%s: unconventional point encoding", fn);
454 
455 	nid = EC_GROUP_get_curve_name(ecg);
456 	if (nid != NID_X9_62_prime256v1) {
457 		if ((cname = EC_curve_nid2nist(nid)) == NULL)
458 			cname = nid2str(nid);
459 		warnx("%s: Expected P-256, got %s", fn, cname);
460 		goto out;
461 	}
462 
463 	if (!EC_KEY_check_key(eckey)) {
464 		warnx("%s: EC_KEY_check_key failed in %s", fn, __func__);
465 		goto out;
466 	}
467 
468 	len = i2d_PUBKEY(pkey, &pubkey);
469 	if (len <= 0) {
470 		warnx("%s: i2d_PUBKEY failed in %s", fn, __func__);
471 		goto out;
472 	}
473 
474 	if (base64_encode(pubkey, len, &res) == -1)
475 		errx(1, "base64_encode failed in %s", __func__);
476 
477  out:
478 	free(pubkey);
479 	return res;
480 }
481 
482 /*
483  * Compute the SKI of an RSA public key in an X509_PUBKEY using SHA-1.
484  * Returns allocated hex-encoded SKI on success, NULL on failure.
485  */
486 char *
x509_pubkey_get_ski(X509_PUBKEY * pubkey,const char * fn)487 x509_pubkey_get_ski(X509_PUBKEY *pubkey, const char *fn)
488 {
489 	ASN1_OBJECT		*obj;
490 	const unsigned char	*der;
491 	int			 der_len, nid;
492 	unsigned char		 md[EVP_MAX_MD_SIZE];
493 	unsigned int		 md_len = EVP_MAX_MD_SIZE;
494 
495 	if (!X509_PUBKEY_get0_param(&obj, &der, &der_len, NULL, pubkey)) {
496 		warnx("%s: X509_PUBKEY_get0_param failed", fn);
497 		return NULL;
498 	}
499 
500 	/* XXX - should allow other keys as well. */
501 	if ((nid = OBJ_obj2nid(obj)) != NID_rsaEncryption) {
502 		warnx("%s: RFC 7935: wrong signature algorithm %s, want %s",
503 		    fn, nid2str(nid), LN_rsaEncryption);
504 		return NULL;
505 	}
506 
507 	if (!EVP_Digest(der, der_len, md, &md_len, EVP_sha1(), NULL)) {
508 		warnx("%s: EVP_Digest failed", fn);
509 		return NULL;
510 	}
511 
512 	return hex_encode(md, md_len);
513 }
514 
515 /*
516  * Parse the Authority Information Access (AIA) extension
517  * See RFC 6487, section 4.8.7 for details.
518  * Returns NULL on failure, on success returns the AIA URI
519  * (which has to be freed after use).
520  */
521 int
x509_get_aia(X509 * x,const char * fn,char ** out_aia)522 x509_get_aia(X509 *x, const char *fn, char **out_aia)
523 {
524 	ACCESS_DESCRIPTION		*ad;
525 	AUTHORITY_INFO_ACCESS		*info;
526 	int				 crit, rc = 0;
527 
528 	assert(*out_aia == NULL);
529 
530 	info = X509_get_ext_d2i(x, NID_info_access, &crit, NULL);
531 	if (info == NULL) {
532 		if (crit != -1) {
533 			warnx("%s: RFC 6487 section 4.8.7: error parsing AIA",
534 			    fn);
535 			return 0;
536 		}
537 		return 1;
538 	}
539 
540 	if (crit != 0) {
541 		warnx("%s: RFC 6487 section 4.8.7: "
542 		    "AIA: extension not non-critical", fn);
543 		goto out;
544 	}
545 
546 	if ((X509_get_extension_flags(x) & EXFLAG_SS) != 0) {
547 		warnx("%s: RFC 6487 section 4.8.7: AIA must be absent from "
548 		    "a self-signed certificate", fn);
549 		goto out;
550 	}
551 
552 	if (sk_ACCESS_DESCRIPTION_num(info) != 1) {
553 		warnx("%s: RFC 6487 section 4.8.7: AIA: "
554 		    "want 1 element, have %d", fn,
555 		    sk_ACCESS_DESCRIPTION_num(info));
556 		goto out;
557 	}
558 
559 	ad = sk_ACCESS_DESCRIPTION_value(info, 0);
560 	if (OBJ_obj2nid(ad->method) != NID_ad_ca_issuers) {
561 		warnx("%s: RFC 6487 section 4.8.7: AIA: "
562 		    "expected caIssuers, have %d", fn, OBJ_obj2nid(ad->method));
563 		goto out;
564 	}
565 
566 	if (!x509_location(fn, "AIA: caIssuers", ad->location, out_aia))
567 		goto out;
568 
569 	rc = 1;
570 
571  out:
572 	AUTHORITY_INFO_ACCESS_free(info);
573 	return rc;
574 }
575 
576 /*
577  * Parse the Subject Information Access (SIA) extension for an EE cert.
578  * See RFC 6487, section 4.8.8.2 for details.
579  * Returns NULL on failure, on success returns the SIA signedObject URI
580  * (which has to be freed after use).
581  */
582 int
x509_get_sia(X509 * x,const char * fn,char ** out_sia)583 x509_get_sia(X509 *x, const char *fn, char **out_sia)
584 {
585 	ACCESS_DESCRIPTION		*ad;
586 	AUTHORITY_INFO_ACCESS		*info;
587 	ASN1_OBJECT			*oid;
588 	int				 i, crit, rc = 0;
589 
590 	assert(*out_sia == NULL);
591 
592 	info = X509_get_ext_d2i(x, NID_sinfo_access, &crit, NULL);
593 	if (info == NULL) {
594 		if (crit != -1) {
595 			warnx("%s: error parsing SIA", fn);
596 			return 0;
597 		}
598 		return 1;
599 	}
600 
601 	if (crit != 0) {
602 		warnx("%s: RFC 6487 section 4.8.8: "
603 		    "SIA: extension not non-critical", fn);
604 		goto out;
605 	}
606 
607 	for (i = 0; i < sk_ACCESS_DESCRIPTION_num(info); i++) {
608 		char	*sia;
609 
610 		ad = sk_ACCESS_DESCRIPTION_value(info, i);
611 		oid = ad->method;
612 
613 		/*
614 		 * XXX: RFC 6487 4.8.8.2 states that the accessMethod MUST be
615 		 * signedObject. However, rpkiNotify accessMethods currently
616 		 * exist in the wild. Consider removing this special case.
617 		 * See also https://www.rfc-editor.org/errata/eid7239.
618 		 */
619 		if (OBJ_cmp(oid, notify_oid) == 0) {
620 			if (verbose > 1)
621 				warnx("%s: RFC 6487 section 4.8.8.2: SIA should"
622 				    " not contain rpkiNotify accessMethod", fn);
623 			continue;
624 		}
625 		if (OBJ_cmp(oid, signedobj_oid) != 0) {
626 			char buf[128];
627 
628 			OBJ_obj2txt(buf, sizeof(buf), oid, 0);
629 			warnx("%s: RFC 6487 section 4.8.8.2: unexpected"
630 			    " accessMethod: %s", fn, buf);
631 			goto out;
632 		}
633 
634 		sia = NULL;
635 		if (!x509_location(fn, "SIA: signedObject", ad->location, &sia))
636 			goto out;
637 
638 		if (*out_sia == NULL && strncasecmp(sia, RSYNC_PROTO,
639 		    RSYNC_PROTO_LEN) == 0) {
640 			const char *p = sia + RSYNC_PROTO_LEN;
641 			size_t fnlen, plen;
642 
643 			if (filemode) {
644 				*out_sia = sia;
645 				continue;
646 			}
647 
648 			fnlen = strlen(fn);
649 			plen = strlen(p);
650 
651 			if (fnlen < plen || strcmp(p, fn + fnlen - plen) != 0) {
652 				warnx("%s: mismatch between pathname and SIA "
653 				    "(%s)", fn, sia);
654 				free(sia);
655 				goto out;
656 			}
657 
658 			*out_sia = sia;
659 			continue;
660 		}
661 		if (verbose)
662 			warnx("%s: RFC 6487 section 4.8.8: SIA: "
663 			    "ignoring location %s", fn, sia);
664 		free(sia);
665 	}
666 
667 	if (*out_sia == NULL) {
668 		warnx("%s: RFC 6487 section 4.8.8.2: "
669 		    "SIA without rsync accessLocation", fn);
670 		goto out;
671 	}
672 
673 	rc = 1;
674 
675  out:
676 	AUTHORITY_INFO_ACCESS_free(info);
677 	return rc;
678 }
679 
680 /*
681  * Extract the notBefore of a certificate.
682  */
683 int
x509_get_notbefore(X509 * x,const char * fn,time_t * tt)684 x509_get_notbefore(X509 *x, const char *fn, time_t *tt)
685 {
686 	const ASN1_TIME	*at;
687 
688 	at = X509_get0_notBefore(x);
689 	if (at == NULL) {
690 		warnx("%s: X509_get0_notBefore failed", fn);
691 		return 0;
692 	}
693 	if (!x509_get_time(at, tt)) {
694 		warnx("%s: ASN1_TIME_to_tm failed", fn);
695 		return 0;
696 	}
697 	return 1;
698 }
699 
700 /*
701  * Extract the notAfter from a certificate.
702  */
703 int
x509_get_notafter(X509 * x,const char * fn,time_t * tt)704 x509_get_notafter(X509 *x, const char *fn, time_t *tt)
705 {
706 	const ASN1_TIME	*at;
707 
708 	at = X509_get0_notAfter(x);
709 	if (at == NULL) {
710 		warnx("%s: X509_get0_notafter failed", fn);
711 		return 0;
712 	}
713 	if (!x509_get_time(at, tt)) {
714 		warnx("%s: ASN1_TIME_to_tm failed", fn);
715 		return 0;
716 	}
717 	return 1;
718 }
719 
720 /*
721  * Check whether all RFC 3779 extensions are set to inherit.
722  * Return 1 if both AS & IP are set to inherit.
723  * Return 0 on failure (such as missing extensions or no inheritance).
724  */
725 int
x509_inherits(X509 * x)726 x509_inherits(X509 *x)
727 {
728 	STACK_OF(IPAddressFamily)	*addrblk = NULL;
729 	ASIdentifiers			*asidentifiers = NULL;
730 	const IPAddressFamily		*af;
731 	int				 crit, i, rc = 0;
732 
733 	addrblk = X509_get_ext_d2i(x, NID_sbgp_ipAddrBlock, &crit, NULL);
734 	if (addrblk == NULL) {
735 		if (crit != -1)
736 			warnx("error parsing ipAddrBlock");
737 		goto out;
738 	}
739 
740 	/*
741 	 * Check by hand, since X509v3_addr_inherits() success only means that
742 	 * at least one address family inherits, not all of them.
743 	 */
744 	for (i = 0; i < sk_IPAddressFamily_num(addrblk); i++) {
745 		af = sk_IPAddressFamily_value(addrblk, i);
746 		if (af->ipAddressChoice->type != IPAddressChoice_inherit)
747 			goto out;
748 	}
749 
750 	asidentifiers = X509_get_ext_d2i(x, NID_sbgp_autonomousSysNum, NULL,
751 	    NULL);
752 	if (asidentifiers == NULL) {
753 		if (crit != -1)
754 			warnx("error parsing asIdentifiers");
755 		goto out;
756 	}
757 
758 	/* We need to have AS numbers and don't want RDIs. */
759 	if (asidentifiers->asnum == NULL || asidentifiers->rdi != NULL)
760 		goto out;
761 	if (!X509v3_asid_inherits(asidentifiers))
762 		goto out;
763 
764 	rc = 1;
765  out:
766 	ASIdentifiers_free(asidentifiers);
767 	sk_IPAddressFamily_pop_free(addrblk, IPAddressFamily_free);
768 	return rc;
769 }
770 
771 /*
772  * Check whether at least one RFC 3779 extension is set to inherit.
773  * Return 1 if an inherit element is encountered in AS or IP.
774  * Return 0 otherwise.
775  */
776 int
x509_any_inherits(X509 * x)777 x509_any_inherits(X509 *x)
778 {
779 	STACK_OF(IPAddressFamily)	*addrblk = NULL;
780 	ASIdentifiers			*asidentifiers = NULL;
781 	int				 crit, rc = 0;
782 
783 	addrblk = X509_get_ext_d2i(x, NID_sbgp_ipAddrBlock, &crit, NULL);
784 	if (addrblk == NULL && crit != -1)
785 		warnx("error parsing ipAddrBlock");
786 	if (X509v3_addr_inherits(addrblk))
787 		rc = 1;
788 
789 	asidentifiers = X509_get_ext_d2i(x, NID_sbgp_autonomousSysNum, &crit,
790 	    NULL);
791 	if (asidentifiers == NULL && crit != -1)
792 		warnx("error parsing asIdentifiers");
793 	if (X509v3_asid_inherits(asidentifiers))
794 		rc = 1;
795 
796 	ASIdentifiers_free(asidentifiers);
797 	sk_IPAddressFamily_pop_free(addrblk, IPAddressFamily_free);
798 	return rc;
799 }
800 
801 /*
802  * Parse the very specific subset of information in the CRL distribution
803  * point extension.
804  * See RFC 6487, section 4.8.6 for details.
805  * Returns NULL on failure, the crl URI on success which has to be freed
806  * after use.
807  */
808 int
x509_get_crl(X509 * x,const char * fn,char ** out_crl)809 x509_get_crl(X509 *x, const char *fn, char **out_crl)
810 {
811 	CRL_DIST_POINTS		*crldp;
812 	DIST_POINT		*dp;
813 	GENERAL_NAMES		*names;
814 	GENERAL_NAME		*name;
815 	int			 i, crit, rc = 0;
816 
817 	assert(*out_crl == NULL);
818 
819 	crldp = X509_get_ext_d2i(x, NID_crl_distribution_points, &crit, NULL);
820 	if (crldp == NULL) {
821 		if (crit != -1) {
822 			warnx("%s: RFC 6487 section 4.8.6: failed to parse "
823 			    "CRL distribution points", fn);
824 			return 0;
825 		}
826 		return 1;
827 	}
828 
829 	if (crit != 0) {
830 		warnx("%s: RFC 6487 section 4.8.6: "
831 		    "CRL distribution point: extension not non-critical", fn);
832 		goto out;
833 	}
834 
835 	if (sk_DIST_POINT_num(crldp) != 1) {
836 		warnx("%s: RFC 6487 section 4.8.6: CRL: "
837 		    "want 1 element, have %d", fn,
838 		    sk_DIST_POINT_num(crldp));
839 		goto out;
840 	}
841 
842 	dp = sk_DIST_POINT_value(crldp, 0);
843 	if (dp->CRLissuer != NULL) {
844 		warnx("%s: RFC 6487 section 4.8.6: CRL CRLIssuer field"
845 		    " disallowed", fn);
846 		goto out;
847 	}
848 	if (dp->reasons != NULL) {
849 		warnx("%s: RFC 6487 section 4.8.6: CRL Reasons field"
850 		    " disallowed", fn);
851 		goto out;
852 	}
853 	if (dp->distpoint == NULL) {
854 		warnx("%s: RFC 6487 section 4.8.6: CRL: "
855 		    "no distribution point name", fn);
856 		goto out;
857 	}
858 	if (dp->distpoint->dpname != NULL) {
859 		warnx("%s: RFC 6487 section 4.8.6: nameRelativeToCRLIssuer"
860 		    " disallowed", fn);
861 		goto out;
862 	}
863 	/* Need to hardcode the alternative 0 due to missing macros or enum. */
864 	if (dp->distpoint->type != 0) {
865 		warnx("%s: RFC 6487 section 4.8.6: CRL DistributionPointName:"
866 		    " expected fullName, have %d", fn, dp->distpoint->type);
867 		goto out;
868 	}
869 
870 	names = dp->distpoint->name.fullname;
871 	for (i = 0; i < sk_GENERAL_NAME_num(names); i++) {
872 		char	*crl = NULL;
873 
874 		name = sk_GENERAL_NAME_value(names, i);
875 
876 		if (!x509_location(fn, "CRL distribution point", name, &crl))
877 			goto out;
878 
879 		if (*out_crl == NULL && strncasecmp(crl, RSYNC_PROTO,
880 		    RSYNC_PROTO_LEN) == 0) {
881 			*out_crl = crl;
882 			continue;
883 		}
884 		if (verbose)
885 			warnx("%s: ignoring CRL distribution point %s",
886 			    fn, crl);
887 		free(crl);
888 	}
889 
890 	if (*out_crl == NULL) {
891 		warnx("%s: RFC 6487 section 4.8.6: no rsync URI "
892 		    "in CRL distributionPoint", fn);
893 		goto out;
894 	}
895 
896 	rc = 1;
897 
898  out:
899 	CRL_DIST_POINTS_free(crldp);
900 	return rc;
901 }
902 
903 /*
904  * Convert passed ASN1_TIME to time_t *t.
905  * Returns 1 on success and 0 on failure.
906  */
907 int
x509_get_time(const ASN1_TIME * at,time_t * t)908 x509_get_time(const ASN1_TIME *at, time_t *t)
909 {
910 	struct tm	 tm;
911 
912 	*t = 0;
913 	memset(&tm, 0, sizeof(tm));
914 	/* Fail instead of silently falling back to the current time. */
915 	if (at == NULL)
916 		return 0;
917 	if (!ASN1_TIME_to_tm(at, &tm))
918 		return 0;
919 	if ((*t = timegm(&tm)) == -1)
920 		errx(1, "timegm failed");
921 	return 1;
922 }
923 
924 /*
925  * Extract and validate an accessLocation, RFC 6487, 4.8 and RFC 8182, 3.2.
926  * Returns 0 on failure and 1 on success.
927  */
928 int
x509_location(const char * fn,const char * descr,GENERAL_NAME * location,char ** out)929 x509_location(const char *fn, const char *descr, GENERAL_NAME *location,
930     char **out)
931 {
932 	ASN1_IA5STRING	*uri;
933 
934 	assert(*out == NULL);
935 
936 	if (location->type != GEN_URI) {
937 		warnx("%s: RFC 6487 section 4.8: %s not URI", fn, descr);
938 		return 0;
939 	}
940 
941 	uri = location->d.uniformResourceIdentifier;
942 
943 	if (!valid_uri(uri->data, uri->length, NULL)) {
944 		warnx("%s: RFC 6487 section 4.8: %s bad location", fn, descr);
945 		return 0;
946 	}
947 
948 	if ((*out = strndup(uri->data, uri->length)) == NULL)
949 		err(1, NULL);
950 
951 	return 1;
952 }
953 
954 /*
955  * Check that subject or issuer only contain commonName and serialNumber.
956  * Return 0 on failure.
957  */
958 int
x509_valid_name(const char * fn,const char * descr,const X509_NAME * xn)959 x509_valid_name(const char *fn, const char *descr, const X509_NAME *xn)
960 {
961 	const X509_NAME_ENTRY *ne;
962 	const ASN1_OBJECT *ao;
963 	const ASN1_STRING *as;
964 	int cn = 0, sn = 0;
965 	int i, nid;
966 
967 	for (i = 0; i < X509_NAME_entry_count(xn); i++) {
968 		if ((ne = X509_NAME_get_entry(xn, i)) == NULL) {
969 			warnx("%s: X509_NAME_get_entry", fn);
970 			return 0;
971 		}
972 		if ((ao = X509_NAME_ENTRY_get_object(ne)) == NULL) {
973 			warnx("%s: X509_NAME_ENTRY_get_object", fn);
974 			return 0;
975 		}
976 
977 		nid = OBJ_obj2nid(ao);
978 		switch (nid) {
979 		case NID_commonName:
980 			if (cn++ > 0) {
981 				warnx("%s: duplicate commonName in %s",
982 				    fn, descr);
983 				return 0;
984 			}
985 			if ((as = X509_NAME_ENTRY_get_data(ne)) == NULL) {
986 				warnx("%s: X509_NAME_ENTRY_get_data failed",
987 				    fn);
988 				return 0;
989 			}
990 /*
991  * The following check can be enabled after AFRINIC re-issues CA certs.
992  * https://lists.afrinic.net/pipermail/dbwg/2023-March/000436.html
993  */
994 #if 0
995 			/*
996 			 * XXX - For some reason RFC 8209, section 3.1.1 decided
997 			 * to allow UTF8String for BGPsec Router Certificates.
998 			 */
999 			if (ASN1_STRING_type(as) != V_ASN1_PRINTABLESTRING) {
1000 				warnx("%s: RFC 6487 section 4.5: commonName is"
1001 				    " not PrintableString", fn);
1002 				return 0;
1003 			}
1004 #endif
1005 			break;
1006 		case NID_serialNumber:
1007 			if (sn++ > 0) {
1008 				warnx("%s: duplicate serialNumber in %s",
1009 				    fn, descr);
1010 				return 0;
1011 			}
1012 			break;
1013 		case NID_undef:
1014 			warnx("%s: OBJ_obj2nid failed", fn);
1015 			return 0;
1016 		default:
1017 			warnx("%s: RFC 6487 section 4.5: unexpected attribute"
1018 			    " %s in %s", fn, nid2str(nid), descr);
1019 			return 0;
1020 		}
1021 	}
1022 
1023 	if (cn == 0) {
1024 		warnx("%s: RFC 6487 section 4.5: %s missing commonName",
1025 		    fn, descr);
1026 		return 0;
1027 	}
1028 
1029 	return 1;
1030 }
1031 
1032 /*
1033  * Check ASN1_INTEGER is non-negative and fits in 20 octets.
1034  * Returns allocated BIGNUM if true, NULL otherwise.
1035  */
1036 static BIGNUM *
x509_seqnum_to_bn(const char * fn,const char * descr,const ASN1_INTEGER * i)1037 x509_seqnum_to_bn(const char *fn, const char *descr, const ASN1_INTEGER *i)
1038 {
1039 	BIGNUM *bn = NULL;
1040 
1041 	if ((bn = ASN1_INTEGER_to_BN(i, NULL)) == NULL) {
1042 		warnx("%s: %s: ASN1_INTEGER_to_BN error", fn, descr);
1043 		goto out;
1044 	}
1045 
1046 	if (BN_is_negative(bn)) {
1047 		warnx("%s: %s should be non-negative", fn, descr);
1048 		goto out;
1049 	}
1050 
1051 	/* Reject values larger than or equal to 2^159. */
1052 	if (BN_num_bytes(bn) > 20 || BN_is_bit_set(bn, 159)) {
1053 		warnx("%s: %s should fit in 20 octets", fn, descr);
1054 		goto out;
1055 	}
1056 
1057 	return bn;
1058 
1059  out:
1060 	BN_free(bn);
1061 	return NULL;
1062 }
1063 
1064 /*
1065  * Convert an ASN1_INTEGER into a hexstring, enforcing that it is non-negative
1066  * and representable by at most 20 octets (RFC 5280, section 4.1.2.2).
1067  * Returned string needs to be freed by the caller.
1068  */
1069 char *
x509_convert_seqnum(const char * fn,const char * descr,const ASN1_INTEGER * i)1070 x509_convert_seqnum(const char *fn, const char *descr, const ASN1_INTEGER *i)
1071 {
1072 	BIGNUM	*bn = NULL;
1073 	char	*s = NULL;
1074 
1075 	if (i == NULL)
1076 		goto out;
1077 
1078 	if ((bn = x509_seqnum_to_bn(fn, descr, i)) == NULL)
1079 		goto out;
1080 
1081 	if ((s = BN_bn2hex(bn)) == NULL)
1082 		warnx("%s: %s: BN_bn2hex error", fn, descr);
1083 
1084  out:
1085 	BN_free(bn);
1086 	return s;
1087 }
1088 
1089 int
x509_valid_seqnum(const char * fn,const char * descr,const ASN1_INTEGER * i)1090 x509_valid_seqnum(const char *fn, const char *descr, const ASN1_INTEGER *i)
1091 {
1092 	BIGNUM *bn;
1093 
1094 	if ((bn = x509_seqnum_to_bn(fn, descr, i)) == NULL)
1095 		return 0;
1096 
1097 	BN_free(bn);
1098 	return 1;
1099 }
1100 
1101 /*
1102  * Find the closest expiry moment by walking the chain of authorities.
1103  */
1104 time_t
x509_find_expires(time_t notafter,struct auth * a,struct crl_tree * crlt)1105 x509_find_expires(time_t notafter, struct auth *a, struct crl_tree *crlt)
1106 {
1107 	struct crl	*crl;
1108 	time_t		 expires;
1109 
1110 	expires = notafter;
1111 
1112 	for (; a != NULL; a = a->issuer) {
1113 		if (expires > a->cert->notafter)
1114 			expires = a->cert->notafter;
1115 		crl = crl_get(crlt, a);
1116 		if (crl != NULL && expires > crl->nextupdate)
1117 			expires = crl->nextupdate;
1118 	}
1119 
1120 	return expires;
1121 }
1122