Source file
src/crypto/x509/root_test.go
1
2
3
4
5 package x509
6
7 import (
8 "bytes"
9 "fmt"
10 "internal/testenv"
11 "os"
12 "path/filepath"
13 "runtime"
14 "slices"
15 "strings"
16 "testing"
17 )
18
19 func TestFallbackPanic(t *testing.T) {
20 defer func() {
21 if recover() == nil {
22 t.Fatal("Multiple calls to SetFallbackRoots should panic")
23 }
24 }()
25 SetFallbackRoots(nil)
26 SetFallbackRoots(nil)
27 }
28
29 func TestFallback(t *testing.T) {
30
31
32 systemRootsPool()
33 if systemRoots != nil {
34 originalSystemRoots := *systemRoots
35 defer func() { systemRoots = &originalSystemRoots }()
36 }
37
38 tests := []struct {
39 name string
40 systemRoots *CertPool
41 systemPool bool
42 poolContent []*Certificate
43 forceFallback bool
44 returnsFallback bool
45 }{
46 {
47 name: "nil systemRoots",
48 returnsFallback: true,
49 },
50 {
51 name: "empty systemRoots",
52 systemRoots: NewCertPool(),
53 returnsFallback: true,
54 },
55 {
56 name: "empty systemRoots system pool",
57 systemRoots: NewCertPool(),
58 systemPool: true,
59 },
60 {
61 name: "filled systemRoots system pool",
62 systemRoots: NewCertPool(),
63 poolContent: []*Certificate{{}},
64 systemPool: true,
65 },
66 {
67 name: "filled systemRoots",
68 systemRoots: NewCertPool(),
69 poolContent: []*Certificate{{}},
70 },
71 {
72 name: "filled systemRoots, force fallback",
73 systemRoots: NewCertPool(),
74 poolContent: []*Certificate{{}},
75 forceFallback: true,
76 returnsFallback: true,
77 },
78 {
79 name: "filled systemRoot system pool, force fallback",
80 systemRoots: NewCertPool(),
81 poolContent: []*Certificate{{}},
82 systemPool: true,
83 forceFallback: true,
84 returnsFallback: true,
85 },
86 }
87
88 for _, tc := range tests {
89 t.Run(tc.name, func(t *testing.T) {
90 useFallbackRoots = false
91 fallbacksSet = false
92 systemRoots = tc.systemRoots
93
94 if systemRoots != nil {
95 systemRoots.systemPool = tc.systemPool
96 }
97 for _, c := range tc.poolContent {
98 systemRoots.AddCert(c)
99 }
100 if tc.forceFallback {
101 t.Setenv("GODEBUG", "x509usefallbackroots=1")
102 } else {
103 t.Setenv("GODEBUG", "x509usefallbackroots=0")
104 }
105
106 fallbackPool := NewCertPool()
107 SetFallbackRoots(fallbackPool)
108
109 systemPoolIsFallback := systemRoots == fallbackPool
110
111 if tc.returnsFallback && !systemPoolIsFallback {
112 t.Error("systemRoots was not set to fallback pool")
113 } else if !tc.returnsFallback && systemPoolIsFallback {
114 t.Error("systemRoots was set to fallback pool when it shouldn't have been")
115 }
116 })
117 }
118 }
119
120 const (
121 testDirCN = "test-dir"
122 testFile = "test-file.crt"
123 testFileCN = "test-file"
124 testMissing = "missing"
125 )
126
127 func TestEnvVars(t *testing.T) {
128 tmpDir := t.TempDir()
129 testCert, err := os.ReadFile("testdata/test-dir.crt")
130 if err != nil {
131 t.Fatalf("failed to read test cert: %s", err)
132 }
133 if err := os.WriteFile(filepath.Join(tmpDir, testFile), testCert, 0644); err != nil {
134 t.Fatalf("failed to write test cert: %s", err)
135 }
136
137 testCases := []struct {
138 name string
139 fileEnv string
140 dirEnv string
141 files []string
142 dirs []string
143 cns []string
144 }{
145 {
146
147 name: "override-defaults",
148 fileEnv: testMissing,
149 dirEnv: testMissing,
150 files: []string{testFile},
151 dirs: []string{tmpDir},
152 cns: nil,
153 },
154 {
155
156 name: "file",
157 fileEnv: testFile,
158 dirEnv: "",
159 files: nil,
160 dirs: nil,
161 cns: []string{testFileCN},
162 },
163 {
164
165 name: "dir",
166 fileEnv: "",
167 dirEnv: tmpDir,
168 files: nil,
169 dirs: nil,
170 cns: []string{testDirCN},
171 },
172 {
173
174 name: "file+dir",
175 fileEnv: testFile,
176 dirEnv: tmpDir,
177 files: nil,
178 dirs: nil,
179 cns: []string{testFileCN, testDirCN},
180 },
181 {
182
183 name: "empty-fall-through",
184 fileEnv: "",
185 dirEnv: "",
186 files: []string{testFile},
187 dirs: []string{tmpDir},
188 cns: []string{testFileCN, testDirCN},
189 },
190 }
191
192
193 origCertFiles, origCertDirectories := certFiles, certDirectories
194 origFile, origDir := os.Getenv(certFileEnv), os.Getenv(certDirEnv)
195 defer func() {
196 certFiles = origCertFiles
197 certDirectories = origCertDirectories
198 os.Setenv(certFileEnv, origFile)
199 os.Setenv(certDirEnv, origDir)
200 }()
201
202 for _, tc := range testCases {
203 t.Run(tc.name, func(t *testing.T) {
204 if err := os.Setenv(certFileEnv, tc.fileEnv); err != nil {
205 t.Fatalf("setenv %q failed: %v", certFileEnv, err)
206 }
207 if err := os.Setenv(certDirEnv, tc.dirEnv); err != nil {
208 t.Fatalf("setenv %q failed: %v", certDirEnv, err)
209 }
210
211 certFiles, certDirectories = tc.files, tc.dirs
212
213 r, err := loadSystemRoots()
214 if err != nil {
215 t.Fatal("unexpected failure:", err)
216 }
217
218 if r == nil {
219 t.Fatal("nil roots")
220 }
221
222 wantSystemPool := (runtime.GOOS == "darwin" || runtime.GOOS == "windows") && tc.dirEnv == "" && tc.fileEnv == ""
223
224 if wantSystemPool {
225 if !r.systemPool {
226 t.Fatal("expected returned cert pool to be a system pool")
227 }
228 if r.len() != 0 {
229 t.Fatalf("expected empty system pool, pool has %d roots", r.len())
230 }
231 return
232 }
233
234
235 for i, cn := range tc.cns {
236 if i >= r.len() {
237 t.Errorf("missing cert %v @ %v", cn, i)
238 } else if r.mustCert(t, i).Subject.CommonName != cn {
239 fmt.Printf("%#v\n", r.mustCert(t, 0).Subject)
240 t.Errorf("unexpected cert common name %q, want %q", r.mustCert(t, i).Subject.CommonName, cn)
241 }
242 }
243 if r.len() > len(tc.cns) {
244 t.Errorf("got %v certs, which is more than %v wanted", r.len(), len(tc.cns))
245 }
246 })
247 }
248 }
249
250
251
252
253
254 func TestLoadSystemCertsLoadColonSeparatedDirs(t *testing.T) {
255 origFile, origDir := os.Getenv(certFileEnv), os.Getenv(certDirEnv)
256 origCertFiles := certFiles[:]
257
258
259
260
261 certFiles = certFiles[:0]
262 os.Setenv(certFileEnv, "")
263
264 defer func() {
265 certFiles = origCertFiles[:]
266 os.Setenv(certDirEnv, origDir)
267 os.Setenv(certFileEnv, origFile)
268 }()
269
270 tmpDir := t.TempDir()
271
272 rootPEMs := []string{
273 gtsRoot,
274 googleLeaf,
275 }
276
277 var certDirs []string
278 for i, certPEM := range rootPEMs {
279 certDir := filepath.Join(tmpDir, fmt.Sprintf("cert-%d", i))
280 if err := os.MkdirAll(certDir, 0755); err != nil {
281 t.Fatalf("failed to create certificate dir: %v", err)
282 }
283 certOutFile := filepath.Join(certDir, "cert.crt")
284 if err := os.WriteFile(certOutFile, []byte(certPEM), 0655); err != nil {
285 t.Fatalf("failed to write certificate to file: %v", err)
286 }
287 certDirs = append(certDirs, certDir)
288 }
289
290
291 if g, w := len(certDirs), len(rootPEMs); g != w {
292 t.Fatalf("failed sanity check: len(certsDir)=%d is not equal to len(rootsPEMS)=%d", g, w)
293 }
294
295
296 concatCertDirs := strings.Join(certDirs, string(filepath.ListSeparator))
297 os.Setenv(certDirEnv, concatCertDirs)
298 gotPool, err := loadSystemRoots()
299 if err != nil {
300 t.Fatalf("failed to load system roots: %v", err)
301 }
302 subjects := gotPool.Subjects()
303
304 if g, w := len(subjects), len(rootPEMs); g != w {
305 t.Fatalf("invalid number of subjects: got %d want %d", g, w)
306 }
307
308 wantPool := NewCertPool()
309 for _, certPEM := range rootPEMs {
310 wantPool.AppendCertsFromPEM([]byte(certPEM))
311 }
312 strCertPool := func(p *CertPool) string {
313 return string(bytes.Join(p.Subjects(), []byte("\n")))
314 }
315
316 if !certPoolEqual(gotPool, wantPool) {
317 got, want := strCertPool(gotPool), strCertPool(wantPool)
318 t.Fatalf("mismatched certPools\nGot:\n%s\n\nWant:\n%s", got, want)
319 }
320 }
321
322 func TestReadUniqueDirectoryEntries(t *testing.T) {
323 testenv.MustHaveSymlink(t)
324 baseTmpDir := t.TempDir()
325 path := func(base string) string { return filepath.Join(baseTmpDir, base) }
326 if f, err := os.Create(path("file")); err != nil {
327 t.Fatal(err)
328 } else {
329 f.Close()
330 }
331 if err := os.Symlink("target-in", path("link-in")); err != nil {
332 t.Fatal(err)
333 }
334 if err := os.Symlink("../target-out", path("link-out")); err != nil {
335 t.Fatal(err)
336 }
337 got, err := readUniqueDirectoryEntries(baseTmpDir)
338 if err != nil {
339 t.Fatal(err)
340 }
341 gotNames := []string{}
342 for _, fi := range got {
343 gotNames = append(gotNames, fi.Name())
344 }
345 wantNames := []string{"file", "link-out"}
346 if !slices.Equal(gotNames, wantNames) {
347 t.Errorf("got %q; want %q", gotNames, wantNames)
348 }
349 }
350
351 func TestSSLCertEnvOverride(t *testing.T) {
352 testenv.SetGODEBUG(t, "x509sslcertoverrideplatform=0")
353 t.Setenv(certFileEnv, "/tmp/nope")
354 t.Setenv(certDirEnv, "/tmp/nope")
355
356 p, err := loadSystemRoots()
357 if err != nil {
358 t.Fatalf("unexpected failure: %s", err)
359 }
360
361 if runtime.GOOS == "windows" || runtime.GOOS == "darwin" || runtime.GOOS == "ios" {
362 if !p.systemPool {
363 t.Fatal("x509sslcertoverrideplatform did not override SSL_CERT_{FILE,DIR}")
364 }
365 } else if p.systemPool {
366 t.Fatal("x509sslcertoverrideplatform caused a systemPool to be returned on OS other than windows or darwin")
367 }
368 }
369
View as plain text