Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
121 changes: 121 additions & 0 deletions tests/api/test_certman.c
Original file line number Diff line number Diff line change
Expand Up @@ -1584,6 +1584,127 @@ int test_wolfSSL_CertManagerNameConstraint5(void)
return EXPECT_RESULT();
}

int test_wolfSSL_CertManagerNameConstraint_DNS_CN(void)
{
EXPECT_DECLS;
#if !defined(NO_FILESYSTEM) && !defined(NO_CERTS) && \
!defined(NO_WOLFSSL_CM_VERIFY) && !defined(NO_RSA) && \
defined(OPENSSL_EXTRA) && defined(WOLFSSL_CERT_GEN) && \
defined(WOLFSSL_CERT_EXT) && defined(WOLFSSL_ALT_NAMES) && \
!defined(NO_SHA256)
/* Test that DNS name constraints are enforced against the Subject CN
* when no SAN extension is present. The CA cert (cert-ext-ncdns.der)
* permits only DNS:wolfssl.com and DNS:example.com. A leaf cert with
* CN=evil.attacker.com and no SAN should be REJECTED. */
WOLFSSL_CERT_MANAGER* cm = NULL;
WOLFSSL_EVP_PKEY *priv = NULL;
WOLFSSL_X509_NAME* name = NULL;
const char* ca_cert = "./certs/test/cert-ext-ncdns.der";
const char* server_cert = "./certs/test/server-goodcn.pem";

byte *der = NULL;
int derSz;
byte *pt;
WOLFSSL_X509 *x509 = NULL;
WOLFSSL_X509 *ca = NULL;

pt = (byte*)server_key_der_2048;
ExpectNotNull(priv = wolfSSL_d2i_PrivateKey(EVP_PKEY_RSA, NULL,
(const unsigned char**)&pt, sizeof_server_key_der_2048));

ExpectNotNull(cm = wolfSSL_CertManagerNew());
ExpectNotNull(ca = wolfSSL_X509_load_certificate_file(ca_cert,
WOLFSSL_FILETYPE_ASN1));
ExpectNotNull((der = (byte*)wolfSSL_X509_get_der(ca, &derSz)));
ExpectIntEQ(wolfSSL_CertManagerLoadCABuffer(cm, der, derSz,
WOLFSSL_FILETYPE_ASN1), WOLFSSL_SUCCESS);

/* Sanity check: cert with SAN=evil.attacker.com is correctly rejected */
ExpectNotNull(x509 = wolfSSL_X509_load_certificate_file(server_cert,
WOLFSSL_FILETYPE_PEM));
ExpectNotNull(name = wolfSSL_X509_get_subject_name(ca));
ExpectIntEQ(wolfSSL_X509_set_issuer_name(x509, name), WOLFSSL_SUCCESS);
name = NULL;

ExpectNotNull(name = X509_NAME_new());
ExpectIntEQ(X509_NAME_add_entry_by_txt(name, "countryName", MBSTRING_UTF8,
(byte*)"US", 2, -1, 0), SSL_SUCCESS);
ExpectIntEQ(X509_NAME_add_entry_by_txt(name, "commonName", MBSTRING_UTF8,
(byte*)"evil.attacker.com", 17, -1, 0),
SSL_SUCCESS);
ExpectIntEQ(wolfSSL_X509_set_subject_name(x509, name), WOLFSSL_SUCCESS);
X509_NAME_free(name);
name = NULL;

ExpectIntEQ(wolfSSL_X509_add_altname(x509, "evil.attacker.com",
ASN_DNS_TYPE), WOLFSSL_SUCCESS);
ExpectIntGT(wolfSSL_X509_sign(x509, priv, EVP_sha256()), 0);
ExpectNotNull((der = (byte*)wolfSSL_X509_get_der(x509, &derSz)));
ExpectIntEQ(wolfSSL_CertManagerVerifyBuffer(cm, der, derSz,
WOLFSSL_FILETYPE_ASN1), WC_NO_ERR_TRACE(ASN_NAME_INVALID_E));
wolfSSL_X509_free(x509);
x509 = NULL;

/* NOW the actual vulnerability test: cert with CN=evil.attacker.com
* but NO SAN. The DNS name constraint should still reject this, since
* wolfSSL's hostname verification falls back to CN when no SAN exists. */
ExpectNotNull(x509 = wolfSSL_X509_load_certificate_file(server_cert,
WOLFSSL_FILETYPE_PEM));
ExpectNotNull(name = wolfSSL_X509_get_subject_name(ca));
ExpectIntEQ(wolfSSL_X509_set_issuer_name(x509, name), WOLFSSL_SUCCESS);
name = NULL;

ExpectNotNull(name = X509_NAME_new());
ExpectIntEQ(X509_NAME_add_entry_by_txt(name, "countryName", MBSTRING_UTF8,
(byte*)"US", 2, -1, 0), SSL_SUCCESS);
ExpectIntEQ(X509_NAME_add_entry_by_txt(name, "commonName", MBSTRING_UTF8,
(byte*)"evil.attacker.com", 17, -1, 0),
SSL_SUCCESS);
ExpectIntEQ(wolfSSL_X509_set_subject_name(x509, name), WOLFSSL_SUCCESS);
X509_NAME_free(name);
name = NULL;

/* Do NOT add any SAN this is the bypass vector */
ExpectIntGT(wolfSSL_X509_sign(x509, priv, EVP_sha256()), 0);
ExpectNotNull((der = (byte*)wolfSSL_X509_get_der(x509, &derSz)));
/* Should be ASN_NAME_INVALID_E because CN violates the constraint */
ExpectIntEQ(wolfSSL_CertManagerVerifyBuffer(cm, der, derSz,
WOLFSSL_FILETYPE_ASN1), WC_NO_ERR_TRACE(ASN_NAME_INVALID_E));
wolfSSL_X509_free(x509);
x509 = NULL;

/* Positive test: CN matches a permitted name (wolfssl.com) and no SAN is
* present. The CN fallback should accept this cert. */
ExpectNotNull(x509 = wolfSSL_X509_load_certificate_file(server_cert,
WOLFSSL_FILETYPE_PEM));
ExpectNotNull(name = wolfSSL_X509_get_subject_name(ca));
ExpectIntEQ(wolfSSL_X509_set_issuer_name(x509, name), WOLFSSL_SUCCESS);
name = NULL;

ExpectNotNull(name = X509_NAME_new());
ExpectIntEQ(X509_NAME_add_entry_by_txt(name, "countryName", MBSTRING_UTF8,
(byte*)"US", 2, -1, 0), SSL_SUCCESS);
ExpectIntEQ(X509_NAME_add_entry_by_txt(name, "commonName", MBSTRING_UTF8,
(byte*)"wolfssl.com", 11, -1, 0),
SSL_SUCCESS);
ExpectIntEQ(wolfSSL_X509_set_subject_name(x509, name), WOLFSSL_SUCCESS);
X509_NAME_free(name);
name = NULL;

/* No SAN added; CN=wolfssl.com matches the permitted DNS constraint. */
ExpectIntGT(wolfSSL_X509_sign(x509, priv, EVP_sha256()), 0);
ExpectNotNull((der = (byte*)wolfSSL_X509_get_der(x509, &derSz)));
ExpectIntEQ(wolfSSL_CertManagerVerifyBuffer(cm, der, derSz,
WOLFSSL_FILETYPE_ASN1), WOLFSSL_SUCCESS);

wolfSSL_CertManagerFree(cm);
wolfSSL_X509_free(x509);
wolfSSL_X509_free(ca);
wolfSSL_EVP_PKEY_free(priv);
#endif
return EXPECT_RESULT();
}

int test_wolfSSL_CertManagerCRL(void)
{
EXPECT_DECLS;
Expand Down
2 changes: 2 additions & 0 deletions tests/api/test_certman.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ int test_wolfSSL_CertManagerNameConstraint2(void);
int test_wolfSSL_CertManagerNameConstraint3(void);
int test_wolfSSL_CertManagerNameConstraint4(void);
int test_wolfSSL_CertManagerNameConstraint5(void);
int test_wolfSSL_CertManagerNameConstraint_DNS_CN(void);
int test_wolfSSL_CertManagerCRL(void);
int test_wolfSSL_CRL_reason_extensions_cleanup(void);
int test_wolfSSL_CRL_static_revoked_list(void);
Expand All @@ -54,6 +55,7 @@ int test_various_pathlen_chains(void);
TEST_DECL_GROUP("certman", test_wolfSSL_CertManagerNameConstraint3), \
TEST_DECL_GROUP("certman", test_wolfSSL_CertManagerNameConstraint4), \
TEST_DECL_GROUP("certman", test_wolfSSL_CertManagerNameConstraint5), \
TEST_DECL_GROUP("certman", test_wolfSSL_CertManagerNameConstraint_DNS_CN), \
TEST_DECL_GROUP("certman", test_wolfSSL_CertManagerCRL), \
TEST_DECL_GROUP("certman", test_wolfSSL_CRL_reason_extensions_cleanup), \
TEST_DECL_GROUP("certman", test_wolfSSL_CRL_static_revoked_list), \
Expand Down
11 changes: 9 additions & 2 deletions wolfcrypt/src/asn.c
Original file line number Diff line number Diff line change
Expand Up @@ -17658,9 +17658,16 @@ static int ConfirmNameConstraints(Signer* signer, DecodedCert* cert)
XMEMSET(&subjectDnsName, 0, sizeof(DNS_entry));
switch (nameType) {
case ASN_DNS_TYPE:
/* Should it also consider CN in subject? It could use
* subjectDnsName too */
name = cert->altNames;

/* When no SAN is present, apply DNS name constraints to the
* Subject CN. */
if (cert->subjectCN != NULL && cert->altNames == NULL) {
subjectDnsName.next = NULL;
subjectDnsName.type = ASN_DNS_TYPE;
subjectDnsName.len = cert->subjectCNLen;
subjectDnsName.name = cert->subjectCN;
}
break;
case ASN_IP_TYPE:
/* IP addresses are stored in altNames with type ASN_IP_TYPE */
Comment thread
rlm2002 marked this conversation as resolved.
Expand Down
Loading