Skip to content

Commit a62ae9f

Browse files
committed
crypto/x509: add SystemCertPool, refactor system cert pool loading
This exports the system cert pool. The system cert loading was refactored to let it be run multiple times (so callers get a copy, and can't mutate global state), and also to not discard errors. SystemCertPool returns an error on Windows. Maybe it's fixable later, but so far we haven't used it, since the system verifies TLS. Fixes #13335 Change-Id: I3dfb4656a373f241bae8529076d24c5f532f113c Reviewed-on: https://go-review.googlesource.com/21293 Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Andrew Gerrand <adg@golang.org>
1 parent 71ab3c1 commit a62ae9f

10 files changed

+80
-41
lines changed

src/crypto/x509/cert_pool.go

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ package x509
66

77
import (
88
"encoding/pem"
9+
"errors"
10+
"runtime"
911
)
1012

1113
// CertPool is a set of certificates.
@@ -18,12 +20,22 @@ type CertPool struct {
1820
// NewCertPool returns a new, empty CertPool.
1921
func NewCertPool() *CertPool {
2022
return &CertPool{
21-
make(map[string][]int),
22-
make(map[string][]int),
23-
nil,
23+
bySubjectKeyId: make(map[string][]int),
24+
byName: make(map[string][]int),
2425
}
2526
}
2627

28+
// SystemCertPool returns a copy of the system cert pool.
29+
//
30+
// Any mutations to the returned pool are not written to disk and do
31+
// not affect any other pool.
32+
func SystemCertPool() (*CertPool, error) {
33+
if runtime.GOOS == "windows" {
34+
return nil, errors.New("crypto/x509: system root pool is not available on Windows")
35+
}
36+
return loadSystemRoots()
37+
}
38+
2739
// findVerifiedParents attempts to find certificates in s which have signed the
2840
// given certificate. If any candidates were rejected then errCert will be set
2941
// to one of them, arbitrarily, and err will contain the reason that it was
@@ -107,10 +119,10 @@ func (s *CertPool) AppendCertsFromPEM(pemCerts []byte) (ok bool) {
107119

108120
// Subjects returns a list of the DER-encoded subjects of
109121
// all of the certificates in the pool.
110-
func (s *CertPool) Subjects() (res [][]byte) {
111-
res = make([][]byte, len(s.certs))
122+
func (s *CertPool) Subjects() [][]byte {
123+
res := make([][]byte, len(s.certs))
112124
for i, c := range s.certs {
113125
res[i] = c.RawSubject
114126
}
115-
return
127+
return res
116128
}

src/crypto/x509/root.go

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,16 @@ package x509
77
import "sync"
88

99
var (
10-
once sync.Once
11-
systemRoots *CertPool
10+
once sync.Once
11+
systemRoots *CertPool
12+
systemRootsErr error
1213
)
1314

1415
func systemRootsPool() *CertPool {
1516
once.Do(initSystemRoots)
1617
return systemRoots
1718
}
19+
20+
func initSystemRoots() {
21+
systemRoots, systemRootsErr = loadSystemRoots()
22+
}

src/crypto/x509/root_cgo_darwin.go

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -61,19 +61,23 @@ int FetchPEMRoots(CFDataRef *pemRoots) {
6161
}
6262
*/
6363
import "C"
64-
import "unsafe"
64+
import (
65+
"errors"
66+
"unsafe"
67+
)
6568

66-
func initSystemRoots() {
69+
func loadSystemRoots() (*CertPool, error) {
6770
roots := NewCertPool()
6871

6972
var data C.CFDataRef = nil
7073
err := C.FetchPEMRoots(&data)
7174
if err == -1 {
72-
return
75+
// TODO: better error message
76+
return nil, errors.New("crypto/x509: failed to load darwin system roots with cgo")
7377
}
7478

7579
defer C.CFRelease(C.CFTypeRef(data))
7680
buf := C.GoBytes(unsafe.Pointer(C.CFDataGetBytePtr(data)), C.int(C.CFDataGetLength(data)))
7781
roots.AppendCertsFromPEM(buf)
78-
systemRoots = roots
82+
return roots, nil
7983
}

src/crypto/x509/root_darwin_arm_gen.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -184,8 +184,9 @@ const header = `
184184
185185
package x509
186186
187-
func initSystemRoots() {
188-
systemRoots = NewCertPool()
189-
systemRoots.AppendCertsFromPEM([]byte(systemRootsPEM))
187+
func loadSystemRoots() (*CertPool, error) {
188+
p := NewCertPool()
189+
p.AppendCertsFromPEM([]byte(systemRootsPEM))
190+
return p
190191
}
191192
`

src/crypto/x509/root_darwin_armx.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,10 @@
1010

1111
package x509
1212

13-
func initSystemRoots() {
14-
systemRoots = NewCertPool()
15-
systemRoots.AppendCertsFromPEM([]byte(systemRootsPEM))
13+
func loadSystemRoots() (*CertPool, error) {
14+
p := NewCertPool()
15+
p.AppendCertsFromPEM([]byte(systemRootsPEM))
16+
return p
1617
}
1718

1819
const systemRootsPEM = `

src/crypto/x509/root_nocgo_darwin.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,6 @@
66

77
package x509
88

9-
func initSystemRoots() {
10-
systemRoots, _ = execSecurityRoots()
9+
func loadSystemRoots() (*CertPool, error) {
10+
return execSecurityRoots()
1111
}

src/crypto/x509/root_plan9.go

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,10 @@
66

77
package x509
88

9-
import "io/ioutil"
9+
import (
10+
"io/ioutil"
11+
"os"
12+
)
1013

1114
// Possible certificate files; stop after finding one.
1215
var certFiles = []string{
@@ -17,17 +20,18 @@ func (c *Certificate) systemVerify(opts *VerifyOptions) (chains [][]*Certificate
1720
return nil, nil
1821
}
1922

20-
func initSystemRoots() {
23+
func loadSystemRoots() (*CertPool, error) {
2124
roots := NewCertPool()
25+
var bestErr error
2226
for _, file := range certFiles {
2327
data, err := ioutil.ReadFile(file)
2428
if err == nil {
2529
roots.AppendCertsFromPEM(data)
26-
systemRoots = roots
27-
return
30+
return roots, nil
31+
}
32+
if bestErr == nil || (os.IsNotExist(bestErr) && !os.IsNotExist(err)) {
33+
bestErr = err
2834
}
2935
}
30-
31-
// All of the files failed to load. systemRoots will be nil which will
32-
// trigger a specific error at verification time.
36+
return nil, bestErr
3337
}

src/crypto/x509/root_unix.go

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,10 @@
66

77
package x509
88

9-
import "io/ioutil"
9+
import (
10+
"io/ioutil"
11+
"os"
12+
)
1013

1114
// Possible directories with certificate files; stop after successfully
1215
// reading at least one file from a directory.
@@ -19,20 +22,26 @@ func (c *Certificate) systemVerify(opts *VerifyOptions) (chains [][]*Certificate
1922
return nil, nil
2023
}
2124

22-
func initSystemRoots() {
25+
func loadSystemRoots() (*CertPool, error) {
2326
roots := NewCertPool()
27+
var firstErr error
2428
for _, file := range certFiles {
2529
data, err := ioutil.ReadFile(file)
2630
if err == nil {
2731
roots.AppendCertsFromPEM(data)
28-
systemRoots = roots
29-
return
32+
return roots, nil
33+
}
34+
if firstErr == nil && !os.IsNotExist(err) {
35+
firstErr = err
3036
}
3137
}
3238

3339
for _, directory := range certDirectories {
3440
fis, err := ioutil.ReadDir(directory)
3541
if err != nil {
42+
if firstErr == nil && !os.IsNotExist(err) {
43+
firstErr = err
44+
}
3645
continue
3746
}
3847
rootsAdded := false
@@ -43,11 +52,9 @@ func initSystemRoots() {
4352
}
4453
}
4554
if rootsAdded {
46-
systemRoots = roots
47-
return
55+
return roots, nil
4856
}
4957
}
5058

51-
// All of the files failed to load. systemRoots will be nil which will
52-
// trigger a specific error at verification time.
59+
return nil, firstErr
5360
}

src/crypto/x509/root_windows.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -225,5 +225,4 @@ func (c *Certificate) systemVerify(opts *VerifyOptions) (chains [][]*Certificate
225225
return chains, nil
226226
}
227227

228-
func initSystemRoots() {
229-
}
228+
func loadSystemRoots() (*CertPool, error) { return nil, nil }

src/crypto/x509/verify.go

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -117,10 +117,16 @@ func (e UnknownAuthorityError) Error() string {
117117
}
118118

119119
// SystemRootsError results when we fail to load the system root certificates.
120-
type SystemRootsError struct{}
120+
type SystemRootsError struct {
121+
Err error
122+
}
121123

122-
func (SystemRootsError) Error() string {
123-
return "x509: failed to load system roots and no roots provided"
124+
func (se SystemRootsError) Error() string {
125+
msg := "x509: failed to load system roots and no roots provided"
126+
if se.Err != nil {
127+
return msg + "; " + se.Err.Error()
128+
}
129+
return msg
124130
}
125131

126132
// errNotParsed is returned when a certificate without ASN.1 contents is
@@ -240,7 +246,7 @@ func (c *Certificate) Verify(opts VerifyOptions) (chains [][]*Certificate, err e
240246
if opts.Roots == nil {
241247
opts.Roots = systemRootsPool()
242248
if opts.Roots == nil {
243-
return nil, SystemRootsError{}
249+
return nil, SystemRootsError{systemRootsErr}
244250
}
245251
}
246252

0 commit comments

Comments
 (0)