...

Source file src/github.com/google/certificate-transparency-go/scanner/matcher.go

Documentation: github.com/google/certificate-transparency-go/scanner

     1  // Copyright 2014 Google LLC. All Rights Reserved.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package scanner
    16  
    17  import (
    18  	"context"
    19  	"log"
    20  	"math/big"
    21  	"regexp"
    22  	"time"
    23  
    24  	ct "github.com/google/certificate-transparency-go"
    25  	"github.com/google/certificate-transparency-go/asn1"
    26  	"github.com/google/certificate-transparency-go/client"
    27  	"github.com/google/certificate-transparency-go/x509"
    28  )
    29  
    30  // Matcher describes how to match certificates and precertificates, based solely on the parsed [pre-]certificate;
    31  // clients should implement this interface to perform their own match criteria.
    32  type Matcher interface {
    33  	// CertificateMatches is called by the scanner for each X509 Certificate found in the log.
    34  	// The implementation should return true if the passed Certificate is interesting, and false otherwise.
    35  	CertificateMatches(*x509.Certificate) bool
    36  
    37  	// PrecertificateMatches is called by the scanner for each CT Precertificate found in the log.
    38  	// The implementation should return true if the passed Precertificate is interesting, and false otherwise.
    39  	PrecertificateMatches(*ct.Precertificate) bool
    40  }
    41  
    42  // MatchAll is a Matcher which will match every possible Certificate and Precertificate.
    43  type MatchAll struct{}
    44  
    45  // CertificateMatches returns true if the given cert should match; in this case, always.
    46  func (m MatchAll) CertificateMatches(_ *x509.Certificate) bool {
    47  	return true
    48  }
    49  
    50  // PrecertificateMatches returns true if the given precert should match, in this case, always.
    51  func (m MatchAll) PrecertificateMatches(_ *ct.Precertificate) bool {
    52  	return true
    53  }
    54  
    55  // MatchNone is a Matcher which will never match any Certificate or Precertificate.
    56  type MatchNone struct{}
    57  
    58  // CertificateMatches returns true if the given cert should match; in this case, never.
    59  func (m MatchNone) CertificateMatches(_ *x509.Certificate) bool {
    60  	return false
    61  }
    62  
    63  // PrecertificateMatches returns true if the given cert should match; in this case, never.
    64  func (m MatchNone) PrecertificateMatches(_ *ct.Precertificate) bool {
    65  	return false
    66  }
    67  
    68  // MatchSerialNumber performs a match for a specific serial number.
    69  type MatchSerialNumber struct {
    70  	SerialNumber big.Int
    71  }
    72  
    73  // CertificateMatches returns true if the given cert should match; in this
    74  // case, only if the serial number matches.
    75  func (m MatchSerialNumber) CertificateMatches(c *x509.Certificate) bool {
    76  	return c.SerialNumber.String() == m.SerialNumber.String()
    77  }
    78  
    79  // PrecertificateMatches returns true if the given cert should match; in this
    80  // case, only if the serial number matches.
    81  func (m MatchSerialNumber) PrecertificateMatches(p *ct.Precertificate) bool {
    82  	return p.TBSCertificate.SerialNumber.String() == m.SerialNumber.String()
    83  }
    84  
    85  // MatchSubjectRegex is a Matcher which will use CertificateSubjectRegex and PrecertificateSubjectRegex
    86  // to determine whether Certificates and Precertificates are interesting.
    87  // The two regexes are tested against Subject CN (Common Name) as well as all
    88  // Subject Alternative Names
    89  type MatchSubjectRegex struct {
    90  	CertificateSubjectRegex    *regexp.Regexp
    91  	PrecertificateSubjectRegex *regexp.Regexp
    92  }
    93  
    94  // CertificateMatches returns true if either CN or any SAN of c matches m.CertificateSubjectRegex.
    95  func (m MatchSubjectRegex) CertificateMatches(c *x509.Certificate) bool {
    96  	if m.CertificateSubjectRegex.FindStringIndex(c.Subject.CommonName) != nil {
    97  		return true
    98  	}
    99  	for _, alt := range c.DNSNames {
   100  		if m.CertificateSubjectRegex.FindStringIndex(alt) != nil {
   101  			return true
   102  		}
   103  	}
   104  	return false
   105  }
   106  
   107  // PrecertificateMatches returns true if either CN or any SAN of p matches m.PrecertificateSubjectRegex.
   108  func (m MatchSubjectRegex) PrecertificateMatches(p *ct.Precertificate) bool {
   109  	if m.PrecertificateSubjectRegex.FindStringIndex(p.TBSCertificate.Subject.CommonName) != nil {
   110  		return true
   111  	}
   112  	for _, alt := range p.TBSCertificate.DNSNames {
   113  		if m.PrecertificateSubjectRegex.FindStringIndex(alt) != nil {
   114  			return true
   115  		}
   116  	}
   117  	return false
   118  }
   119  
   120  // MatchIssuerRegex matches on issuer CN (common name) by regex
   121  type MatchIssuerRegex struct {
   122  	CertificateIssuerRegex    *regexp.Regexp
   123  	PrecertificateIssuerRegex *regexp.Regexp
   124  }
   125  
   126  // CertificateMatches returns true if the given cert's CN matches.
   127  func (m MatchIssuerRegex) CertificateMatches(c *x509.Certificate) bool {
   128  	return m.CertificateIssuerRegex.FindStringIndex(c.Issuer.CommonName) != nil
   129  }
   130  
   131  // PrecertificateMatches returns true if the given precert's CN matches.
   132  func (m MatchIssuerRegex) PrecertificateMatches(p *ct.Precertificate) bool {
   133  	return m.PrecertificateIssuerRegex.FindStringIndex(p.TBSCertificate.Issuer.CommonName) != nil
   134  }
   135  
   136  // MatchSCTTimestamp is a matcher which matches leaf entries with the specified Timestamp.
   137  type MatchSCTTimestamp struct {
   138  	Timestamp uint64
   139  }
   140  
   141  // Matches returns true if the timestamp embedded in the leaf matches the one
   142  // specified by this matcher.
   143  func (m MatchSCTTimestamp) Matches(leaf *ct.LeafEntry) bool {
   144  	entry, _ := ct.LogEntryFromLeaf(1, leaf)
   145  	if entry == nil {
   146  		// Can't validate if we can't parse
   147  		return false
   148  	}
   149  	return entry.Leaf.TimestampedEntry.Timestamp == m.Timestamp
   150  }
   151  
   152  // LeafMatcher describes how to match log entries, based on the Log LeafEntry
   153  // (which includes the unparsed [pre-]certificate; clients should implement this
   154  // interface to perform their own match criteria.
   155  type LeafMatcher interface {
   156  	Matches(*ct.LeafEntry) bool
   157  }
   158  
   159  // CertParseFailMatcher is a LeafMatcher which will match any Certificate or Precertificate that
   160  // triggered an error on parsing.
   161  type CertParseFailMatcher struct {
   162  	MatchNonFatalErrs bool
   163  }
   164  
   165  // Matches returns true for parse errors.
   166  func (m CertParseFailMatcher) Matches(leaf *ct.LeafEntry) bool {
   167  	_, err := ct.LogEntryFromLeaf(1, leaf)
   168  	if err != nil {
   169  		if x509.IsFatal(err) {
   170  			return true
   171  		}
   172  		return m.MatchNonFatalErrs
   173  	}
   174  	return false
   175  }
   176  
   177  // CertVerifyFailMatcher is a LeafMatcher which will match any Certificate or Precertificate that fails
   178  // validation.  The PopulateRoots() method should be called before use.
   179  type CertVerifyFailMatcher struct {
   180  	roots *x509.CertPool
   181  }
   182  
   183  // PopulateRoots adds the accepted roots for the log to the pool for validation.
   184  func (m *CertVerifyFailMatcher) PopulateRoots(ctx context.Context, logClient *client.LogClient) {
   185  	if m.roots != nil {
   186  		return
   187  	}
   188  	m.roots = x509.NewCertPool()
   189  	roots, err := logClient.GetAcceptedRoots(ctx)
   190  	if err != nil {
   191  		log.Fatal(err)
   192  	}
   193  	for _, root := range roots {
   194  		cert, _ := x509.ParseCertificate(root.Data)
   195  		if cert != nil {
   196  			m.roots.AddCert(cert)
   197  		} else {
   198  			log.Fatal(err)
   199  		}
   200  	}
   201  }
   202  
   203  // Matches returns true for validation errors.
   204  func (m CertVerifyFailMatcher) Matches(leaf *ct.LeafEntry) bool {
   205  	entry, _ := ct.LogEntryFromLeaf(1, leaf)
   206  	if entry == nil {
   207  		// Can't validate if we can't parse
   208  		return false
   209  	}
   210  	// Validate the [pre-]certificate as of just before its expiry.
   211  	var notBefore time.Time
   212  	if entry.X509Cert != nil {
   213  		notBefore = entry.X509Cert.NotAfter
   214  	} else {
   215  		notBefore = entry.Precert.TBSCertificate.NotAfter
   216  	}
   217  	when := notBefore.Add(-1 * time.Second)
   218  	opts := x509.VerifyOptions{
   219  		KeyUsages:     []x509.ExtKeyUsage{x509.ExtKeyUsageAny},
   220  		Roots:         m.roots,
   221  		Intermediates: x509.NewCertPool(),
   222  		CurrentTime:   when,
   223  	}
   224  	chain := make([]*x509.Certificate, len(entry.Chain))
   225  	for ii, cert := range entry.Chain {
   226  		intermediate, err := x509.ParseCertificate(cert.Data)
   227  		if intermediate == nil {
   228  			log.Printf("Intermediate %d fails to parse: %v", ii, err)
   229  			return true
   230  		}
   231  		chain[ii] = intermediate
   232  		opts.Intermediates.AddCert(intermediate)
   233  	}
   234  	if entry.X509Cert != nil {
   235  		if _, err := entry.X509Cert.Verify(opts); err != nil {
   236  			log.Printf("Cert fails to validate as of %v: %v", opts.CurrentTime, err)
   237  			return true
   238  		}
   239  		return false
   240  	}
   241  	if entry.Precert != nil {
   242  		precert, err := x509.ParseCertificate(entry.Precert.Submitted.Data)
   243  		if err != nil {
   244  			log.Printf("Precert fails to parse as of %v: %v", opts.CurrentTime, err)
   245  			return true
   246  		}
   247  		// Ignore unhandled poison extension.
   248  		dropUnhandledExtension(precert, x509.OIDExtensionCTPoison)
   249  
   250  		for i := 1; i < len(chain); i++ {
   251  			// PolicyConstraints is legal (and critical) but unparsed.
   252  			dropUnhandledExtension(chain[i], x509.OIDExtensionPolicyConstraints)
   253  		}
   254  
   255  		// Drop CT EKU from preissuer if present.
   256  		if len(chain) > 0 {
   257  			for i, eku := range chain[0].ExtKeyUsage {
   258  				if eku == x509.ExtKeyUsageCertificateTransparency {
   259  					chain[0].ExtKeyUsage = append(chain[0].ExtKeyUsage[:i], chain[0].ExtKeyUsage[i+1:]...)
   260  					break
   261  				}
   262  			}
   263  		}
   264  
   265  		if _, err := precert.Verify(opts); err != nil {
   266  			log.Printf("Precert fails to validate as of %v: %v", opts.CurrentTime, err)
   267  			return true
   268  		}
   269  		return false
   270  	}
   271  	log.Printf("Neither cert nor precert present!")
   272  	return true
   273  }
   274  
   275  func dropUnhandledExtension(cert *x509.Certificate, oid asn1.ObjectIdentifier) {
   276  	for j, extOID := range cert.UnhandledCriticalExtensions {
   277  		if extOID.Equal(oid) {
   278  			cert.UnhandledCriticalExtensions = append(cert.UnhandledCriticalExtensions[:j], cert.UnhandledCriticalExtensions[j+1:]...)
   279  			return
   280  		}
   281  	}
   282  }
   283  

View as plain text