Fix handling of otherName in ConfirmNameConstraints

This commit is contained in:
Eric Blankenhorn
2026-04-28 11:05:37 -05:00
parent 1c9555c121
commit 6fdd0de0ca
3 changed files with 399 additions and 13 deletions
+257
View File
@@ -22193,6 +22193,262 @@ static int test_MakeCertWith0Ser(void)
return EXPECT_RESULT();
}
#if 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)
/* Build a SubjectAltName extension value (a SEQUENCE wrapping a single
* otherName GeneralName) for the Microsoft UPN OID 1.3.6.1.4.1.311.20.2.3
* with the given 7-byte UTF8String value. */
static word32 build_otherName_san(byte* out, word32 outSz, const char* val7)
{
static const byte prefix[] = {
0x30, 0x19, /* SEQUENCE, 25 */
0xA0, 0x17, /* [0] CONSTRUCTED, 23 */
0x06, 0x0A, /* OBJECT ID, 10 */
0x2B, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37,
0x14, 0x02, 0x03, /* UPN OID */
0xA0, 0x09, /* [0] EXPLICIT, 9 */
0x0C, 0x07 /* UTF8String, 7 */
};
if (outSz < sizeof(prefix) + 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 subtree of
* the given list type ([0] permitted or [1] excluded) for an otherName
* UPN whose UTF8 value is the given 7-byte string. */
static word32 build_otherName_nameConstraints(byte* out, word32 outSz,
int excluded, const char* val7)
{
static const byte common[] = {
0x30, 0x1D, /* SEQUENCE, 29 */
0x00, 0x1B, /* listTag, 27 (patched) */
0x30, 0x19, /* GeneralSubtree, 25 */
0xA0, 0x17, /* [0] CONSTRUCTED, 23 */
0x06, 0x0A,
0x2B, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37,
0x14, 0x02, 0x03,
0xA0, 0x09,
0x0C, 0x07
};
if (outSz < sizeof(common) + 7)
return 0;
XMEMCPY(out, common, sizeof(common));
out[2] = excluded ? 0xA1 : 0xA0; /* listTag */
XMEMCPY(out + sizeof(common), val7, 7);
return (word32)(sizeof(common) + 7);
}
/* Build a chain (root -> intermediate -> leaf) where the intermediate
* carries `nameConstraintsDer` as a (possibly critical) nameConstraints
* extension and the leaf carries `sanDer` as its SAN. Loads root and
* intermediate as trusted CAs into a fresh CertManager, parses the leaf
* with VERIFY, and returns the result code from wc_ParseCert(). */
static int verify_with_otherName_chain(const byte* nameConstraintsDer,
word32 nameConstraintsDerSz, int critical,
const byte* sanDer, word32 sanDerSz)
{
Cert cert;
DecodedCert decodedCert;
byte rootDer[FOURK_BUF];
byte icaDer[FOURK_BUF];
byte leafDer[FOURK_BUF];
int rootDerSz = 0, icaDerSz = 0, leafDerSz = 0;
int parseRet = -1;
WC_RNG rng;
ecc_key rootKey, icaKey, leafKey;
WOLFSSL_CERT_MANAGER* cm = NULL;
XMEMSET(&rng, 0, sizeof(rng));
XMEMSET(&rootKey, 0, sizeof(rootKey));
XMEMSET(&icaKey, 0, sizeof(icaKey));
XMEMSET(&leafKey, 0, sizeof(leafKey));
if (wc_InitRng(&rng) != 0) goto done;
if (wc_ecc_init(&rootKey) != 0) goto done;
if (wc_ecc_init(&icaKey) != 0) goto done;
if (wc_ecc_init(&leafKey) != 0) goto done;
if (wc_ecc_make_key(&rng, 32, &rootKey) != 0) goto done;
if (wc_ecc_make_key(&rng, 32, &icaKey) != 0) goto done;
if (wc_ecc_make_key(&rng, 32, &leafKey) != 0) goto done;
/* Self-signed root. */
if (wc_InitCert(&cert) != 0) goto done;
(void)XSTRNCPY(cert.subject.country, "US", CTC_NAME_SIZE);
(void)XSTRNCPY(cert.subject.org, "OtherNCRoot", CTC_NAME_SIZE);
(void)XSTRNCPY(cert.subject.commonName, "OtherNCRoot", CTC_NAME_SIZE);
cert.selfSigned = 1;
cert.isCA = 1;
cert.sigType = CTC_SHA256wECDSA;
cert.keyUsage = KEYUSE_KEY_CERT_SIGN | KEYUSE_CRL_SIGN;
if (wc_SetSubjectKeyIdFromPublicKey_ex(&cert, ECC_TYPE, &rootKey) != 0)
goto done;
if (wc_MakeCert(&cert, rootDer, FOURK_BUF, NULL, &rootKey, &rng) < 0)
goto done;
rootDerSz = wc_SignCert(cert.bodySz, cert.sigType, rootDer, FOURK_BUF,
NULL, &rootKey, &rng);
if (rootDerSz < 0) goto done;
/* Intermediate, signed by root, carrying nameConstraints. */
if (wc_InitCert(&cert) != 0) goto done;
cert.selfSigned = 0;
cert.isCA = 1;
cert.sigType = CTC_SHA256wECDSA;
cert.keyUsage = KEYUSE_KEY_CERT_SIGN | KEYUSE_CRL_SIGN;
(void)XSTRNCPY(cert.subject.country, "US", CTC_NAME_SIZE);
(void)XSTRNCPY(cert.subject.org, "OtherNCICA", CTC_NAME_SIZE);
(void)XSTRNCPY(cert.subject.commonName, "OtherNCICA", CTC_NAME_SIZE);
if (wc_SetIssuerBuffer(&cert, rootDer, rootDerSz) != 0) goto done;
if (wc_SetAuthKeyIdFromPublicKey_ex(&cert, ECC_TYPE, &rootKey) != 0)
goto done;
if (wc_SetSubjectKeyIdFromPublicKey_ex(&cert, ECC_TYPE, &icaKey) != 0)
goto done;
if (nameConstraintsDer != NULL) {
/* nameConstraints OID = 2.5.29.30 */
if (wc_SetCustomExtension(&cert, critical ? 1 : 0, "2.5.29.30",
nameConstraintsDer, nameConstraintsDerSz) != 0)
goto done;
}
if (wc_MakeCert(&cert, icaDer, FOURK_BUF, NULL, &icaKey, &rng) < 0)
goto done;
icaDerSz = wc_SignCert(cert.bodySz, cert.sigType, icaDer, FOURK_BUF,
NULL, &rootKey, &rng);
if (icaDerSz < 0) goto done;
/* Leaf, signed by intermediate, carrying the otherName SAN. */
if (wc_InitCert(&cert) != 0) goto done;
cert.selfSigned = 0;
cert.isCA = 0;
cert.sigType = CTC_SHA256wECDSA;
(void)XSTRNCPY(cert.subject.country, "US", CTC_NAME_SIZE);
(void)XSTRNCPY(cert.subject.org, "OtherNCLeaf", CTC_NAME_SIZE);
(void)XSTRNCPY(cert.subject.commonName, "OtherNCLeaf", CTC_NAME_SIZE);
if (wc_SetIssuerBuffer(&cert, icaDer, icaDerSz) != 0) goto done;
if (wc_SetAuthKeyIdFromPublicKey_ex(&cert, ECC_TYPE, &icaKey) != 0)
goto done;
if (wc_SetSubjectKeyIdFromPublicKey_ex(&cert, ECC_TYPE, &leafKey) != 0)
goto done;
if (sanDerSz > sizeof(cert.altNames)) goto done;
XMEMCPY(cert.altNames, sanDer, sanDerSz);
cert.altNamesSz = (int)sanDerSz;
if (wc_MakeCert(&cert, leafDer, FOURK_BUF, NULL, &leafKey, &rng) < 0)
goto done;
leafDerSz = wc_SignCert(cert.bodySz, cert.sigType, leafDer, FOURK_BUF,
NULL, &icaKey, &rng);
if (leafDerSz < 0) goto done;
cm = wolfSSL_CertManagerNew();
if (cm == NULL) goto done;
if (wolfSSL_CertManagerLoadCABuffer(cm, rootDer, rootDerSz,
WOLFSSL_FILETYPE_ASN1) != WOLFSSL_SUCCESS) goto done;
if (wolfSSL_CertManagerLoadCABuffer(cm, icaDer, icaDerSz,
WOLFSSL_FILETYPE_ASN1) != WOLFSSL_SUCCESS) goto done;
wc_InitDecodedCert(&decodedCert, leafDer, (word32)leafDerSz, NULL);
parseRet = wc_ParseCert(&decodedCert, CERT_TYPE, VERIFY, cm);
wc_FreeDecodedCert(&decodedCert);
done:
if (cm != NULL) wolfSSL_CertManagerFree(cm);
wc_ecc_free(&leafKey);
wc_ecc_free(&icaKey);
wc_ecc_free(&rootKey);
wc_FreeRng(&rng);
return parseRet;
}
#endif
/* Verifies wolfSSL enforces an issuing CA's nameConstraints extension on a
* leaf certificate's otherName SAN (RFC 5280 4.2.1.10). The vulnerability
* was that ConfirmNameConstraints() ignored ASN_OTHER_TYPE entirely, so a
* malicious intermediate could issue leaves whose otherName SAN violated
* its own subtree.
*
* Coverage:
* 1. Critical excluded subtree, leaf SAN matches -> reject
* 2. Critical excluded subtree, leaf SAN does NOT match -> accept
* (positive control: distinguishes 'right rule fired' from
* 'broke everything with otherName')
* 3. Non-critical excluded subtree, leaf SAN matches -> reject
* (excluded is enforced regardless of criticality)
* 4. Critical permitted subtree, leaf SAN matches -> accept
* 5. Critical permitted subtree, leaf SAN does NOT match -> reject
*/
static int test_NameConstraints_OtherName(void)
{
EXPECT_DECLS;
#if 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];
byte ncPermittedAllowed[64];
word32 sanBlockedSz, sanAllowedSz;
word32 ncExcludedBlockedSz, ncPermittedAllowedSz;
sanBlockedSz =
build_otherName_san(sanBlocked, sizeof(sanBlocked), "blocked");
sanAllowedSz =
build_otherName_san(sanAllowed, sizeof(sanAllowed), "allowed");
ncExcludedBlockedSz = build_otherName_nameConstraints(
ncExcludedBlocked, sizeof(ncExcludedBlocked), 1, "blocked");
ncPermittedAllowedSz = build_otherName_nameConstraints(
ncPermittedAllowed, sizeof(ncPermittedAllowed), 0, "allowed");
ExpectIntGT((int)sanBlockedSz, 0);
ExpectIntGT((int)sanAllowedSz, 0);
ExpectIntGT((int)ncExcludedBlockedSz, 0);
ExpectIntGT((int)ncPermittedAllowedSz, 0);
/* (1) Original bypass scenario: critical excluded otherName matches
* the leaf's otherName 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 critical excluded subtree, but the leaf
* carries a DIFFERENT otherName value, so byte-comparison says no
* match and the chain MUST verify. This pins the rejection in (1)
* to the matching path rather than to a blanket 'reject any
* otherName under critical'. */
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));
/* (4) Critical permitted subtree, leaf SAN inside the permitted set:
* verification succeeds. */
ExpectIntEQ(verify_with_otherName_chain(
ncPermittedAllowed, ncPermittedAllowedSz, 1,
sanAllowed, sanAllowedSz),
0);
/* (5) Critical permitted subtree, leaf SAN outside the permitted set:
* verification rejects. */
ExpectIntEQ(verify_with_otherName_chain(
ncPermittedAllowed, ncPermittedAllowedSz, 1,
sanBlocked, sanBlockedSz),
WC_NO_ERR_TRACE(ASN_NAME_INVALID_E));
#endif
return EXPECT_RESULT();
}
static int test_MakeCertWithCaFalse(void)
{
EXPECT_DECLS;
@@ -37012,6 +37268,7 @@ TEST_CASE testCases[] = {
TEST_DECL(test_PathLenSelfIssued),
TEST_DECL(test_PathLenSelfIssuedAllowed),
TEST_DECL(test_PathLenNoKeyUsage),
TEST_DECL(test_NameConstraints_OtherName),
TEST_DECL(test_MakeCertWith0Ser),
TEST_DECL(test_MakeCertWithCaFalse),
#ifdef WOLFSSL_CERT_SIGN_CB
+126 -13
View File
@@ -4375,7 +4375,8 @@ static int DecodeAltNames(const byte* input, word32 sz, DecodedCert* cert);
static int DecodeCrlDist(const byte* input, word32 sz, DecodedCert* cert);
static int DecodeAuthInfo(const byte* input, word32 sz, DecodedCert* cert);
#ifndef IGNORE_NAME_CONSTRAINTS
static int DecodeSubtree(const byte* input, word32 sz, Base_entry** head, word32 limit, void* heap);
static int DecodeSubtree(const byte* input, word32 sz, Base_entry** head,
word32 limit, byte* hasUnsupported, void* heap);
static int DecodeNameConstraints(const byte* input, word32 sz, DecodedCert* cert);
#endif
#if defined(WOLFSSL_SEP) || defined(WOLFSSL_CERT_EXT)
@@ -17654,6 +17655,26 @@ static int PermittedListOk(DNS_entry* name, Base_entry* dnsList, byte nameType)
break;
}
}
else if (nameType == ASN_OTHER_TYPE) {
/* RFC 5280 4.2.1.10: otherName matching is byte-exact
* comparison of the full OtherName encoding. The FPKI/SEP
* path also stores entries that contain only the parsed
* UPN/FASCN value and have oidSum != 0; those are not
* byte-comparable with the OID || [0] EXPLICIT value form
* stored for the constraint, so we explicitly skip them.
* Without that guard, a coincidental length match could
* mis-validate. */
if (
#ifdef WOLFSSL_FPKI
name->oidSum == 0 &&
#endif
name->len == current->nameSz &&
XMEMCMP(name->name, current->name,
(size_t)current->nameSz) == 0) {
match = 1;
break;
}
}
else if (name->len >= current->nameSz &&
wolfssl_local_MatchBaseName(nameType, name->name, name->len,
current->name, current->nameSz)) {
@@ -17701,6 +17722,19 @@ static int IsInExcludedList(DNS_entry* name, Base_entry* dnsList, byte nameType)
break;
}
}
else if (nameType == ASN_OTHER_TYPE) {
/* See note in PermittedListOk about byte-exact matching. */
if (
#ifdef WOLFSSL_FPKI
name->oidSum == 0 &&
#endif
name->len == current->nameSz &&
XMEMCMP(name->name, current->name,
(size_t)current->nameSz) == 0) {
ret = 1;
break;
}
}
else if (name->len >= current->nameSz &&
wolfssl_local_MatchBaseName(nameType, name->name, name->len,
current->name, current->nameSz)) {
@@ -17718,13 +17752,14 @@ static int IsInExcludedList(DNS_entry* name, Base_entry* dnsList, byte nameType)
static int ConfirmNameConstraints(Signer* signer, DecodedCert* cert)
{
const byte nameTypes[] = {ASN_RFC822_TYPE, ASN_DNS_TYPE, ASN_DIR_TYPE,
ASN_IP_TYPE, ASN_URI_TYPE};
ASN_IP_TYPE, ASN_URI_TYPE, ASN_OTHER_TYPE};
int i;
if (signer == NULL || cert == NULL)
return 0;
if (signer->excludedNames == NULL && signer->permittedNames == NULL)
if (signer->excludedNames == NULL && signer->permittedNames == NULL &&
!signer->extNameConstraintHasUnsupported)
return 1;
for (i=0; i < (int)sizeof(nameTypes); i++) {
@@ -17789,10 +17824,18 @@ static int ConfirmNameConstraints(Signer* signer, DecodedCert* cert)
case ASN_URI_TYPE:
name = cert->altNames;
break;
case ASN_OTHER_TYPE:
/* otherName SAN entries are stored on cert->altNames with
* type ASN_OTHER_TYPE. Match by byte-exact comparison of
* the OtherName encoding (OID || [0] EXPLICIT value). For
* FPKI/SEP builds, altNames may also contain entries that
* hold only the parsed UPN/FASCN value (oidSum != 0); the
* explicit oidSum guard in IsInExcludedList /
* PermittedListOk skips those so a coincidental length
* match cannot mis-validate. */
name = cert->altNames;
break;
default:
/* Other types of names are ignored for now.
* Shouldn't it be rejected if it there is a altNamesByType[nameType]
* and signer->extNameConstraintCrit is set? */
return 0;
}
@@ -17833,6 +17876,19 @@ static int ConfirmNameConstraints(Signer* signer, DecodedCert* cert)
}
}
/* RFC 5280 4.2.1.10: "If a name constraints extension that is marked as
* critical imposes constraints on a particular name form ... the
* application MUST either process the constraint or reject the
* certificate." otherName is processed by byte-comparison above; any
* remaining unsupported forms (registeredID, x400Address, ediPartyName)
* trigger the fail-closed reject below. */
if (signer->extNameConstraintCrit &&
signer->extNameConstraintHasUnsupported) {
WOLFSSL_MSG("Critical nameConstraints contains unsupported "
"GeneralName form; rejecting");
return 0;
}
return 1;
}
@@ -18140,8 +18196,34 @@ static int DecodeGeneralName(const byte* input, word32* inOutIdx, byte tag,
/* GeneralName choice: otherName */
else if (tag == (ASN_CONTEXT_SPECIFIC | ASN_CONSTRUCTED | ASN_OTHER_TYPE)) {
/* TODO: test data for code path */
#ifndef IGNORE_NAME_CONSTRAINTS
/* Store the raw OtherName encoding so ConfirmNameConstraints() can
* byte-match it against the issuing CA's subtree (RFC 5280
* 4.2.1.10). DecodeOtherName() may also add an entry that holds
* only the parsed UPN/FASCN value with oidSum != 0; the explicit
* oidSum guard in IsInExcludedList()/PermittedListOk() ensures
* those parsed entries are skipped during byte-comparison. */
ret = SetDNSEntry(cert->heap, (const char*)(input + idx), len,
ASN_OTHER_TYPE, &cert->altNames);
if (ret != 0) {
return ret;
}
#endif
ret = DecodeOtherName(cert, input, &idx, len);
}
#elif !defined(IGNORE_NAME_CONSTRAINTS)
/* GeneralName choice: otherName.
* No OID-specific decoding in this build, but we store the raw
* OtherName encoding (OID || [0] EXPLICIT value) on altNames so
* ConfirmNameConstraints() can byte-match it against the issuing CA's
* nameConstraints subtree (RFC 5280 4.2.1.10). */
else if (tag == (ASN_CONTEXT_SPECIFIC | ASN_CONSTRUCTED | ASN_OTHER_TYPE)) {
ret = SetDNSEntry(cert->heap, (const char*)(input + idx), len,
ASN_OTHER_TYPE, &cert->altNames);
if (ret == 0) {
idx += (word32)len;
}
}
#endif
/* GeneralName choice: dNSName, x400Address, ediPartyName */
else {
@@ -19249,8 +19331,14 @@ static int DecodeSubtreeGeneralName(const byte* input, word32 sz, byte tag,
(void)heap;
/* if constructed has leading sequence */
if ((tag & ASN_CONSTRUCTED) == ASN_CONSTRUCTED) {
/* directoryName is encoded as [4] CONSTRUCTED { Name } where Name is a
* SEQUENCE - strip the inner SEQUENCE header.
* otherName is encoded as [0] CONSTRUCTED { OID, [0] EXPLICIT value }
* where the inner content is NOT a SEQUENCE; keep the bytes as-is so
* we can byte-match a leaf SAN otherName against the constraint.
*/
if ((tag & ASN_CONSTRUCTED) == ASN_CONSTRUCTED &&
(tag & ASN_TYPE_MASK) != ASN_OTHER_TYPE) {
ret = GetASN_Sequence(input, &nameIdx, &strLen, sz, 0);
if (ret < 0) {
ret = ASN_PARSE_E;
@@ -19309,8 +19397,17 @@ static int DecodeSubtreeGeneralName(const byte* input, word32 sz, byte tag,
* @return ASN_PARSE_E when SEQUENCE is not found as expected.
*/
#ifdef WOLFSSL_ASN_TEMPLATE
/* Decode a sub-tree of name constraints.
*
* @param [out] hasUnsupported Set to 1 when an entry with a GeneralName
* form we cannot fully enforce was
* encountered. Drives the RFC 5280 4.2.1.10
* fail-closed requirement for critical
* nameConstraints extensions; must not be
* NULL.
*/
static int DecodeSubtree(const byte* input, word32 sz, Base_entry** head,
word32 limit, void* heap)
word32 limit, byte* hasUnsupported, void* heap)
{
DECL_ASNGETDATA(dataASN, subTreeASN_Length);
word32 idx = 0;
@@ -19352,13 +19449,21 @@ static int DecodeSubtree(const byte* input, word32 sz, Base_entry** head,
t == (ASN_CONTEXT_SPECIFIC | ASN_RFC822_TYPE) ||
t == (ASN_CONTEXT_SPECIFIC | ASN_CONSTRUCTED | ASN_DIR_TYPE) ||
t == (ASN_CONTEXT_SPECIFIC | ASN_IP_TYPE) ||
t == (ASN_CONTEXT_SPECIFIC | ASN_URI_TYPE)) {
t == (ASN_CONTEXT_SPECIFIC | ASN_URI_TYPE) ||
t == (ASN_CONTEXT_SPECIFIC | ASN_CONSTRUCTED |
ASN_OTHER_TYPE)) {
/* Parse the general name and store a new entry. */
ret = DecodeSubtreeGeneralName(input +
GetASNItem_DataIdx(dataASN[SUBTREEASN_IDX_BASE], input),
dataASN[SUBTREEASN_IDX_BASE].length, t, head, heap);
}
/* Skip entry. */
else {
/* GeneralName form (e.g. registeredID, x400Address,
* ediPartyName) we do not enforce. Record so the caller can
* fail-closed when the nameConstraints extension is critical
* (RFC 5280 4.2.1.10). */
*hasUnsupported = 1;
}
}
}
@@ -19406,6 +19511,7 @@ static int DecodeNameConstraints(const byte* input, word32 sz,
DECL_ASNGETDATA(dataASN, nameConstraintsASN_Length);
word32 idx = 0;
int ret = 0;
byte hasUnsupported = 0;
CALLOC_ASNGETDATA(dataASN, nameConstraintsASN_Length, ret, cert->heap);
@@ -19421,7 +19527,7 @@ static int DecodeNameConstraints(const byte* input, word32 sz,
dataASN[NAMECONSTRAINTSASN_IDX_PERMIT].data.ref.data,
dataASN[NAMECONSTRAINTSASN_IDX_PERMIT].data.ref.length,
&cert->permittedNames, WOLFSSL_MAX_NAME_CONSTRAINTS,
cert->heap);
&hasUnsupported, cert->heap);
}
}
if (ret == 0) {
@@ -19431,10 +19537,14 @@ static int DecodeNameConstraints(const byte* input, word32 sz,
dataASN[NAMECONSTRAINTSASN_IDX_EXCLUDE].data.ref.data,
dataASN[NAMECONSTRAINTSASN_IDX_EXCLUDE].data.ref.length,
&cert->excludedNames, WOLFSSL_MAX_NAME_CONSTRAINTS,
cert->heap);
&hasUnsupported, cert->heap);
}
}
if (ret == 0 && hasUnsupported) {
cert->extNameConstraintHasUnsupported = 1;
}
FREE_ASNGETDATA(dataASN, cert->heap);
return ret;
@@ -22878,6 +22988,9 @@ int FillSigner(Signer* signer, DecodedCert* cert, int type, DerBuffer *der)
#ifndef IGNORE_NAME_CONSTRAINTS
signer->permittedNames = cert->permittedNames;
signer->excludedNames = cert->excludedNames;
signer->extNameConstraintCrit = cert->extNameConstraintCrit;
signer->extNameConstraintHasUnsupported =
cert->extNameConstraintHasUnsupported;
#endif
#ifndef NO_SKID
XMEMCPY(signer->subjectKeyIdHash, cert->extSubjKeyId,
+16
View File
@@ -2063,6 +2063,11 @@ struct DecodedCert {
WC_BITFIELD extAuthKeyIdCrit:1;
#ifndef IGNORE_NAME_CONSTRAINTS
WC_BITFIELD extNameConstraintCrit:1;
/* Set when DecodeSubtree encountered a constraint form (e.g.
* registeredID, x400Address, ediPartyName) we cannot enforce. Used
* together with extNameConstraintCrit to implement the RFC 5280
* 4.2.1.10 fail-closed requirement. */
WC_BITFIELD extNameConstraintHasUnsupported:1;
#endif
WC_BITFIELD extSubjKeyIdCrit:1;
WC_BITFIELD extKeyUsageCrit:1;
@@ -2130,6 +2135,17 @@ struct Signer {
byte extKeyUsage;
word16 maxPathLen;
WC_BITFIELD selfSigned:1;
#ifndef IGNORE_NAME_CONSTRAINTS
/* Mirror of DecodedCert::extNameConstraintCrit and
* extNameConstraintHasUnsupported so ConfirmNameConstraints can
* implement the RFC 5280 4.2.1.10 fail-closed requirement when a
* critical nameConstraints extension imposes a constraint form we
* cannot fully enforce. Co-located with selfSigned to share its
* bitfield storage word and avoid growing sizeof(Signer), which is
* load-bearing for PERSIST_CERT_CACHE. */
WC_BITFIELD extNameConstraintCrit:1;
WC_BITFIELD extNameConstraintHasUnsupported:1;
#endif
const byte* publicKey;
int nameLen;
const char*