Skip to content

Commit

Permalink
Permit underscores in DNSNames if-and-only-if replacing all underscor…
Browse files Browse the repository at this point in the history
…es results in valid LDH labels during BR 1.6.2's permissibility period (#661)

Co-authored-by: David Adrian <[email protected]>
  • Loading branch information
christopher-henderson and dadrian authored Sep 27, 2023
1 parent ba30b3b commit 48baa89
Show file tree
Hide file tree
Showing 7 changed files with 241 additions and 20 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/*
* 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_permissible_in_dnsname_if_valid_when_replaced",
Description: "From December 10th 2018 to April 1st 2019 DNSNames may contain underscores if-and-only-if every label within each DNS name is a valid LDH label after replacing all underscores with hyphens",
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 &UnderscorePermissibleInDNSNameIfValidWhenReplaced{} },
})
}

type UnderscorePermissibleInDNSNameIfValidWhenReplaced struct{}

func (l *UnderscorePermissibleInDNSNameIfValidWhenReplaced) CheckApplies(c *x509.Certificate) bool {
return util.IsSubscriberCert(c) && util.DNSNamesExist(c)
}

func (l *UnderscorePermissibleInDNSNameIfValidWhenReplaced) Execute(c *x509.Certificate) *lint.LintResult {
for _, dns := range c.DNSNames {
for _, label := range strings.Split(dns, ".") {
if !strings.Contains(label, "_") || label == "*" {
continue
}
replaced := strings.ReplaceAll(label, "_", "-")
if !util.IsLDHLabel(replaced) {
return &lint.LintResult{Status: lint.Error, Details: fmt.Sprintf("When all underscores (_) in %q are replaced with hypens (-) the result is %q which not a valid LDH label", label, replaced)}
}
}
}
return &lint.LintResult{Status: lint.Pass}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/*
* 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 TestUnderscoresInPermissibilityPeriodBecomeValidAfterReplacement(t *testing.T) {
testCases := []struct {
Name string
InputFilename string
ExpectedResult lint.LintStatus
}{
{
Name: "Valid when replaced",
InputFilename: "dNSNameUnderscoreValidWhenReplaced.pem",
ExpectedResult: lint.Pass,
},
{
Name: "Invalid when replaced",
InputFilename: "dNSNameUnderscoreNotValidWhenReplaced.pem",
ExpectedResult: lint.Error,
},
{
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_permissible_in_dnsname_if_valid_when_replaced", tc.InputFilename)
if result.Status != tc.ExpectedResult {
t.Errorf("expected result %v was %v", tc.ExpectedResult, result.Status)
}
})
}
}
37 changes: 37 additions & 0 deletions v3/testdata/dNSNameUnderscoreNotValidWhenReplaced.pem
Original file line number Diff line number Diff line change
@@ -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:76:81:ad:a1:7c:e7:08:12:02:3d:82:3f:e6:5c:
7a:09:bb:88:70:3e:64:e3:51:ec:e1:c1:62:0c:71:
21:87:48:9c:8e:43:d5:75:42:82:58:02:19:0b:1e:
7d:cf:dc:f1:eb:62:5b:5d:e0:e7:77:63:ff:f5:97:
82:cc:ee:49:81
ASN1 OID: prime256v1
NIST CURVE: P-256
X509v3 extensions:
X509v3 Subject Alternative Name:
DNS:with._an_underscore.test
Signature Algorithm: ecdsa-with-SHA256
30:45:02:20:62:de:b0:2a:43:04:88:12:c9:22:de:fe:db:33:
3a:77:01:cf:51:e1:e0:60:cb:5f:fb:c8:a6:44:b7:ab:91:45:
02:21:00:e7:b4:95:a8:f6:dd:2b:4a:d1:6a:e7:f6:d0:21:90:
6c:70:97:ce:2b:d5:07:b6:1a:63:49:34:64:88:90:25:13
-----BEGIN CERTIFICATE-----
MIIBFTCBvKADAgECAgEDMAoGCCqGSM49BAMCMAAwHhcNMTgxMjEwMDAwMDAwWhcN
MTkwMTEwMDAwMDAwWjAAMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEdoGtoXzn
CBICPYI/5lx6CbuIcD5k41Hs4cFiDHEhh0icjkPVdUKCWAIZCx59z9zx62JbXeDn
d2P/9ZeCzO5JgaMnMCUwIwYDVR0RBBwwGoIYd2l0aC5fYW5fdW5kZXJzY29yZS50
ZXN0MAoGCCqGSM49BAMCA0gAMEUCIGLesCpDBIgSySLe/tszOncBz1Hh4GDLX/vI
pkS3q5FFAiEA57SVqPbdK0rRauf20CGQbHCXzivVB7YaY0k0ZIiQJRM=
-----END CERTIFICATE-----
37 changes: 37 additions & 0 deletions v3/testdata/dNSNameUnderscoreValidWhenReplaced.pem
Original file line number Diff line number Diff line change
@@ -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:60:1e:5b:1d:9d:18:b4:6f:85:97:c8:02:18:7e:
4b:ba:f4:24:9f:e2:34:e4:85:1c:17:b2:e1:e5:be:
cc:56:b9:84:e7:f8:88:21:5d:e1:ba:59:7d:7e:6b:
0e:cb:ec:f7:0c:e7:73:cb:6c:27:79:71:2c:4b:ba:
1b:3e:a9:12:a5
ASN1 OID: prime256v1
NIST CURVE: P-256
X509v3 extensions:
X509v3 Subject Alternative Name:
DNS:with.an_underscore.test
Signature Algorithm: ecdsa-with-SHA256
30:45:02:20:32:84:d7:38:0d:28:2c:fe:9e:6e:40:64:27:05:
42:64:e7:c8:de:ba:91:cc:ce:f9:9c:34:77:55:a5:58:4f:38:
02:21:00:fd:a3:73:bb:b0:45:b3:b3:85:61:db:ad:85:af:6c:
a1:69:ed:0c:9e:bb:ec:a8:41:14:db:c3:73:4c:1c:40:ef
-----BEGIN CERTIFICATE-----
MIIBFDCBu6ADAgECAgEDMAoGCCqGSM49BAMCMAAwHhcNMTgxMjEwMDAwMDAwWhcN
MTkwMTEwMDAwMDAwWjAAMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEYB5bHZ0Y
tG+Fl8gCGH5LuvQkn+I05IUcF7Lh5b7MVrmE5/iIIV3hull9fmsOy+z3DOdzy2wn
eXEsS7obPqkSpaMmMCQwIgYDVR0RBBswGYIXd2l0aC5hbl91bmRlcnNjb3JlLnRl
c3QwCgYIKoZIzj0EAwIDSAAwRQIgMoTXOA0oLP6ebkBkJwVCZOfI3rqRzM75nDR3
VaVYTzgCIQD9o3O7sEWzs4Vh262Fr2yhae0MnrvsqEEU28NzTBxA7w==
-----END CERTIFICATE-----
40 changes: 20 additions & 20 deletions v3/testdata/dNSUnderscoresPermissibleOutOfDateRange.pem
Original file line number Diff line number Diff line change
Expand Up @@ -3,35 +3,35 @@ Certificate:
Version: 3 (0x2)
Serial Number: 3 (0x3)
Signature Algorithm: ecdsa-with-SHA256
Issuer:
Issuer:
Validity
Not Before: May 1 00:00:00 2008 GMT
Not After : Jan 10 00:00:00 2019 GMT
Subject:
Not After : Dec 10 00:00:00 2018 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
04:da:65:3e:9c:55:66:12:20:df:6a:79:3d:59:a8:
a9:00:1c:91:b7:c3:61:00:3c:4f:ba:19:a5:05:7e:
b0:63:a5:60:08:cf:d9:a5:8d:9e:57:71:05:d6:4a:
55:f9:33:c5:23:24:2d:32:1f:94:f3:1f:29:03:09:
98:36:b1:b7:26
ASN1 OID: prime256v1
NIST CURVE: P-256
X509v3 extensions:
X509v3 Subject Alternative Name:
DNS:this.has_underscores.test
X509v3 Subject Alternative Name:
DNS:with._an_underscore.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
30:45:02:20:4f:a4:45:ee:97:f6:37:3e:ad:c1:28:7b:d9:f8:
68:df:cb:52:4e:93:0c:81:ab:ca:94:aa:fa:58:f2:9a:2f:07:
02:21:00:ed:42:41:c5:12:2c:62:8e:a3:64:7e:20:2a:d8:b3:
f8:6a:7f:3f:29:8e:fc:0d:aa:ac:17:14:e4:18:f4:cd:f2
-----BEGIN CERTIFICATE-----
MIIBFTCBvaADAgECAgEDMAoGCCqGSM49BAMCMAAwHhcNMDgwNTAxMDAwMDAwWhcN
MTkwMTEwMDAwMDAwWjAAMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEEFlsIY3H
YW0H5NAxKGdb2jaWoNaSdQ7mnE1si+es/YdMe/2g+7bv+f8hs5u7MWsPjkGwnRuT
wXiPgTlRQpfBF6MoMCYwJAYDVR0RBB0wG4IZdGhpcy5oYXNfdW5kZXJzY29yZXMu
dGVzdDAKBggqhkjOPQQDAgNHADBEAiBimUTL8wxs+WImXmxLYrs4+vf1/JPuA46Z
XqB7EBaijAIgcAdNX4TrTDASxDGxhdFsy1KuTaZTQP+MmLqW7txmmoI=
MIIBFTCBvKADAgECAgEDMAoGCCqGSM49BAMCMAAwHhcNMDgwNTAxMDAwMDAwWhcN
MTgxMjEwMDAwMDAwWjAAMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE2mU+nFVm
EiDfank9WaipAByRt8NhADxPuhmlBX6wY6VgCM/ZpY2eV3EF1kpV+TPFIyQtMh+U
8x8pAwmYNrG3JqMnMCUwIwYDVR0RBBwwGoIYd2l0aC5fYW5fdW5kZXJzY29yZS50
ZXN0MAoGCCqGSM49BAMCA0gAMEUCIE+kRe6X9jc+rcEoe9n4aN/LUk6TDIGrypSq
+ljymi8HAiEA7UJBxRIsYo6jZH4gKtiz+Gp/PymO/A2qrBcU5Bj0zfI=
-----END CERTIFICATE-----
12 changes: 12 additions & 0 deletions v3/util/fqdn.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ package util
import (
"net"
"net/url"
"regexp"
"strings"

zcutil "github.com/zmap/zcrypto/util"
Expand Down Expand Up @@ -117,3 +118,14 @@ func CommonNameIsIP(cert *x509.Certificate) bool {
return true
}
}

var nonLDHCharacterRegex = regexp.MustCompile(`[^a-zA-Z0-9\-]`)

func IsLDHLabel(label string) bool {
return len(label) > 0 &&
len(label) <= 63 &&
!nonLDHCharacterRegex.MatchString(label) &&
!strings.HasPrefix(label, "-") &&
!strings.HasSuffix(label, "-") &&
!(HasReservedLabelPrefix(label) && !HasXNLabelPrefix(label))
}
24 changes: 24 additions & 0 deletions v3/util/fqdn_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1012,3 +1012,27 @@ func TestGetHostWithUserinfoWithPortWithAbsolutePathWithQueryWithFragment(t *tes
)
}
}

func TestIsLDHLabel(t *testing.T) {
data := map[string]bool{
"": false,
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa": false,
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa": true,
"9": true,
"9a": true,
"a9": true,
"a": true,
".": false,
"a-b": true,
"-a": false,
"a-": false,
"-": false,
"%": false,
}
for input, want := range data {
got := IsLDHLabel(input)
if got != want {
t.Errorf("expected %v got %v for '%s'", want, got, input)
}
}
}

0 comments on commit 48baa89

Please sign in to comment.