blob: 0f79d25a649d6cb758c4055044ef9c0f6f24541b [file] [log] [blame]
// Copyright 2018 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.
// +build darwin
// Command macOS-roots-test runs crypto/x509.TestSystemRoots as a
// stand-alone binary for crowdsourced testing.
package main
import (
"crypto/x509"
"fmt"
"log"
"os"
"os/exec"
"time"
"unsafe"
)
type CertPool struct {
bySubjectKeyId map[string][]int
byName map[string][]int
certs []*x509.Certificate
}
func (s *CertPool) contains(cert *x509.Certificate) bool {
if s == nil {
return false
}
candidates := s.byName[string(cert.RawSubject)]
for _, c := range candidates {
if s.certs[c].Equal(cert) {
return true
}
}
return false
}
func main() {
var failed bool
t0 := time.Now()
sysRootsExt, err := loadSystemRoots() // actual system roots
sysRootsDuration := time.Since(t0)
if err != nil {
log.Fatalf("failed to read system roots (cgo): %v", err)
}
sysRoots := (*CertPool)(unsafe.Pointer(sysRootsExt))
t1 := time.Now()
execRootsExt, err := execSecurityRoots() // non-cgo roots
execSysRootsDuration := time.Since(t1)
if err != nil {
log.Fatalf("failed to read system roots (nocgo): %v", err)
}
execRoots := (*CertPool)(unsafe.Pointer(execRootsExt))
fmt.Printf(" cgo sys roots: %v\n", sysRootsDuration)
fmt.Printf("non-cgo sys roots: %v\n", execSysRootsDuration)
// On Mavericks, there are 212 bundled certs, at least there was at
// one point in time on one machine. (Maybe it was a corp laptop
// with extra certs?) Other OS X users report 135, 142, 145...
// Let's try requiring at least 100, since this is just a sanity
// check.
if want, have := 100, len(sysRoots.certs); have < want {
failed = true
fmt.Printf("want at least %d system roots, have %d\n", want, have)
}
// Check that the two cert pools are the same.
sysPool := make(map[string]*x509.Certificate, len(sysRoots.certs))
for _, c := range sysRoots.certs {
sysPool[string(c.Raw)] = c
}
for _, c := range execRoots.certs {
if _, ok := sysPool[string(c.Raw)]; ok {
delete(sysPool, string(c.Raw))
} else {
// verify-cert lets in certificates that are not trusted roots, but are
// signed by trusted roots. This should not be a problem, so confirm that's
// the case and skip them.
if _, err := c.Verify(x509.VerifyOptions{
Roots: sysRootsExt,
Intermediates: execRootsExt, // the intermediates for EAP certs are stored in the keychain
KeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageAny},
}); err != nil {
failed = true
fmt.Printf("certificate only present in non-cgo pool: %v (verify error: %v)\n", c.Subject, err)
} else {
fmt.Printf("signed certificate only present in non-cgo pool (acceptable): %v\n", c.Subject)
}
}
}
for _, c := range sysPool {
failed = true
fmt.Printf("certificate only present in cgo pool: %v\n", c.Subject)
}
if failed && debugDarwinRoots {
cmd := exec.Command("security", "dump-trust-settings")
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
cmd.Run()
cmd = exec.Command("security", "dump-trust-settings", "-d")
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
cmd.Run()
}
if failed {
fmt.Printf("\n\n!!! The test failed!\n\nPlease report *the whole output* at https://github.com/golang/go/issues/24652 wrapping it in ``` a code block ```\nThank you!\n")
} else {
fmt.Printf("\n\nThe test passed, no need to report the output. Thank you.\n")
}
}