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