Skip to content

Commit e83bcd9

Browse files
stevenhbradfitz
authored andcommitted
crypto/x509: load certs from env vars + extra locations
Add the ability to override the default file and directory from which certificates are loaded by setting the OpenSSL compatible environment variables: SSL_CERT_FILE, SSL_CERT_DIR. If the variables are set the default locations are not checked. Added new default file "/usr/local/etc/ssl/cert.pem" for FreeBSD. Certificates in the first valid location found for both file and directory are added, instead of only the first file location if a valid one was found, which is consistent with OpenSSL. Fixes #3905 Fixes #14022 Fixes #14311 Fixes #16920 Fixes #18813 - If user sets SSL_CERT_FILE. Change-Id: Ia24fb7c1c2ffff4338b4cf214bd040326ce27bb0 Reviewed-on: https://go-review.googlesource.com/36093 Reviewed-by: Brad Fitzpatrick <[email protected]> Run-TryBot: Brad Fitzpatrick <[email protected]> TryBot-Result: Gobot Gobot <[email protected]>
1 parent 4d7a5ed commit e83bcd9

File tree

6 files changed

+227
-4
lines changed

6 files changed

+227
-4
lines changed

src/crypto/x509/root_bsd.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@ package x509
88

99
// Possible certificate files; stop after finding one.
1010
var certFiles = []string{
11-
"/usr/local/share/certs/ca-root-nss.crt", // FreeBSD/DragonFly
11+
"/usr/local/etc/ssl/cert.pem", // FreeBSD
1212
"/etc/ssl/cert.pem", // OpenBSD
13+
"/usr/local/share/certs/ca-root-nss.crt", // DragonFly
1314
"/etc/openssl/certs/ca-certificates.crt", // NetBSD
1415
}

src/crypto/x509/root_unix.go

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,27 +16,51 @@ import (
1616
var certDirectories = []string{
1717
"/etc/ssl/certs", // SLES10/SLES11, https://golang.org/issue/12139
1818
"/system/etc/security/cacerts", // Android
19+
"/usr/local/share/certs", // FreeBSD
20+
"/etc/pki/tls/certs", // Fedora/RHEL
21+
"/etc/openssl/certs", // NetBSD
1922
}
2023

24+
const (
25+
// certFileEnv is the environment variable which identifies where to locate
26+
// the SSL certificate file. If set this overrides the system default.
27+
certFileEnv = "SSL_CERT_FILE"
28+
29+
// certDirEnv is the environment variable which identifies which directory
30+
// to check for SSL certificate files. If set this overrides the system default.
31+
certDirEnv = "SSL_CERT_DIR"
32+
)
33+
2134
func (c *Certificate) systemVerify(opts *VerifyOptions) (chains [][]*Certificate, err error) {
2235
return nil, nil
2336
}
2437

2538
func loadSystemRoots() (*CertPool, error) {
2639
roots := NewCertPool()
40+
41+
files := certFiles
42+
if f := os.Getenv(certFileEnv); f != "" {
43+
files = []string{f}
44+
}
45+
2746
var firstErr error
28-
for _, file := range certFiles {
47+
for _, file := range files {
2948
data, err := ioutil.ReadFile(file)
3049
if err == nil {
3150
roots.AppendCertsFromPEM(data)
32-
return roots, nil
51+
break
3352
}
3453
if firstErr == nil && !os.IsNotExist(err) {
3554
firstErr = err
3655
}
3756
}
3857

39-
for _, directory := range certDirectories {
58+
dirs := certDirectories
59+
if d := os.Getenv(certDirEnv); d != "" {
60+
dirs = []string{d}
61+
}
62+
63+
for _, directory := range dirs {
4064
fis, err := ioutil.ReadDir(directory)
4165
if err != nil {
4266
if firstErr == nil && !os.IsNotExist(err) {
@@ -56,5 +80,9 @@ func loadSystemRoots() (*CertPool, error) {
5680
}
5781
}
5882

83+
if len(roots.certs) > 0 {
84+
return roots, nil
85+
}
86+
5987
return nil, firstErr
6088
}

src/crypto/x509/root_unix_test.go

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
// Copyright 2017 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+
// +build dragonfly freebsd linux netbsd openbsd solaris
6+
7+
package x509
8+
9+
import (
10+
"fmt"
11+
"os"
12+
"testing"
13+
)
14+
15+
const (
16+
testDir = "testdata"
17+
testDirCN = "test-dir"
18+
testFile = "test-file.crt"
19+
testFileCN = "test-file"
20+
testMissing = "missing"
21+
)
22+
23+
func TestEnvVars(t *testing.T) {
24+
testCases := []struct {
25+
name string
26+
fileEnv string
27+
dirEnv string
28+
files []string
29+
dirs []string
30+
cns []string
31+
}{
32+
{
33+
// Environment variables override the default locations preventing fall through.
34+
name: "override-defaults",
35+
fileEnv: testMissing,
36+
dirEnv: testMissing,
37+
files: []string{testFile},
38+
dirs: []string{testDir},
39+
cns: nil,
40+
},
41+
{
42+
// File environment overrides default file locations.
43+
name: "file",
44+
fileEnv: testFile,
45+
dirEnv: "",
46+
files: nil,
47+
dirs: nil,
48+
cns: []string{testFileCN},
49+
},
50+
{
51+
// Directory environment overrides default directory locations.
52+
name: "dir",
53+
fileEnv: "",
54+
dirEnv: testDir,
55+
files: nil,
56+
dirs: nil,
57+
cns: []string{testDirCN},
58+
},
59+
{
60+
// File & directory environment overrides both default locations.
61+
name: "file+dir",
62+
fileEnv: testFile,
63+
dirEnv: testDir,
64+
files: nil,
65+
dirs: nil,
66+
cns: []string{testFileCN, testDirCN},
67+
},
68+
{
69+
// Environment variable empty / unset uses default locations.
70+
name: "empty-fall-through",
71+
fileEnv: "",
72+
dirEnv: "",
73+
files: []string{testFile},
74+
dirs: []string{testDir},
75+
cns: []string{testFileCN, testDirCN},
76+
},
77+
}
78+
79+
// Save old settings so we can restore before the test ends.
80+
origCertFiles, origCertDirectories := certFiles, certDirectories
81+
origFile, origDir := os.Getenv(certFileEnv), os.Getenv(certDirEnv)
82+
defer func() {
83+
certFiles = origCertFiles
84+
certDirectories = origCertDirectories
85+
os.Setenv(certFileEnv, origFile)
86+
os.Setenv(certDirEnv, origDir)
87+
}()
88+
89+
for _, tc := range testCases {
90+
t.Run(tc.name, func(t *testing.T) {
91+
if err := os.Setenv(certFileEnv, tc.fileEnv); err != nil {
92+
t.Fatalf("setenv %q failed: %v", certFileEnv, err)
93+
}
94+
if err := os.Setenv(certDirEnv, tc.dirEnv); err != nil {
95+
t.Fatalf("setenv %q failed: %v", certDirEnv, err)
96+
}
97+
98+
certFiles, certDirectories = tc.files, tc.dirs
99+
100+
r, err := loadSystemRoots()
101+
if err != nil {
102+
t.Fatal("unexpected failure: ", err)
103+
}
104+
105+
if r == nil {
106+
if tc.cns == nil {
107+
// Expected nil
108+
return
109+
}
110+
t.Fatal("nil roots")
111+
}
112+
113+
for i, cn := range tc.cns {
114+
if i > len(r.certs) {
115+
t.Errorf("missing cert %v @ %v", cn, i)
116+
} else if r.certs[i].Subject.CommonName != cn {
117+
fmt.Printf("%#v\n", r.certs[0].Subject)
118+
t.Errorf("unexpected cert common name %q expected %q", r.certs[i].Subject.CommonName, cn)
119+
}
120+
}
121+
122+
if len(r.certs) > len(tc.cns) {
123+
t.Errorf("expected %v certs got %v", len(tc.cns), len(r.certs))
124+
}
125+
})
126+
}
127+
}

src/crypto/x509/test-file.crt

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
-----BEGIN CERTIFICATE-----
2+
MIIFbTCCA1WgAwIBAgIJAN338vEmMtLsMA0GCSqGSIb3DQEBCwUAME0xCzAJBgNV
3+
BAYTAlVLMRMwEQYDVQQIDApUZXN0LVN0YXRlMRUwEwYDVQQKDAxHb2xhbmcgVGVz
4+
dHMxEjAQBgNVBAMMCXRlc3QtZmlsZTAeFw0xNzAyMDEyMzUyMDhaFw0yNzAxMzAy
5+
MzUyMDhaME0xCzAJBgNVBAYTAlVLMRMwEQYDVQQIDApUZXN0LVN0YXRlMRUwEwYD
6+
VQQKDAxHb2xhbmcgVGVzdHMxEjAQBgNVBAMMCXRlc3QtZmlsZTCCAiIwDQYJKoZI
7+
hvcNAQEBBQADggIPADCCAgoCggIBAPMGiLjdiffQo3Xc8oUe7wsDhSaAJFOhO6Qs
8+
i0xYrYl7jmCuz9rGD2fdgk5cLqGazKuQ6fIFzHXFU2BKs4CWXt9KO0KFEhfvZeuW
9+
jG5d7C1ZUiuKOrPqjKVu8SZtFPc7y7Ke7msXzY+Z2LLyiJJ93LCMq4+cTSGNXVlI
10+
KqUxhxeoD5/QkUPyQy/ilu3GMYfx/YORhDP6Edcuskfj8wRh1UxBejP8YPMvI6St
11+
cE2GkxoEGqDWnQ/61F18te6WI3MD29tnKXOkXVhnSC+yvRLljotW2/tAhHKBG4tj
12+
iQWT5Ri4Wrw2tXxPKRLsVWc7e1/hdxhnuvYpXkWNhKsm002jzkFXlzfEwPd8nZdw
13+
5aT6gPUBN2AAzdoqZI7E200i0orEF7WaSoMfjU1tbHvExp3vyAPOfJ5PS2MQ6W03
14+
Zsy5dTVH+OBH++rkRzQCFcnIv/OIhya5XZ9KX9nFPgBEP7Xq2A+IjH7B6VN/S/bv
15+
8lhp2V+SQvlew9GttKC4hKuPsl5o7+CMbcqcNUdxm9gGkN8epGEKCuix97bpNlxN
16+
fHZxHE5+8GMzPXMkCD56y5TNKR6ut7JGHMPtGl5lPCLqzG/HzYyFgxsDfDUu2B0A
17+
GKj0lGpnLfGqwhs2/s3jpY7+pcvVQxEpvVTId5byDxu1ujP4HjO/VTQ2P72rE8Ft
18+
C6J2Av0tAgMBAAGjUDBOMB0GA1UdDgQWBBTLT/RbyfBB/Pa07oBnaM+QSJPO9TAf
19+
BgNVHSMEGDAWgBTLT/RbyfBB/Pa07oBnaM+QSJPO9TAMBgNVHRMEBTADAQH/MA0G
20+
CSqGSIb3DQEBCwUAA4ICAQB3sCntCcQwhMgRPPyvOCMyTcQ/Iv+cpfxz2Ck14nlx
21+
AkEAH2CH0ov5GWTt07/ur3aa5x+SAKi0J3wTD1cdiw4U/6Uin6jWGKKxvoo4IaeK
22+
SbM8w/6eKx6UbmHx7PA/eRABY9tTlpdPCVgw7/o3WDr03QM+IAtatzvaCPPczake
23+
pbdLwmBZB/v8V+6jUajy6jOgdSH0PyffGnt7MWgDETmNC6p/Xigp5eh+C8Fb4NGT
24+
xgHES5PBC+sruWp4u22bJGDKTvYNdZHsnw/CaKQWNsQqwisxa3/8N5v+PCff/pxl
25+
r05pE3PdHn9JrCl4iWdVlgtiI9BoPtQyDfa/OEFaScE8KYR8LxaAgdgp3zYncWls
26+
BpwQ6Y/A2wIkhlD9eEp5Ib2hz7isXOs9UwjdriKqrBXqcIAE5M+YIk3+KAQKxAtd
27+
4YsK3CSJ010uphr12YKqlScj4vuKFjuOtd5RyyMIxUG3lrrhAu2AzCeKCLdVgA8+
28+
75FrYMApUdvcjp4uzbBoED4XRQlx9kdFHVbYgmE/+yddBYJM8u4YlgAL0hW2/D8p
29+
z9JWIfxVmjJnBnXaKGBuiUyZ864A3PJndP6EMMo7TzS2CDnfCYuJjvI0KvDjFNmc
30+
rQA04+qfMSEz3nmKhbbZu4eYLzlADhfH8tT4GMtXf71WLA5AUHGf2Y4+HIHTsmHG
31+
vQ==
32+
-----END CERTIFICATE-----

src/crypto/x509/testdata/test-dir.crt

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
-----BEGIN CERTIFICATE-----
2+
MIIFazCCA1OgAwIBAgIJAL8a/lsnspOqMA0GCSqGSIb3DQEBCwUAMEwxCzAJBgNV
3+
BAYTAlVLMRMwEQYDVQQIDApUZXN0LVN0YXRlMRUwEwYDVQQKDAxHb2xhbmcgVGVz
4+
dHMxETAPBgNVBAMMCHRlc3QtZGlyMB4XDTE3MDIwMTIzNTAyN1oXDTI3MDEzMDIz
5+
NTAyN1owTDELMAkGA1UEBhMCVUsxEzARBgNVBAgMClRlc3QtU3RhdGUxFTATBgNV
6+
BAoMDEdvbGFuZyBUZXN0czERMA8GA1UEAwwIdGVzdC1kaXIwggIiMA0GCSqGSIb3
7+
DQEBAQUAA4ICDwAwggIKAoICAQDzBoi43Yn30KN13PKFHu8LA4UmgCRToTukLItM
8+
WK2Je45grs/axg9n3YJOXC6hmsyrkOnyBcx1xVNgSrOAll7fSjtChRIX72Xrloxu
9+
XewtWVIrijqz6oylbvEmbRT3O8uynu5rF82Pmdiy8oiSfdywjKuPnE0hjV1ZSCql
10+
MYcXqA+f0JFD8kMv4pbtxjGH8f2DkYQz+hHXLrJH4/MEYdVMQXoz/GDzLyOkrXBN
11+
hpMaBBqg1p0P+tRdfLXuliNzA9vbZylzpF1YZ0gvsr0S5Y6LVtv7QIRygRuLY4kF
12+
k+UYuFq8NrV8TykS7FVnO3tf4XcYZ7r2KV5FjYSrJtNNo85BV5c3xMD3fJ2XcOWk
13+
+oD1ATdgAM3aKmSOxNtNItKKxBe1mkqDH41NbWx7xMad78gDznyeT0tjEOltN2bM
14+
uXU1R/jgR/vq5Ec0AhXJyL/ziIcmuV2fSl/ZxT4ARD+16tgPiIx+welTf0v27/JY
15+
adlfkkL5XsPRrbSguISrj7JeaO/gjG3KnDVHcZvYBpDfHqRhCgrosfe26TZcTXx2
16+
cRxOfvBjMz1zJAg+esuUzSkerreyRhzD7RpeZTwi6sxvx82MhYMbA3w1LtgdABio
17+
9JRqZy3xqsIbNv7N46WO/qXL1UMRKb1UyHeW8g8btboz+B4zv1U0Nj+9qxPBbQui
18+
dgL9LQIDAQABo1AwTjAdBgNVHQ4EFgQUy0/0W8nwQfz2tO6AZ2jPkEiTzvUwHwYD
19+
VR0jBBgwFoAUy0/0W8nwQfz2tO6AZ2jPkEiTzvUwDAYDVR0TBAUwAwEB/zANBgkq
20+
hkiG9w0BAQsFAAOCAgEAvEVnUYsIOt87rggmLPqEueynkuQ+562M8EDHSQl82zbe
21+
xDCxeg3DvPgKb+RvaUdt1362z/szK10SoeMgx6+EQLoV9LiVqXwNqeYfixrhrdw3
22+
ppAhYYhymdkbUQCEMHypmXP1vPhAz4o8Bs+eES1M+zO6ErBiD7SqkmBElT+GixJC
23+
6epC9ZQFs+dw3lPlbiZSsGE85sqc3VAs0/JgpL/pb1/Eg4s0FUhZD2C2uWdSyZGc
24+
g0/v3aXJCp4j/9VoNhI1WXz3M45nysZIL5OQgXymLqJElQa1pZ3Wa4i/nidvT4AT
25+
Xlxc/qijM8set/nOqp7hVd5J0uG6qdwLRILUddZ6OpXd7ZNi1EXg+Bpc7ehzGsDt
26+
3UFGzYXDjxYnK2frQfjLS8stOQIqSrGthW6x0fdkVx0y8BByvd5J6+JmZl4UZfzA
27+
m99VxXSt4B9x6BvnY7ktzcFDOjtuLc4B/7yg9fv1eQuStA4cHGGAttsCg1X/Kx8W
28+
PvkkeH0UWDZ9vhH9K36703z89da6MWF+bz92B0+4HoOmlVaXRkvblsNaynJnL0LC
29+
Ayry7QBxuh5cMnDdRwJB3AVJIiJ1GVpb7aGvBOnx+s2lwRv9HWtghb+cbwwktx1M
30+
JHyBf3GZNSWTpKY7cD8V+NnBv3UuioOVVo+XAU4LF/bYUjdRpxWADJizNtZrtFo=
31+
-----END CERTIFICATE-----

src/crypto/x509/x509.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@
33
// license that can be found in the LICENSE file.
44

55
// Package x509 parses X.509-encoded keys and certificates.
6+
//
7+
// On UNIX systems the environment variables SSL_CERT_FILE and SSL_CERT_DIR
8+
// can be used to override the system default locations for the SSL certificate
9+
// file and SSL certificate files directory, respectively.
610
package x509
711

812
import (

0 commit comments

Comments
 (0)