From 8919d61b9c2ddf1361401516b4966661c64921e2 Mon Sep 17 00:00:00 2001
From: tb <>
Date: Sun, 25 Jun 2023 13:52:27 +0000
Subject: Check for duplicate X.509v3 extension OIDs

Per RFC 5280, 4.2: A certificate MUST NOT include more than one instance
of a particular extension.

This implements such a check in x509v3_cache_extensions() by sorting the
list of extensions and looking for duplicate neighbors. This sidesteps
complications from extensions we do not know about and keeps algorithmic
complexity reasonable. If the check fails, EXFLAG_INVALID is set on the
certificate, which means that the verifier will not validate it.

ok jsing
---
 src/lib/libcrypto/x509/x509_purp.c | 46 +++++++++++++++++++++++++++++++++++++-
 1 file changed, 45 insertions(+), 1 deletion(-)

diff --git a/src/lib/libcrypto/x509/x509_purp.c b/src/lib/libcrypto/x509/x509_purp.c
index 75d229b03b..f7bc7ea538 100644
--- a/src/lib/libcrypto/x509/x509_purp.c
+++ b/src/lib/libcrypto/x509/x509_purp.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: x509_purp.c,v 1.26 2023/06/20 14:21:19 tb Exp $ */
+/* $OpenBSD: x509_purp.c,v 1.27 2023/06/25 13:52:27 tb Exp $ */
 /* Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL
  * project 2001.
  */
@@ -441,6 +441,47 @@ setup_crldp(X509 *x)
 		setup_dp(x, sk_DIST_POINT_value(x->crldp, i));
 }
 
+static int
+x509_extension_oid_cmp(const X509_EXTENSION *const *a,
+    const X509_EXTENSION *const *b)
+{
+	return OBJ_cmp((*a)->object, (*b)->object);
+}
+
+static int
+x509_extension_oids_are_unique(X509 *x509)
+{
+	STACK_OF(X509_EXTENSION) *exts = NULL;
+	const X509_EXTENSION *prev_ext, *curr_ext;
+	int i;
+	int ret = 0;
+
+	if (X509_get_ext_count(x509) <= 1)
+		goto done;
+
+	if ((exts = sk_X509_EXTENSION_dup(x509->cert_info->extensions)) == NULL)
+		goto err;
+
+	(void)sk_X509_EXTENSION_set_cmp_func(exts, x509_extension_oid_cmp);
+	sk_X509_EXTENSION_sort(exts);
+
+	prev_ext = sk_X509_EXTENSION_value(exts, 0);
+	for (i = 1; i < sk_X509_EXTENSION_num(exts); i++) {
+		curr_ext = sk_X509_EXTENSION_value(exts, i);
+		if (x509_extension_oid_cmp(&prev_ext, &curr_ext) == 0)
+			goto err;
+		prev_ext = curr_ext;
+	}
+
+ done:
+	ret = 1;
+
+ err:
+	sk_X509_EXTENSION_free(exts);
+
+	return ret;
+}
+
 static void
 x509v3_cache_extensions_internal(X509 *x)
 {
@@ -612,6 +653,9 @@ x509v3_cache_extensions_internal(X509 *x)
 		}
 	}
 
+	if (!x509_extension_oids_are_unique(x))
+		x->ex_flags |= EXFLAG_INVALID;
+
 	x509_verify_cert_info_populate(x);
 
 	x->ex_flags |= EXFLAG_SET;
-- 
cgit v1.2.3-55-g6feb