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