Skip to content

Commit 82175e6

Browse files
FiloSottilekatiehockman
authored andcommitted
crypto/x509: respect VerifyOptions.KeyUsages on Windows
When using the platform verifier on Windows (because Roots is nil) we were always enforcing server auth EKUs if DNSName was set, and none otherwise. If an application was setting KeyUsages, they were not being respected. Started correctly surfacing IncompatibleUsage errors from the system verifier, as those are the ones applications will see if they are affected by this change. Also refactored verify_test.go to make it easier to add tests for this, and replaced the EKULeaf chain with a new one that doesn't have a SHA-1 signature. Thanks to Niall Newman for reporting this. Fixes #39360 Fixes CVE-2020-14039 Change-Id: If5c00d615f2944f7d57007891aae1307f9571c32 Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/774414 Reviewed-by: Katie Hockman <katiehockman@google.com> Reviewed-on: https://go-review.googlesource.com/c/go/+/242597 Run-TryBot: Katie Hockman <katie@golang.org> Reviewed-by: Filippo Valsorda <filippo@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org>
1 parent efbe47b commit 82175e6

File tree

3 files changed

+415
-547
lines changed

3 files changed

+415
-547
lines changed

src/crypto/x509/root_windows.go

Lines changed: 33 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,9 @@ func checkChainTrustStatus(c *Certificate, chainCtx *syscall.CertChainContext) e
8888
switch status {
8989
case syscall.CERT_TRUST_IS_NOT_TIME_VALID:
9090
return CertificateInvalidError{c, Expired, ""}
91+
case syscall.CERT_TRUST_IS_NOT_VALID_FOR_USAGE:
92+
return CertificateInvalidError{c, IncompatibleUsage, ""}
93+
// TODO(filippo): surface more error statuses.
9194
default:
9295
return UnknownAuthorityError{c, nil, nil}
9396
}
@@ -138,11 +141,19 @@ func checkChainSSLServerPolicy(c *Certificate, chainCtx *syscall.CertChainContex
138141
return nil
139142
}
140143

144+
// windowsExtKeyUsageOIDs are the C NUL-terminated string representations of the
145+
// OIDs for use with the Windows API.
146+
var windowsExtKeyUsageOIDs = make(map[ExtKeyUsage][]byte, len(extKeyUsageOIDs))
147+
148+
func init() {
149+
for _, eku := range extKeyUsageOIDs {
150+
windowsExtKeyUsageOIDs[eku.extKeyUsage] = []byte(eku.oid.String() + "\x00")
151+
}
152+
}
153+
141154
// systemVerify is like Verify, except that it uses CryptoAPI calls
142155
// to build certificate chains and verify them.
143156
func (c *Certificate) systemVerify(opts *VerifyOptions) (chains [][]*Certificate, err error) {
144-
hasDNSName := opts != nil && len(opts.DNSName) > 0
145-
146157
storeCtx, err := createStoreContext(c, opts)
147158
if err != nil {
148159
return nil, err
@@ -152,17 +163,26 @@ func (c *Certificate) systemVerify(opts *VerifyOptions) (chains [][]*Certificate
152163
para := new(syscall.CertChainPara)
153164
para.Size = uint32(unsafe.Sizeof(*para))
154165

155-
// If there's a DNSName set in opts, assume we're verifying
156-
// a certificate from a TLS server.
157-
if hasDNSName {
158-
oids := []*byte{
159-
&syscall.OID_PKIX_KP_SERVER_AUTH[0],
160-
// Both IE and Chrome allow certificates with
161-
// Server Gated Crypto as well. Some certificates
162-
// in the wild require them.
163-
&syscall.OID_SERVER_GATED_CRYPTO[0],
164-
&syscall.OID_SGC_NETSCAPE[0],
166+
keyUsages := opts.KeyUsages
167+
if len(keyUsages) == 0 {
168+
keyUsages = []ExtKeyUsage{ExtKeyUsageServerAuth}
169+
}
170+
oids := make([]*byte, 0, len(keyUsages))
171+
for _, eku := range keyUsages {
172+
if eku == ExtKeyUsageAny {
173+
oids = nil
174+
break
175+
}
176+
if oid, ok := windowsExtKeyUsageOIDs[eku]; ok {
177+
oids = append(oids, &oid[0])
165178
}
179+
// Like the standard verifier, accept SGC EKUs as equivalent to ServerAuth.
180+
if eku == ExtKeyUsageServerAuth {
181+
oids = append(oids, &syscall.OID_SERVER_GATED_CRYPTO[0])
182+
oids = append(oids, &syscall.OID_SGC_NETSCAPE[0])
183+
}
184+
}
185+
if oids != nil {
166186
para.RequestedUsage.Type = syscall.USAGE_MATCH_TYPE_OR
167187
para.RequestedUsage.Usage.Length = uint32(len(oids))
168188
para.RequestedUsage.Usage.UsageIdentifiers = &oids[0]
@@ -208,7 +228,7 @@ func (c *Certificate) systemVerify(opts *VerifyOptions) (chains [][]*Certificate
208228
return nil, err
209229
}
210230

211-
if hasDNSName {
231+
if opts != nil && len(opts.DNSName) > 0 {
212232
err = checkChainSSLServerPolicy(c, chainCtx, opts)
213233
if err != nil {
214234
return nil, err

src/crypto/x509/verify.go

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,7 @@ var errNotParsed = errors.New("x509: missing ASN.1 contents; use ParseCertificat
194194
// VerifyOptions contains parameters for Certificate.Verify.
195195
type VerifyOptions struct {
196196
// DNSName, if set, is checked against the leaf certificate with
197-
// Certificate.VerifyHostname.
197+
// Certificate.VerifyHostname or the platform verifier.
198198
DNSName string
199199

200200
// Intermediates is an optional pool of certificates that are not trust
@@ -209,20 +209,16 @@ type VerifyOptions struct {
209209
// chain. If zero, the current time is used.
210210
CurrentTime time.Time
211211

212-
// KeyUsage specifies which Extended Key Usage values are acceptable. A leaf
213-
// certificate is accepted if it contains any of the listed values. An empty
214-
// list means ExtKeyUsageServerAuth. To accept any key usage, include
215-
// ExtKeyUsageAny.
216-
//
217-
// Certificate chains are required to nest these extended key usage values.
218-
// (This matches the Windows CryptoAPI behavior, but not the spec.)
212+
// KeyUsages specifies which Extended Key Usage values are acceptable. A
213+
// chain is accepted if it allows any of the listed values. An empty list
214+
// means ExtKeyUsageServerAuth. To accept any key usage, include ExtKeyUsageAny.
219215
KeyUsages []ExtKeyUsage
220216

221217
// MaxConstraintComparisions is the maximum number of comparisons to
222218
// perform when checking a given certificate's name constraints. If
223219
// zero, a sensible default is used. This limit prevents pathological
224220
// certificates from consuming excessive amounts of CPU time when
225-
// validating.
221+
// validating. It does not apply to the platform verifier.
226222
MaxConstraintComparisions int
227223
}
228224

@@ -735,8 +731,9 @@ func (c *Certificate) isValid(certType int, currentChain []*Certificate, opts *V
735731
// needed. If successful, it returns one or more chains where the first
736732
// element of the chain is c and the last element is from opts.Roots.
737733
//
738-
// If opts.Roots is nil and system roots are unavailable the returned error
739-
// will be of type SystemRootsError.
734+
// If opts.Roots is nil, the platform verifier might be used, and
735+
// verification details might differ from what is described below. If system
736+
// roots are unavailable the returned error will be of type SystemRootsError.
740737
//
741738
// Name constraints in the intermediates will be applied to all names claimed
742739
// in the chain, not just opts.DNSName. Thus it is invalid for a leaf to claim
@@ -750,9 +747,10 @@ func (c *Certificate) isValid(certType int, currentChain []*Certificate, opts *V
750747
// it indicates that at least one additional label must be prepended to
751748
// the constrained name to be considered valid.
752749
//
753-
// Extended Key Usage values are enforced down a chain, so an intermediate or
754-
// root that enumerates EKUs prevents a leaf from asserting an EKU not in that
755-
// list.
750+
// Extended Key Usage values are enforced nested down a chain, so an intermediate
751+
// or root that enumerates EKUs prevents a leaf from asserting an EKU not in that
752+
// list. (While this is not specified, it is common practice in order to limit
753+
// the types of certificates a CA can issue.)
756754
//
757755
// WARNING: this function doesn't do any revocation checking.
758756
func (c *Certificate) Verify(opts VerifyOptions) (chains [][]*Certificate, err error) {

0 commit comments

Comments
 (0)