1
0
mirror of https://github.com/golang/go synced 2024-11-17 13:04:54 -07:00

crypto/x509: load roots from colon separated SSL_CERT_DIR in loadSystemRoots

"SSL_CERT_DIR" is meant to hold more than one directory, when a colon
is used as a delimiter. However, we assumed it'd be a single directory
for all root certificates.
OpenSSL and BoringSSL properly respected the colon separated
"SSL_CERT_DIR", as per:
* OpenSSL 12a765a523/crypto/x509/by_dir.c (L153-L209)
* BoringSSL 3ba9586bc0/crypto/x509/by_dir.c (L194-L247)

This change adds that parity to loadSystemRoots.

RELNOTE=yes

Fixes #35325

Change-Id: I0d554a00ccc34300a7f0529aa741ee7e2d5762f9
Reviewed-on: https://go-review.googlesource.com/c/go/+/205237
Run-TryBot: Emmanuel Odeke <emm.odeke@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
This commit is contained in:
Emmanuel T Odeke 2019-11-04 09:19:59 -08:00 committed by Emmanuel Odeke
parent 6052838bc3
commit 7a03d79498
2 changed files with 91 additions and 7 deletions

View File

@ -9,6 +9,7 @@ package x509
import (
"io/ioutil"
"os"
"strings"
)
// Possible directories with certificate files; stop after successfully
@ -29,6 +30,8 @@ const (
// certDirEnv is the environment variable which identifies which directory
// to check for SSL certificate files. If set this overrides the system default.
// It is a colon separated list of directories.
// See https://www.openssl.org/docs/man1.0.2/man1/c_rehash.html.
certDirEnv = "SSL_CERT_DIR"
)
@ -58,7 +61,11 @@ func loadSystemRoots() (*CertPool, error) {
dirs := certDirectories
if d := os.Getenv(certDirEnv); d != "" {
dirs = []string{d}
// OpenSSL and BoringSSL both use ":" as the SSL_CERT_DIR separator.
// See:
// * https://golang.org/issue/35325
// * https://www.openssl.org/docs/man1.0.2/man1/c_rehash.html
dirs = strings.Split(d, ":")
}
for _, directory := range dirs {
@ -69,16 +76,12 @@ func loadSystemRoots() (*CertPool, error) {
}
continue
}
rootsAdded := false
for _, fi := range fis {
data, err := ioutil.ReadFile(directory + "/" + fi.Name())
if err == nil && roots.AppendCertsFromPEM(data) {
rootsAdded = true
if err == nil {
roots.AppendCertsFromPEM(data)
}
}
if rootsAdded {
return roots, nil
}
}
if len(roots.certs) > 0 || firstErr == nil {

View File

@ -7,8 +7,13 @@
package x509
import (
"bytes"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"reflect"
"strings"
"testing"
)
@ -121,3 +126,79 @@ func TestEnvVars(t *testing.T) {
})
}
}
// Ensure that "SSL_CERT_DIR" when used as the environment
// variable delimited by colons, allows loadSystemRoots to
// load all the roots from the respective directories.
// See https://golang.org/issue/35325.
func TestLoadSystemCertsLoadColonSeparatedDirs(t *testing.T) {
origFile, origDir := os.Getenv(certFileEnv), os.Getenv(certDirEnv)
origCertFiles := certFiles[:]
// To prevent any other certs from being loaded in
// through "SSL_CERT_FILE" or from known "certFiles",
// clear them all, and they'll be reverting on defer.
certFiles = certFiles[:0]
os.Setenv(certFileEnv, "")
defer func() {
certFiles = origCertFiles[:]
os.Setenv(certDirEnv, origDir)
os.Setenv(certFileEnv, origFile)
}()
tmpDir, err := ioutil.TempDir(os.TempDir(), "x509-issue35325")
if err != nil {
t.Fatalf("Failed to create temporary directory: %v", err)
}
defer os.RemoveAll(tmpDir)
rootPEMs := []string{
geoTrustRoot,
googleLeaf,
startComRoot,
}
var certDirs []string
for i, certPEM := range rootPEMs {
certDir := filepath.Join(tmpDir, fmt.Sprintf("cert-%d", i))
if err := os.MkdirAll(certDir, 0755); err != nil {
t.Fatalf("Failed to create certificate dir: %v", err)
}
certOutFile := filepath.Join(certDir, "cert.crt")
if err := ioutil.WriteFile(certOutFile, []byte(certPEM), 0655); err != nil {
t.Fatalf("Failed to write certificate to file: %v", err)
}
certDirs = append(certDirs, certDir)
}
// Sanity check: the number of certDirs should be equal to the number of roots.
if g, w := len(certDirs), len(rootPEMs); g != w {
t.Fatalf("Failed sanity check: len(certsDir)=%d is not equal to len(rootsPEMS)=%d", g, w)
}
// Now finally concatenate them with a colon.
colonConcatCertDirs := strings.Join(certDirs, ":")
os.Setenv(certDirEnv, colonConcatCertDirs)
gotPool, err := loadSystemRoots()
if err != nil {
t.Fatalf("Failed to load system roots: %v", err)
}
subjects := gotPool.Subjects()
// We expect exactly len(rootPEMs) subjects back.
if g, w := len(subjects), len(rootPEMs); g != w {
t.Fatalf("Invalid number of subjects: got %d want %d", g, w)
}
wantPool := NewCertPool()
for _, certPEM := range rootPEMs {
wantPool.AppendCertsFromPEM([]byte(certPEM))
}
strCertPool := func(p *CertPool) string {
return string(bytes.Join(p.Subjects(), []byte("\n")))
}
if !reflect.DeepEqual(gotPool, wantPool) {
g, w := strCertPool(gotPool), strCertPool(wantPool)
t.Fatalf("Mismatched certPools\nGot:\n%s\n\nWant:\n%s", g, w)
}
}