walk cert chain to apply ancestor name constraints

- Ancestor walk added to ParseCertRelative
  - FindSignerByAkidOrName helper: AKID->SKID with name-hash
    validation, name-only fallback only when AKID is absent
  - Signer fields: authKeyIdSet, authKeyIdHash
  - issuerNameHash ifdef widened to include !IGNORE_NAME_CONSTRAINTS
  - WOLFSSL_MAX_CHAIN_DEPTH macro (default 20)
  - Self-loop and A->B->A cycle termination
  - CN-as-DNS fallback in ConfirmNameConstraints gated on !cert->isCA
This commit is contained in:
Ruby Martin
2026-06-10 17:09:43 -06:00
parent 3876746500
commit bc26ce05cd
2 changed files with 134 additions and 20 deletions
+116 -16
View File
@@ -18588,9 +18588,10 @@ static int ConfirmNameConstraints(Signer* signer, DecodedCert* cert)
case ASN_DNS_TYPE:
name = cert->altNames;
/* When no SAN is present, apply DNS name constraints to the
* Subject CN. */
if (cert->subjectCN != NULL && cert->altNames == NULL) {
/* Apply DNS constraints to leaf Subject CN when no SAN
* (legacy hostname-in-CN). Skipped for CAs. */
if (cert->subjectCN != NULL && cert->altNames == NULL &&
!cert->isCA) {
subjectDnsName.next = NULL;
subjectDnsName.type = ASN_DNS_TYPE;
subjectDnsName.len = cert->subjectCNLen;
@@ -23149,6 +23150,71 @@ Signer* findSignerByName(Signer *list, byte *hash)
return NULL;
}
#ifndef IGNORE_NAME_CONSTRAINTS
/* Find a signer for cert in cm and extraCAList. Prefers AKID->SKID
* with name-hash validation. Fall back to name-only when AKID is
* absent. */
static Signer* FindSignerByAkidOrName(void* cm, Signer* extraCAList,
Signer* cert)
{
Signer* signer = NULL;
#ifdef HAVE_CERTIFICATE_STATUS_REQUEST_V2
#ifndef NO_SKID
Signer* exCaSigner;
#endif
#else
(void)extraCAList;
#endif
#ifndef NO_SKID
if (cert->authKeyIdSet) {
signer = GetCA(cm, cert->authKeyIdHash);
if (signer != NULL &&
XMEMCMP(signer->subjectNameHash, cert->issuerNameHash,
SIGNER_DIGEST_SIZE) != 0) {
signer = NULL;
}
/* AKID is authoritative; do not fall back to name when AKID
* is set (could substitute a same-DN sibling). */
}
else {
signer = GetCAByName(cm, cert->issuerNameHash);
}
#else
signer = GetCA(cm, cert->issuerNameHash);
#endif
#ifdef HAVE_CERTIFICATE_STATUS_REQUEST_V2
if (signer == NULL && extraCAList != NULL) {
#ifndef NO_SKID
if (cert->authKeyIdSet) {
for (exCaSigner = extraCAList; exCaSigner != NULL;
exCaSigner = exCaSigner->next) {
if (XMEMCMP(exCaSigner->subjectKeyIdHash,
cert->authKeyIdHash,
SIGNER_DIGEST_SIZE) == 0 &&
XMEMCMP(exCaSigner->subjectNameHash,
cert->issuerNameHash,
SIGNER_DIGEST_SIZE) == 0) {
signer = exCaSigner;
break;
}
}
/* AKID is authoritative; do not fall back to name. */
}
else {
signer = findSignerByName(extraCAList, cert->issuerNameHash);
}
#else
signer = findSignerByName(extraCAList, cert->issuerNameHash);
#endif
}
#endif
return signer;
}
#endif /* !IGNORE_NAME_CONSTRAINTS */
int ParseCertRelative(DecodedCert* cert, int type, int verify, void* cm,
Signer *extraCAList)
{
@@ -23163,6 +23229,12 @@ int ParseCertRelative(DecodedCert* cert, int type, int verify, void* cm,
int idx = 0;
#endif
byte* sce_tsip_encRsaKeyIdx;
#ifndef IGNORE_NAME_CONSTRAINTS
int ncDepth = 0;
Signer* ncSigner = NULL;
Signer* ncParent = NULL;
Signer* ncPrev = NULL;
#endif
(void)extraCAList;
if (cert == NULL) {
@@ -23795,18 +23867,6 @@ int ParseCertRelative(DecodedCert* cert, int type, int verify, void* cm,
}
#endif /* WOLFSSL_DUAL_ALG_CERTS */
}
#ifndef IGNORE_NAME_CONSTRAINTS
if (verify == VERIFY || verify == VERIFY_OCSP ||
verify == VERIFY_NAME || verify == VERIFY_SKIP_DATE) {
/* check that this cert's name is permitted by the signer's
* name constraints */
if (!ConfirmNameConstraints(cert->ca, cert)) {
WOLFSSL_MSG("Confirm name constraint failed");
WOLFSSL_ERROR_VERBOSE(ASN_NAME_INVALID_E);
return ASN_NAME_INVALID_E;
}
}
#endif /* IGNORE_NAME_CONSTRAINTS */
} /* cert->ca */
#ifdef WOLFSSL_CERT_REQ
else if (type == CERTREQ_TYPE) {
@@ -23888,6 +23948,38 @@ int ParseCertRelative(DecodedCert* cert, int type, int verify, void* cm,
}
} /* verify != NO_VERIFY && type != CA_TYPE && type != TRUSTED_PEER_TYPE */
#ifndef IGNORE_NAME_CONSTRAINTS
/* Apply each ancestor CA's name constraints to this cert.
* Signer pointers between lookups are not lock-protected
* (see wolfssl_cm_get_certs_der). */
if ((verify == VERIFY || verify == VERIFY_OCSP ||
verify == VERIFY_NAME || verify == VERIFY_SKIP_DATE) &&
type != TRUSTED_PEER_TYPE && cert->ca != NULL) {
ncSigner = cert->ca;
while (ncSigner != NULL) {
if (!ConfirmNameConstraints(ncSigner, cert)) {
WOLFSSL_MSG("Confirm name constraint failed");
WOLFSSL_ERROR_VERBOSE(ASN_NAME_INVALID_E);
return ASN_NAME_INVALID_E;
}
/* Stop at trust anchor (self-issued). */
if (ncSigner->selfSigned)
break;
ncParent = FindSignerByAkidOrName(cm, extraCAList, ncSigner);
/* Stop on missing parent, self-loop, or A->B->A cycle. */
if (ncParent == NULL || ncParent == ncSigner ||
ncParent == ncPrev)
break;
if (++ncDepth >= WOLFSSL_MAX_CHAIN_DEPTH) {
WOLFSSL_MSG("NC ancestor walk exceeded WOLFSSL_MAX_CHAIN_DEPTH");
WOLFSSL_ERROR_VERBOSE(ASN_PATHLEN_SIZE_E);
return ASN_PATHLEN_SIZE_E;
}
ncPrev = ncSigner;
ncSigner = ncParent;
}
}
#endif /* IGNORE_NAME_CONSTRAINTS */
#if defined(WOLFSSL_NO_TRUSTED_CERTS_VERIFY) && !defined(NO_SKID)
exit_pcr:
#endif
@@ -23966,10 +24058,18 @@ int FillSigner(Signer* signer, DecodedCert* cert, int type, DerBuffer *der)
#ifndef NO_SKID
XMEMCPY(signer->subjectKeyIdHash, cert->extSubjKeyId,
SIGNER_DIGEST_SIZE);
#ifndef IGNORE_NAME_CONSTRAINTS
if (cert->extAuthKeyIdSet) {
XMEMCPY(signer->authKeyIdHash, cert->extAuthKeyId,
SIGNER_DIGEST_SIZE);
signer->authKeyIdSet = 1;
}
#endif
#endif
XMEMCPY(signer->subjectNameHash, cert->subjectHash,
SIGNER_DIGEST_SIZE);
#if defined(HAVE_OCSP) || defined(HAVE_CRL) || defined(WOLFSSL_AKID_NAME)
#if defined(HAVE_OCSP) || defined(HAVE_CRL) || \
defined(WOLFSSL_AKID_NAME) || !defined(IGNORE_NAME_CONSTRAINTS)
XMEMCPY(signer->issuerNameHash, cert->issuerHash,
SIGNER_DIGEST_SIZE);
#endif
+18 -4
View File
@@ -1736,6 +1736,11 @@ typedef struct EncodedName {
#define WOLFSSL_MAX_PATH_LEN 127
#endif
#ifndef WOLFSSL_MAX_CHAIN_DEPTH
/* Max cert chain depth for ancestor walks. */
#define WOLFSSL_MAX_CHAIN_DEPTH 20
#endif
typedef struct DecodedName DecodedName;
typedef struct DecodedCert DecodedCert;
typedef struct Signer Signer;
@@ -2207,6 +2212,10 @@ struct Signer {
*/
WC_BITFIELD extNameConstraintCrit:1;
WC_BITFIELD extNameConstraintHasUnsupported:1;
#ifndef NO_SKID
WC_BITFIELD authKeyIdSet:1; /* true when authKeyIdHash holds the
* AKID extension's keyId */
#endif
#endif
const byte* publicKey;
int nameLen;
@@ -2218,15 +2227,20 @@ struct Signer {
#endif
byte subjectNameHash[SIGNER_DIGEST_SIZE];
/* sha hash of names in certificate */
#if defined(HAVE_OCSP) || defined(HAVE_CRL) || defined(WOLFSSL_AKID_NAME)
#if defined(HAVE_OCSP) || defined(HAVE_CRL) || defined(WOLFSSL_AKID_NAME) || \
!defined(IGNORE_NAME_CONSTRAINTS)
byte issuerNameHash[SIGNER_DIGEST_SIZE];
/* sha hash of issuer names in certificate.
* Used in OCSP to check for authorized
* responders. */
/* sha hash of issuer names; used by
* OCSP and name-constraint walk */
#endif
#ifndef NO_SKID
byte subjectKeyIdHash[SIGNER_DIGEST_SIZE];
/* sha hash of key in certificate */
#ifndef IGNORE_NAME_CONSTRAINTS
byte authKeyIdHash[SIGNER_DIGEST_SIZE];
/* sha hash of AKID; locates signer
* during name-constraint walk */
#endif
#endif
#ifdef HAVE_OCSP
byte subjectKeyHash[KEYID_SIZE];