diff options
Diffstat (limited to '')
-rw-r--r-- | dependencies/pkg/mod/golang.org/x/exp@v0.0.0-20220613132600-b0d781184e0d/cmd/macos-roots-test/root_darwin.go | 174 |
1 files changed, 174 insertions, 0 deletions
diff --git a/dependencies/pkg/mod/golang.org/x/exp@v0.0.0-20220613132600-b0d781184e0d/cmd/macos-roots-test/root_darwin.go b/dependencies/pkg/mod/golang.org/x/exp@v0.0.0-20220613132600-b0d781184e0d/cmd/macos-roots-test/root_darwin.go new file mode 100644 index 0000000..b5bf0f7 --- /dev/null +++ b/dependencies/pkg/mod/golang.org/x/exp@v0.0.0-20220613132600-b0d781184e0d/cmd/macos-roots-test/root_darwin.go @@ -0,0 +1,174 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "bytes" + "crypto/x509" + "encoding/pem" + "fmt" + "io/ioutil" + "os" + "os/exec" + "os/user" + "path/filepath" + "sync" +) + +var debugDarwinRoots = true + +// This code is only used when compiling without cgo. +// It is here, instead of root_nocgo_darwin.go, so that tests can check it +// even if the tests are run with cgo enabled. +// The linker will not include these unused functions in binaries built with cgo enabled. + +// execSecurityRoots finds the macOS list of trusted root certificates +// using only command-line tools. This is our fallback path when cgo isn't available. +// +// The strategy is as follows: +// +// 1. Run "security find-certificate" to dump the list of system root +// CAs in PEM format. +// +// 2. For each dumped cert, conditionally verify it with "security +// verify-cert" if that cert was not in the SystemRootCertificates +// keychain, which can't have custom trust policies. +// +// We need to run "verify-cert" for all certificates not in SystemRootCertificates +// because there might be certificates in the keychains without a corresponding +// trust entry, in which case the logic is complicated (see root_cgo_darwin.go). +// +// TODO: actually parse the "trust-settings-export" output and apply the full +// logic. See Issue 26830. +func execSecurityRoots() (*x509.CertPool, error) { + keychains := []string{"/Library/Keychains/System.keychain"} + + // Note that this results in trusting roots from $HOME/... (the environment + // variable), which might not be expected. + u, err := user.Current() + if err != nil { + if debugDarwinRoots { + fmt.Printf("crypto/x509: get current user: %v\n", err) + } + } else { + keychains = append(keychains, + filepath.Join(u.HomeDir, "/Library/Keychains/login.keychain"), + + // Fresh installs of Sierra use a slightly different path for the login keychain + filepath.Join(u.HomeDir, "/Library/Keychains/login.keychain-db"), + ) + } + + var ( + mu sync.Mutex + roots = x509.NewCertPool() + numVerified int // number of execs of 'security verify-cert', for debug stats + wg sync.WaitGroup + verifyCh = make(chan *x509.Certificate) + ) + + // 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 + // running 16 at a time for instance doesn't help at all. + for i := 0; i < 4; i++ { + wg.Add(1) + go func() { + defer wg.Done() + for cert := range verifyCh { + valid := verifyCertWithSystem(cert) + + mu.Lock() + numVerified++ + if valid { + roots.AddCert(cert) + } + mu.Unlock() + } + }() + } + err = forEachCertInKeychains(keychains, func(cert *x509.Certificate) { + verifyCh <- cert + }) + if err != nil { + return nil, err + } + close(verifyCh) + wg.Wait() + + if debugDarwinRoots { + fmt.Printf("crypto/x509: ran security verify-cert %d times\n", numVerified) + } + + err = forEachCertInKeychains([]string{ + "/System/Library/Keychains/SystemRootCertificates.keychain", + }, roots.AddCert) + if err != nil { + return nil, err + } + + return roots, nil +} + +func forEachCertInKeychains(paths []string, f func(*x509.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) + if block == nil { + break + } + if block.Type != "CERTIFICATE" || len(block.Headers) != 0 { + continue + } + cert, err := x509.ParseCertificate(block.Bytes) + if err != nil { + continue + } + f(cert) + } + return nil +} + +func verifyCertWithSystem(cert *x509.Certificate) bool { + data := pem.EncodeToMemory(&pem.Block{ + Type: "CERTIFICATE", Bytes: cert.Raw, + }) + + f, err := ioutil.TempFile("", "cert") + if err != nil { + fmt.Fprintf(os.Stderr, "can't create temporary file for cert: %v", err) + return false + } + defer os.Remove(f.Name()) + if _, err := f.Write(data); err != nil { + fmt.Fprintf(os.Stderr, "can't write temporary file for cert: %v", err) + return false + } + if err := f.Close(); err != nil { + fmt.Fprintf(os.Stderr, "can't write temporary file for cert: %v", err) + return false + } + 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 { + fmt.Printf("crypto/x509: verify-cert rejected %s: %q\n", cert.Subject, bytes.TrimSpace(stderr.Bytes())) + } + return false + } + if debugDarwinRoots { + fmt.Printf("crypto/x509: verify-cert approved %s\n", cert.Subject) + } + return true +} |