Skip to content

Commit 9536c5f

Browse files
committed
crypto/x509: re-enable TestSystemRoots
Now that the cgo and no-cgo paths should be correct and equivalent, re-enable the TestSystemRoots test without any margin of error (which was tripping anyway when users had too many of a certain edge-case). As a last quirk, the verify-cert invocation will validate certificates that aren't roots, but are signed by valid roots. Ignore them. Fixes #24652 Change-Id: I6a8ff3c2282136d7122a4e7e387eb8014da0d28a Reviewed-on: https://go-review.googlesource.com/c/128117 TryBot-Result: Gobot Gobot <[email protected]> Run-TryBot: Filippo Valsorda <[email protected]> Reviewed-by: Adam Langley <[email protected]>
1 parent aa24158 commit 9536c5f

File tree

1 file changed

+74
-34
lines changed

1 file changed

+74
-34
lines changed

src/crypto/x509/root_darwin_test.go

Lines changed: 74 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@
55
package x509
66

77
import (
8+
"os"
9+
"os/exec"
10+
"path/filepath"
811
"runtime"
912
"testing"
1013
"time"
@@ -16,11 +19,6 @@ func TestSystemRoots(t *testing.T) {
1619
t.Skipf("skipping on %s/%s, no system root", runtime.GOOS, runtime.GOARCH)
1720
}
1821

19-
switch runtime.GOOS {
20-
case "darwin":
21-
t.Skipf("skipping on %s/%s until golang.org/issue/24652 has been resolved.", runtime.GOOS, runtime.GOARCH)
22-
}
23-
2422
t0 := time.Now()
2523
sysRoots := systemRootsPool() // actual system roots
2624
sysRootsDuration := time.Since(t0)
@@ -36,45 +34,87 @@ func TestSystemRoots(t *testing.T) {
3634
t.Logf(" cgo sys roots: %v", sysRootsDuration)
3735
t.Logf("non-cgo sys roots: %v", execSysRootsDuration)
3836

39-
for _, tt := range []*CertPool{sysRoots, execRoots} {
40-
if tt == nil {
41-
t.Fatal("no system roots")
42-
}
43-
// On Mavericks, there are 212 bundled certs, at least
44-
// there was at one point in time on one machine.
45-
// (Maybe it was a corp laptop with extra certs?)
46-
// Other OS X users report
47-
// 135, 142, 145... Let's try requiring at least 100,
48-
// since this is just a sanity check.
49-
t.Logf("got %d roots", len(tt.certs))
50-
if want, have := 100, len(tt.certs); have < want {
51-
t.Fatalf("want at least %d system roots, have %d", want, have)
52-
}
37+
// On Mavericks, there are 212 bundled certs, at least there was at
38+
// one point in time on one machine. (Maybe it was a corp laptop
39+
// with extra certs?) Other OS X users report 135, 142, 145...
40+
// Let's try requiring at least 100, since this is just a sanity
41+
// check.
42+
if want, have := 100, len(sysRoots.certs); have < want {
43+
t.Errorf("want at least %d system roots, have %d", want, have)
5344
}
5445

55-
// Check that the two cert pools are roughly the same;
56-
// |A∩B| > max(|A|, |B|) / 2 should be a reasonably robust check.
46+
// Fetch any intermediate certificate that verify-cert might be aware of.
47+
out, err := exec.Command("/usr/bin/security", "find-certificate", "-a", "-p",
48+
"/Library/Keychains/System.keychain",
49+
filepath.Join(os.Getenv("HOME"), "/Library/Keychains/login.keychain"),
50+
filepath.Join(os.Getenv("HOME"), "/Library/Keychains/login.keychain-db")).Output()
51+
if err != nil {
52+
t.Fatal(err)
53+
}
54+
allCerts := NewCertPool()
55+
allCerts.AppendCertsFromPEM(out)
5756

58-
isect := make(map[string]bool, len(sysRoots.certs))
57+
// Check that the two cert pools are the same.
58+
sysPool := make(map[string]*Certificate, len(sysRoots.certs))
5959
for _, c := range sysRoots.certs {
60-
isect[string(c.Raw)] = true
60+
sysPool[string(c.Raw)] = c
6161
}
62-
63-
have := 0
6462
for _, c := range execRoots.certs {
65-
if isect[string(c.Raw)] {
66-
have++
63+
if _, ok := sysPool[string(c.Raw)]; ok {
64+
delete(sysPool, string(c.Raw))
65+
} else {
66+
// verify-cert lets in certificates that are not trusted roots, but are
67+
// signed by trusted roots. This should not be a problem, so confirm that's
68+
// the case and skip them.
69+
if _, err := c.Verify(VerifyOptions{
70+
Roots: sysRoots,
71+
Intermediates: allCerts,
72+
KeyUsages: []ExtKeyUsage{ExtKeyUsageAny},
73+
}); err != nil {
74+
t.Errorf("certificate only present in non-cgo pool: %v (verify error: %v)", c.Subject, err)
75+
} else {
76+
t.Logf("signed certificate only present in non-cgo pool (acceptable): %v", c.Subject)
77+
}
6778
}
6879
}
80+
for _, c := range sysPool {
81+
// The nocgo codepath uses verify-cert with the ssl policy, which also
82+
// happens to check EKUs, so some certificates will appear only in the
83+
// cgo pool. We can't easily make them consistent because the EKU check
84+
// is only applied to the certificates passed to verify-cert.
85+
var ekuOk bool
86+
for _, eku := range c.ExtKeyUsage {
87+
if eku == ExtKeyUsageServerAuth || eku == ExtKeyUsageNetscapeServerGatedCrypto ||
88+
eku == ExtKeyUsageMicrosoftServerGatedCrypto || eku == ExtKeyUsageAny {
89+
ekuOk = true
90+
}
91+
}
92+
if len(c.ExtKeyUsage) == 0 && len(c.UnknownExtKeyUsage) == 0 {
93+
ekuOk = true
94+
}
95+
if !ekuOk {
96+
t.Logf("off-EKU certificate only present in cgo pool (acceptable): %v", c.Subject)
97+
continue
98+
}
99+
100+
// Same for expired certificates. We don't chain to them anyway.
101+
now := time.Now()
102+
if now.Before(c.NotBefore) || now.After(c.NotAfter) {
103+
t.Logf("expired certificate only present in cgo pool (acceptable): %v", c.Subject)
104+
continue
105+
}
69106

70-
var want int
71-
if nsys, nexec := len(sysRoots.certs), len(execRoots.certs); nsys > nexec {
72-
want = nsys / 2
73-
} else {
74-
want = nexec / 2
107+
t.Errorf("certificate only present in cgo pool: %v", c.Subject)
75108
}
76109

77-
if have < want {
78-
t.Errorf("insufficient overlap between cgo and non-cgo roots; want at least %d, have %d", want, have)
110+
if t.Failed() && debugDarwinRoots {
111+
cmd := exec.Command("security", "dump-trust-settings")
112+
cmd.Stdout = os.Stdout
113+
cmd.Stderr = os.Stderr
114+
cmd.Run()
115+
cmd = exec.Command("security", "dump-trust-settings", "-d")
116+
cmd.Stdout = os.Stdout
117+
cmd.Stderr = os.Stderr
118+
cmd.Run()
79119
}
80120
}

0 commit comments

Comments
 (0)