diff --git a/v3/lints/cabf_br/lint_underscore_present_with_too_long_validity.go b/v3/lints/cabf_br/lint_underscore_present_with_too_long_validity.go new file mode 100644 index 000000000..5041057d4 --- /dev/null +++ b/v3/lints/cabf_br/lint_underscore_present_with_too_long_validity.go @@ -0,0 +1,59 @@ +/* + * ZLint Copyright 2021 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. + */ + +package cabf_br + +import ( + "fmt" + "strings" + + "github.com/zmap/zcrypto/x509" + "github.com/zmap/zlint/v3/lint" + "github.com/zmap/zlint/v3/util" +) + +func init() { + lint.RegisterLint(&lint.Lint{ + Name: "e_underscore_present_with_too_long_validity", + Description: "From 2018-12-10 to 2019-04-01, DNSNames may contain underscores if-and-only-if the certificate is valid for less than thirty days.", + Citation: "BR 7.1.4.2.1", + Source: lint.CABFBaselineRequirements, + EffectiveDate: util.CABFBRs_1_6_2_Date, + IneffectiveDate: util.CABFBRs_1_6_2_UnderscorePermissibilitySunsetDate, + Lint: func() lint.LintInterface { return &UnderscorePresentWithTooLongValidity{} }, + }) +} + +type UnderscorePresentWithTooLongValidity struct{} + +func (l *UnderscorePresentWithTooLongValidity) CheckApplies(c *x509.Certificate) bool { + longValidity := util.BeforeOrOn(c.NotBefore.AddDate(0, 0, 30), c.NotAfter) + return util.IsSubscriberCert(c) && util.DNSNamesExist(c) && longValidity +} + +func (l *UnderscorePresentWithTooLongValidity) Execute(c *x509.Certificate) *lint.LintResult { + for _, dns := range c.DNSNames { + if strings.Contains(dns, "_") { + return &lint.LintResult{ + Status: lint.Error, + Details: fmt.Sprintf( + "The DNSName '%s' contains an underscore character which is only permissible if the certiticate is valid for less than 30 days (this certificate is valid for %d days)", + dns, + c.NotAfter.Sub(c.NotBefore)/util.DurationDay, + ), + } + } + } + return &lint.LintResult{Status: lint.Pass} +} diff --git a/v3/lints/cabf_br/lint_underscore_present_with_too_long_validity_test.go b/v3/lints/cabf_br/lint_underscore_present_with_too_long_validity_test.go new file mode 100644 index 000000000..5380103f0 --- /dev/null +++ b/v3/lints/cabf_br/lint_underscore_present_with_too_long_validity_test.go @@ -0,0 +1,59 @@ +/* + * ZLint Copyright 2021 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. + */ + +package cabf_br + +import ( + "testing" + + "github.com/zmap/zlint/v3/lint" + "github.com/zmap/zlint/v3/test" +) + +func TestNoUnderscoreBefore1_6_2WithLongValidity(t *testing.T) { + testCases := []struct { + Name string + InputFilename string + ExpectedResult lint.LintStatus + }{ + { + Name: "Underscores but 30 day validity", + InputFilename: "dNSUnderscoresShortValidity.pem", + ExpectedResult: lint.NA, + }, + { + Name: "Underscores with too long validity", + InputFilename: "dNSUnderscoresLongValidity.pem", + ExpectedResult: lint.Error, + }, + { + Name: "No underscores", + InputFilename: "dNSNoUnderscoresLongValidity.pem", + ExpectedResult: lint.Pass, + }, + { + Name: "Not effective", + InputFilename: "dNSUnderscoresPermissibleOutOfDateRange.pem", + ExpectedResult: lint.NE, + }, + } + for _, tc := range testCases { + t.Run(tc.Name, func(t *testing.T) { + result := test.TestLint("e_underscore_present_with_too_long_validity", tc.InputFilename) + if result.Status != tc.ExpectedResult { + t.Errorf("expected result %v was %v", tc.ExpectedResult, result.Status) + } + }) + } +} diff --git a/v3/testdata/dNSNoUnderscoresLongValidity.pem b/v3/testdata/dNSNoUnderscoresLongValidity.pem new file mode 100644 index 000000000..1bd5d69a5 --- /dev/null +++ b/v3/testdata/dNSNoUnderscoresLongValidity.pem @@ -0,0 +1,37 @@ +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 3 (0x3) + Signature Algorithm: ecdsa-with-SHA256 + Issuer: + Validity + Not Before: Dec 10 00:00:00 2018 GMT + Not After : Jan 10 00:00:00 2019 GMT + Subject: + Subject Public Key Info: + Public Key Algorithm: id-ecPublicKey + Public-Key: (256 bit) + pub: + 04:18:1e:0d:96:53:3c:b4:d6:70:1f:b0:8b:e5:76: + bc:33:50:8f:7a:96:d4:81:af:ad:a1:8d:b3:29:a7: + 02:da:48:c7:ce:e6:83:a0:41:96:38:31:25:8f:29: + b4:a5:a6:79:bc:c9:a1:be:6c:34:b5:4b:4f:04:89: + 6c:41:d5:5d:b7 + ASN1 OID: prime256v1 + NIST CURVE: P-256 + X509v3 extensions: + X509v3 Subject Alternative Name: + DNS:this.has.no.underscores.test + Signature Algorithm: ecdsa-with-SHA256 + 30:46:02:21:00:b0:d4:c1:7d:94:9c:ef:6e:b4:14:aa:62:f9: + 8d:f4:c1:02:1e:3c:5d:91:90:0d:a8:2b:cf:ea:7f:19:b3:8e: + 58:02:21:00:ed:6a:6e:42:28:0f:2c:34:17:39:87:82:8c:14: + 7b:c2:13:e5:c7:55:1c:b1:71:26:c5:e8:15:0a:92:a8:96:44 +-----BEGIN CERTIFICATE----- +MIIBGjCBwKADAgECAgEDMAoGCCqGSM49BAMCMAAwHhcNMTgxMjEwMDAwMDAwWhcN +MTkwMTEwMDAwMDAwWjAAMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEGB4NllM8 +tNZwH7CL5Xa8M1CPepbUga+toY2zKacC2kjHzuaDoEGWODEljym0paZ5vMmhvmw0 +tUtPBIlsQdVdt6MrMCkwJwYDVR0RBCAwHoIcdGhpcy5oYXMubm8udW5kZXJzY29y +ZXMudGVzdDAKBggqhkjOPQQDAgNJADBGAiEAsNTBfZSc7260FKpi+Y30wQIePF2R +kA2oK8/qfxmzjlgCIQDtam5CKA8sNBc5h4KMFHvCE+XHVRyxcSbF6BUKkqiWRA== +-----END CERTIFICATE----- diff --git a/v3/testdata/dNSUnderscoresLongValidity.pem b/v3/testdata/dNSUnderscoresLongValidity.pem new file mode 100644 index 000000000..9e70ed205 --- /dev/null +++ b/v3/testdata/dNSUnderscoresLongValidity.pem @@ -0,0 +1,37 @@ +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 3 (0x3) + Signature Algorithm: ecdsa-with-SHA256 + Issuer: + Validity + Not Before: Dec 10 00:00:00 2018 GMT + Not After : Jan 10 00:00:00 2019 GMT + Subject: + Subject Public Key Info: + Public Key Algorithm: id-ecPublicKey + Public-Key: (256 bit) + pub: + 04:35:ff:3c:73:bf:e8:67:95:36:30:58:a4:a1:4d: + 93:3a:67:17:2c:d6:46:ca:a2:76:98:47:6f:fd:8d: + 00:1f:14:74:df:83:15:bd:95:76:d9:84:a6:b8:46: + 5e:75:35:e3:de:55:91:41:d8:29:d2:c6:5b:88:c4: + 16:a4:b7:51:12 + ASN1 OID: prime256v1 + NIST CURVE: P-256 + X509v3 extensions: + X509v3 Subject Alternative Name: + DNS:this.has_underscores.test + Signature Algorithm: ecdsa-with-SHA256 + 30:45:02:20:54:38:3c:e7:1f:4f:a5:9e:a3:b2:d4:de:20:b4: + 46:ec:93:29:de:c6:57:e7:2e:81:21:4e:bf:48:c3:c9:90:85: + 02:21:00:cc:b6:1f:29:c0:fa:d1:19:67:a0:8f:7b:10:94:ac: + 5e:bf:37:35:dc:0f:12:bd:a5:80:6f:40:d7:aa:d3:cc:3c +-----BEGIN CERTIFICATE----- +MIIBFjCBvaADAgECAgEDMAoGCCqGSM49BAMCMAAwHhcNMTgxMjEwMDAwMDAwWhcN +MTkwMTEwMDAwMDAwWjAAMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAENf88c7/o +Z5U2MFikoU2TOmcXLNZGyqJ2mEdv/Y0AHxR034MVvZV22YSmuEZedTXj3lWRQdgp +0sZbiMQWpLdREqMoMCYwJAYDVR0RBB0wG4IZdGhpcy5oYXNfdW5kZXJzY29yZXMu +dGVzdDAKBggqhkjOPQQDAgNIADBFAiBUODznH0+lnqOy1N4gtEbskynexlfnLoEh +Tr9Iw8mQhQIhAMy2HynA+tEZZ6CPexCUrF6/NzXcDxK9pYBvQNeq08w8 +-----END CERTIFICATE----- diff --git a/v3/testdata/dNSUnderscoresPermissibleOutOfDateRange.pem b/v3/testdata/dNSUnderscoresPermissibleOutOfDateRange.pem new file mode 100644 index 000000000..0919e74c0 --- /dev/null +++ b/v3/testdata/dNSUnderscoresPermissibleOutOfDateRange.pem @@ -0,0 +1,37 @@ +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 3 (0x3) + Signature Algorithm: ecdsa-with-SHA256 + Issuer: + Validity + Not Before: May 1 00:00:00 2008 GMT + Not After : Jan 10 00:00:00 2019 GMT + Subject: + Subject Public Key Info: + Public Key Algorithm: id-ecPublicKey + Public-Key: (256 bit) + pub: + 04:10:59:6c:21:8d:c7:61:6d:07:e4:d0:31:28:67: + 5b:da:36:96:a0:d6:92:75:0e:e6:9c:4d:6c:8b:e7: + ac:fd:87:4c:7b:fd:a0:fb:b6:ef:f9:ff:21:b3:9b: + bb:31:6b:0f:8e:41:b0:9d:1b:93:c1:78:8f:81:39: + 51:42:97:c1:17 + ASN1 OID: prime256v1 + NIST CURVE: P-256 + X509v3 extensions: + X509v3 Subject Alternative Name: + DNS:this.has_underscores.test + Signature Algorithm: ecdsa-with-SHA256 + 30:44:02:20:62:99:44:cb:f3:0c:6c:f9:62:26:5e:6c:4b:62: + bb:38:fa:f7:f5:fc:93:ee:03:8e:99:5e:a0:7b:10:16:a2:8c: + 02:20:70:07:4d:5f:84:eb:4c:30:12:c4:31:b1:85:d1:6c:cb: + 52:ae:4d:a6:53:40:ff:8c:98:ba:96:ee:dc:66:9a:82 +-----BEGIN CERTIFICATE----- +MIIBFTCBvaADAgECAgEDMAoGCCqGSM49BAMCMAAwHhcNMDgwNTAxMDAwMDAwWhcN +MTkwMTEwMDAwMDAwWjAAMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEEFlsIY3H +YW0H5NAxKGdb2jaWoNaSdQ7mnE1si+es/YdMe/2g+7bv+f8hs5u7MWsPjkGwnRuT +wXiPgTlRQpfBF6MoMCYwJAYDVR0RBB0wG4IZdGhpcy5oYXNfdW5kZXJzY29yZXMu +dGVzdDAKBggqhkjOPQQDAgNHADBEAiBimUTL8wxs+WImXmxLYrs4+vf1/JPuA46Z +XqB7EBaijAIgcAdNX4TrTDASxDGxhdFsy1KuTaZTQP+MmLqW7txmmoI= +-----END CERTIFICATE----- diff --git a/v3/testdata/dNSUnderscoresShortValidity.pem b/v3/testdata/dNSUnderscoresShortValidity.pem new file mode 100644 index 000000000..4dcdd35b3 --- /dev/null +++ b/v3/testdata/dNSUnderscoresShortValidity.pem @@ -0,0 +1,37 @@ +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 3 (0x3) + Signature Algorithm: ecdsa-with-SHA256 + Issuer: + Validity + Not Before: Dec 10 00:00:00 2018 GMT + Not After : Dec 11 00:00:00 2018 GMT + Subject: + Subject Public Key Info: + Public Key Algorithm: id-ecPublicKey + Public-Key: (256 bit) + pub: + 04:bc:bf:25:6c:b7:dd:83:33:f3:ad:77:46:36:e4: + bd:52:57:27:99:57:d5:e4:1a:a6:37:ab:f1:50:2a: + ca:aa:fe:c6:e5:47:5a:5c:8b:cb:1e:93:9c:5d:bf: + 66:1c:2a:18:0a:ee:b5:ba:fc:14:31:b1:88:83:62: + 23:20:ca:62:db + ASN1 OID: prime256v1 + NIST CURVE: P-256 + X509v3 extensions: + X509v3 Subject Alternative Name: + DNS:this.has_underscores.test + Signature Algorithm: ecdsa-with-SHA256 + 30:45:02:20:68:41:10:bf:a4:32:85:70:3c:2c:35:00:83:fd: + 07:33:1a:00:6b:59:bf:df:cf:86:c7:cd:11:93:ff:97:5e:6f: + 02:21:00:d3:fe:77:c3:b5:cf:64:bc:eb:30:65:bf:c1:a6:f4: + 61:89:cb:e2:c0:7e:9c:b4:87:db:88:61:78:7d:e6:dd:5e +-----BEGIN CERTIFICATE----- +MIIBFjCBvaADAgECAgEDMAoGCCqGSM49BAMCMAAwHhcNMTgxMjEwMDAwMDAwWhcN +MTgxMjExMDAwMDAwWjAAMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEvL8lbLfd +gzPzrXdGNuS9UlcnmVfV5BqmN6vxUCrKqv7G5UdaXIvLHpOcXb9mHCoYCu61uvwU +MbGIg2IjIMpi26MoMCYwJAYDVR0RBB0wG4IZdGhpcy5oYXNfdW5kZXJzY29yZXMu +dGVzdDAKBggqhkjOPQQDAgNIADBFAiBoQRC/pDKFcDwsNQCD/QczGgBrWb/fz4bH +zRGT/5debwIhANP+d8O1z2S86zBlv8Gm9GGJy+LAfpy0h9uIYXh95t1e +-----END CERTIFICATE----- diff --git a/v3/util/time.go b/v3/util/time.go index 946b8483c..2db1b9a33 100644 --- a/v3/util/time.go +++ b/v3/util/time.go @@ -21,6 +21,10 @@ import ( "github.com/zmap/zcrypto/x509" ) +const ( + DurationDay = 24 * time.Hour +) + var ( ZeroDate = time.Date(0000, time.January, 1, 0, 0, 0, 0, time.UTC) RFC1035Date = time.Date(1987, time.January, 1, 0, 0, 0, 0, time.UTC)