Backed out 2 changesets (bug 1881117) for causing bustage on nsNSSComponent.cpp....
[gecko.git] / security / manager / ssl / EnterpriseRoots.cpp
blobae602dad10b22d5efa768eb7d254c5c497e6c38a
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "EnterpriseRoots.h"
9 #include "mozilla/ArrayUtils.h"
10 #include "mozilla/Logging.h"
11 #include "mozilla/Unused.h"
12 #include "mozpkix/Result.h"
13 #include "nsNSSCertHelper.h"
14 #include "nsThreadUtils.h"
16 #ifdef MOZ_WIDGET_ANDROID
17 # include "mozilla/java/EnterpriseRootsWrappers.h"
18 #endif // MOZ_WIDGET_ANDROID
20 #ifdef XP_MACOSX
21 # include <Security/Security.h>
22 # include "KeychainSecret.h"
23 #endif
25 #ifdef XP_WIN
26 # include <windows.h>
27 # include <wincrypt.h>
28 #endif // XP_WIN
30 extern mozilla::LazyLogModule gPIPNSSLog;
32 using namespace mozilla;
34 void EnterpriseCert::CopyBytes(nsTArray<uint8_t>& dest) const {
35 dest.Assign(mDER);
38 pkix::Result EnterpriseCert::GetInput(pkix::Input& input) const {
39 return input.Init(mDER.Elements(), mDER.Length());
42 bool EnterpriseCert::GetIsRoot() const { return mIsRoot; }
44 bool EnterpriseCert::IsKnownRoot(UniqueSECMODModule& rootsModule) {
45 if (!rootsModule) {
46 return false;
49 SECItem certItem = {siBuffer, mDER.Elements(),
50 static_cast<unsigned int>(mDER.Length())};
51 AutoSECMODListReadLock lock;
52 for (int i = 0; i < rootsModule->slotCount; i++) {
53 PK11SlotInfo* slot = rootsModule->slots[i];
54 if (PK11_FindEncodedCertInSlot(slot, &certItem, nullptr) !=
55 CK_INVALID_HANDLE) {
56 return true;
59 return false;
62 #ifdef XP_WIN
63 const wchar_t* kWindowsDefaultRootStoreNames[] = {L"ROOT", L"CA"};
65 // Helper function to determine if the OS considers the given certificate to be
66 // a trust anchor for TLS server auth certificates. This is to be used in the
67 // context of importing what are presumed to be root certificates from the OS.
68 // If this function returns true but it turns out that the given certificate is
69 // in some way unsuitable to issue certificates, mozilla::pkix will never build
70 // a valid chain that includes the certificate, so importing it even if it
71 // isn't a valid CA poses no risk.
72 static void CertIsTrustAnchorForTLSServerAuth(PCCERT_CONTEXT certificate,
73 bool& isTrusted, bool& isRoot) {
74 isTrusted = false;
75 isRoot = false;
76 MOZ_ASSERT(certificate);
77 if (!certificate) {
78 return;
81 PCCERT_CHAIN_CONTEXT pChainContext = nullptr;
82 CERT_ENHKEY_USAGE enhkeyUsage;
83 memset(&enhkeyUsage, 0, sizeof(CERT_ENHKEY_USAGE));
84 LPCSTR identifiers[] = {
85 "1.3.6.1.5.5.7.3.1", // id-kp-serverAuth
87 enhkeyUsage.cUsageIdentifier = ArrayLength(identifiers);
88 enhkeyUsage.rgpszUsageIdentifier =
89 const_cast<LPSTR*>(identifiers); // -Wwritable-strings
90 CERT_USAGE_MATCH certUsage;
91 memset(&certUsage, 0, sizeof(CERT_USAGE_MATCH));
92 certUsage.dwType = USAGE_MATCH_TYPE_AND;
93 certUsage.Usage = enhkeyUsage;
94 CERT_CHAIN_PARA chainPara;
95 memset(&chainPara, 0, sizeof(CERT_CHAIN_PARA));
96 chainPara.cbSize = sizeof(CERT_CHAIN_PARA);
97 chainPara.RequestedUsage = certUsage;
98 // Disable anything that could result in network I/O.
99 DWORD flags = CERT_CHAIN_REVOCATION_CHECK_CACHE_ONLY |
100 CERT_CHAIN_CACHE_ONLY_URL_RETRIEVAL |
101 CERT_CHAIN_DISABLE_AUTH_ROOT_AUTO_UPDATE |
102 CERT_CHAIN_DISABLE_AIA;
103 if (!CertGetCertificateChain(nullptr, certificate, nullptr, nullptr,
104 &chainPara, flags, nullptr, &pChainContext)) {
105 MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("CertGetCertificateChain failed"));
106 return;
108 isTrusted = pChainContext->TrustStatus.dwErrorStatus == CERT_TRUST_NO_ERROR;
109 if (isTrusted && pChainContext->cChain > 0) {
110 // The so-called "final chain" is what we're after:
111 // https://docs.microsoft.com/en-us/windows/desktop/api/wincrypt/ns-wincrypt-_cert_chain_context
112 CERT_SIMPLE_CHAIN* finalChain =
113 pChainContext->rgpChain[pChainContext->cChain - 1];
114 // This is a root if the final chain consists of only one certificate (i.e.
115 // this one).
116 isRoot = finalChain->cElement == 1;
118 CertFreeCertificateChain(pChainContext);
121 // Because HCERTSTORE is just a typedef void*, we can't use any of the nice
122 // scoped or unique pointer templates. To elaborate, any attempt would
123 // instantiate those templates with T = void. When T gets used in the context
124 // of T&, this results in void&, which isn't legal.
125 class ScopedCertStore final {
126 public:
127 explicit ScopedCertStore(HCERTSTORE certstore) : certstore(certstore) {}
129 ~ScopedCertStore() { CertCloseStore(certstore, 0); }
131 HCERTSTORE get() { return certstore; }
133 private:
134 ScopedCertStore(const ScopedCertStore&) = delete;
135 ScopedCertStore& operator=(const ScopedCertStore&) = delete;
136 HCERTSTORE certstore;
139 // Loads the enterprise roots at the registry location corresponding to the
140 // given location flag.
141 // Supported flags are:
142 // CERT_SYSTEM_STORE_LOCAL_MACHINE
143 // (for HKLM\SOFTWARE\Microsoft\SystemCertificates)
144 // CERT_SYSTEM_STORE_LOCAL_MACHINE_GROUP_POLICY
145 // (for HKLM\SOFTWARE\Policy\Microsoft\SystemCertificates)
146 // CERT_SYSTEM_STORE_LOCAL_MACHINE_ENTERPRISE
147 // (for HKLM\SOFTWARE\Microsoft\EnterpriseCertificates)
148 // CERT_SYSTEM_STORE_CURRENT_USER
149 // (for HKCU\SOFTWARE\Microsoft\SystemCertificates)
150 // CERT_SYSTEM_STORE_CURRENT_USER_GROUP_POLICY
151 // (for HKCU\SOFTWARE\Policy\Microsoft\SystemCertificates)
152 static void GatherEnterpriseCertsForLocation(DWORD locationFlag,
153 nsTArray<EnterpriseCert>& certs,
154 UniqueSECMODModule& rootsModule) {
155 MOZ_ASSERT(locationFlag == CERT_SYSTEM_STORE_LOCAL_MACHINE ||
156 locationFlag == CERT_SYSTEM_STORE_LOCAL_MACHINE_GROUP_POLICY ||
157 locationFlag == CERT_SYSTEM_STORE_LOCAL_MACHINE_ENTERPRISE ||
158 locationFlag == CERT_SYSTEM_STORE_CURRENT_USER ||
159 locationFlag == CERT_SYSTEM_STORE_CURRENT_USER_GROUP_POLICY,
160 "unexpected locationFlag for GatherEnterpriseRootsForLocation");
161 if (!(locationFlag == CERT_SYSTEM_STORE_LOCAL_MACHINE ||
162 locationFlag == CERT_SYSTEM_STORE_LOCAL_MACHINE_GROUP_POLICY ||
163 locationFlag == CERT_SYSTEM_STORE_LOCAL_MACHINE_ENTERPRISE ||
164 locationFlag == CERT_SYSTEM_STORE_CURRENT_USER ||
165 locationFlag == CERT_SYSTEM_STORE_CURRENT_USER_GROUP_POLICY)) {
166 return;
169 DWORD flags =
170 locationFlag | CERT_STORE_OPEN_EXISTING_FLAG | CERT_STORE_READONLY_FLAG;
171 // The certificate store being opened should consist only of certificates
172 // added by a user or administrator and not any certificates that are part
173 // of Microsoft's root store program.
174 // The 3rd parameter to CertOpenStore should be NULL according to
175 // https://msdn.microsoft.com/en-us/library/windows/desktop/aa376559%28v=vs.85%29.aspx
176 for (auto name : kWindowsDefaultRootStoreNames) {
177 ScopedCertStore enterpriseRootStore(
178 CertOpenStore(CERT_STORE_PROV_SYSTEM_REGISTRY_W, 0, NULL, flags, name));
179 if (!enterpriseRootStore.get()) {
180 MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
181 ("failed to open enterprise root store"));
182 continue;
184 PCCERT_CONTEXT certificate = nullptr;
185 uint32_t numImported = 0;
186 while ((certificate = CertFindCertificateInStore(
187 enterpriseRootStore.get(), X509_ASN_ENCODING, 0, CERT_FIND_ANY,
188 nullptr, certificate))) {
189 bool isTrusted;
190 bool isRoot;
191 CertIsTrustAnchorForTLSServerAuth(certificate, isTrusted, isRoot);
192 if (!isTrusted) {
193 MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
194 ("skipping cert not trusted for TLS server auth"));
195 continue;
197 EnterpriseCert enterpriseCert(certificate->pbCertEncoded,
198 certificate->cbCertEncoded, isRoot);
199 if (!enterpriseCert.IsKnownRoot(rootsModule)) {
200 certs.AppendElement(std::move(enterpriseCert));
201 numImported++;
202 } else {
203 MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("skipping known root cert"));
206 MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
207 ("imported %u certs from %S", numImported, name));
211 static void GatherEnterpriseCertsWindows(nsTArray<EnterpriseCert>& certs,
212 UniqueSECMODModule& rootsModule) {
213 GatherEnterpriseCertsForLocation(CERT_SYSTEM_STORE_LOCAL_MACHINE, certs,
214 rootsModule);
215 GatherEnterpriseCertsForLocation(CERT_SYSTEM_STORE_LOCAL_MACHINE_GROUP_POLICY,
216 certs, rootsModule);
217 GatherEnterpriseCertsForLocation(CERT_SYSTEM_STORE_LOCAL_MACHINE_ENTERPRISE,
218 certs, rootsModule);
219 GatherEnterpriseCertsForLocation(CERT_SYSTEM_STORE_CURRENT_USER, certs,
220 rootsModule);
221 GatherEnterpriseCertsForLocation(CERT_SYSTEM_STORE_CURRENT_USER_GROUP_POLICY,
222 certs, rootsModule);
224 #endif // XP_WIN
226 #ifdef XP_MACOSX
227 enum class CertificateTrustResult {
228 CanUseAsIntermediate,
229 CanUseAsTrustAnchor,
230 DoNotUse,
233 ScopedCFType<CFArrayRef> GetCertificateTrustSettingsInDomain(
234 const SecCertificateRef certificate, SecTrustSettingsDomain domain) {
235 CFArrayRef trustSettingsRaw;
236 OSStatus rv =
237 SecTrustSettingsCopyTrustSettings(certificate, domain, &trustSettingsRaw);
238 if (rv != errSecSuccess || !trustSettingsRaw) {
239 MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
240 (" SecTrustSettingsCopyTrustSettings failed (or not found) for "
241 "domain %" PRIu32,
242 domain));
243 return nullptr;
245 ScopedCFType<CFArrayRef> trustSettings(trustSettingsRaw);
246 return trustSettings;
249 // This function processes trust settings returned by
250 // SecTrustSettingsCopyTrustSettings. See the documentation at
251 // https://developer.apple.com/documentation/security/1400261-sectrustsettingscopytrustsetting
252 // `trustSettings` is an array of CFDictionaryRef. Each dictionary may impose
253 // a constraint.
254 CertificateTrustResult ProcessCertificateTrustSettings(
255 ScopedCFType<CFArrayRef>& trustSettings) {
256 // If the array is empty, the certificate is a trust anchor.
257 const CFIndex numTrustDictionaries = CFArrayGetCount(trustSettings.get());
258 if (numTrustDictionaries == 0) {
259 MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
260 (" empty trust settings -> trust anchor"));
261 return CertificateTrustResult::CanUseAsTrustAnchor;
263 CertificateTrustResult currentTrustSettings =
264 CertificateTrustResult::CanUseAsIntermediate;
265 for (CFIndex i = 0; i < numTrustDictionaries; i++) {
266 CFDictionaryRef trustDictionary = reinterpret_cast<CFDictionaryRef>(
267 CFArrayGetValueAtIndex(trustSettings.get(), i));
268 // kSecTrustSettingsApplication specifies an external application that
269 // determines the certificate's trust settings.
270 // kSecTrustSettingsPolicyString appears to be a mechanism like name
271 // constraints.
272 // These are not supported, so conservatively assume this certificate is
273 // distrusted if either are present.
274 if (CFDictionaryContainsKey(trustDictionary,
275 kSecTrustSettingsApplication) ||
276 CFDictionaryContainsKey(trustDictionary,
277 kSecTrustSettingsPolicyString)) {
278 MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
279 (" found unsupported policy -> assuming distrusted"));
280 return CertificateTrustResult::DoNotUse;
283 // kSecTrustSettingsKeyUsage seems to be essentially the equivalent of the
284 // x509 keyUsage extension. For parity, we allow
285 // kSecTrustSettingsKeyUseSignature, kSecTrustSettingsKeyUseSignCert, and
286 // kSecTrustSettingsKeyUseAny.
287 if (CFDictionaryContainsKey(trustDictionary, kSecTrustSettingsKeyUsage)) {
288 CFNumberRef keyUsage = (CFNumberRef)CFDictionaryGetValue(
289 trustDictionary, kSecTrustSettingsKeyUsage);
290 int32_t keyUsageValue;
291 if (!keyUsage ||
292 CFNumberGetValue(keyUsage, kCFNumberSInt32Type, &keyUsageValue) ||
293 keyUsageValue < 0) {
294 MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
295 (" no trust settings key usage or couldn't get value"));
296 return CertificateTrustResult::DoNotUse;
298 switch ((uint64_t)keyUsageValue) {
299 case kSecTrustSettingsKeyUseSignature: // fall-through
300 case kSecTrustSettingsKeyUseSignCert: // fall-through
301 case kSecTrustSettingsKeyUseAny:
302 break;
303 default:
304 return CertificateTrustResult::DoNotUse;
308 // If there is a specific policy, ensure that it's for the
309 // 'kSecPolicyAppleSSL' policy, which is the TLS server auth policy (i.e.
310 // x509 + domain name checking).
311 if (CFDictionaryContainsKey(trustDictionary, kSecTrustSettingsPolicy)) {
312 SecPolicyRef policy = (SecPolicyRef)CFDictionaryGetValue(
313 trustDictionary, kSecTrustSettingsPolicy);
314 if (!policy) {
315 MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
316 (" kSecTrustSettingsPolicy present, but null?"));
317 continue;
319 ScopedCFType<CFDictionaryRef> policyProperties(
320 SecPolicyCopyProperties(policy));
321 CFStringRef policyOid = (CFStringRef)CFDictionaryGetValue(
322 policyProperties.get(), kSecPolicyOid);
323 if (!CFEqual(policyOid, kSecPolicyAppleSSL)) {
324 MOZ_LOG(gPIPNSSLog, LogLevel::Debug, (" policy doesn't match"));
325 continue;
329 // By default, the trust setting result value is
330 // kSecTrustSettingsResultTrustRoot.
331 int32_t trustSettingsValue = kSecTrustSettingsResultTrustRoot;
332 if (CFDictionaryContainsKey(trustDictionary, kSecTrustSettingsResult)) {
333 CFNumberRef trustSetting = (CFNumberRef)CFDictionaryGetValue(
334 trustDictionary, kSecTrustSettingsResult);
335 if (!trustSetting || !CFNumberGetValue(trustSetting, kCFNumberSInt32Type,
336 &trustSettingsValue)) {
337 MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
338 (" no trust settings result or couldn't get value"));
339 continue;
342 MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
343 (" trust setting: %d", trustSettingsValue));
344 if (trustSettingsValue == kSecTrustSettingsResultDeny) {
345 return CertificateTrustResult::DoNotUse;
347 if (trustSettingsValue == kSecTrustSettingsResultTrustRoot ||
348 trustSettingsValue == kSecTrustSettingsResultTrustAsRoot) {
349 currentTrustSettings = CertificateTrustResult::CanUseAsTrustAnchor;
352 return currentTrustSettings;
355 CertificateTrustResult GetCertificateTrustResult(
356 const SecCertificateRef certificate) {
357 ScopedCFType<CFStringRef> subject(
358 SecCertificateCopySubjectSummary(certificate));
359 MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
360 ("determining trust for '%s'",
361 CFStringGetCStringPtr(subject.get(), kCFStringEncodingUTF8)));
362 // There are three trust settings domains: kSecTrustSettingsDomainUser,
363 // kSecTrustSettingsDomainAdmin, and kSecTrustSettingsDomainSystem. User
364 // overrides admin and admin overrides system. However, if the given
365 // certificate has trust settings in the system domain, it shipped with the
366 // OS, so we don't want to use it.
367 ScopedCFType<CFArrayRef> systemTrustSettings(
368 GetCertificateTrustSettingsInDomain(certificate,
369 kSecTrustSettingsDomainSystem));
370 if (systemTrustSettings) {
371 return CertificateTrustResult::DoNotUse;
374 // At this point, if there is no trust information regarding this
375 // certificate, it can be used as an intermediate.
376 CertificateTrustResult certificateTrustResult =
377 CertificateTrustResult::CanUseAsIntermediate;
379 // Process trust information in the user domain, if any.
380 ScopedCFType<CFArrayRef> userTrustSettings(
381 GetCertificateTrustSettingsInDomain(certificate,
382 kSecTrustSettingsDomainUser));
383 if (userTrustSettings) {
384 certificateTrustResult = ProcessCertificateTrustSettings(userTrustSettings);
385 // If there is definite information one way or another (either indicating
386 // this is a trusted root or a distrusted certificate), use that
387 // information.
388 if (certificateTrustResult !=
389 CertificateTrustResult::CanUseAsIntermediate) {
390 return certificateTrustResult;
394 // Process trust information in the admin domain, if any.
395 ScopedCFType<CFArrayRef> adminTrustSettings(
396 GetCertificateTrustSettingsInDomain(certificate,
397 kSecTrustSettingsDomainAdmin));
398 if (adminTrustSettings) {
399 certificateTrustResult =
400 ProcessCertificateTrustSettings(adminTrustSettings);
403 // Use whatever result we ended up with.
404 return certificateTrustResult;
407 OSStatus GatherEnterpriseCertsMacOS(nsTArray<EnterpriseCert>& certs,
408 UniqueSECMODModule& rootsModule) {
409 // The following builds a search dictionary corresponding to:
410 // { class: "certificate",
411 // match limit: "match all" }
412 // This operates on items that have been added to the keychain and thus gives
413 // us all 3rd party certificates. Unfortunately, if a root that shipped with
414 // the OS has had its trust settings changed, it can also be returned from
415 // this query. Further work (below) filters such certificates out.
416 const CFStringRef keys[] = {kSecClass, kSecMatchLimit};
417 const void* values[] = {kSecClassCertificate, kSecMatchLimitAll};
418 static_assert(ArrayLength(keys) == ArrayLength(values),
419 "mismatched SecItemCopyMatching key/value array sizes");
420 // https://developer.apple.com/documentation/corefoundation/1516782-cfdictionarycreate
421 ScopedCFType<CFDictionaryRef> searchDictionary(CFDictionaryCreate(
422 nullptr, (const void**)&keys, (const void**)&values, ArrayLength(keys),
423 &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
424 CFTypeRef items;
425 // https://developer.apple.com/documentation/security/1398306-secitemcopymatching
426 OSStatus rv = SecItemCopyMatching(searchDictionary.get(), &items);
427 if (rv != errSecSuccess) {
428 MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("SecItemCopyMatching failed"));
429 return rv;
431 // If given a match limit greater than 1 (which we did), SecItemCopyMatching
432 // returns a CFArrayRef.
433 ScopedCFType<CFArrayRef> arr(reinterpret_cast<CFArrayRef>(items));
434 CFIndex count = CFArrayGetCount(arr.get());
435 uint32_t numImported = 0;
436 for (CFIndex i = 0; i < count; i++) {
437 // Because we asked for certificates, each CFTypeRef in the array is really
438 // a SecCertificateRef.
439 const SecCertificateRef certificate =
440 (const SecCertificateRef)CFArrayGetValueAtIndex(arr.get(), i);
441 CertificateTrustResult certificateTrustResult =
442 GetCertificateTrustResult(certificate);
443 if (certificateTrustResult == CertificateTrustResult::DoNotUse) {
444 MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("skipping distrusted cert"));
445 continue;
447 ScopedCFType<CFDataRef> der(SecCertificateCopyData(certificate));
448 if (!der) {
449 MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
450 ("couldn't get bytes of certificate?"));
451 continue;
453 bool isRoot =
454 certificateTrustResult == CertificateTrustResult::CanUseAsTrustAnchor;
455 EnterpriseCert enterpriseCert(CFDataGetBytePtr(der.get()),
456 CFDataGetLength(der.get()), isRoot);
457 if (!enterpriseCert.IsKnownRoot(rootsModule)) {
458 certs.AppendElement(std::move(enterpriseCert));
459 numImported++;
460 MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
461 ("importing as %s", isRoot ? "root" : "intermediate"));
462 } else {
463 MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("skipping known root cert"));
466 MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("imported %u certs", numImported));
467 return errSecSuccess;
469 #endif // XP_MACOSX
471 #ifdef MOZ_WIDGET_ANDROID
472 void GatherEnterpriseCertsAndroid(nsTArray<EnterpriseCert>& certs,
473 UniqueSECMODModule& rootsModule) {
474 if (!jni::IsAvailable()) {
475 MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("JNI not available"));
476 return;
478 jni::ObjectArray::LocalRef roots =
479 java::EnterpriseRoots::GatherEnterpriseRoots();
480 uint32_t numImported = 0;
481 for (size_t i = 0; i < roots->Length(); i++) {
482 jni::ByteArray::LocalRef root = roots->GetElement(i);
483 // Currently we treat all certificates gleaned from the Android
484 // CA store as roots.
485 EnterpriseCert enterpriseCert(
486 reinterpret_cast<uint8_t*>(root->GetElements().Elements()),
487 root->Length(), true);
488 if (!enterpriseCert.IsKnownRoot(rootsModule)) {
489 certs.AppendElement(std::move(enterpriseCert));
490 numImported++;
491 } else {
492 MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("skipping known root cert"));
495 MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("imported %u certs", numImported));
497 #endif // MOZ_WIDGET_ANDROID
499 nsresult GatherEnterpriseCerts(nsTArray<EnterpriseCert>& certs) {
500 MOZ_ASSERT(!NS_IsMainThread());
501 if (NS_IsMainThread()) {
502 return NS_ERROR_NOT_SAME_THREAD;
505 certs.Clear();
506 UniqueSECMODModule rootsModule(SECMOD_FindModule(kRootModuleName));
507 #ifdef XP_WIN
508 GatherEnterpriseCertsWindows(certs, rootsModule);
509 #endif // XP_WIN
510 #ifdef XP_MACOSX
511 OSStatus rv = GatherEnterpriseCertsMacOS(certs, rootsModule);
512 if (rv != errSecSuccess) {
513 return NS_ERROR_FAILURE;
515 #endif // XP_MACOSX
516 #ifdef MOZ_WIDGET_ANDROID
517 GatherEnterpriseCertsAndroid(certs, rootsModule);
518 #endif // MOZ_WIDGET_ANDROID
519 return NS_OK;