Index | Thread | Search

From:
Theo Buehler <tb@theobuehler.org>
Subject:
rpki-client: add an AIA handler for all certs
To:
tech@openbsd.org
Date:
Mon, 30 Jun 2025 09:56:32 +0200

Download raw body.

Thread
Here's a replacement for x509_get_aia(). This is quite similar to the
SIA handling we already have in cert.c - I have tried to deduplicate
the SIA and AIA handlers, but it's not entirely trivial to do this
nicely, so I left it straightforward and dumb.

Our x509_get_aia() is slightly incorrect in that it only accepts a
single accessMethod. cert_aia() fixes this and like cert_sia() it will
use the first rsync method it encounter;. Of course, this will only
really be fixed once I remove x509_get_aia() later.

I haven't hooked this into cert_parse_ee_cert() yet since that will
happen for free after a few more steps.

diff --git a/usr.sbin/rpki-client/cert.c b/usr.sbin/rpki-client/cert.c
index 5239080306b..451d9114c60 100644
--- a/usr.sbin/rpki-client/cert.c
+++ b/usr.sbin/rpki-client/cert.c
@@ -31,6 +31,7 @@
 #include "extern.h"
 
 extern ASN1_OBJECT	*bgpsec_oid;	/* id-kp-bgpsec-router Key Purpose */
+extern ASN1_OBJECT	*caissuers_oid;	/* 1.3.6.1.5.5.7.48.2 (caIssuers) */
 extern ASN1_OBJECT	*certpol_oid;	/* id-cp-ipAddr-asNumber cert policy */
 extern ASN1_OBJECT	*carepo_oid;	/* 1.3.6.1.5.5.7.48.5 (caRepository) */
 extern ASN1_OBJECT	*manifest_oid;	/* 1.3.6.1.5.5.7.48.10 (rpkiManifest) */
@@ -500,6 +501,86 @@ sbgp_ipaddrblk(const char *fn, struct cert *cert, X509_EXTENSION *ext)
 	return rc;
 }
 
+/*
+ * Parse "Authority Information Access" extension for non-TA certs,
+ * RFC 6487, section 4.8.7.
+ * Returns zero on failure, non-zero on success.
+ */
+static int
+cert_aia(const char *fn, struct cert *cert, X509_EXTENSION *ext)
+{
+	AUTHORITY_INFO_ACCESS	*aia = NULL;
+	ACCESS_DESCRIPTION	*ad;
+	ASN1_OBJECT		*oid;
+	char			*caissuers = NULL;
+	int			 i, rc = 0;
+
+	assert(cert->aia == NULL);
+
+	if (cert->purpose == CERT_PURPOSE_TA) {
+		warnx("%s: RFC 6487 section 4.8.7: AIA must be absent from "
+		    "a self-signed certificate", fn);
+		goto out;
+	}
+
+	if (X509_EXTENSION_get_critical(ext)) {
+		warnx("%s: RFC 6487 section 4.8.7: SIA: "
+		    "extension not non-critical", fn);
+		goto out;
+	}
+
+	if ((aia = X509V3_EXT_d2i(ext)) == NULL) {
+		warnx("%s: RFC 6487 section 4.8.7: SIA: failed extension parse",
+		    fn);
+		goto out;
+	}
+
+	for (i = 0; i < sk_ACCESS_DESCRIPTION_num(aia); i++) {
+		ad = sk_ACCESS_DESCRIPTION_value(aia, i);
+
+		oid = ad->method;
+
+		if (OBJ_cmp(oid, caissuers_oid) == 0) {
+			if (!x509_location(fn, "AIA: caIssuers", ad->location,
+			    &caissuers))
+				goto out;
+			if (cert->aia == NULL && strncasecmp(caissuers,
+			    RSYNC_PROTO, RSYNC_PROTO_LEN) == 0) {
+				cert->aia = caissuers;
+				caissuers = NULL;
+				continue;
+			}
+			/*
+			 * XXX - unclear how to check "Other accessMethod URIs
+			 * referencing the same object MAY be included".
+			 */
+			if (verbose)
+				warnx("%s: RFC 6487 section 4.8.7: AIA: "
+				    "ignoring location %s", fn, caissuers);
+			free(caissuers);
+			caissuers = NULL;
+		} else {
+			char buf[128];
+
+			OBJ_obj2txt(buf, sizeof(buf), oid, 0);
+			warnx("%s: RFC 6487 section 4.8.7: unexpected"
+			    " accessMethod: %s", fn, buf);
+			goto out;
+		}
+	}
+
+	if (cert->aia == NULL) {
+		warnx("%s: RFC 6487 section 4.8.7: AIA: expected caIssuers "
+		    "accessMethod with rsync protocol", fn);
+		goto out;
+	}
+
+	rc = 1;
+ out:
+	AUTHORITY_INFO_ACCESS_free(aia);
+	return rc;
+}
+
 /*
  * Parse "Subject Information Access" extension for a CA cert,
  * RFC 6487, section 4.8.8.1 and RFC 8182, section 3.2.
@@ -1200,6 +1281,8 @@ cert_parse_pre(const char *fn, const unsigned char *der, size_t len)
 		case NID_info_access:
 			if (aia++ > 0)
 				goto dup;
+			if (!cert_aia(fn, cert, ext))
+				goto out;
 			break;
 		case NID_sinfo_access:
 			if (sia++ > 0)
@@ -1247,8 +1330,6 @@ cert_parse_pre(const char *fn, const unsigned char *der, size_t len)
 		goto out;
 	if (!x509_get_ski(x, fn, &cert->ski))
 		goto out;
-	if (!x509_get_aia(x, fn, &cert->aia))
-		goto out;
 	if (!x509_get_crl(x, fn, &cert->crl))
 		goto out;
 	if (!x509_get_notbefore(x, fn, &cert->notbefore))
diff --git a/usr.sbin/rpki-client/x509.c b/usr.sbin/rpki-client/x509.c
index c58b0fed2f7..b9ca11db25a 100644
--- a/usr.sbin/rpki-client/x509.c
+++ b/usr.sbin/rpki-client/x509.c
@@ -29,6 +29,7 @@
 #include "extern.h"
 
 ASN1_OBJECT	*certpol_oid;	/* id-cp-ipAddr-asNumber cert policy */
+ASN1_OBJECT	*caissuers_oid;	/* 1.3.6.1.5.5.7.48.2 (caIssuers) */
 ASN1_OBJECT	*carepo_oid;	/* 1.3.6.1.5.5.7.48.5 (caRepository) */
 ASN1_OBJECT	*manifest_oid;	/* 1.3.6.1.5.5.7.48.10 (rpkiManifest) */
 ASN1_OBJECT	*signedobj_oid;	/* 1.3.6.1.5.5.7.48.11 (signedObject) */
@@ -54,6 +55,10 @@ static const struct {
 		.oid = "1.3.6.1.5.5.7.14.2",
 		.ptr = &certpol_oid,
 	},
+	{
+		.oid = "1.3.6.1.5.5.7.48.2",
+		.ptr = &caissuers_oid,
+	},
 	{
 		.oid = "1.3.6.1.5.5.7.48.5",
 		.ptr = &carepo_oid,