Source file src/crypto/x509/root_windows.go

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

View as plain text