Nitin Wankhade
2026-06-09 14:54:05 +05:30
committed by Anuj Mittal
parent 0d3124974e
commit fca99be2d5
2 changed files with 177 additions and 0 deletions
@@ -0,0 +1,176 @@
From: Tobias Brunner <tobias@strongswan.org>
Date: Mon, 23 Mar 2026 17:45:11 +0100
Subject: constraints: Case-insensitive matching and reject excluded DN name
constraints
The case is generally ignored when matching identities. So this is
an issue with excluded name constraints where a malicious intermediate
CA could evade the constraints by issuing certificates with names that
just modify the case (e.g. strongSwan.org instead strongswan.org).
Note that it's likely that permitted name constraints are preferred over
excluded name constraints as it might be difficult to come up with a
conclusive list of names to exclude.
With directoryName (DN) name constraints the issue is a bit more comples.
Some RDNs have to be matched in a case-insensitive manner, which we e.g.
do in `identification.c::rdn_equals`. By not doing it for name
constraints, a malicious intermediate CA could evade an excluded name
constraint just by modifying the case in such an RDN.
While we could use the mentioned function in `dn_matches`, this doesn't
properly fix the problem because the function is basically too strict.
Especially in regards to RDNs of type UTF8String, which are only compared
binary. To match these properly, we'd have to implement the string
preparation described in RFC 5280, section 7.1 and the referenced RFCs.
Until that's the case, we reject excluded name constraints of type
directoryName as we are unable to enforce them.
Fixes: a2b340764fac ("Implemented NameConstraint matching in constraints plugin")
Fixes: CVE-2026-35331
CVE: CVE-2026-35331
Upstream-Status: Backport [https://github.com/strongswan/strongswan/commit/64130ede5cd8f61edd35a1b488c874fa328a42b0]
[https://github.com/strongswan/strongswan/commit/c66143db48bab9eb82cc86190687938b809611eb]
Signed-off-by: Nitin Wankhade <nitin.wankhade333@gmail.com>
===
diff --git a/src/libstrongswan/plugins/constraints/constraints_validator.c b/src/libstrongswan/plugins/constraints/constraints_validator.c
index 27bdb89..daa7bfa 100644
--- a/src/libstrongswan/plugins/constraints/constraints_validator.c
+++ b/src/libstrongswan/plugins/constraints/constraints_validator.c
@@ -55,6 +55,18 @@ static bool check_pathlen(x509_t *issuer, int pathlen)
return TRUE;
}
+/**
+ * Check if the constraint and ID strings match case-insensitively
+ */
+static bool string_matches(chunk_t constraint, chunk_t id)
+{
+ /* make sure the two strings have actually the same length */
+ return constraint.len == id.len &&
+ memchr(constraint.ptr, 0, constraint.len) == NULL &&
+ memchr(id.ptr, 0, id.len) == NULL &&
+ strncasecmp(constraint.ptr, id.ptr, constraint.len) == 0;
+}
+
/**
* Check if a FQDN constraint matches
*/
@@ -70,7 +82,7 @@ static bool fqdn_matches(identification_t *constraint, identification_t *id)
return FALSE;
}
diff = chunk_create(i.ptr, i.len - c.len);
- if (!chunk_equals(c, chunk_skip(i, diff.len)))
+ if (!string_matches(c, chunk_skip(i, diff.len)))
{
return FALSE;
}
@@ -101,10 +113,10 @@ static bool email_matches(identification_t *constraint, identification_t *id)
}
if (memchr(c.ptr, '@', c.len))
{ /* constraint is a full email address */
- return chunk_equals(c, i);
+ return string_matches(c, i);
}
diff = chunk_create(i.ptr, i.len - c.len);
- if (!chunk_equals(c, chunk_skip(i, diff.len)))
+ if (!string_matches(c, chunk_skip(i, diff.len)))
{
return FALSE;
}
@@ -389,9 +401,17 @@ static bool collect_constraints(x509_t *x509, bool permitted, hashtable_t **out)
type = constraint->get_type(constraint);
switch (type)
{
+ case ID_DER_ASN1_DN:
+ if (!permitted)
+ {
+ DBG1(DBG_CFG, "excluded %N NameConstraint not supported",
+ id_type_names, type);
+ success = FALSE;
+ break;
+ }
+ /* fall-through */
case ID_FQDN:
case ID_RFC822_ADDR:
- case ID_DER_ASN1_DN:
case ID_IPV4_ADDR_SUBNET:
case ID_IPV6_ADDR_SUBNET:
break;
diff --git a/src/libstrongswan/tests/suites/test_certnames.c b/src/libstrongswan/tests/suites/test_certnames.c
index 2549fb6..14570ee 100644
--- a/src/libstrongswan/tests/suites/test_certnames.c
+++ b/src/libstrongswan/tests/suites/test_certnames.c
@@ -207,8 +207,10 @@ static struct {
bool good;
} permitted_san[] = {
{ ".strongswan.org", "test.strongswan.org", TRUE },
+ { ".strongswan.org", "test.strongSwan.org", TRUE },
{ "strongswan.org", "test.strongswan.org", TRUE },
{ "a.b.c.strongswan.org", "d.a.b.c.strongswan.org", TRUE },
+ { "a.b.c.strongswan.org", "d.A.b.C.strongswan.org", TRUE },
{ "a.b.c.strongswan.org", "a.b.c.d.strongswan.org", FALSE },
{ "strongswan.org", "strongswan.org.com", FALSE },
{ ".strongswan.org", "strongswan.org", FALSE },
@@ -216,8 +218,11 @@ static struct {
{ "strongswan.org", "swan.org", FALSE },
{ "strongswan.org", "swan.org", FALSE },
{ "tester@strongswan.org", "tester@strongswan.org", TRUE },
+ { "tester@strongswan.org", "tester@strongSwan.org", TRUE },
+ { "tester@strongswan.org", "TESTER@strongswan.org", TRUE },
{ "tester@strongswan.org", "atester@strongswan.org", FALSE },
{ "email:strongswan.org", "tester@strongswan.org", TRUE },
+ { "email:strongswan.org", "tester@strongSwan.org", TRUE },
{ "email:strongswan.org", "tester@test.strongswan.org", FALSE },
{ "email:.strongswan.org", "tester@test.strongswan.org", TRUE },
{ "email:.strongswan.org", "tester@strongswan.org", FALSE },
@@ -248,11 +253,11 @@ static struct {
char *subject;
bool good;
} excluded_dn[] = {
- { "C=CH, O=another", "C=CH, O=strongSwan, CN=tester", TRUE },
- { "C=CH, O=another", "C=CH, O=anot", TRUE },
- { "C=CH, O=another", "C=CH, O=anot, CN=tester", TRUE },
+ { "C=CH, O=another", "C=CH, O=strongSwan, CN=tester", FALSE },
+ { "C=CH, O=another", "C=CH, O=anot", FALSE },
+ { "C=CH, O=another", "C=CH, O=anot, CN=tester", FALSE },
{ "C=CH, O=another", "C=CH, O=another, CN=tester", FALSE },
- { "C=CH, O=another", "C=CH, CN=tester, O=another", TRUE },
+ { "C=CH, O=another", "C=CH, CN=tester, O=another", FALSE },
};
START_TEST(test_excluded_dn)
@@ -281,7 +286,9 @@ static struct {
} excluded_san[] = {
{ ".strongswan.org", "test.strongswan.org", FALSE },
{ "strongswan.org", "test.strongswan.org", FALSE },
+ { "strongswan.org", "test.strongSwan.org", FALSE },
{ "a.b.c.strongswan.org", "d.a.b.c.strongswan.org", FALSE },
+ { "a.b.c.strongswan.org", "d.a.b.C.strongswan.org", FALSE },
{ "a.b.c.strongswan.org", "a.b.c.d.strongswan.org", TRUE },
{ "strongswan.org", "strongswan.org.com", TRUE },
{ ".strongswan.org", "strongswan.org", TRUE },
@@ -289,8 +296,10 @@ static struct {
{ "strongswan.org", "swan.org", TRUE },
{ "strongswan.org", "swan.org", TRUE },
{ "tester@strongswan.org", "tester@strongswan.org", FALSE },
+ { "tester@strongswan.org", "TESTER@strongswan.org", FALSE },
{ "tester@strongswan.org", "atester@strongswan.org", TRUE },
{ "email:strongswan.org", "tester@strongswan.org", FALSE },
+ { "email:strongswan.org", "tester@strongSwan.org", FALSE },
{ "email:strongswan.org", "tester@test.strongswan.org", TRUE },
{ "email:.strongswan.org", "tester@test.strongswan.org", FALSE },
{ "email:.strongswan.org", "tester@strongswan.org", TRUE },
@@ -418,9 +427,9 @@ static struct {
char *subject;
bool good;
} excluded_dn_levels[] = {
- { "C=CH, O=strongSwan", "C=CH", "C=DE", TRUE },
+ { "C=CH, O=strongSwan", "C=CH", "C=DE", FALSE },
{ "C=CH, O=strongSwan", "C=CH", "C=CH", FALSE },
- { "C=CH, O=strongSwan", "C=DE", "C=CH", TRUE },
+ { "C=CH, O=strongSwan", "C=DE", "C=CH", FALSE },
{ "C=CH, O=strongSwan", "C=DE", "C=DE", FALSE },
{ "C=CH, O=strongSwan", "C=DE", "C=CH, O=strongSwan", FALSE },
{ NULL, "C=CH", "C=CH, O=strongSwan", FALSE },
@@ -15,6 +15,7 @@ SRC_URI = "https://download.strongswan.org/strongswan-${PV}.tar.bz2 \
file://tls-server-Prevent-infinite-loop-if-supported-versio.patch \
file://pkcs5-pkcs7-Avoid-NULL-pointer-dereference-when-veri.patch \
file://libsimaka-Reject-zero-length-EAP-SIM-AKA-attributes.patch \
file://constraints-Case-insensitive-matching-and-reject-exc.patch \
"
SRC_URI[sha256sum] = "728027ddda4cb34c67c4cec97d3ddb8c274edfbabdaeecf7e74693b54fc33678"