From 1dead2faec6320aaba321eb56f20d442df192b83 Mon Sep 17 00:00:00 2001
From: Alexander Sosedkin <asosedkin@redhat.com>
Date: Tue, 14 Apr 2026 17:41:30 +0200
Subject: x509/name_constraints: fix intersecting empty constraints

Permitted name constraints were wrongfully ignored
when prior CAs only had excluded name constraints,
resulting in a name constraint bypass.

With this change, they are taken into account and propagate.

Reported-by: Haruto Kimura (Stella)
Fixes: #1824
Fixes: CVE-2026-42011
Fixes: GNUTLS-SA-2026-04-29-6
CVSS: 4.8 Medium CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:L/A:N
Signed-off-by: Alexander Sosedkin <asosedkin@redhat.com>
---
 lib/x509/name_constraints.c | 3 ---
 1 file changed, 3 deletions(-)

From 24713b8c63137ce0665b495d22ccce4f5ce05c84 Mon Sep 17 00:00:00 2001
From: Alexander Sosedkin <asosedkin@redhat.com>
Date: Tue, 14 Apr 2026 17:49:50 +0200
Subject: [PATCH] tests/name-constraints-merge: extend to cover #1824

Signed-off-by: Alexander Sosedkin <asosedkin@redhat.com>
---
 tests/name-constraints-merge.c | 113 +++++++++++++++++++++++++++++++++
 1 file changed, 113 insertions(+)

Index: gnutls-3.8.10/lib/x509/name_constraints.c
===================================================================
--- gnutls-3.8.10.orig/lib/x509/name_constraints.c
+++ gnutls-3.8.10/lib/x509/name_constraints.c
@@ -740,9 +740,6 @@ static int name_constraints_node_list_in
 	type_bitmask_t types_in_p1 = 0, types_in_p2 = 0;
 	static const unsigned char universal_ip[32] = { 0 };
 
-	if (permitted->size == 0 || permitted2->size == 0)
-		return GNUTLS_E_SUCCESS;
-
 	/* make sorted views of the arrays */
 	ret = ensure_sorted(permitted);
 	if (ret < 0) {
Index: gnutls-3.8.10/tests/name-constraints-merge.c
===================================================================
--- gnutls-3.8.10.orig/tests/name-constraints-merge.c
+++ gnutls-3.8.10/tests/name-constraints-merge.c
@@ -418,6 +418,119 @@ void doit(void)
 	gnutls_x509_name_constraints_deinit(nc1);
 	gnutls_x509_name_constraints_deinit(nc2);
 
+	/* 6: test intersecting empty permitted with non-empty permitted
+	 * NC1: excluded DNS excluded.example.org  (empty permitted)
+	 * NC2: permitted DNS permitted.example.org
+	 * Expected result:
+	 *   permitted=[permitted.example.org], excluded=[excluded.example.org]
+	 *   unrelated.example.com is rejected
+	 */
+	suite = 6;
+
+	ret = gnutls_x509_name_constraints_init(&nc1);
+	check_for_error(ret);
+
+	ret = gnutls_x509_name_constraints_init(&nc2);
+	check_for_error(ret);
+
+	set_name("excluded.example.org", &name);
+	ret = gnutls_x509_name_constraints_add_excluded(nc1, GNUTLS_SAN_DNSNAME,
+							&name);
+	check_for_error(ret);
+
+	set_name("permitted.example.org", &name);
+	ret = gnutls_x509_name_constraints_add_permitted(
+		nc2, GNUTLS_SAN_DNSNAME, &name);
+	check_for_error(ret);
+
+	ret = _gnutls_x509_name_constraints_merge(nc1, nc2);
+	check_for_error(ret);
+
+	set_name("unrelated.example.com", &name); /* entirely unrelated */
+	ret = gnutls_x509_name_constraints_check(nc1, GNUTLS_SAN_DNSNAME,
+						 &name);
+	check_test_result(suite, ret, NAME_REJECTED, &name); /* #1814 */
+
+	set_name("permitted.example.org", &name); /* permitted, direct */
+	ret = gnutls_x509_name_constraints_check(nc1, GNUTLS_SAN_DNSNAME,
+						 &name);
+	check_test_result(suite, ret, NAME_ACCEPTED, &name); /* sanity */
+
+	set_name("sub.permitted.example.org", &name); /* permitted, subdomain */
+	ret = gnutls_x509_name_constraints_check(nc1, GNUTLS_SAN_DNSNAME,
+						 &name);
+	check_test_result(suite, ret, NAME_ACCEPTED, &name); /* sanity */
+
+	set_name("excluded.example.org", &name); /* excluded, direct */
+	ret = gnutls_x509_name_constraints_check(nc1, GNUTLS_SAN_DNSNAME,
+						 &name);
+	check_test_result(suite, ret, NAME_REJECTED, &name); /* sanity */
+
+	set_name("sub.excluded.example.org", &name); /* excluded, subdomain */
+	ret = gnutls_x509_name_constraints_check(nc1, GNUTLS_SAN_DNSNAME,
+						 &name);
+	check_test_result(suite, ret, NAME_REJECTED, &name); /* sanity */
+
+	gnutls_x509_name_constraints_deinit(nc1);
+	gnutls_x509_name_constraints_deinit(nc2);
+
+	/* 7: test intersecting non-empty permitted with empty permitted
+	 * (same as 6, but swapped to ensure order doesn't matter)
+	 * NC1: permitted DNS permitted.example.org
+	 * NC2: excluded DNS excluded.example.org  (empty permitted)
+	 * Expected result:
+	 *   permitted=[permitted.example.org], excluded=[excluded.example.org]
+	 *   unrelated.example.com is rejected
+	 */
+	suite = 7;
+
+	ret = gnutls_x509_name_constraints_init(&nc1);
+	check_for_error(ret);
+
+	ret = gnutls_x509_name_constraints_init(&nc2);
+	check_for_error(ret);
+
+	set_name("permitted.example.org", &name);
+	ret = gnutls_x509_name_constraints_add_permitted(
+		nc1, GNUTLS_SAN_DNSNAME, &name);
+	check_for_error(ret);
+
+	set_name("excluded.example.org", &name);
+	ret = gnutls_x509_name_constraints_add_excluded(nc2, GNUTLS_SAN_DNSNAME,
+							&name);
+	check_for_error(ret);
+
+	ret = _gnutls_x509_name_constraints_merge(nc1, nc2);
+	check_for_error(ret);
+
+	set_name("unrelated.example.com", &name); /* entirely unrelated */
+	ret = gnutls_x509_name_constraints_check(nc1, GNUTLS_SAN_DNSNAME,
+						 &name);
+	check_test_result(suite, ret, NAME_REJECTED, &name); /* #1814 */
+
+	set_name("permitted.example.org", &name); /* permitted, direct */
+	ret = gnutls_x509_name_constraints_check(nc1, GNUTLS_SAN_DNSNAME,
+						 &name);
+	check_test_result(suite, ret, NAME_ACCEPTED, &name); /* sanity */
+
+	set_name("sub.permitted.example.org", &name); /* permitted, subdomain */
+	ret = gnutls_x509_name_constraints_check(nc1, GNUTLS_SAN_DNSNAME,
+						 &name);
+	check_test_result(suite, ret, NAME_ACCEPTED, &name); /* sanity */
+
+	set_name("excluded.example.org", &name); /* excluded, direct */
+	ret = gnutls_x509_name_constraints_check(nc1, GNUTLS_SAN_DNSNAME,
+						 &name);
+	check_test_result(suite, ret, NAME_REJECTED, &name); /* sanity */
+
+	set_name("sub.excluded.example.org", &name); /* excluded, subdomain */
+	ret = gnutls_x509_name_constraints_check(nc1, GNUTLS_SAN_DNSNAME,
+						 &name);
+	check_test_result(suite, ret, NAME_REJECTED, &name); /* sanity */
+
+	gnutls_x509_name_constraints_deinit(nc1);
+	gnutls_x509_name_constraints_deinit(nc2);
+
 	/* Test footer */
 
 	if (debug)
