Black Lives Matter. Support the Equal Justice Initiative.

Source file src/crypto/x509/root_darwin_test.go

Documentation: crypto/x509

     1  // Copyright 2013 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package x509
     6  
     7  import (
     8  	"crypto/rsa"
     9  	"os"
    10  	"os/exec"
    11  	"path/filepath"
    12  	"runtime"
    13  	"testing"
    14  	"time"
    15  )
    16  
    17  func TestSystemRoots(t *testing.T) {
    18  	switch runtime.GOARCH {
    19  	case "arm", "arm64":
    20  		t.Skipf("skipping on %s/%s, no system root", runtime.GOOS, runtime.GOARCH)
    21  	}
    22  
    23  	t0 := time.Now()
    24  	sysRoots := systemRootsPool() // actual system roots
    25  	sysRootsDuration := time.Since(t0)
    26  
    27  	t1 := time.Now()
    28  	execRoots, err := execSecurityRoots() // non-cgo roots
    29  	execSysRootsDuration := time.Since(t1)
    30  
    31  	if err != nil {
    32  		t.Fatalf("failed to read system roots: %v", err)
    33  	}
    34  
    35  	t.Logf("    cgo sys roots: %v", sysRootsDuration)
    36  	t.Logf("non-cgo sys roots: %v", execSysRootsDuration)
    37  
    38  	// On Mavericks, there are 212 bundled certs, at least there was at
    39  	// one point in time on one machine. (Maybe it was a corp laptop
    40  	// with extra certs?) Other OS X users report 135, 142, 145...
    41  	// Let's try requiring at least 100, since this is just a sanity
    42  	// check.
    43  	if want, have := 100, len(sysRoots.certs); have < want {
    44  		t.Errorf("want at least %d system roots, have %d", want, have)
    45  	}
    46  
    47  	// Fetch any intermediate certificate that verify-cert might be aware of.
    48  	out, err := exec.Command("/usr/bin/security", "find-certificate", "-a", "-p",
    49  		"/Library/Keychains/System.keychain",
    50  		filepath.Join(os.Getenv("HOME"), "/Library/Keychains/login.keychain"),
    51  		filepath.Join(os.Getenv("HOME"), "/Library/Keychains/login.keychain-db")).Output()
    52  	if err != nil {
    53  		t.Fatal(err)
    54  	}
    55  	allCerts := NewCertPool()
    56  	allCerts.AppendCertsFromPEM(out)
    57  
    58  	// Check that the two cert pools are the same.
    59  	sysPool := make(map[string]*Certificate, len(sysRoots.certs))
    60  	for _, c := range sysRoots.certs {
    61  		sysPool[string(c.Raw)] = c
    62  	}
    63  	for _, c := range execRoots.certs {
    64  		if _, ok := sysPool[string(c.Raw)]; ok {
    65  			delete(sysPool, string(c.Raw))
    66  		} else {
    67  			// verify-cert lets in certificates that are not trusted roots, but
    68  			// are signed by trusted roots. This is not great, but unavoidable
    69  			// until we parse real policies without cgo, so confirm that's the
    70  			// case and skip them.
    71  			if _, err := c.Verify(VerifyOptions{
    72  				Roots:         sysRoots,
    73  				Intermediates: allCerts,
    74  				KeyUsages:     []ExtKeyUsage{ExtKeyUsageAny},
    75  				CurrentTime:   c.NotBefore, // verify-cert does not check expiration
    76  			}); err != nil {
    77  				t.Errorf("certificate only present in non-cgo pool: %v (verify error: %v)", c.Subject, err)
    78  			} else {
    79  				t.Logf("signed certificate only present in non-cgo pool (acceptable): %v", c.Subject)
    80  			}
    81  		}
    82  	}
    83  	for _, c := range sysPool {
    84  		// The nocgo codepath uses verify-cert with the ssl policy, which also
    85  		// happens to check EKUs, so some certificates will appear only in the
    86  		// cgo pool. We can't easily make them consistent because the EKU check
    87  		// is only applied to the certificates passed to verify-cert.
    88  		var ekuOk bool
    89  		for _, eku := range c.ExtKeyUsage {
    90  			if eku == ExtKeyUsageServerAuth || eku == ExtKeyUsageNetscapeServerGatedCrypto ||
    91  				eku == ExtKeyUsageMicrosoftServerGatedCrypto || eku == ExtKeyUsageAny {
    92  				ekuOk = true
    93  			}
    94  		}
    95  		if len(c.ExtKeyUsage) == 0 && len(c.UnknownExtKeyUsage) == 0 {
    96  			ekuOk = true
    97  		}
    98  		if !ekuOk {
    99  			t.Logf("off-EKU certificate only present in cgo pool (acceptable): %v", c.Subject)
   100  			continue
   101  		}
   102  
   103  		// Same for expired certificates. We don't chain to them anyway.
   104  		now := time.Now()
   105  		if now.Before(c.NotBefore) || now.After(c.NotAfter) {
   106  			t.Logf("expired certificate only present in cgo pool (acceptable): %v", c.Subject)
   107  			continue
   108  		}
   109  
   110  		// On 10.11 there are five unexplained roots that only show up from the
   111  		// C API. They have in common the fact that they are old, 1024-bit
   112  		// certificates. It's arguably better to ignore them anyway.
   113  		if key, ok := c.PublicKey.(*rsa.PublicKey); ok && key.N.BitLen() == 1024 {
   114  			t.Logf("1024-bit certificate only present in cgo pool (acceptable): %v", c.Subject)
   115  			continue
   116  		}
   117  
   118  		t.Errorf("certificate only present in cgo pool: %v", c.Subject)
   119  	}
   120  
   121  	if t.Failed() && debugDarwinRoots {
   122  		cmd := exec.Command("security", "dump-trust-settings")
   123  		cmd.Stdout, cmd.Stderr = os.Stderr, os.Stderr
   124  		cmd.Run()
   125  		cmd = exec.Command("security", "dump-trust-settings", "-d")
   126  		cmd.Stdout, cmd.Stderr = os.Stderr, os.Stderr
   127  		cmd.Run()
   128  	}
   129  }
   130  

View as plain text