@@ -13,8 +13,20 @@ import (
13
13
"github.com/zmap/zcrypto/ct"
14
14
"github.com/zmap/zcrypto/ct/client"
15
15
"github.com/zmap/zcrypto/ct/x509"
16
+ "github.com/zmap/zcrypto/encoding/asn1"
17
+ "github.com/zmap/zcrypto/x509/pkix"
16
18
)
17
19
20
+ // ASN1Certificate holds the top-level asn1 fields in a certificate.
21
+ //
22
+ // It is used to determine if a certificate contains well-formed asn1 data or is corrupted.
23
+ type ASN1Certificate struct {
24
+ Raw asn1.RawContent
25
+ TBSCertificate asn1.RawValue
26
+ SignatureAlgorithm pkix.AlgorithmIdentifier
27
+ SignatureValue asn1.BitString
28
+ }
29
+
18
30
// Clients wishing to implement their own Matchers should implement this interface:
19
31
type Matcher interface {
20
32
// CertificateMatches is called by the scanner for each X509 Certificate found in the log.
@@ -137,6 +149,9 @@ type ScannerOptions struct {
137
149
Name string
138
150
139
151
MaximumIndex int64
152
+
153
+ // Always output encountered certificates, so long as they are valid ASN.1
154
+ IgnoreParsingErrors bool
140
155
}
141
156
142
157
// Creates a new ScannerOptions struct with sensible defaults
@@ -188,30 +203,87 @@ type fetchRange struct {
188
203
end int64
189
204
}
190
205
191
- // Takes the error returned by either x509.ParseCertificate() or
192
- // x509.ParseTBSCertificate() and determines if it's non-fatal or otherwise.
193
- // In the case of non-fatal errors, the error will be logged,
194
- // entriesWithNonFatalErrors will be incremented, and the return value will be
195
- // nil.
196
- // Fatal errors will be logged, unparsableEntires will be incremented, and the
206
+ // parseCertificate takes a raw certificate, parses it, and if there is an error,
207
+ // determines if it is fatal. In the case of non-fatal errors, the error will be logged,
208
+ // entriesWithNonFatalErrors will be incremented, and the returned error will be nil.
209
+ //
210
+ // Fatal parse errors will be logged, unparsableEntries will be incremented, and the
197
211
// fatal error itself will be returned.
198
- // When |err| is nil, this method does nothing.
199
- func (s * Scanner ) handleParseEntryError (err error , entryType ct.LogEntryType , index int64 ) error {
212
+ //
213
+ // This function does NOT promise that the returned certificate will be non-nil
214
+ // whenever err is non-nil.
215
+ func (s * Scanner ) parseCertificate (
216
+ precert bool ,
217
+ entryType ct.LogEntryType ,
218
+ index int64 ,
219
+ raw []byte ,
220
+ ) (* x509.Certificate , error ) {
221
+ var cert * x509.Certificate
222
+ var err error
223
+ if precert {
224
+ cert , err = x509 .ParseTBSCertificate (raw )
225
+ } else {
226
+ cert , err = x509 .ParseCertificate (raw )
227
+ }
228
+
200
229
if err == nil {
201
230
// No error to handle
202
- return nil
231
+ return cert , nil
203
232
}
233
+
234
+ var isFatal bool
235
+
204
236
switch err .(type ) {
205
237
case x509.NonFatalErrors :
206
238
s .entriesWithNonFatalErrors ++
207
- // We'll make a note, but continue.
208
- s .logger .Warnf ("Non-fatal error in %+v at index %d of log at %s: %s" , entryType , index , s .logClient .Uri , err )
209
239
default :
210
240
s .unparsableEntries ++
211
- s .logger .Warnf ("Failed to parse in %+v at index %d of log at %s: %s" , entryType , index , s .logClient .Uri , err )
212
- return err
241
+ isFatal = true
242
+ }
243
+
244
+ if ! isFatal {
245
+ s .logger .Warnf (
246
+ "Ignored non-fatal error in %+v at index %d of log at %s: %s" ,
247
+ entryType ,
248
+ index ,
249
+ s .logClient .Uri ,
250
+ err ,
251
+ )
252
+ return cert , nil
253
+ }
254
+
255
+ if ! s .opts .IgnoreParsingErrors {
256
+ s .logger .Errorf (
257
+ "Fatal parse error in %+v at index %d of log at %s: %s" ,
258
+ entryType ,
259
+ index ,
260
+ s .logClient .Uri ,
261
+ err ,
262
+ )
263
+ return nil , err
213
264
}
214
- return nil
265
+
266
+ var asn1cert ASN1Certificate
267
+ if _ , perr := asn1 .Unmarshal (raw , & asn1cert ); perr != nil {
268
+ s .logger .Warnf (
269
+ "Corrupted cert ASN.1 in %+v at index %d of log %s: %s" ,
270
+ entryType ,
271
+ index ,
272
+ s .logClient .Uri ,
273
+ perr ,
274
+ )
275
+ return nil , perr
276
+ }
277
+
278
+ s .logger .Warnf (
279
+ "Ignored fatal parse error in %+v at index %d of log %s: %s" ,
280
+ entryType ,
281
+ index ,
282
+ s .logClient .Uri ,
283
+ err ,
284
+ )
285
+
286
+ return cert , nil
215
287
}
216
288
217
289
// Processes the given |entry| in the specified log.
@@ -223,29 +295,52 @@ func (s *Scanner) processEntry(entry ct.LogEntry, foundCert func(*ct.LogEntry, s
223
295
// Only interested in precerts and this is an X.509 cert, early-out.
224
296
return
225
297
}
226
- cert , err := x509 .ParseCertificate (entry .Leaf .TimestampedEntry .X509Entry )
227
- if err = s .handleParseEntryError (err , entry .Leaf .TimestampedEntry .EntryType , entry .Index ); err != nil {
228
- // We hit an unparseable entry, already logged inside handleParseEntryError()
298
+ cert , err := s .parseCertificate (
299
+ false ,
300
+ entry .Leaf .TimestampedEntry .EntryType ,
301
+ entry .Index ,
302
+ entry .Leaf .TimestampedEntry .X509Entry ,
303
+ )
304
+ if err != nil {
229
305
return
230
306
}
231
- if s .opts .Matcher .CertificateMatches (cert ) {
232
- entry .X509Cert = cert
307
+ if cert != nil {
308
+ if s .opts .Matcher .CertificateMatches (cert ) {
309
+ entry .RawCert = entry .Leaf .TimestampedEntry .X509Entry
310
+ entry .X509Cert = cert
311
+ foundCert (& entry , s .opts .Name )
312
+ }
313
+ } else {
314
+ entry .RawCert = entry .Leaf .TimestampedEntry .X509Entry
233
315
foundCert (& entry , s .opts .Name )
234
316
}
235
317
case ct .PrecertLogEntryType :
236
- c , err := x509 .ParseTBSCertificate (entry .Leaf .TimestampedEntry .PrecertEntry .TBSCertificate )
237
- if err = s .handleParseEntryError (err , entry .Leaf .TimestampedEntry .EntryType , entry .Index ); err != nil {
238
- // We hit an unparseable entry, already logged inside handleParseEntryError()
318
+ cert , err := s .parseCertificate (
319
+ true ,
320
+ entry .Leaf .TimestampedEntry .EntryType ,
321
+ entry .Index ,
322
+ entry .Leaf .TimestampedEntry .PrecertEntry .TBSCertificate ,
323
+ )
324
+ if err != nil {
239
325
return
240
326
}
241
327
precert := & ct.Precertificate {
242
328
Raw : entry .Chain [0 ],
243
- TBSCertificate : * c ,
329
+ TBSCertificate : cert ,
244
330
IssuerKeyHash : entry .Leaf .TimestampedEntry .PrecertEntry .IssuerKeyHash }
245
- if s .opts .Matcher .PrecertificateMatches (precert ) {
246
- entry .Precert = precert
331
+
332
+ entry .IsPrecert = true
333
+ entry .RawCert = entry .Leaf .TimestampedEntry .PrecertEntry .TBSCertificate
334
+ entry .Precert = precert
335
+
336
+ if cert != nil {
337
+ if s .opts .Matcher .PrecertificateMatches (precert ) {
338
+ foundPrecert (& entry , s .opts .Name )
339
+ }
340
+ } else {
247
341
foundPrecert (& entry , s .opts .Name )
248
342
}
343
+
249
344
s .precertsSeen ++
250
345
}
251
346
}
0 commit comments