...
Run Format

Source file src/crypto/x509/root_windows.go

Documentation: crypto/x509

  // Copyright 2012 The Go Authors. All rights reserved.
  // Use of this source code is governed by a BSD-style
  // license that can be found in the LICENSE file.
  
  package x509
  
  import (
  	"errors"
  	"syscall"
  	"unsafe"
  )
  
  // Creates a new *syscall.CertContext representing the leaf certificate in an in-memory
  // certificate store containing itself and all of the intermediate certificates specified
  // in the opts.Intermediates CertPool.
  //
  // A pointer to the in-memory store is available in the returned CertContext's Store field.
  // The store is automatically freed when the CertContext is freed using
  // syscall.CertFreeCertificateContext.
  func createStoreContext(leaf *Certificate, opts *VerifyOptions) (*syscall.CertContext, error) {
  	var storeCtx *syscall.CertContext
  
  	leafCtx, err := syscall.CertCreateCertificateContext(syscall.X509_ASN_ENCODING|syscall.PKCS_7_ASN_ENCODING, &leaf.Raw[0], uint32(len(leaf.Raw)))
  	if err != nil {
  		return nil, err
  	}
  	defer syscall.CertFreeCertificateContext(leafCtx)
  
  	handle, err := syscall.CertOpenStore(syscall.CERT_STORE_PROV_MEMORY, 0, 0, syscall.CERT_STORE_DEFER_CLOSE_UNTIL_LAST_FREE_FLAG, 0)
  	if err != nil {
  		return nil, err
  	}
  	defer syscall.CertCloseStore(handle, 0)
  
  	err = syscall.CertAddCertificateContextToStore(handle, leafCtx, syscall.CERT_STORE_ADD_ALWAYS, &storeCtx)
  	if err != nil {
  		return nil, err
  	}
  
  	if opts.Intermediates != nil {
  		for _, intermediate := range opts.Intermediates.certs {
  			ctx, err := syscall.CertCreateCertificateContext(syscall.X509_ASN_ENCODING|syscall.PKCS_7_ASN_ENCODING, &intermediate.Raw[0], uint32(len(intermediate.Raw)))
  			if err != nil {
  				return nil, err
  			}
  
  			err = syscall.CertAddCertificateContextToStore(handle, ctx, syscall.CERT_STORE_ADD_ALWAYS, nil)
  			syscall.CertFreeCertificateContext(ctx)
  			if err != nil {
  				return nil, err
  			}
  		}
  	}
  
  	return storeCtx, nil
  }
  
  // extractSimpleChain extracts the final certificate chain from a CertSimpleChain.
  func extractSimpleChain(simpleChain **syscall.CertSimpleChain, count int) (chain []*Certificate, err error) {
  	if simpleChain == nil || count == 0 {
  		return nil, errors.New("x509: invalid simple chain")
  	}
  
  	simpleChains := (*[1 << 20]*syscall.CertSimpleChain)(unsafe.Pointer(simpleChain))[:]
  	lastChain := simpleChains[count-1]
  	elements := (*[1 << 20]*syscall.CertChainElement)(unsafe.Pointer(lastChain.Elements))[:]
  	for i := 0; i < int(lastChain.NumElements); i++ {
  		// Copy the buf, since ParseCertificate does not create its own copy.
  		cert := elements[i].CertContext
  		encodedCert := (*[1 << 20]byte)(unsafe.Pointer(cert.EncodedCert))[:]
  		buf := make([]byte, cert.Length)
  		copy(buf, encodedCert[:])
  		parsedCert, err := ParseCertificate(buf)
  		if err != nil {
  			return nil, err
  		}
  		chain = append(chain, parsedCert)
  	}
  
  	return chain, nil
  }
  
  // checkChainTrustStatus checks the trust status of the certificate chain, translating
  // any errors it finds into Go errors in the process.
  func checkChainTrustStatus(c *Certificate, chainCtx *syscall.CertChainContext) error {
  	if chainCtx.TrustStatus.ErrorStatus != syscall.CERT_TRUST_NO_ERROR {
  		status := chainCtx.TrustStatus.ErrorStatus
  		switch status {
  		case syscall.CERT_TRUST_IS_NOT_TIME_VALID:
  			return CertificateInvalidError{c, Expired}
  		default:
  			return UnknownAuthorityError{c, nil, nil}
  		}
  	}
  	return nil
  }
  
  // checkChainSSLServerPolicy checks that the certificate chain in chainCtx is valid for
  // use as a certificate chain for a SSL/TLS server.
  func checkChainSSLServerPolicy(c *Certificate, chainCtx *syscall.CertChainContext, opts *VerifyOptions) error {
  	servernamep, err := syscall.UTF16PtrFromString(opts.DNSName)
  	if err != nil {
  		return err
  	}
  	sslPara := &syscall.SSLExtraCertChainPolicyPara{
  		AuthType:   syscall.AUTHTYPE_SERVER,
  		ServerName: servernamep,
  	}
  	sslPara.Size = uint32(unsafe.Sizeof(*sslPara))
  
  	para := &syscall.CertChainPolicyPara{
  		ExtraPolicyPara: uintptr(unsafe.Pointer(sslPara)),
  	}
  	para.Size = uint32(unsafe.Sizeof(*para))
  
  	status := syscall.CertChainPolicyStatus{}
  	err = syscall.CertVerifyCertificateChainPolicy(syscall.CERT_CHAIN_POLICY_SSL, chainCtx, para, &status)
  	if err != nil {
  		return err
  	}
  
  	// TODO(mkrautz): use the lChainIndex and lElementIndex fields
  	// of the CertChainPolicyStatus to provide proper context, instead
  	// using c.
  	if status.Error != 0 {
  		switch status.Error {
  		case syscall.CERT_E_EXPIRED:
  			return CertificateInvalidError{c, Expired}
  		case syscall.CERT_E_CN_NO_MATCH:
  			return HostnameError{c, opts.DNSName}
  		case syscall.CERT_E_UNTRUSTEDROOT:
  			return UnknownAuthorityError{c, nil, nil}
  		default:
  			return UnknownAuthorityError{c, nil, nil}
  		}
  	}
  
  	return nil
  }
  
  // systemVerify is like Verify, except that it uses CryptoAPI calls
  // to build certificate chains and verify them.
  func (c *Certificate) systemVerify(opts *VerifyOptions) (chains [][]*Certificate, err error) {
  	hasDNSName := opts != nil && len(opts.DNSName) > 0
  
  	storeCtx, err := createStoreContext(c, opts)
  	if err != nil {
  		return nil, err
  	}
  	defer syscall.CertFreeCertificateContext(storeCtx)
  
  	para := new(syscall.CertChainPara)
  	para.Size = uint32(unsafe.Sizeof(*para))
  
  	// If there's a DNSName set in opts, assume we're verifying
  	// a certificate from a TLS server.
  	if hasDNSName {
  		oids := []*byte{
  			&syscall.OID_PKIX_KP_SERVER_AUTH[0],
  			// Both IE and Chrome allow certificates with
  			// Server Gated Crypto as well. Some certificates
  			// in the wild require them.
  			&syscall.OID_SERVER_GATED_CRYPTO[0],
  			&syscall.OID_SGC_NETSCAPE[0],
  		}
  		para.RequestedUsage.Type = syscall.USAGE_MATCH_TYPE_OR
  		para.RequestedUsage.Usage.Length = uint32(len(oids))
  		para.RequestedUsage.Usage.UsageIdentifiers = &oids[0]
  	} else {
  		para.RequestedUsage.Type = syscall.USAGE_MATCH_TYPE_AND
  		para.RequestedUsage.Usage.Length = 0
  		para.RequestedUsage.Usage.UsageIdentifiers = nil
  	}
  
  	var verifyTime *syscall.Filetime
  	if opts != nil && !opts.CurrentTime.IsZero() {
  		ft := syscall.NsecToFiletime(opts.CurrentTime.UnixNano())
  		verifyTime = &ft
  	}
  
  	// CertGetCertificateChain will traverse Windows's root stores
  	// in an attempt to build a verified certificate chain. Once
  	// it has found a verified chain, it stops. MSDN docs on
  	// CERT_CHAIN_CONTEXT:
  	//
  	//   When a CERT_CHAIN_CONTEXT is built, the first simple chain
  	//   begins with an end certificate and ends with a self-signed
  	//   certificate. If that self-signed certificate is not a root
  	//   or otherwise trusted certificate, an attempt is made to
  	//   build a new chain. CTLs are used to create the new chain
  	//   beginning with the self-signed certificate from the original
  	//   chain as the end certificate of the new chain. This process
  	//   continues building additional simple chains until the first
  	//   self-signed certificate is a trusted certificate or until
  	//   an additional simple chain cannot be built.
  	//
  	// The result is that we'll only get a single trusted chain to
  	// return to our caller.
  	var chainCtx *syscall.CertChainContext
  	err = syscall.CertGetCertificateChain(syscall.Handle(0), storeCtx, verifyTime, storeCtx.Store, para, 0, 0, &chainCtx)
  	if err != nil {
  		return nil, err
  	}
  	defer syscall.CertFreeCertificateChain(chainCtx)
  
  	err = checkChainTrustStatus(c, chainCtx)
  	if err != nil {
  		return nil, err
  	}
  
  	if hasDNSName {
  		err = checkChainSSLServerPolicy(c, chainCtx, opts)
  		if err != nil {
  			return nil, err
  		}
  	}
  
  	chain, err := extractSimpleChain(chainCtx.Chains, int(chainCtx.ChainCount))
  	if err != nil {
  		return nil, err
  	}
  
  	chains = append(chains, chain)
  
  	return chains, nil
  }
  
  func loadSystemRoots() (*CertPool, error) {
  	// TODO: restore this functionality on Windows. We tried to do
  	// it in Go 1.8 but had to revert it. See Issue 18609.
  	// Returning (nil, nil) was the old behavior, prior to CL 30578.
  	return nil, nil
  
  	const CRYPT_E_NOT_FOUND = 0x80092004
  
  	store, err := syscall.CertOpenSystemStore(0, syscall.StringToUTF16Ptr("ROOT"))
  	if err != nil {
  		return nil, err
  	}
  	defer syscall.CertCloseStore(store, 0)
  
  	roots := NewCertPool()
  	var cert *syscall.CertContext
  	for {
  		cert, err = syscall.CertEnumCertificatesInStore(store, cert)
  		if err != nil {
  			if errno, ok := err.(syscall.Errno); ok {
  				if errno == CRYPT_E_NOT_FOUND {
  					break
  				}
  			}
  			return nil, err
  		}
  		if cert == nil {
  			break
  		}
  		// Copy the buf, since ParseCertificate does not create its own copy.
  		buf := (*[1 << 20]byte)(unsafe.Pointer(cert.EncodedCert))[:]
  		buf2 := make([]byte, cert.Length)
  		copy(buf2, buf)
  		if c, err := ParseCertificate(buf2); err == nil {
  			roots.AddCert(c)
  		}
  	}
  	return roots, nil
  }
  

View as plain text