blob: e4c362f5c8ca2b8bf875736fd9570e7b22d6abab [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 Mueller370a7fda2019-08-22 00:38:5951const void* kResultDebugDataKey = &kResultDebugDataKey;
52
mattmea4ed8232017-02-28 23:13:2353// Returns trust status of usage constraints dictionary |trust_dict| for a
Matt Mueller59ebd7d2019-04-19 17:56:4054// certificate that |is_self_issued|.
mattmea4ed8232017-02-28 23:13:2355TrustStatus IsTrustDictionaryTrustedForPolicy(
56 CFDictionaryRef trust_dict,
Matt Mueller59ebd7d2019-04-19 17:56:4057 bool is_self_issued,
Matt Mueller370a7fda2019-08-22 00:38:5958 const CFStringRef target_policy_oid,
59 int* debug_info) {
mattmea4ed8232017-02-28 23:13:2360 // An empty trust dict should be interpreted as
61 // kSecTrustSettingsResultTrustRoot. This is handled by falling through all
62 // the conditions below with the default value of |trust_settings_result|.
Matt Mueller370a7fda2019-08-22 00:38:5963 CFIndex dict_size = CFDictionaryGetCount(trust_dict);
64 if (dict_size == 0)
65 *debug_info |= TrustStoreMac::TRUST_SETTINGS_DICT_EMPTY;
66
67 CFIndex known_elements = 0;
68 if (CFDictionaryContainsKey(trust_dict, kSecTrustSettingsPolicy)) {
69 *debug_info |= TrustStoreMac::TRUST_SETTINGS_DICT_CONTAINS_POLICY;
70 known_elements++;
71 }
72 if (CFDictionaryContainsKey(trust_dict, kSecTrustSettingsApplication)) {
73 *debug_info |= TrustStoreMac::TRUST_SETTINGS_DICT_CONTAINS_APPLICATION;
74 known_elements++;
75 }
76 if (CFDictionaryContainsKey(trust_dict, kSecTrustSettingsPolicyString)) {
77 *debug_info |= TrustStoreMac::TRUST_SETTINGS_DICT_CONTAINS_POLICY_STRING;
78 known_elements++;
79 }
80 if (CFDictionaryContainsKey(trust_dict, kSecTrustSettingsKeyUsage)) {
81 *debug_info |= TrustStoreMac::TRUST_SETTINGS_DICT_CONTAINS_KEY_USAGE;
82 known_elements++;
83 }
84 if (CFDictionaryContainsKey(trust_dict, kSecTrustSettingsResult)) {
85 *debug_info |= TrustStoreMac::TRUST_SETTINGS_DICT_CONTAINS_RESULT;
86 known_elements++;
87 }
88 if (CFDictionaryContainsKey(trust_dict, kSecTrustSettingsAllowedError)) {
89 *debug_info |= TrustStoreMac::TRUST_SETTINGS_DICT_CONTAINS_ALLOWED_ERROR;
90 known_elements++;
91 }
92 if (known_elements != dict_size)
93 *debug_info |= TrustStoreMac::TRUST_SETTINGS_DICT_UNKNOWN_KEY;
mattmea4ed8232017-02-28 23:13:2394
95 // Trust settings may be scoped to a single application, by checking that the
96 // code signing identity of the current application matches the serialized
97 // code signing identity in the kSecTrustSettingsApplication key.
98 // As this is not presently supported, skip any trust settings scoped to the
99 // application.
100 if (CFDictionaryContainsKey(trust_dict, kSecTrustSettingsApplication))
101 return TrustStatus::UNSPECIFIED;
102
103 // Trust settings may be scoped using policy-specific constraints. For
104 // example, SSL trust settings might be scoped to a single hostname, or EAP
105 // settings specific to a particular WiFi network.
106 // As this is not presently supported, skip any policy-specific trust
107 // settings.
108 if (CFDictionaryContainsKey(trust_dict, kSecTrustSettingsPolicyString))
109 return TrustStatus::UNSPECIFIED;
110
111 // Ignoring kSecTrustSettingsKeyUsage for now; it does not seem relevant to
112 // the TLS case.
113
114 // If the trust settings are scoped to a specific policy (via
115 // kSecTrustSettingsPolicy), ensure that the policy is the same policy as
116 // |target_policy_oid|. If there is no kSecTrustSettingsPolicy key, it's
117 // considered a match for all policies.
Matt Mueller370a7fda2019-08-22 00:38:59118 if (CFDictionaryContainsKey(trust_dict, kSecTrustSettingsPolicy)) {
119 SecPolicyRef policy_ref = base::mac::GetValueFromDictionary<SecPolicyRef>(
120 trust_dict, kSecTrustSettingsPolicy);
121 if (!policy_ref) {
122 *debug_info |= TrustStoreMac::TRUST_SETTINGS_DICT_INVALID_POLICY_TYPE;
123 return TrustStatus::UNSPECIFIED;
124 }
mattmea4ed8232017-02-28 23:13:23125 base::ScopedCFTypeRef<CFDictionaryRef> policy_dict;
126 {
127 base::AutoLock lock(crypto::GetMacSecurityServicesLock());
128 policy_dict.reset(SecPolicyCopyProperties(policy_ref));
129 }
130
131 // kSecPolicyOid is guaranteed to be present in the policy dictionary.
mattmea4ed8232017-02-28 23:13:23132 CFStringRef policy_oid = base::mac::GetValueFromDictionary<CFStringRef>(
Avi Drissman905bac72020-10-24 17:26:31133 policy_dict, kSecPolicyOid);
mattmea4ed8232017-02-28 23:13:23134
135 if (!CFEqual(policy_oid, target_policy_oid))
136 return TrustStatus::UNSPECIFIED;
137 }
138
139 // If kSecTrustSettingsResult is not present in the trust dict,
140 // kSecTrustSettingsResultTrustRoot is assumed.
141 int trust_settings_result = kSecTrustSettingsResultTrustRoot;
Matt Mueller370a7fda2019-08-22 00:38:59142 if (CFDictionaryContainsKey(trust_dict, kSecTrustSettingsResult)) {
143 CFNumberRef trust_settings_result_ref =
144 base::mac::GetValueFromDictionary<CFNumberRef>(trust_dict,
145 kSecTrustSettingsResult);
146 if (!trust_settings_result_ref ||
147 !CFNumberGetValue(trust_settings_result_ref, kCFNumberIntType,
148 &trust_settings_result)) {
149 *debug_info |= TrustStoreMac::TRUST_SETTINGS_DICT_INVALID_RESULT_TYPE;
150 return TrustStatus::UNSPECIFIED;
151 }
mattmea4ed8232017-02-28 23:13:23152 }
153
154 if (trust_settings_result == kSecTrustSettingsResultDeny)
155 return TrustStatus::DISTRUSTED;
156
Matt Mueller59ebd7d2019-04-19 17:56:40157 // This is a bit of a hack: if the cert is self-issued allow either
158 // kSecTrustSettingsResultTrustRoot or kSecTrustSettingsResultTrustAsRoot on
159 // the basis that SecTrustSetTrustSettings should not allow creating an
160 // invalid trust record in the first place. (The spec is that
mattmea4ed8232017-02-28 23:13:23161 // kSecTrustSettingsResultTrustRoot can only be applied to root(self-signed)
Matt Mueller59ebd7d2019-04-19 17:56:40162 // certs and kSecTrustSettingsResultTrustAsRoot is used for other certs.)
163 // This hack avoids having to check the signature on the cert which is slow
164 // if using the platform APIs, and may require supporting MD5 signature
165 // algorithms on some older OSX versions or locally added roots, which is
166 // undesirable in the built-in signature verifier.
167 if (is_self_issued) {
168 return (trust_settings_result == kSecTrustSettingsResultTrustRoot ||
169 trust_settings_result == kSecTrustSettingsResultTrustAsRoot)
mattmea4ed8232017-02-28 23:13:23170 ? TrustStatus::TRUSTED
171 : TrustStatus::UNSPECIFIED;
Matt Mueller59ebd7d2019-04-19 17:56:40172 }
mattmea4ed8232017-02-28 23:13:23173
174 // kSecTrustSettingsResultTrustAsRoot can only be applied to non-root certs.
175 return (trust_settings_result == kSecTrustSettingsResultTrustAsRoot)
176 ? TrustStatus::TRUSTED
177 : TrustStatus::UNSPECIFIED;
178}
179
180// Returns true if the trust settings array |trust_settings| for a certificate
Matt Mueller59ebd7d2019-04-19 17:56:40181// that |is_self_issued| should be treated as a trust anchor.
mattmea4ed8232017-02-28 23:13:23182TrustStatus IsTrustSettingsTrustedForPolicy(CFArrayRef trust_settings,
Matt Mueller59ebd7d2019-04-19 17:56:40183 bool is_self_issued,
Matt Mueller370a7fda2019-08-22 00:38:59184 const CFStringRef policy_oid,
185 int* debug_info) {
mattmea4ed8232017-02-28 23:13:23186 // An empty trust settings array (that is, the trust_settings parameter
187 // returns a valid but empty CFArray) means "always trust this certificate"
188 // with an overall trust setting for the certificate of
189 // kSecTrustSettingsResultTrustRoot.
Matt Mueller370a7fda2019-08-22 00:38:59190 if (CFArrayGetCount(trust_settings) == 0) {
191 *debug_info |= TrustStoreMac::TRUST_SETTINGS_ARRAY_EMPTY;
192 return is_self_issued ? TrustStatus::TRUSTED : TrustStatus::UNSPECIFIED;
193 }
mattmea4ed8232017-02-28 23:13:23194
195 for (CFIndex i = 0, settings_count = CFArrayGetCount(trust_settings);
196 i < settings_count; ++i) {
197 CFDictionaryRef trust_dict = reinterpret_cast<CFDictionaryRef>(
198 const_cast<void*>(CFArrayGetValueAtIndex(trust_settings, i)));
199 TrustStatus trust = IsTrustDictionaryTrustedForPolicy(
Matt Mueller370a7fda2019-08-22 00:38:59200 trust_dict, is_self_issued, policy_oid, debug_info);
mattmea4ed8232017-02-28 23:13:23201 if (trust != TrustStatus::UNSPECIFIED)
202 return trust;
203 }
204 return TrustStatus::UNSPECIFIED;
205}
206
Matt Mueller5d105e72019-07-01 17:38:42207// Returns the trust status for |cert_handle| for the policy |policy_oid| in
208// |trust_domain|.
Matt Mueller1b4c2342021-03-16 17:57:04209TrustStatus IsSecCertificateTrustedForPolicyInDomain(
210 SecCertificateRef cert_handle,
211 const bool is_self_issued,
212 const CFStringRef policy_oid,
213 SecTrustSettingsDomain trust_domain,
214 int* debug_info) {
215 base::ScopedCFTypeRef<CFArrayRef> trust_settings;
216 OSStatus err;
217 {
218 base::AutoLock lock(crypto::GetMacSecurityServicesLock());
219 err = SecTrustSettingsCopyTrustSettings(cert_handle, trust_domain,
220 trust_settings.InitializeInto());
221 }
222 if (err == errSecItemNotFound) {
223 // No trust settings for that domain.. try the next.
224 return TrustStatus::UNSPECIFIED;
225 }
226 if (err) {
227 OSSTATUS_LOG(ERROR, err) << "SecTrustSettingsCopyTrustSettings error";
228 return TrustStatus::UNSPECIFIED;
229 }
230 TrustStatus trust = IsTrustSettingsTrustedForPolicy(
231 trust_settings, is_self_issued, policy_oid, debug_info);
232 return trust;
233}
234
Matt Mueller5d105e72019-07-01 17:38:42235TrustStatus IsCertificateTrustedForPolicyInDomain(
Matt Mueller59ebd7d2019-04-19 17:56:40236 const scoped_refptr<ParsedCertificate>& cert,
Matt Mueller5d105e72019-07-01 17:38:42237 const CFStringRef policy_oid,
Matt Mueller370a7fda2019-08-22 00:38:59238 SecTrustSettingsDomain trust_domain,
239 int* debug_info) {
Matt Mueller5d105e72019-07-01 17:38:42240 // TODO(eroman): Inefficient -- path building will convert between
241 // SecCertificateRef and ParsedCertificate representations multiple times
242 // (when getting the issuers, and again here).
243 //
244 // This conversion will also be done for each domain the cert policy is
245 // checked, but the TrustDomainCache ensures this function is only called on
246 // domains that actually have settings for the cert. The common case is that
247 // a cert will have trust settings in only zero or one domains, and when in
248 // more than one domain it would generally be because one domain is
249 // overriding the setting in the next, so it would only get done once anyway.
250 base::ScopedCFTypeRef<SecCertificateRef> cert_handle =
251 x509_util::CreateSecCertificateFromBytes(cert->der_cert().UnsafeData(),
252 cert->der_cert().Length());
253 if (!cert_handle)
254 return TrustStatus::UNSPECIFIED;
255
Matt Mueller59ebd7d2019-04-19 17:56:40256 const bool is_self_issued =
257 cert->normalized_subject() == cert->normalized_issuer();
Matt Mueller5d105e72019-07-01 17:38:42258
Matt Mueller1b4c2342021-03-16 17:57:04259 return IsSecCertificateTrustedForPolicyInDomain(
260 cert_handle, is_self_issued, policy_oid, trust_domain, debug_info);
Matt Mueller5d105e72019-07-01 17:38:42261}
262
Matt Mueller1b4c2342021-03-16 17:57:04263TrustStatus IsCertificateTrustedForPolicy(
264 const scoped_refptr<ParsedCertificate>& cert,
265 const CFStringRef policy_oid,
266 int* debug_info) {
267 base::ScopedCFTypeRef<SecCertificateRef> cert_handle =
268 x509_util::CreateSecCertificateFromBytes(cert->der_cert().UnsafeData(),
269 cert->der_cert().Length());
270 if (!cert_handle)
271 return TrustStatus::UNSPECIFIED;
272
273 const bool is_self_issued =
274 cert->normalized_subject() == cert->normalized_issuer();
275
276 // Evaluate trust domains in user, admin, system order. Admin settings can
277 // override system ones, and user settings can override both admin and system.
278 for (const auto& trust_domain :
279 {kSecTrustSettingsDomainUser, kSecTrustSettingsDomainAdmin,
280 kSecTrustSettingsDomainSystem}) {
281 base::ScopedCFTypeRef<CFArrayRef> trust_settings;
282 OSStatus err;
283 {
284 base::AutoLock lock(crypto::GetMacSecurityServicesLock());
285 err = SecTrustSettingsCopyTrustSettings(cert_handle, trust_domain,
286 trust_settings.InitializeInto());
287 }
288 if (err == errSecItemNotFound) {
289 // No trust settings for that domain.. try the next.
290 continue;
291 }
292 if (err) {
293 OSSTATUS_LOG(ERROR, err) << "SecTrustSettingsCopyTrustSettings error";
294 continue;
295 }
296 TrustStatus trust = IsTrustSettingsTrustedForPolicy(
297 trust_settings, is_self_issued, policy_oid, debug_info);
298 if (trust != TrustStatus::UNSPECIFIED)
299 return trust;
300 }
301
302 // No trust settings, or none of the settings were for the correct policy, or
303 // had the correct trust result.
304 return TrustStatus::UNSPECIFIED;
305}
306
307void UpdateUserData(int debug_info,
308 base::SupportsUserData* user_data,
309 TrustStoreMac::TrustImplType impl_type) {
Matt Mueller370a7fda2019-08-22 00:38:59310 if (!user_data)
311 return;
312 TrustStoreMac::ResultDebugData* result_debug_data =
313 TrustStoreMac::ResultDebugData::GetOrCreate(user_data);
Matt Mueller1b4c2342021-03-16 17:57:04314 result_debug_data->UpdateTrustDebugInfo(debug_info, impl_type);
Matt Mueller370a7fda2019-08-22 00:38:59315}
316
Matt Mueller5d105e72019-07-01 17:38:42317// Caches calculated trust status for certificates present in a single trust
318// domain.
319class TrustDomainCache {
320 public:
Matt Mueller370a7fda2019-08-22 00:38:59321 struct TrustStatusDetails {
322 TrustStatus trust_status = TrustStatus::UNKNOWN;
323 int debug_info = 0;
324 };
325
Matt Mueller5d105e72019-07-01 17:38:42326 TrustDomainCache(SecTrustSettingsDomain domain, CFStringRef policy_oid)
327 : domain_(domain), policy_oid_(policy_oid) {
328 DCHECK(policy_oid_);
mattmea4ed8232017-02-28 23:13:23329 }
330
Matt Mueller5d105e72019-07-01 17:38:42331 // (Re-)Initializes the cache with the certs in |domain_| set to UNKNOWN trust
332 // status.
333 void Initialize() {
334 trust_status_cache_.clear();
335
336 base::ScopedCFTypeRef<CFArrayRef> cert_array;
337 OSStatus rv;
338 {
339 base::AutoLock lock(crypto::GetMacSecurityServicesLock());
340 rv = SecTrustSettingsCopyCertificates(domain_,
341 cert_array.InitializeInto());
342 }
343 if (rv != noErr) {
344 // Note: SecTrustSettingsCopyCertificates can legitimately return
345 // errSecNoTrustSettings if there are no trust settings in |domain_|.
346 return;
347 }
Matt Mueller370a7fda2019-08-22 00:38:59348 std::vector<std::pair<SHA256HashValue, TrustStatusDetails>>
349 trust_status_vector;
Matt Mueller5d105e72019-07-01 17:38:42350 for (CFIndex i = 0, size = CFArrayGetCount(cert_array); i < size; ++i) {
351 SecCertificateRef cert = reinterpret_cast<SecCertificateRef>(
352 const_cast<void*>(CFArrayGetValueAtIndex(cert_array, i)));
353 trust_status_vector.emplace_back(x509_util::CalculateFingerprint256(cert),
Matt Mueller370a7fda2019-08-22 00:38:59354 TrustStatusDetails());
Matt Mueller5d105e72019-07-01 17:38:42355 }
Matt Mueller370a7fda2019-08-22 00:38:59356 trust_status_cache_ = base::flat_map<SHA256HashValue, TrustStatusDetails>(
Matt Mueller5d105e72019-07-01 17:38:42357 std::move(trust_status_vector));
358 }
359
360 // Returns the trust status for |cert| in |domain_|.
361 TrustStatus IsCertTrusted(const scoped_refptr<ParsedCertificate>& cert,
Matt Mueller370a7fda2019-08-22 00:38:59362 const SHA256HashValue& cert_hash,
363 base::SupportsUserData* debug_data) {
Matt Mueller5d105e72019-07-01 17:38:42364 auto cache_iter = trust_status_cache_.find(cert_hash);
365 if (cache_iter == trust_status_cache_.end()) {
366 // Cert does not have trust settings in this domain, return UNSPECIFIED.
367 return TrustStatus::UNSPECIFIED;
368 }
369
Matt Mueller370a7fda2019-08-22 00:38:59370 if (cache_iter->second.trust_status != TrustStatus::UNKNOWN) {
Matt Mueller5d105e72019-07-01 17:38:42371 // Cert has trust settings and trust has already been calculated, return
372 // the cached value.
Matt Mueller1b4c2342021-03-16 17:57:04373 UpdateUserData(cache_iter->second.debug_info, debug_data,
374 TrustStoreMac::TrustImplType::kDomainCache);
Matt Mueller370a7fda2019-08-22 00:38:59375 return cache_iter->second.trust_status;
Matt Mueller5d105e72019-07-01 17:38:42376 }
377
378 // Cert has trust settings but trust has not been calculated yet.
379 // Calculate it now, insert into cache, and return.
Matt Mueller370a7fda2019-08-22 00:38:59380 TrustStatus cert_trust = IsCertificateTrustedForPolicyInDomain(
381 cert, policy_oid_, domain_, &cache_iter->second.debug_info);
382 cache_iter->second.trust_status = cert_trust;
Matt Mueller1b4c2342021-03-16 17:57:04383 UpdateUserData(cache_iter->second.debug_info, debug_data,
384 TrustStoreMac::TrustImplType::kDomainCache);
Matt Mueller5d105e72019-07-01 17:38:42385 return cert_trust;
386 }
387
388 // Returns true if the certificate with |cert_hash| is present in |domain_|.
389 bool ContainsCert(const SHA256HashValue& cert_hash) const {
390 return trust_status_cache_.find(cert_hash) != trust_status_cache_.end();
391 }
392
393 private:
394 const SecTrustSettingsDomain domain_;
395 const CFStringRef policy_oid_;
Matt Mueller370a7fda2019-08-22 00:38:59396 base::flat_map<SHA256HashValue, TrustStatusDetails> trust_status_cache_;
Matt Mueller5d105e72019-07-01 17:38:42397
398 DISALLOW_COPY_AND_ASSIGN(TrustDomainCache);
399};
400
401SHA256HashValue CalculateFingerprint256(const der::Input& buffer) {
402 SHA256HashValue sha256;
403 SHA256(buffer.UnsafeData(), buffer.Length(), sha256.data);
404 return sha256;
mattmea4ed8232017-02-28 23:13:23405}
406
Matt Mueller5d105e72019-07-01 17:38:42407// Watches macOS keychain for trust setting changes, and notifies any
408// registered callbacks. This is necessary as the keychain callback API is
409// keyed only on the callback function pointer rather than function pointer +
410// context, so it cannot be safely registered multiple callbacks with the same
411// function pointer and different contexts.
412class KeychainTrustSettingsChangedNotifier {
413 public:
414 // Registers |callback| to be run when the keychain trust settings change.
415 // Must be called on the network notification thread. |callback| will be run
Peter Kasting7ba9440c2020-11-22 01:49:02416 // on the network notification thread. The returned subscription must be
Matt Mueller5d105e72019-07-01 17:38:42417 // destroyed on the network notification thread.
Peter Kasting7ba9440c2020-11-22 01:49:02418 static base::CallbackListSubscription AddCallback(
Matt Mueller5d105e72019-07-01 17:38:42419 base::RepeatingClosure callback) {
420 DCHECK(GetNetworkNotificationThreadMac()->RunsTasksInCurrentSequence());
421 return Get()->callback_list_.Add(std::move(callback));
422 }
423
424 private:
425 friend base::NoDestructor<KeychainTrustSettingsChangedNotifier>;
426
427 KeychainTrustSettingsChangedNotifier() {
428 DCHECK(GetNetworkNotificationThreadMac()->RunsTasksInCurrentSequence());
429 OSStatus status = SecKeychainAddCallback(
430 &KeychainTrustSettingsChangedNotifier::KeychainCallback,
431 kSecTrustSettingsChangedEventMask, this);
432 if (status != noErr)
433 OSSTATUS_LOG(ERROR, status) << "SecKeychainAddCallback failed";
434 }
435
436 ~KeychainTrustSettingsChangedNotifier() = delete;
437
438 static OSStatus KeychainCallback(SecKeychainEvent keychain_event,
439 SecKeychainCallbackInfo* info,
440 void* context) {
441 KeychainTrustSettingsChangedNotifier* notifier =
442 reinterpret_cast<KeychainTrustSettingsChangedNotifier*>(context);
443 notifier->callback_list_.Notify();
444 return errSecSuccess;
445 }
446
447 static KeychainTrustSettingsChangedNotifier* Get() {
448 static base::NoDestructor<KeychainTrustSettingsChangedNotifier> notifier;
449 return notifier.get();
450 }
451
Peter Kasting11c507a42020-08-17 19:11:10452 base::RepeatingClosureList callback_list_;
Matt Mueller5d105e72019-07-01 17:38:42453
454 DISALLOW_COPY_AND_ASSIGN(KeychainTrustSettingsChangedNotifier);
455};
456
457// Observes keychain events and increments the value returned by Iteration()
458// each time the trust settings change.
459class KeychainTrustObserver {
460 public:
461 KeychainTrustObserver() {
462 GetNetworkNotificationThreadMac()->PostTask(
463 FROM_HERE,
464 base::BindOnce(
465 &KeychainTrustObserver::RegisterCallbackOnNotificationThread,
466 base::Unretained(this)));
467 }
468
469 // Destroying the observer unregisters the callback. Must be destroyed on the
470 // notification thread in order to safely release |subscription_|.
471 ~KeychainTrustObserver() {
472 DCHECK(GetNetworkNotificationThreadMac()->RunsTasksInCurrentSequence());
473 }
474
475 // Returns the current iteration count, which is incremented every time
476 // keychain trust settings change. This may be called from any thread.
477 int64_t Iteration() const { return base::subtle::Acquire_Load(&iteration_); }
478
479 private:
480 void RegisterCallbackOnNotificationThread() {
481 DCHECK(GetNetworkNotificationThreadMac()->RunsTasksInCurrentSequence());
482 subscription_ =
483 KeychainTrustSettingsChangedNotifier::AddCallback(base::BindRepeating(
484 &KeychainTrustObserver::Increment, base::Unretained(this)));
485 }
486
487 void Increment() { base::subtle::Barrier_AtomicIncrement(&iteration_, 1); }
488
489 // Only accessed on the notification thread.
Peter Kasting7ba9440c2020-11-22 01:49:02490 base::CallbackListSubscription subscription_;
Matt Mueller5d105e72019-07-01 17:38:42491
492 base::subtle::Atomic64 iteration_ = 0;
493
494 DISALLOW_COPY_AND_ASSIGN(KeychainTrustObserver);
495};
496
eroman2a938c32017-04-28 23:16:58497} // namespace
498
Matt Mueller370a7fda2019-08-22 00:38:59499// static
500const TrustStoreMac::ResultDebugData* TrustStoreMac::ResultDebugData::Get(
501 const base::SupportsUserData* debug_data) {
502 return static_cast<ResultDebugData*>(
503 debug_data->GetUserData(kResultDebugDataKey));
504}
505
506// static
507TrustStoreMac::ResultDebugData* TrustStoreMac::ResultDebugData::GetOrCreate(
508 base::SupportsUserData* debug_data) {
509 ResultDebugData* data = static_cast<ResultDebugData*>(
510 debug_data->GetUserData(kResultDebugDataKey));
511 if (!data) {
512 std::unique_ptr<ResultDebugData> new_data =
513 std::make_unique<ResultDebugData>();
514 data = new_data.get();
515 debug_data->SetUserData(kResultDebugDataKey, std::move(new_data));
516 }
517 return data;
518}
519
520void TrustStoreMac::ResultDebugData::UpdateTrustDebugInfo(
Matt Mueller1b4c2342021-03-16 17:57:04521 int trust_debug_info,
522 TrustImplType impl_type) {
Matt Mueller370a7fda2019-08-22 00:38:59523 combined_trust_debug_info_ |= trust_debug_info;
Matt Mueller1b4c2342021-03-16 17:57:04524 trust_impl_ = impl_type;
Matt Mueller370a7fda2019-08-22 00:38:59525}
526
527std::unique_ptr<base::SupportsUserData::Data>
528TrustStoreMac::ResultDebugData::Clone() {
529 return std::make_unique<ResultDebugData>(*this);
530}
531
Matt Mueller1b4c2342021-03-16 17:57:04532// Interface for different implementations of getting trust settings from the
533// Mac APIs. This abstraction can be removed once a single implementation has
534// been chosen and launched.
535class TrustStoreMac::TrustImpl {
Matt Mueller5d105e72019-07-01 17:38:42536 public:
Matt Mueller1b4c2342021-03-16 17:57:04537 virtual ~TrustImpl() = default;
538
539 virtual bool IsKnownRoot(const ParsedCertificate* cert) = 0;
540 virtual TrustStatus IsCertTrusted(
541 const scoped_refptr<ParsedCertificate>& cert,
542 base::SupportsUserData* debug_data) = 0;
543 virtual void InitializeTrustCache() = 0;
544};
545
546// TrustImplDomainCache uses SecTrustSettingsCopyCertificates to get the list
547// of certs in each trust domain and then caches the calculated trust status of
548// those certs on access, and ensures the cache is reset if trust settings are
549// modified.
550class TrustStoreMac::TrustImplDomainCache : public TrustStoreMac::TrustImpl {
551 public:
552 explicit TrustImplDomainCache(CFStringRef policy_oid)
Matt Mueller5d105e72019-07-01 17:38:42553 : system_domain_cache_(kSecTrustSettingsDomainSystem, policy_oid),
554 admin_domain_cache_(kSecTrustSettingsDomainAdmin, policy_oid),
555 user_domain_cache_(kSecTrustSettingsDomainUser, policy_oid) {
556 keychain_observer_ = std::make_unique<KeychainTrustObserver>();
557 }
558
Matt Mueller1b4c2342021-03-16 17:57:04559 ~TrustImplDomainCache() override {
Matt Mueller5d105e72019-07-01 17:38:42560 GetNetworkNotificationThreadMac()->DeleteSoon(
561 FROM_HERE, std::move(keychain_observer_));
562 }
563
564 // Returns true if |cert| is present in kSecTrustSettingsDomainSystem.
Matt Mueller1b4c2342021-03-16 17:57:04565 bool IsKnownRoot(const ParsedCertificate* cert) override {
Matt Mueller5d105e72019-07-01 17:38:42566 SHA256HashValue cert_hash = CalculateFingerprint256(cert->der_cert());
567
568 base::AutoLock lock(cache_lock_);
569 MaybeInitializeCache();
570 return system_domain_cache_.ContainsCert(cert_hash);
571 }
572
573 // Returns the trust status for |cert|.
Matt Mueller370a7fda2019-08-22 00:38:59574 TrustStatus IsCertTrusted(const scoped_refptr<ParsedCertificate>& cert,
Matt Mueller1b4c2342021-03-16 17:57:04575 base::SupportsUserData* debug_data) override {
Matt Mueller5d105e72019-07-01 17:38:42576 SHA256HashValue cert_hash = CalculateFingerprint256(cert->der_cert());
577
578 base::AutoLock lock(cache_lock_);
579 MaybeInitializeCache();
580
581 // Evaluate trust domains in user, admin, system order. Admin settings can
582 // override system ones, and user settings can override both admin and
583 // system.
584 for (TrustDomainCache* trust_domain_cache :
585 {&user_domain_cache_, &admin_domain_cache_, &system_domain_cache_}) {
Matt Mueller370a7fda2019-08-22 00:38:59586 TrustStatus ts =
587 trust_domain_cache->IsCertTrusted(cert, cert_hash, debug_data);
Matt Mueller5d105e72019-07-01 17:38:42588 if (ts != TrustStatus::UNSPECIFIED)
589 return ts;
590 }
591
592 // Cert did not have trust settings in any domain.
593 return TrustStatus::UNSPECIFIED;
594 }
595
Matt Muellerd0506dd82020-12-17 02:25:24596 // Initializes the cache, if it isn't already initialized.
Matt Mueller1b4c2342021-03-16 17:57:04597 void InitializeTrustCache() override {
Matt Muellerd0506dd82020-12-17 02:25:24598 base::AutoLock lock(cache_lock_);
599 MaybeInitializeCache();
600 }
601
Matt Mueller5d105e72019-07-01 17:38:42602 private:
603 // (Re-)Initialize the cache if necessary. Must be called after acquiring
604 // |cache_lock_| and before accessing any of the |*_domain_cache_| members.
605 void MaybeInitializeCache() {
606 cache_lock_.AssertAcquired();
607 int64_t keychain_iteration = keychain_observer_->Iteration();
608 if (iteration_ == keychain_iteration)
609 return;
610
611 iteration_ = keychain_iteration;
612 user_domain_cache_.Initialize();
613 admin_domain_cache_.Initialize();
614 if (!system_domain_initialized_) {
615 // In practice, the system trust domain does not change during runtime,
616 // and SecTrustSettingsCopyCertificates on the system domain is quite
617 // slow, so the system domain cache is not reset on keychain changes.
618 system_domain_cache_.Initialize();
619 system_domain_initialized_ = true;
620 }
621 }
622
623 std::unique_ptr<KeychainTrustObserver> keychain_observer_;
624
625 base::Lock cache_lock_;
626 // |cache_lock_| must be held while accessing any following members.
627 int64_t iteration_ = -1;
628 bool system_domain_initialized_ = false;
629 TrustDomainCache system_domain_cache_;
630 TrustDomainCache admin_domain_cache_;
631 TrustDomainCache user_domain_cache_;
632
Matt Mueller1b4c2342021-03-16 17:57:04633 DISALLOW_COPY_AND_ASSIGN(TrustImplDomainCache);
Matt Mueller5d105e72019-07-01 17:38:42634};
635
Matt Mueller1b4c2342021-03-16 17:57:04636// TrustImplNoCache is the simplest approach which calls
637// SecTrustSettingsCopyTrustSettings on every cert checked, with no caching.
638class TrustStoreMac::TrustImplNoCache : public TrustStoreMac::TrustImpl {
639 public:
640 explicit TrustImplNoCache(CFStringRef policy_oid) : policy_oid_(policy_oid) {}
641
642 ~TrustImplNoCache() override = default;
643
644 // Returns true if |cert| is present in kSecTrustSettingsDomainSystem.
645 bool IsKnownRoot(const ParsedCertificate* cert) override {
646 HashValue cert_hash(CalculateFingerprint256(cert->der_cert()));
647 return net::IsKnownRoot(cert_hash);
648 }
649
650 // Returns the trust status for |cert|.
651 TrustStatus IsCertTrusted(const scoped_refptr<ParsedCertificate>& cert,
652 base::SupportsUserData* debug_data) override {
653 int debug_info = 0;
654 TrustStatus result =
655 IsCertificateTrustedForPolicy(cert, policy_oid_, &debug_info);
656 UpdateUserData(debug_info, debug_data,
657 TrustStoreMac::TrustImplType::kSimple);
658 return result;
659 }
660
661 void InitializeTrustCache() override {
662 // No-op for this impl.
663 }
664
665 private:
666 const CFStringRef policy_oid_;
667
668 DISALLOW_COPY_AND_ASSIGN(TrustImplNoCache);
669};
670
671// TrustImplMRUCache is calls SecTrustSettingsCopyTrustSettings on every cert
672// checked, but caches the results in an MRU cache. The cache is cleared on
673// keychain updates.
674class TrustStoreMac::TrustImplMRUCache : public TrustStoreMac::TrustImpl {
675 public:
676 TrustImplMRUCache(CFStringRef policy_oid, size_t cache_size)
677 : policy_oid_(policy_oid), trust_status_cache_(cache_size) {
678 keychain_observer_ = std::make_unique<KeychainTrustObserver>();
679 }
680
681 ~TrustImplMRUCache() override {
682 GetNetworkNotificationThreadMac()->DeleteSoon(
683 FROM_HERE, std::move(keychain_observer_));
684 }
685
686 // Returns true if |cert| is present in kSecTrustSettingsDomainSystem.
687 bool IsKnownRoot(const ParsedCertificate* cert) override {
688 HashValue cert_hash(CalculateFingerprint256(cert->der_cert()));
689 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 SHA256HashValue cert_hash = CalculateFingerprint256(cert->der_cert());
696
697 int starting_cache_iteration = -1;
698 {
699 base::AutoLock lock(cache_lock_);
700 MaybeResetCache();
701 starting_cache_iteration = iteration_;
702 auto cache_iter = trust_status_cache_.Get(cert_hash);
703 if (cache_iter != trust_status_cache_.end()) {
704 UpdateUserData(cache_iter->second.debug_info, debug_data,
705 TrustStoreMac::TrustImplType::kMruCache);
706 return cache_iter->second.trust_status;
707 }
708 }
709
710 TrustStatusDetails trust_details;
711 trust_details.trust_status = IsCertificateTrustedForPolicy(
712 cert, policy_oid_, &trust_details.debug_info);
713
714 {
715 base::AutoLock lock(cache_lock_);
716 MaybeResetCache();
717 // Only store in the cache if the keychain has not changed since trust
718 // evaluation started. If the keychain changed during evaluation, then
719 // it isn't clear whether the calculated result applies to the new or old
720 // trust settings, so don't put it in the cache.
721 if (iteration_ == starting_cache_iteration)
722 trust_status_cache_.Put(cert_hash, trust_details);
723 }
724 UpdateUserData(trust_details.debug_info, debug_data,
725 TrustStoreMac::TrustImplType::kMruCache);
726 return trust_details.trust_status;
727 }
728
729 void InitializeTrustCache() override {
730 // No-op for this impl.
731 }
732
733 private:
734 struct TrustStatusDetails {
735 TrustStatus trust_status = TrustStatus::UNKNOWN;
736 int debug_info = 0;
737 };
738
739 void MaybeResetCache() {
740 cache_lock_.AssertAcquired();
741 int64_t keychain_iteration = keychain_observer_->Iteration();
742 if (iteration_ == keychain_iteration)
743 return;
744 iteration_ = keychain_iteration;
745 trust_status_cache_.Clear();
746 }
747
748 const CFStringRef policy_oid_;
749 std::unique_ptr<KeychainTrustObserver> keychain_observer_;
750
751 base::Lock cache_lock_;
752 // |cache_lock_| must be held while accessing any following members.
753 base::MRUCache<SHA256HashValue, TrustStatusDetails> trust_status_cache_;
754 int64_t iteration_ = -1;
755
756 DISALLOW_COPY_AND_ASSIGN(TrustImplMRUCache);
757};
758
Matt Mueller884cf6862021-03-16 20:29:32759TrustStoreMac::TrustStoreMac(CFStringRef policy_oid,
760 TrustImplType impl,
761 size_t cache_size) {
Matt Mueller1b4c2342021-03-16 17:57:04762 switch (impl) {
763 case TrustImplType::kUnknown:
764 DCHECK(false);
765 break;
766 case TrustImplType::kDomainCache:
767 trust_cache_ = std::make_unique<TrustImplDomainCache>(policy_oid);
768 break;
769 case TrustImplType::kSimple:
770 trust_cache_ = std::make_unique<TrustImplNoCache>(policy_oid);
771 break;
772 case TrustImplType::kMruCache:
773 trust_cache_ =
774 std::make_unique<TrustImplMRUCache>(policy_oid, cache_size);
775 break;
776 }
777}
eroman2a938c32017-04-28 23:16:58778
779TrustStoreMac::~TrustStoreMac() = default;
780
Matt Muellerd0506dd82020-12-17 02:25:24781void TrustStoreMac::InitializeTrustCache() const {
782 trust_cache_->InitializeTrustCache();
783}
784
Matt Mueller5d105e72019-07-01 17:38:42785bool TrustStoreMac::IsKnownRoot(const ParsedCertificate* cert) const {
786 return trust_cache_->IsKnownRoot(cert);
787}
788
eroman2a938c32017-04-28 23:16:58789void TrustStoreMac::SyncGetIssuersOf(const ParsedCertificate* cert,
790 ParsedCertificateList* issuers) {
791 base::ScopedCFTypeRef<CFDataRef> name_data = GetMacNormalizedIssuer(cert);
Eric Romancdc12c82017-10-18 03:34:06792 if (!name_data)
793 return;
eroman2a938c32017-04-28 23:16:58794
795 base::ScopedCFTypeRef<CFArrayRef> matching_items =
796 FindMatchingCertificatesForMacNormalizedSubject(name_data);
797 if (!matching_items)
798 return;
799
800 // Convert to ParsedCertificate.
mattmea4ed8232017-02-28 23:13:23801 for (CFIndex i = 0, item_count = CFArrayGetCount(matching_items);
802 i < item_count; ++i) {
803 SecCertificateRef match_cert_handle = reinterpret_cast<SecCertificateRef>(
804 const_cast<void*>(CFArrayGetValueAtIndex(matching_items, i)));
805
mattmea4ed8232017-02-28 23:13:23806 base::ScopedCFTypeRef<CFDataRef> der_data(
807 SecCertificateCopyData(match_cert_handle));
808 if (!der_data) {
809 LOG(ERROR) << "SecCertificateCopyData error";
810 continue;
811 }
812
813 CertErrors errors;
814 ParseCertificateOptions options;
815 options.allow_invalid_serial_numbers = true;
816 scoped_refptr<ParsedCertificate> anchor_cert = ParsedCertificate::Create(
817 x509_util::CreateCryptoBuffer(CFDataGetBytePtr(der_data.get()),
818 CFDataGetLength(der_data.get())),
819 options, &errors);
820 if (!anchor_cert) {
821 // TODO(crbug.com/634443): return errors better.
822 LOG(ERROR) << "Error parsing issuer certificate:\n"
823 << errors.ToDebugString();
824 continue;
825 }
826
eroman2a938c32017-04-28 23:16:58827 issuers->push_back(std::move(anchor_cert));
mattmea4ed8232017-02-28 23:13:23828 }
829}
830
eroman2a938c32017-04-28 23:16:58831void TrustStoreMac::GetTrust(const scoped_refptr<ParsedCertificate>& cert,
Matt Mueller2feb4fe2019-08-21 17:35:49832 CertificateTrust* trust,
833 base::SupportsUserData* debug_data) const {
Matt Mueller370a7fda2019-08-22 00:38:59834 TrustStatus trust_status = trust_cache_->IsCertTrusted(cert, debug_data);
eroman2a938c32017-04-28 23:16:58835 switch (trust_status) {
836 case TrustStatus::TRUSTED:
837 *trust = CertificateTrust::ForTrustAnchor();
838 return;
839 case TrustStatus::DISTRUSTED:
840 *trust = CertificateTrust::ForDistrusted();
841 return;
842 case TrustStatus::UNSPECIFIED:
843 *trust = CertificateTrust::ForUnspecified();
844 return;
Matt Mueller5d105e72019-07-01 17:38:42845 case TrustStatus::UNKNOWN:
Matt Mueller1b4c2342021-03-16 17:57:04846 // UNKNOWN is an implementation detail of TrustImpl and should never be
Matt Mueller5d105e72019-07-01 17:38:42847 // returned.
848 NOTREACHED();
849 break;
eroman2a938c32017-04-28 23:16:58850 }
mattmea4ed8232017-02-28 23:13:23851
eroman2a938c32017-04-28 23:16:58852 *trust = CertificateTrust::ForUnspecified();
853 return;
mattmea4ed8232017-02-28 23:13:23854}
855
856// static
857base::ScopedCFTypeRef<CFArrayRef>
858TrustStoreMac::FindMatchingCertificatesForMacNormalizedSubject(
859 CFDataRef name_data) {
860 base::ScopedCFTypeRef<CFArrayRef> matching_items;
861 base::ScopedCFTypeRef<CFMutableDictionaryRef> query(
862 CFDictionaryCreateMutable(nullptr, 0, &kCFTypeDictionaryKeyCallBacks,
863 &kCFTypeDictionaryValueCallBacks));
864
865 CFDictionarySetValue(query, kSecClass, kSecClassCertificate);
866 CFDictionarySetValue(query, kSecReturnRef, kCFBooleanTrue);
867 CFDictionarySetValue(query, kSecMatchLimit, kSecMatchLimitAll);
868 CFDictionarySetValue(query, kSecAttrSubject, name_data);
869
870 base::ScopedCFTypeRef<CFArrayRef> scoped_alternate_keychain_search_list;
871 if (TestKeychainSearchList::HasInstance()) {
872 OSStatus status = TestKeychainSearchList::GetInstance()->CopySearchList(
873 scoped_alternate_keychain_search_list.InitializeInto());
874 if (status) {
875 OSSTATUS_LOG(ERROR, status)
876 << "TestKeychainSearchList::CopySearchList error";
877 return matching_items;
878 }
879 }
880
881 base::AutoLock lock(crypto::GetMacSecurityServicesLock());
882
883 // If a TestKeychainSearchList is present, it will have already set
884 // |scoped_alternate_keychain_search_list|, which will be used as the
885 // basis for reordering the keychain. Otherwise, get the current keychain
886 // search list and use that.
887 if (!scoped_alternate_keychain_search_list) {
888 OSStatus status = SecKeychainCopySearchList(
889 scoped_alternate_keychain_search_list.InitializeInto());
890 if (status) {
891 OSSTATUS_LOG(ERROR, status) << "SecKeychainCopySearchList error";
892 return matching_items;
893 }
894 }
895
896 CFMutableArrayRef mutable_keychain_search_list = CFArrayCreateMutableCopy(
897 kCFAllocatorDefault,
898 CFArrayGetCount(scoped_alternate_keychain_search_list.get()) + 1,
899 scoped_alternate_keychain_search_list.get());
900 if (!mutable_keychain_search_list) {
901 LOG(ERROR) << "CFArrayCreateMutableCopy";
902 return matching_items;
903 }
904 scoped_alternate_keychain_search_list.reset(mutable_keychain_search_list);
905
906 base::ScopedCFTypeRef<SecKeychainRef> roots_keychain;
907 // The System Roots keychain is not normally searched by SecItemCopyMatching.
908 // Get a reference to it and include in the keychain search list.
909 OSStatus status = SecKeychainOpen(
910 "/System/Library/Keychains/SystemRootCertificates.keychain",
911 roots_keychain.InitializeInto());
912 if (status) {
913 OSSTATUS_LOG(ERROR, status) << "SecKeychainOpen error";
914 return matching_items;
915 }
916 CFArrayAppendValue(mutable_keychain_search_list, roots_keychain);
917
918 CFDictionarySetValue(query, kSecMatchSearchList,
919 scoped_alternate_keychain_search_list.get());
920
921 OSStatus err = SecItemCopyMatching(
922 query, reinterpret_cast<CFTypeRef*>(matching_items.InitializeInto()));
923 if (err == errSecItemNotFound) {
924 // No matches found.
925 return matching_items;
926 }
927 if (err) {
928 OSSTATUS_LOG(ERROR, err) << "SecItemCopyMatching error";
929 return matching_items;
930 }
931 return matching_items;
932}
933
934// static
935base::ScopedCFTypeRef<CFDataRef> TrustStoreMac::GetMacNormalizedIssuer(
eroman2a938c32017-04-28 23:16:58936 const ParsedCertificate* cert) {
mattmea4ed8232017-02-28 23:13:23937 base::ScopedCFTypeRef<CFDataRef> name_data;
938 // There does not appear to be any public API to get the normalized version
939 // of a Name without creating a SecCertificate.
940 base::ScopedCFTypeRef<SecCertificateRef> cert_handle(
mattm4cede8d2017-04-11 02:55:01941 x509_util::CreateSecCertificateFromBytes(cert->der_cert().UnsafeData(),
942 cert->der_cert().Length()));
mattmea4ed8232017-02-28 23:13:23943 if (!cert_handle) {
Matt Muellera4193272017-12-07 00:23:34944 LOG(ERROR) << "CreateCertBufferFromBytes";
mattmea4ed8232017-02-28 23:13:23945 return name_data;
946 }
947 {
948 base::AutoLock lock(crypto::GetMacSecurityServicesLock());
949 name_data.reset(
950 SecCertificateCopyNormalizedIssuerContent(cert_handle, nullptr));
951 }
952 if (!name_data)
953 LOG(ERROR) << "SecCertificateCopyNormalizedIssuerContent";
954 return name_data;
955}
956
mattmea4ed8232017-02-28 23:13:23957} // namespace net