blob: e58e82c7d24036c25806d82125fb2052a8f429df [file] [log] [blame]
Avi Drissman64595482022-09-14 20:52:291// Copyright 2017 The Chromium Authors
mattmea4ed8232017-02-28 23:13:232// 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
Matt Mueller5d105e72019-07-01 17:38:429#include "base/atomicops.h"
Matt Mueller5d105e72019-07-01 17:38:4210#include "base/callback_list.h"
Matt Mueller394c6502022-09-21 06:22:4111#include "base/containers/contains.h"
Matt Mueller5d105e72019-07-01 17:38:4212#include "base/containers/flat_map.h"
Avi Drissman41c4a412023-01-11 22:45:3713#include "base/functional/bind.h"
mattmea4ed8232017-02-28 23:13:2314#include "base/logging.h"
15#include "base/mac/foundation_util.h"
16#include "base/mac/mac_logging.h"
Matt Mueller880337f2022-06-15 04:25:1517#include "base/metrics/histogram_functions.h"
Matt Mueller5d105e72019-07-01 17:38:4218#include "base/no_destructor.h"
Matt Mueller880337f2022-06-15 04:25:1519#include "base/strings/strcat.h"
mattmea4ed8232017-02-28 23:13:2320#include "base/synchronization/lock.h"
Matt Mueller394c6502022-09-21 06:22:4121#include "base/timer/elapsed_timer.h"
mattmea4ed8232017-02-28 23:13:2322#include "crypto/mac_security_services_lock.h"
Matt Mueller5d105e72019-07-01 17:38:4223#include "net/base/hash_value.h"
24#include "net/base/network_notification_thread_mac.h"
Bob Beckb71444c2022-07-19 16:06:2625#include "net/cert/pki/cert_errors.h"
26#include "net/cert/pki/cert_issuer_source_static.h"
Matt Mueller394c6502022-09-21 06:22:4127#include "net/cert/pki/extended_key_usage.h"
Bob Beckb71444c2022-07-19 16:06:2628#include "net/cert/pki/parse_name.h"
29#include "net/cert/pki/parsed_certificate.h"
mattmea4ed8232017-02-28 23:13:2330#include "net/cert/test_keychain_search_list_mac.h"
mattmea4ed8232017-02-28 23:13:2331#include "net/cert/x509_util.h"
David Benjamin33a59b1c2022-05-25 23:40:3332#include "net/cert/x509_util_apple.h"
Matt Mueller5d105e72019-07-01 17:38:4233#include "third_party/boringssl/src/include/openssl/sha.h"
mattmea4ed8232017-02-28 23:13:2334
35namespace net {
36
37namespace {
38
39// The rules for interpreting trust settings are documented at:
40// https://developer.apple.com/reference/security/1400261-sectrustsettingscopytrustsetting?language=objc
41
42// Indicates the trust status of a certificate.
43enum class TrustStatus {
Matt Mueller5d105e72019-07-01 17:38:4244 // Trust status is unknown / uninitialized.
45 UNKNOWN,
mattmea4ed8232017-02-28 23:13:2346 // Certificate inherits trust value from its issuer. If the certificate is the
47 // root of the chain, this implies distrust.
48 UNSPECIFIED,
49 // Certificate is a trust anchor.
50 TRUSTED,
Ryan Sleevia9d6aa62019-07-26 13:32:1851 // Certificate is blocked / explicitly distrusted.
mattmea4ed8232017-02-28 23:13:2352 DISTRUSTED
53};
54
Matt Mueller370a7fda2019-08-22 00:38:5955const void* kResultDebugDataKey = &kResultDebugDataKey;
56
mattmea4ed8232017-02-28 23:13:2357// Returns trust status of usage constraints dictionary |trust_dict| for a
Matt Mueller59ebd7d2019-04-19 17:56:4058// certificate that |is_self_issued|.
mattmea4ed8232017-02-28 23:13:2359TrustStatus IsTrustDictionaryTrustedForPolicy(
60 CFDictionaryRef trust_dict,
Matt Mueller59ebd7d2019-04-19 17:56:4061 bool is_self_issued,
Matt Mueller370a7fda2019-08-22 00:38:5962 const CFStringRef target_policy_oid,
63 int* debug_info) {
mattmea4ed8232017-02-28 23:13:2364 // An empty trust dict should be interpreted as
65 // kSecTrustSettingsResultTrustRoot. This is handled by falling through all
66 // the conditions below with the default value of |trust_settings_result|.
Matt Mueller370a7fda2019-08-22 00:38:5967 CFIndex dict_size = CFDictionaryGetCount(trust_dict);
68 if (dict_size == 0)
69 *debug_info |= TrustStoreMac::TRUST_SETTINGS_DICT_EMPTY;
70
71 CFIndex known_elements = 0;
72 if (CFDictionaryContainsKey(trust_dict, kSecTrustSettingsPolicy)) {
73 *debug_info |= TrustStoreMac::TRUST_SETTINGS_DICT_CONTAINS_POLICY;
74 known_elements++;
75 }
76 if (CFDictionaryContainsKey(trust_dict, kSecTrustSettingsApplication)) {
77 *debug_info |= TrustStoreMac::TRUST_SETTINGS_DICT_CONTAINS_APPLICATION;
78 known_elements++;
79 }
80 if (CFDictionaryContainsKey(trust_dict, kSecTrustSettingsPolicyString)) {
81 *debug_info |= TrustStoreMac::TRUST_SETTINGS_DICT_CONTAINS_POLICY_STRING;
82 known_elements++;
83 }
84 if (CFDictionaryContainsKey(trust_dict, kSecTrustSettingsKeyUsage)) {
85 *debug_info |= TrustStoreMac::TRUST_SETTINGS_DICT_CONTAINS_KEY_USAGE;
86 known_elements++;
87 }
88 if (CFDictionaryContainsKey(trust_dict, kSecTrustSettingsResult)) {
89 *debug_info |= TrustStoreMac::TRUST_SETTINGS_DICT_CONTAINS_RESULT;
90 known_elements++;
91 }
92 if (CFDictionaryContainsKey(trust_dict, kSecTrustSettingsAllowedError)) {
93 *debug_info |= TrustStoreMac::TRUST_SETTINGS_DICT_CONTAINS_ALLOWED_ERROR;
94 known_elements++;
95 }
96 if (known_elements != dict_size)
97 *debug_info |= TrustStoreMac::TRUST_SETTINGS_DICT_UNKNOWN_KEY;
mattmea4ed8232017-02-28 23:13:2398
99 // Trust settings may be scoped to a single application, by checking that the
100 // code signing identity of the current application matches the serialized
101 // code signing identity in the kSecTrustSettingsApplication key.
102 // As this is not presently supported, skip any trust settings scoped to the
103 // application.
104 if (CFDictionaryContainsKey(trust_dict, kSecTrustSettingsApplication))
105 return TrustStatus::UNSPECIFIED;
106
107 // Trust settings may be scoped using policy-specific constraints. For
108 // example, SSL trust settings might be scoped to a single hostname, or EAP
109 // settings specific to a particular WiFi network.
110 // As this is not presently supported, skip any policy-specific trust
111 // settings.
112 if (CFDictionaryContainsKey(trust_dict, kSecTrustSettingsPolicyString))
113 return TrustStatus::UNSPECIFIED;
114
115 // Ignoring kSecTrustSettingsKeyUsage for now; it does not seem relevant to
116 // the TLS case.
117
118 // If the trust settings are scoped to a specific policy (via
119 // kSecTrustSettingsPolicy), ensure that the policy is the same policy as
120 // |target_policy_oid|. If there is no kSecTrustSettingsPolicy key, it's
121 // considered a match for all policies.
Matt Mueller370a7fda2019-08-22 00:38:59122 if (CFDictionaryContainsKey(trust_dict, kSecTrustSettingsPolicy)) {
123 SecPolicyRef policy_ref = base::mac::GetValueFromDictionary<SecPolicyRef>(
124 trust_dict, kSecTrustSettingsPolicy);
125 if (!policy_ref) {
126 *debug_info |= TrustStoreMac::TRUST_SETTINGS_DICT_INVALID_POLICY_TYPE;
127 return TrustStatus::UNSPECIFIED;
128 }
mattmea4ed8232017-02-28 23:13:23129 base::ScopedCFTypeRef<CFDictionaryRef> policy_dict;
130 {
131 base::AutoLock lock(crypto::GetMacSecurityServicesLock());
132 policy_dict.reset(SecPolicyCopyProperties(policy_ref));
133 }
134
135 // kSecPolicyOid is guaranteed to be present in the policy dictionary.
mattmea4ed8232017-02-28 23:13:23136 CFStringRef policy_oid = base::mac::GetValueFromDictionary<CFStringRef>(
Avi Drissman905bac72020-10-24 17:26:31137 policy_dict, kSecPolicyOid);
mattmea4ed8232017-02-28 23:13:23138
139 if (!CFEqual(policy_oid, target_policy_oid))
140 return TrustStatus::UNSPECIFIED;
141 }
142
143 // If kSecTrustSettingsResult is not present in the trust dict,
144 // kSecTrustSettingsResultTrustRoot is assumed.
145 int trust_settings_result = kSecTrustSettingsResultTrustRoot;
Matt Mueller370a7fda2019-08-22 00:38:59146 if (CFDictionaryContainsKey(trust_dict, kSecTrustSettingsResult)) {
147 CFNumberRef trust_settings_result_ref =
148 base::mac::GetValueFromDictionary<CFNumberRef>(trust_dict,
149 kSecTrustSettingsResult);
150 if (!trust_settings_result_ref ||
151 !CFNumberGetValue(trust_settings_result_ref, kCFNumberIntType,
152 &trust_settings_result)) {
153 *debug_info |= TrustStoreMac::TRUST_SETTINGS_DICT_INVALID_RESULT_TYPE;
154 return TrustStatus::UNSPECIFIED;
155 }
mattmea4ed8232017-02-28 23:13:23156 }
157
158 if (trust_settings_result == kSecTrustSettingsResultDeny)
159 return TrustStatus::DISTRUSTED;
160
Matt Mueller59ebd7d2019-04-19 17:56:40161 // This is a bit of a hack: if the cert is self-issued allow either
162 // kSecTrustSettingsResultTrustRoot or kSecTrustSettingsResultTrustAsRoot on
163 // the basis that SecTrustSetTrustSettings should not allow creating an
164 // invalid trust record in the first place. (The spec is that
mattmea4ed8232017-02-28 23:13:23165 // kSecTrustSettingsResultTrustRoot can only be applied to root(self-signed)
Matt Mueller59ebd7d2019-04-19 17:56:40166 // certs and kSecTrustSettingsResultTrustAsRoot is used for other certs.)
167 // This hack avoids having to check the signature on the cert which is slow
168 // if using the platform APIs, and may require supporting MD5 signature
169 // algorithms on some older OSX versions or locally added roots, which is
170 // undesirable in the built-in signature verifier.
171 if (is_self_issued) {
172 return (trust_settings_result == kSecTrustSettingsResultTrustRoot ||
173 trust_settings_result == kSecTrustSettingsResultTrustAsRoot)
mattmea4ed8232017-02-28 23:13:23174 ? TrustStatus::TRUSTED
175 : TrustStatus::UNSPECIFIED;
Matt Mueller59ebd7d2019-04-19 17:56:40176 }
mattmea4ed8232017-02-28 23:13:23177
178 // kSecTrustSettingsResultTrustAsRoot can only be applied to non-root certs.
179 return (trust_settings_result == kSecTrustSettingsResultTrustAsRoot)
180 ? TrustStatus::TRUSTED
181 : TrustStatus::UNSPECIFIED;
182}
183
184// Returns true if the trust settings array |trust_settings| for a certificate
Matt Mueller59ebd7d2019-04-19 17:56:40185// that |is_self_issued| should be treated as a trust anchor.
mattmea4ed8232017-02-28 23:13:23186TrustStatus IsTrustSettingsTrustedForPolicy(CFArrayRef trust_settings,
Matt Mueller59ebd7d2019-04-19 17:56:40187 bool is_self_issued,
Matt Mueller370a7fda2019-08-22 00:38:59188 const CFStringRef policy_oid,
189 int* debug_info) {
mattmea4ed8232017-02-28 23:13:23190 // An empty trust settings array (that is, the trust_settings parameter
191 // returns a valid but empty CFArray) means "always trust this certificate"
192 // with an overall trust setting for the certificate of
193 // kSecTrustSettingsResultTrustRoot.
Matt Mueller370a7fda2019-08-22 00:38:59194 if (CFArrayGetCount(trust_settings) == 0) {
195 *debug_info |= TrustStoreMac::TRUST_SETTINGS_ARRAY_EMPTY;
196 return is_self_issued ? TrustStatus::TRUSTED : TrustStatus::UNSPECIFIED;
197 }
mattmea4ed8232017-02-28 23:13:23198
199 for (CFIndex i = 0, settings_count = CFArrayGetCount(trust_settings);
200 i < settings_count; ++i) {
201 CFDictionaryRef trust_dict = reinterpret_cast<CFDictionaryRef>(
202 const_cast<void*>(CFArrayGetValueAtIndex(trust_settings, i)));
203 TrustStatus trust = IsTrustDictionaryTrustedForPolicy(
Matt Mueller370a7fda2019-08-22 00:38:59204 trust_dict, is_self_issued, policy_oid, debug_info);
mattmea4ed8232017-02-28 23:13:23205 if (trust != TrustStatus::UNSPECIFIED)
206 return trust;
207 }
208 return TrustStatus::UNSPECIFIED;
209}
210
Matt Mueller5d105e72019-07-01 17:38:42211// Returns the trust status for |cert_handle| for the policy |policy_oid| in
212// |trust_domain|.
Matt Mueller1b4c2342021-03-16 17:57:04213TrustStatus IsSecCertificateTrustedForPolicyInDomain(
214 SecCertificateRef cert_handle,
215 const bool is_self_issued,
216 const CFStringRef policy_oid,
217 SecTrustSettingsDomain trust_domain,
218 int* debug_info) {
219 base::ScopedCFTypeRef<CFArrayRef> trust_settings;
220 OSStatus err;
221 {
222 base::AutoLock lock(crypto::GetMacSecurityServicesLock());
223 err = SecTrustSettingsCopyTrustSettings(cert_handle, trust_domain,
224 trust_settings.InitializeInto());
225 }
226 if (err == errSecItemNotFound) {
227 // No trust settings for that domain.. try the next.
228 return TrustStatus::UNSPECIFIED;
229 }
230 if (err) {
231 OSSTATUS_LOG(ERROR, err) << "SecTrustSettingsCopyTrustSettings error";
Matt Muellerb87777a2021-09-07 18:16:07232 *debug_info |= TrustStoreMac::COPY_TRUST_SETTINGS_ERROR;
Matt Mueller1b4c2342021-03-16 17:57:04233 return TrustStatus::UNSPECIFIED;
234 }
235 TrustStatus trust = IsTrustSettingsTrustedForPolicy(
236 trust_settings, is_self_issued, policy_oid, debug_info);
237 return trust;
238}
239
Matt Mueller5d105e72019-07-01 17:38:42240TrustStatus IsCertificateTrustedForPolicyInDomain(
David Benjamin24381632021-09-03 17:01:45241 const ParsedCertificate* cert,
Matt Mueller5d105e72019-07-01 17:38:42242 const CFStringRef policy_oid,
Matt Mueller370a7fda2019-08-22 00:38:59243 SecTrustSettingsDomain trust_domain,
244 int* debug_info) {
Matt Mueller5d105e72019-07-01 17:38:42245 // TODO(eroman): Inefficient -- path building will convert between
246 // SecCertificateRef and ParsedCertificate representations multiple times
247 // (when getting the issuers, and again here).
248 //
249 // This conversion will also be done for each domain the cert policy is
250 // checked, but the TrustDomainCache ensures this function is only called on
251 // domains that actually have settings for the cert. The common case is that
252 // a cert will have trust settings in only zero or one domains, and when in
253 // more than one domain it would generally be because one domain is
254 // overriding the setting in the next, so it would only get done once anyway.
255 base::ScopedCFTypeRef<SecCertificateRef> cert_handle =
256 x509_util::CreateSecCertificateFromBytes(cert->der_cert().UnsafeData(),
257 cert->der_cert().Length());
258 if (!cert_handle)
259 return TrustStatus::UNSPECIFIED;
260
Matt Mueller59ebd7d2019-04-19 17:56:40261 const bool is_self_issued =
262 cert->normalized_subject() == cert->normalized_issuer();
Matt Mueller5d105e72019-07-01 17:38:42263
Matt Mueller1b4c2342021-03-16 17:57:04264 return IsSecCertificateTrustedForPolicyInDomain(
265 cert_handle, is_self_issued, policy_oid, trust_domain, debug_info);
Matt Mueller5d105e72019-07-01 17:38:42266}
267
Matt Mueller7ac4af5c2021-03-30 18:58:53268TrustStatus IsCertificateTrustedForPolicy(const ParsedCertificate* cert,
Matt Mueller55822f82022-12-01 23:11:32269 SecCertificateRef cert_handle,
Matt Mueller7ac4af5c2021-03-30 18:58:53270 const CFStringRef policy_oid,
Matt Muellerae5071512022-10-12 18:25:35271 int* debug_info) {
Matt Mueller55822f82022-12-01 23:11:32272 crypto::GetMacSecurityServicesLock().AssertAcquired();
Matt Mueller1b4c2342021-03-16 17:57:04273
274 const bool is_self_issued =
275 cert->normalized_subject() == cert->normalized_issuer();
276
Matt Muellerae5071512022-10-12 18:25:35277 // Evaluate user trust domain, then admin. User settings can override
278 // admin (and both override the system domain, but we don't check that).
Matt Mueller1b4c2342021-03-16 17:57:04279 for (const auto& trust_domain :
Matt Muellerae5071512022-10-12 18:25:35280 {kSecTrustSettingsDomainUser, kSecTrustSettingsDomainAdmin}) {
Matt Mueller1b4c2342021-03-16 17:57:04281 base::ScopedCFTypeRef<CFArrayRef> trust_settings;
282 OSStatus err;
Matt Mueller55822f82022-12-01 23:11:32283 err = SecTrustSettingsCopyTrustSettings(cert_handle, trust_domain,
284 trust_settings.InitializeInto());
Matt Mueller7ac4af5c2021-03-30 18:58:53285 if (err != errSecSuccess) {
Matt Mueller7ac4af5c2021-03-30 18:58:53286 if (err == errSecItemNotFound) {
287 // No trust settings for that domain.. try the next.
288 continue;
289 }
Matt Mueller1b4c2342021-03-16 17:57:04290 OSSTATUS_LOG(ERROR, err) << "SecTrustSettingsCopyTrustSettings error";
Matt Muellerb87777a2021-09-07 18:16:07291 *debug_info |= TrustStoreMac::COPY_TRUST_SETTINGS_ERROR;
Matt Mueller1b4c2342021-03-16 17:57:04292 continue;
293 }
294 TrustStatus trust = IsTrustSettingsTrustedForPolicy(
295 trust_settings, is_self_issued, policy_oid, debug_info);
296 if (trust != TrustStatus::UNSPECIFIED)
297 return trust;
298 }
299
300 // No trust settings, or none of the settings were for the correct policy, or
301 // had the correct trust result.
302 return TrustStatus::UNSPECIFIED;
303}
304
Matt Mueller55822f82022-12-01 23:11:32305TrustStatus IsCertificateTrustedForPolicy(const ParsedCertificate* cert,
306 const CFStringRef policy_oid,
307 int* debug_info) {
308 base::ScopedCFTypeRef<SecCertificateRef> cert_handle =
309 x509_util::CreateSecCertificateFromBytes(cert->der_cert().UnsafeData(),
310 cert->der_cert().Length());
311
312 if (!cert_handle)
313 return TrustStatus::UNSPECIFIED;
314
315 return IsCertificateTrustedForPolicy(cert, cert_handle, policy_oid,
316 debug_info);
317}
318
Matt Mueller1b4c2342021-03-16 17:57:04319void UpdateUserData(int debug_info,
320 base::SupportsUserData* user_data,
321 TrustStoreMac::TrustImplType impl_type) {
Matt Mueller370a7fda2019-08-22 00:38:59322 if (!user_data)
323 return;
324 TrustStoreMac::ResultDebugData* result_debug_data =
325 TrustStoreMac::ResultDebugData::GetOrCreate(user_data);
Matt Mueller1b4c2342021-03-16 17:57:04326 result_debug_data->UpdateTrustDebugInfo(debug_info, impl_type);
Matt Mueller370a7fda2019-08-22 00:38:59327}
328
Matt Mueller55822f82022-12-01 23:11:32329// Returns true if |cert| would never be a valid intermediate. (A return
330// value of false does not imply that it is valid.) This is an optimization
331// to avoid using memory for caching certs that would never lead to a valid
332// chain. It's not intended to exhaustively test everything that
333// VerifyCertificateChain does, just to filter out some of the most obviously
334// unusable certs.
Bob Beck03509d282022-12-07 21:49:05335bool IsNotAcceptableIntermediate(const ParsedCertificate* cert,
Matt Mueller55822f82022-12-01 23:11:32336 const CFStringRef policy_oid) {
337 if (!cert->has_basic_constraints() || !cert->basic_constraints().is_ca) {
338 return true;
339 }
340
341 // EKU filter is only implemented for TLS server auth since that's all we
342 // actually care about.
343 if (cert->has_extended_key_usage() &&
344 CFEqual(policy_oid, kSecPolicyAppleSSL) &&
345 !base::Contains(cert->extended_key_usage(), der::Input(kAnyEKU)) &&
346 !base::Contains(cert->extended_key_usage(), der::Input(kServerAuth))) {
347 return true;
348 }
349
350 // TODO(mattm): filter on other things too? (key usage, ...?)
351 return false;
352}
353
Matt Mueller880337f2022-06-15 04:25:15354// Caches certificates and calculated trust status for certificates present in
355// a single trust domain.
356class TrustDomainCacheFullCerts {
357 public:
358 struct TrustStatusDetails {
359 TrustStatus trust_status = TrustStatus::UNKNOWN;
360 int debug_info = 0;
361 };
362
363 TrustDomainCacheFullCerts(SecTrustSettingsDomain domain,
364 CFStringRef policy_oid)
365 : domain_(domain), policy_oid_(policy_oid) {
366 DCHECK(policy_oid_);
367 }
368
369 TrustDomainCacheFullCerts(const TrustDomainCacheFullCerts&) = delete;
370 TrustDomainCacheFullCerts& operator=(const TrustDomainCacheFullCerts&) =
371 delete;
372
373 // (Re-)Initializes the cache with the certs in |domain_| set to UNKNOWN trust
374 // status.
375 void Initialize() {
376 trust_status_cache_.clear();
377 cert_issuer_source_.Clear();
378
379 base::ScopedCFTypeRef<CFArrayRef> cert_array;
380 OSStatus rv;
381 {
382 base::AutoLock lock(crypto::GetMacSecurityServicesLock());
383 rv = SecTrustSettingsCopyCertificates(domain_,
384 cert_array.InitializeInto());
385 }
386 if (rv != noErr) {
387 // Note: SecTrustSettingsCopyCertificates can legitimately return
388 // errSecNoTrustSettings if there are no trust settings in |domain_|.
389 HistogramTrustDomainCertCount(0U);
390 return;
391 }
392 std::vector<std::pair<SHA256HashValue, TrustStatusDetails>>
393 trust_status_vector;
394 for (CFIndex i = 0, size = CFArrayGetCount(cert_array); i < size; ++i) {
395 SecCertificateRef cert = reinterpret_cast<SecCertificateRef>(
396 const_cast<void*>(CFArrayGetValueAtIndex(cert_array, i)));
397 base::ScopedCFTypeRef<CFDataRef> der_data(SecCertificateCopyData(cert));
398 if (!der_data) {
399 LOG(ERROR) << "SecCertificateCopyData error";
400 continue;
401 }
402 auto buffer = x509_util::CreateCryptoBuffer(base::make_span(
403 CFDataGetBytePtr(der_data.get()), CFDataGetLength(der_data.get())));
404 CertErrors errors;
405 ParseCertificateOptions options;
406 options.allow_invalid_serial_numbers = true;
Bob Beck03509d282022-12-07 21:49:05407 std::shared_ptr<const ParsedCertificate> parsed_cert =
Matt Mueller880337f2022-06-15 04:25:15408 ParsedCertificate::Create(std::move(buffer), options, &errors);
409 if (!parsed_cert) {
410 LOG(ERROR) << "Error parsing certificate:\n" << errors.ToDebugString();
411 continue;
412 }
413 cert_issuer_source_.AddCert(parsed_cert);
414 trust_status_vector.emplace_back(x509_util::CalculateFingerprint256(cert),
415 TrustStatusDetails());
416 }
417 HistogramTrustDomainCertCount(trust_status_vector.size());
418 trust_status_cache_ = base::flat_map<SHA256HashValue, TrustStatusDetails>(
419 std::move(trust_status_vector));
420 }
421
422 // Returns the trust status for |cert| in |domain_|.
423 TrustStatus IsCertTrusted(const ParsedCertificate* cert,
424 const SHA256HashValue& cert_hash,
425 base::SupportsUserData* debug_data) {
426 auto cache_iter = trust_status_cache_.find(cert_hash);
427 if (cache_iter == trust_status_cache_.end()) {
428 // Cert does not have trust settings in this domain, return UNSPECIFIED.
429 UpdateUserData(0, debug_data,
430 TrustStoreMac::TrustImplType::kDomainCacheFullCerts);
431 return TrustStatus::UNSPECIFIED;
432 }
433
434 if (cache_iter->second.trust_status != TrustStatus::UNKNOWN) {
435 // Cert has trust settings and trust has already been calculated, return
436 // the cached value.
437 UpdateUserData(cache_iter->second.debug_info, debug_data,
438 TrustStoreMac::TrustImplType::kDomainCacheFullCerts);
439 return cache_iter->second.trust_status;
440 }
441
442 // Cert has trust settings but trust has not been calculated yet.
443 // Calculate it now, insert into cache, and return.
444 TrustStatus cert_trust = IsCertificateTrustedForPolicyInDomain(
445 cert, policy_oid_, domain_, &cache_iter->second.debug_info);
446 cache_iter->second.trust_status = cert_trust;
447 UpdateUserData(cache_iter->second.debug_info, debug_data,
448 TrustStoreMac::TrustImplType::kDomainCacheFullCerts);
449 return cert_trust;
450 }
451
452 // Returns true if the certificate with |cert_hash| is present in |domain_|.
453 bool ContainsCert(const SHA256HashValue& cert_hash) const {
454 return trust_status_cache_.find(cert_hash) != trust_status_cache_.end();
455 }
456
457 // Returns a CertIssuerSource containing all the certificates that are
458 // present in |domain_|.
459 CertIssuerSource& cert_issuer_source() { return cert_issuer_source_; }
460
461 private:
462 void HistogramTrustDomainCertCount(size_t count) const {
463 base::StringPiece domain_name;
464 switch (domain_) {
465 case kSecTrustSettingsDomainUser:
466 domain_name = "User";
467 break;
468 case kSecTrustSettingsDomainAdmin:
469 domain_name = "Admin";
470 break;
471 case kSecTrustSettingsDomainSystem:
Matt Muellerae5071512022-10-12 18:25:35472 NOTREACHED();
Matt Mueller880337f2022-06-15 04:25:15473 break;
474 }
475 base::UmaHistogramCounts1000(
476 base::StrCat(
477 {"Net.CertVerifier.MacTrustDomainCertCount.", domain_name}),
478 count);
479 }
480
481 const SecTrustSettingsDomain domain_;
482 const CFStringRef policy_oid_;
483 base::flat_map<SHA256HashValue, TrustStatusDetails> trust_status_cache_;
484 CertIssuerSourceStatic cert_issuer_source_;
485};
486
Matt Mueller5d105e72019-07-01 17:38:42487SHA256HashValue CalculateFingerprint256(const der::Input& buffer) {
488 SHA256HashValue sha256;
489 SHA256(buffer.UnsafeData(), buffer.Length(), sha256.data);
490 return sha256;
mattmea4ed8232017-02-28 23:13:23491}
492
Matt Mueller394c6502022-09-21 06:22:41493// Watches macOS keychain for |event_mask| notifications, and notifies any
Matt Mueller5d105e72019-07-01 17:38:42494// registered callbacks. This is necessary as the keychain callback API is
495// keyed only on the callback function pointer rather than function pointer +
496// context, so it cannot be safely registered multiple callbacks with the same
497// function pointer and different contexts.
Matt Mueller394c6502022-09-21 06:22:41498template <SecKeychainEventMask event_mask>
499class KeychainChangedNotifier {
Matt Mueller5d105e72019-07-01 17:38:42500 public:
Matt Mueller394c6502022-09-21 06:22:41501 KeychainChangedNotifier(const KeychainChangedNotifier&) = delete;
502 KeychainChangedNotifier& operator=(const KeychainChangedNotifier&) = delete;
Peter Boström407869b2021-10-07 04:42:48503
Matt Mueller5d105e72019-07-01 17:38:42504 // Registers |callback| to be run when the keychain trust settings change.
505 // Must be called on the network notification thread. |callback| will be run
Peter Kasting7ba9440c2020-11-22 01:49:02506 // on the network notification thread. The returned subscription must be
Matt Mueller5d105e72019-07-01 17:38:42507 // destroyed on the network notification thread.
Peter Kasting7ba9440c2020-11-22 01:49:02508 static base::CallbackListSubscription AddCallback(
Matt Mueller5d105e72019-07-01 17:38:42509 base::RepeatingClosure callback) {
510 DCHECK(GetNetworkNotificationThreadMac()->RunsTasksInCurrentSequence());
511 return Get()->callback_list_.Add(std::move(callback));
512 }
513
514 private:
Matt Mueller394c6502022-09-21 06:22:41515 friend base::NoDestructor<KeychainChangedNotifier>;
Matt Mueller5d105e72019-07-01 17:38:42516
Avi Drissman38e3a532022-08-05 16:23:44517// Much of the Keychain API was marked deprecated as of the macOS 13 SDK.
518// Removal of its use is tracked in https://crbug.com/1348251 but deprecation
519// warnings are disabled in the meanwhile.
520#pragma clang diagnostic push
521#pragma clang diagnostic ignored "-Wdeprecated-declarations"
522
Matt Mueller394c6502022-09-21 06:22:41523 KeychainChangedNotifier() {
Matt Mueller5d105e72019-07-01 17:38:42524 DCHECK(GetNetworkNotificationThreadMac()->RunsTasksInCurrentSequence());
Matt Mueller92821e8d2022-09-22 16:28:47525 OSStatus status =
526 SecKeychainAddCallback(&KeychainChangedNotifier::KeychainCallback,
527 event_mask, /*context=*/nullptr);
Matt Mueller5d105e72019-07-01 17:38:42528 if (status != noErr)
529 OSSTATUS_LOG(ERROR, status) << "SecKeychainAddCallback failed";
530 }
531
Avi Drissman38e3a532022-08-05 16:23:44532#pragma clang diagnostic pop
533
Matt Mueller394c6502022-09-21 06:22:41534 ~KeychainChangedNotifier() = delete;
Matt Mueller5d105e72019-07-01 17:38:42535
536 static OSStatus KeychainCallback(SecKeychainEvent keychain_event,
537 SecKeychainCallbackInfo* info,
538 void* context) {
Matt Mueller92821e8d2022-09-22 16:28:47539 // Since SecKeychainAddCallback is keyed on the function pointer only, we
540 // need to ensure that each template instantiation of this function has a
541 // different address. Calling the static Get() method here to get the
542 // |callback_list_| (rather than passing a |this| pointer through
543 // |context|) should require each instantiation of KeychainCallback to be
544 // unique.
545 Get()->callback_list_.Notify();
Matt Mueller5d105e72019-07-01 17:38:42546 return errSecSuccess;
547 }
548
Matt Mueller394c6502022-09-21 06:22:41549 static KeychainChangedNotifier* Get() {
550 static base::NoDestructor<KeychainChangedNotifier> notifier;
Matt Mueller5d105e72019-07-01 17:38:42551 return notifier.get();
552 }
553
Peter Kasting11c507a42020-08-17 19:11:10554 base::RepeatingClosureList callback_list_;
Matt Mueller5d105e72019-07-01 17:38:42555};
556
557// Observes keychain events and increments the value returned by Iteration()
Matt Mueller394c6502022-09-21 06:22:41558// each time an event indicated by |event_mask| is notified.
559template <SecKeychainEventMask event_mask>
560class KeychainObserver {
Matt Mueller5d105e72019-07-01 17:38:42561 public:
Matt Mueller394c6502022-09-21 06:22:41562 KeychainObserver() {
Matt Mueller5d105e72019-07-01 17:38:42563 GetNetworkNotificationThreadMac()->PostTask(
564 FROM_HERE,
Matt Mueller394c6502022-09-21 06:22:41565 base::BindOnce(&KeychainObserver::RegisterCallbackOnNotificationThread,
566 base::Unretained(this)));
Matt Mueller5d105e72019-07-01 17:38:42567 }
568
Matt Mueller394c6502022-09-21 06:22:41569 KeychainObserver(const KeychainObserver&) = delete;
570 KeychainObserver& operator=(const KeychainObserver&) = delete;
Peter Boström293b1342021-09-22 17:31:43571
Matt Mueller5d105e72019-07-01 17:38:42572 // Destroying the observer unregisters the callback. Must be destroyed on the
573 // notification thread in order to safely release |subscription_|.
Matt Mueller394c6502022-09-21 06:22:41574 ~KeychainObserver() {
Matt Mueller5d105e72019-07-01 17:38:42575 DCHECK(GetNetworkNotificationThreadMac()->RunsTasksInCurrentSequence());
576 }
577
578 // Returns the current iteration count, which is incremented every time
579 // keychain trust settings change. This may be called from any thread.
580 int64_t Iteration() const { return base::subtle::Acquire_Load(&iteration_); }
581
582 private:
583 void RegisterCallbackOnNotificationThread() {
584 DCHECK(GetNetworkNotificationThreadMac()->RunsTasksInCurrentSequence());
585 subscription_ =
Matt Mueller394c6502022-09-21 06:22:41586 KeychainChangedNotifier<event_mask>::AddCallback(base::BindRepeating(
587 &KeychainObserver::Increment, base::Unretained(this)));
Matt Mueller5d105e72019-07-01 17:38:42588 }
589
590 void Increment() { base::subtle::Barrier_AtomicIncrement(&iteration_, 1); }
591
592 // Only accessed on the notification thread.
Peter Kasting7ba9440c2020-11-22 01:49:02593 base::CallbackListSubscription subscription_;
Matt Mueller5d105e72019-07-01 17:38:42594
595 base::subtle::Atomic64 iteration_ = 0;
Matt Mueller5d105e72019-07-01 17:38:42596};
597
Matt Mueller394c6502022-09-21 06:22:41598using KeychainTrustObserver =
599 KeychainObserver<kSecTrustSettingsChangedEventMask>;
600
601// kSecDeleteEventMask events could also be checked here, but it's not
602// necessary for correct behavior. Not including that just means the
603// intermediates cache might occasionally be a little larger then necessary.
604// In theory, the kSecAddEvent events could also be filtered to only notify on
605// events for added certificates as opposed to other keychain objects, however
606// that requires some fairly nasty CSSM hackery, so we don't do it.
607using KeychainCertsObserver =
608 KeychainObserver<kSecAddEventMask | kSecKeychainListChangedMask>;
609
Matt Mueller55822f82022-12-01 23:11:32610using KeychainTrustOrCertsObserver =
611 KeychainObserver<kSecTrustSettingsChangedEventMask | kSecAddEventMask |
612 kSecKeychainListChangedMask>;
613
eroman2a938c32017-04-28 23:16:58614} // namespace
615
Matt Mueller370a7fda2019-08-22 00:38:59616// static
617const TrustStoreMac::ResultDebugData* TrustStoreMac::ResultDebugData::Get(
618 const base::SupportsUserData* debug_data) {
619 return static_cast<ResultDebugData*>(
620 debug_data->GetUserData(kResultDebugDataKey));
621}
622
623// static
624TrustStoreMac::ResultDebugData* TrustStoreMac::ResultDebugData::GetOrCreate(
625 base::SupportsUserData* debug_data) {
626 ResultDebugData* data = static_cast<ResultDebugData*>(
627 debug_data->GetUserData(kResultDebugDataKey));
628 if (!data) {
629 std::unique_ptr<ResultDebugData> new_data =
630 std::make_unique<ResultDebugData>();
631 data = new_data.get();
632 debug_data->SetUserData(kResultDebugDataKey, std::move(new_data));
633 }
634 return data;
635}
636
637void TrustStoreMac::ResultDebugData::UpdateTrustDebugInfo(
Matt Mueller1b4c2342021-03-16 17:57:04638 int trust_debug_info,
639 TrustImplType impl_type) {
Matt Mueller370a7fda2019-08-22 00:38:59640 combined_trust_debug_info_ |= trust_debug_info;
Matt Mueller1b4c2342021-03-16 17:57:04641 trust_impl_ = impl_type;
Matt Mueller370a7fda2019-08-22 00:38:59642}
643
644std::unique_ptr<base::SupportsUserData::Data>
645TrustStoreMac::ResultDebugData::Clone() {
646 return std::make_unique<ResultDebugData>(*this);
647}
648
Matt Mueller1b4c2342021-03-16 17:57:04649// Interface for different implementations of getting trust settings from the
650// Mac APIs. This abstraction can be removed once a single implementation has
651// been chosen and launched.
652class TrustStoreMac::TrustImpl {
Matt Mueller5d105e72019-07-01 17:38:42653 public:
Matt Mueller1b4c2342021-03-16 17:57:04654 virtual ~TrustImpl() = default;
655
David Benjamin24381632021-09-03 17:01:45656 virtual TrustStatus IsCertTrusted(const ParsedCertificate* cert,
657 base::SupportsUserData* debug_data) = 0;
Matt Mueller880337f2022-06-15 04:25:15658 virtual bool ImplementsSyncGetIssuersOf() const { return false; }
659 virtual void SyncGetIssuersOf(const ParsedCertificate* cert,
660 ParsedCertificateList* issuers) {}
Matt Mueller1b4c2342021-03-16 17:57:04661 virtual void InitializeTrustCache() = 0;
662};
663
Matt Mueller880337f2022-06-15 04:25:15664// TrustImplDomainCacheFullCerts uses SecTrustSettingsCopyCertificates to get
665// the list of certs in each trust domain and caches the full certificates so
666// that pathbuilding does not need to touch any Mac APIs unless one of those
667// certificates is encountered, at which point the calculated trust status of
668// that cert is cached. The cache is reset if trust settings are modified.
669class TrustStoreMac::TrustImplDomainCacheFullCerts
670 : public TrustStoreMac::TrustImpl {
671 public:
Matt Muellerae5071512022-10-12 18:25:35672 explicit TrustImplDomainCacheFullCerts(CFStringRef policy_oid)
Matt Mueller55822f82022-12-01 23:11:32673 // KeyChainObservers must be destroyed on the network notification
674 // thread as they use a non-threadsafe CallbackListSubscription.
675 : keychain_trust_observer_(
676 new KeychainTrustObserver,
677 base::OnTaskRunnerDeleter(GetNetworkNotificationThreadMac())),
678 keychain_certs_observer_(
679 new KeychainCertsObserver,
680 base::OnTaskRunnerDeleter(GetNetworkNotificationThreadMac())),
681 policy_oid_(policy_oid, base::scoped_policy::RETAIN),
Matt Mueller880337f2022-06-15 04:25:15682 admin_domain_cache_(kSecTrustSettingsDomainAdmin, policy_oid),
Matt Mueller55822f82022-12-01 23:11:32683 user_domain_cache_(kSecTrustSettingsDomainUser, policy_oid) {}
Matt Mueller880337f2022-06-15 04:25:15684
685 TrustImplDomainCacheFullCerts(const TrustImplDomainCacheFullCerts&) = delete;
686 TrustImplDomainCacheFullCerts& operator=(
687 const TrustImplDomainCacheFullCerts&) = delete;
688
Matt Mueller880337f2022-06-15 04:25:15689 // Returns the trust status for |cert|.
690 TrustStatus IsCertTrusted(const ParsedCertificate* cert,
691 base::SupportsUserData* debug_data) override {
692 SHA256HashValue cert_hash = CalculateFingerprint256(cert->der_cert());
693
694 base::AutoLock lock(cache_lock_);
695 MaybeInitializeCache();
696
Matt Muellerae5071512022-10-12 18:25:35697 // Evaluate user trust domain, then admin. User settings can override
698 // admin (and both override the system domain, but we don't check that).
Matt Mueller880337f2022-06-15 04:25:15699 for (TrustDomainCacheFullCerts* trust_domain_cache :
700 {&user_domain_cache_, &admin_domain_cache_}) {
701 TrustStatus ts =
702 trust_domain_cache->IsCertTrusted(cert, cert_hash, debug_data);
703 if (ts != TrustStatus::UNSPECIFIED)
704 return ts;
705 }
Matt Mueller880337f2022-06-15 04:25:15706
707 // Cert did not have trust settings in any domain.
708 return TrustStatus::UNSPECIFIED;
709 }
710
711 bool ImplementsSyncGetIssuersOf() const override { return true; }
712
713 void SyncGetIssuersOf(const ParsedCertificate* cert,
714 ParsedCertificateList* issuers) override {
715 base::AutoLock lock(cache_lock_);
716 MaybeInitializeCache();
717 user_domain_cache_.cert_issuer_source().SyncGetIssuersOf(cert, issuers);
718 admin_domain_cache_.cert_issuer_source().SyncGetIssuersOf(cert, issuers);
Matt Mueller394c6502022-09-21 06:22:41719 intermediates_cert_issuer_source_.SyncGetIssuersOf(cert, issuers);
Matt Mueller880337f2022-06-15 04:25:15720 }
721
722 // Initializes the cache, if it isn't already initialized.
723 void InitializeTrustCache() override {
724 base::AutoLock lock(cache_lock_);
725 MaybeInitializeCache();
726 }
727
728 private:
729 // (Re-)Initialize the cache if necessary. Must be called after acquiring
730 // |cache_lock_| and before accessing any of the |*_domain_cache_| members.
731 void MaybeInitializeCache() EXCLUSIVE_LOCKS_REQUIRED(cache_lock_) {
732 cache_lock_.AssertAcquired();
Matt Mueller880337f2022-06-15 04:25:15733
Matt Mueller92821e8d2022-09-22 16:28:47734 const int64_t keychain_trust_iteration =
735 keychain_trust_observer_->Iteration();
736 const bool trust_changed = trust_iteration_ != keychain_trust_iteration;
Matt Mueller55822f82022-12-01 23:11:32737 base::ElapsedTimer trust_domain_cache_init_timer;
Matt Mueller92821e8d2022-09-22 16:28:47738 if (trust_changed) {
Matt Mueller394c6502022-09-21 06:22:41739 trust_iteration_ = keychain_trust_iteration;
740 user_domain_cache_.Initialize();
741 admin_domain_cache_.Initialize();
Matt Mueller394c6502022-09-21 06:22:41742 base::UmaHistogramMediumTimes(
743 "Net.CertVerifier.MacTrustDomainCacheInitTime",
744 trust_domain_cache_init_timer.Elapsed());
745 }
746
Matt Mueller92821e8d2022-09-22 16:28:47747 const int64_t keychain_certs_iteration =
748 keychain_certs_observer_->Iteration();
749 const bool certs_changed = certs_iteration_ != keychain_certs_iteration;
Matt Mueller394c6502022-09-21 06:22:41750 // Intermediates cache is updated on trust changes too, since the
751 // intermediates cache is exclusive of any certs in trust domain caches.
Matt Mueller92821e8d2022-09-22 16:28:47752 if (trust_changed || certs_changed) {
Matt Mueller394c6502022-09-21 06:22:41753 certs_iteration_ = keychain_certs_iteration;
754 IntializeIntermediatesCache();
Matt Mueller880337f2022-06-15 04:25:15755 }
Matt Mueller55822f82022-12-01 23:11:32756 if (trust_changed) {
757 // Histogram of total init time for the case where both the trust cache
758 // and intermediates cache were updated.
759 base::UmaHistogramMediumTimes(
760 "Net.CertVerifier.MacTrustImplCacheInitTime",
761 trust_domain_cache_init_timer.Elapsed());
762 }
Matt Mueller880337f2022-06-15 04:25:15763 }
764
Matt Mueller394c6502022-09-21 06:22:41765 void IntializeIntermediatesCache() EXCLUSIVE_LOCKS_REQUIRED(cache_lock_) {
766 cache_lock_.AssertAcquired();
767
768 base::ElapsedTimer timer;
769
770 intermediates_cert_issuer_source_.Clear();
771
772 base::ScopedCFTypeRef<CFMutableDictionaryRef> query(
773 CFDictionaryCreateMutable(nullptr, 0, &kCFTypeDictionaryKeyCallBacks,
774 &kCFTypeDictionaryValueCallBacks));
775
776 CFDictionarySetValue(query, kSecClass, kSecClassCertificate);
777 CFDictionarySetValue(query, kSecReturnRef, kCFBooleanTrue);
778 CFDictionarySetValue(query, kSecMatchLimit, kSecMatchLimitAll);
779
780 base::AutoLock lock(crypto::GetMacSecurityServicesLock());
781
782 base::ScopedCFTypeRef<CFArrayRef> scoped_alternate_keychain_search_list;
783 if (TestKeychainSearchList::HasInstance()) {
784 OSStatus status = TestKeychainSearchList::GetInstance()->CopySearchList(
785 scoped_alternate_keychain_search_list.InitializeInto());
786 if (status) {
787 OSSTATUS_LOG(ERROR, status)
788 << "TestKeychainSearchList::CopySearchList error";
789 return;
790 }
791 CFDictionarySetValue(query, kSecMatchSearchList,
792 scoped_alternate_keychain_search_list.get());
793 }
794
795 base::ScopedCFTypeRef<CFTypeRef> matching_items;
796 OSStatus err = SecItemCopyMatching(query, matching_items.InitializeInto());
797 if (err == errSecItemNotFound) {
798 RecordCachedIntermediatesHistograms(0, timer.Elapsed());
799 // No matches found.
800 return;
801 }
802 if (err) {
803 RecordCachedIntermediatesHistograms(0, timer.Elapsed());
804 OSSTATUS_LOG(ERROR, err) << "SecItemCopyMatching error";
805 return;
806 }
807 CFArrayRef matching_items_array =
808 base::mac::CFCastStrict<CFArrayRef>(matching_items);
809 for (CFIndex i = 0, item_count = CFArrayGetCount(matching_items_array);
810 i < item_count; ++i) {
811 SecCertificateRef match_cert_handle =
812 base::mac::CFCastStrict<SecCertificateRef>(
813 CFArrayGetValueAtIndex(matching_items_array, i));
814
815 // If cert is already in the trust domain certs cache, don't bother
816 // including it in the intermediates cache.
817 SHA256HashValue cert_hash =
818 x509_util::CalculateFingerprint256(match_cert_handle);
819 if (user_domain_cache_.ContainsCert(cert_hash) ||
Matt Muellerae5071512022-10-12 18:25:35820 admin_domain_cache_.ContainsCert(cert_hash)) {
Matt Mueller394c6502022-09-21 06:22:41821 continue;
822 }
823
824 base::ScopedCFTypeRef<CFDataRef> der_data(
825 SecCertificateCopyData(match_cert_handle));
826 if (!der_data) {
827 LOG(ERROR) << "SecCertificateCopyData error";
828 continue;
829 }
830 auto buffer = x509_util::CreateCryptoBuffer(base::make_span(
831 CFDataGetBytePtr(der_data.get()), CFDataGetLength(der_data.get())));
832 CertErrors errors;
833 ParseCertificateOptions options;
834 options.allow_invalid_serial_numbers = true;
Bob Beck03509d282022-12-07 21:49:05835 std::shared_ptr<const ParsedCertificate> parsed_cert =
Matt Mueller394c6502022-09-21 06:22:41836 ParsedCertificate::Create(std::move(buffer), options, &errors);
837 if (!parsed_cert) {
838 LOG(ERROR) << "Error parsing certificate:\n" << errors.ToDebugString();
839 continue;
840 }
Matt Mueller55822f82022-12-01 23:11:32841 if (IsNotAcceptableIntermediate(parsed_cert.get(), policy_oid_)) {
Matt Mueller394c6502022-09-21 06:22:41842 continue;
843 }
844 intermediates_cert_issuer_source_.AddCert(std::move(parsed_cert));
845 }
846 RecordCachedIntermediatesHistograms(CFArrayGetCount(matching_items_array),
847 timer.Elapsed());
848 }
849
Matt Mueller394c6502022-09-21 06:22:41850 void RecordCachedIntermediatesHistograms(CFIndex total_cert_count,
851 base::TimeDelta cache_init_time)
852 const EXCLUSIVE_LOCKS_REQUIRED(cache_lock_) {
853 cache_lock_.AssertAcquired();
854 base::UmaHistogramMediumTimes(
855 "Net.CertVerifier.MacKeychainCerts.IntermediateCacheInitTime",
856 cache_init_time);
857 base::UmaHistogramCounts1000("Net.CertVerifier.MacKeychainCerts.TotalCount",
858 total_cert_count);
859 base::UmaHistogramCounts1000(
860 "Net.CertVerifier.MacKeychainCerts.IntermediateCount",
861 intermediates_cert_issuer_source_.size());
862 }
863
Matt Mueller55822f82022-12-01 23:11:32864 const std::unique_ptr<KeychainTrustObserver, base::OnTaskRunnerDeleter>
865 keychain_trust_observer_;
866 const std::unique_ptr<KeychainCertsObserver, base::OnTaskRunnerDeleter>
867 keychain_certs_observer_;
Matt Mueller394c6502022-09-21 06:22:41868 const base::ScopedCFTypeRef<CFStringRef> policy_oid_;
Matt Mueller880337f2022-06-15 04:25:15869
870 base::Lock cache_lock_;
871 // |cache_lock_| must be held while accessing any following members.
Matt Mueller394c6502022-09-21 06:22:41872 int64_t trust_iteration_ GUARDED_BY(cache_lock_) = -1;
873 int64_t certs_iteration_ GUARDED_BY(cache_lock_) = -1;
874
Matt Mueller880337f2022-06-15 04:25:15875 TrustDomainCacheFullCerts admin_domain_cache_ GUARDED_BY(cache_lock_);
876 TrustDomainCacheFullCerts user_domain_cache_ GUARDED_BY(cache_lock_);
Matt Mueller394c6502022-09-21 06:22:41877
878 CertIssuerSourceStatic intermediates_cert_issuer_source_
879 GUARDED_BY(cache_lock_);
Matt Mueller880337f2022-06-15 04:25:15880};
881
Matt Mueller55822f82022-12-01 23:11:32882// TrustImplKeychainCacheFullCerts uses SecItemCopyMatching to get the list of
883// all user and admin added certificates, then checks each to see if has trust
884// settings. Certs will be cached if they are trusted or are potentially valid
885// intermediates.
886class TrustStoreMac::TrustImplKeychainCacheFullCerts
887 : public TrustStoreMac::TrustImpl {
888 public:
889 explicit TrustImplKeychainCacheFullCerts(CFStringRef policy_oid)
890 : keychain_observer_(
891 new KeychainTrustOrCertsObserver,
892 // KeyChainObserver must be destroyed on the network notification
893 // thread as it uses a non-threadsafe CallbackListSubscription.
894 base::OnTaskRunnerDeleter(GetNetworkNotificationThreadMac())),
895 policy_oid_(policy_oid, base::scoped_policy::RETAIN) {}
896
897 TrustImplKeychainCacheFullCerts(const TrustImplKeychainCacheFullCerts&) =
898 delete;
899 TrustImplKeychainCacheFullCerts& operator=(
900 const TrustImplKeychainCacheFullCerts&) = delete;
901
902 TrustStatus IsCertTrusted(const ParsedCertificate* cert,
903 base::SupportsUserData* debug_data) override {
904 SHA256HashValue cert_hash = CalculateFingerprint256(cert->der_cert());
905
906 // This impl doesn't bother to set the debug_info field since we're not
907 // using that anymore, but the related code hasn't been cleaned up yet.
908 // TODO(https://crbug.com/1379461): delete the debug user data code.
909 UpdateUserData(0, debug_data,
910 TrustStoreMac::TrustImplType::kKeychainCacheFullCerts);
911
912 base::AutoLock lock(cache_lock_);
913 MaybeInitializeCache();
914
915 auto cache_iter = trust_status_cache_.find(cert_hash);
916 if (cache_iter == trust_status_cache_.end())
917 return TrustStatus::UNSPECIFIED;
918 return cache_iter->second;
919 }
920
921 bool ImplementsSyncGetIssuersOf() const override { return true; }
922
923 void SyncGetIssuersOf(const ParsedCertificate* cert,
924 ParsedCertificateList* issuers) override {
925 base::AutoLock lock(cache_lock_);
926 MaybeInitializeCache();
927 cert_issuer_source_.SyncGetIssuersOf(cert, issuers);
928 }
929
930 // Initializes the cache, if it isn't already initialized.
931 void InitializeTrustCache() override {
932 base::AutoLock lock(cache_lock_);
933 MaybeInitializeCache();
934 }
935
936 private:
937 // (Re-)Initialize the cache if necessary. Must be called after acquiring
938 // |cache_lock_| and before accessing any of the |*_domain_cache_| members.
939 void MaybeInitializeCache() EXCLUSIVE_LOCKS_REQUIRED(cache_lock_) {
940 cache_lock_.AssertAcquired();
941
942 const int64_t keychain_iteration = keychain_observer_->Iteration();
943 const bool keychain_changed = keychain_iteration_ != keychain_iteration;
944 if (!keychain_changed)
945 return;
946 keychain_iteration_ = keychain_iteration;
947
948 base::ElapsedTimer timer;
949
950 trust_status_cache_.clear();
951 cert_issuer_source_.Clear();
952
953 base::ScopedCFTypeRef<CFMutableDictionaryRef> query(
954 CFDictionaryCreateMutable(nullptr, 0, &kCFTypeDictionaryKeyCallBacks,
955 &kCFTypeDictionaryValueCallBacks));
956
957 CFDictionarySetValue(query, kSecClass, kSecClassCertificate);
958 CFDictionarySetValue(query, kSecReturnRef, kCFBooleanTrue);
959 CFDictionarySetValue(query, kSecMatchLimit, kSecMatchLimitAll);
960
961 base::AutoLock lock(crypto::GetMacSecurityServicesLock());
962
963 base::ScopedCFTypeRef<CFArrayRef> scoped_alternate_keychain_search_list;
964 if (TestKeychainSearchList::HasInstance()) {
965 OSStatus status = TestKeychainSearchList::GetInstance()->CopySearchList(
966 scoped_alternate_keychain_search_list.InitializeInto());
967 if (status) {
968 OSSTATUS_LOG(ERROR, status)
969 << "TestKeychainSearchList::CopySearchList error";
970 return;
971 }
972 CFDictionarySetValue(query, kSecMatchSearchList,
973 scoped_alternate_keychain_search_list.get());
974 }
975
976 base::ScopedCFTypeRef<CFTypeRef> matching_items;
977 OSStatus err = SecItemCopyMatching(query, matching_items.InitializeInto());
978 if (err == errSecItemNotFound) {
979 RecordHistograms(0, timer.Elapsed());
980 // No matches found.
981 return;
982 }
983 if (err) {
984 RecordHistograms(0, timer.Elapsed());
985 OSSTATUS_LOG(ERROR, err) << "SecItemCopyMatching error";
986 return;
987 }
988 CFArrayRef matching_items_array =
989 base::mac::CFCastStrict<CFArrayRef>(matching_items);
990 std::vector<std::pair<SHA256HashValue, TrustStatus>> trust_status_vector;
991 for (CFIndex i = 0, item_count = CFArrayGetCount(matching_items_array);
992 i < item_count; ++i) {
993 SecCertificateRef sec_cert = base::mac::CFCastStrict<SecCertificateRef>(
994 CFArrayGetValueAtIndex(matching_items_array, i));
995
996 base::ScopedCFTypeRef<CFDataRef> der_data(
997 SecCertificateCopyData(sec_cert));
998 if (!der_data) {
999 LOG(ERROR) << "SecCertificateCopyData error";
1000 continue;
1001 }
1002 auto buffer = x509_util::CreateCryptoBuffer(base::make_span(
1003 CFDataGetBytePtr(der_data.get()), CFDataGetLength(der_data.get())));
1004 CertErrors errors;
1005 ParseCertificateOptions options;
1006 options.allow_invalid_serial_numbers = true;
Bob Beck03509d282022-12-07 21:49:051007 std::shared_ptr<const ParsedCertificate> parsed_cert =
Matt Mueller55822f82022-12-01 23:11:321008 ParsedCertificate::Create(std::move(buffer), options, &errors);
1009 if (!parsed_cert) {
1010 LOG(ERROR) << "Error parsing certificate:\n" << errors.ToDebugString();
1011 continue;
1012 }
1013
1014 int debug_info = 0;
1015 TrustStatus trust_status = IsCertificateTrustedForPolicy(
1016 parsed_cert.get(), sec_cert, policy_oid_, &debug_info);
1017
1018 if (trust_status == TrustStatus::TRUSTED ||
1019 trust_status == TrustStatus::DISTRUSTED) {
1020 trust_status_vector.emplace_back(
1021 X509Certificate::CalculateFingerprint256(
1022 parsed_cert->cert_buffer()),
1023 trust_status);
1024 cert_issuer_source_.AddCert(std::move(parsed_cert));
1025 continue;
1026 }
1027
1028 if (IsNotAcceptableIntermediate(parsed_cert.get(), policy_oid_)) {
1029 continue;
1030 }
1031 cert_issuer_source_.AddCert(std::move(parsed_cert));
1032 }
1033 trust_status_cache_ = base::flat_map<SHA256HashValue, TrustStatus>(
1034 std::move(trust_status_vector));
1035 RecordHistograms(CFArrayGetCount(matching_items_array), timer.Elapsed());
1036 }
1037
1038 void RecordHistograms(CFIndex total_cert_count,
1039 base::TimeDelta init_time) const
1040 EXCLUSIVE_LOCKS_REQUIRED(cache_lock_) {
1041 cache_lock_.AssertAcquired();
1042 base::UmaHistogramMediumTimes("Net.CertVerifier.MacTrustImplCacheInitTime",
1043 init_time);
1044 base::UmaHistogramCounts1000("Net.CertVerifier.MacKeychainCerts.TotalCount",
1045 total_cert_count);
1046 base::UmaHistogramCounts1000(
1047 "Net.CertVerifier.MacKeychainCerts.IntermediateCount",
1048 cert_issuer_source_.size() - trust_status_cache_.size());
1049 base::UmaHistogramCounts1000("Net.CertVerifier.MacKeychainCerts.TrustCount",
1050 trust_status_cache_.size());
1051 }
1052
1053 const std::unique_ptr<KeychainTrustOrCertsObserver, base::OnTaskRunnerDeleter>
1054 keychain_observer_;
1055 const base::ScopedCFTypeRef<CFStringRef> policy_oid_;
1056
1057 base::Lock cache_lock_;
1058 // |cache_lock_| must be held while accessing any following members.
1059 int64_t keychain_iteration_ GUARDED_BY(cache_lock_) = -1;
1060 base::flat_map<SHA256HashValue, TrustStatus> trust_status_cache_
1061 GUARDED_BY(cache_lock_);
1062 CertIssuerSourceStatic cert_issuer_source_ GUARDED_BY(cache_lock_);
1063};
1064
Matt Mueller1b4c2342021-03-16 17:57:041065// TrustImplNoCache is the simplest approach which calls
1066// SecTrustSettingsCopyTrustSettings on every cert checked, with no caching.
1067class TrustStoreMac::TrustImplNoCache : public TrustStoreMac::TrustImpl {
1068 public:
Matt Muellerae5071512022-10-12 18:25:351069 explicit TrustImplNoCache(CFStringRef policy_oid) : policy_oid_(policy_oid) {}
Matt Mueller1b4c2342021-03-16 17:57:041070
Peter Boström293b1342021-09-22 17:31:431071 TrustImplNoCache(const TrustImplNoCache&) = delete;
1072 TrustImplNoCache& operator=(const TrustImplNoCache&) = delete;
1073
Matt Mueller1b4c2342021-03-16 17:57:041074 ~TrustImplNoCache() override = default;
1075
Matt Mueller1b4c2342021-03-16 17:57:041076 // Returns the trust status for |cert|.
David Benjamin24381632021-09-03 17:01:451077 TrustStatus IsCertTrusted(const ParsedCertificate* cert,
Matt Mueller1b4c2342021-03-16 17:57:041078 base::SupportsUserData* debug_data) override {
1079 int debug_info = 0;
Matt Mueller55822f82022-12-01 23:11:321080 base::AutoLock lock(crypto::GetMacSecurityServicesLock());
Matt Muellercc72adf2022-06-14 21:22:591081 TrustStatus result =
Matt Muellerae5071512022-10-12 18:25:351082 IsCertificateTrustedForPolicy(cert, policy_oid_, &debug_info);
Matt Mueller1b4c2342021-03-16 17:57:041083 UpdateUserData(debug_info, debug_data,
1084 TrustStoreMac::TrustImplType::kSimple);
1085 return result;
1086 }
1087
1088 void InitializeTrustCache() override {
1089 // No-op for this impl.
1090 }
1091
1092 private:
1093 const CFStringRef policy_oid_;
Matt Mueller1b4c2342021-03-16 17:57:041094};
1095
Matt Mueller06cc41e2022-11-29 02:26:561096TrustStoreMac::TrustStoreMac(CFStringRef policy_oid, TrustImplType impl) {
Matt Mueller1b4c2342021-03-16 17:57:041097 switch (impl) {
1098 case TrustImplType::kUnknown:
1099 DCHECK(false);
1100 break;
Matt Mueller1b4c2342021-03-16 17:57:041101 case TrustImplType::kSimple:
Matt Muellerae5071512022-10-12 18:25:351102 trust_cache_ = std::make_unique<TrustImplNoCache>(policy_oid);
Matt Mueller1b4c2342021-03-16 17:57:041103 break;
Matt Mueller880337f2022-06-15 04:25:151104 case TrustImplType::kDomainCacheFullCerts:
1105 trust_cache_ =
Matt Muellerae5071512022-10-12 18:25:351106 std::make_unique<TrustImplDomainCacheFullCerts>(policy_oid);
Matt Mueller880337f2022-06-15 04:25:151107 break;
Matt Mueller55822f82022-12-01 23:11:321108 case TrustImplType::kKeychainCacheFullCerts:
1109 trust_cache_ =
1110 std::make_unique<TrustImplKeychainCacheFullCerts>(policy_oid);
1111 break;
Matt Mueller1b4c2342021-03-16 17:57:041112 }
1113}
eroman2a938c32017-04-28 23:16:581114
1115TrustStoreMac::~TrustStoreMac() = default;
1116
Matt Muellerd0506dd82020-12-17 02:25:241117void TrustStoreMac::InitializeTrustCache() const {
1118 trust_cache_->InitializeTrustCache();
1119}
1120
eroman2a938c32017-04-28 23:16:581121void TrustStoreMac::SyncGetIssuersOf(const ParsedCertificate* cert,
1122 ParsedCertificateList* issuers) {
Matt Mueller880337f2022-06-15 04:25:151123 if (trust_cache_->ImplementsSyncGetIssuersOf()) {
1124 trust_cache_->SyncGetIssuersOf(cert, issuers);
1125 return;
1126 }
1127
eroman2a938c32017-04-28 23:16:581128 base::ScopedCFTypeRef<CFDataRef> name_data = GetMacNormalizedIssuer(cert);
Eric Romancdc12c82017-10-18 03:34:061129 if (!name_data)
1130 return;
eroman2a938c32017-04-28 23:16:581131
Matt Mueller79368e272022-03-01 06:11:231132 std::vector<bssl::UniquePtr<CRYPTO_BUFFER>> matching_cert_buffers =
Matt Muellerae5071512022-10-12 18:25:351133 FindMatchingCertificatesForMacNormalizedSubject(name_data);
eroman2a938c32017-04-28 23:16:581134
1135 // Convert to ParsedCertificate.
Matt Mueller79368e272022-03-01 06:11:231136 for (auto& buffer : matching_cert_buffers) {
mattmea4ed8232017-02-28 23:13:231137 CertErrors errors;
1138 ParseCertificateOptions options;
1139 options.allow_invalid_serial_numbers = true;
Bob Beck03509d282022-12-07 21:49:051140 std::shared_ptr<const ParsedCertificate> anchor_cert =
Matt Mueller79368e272022-03-01 06:11:231141 ParsedCertificate::Create(std::move(buffer), options, &errors);
mattmea4ed8232017-02-28 23:13:231142 if (!anchor_cert) {
1143 // TODO(crbug.com/634443): return errors better.
1144 LOG(ERROR) << "Error parsing issuer certificate:\n"
1145 << errors.ToDebugString();
1146 continue;
1147 }
1148
eroman2a938c32017-04-28 23:16:581149 issuers->push_back(std::move(anchor_cert));
mattmea4ed8232017-02-28 23:13:231150 }
1151}
1152
Hubert Chao6a4780622023-01-06 20:25:491153CertificateTrust TrustStoreMac::GetTrust(const ParsedCertificate* cert,
1154 base::SupportsUserData* debug_data) {
Matt Mueller370a7fda2019-08-22 00:38:591155 TrustStatus trust_status = trust_cache_->IsCertTrusted(cert, debug_data);
eroman2a938c32017-04-28 23:16:581156 switch (trust_status) {
1157 case TrustStatus::TRUSTED:
Matt Muellerde8bd0c2022-12-19 20:06:131158 return CertificateTrust::ForTrustAnchor().WithEnforceAnchorExpiry();
eroman2a938c32017-04-28 23:16:581159 case TrustStatus::DISTRUSTED:
David Benjamin24381632021-09-03 17:01:451160 return CertificateTrust::ForDistrusted();
eroman2a938c32017-04-28 23:16:581161 case TrustStatus::UNSPECIFIED:
David Benjamin24381632021-09-03 17:01:451162 return CertificateTrust::ForUnspecified();
Matt Mueller5d105e72019-07-01 17:38:421163 case TrustStatus::UNKNOWN:
Matt Mueller1b4c2342021-03-16 17:57:041164 // UNKNOWN is an implementation detail of TrustImpl and should never be
Matt Mueller5d105e72019-07-01 17:38:421165 // returned.
1166 NOTREACHED();
1167 break;
eroman2a938c32017-04-28 23:16:581168 }
mattmea4ed8232017-02-28 23:13:231169
David Benjamin24381632021-09-03 17:01:451170 return CertificateTrust::ForUnspecified();
mattmea4ed8232017-02-28 23:13:231171}
1172
1173// static
Matt Mueller79368e272022-03-01 06:11:231174std::vector<bssl::UniquePtr<CRYPTO_BUFFER>>
mattmea4ed8232017-02-28 23:13:231175TrustStoreMac::FindMatchingCertificatesForMacNormalizedSubject(
Matt Muellerae5071512022-10-12 18:25:351176 CFDataRef name_data) {
Matt Mueller79368e272022-03-01 06:11:231177 std::vector<bssl::UniquePtr<CRYPTO_BUFFER>> matching_cert_buffers;
mattmea4ed8232017-02-28 23:13:231178 base::ScopedCFTypeRef<CFMutableDictionaryRef> query(
1179 CFDictionaryCreateMutable(nullptr, 0, &kCFTypeDictionaryKeyCallBacks,
1180 &kCFTypeDictionaryValueCallBacks));
1181
1182 CFDictionarySetValue(query, kSecClass, kSecClassCertificate);
1183 CFDictionarySetValue(query, kSecReturnRef, kCFBooleanTrue);
1184 CFDictionarySetValue(query, kSecMatchLimit, kSecMatchLimitAll);
1185 CFDictionarySetValue(query, kSecAttrSubject, name_data);
1186
Matt Muellerc0dc1452022-03-01 15:49:261187 base::AutoLock lock(crypto::GetMacSecurityServicesLock());
1188
mattmea4ed8232017-02-28 23:13:231189 base::ScopedCFTypeRef<CFArrayRef> scoped_alternate_keychain_search_list;
1190 if (TestKeychainSearchList::HasInstance()) {
1191 OSStatus status = TestKeychainSearchList::GetInstance()->CopySearchList(
1192 scoped_alternate_keychain_search_list.InitializeInto());
1193 if (status) {
1194 OSSTATUS_LOG(ERROR, status)
1195 << "TestKeychainSearchList::CopySearchList error";
Matt Mueller79368e272022-03-01 06:11:231196 return matching_cert_buffers;
mattmea4ed8232017-02-28 23:13:231197 }
1198 }
1199
Matt Muellercc72adf2022-06-14 21:22:591200 if (scoped_alternate_keychain_search_list) {
1201 CFDictionarySetValue(query, kSecMatchSearchList,
1202 scoped_alternate_keychain_search_list.get());
mattmea4ed8232017-02-28 23:13:231203 }
mattmea4ed8232017-02-28 23:13:231204
Matt Mueller79368e272022-03-01 06:11:231205 base::ScopedCFTypeRef<CFArrayRef> matching_items;
mattmea4ed8232017-02-28 23:13:231206 OSStatus err = SecItemCopyMatching(
1207 query, reinterpret_cast<CFTypeRef*>(matching_items.InitializeInto()));
1208 if (err == errSecItemNotFound) {
1209 // No matches found.
Matt Mueller79368e272022-03-01 06:11:231210 return matching_cert_buffers;
mattmea4ed8232017-02-28 23:13:231211 }
1212 if (err) {
1213 OSSTATUS_LOG(ERROR, err) << "SecItemCopyMatching error";
Matt Mueller79368e272022-03-01 06:11:231214 return matching_cert_buffers;
mattmea4ed8232017-02-28 23:13:231215 }
Matt Mueller79368e272022-03-01 06:11:231216
1217 for (CFIndex i = 0, item_count = CFArrayGetCount(matching_items);
1218 i < item_count; ++i) {
1219 SecCertificateRef match_cert_handle = reinterpret_cast<SecCertificateRef>(
1220 const_cast<void*>(CFArrayGetValueAtIndex(matching_items, i)));
1221
1222 base::ScopedCFTypeRef<CFDataRef> der_data(
1223 SecCertificateCopyData(match_cert_handle));
1224 if (!der_data) {
1225 LOG(ERROR) << "SecCertificateCopyData error";
1226 continue;
1227 }
1228 matching_cert_buffers.push_back(x509_util::CreateCryptoBuffer(
1229 base::make_span(CFDataGetBytePtr(der_data.get()),
1230 CFDataGetLength(der_data.get()))));
1231 }
1232 return matching_cert_buffers;
mattmea4ed8232017-02-28 23:13:231233}
1234
1235// static
1236base::ScopedCFTypeRef<CFDataRef> TrustStoreMac::GetMacNormalizedIssuer(
eroman2a938c32017-04-28 23:16:581237 const ParsedCertificate* cert) {
mattmea4ed8232017-02-28 23:13:231238 base::ScopedCFTypeRef<CFDataRef> name_data;
Matt Muellerd169b9092022-03-01 04:16:271239 base::AutoLock lock(crypto::GetMacSecurityServicesLock());
mattmea4ed8232017-02-28 23:13:231240 // There does not appear to be any public API to get the normalized version
1241 // of a Name without creating a SecCertificate.
1242 base::ScopedCFTypeRef<SecCertificateRef> cert_handle(
mattm4cede8d2017-04-11 02:55:011243 x509_util::CreateSecCertificateFromBytes(cert->der_cert().UnsafeData(),
1244 cert->der_cert().Length()));
mattmea4ed8232017-02-28 23:13:231245 if (!cert_handle) {
Matt Muellera4193272017-12-07 00:23:341246 LOG(ERROR) << "CreateCertBufferFromBytes";
mattmea4ed8232017-02-28 23:13:231247 return name_data;
1248 }
Avi Drissman1debcc62022-05-16 17:56:091249 name_data.reset(SecCertificateCopyNormalizedIssuerSequence(cert_handle));
mattmea4ed8232017-02-28 23:13:231250 if (!name_data)
1251 LOG(ERROR) << "SecCertificateCopyNormalizedIssuerContent";
1252 return name_data;
1253}
1254
mattmea4ed8232017-02-28 23:13:231255} // namespace net