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 _, 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))[:count:count]
    65  	lastChain := simpleChains[count-1]
    66  	elements := (*[1 << 20]*syscall.CertChainElement)(unsafe.Pointer(lastChain.Elements))[:lastChain.NumElements:lastChain.NumElements]
    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))[:cert.Length:cert.Length]
    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  		case syscall.CERT_TRUST_IS_NOT_VALID_FOR_USAGE:
    92  			return CertificateInvalidError{c, IncompatibleUsage, ""}
    93  		// TODO(filippo): surface more error statuses.
    94  		default:
    95  			return UnknownAuthorityError{c, nil, nil}
    96  		}
    97  	}
    98  	return nil
    99  }
   100  
   101  // checkChainSSLServerPolicy checks that the certificate chain in chainCtx is valid for
   102  // use as a certificate chain for a SSL/TLS server.
   103  func checkChainSSLServerPolicy(c *Certificate, chainCtx *syscall.CertChainContext, opts *VerifyOptions) error {
   104  	servernamep, err := syscall.UTF16PtrFromString(opts.DNSName)
   105  	if err != nil {
   106  		return err
   107  	}
   108  	sslPara := &syscall.SSLExtraCertChainPolicyPara{
   109  		AuthType:   syscall.AUTHTYPE_SERVER,
   110  		ServerName: servernamep,
   111  	}
   112  	sslPara.Size = uint32(unsafe.Sizeof(*sslPara))
   113  
   114  	para := &syscall.CertChainPolicyPara{
   115  		ExtraPolicyPara: (syscall.Pointer)(unsafe.Pointer(sslPara)),
   116  	}
   117  	para.Size = uint32(unsafe.Sizeof(*para))
   118  
   119  	status := syscall.CertChainPolicyStatus{}
   120  	err = syscall.CertVerifyCertificateChainPolicy(syscall.CERT_CHAIN_POLICY_SSL, chainCtx, para, &status)
   121  	if err != nil {
   122  		return err
   123  	}
   124  
   125  	// TODO(mkrautz): use the lChainIndex and lElementIndex fields
   126  	// of the CertChainPolicyStatus to provide proper context, instead
   127  	// using c.
   128  	if status.Error != 0 {
   129  		switch status.Error {
   130  		case syscall.CERT_E_EXPIRED:
   131  			return CertificateInvalidError{c, Expired, ""}
   132  		case syscall.CERT_E_CN_NO_MATCH:
   133  			return HostnameError{c, opts.DNSName}
   134  		case syscall.CERT_E_UNTRUSTEDROOT:
   135  			return UnknownAuthorityError{c, nil, nil}
   136  		default:
   137  			return UnknownAuthorityError{c, nil, nil}
   138  		}
   139  	}
   140  
   141  	return nil
   142  }
   143  
   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  
   154  // systemVerify is like Verify, except that it uses CryptoAPI calls
   155  // to build certificate chains and verify them.
   156  func (c *Certificate) systemVerify(opts *VerifyOptions) (chains [][]*Certificate, err error) {
   157  	storeCtx, err := createStoreContext(c, opts)
   158  	if err != nil {
   159  		return nil, err
   160  	}
   161  	defer syscall.CertFreeCertificateContext(storeCtx)
   162  
   163  	para := new(syscall.CertChainPara)
   164  	para.Size = uint32(unsafe.Sizeof(*para))
   165  
   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])
   178  		}
   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 {
   186  		para.RequestedUsage.Type = syscall.USAGE_MATCH_TYPE_OR
   187  		para.RequestedUsage.Usage.Length = uint32(len(oids))
   188  		para.RequestedUsage.Usage.UsageIdentifiers = &oids[0]
   189  	} else {
   190  		para.RequestedUsage.Type = syscall.USAGE_MATCH_TYPE_AND
   191  		para.RequestedUsage.Usage.Length = 0
   192  		para.RequestedUsage.Usage.UsageIdentifiers = nil
   193  	}
   194  
   195  	var verifyTime *syscall.Filetime
   196  	if opts != nil && !opts.CurrentTime.IsZero() {
   197  		ft := syscall.NsecToFiletime(opts.CurrentTime.UnixNano())
   198  		verifyTime = &ft
   199  	}
   200  
   201  	// CertGetCertificateChain will traverse Windows's root stores
   202  	// in an attempt to build a verified certificate chain. Once
   203  	// it has found a verified chain, it stops. MSDN docs on
   204  	// CERT_CHAIN_CONTEXT:
   205  	//
   206  	//   When a CERT_CHAIN_CONTEXT is built, the first simple chain
   207  	//   begins with an end certificate and ends with a self-signed
   208  	//   certificate. If that self-signed certificate is not a root
   209  	//   or otherwise trusted certificate, an attempt is made to
   210  	//   build a new chain. CTLs are used to create the new chain
   211  	//   beginning with the self-signed certificate from the original
   212  	//   chain as the end certificate of the new chain. This process
   213  	//   continues building additional simple chains until the first
   214  	//   self-signed certificate is a trusted certificate or until
   215  	//   an additional simple chain cannot be built.
   216  	//
   217  	// The result is that we'll only get a single trusted chain to
   218  	// return to our caller.
   219  	var chainCtx *syscall.CertChainContext
   220  	err = syscall.CertGetCertificateChain(syscall.Handle(0), storeCtx, verifyTime, storeCtx.Store, para, 0, 0, &chainCtx)
   221  	if err != nil {
   222  		return nil, err
   223  	}
   224  	defer syscall.CertFreeCertificateChain(chainCtx)
   225  
   226  	err = checkChainTrustStatus(c, chainCtx)
   227  	if err != nil {
   228  		return nil, err
   229  	}
   230  
   231  	if opts != nil && len(opts.DNSName) > 0 {
   232  		err = checkChainSSLServerPolicy(c, chainCtx, opts)
   233  		if err != nil {
   234  			return nil, err
   235  		}
   236  	}
   237  
   238  	chain, err := extractSimpleChain(chainCtx.Chains, int(chainCtx.ChainCount))
   239  	if err != nil {
   240  		return nil, err
   241  	}
   242  	if len(chain) < 1 {
   243  		return nil, errors.New("x509: internal error: system verifier returned an empty chain")
   244  	}
   245  
   246  	// Mitigate CVE-2020-0601, where the Windows system verifier might be
   247  	// tricked into using custom curve parameters for a trusted root, by
   248  	// double-checking all ECDSA signatures. If the system was tricked into
   249  	// using spoofed parameters, the signature will be invalid for the correct
   250  	// ones we parsed. (We don't support custom curves ourselves.)
   251  	for i, parent := range chain[1:] {
   252  		if parent.PublicKeyAlgorithm != ECDSA {
   253  			continue
   254  		}
   255  		if err := parent.CheckSignature(chain[i].SignatureAlgorithm,
   256  			chain[i].RawTBSCertificate, chain[i].Signature); err != nil {
   257  			return nil, err
   258  		}
   259  	}
   260  
   261  	return [][]*Certificate{chain}, nil
   262  }
   263  
   264  func loadSystemRoots() (*CertPool, error) {
   265  	// TODO: restore this functionality on Windows. We tried to do
   266  	// it in Go 1.8 but had to revert it. See Issue 18609.
   267  	// Returning (nil, nil) was the old behavior, prior to CL 30578.
   268  	// The if statement here avoids vet complaining about
   269  	// unreachable code below.
   270  	if true {
   271  		return nil, nil
   272  	}
   273  
   274  	const CRYPT_E_NOT_FOUND = 0x80092004
   275  
   276  	store, err := syscall.CertOpenSystemStore(0, syscall.StringToUTF16Ptr("ROOT"))
   277  	if err != nil {
   278  		return nil, err
   279  	}
   280  	defer syscall.CertCloseStore(store, 0)
   281  
   282  	roots := NewCertPool()
   283  	var cert *syscall.CertContext
   284  	for {
   285  		cert, err = syscall.CertEnumCertificatesInStore(store, cert)
   286  		if err != nil {
   287  			if errno, ok := err.(syscall.Errno); ok {
   288  				if errno == CRYPT_E_NOT_FOUND {
   289  					break
   290  				}
   291  			}
   292  			return nil, err
   293  		}
   294  		if cert == nil {
   295  			break
   296  		}
   297  		// Copy the buf, since ParseCertificate does not create its own copy.
   298  		buf := (*[1 << 20]byte)(unsafe.Pointer(cert.EncodedCert))[:cert.Length:cert.Length]
   299  		buf2 := make([]byte, cert.Length)
   300  		copy(buf2, buf)
   301  		if c, err := ParseCertificate(buf2); err == nil {
   302  			roots.AddCert(c)
   303  		}
   304  	}
   305  	return roots, nil
   306  }
   307  

View as plain text