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