Skip to content

Commit 133d29d

Browse files
authored
Merge pull request #9626 from rizlik/name_contraints_fixes
asn: MatchBaseName fixes
2 parents f57484d + 94dc7ae commit 133d29d

File tree

4 files changed

+204
-23
lines changed

4 files changed

+204
-23
lines changed

tests/api/test_asn.c

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@
2323

2424
#include <tests/api/test_asn.h>
2525

26+
#include <wolfssl/wolfcrypt/asn.h>
27+
2628
#if defined(WC_ENABLE_ASYM_KEY_EXPORT) && defined(HAVE_ED25519)
2729
static int test_SetAsymKeyDer_once(byte* privKey, word32 privKeySz, byte* pubKey,
2830
word32 pubKeySz, byte* trueDer, word32 trueDerSz)
@@ -638,3 +640,150 @@ int test_wc_IndexSequenceOf(void)
638640

639641
return EXPECT_RESULT();
640642
}
643+
644+
int test_wolfssl_local_MatchBaseName(void)
645+
{
646+
EXPECT_DECLS;
647+
648+
#if !defined(NO_CERTS) && !defined(NO_ASN) && !defined(IGNORE_NAME_CONSTRAINTS)
649+
/*
650+
* Tests for DNS type (ASN_DNS_TYPE = 0x02)
651+
*/
652+
653+
/* Positive tests - should match */
654+
/* Exact match */
655+
ExpectIntEQ(wolfssl_local_MatchBaseName(ASN_DNS_TYPE,
656+
"domain.com", 10, "domain.com", 10), 1);
657+
/* Case insensitive match */
658+
ExpectIntEQ(wolfssl_local_MatchBaseName(ASN_DNS_TYPE,
659+
"DOMAIN.COM", 10, "domain.com", 10), 1);
660+
/* Subdomain match (RFC 5280: adding labels to the left) */
661+
ExpectIntEQ(wolfssl_local_MatchBaseName(ASN_DNS_TYPE,
662+
"sub.domain.com", 14, "domain.com", 10), 1);
663+
ExpectIntEQ(wolfssl_local_MatchBaseName(ASN_DNS_TYPE,
664+
"a.b.domain.com", 14, "domain.com", 10), 1);
665+
/* Leading dot constraint with subdomain (not RFC 5280 compliant for DNS,
666+
* but kept for backwards compatibility) */
667+
ExpectIntEQ(wolfssl_local_MatchBaseName(ASN_DNS_TYPE,
668+
"sub.domain.com", 14, ".domain.com", 11), 1);
669+
ExpectIntEQ(wolfssl_local_MatchBaseName(ASN_DNS_TYPE,
670+
"a.b.domain.com", 14, ".domain.com", 11), 1);
671+
672+
/* Negative tests - should NOT match */
673+
/* Bug #3: fakedomain.com should NOT match domain.com (no dot boundary) */
674+
ExpectIntEQ(wolfssl_local_MatchBaseName(ASN_DNS_TYPE,
675+
"fakedomain.com", 14, "domain.com", 10), 0);
676+
ExpectIntEQ(wolfssl_local_MatchBaseName(ASN_DNS_TYPE,
677+
"notdomain.com", 13, "domain.com", 10), 0);
678+
ExpectIntEQ(wolfssl_local_MatchBaseName(ASN_DNS_TYPE,
679+
"xexample.com", 12, "example.com", 11), 0);
680+
/* Bug #3: fakedomain.com should NOT match .domain.com */
681+
ExpectIntEQ(wolfssl_local_MatchBaseName(ASN_DNS_TYPE,
682+
"fakedomain.com", 14, ".domain.com", 11), 0);
683+
/* domain.com should NOT match .domain.com (leading dot requires subdomain) */
684+
ExpectIntEQ(wolfssl_local_MatchBaseName(ASN_DNS_TYPE,
685+
"domain.com", 10, ".domain.com", 11), 0);
686+
/* Different domain */
687+
ExpectIntEQ(wolfssl_local_MatchBaseName(ASN_DNS_TYPE,
688+
"other.com", 9, "domain.com", 10), 0);
689+
/* Name starting with dot */
690+
ExpectIntEQ(wolfssl_local_MatchBaseName(ASN_DNS_TYPE,
691+
".domain.com", 11, "domain.com", 10), 0);
692+
693+
/*
694+
* Tests for email type (ASN_RFC822_TYPE = 0x01)
695+
*/
696+
697+
/* Positive tests - should match */
698+
/* Exact email match */
699+
ExpectIntEQ(wolfssl_local_MatchBaseName(ASN_RFC822_TYPE,
700+
701+
/* Email with domain constraint (leading dot) - subdomain present */
702+
ExpectIntEQ(wolfssl_local_MatchBaseName(ASN_RFC822_TYPE,
703+
"[email protected]", 19, ".domain.com", 11), 1);
704+
/* Email with domain constraint (no leading dot) - exact domain */
705+
ExpectIntEQ(wolfssl_local_MatchBaseName(ASN_RFC822_TYPE,
706+
"[email protected]", 15, "domain.com", 10), 1);
707+
708+
/* Negative tests - should NOT match */
709+
/* [email protected] should NOT match .domain.com (subdomain required) */
710+
ExpectIntEQ(wolfssl_local_MatchBaseName(ASN_RFC822_TYPE,
711+
"[email protected]", 15, ".domain.com", 11), 0);
712+
/* [email protected] should NOT match domain.com (exact domain only) */
713+
ExpectIntEQ(wolfssl_local_MatchBaseName(ASN_RFC822_TYPE,
714+
"[email protected]", 19, "domain.com", 10), 0);
715+
/* @ at start is invalid */
716+
ExpectIntEQ(wolfssl_local_MatchBaseName(ASN_RFC822_TYPE,
717+
"@domain.com", 11, ".domain.com", 11), 0);
718+
/* @ at end is invalid */
719+
ExpectIntEQ(wolfssl_local_MatchBaseName(ASN_RFC822_TYPE,
720+
"user@", 5, ".domain.com", 11), 0);
721+
/* double @ is invalid */
722+
ExpectIntEQ(wolfssl_local_MatchBaseName(ASN_RFC822_TYPE,
723+
"user@@domain.com", 16, ".domain.com", 11), 0);
724+
/* multiple @ is invalid */
725+
ExpectIntEQ(wolfssl_local_MatchBaseName(ASN_RFC822_TYPE,
726+
"user@[email protected]", 21, ".domain.com", 11), 0);
727+
/* No @ in email name */
728+
ExpectIntEQ(wolfssl_local_MatchBaseName(ASN_RFC822_TYPE,
729+
"userdomain.com", 14, ".domain.com", 11), 0);
730+
/* Email domain doesn't match constraint */
731+
ExpectIntEQ(wolfssl_local_MatchBaseName(ASN_RFC822_TYPE,
732+
"[email protected]", 14, ".domain.com", 11), 0);
733+
/* Email suffix without dot boundary (fakedomain) */
734+
ExpectIntEQ(wolfssl_local_MatchBaseName(ASN_RFC822_TYPE,
735+
"[email protected]", 19, ".domain.com", 11), 0);
736+
/* Base constraint with invalid @ position */
737+
ExpectIntEQ(wolfssl_local_MatchBaseName(ASN_RFC822_TYPE,
738+
"[email protected]", 15, "@domain.com", 11), 0);
739+
ExpectIntEQ(wolfssl_local_MatchBaseName(ASN_RFC822_TYPE,
740+
"[email protected]", 15, "user@", 5), 0);
741+
742+
/*
743+
* Tests for directory type (ASN_DIR_TYPE = 0x04)
744+
*/
745+
746+
/* Positive tests - should match */
747+
/* Exact match */
748+
ExpectIntEQ(wolfssl_local_MatchBaseName(ASN_DIR_TYPE,
749+
"CN=test", 7, "CN=test", 7), 1);
750+
/* Prefix match (name longer than base) */
751+
ExpectIntEQ(wolfssl_local_MatchBaseName(ASN_DIR_TYPE,
752+
"CN=test,O=org", 13, "CN=test", 7), 1);
753+
754+
/* Negative tests - should NOT match */
755+
/* Different content */
756+
ExpectIntEQ(wolfssl_local_MatchBaseName(ASN_DIR_TYPE,
757+
"CN=other", 8, "CN=test", 7), 0);
758+
/* Case sensitive for directory */
759+
ExpectIntEQ(wolfssl_local_MatchBaseName(ASN_DIR_TYPE,
760+
"CN=TEST", 7, "CN=test", 7), 0);
761+
762+
/*
763+
* Edge cases and error handling
764+
*/
765+
766+
/* NULL pointers */
767+
ExpectIntEQ(wolfssl_local_MatchBaseName(ASN_DNS_TYPE,
768+
NULL, 10, "domain.com", 10), 0);
769+
ExpectIntEQ(wolfssl_local_MatchBaseName(ASN_DNS_TYPE,
770+
"domain.com", 10, NULL, 10), 0);
771+
/* Empty/zero size */
772+
ExpectIntEQ(wolfssl_local_MatchBaseName(ASN_DNS_TYPE,
773+
"", 0, "domain.com", 10), 0);
774+
ExpectIntEQ(wolfssl_local_MatchBaseName(ASN_DNS_TYPE,
775+
"domain.com", 10, "", 0), 0);
776+
/* Invalid type */
777+
ExpectIntEQ(wolfssl_local_MatchBaseName(0xFF,
778+
"domain.com", 10, "domain.com", 10), 0);
779+
/* Name starting with dot */
780+
ExpectIntEQ(wolfssl_local_MatchBaseName(ASN_DNS_TYPE,
781+
".", 1, ".", 1), 0);
782+
/* Name shorter than base */
783+
ExpectIntEQ(wolfssl_local_MatchBaseName(ASN_DNS_TYPE,
784+
"a.com", 5, "domain.com", 10), 0);
785+
786+
#endif /* !NO_CERTS && !NO_ASN && !IGNORE_NAME_CONSTRAINTS */
787+
788+
return EXPECT_RESULT();
789+
}

tests/api/test_asn.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,12 @@
2727
int test_SetAsymKeyDer(void);
2828
int test_GetSetShortInt(void);
2929
int test_wc_IndexSequenceOf(void);
30+
int test_wolfssl_local_MatchBaseName(void);
3031

3132
#define TEST_ASN_DECLS \
3233
TEST_DECL_GROUP("asn", test_SetAsymKeyDer), \
3334
TEST_DECL_GROUP("asn", test_GetSetShortInt), \
34-
TEST_DECL_GROUP("asn", test_wc_IndexSequenceOf)
35+
TEST_DECL_GROUP("asn", test_wc_IndexSequenceOf), \
36+
TEST_DECL_GROUP("asn", test_wolfssl_local_MatchBaseName)
3537

3638
#endif /* WOLFCRYPT_TEST_ASN_H */

wolfcrypt/src/asn.c

Lines changed: 46 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -19315,8 +19315,8 @@ int ConfirmSignature(SignatureCtx* sigCtx,
1931519315

1931619316
#ifndef IGNORE_NAME_CONSTRAINTS
1931719317

19318-
static int MatchBaseName(int type, const char* name, int nameSz,
19319-
const char* base, int baseSz)
19318+
int wolfssl_local_MatchBaseName(int type, const char* name, int nameSz,
19319+
const char* base, int baseSz)
1932019320
{
1932119321
if (base == NULL || baseSz <= 0 || name == NULL || nameSz <= 0 ||
1932219322
name[0] == '.' || nameSz < baseSz ||
@@ -19333,6 +19333,8 @@ static int MatchBaseName(int type, const char* name, int nameSz,
1933319333
if (type == ASN_RFC822_TYPE) {
1933419334
const char* p = NULL;
1933519335
int count = 0;
19336+
int baseIsEmail = 0;
19337+
int atPos = -1;
1933619338

1933719339
if (base[0] != '.') {
1933819340
p = base;
@@ -19344,39 +19346,61 @@ static int MatchBaseName(int type, const char* name, int nameSz,
1934419346
p++;
1934519347
}
1934619348

19347-
/* No '@' in base, reset p to NULL */
19348-
if (count >= baseSz)
19349-
p = NULL;
19349+
if (count < baseSz) {
19350+
/* '@' found in base - validate it's not at start/end and only one */
19351+
if (count == 0 || count == baseSz - 1)
19352+
return 0; /* '@' at start or end of base is invalid */
19353+
baseIsEmail = 1;
19354+
}
1935019355
}
1935119356

19352-
if (p == NULL) {
19353-
/* Base isn't an email address, it is a domain name,
19354-
* wind the name forward one character past its '@'. */
19355-
p = name;
19356-
count = 0;
19357-
while (*p != '@' && count < baseSz) {
19358-
count++;
19359-
p++;
19357+
/* verify that name is a valid email address, store @ position */
19358+
p = name;
19359+
count = 0;
19360+
while (count < nameSz) {
19361+
if (*p == '@') {
19362+
if (atPos >= 0)
19363+
return 0; /* Multiple '@' in name is invalid */
19364+
atPos = count;
1936019365
}
19366+
count++;
19367+
p++;
19368+
}
1936119369

19362-
if (count < baseSz && *p == '@') {
19363-
name = p + 1;
19364-
nameSz -= count + 1;
19365-
}
19370+
/* Validate '@' exists and is not at start or end */
19371+
if (atPos < 0 || atPos == 0 || atPos == nameSz - 1)
19372+
return 0;
19373+
19374+
if (!baseIsEmail) {
19375+
/* Base isn't an email address but a domain or host.
19376+
* wind the name forward one character past its '@'. */
19377+
name = name + atPos + 1;
19378+
nameSz -= atPos + 1;
1936619379
}
1936719380
}
1936819381

1936919382
/* RFC 5280 section 4.2.1.10
1937019383
* "...Any DNS name that can be constructed by simply adding zero or more
1937119384
* labels to the left-hand side of the name satisfies the name constraint."
1937219385
* i.e www.host.example.com works for host.example.com name constraint and
19373-
* host1.example.com does not. */
19386+
* host1.example.com does not.
19387+
*
19388+
* Note: For DNS type, RFC 5280 does not allow leading dot in constraint.
19389+
* However, we accept it here for backwards compatibility. */
1937419390
if (type == ASN_DNS_TYPE || (type == ASN_RFC822_TYPE && base[0] == '.')) {
1937519391
int szAdjust = nameSz - baseSz;
19392+
/* Check dot boundary: if there's a prefix and base doesn't start with
19393+
* '.', the character before the matched suffix must be '.'.
19394+
* When base starts with '.', the dot is included in the comparison. */
19395+
if (szAdjust > 0 && base[0] != '.' && name[szAdjust - 1] != '.')
19396+
return 0;
1937619397
name += szAdjust;
1937719398
nameSz -= szAdjust;
1937819399
}
1937919400

19401+
if (nameSz != baseSz)
19402+
return 0;
19403+
1938019404
while (nameSz > 0) {
1938119405
if (XTOLOWER((unsigned char)*name) !=
1938219406
XTOLOWER((unsigned char)*base))
@@ -19408,8 +19432,8 @@ static int PermittedListOk(DNS_entry* name, Base_entry* dnsList, byte nameType)
1940819432
if (current->type == nameType) {
1940919433
need = 1; /* restriction on permitted names is set for this type */
1941019434
if (name->len >= current->nameSz &&
19411-
MatchBaseName(nameType, name->name, name->len,
19412-
current->name, current->nameSz)) {
19435+
wolfssl_local_MatchBaseName(nameType, name->name, name->len,
19436+
current->name, current->nameSz)) {
1941319437
match = 1; /* found the current name in the permitted list*/
1941419438
break;
1941519439
}
@@ -19439,8 +19463,8 @@ static int IsInExcludedList(DNS_entry* name, Base_entry* dnsList, byte nameType)
1943919463
while (current != NULL) {
1944019464
if (current->type == nameType) {
1944119465
if (name->len >= current->nameSz &&
19442-
MatchBaseName(nameType, name->name, name->len,
19443-
current->name, current->nameSz)) {
19466+
wolfssl_local_MatchBaseName(nameType, name->name, name->len,
19467+
current->name, current->nameSz)) {
1944419468
ret = 1;
1944519469
break;
1944619470
}

wolfssl/wolfcrypt/asn.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2789,6 +2789,12 @@ WOLFSSL_LOCAL int VerifyX509Acert(const byte* cert, word32 certSz,
27892789
#endif /* WOLFSSL_ACERT */
27902790

27912791

2792+
#ifndef IGNORE_NAME_CONSTRAINTS
2793+
WOLFSSL_TEST_VIS int wolfssl_local_MatchBaseName(int type, const char* name,
2794+
int nameSz, const char* base,
2795+
int baseSz);
2796+
#endif
2797+
27922798
#if ((defined(HAVE_ED25519) && defined(HAVE_ED25519_KEY_IMPORT)) \
27932799
|| (defined(HAVE_CURVE25519) && defined(HAVE_CURVE25519_KEY_IMPORT)) \
27942800
|| (defined(HAVE_ED448) && defined(HAVE_ED448_KEY_IMPORT)) \

0 commit comments

Comments
 (0)