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