aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/crypto/x509/root_darwin.go111
1 files changed, 67 insertions, 44 deletions
diff --git a/src/crypto/x509/root_darwin.go b/src/crypto/x509/root_darwin.go
index ae396d80a9..b0460527b1 100644
--- a/src/crypto/x509/root_darwin.go
+++ b/src/crypto/x509/root_darwin.go
@@ -40,8 +40,8 @@ func (c *Certificate) systemVerify(opts *VerifyOptions) (chains [][]*Certificate
//
// 1. Run "security trust-settings-export" and "security
// trust-settings-export -d" to discover the set of certs with some
-// user-tweaked trust policy. We're too lazy to parse the XML (at
-// least at this stage of Go 1.8) to understand what the trust
+// user-tweaked trust policy. We're too lazy to parse the XML
+// (Issue 26830) to understand what the trust
// policy actually is. We just learn that there is _some_ policy.
//
// 2. Run "security find-certificate" to dump the list of system root
@@ -59,21 +59,18 @@ func execSecurityRoots() (*CertPool, error) {
return nil, err
}
if debugDarwinRoots {
- println(fmt.Sprintf("crypto/x509: %d certs have a trust policy", len(hasPolicy)))
+ fmt.Printf("crypto/x509: %d certs have a trust policy\n", len(hasPolicy))
}
- args := []string{"find-certificate", "-a", "-p",
- "/System/Library/Keychains/SystemRootCertificates.keychain",
- "/Library/Keychains/System.keychain",
- }
+ keychains := []string{"/Library/Keychains/System.keychain"}
u, err := user.Current()
if err != nil {
if debugDarwinRoots {
- println(fmt.Sprintf("crypto/x509: can't get user home directory: %v", err))
+ fmt.Printf("crypto/x509: can't get user home directory: %v\n", err)
}
} else {
- args = append(args,
+ keychains = append(keychains,
filepath.Join(u.HomeDir, "/Library/Keychains/login.keychain"),
// Fresh installs of Sierra use a slightly different path for the login keychain
@@ -81,21 +78,19 @@ func execSecurityRoots() (*CertPool, error) {
)
}
- cmd := exec.Command("/usr/bin/security", args...)
- data, err := cmd.Output()
- if err != nil {
- return nil, err
+ type rootCandidate struct {
+ c *Certificate
+ system bool
}
var (
mu sync.Mutex
roots = NewCertPool()
numVerified int // number of execs of 'security verify-cert', for debug stats
+ wg sync.WaitGroup
+ verifyCh = make(chan rootCandidate)
)
- blockCh := make(chan *pem.Block)
- var wg sync.WaitGroup
-
// Using 4 goroutines to pipe into verify-cert seems to be
// about the best we can do. The verify-cert binary seems to
// just RPC to another server with coarse locking anyway, so
@@ -109,31 +104,62 @@ func execSecurityRoots() (*CertPool, error) {
wg.Add(1)
go func() {
defer wg.Done()
- for block := range blockCh {
- cert, err := ParseCertificate(block.Bytes)
- if err != nil {
- continue
- }
- sha1CapHex := fmt.Sprintf("%X", sha1.Sum(block.Bytes))
+ for cert := range verifyCh {
+ sha1CapHex := fmt.Sprintf("%X", sha1.Sum(cert.c.Raw))
- valid := true
+ var valid bool
verifyChecks := 0
if hasPolicy[sha1CapHex] {
verifyChecks++
- if !verifyCertWithSystem(block, cert) {
- valid = false
- }
+ valid = verifyCertWithSystem(cert.c)
+ } else {
+ // Certificates not in SystemRootCertificates without user
+ // or admin trust settings are not trusted.
+ valid = cert.system
}
mu.Lock()
numVerified += verifyChecks
if valid {
- roots.AddCert(cert)
+ roots.AddCert(cert.c)
}
mu.Unlock()
}
}()
}
+ err = forEachCertInKeychains(keychains, func(cert *Certificate) {
+ verifyCh <- rootCandidate{c: cert, system: false}
+ })
+ if err != nil {
+ close(verifyCh)
+ return nil, err
+ }
+ err = forEachCertInKeychains([]string{
+ "/System/Library/Keychains/SystemRootCertificates.keychain",
+ }, func(cert *Certificate) {
+ verifyCh <- rootCandidate{c: cert, system: true}
+ })
+ if err != nil {
+ close(verifyCh)
+ return nil, err
+ }
+ close(verifyCh)
+ wg.Wait()
+
+ if debugDarwinRoots {
+ fmt.Printf("crypto/x509: ran security verify-cert %d times\n", numVerified)
+ }
+
+ return roots, nil
+}
+
+func forEachCertInKeychains(paths []string, f func(*Certificate)) error {
+ args := append([]string{"find-certificate", "-a", "-p"}, paths...)
+ cmd := exec.Command("/usr/bin/security", args...)
+ data, err := cmd.Output()
+ if err != nil {
+ return err
+ }
for len(data) > 0 {
var block *pem.Block
block, data = pem.Decode(data)
@@ -143,22 +169,19 @@ func execSecurityRoots() (*CertPool, error) {
if block.Type != "CERTIFICATE" || len(block.Headers) != 0 {
continue
}
- blockCh <- block
- }
- close(blockCh)
- wg.Wait()
-
- if debugDarwinRoots {
- mu.Lock()
- defer mu.Unlock()
- println(fmt.Sprintf("crypto/x509: ran security verify-cert %d times", numVerified))
+ cert, err := ParseCertificate(block.Bytes)
+ if err != nil {
+ continue
+ }
+ f(cert)
}
-
- return roots, nil
+ return nil
}
-func verifyCertWithSystem(block *pem.Block, cert *Certificate) bool {
- data := pem.EncodeToMemory(block)
+func verifyCertWithSystem(cert *Certificate) bool {
+ data := pem.EncodeToMemory(&pem.Block{
+ Type: "CERTIFICATE", Bytes: cert.Raw,
+ })
f, err := ioutil.TempFile("", "cert")
if err != nil {
@@ -174,19 +197,19 @@ func verifyCertWithSystem(block *pem.Block, cert *Certificate) bool {
fmt.Fprintf(os.Stderr, "can't write temporary file for cert: %v", err)
return false
}
- cmd := exec.Command("/usr/bin/security", "verify-cert", "-c", f.Name(), "-l", "-L")
+ cmd := exec.Command("/usr/bin/security", "verify-cert", "-p", "ssl", "-c", f.Name(), "-l", "-L")
var stderr bytes.Buffer
if debugDarwinRoots {
cmd.Stderr = &stderr
}
if err := cmd.Run(); err != nil {
if debugDarwinRoots {
- println(fmt.Sprintf("crypto/x509: verify-cert rejected %s: %q", cert.Subject, bytes.TrimSpace(stderr.Bytes())))
+ fmt.Printf("crypto/x509: verify-cert rejected %s: %q\n", cert.Subject, bytes.TrimSpace(stderr.Bytes()))
}
return false
}
if debugDarwinRoots {
- println(fmt.Sprintf("crypto/x509: verify-cert approved %s", cert.Subject))
+ fmt.Printf("crypto/x509: verify-cert approved %s\n", cert.Subject)
}
return true
}
@@ -219,7 +242,7 @@ func getCertsWithTrustPolicy() (map[string]bool, error) {
// localized on macOS, just interpret any failure to mean that
// there are no trust settings.
if debugDarwinRoots {
- println(fmt.Sprintf("crypto/x509: exec %q: %v, %s", cmd.Args, err, stderr.Bytes()))
+ fmt.Printf("crypto/x509: exec %q: %v, %s\n", cmd.Args, err, stderr.Bytes())
}
return nil
}