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