tor-browser

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

PKCS11ModuleDB.cpp (10427B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
      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 "PKCS11ModuleDB.h"
      8 
      9 #include "CertVerifier.h"
     10 #include "ScopedNSSTypes.h"
     11 #include "mozilla/glean/SecurityManagerSslMetrics.h"
     12 #include "nsComponentManagerUtils.h"
     13 #include "nsIMutableArray.h"
     14 #include "nsNSSCertHelper.h"
     15 #include "nsNSSComponent.h"
     16 #include "nsNativeCharsetUtils.h"
     17 #include "nsPKCS11Slot.h"
     18 #include "nsServiceManagerUtils.h"
     19 
     20 #if defined(XP_MACOSX)
     21 #  include "nsMacUtilsImpl.h"
     22 #  include "nsIFile.h"
     23 #endif  // defined(XP_MACOSX)
     24 
     25 namespace mozilla {
     26 namespace psm {
     27 
     28 NS_IMPL_ISUPPORTS(PKCS11ModuleDB, nsIPKCS11ModuleDB)
     29 
     30 // Convert the UTF16 name of the module as it appears to the user to the
     31 // internal representation. For most modules this just involves converting from
     32 // UTF16 to UTF8. For the builtin root module, it also involves mapping from the
     33 // localized name to the internal, non-localized name.
     34 static nsresult NormalizeModuleNameIn(const nsAString& moduleNameIn,
     35                                      nsCString& moduleNameOut) {
     36  nsAutoString localizedRootModuleName;
     37  nsresult rv =
     38      GetPIPNSSBundleString("RootCertModuleName", localizedRootModuleName);
     39  if (NS_FAILED(rv)) {
     40    return rv;
     41  }
     42  if (moduleNameIn.Equals(localizedRootModuleName)) {
     43    moduleNameOut.Assign(kRootModuleName.get());
     44    return NS_OK;
     45  }
     46  moduleNameOut.Assign(NS_ConvertUTF16toUTF8(moduleNameIn));
     47  return NS_OK;
     48 }
     49 
     50 // Delete a PKCS11 module from the user's profile.
     51 NS_IMETHODIMP
     52 PKCS11ModuleDB::DeleteModule(const nsAString& aModuleName) {
     53  if (aModuleName.IsEmpty()) {
     54    return NS_ERROR_INVALID_ARG;
     55  }
     56 
     57  nsAutoCString moduleNameNormalized;
     58  nsresult rv = NormalizeModuleNameIn(aModuleName, moduleNameNormalized);
     59  if (NS_FAILED(rv)) {
     60    return rv;
     61  }
     62  // modType is an output variable. We ignore it.
     63  int32_t modType;
     64  SECStatus srv = SECMOD_DeleteModule(moduleNameNormalized.get(), &modType);
     65  if (srv != SECSuccess) {
     66    return NS_ERROR_FAILURE;
     67  }
     68 
     69  RefPtr<SharedCertVerifier> certVerifier(GetDefaultCertVerifier());
     70  if (!certVerifier) {
     71    return NS_ERROR_FAILURE;
     72  }
     73  certVerifier->ClearTrustCache();
     74 
     75  CollectThirdPartyPKCS11ModuleTelemetry();
     76 
     77  return NS_OK;
     78 }
     79 
     80 #if defined(XP_MACOSX)
     81 // Given a path to a module, return the filename in `aFilename`.
     82 nsresult ModulePathToFilename(const nsCString& aModulePath,
     83                              nsCString& aFilename) {
     84  nsCOMPtr<nsIFile> file;
     85  nsresult rv =
     86      NS_NewLocalFile(NS_ConvertUTF8toUTF16(aModulePath), getter_AddRefs(file));
     87  NS_ENSURE_SUCCESS(rv, rv);
     88 
     89  nsAutoString filename;
     90  rv = file->GetLeafName(filename);
     91  NS_ENSURE_SUCCESS(rv, rv);
     92 
     93  aFilename = NS_ConvertUTF16toUTF8(filename);
     94  return NS_OK;
     95 }
     96 
     97 // Collect the signature type and filename of a third-party PKCS11 module to
     98 // inform future decisions about module loading restrictions on macOS.
     99 void CollectThirdPartyModuleSignatureType(const nsCString& aModulePath) {
    100  using mozilla::glean::pkcs11::third_party_module_signature_type;
    101  using mozilla::glean::pkcs11::ThirdPartyModuleSignatureTypeExtra;
    102  using nsMacUtilsImpl::CodeSignatureTypeToString;
    103 
    104  nsMacUtilsImpl::CodeSignatureType signatureType =
    105      nsMacUtilsImpl::GetSignatureType(aModulePath);
    106 
    107  nsCString filename;
    108  nsresult rv = ModulePathToFilename(aModulePath, filename);
    109  NS_ENSURE_SUCCESS_VOID(rv);
    110 
    111  nsCString signatureTypeStr(CodeSignatureTypeToString(signatureType));
    112  third_party_module_signature_type.Record(
    113      Some(ThirdPartyModuleSignatureTypeExtra{
    114          Some(filename),
    115          Some(signatureTypeStr),
    116      }));
    117 }
    118 
    119 // Collect the filename of a third-party PKCS11 module to inform future
    120 // decisions about module loading restrictions on macOS.
    121 void CollectThirdPartyModuleFilename(const nsCString& aModulePath) {
    122  using mozilla::glean::pkcs11::third_party_module_profile_entries;
    123  nsCString filename;
    124  nsresult rv = ModulePathToFilename(aModulePath, filename);
    125  NS_ENSURE_SUCCESS_VOID(rv);
    126  third_party_module_profile_entries.Add(filename);
    127 }
    128 #endif  // defined(XP_MACOSX)
    129 
    130 // Add a new PKCS11 module to the user's profile.
    131 NS_IMETHODIMP
    132 PKCS11ModuleDB::AddModule(const nsAString& aModuleName,
    133                          const nsAString& aLibraryFullPath,
    134                          int32_t aCryptoMechanismFlags, int32_t aCipherFlags) {
    135  if (aModuleName.IsEmpty()) {
    136    return NS_ERROR_INVALID_ARG;
    137  }
    138 
    139  // "Root Certs" is the name some NSS command-line utilities will give the
    140  // roots module if they decide to load it when there happens to be a
    141  // `MOZ_DLL_PREFIX "nssckbi" MOZ_DLL_SUFFIX` file in the directory being
    142  // operated on.  This causes failures, so as a workaround, the PSM
    143  // initialization code will unconditionally remove any module named "Root
    144  // Certs". We should prevent the user from adding an unrelated module named
    145  // "Root Certs" in the first place so PSM doesn't delete it. See bug 1406396.
    146  if (aModuleName.EqualsLiteral("Root Certs")) {
    147    return NS_ERROR_ILLEGAL_VALUE;
    148  }
    149 
    150  // There appears to be a deadlock if we try to load modules concurrently, so
    151  // just wait until the loadable roots module has been loaded.
    152  nsresult rv = BlockUntilLoadableCertsLoaded();
    153  if (NS_FAILED(rv)) {
    154    return rv;
    155  }
    156 
    157  nsAutoCString moduleNameNormalized;
    158  rv = NormalizeModuleNameIn(aModuleName, moduleNameNormalized);
    159  if (NS_FAILED(rv)) {
    160    return rv;
    161  }
    162  nsCString fullPath;
    163  CopyUTF16toUTF8(aLibraryFullPath, fullPath);
    164  uint32_t mechFlags = SECMOD_PubMechFlagstoInternal(aCryptoMechanismFlags);
    165  uint32_t cipherFlags = SECMOD_PubCipherFlagstoInternal(aCipherFlags);
    166  SECStatus srv = SECMOD_AddNewModule(moduleNameNormalized.get(),
    167                                      fullPath.get(), mechFlags, cipherFlags);
    168  if (srv != SECSuccess) {
    169    return NS_ERROR_FAILURE;
    170  }
    171 
    172  RefPtr<SharedCertVerifier> certVerifier(GetDefaultCertVerifier());
    173  if (!certVerifier) {
    174    return NS_ERROR_FAILURE;
    175  }
    176  certVerifier->ClearTrustCache();
    177 
    178 #if defined(XP_MACOSX)
    179  CollectThirdPartyModuleSignatureType(fullPath);
    180 #endif  // defined(XP_MACOSX)
    181 
    182  CollectThirdPartyPKCS11ModuleTelemetry();
    183 
    184  return NS_OK;
    185 }
    186 
    187 NS_IMETHODIMP
    188 PKCS11ModuleDB::ListModules(nsISimpleEnumerator** _retval) {
    189  NS_ENSURE_ARG_POINTER(_retval);
    190 
    191  nsresult rv = BlockUntilLoadableCertsLoaded();
    192  if (NS_FAILED(rv)) {
    193    return rv;
    194  }
    195 
    196  nsCOMPtr<nsIMutableArray> array = do_CreateInstance(NS_ARRAY_CONTRACTID);
    197  if (!array) {
    198    return NS_ERROR_FAILURE;
    199  }
    200 
    201  /* lock down the list for reading */
    202  AutoSECMODListReadLock lock;
    203  for (SECMODModuleList* list = SECMOD_GetDefaultModuleList(); list;
    204       list = list->next) {
    205    nsCOMPtr<nsIPKCS11Module> module = new nsPKCS11Module(list->module);
    206    nsresult rv = array->AppendElement(module);
    207    if (NS_FAILED(rv)) {
    208      return rv;
    209    }
    210  }
    211 
    212  /* Get the modules in the database that didn't load */
    213  for (SECMODModuleList* list = SECMOD_GetDeadModuleList(); list;
    214       list = list->next) {
    215    nsCOMPtr<nsIPKCS11Module> module = new nsPKCS11Module(list->module);
    216    nsresult rv = array->AppendElement(module);
    217    if (NS_FAILED(rv)) {
    218      return rv;
    219    }
    220  }
    221 
    222  return array->Enumerate(_retval, NS_GET_IID(nsIPKCS11Module));
    223 }
    224 
    225 NS_IMETHODIMP
    226 PKCS11ModuleDB::GetCanToggleFIPS(bool* aCanToggleFIPS) {
    227  NS_ENSURE_ARG_POINTER(aCanToggleFIPS);
    228 
    229  *aCanToggleFIPS = SECMOD_CanDeleteInternalModule();
    230  return NS_OK;
    231 }
    232 
    233 NS_IMETHODIMP
    234 PKCS11ModuleDB::ToggleFIPSMode() {
    235  // The way to toggle FIPS mode in NSS is extremely obscure. Basically, we
    236  // delete the internal module, and it gets replaced with the opposite module
    237  // (i.e. if it was FIPS before, then it becomes non-FIPS next).
    238  // SECMOD_GetInternalModule() returns a pointer to a local copy of the
    239  // internal module stashed in NSS.  We don't want to delete it since it will
    240  // cause much pain in NSS.
    241  SECMODModule* internal = SECMOD_GetInternalModule();
    242  if (!internal) {
    243    return NS_ERROR_FAILURE;
    244  }
    245 
    246  if (SECMOD_DeleteInternalModule(internal->commonName) != SECSuccess) {
    247    return NS_ERROR_FAILURE;
    248  }
    249 
    250  return NS_OK;
    251 }
    252 
    253 NS_IMETHODIMP
    254 PKCS11ModuleDB::GetIsFIPSEnabled(bool* aIsFIPSEnabled) {
    255  NS_ENSURE_ARG_POINTER(aIsFIPSEnabled);
    256 
    257  *aIsFIPSEnabled = PK11_IsFIPS();
    258  return NS_OK;
    259 }
    260 
    261 const nsLiteralCString kBuiltInModuleNames[] = {
    262    kNSSInternalModuleName,
    263    kRootModuleName,
    264    kOSClientCertsModuleName,
    265    kIPCClientCertsModuleName,
    266 };
    267 
    268 void CollectThirdPartyPKCS11ModuleTelemetry(bool aIsInitialization) {
    269  size_t thirdPartyModulesLoaded = 0;
    270  AutoSECMODListReadLock lock;
    271  for (SECMODModuleList* list = SECMOD_GetDefaultModuleList(); list;
    272       list = list->next) {
    273    bool isThirdParty = true;
    274    for (const auto& builtInModuleName : kBuiltInModuleNames) {
    275      if (builtInModuleName.Equals(list->module->commonName)) {
    276        isThirdParty = false;
    277        break;
    278      }
    279    }
    280    if (isThirdParty) {
    281      thirdPartyModulesLoaded++;
    282 #if defined(XP_MACOSX)
    283      // Collect third party module filenames once per launch.
    284      // We collect signature type when adding a module. It would be wasteful
    285      // and duplicative to collect signature information on each launch given
    286      // that it requires file I/O. Combining the filename of modules collected
    287      // here with signature type and filename collected when adding a module
    288      // provides information about existing modules already in use and new
    289      // modules. No I/O is required to obtain the filename given the path on
    290      // macOS, but defer it to idle-time to avoid adding more work at startup.
    291      if (aIsInitialization) {
    292        nsCString modulePath(list->module->dllName);
    293        NS_DispatchToMainThreadQueue(
    294            NS_NewRunnableFunction("CollectThirdPartyModuleFilenameIdle",
    295                                   [modulePath]() {
    296                                     CollectThirdPartyModuleFilename(
    297                                         modulePath);
    298                                   }),
    299            EventQueuePriority::Idle);
    300      }
    301 #endif  // defined(XP_MACOSX)
    302    }
    303  }
    304  mozilla::glean::pkcs11::third_party_modules_loaded.Set(
    305      thirdPartyModulesLoaded);
    306 }
    307 
    308 }  // namespace psm
    309 }  // namespace mozilla