Skip to content

Commit f878220

Browse files
committed
add WOLFSSL_LEFT_MOST_WILDCARD_ONLY support to X509_check_host()
1 parent bf29b68 commit f878220

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
@@ -55652,20 +55652,42 @@ static int test_wolfSSL_X509_check_host(void)
5565255652
&& !defined(NO_SHA) && !defined(NO_RSA)
5565355653
X509* x509 = NULL;
5565455654
const char altName[] = "example.com";
55655+
const char badAltName[] = "a.example.com";
5565555656

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

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

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

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

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

6336463392
#endif
@@ -63479,6 +63507,26 @@ static int test_wolfSSL_X509_name_match(void)
6347963507
ExpectIntNE(wolfSSL_X509_check_host(x509, name4, nameLen4,
6348063508
WOLFSSL_ALWAYS_CHECK_SUBJECT, NULL), 1);
6348163509

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

6348463532
#endif
@@ -63601,6 +63649,21 @@ static int test_wolfSSL_X509_name_match2(void)
6360163649
ExpectIntNE(wolfSSL_X509_check_host(x509, name4, nameLen4,
6360263650
WOLFSSL_ALWAYS_CHECK_SUBJECT, NULL), WOLFSSL_SUCCESS);
6360363651

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

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

6372863809
wolfSSL_X509_free(x509);
6372963810

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)