Skip to content

Commit e4dafa3

Browse files
committed
crypto/x509: fix name constraints handling.
This change brings the behaviour of X.509 name constraints into line with NSS[1]. In this area, the behavior specified by the RFC and by NIST differs and this code follows the NIST behaviour. [1] https://github.com/servo/nss/blob/master/lib/certdb/genname.c Fixes #16347, fixes #14833. Change-Id: I5acd1970041291c2e3936f5b1fd36f2a0338e613 Reviewed-on: https://go-review.googlesource.com/30155 Reviewed-by: Brad Fitzpatrick <[email protected]>
1 parent 2d573ee commit e4dafa3

File tree

2 files changed

+52
-6
lines changed

2 files changed

+52
-6
lines changed

src/crypto/x509/verify.go

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,31 @@ const (
154154
rootCertificate
155155
)
156156

157+
func matchNameConstraint(domain, constraint string) bool {
158+
// The meaning of zero length constraints is not specified, but this
159+
// code follows NSS and accepts them as valid for everything.
160+
if len(constraint) == 0 {
161+
return true
162+
}
163+
164+
if len(domain) < len(constraint) {
165+
return false
166+
}
167+
168+
prefixLen := len(domain) - len(constraint)
169+
if !strings.EqualFold(domain[prefixLen:], constraint) {
170+
return false
171+
}
172+
173+
if prefixLen == 0 {
174+
return true
175+
}
176+
177+
isSubdomain := domain[prefixLen-1] == '.'
178+
constraintHasLeadingDot := constraint[0] == '.'
179+
return isSubdomain != constraintHasLeadingDot
180+
}
181+
157182
// isValid performs validity checks on the c.
158183
func (c *Certificate) isValid(certType int, currentChain []*Certificate, opts *VerifyOptions) error {
159184
now := opts.CurrentTime
@@ -166,12 +191,9 @@ func (c *Certificate) isValid(certType int, currentChain []*Certificate, opts *V
166191

167192
if len(c.PermittedDNSDomains) > 0 {
168193
ok := false
169-
for _, domain := range c.PermittedDNSDomains {
170-
if opts.DNSName == domain ||
171-
(strings.HasSuffix(opts.DNSName, domain) &&
172-
len(opts.DNSName) >= 1+len(domain) &&
173-
opts.DNSName[len(opts.DNSName)-len(domain)-1] == '.') {
174-
ok = true
194+
for _, constraint := range c.PermittedDNSDomains {
195+
ok = matchNameConstraint(opts.DNSName, constraint)
196+
if ok {
175197
break
176198
}
177199
}

src/crypto/x509/verify_test.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1164,6 +1164,30 @@ func TestUnknownAuthorityError(t *testing.T) {
11641164
}
11651165
}
11661166

1167+
var nameConstraintTests = []struct {
1168+
constraint, domain string
1169+
shouldMatch bool
1170+
}{
1171+
{"", "anything.com", true},
1172+
{"example.com", "example.com", true},
1173+
{"example.com", "ExAmPle.coM", true},
1174+
{"example.com", "exampl1.com", false},
1175+
{"example.com", "www.ExAmPle.coM", true},
1176+
{"example.com", "notexample.com", false},
1177+
{".example.com", "example.com", false},
1178+
{".example.com", "www.example.com", true},
1179+
{".example.com", "www..example.com", false},
1180+
}
1181+
1182+
func TestNameConstraints(t *testing.T) {
1183+
for i, test := range nameConstraintTests {
1184+
result := matchNameConstraint(test.domain, test.constraint)
1185+
if result != test.shouldMatch {
1186+
t.Errorf("unexpected result for test #%d: domain=%s, constraint=%s, result=%t", i, test.domain, test.constraint, result)
1187+
}
1188+
}
1189+
}
1190+
11671191
const selfSignedWithCommonName = `-----BEGIN CERTIFICATE-----
11681192
MIIDCjCCAfKgAwIBAgIBADANBgkqhkiG9w0BAQsFADAaMQswCQYDVQQKEwJjYTEL
11691193
MAkGA1UEAxMCY2EwHhcNMTYwODI4MTcwOTE4WhcNMjEwODI3MTcwOTE4WjAcMQsw

0 commit comments

Comments
 (0)