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