mirror of
https://github.com/golang/go
synced 2024-11-23 18:30:06 -07:00
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 <bradfitz@golang.org> Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org>
This commit is contained in:
parent
4d7a5edb7e
commit
e83bcd95a4
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
127
src/crypto/x509/root_unix_test.go
Normal file
127
src/crypto/x509/root_unix_test.go
Normal file
@ -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))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
32
src/crypto/x509/test-file.crt
Normal file
32
src/crypto/x509/test-file.crt
Normal file
@ -0,0 +1,32 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
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
|
||||
vQ==
|
||||
-----END CERTIFICATE-----
|
31
src/crypto/x509/testdata/test-dir.crt
vendored
Normal file
31
src/crypto/x509/testdata/test-dir.crt
vendored
Normal file
@ -0,0 +1,31 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
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
|
||||
-----END CERTIFICATE-----
|
@ -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 (
|
||||
|
Loading…
Reference in New Issue
Block a user