Index | Thread | Search

From:
Theo Buehler <tb@theobuehler.org>
Subject:
rpki-client: more validation on printable strings
To:
tech@openbsd.org
Date:
Mon, 17 Nov 2025 16:06:38 +0100

Download raw body.

Thread
We should not only be checking that the commonName has the proper
PrintableString type, we should also check that its octets belong
to the limited character set allowed by this type. You can get a
a copy of X.680 here; table 10 is on page 86 (logical page 75):

https://www.itu.int/rec/T-REC-X.680-202102-I/en

We still can't fix the type check due to UTF8String in AfriNIC.

Another thing that needs fixing here is yet another BGPsec mess, as
is noted in an XXX below. I'll leave that for a separate diff.

Index: x509.c
===================================================================
RCS file: /cvs/src/usr.sbin/rpki-client/x509.c,v
diff -u -p -r1.121 x509.c
--- x509.c	13 Nov 2025 15:18:53 -0000	1.121
+++ x509.c	17 Nov 2025 15:01:06 -0000
@@ -353,6 +353,52 @@ x509_location(const char *fn, const char
 	return 1;
 }
 
+static int
+valid_printable_octet(const uint8_t u8)
+{
+	/*
+	 * X.680, 41.4, Table 10 lists the allowed characters in this order.
+	 */
+
+	if ('A' <= u8 && u8 <= 'Z')
+		return 1;
+	if ('a' <= u8 && u8 <= 'z')
+		return 1;
+	if ('0' <= u8 && u8 <= '9')
+		return 1;
+
+	return u8 == ' ' || u8 == '\'' || u8 == '(' || u8 == ')' || u8 == '+' ||
+	    u8 == ',' || u8 == '-' || u8 == '.' || u8 == '/' || u8 == ':' ||
+	    u8 == '=' || u8 == '?';
+}
+
+static int
+valid_printable_string(const char *fn, const char *descr, const ASN1_STRING *as)
+{
+	int i;
+
+	/*
+	 * The following check can be enabled after AFRINIC re-issues CA certs.
+	 * https://lists.afrinic.net/pipermail/dbwg/2023-March/000436.html
+	 * https://lists.afrinic.net/pipermail/dbwg/2025-November/000546.html
+	 */
+	if (0 && ASN1_STRING_type(as) != V_ASN1_PRINTABLESTRING) {
+		warnx("%s: RFC 6487 section 4.5: commonName is"
+		    " not PrintableString", fn);
+		return 0;
+	}
+
+	for (i = 0; i < as->length; i++) {
+		if (!valid_printable_octet(as->data[i])) {
+			warnx("%s: invalid %s: PrintableString contains 0x%02x",
+			    fn, descr, as->data[i]);
+			return 0;
+		}
+	}
+
+	return 1;
+}
+
 /*
  * Check that subject or issuer only contain commonName and serialNumber.
  * Return 0 on failure.
@@ -389,21 +435,12 @@ x509_valid_name(const char *fn, const ch
 				    fn);
 				return 0;
 			}
-/*
- * The following check can be enabled after AFRINIC re-issues CA certs.
- * https://lists.afrinic.net/pipermail/dbwg/2023-March/000436.html
- */
-#if 0
 			/*
 			 * XXX - For some reason RFC 8209, section 3.1.1 decided
 			 * to allow UTF8String for BGPsec Router Certificates.
 			 */
-			if (ASN1_STRING_type(as) != V_ASN1_PRINTABLESTRING) {
-				warnx("%s: RFC 6487 section 4.5: commonName is"
-				    " not PrintableString", fn);
+			if (!valid_printable_string(fn, descr, as))
 				return 0;
-			}
-#endif
 			break;
 		case NID_serialNumber:
 			if (sn++ > 0) {