Skip to content

Commit

Permalink
Merge pull request #7604 from ColtonWilley/explicit_len_pattern_match
Browse files Browse the repository at this point in the history
Rewrite pattern matching to use explicit length
  • Loading branch information
dgarske authored Jun 3, 2024
2 parents 43f4ba9 + 0c00693 commit 1f684e6
Show file tree
Hide file tree
Showing 4 changed files with 464 additions and 40 deletions.
114 changes: 76 additions & 38 deletions src/internal.c
Original file line number Diff line number Diff line change
Expand Up @@ -12393,55 +12393,77 @@ int CipherRequires(byte first, byte second, int requirement)
*.z.com matches y.z.com but not x.y.z.com

return 1 on success */
int MatchDomainName(const char* pattern, int len, const char* str)
int MatchDomainName(const char* pattern, int patternLen, const char* str,
word32 strLen)
{
int ret = 0;

if (pattern == NULL || str == NULL || len <= 0)
if (pattern == NULL || str == NULL || patternLen <= 0 || strLen == 0)
return 0;

while (len > 0) {

char p = (char)XTOLOWER((unsigned char)*pattern++);
while (patternLen > 0) {
/* Get the next pattern char to evaluate */
char p = (char)XTOLOWER((unsigned char)*pattern);
if (p == '\0')
break;

pattern++;

if (p == '*') {
char s;
/* We will always match '*' */
patternLen--;

while (--len > 0) {
/* Consume any extra '*' chars until the next non '*' char. */
while (patternLen > 0) {
p = (char)XTOLOWER((unsigned char)*pattern);
pattern++;
if (p == '\0' && len > 0)
if (p == '\0' && patternLen > 0)
return 0;
if (p != '*')
break;

patternLen--;
}

if (len == 0)
p = '\0';
/* Consume str until we reach next char in pattern after '*' or
* end of string */
while (strLen > 0) {
s = (char)XTOLOWER((unsigned char) *str);
str++;
strLen--;

/* p is next char in pattern after '*', or '*' if '*' is the
* last char in the pattern (in which case patternLen is 1) */
if ( ((s == p) && (patternLen > 0))) {
/* We had already counted the '*' as matched, this means
* we also matched the next non '*' char in pattern */
patternLen--;
break;
}

while ( (s = (char)XTOLOWER((unsigned char) *str)) != '\0') {
if (s == p)
/* If strlen is 0, we have consumed the entire string. Count that
* as a match of '*' */
if (strLen == 0) {
break;
}

if (s == '.')
return 0;
str++;
}
}
else {
/* Simple case, pattern match exactly */
if (p != (char)XTOLOWER((unsigned char) *str))
return 0;
}


if (len > 0) {
str++;
len--;
strLen--;
patternLen--;
}
}

if (*str == '\0' && len == 0) {
if (strLen == 0 && patternLen == 0) {
ret = 1; /* success */
}

Expand All @@ -12453,14 +12475,16 @@ int MatchDomainName(const char* pattern, int len, const char* str)
* Fail if there are wild patterns and they didn't match.
* Check the common name if no alternative names matched.
*
* dCert Decoded cert to get the alternative names from.
* domain Domain name to compare against.
* checkCN Whether to check the common name.
* returns 1 : match was found.
* 0 : no match found.
* -1 : No matches and wild pattern match failed.
* dCert Decoded cert to get the alternative names from.
* domain Domain name to compare against.
* domainLen Length of the domain name.
* checkCN Whether to check the common name.
* returns 1 : match was found.
* 0 : no match found.
* -1 : No matches and wild pattern match failed.
*/
int CheckForAltNames(DecodedCert* dCert, const char* domain, int* checkCN)
int CheckForAltNames(DecodedCert* dCert, const char* domain, word32 domainLen,
int* checkCN)
{
int match = 0;
DNS_entry* altName = NULL;
Expand Down Expand Up @@ -12491,7 +12515,7 @@ int CheckForAltNames(DecodedCert* dCert, const char* domain, int* checkCN)
len = (word32)altName->len;
}

if (MatchDomainName(buf, (int)len, domain)) {
if (MatchDomainName(buf, (int)len, domain, domainLen)) {
match = 1;
if (checkCN != NULL) {
*checkCN = 0;
Expand Down Expand Up @@ -12525,10 +12549,8 @@ int CheckHostName(DecodedCert* dCert, const char *domainName, size_t domainNameL
int checkCN;
int ret = DOMAIN_NAME_MISMATCH;

/* Assume name is NUL terminated. */
(void)domainNameLen;

if (CheckForAltNames(dCert, domainName, &checkCN) != 1) {
if (CheckForAltNames(dCert, domainName, (word32)domainNameLen,
&checkCN) != 1) {
WOLFSSL_MSG("DomainName match on alt names failed");
}
else {
Expand All @@ -12538,7 +12560,7 @@ int CheckHostName(DecodedCert* dCert, const char *domainName, size_t domainNameL
#ifndef WOLFSSL_HOSTNAME_VERIFY_ALT_NAME_ONLY
if (checkCN == 1) {
if (MatchDomainName(dCert->subjectCN, dCert->subjectCNLen,
domainName) == 1) {
domainName, (word32)domainNameLen) == 1) {
ret = 0;
}
else {
Expand Down Expand Up @@ -13576,7 +13598,8 @@ int DoVerifyCallback(WOLFSSL_CERT_MANAGER* cm, WOLFSSL* ssl, int cert_err,
ssl->param && ssl->param->hostName[0]) {
/* If altNames names is present, then subject common name is ignored */
if (args->dCert->altNames != NULL) {
if (CheckForAltNames(args->dCert, ssl->param->hostName, NULL) != 1) {
if (CheckForAltNames(args->dCert, ssl->param->hostName,
(word32)XSTRLEN(ssl->param->hostName), NULL) != 1) {
if (cert_err == 0) {
ret = DOMAIN_NAME_MISMATCH;
WOLFSSL_ERROR_VERBOSE(ret);
Expand All @@ -13586,9 +13609,11 @@ int DoVerifyCallback(WOLFSSL_CERT_MANAGER* cm, WOLFSSL* ssl, int cert_err,
#ifndef WOLFSSL_HOSTNAME_VERIFY_ALT_NAME_ONLY
else {
if (args->dCert->subjectCN) {
if (MatchDomainName(args->dCert->subjectCN,
args->dCert->subjectCNLen,
ssl->param->hostName) == 0) {
if (MatchDomainName(
args->dCert->subjectCN,
args->dCert->subjectCNLen,
ssl->param->hostName,
(word32)XSTRLEN(ssl->param->hostName)) == 0) {
if (cert_err == 0) {
ret = DOMAIN_NAME_MISMATCH;
WOLFSSL_ERROR_VERBOSE(ret);
Expand Down Expand Up @@ -15401,6 +15426,9 @@ int ProcessPeerCerts(WOLFSSL* ssl, byte* input, word32* inOutIdx,
if (args->dCert->altNames) {
if (CheckForAltNames(args->dCert,
(char*)ssl->buffers.domainName.buffer,
(ssl->buffers.domainName.buffer == NULL ? 0 :
(word32)XSTRLEN(
(const char *)ssl->buffers.domainName.buffer)),
NULL) != 1) {
WOLFSSL_MSG("DomainName match on alt names failed");
/* try to get peer key still */
Expand All @@ -15410,9 +15438,14 @@ int ProcessPeerCerts(WOLFSSL* ssl, byte* input, word32* inOutIdx,
}
else {
if (MatchDomainName(
args->dCert->subjectCN,
args->dCert->subjectCNLen,
(char*)ssl->buffers.domainName.buffer) == 0) {
args->dCert->subjectCN,
args->dCert->subjectCNLen,
(char*)ssl->buffers.domainName.buffer,
(ssl->buffers.domainName.buffer == NULL ? 0 :
(word32)XSTRLEN(
(const char *)ssl->buffers.domainName.buffer)
)) == 0)
{
WOLFSSL_MSG("DomainName match on common name failed");
ret = DOMAIN_NAME_MISMATCH;
WOLFSSL_ERROR_VERBOSE(ret);
Expand All @@ -15422,10 +15455,15 @@ int ProcessPeerCerts(WOLFSSL* ssl, byte* input, word32* inOutIdx,
/* Old behavior. */
if (MatchDomainName(args->dCert->subjectCN,
args->dCert->subjectCNLen,
(char*)ssl->buffers.domainName.buffer) == 0) {
(char*)ssl->buffers.domainName.buffer,
(ssl->buffers.domainName.buffer == NULL ? 0 :
(word32)XSTRLEN(ssl->buffers.domainName.buffer))) == 0)
{
WOLFSSL_MSG("DomainName match on common name failed");
if (CheckForAltNames(args->dCert,
(char*)ssl->buffers.domainName.buffer,
(ssl->buffers.domainName.buffer == NULL ? 0 :
(word32)XSTRLEN(ssl->buffers.domainName.buffer)),
NULL) != 1) {
WOLFSSL_MSG(
"DomainName match on alt names failed too");
Expand Down
17 changes: 17 additions & 0 deletions src/x509.c
Original file line number Diff line number Diff line change
Expand Up @@ -13343,6 +13343,7 @@ int wolfSSL_X509_check_host(WOLFSSL_X509 *x, const char *chk, size_t chklen,
unsigned int flags, char **peername)
{
int ret;
size_t i;
#ifdef WOLFSSL_SMALL_STACK
DecodedCert *dCert;
#else
Expand Down Expand Up @@ -13384,6 +13385,22 @@ int wolfSSL_X509_check_host(WOLFSSL_X509 *x, const char *chk, size_t chklen,
goto out;
}

/* Replicate openssl behavior for checklen */
if (chklen == 0) {
chklen = (size_t)(XSTRLEN(chk));
}
else {
for (i = 0; i < (chklen > 1 ? chklen - 1 : chklen); i++) {
if (chk[i] == '\0') {
ret = -1;
goto out;
}
}
}
if (chklen > 1 && (chk[chklen - 1] == '\0')) {
chklen--;
}

ret = CheckHostName(dCert, (char *)chk, chklen);

out:
Expand Down
Loading

0 comments on commit 1f684e6

Please sign in to comment.