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