From 2f1de1593e7993b233aa90ce874c407ae03741f8 Mon Sep 17 00:00:00 2001 From: Adam Langley Date: Mon, 13 Nov 2017 10:38:05 -0800 Subject: [PATCH] crypto/x509: relax EKU checking in some cases. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CL 71030 enforced EKU nesting at verification time, to go along with the change in name constraints behaviour. From scanning the Certificate Transparency logs, it's clear that some CAs are not getting EKU nesting correct. This change relaxes the EKU rules in a few ways: ∙ EKUs in roots are no longer checked. ∙ Any CA certificate may issue OCSP responder certificates. ∙ The ServerAuth and SGC EKUs are treated as a single EKU when checking nesting. ∙ ServerAuth in a CA can now authorise ClientAuth. ∙ The generic CodeSigning EKU can now authorise two, Microsoft-specific code-signing EKUs. Change-Id: I7b7ac787709af0dcd177fe419ec2e485b8d85540 Reviewed-on: https://go-review.googlesource.com/77330 Reviewed-by: Brad Fitzpatrick --- src/crypto/x509/name_constraints_test.go | 5 +--- src/crypto/x509/verify.go | 27 +++++++++++++++++---- src/crypto/x509/x509.go | 30 ++++++++++++++---------- 3 files changed, 41 insertions(+), 21 deletions(-) diff --git a/src/crypto/x509/name_constraints_test.go b/src/crypto/x509/name_constraints_test.go index e75770b84d..10cc348357 100644 --- a/src/crypto/x509/name_constraints_test.go +++ b/src/crypto/x509/name_constraints_test.go @@ -1282,9 +1282,7 @@ var nameConstraintsTests = []nameConstraintsTest{ }, }, - // #66: trying to add extra permitted key usages in an intermediate - // (after a limitation in the root) doesn't allow those usages in a - // leaf. + // #66: EKUs in roots are ignored. nameConstraintsTest{ roots: []constraintsSpec{ constraintsSpec{ @@ -1302,7 +1300,6 @@ var nameConstraintsTests = []nameConstraintsTest{ sans: []string{"dns:example.com"}, ekus: []string{"serverAuth", "email"}, }, - expectedError: "EKU not permitted", }, // #67: in order to support COMODO chains, SGC key usages permit diff --git a/src/crypto/x509/verify.go b/src/crypto/x509/verify.go index e89585e223..7a6bd454f2 100644 --- a/src/crypto/x509/verify.go +++ b/src/crypto/x509/verify.go @@ -546,15 +546,32 @@ func (c *Certificate) checkNameConstraints(count *int, // ekuPermittedBy returns true iff the given extended key usage is permitted by // the given EKU from a certificate. Normally, this would be a simple // comparison plus a special case for the “any” EKU. But, in order to support -// COMODO chains, SGC EKUs permit generic server and client authentication -// EKUs. +// existing certificates, some exceptions are made. func ekuPermittedBy(eku, certEKU ExtKeyUsage) bool { if certEKU == ExtKeyUsageAny || eku == certEKU { return true } - if (eku == ExtKeyUsageServerAuth || eku == ExtKeyUsageClientAuth) && - (certEKU == ExtKeyUsageNetscapeServerGatedCrypto || certEKU == ExtKeyUsageMicrosoftServerGatedCrypto) { + // Some exceptions are made to support existing certificates. Firstly, + // the ServerAuth and SGC EKUs are treated as a group. + mapServerAuthEKUs := func(eku ExtKeyUsage) ExtKeyUsage { + if eku == ExtKeyUsageNetscapeServerGatedCrypto || eku == ExtKeyUsageMicrosoftServerGatedCrypto { + return ExtKeyUsageServerAuth + } + return eku + } + + eku = mapServerAuthEKUs(eku) + certEKU = mapServerAuthEKUs(certEKU) + + if eku == certEKU || + // ServerAuth in a CA permits ClientAuth in the leaf. + (eku == ExtKeyUsageClientAuth && certEKU == ExtKeyUsageServerAuth) || + // Any CA may issue an OCSP responder certificate. + eku == ExtKeyUsageOCSPSigning || + // Code-signing CAs can use Microsoft's commercial and + // kernel-mode EKUs. + ((eku == ExtKeyUsageMicrosoftCommercialCodeSigning || eku == ExtKeyUsageMicrosoftKernelCodeSigning) && certEKU == ExtKeyUsageCodeSigning) { return true } @@ -672,7 +689,7 @@ func (c *Certificate) isValid(certType int, currentChain []*Certificate, opts *V } } - checkEKUs := certType == intermediateCertificate || certType == rootCertificate + checkEKUs := certType == intermediateCertificate // If no extended key usages are specified, then all are acceptable. if checkEKUs && (len(c.ExtKeyUsage) == 0 && len(c.UnknownExtKeyUsage) == 0) { diff --git a/src/crypto/x509/x509.go b/src/crypto/x509/x509.go index a4a9941bf5..5e43a1a915 100644 --- a/src/crypto/x509/x509.go +++ b/src/crypto/x509/x509.go @@ -553,18 +553,20 @@ const ( // id-kp-timeStamping OBJECT IDENTIFIER ::= { id-kp 8 } // id-kp-OCSPSigning OBJECT IDENTIFIER ::= { id-kp 9 } var ( - oidExtKeyUsageAny = asn1.ObjectIdentifier{2, 5, 29, 37, 0} - oidExtKeyUsageServerAuth = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 1} - oidExtKeyUsageClientAuth = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 2} - oidExtKeyUsageCodeSigning = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 3} - oidExtKeyUsageEmailProtection = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 4} - oidExtKeyUsageIPSECEndSystem = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 5} - oidExtKeyUsageIPSECTunnel = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 6} - oidExtKeyUsageIPSECUser = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 7} - oidExtKeyUsageTimeStamping = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 8} - oidExtKeyUsageOCSPSigning = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 9} - oidExtKeyUsageMicrosoftServerGatedCrypto = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 311, 10, 3, 3} - oidExtKeyUsageNetscapeServerGatedCrypto = asn1.ObjectIdentifier{2, 16, 840, 1, 113730, 4, 1} + oidExtKeyUsageAny = asn1.ObjectIdentifier{2, 5, 29, 37, 0} + oidExtKeyUsageServerAuth = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 1} + oidExtKeyUsageClientAuth = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 2} + oidExtKeyUsageCodeSigning = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 3} + oidExtKeyUsageEmailProtection = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 4} + oidExtKeyUsageIPSECEndSystem = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 5} + oidExtKeyUsageIPSECTunnel = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 6} + oidExtKeyUsageIPSECUser = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 7} + oidExtKeyUsageTimeStamping = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 8} + oidExtKeyUsageOCSPSigning = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 9} + oidExtKeyUsageMicrosoftServerGatedCrypto = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 311, 10, 3, 3} + oidExtKeyUsageNetscapeServerGatedCrypto = asn1.ObjectIdentifier{2, 16, 840, 1, 113730, 4, 1} + oidExtKeyUsageMicrosoftCommercialCodeSigning = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 311, 2, 1, 22} + oidExtKeyUsageMicrosoftKernelCodeSigning = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 311, 61, 1, 1} ) // ExtKeyUsage represents an extended set of actions that are valid for a given key. @@ -584,6 +586,8 @@ const ( ExtKeyUsageOCSPSigning ExtKeyUsageMicrosoftServerGatedCrypto ExtKeyUsageNetscapeServerGatedCrypto + ExtKeyUsageMicrosoftCommercialCodeSigning + ExtKeyUsageMicrosoftKernelCodeSigning ) // extKeyUsageOIDs contains the mapping between an ExtKeyUsage and its OID. @@ -603,6 +607,8 @@ var extKeyUsageOIDs = []struct { {ExtKeyUsageOCSPSigning, oidExtKeyUsageOCSPSigning}, {ExtKeyUsageMicrosoftServerGatedCrypto, oidExtKeyUsageMicrosoftServerGatedCrypto}, {ExtKeyUsageNetscapeServerGatedCrypto, oidExtKeyUsageNetscapeServerGatedCrypto}, + {ExtKeyUsageMicrosoftCommercialCodeSigning, oidExtKeyUsageMicrosoftCommercialCodeSigning}, + {ExtKeyUsageMicrosoftKernelCodeSigning, oidExtKeyUsageMicrosoftKernelCodeSigning}, } func extKeyUsageFromOID(oid asn1.ObjectIdentifier) (eku ExtKeyUsage, ok bool) {