...

Source file src/crypto/x509/root_unix_test.go

Documentation: crypto/x509

		 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  //go:build dragonfly || freebsd || linux || netbsd || openbsd || solaris
		 6  // +build dragonfly freebsd linux netbsd openbsd solaris
		 7  
		 8  package x509
		 9  
		10  import (
		11  	"bytes"
		12  	"fmt"
		13  	"os"
		14  	"path/filepath"
		15  	"reflect"
		16  	"strings"
		17  	"testing"
		18  )
		19  
		20  const (
		21  	testDir		 = "testdata"
		22  	testDirCN	 = "test-dir"
		23  	testFile		= "test-file.crt"
		24  	testFileCN	= "test-file"
		25  	testMissing = "missing"
		26  )
		27  
		28  func TestEnvVars(t *testing.T) {
		29  	testCases := []struct {
		30  		name		string
		31  		fileEnv string
		32  		dirEnv	string
		33  		files	 []string
		34  		dirs		[]string
		35  		cns		 []string
		36  	}{
		37  		{
		38  			// Environment variables override the default locations preventing fall through.
		39  			name:		"override-defaults",
		40  			fileEnv: testMissing,
		41  			dirEnv:	testMissing,
		42  			files:	 []string{testFile},
		43  			dirs:		[]string{testDir},
		44  			cns:		 nil,
		45  		},
		46  		{
		47  			// File environment overrides default file locations.
		48  			name:		"file",
		49  			fileEnv: testFile,
		50  			dirEnv:	"",
		51  			files:	 nil,
		52  			dirs:		nil,
		53  			cns:		 []string{testFileCN},
		54  		},
		55  		{
		56  			// Directory environment overrides default directory locations.
		57  			name:		"dir",
		58  			fileEnv: "",
		59  			dirEnv:	testDir,
		60  			files:	 nil,
		61  			dirs:		nil,
		62  			cns:		 []string{testDirCN},
		63  		},
		64  		{
		65  			// File & directory environment overrides both default locations.
		66  			name:		"file+dir",
		67  			fileEnv: testFile,
		68  			dirEnv:	testDir,
		69  			files:	 nil,
		70  			dirs:		nil,
		71  			cns:		 []string{testFileCN, testDirCN},
		72  		},
		73  		{
		74  			// Environment variable empty / unset uses default locations.
		75  			name:		"empty-fall-through",
		76  			fileEnv: "",
		77  			dirEnv:	"",
		78  			files:	 []string{testFile},
		79  			dirs:		[]string{testDir},
		80  			cns:		 []string{testFileCN, testDirCN},
		81  		},
		82  	}
		83  
		84  	// Save old settings so we can restore before the test ends.
		85  	origCertFiles, origCertDirectories := certFiles, certDirectories
		86  	origFile, origDir := os.Getenv(certFileEnv), os.Getenv(certDirEnv)
		87  	defer func() {
		88  		certFiles = origCertFiles
		89  		certDirectories = origCertDirectories
		90  		os.Setenv(certFileEnv, origFile)
		91  		os.Setenv(certDirEnv, origDir)
		92  	}()
		93  
		94  	for _, tc := range testCases {
		95  		t.Run(tc.name, func(t *testing.T) {
		96  			if err := os.Setenv(certFileEnv, tc.fileEnv); err != nil {
		97  				t.Fatalf("setenv %q failed: %v", certFileEnv, err)
		98  			}
		99  			if err := os.Setenv(certDirEnv, tc.dirEnv); err != nil {
	 100  				t.Fatalf("setenv %q failed: %v", certDirEnv, err)
	 101  			}
	 102  
	 103  			certFiles, certDirectories = tc.files, tc.dirs
	 104  
	 105  			r, err := loadSystemRoots()
	 106  			if err != nil {
	 107  				t.Fatal("unexpected failure:", err)
	 108  			}
	 109  
	 110  			if r == nil {
	 111  				t.Fatal("nil roots")
	 112  			}
	 113  
	 114  			// Verify that the returned certs match, otherwise report where the mismatch is.
	 115  			for i, cn := range tc.cns {
	 116  				if i >= r.len() {
	 117  					t.Errorf("missing cert %v @ %v", cn, i)
	 118  				} else if r.mustCert(t, i).Subject.CommonName != cn {
	 119  					fmt.Printf("%#v\n", r.mustCert(t, 0).Subject)
	 120  					t.Errorf("unexpected cert common name %q, want %q", r.mustCert(t, i).Subject.CommonName, cn)
	 121  				}
	 122  			}
	 123  			if r.len() > len(tc.cns) {
	 124  				t.Errorf("got %v certs, which is more than %v wanted", r.len(), len(tc.cns))
	 125  			}
	 126  		})
	 127  	}
	 128  }
	 129  
	 130  // Ensure that "SSL_CERT_DIR" when used as the environment
	 131  // variable delimited by colons, allows loadSystemRoots to
	 132  // load all the roots from the respective directories.
	 133  // See https://golang.org/issue/35325.
	 134  func TestLoadSystemCertsLoadColonSeparatedDirs(t *testing.T) {
	 135  	origFile, origDir := os.Getenv(certFileEnv), os.Getenv(certDirEnv)
	 136  	origCertFiles := certFiles[:]
	 137  
	 138  	// To prevent any other certs from being loaded in
	 139  	// through "SSL_CERT_FILE" or from known "certFiles",
	 140  	// clear them all, and they'll be reverting on defer.
	 141  	certFiles = certFiles[:0]
	 142  	os.Setenv(certFileEnv, "")
	 143  
	 144  	defer func() {
	 145  		certFiles = origCertFiles[:]
	 146  		os.Setenv(certDirEnv, origDir)
	 147  		os.Setenv(certFileEnv, origFile)
	 148  	}()
	 149  
	 150  	tmpDir := t.TempDir()
	 151  
	 152  	rootPEMs := []string{
	 153  		geoTrustRoot,
	 154  		googleLeaf,
	 155  		startComRoot,
	 156  	}
	 157  
	 158  	var certDirs []string
	 159  	for i, certPEM := range rootPEMs {
	 160  		certDir := filepath.Join(tmpDir, fmt.Sprintf("cert-%d", i))
	 161  		if err := os.MkdirAll(certDir, 0755); err != nil {
	 162  			t.Fatalf("Failed to create certificate dir: %v", err)
	 163  		}
	 164  		certOutFile := filepath.Join(certDir, "cert.crt")
	 165  		if err := os.WriteFile(certOutFile, []byte(certPEM), 0655); err != nil {
	 166  			t.Fatalf("Failed to write certificate to file: %v", err)
	 167  		}
	 168  		certDirs = append(certDirs, certDir)
	 169  	}
	 170  
	 171  	// Sanity check: the number of certDirs should be equal to the number of roots.
	 172  	if g, w := len(certDirs), len(rootPEMs); g != w {
	 173  		t.Fatalf("Failed sanity check: len(certsDir)=%d is not equal to len(rootsPEMS)=%d", g, w)
	 174  	}
	 175  
	 176  	// Now finally concatenate them with a colon.
	 177  	colonConcatCertDirs := strings.Join(certDirs, ":")
	 178  	os.Setenv(certDirEnv, colonConcatCertDirs)
	 179  	gotPool, err := loadSystemRoots()
	 180  	if err != nil {
	 181  		t.Fatalf("Failed to load system roots: %v", err)
	 182  	}
	 183  	subjects := gotPool.Subjects()
	 184  	// We expect exactly len(rootPEMs) subjects back.
	 185  	if g, w := len(subjects), len(rootPEMs); g != w {
	 186  		t.Fatalf("Invalid number of subjects: got %d want %d", g, w)
	 187  	}
	 188  
	 189  	wantPool := NewCertPool()
	 190  	for _, certPEM := range rootPEMs {
	 191  		wantPool.AppendCertsFromPEM([]byte(certPEM))
	 192  	}
	 193  	strCertPool := func(p *CertPool) string {
	 194  		return string(bytes.Join(p.Subjects(), []byte("\n")))
	 195  	}
	 196  
	 197  	if !certPoolEqual(gotPool, wantPool) {
	 198  		g, w := strCertPool(gotPool), strCertPool(wantPool)
	 199  		t.Fatalf("Mismatched certPools\nGot:\n%s\n\nWant:\n%s", g, w)
	 200  	}
	 201  }
	 202  
	 203  func TestReadUniqueDirectoryEntries(t *testing.T) {
	 204  	tmp := t.TempDir()
	 205  	temp := func(base string) string { return filepath.Join(tmp, base) }
	 206  	if f, err := os.Create(temp("file")); err != nil {
	 207  		t.Fatal(err)
	 208  	} else {
	 209  		f.Close()
	 210  	}
	 211  	if err := os.Symlink("target-in", temp("link-in")); err != nil {
	 212  		t.Fatal(err)
	 213  	}
	 214  	if err := os.Symlink("../target-out", temp("link-out")); err != nil {
	 215  		t.Fatal(err)
	 216  	}
	 217  	got, err := readUniqueDirectoryEntries(tmp)
	 218  	if err != nil {
	 219  		t.Fatal(err)
	 220  	}
	 221  	gotNames := []string{}
	 222  	for _, fi := range got {
	 223  		gotNames = append(gotNames, fi.Name())
	 224  	}
	 225  	wantNames := []string{"file", "link-out"}
	 226  	if !reflect.DeepEqual(gotNames, wantNames) {
	 227  		t.Errorf("got %q; want %q", gotNames, wantNames)
	 228  	}
	 229  }
	 230  

View as plain text