Check for duplicate X.509v3 extension OIDs
authortb <tb@openbsd.org>
Sun, 25 Jun 2023 13:52:27 +0000 (13:52 +0000)
committertb <tb@openbsd.org>
Sun, 25 Jun 2023 13:52:27 +0000 (13:52 +0000)
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

lib/libcrypto/x509/x509_purp.c

index 75d229b..f7bc7ea 100644 (file)
@@ -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;