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