Home | History | Annotate | Download | only in 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))[:]
     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 // checkChainSSLServerPolicy checks that the certificate chain in chainCtx is valid for
     99 // use as a certificate chain for a SSL/TLS server.
    100 func checkChainSSLServerPolicy(c *Certificate, chainCtx *syscall.CertChainContext, opts *VerifyOptions) error {
    101 	servernamep, err := syscall.UTF16PtrFromString(opts.DNSName)
    102 	if err != nil {
    103 		return err
    104 	}
    105 	sslPara := &syscall.SSLExtraCertChainPolicyPara{
    106 		AuthType:   syscall.AUTHTYPE_SERVER,
    107 		ServerName: servernamep,
    108 	}
    109 	sslPara.Size = uint32(unsafe.Sizeof(*sslPara))
    110 
    111 	para := &syscall.CertChainPolicyPara{
    112 		ExtraPolicyPara: uintptr(unsafe.Pointer(sslPara)),
    113 	}
    114 	para.Size = uint32(unsafe.Sizeof(*para))
    115 
    116 	status := syscall.CertChainPolicyStatus{}
    117 	err = syscall.CertVerifyCertificateChainPolicy(syscall.CERT_CHAIN_POLICY_SSL, chainCtx, para, &status)
    118 	if err != nil {
    119 		return err
    120 	}
    121 
    122 	// TODO(mkrautz): use the lChainIndex and lElementIndex fields
    123 	// of the CertChainPolicyStatus to provide proper context, instead
    124 	// using c.
    125 	if status.Error != 0 {
    126 		switch status.Error {
    127 		case syscall.CERT_E_EXPIRED:
    128 			return CertificateInvalidError{c, Expired}
    129 		case syscall.CERT_E_CN_NO_MATCH:
    130 			return HostnameError{c, opts.DNSName}
    131 		case syscall.CERT_E_UNTRUSTEDROOT:
    132 			return UnknownAuthorityError{c, nil, nil}
    133 		default:
    134 			return UnknownAuthorityError{c, nil, nil}
    135 		}
    136 	}
    137 
    138 	return nil
    139 }
    140 
    141 // systemVerify is like Verify, except that it uses CryptoAPI calls
    142 // to build certificate chains and verify them.
    143 func (c *Certificate) systemVerify(opts *VerifyOptions) (chains [][]*Certificate, err error) {
    144 	hasDNSName := opts != nil && len(opts.DNSName) > 0
    145 
    146 	storeCtx, err := createStoreContext(c, opts)
    147 	if err != nil {
    148 		return nil, err
    149 	}
    150 	defer syscall.CertFreeCertificateContext(storeCtx)
    151 
    152 	para := new(syscall.CertChainPara)
    153 	para.Size = uint32(unsafe.Sizeof(*para))
    154 
    155 	// If there's a DNSName set in opts, assume we're verifying
    156 	// a certificate from a TLS server.
    157 	if hasDNSName {
    158 		oids := []*byte{
    159 			&syscall.OID_PKIX_KP_SERVER_AUTH[0],
    160 			// Both IE and Chrome allow certificates with
    161 			// Server Gated Crypto as well. Some certificates
    162 			// in the wild require them.
    163 			&syscall.OID_SERVER_GATED_CRYPTO[0],
    164 			&syscall.OID_SGC_NETSCAPE[0],
    165 		}
    166 		para.RequestedUsage.Type = syscall.USAGE_MATCH_TYPE_OR
    167 		para.RequestedUsage.Usage.Length = uint32(len(oids))
    168 		para.RequestedUsage.Usage.UsageIdentifiers = &oids[0]
    169 	} else {
    170 		para.RequestedUsage.Type = syscall.USAGE_MATCH_TYPE_AND
    171 		para.RequestedUsage.Usage.Length = 0
    172 		para.RequestedUsage.Usage.UsageIdentifiers = nil
    173 	}
    174 
    175 	var verifyTime *syscall.Filetime
    176 	if opts != nil && !opts.CurrentTime.IsZero() {
    177 		ft := syscall.NsecToFiletime(opts.CurrentTime.UnixNano())
    178 		verifyTime = &ft
    179 	}
    180 
    181 	// CertGetCertificateChain will traverse Windows's root stores
    182 	// in an attempt to build a verified certificate chain. Once
    183 	// it has found a verified chain, it stops. MSDN docs on
    184 	// CERT_CHAIN_CONTEXT:
    185 	//
    186 	//   When a CERT_CHAIN_CONTEXT is built, the first simple chain
    187 	//   begins with an end certificate and ends with a self-signed
    188 	//   certificate. If that self-signed certificate is not a root
    189 	//   or otherwise trusted certificate, an attempt is made to
    190 	//   build a new chain. CTLs are used to create the new chain
    191 	//   beginning with the self-signed certificate from the original
    192 	//   chain as the end certificate of the new chain. This process
    193 	//   continues building additional simple chains until the first
    194 	//   self-signed certificate is a trusted certificate or until
    195 	//   an additional simple chain cannot be built.
    196 	//
    197 	// The result is that we'll only get a single trusted chain to
    198 	// return to our caller.
    199 	var chainCtx *syscall.CertChainContext
    200 	err = syscall.CertGetCertificateChain(syscall.Handle(0), storeCtx, verifyTime, storeCtx.Store, para, 0, 0, &chainCtx)
    201 	if err != nil {
    202 		return nil, err
    203 	}
    204 	defer syscall.CertFreeCertificateChain(chainCtx)
    205 
    206 	err = checkChainTrustStatus(c, chainCtx)
    207 	if err != nil {
    208 		return nil, err
    209 	}
    210 
    211 	if hasDNSName {
    212 		err = checkChainSSLServerPolicy(c, chainCtx, opts)
    213 		if err != nil {
    214 			return nil, err
    215 		}
    216 	}
    217 
    218 	chain, err := extractSimpleChain(chainCtx.Chains, int(chainCtx.ChainCount))
    219 	if err != nil {
    220 		return nil, err
    221 	}
    222 
    223 	chains = append(chains, chain)
    224 
    225 	return chains, nil
    226 }
    227 
    228 func loadSystemRoots() (*CertPool, error) {
    229 	// TODO: restore this functionality on Windows. We tried to do
    230 	// it in Go 1.8 but had to revert it. See Issue 18609.
    231 	// Returning (nil, nil) was the old behavior, prior to CL 30578.
    232 	return nil, nil
    233 
    234 	const CRYPT_E_NOT_FOUND = 0x80092004
    235 
    236 	store, err := syscall.CertOpenSystemStore(0, syscall.StringToUTF16Ptr("ROOT"))
    237 	if err != nil {
    238 		return nil, err
    239 	}
    240 	defer syscall.CertCloseStore(store, 0)
    241 
    242 	roots := NewCertPool()
    243 	var cert *syscall.CertContext
    244 	for {
    245 		cert, err = syscall.CertEnumCertificatesInStore(store, cert)
    246 		if err != nil {
    247 			if errno, ok := err.(syscall.Errno); ok {
    248 				if errno == CRYPT_E_NOT_FOUND {
    249 					break
    250 				}
    251 			}
    252 			return nil, err
    253 		}
    254 		if cert == nil {
    255 			break
    256 		}
    257 		// Copy the buf, since ParseCertificate does not create its own copy.
    258 		buf := (*[1 << 20]byte)(unsafe.Pointer(cert.EncodedCert))[:]
    259 		buf2 := make([]byte, cert.Length)
    260 		copy(buf2, buf)
    261 		if c, err := ParseCertificate(buf2); err == nil {
    262 			roots.AddCert(c)
    263 		}
    264 	}
    265 	return roots, nil
    266 }
    267