]> Cypherpunks.ru repositories - gostls13.git/blob - src/crypto/x509/root_windows.go
crypto/x509: implement AddCertWithConstraint
[gostls13.git] / src / crypto / x509 / root_windows.go
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 }