Skip to content

Commit

Permalink
Lint for Non-XN Reserved Labels (#635)
Browse files Browse the repository at this point in the history
* Lint for Non-XN Reserved Labels

* Refactor to use idna functions

Co-authored-by: Corey Bonnell <corey.bonnell@digicert.com>
Co-authored-by: Zakir Durumeric <zakird@gmail.com>
Co-authored-by: Christopher Henderson <chris@chenderson.org>
  • Loading branch information
4 people authored Oct 19, 2021
1 parent 9113ed8 commit cb17369
Show file tree
Hide file tree
Showing 7 changed files with 209 additions and 2 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/*
* 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 (
"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_dnsname_contains_prohibited_reserved_label",
Description: "FQDNs MUST consist solely of Domain Labels that are P‐Labels or Non‐Reserved LDH Labels",
Citation: "BRs: 7.1.4.2.1",
Source: lint.CABFBaselineRequirements,
EffectiveDate: util.NoReservedDomainLabelsDate,
Lint: NewDNSNameContainsProhibitedReservedLabel,
})
}

type DNSNameContainsProhibitedReservedLabel struct{}

func NewDNSNameContainsProhibitedReservedLabel() lint.LintInterface {
return &DNSNameContainsProhibitedReservedLabel{}
}

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

func (l *DNSNameContainsProhibitedReservedLabel) Execute(c *x509.Certificate) *lint.LintResult {
for _, dns := range c.DNSNames {
labels := strings.Split(dns, ".")

for _, label := range labels {
if util.HasReservedLabelPrefix(label) && !util.HasXNLabelPrefix(label) {
return &lint.LintResult{Status: lint.Error}
}
}
}

return &lint.LintResult{Status: lint.Pass}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package cabf_br

/*
* 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.
*/

import (
"testing"

"github.com/zmap/zlint/v3/lint"
"github.com/zmap/zlint/v3/test"
)

func TestDNSNameHasProhibitedReservedLabel(t *testing.T) {
inputPath := "dnsNameProhibitedReservedLabel.pem"
expected := lint.Error
out := test.TestLint("e_dnsname_contains_prohibited_reserved_label", inputPath)
if out.Status != expected {
t.Errorf("%s: expected %s, got %s", inputPath, expected, out.Status)
}
}

func TestDNSNameHasXNLabel(t *testing.T) {
inputPath := "dnsNameXNLabel.pem"
expected := lint.Pass
out := test.TestLint("e_dnsname_contains_prohibited_reserved_label", inputPath)
if out.Status != expected {
t.Errorf("%s: expected %s, got %s", inputPath, expected, out.Status)
}
}
38 changes: 38 additions & 0 deletions v3/testdata/dnsNameProhibitedReservedLabel.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
Certificate:
Data:
Version: 3 (0x2)
Serial Number:
78:a9:8c:5d:a5:fd:28:95:fa:61:28:08:69:87:76:f5:b1:9e:67:fd
Signature Algorithm: sha256WithRSAEncryption
Issuer: CN = Bar
Validity
Not Before: Oct 1 00:00:00 2021 GMT
Not After : Oct 1 00:00:00 2022 GMT
Subject: CN = Foo
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
RSA Public-Key: (512 bit)
Modulus:
00:e8:a0:a2:22:4e:8d:a1:62:63:ca:d2:4e:c8:10:
97:97:d7:ad:c5:cc:27:f7:fd:5c:78:fc:dc:87:b1:
cf:b7:15:44:4a:1b:42:5b:7d:08:93:54:80:7a:bf:
af:d1:cd:4a:9a:9b:ad:f5:36:9e:5f:69:20:98:d1:
9a:7e:9c:67:73
Exponent: 65537 (0x10001)
X509v3 extensions:
X509v3 Subject Alternative Name:
DNS:xr--rahrah
Signature Algorithm: sha256WithRSAEncryption
2c:e5:0b:6e:d7:51:ff:f2:07:6a:4f:91:0e:8d:8c:84:6f:ea:
ba:11:85:b0:f2:1a:18:92:90:a0:93:d5:dd:70:3b:50:7a:47:
9b:2e:d1:2c:4a:c3:34:63:fa:33:c7:f1:76:2c:95:23:91:5d:
c4:45:ea:db:54:07:6e:0c:cb:18
-----BEGIN CERTIFICATE-----
MIIBODCB46ADAgECAhR4qYxdpf0olfphKAhph3b1sZ5n/TANBgkqhkiG9w0BAQsF
ADAOMQwwCgYDVQQDDANCYXIwHhcNMjExMDAxMDAwMDAwWhcNMjIxMDAxMDAwMDAw
WjAOMQwwCgYDVQQDDANGb28wXDANBgkqhkiG9w0BAQEFAANLADBIAkEA6KCiIk6N
oWJjytJOyBCXl9etxcwn9/1cePzch7HPtxVEShtCW30Ik1SAer+v0c1Kmput9Tae
X2kgmNGafpxncwIDAQABoxkwFzAVBgNVHREEDjAMggp4ci0tcmFocmFoMA0GCSqG
SIb3DQEBCwUAA0EALOULbtdR//IHak+RDo2MhG/quhGFsPIaGJKQoJPV3XA7UHpH
my7RLErDNGP6M8fxdiyVI5FdxEXq21QHbgzLGA==
-----END CERTIFICATE-----
38 changes: 38 additions & 0 deletions v3/testdata/dnsNameXNLabel.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
Certificate:
Data:
Version: 3 (0x2)
Serial Number:
72:97:35:23:08:57:73:30:eb:cf:f5:47:18:81:0b:4f:25:e2:6a:ef
Signature Algorithm: sha256WithRSAEncryption
Issuer: CN = Bar
Validity
Not Before: Oct 1 00:00:00 2021 GMT
Not After : Oct 1 00:00:00 2022 GMT
Subject: CN = Foo
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
RSA Public-Key: (512 bit)
Modulus:
00:aa:71:4b:ae:d4:0c:ee:da:6c:b8:f0:1e:a0:e8:
dc:1e:98:91:7d:64:b3:26:0a:77:70:f7:6f:6f:e3:
f2:ed:05:7f:4a:0e:45:07:98:32:3b:66:0c:01:9f:
7d:6f:75:c1:ed:08:c0:dd:73:bf:a9:80:9b:31:1a:
e7:db:40:41:4b
Exponent: 65537 (0x10001)
X509v3 extensions:
X509v3 Subject Alternative Name:
DNS:xN--foo
Signature Algorithm: sha256WithRSAEncryption
9f:14:e2:58:4e:28:a2:0e:bb:53:68:63:07:ba:ba:3c:ce:72:
52:b2:22:66:2d:8a:e8:7e:fc:83:fd:83:8f:96:b7:96:81:9e:
4b:e0:6f:c1:86:bf:99:de:c5:fd:b6:f1:dd:f6:86:2c:b9:3f:
3f:93:31:a1:5c:20:a7:2d:46:08
-----BEGIN CERTIFICATE-----
MIIBNTCB4KADAgECAhRylzUjCFdzMOvP9UcYgQtPJeJq7zANBgkqhkiG9w0BAQsF
ADAOMQwwCgYDVQQDDANCYXIwHhcNMjExMDAxMDAwMDAwWhcNMjIxMDAxMDAwMDAw
WjAOMQwwCgYDVQQDDANGb28wXDANBgkqhkiG9w0BAQEFAANLADBIAkEAqnFLrtQM
7tpsuPAeoOjcHpiRfWSzJgp3cPdvb+Py7QV/Sg5FB5gyO2YMAZ99b3XB7QjA3XO/
qYCbMRrn20BBSwIDAQABoxYwFDASBgNVHREECzAJggd4Ti0tZm9vMA0GCSqGSIb3
DQEBCwUAA0EAnxTiWE4oog67U2hjB7q6PM5yUrIiZi2K6H78g/2Dj5a3loGeS+Bv
wYa/md7F/bbx3faGLLk/P5MxoVwgpy1GCA==
-----END CERTIFICATE-----
13 changes: 12 additions & 1 deletion v3/util/idna.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,20 @@ import (
"golang.org/x/net/idna"
)

var reservedLabelPrefix = regexp.MustCompile(`^..--`)

var xnLabelPrefix = regexp.MustCompile(`(?i)^xn--`)

// HasXNLabelPrefix checks whether-or-not the given string (presumably a
// HasReservedLabelPrefix checks whether the given string (presumably
// a domain label) has hyphens ("-") as the third and fourth characters. Domain
// labels with hyphens in these positions are considered to be "Reserved Labels"
// per RFC 5890, section 2.3.1.
// (https://datatracker.ietf.org/doc/html/rfc5890#section-2.3.1)
func HasReservedLabelPrefix(s string) bool {
return reservedLabelPrefix.MatchString(s)
}

// HasXNLabelPrefix checks whether the given string (presumably a
// domain label) is prefixed with the case-insensitive string "xn--" (the
// IDNA ACE prefix).
//
Expand Down
23 changes: 22 additions & 1 deletion v3/util/idna_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,28 @@ import (
"golang.org/x/net/idna"
)

func TestIsIdnaACEPrefixed(t *testing.T) {
func TestHasReservedLabelPrefix(t *testing.T) {
input := map[string]bool{
"ab--": true,
"ab--foo": true,
"a---foo": true,
"A---foo": true,
"XN--foo": true,
"": false,
"a-b": false,
"a--": false,
"foobar--aa": false,
"XNA--foo": false,
}
for input, want := range input {
got := HasReservedLabelPrefix(input)
if got != want {
t.Errorf("got %v want %v for input '%s'", got, want, input)
}
}
}

func TestHasXNLabelPrefix(t *testing.T) {
input := map[string]bool{
"xn--zlint.org": true,
"Xn--zlint.org": true,
Expand Down
1 change: 1 addition & 0 deletions v3/util/time.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ var (
CABFBRs_1_7_1_Date = time.Date(2020, time.August, 20, 0, 0, 0, 0, time.UTC)
AppleReducedLifetimeDate = time.Date(2020, time.September, 1, 0, 0, 0, 0, time.UTC)
CABFBRs_1_8_0_Date = time.Date(2021, time.August, 21, 0, 0, 0, 0, time.UTC)
NoReservedDomainLabelsDate = time.Date(2021, time.October, 1, 0, 0, 0, 0, time.UTC)
)

var (
Expand Down

0 comments on commit cb17369

Please sign in to comment.