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 }