mirror of
https://github.com/tailscale/tailscale.git
synced 2024-11-29 04:55:31 +00:00
f5993f2440
(from patchset 1, 7cdc3c3e7427c9ef69e19224d6036c09c5ea1723, of https://go-review.googlesource.com/c/go/+/229917/1) This will allow building CertPools that consume less memory. (Most certs are never accessed. Different users/programs access different ones, but not many.) This CL only adds the new internal mechanism (and uses it for the old AddCert) but does not modify any existing root pool behavior. (That is, the default Unix roots are still all slurped into memory as of this CL) Change-Id: Ib3a42e4050627b5e34413c595d8ced839c7bfa14
209 lines
5.6 KiB
Go
209 lines
5.6 KiB
Go
// 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 (
|
|
"bytes"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"os"
|
|
"path/filepath"
|
|
"reflect"
|
|
"strings"
|
|
"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 {
|
|
t.Fatal("nil roots")
|
|
}
|
|
|
|
// Verify that the returned certs match, otherwise report where the mismatch is.
|
|
for i, cn := range tc.cns {
|
|
if i >= r.len() {
|
|
t.Errorf("missing cert %v @ %v", cn, i)
|
|
} else if r.mustCert(i).Subject.CommonName != cn {
|
|
fmt.Printf("%#v\n", r.mustCert(0).Subject)
|
|
t.Errorf("unexpected cert common name %q, want %q", r.mustCert(i).Subject.CommonName, cn)
|
|
}
|
|
}
|
|
if r.len() > len(tc.cns) {
|
|
t.Errorf("got %v certs, which is more than %v wanted", r.len(), len(tc.cns))
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// 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")))
|
|
}
|
|
|
|
zeroPoolFuncs(gotPool)
|
|
zeroPoolFuncs(wantPool)
|
|
|
|
if !reflect.DeepEqual(gotPool, wantPool) {
|
|
g, w := strCertPool(gotPool), strCertPool(wantPool)
|
|
t.Fatalf("Mismatched certPools\nGot:\n%s\n\nWant:\n%s", g, w)
|
|
}
|
|
}
|