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 }