Skip to content

Commit

Permalink
crypto/x509: don't accept a root that already appears in a chain.
Browse files Browse the repository at this point in the history
Since a root certificate is self-signed, it's a valid child of itself.
If a root certificate appeared both in the pool of intermediates and
roots the verification code could find a chain which included it twice:
first as an intermediate and then as a root. (Existing checks prevented
the code from looping any more.)

This change stops the exact same certificate from appearing twice in a
chain. This simplifies the results in the face of the common
configuration error of a TLS server returning a root certificate.

(This should also stop two different versions of the “same” root
appearing in a chain because the self-signature on one will not validate
for the other.)

Fixes #16800.

Change-Id: I004853baa0eea27b44d47b9b34f96113a92ebac8
Reviewed-on: https://go-review.googlesource.com/32121
Run-TryBot: Adam Langley <agl@golang.org>
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
  • Loading branch information
agl committed Oct 27, 2016
1 parent a047b6b commit 07a31bc
Show file tree
Hide file tree
Showing 2 changed files with 9 additions and 9 deletions.
10 changes: 9 additions & 1 deletion src/crypto/x509/verify.go
Original file line number Diff line number Diff line change
Expand Up @@ -346,8 +346,16 @@ func appendToFreshChain(chain []*Certificate, cert *Certificate) []*Certificate

func (c *Certificate) buildChains(cache map[int][][]*Certificate, currentChain []*Certificate, opts *VerifyOptions) (chains [][]*Certificate, err error) {
possibleRoots, failedRoot, rootErr := opts.Roots.findVerifiedParents(c)
nextRoot:
for _, rootNum := range possibleRoots {
root := opts.Roots.certs[rootNum]

This comment has been minimized.

Copy link
@ramoas

ramoas Nov 18, 2016

@agl Reiterating my comment from Gerrit CR: Is this loop and label needed? Unlike intermediates, there should only be one root. Thus, unless I'm just missing something, it should be sufficient to just compare root to the very last certificate of currentChain.

This comment has been minimized.

Copy link
@bradfitz

bradfitz Nov 19, 2016

Contributor

We don't do code review here on Github. You're likely speaking into a void. Few people see these comments.

for _, cert := range currentChain {
if cert.Equal(root) {
continue nextRoot
}
}

err = root.isValid(rootCertificate, currentChain, opts)
if err != nil {
continue
Expand All @@ -360,7 +368,7 @@ nextIntermediate:
for _, intermediateNum := range possibleIntermediates {
intermediate := opts.Intermediates.certs[intermediateNum]
for _, cert := range currentChain {
if cert == intermediate {
if cert.Equal(intermediate) {
continue nextIntermediate
}
}
Expand Down
8 changes: 0 additions & 8 deletions src/crypto/x509/verify_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,10 +104,6 @@ var verifyTests = []verifyTest{

expectedChains: [][]string{
{"Google", "Google Internet Authority", "GeoTrust"},
// TODO(agl): this is ok, but it would be nice if the
// chain building didn't visit the same SPKI
// twice.
{"Google", "Google Internet Authority", "GeoTrust", "GeoTrust"},
},
// CAPI doesn't build the chain with the duplicated GeoTrust
// entry so the results don't match. Thus we skip this test
Expand All @@ -130,12 +126,8 @@ var verifyTests = []verifyTest{
roots: []string{startComRoot},
currentTime: 1302726541,

// Skip when using systemVerify, since Windows
// can only return a single chain to us (for now).
systemSkip: true,
expectedChains: [][]string{
{"dnssec-exp", "StartCom Class 1", "StartCom Certification Authority"},
{"dnssec-exp", "StartCom Class 1", "StartCom Certification Authority", "StartCom Certification Authority"},
},
},
{
Expand Down

0 comments on commit 07a31bc

Please sign in to comment.