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