mirror of
https://github.com/wolfSSL/wolfssl.git
synced 2026-08-15 08:01:21 +02:00
6823 - Apply dirName constraints to SANs under WOLFSSL_NO_ASN_STRICT
This commit is contained in:
@@ -436,7 +436,12 @@ jobs:
|
||||
{"name": "opensslextra-no-filesystem-no-bio", "minutes": 0.9,
|
||||
"configure": ["--enable-opensslextra", "--disable-filesystem", "CPPFLAGS=-DNO_BIO"]},
|
||||
{"name": "no-examples-no-malloc", "minutes": 0.8,
|
||||
"configure": ["--disable-examples", "CPPFLAGS=-DWOLFSSL_NO_MALLOC"]}
|
||||
"configure": ["--disable-examples", "CPPFLAGS=-DWOLFSSL_NO_MALLOC"]},
|
||||
{"comment": "WOLFSSL_NO_ASN_STRICT still enforces the RFC 5280 4.2 and 4.2.1.10 MUSTs",
|
||||
"name": "no-asn-strict-certs",
|
||||
"configure": ["--enable-testcert", "--enable-opensslextra", "--enable-certgen",
|
||||
"--enable-certreq", "--enable-certext", "--enable-ecc",
|
||||
"CPPFLAGS=-DWOLFSSL_NO_ASN_STRICT -DWOLFSSL_CUSTOM_OID -DHAVE_OID_ENCODING -DWOLFSSL_ALT_NAMES"]}
|
||||
]
|
||||
EOF
|
||||
.github/scripts/parallel-make-check.py \
|
||||
|
||||
+111
@@ -24314,6 +24314,51 @@ static word32 build_registeredID_san(byte* out, word32 outSz)
|
||||
return (word32)sizeof(ridSan);
|
||||
}
|
||||
|
||||
/* Build a SubjectAltName extension value (a SEQUENCE wrapping a single
|
||||
* directoryName GeneralName) holding the Name "CN=<val7>". */
|
||||
static word32 build_dirName_san(byte* out, word32 outSz, const char* val7)
|
||||
{
|
||||
static const byte prefix[] = {
|
||||
0x30, 0x16, /* SEQUENCE, 22 */
|
||||
0xA4, 0x14, /* [4] dirName, 20 */
|
||||
0x30, 0x12, /* Name SEQUENCE, 18 */
|
||||
0x31, 0x10, /* RDN SET, 16 */
|
||||
0x30, 0x0E, /* ATV SEQUENCE, 14 */
|
||||
0x06, 0x03, 0x55, 0x04, 0x03, /* OID 2.5.4.3 (CN) */
|
||||
0x0C, 0x07 /* UTF8String, 7 */
|
||||
};
|
||||
/* The UTF8String length above is baked in, so val7 must be exactly 7. */
|
||||
if (outSz < sizeof(prefix) + 7 || XSTRLEN(val7) != 7)
|
||||
return 0;
|
||||
XMEMCPY(out, prefix, sizeof(prefix));
|
||||
XMEMCPY(out + sizeof(prefix), val7, 7);
|
||||
return (word32)(sizeof(prefix) + 7);
|
||||
}
|
||||
|
||||
/* Build a NameConstraints extension value carrying a single excludedSubtree
|
||||
* ([1]) for the directoryName "CN=<val7>". */
|
||||
static word32 build_dirName_nameConstraints(byte* out, word32 outSz,
|
||||
const char* val7)
|
||||
{
|
||||
static const byte nc[] = {
|
||||
0x30, 0x1A, /* SEQUENCE, 26 */
|
||||
0xA1, 0x18, /* [1] excluded, 24 */
|
||||
0x30, 0x16, /* GeneralSubtree, 22 */
|
||||
0xA4, 0x14, /* [4] dirName, 20 */
|
||||
0x30, 0x12, /* Name SEQUENCE, 18 */
|
||||
0x31, 0x10, /* RDN SET, 16 */
|
||||
0x30, 0x0E, /* ATV SEQUENCE, 14 */
|
||||
0x06, 0x03, 0x55, 0x04, 0x03, /* OID 2.5.4.3 (CN) */
|
||||
0x0C, 0x07 /* UTF8String, 7 */
|
||||
};
|
||||
/* The UTF8String length above is baked in, so val7 must be exactly 7. */
|
||||
if (outSz < sizeof(nc) + 7 || XSTRLEN(val7) != 7)
|
||||
return 0;
|
||||
XMEMCPY(out, nc, sizeof(nc));
|
||||
XMEMCPY(out + sizeof(nc), val7, 7);
|
||||
return (word32)(sizeof(nc) + 7);
|
||||
}
|
||||
|
||||
/* Build a NameConstraints extension value with a single excludedSubtree
|
||||
* carrying a registeredID GeneralName for OID 1.2.3.4. wolfSSL enforces
|
||||
* registeredID name constraints by byte-comparing OID bodies, so a leaf
|
||||
@@ -24600,6 +24645,71 @@ static int test_NameConstraints_OtherName(void)
|
||||
return EXPECT_RESULT();
|
||||
}
|
||||
|
||||
/* Verifies wolfSSL applies an issuing CA's directoryName nameConstraints to a
|
||||
* leaf's directoryName SAN entries, not just to the subject field (RFC 5280
|
||||
* 4.2.1.10: "Restrictions of the form directoryName MUST be applied to the
|
||||
* subject field ... and to any names of type directoryName in the
|
||||
* subjectAltName extension"). The leaf subject never matches the constraint,
|
||||
* so only the SAN can trigger it.
|
||||
*
|
||||
* Coverage:
|
||||
* 1. Critical excluded subtree, leaf dirName SAN matches -> reject
|
||||
* 2. Critical excluded subtree, leaf dirName SAN differs -> accept
|
||||
* (positive control: pins the rejection to the matching path)
|
||||
* 3. Non-critical excluded subtree, leaf SAN matches -> reject
|
||||
* (excluded is enforced regardless of criticality)
|
||||
*
|
||||
* A permitted-subtree case is not included: for directoryName the leaf's
|
||||
* subject field is always checked against the permitted list as well, so the
|
||||
* generated leaf's subject would drive the result rather than its SAN.
|
||||
*/
|
||||
static int test_NameConstraints_DirName(void)
|
||||
{
|
||||
EXPECT_DECLS;
|
||||
#if defined(WOLFSSL_ASN_TEMPLATE) && \
|
||||
defined(WOLFSSL_CERT_REQ) && !defined(NO_ASN_TIME) && \
|
||||
defined(WOLFSSL_CERT_GEN) && defined(HAVE_ECC) && \
|
||||
defined(WOLFSSL_CERT_EXT) && !defined(NO_CERTS) && \
|
||||
defined(WOLFSSL_ALT_NAMES) && defined(WOLFSSL_CUSTOM_OID) && \
|
||||
defined(HAVE_OID_ENCODING) && !defined(IGNORE_NAME_CONSTRAINTS)
|
||||
byte sanBlocked[64];
|
||||
byte sanAllowed[64];
|
||||
byte ncExcludedBlocked[64];
|
||||
word32 sanBlockedSz, sanAllowedSz;
|
||||
word32 ncExcludedBlockedSz;
|
||||
|
||||
sanBlockedSz = build_dirName_san(sanBlocked, sizeof(sanBlocked), "blocked");
|
||||
sanAllowedSz = build_dirName_san(sanAllowed, sizeof(sanAllowed), "allowed");
|
||||
ncExcludedBlockedSz = build_dirName_nameConstraints(
|
||||
ncExcludedBlocked, sizeof(ncExcludedBlocked), "blocked");
|
||||
ExpectIntGT((int)sanBlockedSz, 0);
|
||||
ExpectIntGT((int)sanAllowedSz, 0);
|
||||
ExpectIntGT((int)ncExcludedBlockedSz, 0);
|
||||
|
||||
/* (1) Critical excluded directoryName matches the leaf's dirName SAN.
|
||||
* Must be rejected. */
|
||||
ExpectIntEQ(verify_with_otherName_chain(
|
||||
ncExcludedBlocked, ncExcludedBlockedSz, 1,
|
||||
sanBlocked, sanBlockedSz),
|
||||
WC_NO_ERR_TRACE(ASN_NAME_INVALID_E));
|
||||
|
||||
/* (2) Positive control: same excluded subtree, leaf carries a DIFFERENT
|
||||
* dirName SAN, so no match and the chain MUST verify. */
|
||||
ExpectIntEQ(verify_with_otherName_chain(
|
||||
ncExcludedBlocked, ncExcludedBlockedSz, 1,
|
||||
sanAllowed, sanAllowedSz),
|
||||
0);
|
||||
|
||||
/* (3) Non-critical excluded subtree, leaf SAN matches: exclusion is
|
||||
* enforced regardless of criticality. */
|
||||
ExpectIntEQ(verify_with_otherName_chain(
|
||||
ncExcludedBlocked, ncExcludedBlockedSz, 0,
|
||||
sanBlocked, sanBlockedSz),
|
||||
WC_NO_ERR_TRACE(ASN_NAME_INVALID_E));
|
||||
#endif
|
||||
return EXPECT_RESULT();
|
||||
}
|
||||
|
||||
#if defined(WOLFSSL_ASN_TEMPLATE) && \
|
||||
defined(WOLFSSL_CERT_REQ) && !defined(NO_ASN_TIME) && \
|
||||
defined(WOLFSSL_CERT_GEN) && defined(HAVE_ECC) && \
|
||||
@@ -37030,6 +37140,7 @@ TEST_CASE testCases[] = {
|
||||
TEST_DECL(test_PathLenSelfIssuedAllowed),
|
||||
TEST_DECL(test_PathLenNoKeyUsage),
|
||||
TEST_DECL(test_NameConstraints_OtherName),
|
||||
TEST_DECL(test_NameConstraints_DirName),
|
||||
TEST_DECL(test_NameConstraints_DnsUriWildcard),
|
||||
TEST_DECL(test_NameConstraints_SubtreeMinMax),
|
||||
TEST_DECL(test_ParseSerial0FixtureMatrix),
|
||||
|
||||
@@ -1024,13 +1024,8 @@ int test_wolfSSL_CertManagerNameConstraint2(void)
|
||||
wolfSSL_X509_sign(x509, priv, EVP_sha256());
|
||||
#endif
|
||||
ExpectNotNull((der = wolfSSL_X509_get_der(x509, &derSz)));
|
||||
#ifndef WOLFSSL_NO_ASN_STRICT
|
||||
ExpectIntEQ(wolfSSL_CertManagerVerifyBuffer(cm, der, derSz,
|
||||
WOLFSSL_FILETYPE_ASN1), WC_NO_ERR_TRACE(ASN_NAME_INVALID_E));
|
||||
#else
|
||||
ExpectIntEQ(wolfSSL_CertManagerVerifyBuffer(cm, der, derSz,
|
||||
WOLFSSL_FILETYPE_ASN1), WOLFSSL_SUCCESS);
|
||||
#endif
|
||||
|
||||
/* check that it still fails if one bad altname and one good altname is in
|
||||
* the certificate */
|
||||
@@ -1050,13 +1045,8 @@ int test_wolfSSL_CertManagerNameConstraint2(void)
|
||||
wolfSSL_X509_sign(x509, priv, EVP_sha256());
|
||||
#endif
|
||||
ExpectNotNull((der = wolfSSL_X509_get_der(x509, &derSz)));
|
||||
#ifndef WOLFSSL_NO_ASN_STRICT
|
||||
ExpectIntEQ(wolfSSL_CertManagerVerifyBuffer(cm, der, derSz,
|
||||
WOLFSSL_FILETYPE_ASN1), WC_NO_ERR_TRACE(ASN_NAME_INVALID_E));
|
||||
#else
|
||||
ExpectIntEQ(wolfSSL_CertManagerVerifyBuffer(cm, der, derSz,
|
||||
WOLFSSL_FILETYPE_ASN1), WOLFSSL_SUCCESS);
|
||||
#endif
|
||||
|
||||
/* check it fails with switching position of bad altname */
|
||||
wolfSSL_X509_free(x509);
|
||||
@@ -1075,13 +1065,8 @@ int test_wolfSSL_CertManagerNameConstraint2(void)
|
||||
wolfSSL_X509_sign(x509, priv, EVP_sha256());
|
||||
#endif
|
||||
ExpectNotNull((der = wolfSSL_X509_get_der(x509, &derSz)));
|
||||
#ifndef WOLFSSL_NO_ASN_STRICT
|
||||
ExpectIntEQ(wolfSSL_CertManagerVerifyBuffer(cm, der, derSz,
|
||||
WOLFSSL_FILETYPE_ASN1), WC_NO_ERR_TRACE(ASN_NAME_INVALID_E));
|
||||
#else
|
||||
ExpectIntEQ(wolfSSL_CertManagerVerifyBuffer(cm, der, derSz,
|
||||
WOLFSSL_FILETYPE_ASN1), WOLFSSL_SUCCESS);
|
||||
#endif
|
||||
wolfSSL_CertManagerFree(cm);
|
||||
|
||||
wolfSSL_X509_free(x509);
|
||||
@@ -1110,13 +1095,8 @@ int test_wolfSSL_CertManagerNameConstraint2(void)
|
||||
wolfSSL_X509_sign(x509, priv, EVP_sha256());
|
||||
#endif
|
||||
ExpectNotNull((der = wolfSSL_X509_get_der(x509, &derSz)));
|
||||
#ifndef WOLFSSL_NO_ASN_STRICT
|
||||
ExpectIntEQ(wolfSSL_CertManagerVerifyBuffer(cm, der, derSz,
|
||||
WOLFSSL_FILETYPE_ASN1), WC_NO_ERR_TRACE(ASN_NAME_INVALID_E));
|
||||
#else
|
||||
ExpectIntEQ(wolfSSL_CertManagerVerifyBuffer(cm, der, derSz,
|
||||
WOLFSSL_FILETYPE_ASN1), WOLFSSL_SUCCESS);
|
||||
#endif
|
||||
wolfSSL_CertManagerFree(cm);
|
||||
wolfSSL_X509_free(x509);
|
||||
wolfSSL_X509_free(ca);
|
||||
|
||||
+8
-3
@@ -52,7 +52,14 @@ ASN Options:
|
||||
* RSA_DECODE_EXTRA: Decodes extra information in RSA public key.
|
||||
* WOLFSSL_CERT_GEN: Cert generation. Saves extra certificate info in GetName.
|
||||
* WOLFSSL_NO_ASN_STRICT: Disable strict RFC compliance checks to
|
||||
restore 3.13.0 behavior.
|
||||
restore 3.13.0 behavior. It no longer disables these RFC 5280 checks:
|
||||
duplicate detection on the certificate extensions that use
|
||||
VERIFY_AND_SET_OID (4.2), rejection of a critical extension whose OID is
|
||||
unrecognized (4.2, subject to any WC_ASN_UNKNOWN_EXT_CB callback), and
|
||||
application of directoryName name constraints to subjectAltName entries as
|
||||
well as the subject (4.2.1.10). It still relaxes the critical check for a
|
||||
recognized-but-unsupported extension (e.g. certificatePolicies without
|
||||
WOLFSSL_SEP or WOLFSSL_CERT_EXT) and the CRL duplicate-extension check.
|
||||
* WOLFSSL_ASN_ALLOW_0_SERIAL: Even if WOLFSSL_NO_ASN_STRICT is not defined,
|
||||
allow a length=1, but zero value serial number.
|
||||
* WOLFSSL_NO_OCSP_OPTIONAL_CERTS: Skip optional OCSP certs (responder issuer
|
||||
@@ -19048,9 +19055,7 @@ static int ConfirmNameConstraints(Signer* signer, DecodedCert* cert)
|
||||
}
|
||||
break;
|
||||
case ASN_DIR_TYPE:
|
||||
#ifndef WOLFSSL_NO_ASN_STRICT
|
||||
name = cert->altDirNames;
|
||||
#endif
|
||||
|
||||
/* RFC 5280 section 4.2.1.10
|
||||
"Restrictions of the form directoryName MUST be
|
||||
|
||||
Reference in New Issue
Block a user