...
1
2
3
4
5 package main
6
7 import (
8 "bytes"
9 "crypto/x509"
10 "encoding/pem"
11 "fmt"
12 "os"
13 "os/exec"
14 "os/user"
15 "path/filepath"
16 "sync"
17 )
18
19 var debugDarwinRoots = true
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44 func execSecurityRoots() (*x509.CertPool, error) {
45 keychains := []string{"/Library/Keychains/System.keychain"}
46
47
48
49 u, err := user.Current()
50 if err != nil {
51 if debugDarwinRoots {
52 fmt.Printf("crypto/x509: get current user: %v\n", err)
53 }
54 } else {
55 keychains = append(keychains,
56 filepath.Join(u.HomeDir, "/Library/Keychains/login.keychain"),
57
58
59 filepath.Join(u.HomeDir, "/Library/Keychains/login.keychain-db"),
60 )
61 }
62
63 var (
64 mu sync.Mutex
65 roots = x509.NewCertPool()
66 numVerified int
67 wg sync.WaitGroup
68 verifyCh = make(chan *x509.Certificate)
69 )
70
71
72
73
74
75 for i := 0; i < 4; i++ {
76 wg.Add(1)
77 go func() {
78 defer wg.Done()
79 for cert := range verifyCh {
80 valid := verifyCertWithSystem(cert)
81
82 mu.Lock()
83 numVerified++
84 if valid {
85 roots.AddCert(cert)
86 }
87 mu.Unlock()
88 }
89 }()
90 }
91 err = forEachCertInKeychains(keychains, func(cert *x509.Certificate) {
92 verifyCh <- cert
93 })
94 if err != nil {
95 return nil, err
96 }
97 close(verifyCh)
98 wg.Wait()
99
100 if debugDarwinRoots {
101 fmt.Printf("crypto/x509: ran security verify-cert %d times\n", numVerified)
102 }
103
104 err = forEachCertInKeychains([]string{
105 "/System/Library/Keychains/SystemRootCertificates.keychain",
106 }, roots.AddCert)
107 if err != nil {
108 return nil, err
109 }
110
111 return roots, nil
112 }
113
114 func forEachCertInKeychains(paths []string, f func(*x509.Certificate)) error {
115 args := append([]string{"find-certificate", "-a", "-p"}, paths...)
116 cmd := exec.Command("/usr/bin/security", args...)
117 data, err := cmd.Output()
118 if err != nil {
119 return err
120 }
121 for len(data) > 0 {
122 var block *pem.Block
123 block, data = pem.Decode(data)
124 if block == nil {
125 break
126 }
127 if block.Type != "CERTIFICATE" || len(block.Headers) != 0 {
128 continue
129 }
130 cert, err := x509.ParseCertificate(block.Bytes)
131 if err != nil {
132 continue
133 }
134 f(cert)
135 }
136 return nil
137 }
138
139 func verifyCertWithSystem(cert *x509.Certificate) bool {
140 data := pem.EncodeToMemory(&pem.Block{
141 Type: "CERTIFICATE", Bytes: cert.Raw,
142 })
143
144 f, err := os.CreateTemp("", "cert")
145 if err != nil {
146 fmt.Fprintf(os.Stderr, "can't create temporary file for cert: %v", err)
147 return false
148 }
149 defer os.Remove(f.Name())
150 if _, err := f.Write(data); err != nil {
151 fmt.Fprintf(os.Stderr, "can't write temporary file for cert: %v", err)
152 return false
153 }
154 if err := f.Close(); err != nil {
155 fmt.Fprintf(os.Stderr, "can't write temporary file for cert: %v", err)
156 return false
157 }
158 cmd := exec.Command("/usr/bin/security", "verify-cert", "-p", "ssl", "-c", f.Name(), "-l", "-L")
159 var stderr bytes.Buffer
160 if debugDarwinRoots {
161 cmd.Stderr = &stderr
162 }
163 if err := cmd.Run(); err != nil {
164 if debugDarwinRoots {
165 fmt.Printf("crypto/x509: verify-cert rejected %s: %q\n", cert.Subject, bytes.TrimSpace(stderr.Bytes()))
166 }
167 return false
168 }
169 if debugDarwinRoots {
170 fmt.Printf("crypto/x509: verify-cert approved %s\n", cert.Subject)
171 }
172 return true
173 }
174
View as plain text