Merge pull request #10223 from rlm2002/zd21611

CN constraints fix
This commit is contained in:
Sean Parkinson
2026-04-19 21:28:29 +10:00
committed by GitHub
3 changed files with 132 additions and 2 deletions
+121
View File
@@ -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;
+2
View File
@@ -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);
@@ -55,6 +56,7 @@ int test_wolfSSL_CertManagerRejectMD5Cert(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), \
+9 -2
View File
@@ -17717,9 +17717,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 */