Skip to content

Commit af3828b

Browse files
committed
Rewrite pattern matching to always use explicit lengths instead of expecting NULL terminated strings, thus replicating the behavior of openssl X509_check_host()
1 parent 61fea76 commit af3828b

4 files changed

Lines changed: 350 additions & 41 deletions

File tree

src/internal.c

Lines changed: 74 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -12377,55 +12377,77 @@ int CipherRequires(byte first, byte second, int requirement)
1237712377
*.z.com matches y.z.com but not x.y.z.com
1237812378

1237912379
return 1 on success */
12380-
int MatchDomainName(const char* pattern, int len, const char* str)
12380+
int MatchDomainName(const char* pattern, int patternLen, const char* str,
12381+
word32 strLen)
1238112382
{
1238212383
int ret = 0;
1238312384

12384-
if (pattern == NULL || str == NULL || len <= 0)
12385+
if (pattern == NULL || str == NULL || patternLen <= 0 || strLen == 0)
1238512386
return 0;
1238612387

12387-
while (len > 0) {
12388-
12389-
char p = (char)XTOLOWER((unsigned char)*pattern++);
12388+
while (patternLen > 0) {
12389+
/* Get the next pattern char to evaluate */
12390+
char p = (char)XTOLOWER((unsigned char)*pattern);
1239012391
if (p == '\0')
1239112392
break;
1239212393

12394+
pattern++;
12395+
1239312396
if (p == '*') {
1239412397
char s;
12398+
/* We will always match '*' */
12399+
patternLen--;
1239512400

12396-
while (--len > 0) {
12401+
/* Consume any extra '*' chars until the next non '*' char. */
12402+
while (patternLen > 0) {
1239712403
p = (char)XTOLOWER((unsigned char)*pattern);
1239812404
pattern++;
12399-
if (p == '\0' && len > 0)
12405+
if (p == '\0' && patternLen > 0)
1240012406
return 0;
1240112407
if (p != '*')
1240212408
break;
12409+
12410+
patternLen--;
1240312411
}
1240412412

12405-
if (len == 0)
12406-
p = '\0';
12413+
/* Consume str until we reach next char in pattern after '*' or
12414+
* end of string */
12415+
while (strLen > 0) {
12416+
s = (char)XTOLOWER((unsigned char) *str);
12417+
str++;
12418+
strLen--;
1240712419

12408-
while ( (s = (char)XTOLOWER((unsigned char) *str)) != '\0') {
12409-
if (s == p)
12410-
break;
1241112420
if (s == '.')
1241212421
return 0;
12413-
str++;
12422+
12423+
/* p is next char in pattern after '*', or '*' if '*' is the
12424+
* last char in the pattern (in which case patternLen is 1) */
12425+
if ( ((s == p) && (patternLen > 0))) {
12426+
/* We had already counted the '*' as matched, this means
12427+
* we also matched the next non '*' char in pattern */
12428+
patternLen--;
12429+
break;
12430+
}
12431+
12432+
/* If strlen is 0, we have consumed the entire string. Count that
12433+
* as a match of '*' */
12434+
if (strLen == 0) {
12435+
break;
12436+
}
1241412437
}
1241512438
}
1241612439
else {
12440+
/* Simple case, pattern match exactly */
1241712441
if (p != (char)XTOLOWER((unsigned char) *str))
1241812442
return 0;
12419-
}
12420-
1242112443

12422-
if (len > 0) {
1242312444
str++;
12424-
len--;
12445+
strLen--;
12446+
patternLen--;
1242512447
}
1242612448
}
1242712449

12428-
if (*str == '\0' && len == 0) {
12450+
if (strLen == 0 && patternLen == 0) {
1242912451
ret = 1; /* success */
1243012452
}
1243112453

@@ -12437,14 +12459,16 @@ int MatchDomainName(const char* pattern, int len, const char* str)
1243712459
* Fail if there are wild patterns and they didn't match.
1243812460
* Check the common name if no alternative names matched.
1243912461
*
12440-
* dCert Decoded cert to get the alternative names from.
12441-
* domain Domain name to compare against.
12442-
* checkCN Whether to check the common name.
12443-
* returns 1 : match was found.
12444-
* 0 : no match found.
12445-
* -1 : No matches and wild pattern match failed.
12462+
* dCert Decoded cert to get the alternative names from.
12463+
* domain Domain name to compare against.
12464+
* domainLen Length of the domain name.
12465+
* checkCN Whether to check the common name.
12466+
* returns 1 : match was found.
12467+
* 0 : no match found.
12468+
* -1 : No matches and wild pattern match failed.
1244612469
*/
12447-
int CheckForAltNames(DecodedCert* dCert, const char* domain, int* checkCN)
12470+
int CheckForAltNames(DecodedCert* dCert, const char* domain, word32 domainLen,
12471+
int* checkCN)
1244812472
{
1244912473
int match = 0;
1245012474
DNS_entry* altName = NULL;
@@ -12475,7 +12499,7 @@ int CheckForAltNames(DecodedCert* dCert, const char* domain, int* checkCN)
1247512499
len = (word32)altName->len;
1247612500
}
1247712501

12478-
if (MatchDomainName(buf, (int)len, domain)) {
12502+
if (MatchDomainName(buf, (int)len, domain, domainLen)) {
1247912503
match = 1;
1248012504
if (checkCN != NULL) {
1248112505
*checkCN = 0;
@@ -12509,10 +12533,8 @@ int CheckHostName(DecodedCert* dCert, const char *domainName, size_t domainNameL
1250912533
int checkCN;
1251012534
int ret = DOMAIN_NAME_MISMATCH;
1251112535

12512-
/* Assume name is NUL terminated. */
12513-
(void)domainNameLen;
12514-
12515-
if (CheckForAltNames(dCert, domainName, &checkCN) != 1) {
12536+
if (CheckForAltNames(dCert, domainName, (word32)domainNameLen,
12537+
&checkCN) != 1) {
1251612538
WOLFSSL_MSG("DomainName match on alt names failed");
1251712539
}
1251812540
else {
@@ -12522,7 +12544,7 @@ int CheckHostName(DecodedCert* dCert, const char *domainName, size_t domainNameL
1252212544
#ifndef WOLFSSL_HOSTNAME_VERIFY_ALT_NAME_ONLY
1252312545
if (checkCN == 1) {
1252412546
if (MatchDomainName(dCert->subjectCN, dCert->subjectCNLen,
12525-
domainName) == 1) {
12547+
domainName, (word32)domainNameLen) == 1) {
1252612548
ret = 0;
1252712549
}
1252812550
else {
@@ -13560,7 +13582,8 @@ int DoVerifyCallback(WOLFSSL_CERT_MANAGER* cm, WOLFSSL* ssl, int cert_err,
1356013582
ssl->param && ssl->param->hostName[0]) {
1356113583
/* If altNames names is present, then subject common name is ignored */
1356213584
if (args->dCert->altNames != NULL) {
13563-
if (CheckForAltNames(args->dCert, ssl->param->hostName, NULL) != 1) {
13585+
if (CheckForAltNames(args->dCert, ssl->param->hostName,
13586+
(word32)XSTRLEN(ssl->param->hostName), NULL) != 1) {
1356413587
if (cert_err == 0) {
1356513588
ret = DOMAIN_NAME_MISMATCH;
1356613589
WOLFSSL_ERROR_VERBOSE(ret);
@@ -13570,9 +13593,11 @@ int DoVerifyCallback(WOLFSSL_CERT_MANAGER* cm, WOLFSSL* ssl, int cert_err,
1357013593
#ifndef WOLFSSL_HOSTNAME_VERIFY_ALT_NAME_ONLY
1357113594
else {
1357213595
if (args->dCert->subjectCN) {
13573-
if (MatchDomainName(args->dCert->subjectCN,
13574-
args->dCert->subjectCNLen,
13575-
ssl->param->hostName) == 0) {
13596+
if (MatchDomainName(
13597+
args->dCert->subjectCN,
13598+
args->dCert->subjectCNLen,
13599+
ssl->param->hostName,
13600+
(word32)XSTRLEN(ssl->param->hostName)) == 0) {
1357613601
if (cert_err == 0) {
1357713602
ret = DOMAIN_NAME_MISMATCH;
1357813603
WOLFSSL_ERROR_VERBOSE(ret);
@@ -15385,6 +15410,8 @@ int ProcessPeerCerts(WOLFSSL* ssl, byte* input, word32* inOutIdx,
1538515410
if (args->dCert->altNames) {
1538615411
if (CheckForAltNames(args->dCert,
1538715412
(char*)ssl->buffers.domainName.buffer,
15413+
(ssl->buffers.domainName.buffer == NULL ? 0 :
15414+
(word32)XSTRLEN((const char *)ssl->buffers.domainName.buffer)),
1538815415
NULL) != 1) {
1538915416
WOLFSSL_MSG("DomainName match on alt names failed");
1539015417
/* try to get peer key still */
@@ -15394,9 +15421,12 @@ int ProcessPeerCerts(WOLFSSL* ssl, byte* input, word32* inOutIdx,
1539415421
}
1539515422
else {
1539615423
if (MatchDomainName(
15397-
args->dCert->subjectCN,
15398-
args->dCert->subjectCNLen,
15399-
(char*)ssl->buffers.domainName.buffer) == 0) {
15424+
args->dCert->subjectCN,
15425+
args->dCert->subjectCNLen,
15426+
(char*)ssl->buffers.domainName.buffer,
15427+
(ssl->buffers.domainName.buffer == NULL ? 0 :
15428+
(word32)XSTRLEN((const char *)ssl->buffers.domainName.buffer))) == 0)
15429+
{
1540015430
WOLFSSL_MSG("DomainName match on common name failed");
1540115431
ret = DOMAIN_NAME_MISMATCH;
1540215432
WOLFSSL_ERROR_VERBOSE(ret);
@@ -15406,10 +15436,15 @@ int ProcessPeerCerts(WOLFSSL* ssl, byte* input, word32* inOutIdx,
1540615436
/* Old behavior. */
1540715437
if (MatchDomainName(args->dCert->subjectCN,
1540815438
args->dCert->subjectCNLen,
15409-
(char*)ssl->buffers.domainName.buffer) == 0) {
15439+
(char*)ssl->buffers.domainName.buffer,
15440+
(ssl->buffers.domainName.buffer == NULL ? 0 :
15441+
(word32)XSTRLEN(ssl->buffers.domainName.buffer))) == 0)
15442+
{
1541015443
WOLFSSL_MSG("DomainName match on common name failed");
1541115444
if (CheckForAltNames(args->dCert,
1541215445
(char*)ssl->buffers.domainName.buffer,
15446+
(ssl->buffers.domainName.buffer == NULL ? 0 :
15447+
(word32)XSTRLEN(ssl->buffers.domainName.buffer)),
1541315448
NULL) != 1) {
1541415449
WOLFSSL_MSG(
1541515450
"DomainName match on alt names failed too");

src/x509.c

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13343,6 +13343,7 @@ int wolfSSL_X509_check_host(WOLFSSL_X509 *x, const char *chk, size_t chklen,
1334313343
unsigned int flags, char **peername)
1334413344
{
1334513345
int ret;
13346+
word32 i;
1334613347
#ifdef WOLFSSL_SMALL_STACK
1334713348
DecodedCert *dCert;
1334813349
#else
@@ -13384,6 +13385,22 @@ int wolfSSL_X509_check_host(WOLFSSL_X509 *x, const char *chk, size_t chklen,
1338413385
goto out;
1338513386
}
1338613387

13388+
/* Replicate openssl behavior for checklen */
13389+
if (chklen == 0) {
13390+
chklen = (size_t)(XSTRLEN(chk));
13391+
}
13392+
else {
13393+
for (i = 0; i < (chklen > 1 ? chklen - 1 : chklen); i++) {
13394+
if (chk[i] == '\0') {
13395+
ret = -1;
13396+
goto out;
13397+
}
13398+
}
13399+
}
13400+
if (chklen > 1 && (chk[chklen - 1] == '\0')) {
13401+
chklen--;
13402+
}
13403+
1338713404
ret = CheckHostName(dCert, (char *)chk, chklen);
1338813405

1338913406
out:

0 commit comments

Comments
 (0)