Bug 1935611 - Fix libyuv/libpng link failed for loongarch64. r=glandium,tnikkel,ng
[gecko.git] / security / manager / ssl / EnterpriseRoots.cpp
blob5ba0313c99eb63dd865d07e37cb284798a35a869
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 "PKCS11ModuleDB.h"
10 #include "mozilla/ArrayUtils.h"
11 #include "mozilla/IntegerPrintfMacros.h"
12 #include "mozilla/Casting.h"
13 #include "mozilla/Logging.h"
14 #include "mozilla/Unused.h"
15 #include "mozpkix/Result.h"
16 #include "nsCRT.h"
17 #include "nsThreadUtils.h"
19 #ifdef MOZ_WIDGET_ANDROID
20 # include "mozilla/java/EnterpriseRootsWrappers.h"
21 #endif // MOZ_WIDGET_ANDROID
23 #ifdef XP_MACOSX
24 # include <Security/Security.h>
25 # include "KeychainSecret.h"
26 #endif
28 #ifdef XP_WIN
29 # include <windows.h>
30 # include <wincrypt.h>
31 #endif // XP_WIN
33 extern mozilla::LazyLogModule gPIPNSSLog;
35 using namespace mozilla;
36 using namespace psm;
38 void EnterpriseCert::CopyBytes(nsTArray<uint8_t>& dest) const {
39 dest.Assign(mDER);
42 pkix::Result EnterpriseCert::GetInput(pkix::Input& input) const {
43 return input.Init(mDER.Elements(), mDER.Length());
46 bool EnterpriseCert::GetIsRoot() const { return mIsRoot; }
48 bool EnterpriseCert::IsKnownRoot(UniqueSECMODModule& rootsModule) {
49 if (!rootsModule) {
50 return false;
53 SECItem certItem = {siBuffer, mDER.Elements(),
54 static_cast<unsigned int>(mDER.Length())};
55 AutoSECMODListReadLock lock;
56 for (int i = 0; i < rootsModule->slotCount; i++) {
57 PK11SlotInfo* slot = rootsModule->slots[i];
58 if (PK11_FindEncodedCertInSlot(slot, &certItem, nullptr) !=
59 CK_INVALID_HANDLE) {
60 return true;
63 return false;
66 #ifdef XP_WIN
67 struct CertStoreLocation {
68 const wchar_t* mName;
69 const bool mIsRoot;
71 CertStoreLocation(const wchar_t* name, bool isRoot)
72 : mName(name), mIsRoot(isRoot) {}
75 // The documentation doesn't make this clear, but the certificate location
76 // identified by "ROOT" contains trusted root certificates. The certificate
77 // location identified by "CA" contains intermediate certificates.
78 MOZ_RUNINIT const CertStoreLocation kCertStoreLocations[] = {
79 CertStoreLocation(L"ROOT", true), CertStoreLocation(L"CA", false)};
81 // Because HCERTSTORE is just a typedef void*, we can't use any of the nice
82 // scoped or unique pointer templates. To elaborate, any attempt would
83 // instantiate those templates with T = void. When T gets used in the context
84 // of T&, this results in void&, which isn't legal.
85 class ScopedCertStore final {
86 public:
87 explicit ScopedCertStore(HCERTSTORE certstore) : certstore(certstore) {}
89 ~ScopedCertStore() { CertCloseStore(certstore, 0); }
91 HCERTSTORE get() { return certstore; }
93 private:
94 ScopedCertStore(const ScopedCertStore&) = delete;
95 ScopedCertStore& operator=(const ScopedCertStore&) = delete;
96 HCERTSTORE certstore;
99 // To determine if a certificate would be useful when verifying a server
100 // certificate for TLS server auth, Windows provides the function
101 // `CertGetEnhancedKeyUsage`, which combines the extended key usage extension
102 // with something called "enhanced key usage", which appears to be a Microsoft
103 // concept.
104 static bool CertCanBeUsedForTLSServerAuth(PCCERT_CONTEXT certificate) {
105 DWORD usageSize = 0;
106 if (!CertGetEnhancedKeyUsage(certificate, 0, NULL, &usageSize)) {
107 return false;
109 nsTArray<uint8_t> usageBytes;
110 usageBytes.SetLength(usageSize);
111 PCERT_ENHKEY_USAGE usage(
112 reinterpret_cast<PCERT_ENHKEY_USAGE>(usageBytes.Elements()));
113 if (!CertGetEnhancedKeyUsage(certificate, 0, usage, &usageSize)) {
114 return false;
116 // https://learn.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-certgetenhancedkeyusage:
117 // "If the cUsageIdentifier member is zero, the certificate might be valid
118 // for all uses or the certificate might have no valid uses. The return from
119 // a call to GetLastError can be used to determine whether the certificate is
120 // good for all uses or for none. If GetLastError returns CRYPT_E_NOT_FOUND,
121 // the certificate is good for all uses. If it returns zero, the certificate
122 // has no valid uses."
123 if (usage->cUsageIdentifier == 0) {
124 return GetLastError() == static_cast<DWORD>(CRYPT_E_NOT_FOUND);
126 for (DWORD i = 0; i < usage->cUsageIdentifier; i++) {
127 if (!nsCRT::strcmp(usage->rgpszUsageIdentifier[i],
128 szOID_PKIX_KP_SERVER_AUTH) ||
129 !nsCRT::strcmp(usage->rgpszUsageIdentifier[i],
130 szOID_ANY_ENHANCED_KEY_USAGE)) {
131 return true;
134 return false;
137 // Loads the enterprise roots at the registry location corresponding to the
138 // given location flag.
139 // Supported flags are:
140 // CERT_SYSTEM_STORE_LOCAL_MACHINE
141 // (for HKLM\SOFTWARE\Microsoft\SystemCertificates)
142 // CERT_SYSTEM_STORE_LOCAL_MACHINE_GROUP_POLICY
143 // (for HKLM\SOFTWARE\Policy\Microsoft\SystemCertificates)
144 // CERT_SYSTEM_STORE_LOCAL_MACHINE_ENTERPRISE
145 // (for HKLM\SOFTWARE\Microsoft\EnterpriseCertificates)
146 // CERT_SYSTEM_STORE_CURRENT_USER
147 // (for HKCU\SOFTWARE\Microsoft\SystemCertificates)
148 // CERT_SYSTEM_STORE_CURRENT_USER_GROUP_POLICY
149 // (for HKCU\SOFTWARE\Policy\Microsoft\SystemCertificates)
150 static void GatherEnterpriseCertsForLocation(DWORD locationFlag,
151 nsTArray<EnterpriseCert>& certs,
152 UniqueSECMODModule& rootsModule) {
153 MOZ_ASSERT(locationFlag == CERT_SYSTEM_STORE_LOCAL_MACHINE ||
154 locationFlag == CERT_SYSTEM_STORE_LOCAL_MACHINE_GROUP_POLICY ||
155 locationFlag == CERT_SYSTEM_STORE_LOCAL_MACHINE_ENTERPRISE ||
156 locationFlag == CERT_SYSTEM_STORE_CURRENT_USER ||
157 locationFlag == CERT_SYSTEM_STORE_CURRENT_USER_GROUP_POLICY,
158 "unexpected locationFlag for GatherEnterpriseRootsForLocation");
159 if (!(locationFlag == CERT_SYSTEM_STORE_LOCAL_MACHINE ||
160 locationFlag == CERT_SYSTEM_STORE_LOCAL_MACHINE_GROUP_POLICY ||
161 locationFlag == CERT_SYSTEM_STORE_LOCAL_MACHINE_ENTERPRISE ||
162 locationFlag == CERT_SYSTEM_STORE_CURRENT_USER ||
163 locationFlag == CERT_SYSTEM_STORE_CURRENT_USER_GROUP_POLICY)) {
164 return;
167 DWORD flags =
168 locationFlag | CERT_STORE_OPEN_EXISTING_FLAG | CERT_STORE_READONLY_FLAG;
169 // The certificate store being opened should consist only of certificates
170 // added by a user or administrator and not any certificates that are part
171 // of Microsoft's root store program.
172 // The 3rd parameter to CertOpenStore should be NULL according to
173 // https://msdn.microsoft.com/en-us/library/windows/desktop/aa376559%28v=vs.85%29.aspx
174 for (const auto& location : kCertStoreLocations) {
175 ScopedCertStore certStore(CertOpenStore(CERT_STORE_PROV_SYSTEM_REGISTRY_W,
176 0, NULL, flags, location.mName));
177 if (!certStore.get()) {
178 MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
179 ("failed to open certificate store"));
180 continue;
182 PCCERT_CONTEXT certificate = nullptr;
183 uint32_t numImported = 0;
184 while ((certificate = CertFindCertificateInStore(
185 certStore.get(), X509_ASN_ENCODING, 0, CERT_FIND_ANY, nullptr,
186 certificate))) {
187 if (!CertCanBeUsedForTLSServerAuth(certificate)) {
188 MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
189 ("skipping cert not relevant for TLS server auth"));
190 continue;
192 EnterpriseCert enterpriseCert(certificate->pbCertEncoded,
193 certificate->cbCertEncoded,
194 location.mIsRoot);
195 if (enterpriseCert.GetIsRoot() ||
196 !enterpriseCert.IsKnownRoot(rootsModule)) {
197 certs.AppendElement(std::move(enterpriseCert));
198 numImported++;
199 } else {
200 MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
201 ("skipping intermediate that is a known root cert"));
204 MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
205 ("imported %u certs from %S", numImported, location.mName));
209 static void GatherEnterpriseCertsWindows(nsTArray<EnterpriseCert>& certs,
210 UniqueSECMODModule& rootsModule) {
211 GatherEnterpriseCertsForLocation(CERT_SYSTEM_STORE_LOCAL_MACHINE, certs,
212 rootsModule);
213 GatherEnterpriseCertsForLocation(CERT_SYSTEM_STORE_LOCAL_MACHINE_GROUP_POLICY,
214 certs, rootsModule);
215 GatherEnterpriseCertsForLocation(CERT_SYSTEM_STORE_LOCAL_MACHINE_ENTERPRISE,
216 certs, rootsModule);
217 GatherEnterpriseCertsForLocation(CERT_SYSTEM_STORE_CURRENT_USER, certs,
218 rootsModule);
219 GatherEnterpriseCertsForLocation(CERT_SYSTEM_STORE_CURRENT_USER_GROUP_POLICY,
220 certs, rootsModule);
222 #endif // XP_WIN
224 #ifdef XP_MACOSX
225 enum class CertificateTrustResult {
226 CanUseAsIntermediate,
227 CanUseAsTrustAnchor,
228 DoNotUse,
231 ScopedCFType<CFArrayRef> GetCertificateTrustSettingsInDomain(
232 const SecCertificateRef certificate, SecTrustSettingsDomain domain) {
233 CFArrayRef trustSettingsRaw;
234 OSStatus rv =
235 SecTrustSettingsCopyTrustSettings(certificate, domain, &trustSettingsRaw);
236 if (rv != errSecSuccess || !trustSettingsRaw) {
237 MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
238 (" SecTrustSettingsCopyTrustSettings failed (or not found) for "
239 "domain %" PRIu32,
240 domain));
241 return nullptr;
243 ScopedCFType<CFArrayRef> trustSettings(trustSettingsRaw);
244 return trustSettings;
247 // This function processes trust settings returned by
248 // SecTrustSettingsCopyTrustSettings. See the documentation at
249 // https://developer.apple.com/documentation/security/1400261-sectrustsettingscopytrustsetting
250 // `trustSettings` is an array of CFDictionaryRef. Each dictionary may impose
251 // a constraint.
252 CertificateTrustResult ProcessCertificateTrustSettings(
253 ScopedCFType<CFArrayRef>& trustSettings) {
254 // If the array is empty, the certificate is a trust anchor.
255 const CFIndex numTrustDictionaries = CFArrayGetCount(trustSettings.get());
256 if (numTrustDictionaries == 0) {
257 MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
258 (" empty trust settings -> trust anchor"));
259 return CertificateTrustResult::CanUseAsTrustAnchor;
261 CertificateTrustResult currentTrustSettings =
262 CertificateTrustResult::CanUseAsIntermediate;
263 for (CFIndex i = 0; i < numTrustDictionaries; i++) {
264 CFDictionaryRef trustDictionary = reinterpret_cast<CFDictionaryRef>(
265 CFArrayGetValueAtIndex(trustSettings.get(), i));
266 // kSecTrustSettingsApplication specifies an external application that
267 // determines the certificate's trust settings.
268 // kSecTrustSettingsPolicyString appears to be a mechanism like name
269 // constraints.
270 // These are not supported, so conservatively assume this certificate is
271 // distrusted if either are present.
272 if (CFDictionaryContainsKey(trustDictionary,
273 kSecTrustSettingsApplication) ||
274 CFDictionaryContainsKey(trustDictionary,
275 kSecTrustSettingsPolicyString)) {
276 MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
277 (" found unsupported policy -> assuming distrusted"));
278 return CertificateTrustResult::DoNotUse;
281 // kSecTrustSettingsKeyUsage seems to be essentially the equivalent of the
282 // x509 keyUsage extension. For parity, we allow
283 // kSecTrustSettingsKeyUseSignature, kSecTrustSettingsKeyUseSignCert, and
284 // kSecTrustSettingsKeyUseAny.
285 if (CFDictionaryContainsKey(trustDictionary, kSecTrustSettingsKeyUsage)) {
286 CFNumberRef keyUsage = (CFNumberRef)CFDictionaryGetValue(
287 trustDictionary, kSecTrustSettingsKeyUsage);
288 int32_t keyUsageValue;
289 if (!keyUsage ||
290 CFNumberGetValue(keyUsage, kCFNumberSInt32Type, &keyUsageValue) ||
291 keyUsageValue < 0) {
292 MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
293 (" no trust settings key usage or couldn't get value"));
294 return CertificateTrustResult::DoNotUse;
296 switch ((uint64_t)keyUsageValue) {
297 case kSecTrustSettingsKeyUseSignature: // fall-through
298 case kSecTrustSettingsKeyUseSignCert: // fall-through
299 case kSecTrustSettingsKeyUseAny:
300 break;
301 default:
302 return CertificateTrustResult::DoNotUse;
306 // If there is a specific policy, ensure that it's for the
307 // 'kSecPolicyAppleSSL' policy, which is the TLS server auth policy (i.e.
308 // x509 + domain name checking).
309 if (CFDictionaryContainsKey(trustDictionary, kSecTrustSettingsPolicy)) {
310 SecPolicyRef policy = (SecPolicyRef)CFDictionaryGetValue(
311 trustDictionary, kSecTrustSettingsPolicy);
312 if (!policy) {
313 MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
314 (" kSecTrustSettingsPolicy present, but null?"));
315 continue;
317 ScopedCFType<CFDictionaryRef> policyProperties(
318 SecPolicyCopyProperties(policy));
319 CFStringRef policyOid = (CFStringRef)CFDictionaryGetValue(
320 policyProperties.get(), kSecPolicyOid);
321 if (!CFEqual(policyOid, kSecPolicyAppleSSL)) {
322 MOZ_LOG(gPIPNSSLog, LogLevel::Debug, (" policy doesn't match"));
323 continue;
327 // By default, the trust setting result value is
328 // kSecTrustSettingsResultTrustRoot.
329 int32_t trustSettingsValue = kSecTrustSettingsResultTrustRoot;
330 if (CFDictionaryContainsKey(trustDictionary, kSecTrustSettingsResult)) {
331 CFNumberRef trustSetting = (CFNumberRef)CFDictionaryGetValue(
332 trustDictionary, kSecTrustSettingsResult);
333 if (!trustSetting || !CFNumberGetValue(trustSetting, kCFNumberSInt32Type,
334 &trustSettingsValue)) {
335 MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
336 (" no trust settings result or couldn't get value"));
337 continue;
340 MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
341 (" trust setting: %d", trustSettingsValue));
342 if (trustSettingsValue == kSecTrustSettingsResultDeny) {
343 return CertificateTrustResult::DoNotUse;
345 if (trustSettingsValue == kSecTrustSettingsResultTrustRoot ||
346 trustSettingsValue == kSecTrustSettingsResultTrustAsRoot) {
347 currentTrustSettings = CertificateTrustResult::CanUseAsTrustAnchor;
350 return currentTrustSettings;
353 CertificateTrustResult GetCertificateTrustResult(
354 const SecCertificateRef certificate) {
355 ScopedCFType<CFStringRef> subject(
356 SecCertificateCopySubjectSummary(certificate));
357 MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
358 ("determining trust for '%s'",
359 CFStringGetCStringPtr(subject.get(), kCFStringEncodingUTF8)));
360 // There are three trust settings domains: kSecTrustSettingsDomainUser,
361 // kSecTrustSettingsDomainAdmin, and kSecTrustSettingsDomainSystem. User
362 // overrides admin and admin overrides system. However, if the given
363 // certificate has trust settings in the system domain, it shipped with the
364 // OS, so we don't want to use it.
365 ScopedCFType<CFArrayRef> systemTrustSettings(
366 GetCertificateTrustSettingsInDomain(certificate,
367 kSecTrustSettingsDomainSystem));
368 if (systemTrustSettings) {
369 return CertificateTrustResult::DoNotUse;
372 // At this point, if there is no trust information regarding this
373 // certificate, it can be used as an intermediate.
374 CertificateTrustResult certificateTrustResult =
375 CertificateTrustResult::CanUseAsIntermediate;
377 // Process trust information in the user domain, if any.
378 ScopedCFType<CFArrayRef> userTrustSettings(
379 GetCertificateTrustSettingsInDomain(certificate,
380 kSecTrustSettingsDomainUser));
381 if (userTrustSettings) {
382 certificateTrustResult = ProcessCertificateTrustSettings(userTrustSettings);
383 // If there is definite information one way or another (either indicating
384 // this is a trusted root or a distrusted certificate), use that
385 // information.
386 if (certificateTrustResult !=
387 CertificateTrustResult::CanUseAsIntermediate) {
388 return certificateTrustResult;
392 // Process trust information in the admin domain, if any.
393 ScopedCFType<CFArrayRef> adminTrustSettings(
394 GetCertificateTrustSettingsInDomain(certificate,
395 kSecTrustSettingsDomainAdmin));
396 if (adminTrustSettings) {
397 certificateTrustResult =
398 ProcessCertificateTrustSettings(adminTrustSettings);
401 // Use whatever result we ended up with.
402 return certificateTrustResult;
405 OSStatus GatherEnterpriseCertsMacOS(nsTArray<EnterpriseCert>& certs,
406 UniqueSECMODModule& rootsModule) {
407 // The following builds a search dictionary corresponding to:
408 // { class: "certificate",
409 // match limit: "match all" }
410 // This operates on items that have been added to the keychain and thus gives
411 // us all 3rd party certificates. Unfortunately, if a root that shipped with
412 // the OS has had its trust settings changed, it can also be returned from
413 // this query. Further work (below) filters such certificates out.
414 const CFStringRef keys[] = {kSecClass, kSecMatchLimit};
415 const void* values[] = {kSecClassCertificate, kSecMatchLimitAll};
416 static_assert(std::size(keys) == std::size(values),
417 "mismatched SecItemCopyMatching key/value array sizes");
418 // https://developer.apple.com/documentation/corefoundation/1516782-cfdictionarycreate
419 ScopedCFType<CFDictionaryRef> searchDictionary(CFDictionaryCreate(
420 nullptr, (const void**)&keys, (const void**)&values, std::size(keys),
421 &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
422 CFTypeRef items;
423 // https://developer.apple.com/documentation/security/1398306-secitemcopymatching
424 OSStatus rv = SecItemCopyMatching(searchDictionary.get(), &items);
425 if (rv != errSecSuccess) {
426 MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("SecItemCopyMatching failed"));
427 return rv;
429 // If given a match limit greater than 1 (which we did), SecItemCopyMatching
430 // returns a CFArrayRef.
431 ScopedCFType<CFArrayRef> arr(reinterpret_cast<CFArrayRef>(items));
432 CFIndex count = CFArrayGetCount(arr.get());
433 uint32_t numImported = 0;
434 for (CFIndex i = 0; i < count; i++) {
435 // Because we asked for certificates, each CFTypeRef in the array is really
436 // a SecCertificateRef.
437 const SecCertificateRef certificate =
438 (const SecCertificateRef)CFArrayGetValueAtIndex(arr.get(), i);
439 CertificateTrustResult certificateTrustResult =
440 GetCertificateTrustResult(certificate);
441 if (certificateTrustResult == CertificateTrustResult::DoNotUse) {
442 MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("skipping distrusted cert"));
443 continue;
445 ScopedCFType<CFDataRef> der(SecCertificateCopyData(certificate));
446 if (!der) {
447 MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
448 ("couldn't get bytes of certificate?"));
449 continue;
451 bool isRoot =
452 certificateTrustResult == CertificateTrustResult::CanUseAsTrustAnchor;
453 EnterpriseCert enterpriseCert(CFDataGetBytePtr(der.get()),
454 CFDataGetLength(der.get()), isRoot);
455 if (enterpriseCert.GetIsRoot() ||
456 !enterpriseCert.IsKnownRoot(rootsModule)) {
457 certs.AppendElement(std::move(enterpriseCert));
458 numImported++;
459 MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
460 ("importing as %s", isRoot ? "root" : "intermediate"));
461 } else {
462 MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
463 ("skipping intermediate that is a 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.GetIsRoot() ||
489 !enterpriseCert.IsKnownRoot(rootsModule)) {
490 certs.AppendElement(std::move(enterpriseCert));
491 numImported++;
492 } else {
493 MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
494 ("skipping intermediate that is a known root cert"));
497 MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("imported %u certs", numImported));
499 #endif // MOZ_WIDGET_ANDROID
501 nsresult GatherEnterpriseCerts(nsTArray<EnterpriseCert>& certs) {
502 MOZ_ASSERT(!NS_IsMainThread());
503 if (NS_IsMainThread()) {
504 return NS_ERROR_NOT_SAME_THREAD;
507 certs.Clear();
508 UniqueSECMODModule rootsModule(SECMOD_FindModule(kRootModuleName.get()));
509 #ifdef XP_WIN
510 GatherEnterpriseCertsWindows(certs, rootsModule);
511 #endif // XP_WIN
512 #ifdef XP_MACOSX
513 OSStatus rv = GatherEnterpriseCertsMacOS(certs, rootsModule);
514 if (rv != errSecSuccess) {
515 return NS_ERROR_FAILURE;
517 #endif // XP_MACOSX
518 #ifdef MOZ_WIDGET_ANDROID
519 GatherEnterpriseCertsAndroid(certs, rootsModule);
520 #endif // MOZ_WIDGET_ANDROID
521 return NS_OK;