123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267 |
- // 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: convertToPolicyParaType(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
- }
|