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
5package x509
6
7import (
8	"bytes"
9	"errors"
10	"strings"
11	"syscall"
12	"unsafe"
13)
14
15func 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.
26func 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.
69func 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.
94func 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.
112func 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.
155var windowsExtKeyUsageOIDs = make(map[ExtKeyUsage][]byte, len(extKeyUsageOIDs))
156
157func init() {
158	for _, eku := range extKeyUsageOIDs {
159		windowsExtKeyUsageOIDs[eku.extKeyUsage] = []byte(eku.oid.String() + "\x00")
160	}
161}
162
163func 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.
203func (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}
278