Black Lives Matter. Support the Equal Justice Initiative.

Source file src/crypto/x509/root_windows.go

Documentation: crypto/x509

     1  // Copyright 2012 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package x509
     6  
     7  import (
     8  	"errors"
     9  	"syscall"
    10  	"unsafe"
    11  )
    12  
    13  // Creates a new *syscall.CertContext representing the leaf certificate in an in-memory
    14  // certificate store containing itself and all of the intermediate certificates specified
    15  // in the opts.Intermediates CertPool.
    16  //
    17  // A pointer to the in-memory store is available in the returned CertContext's Store field.
    18  // The store is automatically freed when the CertContext is freed using
    19  // syscall.CertFreeCertificateContext.
    20  func createStoreContext(leaf *Certificate, opts *VerifyOptions) (*syscall.CertContext, error) {
    21  	var storeCtx *syscall.CertContext
    22  
    23  	leafCtx, err := syscall.CertCreateCertificateContext(syscall.X509_ASN_ENCODING|syscall.PKCS_7_ASN_ENCODING, &leaf.Raw[0], uint32(len(leaf.Raw)))
    24  	if err != nil {
    25  		return nil, err
    26  	}
    27  	defer syscall.CertFreeCertificateContext(leafCtx)
    28  
    29  	handle, err := syscall.CertOpenStore(syscall.CERT_STORE_PROV_MEMORY, 0, 0, syscall.CERT_STORE_DEFER_CLOSE_UNTIL_LAST_FREE_FLAG, 0)
    30  	if err != nil {
    31  		return nil, err
    32  	}
    33  	defer syscall.CertCloseStore(handle, 0)
    34  
    35  	err = syscall.CertAddCertificateContextToStore(handle, leafCtx, syscall.CERT_STORE_ADD_ALWAYS, &storeCtx)
    36  	if err != nil {
    37  		return nil, err
    38  	}
    39  
    40  	if opts.Intermediates != nil {
    41  		for i := 0; i < opts.Intermediates.len(); i++ {
    42  			intermediate, err := opts.Intermediates.cert(i)
    43  			if err != nil {
    44  				return nil, err
    45  			}
    46  			ctx, err := syscall.CertCreateCertificateContext(syscall.X509_ASN_ENCODING|syscall.PKCS_7_ASN_ENCODING, &intermediate.Raw[0], uint32(len(intermediate.Raw)))
    47  			if err != nil {
    48  				return nil, err
    49  			}
    50  
    51  			err = syscall.CertAddCertificateContextToStore(handle, ctx, syscall.CERT_STORE_ADD_ALWAYS, nil)
    52  			syscall.CertFreeCertificateContext(ctx)
    53  			if err != nil {
    54  				return nil, err
    55  			}
    56  		}
    57  	}
    58  
    59  	return storeCtx, nil
    60  }
    61  
    62  // extractSimpleChain extracts the final certificate chain from a CertSimpleChain.
    63  func extractSimpleChain(simpleChain **syscall.CertSimpleChain, count int) (chain []*Certificate, err error) {
    64  	if simpleChain == nil || count == 0 {
    65  		return nil, errors.New("x509: invalid simple chain")
    66  	}
    67  
    68  	simpleChains := (*[1 << 20]*syscall.CertSimpleChain)(unsafe.Pointer(simpleChain))[:count:count]
    69  	lastChain := simpleChains[count-1]
    70  	elements := (*[1 << 20]*syscall.CertChainElement)(unsafe.Pointer(lastChain.Elements))[:lastChain.NumElements:lastChain.NumElements]
    71  	for i := 0; i < int(lastChain.NumElements); i++ {
    72  		// Copy the buf, since ParseCertificate does not create its own copy.
    73  		cert := elements[i].CertContext
    74  		encodedCert := (*[1 << 20]byte)(unsafe.Pointer(cert.EncodedCert))[:cert.Length:cert.Length]
    75  		buf := make([]byte, cert.Length)
    76  		copy(buf, encodedCert)
    77  		parsedCert, err := ParseCertificate(buf)
    78  		if err != nil {
    79  			return nil, err
    80  		}
    81  		chain = append(chain, parsedCert)
    82  	}
    83  
    84  	return chain, nil
    85  }
    86  
    87  // checkChainTrustStatus checks the trust status of the certificate chain, translating
    88  // any errors it finds into Go errors in the process.
    89  func checkChainTrustStatus(c *Certificate, chainCtx *syscall.CertChainContext) error {
    90  	if chainCtx.TrustStatus.ErrorStatus != syscall.CERT_TRUST_NO_ERROR {
    91  		status := chainCtx.TrustStatus.ErrorStatus
    92  		switch status {
    93  		case syscall.CERT_TRUST_IS_NOT_TIME_VALID:
    94  			return CertificateInvalidError{c, Expired, ""}
    95  		case syscall.CERT_TRUST_IS_NOT_VALID_FOR_USAGE:
    96  			return CertificateInvalidError{c, IncompatibleUsage, ""}
    97  		// TODO(filippo): surface more error statuses.
    98  		default:
    99  			return UnknownAuthorityError{c, nil, nil}
   100  		}
   101  	}
   102  	return nil
   103  }
   104  
   105  // checkChainSSLServerPolicy checks that the certificate chain in chainCtx is valid for
   106  // use as a certificate chain for a SSL/TLS server.
   107  func checkChainSSLServerPolicy(c *Certificate, chainCtx *syscall.CertChainContext, opts *VerifyOptions) error {
   108  	servernamep, err := syscall.UTF16PtrFromString(opts.DNSName)
   109  	if err != nil {
   110  		return err
   111  	}
   112  	sslPara := &syscall.SSLExtraCertChainPolicyPara{
   113  		AuthType:   syscall.AUTHTYPE_SERVER,
   114  		ServerName: servernamep,
   115  	}
   116  	sslPara.Size = uint32(unsafe.Sizeof(*sslPara))
   117  
   118  	para := &syscall.CertChainPolicyPara{
   119  		ExtraPolicyPara: (syscall.Pointer)(unsafe.Pointer(sslPara)),
   120  	}
   121  	para.Size = uint32(unsafe.Sizeof(*para))
   122  
   123  	status := syscall.CertChainPolicyStatus{}
   124  	err = syscall.CertVerifyCertificateChainPolicy(syscall.CERT_CHAIN_POLICY_SSL, chainCtx, para, &status)
   125  	if err != nil {
   126  		return err
   127  	}
   128  
   129  	// TODO(mkrautz): use the lChainIndex and lElementIndex fields
   130  	// of the CertChainPolicyStatus to provide proper context, instead
   131  	// using c.
   132  	if status.Error != 0 {
   133  		switch status.Error {
   134  		case syscall.CERT_E_EXPIRED:
   135  			return CertificateInvalidError{c, Expired, ""}
   136  		case syscall.CERT_E_CN_NO_MATCH:
   137  			return HostnameError{c, opts.DNSName}
   138  		case syscall.CERT_E_UNTRUSTEDROOT:
   139  			return UnknownAuthorityError{c, nil, nil}
   140  		default:
   141  			return UnknownAuthorityError{c, nil, nil}
   142  		}
   143  	}
   144  
   145  	return nil
   146  }
   147  
   148  // windowsExtKeyUsageOIDs are the C NUL-terminated string representations of the
   149  // OIDs for use with the Windows API.
   150  var windowsExtKeyUsageOIDs = make(map[ExtKeyUsage][]byte, len(extKeyUsageOIDs))
   151  
   152  func init() {
   153  	for _, eku := range extKeyUsageOIDs {
   154  		windowsExtKeyUsageOIDs[eku.extKeyUsage] = []byte(eku.oid.String() + "\x00")
   155  	}
   156  }
   157  
   158  func verifyChain(c *Certificate, chainCtx *syscall.CertChainContext, opts *VerifyOptions) (chain []*Certificate, err error) {
   159  	err = checkChainTrustStatus(c, chainCtx)
   160  	if err != nil {
   161  		return nil, err
   162  	}
   163  
   164  	if opts != nil && len(opts.DNSName) > 0 {
   165  		err = checkChainSSLServerPolicy(c, chainCtx, opts)
   166  		if err != nil {
   167  			return nil, err
   168  		}
   169  	}
   170  
   171  	chain, err = extractSimpleChain(chainCtx.Chains, int(chainCtx.ChainCount))
   172  	if err != nil {
   173  		return nil, err
   174  	}
   175  	if len(chain) == 0 {
   176  		return nil, errors.New("x509: internal error: system verifier returned an empty chain")
   177  	}
   178  
   179  	// Mitigate CVE-2020-0601, where the Windows system verifier might be
   180  	// tricked into using custom curve parameters for a trusted root, by
   181  	// double-checking all ECDSA signatures. If the system was tricked into
   182  	// using spoofed parameters, the signature will be invalid for the correct
   183  	// ones we parsed. (We don't support custom curves ourselves.)
   184  	for i, parent := range chain[1:] {
   185  		if parent.PublicKeyAlgorithm != ECDSA {
   186  			continue
   187  		}
   188  		if err := parent.CheckSignature(chain[i].SignatureAlgorithm,
   189  			chain[i].RawTBSCertificate, chain[i].Signature); err != nil {
   190  			return nil, err
   191  		}
   192  	}
   193  	return chain, nil
   194  }
   195  
   196  // systemVerify is like Verify, except that it uses CryptoAPI calls
   197  // to build certificate chains and verify them.
   198  func (c *Certificate) systemVerify(opts *VerifyOptions) (chains [][]*Certificate, err error) {
   199  	storeCtx, err := createStoreContext(c, opts)
   200  	if err != nil {
   201  		return nil, err
   202  	}
   203  	defer syscall.CertFreeCertificateContext(storeCtx)
   204  
   205  	para := new(syscall.CertChainPara)
   206  	para.Size = uint32(unsafe.Sizeof(*para))
   207  
   208  	keyUsages := opts.KeyUsages
   209  	if len(keyUsages) == 0 {
   210  		keyUsages = []ExtKeyUsage{ExtKeyUsageServerAuth}
   211  	}
   212  	oids := make([]*byte, 0, len(keyUsages))
   213  	for _, eku := range keyUsages {
   214  		if eku == ExtKeyUsageAny {
   215  			oids = nil
   216  			break
   217  		}
   218  		if oid, ok := windowsExtKeyUsageOIDs[eku]; ok {
   219  			oids = append(oids, &oid[0])
   220  		}
   221  		// Like the standard verifier, accept SGC EKUs as equivalent to ServerAuth.
   222  		if eku == ExtKeyUsageServerAuth {
   223  			oids = append(oids, &syscall.OID_SERVER_GATED_CRYPTO[0])
   224  			oids = append(oids, &syscall.OID_SGC_NETSCAPE[0])
   225  		}
   226  	}
   227  	if oids != nil {
   228  		para.RequestedUsage.Type = syscall.USAGE_MATCH_TYPE_OR
   229  		para.RequestedUsage.Usage.Length = uint32(len(oids))
   230  		para.RequestedUsage.Usage.UsageIdentifiers = &oids[0]
   231  	} else {
   232  		para.RequestedUsage.Type = syscall.USAGE_MATCH_TYPE_AND
   233  		para.RequestedUsage.Usage.Length = 0
   234  		para.RequestedUsage.Usage.UsageIdentifiers = nil
   235  	}
   236  
   237  	var verifyTime *syscall.Filetime
   238  	if opts != nil && !opts.CurrentTime.IsZero() {
   239  		ft := syscall.NsecToFiletime(opts.CurrentTime.UnixNano())
   240  		verifyTime = &ft
   241  	}
   242  
   243  	// The default is to return only the highest quality chain,
   244  	// setting this flag will add additional lower quality contexts.
   245  	// These are returned in the LowerQualityChains field.
   246  	const CERT_CHAIN_RETURN_LOWER_QUALITY_CONTEXTS = 0x00000080
   247  
   248  	// CertGetCertificateChain will traverse Windows's root stores in an attempt to build a verified certificate chain
   249  	var topCtx *syscall.CertChainContext
   250  	err = syscall.CertGetCertificateChain(syscall.Handle(0), storeCtx, verifyTime, storeCtx.Store, para, CERT_CHAIN_RETURN_LOWER_QUALITY_CONTEXTS, 0, &topCtx)
   251  	if err != nil {
   252  		return nil, err
   253  	}
   254  	defer syscall.CertFreeCertificateChain(topCtx)
   255  
   256  	chain, topErr := verifyChain(c, topCtx, opts)
   257  	if topErr == nil {
   258  		chains = append(chains, chain)
   259  	}
   260  
   261  	if lqCtxCount := topCtx.LowerQualityChainCount; lqCtxCount > 0 {
   262  		lqCtxs := (*[1 << 20]*syscall.CertChainContext)(unsafe.Pointer(topCtx.LowerQualityChains))[:lqCtxCount:lqCtxCount]
   263  
   264  		for _, ctx := range lqCtxs {
   265  			chain, err := verifyChain(c, ctx, opts)
   266  			if err == nil {
   267  				chains = append(chains, chain)
   268  			}
   269  		}
   270  	}
   271  
   272  	if len(chains) == 0 {
   273  		// Return the error from the highest quality context.
   274  		return nil, topErr
   275  	}
   276  
   277  	return chains, nil
   278  }
   279  
   280  func loadSystemRoots() (*CertPool, error) {
   281  	// TODO: restore this functionality on Windows. We tried to do
   282  	// it in Go 1.8 but had to revert it. See Issue 18609.
   283  	// Returning (nil, nil) was the old behavior, prior to CL 30578.
   284  	// The if statement here avoids vet complaining about
   285  	// unreachable code below.
   286  	if true {
   287  		return nil, nil
   288  	}
   289  
   290  	const CRYPT_E_NOT_FOUND = 0x80092004
   291  
   292  	store, err := syscall.CertOpenSystemStore(0, syscall.StringToUTF16Ptr("ROOT"))
   293  	if err != nil {
   294  		return nil, err
   295  	}
   296  	defer syscall.CertCloseStore(store, 0)
   297  
   298  	roots := NewCertPool()
   299  	var cert *syscall.CertContext
   300  	for {
   301  		cert, err = syscall.CertEnumCertificatesInStore(store, cert)
   302  		if err != nil {
   303  			if errno, ok := err.(syscall.Errno); ok {
   304  				if errno == CRYPT_E_NOT_FOUND {
   305  					break
   306  				}
   307  			}
   308  			return nil, err
   309  		}
   310  		if cert == nil {
   311  			break
   312  		}
   313  		// Copy the buf, since ParseCertificate does not create its own copy.
   314  		buf := (*[1 << 20]byte)(unsafe.Pointer(cert.EncodedCert))[:cert.Length:cert.Length]
   315  		buf2 := make([]byte, cert.Length)
   316  		copy(buf2, buf)
   317  		if c, err := ParseCertificate(buf2); err == nil {
   318  			roots.AddCert(c)
   319  		}
   320  	}
   321  	return roots, nil
   322  }
   323  

View as plain text