1// Copyright 2011 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	"crypto/sha256"
10	"encoding/pem"
11	"errors"
12	"runtime"
13	"sync"
14)
15
16type sum224 [sha256.Size224]byte
17
18// CertPool is a set of certificates.
19type CertPool struct {
20	byName map[string][]int // cert.RawSubject => index into lazyCerts
21
22	// lazyCerts contains funcs that return a certificate,
23	// lazily parsing/decompressing it as needed.
24	lazyCerts []lazyCert
25
26	// haveSum maps from sum224(cert.Raw) to true. It's used only
27	// for AddCert duplicate detection, to avoid CertPool.contains
28	// calls in the AddCert path (because the contains method can
29	// call getCert and otherwise negate savings from lazy getCert
30	// funcs).
31	haveSum map[sum224]bool
32}
33
34// lazyCert is minimal metadata about a Cert and a func to retrieve it
35// in its normal expanded *Certificate form.
36type lazyCert struct {
37	// rawSubject is the Certificate.RawSubject value.
38	// It's the same as the CertPool.byName key, but in []byte
39	// form to make CertPool.Subjects (as used by crypto/tls) do
40	// fewer allocations.
41	rawSubject []byte
42
43	// getCert returns the certificate.
44	//
45	// It is not meant to do network operations or anything else
46	// where a failure is likely; the func is meant to lazily
47	// parse/decompress data that is already known to be good. The
48	// error in the signature primarily is meant for use in the
49	// case where a cert file existed on local disk when the program
50	// started up is deleted later before it's read.
51	getCert func() (*Certificate, error)
52}
53
54// NewCertPool returns a new, empty CertPool.
55func NewCertPool() *CertPool {
56	return &CertPool{
57		byName:  make(map[string][]int),
58		haveSum: make(map[sum224]bool),
59	}
60}
61
62// len returns the number of certs in the set.
63// A nil set is a valid empty set.
64func (s *CertPool) len() int {
65	if s == nil {
66		return 0
67	}
68	return len(s.lazyCerts)
69}
70
71// cert returns cert index n in s.
72func (s *CertPool) cert(n int) (*Certificate, error) {
73	return s.lazyCerts[n].getCert()
74}
75
76func (s *CertPool) copy() *CertPool {
77	p := &CertPool{
78		byName:    make(map[string][]int, len(s.byName)),
79		lazyCerts: make([]lazyCert, len(s.lazyCerts)),
80		haveSum:   make(map[sum224]bool, len(s.haveSum)),
81	}
82	for k, v := range s.byName {
83		indexes := make([]int, len(v))
84		copy(indexes, v)
85		p.byName[k] = indexes
86	}
87	for k := range s.haveSum {
88		p.haveSum[k] = true
89	}
90	copy(p.lazyCerts, s.lazyCerts)
91	return p
92}
93
94// SystemCertPool returns a copy of the system cert pool.
95//
96// On Unix systems other than macOS the environment variables SSL_CERT_FILE and
97// SSL_CERT_DIR can be used to override the system default locations for the SSL
98// certificate file and SSL certificate files directory, respectively. The
99// latter can be a colon-separated list.
100//
101// Any mutations to the returned pool are not written to disk and do not affect
102// any other pool returned by SystemCertPool.
103//
104// New changes in the system cert pool might not be reflected in subsequent calls.
105func SystemCertPool() (*CertPool, error) {
106	if runtime.GOOS == "windows" {
107		// Issue 16736, 18609:
108		return nil, errors.New("crypto/x509: system root pool is not available on Windows")
109	}
110
111	if sysRoots := systemRootsPool(); sysRoots != nil {
112		return sysRoots.copy(), nil
113	}
114
115	return loadSystemRoots()
116}
117
118// findPotentialParents returns the indexes of certificates in s which might
119// have signed cert.
120func (s *CertPool) findPotentialParents(cert *Certificate) []*Certificate {
121	if s == nil {
122		return nil
123	}
124
125	// consider all candidates where cert.Issuer matches cert.Subject.
126	// when picking possible candidates the list is built in the order
127	// of match plausibility as to save cycles in buildChains:
128	//   AKID and SKID match
129	//   AKID present, SKID missing / AKID missing, SKID present
130	//   AKID and SKID don't match
131	var matchingKeyID, oneKeyID, mismatchKeyID []*Certificate
132	for _, c := range s.byName[string(cert.RawIssuer)] {
133		candidate, err := s.cert(c)
134		if err != nil {
135			continue
136		}
137		kidMatch := bytes.Equal(candidate.SubjectKeyId, cert.AuthorityKeyId)
138		switch {
139		case kidMatch:
140			matchingKeyID = append(matchingKeyID, candidate)
141		case (len(candidate.SubjectKeyId) == 0 && len(cert.AuthorityKeyId) > 0) ||
142			(len(candidate.SubjectKeyId) > 0 && len(cert.AuthorityKeyId) == 0):
143			oneKeyID = append(oneKeyID, candidate)
144		default:
145			mismatchKeyID = append(mismatchKeyID, candidate)
146		}
147	}
148
149	found := len(matchingKeyID) + len(oneKeyID) + len(mismatchKeyID)
150	if found == 0 {
151		return nil
152	}
153	candidates := make([]*Certificate, 0, found)
154	candidates = append(candidates, matchingKeyID...)
155	candidates = append(candidates, oneKeyID...)
156	candidates = append(candidates, mismatchKeyID...)
157	return candidates
158}
159
160func (s *CertPool) contains(cert *Certificate) bool {
161	if s == nil {
162		return false
163	}
164	return s.haveSum[sha256.Sum224(cert.Raw)]
165}
166
167// AddCert adds a certificate to a pool.
168func (s *CertPool) AddCert(cert *Certificate) {
169	if cert == nil {
170		panic("adding nil Certificate to CertPool")
171	}
172	s.addCertFunc(sha256.Sum224(cert.Raw), string(cert.RawSubject), func() (*Certificate, error) {
173		return cert, nil
174	})
175}
176
177// addCertFunc adds metadata about a certificate to a pool, along with
178// a func to fetch that certificate later when needed.
179//
180// The rawSubject is Certificate.RawSubject and must be non-empty.
181// The getCert func may be called 0 or more times.
182func (s *CertPool) addCertFunc(rawSum224 sum224, rawSubject string, getCert func() (*Certificate, error)) {
183	if getCert == nil {
184		panic("getCert can't be nil")
185	}
186
187	// Check that the certificate isn't being added twice.
188	if s.haveSum[rawSum224] {
189		return
190	}
191
192	s.haveSum[rawSum224] = true
193	s.lazyCerts = append(s.lazyCerts, lazyCert{
194		rawSubject: []byte(rawSubject),
195		getCert:    getCert,
196	})
197	s.byName[rawSubject] = append(s.byName[rawSubject], len(s.lazyCerts)-1)
198}
199
200// AppendCertsFromPEM attempts to parse a series of PEM encoded certificates.
201// It appends any certificates found to s and reports whether any certificates
202// were successfully parsed.
203//
204// On many Linux systems, /etc/ssl/cert.pem will contain the system wide set
205// of root CAs in a format suitable for this function.
206func (s *CertPool) AppendCertsFromPEM(pemCerts []byte) (ok bool) {
207	for len(pemCerts) > 0 {
208		var block *pem.Block
209		block, pemCerts = pem.Decode(pemCerts)
210		if block == nil {
211			break
212		}
213		if block.Type != "CERTIFICATE" || len(block.Headers) != 0 {
214			continue
215		}
216
217		certBytes := block.Bytes
218		cert, err := ParseCertificate(certBytes)
219		if err != nil {
220			continue
221		}
222		var lazyCert struct {
223			sync.Once
224			v *Certificate
225		}
226		s.addCertFunc(sha256.Sum224(cert.Raw), string(cert.RawSubject), func() (*Certificate, error) {
227			lazyCert.Do(func() {
228				// This can't fail, as the same bytes already parsed above.
229				lazyCert.v, _ = ParseCertificate(certBytes)
230				certBytes = nil
231			})
232			return lazyCert.v, nil
233		})
234		ok = true
235	}
236
237	return ok
238}
239
240// Subjects returns a list of the DER-encoded subjects of
241// all of the certificates in the pool.
242func (s *CertPool) Subjects() [][]byte {
243	res := make([][]byte, s.len())
244	for i, lc := range s.lazyCerts {
245		res[i] = lc.rawSubject
246	}
247	return res
248}
249