github.com/twelsh-aw/go/src@v0.0.0-20230516233729-a56fe86a7c81/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 "syscall" 11 "unsafe" 12 ) 13 14 func loadSystemRoots() (*CertPool, error) { 15 return &CertPool{systemPool: true}, nil 16 } 17 18 // Creates a new *syscall.CertContext representing the leaf certificate in an in-memory 19 // certificate store containing itself and all of the intermediate certificates specified 20 // in the opts.Intermediates CertPool. 21 // 22 // A pointer to the in-memory store is available in the returned CertContext's Store field. 23 // The store is automatically freed when the CertContext is freed using 24 // syscall.CertFreeCertificateContext. 25 func createStoreContext(leaf *Certificate, opts *VerifyOptions) (*syscall.CertContext, error) { 26 var storeCtx *syscall.CertContext 27 28 leafCtx, err := syscall.CertCreateCertificateContext(syscall.X509_ASN_ENCODING|syscall.PKCS_7_ASN_ENCODING, &leaf.Raw[0], uint32(len(leaf.Raw))) 29 if err != nil { 30 return nil, err 31 } 32 defer syscall.CertFreeCertificateContext(leafCtx) 33 34 handle, err := syscall.CertOpenStore(syscall.CERT_STORE_PROV_MEMORY, 0, 0, syscall.CERT_STORE_DEFER_CLOSE_UNTIL_LAST_FREE_FLAG, 0) 35 if err != nil { 36 return nil, err 37 } 38 defer syscall.CertCloseStore(handle, 0) 39 40 err = syscall.CertAddCertificateContextToStore(handle, leafCtx, syscall.CERT_STORE_ADD_ALWAYS, &storeCtx) 41 if err != nil { 42 return nil, err 43 } 44 45 if opts.Intermediates != nil { 46 for i := 0; i < opts.Intermediates.len(); i++ { 47 intermediate, err := opts.Intermediates.cert(i) 48 if err != nil { 49 return nil, err 50 } 51 ctx, err := syscall.CertCreateCertificateContext(syscall.X509_ASN_ENCODING|syscall.PKCS_7_ASN_ENCODING, &intermediate.Raw[0], uint32(len(intermediate.Raw))) 52 if err != nil { 53 return nil, err 54 } 55 56 err = syscall.CertAddCertificateContextToStore(handle, ctx, syscall.CERT_STORE_ADD_ALWAYS, nil) 57 syscall.CertFreeCertificateContext(ctx) 58 if err != nil { 59 return nil, err 60 } 61 } 62 } 63 64 return storeCtx, nil 65 } 66 67 // extractSimpleChain extracts the final certificate chain from a CertSimpleChain. 68 func extractSimpleChain(simpleChain **syscall.CertSimpleChain, count int) (chain []*Certificate, err error) { 69 if simpleChain == nil || count == 0 { 70 return nil, errors.New("x509: invalid simple chain") 71 } 72 73 simpleChains := unsafe.Slice(simpleChain, count) 74 lastChain := simpleChains[count-1] 75 elements := unsafe.Slice(lastChain.Elements, lastChain.NumElements) 76 for i := 0; i < int(lastChain.NumElements); i++ { 77 // Copy the buf, since ParseCertificate does not create its own copy. 78 cert := elements[i].CertContext 79 encodedCert := unsafe.Slice(cert.EncodedCert, cert.Length) 80 buf := bytes.Clone(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 := unsafe.Slice(topCtx.LowerQualityChains, lqCtxCount) 262 for _, ctx := range lqCtxs { 263 chain, err := verifyChain(c, ctx, opts) 264 if err == nil { 265 chains = append(chains, chain) 266 } 267 } 268 } 269 270 if len(chains) == 0 { 271 // Return the error from the highest quality context. 272 return nil, topErr 273 } 274 275 return chains, nil 276 }