@@ -12139,6 +12139,8 @@ void FreeDecodedCert(DecodedCert* cert)
1213912139 FreeAltNames(cert->altEmailNames, cert->heap);
1214012140 if (cert->altDirNames)
1214112141 FreeAltNames(cert->altDirNames, cert->heap);
12142+ if (cert->altOtherNamesRaw)
12143+ FreeAltNames(cert->altOtherNamesRaw, cert->heap);
1214212144 if (cert->permittedNames)
1214312145 FreeNameSubtrees(cert->permittedNames, cert->heap);
1214412146 if (cert->excludedNames)
@@ -17622,6 +17624,19 @@ int wolfssl_local_MatchIpSubnet(const byte* ip, int ipSz,
1762217624 return match;
1762317625}
1762417626
17627+ /* RFC 5280 4.2.1.10: otherName matching is byte-exact comparison of the
17628+ * full OtherName encoding (OID || [0] EXPLICIT value). Both the leaf SAN
17629+ * (cert->altOtherNamesRaw) and the constraint subtree (Base_entry from
17630+ * DecodeSubtree) store the same form, so a memcmp suffices. */
17631+ static int MatchOtherNameConstraint(DNS_entry* name, Base_entry* current)
17632+ {
17633+ if (name == NULL || current == NULL)
17634+ return 0;
17635+ if (name->len != current->nameSz)
17636+ return 0;
17637+ return XMEMCMP(name->name, current->name, (size_t)current->nameSz) == 0;
17638+ }
17639+
1762517640/* Search through the list to find if the name is permitted.
1762617641 * name The DNS name to search for
1762717642 * dnsList The list to search through
@@ -17656,21 +17671,7 @@ static int PermittedListOk(DNS_entry* name, Base_entry* dnsList, byte nameType)
1765617671 }
1765717672 }
1765817673 else if (nameType == ASN_OTHER_TYPE) {
17659- /* RFC 5280 4.2.1.10: otherName matching is byte-exact
17660- * comparison of the full OtherName encoding. The FPKI/SEP
17661- * path also stores entries that contain only the parsed
17662- * UPN/FASCN value and have oidSum != 0; those are not
17663- * byte-comparable with the OID || [0] EXPLICIT value form
17664- * stored for the constraint, so we explicitly skip them.
17665- * Without that guard, a coincidental length match could
17666- * mis-validate. */
17667- if (
17668- #ifdef WOLFSSL_FPKI
17669- name->oidSum == 0 &&
17670- #endif
17671- name->len == current->nameSz &&
17672- XMEMCMP(name->name, current->name,
17673- (size_t)current->nameSz) == 0) {
17674+ if (MatchOtherNameConstraint(name, current)) {
1767417675 match = 1;
1767517676 break;
1767617677 }
@@ -17723,14 +17724,7 @@ static int IsInExcludedList(DNS_entry* name, Base_entry* dnsList, byte nameType)
1772317724 }
1772417725 }
1772517726 else if (nameType == ASN_OTHER_TYPE) {
17726- /* See note in PermittedListOk about byte-exact matching. */
17727- if (
17728- #ifdef WOLFSSL_FPKI
17729- name->oidSum == 0 &&
17730- #endif
17731- name->len == current->nameSz &&
17732- XMEMCMP(name->name, current->name,
17733- (size_t)current->nameSz) == 0) {
17727+ if (MatchOtherNameConstraint(name, current)) {
1773417728 ret = 1;
1773517729 break;
1773617730 }
@@ -17825,15 +17819,12 @@ static int ConfirmNameConstraints(Signer* signer, DecodedCert* cert)
1782517819 name = cert->altNames;
1782617820 break;
1782717821 case ASN_OTHER_TYPE:
17828- /* otherName SAN entries are stored on cert->altNames with
17829- * type ASN_OTHER_TYPE. Match by byte-exact comparison of
17830- * the OtherName encoding (OID || [0] EXPLICIT value). For
17831- * FPKI/SEP builds, altNames may also contain entries that
17832- * hold only the parsed UPN/FASCN value (oidSum != 0); the
17833- * explicit oidSum guard in IsInExcludedList /
17834- * PermittedListOk skips those so a coincidental length
17835- * match cannot mis-validate. */
17836- name = cert->altNames;
17822+ /* otherName SAN entries are stored on cert->altOtherNamesRaw
17823+ * (kept separate from altNames so the public altNames view
17824+ * is unaffected). Each entry holds the raw OtherName
17825+ * encoding (OID || [0] EXPLICIT value) and is byte-matched
17826+ * against the issuing CA's subtree. */
17827+ name = cert->altOtherNamesRaw;
1783717828 break;
1783817829 default:
1783917830 return 0;
@@ -18192,37 +18183,37 @@ static int DecodeGeneralName(const byte* input, word32* inOutIdx, byte tag,
1819218183 }
1819318184 #endif /* WOLFSSL_RID_ALT_NAME */
1819418185#endif /* IGNORE_NAME_CONSTRAINTS */
18195- #if defined(WOLFSSL_SEP) || defined(WOLFSSL_FPKI)
18196- /* GeneralName choice: otherName */
18186+ #ifndef IGNORE_NAME_CONSTRAINTS
18187+ /* GeneralName choice: otherName.
18188+ * Store the raw OtherName encoding (OID || [0] EXPLICIT value) on a
18189+ * dedicated internal list so ConfirmNameConstraints() can byte-match
18190+ * it against the issuing CA's nameConstraints subtree (RFC 5280
18191+ * 4.2.1.10). The raw form is kept separate from cert->altNames so
18192+ * the public altNames view (used by OpenSSL-compat APIs) reflects
18193+ * exactly what the SAN extension carries. */
1819718194 else if (tag == (ASN_CONTEXT_SPECIFIC | ASN_CONSTRUCTED | ASN_OTHER_TYPE)) {
18198- /* TODO: test data for code path */
18199- #ifndef IGNORE_NAME_CONSTRAINTS
18200- /* Store the raw OtherName encoding so ConfirmNameConstraints() can
18201- * byte-match it against the issuing CA's subtree (RFC 5280
18202- * 4.2.1.10). DecodeOtherName() may also add an entry that holds
18203- * only the parsed UPN/FASCN value with oidSum != 0; the explicit
18204- * oidSum guard in IsInExcludedList()/PermittedListOk() ensures
18205- * those parsed entries are skipped during byte-comparison. */
1820618195 ret = SetDNSEntry(cert->heap, (const char*)(input + idx), len,
18207- ASN_OTHER_TYPE, &cert->altNames );
18196+ ASN_OTHER_TYPE, &cert->altOtherNamesRaw );
1820818197 if (ret != 0) {
1820918198 return ret;
1821018199 }
18211- #endif
18200+ #if defined(WOLFSSL_SEP) || defined(WOLFSSL_FPKI)
18201+ /* FPKI/SEP also OID-decode the otherName into a separate altNames
18202+ * entry that holds the parsed UPN/FASCN value (with oidSum != 0).
18203+ * That parsed entry is consumed by wc_GetUUIDFromCert /
18204+ * wc_GetFASCNFromCert; ConfirmNameConstraints() does not look at
18205+ * it - it iterates altOtherNamesRaw instead. */
1821218206 ret = DecodeOtherName(cert, input, &idx, len);
18207+ #else
18208+ idx += (word32)len;
18209+ #endif
1821318210 }
18214- #elif !defined(IGNORE_NAME_CONSTRAINTS)
18215- /* GeneralName choice: otherName.
18216- * No OID-specific decoding in this build, but we store the raw
18217- * OtherName encoding (OID || [0] EXPLICIT value) on altNames so
18218- * ConfirmNameConstraints() can byte-match it against the issuing CA's
18219- * nameConstraints subtree (RFC 5280 4.2.1.10). */
18211+ #elif defined(WOLFSSL_SEP) || defined(WOLFSSL_FPKI)
18212+ /* No name constraints support in the build, but FPKI/SEP still need
18213+ * the parsed otherName entry for wc_GetUUIDFromCert /
18214+ * wc_GetFASCNFromCert. */
1822018215 else if (tag == (ASN_CONTEXT_SPECIFIC | ASN_CONSTRUCTED | ASN_OTHER_TYPE)) {
18221- ret = SetDNSEntry(cert->heap, (const char*)(input + idx), len,
18222- ASN_OTHER_TYPE, &cert->altNames);
18223- if (ret == 0) {
18224- idx += (word32)len;
18225- }
18216+ ret = DecodeOtherName(cert, input, &idx, len);
1822618217 }
1822718218#endif
1822818219 /* GeneralName choice: dNSName, x400Address, ediPartyName */
0 commit comments