mirror of https://github.com/golang/go.git
crypto/x509: support excluded domains in name constraints.
Change-Id: I4c2c82cb0354f843a3283a650ed2cd2b6aef5895 Reviewed-on: https://go-review.googlesource.com/36900 Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org> Run-TryBot: Adam Langley <agl@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org>
This commit is contained in:
parent
17c228b29b
commit
d1211b9a9f
|
|
@ -166,7 +166,7 @@ const (
|
||||||
|
|
||||||
func matchNameConstraint(domain, constraint string) bool {
|
func matchNameConstraint(domain, constraint string) bool {
|
||||||
// The meaning of zero length constraints is not specified, but this
|
// The meaning of zero length constraints is not specified, but this
|
||||||
// code follows NSS and accepts them as valid for everything.
|
// code follows NSS and accepts them as matching everything.
|
||||||
if len(constraint) == 0 {
|
if len(constraint) == 0 {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
@ -220,6 +220,12 @@ func (c *Certificate) isValid(certType int, currentChain []*Certificate, opts *V
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for _, constraint := range c.ExcludedDNSDomains {
|
||||||
|
if matchNameConstraint(opts.DNSName, constraint) {
|
||||||
|
return CertificateInvalidError{c, CANotAuthorizedForThisName}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// KeyUsage status flags are ignored. From Engineering Security, Peter
|
// KeyUsage status flags are ignored. From Engineering Security, Peter
|
||||||
// Gutmann: A European government CA marked its signing certificates as
|
// Gutmann: A European government CA marked its signing certificates as
|
||||||
// being valid for encryption only, but no-one noticed. Another
|
// being valid for encryption only, but no-one noticed. Another
|
||||||
|
|
|
||||||
|
|
@ -285,6 +285,17 @@ var verifyTests = []verifyTest{
|
||||||
|
|
||||||
errorCallback: expectHostnameError,
|
errorCallback: expectHostnameError,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
// Test that excluded names are respected.
|
||||||
|
leaf: excludedNamesLeaf,
|
||||||
|
dnsName: "bender.local",
|
||||||
|
intermediates: []string{excludedNamesIntermediate},
|
||||||
|
roots: []string{excludedNamesRoot},
|
||||||
|
currentTime: 1486684488,
|
||||||
|
systemSkip: true,
|
||||||
|
|
||||||
|
errorCallback: expectNameConstraintsError,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
func expectHostnameError(t *testing.T, i int, err error) (ok bool) {
|
func expectHostnameError(t *testing.T, i int, err error) (ok bool) {
|
||||||
|
|
@ -352,6 +363,14 @@ func expectSubjectIssuerMismatcthError(t *testing.T, i int, err error) (ok bool)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func expectNameConstraintsError(t *testing.T, i int, err error) (ok bool) {
|
||||||
|
if inval, ok := err.(CertificateInvalidError); !ok || inval.Reason != CANotAuthorizedForThisName {
|
||||||
|
t.Errorf("#%d: error was not a CANotAuthorizedForThisName: %s", i, err)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
func expectNotAuthorizedError(t *testing.T, i int, err error) (ok bool) {
|
func expectNotAuthorizedError(t *testing.T, i int, err error) (ok bool) {
|
||||||
if inval, ok := err.(CertificateInvalidError); !ok || inval.Reason != NotAuthorizedToSign {
|
if inval, ok := err.(CertificateInvalidError); !ok || inval.Reason != NotAuthorizedToSign {
|
||||||
t.Errorf("#%d: error was not a NotAuthorizedToSign: %s", i, err)
|
t.Errorf("#%d: error was not a NotAuthorizedToSign: %s", i, err)
|
||||||
|
|
@ -1390,6 +1409,83 @@ j2kBQyvnyKsXHLAKUoUOpd6t/1PHrfXnGj+HmzZNloJ/BZ1kiWb4eLvMljoLGkZn
|
||||||
xZbqP3Krgjj4XNaXjg==
|
xZbqP3Krgjj4XNaXjg==
|
||||||
-----END CERTIFICATE-----`
|
-----END CERTIFICATE-----`
|
||||||
|
|
||||||
|
const excludedNamesLeaf = `
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIID4DCCAsigAwIBAgIHDUSFtJknhzANBgkqhkiG9w0BAQsFADCBnjELMAkGA1UE
|
||||||
|
BhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExEjAQBgNVBAcMCUxvcyBHYXRvczEU
|
||||||
|
MBIGA1UECgwLTmV0ZmxpeCBJbmMxLTArBgNVBAsMJFBsYXRmb3JtIFNlY3VyaXR5
|
||||||
|
ICgzNzM0NTE1NTYyODA2Mzk3KTEhMB8GA1UEAwwYSW50ZXJtZWRpYXRlIENBIGZv
|
||||||
|
ciAzMzkyMB4XDTE3MDIwODIxMTUwNFoXDTE4MDIwODIwMjQ1OFowgZAxCzAJBgNV
|
||||||
|
BAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRIwEAYDVQQHDAlMb3MgR2F0b3Mx
|
||||||
|
FDASBgNVBAoMC05ldGZsaXggSW5jMS0wKwYDVQQLDCRQbGF0Zm9ybSBTZWN1cml0
|
||||||
|
eSAoMzczNDUxNTc0ODUwMjY5NikxEzARBgNVBAMMCjE3Mi4xNi4wLjEwggEiMA0G
|
||||||
|
CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCZ0oP1bMv6bOeqcKbzinnGpNOpenhA
|
||||||
|
zdFFsgea62znWsH3Wg4+1Md8uPCqlaQIsaJQKZHc50eKD3bg0Io7c6kxHkBQr1b8
|
||||||
|
Q7cGeK3CjdqG3NwS/aizzrLKOwL693hFwwy7JY7GGCvogbhyQRKn6iV0U9zMm7bu
|
||||||
|
/9pQVV/wx8u01u2uAlLttjyQ5LJkxo5t8cATFVqxdN5J9eY//VSDiTwXnlpQITBP
|
||||||
|
/Ow+zYuZ3kFlzH3CtCOhOEvNG3Ar1NvP3Icq35PlHV+Eki4otnKfixwByoiGpqCB
|
||||||
|
UEIY04VrZJjwBxk08y/3jY2B3VLYGgi+rryyCxIqkB7UpSNPMMWSG4UpAgMBAAGj
|
||||||
|
LzAtMAwGA1UdEwEB/wQCMAAwHQYDVR0RBBYwFIIMYmVuZGVyLmxvY2FshwSsEAAB
|
||||||
|
MA0GCSqGSIb3DQEBCwUAA4IBAQCLW3JO8L7LKByjzj2RciPjCGH5XF87Wd20gYLq
|
||||||
|
sNKcFwCIeyZhnQy5aZ164a5G9AIk2HLvH6HevBFPhA9Ivmyv/wYEfnPd1VcFkpgP
|
||||||
|
hDt8MCFJ8eSjCyKdtZh1MPMLrLVymmJV+Rc9JUUYM9TIeERkpl0rskcO1YGewkYt
|
||||||
|
qKlWE+0S16+pzsWvKn831uylqwIb8ANBPsCX4aM4muFBHavSWAHgRO+P+yXVw8Q+
|
||||||
|
VQDnMHUe5PbZd1/+1KKVs1K/CkBCtoHNHp1d/JT+2zUQJphwja9CcgfFdVhSnHL4
|
||||||
|
oEEOFtqVMIuQfR2isi08qW/JGOHc4sFoLYB8hvdaxKWSE19A
|
||||||
|
-----END CERTIFICATE-----
|
||||||
|
`
|
||||||
|
|
||||||
|
const excludedNamesIntermediate = `
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIDzTCCArWgAwIBAgIHDUSFqYeczDANBgkqhkiG9w0BAQsFADCBmTELMAkGA1UE
|
||||||
|
BhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExEjAQBgNVBAcMCUxvcyBHYXRvczEU
|
||||||
|
MBIGA1UECgwLTmV0ZmxpeCBJbmMxLTArBgNVBAsMJFBsYXRmb3JtIFNlY3VyaXR5
|
||||||
|
ICgzNzM0NTE1NDc5MDY0NjAyKTEcMBoGA1UEAwwTTG9jYWwgUm9vdCBmb3IgMzM5
|
||||||
|
MjAeFw0xNzAyMDgyMTE1MDRaFw0xODAyMDgyMDI0NThaMIGeMQswCQYDVQQGEwJV
|
||||||
|
UzETMBEGA1UECAwKQ2FsaWZvcm5pYTESMBAGA1UEBwwJTG9zIEdhdG9zMRQwEgYD
|
||||||
|
VQQKDAtOZXRmbGl4IEluYzEtMCsGA1UECwwkUGxhdGZvcm0gU2VjdXJpdHkgKDM3
|
||||||
|
MzQ1MTU1NjI4MDYzOTcpMSEwHwYDVQQDDBhJbnRlcm1lZGlhdGUgQ0EgZm9yIDMz
|
||||||
|
OTIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCOyEs6tJ/t9emQTvlx
|
||||||
|
3FS7uJSou5rKkuqVxZdIuYQ+B2ZviBYUnMRT9bXDB0nsVdKZdp0hdchdiwNXDG/I
|
||||||
|
CiWu48jkcv/BdynVyayOT+0pOJSYLaPYpzBx1Pb9M5651ct9GSbj6Tz0ChVonoIE
|
||||||
|
1AIZ0kkebucZRRFHd0xbAKVRKyUzPN6HJ7WfgyauUp7RmlC35wTmrmARrFohQLlL
|
||||||
|
7oICy+hIQePMy9x1LSFTbPxZ5AUUXVC3eUACU3vLClF/Xs8XGHebZpUXCdMQjOGS
|
||||||
|
nq1eFguFHR1poSB8uSmmLqm4vqUH9CDhEgiBAC8yekJ8//kZQ7lUEqZj3YxVbk+Y
|
||||||
|
E4H5AgMBAAGjEzARMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEB
|
||||||
|
ADxrnmNX5gWChgX9K5fYwhFDj5ofxZXAKVQk+WjmkwMcmCx3dtWSm++Wdksj/ZlA
|
||||||
|
V1cLW3ohWv1/OAZuOlw7sLf98aJpX+UUmIYYQxDubq+4/q7VA7HzEf2k/i/oN1NI
|
||||||
|
JgtrhpPcZ/LMO6k7DYx0qlfYq8pTSfd6MI4LnWKgLc+JSPJJjmvspgio2ZFcnYr7
|
||||||
|
A264BwLo6v1Mos1o1JUvFFcp4GANlw0XFiWh7JXYRl8WmS5DoouUC+aNJ3lmyF6z
|
||||||
|
LbIjZCSfgZnk/LK1KU1j91FI2bc2ULYZvAC1PAg8/zvIgxn6YM2Q7ZsdEgWw0FpS
|
||||||
|
zMBX1/lk4wkFckeUIlkD55Y=
|
||||||
|
-----END CERTIFICATE-----`
|
||||||
|
|
||||||
|
const excludedNamesRoot = `
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIEGTCCAwGgAwIBAgIHDUSFpInn/zANBgkqhkiG9w0BAQsFADCBozELMAkGA1UE
|
||||||
|
BhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExEjAQBgNVBAcMCUxvcyBHYXRvczEU
|
||||||
|
MBIGA1UECgwLTmV0ZmxpeCBJbmMxLTArBgNVBAsMJFBsYXRmb3JtIFNlY3VyaXR5
|
||||||
|
ICgzNzMxNTA5NDM3NDYyNDg1KTEmMCQGA1UEAwwdTmFtZSBDb25zdHJhaW50cyBU
|
||||||
|
ZXN0IFJvb3QgQ0EwHhcNMTcwMjA4MjExNTA0WhcNMTgwMjA4MjAyNDU4WjCBmTEL
|
||||||
|
MAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExEjAQBgNVBAcMCUxvcyBH
|
||||||
|
YXRvczEUMBIGA1UECgwLTmV0ZmxpeCBJbmMxLTArBgNVBAsMJFBsYXRmb3JtIFNl
|
||||||
|
Y3VyaXR5ICgzNzM0NTE1NDc5MDY0NjAyKTEcMBoGA1UEAwwTTG9jYWwgUm9vdCBm
|
||||||
|
b3IgMzM5MjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJymcnX29ekc
|
||||||
|
7+MLyr8QuAzoHWznmGdDd2sITwWRjM89/21cdlHCGKSpULUNdFp9HDLWvYECtxt+
|
||||||
|
8TuzKiQz7qAerzGUT1zI5McIjHy0e/i4xIkfiBiNeTCuB/N9QRbZlcfM80ErkaA4
|
||||||
|
gCAFK8qZAcWkHIl6e+KaQFMPLKk9kckgAnVDHEJe8oLNCogCJ15558b65g05p9eb
|
||||||
|
5Lg+E98hoPRTQaDwlz3CZPfTTA2EiEZInSi8qzodFCbTpJUVTbiVUH/JtVjlibbb
|
||||||
|
smdcx5PORK+8ZJkhLEh54AjaWOX4tB/7Tkk8stg2VBmrIARt/j4UVj7cTrIWU3bV
|
||||||
|
m8TwHJG+YgsCAwEAAaNaMFgwDwYDVR0TAQH/BAUwAwEB/zBFBgNVHR4EPjA8oBww
|
||||||
|
CocICgEAAP//AAAwDoIMYmVuZGVyLmxvY2FsoRwwCocICgEAAP//AAAwDoIMYmVu
|
||||||
|
ZGVyLmxvY2FsMA0GCSqGSIb3DQEBCwUAA4IBAQAMjbheffPxtSKSv9NySW+8qmHs
|
||||||
|
n7Mb5GGyCFu+cMZSoSaabstbml+zHEFJvWz6/1E95K4F8jKhAcu/CwDf4IZrSD2+
|
||||||
|
Hee0DolVSQhZpnHgPyj7ZATz48e3aJaQPUlhCEOh0wwF4Y0N4FV0t7R6woLylYRZ
|
||||||
|
yU1yRHUqUYpN0DWFpsPbBqgM6uUAVO2ayBFhPgWUaqkmSbZ/Nq7isGvknaTmcIwT
|
||||||
|
6mOAFN0qFb4RGzfGJW7x6z7KCULS7qVDp6fU3tRoScHFEgRubks6jzQ1W5ooSm4o
|
||||||
|
+NQCZDd5eFeU8PpNX7rgaYE4GPq+EEmLVCBYmdctr8QVdqJ//8Xu3+1phjDy
|
||||||
|
-----END CERTIFICATE-----`
|
||||||
|
|
||||||
var unknownAuthorityErrorTests = []struct {
|
var unknownAuthorityErrorTests = []struct {
|
||||||
cert string
|
cert string
|
||||||
expected string
|
expected string
|
||||||
|
|
|
||||||
|
|
@ -689,6 +689,7 @@ type Certificate struct {
|
||||||
// Name constraints
|
// Name constraints
|
||||||
PermittedDNSDomainsCritical bool // if true then the name constraints are marked critical.
|
PermittedDNSDomainsCritical bool // if true then the name constraints are marked critical.
|
||||||
PermittedDNSDomains []string
|
PermittedDNSDomains []string
|
||||||
|
ExcludedDNSDomains []string
|
||||||
|
|
||||||
// CRL Distribution Points
|
// CRL Distribution Points
|
||||||
CRLDistributionPoints []string
|
CRLDistributionPoints []string
|
||||||
|
|
@ -1183,20 +1184,28 @@ func parseCertificate(in *certificate) (*Certificate, error) {
|
||||||
return nil, errors.New("x509: trailing data after X.509 NameConstraints")
|
return nil, errors.New("x509: trailing data after X.509 NameConstraints")
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(constraints.Excluded) > 0 && e.Critical {
|
getDNSNames := func(subtrees []generalSubtree, isCritical bool) (dnsNames []string, err error) {
|
||||||
return out, UnhandledCriticalExtension{}
|
for _, subtree := range subtrees {
|
||||||
}
|
|
||||||
|
|
||||||
for _, subtree := range constraints.Permitted {
|
|
||||||
if len(subtree.Name) == 0 {
|
if len(subtree.Name) == 0 {
|
||||||
if e.Critical {
|
if isCritical {
|
||||||
return out, UnhandledCriticalExtension{}
|
return nil, UnhandledCriticalExtension{}
|
||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
out.PermittedDNSDomains = append(out.PermittedDNSDomains, subtree.Name)
|
dnsNames = append(dnsNames, subtree.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return dnsNames, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if out.PermittedDNSDomains, err = getDNSNames(constraints.Permitted, e.Critical); err != nil {
|
||||||
|
return out, err
|
||||||
|
}
|
||||||
|
if out.ExcludedDNSDomains, err = getDNSNames(constraints.Excluded, e.Critical); err != nil {
|
||||||
|
return out, err
|
||||||
|
}
|
||||||
|
out.PermittedDNSDomainsCritical = e.Critical
|
||||||
|
|
||||||
case 31:
|
case 31:
|
||||||
// RFC 5280, 4.2.1.13
|
// RFC 5280, 4.2.1.13
|
||||||
|
|
||||||
|
|
@ -1579,16 +1588,22 @@ func buildExtensions(template *Certificate, authorityKeyId []byte) (ret []pkix.E
|
||||||
n++
|
n++
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(template.PermittedDNSDomains) > 0 &&
|
if (len(template.PermittedDNSDomains) > 0 || len(template.ExcludedDNSDomains) > 0) &&
|
||||||
!oidInExtensions(oidExtensionNameConstraints, template.ExtraExtensions) {
|
!oidInExtensions(oidExtensionNameConstraints, template.ExtraExtensions) {
|
||||||
ret[n].Id = oidExtensionNameConstraints
|
ret[n].Id = oidExtensionNameConstraints
|
||||||
ret[n].Critical = template.PermittedDNSDomainsCritical
|
ret[n].Critical = template.PermittedDNSDomainsCritical
|
||||||
|
|
||||||
var out nameConstraints
|
var out nameConstraints
|
||||||
|
|
||||||
out.Permitted = make([]generalSubtree, len(template.PermittedDNSDomains))
|
out.Permitted = make([]generalSubtree, len(template.PermittedDNSDomains))
|
||||||
for i, permitted := range template.PermittedDNSDomains {
|
for i, permitted := range template.PermittedDNSDomains {
|
||||||
out.Permitted[i] = generalSubtree{Name: permitted}
|
out.Permitted[i] = generalSubtree{Name: permitted}
|
||||||
}
|
}
|
||||||
|
out.Excluded = make([]generalSubtree, len(template.ExcludedDNSDomains))
|
||||||
|
for i, excluded := range template.ExcludedDNSDomains {
|
||||||
|
out.Excluded[i] = generalSubtree{Name: excluded}
|
||||||
|
}
|
||||||
|
|
||||||
ret[n].Value, err = asn1.Marshal(out)
|
ret[n].Value, err = asn1.Marshal(out)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
|
|
@ -1704,10 +1719,10 @@ func signingParamsForPublicKey(pub interface{}, requestedSigAlgo SignatureAlgori
|
||||||
|
|
||||||
// CreateCertificate creates a new certificate based on a template. The
|
// CreateCertificate creates a new certificate based on a template. The
|
||||||
// following members of template are used: AuthorityKeyId,
|
// following members of template are used: AuthorityKeyId,
|
||||||
// BasicConstraintsValid, DNSNames, ExtKeyUsage, IsCA, KeyUsage, MaxPathLen,
|
// BasicConstraintsValid, DNSNames, ExcludedDNSDomains, ExtKeyUsage, IsCA,
|
||||||
// NotAfter, NotBefore, PermittedDNSDomains, PermittedDNSDomainsCritical,
|
// KeyUsage, MaxPathLen, NotAfter, NotBefore, PermittedDNSDomains,
|
||||||
// SerialNumber, SignatureAlgorithm, Subject, SubjectKeyId, and
|
// PermittedDNSDomainsCritical, SerialNumber, SignatureAlgorithm, Subject,
|
||||||
// UnknownExtKeyUsage.
|
// SubjectKeyId, and UnknownExtKeyUsage.
|
||||||
//
|
//
|
||||||
// The certificate is signed by parent. If parent is equal to template then the
|
// The certificate is signed by parent. If parent is equal to template then the
|
||||||
// certificate is self-signed. The parameter pub is the public key of the
|
// certificate is self-signed. The parameter pub is the public key of the
|
||||||
|
|
|
||||||
|
|
@ -405,6 +405,7 @@ func TestCreateSelfSignedCertificate(t *testing.T) {
|
||||||
|
|
||||||
PolicyIdentifiers: []asn1.ObjectIdentifier{[]int{1, 2, 3}},
|
PolicyIdentifiers: []asn1.ObjectIdentifier{[]int{1, 2, 3}},
|
||||||
PermittedDNSDomains: []string{".example.com", "example.com"},
|
PermittedDNSDomains: []string{".example.com", "example.com"},
|
||||||
|
ExcludedDNSDomains: []string{"bar.example.com"},
|
||||||
|
|
||||||
CRLDistributionPoints: []string{"http://crl1.example.com/ca1.crl", "http://crl2.example.com/ca1.crl"},
|
CRLDistributionPoints: []string{"http://crl1.example.com/ca1.crl", "http://crl2.example.com/ca1.crl"},
|
||||||
|
|
||||||
|
|
@ -442,6 +443,10 @@ func TestCreateSelfSignedCertificate(t *testing.T) {
|
||||||
t.Errorf("%s: failed to parse name constraints: %#v", test.name, cert.PermittedDNSDomains)
|
t.Errorf("%s: failed to parse name constraints: %#v", test.name, cert.PermittedDNSDomains)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if len(cert.ExcludedDNSDomains) != 1 || cert.ExcludedDNSDomains[0] != "bar.example.com" {
|
||||||
|
t.Errorf("%s: failed to parse name constraint exclusions: %#v", test.name, cert.ExcludedDNSDomains)
|
||||||
|
}
|
||||||
|
|
||||||
if cert.Subject.CommonName != commonName {
|
if cert.Subject.CommonName != commonName {
|
||||||
t.Errorf("%s: subject wasn't correctly copied from the template. Got %s, want %s", test.name, cert.Subject.CommonName, commonName)
|
t.Errorf("%s: subject wasn't correctly copied from the template. Got %s, want %s", test.name, cert.Subject.CommonName, commonName)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue