1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122
|
// 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.
//go:build darwin
// +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")
}
}
|