add WOLFSSL_LEFT_MOST_WILDCARD_ONLY support to X509_check_host()

This commit is contained in:
Chris Conlon 2024-09-04 17:06:34 -06:00
parent bf29b68600
commit f878220b81
5 changed files with 125 additions and 20 deletions

View File

@ -12508,16 +12508,20 @@ int CipherRequires(byte first, byte second, int requirement)
#ifndef NO_CERTS
/* Match names with wildcards, each wildcard can represent a single name
component or fragment but not multiple names, i.e.,
*.z.com matches y.z.com but not x.y.z.com
If flags contains WOLFSSL_LEFT_MOST_WILDCARD_ONLY, wildcard only applies
to left-most name component, compatible with RFC 2830 identity checking.
return 1 on success */
int MatchDomainName(const char* pattern, int patternLen, const char* str,
word32 strLen)
word32 strLen, unsigned int flags)
{
int ret = 0;
byte wildcardEligible = 1;
byte leftWildcardOnly = flags & WOLFSSL_LEFT_MOST_WILDCARD_ONLY;
if (pattern == NULL || str == NULL || patternLen <= 0 || strLen == 0)
return 0;
@ -12530,11 +12534,16 @@ int MatchDomainName(const char* pattern, int patternLen, const char* str,
pattern++;
if (p == '*') {
if ((p == '*') && wildcardEligible) {
char s;
/* We will always match '*' */
patternLen--;
/* Only single wildcard allowed with strict left only */
if (leftWildcardOnly) {
wildcardEligible = 0;
}
/* Consume any extra '*' chars until the next non '*' char. */
while (patternLen > 0) {
p = (char)XTOLOWER((unsigned char)*pattern);
@ -12543,6 +12552,10 @@ int MatchDomainName(const char* pattern, int patternLen, const char* str,
return 0;
if (p != '*')
break;
if (leftWildcardOnly && (p == '*')) {
/* RFC2830 only allows single left-most wildcard */
return 0;
}
patternLen--;
}
@ -12574,6 +12587,11 @@ int MatchDomainName(const char* pattern, int patternLen, const char* str,
}
}
else {
/* Past left-most wildcard location, not eligible if flag set*/
if (leftWildcardOnly && wildcardEligible) {
wildcardEligible = 0;
}
/* Simple case, pattern match exactly */
if (p != (char)XTOLOWER((unsigned char) *str))
return 0;
@ -12605,7 +12623,7 @@ int MatchDomainName(const char* pattern, int patternLen, const char* str,
* -1 : No matches and wild pattern match failed.
*/
int CheckForAltNames(DecodedCert* dCert, const char* domain, word32 domainLen,
int* checkCN)
int* checkCN, unsigned int flags)
{
int match = 0;
DNS_entry* altName = NULL;
@ -12636,7 +12654,7 @@ int CheckForAltNames(DecodedCert* dCert, const char* domain, word32 domainLen,
len = (word32)altName->len;
}
if (MatchDomainName(buf, (int)len, domain, domainLen)) {
if (MatchDomainName(buf, (int)len, domain, domainLen, flags)) {
match = 1;
if (checkCN != NULL) {
*checkCN = 0;
@ -12665,13 +12683,14 @@ int CheckForAltNames(DecodedCert* dCert, const char* domain, word32 domainLen,
* domainNameLen The length of the domain name.
* returns DOMAIN_NAME_MISMATCH when no match found and 0 on success.
*/
int CheckHostName(DecodedCert* dCert, const char *domainName, size_t domainNameLen)
int CheckHostName(DecodedCert* dCert, const char *domainName,
size_t domainNameLen, unsigned int flags)
{
int checkCN;
int ret = WC_NO_ERR_TRACE(DOMAIN_NAME_MISMATCH);
if (CheckForAltNames(dCert, domainName, (word32)domainNameLen,
&checkCN) != 1) {
&checkCN, flags) != 1) {
ret = DOMAIN_NAME_MISMATCH;
WOLFSSL_MSG("DomainName match on alt names failed");
}
@ -12682,7 +12701,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, (word32)domainNameLen) == 1) {
domainName, (word32)domainNameLen, flags) == 1) {
ret = 0;
}
else {
@ -12699,7 +12718,7 @@ int CheckIPAddr(DecodedCert* dCert, const char* ipasc)
{
WOLFSSL_MSG("Checking IPAddr");
return CheckHostName(dCert, ipasc, (size_t)XSTRLEN(ipasc));
return CheckHostName(dCert, ipasc, (size_t)XSTRLEN(ipasc), 0);
}
@ -13843,7 +13862,7 @@ int DoVerifyCallback(WOLFSSL_CERT_MANAGER* cm, WOLFSSL* ssl, int cert_err,
/* If altNames names is present, then subject common name is ignored */
if (args->dCert->altNames != NULL) {
if (CheckForAltNames(args->dCert, ssl->param->hostName,
(word32)XSTRLEN(ssl->param->hostName), NULL) != 1) {
(word32)XSTRLEN(ssl->param->hostName), NULL, 0) != 1) {
if (cert_err == 0) {
ret = DOMAIN_NAME_MISMATCH;
WOLFSSL_ERROR_VERBOSE(ret);
@ -13857,7 +13876,7 @@ int DoVerifyCallback(WOLFSSL_CERT_MANAGER* cm, WOLFSSL* ssl, int cert_err,
args->dCert->subjectCN,
args->dCert->subjectCNLen,
ssl->param->hostName,
(word32)XSTRLEN(ssl->param->hostName)) == 0) {
(word32)XSTRLEN(ssl->param->hostName), 0) == 0) {
if (cert_err == 0) {
ret = DOMAIN_NAME_MISMATCH;
WOLFSSL_ERROR_VERBOSE(ret);
@ -15747,7 +15766,7 @@ int ProcessPeerCerts(WOLFSSL* ssl, byte* input, word32* inOutIdx,
(ssl->buffers.domainName.buffer == NULL ? 0 :
(word32)XSTRLEN(
(const char *)ssl->buffers.domainName.buffer)),
NULL) != 1) {
NULL, 0) != 1) {
WOLFSSL_MSG("DomainName match on alt names failed");
/* try to get peer key still */
ret = DOMAIN_NAME_MISMATCH;
@ -15762,7 +15781,7 @@ int ProcessPeerCerts(WOLFSSL* ssl, byte* input, word32* inOutIdx,
(ssl->buffers.domainName.buffer == NULL ? 0 :
(word32)XSTRLEN(
(const char *)ssl->buffers.domainName.buffer)
)) == 0)
), 0) == 0)
{
WOLFSSL_MSG("DomainName match on common name failed");
ret = DOMAIN_NAME_MISMATCH;
@ -15775,14 +15794,14 @@ int ProcessPeerCerts(WOLFSSL* ssl, byte* input, word32* inOutIdx,
args->dCert->subjectCNLen,
(char*)ssl->buffers.domainName.buffer,
(ssl->buffers.domainName.buffer == NULL ? 0 :
(word32)XSTRLEN(ssl->buffers.domainName.buffer))) == 0)
(word32)XSTRLEN(ssl->buffers.domainName.buffer)), 0) == 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) {
NULL, 0) != 1) {
WOLFSSL_MSG(
"DomainName match on alt names failed too");
/* try to get peer key still */

View File

@ -14338,7 +14338,6 @@ int wolfSSL_X509_check_host(WOLFSSL_X509 *x, const char *chk, size_t chklen,
WOLFSSL_ENTER("wolfSSL_X509_check_host");
/* flags and peername not needed for Nginx. */
(void)flags;
(void)peername;
if ((x == NULL) || (chk == NULL)) {
@ -14390,7 +14389,7 @@ int wolfSSL_X509_check_host(WOLFSSL_X509 *x, const char *chk, size_t chklen,
chklen--;
}
ret = CheckHostName(dCert, (char *)chk, chklen);
ret = CheckHostName(dCert, (char *)chk, chklen, flags);
out:

View File

@ -55652,20 +55652,42 @@ static int test_wolfSSL_X509_check_host(void)
&& !defined(NO_SHA) && !defined(NO_RSA)
X509* x509 = NULL;
const char altName[] = "example.com";
const char badAltName[] = "a.example.com";
/* cliCertFile has subjectAltName set to 'example.com', '127.0.0.1' */
ExpectNotNull(x509 = wolfSSL_X509_load_certificate_file(cliCertFile,
SSL_FILETYPE_PEM));
ExpectIntEQ(X509_check_host(x509, altName, XSTRLEN(altName), 0, NULL),
WOLFSSL_SUCCESS);
ExpectIntEQ(X509_check_host(x509, badAltName, XSTRLEN(badAltName), 0, NULL),
WC_NO_ERR_TRACE(WOLFSSL_FAILURE));
ExpectIntEQ(X509_check_host(x509, NULL, 0, 0, NULL),
WC_NO_ERR_TRACE(WOLFSSL_FAILURE));
/* Check WOLFSSL_LEFT_MOST_WILDCARD_ONLY flag set */
ExpectIntEQ(X509_check_host(x509, altName, XSTRLEN(altName),
WOLFSSL_LEFT_MOST_WILDCARD_ONLY, NULL), WOLFSSL_SUCCESS);
ExpectIntEQ(X509_check_host(x509, NULL, 0,
WOLFSSL_LEFT_MOST_WILDCARD_ONLY, NULL),
WC_NO_ERR_TRACE(WOLFSSL_FAILURE));
ExpectIntEQ(X509_check_host(x509, badAltName, XSTRLEN(badAltName),
WOLFSSL_LEFT_MOST_WILDCARD_ONLY, NULL),
WC_NO_ERR_TRACE(WOLFSSL_FAILURE));
X509_free(x509);
ExpectIntEQ(X509_check_host(NULL, altName, XSTRLEN(altName), 0, NULL),
WC_NO_ERR_TRACE(WOLFSSL_FAILURE));
/* Check again with WOLFSSL_LEFT_MOST_WILDCARD_ONLY flag set */
ExpectIntEQ(X509_check_host(NULL, altName, XSTRLEN(altName),
WOLFSSL_LEFT_MOST_WILDCARD_ONLY, NULL),
WC_NO_ERR_TRACE(WOLFSSL_FAILURE));
#endif
return EXPECT_RESULT();
}
@ -63359,6 +63381,12 @@ static int test_wolfSSL_X509_bad_altname(void)
* name of "a*\0*". Ensure that it does not match "aaaaa" */
ExpectIntNE(wolfSSL_X509_check_host(x509, name, nameLen,
WOLFSSL_ALWAYS_CHECK_SUBJECT, NULL), 1);
/* Also make sure WOLFSSL_LEFT_MOST_WILDCARD_ONLY fails too */
ExpectIntNE(wolfSSL_X509_check_host(x509, name, nameLen,
WOLFSSL_ALWAYS_CHECK_SUBJECT | WOLFSSL_LEFT_MOST_WILDCARD_ONLY,
NULL), 1);
X509_free(x509);
#endif
@ -63479,6 +63507,26 @@ static int test_wolfSSL_X509_name_match(void)
ExpectIntNE(wolfSSL_X509_check_host(x509, name4, nameLen4,
WOLFSSL_ALWAYS_CHECK_SUBJECT, NULL), 1);
/* WOLFSSL_LEFT_MOST_WILDCARD_ONLY flag should fail on all cases, since
* 'a*' alt name does not have wildcard left-most */
/* Ensure that "a*" does not match "aaaaa" */
ExpectIntNE(wolfSSL_X509_check_host(x509, name1, nameLen1,
WOLFSSL_ALWAYS_CHECK_SUBJECT | WOLFSSL_LEFT_MOST_WILDCARD_ONLY,
NULL), WOLFSSL_SUCCESS);
/* Ensure that "a*" does not match "a" */
ExpectIntNE(wolfSSL_X509_check_host(x509, name2, nameLen2,
WOLFSSL_ALWAYS_CHECK_SUBJECT | WOLFSSL_LEFT_MOST_WILDCARD_ONLY,
NULL), WOLFSSL_SUCCESS);
/* Ensure that "a*" does not match "abbbb" */
ExpectIntNE(wolfSSL_X509_check_host(x509, name3, nameLen3,
WOLFSSL_ALWAYS_CHECK_SUBJECT | WOLFSSL_LEFT_MOST_WILDCARD_ONLY,
NULL), WOLFSSL_SUCCESS);
/* Ensure that "a*" does not match "bbb" */
ExpectIntNE(wolfSSL_X509_check_host(x509, name4, nameLen4,
WOLFSSL_ALWAYS_CHECK_SUBJECT | WOLFSSL_LEFT_MOST_WILDCARD_ONLY,
NULL), WOLFSSL_SUCCESS);
wolfSSL_X509_free(x509);
#endif
@ -63601,6 +63649,21 @@ static int test_wolfSSL_X509_name_match2(void)
ExpectIntNE(wolfSSL_X509_check_host(x509, name4, nameLen4,
WOLFSSL_ALWAYS_CHECK_SUBJECT, NULL), WOLFSSL_SUCCESS);
/* WOLFSSL_LEFT_MOST_WILDCARD_ONLY flag should fail on all cases, since
* 'a*b*' alt name does not have wildcard left-most */
ExpectIntEQ(wolfSSL_X509_check_host(x509, name1, nameLen1,
WOLFSSL_ALWAYS_CHECK_SUBJECT | WOLFSSL_LEFT_MOST_WILDCARD_ONLY,
NULL), WOLFSSL_FAILURE);
ExpectIntEQ(wolfSSL_X509_check_host(x509, name2, nameLen2,
WOLFSSL_ALWAYS_CHECK_SUBJECT | WOLFSSL_LEFT_MOST_WILDCARD_ONLY,
NULL), WOLFSSL_FAILURE);
ExpectIntEQ(wolfSSL_X509_check_host(x509, name3, nameLen3,
WOLFSSL_ALWAYS_CHECK_SUBJECT | WOLFSSL_LEFT_MOST_WILDCARD_ONLY,
NULL), WOLFSSL_FAILURE);
ExpectIntEQ(wolfSSL_X509_check_host(x509, name4, nameLen4,
WOLFSSL_ALWAYS_CHECK_SUBJECT | WOLFSSL_LEFT_MOST_WILDCARD_ONLY,
NULL), WOLFSSL_FAILURE);
/* Ensure that "a*b*" matches "ab", testing openssl behavior replication
* on check len input handling, 0 for len is OK as it should then use
* strlen(name1) */
@ -63714,6 +63777,8 @@ static int test_wolfSSL_X509_name_match3(void)
int nameLen1 = (int)(XSTRLEN(name1));
const char *name2 = "x.y.example.com";
int nameLen2 = (int)(XSTRLEN(name2));
const char *name3 = "example.com";
int nameLen3 = (int)(XSTRLEN(name3));
ExpectNotNull(x509 = wolfSSL_X509_load_certificate_buffer(
cert_der, certSize, WOLFSSL_FILETYPE_ASN1));
@ -63724,6 +63789,22 @@ static int test_wolfSSL_X509_name_match3(void)
/* Ensure that "*.example.com" does NOT match "x.y.example.com" */
ExpectIntNE(wolfSSL_X509_check_host(x509, name2, nameLen2,
WOLFSSL_ALWAYS_CHECK_SUBJECT, NULL), WOLFSSL_SUCCESS);
/* Ensure that "*.example.com" does NOT match "example.com" */
ExpectIntNE(wolfSSL_X509_check_host(x509, name3, nameLen3,
WOLFSSL_ALWAYS_CHECK_SUBJECT, NULL), WOLFSSL_SUCCESS);
/* WOLFSSL_LEFT_MOST_WILDCARD_ONLY, should match "foo.example.com" */
ExpectIntEQ(wolfSSL_X509_check_host(x509, name1, nameLen1,
WOLFSSL_ALWAYS_CHECK_SUBJECT | WOLFSSL_LEFT_MOST_WILDCARD_ONLY,
NULL), WOLFSSL_SUCCESS);
/* WOLFSSL_LEFT_MOST_WILDCARD_ONLY, should NOT match "x.y.example.com" */
ExpectIntNE(wolfSSL_X509_check_host(x509, name2, nameLen2,
WOLFSSL_ALWAYS_CHECK_SUBJECT | WOLFSSL_LEFT_MOST_WILDCARD_ONLY,
NULL), WOLFSSL_SUCCESS);
/* WOLFSSL_LEFT_MOST_WILDCARD_ONLY, should NOT match "example.com" */
ExpectIntNE(wolfSSL_X509_check_host(x509, name3, nameLen3,
WOLFSSL_ALWAYS_CHECK_SUBJECT | WOLFSSL_LEFT_MOST_WILDCARD_ONLY,
NULL), WOLFSSL_SUCCESS);
wolfSSL_X509_free(x509);

View File

@ -2240,9 +2240,13 @@ WOLFSSL_LOCAL void FreeAsyncCtx(WOLFSSL* ssl, byte freeAsync);
WOLFSSL_LOCAL void FreeKeyExchange(WOLFSSL* ssl);
WOLFSSL_LOCAL void FreeSuites(WOLFSSL* ssl);
WOLFSSL_LOCAL int ProcessPeerCerts(WOLFSSL* ssl, byte* input, word32* inOutIdx, word32 totalSz);
WOLFSSL_LOCAL int MatchDomainName(const char* pattern, int len, const char* str, word32 strLen);
WOLFSSL_LOCAL int MatchDomainName(const char* pattern, int len,
const char* str, word32 strLen,
unsigned int flags);
#if !defined(NO_CERTS) && !defined(NO_ASN)
WOLFSSL_LOCAL int CheckForAltNames(DecodedCert* dCert, const char* domain, word32 domainLen, int* checkCN);
WOLFSSL_LOCAL int CheckForAltNames(DecodedCert* dCert, const char* domain,
word32 domainLen, int* checkCN,
unsigned int flags);
WOLFSSL_LOCAL int CheckIPAddr(DecodedCert* dCert, const char* ipasc);
WOLFSSL_LOCAL void CopyDecodedName(WOLFSSL_X509_NAME* name, DecodedCert* dCert, int nameType);
#endif
@ -6252,7 +6256,7 @@ WOLFSSL_API void SSL_ResourceFree(WOLFSSL* ssl); /* Micrium uses */
#ifndef NO_ASN
WOLFSSL_LOCAL int CheckHostName(DecodedCert* dCert, const char *domainName,
size_t domainNameLen);
size_t domainNameLen, unsigned int flags);
#endif
#endif

View File

@ -606,6 +606,8 @@ struct WOLFSSL_X509_STORE {
#define WOLFSSL_NO_WILDCARDS 0x2
#define WOLFSSL_NO_PARTIAL_WILDCARDS 0x4
#define WOLFSSL_MULTI_LABEL_WILDCARDS 0x8
/* Custom to wolfSSL, OpenSSL compat goes up to 0x20 */
#define WOLFSSL_LEFT_MOST_WILDCARD_ONLY 0x40
#if defined(OPENSSL_EXTRA) || defined(WOLFSSL_WPAS_SMALL)
#define WOLFSSL_USE_CHECK_TIME 0x2