Skip to content

Commit ac17bb6

Browse files
rolandshoemakergopherbot
authored andcommitted
crypto/x509: properly apply name constrains to roots and intermediates
Name constraints are checked during path building. When a new certificate is considered for inclusion in a chain we check if it has name constraints, and if it does, check that they apply to the certs already in the chain, discarding it if the current chain violates any of the constraints the candidate introduces. This check was not acting as intended in two ways. The first was that we only checked that the constraints on the candidate certificate applied to the leaf certificate, and not the rest of the certiifcates in the chain. This was the intended behavior pre-1.19, but in 1.19 we intended for the constraints to be applied to the entire chain (although obviously they were not). The second was that we checked that the candidates constraints applied to the candidate itself. This is not conformant with RFC 5280, which says that during path building the constraint should only be applied to the certificates which follow the certificate which introduces the constraint (e.g. in the chain A -> B -> C, if certificate Bcontains a name constraint, the constraint should only apply to certificate C). The intended behavior introduced in 1.19 was mainly intended to reject dubious chains which the WebPKI disallows, and are relatively rare, but don't have significant security impact. Since the constraints were properly applied to the leaf certificate, there should be no real impact to the majority of users. Fixes #59171 Change-Id: Ie6def55b8ab7f14d6ed2c09351f664e148a4160d Reviewed-on: https://go-review.googlesource.com/c/go/+/478216 Reviewed-by: Damien Neil <[email protected]> Auto-Submit: Roland Shoemaker <[email protected]> Run-TryBot: Roland Shoemaker <[email protected]> TryBot-Result: Gopher Robot <[email protected]>
1 parent 4042b90 commit ac17bb6

File tree

2 files changed

+86
-17
lines changed

2 files changed

+86
-17
lines changed

src/crypto/x509/verify.go

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -591,22 +591,19 @@ func (c *Certificate) isValid(certType int, currentChain []*Certificate, opts *V
591591
}
592592
comparisonCount := 0
593593

594-
var leaf *Certificate
595594
if certType == intermediateCertificate || certType == rootCertificate {
596595
if len(currentChain) == 0 {
597596
return errors.New("x509: internal error: empty chain when appending CA cert")
598597
}
599-
leaf = currentChain[0]
600598
}
601599

602600
if (certType == intermediateCertificate || certType == rootCertificate) &&
603601
c.hasNameConstraints() {
604602
toCheck := []*Certificate{}
605-
if leaf.hasSANExtension() {
606-
toCheck = append(toCheck, leaf)
607-
}
608-
if c.hasSANExtension() {
609-
toCheck = append(toCheck, c)
603+
for _, c := range currentChain {
604+
if c.hasSANExtension() {
605+
toCheck = append(toCheck, c)
606+
}
610607
}
611608
for _, sanCert := range toCheck {
612609
err := forEachSAN(sanCert.getSANExtension(), func(tag int, data []byte) error {

src/crypto/x509/verify_test.go

Lines changed: 82 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1921,8 +1921,13 @@ type trustGraphEdge struct {
19211921
MutateTemplate func(*Certificate)
19221922
}
19231923

1924+
type rootDescription struct {
1925+
Subject string
1926+
MutateTemplate func(*Certificate)
1927+
}
1928+
19241929
type trustGraphDescription struct {
1925-
Roots []string
1930+
Roots []rootDescription
19261931
Leaf string
19271932
Graph []trustGraphEdge
19281933
}
@@ -1977,10 +1982,10 @@ func buildTrustGraph(t *testing.T, d trustGraphDescription) (*CertPool, *CertPoo
19771982
if err != nil {
19781983
t.Fatalf("failed to generate test key: %s", err)
19791984
}
1980-
root := genCertEdge(t, r, k, nil, rootCertificate, nil, nil)
1985+
root := genCertEdge(t, r.Subject, k, r.MutateTemplate, rootCertificate, nil, nil)
19811986
roots = append(roots, root)
1982-
certs[r] = root
1983-
keys[r] = k
1987+
certs[r.Subject] = root
1988+
keys[r.Subject] = k
19841989
}
19851990

19861991
intermediates := []*Certificate{}
@@ -2072,7 +2077,7 @@ func TestPathBuilding(t *testing.T) {
20722077
// +----+
20732078
name: "bad EKU",
20742079
graph: trustGraphDescription{
2075-
Roots: []string{"root"},
2080+
Roots: []rootDescription{{Subject: "root"}},
20762081
Leaf: "leaf",
20772082
Graph: []trustGraphEdge{
20782083
{
@@ -2148,7 +2153,7 @@ func TestPathBuilding(t *testing.T) {
21482153
// +----+
21492154
name: "bad EKU",
21502155
graph: trustGraphDescription{
2151-
Roots: []string{"root"},
2156+
Roots: []rootDescription{{Subject: "root"}},
21522157
Leaf: "leaf",
21532158
Graph: []trustGraphEdge{
21542159
{
@@ -2230,7 +2235,7 @@ func TestPathBuilding(t *testing.T) {
22302235
// +----+
22312236
name: "all paths",
22322237
graph: trustGraphDescription{
2233-
Roots: []string{"root"},
2238+
Roots: []rootDescription{{Subject: "root"}},
22342239
Leaf: "leaf",
22352240
Graph: []trustGraphEdge{
22362241
{
@@ -2294,7 +2299,7 @@ func TestPathBuilding(t *testing.T) {
22942299
// +----+
22952300
name: "ignore cross-sig loops",
22962301
graph: trustGraphDescription{
2297-
Roots: []string{"root"},
2302+
Roots: []rootDescription{{Subject: "root"}},
22982303
Leaf: "leaf",
22992304
Graph: []trustGraphEdge{
23002305
{
@@ -2347,7 +2352,7 @@ func TestPathBuilding(t *testing.T) {
23472352
// the leaf has SANs.
23482353
name: "leaf with same subject, key, as parent but with SAN",
23492354
graph: trustGraphDescription{
2350-
Roots: []string{"root"},
2355+
Roots: []rootDescription{{Subject: "root"}},
23512356
Leaf: "root",
23522357
Graph: []trustGraphEdge{
23532358
{
@@ -2369,7 +2374,7 @@ func TestPathBuilding(t *testing.T) {
23692374
// through C should be ignored, because it has invalid EKU nesting.
23702375
name: "ignore invalid EKU path",
23712376
graph: trustGraphDescription{
2372-
Roots: []string{"root"},
2377+
Roots: []rootDescription{{Subject: "root"}},
23732378
Leaf: "leaf",
23742379
Graph: []trustGraphEdge{
23752380
{
@@ -2412,6 +2417,70 @@ func TestPathBuilding(t *testing.T) {
24122417
"CN=leaf -> CN=inter b -> CN=inter a -> CN=root",
24132418
},
24142419
},
2420+
{
2421+
// A name constraint on the root should apply to any names that appear
2422+
// on the intermediate, meaning there is no valid chain.
2423+
name: "contrained root, invalid intermediate",
2424+
graph: trustGraphDescription{
2425+
Roots: []rootDescription{
2426+
{
2427+
Subject: "root",
2428+
MutateTemplate: func(t *Certificate) {
2429+
t.PermittedDNSDomains = []string{"example.com"}
2430+
},
2431+
},
2432+
},
2433+
Leaf: "leaf",
2434+
Graph: []trustGraphEdge{
2435+
{
2436+
Issuer: "root",
2437+
Subject: "inter",
2438+
Type: intermediateCertificate,
2439+
MutateTemplate: func(t *Certificate) {
2440+
t.DNSNames = []string{"beep.com"}
2441+
},
2442+
},
2443+
{
2444+
Issuer: "inter",
2445+
Subject: "leaf",
2446+
Type: leafCertificate,
2447+
MutateTemplate: func(t *Certificate) {
2448+
t.DNSNames = []string{"www.example.com"}
2449+
},
2450+
},
2451+
},
2452+
},
2453+
expectedErr: "x509: a root or intermediate certificate is not authorized to sign for this name: DNS name \"beep.com\" is not permitted by any constraint",
2454+
},
2455+
{
2456+
// A name constraint on the intermediate does not apply to the intermediate
2457+
// itself, so this is a valid chain.
2458+
name: "contrained intermediate, non-matching SAN",
2459+
graph: trustGraphDescription{
2460+
Roots: []rootDescription{{Subject: "root"}},
2461+
Leaf: "leaf",
2462+
Graph: []trustGraphEdge{
2463+
{
2464+
Issuer: "root",
2465+
Subject: "inter",
2466+
Type: intermediateCertificate,
2467+
MutateTemplate: func(t *Certificate) {
2468+
t.DNSNames = []string{"beep.com"}
2469+
t.PermittedDNSDomains = []string{"example.com"}
2470+
},
2471+
},
2472+
{
2473+
Issuer: "inter",
2474+
Subject: "leaf",
2475+
Type: leafCertificate,
2476+
MutateTemplate: func(t *Certificate) {
2477+
t.DNSNames = []string{"www.example.com"}
2478+
},
2479+
},
2480+
},
2481+
},
2482+
expectedChains: []string{"CN=leaf -> CN=inter -> CN=root"},
2483+
},
24152484
}
24162485

24172486
for _, tc := range tests {
@@ -2424,6 +2493,9 @@ func TestPathBuilding(t *testing.T) {
24242493
if err != nil && err.Error() != tc.expectedErr {
24252494
t.Fatalf("unexpected error: got %q, want %q", err, tc.expectedErr)
24262495
}
2496+
if len(tc.expectedChains) == 0 {
2497+
return
2498+
}
24272499
gotChains := chainsToStrings(chains)
24282500
if !reflect.DeepEqual(gotChains, tc.expectedChains) {
24292501
t.Errorf("unexpected chains returned:\ngot:\n\t%s\nwant:\n\t%s", strings.Join(gotChains, "\n\t"), strings.Join(tc.expectedChains, "\n\t"))

0 commit comments

Comments
 (0)