github.com/zmap/zlint@v1.1.0/lints/base.go (about)

     1  package lints
     2  
     3  /*
     4   * ZLint Copyright 2017 Regents of the University of Michigan
     5   *
     6   * Licensed under the Apache License, Version 2.0 (the "License"); you may not
     7   * use this file except in compliance with the License. You may obtain a copy
     8   * of the License at http://www.apache.org/licenses/LICENSE-2.0
     9   *
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
    13   * implied. See the License for the specific language governing
    14   * permissions and limitations under the License.
    15   */
    16  
    17  import (
    18  	"time"
    19  
    20  	"github.com/zmap/zcrypto/x509"
    21  	"github.com/zmap/zlint/util"
    22  )
    23  
    24  var (
    25  	// Lints is a map of all known lints by name. Add a Lint to the map by calling
    26  	// RegisterLint.
    27  	Lints = make(map[string]*Lint)
    28  )
    29  
    30  // LintInterface is implemented by each Lint.
    31  type LintInterface interface {
    32  	// Initialize runs once per-lint. It is called during RegisterLint().
    33  	Initialize() error
    34  
    35  	// CheckApplies runs once per certificate. It returns true if the Lint should
    36  	// run on the given certificate. If CheckApplies returns false, the Lint
    37  	// result is automatically set to NA without calling CheckEffective() or
    38  	// Run().
    39  	CheckApplies(c *x509.Certificate) bool
    40  
    41  	// Execute() is the body of the lint. It is called for every certificate for
    42  	// which CheckApplies() returns true.
    43  	Execute(c *x509.Certificate) *LintResult
    44  }
    45  
    46  // An Enum to programmatically represent the source of a lint
    47  type LintSource int
    48  
    49  const (
    50  	UnknownLintSource LintSource = iota
    51  	CABFBaselineRequirements
    52  	RFC5280
    53  	RFC5480
    54  	RFC5891
    55  	ZLint
    56  	AWSLabs
    57  	EtsiEsi // ETSI - Electronic Signatures and Infrastructures (ESI)
    58  	CABFEVGuidelines
    59  	AppleCTPolicy // https://support.apple.com/en-us/HT205280
    60  )
    61  
    62  // A Lint struct represents a single lint, e.g.
    63  // "e_basic_constraints_not_critical". It contains an implementation of LintInterface.
    64  type Lint struct {
    65  
    66  	// Name is a lowercase underscore-separated string describing what a given
    67  	// Lint checks. If Name beings with "w", the lint MUST NOT return Error, only
    68  	// Warn. If Name beings with "e", the Lint MUST NOT return Warn, only Error.
    69  	Name string `json:"name,omitempty"`
    70  
    71  	// A human-readable description of what the Lint checks. Usually copied
    72  	// directly from the CA/B Baseline Requirements or RFC 5280.
    73  	Description string `json:"description,omitempty"`
    74  
    75  	// The source of the check, e.g. "BRs: 6.1.6" or "RFC 5280: 4.1.2.6".
    76  	Citation string `json:"citation,omitempty"`
    77  
    78  	// Programmatic source of the check, BRs, RFC5280, or ZLint
    79  	Source LintSource `json:"-"`
    80  
    81  	// Lints automatically returns NE for all certificates where CheckApplies() is
    82  	// true but with NotBefore < EffectiveDate. This check is bypassed if
    83  	// EffectiveDate is zero.
    84  	EffectiveDate time.Time `json:"-"`
    85  
    86  	// The implementation of the lint logic.
    87  	Lint LintInterface `json:"-"`
    88  }
    89  
    90  // CheckEffective returns true if c was issued on or after the EffectiveDate. If
    91  // EffectiveDate is zero, CheckEffective always returns true.
    92  func (l *Lint) CheckEffective(c *x509.Certificate) bool {
    93  	if l.EffectiveDate.IsZero() || !l.EffectiveDate.After(c.NotBefore) {
    94  		return true
    95  	}
    96  	return false
    97  }
    98  
    99  // Execute runs the lint against a certificate. For lints that are
   100  // sourced from the CA/B Forum Baseline Requirements, we first determine
   101  // if they are within the purview of the BRs. See LintInterface for details
   102  // about the other methods called. The ordering is as follows:
   103  //
   104  // CheckApplies()
   105  // CheckEffective()
   106  // Execute()
   107  func (l *Lint) Execute(cert *x509.Certificate) *LintResult {
   108  	if l.Source == CABFBaselineRequirements && !util.IsServerAuthCert(cert) {
   109  		return &LintResult{Status: NA}
   110  	}
   111  	if !l.Lint.CheckApplies(cert) {
   112  		return &LintResult{Status: NA}
   113  	} else if !l.CheckEffective(cert) {
   114  		return &LintResult{Status: NE}
   115  	}
   116  	res := l.Lint.Execute(cert)
   117  	return res
   118  }
   119  
   120  // RegisterLint must be called once for each lint to be excuted. Duplicate lint
   121  // names are squashed. Normally, RegisterLint is called during init().
   122  func RegisterLint(l *Lint) {
   123  	if err := l.Lint.Initialize(); err != nil {
   124  		panic("could not initialize lint: " + l.Name + ": " + err.Error())
   125  	}
   126  	Lints[l.Name] = l
   127  }