URI name constraints accept an IP-literal authority with excludedSubtrees only
Summary
When URI name constraints are active, OpenSSL accepts a leaf certificate whose URI subjectAltName has an IP-literal authority if the issuing CA contains only an excluded URI subtree. For example, a CA constrained with excluded;URI:bad.example.com accepts a leaf containing URI:https://192.0.2.1/a.
RFC 5280 section 4.2.1.10 requires rejection when URI constraints apply and the URI authority does not contain a DNS name, including when its host is an IP address. A corresponding permitted-subtree control is rejected, so the result depends on whether the active constraint is permitted or excluded.
The OpenSSL security team reviewed my private report and asked me to file this as a regular public bug.
Versions tested
- OpenSSL 4.0.2 (Homebrew
openssl@4bottle) - OpenSSL 3.6.3 (Homebrew
openssl@3bottle) - macOS on arm64
As of 2026-09-16, OpenSSL 4.0.2 was the latest stable OpenSSL release. The complete five-case script was run against both versions and produced the same results.
Reproduction
The attached repro.sh uses only the OpenSSL command-line application. It creates a disposable root, two constrained intermediate CAs, and five leaf certificates, then calls openssl verify with explicit trust inputs and the default trust stores disabled.
Run it from an empty directory:
chmod +x repro.sh
OPENSSL_BIN=/path/to/openssl ./repro.shObserved results:
| Case | Expected | OpenSSL 3.6.3 | OpenSSL 4.0.2 |
|---|---|---|---|
| Excluded-only, no URI SAN | Accept | Accept | Accept |
| Excluded-only, unrelated DNS authority | Accept | Accept | Accept |
| Excluded-only, excluded DNS authority | Reject | Reject | Reject |
| Excluded-only, IP-literal authority | Reject | Accept | Accept |
| Permitted-only, IP-literal authority | Reject | Reject | Reject |
Expected behavior
The excluded-only IP-literal case should be rejected. RFC 5280 requires certificate rejection when URI constraints apply and a URI authority uses an IP address instead of a DNS name:
https://www.rfc-editor.org/rfc/rfc5280.html#section-4.2.1.10
Source-level explanation
nc_uri() parses https://192.0.2.1/a, receives 192.0.2.1 as the host, and compares that text with the DNS constraint. Because it does not match bad.example.com, the function returns X509_V_ERR_PERMITTED_VIOLATION:
https://github.com/openssl/openssl/blob/openssl-4.0.2/crypto/x509/v3_ncons.c#L779-L833
nc_match() uses this return value differently for the two subtree lists. The permitted-subtree loop ultimately rejects when no subtree matches. The excluded-subtree loop treats X509_V_ERR_PERMITTED_VIOLATION as an ordinary non-match and continues, so the chain is accepted:
https://github.com/openssl/openssl/blob/openssl-4.0.2/crypto/x509/v3_ncons.c#L503-L562
The likely fix is to classify an IP-literal URI authority as unsupported name syntax before comparing it with a URI subtree, rather than representing it as an ordinary DNS-subtree mismatch.
Related issue and scope
Issue #31686 concerns the same nc_uri() path but a different input and source mechanism: embedded-NUL truncation. This report concerns the mandatory classification of an IP-literal URI authority.
The demonstrated consequence is incorrect certificate-path acceptance in a specialized or private PKI using URI name constraints. As the security team noted during triage, practical exploitation would require control of a certificate authority. I have not demonstrated a WebPKI hostname-validation bypass, a deployed exploit, or any particular severity.
AI-assistance disclosure
Codex and Claude Code assisted with the research workflow, test design, source review, and early drafting. I independently executed the reproductions and reviewed the technical claims before preparing this report. No agent submitted, edited, or commented on anything in the OpenSSL GitHub repository.
Source: openssl/openssl