blob: 9c8146559e36b3f3ed460e237054b9a8d250d793 [file] [log] [blame]
mattmea4ed8232017-02-28 23:13:231// Copyright 2017 The Chromium Authors. All rights reserved.
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
Matt Mueller5d105e72019-07-01 17:38:429#include "base/atomicops.h"
10#include "base/bind.h"
11#include "base/callback_list.h"
12#include "base/containers/flat_map.h"
Matt Mueller1b4c2342021-03-16 17:57:0413#include "base/containers/mru_cache.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 Mueller5d105e72019-07-01 17:38:4217#include "base/no_destructor.h"
mattmea4ed8232017-02-28 23:13:2318#include "base/synchronization/lock.h"
19#include "crypto/mac_security_services_lock.h"
Matt Mueller5d105e72019-07-01 17:38:4220#include "net/base/hash_value.h"
21#include "net/base/network_notification_thread_mac.h"
mattmea4ed8232017-02-28 23:13:2322#include "net/cert/internal/cert_errors.h"
23#include "net/cert/internal/parse_name.h"
24#include "net/cert/internal/parsed_certificate.h"
Matt Mueller1b4c2342021-03-16 17:57:0425#include "net/cert/known_roots_mac.h"
mattmea4ed8232017-02-28 23:13:2326#include "net/cert/test_keychain_search_list_mac.h"
mattmea4ed8232017-02-28 23:13:2327#include "net/cert/x509_util.h"
mattm4cede8d2017-04-11 02:55:0128#include "net/cert/x509_util_mac.h"
Matt Mueller5d105e72019-07-01 17:38:4229#include "third_party/boringssl/src/include/openssl/sha.h"
mattmea4ed8232017-02-28 23:13:2330
31namespace net {
32
33namespace {
34
35// The rules for interpreting trust settings are documented at:
36// https://developer.apple.com/reference/security/1400261-sectrustsettingscopytrustsetting?language=objc
37
38// Indicates the trust status of a certificate.
39enum class TrustStatus {
Matt Mueller5d105e72019-07-01 17:38:4240 // Trust status is unknown / uninitialized.
41 UNKNOWN,
mattmea4ed8232017-02-28 23:13:2342 // Certificate inherits trust value from its issuer. If the certificate is the
43 // root of the chain, this implies distrust.
44 UNSPECIFIED,
45 // Certificate is a trust anchor.
46 TRUSTED,
Ryan Sleevia9d6aa62019-07-26 13:32:1847 // Certificate is blocked / explicitly distrusted.
mattmea4ed8232017-02-28 23:13:2348 DISTRUSTED
49};
50
Matt Mueller7ac4af5c2021-03-30 18:58:5351enum class KnownRootStatus {
52 UNKNOWN,
53 IS_KNOWN_ROOT,
54 NOT_KNOWN_ROOT,
55};
56
Matt Mueller370a7fda2019-08-22 00:38:5957const void* kResultDebugDataKey = &kResultDebugDataKey;
58
mattmea4ed8232017-02-28 23:13:2359// Returns trust status of usage constraints dictionary |trust_dict| for a
Matt Mueller59ebd7d2019-04-19 17:56:4060// certificate that |is_self_issued|.
mattmea4ed8232017-02-28 23:13:2361TrustStatus IsTrustDictionaryTrustedForPolicy(
62 CFDictionaryRef trust_dict,
Matt Mueller59ebd7d2019-04-19 17:56:4063 bool is_self_issued,
Matt Mueller370a7fda2019-08-22 00:38:5964 const CFStringRef target_policy_oid,
65 int* debug_info) {
mattmea4ed8232017-02-28 23:13:2366 // An empty trust dict should be interpreted as
67 // kSecTrustSettingsResultTrustRoot. This is handled by falling through all
68 // the conditions below with the default value of |trust_settings_result|.
Matt Mueller370a7fda2019-08-22 00:38:5969 CFIndex dict_size = CFDictionaryGetCount(trust_dict);
70 if (dict_size == 0)
71 *debug_info |= TrustStoreMac::TRUST_SETTINGS_DICT_EMPTY;
72
73 CFIndex known_elements = 0;
74 if (CFDictionaryContainsKey(trust_dict, kSecTrustSettingsPolicy)) {
75 *debug_info |= TrustStoreMac::TRUST_SETTINGS_DICT_CONTAINS_POLICY;
76 known_elements++;
77 }
78 if (CFDictionaryContainsKey(trust_dict, kSecTrustSettingsApplication)) {
79 *debug_info |= TrustStoreMac::TRUST_SETTINGS_DICT_CONTAINS_APPLICATION;
80 known_elements++;
81 }
82 if (CFDictionaryContainsKey(trust_dict, kSecTrustSettingsPolicyString)) {
83 *debug_info |= TrustStoreMac::TRUST_SETTINGS_DICT_CONTAINS_POLICY_STRING;
84 known_elements++;
85 }
86 if (CFDictionaryContainsKey(trust_dict, kSecTrustSettingsKeyUsage)) {
87 *debug_info |= TrustStoreMac::TRUST_SETTINGS_DICT_CONTAINS_KEY_USAGE;
88 known_elements++;
89 }
90 if (CFDictionaryContainsKey(trust_dict, kSecTrustSettingsResult)) {
91 *debug_info |= TrustStoreMac::TRUST_SETTINGS_DICT_CONTAINS_RESULT;
92 known_elements++;
93 }
94 if (CFDictionaryContainsKey(trust_dict, kSecTrustSettingsAllowedError)) {
95 *debug_info |= TrustStoreMac::TRUST_SETTINGS_DICT_CONTAINS_ALLOWED_ERROR;
96 known_elements++;
97 }
98 if (known_elements != dict_size)
99 *debug_info |= TrustStoreMac::TRUST_SETTINGS_DICT_UNKNOWN_KEY;
mattmea4ed8232017-02-28 23:13:23100
101 // Trust settings may be scoped to a single application, by checking that the
102 // code signing identity of the current application matches the serialized
103 // code signing identity in the kSecTrustSettingsApplication key.
104 // As this is not presently supported, skip any trust settings scoped to the
105 // application.
106 if (CFDictionaryContainsKey(trust_dict, kSecTrustSettingsApplication))
107 return TrustStatus::UNSPECIFIED;
108
109 // Trust settings may be scoped using policy-specific constraints. For
110 // example, SSL trust settings might be scoped to a single hostname, or EAP
111 // settings specific to a particular WiFi network.
112 // As this is not presently supported, skip any policy-specific trust
113 // settings.
114 if (CFDictionaryContainsKey(trust_dict, kSecTrustSettingsPolicyString))
115 return TrustStatus::UNSPECIFIED;
116
117 // Ignoring kSecTrustSettingsKeyUsage for now; it does not seem relevant to
118 // the TLS case.
119
120 // If the trust settings are scoped to a specific policy (via
121 // kSecTrustSettingsPolicy), ensure that the policy is the same policy as
122 // |target_policy_oid|. If there is no kSecTrustSettingsPolicy key, it's
123 // considered a match for all policies.
Matt Mueller370a7fda2019-08-22 00:38:59124 if (CFDictionaryContainsKey(trust_dict, kSecTrustSettingsPolicy)) {
125 SecPolicyRef policy_ref = base::mac::GetValueFromDictionary<SecPolicyRef>(
126 trust_dict, kSecTrustSettingsPolicy);
127 if (!policy_ref) {
128 *debug_info |= TrustStoreMac::TRUST_SETTINGS_DICT_INVALID_POLICY_TYPE;
129 return TrustStatus::UNSPECIFIED;
130 }
mattmea4ed8232017-02-28 23:13:23131 base::ScopedCFTypeRef<CFDictionaryRef> policy_dict;
132 {
133 base::AutoLock lock(crypto::GetMacSecurityServicesLock());
134 policy_dict.reset(SecPolicyCopyProperties(policy_ref));
135 }
136
137 // kSecPolicyOid is guaranteed to be present in the policy dictionary.
mattmea4ed8232017-02-28 23:13:23138 CFStringRef policy_oid = base::mac::GetValueFromDictionary<CFStringRef>(
Avi Drissman905bac72020-10-24 17:26:31139 policy_dict, kSecPolicyOid);
mattmea4ed8232017-02-28 23:13:23140
141 if (!CFEqual(policy_oid, target_policy_oid))
142 return TrustStatus::UNSPECIFIED;
143 }
144
145 // If kSecTrustSettingsResult is not present in the trust dict,
146 // kSecTrustSettingsResultTrustRoot is assumed.
147 int trust_settings_result = kSecTrustSettingsResultTrustRoot;
Matt Mueller370a7fda2019-08-22 00:38:59148 if (CFDictionaryContainsKey(trust_dict, kSecTrustSettingsResult)) {
149 CFNumberRef trust_settings_result_ref =
150 base::mac::GetValueFromDictionary<CFNumberRef>(trust_dict,
151 kSecTrustSettingsResult);
152 if (!trust_settings_result_ref ||
153 !CFNumberGetValue(trust_settings_result_ref, kCFNumberIntType,
154 &trust_settings_result)) {
155 *debug_info |= TrustStoreMac::TRUST_SETTINGS_DICT_INVALID_RESULT_TYPE;
156 return TrustStatus::UNSPECIFIED;
157 }
mattmea4ed8232017-02-28 23:13:23158 }
159
160 if (trust_settings_result == kSecTrustSettingsResultDeny)
161 return TrustStatus::DISTRUSTED;
162
Matt Mueller59ebd7d2019-04-19 17:56:40163 // This is a bit of a hack: if the cert is self-issued allow either
164 // kSecTrustSettingsResultTrustRoot or kSecTrustSettingsResultTrustAsRoot on
165 // the basis that SecTrustSetTrustSettings should not allow creating an
166 // invalid trust record in the first place. (The spec is that
mattmea4ed8232017-02-28 23:13:23167 // kSecTrustSettingsResultTrustRoot can only be applied to root(self-signed)
Matt Mueller59ebd7d2019-04-19 17:56:40168 // certs and kSecTrustSettingsResultTrustAsRoot is used for other certs.)
169 // This hack avoids having to check the signature on the cert which is slow
170 // if using the platform APIs, and may require supporting MD5 signature
171 // algorithms on some older OSX versions or locally added roots, which is
172 // undesirable in the built-in signature verifier.
173 if (is_self_issued) {
174 return (trust_settings_result == kSecTrustSettingsResultTrustRoot ||
175 trust_settings_result == kSecTrustSettingsResultTrustAsRoot)
mattmea4ed8232017-02-28 23:13:23176 ? TrustStatus::TRUSTED
177 : TrustStatus::UNSPECIFIED;
Matt Mueller59ebd7d2019-04-19 17:56:40178 }
mattmea4ed8232017-02-28 23:13:23179
180 // kSecTrustSettingsResultTrustAsRoot can only be applied to non-root certs.
181 return (trust_settings_result == kSecTrustSettingsResultTrustAsRoot)
182 ? TrustStatus::TRUSTED
183 : TrustStatus::UNSPECIFIED;
184}
185
186// Returns true if the trust settings array |trust_settings| for a certificate
Matt Mueller59ebd7d2019-04-19 17:56:40187// that |is_self_issued| should be treated as a trust anchor.
mattmea4ed8232017-02-28 23:13:23188TrustStatus IsTrustSettingsTrustedForPolicy(CFArrayRef trust_settings,
Matt Mueller59ebd7d2019-04-19 17:56:40189 bool is_self_issued,
Matt Mueller370a7fda2019-08-22 00:38:59190 const CFStringRef policy_oid,
191 int* debug_info) {
mattmea4ed8232017-02-28 23:13:23192 // An empty trust settings array (that is, the trust_settings parameter
193 // returns a valid but empty CFArray) means "always trust this certificate"
194 // with an overall trust setting for the certificate of
195 // kSecTrustSettingsResultTrustRoot.
Matt Mueller370a7fda2019-08-22 00:38:59196 if (CFArrayGetCount(trust_settings) == 0) {
197 *debug_info |= TrustStoreMac::TRUST_SETTINGS_ARRAY_EMPTY;
198 return is_self_issued ? TrustStatus::TRUSTED : TrustStatus::UNSPECIFIED;
199 }
mattmea4ed8232017-02-28 23:13:23200
201 for (CFIndex i = 0, settings_count = CFArrayGetCount(trust_settings);
202 i < settings_count; ++i) {
203 CFDictionaryRef trust_dict = reinterpret_cast<CFDictionaryRef>(
204 const_cast<void*>(CFArrayGetValueAtIndex(trust_settings, i)));
205 TrustStatus trust = IsTrustDictionaryTrustedForPolicy(
Matt Mueller370a7fda2019-08-22 00:38:59206 trust_dict, is_self_issued, policy_oid, debug_info);
mattmea4ed8232017-02-28 23:13:23207 if (trust != TrustStatus::UNSPECIFIED)
208 return trust;
209 }
210 return TrustStatus::UNSPECIFIED;
211}
212
Matt Mueller5d105e72019-07-01 17:38:42213// Returns the trust status for |cert_handle| for the policy |policy_oid| in
214// |trust_domain|.
Matt Mueller1b4c2342021-03-16 17:57:04215TrustStatus IsSecCertificateTrustedForPolicyInDomain(
216 SecCertificateRef cert_handle,
217 const bool is_self_issued,
218 const CFStringRef policy_oid,
219 SecTrustSettingsDomain trust_domain,
220 int* debug_info) {
221 base::ScopedCFTypeRef<CFArrayRef> trust_settings;
222 OSStatus err;
223 {
224 base::AutoLock lock(crypto::GetMacSecurityServicesLock());
225 err = SecTrustSettingsCopyTrustSettings(cert_handle, trust_domain,
226 trust_settings.InitializeInto());
227 }
228 if (err == errSecItemNotFound) {
229 // No trust settings for that domain.. try the next.
230 return TrustStatus::UNSPECIFIED;
231 }
232 if (err) {
233 OSSTATUS_LOG(ERROR, err) << "SecTrustSettingsCopyTrustSettings error";
234 return TrustStatus::UNSPECIFIED;
235 }
236 TrustStatus trust = IsTrustSettingsTrustedForPolicy(
237 trust_settings, is_self_issued, policy_oid, debug_info);
238 return trust;
239}
240
Matt Mueller5d105e72019-07-01 17:38:42241TrustStatus IsCertificateTrustedForPolicyInDomain(
Matt Mueller59ebd7d2019-04-19 17:56:40242 const scoped_refptr<ParsedCertificate>& cert,
Matt Mueller5d105e72019-07-01 17:38:42243 const CFStringRef policy_oid,
Matt Mueller370a7fda2019-08-22 00:38:59244 SecTrustSettingsDomain trust_domain,
245 int* debug_info) {
Matt Mueller5d105e72019-07-01 17:38:42246 // TODO(eroman): Inefficient -- path building will convert between
247 // SecCertificateRef and ParsedCertificate representations multiple times
248 // (when getting the issuers, and again here).
249 //
250 // This conversion will also be done for each domain the cert policy is
251 // checked, but the TrustDomainCache ensures this function is only called on
252 // domains that actually have settings for the cert. The common case is that
253 // a cert will have trust settings in only zero or one domains, and when in
254 // more than one domain it would generally be because one domain is
255 // overriding the setting in the next, so it would only get done once anyway.
256 base::ScopedCFTypeRef<SecCertificateRef> cert_handle =
257 x509_util::CreateSecCertificateFromBytes(cert->der_cert().UnsafeData(),
258 cert->der_cert().Length());
259 if (!cert_handle)
260 return TrustStatus::UNSPECIFIED;
261
Matt Mueller59ebd7d2019-04-19 17:56:40262 const bool is_self_issued =
263 cert->normalized_subject() == cert->normalized_issuer();
Matt Mueller5d105e72019-07-01 17:38:42264
Matt Mueller1b4c2342021-03-16 17:57:04265 return IsSecCertificateTrustedForPolicyInDomain(
266 cert_handle, is_self_issued, policy_oid, trust_domain, debug_info);
Matt Mueller5d105e72019-07-01 17:38:42267}
268
Matt Mueller7ac4af5c2021-03-30 18:58:53269KnownRootStatus IsCertificateKnownRoot(const ParsedCertificate* cert) {
270 base::ScopedCFTypeRef<SecCertificateRef> cert_handle =
271 x509_util::CreateSecCertificateFromBytes(cert->der_cert().UnsafeData(),
272 cert->der_cert().Length());
273 if (!cert_handle)
274 return KnownRootStatus::NOT_KNOWN_ROOT;
275
276 base::ScopedCFTypeRef<CFArrayRef> trust_settings;
277 OSStatus err;
278 {
279 base::AutoLock lock(crypto::GetMacSecurityServicesLock());
280 err = SecTrustSettingsCopyTrustSettings(cert_handle,
281 kSecTrustSettingsDomainSystem,
282 trust_settings.InitializeInto());
283 }
284 return (err == errSecSuccess) ? KnownRootStatus::IS_KNOWN_ROOT
285 : KnownRootStatus::NOT_KNOWN_ROOT;
286}
287
288TrustStatus IsCertificateTrustedForPolicy(const ParsedCertificate* cert,
289 const CFStringRef policy_oid,
290 int* debug_info,
291 KnownRootStatus* out_is_known_root) {
292 // |*out_is_known_root| is intentionally not cleared before starting, as
293 // there may have been a value already calculated and cached independently.
294 // The caller is expected to initialize |*out_is_known_root| to UNKNOWN if
295 // the value has not been calculated.
296
Matt Mueller1b4c2342021-03-16 17:57:04297 base::ScopedCFTypeRef<SecCertificateRef> cert_handle =
298 x509_util::CreateSecCertificateFromBytes(cert->der_cert().UnsafeData(),
299 cert->der_cert().Length());
300 if (!cert_handle)
301 return TrustStatus::UNSPECIFIED;
302
303 const bool is_self_issued =
304 cert->normalized_subject() == cert->normalized_issuer();
305
306 // Evaluate trust domains in user, admin, system order. Admin settings can
307 // override system ones, and user settings can override both admin and system.
308 for (const auto& trust_domain :
309 {kSecTrustSettingsDomainUser, kSecTrustSettingsDomainAdmin,
310 kSecTrustSettingsDomainSystem}) {
311 base::ScopedCFTypeRef<CFArrayRef> trust_settings;
312 OSStatus err;
313 {
314 base::AutoLock lock(crypto::GetMacSecurityServicesLock());
315 err = SecTrustSettingsCopyTrustSettings(cert_handle, trust_domain,
316 trust_settings.InitializeInto());
317 }
Matt Mueller7ac4af5c2021-03-30 18:58:53318 if (err != errSecSuccess) {
319 if (out_is_known_root && trust_domain == kSecTrustSettingsDomainSystem) {
320 // If trust settings are not present for |cert| in the system domain,
321 // record it as not a known root.
322 *out_is_known_root = KnownRootStatus::NOT_KNOWN_ROOT;
323 }
324 if (err == errSecItemNotFound) {
325 // No trust settings for that domain.. try the next.
326 continue;
327 }
Matt Mueller1b4c2342021-03-16 17:57:04328 OSSTATUS_LOG(ERROR, err) << "SecTrustSettingsCopyTrustSettings error";
329 continue;
330 }
Matt Mueller7ac4af5c2021-03-30 18:58:53331 if (out_is_known_root && trust_domain == kSecTrustSettingsDomainSystem) {
332 // If trust settings are present for |cert| in the system domain, record
333 // it as a known root.
334 *out_is_known_root = KnownRootStatus::IS_KNOWN_ROOT;
335 }
Matt Mueller1b4c2342021-03-16 17:57:04336 TrustStatus trust = IsTrustSettingsTrustedForPolicy(
337 trust_settings, is_self_issued, policy_oid, debug_info);
338 if (trust != TrustStatus::UNSPECIFIED)
339 return trust;
340 }
341
342 // No trust settings, or none of the settings were for the correct policy, or
343 // had the correct trust result.
344 return TrustStatus::UNSPECIFIED;
345}
346
347void UpdateUserData(int debug_info,
348 base::SupportsUserData* user_data,
349 TrustStoreMac::TrustImplType impl_type) {
Matt Mueller370a7fda2019-08-22 00:38:59350 if (!user_data)
351 return;
352 TrustStoreMac::ResultDebugData* result_debug_data =
353 TrustStoreMac::ResultDebugData::GetOrCreate(user_data);
Matt Mueller1b4c2342021-03-16 17:57:04354 result_debug_data->UpdateTrustDebugInfo(debug_info, impl_type);
Matt Mueller370a7fda2019-08-22 00:38:59355}
356
Matt Mueller5d105e72019-07-01 17:38:42357// Caches calculated trust status for certificates present in a single trust
358// domain.
359class TrustDomainCache {
360 public:
Matt Mueller370a7fda2019-08-22 00:38:59361 struct TrustStatusDetails {
362 TrustStatus trust_status = TrustStatus::UNKNOWN;
363 int debug_info = 0;
364 };
365
Matt Mueller5d105e72019-07-01 17:38:42366 TrustDomainCache(SecTrustSettingsDomain domain, CFStringRef policy_oid)
367 : domain_(domain), policy_oid_(policy_oid) {
368 DCHECK(policy_oid_);
mattmea4ed8232017-02-28 23:13:23369 }
370
Matt Mueller5d105e72019-07-01 17:38:42371 // (Re-)Initializes the cache with the certs in |domain_| set to UNKNOWN trust
372 // status.
373 void Initialize() {
374 trust_status_cache_.clear();
375
376 base::ScopedCFTypeRef<CFArrayRef> cert_array;
377 OSStatus rv;
378 {
379 base::AutoLock lock(crypto::GetMacSecurityServicesLock());
380 rv = SecTrustSettingsCopyCertificates(domain_,
381 cert_array.InitializeInto());
382 }
383 if (rv != noErr) {
384 // Note: SecTrustSettingsCopyCertificates can legitimately return
385 // errSecNoTrustSettings if there are no trust settings in |domain_|.
386 return;
387 }
Matt Mueller370a7fda2019-08-22 00:38:59388 std::vector<std::pair<SHA256HashValue, TrustStatusDetails>>
389 trust_status_vector;
Matt Mueller5d105e72019-07-01 17:38:42390 for (CFIndex i = 0, size = CFArrayGetCount(cert_array); i < size; ++i) {
391 SecCertificateRef cert = reinterpret_cast<SecCertificateRef>(
392 const_cast<void*>(CFArrayGetValueAtIndex(cert_array, i)));
393 trust_status_vector.emplace_back(x509_util::CalculateFingerprint256(cert),
Matt Mueller370a7fda2019-08-22 00:38:59394 TrustStatusDetails());
Matt Mueller5d105e72019-07-01 17:38:42395 }
Matt Mueller370a7fda2019-08-22 00:38:59396 trust_status_cache_ = base::flat_map<SHA256HashValue, TrustStatusDetails>(
Matt Mueller5d105e72019-07-01 17:38:42397 std::move(trust_status_vector));
398 }
399
400 // Returns the trust status for |cert| in |domain_|.
401 TrustStatus IsCertTrusted(const scoped_refptr<ParsedCertificate>& cert,
Matt Mueller370a7fda2019-08-22 00:38:59402 const SHA256HashValue& cert_hash,
403 base::SupportsUserData* debug_data) {
Matt Mueller5d105e72019-07-01 17:38:42404 auto cache_iter = trust_status_cache_.find(cert_hash);
405 if (cache_iter == trust_status_cache_.end()) {
406 // Cert does not have trust settings in this domain, return UNSPECIFIED.
Matt Muellerb3c3cbc2021-03-30 20:20:11407 UpdateUserData(0, debug_data, TrustStoreMac::TrustImplType::kDomainCache);
Matt Mueller5d105e72019-07-01 17:38:42408 return TrustStatus::UNSPECIFIED;
409 }
410
Matt Mueller370a7fda2019-08-22 00:38:59411 if (cache_iter->second.trust_status != TrustStatus::UNKNOWN) {
Matt Mueller5d105e72019-07-01 17:38:42412 // Cert has trust settings and trust has already been calculated, return
413 // the cached value.
Matt Mueller1b4c2342021-03-16 17:57:04414 UpdateUserData(cache_iter->second.debug_info, debug_data,
415 TrustStoreMac::TrustImplType::kDomainCache);
Matt Mueller370a7fda2019-08-22 00:38:59416 return cache_iter->second.trust_status;
Matt Mueller5d105e72019-07-01 17:38:42417 }
418
419 // Cert has trust settings but trust has not been calculated yet.
420 // Calculate it now, insert into cache, and return.
Matt Mueller370a7fda2019-08-22 00:38:59421 TrustStatus cert_trust = IsCertificateTrustedForPolicyInDomain(
422 cert, policy_oid_, domain_, &cache_iter->second.debug_info);
423 cache_iter->second.trust_status = cert_trust;
Matt Mueller1b4c2342021-03-16 17:57:04424 UpdateUserData(cache_iter->second.debug_info, debug_data,
425 TrustStoreMac::TrustImplType::kDomainCache);
Matt Mueller5d105e72019-07-01 17:38:42426 return cert_trust;
427 }
428
429 // Returns true if the certificate with |cert_hash| is present in |domain_|.
430 bool ContainsCert(const SHA256HashValue& cert_hash) const {
431 return trust_status_cache_.find(cert_hash) != trust_status_cache_.end();
432 }
433
434 private:
435 const SecTrustSettingsDomain domain_;
436 const CFStringRef policy_oid_;
Matt Mueller370a7fda2019-08-22 00:38:59437 base::flat_map<SHA256HashValue, TrustStatusDetails> trust_status_cache_;
Matt Mueller5d105e72019-07-01 17:38:42438
439 DISALLOW_COPY_AND_ASSIGN(TrustDomainCache);
440};
441
442SHA256HashValue CalculateFingerprint256(const der::Input& buffer) {
443 SHA256HashValue sha256;
444 SHA256(buffer.UnsafeData(), buffer.Length(), sha256.data);
445 return sha256;
mattmea4ed8232017-02-28 23:13:23446}
447
Matt Mueller5d105e72019-07-01 17:38:42448// Watches macOS keychain for trust setting changes, and notifies any
449// registered callbacks. This is necessary as the keychain callback API is
450// keyed only on the callback function pointer rather than function pointer +
451// context, so it cannot be safely registered multiple callbacks with the same
452// function pointer and different contexts.
453class KeychainTrustSettingsChangedNotifier {
454 public:
455 // Registers |callback| to be run when the keychain trust settings change.
456 // Must be called on the network notification thread. |callback| will be run
Peter Kasting7ba9440c2020-11-22 01:49:02457 // on the network notification thread. The returned subscription must be
Matt Mueller5d105e72019-07-01 17:38:42458 // destroyed on the network notification thread.
Peter Kasting7ba9440c2020-11-22 01:49:02459 static base::CallbackListSubscription AddCallback(
Matt Mueller5d105e72019-07-01 17:38:42460 base::RepeatingClosure callback) {
461 DCHECK(GetNetworkNotificationThreadMac()->RunsTasksInCurrentSequence());
462 return Get()->callback_list_.Add(std::move(callback));
463 }
464
465 private:
466 friend base::NoDestructor<KeychainTrustSettingsChangedNotifier>;
467
468 KeychainTrustSettingsChangedNotifier() {
469 DCHECK(GetNetworkNotificationThreadMac()->RunsTasksInCurrentSequence());
470 OSStatus status = SecKeychainAddCallback(
471 &KeychainTrustSettingsChangedNotifier::KeychainCallback,
472 kSecTrustSettingsChangedEventMask, this);
473 if (status != noErr)
474 OSSTATUS_LOG(ERROR, status) << "SecKeychainAddCallback failed";
475 }
476
477 ~KeychainTrustSettingsChangedNotifier() = delete;
478
479 static OSStatus KeychainCallback(SecKeychainEvent keychain_event,
480 SecKeychainCallbackInfo* info,
481 void* context) {
482 KeychainTrustSettingsChangedNotifier* notifier =
483 reinterpret_cast<KeychainTrustSettingsChangedNotifier*>(context);
484 notifier->callback_list_.Notify();
485 return errSecSuccess;
486 }
487
488 static KeychainTrustSettingsChangedNotifier* Get() {
489 static base::NoDestructor<KeychainTrustSettingsChangedNotifier> notifier;
490 return notifier.get();
491 }
492
Peter Kasting11c507a42020-08-17 19:11:10493 base::RepeatingClosureList callback_list_;
Matt Mueller5d105e72019-07-01 17:38:42494
495 DISALLOW_COPY_AND_ASSIGN(KeychainTrustSettingsChangedNotifier);
496};
497
498// Observes keychain events and increments the value returned by Iteration()
499// each time the trust settings change.
500class KeychainTrustObserver {
501 public:
502 KeychainTrustObserver() {
503 GetNetworkNotificationThreadMac()->PostTask(
504 FROM_HERE,
505 base::BindOnce(
506 &KeychainTrustObserver::RegisterCallbackOnNotificationThread,
507 base::Unretained(this)));
508 }
509
510 // Destroying the observer unregisters the callback. Must be destroyed on the
511 // notification thread in order to safely release |subscription_|.
512 ~KeychainTrustObserver() {
513 DCHECK(GetNetworkNotificationThreadMac()->RunsTasksInCurrentSequence());
514 }
515
516 // Returns the current iteration count, which is incremented every time
517 // keychain trust settings change. This may be called from any thread.
518 int64_t Iteration() const { return base::subtle::Acquire_Load(&iteration_); }
519
520 private:
521 void RegisterCallbackOnNotificationThread() {
522 DCHECK(GetNetworkNotificationThreadMac()->RunsTasksInCurrentSequence());
523 subscription_ =
524 KeychainTrustSettingsChangedNotifier::AddCallback(base::BindRepeating(
525 &KeychainTrustObserver::Increment, base::Unretained(this)));
526 }
527
528 void Increment() { base::subtle::Barrier_AtomicIncrement(&iteration_, 1); }
529
530 // Only accessed on the notification thread.
Peter Kasting7ba9440c2020-11-22 01:49:02531 base::CallbackListSubscription subscription_;
Matt Mueller5d105e72019-07-01 17:38:42532
533 base::subtle::Atomic64 iteration_ = 0;
534
535 DISALLOW_COPY_AND_ASSIGN(KeychainTrustObserver);
536};
537
eroman2a938c32017-04-28 23:16:58538} // namespace
539
Matt Mueller370a7fda2019-08-22 00:38:59540// static
541const TrustStoreMac::ResultDebugData* TrustStoreMac::ResultDebugData::Get(
542 const base::SupportsUserData* debug_data) {
543 return static_cast<ResultDebugData*>(
544 debug_data->GetUserData(kResultDebugDataKey));
545}
546
547// static
548TrustStoreMac::ResultDebugData* TrustStoreMac::ResultDebugData::GetOrCreate(
549 base::SupportsUserData* debug_data) {
550 ResultDebugData* data = static_cast<ResultDebugData*>(
551 debug_data->GetUserData(kResultDebugDataKey));
552 if (!data) {
553 std::unique_ptr<ResultDebugData> new_data =
554 std::make_unique<ResultDebugData>();
555 data = new_data.get();
556 debug_data->SetUserData(kResultDebugDataKey, std::move(new_data));
557 }
558 return data;
559}
560
561void TrustStoreMac::ResultDebugData::UpdateTrustDebugInfo(
Matt Mueller1b4c2342021-03-16 17:57:04562 int trust_debug_info,
563 TrustImplType impl_type) {
Matt Mueller370a7fda2019-08-22 00:38:59564 combined_trust_debug_info_ |= trust_debug_info;
Matt Mueller1b4c2342021-03-16 17:57:04565 trust_impl_ = impl_type;
Matt Mueller370a7fda2019-08-22 00:38:59566}
567
568std::unique_ptr<base::SupportsUserData::Data>
569TrustStoreMac::ResultDebugData::Clone() {
570 return std::make_unique<ResultDebugData>(*this);
571}
572
Matt Mueller1b4c2342021-03-16 17:57:04573// Interface for different implementations of getting trust settings from the
574// Mac APIs. This abstraction can be removed once a single implementation has
575// been chosen and launched.
576class TrustStoreMac::TrustImpl {
Matt Mueller5d105e72019-07-01 17:38:42577 public:
Matt Mueller1b4c2342021-03-16 17:57:04578 virtual ~TrustImpl() = default;
579
580 virtual bool IsKnownRoot(const ParsedCertificate* cert) = 0;
581 virtual TrustStatus IsCertTrusted(
582 const scoped_refptr<ParsedCertificate>& cert,
583 base::SupportsUserData* debug_data) = 0;
584 virtual void InitializeTrustCache() = 0;
585};
586
587// TrustImplDomainCache uses SecTrustSettingsCopyCertificates to get the list
588// of certs in each trust domain and then caches the calculated trust status of
589// those certs on access, and ensures the cache is reset if trust settings are
590// modified.
591class TrustStoreMac::TrustImplDomainCache : public TrustStoreMac::TrustImpl {
592 public:
593 explicit TrustImplDomainCache(CFStringRef policy_oid)
Matt Mueller5d105e72019-07-01 17:38:42594 : system_domain_cache_(kSecTrustSettingsDomainSystem, policy_oid),
595 admin_domain_cache_(kSecTrustSettingsDomainAdmin, policy_oid),
596 user_domain_cache_(kSecTrustSettingsDomainUser, policy_oid) {
597 keychain_observer_ = std::make_unique<KeychainTrustObserver>();
598 }
599
Matt Mueller1b4c2342021-03-16 17:57:04600 ~TrustImplDomainCache() override {
Matt Mueller5d105e72019-07-01 17:38:42601 GetNetworkNotificationThreadMac()->DeleteSoon(
602 FROM_HERE, std::move(keychain_observer_));
603 }
604
605 // Returns true if |cert| is present in kSecTrustSettingsDomainSystem.
Matt Mueller1b4c2342021-03-16 17:57:04606 bool IsKnownRoot(const ParsedCertificate* cert) override {
Matt Mueller5d105e72019-07-01 17:38:42607 SHA256HashValue cert_hash = CalculateFingerprint256(cert->der_cert());
608
609 base::AutoLock lock(cache_lock_);
610 MaybeInitializeCache();
611 return system_domain_cache_.ContainsCert(cert_hash);
612 }
613
614 // Returns the trust status for |cert|.
Matt Mueller370a7fda2019-08-22 00:38:59615 TrustStatus IsCertTrusted(const scoped_refptr<ParsedCertificate>& cert,
Matt Mueller1b4c2342021-03-16 17:57:04616 base::SupportsUserData* debug_data) override {
Matt Mueller5d105e72019-07-01 17:38:42617 SHA256HashValue cert_hash = CalculateFingerprint256(cert->der_cert());
618
619 base::AutoLock lock(cache_lock_);
620 MaybeInitializeCache();
621
622 // Evaluate trust domains in user, admin, system order. Admin settings can
623 // override system ones, and user settings can override both admin and
624 // system.
625 for (TrustDomainCache* trust_domain_cache :
626 {&user_domain_cache_, &admin_domain_cache_, &system_domain_cache_}) {
Matt Mueller370a7fda2019-08-22 00:38:59627 TrustStatus ts =
628 trust_domain_cache->IsCertTrusted(cert, cert_hash, debug_data);
Matt Mueller5d105e72019-07-01 17:38:42629 if (ts != TrustStatus::UNSPECIFIED)
630 return ts;
631 }
632
633 // Cert did not have trust settings in any domain.
634 return TrustStatus::UNSPECIFIED;
635 }
636
Matt Muellerd0506dd82020-12-17 02:25:24637 // Initializes the cache, if it isn't already initialized.
Matt Mueller1b4c2342021-03-16 17:57:04638 void InitializeTrustCache() override {
Matt Muellerd0506dd82020-12-17 02:25:24639 base::AutoLock lock(cache_lock_);
640 MaybeInitializeCache();
641 }
642
Matt Mueller5d105e72019-07-01 17:38:42643 private:
644 // (Re-)Initialize the cache if necessary. Must be called after acquiring
645 // |cache_lock_| and before accessing any of the |*_domain_cache_| members.
646 void MaybeInitializeCache() {
647 cache_lock_.AssertAcquired();
648 int64_t keychain_iteration = keychain_observer_->Iteration();
649 if (iteration_ == keychain_iteration)
650 return;
651
652 iteration_ = keychain_iteration;
653 user_domain_cache_.Initialize();
654 admin_domain_cache_.Initialize();
655 if (!system_domain_initialized_) {
656 // In practice, the system trust domain does not change during runtime,
657 // and SecTrustSettingsCopyCertificates on the system domain is quite
658 // slow, so the system domain cache is not reset on keychain changes.
659 system_domain_cache_.Initialize();
660 system_domain_initialized_ = true;
661 }
662 }
663
664 std::unique_ptr<KeychainTrustObserver> keychain_observer_;
665
666 base::Lock cache_lock_;
667 // |cache_lock_| must be held while accessing any following members.
668 int64_t iteration_ = -1;
669 bool system_domain_initialized_ = false;
670 TrustDomainCache system_domain_cache_;
671 TrustDomainCache admin_domain_cache_;
672 TrustDomainCache user_domain_cache_;
673
Matt Mueller1b4c2342021-03-16 17:57:04674 DISALLOW_COPY_AND_ASSIGN(TrustImplDomainCache);
Matt Mueller5d105e72019-07-01 17:38:42675};
676
Matt Mueller1b4c2342021-03-16 17:57:04677// TrustImplNoCache is the simplest approach which calls
678// SecTrustSettingsCopyTrustSettings on every cert checked, with no caching.
679class TrustStoreMac::TrustImplNoCache : public TrustStoreMac::TrustImpl {
680 public:
681 explicit TrustImplNoCache(CFStringRef policy_oid) : policy_oid_(policy_oid) {}
682
683 ~TrustImplNoCache() override = default;
684
685 // Returns true if |cert| is present in kSecTrustSettingsDomainSystem.
686 bool IsKnownRoot(const ParsedCertificate* cert) override {
687 HashValue cert_hash(CalculateFingerprint256(cert->der_cert()));
Matt Muellerccc7913a2021-03-23 02:26:00688 base::AutoLock lock(crypto::GetMacSecurityServicesLock());
Matt Mueller1b4c2342021-03-16 17:57:04689 return net::IsKnownRoot(cert_hash);
690 }
691
692 // Returns the trust status for |cert|.
693 TrustStatus IsCertTrusted(const scoped_refptr<ParsedCertificate>& cert,
694 base::SupportsUserData* debug_data) override {
695 int debug_info = 0;
Matt Mueller7ac4af5c2021-03-30 18:58:53696 TrustStatus result = IsCertificateTrustedForPolicy(
697 cert.get(), policy_oid_, &debug_info, /*out_is_known_root=*/nullptr);
Matt Mueller1b4c2342021-03-16 17:57:04698 UpdateUserData(debug_info, debug_data,
699 TrustStoreMac::TrustImplType::kSimple);
700 return result;
701 }
702
703 void InitializeTrustCache() override {
704 // No-op for this impl.
705 }
706
707 private:
708 const CFStringRef policy_oid_;
709
710 DISALLOW_COPY_AND_ASSIGN(TrustImplNoCache);
711};
712
713// TrustImplMRUCache is calls SecTrustSettingsCopyTrustSettings on every cert
714// checked, but caches the results in an MRU cache. The cache is cleared on
715// keychain updates.
716class TrustStoreMac::TrustImplMRUCache : public TrustStoreMac::TrustImpl {
717 public:
718 TrustImplMRUCache(CFStringRef policy_oid, size_t cache_size)
719 : policy_oid_(policy_oid), trust_status_cache_(cache_size) {
720 keychain_observer_ = std::make_unique<KeychainTrustObserver>();
721 }
722
723 ~TrustImplMRUCache() override {
724 GetNetworkNotificationThreadMac()->DeleteSoon(
725 FROM_HERE, std::move(keychain_observer_));
726 }
727
Matt Mueller7ac4af5c2021-03-30 18:58:53728 // Returns true if |cert| has trust settings in kSecTrustSettingsDomainSystem.
Matt Mueller1b4c2342021-03-16 17:57:04729 bool IsKnownRoot(const ParsedCertificate* cert) override {
Matt Mueller7ac4af5c2021-03-30 18:58:53730 return GetKnownRootStatus(cert) == KnownRootStatus::IS_KNOWN_ROOT;
Matt Mueller1b4c2342021-03-16 17:57:04731 }
732
733 // Returns the trust status for |cert|.
734 TrustStatus IsCertTrusted(const scoped_refptr<ParsedCertificate>& cert,
735 base::SupportsUserData* debug_data) override {
Matt Mueller7ac4af5c2021-03-30 18:58:53736 TrustStatusDetails trust_details = GetTrustStatus(cert.get());
Matt Mueller1b4c2342021-03-16 17:57:04737 UpdateUserData(trust_details.debug_info, debug_data,
738 TrustStoreMac::TrustImplType::kMruCache);
739 return trust_details.trust_status;
740 }
741
742 void InitializeTrustCache() override {
743 // No-op for this impl.
744 }
745
746 private:
747 struct TrustStatusDetails {
748 TrustStatus trust_status = TrustStatus::UNKNOWN;
749 int debug_info = 0;
Matt Mueller7ac4af5c2021-03-30 18:58:53750 KnownRootStatus is_known_root = KnownRootStatus::UNKNOWN;
Matt Mueller1b4c2342021-03-16 17:57:04751 };
752
Matt Mueller7ac4af5c2021-03-30 18:58:53753 KnownRootStatus GetKnownRootStatus(const ParsedCertificate* cert) {
754 SHA256HashValue cert_hash = CalculateFingerprint256(cert->der_cert());
755
756 int starting_cache_iteration = -1;
757 {
758 base::AutoLock lock(cache_lock_);
759 MaybeResetCache();
760 starting_cache_iteration = iteration_;
761 auto cache_iter = trust_status_cache_.Get(cert_hash);
762 if (cache_iter != trust_status_cache_.end() &&
763 cache_iter->second.is_known_root != KnownRootStatus::UNKNOWN) {
764 return cache_iter->second.is_known_root;
765 }
766 }
767
768 KnownRootStatus is_known_root = IsCertificateKnownRoot(cert);
769
770 {
771 base::AutoLock lock(cache_lock_);
772 MaybeResetCache();
773 if (iteration_ != starting_cache_iteration)
774 return is_known_root;
775
776 auto cache_iter = trust_status_cache_.Get(cert_hash);
777 // Update |is_known_root| on existing cache entry if there is one,
778 // otherwise create a new cache entry.
779 if (cache_iter != trust_status_cache_.end()) {
780 cache_iter->second.is_known_root = is_known_root;
781 } else {
782 TrustStatusDetails trust_details;
783 trust_details.is_known_root = is_known_root;
784 trust_status_cache_.Put(cert_hash, trust_details);
785 }
786 }
787 return is_known_root;
788 }
789
790 TrustStatusDetails GetTrustStatus(const ParsedCertificate* cert) {
791 SHA256HashValue cert_hash = CalculateFingerprint256(cert->der_cert());
792 TrustStatusDetails trust_details;
793
794 int starting_cache_iteration = -1;
795 {
796 base::AutoLock lock(cache_lock_);
797 MaybeResetCache();
798 starting_cache_iteration = iteration_;
799 auto cache_iter = trust_status_cache_.Get(cert_hash);
800 if (cache_iter != trust_status_cache_.end()) {
801 if (cache_iter->second.trust_status != TrustStatus::UNKNOWN)
802 return cache_iter->second;
803 // If there was a cache entry but the trust status was not initialized,
804 // copy the existing values. (|is_known_root| might already be cached.)
805 trust_details = cache_iter->second;
806 }
807 }
808
809 trust_details.trust_status = IsCertificateTrustedForPolicy(
810 cert, policy_oid_, &trust_details.debug_info,
811 &trust_details.is_known_root);
812
813 {
814 base::AutoLock lock(cache_lock_);
815 MaybeResetCache();
816 if (iteration_ != starting_cache_iteration)
817 return trust_details;
818 trust_status_cache_.Put(cert_hash, trust_details);
819 }
820 return trust_details;
821 }
822
Matt Mueller1b4c2342021-03-16 17:57:04823 void MaybeResetCache() {
824 cache_lock_.AssertAcquired();
825 int64_t keychain_iteration = keychain_observer_->Iteration();
826 if (iteration_ == keychain_iteration)
827 return;
828 iteration_ = keychain_iteration;
829 trust_status_cache_.Clear();
830 }
831
832 const CFStringRef policy_oid_;
833 std::unique_ptr<KeychainTrustObserver> keychain_observer_;
834
835 base::Lock cache_lock_;
836 // |cache_lock_| must be held while accessing any following members.
837 base::MRUCache<SHA256HashValue, TrustStatusDetails> trust_status_cache_;
Matt Mueller7ac4af5c2021-03-30 18:58:53838 // Tracks the number of keychain changes that have been observed. If the
839 // keychain observer has noted a change, MaybeResetCache will update
840 // |iteration_| and the cache will be cleared. Any in-flight trust
841 // resolutions that started before the keychain update was observed should
842 // not cache their results, as it isn't clear whether the calculated result
843 // applies to the new or old trust settings.
Matt Mueller1b4c2342021-03-16 17:57:04844 int64_t iteration_ = -1;
845
846 DISALLOW_COPY_AND_ASSIGN(TrustImplMRUCache);
847};
848
Matt Mueller884cf6862021-03-16 20:29:32849TrustStoreMac::TrustStoreMac(CFStringRef policy_oid,
850 TrustImplType impl,
851 size_t cache_size) {
Matt Mueller1b4c2342021-03-16 17:57:04852 switch (impl) {
853 case TrustImplType::kUnknown:
854 DCHECK(false);
855 break;
856 case TrustImplType::kDomainCache:
857 trust_cache_ = std::make_unique<TrustImplDomainCache>(policy_oid);
858 break;
859 case TrustImplType::kSimple:
860 trust_cache_ = std::make_unique<TrustImplNoCache>(policy_oid);
861 break;
862 case TrustImplType::kMruCache:
863 trust_cache_ =
864 std::make_unique<TrustImplMRUCache>(policy_oid, cache_size);
865 break;
866 }
867}
eroman2a938c32017-04-28 23:16:58868
869TrustStoreMac::~TrustStoreMac() = default;
870
Matt Muellerd0506dd82020-12-17 02:25:24871void TrustStoreMac::InitializeTrustCache() const {
872 trust_cache_->InitializeTrustCache();
873}
874
Matt Mueller5d105e72019-07-01 17:38:42875bool TrustStoreMac::IsKnownRoot(const ParsedCertificate* cert) const {
876 return trust_cache_->IsKnownRoot(cert);
877}
878
eroman2a938c32017-04-28 23:16:58879void TrustStoreMac::SyncGetIssuersOf(const ParsedCertificate* cert,
880 ParsedCertificateList* issuers) {
881 base::ScopedCFTypeRef<CFDataRef> name_data = GetMacNormalizedIssuer(cert);
Eric Romancdc12c82017-10-18 03:34:06882 if (!name_data)
883 return;
eroman2a938c32017-04-28 23:16:58884
885 base::ScopedCFTypeRef<CFArrayRef> matching_items =
886 FindMatchingCertificatesForMacNormalizedSubject(name_data);
887 if (!matching_items)
888 return;
889
890 // Convert to ParsedCertificate.
mattmea4ed8232017-02-28 23:13:23891 for (CFIndex i = 0, item_count = CFArrayGetCount(matching_items);
892 i < item_count; ++i) {
893 SecCertificateRef match_cert_handle = reinterpret_cast<SecCertificateRef>(
894 const_cast<void*>(CFArrayGetValueAtIndex(matching_items, i)));
895
mattmea4ed8232017-02-28 23:13:23896 base::ScopedCFTypeRef<CFDataRef> der_data(
897 SecCertificateCopyData(match_cert_handle));
898 if (!der_data) {
899 LOG(ERROR) << "SecCertificateCopyData error";
900 continue;
901 }
902
903 CertErrors errors;
904 ParseCertificateOptions options;
905 options.allow_invalid_serial_numbers = true;
906 scoped_refptr<ParsedCertificate> anchor_cert = ParsedCertificate::Create(
907 x509_util::CreateCryptoBuffer(CFDataGetBytePtr(der_data.get()),
908 CFDataGetLength(der_data.get())),
909 options, &errors);
910 if (!anchor_cert) {
911 // TODO(crbug.com/634443): return errors better.
912 LOG(ERROR) << "Error parsing issuer certificate:\n"
913 << errors.ToDebugString();
914 continue;
915 }
916
eroman2a938c32017-04-28 23:16:58917 issuers->push_back(std::move(anchor_cert));
mattmea4ed8232017-02-28 23:13:23918 }
919}
920
eroman2a938c32017-04-28 23:16:58921void TrustStoreMac::GetTrust(const scoped_refptr<ParsedCertificate>& cert,
Matt Mueller2feb4fe2019-08-21 17:35:49922 CertificateTrust* trust,
923 base::SupportsUserData* debug_data) const {
Matt Mueller370a7fda2019-08-22 00:38:59924 TrustStatus trust_status = trust_cache_->IsCertTrusted(cert, debug_data);
eroman2a938c32017-04-28 23:16:58925 switch (trust_status) {
926 case TrustStatus::TRUSTED:
927 *trust = CertificateTrust::ForTrustAnchor();
928 return;
929 case TrustStatus::DISTRUSTED:
930 *trust = CertificateTrust::ForDistrusted();
931 return;
932 case TrustStatus::UNSPECIFIED:
933 *trust = CertificateTrust::ForUnspecified();
934 return;
Matt Mueller5d105e72019-07-01 17:38:42935 case TrustStatus::UNKNOWN:
Matt Mueller1b4c2342021-03-16 17:57:04936 // UNKNOWN is an implementation detail of TrustImpl and should never be
Matt Mueller5d105e72019-07-01 17:38:42937 // returned.
938 NOTREACHED();
939 break;
eroman2a938c32017-04-28 23:16:58940 }
mattmea4ed8232017-02-28 23:13:23941
eroman2a938c32017-04-28 23:16:58942 *trust = CertificateTrust::ForUnspecified();
943 return;
mattmea4ed8232017-02-28 23:13:23944}
945
946// static
947base::ScopedCFTypeRef<CFArrayRef>
948TrustStoreMac::FindMatchingCertificatesForMacNormalizedSubject(
949 CFDataRef name_data) {
950 base::ScopedCFTypeRef<CFArrayRef> matching_items;
951 base::ScopedCFTypeRef<CFMutableDictionaryRef> query(
952 CFDictionaryCreateMutable(nullptr, 0, &kCFTypeDictionaryKeyCallBacks,
953 &kCFTypeDictionaryValueCallBacks));
954
955 CFDictionarySetValue(query, kSecClass, kSecClassCertificate);
956 CFDictionarySetValue(query, kSecReturnRef, kCFBooleanTrue);
957 CFDictionarySetValue(query, kSecMatchLimit, kSecMatchLimitAll);
958 CFDictionarySetValue(query, kSecAttrSubject, name_data);
959
960 base::ScopedCFTypeRef<CFArrayRef> scoped_alternate_keychain_search_list;
961 if (TestKeychainSearchList::HasInstance()) {
962 OSStatus status = TestKeychainSearchList::GetInstance()->CopySearchList(
963 scoped_alternate_keychain_search_list.InitializeInto());
964 if (status) {
965 OSSTATUS_LOG(ERROR, status)
966 << "TestKeychainSearchList::CopySearchList error";
967 return matching_items;
968 }
969 }
970
971 base::AutoLock lock(crypto::GetMacSecurityServicesLock());
972
973 // If a TestKeychainSearchList is present, it will have already set
974 // |scoped_alternate_keychain_search_list|, which will be used as the
975 // basis for reordering the keychain. Otherwise, get the current keychain
976 // search list and use that.
977 if (!scoped_alternate_keychain_search_list) {
978 OSStatus status = SecKeychainCopySearchList(
979 scoped_alternate_keychain_search_list.InitializeInto());
980 if (status) {
981 OSSTATUS_LOG(ERROR, status) << "SecKeychainCopySearchList error";
982 return matching_items;
983 }
984 }
985
986 CFMutableArrayRef mutable_keychain_search_list = CFArrayCreateMutableCopy(
987 kCFAllocatorDefault,
988 CFArrayGetCount(scoped_alternate_keychain_search_list.get()) + 1,
989 scoped_alternate_keychain_search_list.get());
990 if (!mutable_keychain_search_list) {
991 LOG(ERROR) << "CFArrayCreateMutableCopy";
992 return matching_items;
993 }
994 scoped_alternate_keychain_search_list.reset(mutable_keychain_search_list);
995
996 base::ScopedCFTypeRef<SecKeychainRef> roots_keychain;
997 // The System Roots keychain is not normally searched by SecItemCopyMatching.
998 // Get a reference to it and include in the keychain search list.
999 OSStatus status = SecKeychainOpen(
1000 "/System/Library/Keychains/SystemRootCertificates.keychain",
1001 roots_keychain.InitializeInto());
1002 if (status) {
1003 OSSTATUS_LOG(ERROR, status) << "SecKeychainOpen error";
1004 return matching_items;
1005 }
1006 CFArrayAppendValue(mutable_keychain_search_list, roots_keychain);
1007
1008 CFDictionarySetValue(query, kSecMatchSearchList,
1009 scoped_alternate_keychain_search_list.get());
1010
1011 OSStatus err = SecItemCopyMatching(
1012 query, reinterpret_cast<CFTypeRef*>(matching_items.InitializeInto()));
1013 if (err == errSecItemNotFound) {
1014 // No matches found.
1015 return matching_items;
1016 }
1017 if (err) {
1018 OSSTATUS_LOG(ERROR, err) << "SecItemCopyMatching error";
1019 return matching_items;
1020 }
1021 return matching_items;
1022}
1023
1024// static
1025base::ScopedCFTypeRef<CFDataRef> TrustStoreMac::GetMacNormalizedIssuer(
eroman2a938c32017-04-28 23:16:581026 const ParsedCertificate* cert) {
mattmea4ed8232017-02-28 23:13:231027 base::ScopedCFTypeRef<CFDataRef> name_data;
1028 // There does not appear to be any public API to get the normalized version
1029 // of a Name without creating a SecCertificate.
1030 base::ScopedCFTypeRef<SecCertificateRef> cert_handle(
mattm4cede8d2017-04-11 02:55:011031 x509_util::CreateSecCertificateFromBytes(cert->der_cert().UnsafeData(),
1032 cert->der_cert().Length()));
mattmea4ed8232017-02-28 23:13:231033 if (!cert_handle) {
Matt Muellera4193272017-12-07 00:23:341034 LOG(ERROR) << "CreateCertBufferFromBytes";
mattmea4ed8232017-02-28 23:13:231035 return name_data;
1036 }
1037 {
1038 base::AutoLock lock(crypto::GetMacSecurityServicesLock());
1039 name_data.reset(
1040 SecCertificateCopyNormalizedIssuerContent(cert_handle, nullptr));
1041 }
1042 if (!name_data)
1043 LOG(ERROR) << "SecCertificateCopyNormalizedIssuerContent";
1044 return name_data;
1045}
1046
mattmea4ed8232017-02-28 23:13:231047} // namespace net