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