Problem
The fix for GHSA-3jrg-j22w-mpmc rejects certificate chains whose CN is an ordinary human-readable name. A CN containing one internal dot and at least one space is classified as a DNS name and then hard-fails with X509_V_ERR_UNSUPPORTED_NAME_SYNTAX (53), where OpenSSL and aws-lc v1.70.0 verify the chain.
CN=ACME Corp. Issuing CA G2 is enough. It is a textbook intermediate-CA subject.
Mechanism
crypto/x509/v3_ncons.c, in cn2dnsid:
if (c >= 0x80 || c <= 0x20 || c == 0x7F) {
has_non_dns_char = 1;
continue; // note: does not clear isdnsname
}
Upstream OpenSSL has no such branch: any byte that is not alphanumeric, _, an internal - or a qualifying internal . reaches isdnsname = 0; break; and the CN is quietly skipped as "not a DNS name".
The character class here includes 0x20, the space. The comment above the resulting error describes the intent as "Multi-label CN with non-ASCII bytes or control characters", and a space is neither, so the <= looks like it is one off from what was meant. Because the branch continues without clearing isdnsname, a single qualifying internal dot latches the classification and nothing later can undo it.
Reproduction
Three-certificate chain, every name inside the permitted subtree, built with the OpenSSL CLI:
- root:
CN=Test Constrained CA, nameConstraints=critical,permitted;DNS:permitted.example
- intermediate:
CN=ACME Corp. Issuing CA G2, CA:TRUE,pathlen:0, no SAN
- leaf:
CN=www.permitted.example, subjectAltName=DNS:www.permitted.example
Same source compiled against both libraries:
=== AWS-LC ===
int=int.pem -> verify=0 err=53(unsupported or invalid name syntax) depth=1
int=int2.pem -> verify=1 err=0(ok) depth=0
=== OpenSSL 3.6.4 ===
int=int.pem -> verify=1 err=0(ok) depth=0
int=int2.pem -> verify=1 err=0(ok) depth=0
int2.pem is the same intermediate reissued as CN=ACME Corp Issuing CA G2, the full stop removed and nothing else changed. depth=1 names the intermediate.
Present in v1.71.0, v1.72.1 and v1.73.0; absent in v1.70.0, which matches PR #3108 as the origin.
Why I am raising it rather than sending a patch
PR #3108's call-out says:
Only certificates that were already in violation of their CA's constraints will be newly rejected.
A CN of Acme Corp. Inc under a CA constrained only on excluded;IP:10.0.0.0/255.0.0.0 is in violation of nothing, and it is newly rejected. Acme Corp Inc, one character different, is not.
The obvious edit is c < 0x20 instead of c <= 0x20, which excludes the space while keeping non-ASCII and control characters sticky. I built that and ran it: it clears the false rejects and keeps both halves of the advisory fix (the wildcard case still errors 47, the Unicode case still errors 53). But it also fails your own X509CompatTest.CommonNameToDNS:
crypto/x509/x509_compat_test.cc:2583: Failure
[ FAILED ] X509CompatTest.CommonNameToDNS
because the table at x509_compat_test.cc:2471 deliberately expects "foo .evil.com" to produce X509_V_ERR_UNSUPPORTED_NAME_SYNTAX. So a space was considered when the boundary was drawn, and changing it is a policy call about whether a multi-label CN containing a space should be treated as a malformed DNS name or as not a DNS name at all. That is yours to make, not mine to assume in a patch.
A second option, which I have not built: stop latching isdnsname once a disqualifying byte has been seen, so the classification is decided by the whole string rather than by whichever dot came first.
Scope
This is fail-closed. Valid chains are rejected; nothing invalid is accepted. There is no attacker role, since the offending string is a CA operator's own display name for their own intermediate. Raising it as a correctness and availability regression, not a security issue.
Problem
The fix for GHSA-3jrg-j22w-mpmc rejects certificate chains whose CN is an ordinary human-readable name. A CN containing one internal dot and at least one space is classified as a DNS name and then hard-fails with
X509_V_ERR_UNSUPPORTED_NAME_SYNTAX(53), where OpenSSL and aws-lc v1.70.0 verify the chain.CN=ACME Corp. Issuing CA G2is enough. It is a textbook intermediate-CA subject.Mechanism
crypto/x509/v3_ncons.c, incn2dnsid:Upstream OpenSSL has no such branch: any byte that is not alphanumeric,
_, an internal-or a qualifying internal.reachesisdnsname = 0; break;and the CN is quietly skipped as "not a DNS name".The character class here includes
0x20, the space. The comment above the resulting error describes the intent as "Multi-label CN with non-ASCII bytes or control characters", and a space is neither, so the<=looks like it is one off from what was meant. Because the branchcontinues without clearingisdnsname, a single qualifying internal dot latches the classification and nothing later can undo it.Reproduction
Three-certificate chain, every name inside the permitted subtree, built with the OpenSSL CLI:
CN=Test Constrained CA,nameConstraints=critical,permitted;DNS:permitted.exampleCN=ACME Corp. Issuing CA G2,CA:TRUE,pathlen:0, no SANCN=www.permitted.example,subjectAltName=DNS:www.permitted.exampleSame source compiled against both libraries:
int2.pemis the same intermediate reissued asCN=ACME Corp Issuing CA G2, the full stop removed and nothing else changed.depth=1names the intermediate.Present in v1.71.0, v1.72.1 and v1.73.0; absent in v1.70.0, which matches PR #3108 as the origin.
Why I am raising it rather than sending a patch
PR #3108's call-out says:
A CN of
Acme Corp. Incunder a CA constrained only onexcluded;IP:10.0.0.0/255.0.0.0is in violation of nothing, and it is newly rejected.Acme Corp Inc, one character different, is not.The obvious edit is
c < 0x20instead ofc <= 0x20, which excludes the space while keeping non-ASCII and control characters sticky. I built that and ran it: it clears the false rejects and keeps both halves of the advisory fix (the wildcard case still errors 47, the Unicode case still errors 53). But it also fails your ownX509CompatTest.CommonNameToDNS:because the table at
x509_compat_test.cc:2471deliberately expects"foo .evil.com"to produceX509_V_ERR_UNSUPPORTED_NAME_SYNTAX. So a space was considered when the boundary was drawn, and changing it is a policy call about whether a multi-label CN containing a space should be treated as a malformed DNS name or as not a DNS name at all. That is yours to make, not mine to assume in a patch.A second option, which I have not built: stop latching
isdnsnameonce a disqualifying byte has been seen, so the classification is decided by the whole string rather than by whichever dot came first.Scope
This is fail-closed. Valid chains are rejected; nothing invalid is accepted. There is no attacker role, since the offending string is a CA operator's own display name for their own intermediate. Raising it as a correctness and availability regression, not a security issue.