Skip to content

Add cross-sign configuration for CA name tests#987

Merged
christopher-henderson merged 2 commits intozmap:masterfrom
mhyder13:cross-sign-configure
Sep 1, 2025
Merged

Add cross-sign configuration for CA name tests#987
christopher-henderson merged 2 commits intozmap:masterfrom
mhyder13:cross-sign-configure

Conversation

@mhyder13
Copy link
Copy Markdown
Contributor

Notes

This adds a new source level configuration to allow the user to specify that the input is a "Cross-Certified Subordinate CA Certificate" under the profile in § 7.1.2.2 to support proper evaluation of the subjects of those certificates. Unlike all the other CA profiles, these certificates are exempt from the CA naming rules in § 7.1.2.10.2 and instead have their own naming rules in § 7.1.2.2.2 that require only that they be byte-for-byte identical to the subject of the previous CA certificate. The only restriction is that the previous certificate must have been "issued in compliance with the then-current version of the Baseline Requirements." There's no way for zlint to check this or what naming requirements existed at the time, so instead it treats the CA naming rules as not applicable.

The reason this configuration is required is that there is no way to detect a cross-signed CA from the certificate itself. The only way to determine this would be a global search for matching CA certificates, which is clearly outside the scope of a static analysis tool like zlint. Instead, this allows the user (which might be the CA organization which issued it) to specify which rule set should apply based on outside context.

  • Added a new source level configuration for the (TLS) Baseline Requirements to allow the user to specify that the input is a "Cross-Certified Subordinate CA Certificate" under the profile in § 7.1.2.2
    • This is not a global config: I've limited the scope to only the TLS BRs where this concept comes from.
    • I made this a boolean because TOML and the zlint configuration system don't really support enumerated types or another similar way to specific which profile applied. Each lint independently evaluates the configuration based on which parts it wants to consider, so enforcing consistency in a string specifying the profile would be difficult.
    • This is the first source-level config to be added to zlint, but this applies to at least these 4 lints and probably at least two more which have this configuration at the individual level and could probably use to be set in a single place. This profile also ignores the normal rules for the certificatePolicies and extendedKeyUsage extensions and the encoding rules for name types, so it's likely it will be used in more places in the future.
  • Removed a redundant entry for CABFBaselineRequirementsConfig from the global config list.
    • This looks like a bug that was included in the original configuration system commits that was being covered by the deduplication logic of the global configs and the fact that nothing was actually using this feature yet.
  • Added new logic to e_ca_common_name_missing, e_ca_country_name_invalid, e_ca_country_name_missing, and e_ca_organization_name_missing to exempt CA certificates where the user indicates that they are cross-signed CAs
    • I think it makes more sense to mark these as NA than PASS because they don't actually pass the checks, the section those checks are based on just doesn't apply to them, similar to how it doesn't apply to subscriber certificates.
  • Updated the comments in that affected lints to indicate that cross-signed CA cert are now supported via a config setting and making note of the limitation on checking whether the certificates we issued during the period where such certs were/are allowed.
    • As noted in the comment, there's no way to tell when the certs were actually signed because CAs are permitted to back-date the notBefore field.
    • I decided against trying to add any way to specify when a certificate was actually issued because it would either require a massive change across zlint in how it processes issuance time, or else would be misleading/confusing in what it actually applies to. The limited value gained here doesn't seem to justify a change of that scale.
    • This is a two-way door that shouldn't constrain future development either way.
  • Added some extra testing to the countryName lints, including a test where countryName is actually missing from the subject
    • For some reason, the name missing lint was being tested on a certificate that had a countryName in its subject sequence but where the name was an empty string. This seems more like an invalid name than a missing name to me so I've added a new test case that tests a certificate that actually has no countryName in its subject. I've also added the blank name cert as a test case to the e_ca_country_name_invalid test cases as an empty string is clearly an invalid name.
    • These tests aren't related to the rest of the change, but I wanted a more clearly appropriate certificate for the new tests I was adding for e_ca_country_name_missing anyway, so I included them here.

Testing

  • Updated the unit tests for the global config printer
  • Updated tests for each of the affected lints with three test cases:
    • The config is present but set to false when the CA cert fails the test
    • The config is present and set to true which makes the test not applicable when it would otherwise fail
    • The config is present and set to true which makes the test not applicable when it would otherwise pass
    • The cases where the config is not present are retained from before this change.
  • Added a new test certificate which is actually missing a RelativeDistinguishedName with a countryName AttributeTypeAndValue in the subject sequence because this is closer to what e_ca_country_name_missing is intended to test for
    • (It was modified from the existing test certificate, so the values are from that.)

Document References

(Direct section fragment links work in Chrome, YMMV.)

  • I developed these updates based on the then-current version 2.1.6, but 2.1.7 (released yesterday) doesn't make any changes to the certificate profiles and should not affect the correctness of these changes.
  • See "Cross-Certified Subordinate CA Certificate Profile" in § 7.1.2.2
  • See also, "Cross-Certified Subordinate CA Naming" in § 7.1.2.2.2
  • The normal CA naming rules are in § 7.1.2.10.2

Related Items

Copy link
Copy Markdown
Member

@christopher-henderson christopher-henderson left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a great use of the configuration framework!

And thank you for the extremely thorough due diligence and explanation. It is a refreshing change of pace for me as, in my duties elsewhere, I have grown numb to change lists which feature completely empty PR bodies and zero code comments or rationale.

// out a TOML document that is the full default configuration for ZLint.
var defaultGlobals = []GlobalConfiguration{
&Global{},
&CABFBaselineRequirementsConfig{},
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🙏 thank you for the inline fix!

@christopher-henderson christopher-henderson merged commit 81bb184 into zmap:master Sep 1, 2025
4 checks passed
@mhyder13 mhyder13 deleted the cross-sign-configure branch September 1, 2025 22:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Some CA subject lints improperly handle cross-signed CAs

2 participants