blob: 59b303d64f74efc92137858ee39252d6e7e25e31 [file] [log] [blame]
// 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
}