1
2
3
4
5 package x509
6
7 import (
8 "internal/godebug"
9 "io/fs"
10 "os"
11 "path/filepath"
12 "runtime"
13 "strings"
14 "sync"
15 _ "unsafe"
16 )
17
18
19
20
21
22
23
24
25
26
27 var (
28 once sync.Once
29 systemRootsMu sync.RWMutex
30 systemRoots *CertPool
31 systemRootsErr error
32 fallbacksSet bool
33 useFallbackRoots bool
34 )
35
36 func systemRootsPool() *CertPool {
37 once.Do(initSystemRoots)
38 systemRootsMu.RLock()
39 defer systemRootsMu.RUnlock()
40 return systemRoots
41 }
42
43 func initSystemRoots() {
44 systemRootsMu.Lock()
45 defer systemRootsMu.Unlock()
46
47 fallbackRoots := systemRoots
48 systemRoots, systemRootsErr = loadSystemRoots()
49 if systemRootsErr != nil {
50 systemRoots = nil
51 }
52
53 if fallbackRoots == nil {
54 return
55 }
56
57 systemCertsAvail := systemRoots != nil && (systemRoots.len() > 0 || systemRoots.systemPool)
58
59 if !useFallbackRoots && systemCertsAvail {
60 return
61 }
62
63 if useFallbackRoots && systemCertsAvail {
64 x509usefallbackroots.IncNonDefault()
65 }
66
67 systemRoots, systemRootsErr = fallbackRoots, nil
68 }
69
70 var x509usefallbackroots = godebug.New("x509usefallbackroots")
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85 func SetFallbackRoots(roots *CertPool) {
86 if roots == nil {
87 panic("roots must be non-nil")
88 }
89
90 systemRootsMu.Lock()
91 defer systemRootsMu.Unlock()
92
93 if fallbacksSet {
94 panic("SetFallbackRoots has already been called")
95 }
96 fallbacksSet = true
97
98
99
100
101
102 if systemRoots == nil && systemRootsErr == nil {
103 systemRoots = roots
104 useFallbackRoots = x509usefallbackroots.Value() == "1"
105 return
106 }
107
108 once.Do(func() { panic("unreachable") })
109
110 forceFallbackRoots := x509usefallbackroots.Value() == "1"
111 systemCertsAvail := systemRoots != nil && (systemRoots.len() > 0 || systemRoots.systemPool)
112
113 if !forceFallbackRoots && systemCertsAvail {
114 return
115 }
116
117 if forceFallbackRoots && systemCertsAvail {
118 x509usefallbackroots.IncNonDefault()
119 }
120
121 systemRoots, systemRootsErr = roots, nil
122 }
123
124 const (
125
126
127 certFileEnv = "SSL_CERT_FILE"
128
129
130
131
132 certDirEnv = "SSL_CERT_DIR"
133 )
134
135 var x509sslcertoverrideplatform = godebug.New("x509sslcertoverrideplatform")
136
137 func loadSystemRoots() (*CertPool, error) {
138 certFilePath, certDirPath := os.Getenv(certFileEnv), os.Getenv(certDirEnv)
139
140 if runtime.GOOS == "windows" || runtime.GOOS == "darwin" || runtime.GOOS == "ios" {
141 if certFilePath == "" && certDirPath == "" {
142 return &CertPool{systemPool: true}, nil
143 }
144 if x509sslcertoverrideplatform.Value() == "0" {
145 x509sslcertoverrideplatform.IncNonDefault()
146 return &CertPool{systemPool: true}, nil
147 }
148 }
149
150 return loadOnDiskRoots(certFilePath, certDirPath)
151 }
152
153 func loadOnDiskRoots(certFilePath, certDirPath string) (*CertPool, error) {
154 roots := NewCertPool()
155
156 files := certFiles
157 if certFilePath != "" {
158 files = []string{certFilePath}
159 }
160
161 var firstErr error
162 for _, file := range files {
163 data, err := os.ReadFile(file)
164 if err == nil {
165 roots.AppendCertsFromPEM(data)
166 break
167 }
168 if firstErr == nil && !os.IsNotExist(err) {
169 firstErr = err
170 }
171 }
172
173 dirs := certDirectories
174 if certDirPath != "" {
175
176
177
178
179
180 dirs = filepath.SplitList(certDirPath)
181 }
182
183 for _, directory := range dirs {
184 fis, err := readUniqueDirectoryEntries(directory)
185 if err != nil {
186 if firstErr == nil && !os.IsNotExist(err) {
187 firstErr = err
188 }
189 continue
190 }
191 for _, fi := range fis {
192 data, err := os.ReadFile(filepath.Join(directory, fi.Name()))
193 if err == nil {
194 roots.AppendCertsFromPEM(data)
195 }
196 }
197 }
198
199 if roots.len() > 0 || firstErr == nil {
200 return roots, nil
201 }
202
203 return nil, firstErr
204 }
205
206
207
208 func readUniqueDirectoryEntries(dir string) ([]fs.DirEntry, error) {
209 files, err := os.ReadDir(dir)
210 if err != nil {
211 return nil, err
212 }
213 uniq := files[:0]
214 for _, f := range files {
215 if !isSameDirSymlink(f, dir) {
216 uniq = append(uniq, f)
217 }
218 }
219 return uniq, nil
220 }
221
222
223
224 func isSameDirSymlink(f fs.DirEntry, dir string) bool {
225 if f.Type()&fs.ModeSymlink == 0 {
226 return false
227 }
228 target, err := os.Readlink(filepath.Join(dir, f.Name()))
229 return err == nil && !strings.ContainsRune(target, filepath.Separator)
230 }
231
View as plain text