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