• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2017 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #ifdef UNSAFE_BUFFERS_BUILD
6 // TODO(crbug.com/40284755): Remove this and spanify to fix the errors.
7 #pragma allow_unsafe_buffers
8 #endif
9 
10 #include "net/cert/internal/trust_store_mac.h"
11 
12 #include <Security/Security.h>
13 
14 #include <map>
15 #include <string_view>
16 #include <vector>
17 
18 #include "base/apple/foundation_util.h"
19 #include "base/apple/osstatus_logging.h"
20 #include "base/apple/scoped_cftyperef.h"
21 #include "base/atomicops.h"
22 #include "base/callback_list.h"
23 #include "base/containers/contains.h"
24 #include "base/containers/flat_map.h"
25 #include "base/containers/to_vector.h"
26 #include "base/functional/bind.h"
27 #include "base/logging.h"
28 #include "base/metrics/histogram_functions.h"
29 #include "base/no_destructor.h"
30 #include "base/numerics/safe_conversions.h"
31 #include "base/strings/strcat.h"
32 #include "base/synchronization/lock.h"
33 #include "base/timer/elapsed_timer.h"
34 #include "crypto/mac_security_services_lock.h"
35 #include "net/base/features.h"
36 #include "net/base/hash_value.h"
37 #include "net/base/network_notification_thread_mac.h"
38 #include "net/cert/internal/platform_trust_store.h"
39 #include "net/cert/test_keychain_search_list_mac.h"
40 #include "net/cert/x509_util.h"
41 #include "net/cert/x509_util_apple.h"
42 #include "third_party/boringssl/src/include/openssl/sha.h"
43 #include "third_party/boringssl/src/pki/cert_errors.h"
44 #include "third_party/boringssl/src/pki/cert_issuer_source_static.h"
45 #include "third_party/boringssl/src/pki/extended_key_usage.h"
46 #include "third_party/boringssl/src/pki/parse_name.h"
47 #include "third_party/boringssl/src/pki/parsed_certificate.h"
48 #include "third_party/boringssl/src/pki/trust_store.h"
49 
50 namespace net {
51 
52 namespace {
53 
54 // The rules for interpreting trust settings are documented at:
55 // https://developer.apple.com/reference/security/1400261-sectrustsettingscopytrustsetting?language=objc
56 
57 // Indicates the trust status of a certificate.
58 enum class TrustStatus {
59   // Trust status is unknown / uninitialized.
60   UNKNOWN,
61   // Certificate inherits trust value from its issuer. If the certificate is the
62   // root of the chain, this implies distrust.
63   UNSPECIFIED,
64   // Certificate is a trust anchor.
65   TRUSTED,
66   // Certificate is blocked / explicitly distrusted.
67   DISTRUSTED
68 };
69 
TrustStatusToCertificateTrust(TrustStatus trust_status)70 bssl::CertificateTrust TrustStatusToCertificateTrust(TrustStatus trust_status) {
71   switch (trust_status) {
72     case TrustStatus::TRUSTED: {
73       // Mac trust settings don't distinguish between trusted anchors and
74       // trusted leafs, return a trust record valid for both, which will
75       // depend on the context the certificate is encountered in.
76       bssl::CertificateTrust trust =
77           bssl::CertificateTrust::ForTrustAnchorOrLeaf()
78               .WithEnforceAnchorExpiry()
79               .WithEnforceAnchorConstraints()
80               .WithRequireAnchorBasicConstraints();
81       return trust;
82     }
83     case TrustStatus::DISTRUSTED:
84       return bssl::CertificateTrust::ForDistrusted();
85     case TrustStatus::UNSPECIFIED:
86       return bssl::CertificateTrust::ForUnspecified();
87     case TrustStatus::UNKNOWN:
88       // UNKNOWN is an implementation detail of TrustImpl and should never be
89       // returned.
90       NOTREACHED();
91   }
92 
93   return bssl::CertificateTrust::ForUnspecified();
94 }
95 
96 // Returns trust status of usage constraints dictionary |trust_dict| for a
97 // certificate that |is_self_issued|.
IsTrustDictionaryTrustedForPolicy(CFDictionaryRef trust_dict,bool is_self_issued,const CFStringRef target_policy_oid)98 TrustStatus IsTrustDictionaryTrustedForPolicy(
99     CFDictionaryRef trust_dict,
100     bool is_self_issued,
101     const CFStringRef target_policy_oid) {
102   crypto::GetMacSecurityServicesLock().AssertAcquired();
103 
104   // An empty trust dict should be interpreted as
105   // kSecTrustSettingsResultTrustRoot. This is handled by falling through all
106   // the conditions below with the default value of |trust_settings_result|.
107 
108   // Trust settings may be scoped to a single application, by checking that the
109   // code signing identity of the current application matches the serialized
110   // code signing identity in the kSecTrustSettingsApplication key.
111   // As this is not presently supported, skip any trust settings scoped to the
112   // application.
113   if (CFDictionaryContainsKey(trust_dict, kSecTrustSettingsApplication))
114     return TrustStatus::UNSPECIFIED;
115 
116   // Trust settings may be scoped using policy-specific constraints. For
117   // example, SSL trust settings might be scoped to a single hostname, or EAP
118   // settings specific to a particular WiFi network.
119   // As this is not presently supported, skip any policy-specific trust
120   // settings.
121   if (CFDictionaryContainsKey(trust_dict, kSecTrustSettingsPolicyString))
122     return TrustStatus::UNSPECIFIED;
123 
124   // Ignoring kSecTrustSettingsKeyUsage for now; it does not seem relevant to
125   // the TLS case.
126 
127   // If the trust settings are scoped to a specific policy (via
128   // kSecTrustSettingsPolicy), ensure that the policy is the same policy as
129   // |target_policy_oid|. If there is no kSecTrustSettingsPolicy key, it's
130   // considered a match for all policies.
131   if (CFDictionaryContainsKey(trust_dict, kSecTrustSettingsPolicy)) {
132     SecPolicyRef policy_ref = base::apple::GetValueFromDictionary<SecPolicyRef>(
133         trust_dict, kSecTrustSettingsPolicy);
134     if (!policy_ref) {
135       return TrustStatus::UNSPECIFIED;
136     }
137     base::apple::ScopedCFTypeRef<CFDictionaryRef> policy_dict(
138         SecPolicyCopyProperties(policy_ref));
139 
140     // kSecPolicyOid is guaranteed to be present in the policy dictionary.
141     CFStringRef policy_oid = base::apple::GetValueFromDictionary<CFStringRef>(
142         policy_dict.get(), kSecPolicyOid);
143 
144     if (!CFEqual(policy_oid, target_policy_oid))
145       return TrustStatus::UNSPECIFIED;
146   }
147 
148   // If kSecTrustSettingsResult is not present in the trust dict,
149   // kSecTrustSettingsResultTrustRoot is assumed.
150   int trust_settings_result = kSecTrustSettingsResultTrustRoot;
151   if (CFDictionaryContainsKey(trust_dict, kSecTrustSettingsResult)) {
152     CFNumberRef trust_settings_result_ref =
153         base::apple::GetValueFromDictionary<CFNumberRef>(
154             trust_dict, kSecTrustSettingsResult);
155     if (!trust_settings_result_ref ||
156         !CFNumberGetValue(trust_settings_result_ref, kCFNumberIntType,
157                           &trust_settings_result)) {
158       return TrustStatus::UNSPECIFIED;
159     }
160   }
161 
162   if (trust_settings_result == kSecTrustSettingsResultDeny)
163     return TrustStatus::DISTRUSTED;
164 
165   // This is a bit of a hack: if the cert is self-issued allow either
166   // kSecTrustSettingsResultTrustRoot or kSecTrustSettingsResultTrustAsRoot on
167   // the basis that SecTrustSetTrustSettings should not allow creating an
168   // invalid trust record in the first place. (The spec is that
169   // kSecTrustSettingsResultTrustRoot can only be applied to root(self-signed)
170   // certs and kSecTrustSettingsResultTrustAsRoot is used for other certs.)
171   // This hack avoids having to check the signature on the cert which is slow
172   // if using the platform APIs, and may require supporting MD5 signature
173   // algorithms on some older OSX versions or locally added roots, which is
174   // undesirable in the built-in signature verifier.
175   if (is_self_issued) {
176     return (trust_settings_result == kSecTrustSettingsResultTrustRoot ||
177             trust_settings_result == kSecTrustSettingsResultTrustAsRoot)
178                ? TrustStatus::TRUSTED
179                : TrustStatus::UNSPECIFIED;
180   }
181 
182   // kSecTrustSettingsResultTrustAsRoot can only be applied to non-root certs.
183   return (trust_settings_result == kSecTrustSettingsResultTrustAsRoot)
184              ? TrustStatus::TRUSTED
185              : TrustStatus::UNSPECIFIED;
186 }
187 
188 // Returns true if the trust settings array |trust_settings| for a certificate
189 // that |is_self_issued| should be treated as a trust anchor.
IsTrustSettingsTrustedForPolicy(CFArrayRef trust_settings,bool is_self_issued,const CFStringRef policy_oid)190 TrustStatus IsTrustSettingsTrustedForPolicy(CFArrayRef trust_settings,
191                                             bool is_self_issued,
192                                             const CFStringRef policy_oid) {
193   // An empty trust settings array (that is, the trust_settings parameter
194   // returns a valid but empty CFArray) means "always trust this certificate"
195   // with an overall trust setting for the certificate of
196   // kSecTrustSettingsResultTrustRoot.
197   if (CFArrayGetCount(trust_settings) == 0) {
198     return is_self_issued ? TrustStatus::TRUSTED : TrustStatus::UNSPECIFIED;
199   }
200 
201   for (CFIndex i = 0, settings_count = CFArrayGetCount(trust_settings);
202        i < settings_count; ++i) {
203     CFDictionaryRef trust_dict = reinterpret_cast<CFDictionaryRef>(
204         const_cast<void*>(CFArrayGetValueAtIndex(trust_settings, i)));
205     TrustStatus trust = IsTrustDictionaryTrustedForPolicy(
206         trust_dict, is_self_issued, policy_oid);
207     if (trust != TrustStatus::UNSPECIFIED)
208       return trust;
209   }
210   return TrustStatus::UNSPECIFIED;
211 }
212 
213 // Returns the trust status for |cert_handle| for the policy |policy_oid| in
214 // |trust_domain|.
IsSecCertificateTrustedForPolicyInDomain(SecCertificateRef cert_handle,const bool is_self_issued,const CFStringRef policy_oid,SecTrustSettingsDomain trust_domain)215 TrustStatus IsSecCertificateTrustedForPolicyInDomain(
216     SecCertificateRef cert_handle,
217     const bool is_self_issued,
218     const CFStringRef policy_oid,
219     SecTrustSettingsDomain trust_domain) {
220   crypto::GetMacSecurityServicesLock().AssertAcquired();
221 
222   base::apple::ScopedCFTypeRef<CFArrayRef> trust_settings;
223   OSStatus err = SecTrustSettingsCopyTrustSettings(
224       cert_handle, trust_domain, trust_settings.InitializeInto());
225 
226   if (err == errSecItemNotFound) {
227     // No trust settings for that domain.. try the next.
228     return TrustStatus::UNSPECIFIED;
229   }
230   if (err) {
231     OSSTATUS_LOG(ERROR, err) << "SecTrustSettingsCopyTrustSettings error";
232     return TrustStatus::UNSPECIFIED;
233   }
234   TrustStatus trust = IsTrustSettingsTrustedForPolicy(
235       trust_settings.get(), is_self_issued, policy_oid);
236   return trust;
237 }
238 
IsCertificateTrustedForPolicyInDomain(const bssl::ParsedCertificate * cert,const CFStringRef policy_oid,SecTrustSettingsDomain trust_domain)239 TrustStatus IsCertificateTrustedForPolicyInDomain(
240     const bssl::ParsedCertificate* cert,
241     const CFStringRef policy_oid,
242     SecTrustSettingsDomain trust_domain) {
243   // TODO(eroman): Inefficient -- path building will convert between
244   // SecCertificateRef and bssl::ParsedCertificate representations multiple
245   // times (when getting the issuers, and again here).
246   //
247   // This conversion will also be done for each domain the cert policy is
248   // checked, but the TrustDomainCache ensures this function is only called on
249   // domains that actually have settings for the cert. The common case is that
250   // a cert will have trust settings in only zero or one domains, and when in
251   // more than one domain it would generally be because one domain is
252   // overriding the setting in the next, so it would only get done once anyway.
253   base::apple::ScopedCFTypeRef<SecCertificateRef> cert_handle =
254       x509_util::CreateSecCertificateFromBytes(cert->der_cert());
255   if (!cert_handle)
256     return TrustStatus::UNSPECIFIED;
257 
258   const bool is_self_issued =
259       cert->normalized_subject() == cert->normalized_issuer();
260 
261   return IsSecCertificateTrustedForPolicyInDomain(
262       cert_handle.get(), is_self_issued, policy_oid, trust_domain);
263 }
264 
IsCertificateTrustedForPolicy(const bssl::ParsedCertificate * cert,SecCertificateRef cert_handle,const CFStringRef policy_oid)265 TrustStatus IsCertificateTrustedForPolicy(const bssl::ParsedCertificate* cert,
266                                           SecCertificateRef cert_handle,
267                                           const CFStringRef policy_oid) {
268   crypto::GetMacSecurityServicesLock().AssertAcquired();
269 
270   const bool is_self_issued =
271       cert->normalized_subject() == cert->normalized_issuer();
272 
273   // Evaluate user trust domain, then admin. User settings can override
274   // admin (and both override the system domain, but we don't check that).
275   for (const auto& trust_domain :
276        {kSecTrustSettingsDomainUser, kSecTrustSettingsDomainAdmin}) {
277     base::apple::ScopedCFTypeRef<CFArrayRef> trust_settings;
278     OSStatus err;
279     err = SecTrustSettingsCopyTrustSettings(cert_handle, trust_domain,
280                                             trust_settings.InitializeInto());
281     if (err != errSecSuccess) {
282       if (err == errSecItemNotFound) {
283         // No trust settings for that domain.. try the next.
284         continue;
285       }
286       OSSTATUS_LOG(ERROR, err) << "SecTrustSettingsCopyTrustSettings error";
287       continue;
288     }
289     TrustStatus trust = IsTrustSettingsTrustedForPolicy(
290         trust_settings.get(), is_self_issued, policy_oid);
291     if (trust != TrustStatus::UNSPECIFIED)
292       return trust;
293   }
294 
295   // No trust settings, or none of the settings were for the correct policy, or
296   // had the correct trust result.
297   return TrustStatus::UNSPECIFIED;
298 }
299 
300 // Returns true if |cert| would never be a valid intermediate. (A return
301 // value of false does not imply that it is valid.) This is an optimization
302 // to avoid using memory for caching certs that would never lead to a valid
303 // chain. It's not intended to exhaustively test everything that
304 // VerifyCertificateChain does, just to filter out some of the most obviously
305 // unusable certs.
IsNotAcceptableIntermediate(const bssl::ParsedCertificate * cert,const CFStringRef policy_oid)306 bool IsNotAcceptableIntermediate(const bssl::ParsedCertificate* cert,
307                                  const CFStringRef policy_oid) {
308   if (!cert->has_basic_constraints() || !cert->basic_constraints().is_ca) {
309     return true;
310   }
311 
312   // EKU filter is only implemented for TLS server auth since that's all we
313   // actually care about.
314   if (cert->has_extended_key_usage() &&
315       CFEqual(policy_oid, kSecPolicyAppleSSL) &&
316       !base::Contains(cert->extended_key_usage(),
317                       bssl::der::Input(bssl::kAnyEKU)) &&
318       !base::Contains(cert->extended_key_usage(),
319                       bssl::der::Input(bssl::kServerAuth))) {
320     return true;
321   }
322 
323   // TODO(mattm): filter on other things too? (key usage, ...?)
324   return false;
325 }
326 
327 // Caches certificates and calculated trust status for certificates present in
328 // a single trust domain.
329 class TrustDomainCacheFullCerts {
330  public:
331   struct TrustStatusDetails {
332     TrustStatus trust_status = TrustStatus::UNKNOWN;
333   };
334 
TrustDomainCacheFullCerts(SecTrustSettingsDomain domain,CFStringRef policy_oid)335   TrustDomainCacheFullCerts(SecTrustSettingsDomain domain,
336                             CFStringRef policy_oid)
337       : domain_(domain), policy_oid_(policy_oid) {
338     DCHECK(policy_oid_);
339   }
340 
341   TrustDomainCacheFullCerts(const TrustDomainCacheFullCerts&) = delete;
342   TrustDomainCacheFullCerts& operator=(const TrustDomainCacheFullCerts&) =
343       delete;
344 
345   // (Re-)Initializes the cache with the certs in |domain_| set to UNKNOWN trust
346   // status.
Initialize()347   void Initialize() {
348     trust_status_cache_.clear();
349     cert_issuer_source_.Clear();
350 
351     base::apple::ScopedCFTypeRef<CFArrayRef> cert_array;
352     OSStatus rv;
353     {
354       base::AutoLock lock(crypto::GetMacSecurityServicesLock());
355       rv = SecTrustSettingsCopyCertificates(domain_,
356                                             cert_array.InitializeInto());
357     }
358     if (rv != noErr) {
359       // Note: SecTrustSettingsCopyCertificates can legitimately return
360       // errSecNoTrustSettings if there are no trust settings in |domain_|.
361       HistogramTrustDomainCertCount(0U);
362       return;
363     }
364     std::vector<std::pair<SHA256HashValue, TrustStatusDetails>>
365         trust_status_vector;
366     for (CFIndex i = 0, size = CFArrayGetCount(cert_array.get()); i < size;
367          ++i) {
368       SecCertificateRef cert = reinterpret_cast<SecCertificateRef>(
369           const_cast<void*>(CFArrayGetValueAtIndex(cert_array.get(), i)));
370       base::apple::ScopedCFTypeRef<CFDataRef> der_data(
371           SecCertificateCopyData(cert));
372       if (!der_data) {
373         LOG(ERROR) << "SecCertificateCopyData error";
374         continue;
375       }
376       auto buffer = x509_util::CreateCryptoBuffer(
377           base::apple::CFDataToSpan(der_data.get()));
378       bssl::CertErrors errors;
379       bssl::ParseCertificateOptions options;
380       options.allow_invalid_serial_numbers = true;
381       std::shared_ptr<const bssl::ParsedCertificate> parsed_cert =
382           bssl::ParsedCertificate::Create(std::move(buffer), options, &errors);
383       if (!parsed_cert) {
384         LOG(ERROR) << "Error parsing certificate:\n" << errors.ToDebugString();
385         continue;
386       }
387       cert_issuer_source_.AddCert(std::move(parsed_cert));
388       trust_status_vector.emplace_back(x509_util::CalculateFingerprint256(cert),
389                                        TrustStatusDetails());
390     }
391     HistogramTrustDomainCertCount(trust_status_vector.size());
392     trust_status_cache_ = base::flat_map<SHA256HashValue, TrustStatusDetails>(
393         std::move(trust_status_vector));
394   }
395 
396   // Returns the trust status for |cert| in |domain_|.
IsCertTrusted(const bssl::ParsedCertificate * cert,const SHA256HashValue & cert_hash)397   TrustStatus IsCertTrusted(const bssl::ParsedCertificate* cert,
398                             const SHA256HashValue& cert_hash) {
399     auto cache_iter = trust_status_cache_.find(cert_hash);
400     if (cache_iter == trust_status_cache_.end()) {
401       // Cert does not have trust settings in this domain, return UNSPECIFIED.
402       return TrustStatus::UNSPECIFIED;
403     }
404 
405     if (cache_iter->second.trust_status != TrustStatus::UNKNOWN) {
406       // Cert has trust settings and trust has already been calculated, return
407       // the cached value.
408       return cache_iter->second.trust_status;
409     }
410 
411     base::AutoLock lock(crypto::GetMacSecurityServicesLock());
412 
413     // Cert has trust settings but trust has not been calculated yet.
414     // Calculate it now, insert into cache, and return.
415     TrustStatus cert_trust =
416         IsCertificateTrustedForPolicyInDomain(cert, policy_oid_, domain_);
417     cache_iter->second.trust_status = cert_trust;
418     return cert_trust;
419   }
420 
421   // Returns true if the certificate with |cert_hash| is present in |domain_|.
ContainsCert(const SHA256HashValue & cert_hash) const422   bool ContainsCert(const SHA256HashValue& cert_hash) const {
423     return trust_status_cache_.find(cert_hash) != trust_status_cache_.end();
424   }
425 
426   // Returns a bssl::CertIssuerSource containing all the certificates that are
427   // present in |domain_|.
cert_issuer_source()428   bssl::CertIssuerSourceStatic& cert_issuer_source() {
429     return cert_issuer_source_;
430   }
431 
432  private:
HistogramTrustDomainCertCount(size_t count) const433   void HistogramTrustDomainCertCount(size_t count) const {
434     std::string_view domain_name;
435     switch (domain_) {
436       case kSecTrustSettingsDomainUser:
437         domain_name = "User";
438         break;
439       case kSecTrustSettingsDomainAdmin:
440         domain_name = "Admin";
441         break;
442       case kSecTrustSettingsDomainSystem:
443         NOTREACHED();
444     }
445     base::UmaHistogramCounts1000(
446         base::StrCat(
447             {"Net.CertVerifier.MacTrustDomainCertCount.", domain_name}),
448         count);
449   }
450 
451   const SecTrustSettingsDomain domain_;
452   const CFStringRef policy_oid_;
453   base::flat_map<SHA256HashValue, TrustStatusDetails> trust_status_cache_;
454   bssl::CertIssuerSourceStatic cert_issuer_source_;
455 };
456 
CalculateFingerprint256(const bssl::der::Input & buffer)457 SHA256HashValue CalculateFingerprint256(const bssl::der::Input& buffer) {
458   SHA256HashValue sha256;
459   SHA256(buffer.data(), buffer.size(), sha256.data);
460   return sha256;
461 }
462 
463 // Watches macOS keychain for |event_mask| notifications, and notifies any
464 // registered callbacks. This is necessary as the keychain callback API is
465 // keyed only on the callback function pointer rather than function pointer +
466 // context, so it cannot be safely registered multiple callbacks with the same
467 // function pointer and different contexts.
468 template <SecKeychainEventMask event_mask>
469 class KeychainChangedNotifier {
470  public:
471   KeychainChangedNotifier(const KeychainChangedNotifier&) = delete;
472   KeychainChangedNotifier& operator=(const KeychainChangedNotifier&) = delete;
473 
474   // Registers |callback| to be run when the keychain trust settings change.
475   // Must be called on the network notification thread.  |callback| will be run
476   // on the network notification thread. The returned subscription must be
477   // destroyed on the network notification thread.
AddCallback(base::RepeatingClosure callback)478   static base::CallbackListSubscription AddCallback(
479       base::RepeatingClosure callback) {
480     DCHECK(GetNetworkNotificationThreadMac()->RunsTasksInCurrentSequence());
481     return Get()->callback_list_.Add(std::move(callback));
482   }
483 
484  private:
485   friend base::NoDestructor<KeychainChangedNotifier>;
486 
487 // Much of the Keychain API was marked deprecated as of the macOS 13 SDK.
488 // Removal of its use is tracked in https://crbug.com/1348251 but deprecation
489 // warnings are disabled in the meanwhile.
490 #pragma clang diagnostic push
491 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
492 
KeychainChangedNotifier()493   KeychainChangedNotifier() {
494     DCHECK(GetNetworkNotificationThreadMac()->RunsTasksInCurrentSequence());
495     OSStatus status =
496         SecKeychainAddCallback(&KeychainChangedNotifier::KeychainCallback,
497                                event_mask, /*context=*/nullptr);
498     if (status != noErr)
499       OSSTATUS_LOG(ERROR, status) << "SecKeychainAddCallback failed";
500   }
501 
502 #pragma clang diagnostic pop
503 
504   ~KeychainChangedNotifier() = delete;
505 
KeychainCallback(SecKeychainEvent keychain_event,SecKeychainCallbackInfo * info,void * context)506   static OSStatus KeychainCallback(SecKeychainEvent keychain_event,
507                                    SecKeychainCallbackInfo* info,
508                                    void* context) {
509     // Since SecKeychainAddCallback is keyed on the function pointer only, we
510     // need to ensure that each template instantiation of this function has a
511     // different address. Calling the static Get() method here to get the
512     // |callback_list_| (rather than passing a |this| pointer through
513     // |context|) should require each instantiation of KeychainCallback to be
514     // unique.
515     Get()->callback_list_.Notify();
516     return errSecSuccess;
517   }
518 
Get()519   static KeychainChangedNotifier* Get() {
520     static base::NoDestructor<KeychainChangedNotifier> notifier;
521     return notifier.get();
522   }
523 
524   base::RepeatingClosureList callback_list_;
525 };
526 
527 // Observes keychain events and increments the value returned by Iteration()
528 // each time an event indicated by |event_mask| is notified.
529 template <SecKeychainEventMask event_mask>
530 class KeychainObserver {
531  public:
KeychainObserver()532   KeychainObserver() {
533     GetNetworkNotificationThreadMac()->PostTask(
534         FROM_HERE,
535         base::BindOnce(&KeychainObserver::RegisterCallbackOnNotificationThread,
536                        base::Unretained(this)));
537   }
538 
539   KeychainObserver(const KeychainObserver&) = delete;
540   KeychainObserver& operator=(const KeychainObserver&) = delete;
541 
542   // Destroying the observer unregisters the callback. Must be destroyed on the
543   // notification thread in order to safely release |subscription_|.
~KeychainObserver()544   ~KeychainObserver() {
545     DCHECK(GetNetworkNotificationThreadMac()->RunsTasksInCurrentSequence());
546   }
547 
548   // Returns the current iteration count, which is incremented every time
549   // keychain trust settings change. This may be called from any thread.
Iteration() const550   int64_t Iteration() const { return base::subtle::Acquire_Load(&iteration_); }
551 
552  private:
RegisterCallbackOnNotificationThread()553   void RegisterCallbackOnNotificationThread() {
554     DCHECK(GetNetworkNotificationThreadMac()->RunsTasksInCurrentSequence());
555     subscription_ =
556         KeychainChangedNotifier<event_mask>::AddCallback(base::BindRepeating(
557             &KeychainObserver::Increment, base::Unretained(this)));
558   }
559 
Increment()560   void Increment() { base::subtle::Barrier_AtomicIncrement(&iteration_, 1); }
561 
562   // Only accessed on the notification thread.
563   base::CallbackListSubscription subscription_;
564 
565   base::subtle::Atomic64 iteration_ = 0;
566 };
567 
568 using KeychainTrustObserver =
569     KeychainObserver<kSecTrustSettingsChangedEventMask>;
570 
571 // kSecDeleteEventMask events could also be checked here, but it's not
572 // necessary for correct behavior. Not including that just means the
573 // intermediates cache might occasionally be a little larger then necessary.
574 // In theory, the kSecAddEvent events could also be filtered to only notify on
575 // events for added certificates as opposed to other keychain objects, however
576 // that requires some fairly nasty CSSM hackery, so we don't do it.
577 using KeychainCertsObserver =
578     KeychainObserver<kSecAddEventMask | kSecKeychainListChangedMask>;
579 
580 using KeychainTrustOrCertsObserver =
581     KeychainObserver<kSecTrustSettingsChangedEventMask | kSecAddEventMask |
582                      kSecKeychainListChangedMask>;
583 
584 }  // namespace
585 
586 
587 // Interface for different implementations of getting trust settings from the
588 // Mac APIs. This abstraction can be removed once a single implementation has
589 // been chosen and launched.
590 class TrustStoreMac::TrustImpl {
591  public:
592   virtual ~TrustImpl() = default;
593 
594   virtual TrustStatus IsCertTrusted(const bssl::ParsedCertificate* cert) = 0;
SyncGetIssuersOf(const bssl::ParsedCertificate * cert,bssl::ParsedCertificateList * issuers)595   virtual void SyncGetIssuersOf(const bssl::ParsedCertificate* cert,
596                                 bssl::ParsedCertificateList* issuers) {}
597   virtual void InitializeTrustCache() = 0;
598   virtual std::vector<PlatformTrustStore::CertWithTrust>
599   GetAllUserAddedCerts() = 0;
600 };
601 
602 // TrustImplDomainCacheFullCerts uses SecTrustSettingsCopyCertificates to get
603 // the list of certs in each trust domain and caches the full certificates so
604 // that pathbuilding does not need to touch any Mac APIs unless one of those
605 // certificates is encountered, at which point the calculated trust status of
606 // that cert is cached. The cache is reset if trust settings are modified.
607 class TrustStoreMac::TrustImplDomainCacheFullCerts
608     : public TrustStoreMac::TrustImpl {
609  public:
TrustImplDomainCacheFullCerts(CFStringRef policy_oid)610   explicit TrustImplDomainCacheFullCerts(CFStringRef policy_oid)
611       // KeyChainObservers must be destroyed on the network notification
612       // thread as they use a non-threadsafe CallbackListSubscription.
613       : keychain_trust_observer_(
614             new KeychainTrustObserver,
615             base::OnTaskRunnerDeleter(GetNetworkNotificationThreadMac())),
616         keychain_certs_observer_(
617             new KeychainCertsObserver,
618             base::OnTaskRunnerDeleter(GetNetworkNotificationThreadMac())),
619         policy_oid_(policy_oid, base::scoped_policy::RETAIN),
620         admin_domain_cache_(kSecTrustSettingsDomainAdmin, policy_oid),
621         user_domain_cache_(kSecTrustSettingsDomainUser, policy_oid) {}
622 
623   TrustImplDomainCacheFullCerts(const TrustImplDomainCacheFullCerts&) = delete;
624   TrustImplDomainCacheFullCerts& operator=(
625       const TrustImplDomainCacheFullCerts&) = delete;
626 
627   // Returns the trust status for |cert|.
IsCertTrusted(const bssl::ParsedCertificate * cert)628   TrustStatus IsCertTrusted(const bssl::ParsedCertificate* cert) override {
629     SHA256HashValue cert_hash = CalculateFingerprint256(cert->der_cert());
630 
631     base::AutoLock lock(cache_lock_);
632     MaybeInitializeCache();
633 
634     return IsCertTrustedImpl(cert, cert_hash);
635   }
636 
IsCertTrustedImpl(const bssl::ParsedCertificate * cert,const SHA256HashValue & cert_hash)637   TrustStatus IsCertTrustedImpl(const bssl::ParsedCertificate* cert,
638                                 const SHA256HashValue& cert_hash)
639       EXCLUSIVE_LOCKS_REQUIRED(cache_lock_) {
640     // Evaluate user trust domain, then admin. User settings can override
641     // admin (and both override the system domain, but we don't check that).
642     for (TrustDomainCacheFullCerts* trust_domain_cache :
643          {&user_domain_cache_, &admin_domain_cache_}) {
644       TrustStatus ts = trust_domain_cache->IsCertTrusted(cert, cert_hash);
645       if (ts != TrustStatus::UNSPECIFIED)
646         return ts;
647     }
648 
649     // Cert did not have trust settings in any domain.
650     return TrustStatus::UNSPECIFIED;
651   }
652 
SyncGetIssuersOf(const bssl::ParsedCertificate * cert,bssl::ParsedCertificateList * issuers)653   void SyncGetIssuersOf(const bssl::ParsedCertificate* cert,
654                         bssl::ParsedCertificateList* issuers) override {
655     base::AutoLock lock(cache_lock_);
656     MaybeInitializeCache();
657     user_domain_cache_.cert_issuer_source().SyncGetIssuersOf(cert, issuers);
658     admin_domain_cache_.cert_issuer_source().SyncGetIssuersOf(cert, issuers);
659     intermediates_cert_issuer_source_.SyncGetIssuersOf(cert, issuers);
660   }
661 
662   // Initializes the cache, if it isn't already initialized.
InitializeTrustCache()663   void InitializeTrustCache() override {
664     base::AutoLock lock(cache_lock_);
665     MaybeInitializeCache();
666   }
667 
GetAllUserAddedCerts()668   std::vector<PlatformTrustStore::CertWithTrust> GetAllUserAddedCerts()
669       override {
670     base::AutoLock lock(cache_lock_);
671     MaybeInitializeCache();
672 
673     std::vector<net::PlatformTrustStore::CertWithTrust> results;
674 
675     // The same cert might be present in both user_domain_cache_ and
676     // admin_domain_cache_ so we need to dedupe and only include each cert in
677     // the results once.
678     std::map<base::span<const uint8_t>,
679              std::shared_ptr<const bssl::ParsedCertificate>>
680         all_trusted_certs;
681     for (auto& cert : user_domain_cache_.cert_issuer_source().Certs()) {
682       all_trusted_certs[cert->der_cert()] = std::move(cert);
683     }
684     for (auto& cert : admin_domain_cache_.cert_issuer_source().Certs()) {
685       all_trusted_certs[cert->der_cert()] = std::move(cert);
686     }
687     for (const auto& [key, cert] : all_trusted_certs) {
688       SHA256HashValue cert_hash = CalculateFingerprint256(cert->der_cert());
689       results.emplace_back(base::ToVector(cert->der_cert()),
690                            TrustStatusToCertificateTrust(
691                                IsCertTrustedImpl(cert.get(), cert_hash)));
692     }
693 
694     // InitializeIntermediatesCache already ensures that certs in the domain
695     // caches are not duplicated in the intemediate cert source, so we don't
696     // need to check for duplicates here.
697     for (const auto& cert : intermediates_cert_issuer_source_.Certs()) {
698       results.emplace_back(base::ToVector(cert->der_cert()),
699                            bssl::CertificateTrust::ForUnspecified());
700     }
701 
702     return results;
703   }
704 
705  private:
706   // (Re-)Initialize the cache if necessary. Must be called after acquiring
707   // |cache_lock_| and before accessing any of the |*_domain_cache_| members.
MaybeInitializeCache()708   void MaybeInitializeCache() EXCLUSIVE_LOCKS_REQUIRED(cache_lock_) {
709     cache_lock_.AssertAcquired();
710 
711     const int64_t keychain_trust_iteration =
712         keychain_trust_observer_->Iteration();
713     const bool trust_changed = trust_iteration_ != keychain_trust_iteration;
714     base::ElapsedTimer trust_domain_cache_init_timer;
715     if (trust_changed) {
716       trust_iteration_ = keychain_trust_iteration;
717       user_domain_cache_.Initialize();
718       admin_domain_cache_.Initialize();
719       base::UmaHistogramMediumTimes(
720           "Net.CertVerifier.MacTrustDomainCacheInitTime",
721           trust_domain_cache_init_timer.Elapsed());
722     }
723 
724     const int64_t keychain_certs_iteration =
725         keychain_certs_observer_->Iteration();
726     const bool certs_changed = certs_iteration_ != keychain_certs_iteration;
727     // Intermediates cache is updated on trust changes too, since the
728     // intermediates cache is exclusive of any certs in trust domain caches.
729     if (trust_changed || certs_changed) {
730       certs_iteration_ = keychain_certs_iteration;
731       InitializeIntermediatesCache();
732     }
733     if (trust_changed) {
734       // Histogram of total init time for the case where both the trust cache
735       // and intermediates cache were updated.
736       base::UmaHistogramMediumTimes(
737           "Net.CertVerifier.MacTrustImplCacheInitTime",
738           trust_domain_cache_init_timer.Elapsed());
739     }
740   }
741 
InitializeIntermediatesCache()742   void InitializeIntermediatesCache() EXCLUSIVE_LOCKS_REQUIRED(cache_lock_) {
743     cache_lock_.AssertAcquired();
744 
745     base::ElapsedTimer timer;
746 
747     intermediates_cert_issuer_source_.Clear();
748 
749     base::apple::ScopedCFTypeRef<CFMutableDictionaryRef> query(
750         CFDictionaryCreateMutable(nullptr, 0, &kCFTypeDictionaryKeyCallBacks,
751                                   &kCFTypeDictionaryValueCallBacks));
752 
753     CFDictionarySetValue(query.get(), kSecClass, kSecClassCertificate);
754     CFDictionarySetValue(query.get(), kSecReturnRef, kCFBooleanTrue);
755     CFDictionarySetValue(query.get(), kSecMatchLimit, kSecMatchLimitAll);
756 
757     base::AutoLock lock(crypto::GetMacSecurityServicesLock());
758 
759     base::apple::ScopedCFTypeRef<CFArrayRef>
760         scoped_alternate_keychain_search_list;
761     if (TestKeychainSearchList::HasInstance()) {
762       OSStatus status = TestKeychainSearchList::GetInstance()->CopySearchList(
763           scoped_alternate_keychain_search_list.InitializeInto());
764       if (status) {
765         OSSTATUS_LOG(ERROR, status)
766             << "TestKeychainSearchList::CopySearchList error";
767         return;
768       }
769       CFDictionarySetValue(query.get(), kSecMatchSearchList,
770                            scoped_alternate_keychain_search_list.get());
771     }
772 
773     base::apple::ScopedCFTypeRef<CFTypeRef> matching_items;
774     OSStatus err =
775         SecItemCopyMatching(query.get(), matching_items.InitializeInto());
776     if (err == errSecItemNotFound) {
777       RecordCachedIntermediatesHistograms(0, timer.Elapsed());
778       // No matches found.
779       return;
780     }
781     if (err) {
782       RecordCachedIntermediatesHistograms(0, timer.Elapsed());
783       OSSTATUS_LOG(ERROR, err) << "SecItemCopyMatching error";
784       return;
785     }
786     CFArrayRef matching_items_array =
787         base::apple::CFCastStrict<CFArrayRef>(matching_items.get());
788     for (CFIndex i = 0, item_count = CFArrayGetCount(matching_items_array);
789          i < item_count; ++i) {
790       SecCertificateRef match_cert_handle =
791           base::apple::CFCastStrict<SecCertificateRef>(
792               CFArrayGetValueAtIndex(matching_items_array, i));
793 
794       // If cert is already in the trust domain certs cache, don't bother
795       // including it in the intermediates cache.
796       SHA256HashValue cert_hash =
797           x509_util::CalculateFingerprint256(match_cert_handle);
798       if (user_domain_cache_.ContainsCert(cert_hash) ||
799           admin_domain_cache_.ContainsCert(cert_hash)) {
800         continue;
801       }
802 
803       base::apple::ScopedCFTypeRef<CFDataRef> der_data(
804           SecCertificateCopyData(match_cert_handle));
805       if (!der_data) {
806         LOG(ERROR) << "SecCertificateCopyData error";
807         continue;
808       }
809       auto buffer = x509_util::CreateCryptoBuffer(
810           base::apple::CFDataToSpan(der_data.get()));
811       bssl::CertErrors errors;
812       bssl::ParseCertificateOptions options;
813       options.allow_invalid_serial_numbers = true;
814       std::shared_ptr<const bssl::ParsedCertificate> parsed_cert =
815           bssl::ParsedCertificate::Create(std::move(buffer), options, &errors);
816       if (!parsed_cert) {
817         LOG(ERROR) << "Error parsing certificate:\n" << errors.ToDebugString();
818         continue;
819       }
820       if (IsNotAcceptableIntermediate(parsed_cert.get(), policy_oid_.get())) {
821         continue;
822       }
823       intermediates_cert_issuer_source_.AddCert(std::move(parsed_cert));
824     }
825     RecordCachedIntermediatesHistograms(CFArrayGetCount(matching_items_array),
826                                         timer.Elapsed());
827   }
828 
RecordCachedIntermediatesHistograms(CFIndex total_cert_count,base::TimeDelta cache_init_time) const829   void RecordCachedIntermediatesHistograms(CFIndex total_cert_count,
830                                            base::TimeDelta cache_init_time)
831       const EXCLUSIVE_LOCKS_REQUIRED(cache_lock_) {
832     cache_lock_.AssertAcquired();
833     base::UmaHistogramMediumTimes(
834         "Net.CertVerifier.MacKeychainCerts.IntermediateCacheInitTime",
835         cache_init_time);
836     base::UmaHistogramCounts1000("Net.CertVerifier.MacKeychainCerts.TotalCount",
837                                  total_cert_count);
838     base::UmaHistogramCounts1000(
839         "Net.CertVerifier.MacKeychainCerts.IntermediateCount",
840         intermediates_cert_issuer_source_.size());
841   }
842 
843   const std::unique_ptr<KeychainTrustObserver, base::OnTaskRunnerDeleter>
844       keychain_trust_observer_;
845   const std::unique_ptr<KeychainCertsObserver, base::OnTaskRunnerDeleter>
846       keychain_certs_observer_;
847   const base::apple::ScopedCFTypeRef<CFStringRef> policy_oid_;
848 
849   base::Lock cache_lock_;
850   // |cache_lock_| must be held while accessing any following members.
851   int64_t trust_iteration_ GUARDED_BY(cache_lock_) = -1;
852   int64_t certs_iteration_ GUARDED_BY(cache_lock_) = -1;
853 
854   TrustDomainCacheFullCerts admin_domain_cache_ GUARDED_BY(cache_lock_);
855   TrustDomainCacheFullCerts user_domain_cache_ GUARDED_BY(cache_lock_);
856 
857   bssl::CertIssuerSourceStatic intermediates_cert_issuer_source_
858       GUARDED_BY(cache_lock_);
859 };
860 
861 // TrustImplKeychainCacheFullCerts uses SecItemCopyMatching to get the list of
862 // all user and admin added certificates, then checks each to see if has trust
863 // settings. Certs will be cached if they are trusted or are potentially valid
864 // intermediates.
865 class TrustStoreMac::TrustImplKeychainCacheFullCerts
866     : public TrustStoreMac::TrustImpl {
867  public:
TrustImplKeychainCacheFullCerts(CFStringRef policy_oid)868   explicit TrustImplKeychainCacheFullCerts(CFStringRef policy_oid)
869       : keychain_observer_(
870             new KeychainTrustOrCertsObserver,
871             // KeyChainObserver must be destroyed on the network notification
872             // thread as it uses a non-threadsafe CallbackListSubscription.
873             base::OnTaskRunnerDeleter(GetNetworkNotificationThreadMac())),
874         policy_oid_(policy_oid, base::scoped_policy::RETAIN) {}
875 
876   TrustImplKeychainCacheFullCerts(const TrustImplKeychainCacheFullCerts&) =
877       delete;
878   TrustImplKeychainCacheFullCerts& operator=(
879       const TrustImplKeychainCacheFullCerts&) = delete;
880 
IsCertTrusted(const bssl::ParsedCertificate * cert)881   TrustStatus IsCertTrusted(const bssl::ParsedCertificate* cert) override {
882     SHA256HashValue cert_hash = CalculateFingerprint256(cert->der_cert());
883 
884     base::AutoLock lock(cache_lock_);
885     MaybeInitializeCache();
886 
887     return IsCertTrustedImpl(cert_hash);
888   }
889 
IsCertTrustedImpl(const SHA256HashValue & cert_hash)890   TrustStatus IsCertTrustedImpl(const SHA256HashValue& cert_hash)
891       EXCLUSIVE_LOCKS_REQUIRED(cache_lock_) {
892     auto cache_iter = trust_status_cache_.find(cert_hash);
893     if (cache_iter == trust_status_cache_.end())
894       return TrustStatus::UNSPECIFIED;
895     return cache_iter->second;
896   }
897 
SyncGetIssuersOf(const bssl::ParsedCertificate * cert,bssl::ParsedCertificateList * issuers)898   void SyncGetIssuersOf(const bssl::ParsedCertificate* cert,
899                         bssl::ParsedCertificateList* issuers) override {
900     base::AutoLock lock(cache_lock_);
901     MaybeInitializeCache();
902     cert_issuer_source_.SyncGetIssuersOf(cert, issuers);
903   }
904 
905   // Initializes the cache, if it isn't already initialized.
InitializeTrustCache()906   void InitializeTrustCache() override {
907     base::AutoLock lock(cache_lock_);
908     MaybeInitializeCache();
909   }
910 
GetAllUserAddedCerts()911   std::vector<PlatformTrustStore::CertWithTrust> GetAllUserAddedCerts()
912       override {
913     base::AutoLock lock(cache_lock_);
914     MaybeInitializeCache();
915 
916     std::vector<net::PlatformTrustStore::CertWithTrust> results;
917     for (const auto& cert : cert_issuer_source_.Certs()) {
918       SHA256HashValue cert_hash = CalculateFingerprint256(cert->der_cert());
919       results.emplace_back(
920           base::ToVector(cert->der_cert()),
921           TrustStatusToCertificateTrust(IsCertTrustedImpl(cert_hash)));
922     }
923     return results;
924   }
925 
926  private:
927   // (Re-)Initialize the cache if necessary. Must be called after acquiring
928   // |cache_lock_| and before accessing any of the |*_domain_cache_| members.
MaybeInitializeCache()929   void MaybeInitializeCache() EXCLUSIVE_LOCKS_REQUIRED(cache_lock_) {
930     cache_lock_.AssertAcquired();
931 
932     const int64_t keychain_iteration = keychain_observer_->Iteration();
933     const bool keychain_changed = keychain_iteration_ != keychain_iteration;
934     if (!keychain_changed)
935       return;
936     keychain_iteration_ = keychain_iteration;
937 
938     base::ElapsedTimer timer;
939 
940     trust_status_cache_.clear();
941     cert_issuer_source_.Clear();
942 
943     base::apple::ScopedCFTypeRef<CFMutableDictionaryRef> query(
944         CFDictionaryCreateMutable(nullptr, 0, &kCFTypeDictionaryKeyCallBacks,
945                                   &kCFTypeDictionaryValueCallBacks));
946 
947     CFDictionarySetValue(query.get(), kSecClass, kSecClassCertificate);
948     CFDictionarySetValue(query.get(), kSecReturnRef, kCFBooleanTrue);
949     CFDictionarySetValue(query.get(), kSecMatchLimit, kSecMatchLimitAll);
950 
951     base::AutoLock lock(crypto::GetMacSecurityServicesLock());
952 
953     base::apple::ScopedCFTypeRef<CFArrayRef>
954         scoped_alternate_keychain_search_list;
955     if (TestKeychainSearchList::HasInstance()) {
956       OSStatus status = TestKeychainSearchList::GetInstance()->CopySearchList(
957           scoped_alternate_keychain_search_list.InitializeInto());
958       if (status) {
959         OSSTATUS_LOG(ERROR, status)
960             << "TestKeychainSearchList::CopySearchList error";
961         return;
962       }
963       CFDictionarySetValue(query.get(), kSecMatchSearchList,
964                            scoped_alternate_keychain_search_list.get());
965     }
966 
967     base::apple::ScopedCFTypeRef<CFTypeRef> matching_items;
968     OSStatus err =
969         SecItemCopyMatching(query.get(), matching_items.InitializeInto());
970     if (err == errSecItemNotFound) {
971       RecordHistograms(0, timer.Elapsed());
972       // No matches found.
973       return;
974     }
975     if (err) {
976       RecordHistograms(0, timer.Elapsed());
977       OSSTATUS_LOG(ERROR, err) << "SecItemCopyMatching error";
978       return;
979     }
980     CFArrayRef matching_items_array =
981         base::apple::CFCastStrict<CFArrayRef>(matching_items.get());
982     std::vector<std::pair<SHA256HashValue, TrustStatus>> trust_status_vector;
983     for (CFIndex i = 0, item_count = CFArrayGetCount(matching_items_array);
984          i < item_count; ++i) {
985       SecCertificateRef sec_cert = base::apple::CFCastStrict<SecCertificateRef>(
986           CFArrayGetValueAtIndex(matching_items_array, i));
987 
988       base::apple::ScopedCFTypeRef<CFDataRef> der_data(
989           SecCertificateCopyData(sec_cert));
990       if (!der_data) {
991         LOG(ERROR) << "SecCertificateCopyData error";
992         continue;
993       }
994       auto buffer = x509_util::CreateCryptoBuffer(
995           base::apple::CFDataToSpan(der_data.get()));
996       bssl::CertErrors errors;
997       bssl::ParseCertificateOptions options;
998       options.allow_invalid_serial_numbers = true;
999       std::shared_ptr<const bssl::ParsedCertificate> parsed_cert =
1000           bssl::ParsedCertificate::Create(std::move(buffer), options, &errors);
1001       if (!parsed_cert) {
1002         LOG(ERROR) << "Error parsing certificate:\n" << errors.ToDebugString();
1003         continue;
1004       }
1005 
1006       TrustStatus trust_status = IsCertificateTrustedForPolicy(
1007           parsed_cert.get(), sec_cert, policy_oid_.get());
1008 
1009       if (trust_status == TrustStatus::TRUSTED ||
1010           trust_status == TrustStatus::DISTRUSTED) {
1011         trust_status_vector.emplace_back(
1012             X509Certificate::CalculateFingerprint256(
1013                 parsed_cert->cert_buffer()),
1014             trust_status);
1015         cert_issuer_source_.AddCert(std::move(parsed_cert));
1016         continue;
1017       }
1018 
1019       if (IsNotAcceptableIntermediate(parsed_cert.get(), policy_oid_.get())) {
1020         continue;
1021       }
1022       cert_issuer_source_.AddCert(std::move(parsed_cert));
1023     }
1024     trust_status_cache_ = base::flat_map<SHA256HashValue, TrustStatus>(
1025         std::move(trust_status_vector));
1026     RecordHistograms(CFArrayGetCount(matching_items_array), timer.Elapsed());
1027   }
1028 
RecordHistograms(CFIndex total_cert_count,base::TimeDelta init_time) const1029   void RecordHistograms(CFIndex total_cert_count,
1030                         base::TimeDelta init_time) const
1031       EXCLUSIVE_LOCKS_REQUIRED(cache_lock_) {
1032     cache_lock_.AssertAcquired();
1033     base::UmaHistogramMediumTimes("Net.CertVerifier.MacTrustImplCacheInitTime",
1034                                   init_time);
1035     base::UmaHistogramCounts1000("Net.CertVerifier.MacKeychainCerts.TotalCount",
1036                                  total_cert_count);
1037     base::UmaHistogramCounts1000(
1038         "Net.CertVerifier.MacKeychainCerts.IntermediateCount",
1039         cert_issuer_source_.size() - trust_status_cache_.size());
1040     base::UmaHistogramCounts1000("Net.CertVerifier.MacKeychainCerts.TrustCount",
1041                                  trust_status_cache_.size());
1042   }
1043 
1044   const std::unique_ptr<KeychainTrustOrCertsObserver, base::OnTaskRunnerDeleter>
1045       keychain_observer_;
1046   const base::apple::ScopedCFTypeRef<CFStringRef> policy_oid_;
1047 
1048   base::Lock cache_lock_;
1049   // |cache_lock_| must be held while accessing any following members.
1050   int64_t keychain_iteration_ GUARDED_BY(cache_lock_) = -1;
1051   base::flat_map<SHA256HashValue, TrustStatus> trust_status_cache_
1052       GUARDED_BY(cache_lock_);
1053   bssl::CertIssuerSourceStatic cert_issuer_source_ GUARDED_BY(cache_lock_);
1054 };
1055 
TrustStoreMac(CFStringRef policy_oid,TrustImplType impl)1056 TrustStoreMac::TrustStoreMac(CFStringRef policy_oid, TrustImplType impl) {
1057   switch (impl) {
1058     case TrustImplType::kUnknown:
1059       DCHECK(false);
1060       break;
1061     case TrustImplType::kDomainCacheFullCerts:
1062       trust_cache_ =
1063           std::make_unique<TrustImplDomainCacheFullCerts>(policy_oid);
1064       break;
1065     case TrustImplType::kKeychainCacheFullCerts:
1066       trust_cache_ =
1067           std::make_unique<TrustImplKeychainCacheFullCerts>(policy_oid);
1068       break;
1069   }
1070 }
1071 
1072 TrustStoreMac::~TrustStoreMac() = default;
1073 
InitializeTrustCache() const1074 void TrustStoreMac::InitializeTrustCache() const {
1075   trust_cache_->InitializeTrustCache();
1076 }
1077 
SyncGetIssuersOf(const bssl::ParsedCertificate * cert,bssl::ParsedCertificateList * issuers)1078 void TrustStoreMac::SyncGetIssuersOf(const bssl::ParsedCertificate* cert,
1079                                      bssl::ParsedCertificateList* issuers) {
1080   trust_cache_->SyncGetIssuersOf(cert, issuers);
1081 }
1082 
GetTrust(const bssl::ParsedCertificate * cert)1083 bssl::CertificateTrust TrustStoreMac::GetTrust(
1084     const bssl::ParsedCertificate* cert) {
1085   return TrustStatusToCertificateTrust(trust_cache_->IsCertTrusted(cert));
1086 }
1087 
1088 std::vector<PlatformTrustStore::CertWithTrust>
GetAllUserAddedCerts()1089 TrustStoreMac::GetAllUserAddedCerts() {
1090   return trust_cache_->GetAllUserAddedCerts();
1091 }
1092 
1093 }  // namespace net
1094