Skip to content

Commit e6dac68

Browse files
authored
Merge pull request #7966 from cconlon/x509CheckHostLeftWildcardOnly
Add left-most wildcard matching support to X509_check_host()
2 parents 4a37947 + f878220 commit e6dac68

5 files changed

Lines changed: 125 additions & 20 deletions

File tree

src/internal.c

Lines changed: 34 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -12508,16 +12508,20 @@ int CipherRequires(byte first, byte second, int requirement)
1250812508

1250912509
#ifndef NO_CERTS
1251012510

12511-
1251212511
/* Match names with wildcards, each wildcard can represent a single name
1251312512
component or fragment but not multiple names, i.e.,
1251412513
*.z.com matches y.z.com but not x.y.z.com
1251512514

12515+
If flags contains WOLFSSL_LEFT_MOST_WILDCARD_ONLY, wildcard only applies
12516+
to left-most name component, compatible with RFC 2830 identity checking.
12517+
1251612518
return 1 on success */
1251712519
int MatchDomainName(const char* pattern, int patternLen, const char* str,
12518-
word32 strLen)
12520+
word32 strLen, unsigned int flags)
1251912521
{
1252012522
int ret = 0;
12523+
byte wildcardEligible = 1;
12524+
byte leftWildcardOnly = flags & WOLFSSL_LEFT_MOST_WILDCARD_ONLY;
1252112525

1252212526
if (pattern == NULL || str == NULL || patternLen <= 0 || strLen == 0)
1252312527
return 0;
@@ -12530,11 +12534,16 @@ int MatchDomainName(const char* pattern, int patternLen, const char* str,
1253012534

1253112535
pattern++;
1253212536

12533-
if (p == '*') {
12537+
if ((p == '*') && wildcardEligible) {
1253412538
char s;
1253512539
/* We will always match '*' */
1253612540
patternLen--;
1253712541

12542+
/* Only single wildcard allowed with strict left only */
12543+
if (leftWildcardOnly) {
12544+
wildcardEligible = 0;
12545+
}
12546+
1253812547
/* Consume any extra '*' chars until the next non '*' char. */
1253912548
while (patternLen > 0) {
1254012549
p = (char)XTOLOWER((unsigned char)*pattern);
@@ -12543,6 +12552,10 @@ int MatchDomainName(const char* pattern, int patternLen, const char* str,
1254312552
return 0;
1254412553
if (p != '*')
1254512554
break;
12555+
if (leftWildcardOnly && (p == '*')) {
12556+
/* RFC2830 only allows single left-most wildcard */
12557+
return 0;
12558+
}
1254612559

1254712560
patternLen--;
1254812561
}
@@ -12574,6 +12587,11 @@ int MatchDomainName(const char* pattern, int patternLen, const char* str,
1257412587
}
1257512588
}
1257612589
else {
12590+
/* Past left-most wildcard location, not eligible if flag set*/
12591+
if (leftWildcardOnly && wildcardEligible) {
12592+
wildcardEligible = 0;
12593+
}
12594+
1257712595
/* Simple case, pattern match exactly */
1257812596
if (p != (char)XTOLOWER((unsigned char) *str))
1257912597
return 0;
@@ -12605,7 +12623,7 @@ int MatchDomainName(const char* pattern, int patternLen, const char* str,
1260512623
* -1 : No matches and wild pattern match failed.
1260612624
*/
1260712625
int CheckForAltNames(DecodedCert* dCert, const char* domain, word32 domainLen,
12608-
int* checkCN)
12626+
int* checkCN, unsigned int flags)
1260912627
{
1261012628
int match = 0;
1261112629
DNS_entry* altName = NULL;
@@ -12636,7 +12654,7 @@ int CheckForAltNames(DecodedCert* dCert, const char* domain, word32 domainLen,
1263612654
len = (word32)altName->len;
1263712655
}
1263812656

12639-
if (MatchDomainName(buf, (int)len, domain, domainLen)) {
12657+
if (MatchDomainName(buf, (int)len, domain, domainLen, flags)) {
1264012658
match = 1;
1264112659
if (checkCN != NULL) {
1264212660
*checkCN = 0;
@@ -12665,13 +12683,14 @@ int CheckForAltNames(DecodedCert* dCert, const char* domain, word32 domainLen,
1266512683
* domainNameLen The length of the domain name.
1266612684
* returns DOMAIN_NAME_MISMATCH when no match found and 0 on success.
1266712685
*/
12668-
int CheckHostName(DecodedCert* dCert, const char *domainName, size_t domainNameLen)
12686+
int CheckHostName(DecodedCert* dCert, const char *domainName,
12687+
size_t domainNameLen, unsigned int flags)
1266912688
{
1267012689
int checkCN;
1267112690
int ret = WC_NO_ERR_TRACE(DOMAIN_NAME_MISMATCH);
1267212691

1267312692
if (CheckForAltNames(dCert, domainName, (word32)domainNameLen,
12674-
&checkCN) != 1) {
12693+
&checkCN, flags) != 1) {
1267512694
ret = DOMAIN_NAME_MISMATCH;
1267612695
WOLFSSL_MSG("DomainName match on alt names failed");
1267712696
}
@@ -12682,7 +12701,7 @@ int CheckHostName(DecodedCert* dCert, const char *domainName, size_t domainNameL
1268212701
#ifndef WOLFSSL_HOSTNAME_VERIFY_ALT_NAME_ONLY
1268312702
if (checkCN == 1) {
1268412703
if (MatchDomainName(dCert->subjectCN, dCert->subjectCNLen,
12685-
domainName, (word32)domainNameLen) == 1) {
12704+
domainName, (word32)domainNameLen, flags) == 1) {
1268612705
ret = 0;
1268712706
}
1268812707
else {
@@ -12699,7 +12718,7 @@ int CheckIPAddr(DecodedCert* dCert, const char* ipasc)
1269912718
{
1270012719
WOLFSSL_MSG("Checking IPAddr");
1270112720

12702-
return CheckHostName(dCert, ipasc, (size_t)XSTRLEN(ipasc));
12721+
return CheckHostName(dCert, ipasc, (size_t)XSTRLEN(ipasc), 0);
1270312722
}
1270412723

1270512724

@@ -13843,7 +13862,7 @@ int DoVerifyCallback(WOLFSSL_CERT_MANAGER* cm, WOLFSSL* ssl, int cert_err,
1384313862
/* If altNames names is present, then subject common name is ignored */
1384413863
if (args->dCert->altNames != NULL) {
1384513864
if (CheckForAltNames(args->dCert, ssl->param->hostName,
13846-
(word32)XSTRLEN(ssl->param->hostName), NULL) != 1) {
13865+
(word32)XSTRLEN(ssl->param->hostName), NULL, 0) != 1) {
1384713866
if (cert_err == 0) {
1384813867
ret = DOMAIN_NAME_MISMATCH;
1384913868
WOLFSSL_ERROR_VERBOSE(ret);
@@ -13857,7 +13876,7 @@ int DoVerifyCallback(WOLFSSL_CERT_MANAGER* cm, WOLFSSL* ssl, int cert_err,
1385713876
args->dCert->subjectCN,
1385813877
args->dCert->subjectCNLen,
1385913878
ssl->param->hostName,
13860-
(word32)XSTRLEN(ssl->param->hostName)) == 0) {
13879+
(word32)XSTRLEN(ssl->param->hostName), 0) == 0) {
1386113880
if (cert_err == 0) {
1386213881
ret = DOMAIN_NAME_MISMATCH;
1386313882
WOLFSSL_ERROR_VERBOSE(ret);
@@ -15747,7 +15766,7 @@ int ProcessPeerCerts(WOLFSSL* ssl, byte* input, word32* inOutIdx,
1574715766
(ssl->buffers.domainName.buffer == NULL ? 0 :
1574815767
(word32)XSTRLEN(
1574915768
(const char *)ssl->buffers.domainName.buffer)),
15750-
NULL) != 1) {
15769+
NULL, 0) != 1) {
1575115770
WOLFSSL_MSG("DomainName match on alt names failed");
1575215771
/* try to get peer key still */
1575315772
ret = DOMAIN_NAME_MISMATCH;
@@ -15762,7 +15781,7 @@ int ProcessPeerCerts(WOLFSSL* ssl, byte* input, word32* inOutIdx,
1576215781
(ssl->buffers.domainName.buffer == NULL ? 0 :
1576315782
(word32)XSTRLEN(
1576415783
(const char *)ssl->buffers.domainName.buffer)
15765-
)) == 0)
15784+
), 0) == 0)
1576615785
{
1576715786
WOLFSSL_MSG("DomainName match on common name failed");
1576815787
ret = DOMAIN_NAME_MISMATCH;
@@ -15775,14 +15794,14 @@ int ProcessPeerCerts(WOLFSSL* ssl, byte* input, word32* inOutIdx,
1577515794
args->dCert->subjectCNLen,
1577615795
(char*)ssl->buffers.domainName.buffer,
1577715796
(ssl->buffers.domainName.buffer == NULL ? 0 :
15778-
(word32)XSTRLEN(ssl->buffers.domainName.buffer))) == 0)
15797+
(word32)XSTRLEN(ssl->buffers.domainName.buffer)), 0) == 0)
1577915798
{
1578015799
WOLFSSL_MSG("DomainName match on common name failed");
1578115800
if (CheckForAltNames(args->dCert,
1578215801
(char*)ssl->buffers.domainName.buffer,
1578315802
(ssl->buffers.domainName.buffer == NULL ? 0 :
1578415803
(word32)XSTRLEN(ssl->buffers.domainName.buffer)),
15785-
NULL) != 1) {
15804+
NULL, 0) != 1) {
1578615805
WOLFSSL_MSG(
1578715806
"DomainName match on alt names failed too");
1578815807
/* try to get peer key still */

src/x509.c

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14338,7 +14338,6 @@ int wolfSSL_X509_check_host(WOLFSSL_X509 *x, const char *chk, size_t chklen,
1433814338
WOLFSSL_ENTER("wolfSSL_X509_check_host");
1433914339

1434014340
/* flags and peername not needed for Nginx. */
14341-
(void)flags;
1434214341
(void)peername;
1434314342

1434414343
if ((x == NULL) || (chk == NULL)) {
@@ -14390,7 +14389,7 @@ int wolfSSL_X509_check_host(WOLFSSL_X509 *x, const char *chk, size_t chklen,
1439014389
chklen--;
1439114390
}
1439214391

14393-
ret = CheckHostName(dCert, (char *)chk, chklen);
14392+
ret = CheckHostName(dCert, (char *)chk, chklen, flags);
1439414393

1439514394
out:
1439614395

tests/api.c

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55653,20 +55653,42 @@ static int test_wolfSSL_X509_check_host(void)
5565355653
&& !defined(NO_SHA) && !defined(NO_RSA)
5565455654
X509* x509 = NULL;
5565555655
const char altName[] = "example.com";
55656+
const char badAltName[] = "a.example.com";
5565655657

55658+
/* cliCertFile has subjectAltName set to 'example.com', '127.0.0.1' */
5565755659
ExpectNotNull(x509 = wolfSSL_X509_load_certificate_file(cliCertFile,
5565855660
SSL_FILETYPE_PEM));
5565955661

5566055662
ExpectIntEQ(X509_check_host(x509, altName, XSTRLEN(altName), 0, NULL),
5566155663
WOLFSSL_SUCCESS);
5566255664

55665+
ExpectIntEQ(X509_check_host(x509, badAltName, XSTRLEN(badAltName), 0, NULL),
55666+
WC_NO_ERR_TRACE(WOLFSSL_FAILURE));
55667+
5566355668
ExpectIntEQ(X509_check_host(x509, NULL, 0, 0, NULL),
5566455669
WC_NO_ERR_TRACE(WOLFSSL_FAILURE));
5566555670

55671+
/* Check WOLFSSL_LEFT_MOST_WILDCARD_ONLY flag set */
55672+
ExpectIntEQ(X509_check_host(x509, altName, XSTRLEN(altName),
55673+
WOLFSSL_LEFT_MOST_WILDCARD_ONLY, NULL), WOLFSSL_SUCCESS);
55674+
55675+
ExpectIntEQ(X509_check_host(x509, NULL, 0,
55676+
WOLFSSL_LEFT_MOST_WILDCARD_ONLY, NULL),
55677+
WC_NO_ERR_TRACE(WOLFSSL_FAILURE));
55678+
55679+
ExpectIntEQ(X509_check_host(x509, badAltName, XSTRLEN(badAltName),
55680+
WOLFSSL_LEFT_MOST_WILDCARD_ONLY, NULL),
55681+
WC_NO_ERR_TRACE(WOLFSSL_FAILURE));
55682+
5566655683
X509_free(x509);
5566755684

5566855685
ExpectIntEQ(X509_check_host(NULL, altName, XSTRLEN(altName), 0, NULL),
5566955686
WC_NO_ERR_TRACE(WOLFSSL_FAILURE));
55687+
55688+
/* Check again with WOLFSSL_LEFT_MOST_WILDCARD_ONLY flag set */
55689+
ExpectIntEQ(X509_check_host(NULL, altName, XSTRLEN(altName),
55690+
WOLFSSL_LEFT_MOST_WILDCARD_ONLY, NULL),
55691+
WC_NO_ERR_TRACE(WOLFSSL_FAILURE));
5567055692
#endif
5567155693
return EXPECT_RESULT();
5567255694
}
@@ -63360,6 +63382,12 @@ static int test_wolfSSL_X509_bad_altname(void)
6336063382
* name of "a*\0*". Ensure that it does not match "aaaaa" */
6336163383
ExpectIntNE(wolfSSL_X509_check_host(x509, name, nameLen,
6336263384
WOLFSSL_ALWAYS_CHECK_SUBJECT, NULL), 1);
63385+
63386+
/* Also make sure WOLFSSL_LEFT_MOST_WILDCARD_ONLY fails too */
63387+
ExpectIntNE(wolfSSL_X509_check_host(x509, name, nameLen,
63388+
WOLFSSL_ALWAYS_CHECK_SUBJECT | WOLFSSL_LEFT_MOST_WILDCARD_ONLY,
63389+
NULL), 1);
63390+
6336363391
X509_free(x509);
6336463392

6336563393
#endif
@@ -63480,6 +63508,26 @@ static int test_wolfSSL_X509_name_match(void)
6348063508
ExpectIntNE(wolfSSL_X509_check_host(x509, name4, nameLen4,
6348163509
WOLFSSL_ALWAYS_CHECK_SUBJECT, NULL), 1);
6348263510

63511+
/* WOLFSSL_LEFT_MOST_WILDCARD_ONLY flag should fail on all cases, since
63512+
* 'a*' alt name does not have wildcard left-most */
63513+
63514+
/* Ensure that "a*" does not match "aaaaa" */
63515+
ExpectIntNE(wolfSSL_X509_check_host(x509, name1, nameLen1,
63516+
WOLFSSL_ALWAYS_CHECK_SUBJECT | WOLFSSL_LEFT_MOST_WILDCARD_ONLY,
63517+
NULL), WOLFSSL_SUCCESS);
63518+
/* Ensure that "a*" does not match "a" */
63519+
ExpectIntNE(wolfSSL_X509_check_host(x509, name2, nameLen2,
63520+
WOLFSSL_ALWAYS_CHECK_SUBJECT | WOLFSSL_LEFT_MOST_WILDCARD_ONLY,
63521+
NULL), WOLFSSL_SUCCESS);
63522+
/* Ensure that "a*" does not match "abbbb" */
63523+
ExpectIntNE(wolfSSL_X509_check_host(x509, name3, nameLen3,
63524+
WOLFSSL_ALWAYS_CHECK_SUBJECT | WOLFSSL_LEFT_MOST_WILDCARD_ONLY,
63525+
NULL), WOLFSSL_SUCCESS);
63526+
/* Ensure that "a*" does not match "bbb" */
63527+
ExpectIntNE(wolfSSL_X509_check_host(x509, name4, nameLen4,
63528+
WOLFSSL_ALWAYS_CHECK_SUBJECT | WOLFSSL_LEFT_MOST_WILDCARD_ONLY,
63529+
NULL), WOLFSSL_SUCCESS);
63530+
6348363531
wolfSSL_X509_free(x509);
6348463532

6348563533
#endif
@@ -63602,6 +63650,21 @@ static int test_wolfSSL_X509_name_match2(void)
6360263650
ExpectIntNE(wolfSSL_X509_check_host(x509, name4, nameLen4,
6360363651
WOLFSSL_ALWAYS_CHECK_SUBJECT, NULL), WOLFSSL_SUCCESS);
6360463652

63653+
/* WOLFSSL_LEFT_MOST_WILDCARD_ONLY flag should fail on all cases, since
63654+
* 'a*b*' alt name does not have wildcard left-most */
63655+
ExpectIntEQ(wolfSSL_X509_check_host(x509, name1, nameLen1,
63656+
WOLFSSL_ALWAYS_CHECK_SUBJECT | WOLFSSL_LEFT_MOST_WILDCARD_ONLY,
63657+
NULL), WOLFSSL_FAILURE);
63658+
ExpectIntEQ(wolfSSL_X509_check_host(x509, name2, nameLen2,
63659+
WOLFSSL_ALWAYS_CHECK_SUBJECT | WOLFSSL_LEFT_MOST_WILDCARD_ONLY,
63660+
NULL), WOLFSSL_FAILURE);
63661+
ExpectIntEQ(wolfSSL_X509_check_host(x509, name3, nameLen3,
63662+
WOLFSSL_ALWAYS_CHECK_SUBJECT | WOLFSSL_LEFT_MOST_WILDCARD_ONLY,
63663+
NULL), WOLFSSL_FAILURE);
63664+
ExpectIntEQ(wolfSSL_X509_check_host(x509, name4, nameLen4,
63665+
WOLFSSL_ALWAYS_CHECK_SUBJECT | WOLFSSL_LEFT_MOST_WILDCARD_ONLY,
63666+
NULL), WOLFSSL_FAILURE);
63667+
6360563668
/* Ensure that "a*b*" matches "ab", testing openssl behavior replication
6360663669
* on check len input handling, 0 for len is OK as it should then use
6360763670
* strlen(name1) */
@@ -63715,6 +63778,8 @@ static int test_wolfSSL_X509_name_match3(void)
6371563778
int nameLen1 = (int)(XSTRLEN(name1));
6371663779
const char *name2 = "x.y.example.com";
6371763780
int nameLen2 = (int)(XSTRLEN(name2));
63781+
const char *name3 = "example.com";
63782+
int nameLen3 = (int)(XSTRLEN(name3));
6371863783

6371963784
ExpectNotNull(x509 = wolfSSL_X509_load_certificate_buffer(
6372063785
cert_der, certSize, WOLFSSL_FILETYPE_ASN1));
@@ -63725,6 +63790,22 @@ static int test_wolfSSL_X509_name_match3(void)
6372563790
/* Ensure that "*.example.com" does NOT match "x.y.example.com" */
6372663791
ExpectIntNE(wolfSSL_X509_check_host(x509, name2, nameLen2,
6372763792
WOLFSSL_ALWAYS_CHECK_SUBJECT, NULL), WOLFSSL_SUCCESS);
63793+
/* Ensure that "*.example.com" does NOT match "example.com" */
63794+
ExpectIntNE(wolfSSL_X509_check_host(x509, name3, nameLen3,
63795+
WOLFSSL_ALWAYS_CHECK_SUBJECT, NULL), WOLFSSL_SUCCESS);
63796+
63797+
/* WOLFSSL_LEFT_MOST_WILDCARD_ONLY, should match "foo.example.com" */
63798+
ExpectIntEQ(wolfSSL_X509_check_host(x509, name1, nameLen1,
63799+
WOLFSSL_ALWAYS_CHECK_SUBJECT | WOLFSSL_LEFT_MOST_WILDCARD_ONLY,
63800+
NULL), WOLFSSL_SUCCESS);
63801+
/* WOLFSSL_LEFT_MOST_WILDCARD_ONLY, should NOT match "x.y.example.com" */
63802+
ExpectIntNE(wolfSSL_X509_check_host(x509, name2, nameLen2,
63803+
WOLFSSL_ALWAYS_CHECK_SUBJECT | WOLFSSL_LEFT_MOST_WILDCARD_ONLY,
63804+
NULL), WOLFSSL_SUCCESS);
63805+
/* WOLFSSL_LEFT_MOST_WILDCARD_ONLY, should NOT match "example.com" */
63806+
ExpectIntNE(wolfSSL_X509_check_host(x509, name3, nameLen3,
63807+
WOLFSSL_ALWAYS_CHECK_SUBJECT | WOLFSSL_LEFT_MOST_WILDCARD_ONLY,
63808+
NULL), WOLFSSL_SUCCESS);
6372863809

6372963810
wolfSSL_X509_free(x509);
6373063811

wolfssl/internal.h

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2240,9 +2240,13 @@ WOLFSSL_LOCAL void FreeAsyncCtx(WOLFSSL* ssl, byte freeAsync);
22402240
WOLFSSL_LOCAL void FreeKeyExchange(WOLFSSL* ssl);
22412241
WOLFSSL_LOCAL void FreeSuites(WOLFSSL* ssl);
22422242
WOLFSSL_LOCAL int ProcessPeerCerts(WOLFSSL* ssl, byte* input, word32* inOutIdx, word32 totalSz);
2243-
WOLFSSL_LOCAL int MatchDomainName(const char* pattern, int len, const char* str, word32 strLen);
2243+
WOLFSSL_LOCAL int MatchDomainName(const char* pattern, int len,
2244+
const char* str, word32 strLen,
2245+
unsigned int flags);
22442246
#if !defined(NO_CERTS) && !defined(NO_ASN)
2245-
WOLFSSL_LOCAL int CheckForAltNames(DecodedCert* dCert, const char* domain, word32 domainLen, int* checkCN);
2247+
WOLFSSL_LOCAL int CheckForAltNames(DecodedCert* dCert, const char* domain,
2248+
word32 domainLen, int* checkCN,
2249+
unsigned int flags);
22462250
WOLFSSL_LOCAL int CheckIPAddr(DecodedCert* dCert, const char* ipasc);
22472251
WOLFSSL_LOCAL void CopyDecodedName(WOLFSSL_X509_NAME* name, DecodedCert* dCert, int nameType);
22482252
#endif
@@ -6252,7 +6256,7 @@ WOLFSSL_API void SSL_ResourceFree(WOLFSSL* ssl); /* Micrium uses */
62526256

62536257
#ifndef NO_ASN
62546258
WOLFSSL_LOCAL int CheckHostName(DecodedCert* dCert, const char *domainName,
6255-
size_t domainNameLen);
6259+
size_t domainNameLen, unsigned int flags);
62566260
#endif
62576261
#endif
62586262

wolfssl/ssl.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -606,6 +606,8 @@ struct WOLFSSL_X509_STORE {
606606
#define WOLFSSL_NO_WILDCARDS 0x2
607607
#define WOLFSSL_NO_PARTIAL_WILDCARDS 0x4
608608
#define WOLFSSL_MULTI_LABEL_WILDCARDS 0x8
609+
/* Custom to wolfSSL, OpenSSL compat goes up to 0x20 */
610+
#define WOLFSSL_LEFT_MOST_WILDCARD_ONLY 0x40
609611

610612
#if defined(OPENSSL_EXTRA) || defined(WOLFSSL_WPAS_SMALL)
611613
#define WOLFSSL_USE_CHECK_TIME 0x2

0 commit comments

Comments
 (0)