Skip to content

Commit

Permalink
crypto/x509: add SystemCertPool, refactor system cert pool loading
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
bradfitz committed Mar 31, 2016
1 parent 71ab3c1 commit a62ae9f
Show file tree
Hide file tree
Showing 10 changed files with 80 additions and 41 deletions.
24 changes: 18 additions & 6 deletions src/crypto/x509/cert_pool.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ package x509

import (
"encoding/pem"
"errors"
"runtime"
)

// CertPool is a set of certificates.
Expand All @@ -18,12 +20,22 @@ type CertPool struct {
// NewCertPool returns a new, empty CertPool.
func NewCertPool() *CertPool {
return &CertPool{
make(map[string][]int),
make(map[string][]int),
nil,
bySubjectKeyId: make(map[string][]int),
byName: make(map[string][]int),
}
}

// SystemCertPool returns a copy of the system cert pool.
//
// Any mutations to the returned pool are not written to disk and do
// not affect any other pool.
func SystemCertPool() (*CertPool, error) {
if runtime.GOOS == "windows" {
return nil, errors.New("crypto/x509: system root pool is not available on Windows")
}
return loadSystemRoots()
}

// findVerifiedParents attempts to find certificates in s which have signed the
// given certificate. If any candidates were rejected then errCert will be set
// to one of them, arbitrarily, and err will contain the reason that it was
Expand Down Expand Up @@ -107,10 +119,10 @@ func (s *CertPool) AppendCertsFromPEM(pemCerts []byte) (ok bool) {

// Subjects returns a list of the DER-encoded subjects of
// all of the certificates in the pool.
func (s *CertPool) Subjects() (res [][]byte) {
res = make([][]byte, len(s.certs))
func (s *CertPool) Subjects() [][]byte {
res := make([][]byte, len(s.certs))
for i, c := range s.certs {
res[i] = c.RawSubject
}
return
return res
}
9 changes: 7 additions & 2 deletions src/crypto/x509/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,16 @@ package x509
import "sync"

var (
once sync.Once
systemRoots *CertPool
once sync.Once
systemRoots *CertPool
systemRootsErr error
)

func systemRootsPool() *CertPool {
once.Do(initSystemRoots)
return systemRoots
}

func initSystemRoots() {
systemRoots, systemRootsErr = loadSystemRoots()
}
12 changes: 8 additions & 4 deletions src/crypto/x509/root_cgo_darwin.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,19 +61,23 @@ int FetchPEMRoots(CFDataRef *pemRoots) {
}
*/
import "C"
import "unsafe"
import (
"errors"
"unsafe"
)

func initSystemRoots() {
func loadSystemRoots() (*CertPool, error) {
roots := NewCertPool()

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

defer C.CFRelease(C.CFTypeRef(data))
buf := C.GoBytes(unsafe.Pointer(C.CFDataGetBytePtr(data)), C.int(C.CFDataGetLength(data)))
roots.AppendCertsFromPEM(buf)
systemRoots = roots
return roots, nil
}
7 changes: 4 additions & 3 deletions src/crypto/x509/root_darwin_arm_gen.go
Original file line number Diff line number Diff line change
Expand Up @@ -184,8 +184,9 @@ const header = `
package x509
func initSystemRoots() {
systemRoots = NewCertPool()
systemRoots.AppendCertsFromPEM([]byte(systemRootsPEM))
func loadSystemRoots() (*CertPool, error) {
p := NewCertPool()
p.AppendCertsFromPEM([]byte(systemRootsPEM))
return p
}
`
7 changes: 4 additions & 3 deletions src/crypto/x509/root_darwin_armx.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,10 @@

package x509

func initSystemRoots() {
systemRoots = NewCertPool()
systemRoots.AppendCertsFromPEM([]byte(systemRootsPEM))
func loadSystemRoots() (*CertPool, error) {
p := NewCertPool()
p.AppendCertsFromPEM([]byte(systemRootsPEM))
return p
}

const systemRootsPEM = `
Expand Down
4 changes: 2 additions & 2 deletions src/crypto/x509/root_nocgo_darwin.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,6 @@

package x509

func initSystemRoots() {
systemRoots, _ = execSecurityRoots()
func loadSystemRoots() (*CertPool, error) {
return execSecurityRoots()
}
18 changes: 11 additions & 7 deletions src/crypto/x509/root_plan9.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@

package x509

import "io/ioutil"
import (
"io/ioutil"
"os"
)

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

func initSystemRoots() {
func loadSystemRoots() (*CertPool, error) {
roots := NewCertPool()
var bestErr error
for _, file := range certFiles {
data, err := ioutil.ReadFile(file)
if err == nil {
roots.AppendCertsFromPEM(data)
systemRoots = roots
return
return roots, nil
}
if bestErr == nil || (os.IsNotExist(bestErr) && !os.IsNotExist(err)) {
bestErr = err
}
}

// All of the files failed to load. systemRoots will be nil which will
// trigger a specific error at verification time.
return nil, bestErr
}
23 changes: 15 additions & 8 deletions src/crypto/x509/root_unix.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@

package x509

import "io/ioutil"
import (
"io/ioutil"
"os"
)

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

func initSystemRoots() {
func loadSystemRoots() (*CertPool, error) {
roots := NewCertPool()
var firstErr error
for _, file := range certFiles {
data, err := ioutil.ReadFile(file)
if err == nil {
roots.AppendCertsFromPEM(data)
systemRoots = roots
return
return roots, nil
}
if firstErr == nil && !os.IsNotExist(err) {
firstErr = err
}
}

for _, directory := range certDirectories {
fis, err := ioutil.ReadDir(directory)
if err != nil {
if firstErr == nil && !os.IsNotExist(err) {
firstErr = err
}
continue
}
rootsAdded := false
Expand All @@ -43,11 +52,9 @@ func initSystemRoots() {
}
}
if rootsAdded {
systemRoots = roots
return
return roots, nil
}
}

// All of the files failed to load. systemRoots will be nil which will
// trigger a specific error at verification time.
return nil, firstErr
}
3 changes: 1 addition & 2 deletions src/crypto/x509/root_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -225,5 +225,4 @@ func (c *Certificate) systemVerify(opts *VerifyOptions) (chains [][]*Certificate
return chains, nil
}

func initSystemRoots() {
}
func loadSystemRoots() (*CertPool, error) { return nil, nil }
14 changes: 10 additions & 4 deletions src/crypto/x509/verify.go
Original file line number Diff line number Diff line change
Expand Up @@ -117,10 +117,16 @@ func (e UnknownAuthorityError) Error() string {
}

// SystemRootsError results when we fail to load the system root certificates.
type SystemRootsError struct{}
type SystemRootsError struct {
Err error
}

func (SystemRootsError) Error() string {
return "x509: failed to load system roots and no roots provided"
func (se SystemRootsError) Error() string {
msg := "x509: failed to load system roots and no roots provided"
if se.Err != nil {
return msg + "; " + se.Err.Error()
}
return msg
}

// errNotParsed is returned when a certificate without ASN.1 contents is
Expand Down Expand Up @@ -240,7 +246,7 @@ func (c *Certificate) Verify(opts VerifyOptions) (chains [][]*Certificate, err e
if opts.Roots == nil {
opts.Roots = systemRootsPool()
if opts.Roots == nil {
return nil, SystemRootsError{}
return nil, SystemRootsError{systemRootsErr}
}
}

Expand Down

0 comments on commit a62ae9f

Please sign in to comment.