Verify and validate certificate policies extensions#232
Verify and validate certificate policies extensions#232kikuomax wants to merge 6 commits intorustls:mainfrom
Conversation
|
Thanks for the PR, I appreciate the detailed description 👍 I think you have some CI failures to sort out for the builds that activate the new feature flag.
Quickly looking at the diff I'm not sure I'm convinced yet this should be included in webpki, even behind a feature flag. It looks like the meat of the policy validation is integrated by way of a fn to be called as a I'm generally nervous about wading into the policy space. It's pretty gross and I think there aren't many great examples to validate against (e.g. I think Go's stdlib avoids doing anything other than representing policies in a cert). It's a big chunk of new code and complexity.
It seems like the algorithm in 5280 is outright dangerous (see draft-ietf-lamps-x509-policy-graph) which is another reason to be wary of this entire area IMO 😬 |
|
@cpu Thank you for your reply.
Sorry, I have not tested building with the latest Rust.
Separating the policy validation in a different crate makes sense as it is highly application specific.
Thanks for guiding me to the draft. I will look into it. |
@cpu I found my branch did not include the commit that fixes some clippy issues in the nightly build. I will rebase and force push my branch to sync |
- `Cert` introduces a new field `certificate_policies` for an optional "Certificate Policies" extension (RFC 5280 4.2.1.4). - `remember_cert_extension` remembers the extension data in `Cert::certificate_policies`. - Introduces a new test case `win_hello_attest_tpm` that tests `verify_for_usage` with certificates including a critical non-any certificate policy extension. The test fixtures were taken from `core::tests::win_hello_attest_tpm` of `kanidm/webauthn-rs` repository.
A new module `cert_policy` which provides structs, enums, functions, and
constants for certificate policy verification and validation. There are
two important functions:
- `check_certificate_policies`: verifies if the structure of individual
certificate policies extensions in a given certificate paths conform
to the definition in RFC 5280 Section 4.2.1.4.
`verify_cert::build_chain` calls this to verify certificate policies.
This function prevents invalid certificate policy extensions from
being accepted.
- `validate_policy_tree_paths`: validates if at least one of given
certificate policy IDs (`user-initial-policy-set`) is valid in the
certificate policy tree built from a specified certificate path.
Unlike RFC 5280 Section 6.1, this function does not build the entire
policy tree but traverses the virtual policy tree in a depth-first
manner to check validity of given policy IDs one-by-one. If an
application meets all of the following conditions, what this function
can validate is equivalent to that provided by the algorithm described
in RFC 5280:
- the entire policy tree structure is unnecessary
- at least one policy must be valid in the policy tree; i.e.,
`initial-explicity-policy` is set
- anyPolicy is always allowed; i.e., `initial-any-policy-inhibit` is
unset
- policy qualifiers are unncessary
- policy mapping extension is not supported
- policy constraints extension is not supported
- inhibit anyPolicy extension is not supported
This function is not automatically called, but applications have to
call it in `verify_path` callback of `EndEntity::verify_for_usage`.
Because necessity of policy tree validation is up to applications.
`cert` module includes the following changes:
- `build_chain_inner` calls `check_certificate_policies` when it reaches
a trust anchor
`error` module includes the following changes:
- new `Error` variants:
- `InvalidPolicyTree`: returned by `validate_policy_tree_paths` when
the policy tree is invalid. priority = 195
- `EmptyCertificatePolicies`: returned by `check_certificate_policies`
when certificate policies extension is empty. priority = 157
- `DuplicateCertificatePolicyOid`: returned by
`check_certificate_policies` when policy IDs duplicate in
a certificate policies extension. priority = 155
- `UnknownPolicyQualifier`: returned by may be returned by
`check_certificate_policies` when unknown policy qualifier is
included in a certificate policies extension. priority = 152
- new `DerTypeId` variants for components of certificate policies
extension structure: `CertificatePolicy`, `CPSuri`, `DisplayText`,
`NoticeReference`, `PolicyQualifierInfo`, and `UserNotice`
In `der` module, `Tag` has a new variant `IA5String` (=0x16).
`lib` exports `cert_policy::validate_policy_tree_paths` so that
applications can call it from `verify_path` callback.
`Cargo.toml` includes `src/cert_policy.rs`.
`win_hello_attest_tpm` includes tests that validate the policy tree with `validate_policy_tree_paths`. A new test case `netflix_invalid_policy_tree` that tries to validate the policy tree of netflix's cert chain. This is interesting, because netflix's certs contain non-critical certificate policies extensions, and we end up with a NULL policy tree built from the cert chain.
`cert_policy` is optionally provided if the feature `cert_policy` is included, because additional computation and binary size may not be tolerated.
`validate_policy_tree_paths` is removed from this crate because requirements for policy tree validation are very specific to applications. Instead, `cert_policy` module itself, and its types, methods and functions necessary for applications to implement custom policy validation become public. Additionally, `verify_cert::IntermediateIterator` is also exposed. The feature table in the documentation includes `cert_policy`.
927edee to
c868871
Compare
I found that `verify_cert::IntermediateIterator` is not necessary to be exported to applications.
|
@cpu I removed I also added a new row for |
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #232 +/- ##
==========================================
- Coverage 97.14% 97.03% -0.11%
==========================================
Files 19 20 +1
Lines 4336 5166 +830
==========================================
+ Hits 4212 5013 +801
- Misses 124 153 +29 ☔ View full report in Codecov by Sentry. |
| /// the valid policy tree path that our procedure finds is vitually included in | ||
| /// the intersection of the *valid_policy_tree* and the | ||
| /// *user-initial-policy-set*. | ||
| pub fn validate_policy_tree_paths( |
There was a problem hiding this comment.
I'm not sure what @cpu had in mind, but adding all of this code is definitely not what I had in mind when I was reading @cpu's suggestion to minimize the scope of the code added to this crate. Instead, I think we should keep your previous commit (that stores the policy extension) and make minimal extensions that exposes that information to downstream crates. Given the state as summarized previously, I'm not sure it makes sense for this crate's maintainers to maintain all this code.
There was a problem hiding this comment.
Instead, I think we should keep your previous commit (that stores the policy extension) and make minimal extensions that exposes that information to downstream crates.
That matches what I was thinking as well. Thanks for making it explicit 👍
There was a problem hiding this comment.
The function validate_policy_tree_path has already been removed from this PR. cert_policy.rs is still huge, though.
check_certificate_policies function might be another concern. The other part is just the structural definition of Certificate Policies Extension and tests.
There was a problem hiding this comment.
I think our point is that we don't want any of this code to live in the rustls-webpki crate. As long as the caller is able to effectively get to a &[u8] representing the policy extension's contents, it seems like everything else should be able to live in other crates.
There was a problem hiding this comment.
I see. Now I understand my "previous commit" means the one I made in PR #211. But it would let certificates containing malformed certificate policies extensions marked as critical pass. I think that was the reason we rejected PR#211.
There was a problem hiding this comment.
Okay, that's fair. @ctz what do you think, should we:
- Accept parsing code like in this PR to ensure we don't ignore invalid policies
- Continue to not process/remember policies at all
- Some middle ground where we do high-level parsing of the policy extension, but not in too much depth
- Some other solution, perhaps involving configuration for another optional callback/trait
There was a problem hiding this comment.
My concern is that we continue to reject critical extensions if we don't fully and unconditionally implement the constraints they specify. That is what critical means.
So solutions acceptable to me would bind together the acceptance of critical extensions with the validation and enforcement of their contents.
But I think a better overall solution is to use one of the more general certificate verification crates, rather than extending one that is designed to be specific to the webpki.
There was a problem hiding this comment.
But I think a better overall solution is to use one of the more general certificate verification crates, rather than extending one that is designed to be specific to the webpki.
@ctz Do you have any recommended crates to look into? I briefly searched for some crates with keywords like x509 and pkix:
x509-parser:- does not seem to expose any method to specifically parse certificate policies extensions
- lacks types for policy qualifiers
x509-cert:- seems to expose the way to parse most of the necessary types but lacks
DisplayTextvariants forVisibleStringandBMPStringwhich I faced in the test cases I added
- seems to expose the way to parse most of the necessary types but lacks
pkix:- seems to expose the way to parse all the necessary types
rasn-pkix:- similar to
pkix
- similar to
There was a problem hiding this comment.
x509-parser
...
I would be open to reviewing a PR on x509-parser adding the required methods/types. I can't promise the primary maintainer would be receptive but I suspect it fits within the vision of the crate.
|
Sorry for letting this sit for a long while.
We discussed this a bit more in Discord (conversation link) back in Feb and it sounded like we all thought the approach Go's stdlib uses was sensible. Pulling out a couple quotes from that coversation:
I don't think any of the core maintainers have availability to implement this strategy, but I think we would be supportive of a PR matching CONTRIBUTING.md guidelines that explored bringing this model to the webpki crate. As mentioned I'm also willing to support upstream changes in x509-parser if they would be helpful to you. Thanks again for the contribution. |
Summary
Deal with certificate policies extensions so that rustls-webpki won't fail when it faces a critical certificate policies extensions.
With this PR:
rustls-webpkino longer rejects certificates with critical certificate policies extensions as long as they conform to the definition described in RFC 5280, Section 4.2.1.4.rustls-webpkiprovides a utility function that applications can call to validate the policy tree.rustls-webpkiintroduces optional featurecert_policythat enables the above functionalitiesHow verification works
rustls-webpkifirst remembers a certificate policies extension incert::Cert::certificate_policies.rustls-webpkiverifies the structure of the remembered certificate policies extension by callingcert_policy::check_certificate_policiesfromverify_cert::ChainOptions::build_chain_innerwhen it reaches a trust anchor.cert_policy::check_certificate_policiesverifies:cert_policy::check_certificate_policiesdoes not validate the policy tree.How validation works
Applications can call
cert_policy::validate_policy_tree_pathsfunction, exported asvalidate_policy_tree_paths, inverify_pathcallback ofEndEntityCert::verify_for_usageto validate the policy tree.Unlike the algorithm described in RFC 5280, Section 6.1,
validate_policy_tree_pathsdoes not build the entire policy tree but performs depth-first search. As long as requirements of applications meet all of the following conditions, whatvalidate_policy_tree_pathsvalidates should be equivalent to what they can do with the full policy tree:You can find justification of my approach in the comments of
cert_policy::validate_policy_tree_paths. I also excerpted it as a gist.Why optional?
cert_policyfeature is optional because additional computation and binary size may not be tolerated.