-
Notifications
You must be signed in to change notification settings - Fork 108
/
base.go
296 lines (264 loc) · 11.2 KB
/
base.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
package lint
/*
* ZLint Copyright 2023 Regents of the University of Michigan
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy
* of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
* implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
import (
"time"
"github.com/zmap/zcrypto/x509"
"github.com/zmap/zlint/v3/util"
)
// LintInterface is implemented by each certificate linter.
//
// @deprecated - use CertificateLintInterface instead.
type LintInterface = CertificateLintInterface
// RevocationListLintInterface is implemented by each revocation list linter.
type RevocationListLintInterface interface {
// CheckApplies runs once per revocation list. It returns true if the
// Lint should run on the given certificate. If CheckApplies returns
// false, the Lint result is automatically set to NA without calling
// CheckEffective() or Run().
CheckApplies(r *x509.RevocationList) bool
// Execute is the body of the lint. It is called for every revocation list
// for which CheckApplies returns true.
Execute(r *x509.RevocationList) *LintResult
}
// CertificateLintInterface is implemented by each certificate linter.
type CertificateLintInterface interface {
// CheckApplies runs once per certificate. It returns true if the Lint should
// run on the given certificate. If CheckApplies returns false, the Lint
// result is automatically set to NA without calling CheckEffective() or
// Run().
CheckApplies(c *x509.Certificate) bool
// Execute is the body of the lint. It is called for every certificate for
// which CheckApplies returns true.
Execute(c *x509.Certificate) *LintResult
}
// Configurable lints return a pointer into a struct that they wish to receive their configuration into.
type Configurable interface {
Configure() interface{}
}
// LintMetadata represents the metadata that are broadly associated across all types of lints.
//
// That is, all lints (irrespective of being a certificate lint, a CRL lint, and OCSP, etc.)
// have a Name, a Description, a Citation, and so on.
//
// In this way, this struct may be embedded in any linting type in order to maintain this
// data, while each individual linting type provides the behavior over this data.
type LintMetadata struct {
// Name is a lowercase underscore-separated string describing what a given
// Lint checks. If Name beings with "w", the lint MUST NOT return Error, only
// Warn. If Name beings with "e", the Lint MUST NOT return Warn, only Error.
Name string `json:"name,omitempty"`
// A human-readable description of what the Lint checks. Usually copied
// directly from the CA/B Baseline Requirements or RFC 5280.
Description string `json:"description,omitempty"`
// The source of the check, e.g. "BRs: 6.1.6" or "RFC 5280: 4.1.2.6".
Citation string `json:"citation,omitempty"`
// Programmatic source of the check, BRs, RFC5280, or ZLint
Source LintSource `json:"source"`
// Lints automatically returns NE for all certificates where CheckApplies() is
// true but with NotBefore < EffectiveDate. This check is bypassed if
// EffectiveDate is zero. Please see CheckEffective for more information.
EffectiveDate time.Time `json:"-"`
// Lints automatically returns NE for all certificates where CheckApplies() is
// true but with NotBefore >= IneffectiveDate. This check is bypassed if
// IneffectiveDate is zero. Please see CheckEffective for more information.
IneffectiveDate time.Time `json:"-"`
}
// A Lint struct represents a single lint, e.g.
// "e_basic_constraints_not_critical". It contains an implementation of LintInterface.
//
// @deprecated - use CertificateLint instead.
type Lint struct {
// Name is a lowercase underscore-separated string describing what a given
// Lint checks. If Name beings with "w", the lint MUST NOT return Error, only
// Warn. If Name beings with "e", the Lint MUST NOT return Warn, only Error.
Name string `json:"name,omitempty"`
// A human-readable description of what the Lint checks. Usually copied
// directly from the CA/B Baseline Requirements or RFC 5280.
Description string `json:"description,omitempty"`
// The source of the check, e.g. "BRs: 6.1.6" or "RFC 5280: 4.1.2.6".
Citation string `json:"citation,omitempty"`
// Programmatic source of the check, BRs, RFC5280, or ZLint
Source LintSource `json:"source"`
// Lints automatically returns NE for all certificates where CheckApplies() is
// true but with NotBefore < EffectiveDate. This check is bypassed if
// EffectiveDate is zero. Please see CheckEffective for more information.
EffectiveDate time.Time `json:"-"`
// Lints automatically returns NE for all certificates where CheckApplies() is
// true but with NotBefore >= IneffectiveDate. This check is bypassed if
// IneffectiveDate is zero. Please see CheckEffective for more information.
IneffectiveDate time.Time `json:"-"`
// A constructor which returns the implementation of the lint logic.
Lint func() LintInterface `json:"-"`
}
// toCertificateLint converts a Lint to a CertificateLint for backwards compatibility.
//
// @deprecated - Use CertificateLint directly.
func (l *Lint) toCertificateLint() *CertificateLint {
return &CertificateLint{
LintMetadata: LintMetadata{
Name: l.Name,
Description: l.Description,
Citation: l.Citation,
Source: l.Source,
EffectiveDate: l.EffectiveDate,
IneffectiveDate: l.IneffectiveDate,
},
Lint: l.Lint,
}
}
// CheckEffective returns true if c was issued on or after the EffectiveDate
// AND before (but not on) the Ineffective date. That is, CheckEffective
// returns true if...
//
// c.NotBefore in [EffectiveDate, IneffectiveDate)
//
// If EffectiveDate is zero, then only IneffectiveDate is checked. Conversely,
// if IneffectiveDate is zero then only EffectiveDate is checked. If both EffectiveDate
// and IneffectiveDate are zero then CheckEffective always returns true.
//
// @deprecated - use CertificateLint instead.
func (l *Lint) CheckEffective(c *x509.Certificate) bool {
return l.toCertificateLint().CheckEffective(c)
}
// Execute runs the lint against a certificate. For lints that are
// sourced from the CA/B Forum Baseline Requirements, we first determine
// if they are within the purview of the BRs. See LintInterface for details
// about the other methods called. The ordering is as follows:
//
// Configure() ----> only if the lint implements Configurable
// CheckApplies()
// CheckEffective()
// Execute()
//
// @deprecated - use CertificateLint instead
func (l *Lint) Execute(cert *x509.Certificate, config Configuration) *LintResult {
return l.toCertificateLint().Execute(cert, config)
}
// CertificateLint represents a single x509 certificate linter.
type CertificateLint struct {
// Metadata associated with the linter.
LintMetadata
// A constructor which returns the implementation of the linter.
Lint func() CertificateLintInterface `json:"-"`
}
// toLint converts a CertificateLint to Lint for backwards compatibility
//
// @deprecated - use CertificateLint directly.
func (l *CertificateLint) toLint() *Lint {
return &Lint{
Name: l.Name,
Description: l.Description,
Citation: l.Citation,
Source: l.Source,
EffectiveDate: l.EffectiveDate,
IneffectiveDate: l.IneffectiveDate,
Lint: l.Lint,
}
}
// CheckEffective returns true if c was issued on or after the EffectiveDate
// AND before (but not on) the Ineffective date. That is, CheckEffective
// returns true if...
//
// c.NotBefore in [EffectiveDate, IneffectiveDate)
//
// If EffectiveDate is zero, then only IneffectiveDate is checked. Conversely,
// if IneffectiveDate is zero then only EffectiveDate is checked. If both EffectiveDate
// and IneffectiveDate are zero then CheckEffective always returns true.
func (l *CertificateLint) CheckEffective(c *x509.Certificate) bool {
return checkEffective(l.EffectiveDate, l.IneffectiveDate, c.NotBefore)
}
// Execute runs the lint against a certificate. For lints that are
// sourced from the CA/B Forum Baseline Requirements, we first determine
// if they are within the purview of the BRs. See CertificateLintInterface
// for details about the other methods called.
// The ordering is as follows:
//
// Configure() ----> only if the lint implements Configurable
// CheckApplies()
// CheckEffective()
// Execute()
func (l *CertificateLint) Execute(cert *x509.Certificate, config Configuration) *LintResult {
if l.Source == CABFBaselineRequirements && !util.IsServerAuthCert(cert) {
return &LintResult{Status: NA}
}
lint := l.Lint()
err := config.MaybeConfigure(lint, l.Name)
if err != nil {
return &LintResult{
Status: Fatal,
Details: err.Error()}
}
if !lint.CheckApplies(cert) {
return &LintResult{Status: NA}
} else if !l.CheckEffective(cert) {
return &LintResult{Status: NE}
}
return lint.Execute(cert)
}
// RevocationListLint represents a single x509 CRL linter.
type RevocationListLint struct {
// Metadata associated with the linter.
LintMetadata
// A constructor which returns the implementation of the linter.
Lint func() RevocationListLintInterface `json:"-"`
}
// CheckEffective returns true if r was generated on or after the EffectiveDate
// AND before (but not on) the Ineffective date. That is, CheckEffective
// returns true if...
//
// r.ThisUpdate in [EffectiveDate, IneffectiveDate)
//
// If EffectiveDate is zero, then only IneffectiveDate is checked. Conversely,
// if IneffectiveDate is zero then only EffectiveDate is checked. If both EffectiveDate
// and IneffectiveDate are zero then CheckEffective always returns true.
func (l *RevocationListLint) CheckEffective(r *x509.RevocationList) bool {
return checkEffective(l.EffectiveDate, l.IneffectiveDate, r.ThisUpdate)
}
// Execute runs the lint against a revocation list.
// The ordering is as follows:
//
// Configure() ----> only if the lint implements Configurable
// CheckApplies()
// CheckEffective()
// Execute()
func (l *RevocationListLint) Execute(r *x509.RevocationList, config Configuration) *LintResult {
lint := l.Lint()
err := config.MaybeConfigure(lint, l.Name)
if err != nil {
return &LintResult{
Status: Fatal,
Details: err.Error()}
}
if !lint.CheckApplies(r) {
return &LintResult{Status: NA}
} else if !l.CheckEffective(r) {
return &LintResult{Status: NE}
}
return lint.Execute(r)
}
// checkEffective returns true if target was generated on or after the EffectiveDate
// AND before (but not on) the Ineffective date. That is, CheckEffective
// returns true if...
//
// target in [effective, ineffective)
//
// If effective is zero, then only ineffective is checked. Conversely,
// if ineffective is zero then only effect is checked. If both effective
// and ineffective are zero then checkEffective always returns true.
func checkEffective(effective, ineffective, target time.Time) bool {
onOrAfterEffective := effective.IsZero() || util.OnOrAfter(target, effective)
strictlyBeforeIneffective := ineffective.IsZero() || target.Before(ineffective)
return onOrAfterEffective && strictlyBeforeIneffective
}