github.com/hxx258456/ccgo@v0.0.5-0.20230213014102-48b35f46f66f/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 i := 0; i < opts.Intermediates.len(); i++ { 42 intermediate, err := opts.Intermediates.cert(i) 43 if err != nil { 44 return nil, err 45 } 46 ctx, err := syscall.CertCreateCertificateContext(syscall.X509_ASN_ENCODING|syscall.PKCS_7_ASN_ENCODING, &intermediate.Raw[0], uint32(len(intermediate.Raw))) 47 if err != nil { 48 return nil, err 49 } 50 51 err = syscall.CertAddCertificateContextToStore(handle, ctx, syscall.CERT_STORE_ADD_ALWAYS, nil) 52 syscall.CertFreeCertificateContext(ctx) 53 if err != nil { 54 return nil, err 55 } 56 } 57 } 58 59 return storeCtx, nil 60 } 61 62 // extractSimpleChain extracts the final certificate chain from a CertSimpleChain. 63 func extractSimpleChain(simpleChain **syscall.CertSimpleChain, count int) (chain []*Certificate, err error) { 64 if simpleChain == nil || count == 0 { 65 return nil, errors.New("x509: invalid simple chain") 66 } 67 68 simpleChains := (*[1 << 20]*syscall.CertSimpleChain)(unsafe.Pointer(simpleChain))[:count:count] 69 lastChain := simpleChains[count-1] 70 elements := (*[1 << 20]*syscall.CertChainElement)(unsafe.Pointer(lastChain.Elements))[:lastChain.NumElements:lastChain.NumElements] 71 for i := 0; i < int(lastChain.NumElements); i++ { 72 // Copy the buf, since ParseCertificate does not create its own copy. 73 cert := elements[i].CertContext 74 encodedCert := (*[1 << 20]byte)(unsafe.Pointer(cert.EncodedCert))[:cert.Length:cert.Length] 75 buf := make([]byte, cert.Length) 76 copy(buf, encodedCert) 77 parsedCert, err := ParseCertificate(buf) 78 if err != nil { 79 return nil, err 80 } 81 chain = append(chain, parsedCert) 82 } 83 84 return chain, nil 85 } 86 87 // checkChainTrustStatus checks the trust status of the certificate chain, translating 88 // any errors it finds into Go errors in the process. 89 func checkChainTrustStatus(c *Certificate, chainCtx *syscall.CertChainContext) error { 90 if chainCtx.TrustStatus.ErrorStatus != syscall.CERT_TRUST_NO_ERROR { 91 status := chainCtx.TrustStatus.ErrorStatus 92 switch status { 93 case syscall.CERT_TRUST_IS_NOT_TIME_VALID: 94 return CertificateInvalidError{c, Expired, ""} 95 case syscall.CERT_TRUST_IS_NOT_VALID_FOR_USAGE: 96 return CertificateInvalidError{c, IncompatibleUsage, ""} 97 // TODO(filippo): surface more error statuses. 98 default: 99 return UnknownAuthorityError{c, nil, nil} 100 } 101 } 102 return nil 103 } 104 105 // checkChainSSLServerPolicy checks that the certificate chain in chainCtx is valid for 106 // use as a certificate chain for a SSL/TLS server. 107 func checkChainSSLServerPolicy(c *Certificate, chainCtx *syscall.CertChainContext, opts *VerifyOptions) error { 108 servernamep, err := syscall.UTF16PtrFromString(opts.DNSName) 109 if err != nil { 110 return err 111 } 112 sslPara := &syscall.SSLExtraCertChainPolicyPara{ 113 AuthType: syscall.AUTHTYPE_SERVER, 114 ServerName: servernamep, 115 } 116 sslPara.Size = uint32(unsafe.Sizeof(*sslPara)) 117 118 para := &syscall.CertChainPolicyPara{ 119 ExtraPolicyPara: (syscall.Pointer)(unsafe.Pointer(sslPara)), 120 } 121 para.Size = uint32(unsafe.Sizeof(*para)) 122 123 status := syscall.CertChainPolicyStatus{} 124 err = syscall.CertVerifyCertificateChainPolicy(syscall.CERT_CHAIN_POLICY_SSL, chainCtx, para, &status) 125 if err != nil { 126 return err 127 } 128 129 // TODO(mkrautz): use the lChainIndex and lElementIndex fields 130 // of the CertChainPolicyStatus to provide proper context, instead 131 // using c. 132 if status.Error != 0 { 133 switch status.Error { 134 case syscall.CERT_E_EXPIRED: 135 return CertificateInvalidError{c, Expired, ""} 136 case syscall.CERT_E_CN_NO_MATCH: 137 return HostnameError{c, opts.DNSName} 138 case syscall.CERT_E_UNTRUSTEDROOT: 139 return UnknownAuthorityError{c, nil, nil} 140 default: 141 return UnknownAuthorityError{c, nil, nil} 142 } 143 } 144 145 return nil 146 } 147 148 // windowsExtKeyUsageOIDs are the C NUL-terminated string representations of the 149 // OIDs for use with the Windows API. 150 var windowsExtKeyUsageOIDs = make(map[ExtKeyUsage][]byte, len(extKeyUsageOIDs)) 151 152 func init() { 153 for _, eku := range extKeyUsageOIDs { 154 windowsExtKeyUsageOIDs[eku.extKeyUsage] = []byte(eku.oid.String() + "\x00") 155 } 156 } 157 158 func verifyChain(c *Certificate, chainCtx *syscall.CertChainContext, opts *VerifyOptions) (chain []*Certificate, err error) { 159 err = checkChainTrustStatus(c, chainCtx) 160 if err != nil { 161 return nil, err 162 } 163 164 if opts != nil && len(opts.DNSName) > 0 { 165 err = checkChainSSLServerPolicy(c, chainCtx, opts) 166 if err != nil { 167 return nil, err 168 } 169 } 170 171 chain, err = extractSimpleChain(chainCtx.Chains, int(chainCtx.ChainCount)) 172 if err != nil { 173 return nil, err 174 } 175 if len(chain) == 0 { 176 return nil, errors.New("x509: internal error: system verifier returned an empty chain") 177 } 178 179 // Mitigate CVE-2020-0601, where the Windows system verifier might be 180 // tricked into using custom curve parameters for a trusted root, by 181 // double-checking all ECDSA signatures. If the system was tricked into 182 // using spoofed parameters, the signature will be invalid for the correct 183 // ones we parsed. (We don't support custom curves ourselves.) 184 for i, parent := range chain[1:] { 185 if parent.PublicKeyAlgorithm != ECDSA { 186 continue 187 } 188 if err := parent.CheckSignature(chain[i].SignatureAlgorithm, 189 chain[i].RawTBSCertificate, chain[i].Signature); err != nil { 190 return nil, err 191 } 192 } 193 return chain, nil 194 } 195 196 // systemVerify is like Verify, except that it uses CryptoAPI calls 197 // to build certificate chains and verify them. 198 func (c *Certificate) systemVerify(opts *VerifyOptions) (chains [][]*Certificate, err error) { 199 storeCtx, err := createStoreContext(c, opts) 200 if err != nil { 201 return nil, err 202 } 203 defer syscall.CertFreeCertificateContext(storeCtx) 204 205 para := new(syscall.CertChainPara) 206 para.Size = uint32(unsafe.Sizeof(*para)) 207 208 keyUsages := opts.KeyUsages 209 if len(keyUsages) == 0 { 210 keyUsages = []ExtKeyUsage{ExtKeyUsageServerAuth} 211 } 212 oids := make([]*byte, 0, len(keyUsages)) 213 for _, eku := range keyUsages { 214 if eku == ExtKeyUsageAny { 215 oids = nil 216 break 217 } 218 if oid, ok := windowsExtKeyUsageOIDs[eku]; ok { 219 oids = append(oids, &oid[0]) 220 } 221 // Like the standard verifier, accept SGC EKUs as equivalent to ServerAuth. 222 if eku == ExtKeyUsageServerAuth { 223 oids = append(oids, &syscall.OID_SERVER_GATED_CRYPTO[0]) 224 oids = append(oids, &syscall.OID_SGC_NETSCAPE[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 := (*[1 << 20]*syscall.CertChainContext)(unsafe.Pointer(topCtx.LowerQualityChains))[:lqCtxCount:lqCtxCount] 263 264 for _, ctx := range lqCtxs { 265 chain, err := verifyChain(c, ctx, opts) 266 if err == nil { 267 chains = append(chains, chain) 268 } 269 } 270 } 271 272 if len(chains) == 0 { 273 // Return the error from the highest quality context. 274 return nil, topErr 275 } 276 277 return chains, nil 278 } 279 280 func loadSystemRoots() (*CertPool, error) { 281 // TODO: restore this functionality on Windows. We tried to do 282 // it in Go 1.8 but had to revert it. See Issue 18609. 283 // Returning (nil, nil) was the old behavior, prior to CL 30578. 284 // The if statement here avoids vet complaining about 285 // unreachable code below. 286 if true { 287 return nil, nil 288 } 289 290 const CRYPT_E_NOT_FOUND = 0x80092004 291 292 store, err := syscall.CertOpenSystemStore(0, syscall.StringToUTF16Ptr("ROOT")) 293 if err != nil { 294 return nil, err 295 } 296 defer syscall.CertCloseStore(store, 0) 297 298 roots := NewCertPool() 299 var cert *syscall.CertContext 300 for { 301 cert, err = syscall.CertEnumCertificatesInStore(store, cert) 302 if err != nil { 303 if errno, ok := err.(syscall.Errno); ok { 304 if errno == CRYPT_E_NOT_FOUND { 305 break 306 } 307 } 308 return nil, err 309 } 310 if cert == nil { 311 break 312 } 313 // Copy the buf, since ParseCertificate does not create its own copy. 314 buf := (*[1 << 20]byte)(unsafe.Pointer(cert.EncodedCert))[:cert.Length:cert.Length] 315 buf2 := make([]byte, cert.Length) 316 copy(buf2, buf) 317 if c, err := ParseCertificate(buf2); err == nil { 318 roots.AddCert(c) 319 } 320 } 321 return roots, nil 322 }