tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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 }