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