| // 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. |
| |
| //go:generate go run root_darwin_arm_gen.go -output root_darwin_armx.go |
| |
| package x509 |
| |
| import ( |
| "bytes" |
| "encoding/pem" |
| "fmt" |
| "io/ioutil" |
| "os" |
| "os/exec" |
| "strconv" |
| "sync" |
| "syscall" |
| ) |
| |
| func (c *Certificate) systemVerify(opts *VerifyOptions) (chains [][]*Certificate, err error) { |
| return nil, nil |
| } |
| |
| // 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. |
| |
| func execSecurityRoots() (*CertPool, error) { |
| cmd := exec.Command("/usr/bin/security", "find-certificate", "-a", "-p", "/System/Library/Keychains/SystemRootCertificates.keychain") |
| data, err := cmd.Output() |
| if err != nil { |
| return nil, err |
| } |
| |
| var ( |
| mu sync.Mutex |
| roots = NewCertPool() |
| ) |
| add := func(cert *Certificate) { |
| mu.Lock() |
| defer mu.Unlock() |
| roots.AddCert(cert) |
| } |
| blockCh := make(chan *pem.Block) |
| var wg sync.WaitGroup |
| for i := 0; i < 4; i++ { |
| wg.Add(1) |
| go func() { |
| defer wg.Done() |
| for block := range blockCh { |
| verifyCertWithSystem(block, add) |
| } |
| }() |
| } |
| 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 |
| } |
| blockCh <- block |
| } |
| close(blockCh) |
| wg.Wait() |
| return roots, nil |
| } |
| |
| func verifyCertWithSystem(block *pem.Block, add func(*Certificate)) { |
| data := pem.EncodeToMemory(block) |
| var cmd *exec.Cmd |
| if needsTmpFiles() { |
| f, err := ioutil.TempFile("", "cert") |
| if err != nil { |
| fmt.Fprintf(os.Stderr, "can't create temporary file for cert: %v", err) |
| return |
| } |
| 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 |
| } |
| if err := f.Close(); err != nil { |
| fmt.Fprintf(os.Stderr, "can't write temporary file for cert: %v", err) |
| return |
| } |
| cmd = exec.Command("/usr/bin/security", "verify-cert", "-c", f.Name(), "-l") |
| } else { |
| cmd = exec.Command("/usr/bin/security", "verify-cert", "-c", "/dev/stdin", "-l") |
| cmd.Stdin = bytes.NewReader(data) |
| } |
| if cmd.Run() == nil { |
| // Non-zero exit means untrusted |
| cert, err := ParseCertificate(block.Bytes) |
| if err != nil { |
| return |
| } |
| |
| add(cert) |
| } |
| } |
| |
| var versionCache struct { |
| sync.Once |
| major int |
| } |
| |
| // needsTmpFiles reports whether the OS is <= 10.11 (which requires real |
| // files as arguments to the security command). |
| func needsTmpFiles() bool { |
| versionCache.Do(func() { |
| release, err := syscall.Sysctl("kern.osrelease") |
| if err != nil { |
| return |
| } |
| for i, c := range release { |
| if c == '.' { |
| release = release[:i] |
| break |
| } |
| } |
| major, err := strconv.Atoi(release) |
| if err != nil { |
| return |
| } |
| versionCache.major = major |
| }) |
| return versionCache.major <= 15 |
| } |