1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package fixchain
17
18 import (
19 "encoding/pem"
20 "net/http"
21
22 "github.com/google/certificate-transparency-go/x509"
23 )
24
25
26
27
28
29
30 func Fix(cert *x509.Certificate, chain []*x509.Certificate, roots *x509.CertPool, client *http.Client) ([][]*x509.Certificate, []*FixError) {
31 fix := &toFix{
32 cert: cert,
33 chain: newDedupedChain(chain),
34 roots: roots,
35 cache: newURLCache(client, false),
36 }
37 return fix.handleChain()
38 }
39
40 const maxChainLength = 20
41
42 type toFix struct {
43 cert *x509.Certificate
44 chain *dedupedChain
45 roots *x509.CertPool
46 opts *x509.VerifyOptions
47 cache *urlCache
48 }
49
50 func (fix *toFix) handleChain() ([][]*x509.Certificate, []*FixError) {
51 intermediates := x509.NewCertPool()
52 for _, c := range fix.chain.certs {
53 intermediates.AddCert(c)
54 }
55
56 fix.opts = &x509.VerifyOptions{
57 Intermediates: intermediates,
58 Roots: fix.roots,
59 DisableTimeChecks: true,
60 KeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageAny},
61 }
62
63 var retferrs []*FixError
64 chains, ferrs := fix.constructChain()
65 if ferrs != nil {
66 retferrs = append(retferrs, ferrs...)
67 chains, ferrs = fix.fixChain()
68 if ferrs != nil {
69 retferrs = append(retferrs, ferrs...)
70 }
71 }
72 return chains, retferrs
73 }
74
75 func (fix *toFix) constructChain() ([][]*x509.Certificate, []*FixError) {
76 chains, err := fix.cert.Verify(*fix.opts)
77 if err != nil {
78 return chains, []*FixError{
79 {
80 Type: VerifyFailed,
81 Cert: fix.cert,
82 Chain: fix.chain.certs,
83 Error: err,
84 },
85 }
86 }
87 return chains, nil
88 }
89
90
91
92
93
94
95
96
97
98 func (fix *toFix) fixChain() ([][]*x509.Certificate, []*FixError) {
99 var retferrs []*FixError
100
101
102 dchain := *fix.chain
103 dchain.addCertToFront(fix.cert)
104
105 explored := make([]bool, len(dchain.certs))
106 lookup := make(map[[hashSize]byte]int)
107 for i, cert := range dchain.certs {
108 lookup[hash(cert)] = i
109 }
110
111
112 for i, cert := range dchain.certs {
113
114
115 if explored[i] {
116 continue
117 }
118
119 seen := make(map[[hashSize]byte]bool)
120
121
122
123
124
125 chains, ferrs := fix.augmentIntermediates(cert, 1, seen)
126 if ferrs != nil {
127 retferrs = append(retferrs, ferrs...)
128 }
129
130
131
132 if chains != nil {
133 return chains, retferrs
134 }
135
136
137
138 for certHash := range seen {
139 index, ok := lookup[certHash]
140 if ok {
141 explored[index] = true
142 }
143 }
144 }
145
146 return nil, append(retferrs, &FixError{
147 Type: FixFailed,
148 Cert: fix.cert,
149 Chain: fix.chain.certs,
150 })
151 }
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170 func (fix *toFix) augmentIntermediates(cert *x509.Certificate, length int, seen map[[hashSize]byte]bool) ([][]*x509.Certificate, []*FixError) {
171
172
173 if length > maxChainLength || seen[hash(cert)] {
174 return nil, nil
175 }
176
177 seen[hash(cert)] = true
178
179
180
181 fix.opts.Intermediates.AddCert(cert)
182 chains, err := fix.cert.Verify(*fix.opts)
183 if err == nil {
184 return chains, nil
185 }
186
187
188
189
190
191
192 var retferrs []*FixError
193 for _, url := range cert.IssuingCertificateURL {
194 icerts, ferr := fix.getIntermediates(url)
195 if ferr != nil {
196 retferrs = append(retferrs, ferr)
197 }
198
199 for _, icert := range icerts {
200 chains, ferrs := fix.augmentIntermediates(icert, length+1, seen)
201 if ferrs != nil {
202 retferrs = append(retferrs, ferrs...)
203 }
204 if chains != nil {
205 return chains, retferrs
206 }
207 }
208 }
209 return nil, retferrs
210 }
211
212
213 func (fix *toFix) getIntermediates(url string) ([]*x509.Certificate, *FixError) {
214 var icerts []*x509.Certificate
215
216
217 r := urlReplacement(url)
218 if r != nil {
219 return r, nil
220 }
221
222 body, err := fix.cache.getURL(url)
223 if err != nil {
224 return nil, &FixError{
225 Type: CannotFetchURL,
226 Cert: fix.cert,
227 Chain: fix.chain.certs,
228 URL: url,
229 Error: err,
230 }
231 }
232
233 icert, err := x509.ParseCertificate(body)
234 if x509.IsFatal(err) {
235 s, _ := pem.Decode(body)
236 if s != nil {
237 icert, err = x509.ParseCertificate(s.Bytes)
238 }
239 }
240
241 if x509.IsFatal(err) {
242 return nil, &FixError{
243 Type: ParseFailure,
244 Cert: fix.cert,
245 Chain: fix.chain.certs,
246 URL: url,
247 Bad: body,
248 Error: err,
249 }
250 }
251
252 icerts = append(icerts, icert)
253 return icerts, nil
254 }
255
View as plain text