crypto/x509: don't accept a root that already appears in a chain.

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>
diff --git a/src/crypto/x509/verify.go b/src/crypto/x509/verify.go
index aa9e374..6988ad7 100644
--- a/src/crypto/x509/verify.go
+++ b/src/crypto/x509/verify.go
@@ -346,8 +346,16 @@
 
 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]
+
+		for _, cert := range currentChain {
+			if cert.Equal(root) {
+				continue nextRoot
+			}
+		}
+
 		err = root.isValid(rootCertificate, currentChain, opts)
 		if err != nil {
 			continue
@@ -360,7 +368,7 @@
 	for _, intermediateNum := range possibleIntermediates {
 		intermediate := opts.Intermediates.certs[intermediateNum]
 		for _, cert := range currentChain {
-			if cert == intermediate {
+			if cert.Equal(intermediate) {
 				continue nextIntermediate
 			}
 		}
diff --git a/src/crypto/x509/verify_test.go b/src/crypto/x509/verify_test.go
index 3461292..5a7481f 100644
--- a/src/crypto/x509/verify_test.go
+++ b/src/crypto/x509/verify_test.go
@@ -104,10 +104,6 @@
 
 		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
@@ -130,12 +126,8 @@
 		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"},
 		},
 	},
 	{