1 // Copyright 2012 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 package x509
6 7 import (
8 "internal/godebug"
9 "sync"
10 _ "unsafe" // for linkname
11 )
12 13 // systemRoots should be an internal detail,
14 // but widely used packages access it using linkname.
15 // Notable members of the hall of shame include:
16 // - github.com/breml/rootcerts
17 //
18 // Do not remove or change the type signature.
19 // See go.dev/issue/67401.
20 //
21 //go:linkname systemRoots
22 var (
23 once sync.Once
24 systemRootsMu sync.RWMutex
25 systemRoots *CertPool
26 systemRootsErr error
27 fallbacksSet bool
28 )
29 30 func systemRootsPool() *CertPool {
31 once.Do(initSystemRoots)
32 systemRootsMu.RLock()
33 defer systemRootsMu.RUnlock()
34 return systemRoots
35 }
36 37 func initSystemRoots() {
38 systemRootsMu.Lock()
39 defer systemRootsMu.Unlock()
40 systemRoots, systemRootsErr = loadSystemRoots()
41 if systemRootsErr != nil {
42 systemRoots = nil
43 }
44 }
45 46 var x509usefallbackroots = godebug.New("x509usefallbackroots")
47 48 // SetFallbackRoots sets the roots to use during certificate verification, if no
49 // custom roots are specified and a platform verifier or a system certificate
50 // pool is not available (for instance in a container which does not have a root
51 // certificate bundle). SetFallbackRoots will panic if roots is nil.
52 //
53 // SetFallbackRoots may only be called once, if called multiple times it will
54 // panic.
55 //
56 // The fallback behavior can be forced on all platforms, even when there is a
57 // system certificate pool, by setting GODEBUG=x509usefallbackroots=1 (note that
58 // on Windows and macOS this will disable usage of the platform verification
59 // APIs and cause the pure Go verifier to be used). Setting
60 // x509usefallbackroots=1 without calling SetFallbackRoots has no effect.
61 func SetFallbackRoots(roots *CertPool) {
62 if roots == nil {
63 panic("roots must be non-nil")
64 }
65 66 // trigger initSystemRoots if it hasn't already been called before we
67 // take the lock
68 _ = systemRootsPool()
69 70 systemRootsMu.Lock()
71 defer systemRootsMu.Unlock()
72 73 if fallbacksSet {
74 panic("SetFallbackRoots has already been called")
75 }
76 fallbacksSet = true
77 78 if systemRoots != nil && (systemRoots.len() > 0 || systemRoots.systemPool) {
79 if x509usefallbackroots.Value() != "1" {
80 return
81 }
82 x509usefallbackroots.IncNonDefault()
83 }
84 systemRoots, systemRootsErr = roots, nil
85 }
86