github.com/ltltlt/go-source-code@v0.0.0-20190830023027-95be009773aa/crypto/x509/root_windows.go (about)

     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 _, intermediate := range opts.Intermediates.certs {
    42  			ctx, err := syscall.CertCreateCertificateContext(syscall.X509_ASN_ENCODING|syscall.PKCS_7_ASN_ENCODING, &intermediate.Raw[0], uint32(len(intermediate.Raw)))
    43  			if err != nil {
    44  				return nil, err
    45  			}
    46  
    47  			err = syscall.CertAddCertificateContextToStore(handle, ctx, syscall.CERT_STORE_ADD_ALWAYS, nil)
    48  			syscall.CertFreeCertificateContext(ctx)
    49  			if err != nil {
    50  				return nil, err
    51  			}
    52  		}
    53  	}
    54  
    55  	return storeCtx, nil
    56  }
    57  
    58  // extractSimpleChain extracts the final certificate chain from a CertSimpleChain.
    59  func extractSimpleChain(simpleChain **syscall.CertSimpleChain, count int) (chain []*Certificate, err error) {
    60  	if simpleChain == nil || count == 0 {
    61  		return nil, errors.New("x509: invalid simple chain")
    62  	}
    63  
    64  	simpleChains := (*[1 << 20]*syscall.CertSimpleChain)(unsafe.Pointer(simpleChain))[:]
    65  	lastChain := simpleChains[count-1]
    66  	elements := (*[1 << 20]*syscall.CertChainElement)(unsafe.Pointer(lastChain.Elements))[:]
    67  	for i := 0; i < int(lastChain.NumElements); i++ {
    68  		// Copy the buf, since ParseCertificate does not create its own copy.
    69  		cert := elements[i].CertContext
    70  		encodedCert := (*[1 << 20]byte)(unsafe.Pointer(cert.EncodedCert))[:]
    71  		buf := make([]byte, cert.Length)
    72  		copy(buf, encodedCert[:])
    73  		parsedCert, err := ParseCertificate(buf)
    74  		if err != nil {
    75  			return nil, err
    76  		}
    77  		chain = append(chain, parsedCert)
    78  	}
    79  
    80  	return chain, nil
    81  }
    82  
    83  // checkChainTrustStatus checks the trust status of the certificate chain, translating
    84  // any errors it finds into Go errors in the process.
    85  func checkChainTrustStatus(c *Certificate, chainCtx *syscall.CertChainContext) error {
    86  	if chainCtx.TrustStatus.ErrorStatus != syscall.CERT_TRUST_NO_ERROR {
    87  		status := chainCtx.TrustStatus.ErrorStatus
    88  		switch status {
    89  		case syscall.CERT_TRUST_IS_NOT_TIME_VALID:
    90  			return CertificateInvalidError{c, Expired, ""}
    91  		default:
    92  			return UnknownAuthorityError{c, nil, nil}
    93  		}
    94  	}
    95  	return nil
    96  }
    97  
    98  type _CertChainPolicyPara struct {
    99  	Size            uint32
   100  	Flags           uint32
   101  	ExtraPolicyPara unsafe.Pointer
   102  }
   103  
   104  // checkChainSSLServerPolicy checks that the certificate chain in chainCtx is valid for
   105  // use as a certificate chain for a SSL/TLS server.
   106  func checkChainSSLServerPolicy(c *Certificate, chainCtx *syscall.CertChainContext, opts *VerifyOptions) error {
   107  	servernamep, err := syscall.UTF16PtrFromString(opts.DNSName)
   108  	if err != nil {
   109  		return err
   110  	}
   111  	sslPara := &syscall.SSLExtraCertChainPolicyPara{
   112  		AuthType:   syscall.AUTHTYPE_SERVER,
   113  		ServerName: servernamep,
   114  	}
   115  	sslPara.Size = uint32(unsafe.Sizeof(*sslPara))
   116  
   117  	para := &_CertChainPolicyPara{
   118  		ExtraPolicyPara: unsafe.Pointer(sslPara),
   119  	}
   120  	para.Size = uint32(unsafe.Sizeof(*para))
   121  
   122  	status := syscall.CertChainPolicyStatus{}
   123  	err = syscall.CertVerifyCertificateChainPolicy(syscall.CERT_CHAIN_POLICY_SSL, chainCtx, (*syscall.CertChainPolicyPara)(unsafe.Pointer(para)), &status)
   124  	if err != nil {
   125  		return err
   126  	}
   127  
   128  	// TODO(mkrautz): use the lChainIndex and lElementIndex fields
   129  	// of the CertChainPolicyStatus to provide proper context, instead
   130  	// using c.
   131  	if status.Error != 0 {
   132  		switch status.Error {
   133  		case syscall.CERT_E_EXPIRED:
   134  			return CertificateInvalidError{c, Expired, ""}
   135  		case syscall.CERT_E_CN_NO_MATCH:
   136  			return HostnameError{c, opts.DNSName}
   137  		case syscall.CERT_E_UNTRUSTEDROOT:
   138  			return UnknownAuthorityError{c, nil, nil}
   139  		default:
   140  			return UnknownAuthorityError{c, nil, nil}
   141  		}
   142  	}
   143  
   144  	return nil
   145  }
   146  
   147  // systemVerify is like Verify, except that it uses CryptoAPI calls
   148  // to build certificate chains and verify them.
   149  func (c *Certificate) systemVerify(opts *VerifyOptions) (chains [][]*Certificate, err error) {
   150  	hasDNSName := opts != nil && len(opts.DNSName) > 0
   151  
   152  	storeCtx, err := createStoreContext(c, opts)
   153  	if err != nil {
   154  		return nil, err
   155  	}
   156  	defer syscall.CertFreeCertificateContext(storeCtx)
   157  
   158  	para := new(syscall.CertChainPara)
   159  	para.Size = uint32(unsafe.Sizeof(*para))
   160  
   161  	// If there's a DNSName set in opts, assume we're verifying
   162  	// a certificate from a TLS server.
   163  	if hasDNSName {
   164  		oids := []*byte{
   165  			&syscall.OID_PKIX_KP_SERVER_AUTH[0],
   166  			// Both IE and Chrome allow certificates with
   167  			// Server Gated Crypto as well. Some certificates
   168  			// in the wild require them.
   169  			&syscall.OID_SERVER_GATED_CRYPTO[0],
   170  			&syscall.OID_SGC_NETSCAPE[0],
   171  		}
   172  		para.RequestedUsage.Type = syscall.USAGE_MATCH_TYPE_OR
   173  		para.RequestedUsage.Usage.Length = uint32(len(oids))
   174  		para.RequestedUsage.Usage.UsageIdentifiers = &oids[0]
   175  	} else {
   176  		para.RequestedUsage.Type = syscall.USAGE_MATCH_TYPE_AND
   177  		para.RequestedUsage.Usage.Length = 0
   178  		para.RequestedUsage.Usage.UsageIdentifiers = nil
   179  	}
   180  
   181  	var verifyTime *syscall.Filetime
   182  	if opts != nil && !opts.CurrentTime.IsZero() {
   183  		ft := syscall.NsecToFiletime(opts.CurrentTime.UnixNano())
   184  		verifyTime = &ft
   185  	}
   186  
   187  	// CertGetCertificateChain will traverse Windows's root stores
   188  	// in an attempt to build a verified certificate chain. Once
   189  	// it has found a verified chain, it stops. MSDN docs on
   190  	// CERT_CHAIN_CONTEXT:
   191  	//
   192  	//   When a CERT_CHAIN_CONTEXT is built, the first simple chain
   193  	//   begins with an end certificate and ends with a self-signed
   194  	//   certificate. If that self-signed certificate is not a root
   195  	//   or otherwise trusted certificate, an attempt is made to
   196  	//   build a new chain. CTLs are used to create the new chain
   197  	//   beginning with the self-signed certificate from the original
   198  	//   chain as the end certificate of the new chain. This process
   199  	//   continues building additional simple chains until the first
   200  	//   self-signed certificate is a trusted certificate or until
   201  	//   an additional simple chain cannot be built.
   202  	//
   203  	// The result is that we'll only get a single trusted chain to
   204  	// return to our caller.
   205  	var chainCtx *syscall.CertChainContext
   206  	err = syscall.CertGetCertificateChain(syscall.Handle(0), storeCtx, verifyTime, storeCtx.Store, para, 0, 0, &chainCtx)
   207  	if err != nil {
   208  		return nil, err
   209  	}
   210  	defer syscall.CertFreeCertificateChain(chainCtx)
   211  
   212  	err = checkChainTrustStatus(c, chainCtx)
   213  	if err != nil {
   214  		return nil, err
   215  	}
   216  
   217  	if hasDNSName {
   218  		err = checkChainSSLServerPolicy(c, chainCtx, opts)
   219  		if err != nil {
   220  			return nil, err
   221  		}
   222  	}
   223  
   224  	chain, err := extractSimpleChain(chainCtx.Chains, int(chainCtx.ChainCount))
   225  	if err != nil {
   226  		return nil, err
   227  	}
   228  
   229  	chains = append(chains, chain)
   230  
   231  	return chains, nil
   232  }
   233  
   234  func loadSystemRoots() (*CertPool, error) {
   235  	// TODO: restore this functionality on Windows. We tried to do
   236  	// it in Go 1.8 but had to revert it. See Issue 18609.
   237  	// Returning (nil, nil) was the old behavior, prior to CL 30578.
   238  	return nil, nil
   239  
   240  	const CRYPT_E_NOT_FOUND = 0x80092004
   241  
   242  	store, err := syscall.CertOpenSystemStore(0, syscall.StringToUTF16Ptr("ROOT"))
   243  	if err != nil {
   244  		return nil, err
   245  	}
   246  	defer syscall.CertCloseStore(store, 0)
   247  
   248  	roots := NewCertPool()
   249  	var cert *syscall.CertContext
   250  	for {
   251  		cert, err = syscall.CertEnumCertificatesInStore(store, cert)
   252  		if err != nil {
   253  			if errno, ok := err.(syscall.Errno); ok {
   254  				if errno == CRYPT_E_NOT_FOUND {
   255  					break
   256  				}
   257  			}
   258  			return nil, err
   259  		}
   260  		if cert == nil {
   261  			break
   262  		}
   263  		// Copy the buf, since ParseCertificate does not create its own copy.
   264  		buf := (*[1 << 20]byte)(unsafe.Pointer(cert.EncodedCert))[:]
   265  		buf2 := make([]byte, cert.Length)
   266  		copy(buf2, buf)
   267  		if c, err := ParseCertificate(buf2); err == nil {
   268  			roots.AddCert(c)
   269  		}
   270  	}
   271  	return roots, nil
   272  }