- "details": "There is a certificate revocation enforcement bug in `rustls-webpki` CRL processing. when both the certificate CRL distribution point and the CRL issuing distribution point contain multiple URI names, `IssuingDistributionPoint::authoritative_for()` reuses one-shot DER iterators across nested comparisons. If the only matching URI pair appears later in both sequences, the implementation misses the match, treats the CRL as non-authoritative, and under `UnknownStatusPolicy::Allow` accepts a revoked certificate.\n\n## affected versions\n\nrevocation support shipped in `0.104.0-alpha.4`, and the same iterator-reuse logic is still present at commit `e4590782afc1207c3e46ba1249e7c3fb9da95198` on 2026-03-20. i did not identify an upstream fix as of that date.\n\n## affected component\n\n- component: CRL authority matching for certificate DP vs CRL IDP names\n- repo pin: `https://github.com/rustls/webpki` @ `e4590782afc1207c3e46ba1249e7c3fb9da95198`\n- callsite: `src/crl/types.rs:626`, `src/crl/types.rs:649`, `src/crl/types.rs:664`\n- pinned source: [https://github.com/rustls/webpki/blob/e4590782afc1207c3e46ba1249e7c3fb9da95198/src/crl/types.rs#L626-L678](https://github.com/rustls/webpki/blob/e4590782afc1207c3e46ba1249e7c3fb9da95198/src/crl/types.rs#L626-L678)\n\n## technical details\n\nRFC 5280 section 5.2.5 and section 6.3.3(b)(2)(i) require the verifier to check whether one of the names in the IDP matches one of the names in the certificate DP. the current implementation contradicts that requirement in the later-match case because iterator state, not the actual set of URI names, determines whether later names are considered.\n\nthe attached PoC uses a single trust anchor and a single relevant CRL path. there is no alternate unconstrained root or duplicate trust path for the same key material, so the acceptance result is attributable to the DP/IDP matching bug rather than chain-builder fallback.\n\nthe vulnerable flow is:\n\n1. `authoritative_for()` parses `idp_general_names` once before iterating certificate distribution points.\n2. each certificate DP fullName is parsed into another one-shot `DerIterator`.\n3. `uri_name_in_common()` iterates the first IDP URI and drains the DP iterator while looking for a match.\n4. if the matching URI pair is second in both lists, it is never compared.\n5. the CRL is treated as non-authoritative, and `UnknownStatusPolicy::Allow` converts that into a successful verification result for a revoked certificate.\n\nthe repository already has a single-URI happy-path revocation test, which demonstrates the expected rejection path when the first URI matches. the attached PoC shows that simply moving the valid match to second position flips the result from `Err(CertRevoked)` to `Ok(())` under the permissive status policy.\n\n## steps to reproduce\n\nthe attached `poc.zip` contains a cargo-based integration harness. after extracting it, run `make canonical` to produce the vulnerable result and `make control` for the negative controls.\n\n```bash\nunzip poc.zip -d poc\ncd poc\nmake canonical\nmake control\n```\n\nthe canonical run emits:\n\n```text\n[CALLSITE_HIT]: authoritative_for::uri_name_in_common later_uri_pair_skipped=true\n[PROOF_MARKER]: vuln_case=Ok(()) first_uri_control=Err(CertRevoked) later_match_position=second allow_unknown=true\n[IMPACT_MARKER]: revoked_cert_accepted=true policy=UnknownStatusPolicy::Allow\n```\n\nthe control run emits:\n\n```text\n[CALLSITE_HIT]: authoritative_for::uri_name_in_common control_path=true\n[NC_MARKER]: first_uri_control=Err(CertRevoked) deny_policy=Err(UnknownRevocationStatus) vuln_blocked=true\n```\n\n## recommended fix\n\navoid reusing exhausted DER iterators across nested DP/IDP comparisons. a minimal fix is to reparse or snapshot the URI name sets for each comparison so that every IDP URI can be compared against the full DP URI set. a regression test should cover the case where the only valid URI match is later in both sequences.\n\ncheers,\nOleh Konko",
0 commit comments