]> Cypherpunks repositories - gostls13.git/commitdiff
crypto/x509: load certs from env vars + extra locations
authorSteven Hartland <steven.hartland@multiplay.co.uk>
Thu, 2 Feb 2017 00:34:06 +0000 (00:34 +0000)
committerBrad Fitzpatrick <bradfitz@golang.org>
Wed, 3 May 2017 15:39:29 +0000 (15:39 +0000)
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 <bradfitz@golang.org>
Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>

src/crypto/x509/root_bsd.go
src/crypto/x509/root_unix.go
src/crypto/x509/root_unix_test.go [new file with mode: 0644]
src/crypto/x509/test-file.crt [new file with mode: 0644]
src/crypto/x509/testdata/test-dir.crt [new file with mode: 0644]
src/crypto/x509/x509.go

index 93172837368935ef444f7e9765e3ded5abaebe69..1371933891030e81682eb083f0aed89f9c464b67 100644 (file)
@@ -8,7 +8,8 @@ package x509
 
 // Possible certificate files; stop after finding one.
 var certFiles = []string{
-       "/usr/local/share/certs/ca-root-nss.crt", // FreeBSD/DragonFly
+       "/usr/local/etc/ssl/cert.pem",            // FreeBSD
        "/etc/ssl/cert.pem",                      // OpenBSD
+       "/usr/local/share/certs/ca-root-nss.crt", // DragonFly
        "/etc/openssl/certs/ca-certificates.crt", // NetBSD
 }
index 7bcb3d63d1f935e2a9897dac62b5d4a10b3cab8b..65b5a5fdbccb390d49733f03220e9fbddf19f508 100644 (file)
@@ -16,27 +16,51 @@ import (
 var certDirectories = []string{
        "/etc/ssl/certs",               // SLES10/SLES11, https://golang.org/issue/12139
        "/system/etc/security/cacerts", // Android
+       "/usr/local/share/certs",       // FreeBSD
+       "/etc/pki/tls/certs",           // Fedora/RHEL
+       "/etc/openssl/certs",           // NetBSD
 }
 
+const (
+       // certFileEnv is the environment variable which identifies where to locate
+       // the SSL certificate file. If set this overrides the system default.
+       certFileEnv = "SSL_CERT_FILE"
+
+       // certDirEnv is the environment variable which identifies which directory
+       // to check for SSL certificate files. If set this overrides the system default.
+       certDirEnv = "SSL_CERT_DIR"
+)
+
 func (c *Certificate) systemVerify(opts *VerifyOptions) (chains [][]*Certificate, err error) {
        return nil, nil
 }
 
 func loadSystemRoots() (*CertPool, error) {
        roots := NewCertPool()
+
+       files := certFiles
+       if f := os.Getenv(certFileEnv); f != "" {
+               files = []string{f}
+       }
+
        var firstErr error
-       for _, file := range certFiles {
+       for _, file := range files {
                data, err := ioutil.ReadFile(file)
                if err == nil {
                        roots.AppendCertsFromPEM(data)
-                       return roots, nil
+                       break
                }
                if firstErr == nil && !os.IsNotExist(err) {
                        firstErr = err
                }
        }
 
-       for _, directory := range certDirectories {
+       dirs := certDirectories
+       if d := os.Getenv(certDirEnv); d != "" {
+               dirs = []string{d}
+       }
+
+       for _, directory := range dirs {
                fis, err := ioutil.ReadDir(directory)
                if err != nil {
                        if firstErr == nil && !os.IsNotExist(err) {
@@ -56,5 +80,9 @@ func loadSystemRoots() (*CertPool, error) {
                }
        }
 
+       if len(roots.certs) > 0 {
+               return roots, nil
+       }
+
        return nil, firstErr
 }
diff --git a/src/crypto/x509/root_unix_test.go b/src/crypto/x509/root_unix_test.go
new file mode 100644 (file)
index 0000000..b6659d9
--- /dev/null
@@ -0,0 +1,127 @@
+// Copyright 2017 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build dragonfly freebsd linux netbsd openbsd solaris
+
+package x509
+
+import (
+       "fmt"
+       "os"
+       "testing"
+)
+
+const (
+       testDir     = "testdata"
+       testDirCN   = "test-dir"
+       testFile    = "test-file.crt"
+       testFileCN  = "test-file"
+       testMissing = "missing"
+)
+
+func TestEnvVars(t *testing.T) {
+       testCases := []struct {
+               name    string
+               fileEnv string
+               dirEnv  string
+               files   []string
+               dirs    []string
+               cns     []string
+       }{
+               {
+                       // Environment variables override the default locations preventing fall through.
+                       name:    "override-defaults",
+                       fileEnv: testMissing,
+                       dirEnv:  testMissing,
+                       files:   []string{testFile},
+                       dirs:    []string{testDir},
+                       cns:     nil,
+               },
+               {
+                       // File environment overrides default file locations.
+                       name:    "file",
+                       fileEnv: testFile,
+                       dirEnv:  "",
+                       files:   nil,
+                       dirs:    nil,
+                       cns:     []string{testFileCN},
+               },
+               {
+                       // Directory environment overrides default directory locations.
+                       name:    "dir",
+                       fileEnv: "",
+                       dirEnv:  testDir,
+                       files:   nil,
+                       dirs:    nil,
+                       cns:     []string{testDirCN},
+               },
+               {
+                       // File & directory environment overrides both default locations.
+                       name:    "file+dir",
+                       fileEnv: testFile,
+                       dirEnv:  testDir,
+                       files:   nil,
+                       dirs:    nil,
+                       cns:     []string{testFileCN, testDirCN},
+               },
+               {
+                       // Environment variable empty / unset uses default locations.
+                       name:    "empty-fall-through",
+                       fileEnv: "",
+                       dirEnv:  "",
+                       files:   []string{testFile},
+                       dirs:    []string{testDir},
+                       cns:     []string{testFileCN, testDirCN},
+               },
+       }
+
+       // Save old settings so we can restore before the test ends.
+       origCertFiles, origCertDirectories := certFiles, certDirectories
+       origFile, origDir := os.Getenv(certFileEnv), os.Getenv(certDirEnv)
+       defer func() {
+               certFiles = origCertFiles
+               certDirectories = origCertDirectories
+               os.Setenv(certFileEnv, origFile)
+               os.Setenv(certDirEnv, origDir)
+       }()
+
+       for _, tc := range testCases {
+               t.Run(tc.name, func(t *testing.T) {
+                       if err := os.Setenv(certFileEnv, tc.fileEnv); err != nil {
+                               t.Fatalf("setenv %q failed: %v", certFileEnv, err)
+                       }
+                       if err := os.Setenv(certDirEnv, tc.dirEnv); err != nil {
+                               t.Fatalf("setenv %q failed: %v", certDirEnv, err)
+                       }
+
+                       certFiles, certDirectories = tc.files, tc.dirs
+
+                       r, err := loadSystemRoots()
+                       if err != nil {
+                               t.Fatal("unexpected failure: ", err)
+                       }
+
+                       if r == nil {
+                               if tc.cns == nil {
+                                       // Expected nil
+                                       return
+                               }
+                               t.Fatal("nil roots")
+                       }
+
+                       for i, cn := range tc.cns {
+                               if i > len(r.certs) {
+                                       t.Errorf("missing cert %v @ %v", cn, i)
+                               } else if r.certs[i].Subject.CommonName != cn {
+                                       fmt.Printf("%#v\n", r.certs[0].Subject)
+                                       t.Errorf("unexpected cert common name %q expected %q", r.certs[i].Subject.CommonName, cn)
+                               }
+                       }
+
+                       if len(r.certs) > len(tc.cns) {
+                               t.Errorf("expected %v certs got %v", len(tc.cns), len(r.certs))
+                       }
+               })
+       }
+}
diff --git a/src/crypto/x509/test-file.crt b/src/crypto/x509/test-file.crt
new file mode 100644 (file)
index 0000000..caa83b9
--- /dev/null
@@ -0,0 +1,32 @@
+-----BEGIN CERTIFICATE-----
+MIIFbTCCA1WgAwIBAgIJAN338vEmMtLsMA0GCSqGSIb3DQEBCwUAME0xCzAJBgNV
+BAYTAlVLMRMwEQYDVQQIDApUZXN0LVN0YXRlMRUwEwYDVQQKDAxHb2xhbmcgVGVz
+dHMxEjAQBgNVBAMMCXRlc3QtZmlsZTAeFw0xNzAyMDEyMzUyMDhaFw0yNzAxMzAy
+MzUyMDhaME0xCzAJBgNVBAYTAlVLMRMwEQYDVQQIDApUZXN0LVN0YXRlMRUwEwYD
+VQQKDAxHb2xhbmcgVGVzdHMxEjAQBgNVBAMMCXRlc3QtZmlsZTCCAiIwDQYJKoZI
+hvcNAQEBBQADggIPADCCAgoCggIBAPMGiLjdiffQo3Xc8oUe7wsDhSaAJFOhO6Qs
+i0xYrYl7jmCuz9rGD2fdgk5cLqGazKuQ6fIFzHXFU2BKs4CWXt9KO0KFEhfvZeuW
+jG5d7C1ZUiuKOrPqjKVu8SZtFPc7y7Ke7msXzY+Z2LLyiJJ93LCMq4+cTSGNXVlI
+KqUxhxeoD5/QkUPyQy/ilu3GMYfx/YORhDP6Edcuskfj8wRh1UxBejP8YPMvI6St
+cE2GkxoEGqDWnQ/61F18te6WI3MD29tnKXOkXVhnSC+yvRLljotW2/tAhHKBG4tj
+iQWT5Ri4Wrw2tXxPKRLsVWc7e1/hdxhnuvYpXkWNhKsm002jzkFXlzfEwPd8nZdw
+5aT6gPUBN2AAzdoqZI7E200i0orEF7WaSoMfjU1tbHvExp3vyAPOfJ5PS2MQ6W03
+Zsy5dTVH+OBH++rkRzQCFcnIv/OIhya5XZ9KX9nFPgBEP7Xq2A+IjH7B6VN/S/bv
+8lhp2V+SQvlew9GttKC4hKuPsl5o7+CMbcqcNUdxm9gGkN8epGEKCuix97bpNlxN
+fHZxHE5+8GMzPXMkCD56y5TNKR6ut7JGHMPtGl5lPCLqzG/HzYyFgxsDfDUu2B0A
+GKj0lGpnLfGqwhs2/s3jpY7+pcvVQxEpvVTId5byDxu1ujP4HjO/VTQ2P72rE8Ft
+C6J2Av0tAgMBAAGjUDBOMB0GA1UdDgQWBBTLT/RbyfBB/Pa07oBnaM+QSJPO9TAf
+BgNVHSMEGDAWgBTLT/RbyfBB/Pa07oBnaM+QSJPO9TAMBgNVHRMEBTADAQH/MA0G
+CSqGSIb3DQEBCwUAA4ICAQB3sCntCcQwhMgRPPyvOCMyTcQ/Iv+cpfxz2Ck14nlx
+AkEAH2CH0ov5GWTt07/ur3aa5x+SAKi0J3wTD1cdiw4U/6Uin6jWGKKxvoo4IaeK
+SbM8w/6eKx6UbmHx7PA/eRABY9tTlpdPCVgw7/o3WDr03QM+IAtatzvaCPPczake
+pbdLwmBZB/v8V+6jUajy6jOgdSH0PyffGnt7MWgDETmNC6p/Xigp5eh+C8Fb4NGT
+xgHES5PBC+sruWp4u22bJGDKTvYNdZHsnw/CaKQWNsQqwisxa3/8N5v+PCff/pxl
+r05pE3PdHn9JrCl4iWdVlgtiI9BoPtQyDfa/OEFaScE8KYR8LxaAgdgp3zYncWls
+BpwQ6Y/A2wIkhlD9eEp5Ib2hz7isXOs9UwjdriKqrBXqcIAE5M+YIk3+KAQKxAtd
+4YsK3CSJ010uphr12YKqlScj4vuKFjuOtd5RyyMIxUG3lrrhAu2AzCeKCLdVgA8+
+75FrYMApUdvcjp4uzbBoED4XRQlx9kdFHVbYgmE/+yddBYJM8u4YlgAL0hW2/D8p
+z9JWIfxVmjJnBnXaKGBuiUyZ864A3PJndP6EMMo7TzS2CDnfCYuJjvI0KvDjFNmc
+rQA04+qfMSEz3nmKhbbZu4eYLzlADhfH8tT4GMtXf71WLA5AUHGf2Y4+HIHTsmHG
+vQ==
+-----END CERTIFICATE-----
diff --git a/src/crypto/x509/testdata/test-dir.crt b/src/crypto/x509/testdata/test-dir.crt
new file mode 100644 (file)
index 0000000..b7fc9c5
--- /dev/null
@@ -0,0 +1,31 @@
+-----BEGIN CERTIFICATE-----
+MIIFazCCA1OgAwIBAgIJAL8a/lsnspOqMA0GCSqGSIb3DQEBCwUAMEwxCzAJBgNV
+BAYTAlVLMRMwEQYDVQQIDApUZXN0LVN0YXRlMRUwEwYDVQQKDAxHb2xhbmcgVGVz
+dHMxETAPBgNVBAMMCHRlc3QtZGlyMB4XDTE3MDIwMTIzNTAyN1oXDTI3MDEzMDIz
+NTAyN1owTDELMAkGA1UEBhMCVUsxEzARBgNVBAgMClRlc3QtU3RhdGUxFTATBgNV
+BAoMDEdvbGFuZyBUZXN0czERMA8GA1UEAwwIdGVzdC1kaXIwggIiMA0GCSqGSIb3
+DQEBAQUAA4ICDwAwggIKAoICAQDzBoi43Yn30KN13PKFHu8LA4UmgCRToTukLItM
+WK2Je45grs/axg9n3YJOXC6hmsyrkOnyBcx1xVNgSrOAll7fSjtChRIX72Xrloxu
+XewtWVIrijqz6oylbvEmbRT3O8uynu5rF82Pmdiy8oiSfdywjKuPnE0hjV1ZSCql
+MYcXqA+f0JFD8kMv4pbtxjGH8f2DkYQz+hHXLrJH4/MEYdVMQXoz/GDzLyOkrXBN
+hpMaBBqg1p0P+tRdfLXuliNzA9vbZylzpF1YZ0gvsr0S5Y6LVtv7QIRygRuLY4kF
+k+UYuFq8NrV8TykS7FVnO3tf4XcYZ7r2KV5FjYSrJtNNo85BV5c3xMD3fJ2XcOWk
++oD1ATdgAM3aKmSOxNtNItKKxBe1mkqDH41NbWx7xMad78gDznyeT0tjEOltN2bM
+uXU1R/jgR/vq5Ec0AhXJyL/ziIcmuV2fSl/ZxT4ARD+16tgPiIx+welTf0v27/JY
+adlfkkL5XsPRrbSguISrj7JeaO/gjG3KnDVHcZvYBpDfHqRhCgrosfe26TZcTXx2
+cRxOfvBjMz1zJAg+esuUzSkerreyRhzD7RpeZTwi6sxvx82MhYMbA3w1LtgdABio
+9JRqZy3xqsIbNv7N46WO/qXL1UMRKb1UyHeW8g8btboz+B4zv1U0Nj+9qxPBbQui
+dgL9LQIDAQABo1AwTjAdBgNVHQ4EFgQUy0/0W8nwQfz2tO6AZ2jPkEiTzvUwHwYD
+VR0jBBgwFoAUy0/0W8nwQfz2tO6AZ2jPkEiTzvUwDAYDVR0TBAUwAwEB/zANBgkq
+hkiG9w0BAQsFAAOCAgEAvEVnUYsIOt87rggmLPqEueynkuQ+562M8EDHSQl82zbe
+xDCxeg3DvPgKb+RvaUdt1362z/szK10SoeMgx6+EQLoV9LiVqXwNqeYfixrhrdw3
+ppAhYYhymdkbUQCEMHypmXP1vPhAz4o8Bs+eES1M+zO6ErBiD7SqkmBElT+GixJC
+6epC9ZQFs+dw3lPlbiZSsGE85sqc3VAs0/JgpL/pb1/Eg4s0FUhZD2C2uWdSyZGc
+g0/v3aXJCp4j/9VoNhI1WXz3M45nysZIL5OQgXymLqJElQa1pZ3Wa4i/nidvT4AT
+Xlxc/qijM8set/nOqp7hVd5J0uG6qdwLRILUddZ6OpXd7ZNi1EXg+Bpc7ehzGsDt
+3UFGzYXDjxYnK2frQfjLS8stOQIqSrGthW6x0fdkVx0y8BByvd5J6+JmZl4UZfzA
+m99VxXSt4B9x6BvnY7ktzcFDOjtuLc4B/7yg9fv1eQuStA4cHGGAttsCg1X/Kx8W
+PvkkeH0UWDZ9vhH9K36703z89da6MWF+bz92B0+4HoOmlVaXRkvblsNaynJnL0LC
+Ayry7QBxuh5cMnDdRwJB3AVJIiJ1GVpb7aGvBOnx+s2lwRv9HWtghb+cbwwktx1M
+JHyBf3GZNSWTpKY7cD8V+NnBv3UuioOVVo+XAU4LF/bYUjdRpxWADJizNtZrtFo=
+-----END CERTIFICATE-----
index 6979c685a4af23158aa2f04d7c34b9235a6d5ee9..3065fe260237a4f607f73a8f42769c7b67d401a6 100644 (file)
@@ -3,6 +3,10 @@
 // license that can be found in the LICENSE file.
 
 // Package x509 parses X.509-encoded keys and certificates.
+//
+// On UNIX systems the environment variables SSL_CERT_FILE and SSL_CERT_DIR
+// can be used to override the system default locations for the SSL certificate
+// file and SSL certificate files directory, respectively.
 package x509
 
 import (