cert_pool.mx raw

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