michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- michael@0: * michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #ifdef MOZ_LOGGING michael@0: #define FORCE_PR_LOG 1 michael@0: #endif michael@0: michael@0: #include "nsNSSComponent.h" michael@0: michael@0: #include "ExtendedValidation.h" michael@0: #include "NSSCertDBTrustDomain.h" michael@0: #include "mozilla/Telemetry.h" michael@0: #include "nsCertVerificationThread.h" michael@0: #include "nsAppDirectoryServiceDefs.h" michael@0: #include "nsComponentManagerUtils.h" michael@0: #include "nsDirectoryServiceDefs.h" michael@0: #include "nsICertOverrideService.h" michael@0: #include "mozilla/Preferences.h" michael@0: #include "nsThreadUtils.h" michael@0: #include "mozilla/PublicSSL.h" michael@0: #include "mozilla/StaticPtr.h" michael@0: michael@0: #ifndef MOZ_DISABLE_CRYPTOLEGACY michael@0: #include "nsIDOMNode.h" michael@0: #include "nsIDOMEvent.h" michael@0: #include "nsIDOMDocument.h" michael@0: #include "nsIDOMWindow.h" michael@0: #include "nsIDOMWindowCollection.h" michael@0: #include "nsIDocument.h" michael@0: #include "nsIDOMSmartCardEvent.h" michael@0: #include "nsSmartCardMonitor.h" michael@0: #include "nsIDOMCryptoLegacy.h" michael@0: #include "nsIPrincipal.h" michael@0: #else michael@0: #include "nsIDOMCrypto.h" michael@0: #endif michael@0: michael@0: #include "nsCRT.h" michael@0: #include "nsNTLMAuthModule.h" michael@0: #include "nsIFile.h" michael@0: #include "nsIProperties.h" michael@0: #include "nsIWindowWatcher.h" michael@0: #include "nsIPrompt.h" michael@0: #include "nsCertificatePrincipal.h" michael@0: #include "nsIBufEntropyCollector.h" michael@0: #include "nsITokenPasswordDialogs.h" michael@0: #include "nsServiceManagerUtils.h" michael@0: #include "nsNSSShutDown.h" michael@0: #include "GeneratedEvents.h" michael@0: #include "SharedSSLState.h" michael@0: michael@0: #include "nss.h" michael@0: #include "ssl.h" michael@0: #include "sslproto.h" michael@0: #include "secmod.h" michael@0: #include "secmime.h" michael@0: #include "ocsp.h" michael@0: #include "secerr.h" michael@0: #include "sslerr.h" michael@0: michael@0: #include "nsXULAppAPI.h" michael@0: michael@0: #ifdef XP_WIN michael@0: #include "nsILocalFileWin.h" michael@0: #endif michael@0: michael@0: #include "p12plcy.h" michael@0: michael@0: using namespace mozilla; michael@0: using namespace mozilla::dom; michael@0: using namespace mozilla::psm; michael@0: michael@0: #ifdef PR_LOGGING michael@0: PRLogModuleInfo* gPIPNSSLog = nullptr; michael@0: #endif michael@0: michael@0: int nsNSSComponent::mInstanceCount = 0; michael@0: michael@0: // XXX tmp callback for slot password michael@0: extern char* pk11PasswordPrompt(PK11SlotInfo* slot, PRBool retry, void* arg); michael@0: michael@0: #ifndef MOZ_DISABLE_CRYPTOLEGACY michael@0: //This class is used to run the callback code michael@0: //passed to the event handlers for smart card notification michael@0: class nsTokenEventRunnable : public nsIRunnable { michael@0: public: michael@0: nsTokenEventRunnable(const nsAString& aType, const nsAString& aTokenName); michael@0: virtual ~nsTokenEventRunnable(); michael@0: michael@0: NS_IMETHOD Run (); michael@0: NS_DECL_THREADSAFE_ISUPPORTS michael@0: private: michael@0: nsString mType; michael@0: nsString mTokenName; michael@0: }; michael@0: michael@0: // ISuuports implementation for nsTokenEventRunnable michael@0: NS_IMPL_ISUPPORTS(nsTokenEventRunnable, nsIRunnable) michael@0: michael@0: nsTokenEventRunnable::nsTokenEventRunnable(const nsAString& aType, michael@0: const nsAString& aTokenName) michael@0: : mType(aType) michael@0: , mTokenName(aTokenName) michael@0: { michael@0: } michael@0: michael@0: nsTokenEventRunnable::~nsTokenEventRunnable() { } michael@0: michael@0: //Implementation that runs the callback passed to michael@0: //crypto.generateCRMFRequest as an event. michael@0: NS_IMETHODIMP michael@0: nsTokenEventRunnable::Run() michael@0: { michael@0: static NS_DEFINE_CID(kNSSComponentCID, NS_NSSCOMPONENT_CID); michael@0: michael@0: nsresult rv; michael@0: nsCOMPtr nssComponent(do_GetService(kNSSComponentCID, &rv)); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: return nssComponent->DispatchEvent(mType, mTokenName); michael@0: } michael@0: #endif // MOZ_DISABLE_CRYPTOLEGACY michael@0: michael@0: bool nsPSMInitPanic::isPanic = false; michael@0: michael@0: // We must ensure that the nsNSSComponent has been loaded before michael@0: // creating any other components. michael@0: bool EnsureNSSInitialized(EnsureNSSOperator op) michael@0: { michael@0: if (nsPSMInitPanic::GetPanic()) michael@0: return false; michael@0: michael@0: if (GeckoProcessType_Default != XRE_GetProcessType()) michael@0: { michael@0: if (op == nssEnsureOnChromeOnly) michael@0: { michael@0: // If the component needs PSM/NSS initialized only on the chrome process, michael@0: // pretend we successfully initiated it but in reality we bypass it. michael@0: // It's up to the programmer to check for process type in such components michael@0: // and take care not to call anything that needs NSS/PSM initiated. michael@0: return true; michael@0: } michael@0: michael@0: NS_ERROR("Trying to initialize PSM/NSS in a non-chrome process!"); michael@0: return false; michael@0: } michael@0: michael@0: static bool loading = false; michael@0: static int32_t haveLoaded = 0; michael@0: michael@0: switch (op) michael@0: { michael@0: // In following 4 cases we are protected by monitor of XPCOM component michael@0: // manager - we are inside of do_GetService call for nss component, so it is michael@0: // safe to move with the flags here. michael@0: case nssLoadingComponent: michael@0: if (loading) michael@0: return false; // We are reentered during nss component creation michael@0: loading = true; michael@0: return true; michael@0: michael@0: case nssInitSucceeded: michael@0: NS_ASSERTION(loading, "Bad call to EnsureNSSInitialized(nssInitSucceeded)"); michael@0: loading = false; michael@0: PR_AtomicSet(&haveLoaded, 1); michael@0: return true; michael@0: michael@0: case nssInitFailed: michael@0: NS_ASSERTION(loading, "Bad call to EnsureNSSInitialized(nssInitFailed)"); michael@0: loading = false; michael@0: // no break michael@0: michael@0: case nssShutdown: michael@0: PR_AtomicSet(&haveLoaded, 0); michael@0: return false; michael@0: michael@0: // In this case we are called from a component to ensure nss initilization. michael@0: // If the component has not yet been loaded and is not currently loading michael@0: // call do_GetService for nss component to ensure it. michael@0: case nssEnsure: michael@0: case nssEnsureOnChromeOnly: michael@0: // We are reentered during nss component creation or nss component is already up michael@0: if (PR_AtomicAdd(&haveLoaded, 0) || loading) michael@0: return true; michael@0: michael@0: { michael@0: nsCOMPtr nssComponent michael@0: = do_GetService(PSM_COMPONENT_CONTRACTID); michael@0: michael@0: // Nss component failed to initialize, inform the caller of that fact. michael@0: // Flags are appropriately set by component constructor itself. michael@0: if (!nssComponent) michael@0: return false; michael@0: michael@0: bool isInitialized; michael@0: nsresult rv = nssComponent->IsNSSInitialized(&isInitialized); michael@0: return NS_SUCCEEDED(rv) && isInitialized; michael@0: } michael@0: michael@0: default: michael@0: NS_ASSERTION(false, "Bad operator to EnsureNSSInitialized"); michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: static void michael@0: SetClassicOCSPBehaviorFromPrefs(/*out*/ CertVerifier::ocsp_download_config* odc, michael@0: /*out*/ CertVerifier::ocsp_strict_config* osc, michael@0: /*out*/ CertVerifier::ocsp_get_config* ogc, michael@0: const MutexAutoLock& /*proofOfLock*/) michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: MOZ_ASSERT(odc); michael@0: MOZ_ASSERT(osc); michael@0: MOZ_ASSERT(ogc); michael@0: michael@0: // 0 = disabled, otherwise enabled michael@0: *odc = Preferences::GetInt("security.OCSP.enabled", 1) michael@0: ? CertVerifier::ocsp_on michael@0: : CertVerifier::ocsp_off; michael@0: michael@0: *osc = Preferences::GetBool("security.OCSP.require", false) michael@0: ? CertVerifier::ocsp_strict michael@0: : CertVerifier::ocsp_relaxed; michael@0: michael@0: // XXX: Always use POST for OCSP; see bug 871954 for undoing this. michael@0: *ogc = Preferences::GetBool("security.OCSP.GET.enabled", false) michael@0: ? CertVerifier::ocsp_get_enabled michael@0: : CertVerifier::ocsp_get_disabled; michael@0: michael@0: SetClassicOCSPBehavior(*odc, *osc, *ogc); michael@0: michael@0: SSL_ClearSessionCache(); michael@0: } michael@0: michael@0: nsNSSComponent::nsNSSComponent() michael@0: :mutex("nsNSSComponent.mutex"), michael@0: mNSSInitialized(false), michael@0: #ifndef MOZ_DISABLE_CRYPTOLEGACY michael@0: mThreadList(nullptr), michael@0: #endif michael@0: mCertVerificationThread(nullptr) michael@0: { michael@0: #ifdef PR_LOGGING michael@0: if (!gPIPNSSLog) michael@0: gPIPNSSLog = PR_NewLogModule("pipnss"); michael@0: #endif michael@0: PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsNSSComponent::ctor\n")); michael@0: mObserversRegistered = false; michael@0: michael@0: NS_ASSERTION( (0 == mInstanceCount), "nsNSSComponent is a singleton, but instantiated multiple times!"); michael@0: ++mInstanceCount; michael@0: mShutdownObjectList = nsNSSShutDownList::construct(); michael@0: mIsNetworkDown = false; michael@0: } michael@0: michael@0: void michael@0: nsNSSComponent::deleteBackgroundThreads() michael@0: { michael@0: if (mCertVerificationThread) michael@0: { michael@0: mCertVerificationThread->requestExit(); michael@0: delete mCertVerificationThread; michael@0: mCertVerificationThread = nullptr; michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsNSSComponent::createBackgroundThreads() michael@0: { michael@0: NS_ASSERTION(!mCertVerificationThread, michael@0: "Cert verification thread already created."); michael@0: michael@0: mCertVerificationThread = new nsCertVerificationThread; michael@0: nsresult rv = mCertVerificationThread->startThread( michael@0: NS_LITERAL_CSTRING("Cert Verify")); michael@0: michael@0: if (NS_FAILED(rv)) { michael@0: delete mCertVerificationThread; michael@0: mCertVerificationThread = nullptr; michael@0: } michael@0: } michael@0: michael@0: nsNSSComponent::~nsNSSComponent() michael@0: { michael@0: PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsNSSComponent::dtor\n")); michael@0: michael@0: deleteBackgroundThreads(); michael@0: michael@0: // All cleanup code requiring services needs to happen in xpcom_shutdown michael@0: michael@0: ShutdownNSS(); michael@0: SharedSSLState::GlobalCleanup(); michael@0: RememberCertErrorsTable::Cleanup(); michael@0: --mInstanceCount; michael@0: delete mShutdownObjectList; michael@0: michael@0: // We are being freed, drop the haveLoaded flag to re-enable michael@0: // potential nss initialization later. michael@0: EnsureNSSInitialized(nssShutdown); michael@0: michael@0: PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsNSSComponent::dtor finished\n")); michael@0: } michael@0: michael@0: #ifndef MOZ_DISABLE_CRYPTOLEGACY michael@0: NS_IMETHODIMP michael@0: nsNSSComponent::PostEvent(const nsAString& eventType, michael@0: const nsAString& tokenName) michael@0: { michael@0: nsCOMPtr runnable = michael@0: new nsTokenEventRunnable(eventType, tokenName); michael@0: michael@0: return NS_DispatchToMainThread(runnable); michael@0: } michael@0: michael@0: michael@0: NS_IMETHODIMP michael@0: nsNSSComponent::DispatchEvent(const nsAString& eventType, michael@0: const nsAString& tokenName) michael@0: { michael@0: // 'Dispatch' the event to all the windows. 'DispatchEventToWindow()' will michael@0: // first check to see if a given window has requested crypto events. michael@0: nsresult rv; michael@0: nsCOMPtr windowWatcher = michael@0: do_GetService(NS_WINDOWWATCHER_CONTRACTID, &rv); michael@0: michael@0: if (NS_FAILED(rv)) { michael@0: return rv; michael@0: } michael@0: michael@0: nsCOMPtr enumerator; michael@0: rv = windowWatcher->GetWindowEnumerator(getter_AddRefs(enumerator)); michael@0: if (NS_FAILED(rv)) { michael@0: return rv; michael@0: } michael@0: michael@0: bool hasMoreWindows; michael@0: michael@0: while (NS_SUCCEEDED(enumerator->HasMoreElements(&hasMoreWindows)) michael@0: && hasMoreWindows) { michael@0: nsCOMPtr supports; michael@0: enumerator->GetNext(getter_AddRefs(supports)); michael@0: nsCOMPtr domWin(do_QueryInterface(supports)); michael@0: if (domWin) { michael@0: nsresult rv2 = DispatchEventToWindow(domWin, eventType, tokenName); michael@0: if (NS_FAILED(rv2)) { michael@0: // return the last failure, don't let a single failure prevent michael@0: // continued delivery of events. michael@0: rv = rv2; michael@0: } michael@0: } michael@0: } michael@0: return rv; michael@0: } michael@0: michael@0: nsresult michael@0: nsNSSComponent::DispatchEventToWindow(nsIDOMWindow* domWin, michael@0: const nsAString& eventType, michael@0: const nsAString& tokenName) michael@0: { michael@0: if (!domWin) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: // first walk the children and dispatch their events michael@0: nsresult rv; michael@0: nsCOMPtr frames; michael@0: rv = domWin->GetFrames(getter_AddRefs(frames)); michael@0: if (NS_FAILED(rv)) { michael@0: return rv; michael@0: } michael@0: michael@0: uint32_t length; michael@0: frames->GetLength(&length); michael@0: uint32_t i; michael@0: for (i = 0; i < length; i++) { michael@0: nsCOMPtr childWin; michael@0: frames->Item(i, getter_AddRefs(childWin)); michael@0: DispatchEventToWindow(childWin, eventType, tokenName); michael@0: } michael@0: michael@0: // check if we've enabled smart card events on this window michael@0: // NOTE: it's not an error to say that we aren't going to dispatch michael@0: // the event. michael@0: nsCOMPtr crypto; michael@0: domWin->GetCrypto(getter_AddRefs(crypto)); michael@0: if (!crypto) { michael@0: return NS_OK; // nope, it doesn't have a crypto property michael@0: } michael@0: michael@0: bool boolrv; michael@0: crypto->GetEnableSmartCardEvents(&boolrv); michael@0: if (!boolrv) { michael@0: return NS_OK; // nope, it's not enabled. michael@0: } michael@0: michael@0: // dispatch the event ... michael@0: michael@0: // find the document michael@0: nsCOMPtr doc; michael@0: rv = domWin->GetDocument(getter_AddRefs(doc)); michael@0: if (!doc) { michael@0: return NS_FAILED(rv) ? rv : NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: nsCOMPtr d = do_QueryInterface(doc); michael@0: michael@0: // create the event michael@0: nsCOMPtr event; michael@0: NS_NewDOMSmartCardEvent(getter_AddRefs(event), d, nullptr, nullptr); michael@0: nsCOMPtr smartCardEvent = do_QueryInterface(event); michael@0: rv = smartCardEvent->InitSmartCardEvent(eventType, false, true, tokenName); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: smartCardEvent->SetTrusted(true); michael@0: michael@0: // Send it michael@0: nsCOMPtr target = do_QueryInterface(doc, &rv); michael@0: if (NS_FAILED(rv)) { michael@0: return rv; michael@0: } michael@0: michael@0: return target->DispatchEvent(smartCardEvent, &boolrv); michael@0: } michael@0: #endif // MOZ_DISABLE_CRYPTOLEGACY michael@0: michael@0: NS_IMETHODIMP michael@0: nsNSSComponent::PIPBundleFormatStringFromName(const char* name, michael@0: const char16_t** params, michael@0: uint32_t numParams, michael@0: nsAString& outString) michael@0: { michael@0: nsresult rv = NS_ERROR_FAILURE; michael@0: michael@0: if (mPIPNSSBundle && name) { michael@0: nsXPIDLString result; michael@0: rv = mPIPNSSBundle->FormatStringFromName(NS_ConvertASCIItoUTF16(name).get(), michael@0: params, numParams, michael@0: getter_Copies(result)); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: outString = result; michael@0: } michael@0: } michael@0: return rv; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsNSSComponent::GetPIPNSSBundleString(const char* name, nsAString& outString) michael@0: { michael@0: nsresult rv = NS_ERROR_FAILURE; michael@0: michael@0: outString.SetLength(0); michael@0: if (mPIPNSSBundle && name) { michael@0: nsXPIDLString result; michael@0: rv = mPIPNSSBundle->GetStringFromName(NS_ConvertASCIItoUTF16(name).get(), michael@0: getter_Copies(result)); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: outString = result; michael@0: rv = NS_OK; michael@0: } michael@0: } michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsNSSComponent::NSSBundleFormatStringFromName(const char* name, michael@0: const char16_t** params, michael@0: uint32_t numParams, michael@0: nsAString& outString) michael@0: { michael@0: nsresult rv = NS_ERROR_FAILURE; michael@0: michael@0: if (mNSSErrorsBundle && name) { michael@0: nsXPIDLString result; michael@0: rv = mNSSErrorsBundle->FormatStringFromName(NS_ConvertASCIItoUTF16(name).get(), michael@0: params, numParams, michael@0: getter_Copies(result)); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: outString = result; michael@0: } michael@0: } michael@0: return rv; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsNSSComponent::GetNSSBundleString(const char* name, nsAString& outString) michael@0: { michael@0: nsresult rv = NS_ERROR_FAILURE; michael@0: michael@0: outString.SetLength(0); michael@0: if (mNSSErrorsBundle && name) { michael@0: nsXPIDLString result; michael@0: rv = mNSSErrorsBundle->GetStringFromName(NS_ConvertASCIItoUTF16(name).get(), michael@0: getter_Copies(result)); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: outString = result; michael@0: rv = NS_OK; michael@0: } michael@0: } michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: #ifndef MOZ_DISABLE_CRYPTOLEGACY michael@0: void michael@0: nsNSSComponent::LaunchSmartCardThreads() michael@0: { michael@0: nsNSSShutDownPreventionLock locker; michael@0: { michael@0: SECMODModuleList* list; michael@0: SECMODListLock* lock = SECMOD_GetDefaultModuleListLock(); michael@0: if (!lock) { michael@0: PR_LOG(gPIPNSSLog, PR_LOG_ERROR, michael@0: ("Couldn't get the module list lock, can't launch smart card threads\n")); michael@0: return; michael@0: } michael@0: SECMOD_GetReadLock(lock); michael@0: list = SECMOD_GetDefaultModuleList(); michael@0: michael@0: while (list) { michael@0: SECMODModule* module = list->module; michael@0: LaunchSmartCardThread(module); michael@0: list = list->next; michael@0: } michael@0: SECMOD_ReleaseReadLock(lock); michael@0: } michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsNSSComponent::LaunchSmartCardThread(SECMODModule* module) michael@0: { michael@0: SmartCardMonitoringThread* newThread; michael@0: if (SECMOD_HasRemovableSlots(module)) { michael@0: if (!mThreadList) { michael@0: mThreadList = new SmartCardThreadList(); michael@0: } michael@0: newThread = new SmartCardMonitoringThread(module); michael@0: // newThread is adopted by the add. michael@0: return mThreadList->Add(newThread); michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsNSSComponent::ShutdownSmartCardThread(SECMODModule* module) michael@0: { michael@0: if (!mThreadList) { michael@0: return NS_OK; michael@0: } michael@0: mThreadList->Remove(module); michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: nsNSSComponent::ShutdownSmartCardThreads() michael@0: { michael@0: delete mThreadList; michael@0: mThreadList = nullptr; michael@0: } michael@0: #endif // MOZ_DISABLE_CRYPTOLEGACY michael@0: michael@0: void michael@0: nsNSSComponent::LoadLoadableRoots() michael@0: { michael@0: nsNSSShutDownPreventionLock locker; michael@0: SECMODModule* RootsModule = nullptr; michael@0: michael@0: // In the past we used SECMOD_AddNewModule to load our module containing michael@0: // root CA certificates. This caused problems, refer to bug 176501. michael@0: // On startup, we fix our database and clean any stored module reference, michael@0: // and will use SECMOD_LoadUserModule to temporarily load it michael@0: // for the session. (This approach requires to clean up michael@0: // using SECMOD_UnloadUserModule at the end of the session.) michael@0: michael@0: { michael@0: // Find module containing root certs michael@0: michael@0: SECMODModuleList* list; michael@0: SECMODListLock* lock = SECMOD_GetDefaultModuleListLock(); michael@0: if (!lock) { michael@0: PR_LOG(gPIPNSSLog, PR_LOG_ERROR, michael@0: ("Couldn't get the module list lock, can't install loadable roots\n")); michael@0: return; michael@0: } michael@0: SECMOD_GetReadLock(lock); michael@0: list = SECMOD_GetDefaultModuleList(); michael@0: michael@0: while (!RootsModule && list) { michael@0: SECMODModule* module = list->module; michael@0: michael@0: for (int i=0; i < module->slotCount; i++) { michael@0: PK11SlotInfo* slot = module->slots[i]; michael@0: if (PK11_IsPresent(slot)) { michael@0: if (PK11_HasRootCerts(slot)) { michael@0: RootsModule = SECMOD_ReferenceModule(module); michael@0: break; michael@0: } michael@0: } michael@0: } michael@0: michael@0: list = list->next; michael@0: } michael@0: SECMOD_ReleaseReadLock(lock); michael@0: } michael@0: michael@0: if (RootsModule) { michael@0: int32_t modType; michael@0: SECMOD_DeleteModule(RootsModule->commonName, &modType); michael@0: SECMOD_DestroyModule(RootsModule); michael@0: RootsModule = nullptr; michael@0: } michael@0: michael@0: // Find the best Roots module for our purposes. michael@0: // Prefer the application's installation directory, michael@0: // but also ensure the library is at least the version we expect. michael@0: michael@0: nsresult rv; michael@0: nsAutoString modName; michael@0: rv = GetPIPNSSBundleString("RootCertModuleName", modName); michael@0: if (NS_FAILED(rv)) return; michael@0: michael@0: nsCOMPtr directoryService(do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID)); michael@0: if (!directoryService) michael@0: return; michael@0: michael@0: static const char nss_lib[] = "nss3"; michael@0: const char* possible_ckbi_locations[] = { michael@0: nss_lib, // This special value means: search for ckbi in the directory michael@0: // where nss3 is. michael@0: NS_XPCOM_CURRENT_PROCESS_DIR, michael@0: NS_GRE_DIR, michael@0: 0 // This special value means: michael@0: // search for ckbi in the directories on the shared michael@0: // library/DLL search path michael@0: }; michael@0: michael@0: for (size_t il = 0; il < sizeof(possible_ckbi_locations)/sizeof(const char*); ++il) { michael@0: nsAutoCString libDir; michael@0: michael@0: if (possible_ckbi_locations[il]) { michael@0: nsCOMPtr mozFile; michael@0: if (possible_ckbi_locations[il] == nss_lib) { michael@0: // Get the location of the nss3 library. michael@0: char* nss_path = PR_GetLibraryFilePathname(DLL_PREFIX "nss3" DLL_SUFFIX, michael@0: (PRFuncPtr) NSS_Initialize); michael@0: if (!nss_path) { michael@0: continue; michael@0: } michael@0: // Get the directory containing the nss3 library. michael@0: nsCOMPtr nssLib(do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv)); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: rv = nssLib->InitWithNativePath(nsDependentCString(nss_path)); michael@0: } michael@0: PR_Free(nss_path); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: nsCOMPtr file; michael@0: if (NS_SUCCEEDED(nssLib->GetParent(getter_AddRefs(file)))) { michael@0: mozFile = do_QueryInterface(file); michael@0: } michael@0: } michael@0: } else { michael@0: directoryService->Get( possible_ckbi_locations[il], michael@0: NS_GET_IID(nsIFile), michael@0: getter_AddRefs(mozFile)); michael@0: } michael@0: michael@0: if (!mozFile) { michael@0: continue; michael@0: } michael@0: michael@0: if (NS_FAILED(mozFile->GetNativePath(libDir))) { michael@0: continue; michael@0: } michael@0: } michael@0: michael@0: NS_ConvertUTF16toUTF8 modNameUTF8(modName); michael@0: if (mozilla::psm::LoadLoadableRoots( michael@0: libDir.Length() > 0 ? libDir.get() : nullptr, michael@0: modNameUTF8.get()) == SECSuccess) { michael@0: break; michael@0: } michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsNSSComponent::UnloadLoadableRoots() michael@0: { michael@0: nsresult rv; michael@0: nsAutoString modName; michael@0: rv = GetPIPNSSBundleString("RootCertModuleName", modName); michael@0: if (NS_FAILED(rv)) return; michael@0: michael@0: NS_ConvertUTF16toUTF8 modNameUTF8(modName); michael@0: ::mozilla::psm::UnloadLoadableRoots(modNameUTF8.get()); michael@0: } michael@0: michael@0: nsresult michael@0: nsNSSComponent::ConfigureInternalPKCS11Token() michael@0: { michael@0: nsNSSShutDownPreventionLock locker; michael@0: nsAutoString manufacturerID; michael@0: nsAutoString libraryDescription; michael@0: nsAutoString tokenDescription; michael@0: nsAutoString privateTokenDescription; michael@0: nsAutoString slotDescription; michael@0: nsAutoString privateSlotDescription; michael@0: nsAutoString fips140TokenDescription; michael@0: nsAutoString fips140SlotDescription; michael@0: michael@0: nsresult rv; michael@0: rv = GetPIPNSSBundleString("ManufacturerID", manufacturerID); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: rv = GetPIPNSSBundleString("LibraryDescription", libraryDescription); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: rv = GetPIPNSSBundleString("TokenDescription", tokenDescription); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: rv = GetPIPNSSBundleString("PrivateTokenDescription", privateTokenDescription); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: rv = GetPIPNSSBundleString("SlotDescription", slotDescription); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: rv = GetPIPNSSBundleString("PrivateSlotDescription", privateSlotDescription); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: rv = GetPIPNSSBundleString("Fips140TokenDescription", fips140TokenDescription); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: rv = GetPIPNSSBundleString("Fips140SlotDescription", fips140SlotDescription); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: PK11_ConfigurePKCS11(NS_ConvertUTF16toUTF8(manufacturerID).get(), michael@0: NS_ConvertUTF16toUTF8(libraryDescription).get(), michael@0: NS_ConvertUTF16toUTF8(tokenDescription).get(), michael@0: NS_ConvertUTF16toUTF8(privateTokenDescription).get(), michael@0: NS_ConvertUTF16toUTF8(slotDescription).get(), michael@0: NS_ConvertUTF16toUTF8(privateSlotDescription).get(), michael@0: NS_ConvertUTF16toUTF8(fips140TokenDescription).get(), michael@0: NS_ConvertUTF16toUTF8(fips140SlotDescription).get(), michael@0: 0, 0); michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsNSSComponent::InitializePIPNSSBundle() michael@0: { michael@0: // Called during init only, no mutex required. michael@0: michael@0: nsresult rv; michael@0: nsCOMPtr bundleService(do_GetService(NS_STRINGBUNDLE_CONTRACTID, &rv)); michael@0: if (NS_FAILED(rv) || !bundleService) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: bundleService->CreateBundle("chrome://pipnss/locale/pipnss.properties", michael@0: getter_AddRefs(mPIPNSSBundle)); michael@0: if (!mPIPNSSBundle) michael@0: rv = NS_ERROR_FAILURE; michael@0: michael@0: bundleService->CreateBundle("chrome://pipnss/locale/nsserrors.properties", michael@0: getter_AddRefs(mNSSErrorsBundle)); michael@0: if (!mNSSErrorsBundle) michael@0: rv = NS_ERROR_FAILURE; michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: // Table of pref names and SSL cipher ID michael@0: typedef struct { michael@0: const char* pref; michael@0: long id; michael@0: bool enabledByDefault; michael@0: } CipherPref; michael@0: michael@0: // Update the switch statement in HandshakeCallback in nsNSSCallbacks.cpp when michael@0: // you add/remove cipher suites here. michael@0: static const CipherPref sCipherPrefs[] = { michael@0: { "security.ssl3.ecdhe_rsa_aes_128_gcm_sha256", michael@0: TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, true }, michael@0: { "security.ssl3.ecdhe_ecdsa_aes_128_gcm_sha256", michael@0: TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, true }, michael@0: { "security.ssl3.ecdhe_rsa_aes_128_sha", michael@0: TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, true }, michael@0: { "security.ssl3.ecdhe_ecdsa_aes_128_sha", michael@0: TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, true }, michael@0: michael@0: { "security.ssl3.ecdhe_rsa_aes_256_sha", michael@0: TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, true }, michael@0: { "security.ssl3.ecdhe_ecdsa_aes_256_sha", michael@0: TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, true }, michael@0: michael@0: { "security.ssl3.ecdhe_rsa_des_ede3_sha", michael@0: TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, true }, // deprecated (3DES) michael@0: michael@0: { "security.ssl3.dhe_rsa_aes_128_sha", michael@0: TLS_DHE_RSA_WITH_AES_128_CBC_SHA, true }, michael@0: { "security.ssl3.dhe_rsa_camellia_128_sha", michael@0: TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA, true }, michael@0: michael@0: { "security.ssl3.dhe_rsa_aes_256_sha", michael@0: TLS_DHE_RSA_WITH_AES_256_CBC_SHA, true }, michael@0: { "security.ssl3.dhe_rsa_camellia_256_sha", michael@0: TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA, true }, michael@0: michael@0: { "security.ssl3.dhe_rsa_des_ede3_sha", michael@0: TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA, true }, // deprecated (3DES) michael@0: michael@0: { "security.ssl3.dhe_dss_aes_128_sha", michael@0: TLS_DHE_DSS_WITH_AES_128_CBC_SHA, true }, // deprecated (DSS) michael@0: { "security.ssl3.dhe_dss_aes_256_sha", michael@0: TLS_DHE_DSS_WITH_AES_256_CBC_SHA, true }, // deprecated (DSS) michael@0: michael@0: { "security.ssl3.ecdhe_rsa_rc4_128_sha", michael@0: TLS_ECDHE_RSA_WITH_RC4_128_SHA, true }, // deprecated (RC4) michael@0: { "security.ssl3.ecdhe_ecdsa_rc4_128_sha", michael@0: TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, true }, // deprecated (RC4) michael@0: michael@0: { "security.ssl3.rsa_aes_128_sha", michael@0: TLS_RSA_WITH_AES_128_CBC_SHA, true }, // deprecated (RSA key exchange) michael@0: { "security.ssl3.rsa_camellia_128_sha", michael@0: TLS_RSA_WITH_CAMELLIA_128_CBC_SHA, true }, // deprecated (RSA key exchange) michael@0: { "security.ssl3.rsa_aes_256_sha", michael@0: TLS_RSA_WITH_AES_256_CBC_SHA, true }, // deprecated (RSA key exchange) michael@0: { "security.ssl3.rsa_camellia_256_sha", michael@0: TLS_RSA_WITH_CAMELLIA_256_CBC_SHA, true }, // deprecated (RSA key exchange) michael@0: { "security.ssl3.rsa_des_ede3_sha", michael@0: TLS_RSA_WITH_3DES_EDE_CBC_SHA, true }, // deprecated (RSA key exchange, 3DES) michael@0: michael@0: { "security.ssl3.rsa_rc4_128_sha", michael@0: TLS_RSA_WITH_RC4_128_SHA, true }, // deprecated (RSA key exchange, RC4) michael@0: { "security.ssl3.rsa_rc4_128_md5", michael@0: TLS_RSA_WITH_RC4_128_MD5, true }, // deprecated (RSA key exchange, RC4, HMAC-MD5) michael@0: michael@0: // All the rest are disabled by default michael@0: michael@0: { "security.ssl3.rsa_fips_des_ede3_sha", michael@0: SSL_RSA_FIPS_WITH_3DES_EDE_CBC_SHA, false }, michael@0: { "security.ssl3.dhe_dss_camellia_256_sha", michael@0: TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA, false }, michael@0: { "security.ssl3.dhe_dss_camellia_128_sha", michael@0: TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA, false }, michael@0: { "security.ssl3.rsa_seed_sha", michael@0: TLS_RSA_WITH_SEED_CBC_SHA, false }, michael@0: michael@0: { nullptr, 0 } // end marker michael@0: }; michael@0: michael@0: static const int32_t OCSP_ENABLED_DEFAULT = 1; michael@0: static const bool REQUIRE_SAFE_NEGOTIATION_DEFAULT = false; michael@0: static const bool ALLOW_UNRESTRICTED_RENEGO_DEFAULT = false; michael@0: static const bool FALSE_START_ENABLED_DEFAULT = true; michael@0: static const bool NPN_ENABLED_DEFAULT = true; michael@0: static const bool ALPN_ENABLED_DEFAULT = false; michael@0: static const bool SECURITY_NOCERTDB_DEFAULT = false; michael@0: static const bool DISABLE_SESSION_IDENTIFIERS_DEFAULT = false; michael@0: michael@0: namespace { michael@0: michael@0: class CipherSuiteChangeObserver : public nsIObserver michael@0: { michael@0: public: michael@0: NS_DECL_ISUPPORTS michael@0: NS_DECL_NSIOBSERVER michael@0: michael@0: virtual ~CipherSuiteChangeObserver() {} michael@0: static nsresult StartObserve(); michael@0: static nsresult StopObserve(); michael@0: michael@0: private: michael@0: static StaticRefPtr sObserver; michael@0: CipherSuiteChangeObserver() {} michael@0: }; michael@0: michael@0: NS_IMPL_ISUPPORTS(CipherSuiteChangeObserver, nsIObserver) michael@0: michael@0: // static michael@0: StaticRefPtr CipherSuiteChangeObserver::sObserver; michael@0: michael@0: // static michael@0: nsresult michael@0: CipherSuiteChangeObserver::StartObserve() michael@0: { michael@0: NS_ASSERTION(NS_IsMainThread(), "CipherSuiteChangeObserver::StartObserve() can only be accessed in main thread"); michael@0: if (!sObserver) { michael@0: nsRefPtr observer = new CipherSuiteChangeObserver(); michael@0: nsresult rv = Preferences::AddStrongObserver(observer.get(), "security."); michael@0: if (NS_FAILED(rv)) { michael@0: sObserver = nullptr; michael@0: return rv; michael@0: } michael@0: sObserver = observer; michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: // static michael@0: nsresult michael@0: CipherSuiteChangeObserver::StopObserve() michael@0: { michael@0: NS_ASSERTION(NS_IsMainThread(), "CipherSuiteChangeObserver::StopObserve() can only be accessed in main thread"); michael@0: if (sObserver) { michael@0: nsresult rv = Preferences::RemoveObserver(sObserver.get(), "security."); michael@0: sObserver = nullptr; michael@0: if (NS_FAILED(rv)) { michael@0: return rv; michael@0: } michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: CipherSuiteChangeObserver::Observe(nsISupports* aSubject, michael@0: const char* aTopic, michael@0: const char16_t* someData) michael@0: { michael@0: NS_ASSERTION(NS_IsMainThread(), "CipherSuiteChangeObserver::Observe can only be accessed in main thread"); michael@0: if (nsCRT::strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID) == 0) { michael@0: NS_ConvertUTF16toUTF8 prefName(someData); michael@0: // Look through the cipher table and set according to pref setting michael@0: for (const CipherPref* cp = sCipherPrefs; cp->pref; ++cp) { michael@0: if (prefName.Equals(cp->pref)) { michael@0: bool cipherEnabled = Preferences::GetBool(cp->pref, michael@0: cp->enabledByDefault); michael@0: SSL_CipherPrefSetDefault(cp->id, cipherEnabled); michael@0: SSL_ClearSessionCache(); michael@0: break; michael@0: } michael@0: } michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: } // anonymous namespace michael@0: michael@0: // Caller must hold a lock on nsNSSComponent::mutex when calling this function michael@0: void nsNSSComponent::setValidationOptions(bool isInitialSetting, michael@0: const MutexAutoLock& lock) michael@0: { michael@0: // This preference controls whether we do OCSP fetching and does not affect michael@0: // OCSP stapling. michael@0: // 0 = disabled, 1 = enabled michael@0: int32_t ocspEnabled = Preferences::GetInt("security.OCSP.enabled", michael@0: OCSP_ENABLED_DEFAULT); michael@0: michael@0: bool ocspRequired = ocspEnabled && michael@0: Preferences::GetBool("security.OCSP.require", false); michael@0: michael@0: // We measure the setting of the pref at startup only to minimize noise by michael@0: // addons that may muck with the settings, though it probably doesn't matter. michael@0: if (isInitialSetting) { michael@0: Telemetry::Accumulate(Telemetry::CERT_OCSP_ENABLED, ocspEnabled); michael@0: Telemetry::Accumulate(Telemetry::CERT_OCSP_REQUIRED, ocspRequired); michael@0: } michael@0: michael@0: #ifndef NSS_NO_LIBPKIX michael@0: bool crlDownloading = Preferences::GetBool("security.CRL_download.enabled", michael@0: false); michael@0: bool aiaDownloadEnabled = michael@0: Preferences::GetBool("security.missing_cert_download.enabled", false); michael@0: michael@0: #endif michael@0: bool ocspStaplingEnabled = Preferences::GetBool("security.ssl.enable_ocsp_stapling", michael@0: true); michael@0: PublicSSLState()->SetOCSPStaplingEnabled(ocspStaplingEnabled); michael@0: PrivateSSLState()->SetOCSPStaplingEnabled(ocspStaplingEnabled); michael@0: michael@0: CertVerifier::implementation_config certVerifierImplementation michael@0: = CertVerifier::classic; michael@0: michael@0: // The mozilla::pkix pref overrides the libpkix pref michael@0: if (Preferences::GetBool("security.use_mozillapkix_verification", true)) { michael@0: certVerifierImplementation = CertVerifier::mozillapkix; michael@0: } else { michael@0: #ifndef NSS_NO_LIBPKIX michael@0: if (Preferences::GetBool("security.use_libpkix_verification", false)) { michael@0: certVerifierImplementation = CertVerifier::libpkix; michael@0: } michael@0: #endif michael@0: } michael@0: michael@0: if (isInitialSetting) { michael@0: if (certVerifierImplementation == CertVerifier::classic) { michael@0: Telemetry::Accumulate(Telemetry::CERT_VALIDATION_LIBRARY, 1); michael@0: #ifndef NSS_NO_LIBPKIX michael@0: } else if (certVerifierImplementation == CertVerifier::libpkix) { michael@0: Telemetry::Accumulate(Telemetry::CERT_VALIDATION_LIBRARY, 2); michael@0: #endif michael@0: } else if (certVerifierImplementation == CertVerifier::mozillapkix) { michael@0: Telemetry::Accumulate(Telemetry::CERT_VALIDATION_LIBRARY, 3); michael@0: } michael@0: } michael@0: michael@0: // Default pinning enforcement level is disabled. michael@0: CertVerifier::pinning_enforcement_config michael@0: pinningEnforcementLevel = michael@0: static_cast michael@0: (Preferences::GetInt("security.cert_pinning.enforcement_level", michael@0: CertVerifier::pinningDisabled)); michael@0: michael@0: CertVerifier::ocsp_download_config odc; michael@0: CertVerifier::ocsp_strict_config osc; michael@0: CertVerifier::ocsp_get_config ogc; michael@0: michael@0: SetClassicOCSPBehaviorFromPrefs(&odc, &osc, &ogc, lock); michael@0: mDefaultCertVerifier = new SharedCertVerifier( michael@0: certVerifierImplementation, michael@0: #ifndef NSS_NO_LIBPKIX michael@0: aiaDownloadEnabled ? michael@0: CertVerifier::missing_cert_download_on : CertVerifier::missing_cert_download_off, michael@0: crlDownloading ? michael@0: CertVerifier::crl_download_allowed : CertVerifier::crl_local_only, michael@0: #endif michael@0: odc, osc, ogc, pinningEnforcementLevel); michael@0: michael@0: // mozilla::pkix has its own OCSP cache, so disable the NSS cache michael@0: // if appropriate. michael@0: if (certVerifierImplementation == CertVerifier::mozillapkix) { michael@0: // Using -1 disables the cache. The other arguments are the default michael@0: // values and aren't exposed by the API. michael@0: CERT_OCSPCacheSettings(-1, 1*60*60L, 24*60*60L); michael@0: } else { michael@0: // Using 1000 enables the cache with the default size of 1000. Again, michael@0: // these values are not exposed by the API. michael@0: CERT_OCSPCacheSettings(1000, 1*60*60L, 24*60*60L); michael@0: } michael@0: michael@0: CERT_ClearOCSPCache(); michael@0: } michael@0: michael@0: // Enable the TLS versions given in the prefs, defaulting to TLS 1.0 (min) and michael@0: // TLS 1.2 (max) when the prefs aren't set or set to invalid values. michael@0: nsresult michael@0: nsNSSComponent::setEnabledTLSVersions() michael@0: { michael@0: // keep these values in sync with security-prefs.js michael@0: static const int32_t PSM_DEFAULT_MIN_TLS_VERSION = 1; michael@0: static const int32_t PSM_DEFAULT_MAX_TLS_VERSION = 3; michael@0: michael@0: int32_t minVersion = Preferences::GetInt("security.tls.version.min", michael@0: PSM_DEFAULT_MIN_TLS_VERSION); michael@0: int32_t maxVersion = Preferences::GetInt("security.tls.version.max", michael@0: PSM_DEFAULT_MAX_TLS_VERSION); michael@0: michael@0: // 0 means SSL 3.0, 1 means TLS 1.0, 2 means TLS 1.1, etc. michael@0: minVersion += SSL_LIBRARY_VERSION_3_0; michael@0: maxVersion += SSL_LIBRARY_VERSION_3_0; michael@0: michael@0: SSLVersionRange range = { (uint16_t) minVersion, (uint16_t) maxVersion }; michael@0: michael@0: if (minVersion != (int32_t) range.min || // prevent truncation michael@0: maxVersion != (int32_t) range.max || // prevent truncation michael@0: SSL_VersionRangeSetDefault(ssl_variant_stream, &range) != SECSuccess) { michael@0: range.min = SSL_LIBRARY_VERSION_3_0 + PSM_DEFAULT_MIN_TLS_VERSION; michael@0: range.max = SSL_LIBRARY_VERSION_3_0 + PSM_DEFAULT_MAX_TLS_VERSION; michael@0: if (SSL_VersionRangeSetDefault(ssl_variant_stream, &range) michael@0: != SECSuccess) { michael@0: return NS_ERROR_UNEXPECTED; michael@0: } michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: static nsresult michael@0: GetNSSProfilePath(nsAutoCString& aProfilePath) michael@0: { michael@0: aProfilePath.Truncate(); michael@0: const char* dbDirOverride = getenv("MOZPSM_NSSDBDIR_OVERRIDE"); michael@0: if (dbDirOverride && strlen(dbDirOverride) > 0) { michael@0: PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, michael@0: ("Using specified MOZPSM_NSSDBDIR_OVERRIDE as NSS DB dir: %s\n", michael@0: dbDirOverride)); michael@0: aProfilePath.Assign(dbDirOverride); michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsCOMPtr profileFile; michael@0: nsresult rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, michael@0: getter_AddRefs(profileFile)); michael@0: if (NS_FAILED(rv)) { michael@0: PR_LOG(gPIPNSSLog, PR_LOG_ERROR, michael@0: ("Unable to get profile directory - continuing with no NSS DB\n")); michael@0: return NS_OK; michael@0: } michael@0: michael@0: #if defined(XP_WIN) michael@0: // Native path will drop Unicode characters that cannot be mapped to system's michael@0: // codepage, using short (canonical) path as workaround. michael@0: nsCOMPtr profileFileWin(do_QueryInterface(profileFile)); michael@0: if (!profileFileWin) { michael@0: PR_LOG(gPIPNSSLog, PR_LOG_ERROR, michael@0: ("Could not get nsILocalFileWin for profile directory.\n")); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: rv = profileFileWin->GetNativeCanonicalPath(aProfilePath); michael@0: #else michael@0: rv = profileFile->GetNativePath(aProfilePath); michael@0: #endif michael@0: if (NS_FAILED(rv)) { michael@0: PR_LOG(gPIPNSSLog, PR_LOG_ERROR, michael@0: ("Could not get native path for profile directory.\n")); michael@0: return rv; michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsNSSComponent::InitializeNSS() michael@0: { michael@0: // Can be called both during init and profile change. michael@0: // Needs mutex protection. michael@0: michael@0: PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsNSSComponent::InitializeNSS\n")); michael@0: michael@0: static_assert(nsINSSErrorsService::NSS_SEC_ERROR_BASE == SEC_ERROR_BASE && michael@0: nsINSSErrorsService::NSS_SEC_ERROR_LIMIT == SEC_ERROR_LIMIT && michael@0: nsINSSErrorsService::NSS_SSL_ERROR_BASE == SSL_ERROR_BASE && michael@0: nsINSSErrorsService::NSS_SSL_ERROR_LIMIT == SSL_ERROR_LIMIT, michael@0: "You must update the values in nsINSSErrorsService.idl"); michael@0: michael@0: MutexAutoLock lock(mutex); michael@0: michael@0: if (mNSSInitialized) { michael@0: PR_ASSERT(!"Trying to initialize NSS twice"); // We should never try to michael@0: // initialize NSS more than michael@0: // once in a process. michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("NSS Initialization beginning\n")); michael@0: michael@0: // The call to ConfigureInternalPKCS11Token needs to be done before NSS is initialized, michael@0: // but affects only static data. michael@0: // If we could assume i18n will not change between profiles, one call per application michael@0: // run were sufficient. As I can't predict what happens in the future, let's repeat michael@0: // this call for every re-init of NSS. michael@0: michael@0: ConfigureInternalPKCS11Token(); michael@0: michael@0: nsAutoCString profileStr; michael@0: nsresult rv = GetNSSProfilePath(profileStr); michael@0: if (NS_FAILED(rv)) { michael@0: nsPSMInitPanic::SetPanic(); michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: } michael@0: michael@0: SECStatus init_rv = SECFailure; michael@0: bool nocertdb = Preferences::GetBool("security.nocertdb", SECURITY_NOCERTDB_DEFAULT); michael@0: michael@0: if (!nocertdb && !profileStr.IsEmpty()) { michael@0: // First try to initialize the NSS DB in read/write mode. michael@0: SECStatus init_rv = ::mozilla::psm::InitializeNSS(profileStr.get(), false); michael@0: // If that fails, attempt read-only mode. michael@0: if (init_rv != SECSuccess) { michael@0: PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("could not init NSS r/w in %s\n", profileStr.get())); michael@0: init_rv = ::mozilla::psm::InitializeNSS(profileStr.get(), true); michael@0: } michael@0: if (init_rv != SECSuccess) { michael@0: PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("could not init in r/o either\n")); michael@0: } michael@0: } michael@0: // If we haven't succeeded in initializing the DB in our profile michael@0: // directory or we don't have a profile at all, or the "security.nocertdb" michael@0: // pref has been set to "true", attempt to initialize with no DB. michael@0: if (nocertdb || init_rv != SECSuccess) { michael@0: init_rv = NSS_NoDB_Init(nullptr); michael@0: } michael@0: if (init_rv != SECSuccess) { michael@0: PR_LOG(gPIPNSSLog, PR_LOG_ERROR, ("could not initialize NSS - panicking\n")); michael@0: nsPSMInitPanic::SetPanic(); michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: } michael@0: michael@0: mNSSInitialized = true; michael@0: michael@0: PK11_SetPasswordFunc(PK11PasswordPrompt); michael@0: michael@0: SharedSSLState::GlobalInit(); michael@0: michael@0: // Register an observer so we can inform NSS when these prefs change michael@0: Preferences::AddStrongObserver(this, "security."); michael@0: michael@0: SSL_OptionSetDefault(SSL_ENABLE_SSL2, false); michael@0: SSL_OptionSetDefault(SSL_V2_COMPATIBLE_HELLO, false); michael@0: michael@0: rv = setEnabledTLSVersions(); michael@0: if (NS_FAILED(rv)) { michael@0: nsPSMInitPanic::SetPanic(); michael@0: return NS_ERROR_UNEXPECTED; michael@0: } michael@0: michael@0: DisableMD5(); michael@0: // Initialize the certverifier log before calling any functions that library. michael@0: InitCertVerifierLog(); michael@0: LoadLoadableRoots(); michael@0: michael@0: bool disableSessionIdentifiers = michael@0: Preferences::GetBool("security.ssl.disable_session_identifiers", michael@0: DISABLE_SESSION_IDENTIFIERS_DEFAULT); michael@0: SSL_OptionSetDefault(SSL_ENABLE_SESSION_TICKETS, !disableSessionIdentifiers); michael@0: SSL_OptionSetDefault(SSL_NO_CACHE, disableSessionIdentifiers); michael@0: michael@0: bool requireSafeNegotiation = michael@0: Preferences::GetBool("security.ssl.require_safe_negotiation", michael@0: REQUIRE_SAFE_NEGOTIATION_DEFAULT); michael@0: SSL_OptionSetDefault(SSL_REQUIRE_SAFE_NEGOTIATION, requireSafeNegotiation); michael@0: michael@0: bool allowUnrestrictedRenego = michael@0: Preferences::GetBool("security.ssl.allow_unrestricted_renego_everywhere__temporarily_available_pref", michael@0: ALLOW_UNRESTRICTED_RENEGO_DEFAULT); michael@0: SSL_OptionSetDefault(SSL_ENABLE_RENEGOTIATION, michael@0: allowUnrestrictedRenego ? michael@0: SSL_RENEGOTIATE_UNRESTRICTED : michael@0: SSL_RENEGOTIATE_REQUIRES_XTN); michael@0: michael@0: SSL_OptionSetDefault(SSL_ENABLE_FALSE_START, michael@0: Preferences::GetBool("security.ssl.enable_false_start", michael@0: FALSE_START_ENABLED_DEFAULT)); michael@0: michael@0: // SSL_ENABLE_NPN and SSL_ENABLE_ALPN also require calling michael@0: // SSL_SetNextProtoNego in order for the extensions to be negotiated. michael@0: // WebRTC does not do that so it will not use NPN or ALPN even when these michael@0: // preferences are true. michael@0: SSL_OptionSetDefault(SSL_ENABLE_NPN, michael@0: Preferences::GetBool("security.ssl.enable_npn", michael@0: NPN_ENABLED_DEFAULT)); michael@0: SSL_OptionSetDefault(SSL_ENABLE_ALPN, michael@0: Preferences::GetBool("security.ssl.enable_alpn", michael@0: ALPN_ENABLED_DEFAULT)); michael@0: michael@0: if (NS_FAILED(InitializeCipherSuite())) { michael@0: PR_LOG(gPIPNSSLog, PR_LOG_ERROR, ("Unable to initialize cipher suite settings\n")); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: // dynamic options from prefs michael@0: setValidationOptions(true, lock); michael@0: michael@0: mHttpForNSS.initTable(); michael@0: mHttpForNSS.registerHttpClient(); michael@0: michael@0: #ifndef MOZ_DISABLE_CRYPTOLEGACY michael@0: LaunchSmartCardThreads(); michael@0: #endif michael@0: michael@0: PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("NSS Initialization done\n")); michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: nsNSSComponent::ShutdownNSS() michael@0: { michael@0: // Can be called both during init and profile change, michael@0: // needs mutex protection. michael@0: michael@0: PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsNSSComponent::ShutdownNSS\n")); michael@0: michael@0: MutexAutoLock lock(mutex); michael@0: michael@0: if (mNSSInitialized) { michael@0: mNSSInitialized = false; michael@0: michael@0: PK11_SetPasswordFunc((PK11PasswordFunc)nullptr); michael@0: mHttpForNSS.unregisterHttpClient(); michael@0: michael@0: Preferences::RemoveObserver(this, "security."); michael@0: if (NS_FAILED(CipherSuiteChangeObserver::StopObserve())) { michael@0: PR_LOG(gPIPNSSLog, PR_LOG_ERROR, ("nsNSSComponent::ShutdownNSS cannot stop observing cipher suite change\n")); michael@0: } michael@0: michael@0: #ifndef MOZ_DISABLE_CRYPTOLEGACY michael@0: ShutdownSmartCardThreads(); michael@0: #endif michael@0: SSL_ClearSessionCache(); michael@0: UnloadLoadableRoots(); michael@0: #ifndef MOZ_NO_EV_CERTS michael@0: CleanupIdentityInfo(); michael@0: #endif michael@0: PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("evaporating psm resources\n")); michael@0: mShutdownObjectList->evaporateAllNSSResources(); michael@0: EnsureNSSInitialized(nssShutdown); michael@0: if (SECSuccess != ::NSS_Shutdown()) { michael@0: PR_LOG(gPIPNSSLog, PR_LOG_ALWAYS, ("NSS SHUTDOWN FAILURE\n")); michael@0: } michael@0: else { michael@0: PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("NSS shutdown =====>> OK <<=====\n")); michael@0: } michael@0: } michael@0: } michael@0: michael@0: static const bool SEND_LM_DEFAULT = false; michael@0: michael@0: NS_IMETHODIMP michael@0: nsNSSComponent::Init() michael@0: { michael@0: // No mutex protection. michael@0: // Assume Init happens before any concurrency on "this" can start. michael@0: michael@0: nsresult rv = NS_OK; michael@0: michael@0: PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("Beginning NSS initialization\n")); michael@0: michael@0: if (!mShutdownObjectList) michael@0: { michael@0: PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("NSS init, out of memory in constructor\n")); michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: } michael@0: michael@0: rv = InitializePIPNSSBundle(); michael@0: if (NS_FAILED(rv)) { michael@0: PR_LOG(gPIPNSSLog, PR_LOG_ERROR, ("Unable to create pipnss bundle.\n")); michael@0: return rv; michael@0: } michael@0: michael@0: // Access our string bundles now, this prevents assertions from I/O michael@0: // - nsStandardURL not thread-safe michael@0: // - wrong thread: 'NS_IsMainThread()' in nsIOService.cpp michael@0: // when loading error strings on the SSL threads. michael@0: { michael@0: NS_NAMED_LITERAL_STRING(dummy_name, "dummy"); michael@0: nsXPIDLString result; michael@0: mPIPNSSBundle->GetStringFromName(dummy_name.get(), michael@0: getter_Copies(result)); michael@0: mNSSErrorsBundle->GetStringFromName(dummy_name.get(), michael@0: getter_Copies(result)); michael@0: } michael@0: michael@0: bool sendLM = Preferences::GetBool("network.ntlm.send-lm-response", michael@0: SEND_LM_DEFAULT); michael@0: nsNTLMAuthModule::SetSendLM(sendLM); michael@0: michael@0: // Do that before NSS init, to make sure we won't get unloaded. michael@0: RegisterObservers(); michael@0: michael@0: rv = InitializeNSS(); michael@0: if (NS_FAILED(rv)) { michael@0: PR_LOG(gPIPNSSLog, PR_LOG_ERROR, ("Unable to Initialize NSS.\n")); michael@0: michael@0: DeregisterObservers(); michael@0: mPIPNSSBundle = nullptr; michael@0: return rv; michael@0: } michael@0: michael@0: RememberCertErrorsTable::Init(); michael@0: michael@0: createBackgroundThreads(); michael@0: if (!mCertVerificationThread) michael@0: { michael@0: PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("NSS init, could not create threads\n")); michael@0: michael@0: DeregisterObservers(); michael@0: mPIPNSSBundle = nullptr; michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: } michael@0: michael@0: nsCOMPtr ec michael@0: = do_GetService(NS_ENTROPYCOLLECTOR_CONTRACTID); michael@0: michael@0: nsCOMPtr bec; michael@0: michael@0: if (ec) { michael@0: bec = do_QueryInterface(ec); michael@0: } michael@0: michael@0: NS_ASSERTION(bec, "No buffering entropy collector. " michael@0: "This means no entropy will be collected."); michael@0: if (bec) { michael@0: bec->ForwardTo(this); michael@0: } michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: // nsISupports Implementation for the class michael@0: NS_IMPL_ISUPPORTS(nsNSSComponent, michael@0: nsISignatureVerifier, michael@0: nsIEntropyCollector, michael@0: nsINSSComponent, michael@0: nsIObserver, michael@0: nsISupportsWeakReference) michael@0: michael@0: michael@0: // Callback functions for decoder. For now, use empty/default functions. michael@0: static void michael@0: ContentCallback(void* arg, const char* buf, unsigned long len) michael@0: { michael@0: } michael@0: michael@0: static PK11SymKey* michael@0: GetDecryptKeyCallback(void* arg, SECAlgorithmID* algid) michael@0: { michael@0: return nullptr; michael@0: } michael@0: michael@0: static PRBool michael@0: DecryptionAllowedCallback(SECAlgorithmID* algid, PK11SymKey* bulkkey) michael@0: { michael@0: return SECMIME_DecryptionAllowed(algid, bulkkey); michael@0: } michael@0: michael@0: static void* michael@0: GetPasswordKeyCallback(void* arg, void* handle) michael@0: { michael@0: return nullptr; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsNSSComponent::VerifySignature(const char* aRSABuf, uint32_t aRSABufLen, michael@0: const char* aPlaintext, uint32_t aPlaintextLen, michael@0: int32_t* aErrorCode, michael@0: nsICertificatePrincipal** aPrincipal) michael@0: { michael@0: if (!aPrincipal || !aErrorCode) { michael@0: return NS_ERROR_NULL_POINTER; michael@0: } michael@0: michael@0: *aErrorCode = 0; michael@0: *aPrincipal = nullptr; michael@0: michael@0: nsNSSShutDownPreventionLock locker; michael@0: ScopedSEC_PKCS7ContentInfo p7_info; michael@0: unsigned char hash[SHA1_LENGTH]; michael@0: michael@0: SECItem item; michael@0: item.type = siEncodedCertBuffer; michael@0: item.data = (unsigned char*)aRSABuf; michael@0: item.len = aRSABufLen; michael@0: p7_info = SEC_PKCS7DecodeItem(&item, michael@0: ContentCallback, nullptr, michael@0: GetPasswordKeyCallback, nullptr, michael@0: GetDecryptKeyCallback, nullptr, michael@0: DecryptionAllowedCallback); michael@0: michael@0: if (!p7_info) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: // Make sure we call SEC_PKCS7DestroyContentInfo after this point; michael@0: // otherwise we leak data in p7_info michael@0: michael@0: //-- If a plaintext was provided, hash it. michael@0: SECItem digest; michael@0: digest.data = nullptr; michael@0: digest.len = 0; michael@0: michael@0: if (aPlaintext) { michael@0: HASHContext* hash_ctxt; michael@0: uint32_t hashLen = 0; michael@0: michael@0: hash_ctxt = HASH_Create(HASH_AlgSHA1); michael@0: HASH_Begin(hash_ctxt); michael@0: HASH_Update(hash_ctxt,(const unsigned char*)aPlaintext, aPlaintextLen); michael@0: HASH_End(hash_ctxt, hash, &hashLen, SHA1_LENGTH); michael@0: HASH_Destroy(hash_ctxt); michael@0: michael@0: digest.data = hash; michael@0: digest.len = SHA1_LENGTH; michael@0: } michael@0: michael@0: //-- Verify signature michael@0: bool rv = SEC_PKCS7VerifyDetachedSignature(p7_info, certUsageObjectSigner, michael@0: &digest, HASH_AlgSHA1, false); michael@0: if (!rv) { michael@0: *aErrorCode = PR_GetError(); michael@0: } michael@0: michael@0: // Get the signing cert // michael@0: CERTCertificate* cert = p7_info->content.signedData->signerInfos[0]->cert; michael@0: nsresult rv2 = NS_OK; michael@0: if (cert) { michael@0: // Use |do { } while (0);| as a "more C++-ish" thing than goto; michael@0: // this way we don't have to worry about goto across variable michael@0: // declarations. We have no loops in this code, so it's OK. michael@0: do { michael@0: nsCOMPtr pCert = nsNSSCertificate::Create(cert); michael@0: if (!pCert) { michael@0: rv2 = NS_ERROR_OUT_OF_MEMORY; michael@0: break; michael@0: } michael@0: michael@0: //-- Create a certificate principal with id and organization data michael@0: nsAutoString fingerprint; michael@0: rv2 = pCert->GetSha1Fingerprint(fingerprint); michael@0: if (NS_FAILED(rv2)) { michael@0: break; michael@0: } michael@0: nsAutoString orgName; michael@0: rv2 = pCert->GetOrganization(orgName); michael@0: if (NS_FAILED(rv2)) { michael@0: break; michael@0: } michael@0: nsAutoString subjectName; michael@0: rv2 = pCert->GetSubjectName(subjectName); michael@0: if (NS_FAILED(rv2)) { michael@0: break; michael@0: } michael@0: michael@0: nsCOMPtr certPrincipal = michael@0: new nsCertificatePrincipal(NS_ConvertUTF16toUTF8(fingerprint), michael@0: NS_ConvertUTF16toUTF8(subjectName), michael@0: NS_ConvertUTF16toUTF8(orgName), michael@0: pCert); michael@0: michael@0: certPrincipal.swap(*aPrincipal); michael@0: } while (0); michael@0: } michael@0: michael@0: return rv2; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsNSSComponent::RandomUpdate(void* entropy, int32_t bufLen) michael@0: { michael@0: nsNSSShutDownPreventionLock locker; michael@0: michael@0: // Asynchronous event happening often, michael@0: // must not interfere with initialization or profile switch. michael@0: michael@0: MutexAutoLock lock(mutex); michael@0: michael@0: if (!mNSSInitialized) michael@0: return NS_ERROR_NOT_INITIALIZED; michael@0: michael@0: PK11_RandomUpdate(entropy, bufLen); michael@0: return NS_OK; michael@0: } michael@0: michael@0: static const char* const PROFILE_CHANGE_NET_TEARDOWN_TOPIC michael@0: = "profile-change-net-teardown"; michael@0: static const char* const PROFILE_CHANGE_NET_RESTORE_TOPIC michael@0: = "profile-change-net-restore"; michael@0: static const char* const PROFILE_CHANGE_TEARDOWN_TOPIC michael@0: = "profile-change-teardown"; michael@0: static const char* const PROFILE_BEFORE_CHANGE_TOPIC = "profile-before-change"; michael@0: static const char* const PROFILE_DO_CHANGE_TOPIC = "profile-do-change"; michael@0: michael@0: NS_IMETHODIMP michael@0: nsNSSComponent::Observe(nsISupports* aSubject, const char* aTopic, michael@0: const char16_t* someData) michael@0: { michael@0: if (nsCRT::strcmp(aTopic, PROFILE_CHANGE_TEARDOWN_TOPIC) == 0) { michael@0: PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("in PSM code, receiving change-teardown\n")); michael@0: DoProfileChangeTeardown(aSubject); michael@0: } michael@0: else if (nsCRT::strcmp(aTopic, PROFILE_BEFORE_CHANGE_TOPIC) == 0) { michael@0: PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("receiving profile change topic\n")); michael@0: DoProfileBeforeChange(aSubject); michael@0: } michael@0: else if (nsCRT::strcmp(aTopic, PROFILE_DO_CHANGE_TOPIC) == 0) { michael@0: if (someData && NS_LITERAL_STRING("startup").Equals(someData)) { michael@0: // The application is initializing against a known profile directory for michael@0: // the first time during process execution. michael@0: // However, earlier code execution might have already triggered NSS init. michael@0: // We must ensure that NSS gets shut down prior to any attempt to init michael@0: // it again. We use the same cleanup functionality used when switching michael@0: // profiles. The order of function calls must correspond to the order michael@0: // of notifications sent by Profile Manager (nsProfile). michael@0: DoProfileChangeNetTeardown(); michael@0: DoProfileChangeTeardown(aSubject); michael@0: DoProfileBeforeChange(aSubject); michael@0: DoProfileChangeNetRestore(); michael@0: } michael@0: michael@0: bool needsInit = true; michael@0: michael@0: { michael@0: MutexAutoLock lock(mutex); michael@0: michael@0: if (mNSSInitialized) { michael@0: // We have already initialized NSS before the profile came up, michael@0: // no need to do it again michael@0: needsInit = false; michael@0: } michael@0: } michael@0: michael@0: if (needsInit) { michael@0: if (NS_FAILED(InitializeNSS())) { michael@0: PR_LOG(gPIPNSSLog, PR_LOG_ERROR, ("Unable to Initialize NSS after profile switch.\n")); michael@0: } michael@0: } michael@0: } michael@0: else if (nsCRT::strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0) { michael@0: michael@0: PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsNSSComponent: XPCom shutdown observed\n")); michael@0: michael@0: // Cleanup code that requires services, it's too late in destructor. michael@0: michael@0: nsCOMPtr ec michael@0: = do_GetService(NS_ENTROPYCOLLECTOR_CONTRACTID); michael@0: michael@0: if (ec) { michael@0: nsCOMPtr bec michael@0: = do_QueryInterface(ec); michael@0: if (bec) { michael@0: bec->DontForward(); michael@0: } michael@0: } michael@0: } michael@0: else if (nsCRT::strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID) == 0) { michael@0: nsNSSShutDownPreventionLock locker; michael@0: bool clearSessionCache = true; michael@0: NS_ConvertUTF16toUTF8 prefName(someData); michael@0: michael@0: if (prefName.Equals("security.tls.version.min") || michael@0: prefName.Equals("security.tls.version.max")) { michael@0: (void) setEnabledTLSVersions(); michael@0: } else if (prefName.Equals("security.ssl.disable_session_identifiers")) { michael@0: bool disableSessionIdentifiers = michael@0: Preferences::GetBool("security.ssl.disable_session_identifiers", michael@0: DISABLE_SESSION_IDENTIFIERS_DEFAULT); michael@0: SSL_OptionSetDefault(SSL_ENABLE_SESSION_TICKETS, !disableSessionIdentifiers); michael@0: SSL_OptionSetDefault(SSL_NO_CACHE, disableSessionIdentifiers); michael@0: } else if (prefName.Equals("security.ssl.require_safe_negotiation")) { michael@0: bool requireSafeNegotiation = michael@0: Preferences::GetBool("security.ssl.require_safe_negotiation", michael@0: REQUIRE_SAFE_NEGOTIATION_DEFAULT); michael@0: SSL_OptionSetDefault(SSL_REQUIRE_SAFE_NEGOTIATION, requireSafeNegotiation); michael@0: } else if (prefName.Equals("security.ssl.allow_unrestricted_renego_everywhere__temporarily_available_pref")) { michael@0: bool allowUnrestrictedRenego = michael@0: Preferences::GetBool("security.ssl.allow_unrestricted_renego_everywhere__temporarily_available_pref", michael@0: ALLOW_UNRESTRICTED_RENEGO_DEFAULT); michael@0: SSL_OptionSetDefault(SSL_ENABLE_RENEGOTIATION, michael@0: allowUnrestrictedRenego ? michael@0: SSL_RENEGOTIATE_UNRESTRICTED : michael@0: SSL_RENEGOTIATE_REQUIRES_XTN); michael@0: } else if (prefName.Equals("security.ssl.enable_false_start")) { michael@0: SSL_OptionSetDefault(SSL_ENABLE_FALSE_START, michael@0: Preferences::GetBool("security.ssl.enable_false_start", michael@0: FALSE_START_ENABLED_DEFAULT)); michael@0: } else if (prefName.Equals("security.ssl.enable_npn")) { michael@0: SSL_OptionSetDefault(SSL_ENABLE_NPN, michael@0: Preferences::GetBool("security.ssl.enable_npn", michael@0: NPN_ENABLED_DEFAULT)); michael@0: } else if (prefName.Equals("security.ssl.enable_alpn")) { michael@0: SSL_OptionSetDefault(SSL_ENABLE_ALPN, michael@0: Preferences::GetBool("security.ssl.enable_alpn", michael@0: ALPN_ENABLED_DEFAULT)); michael@0: } else if (prefName.Equals("security.OCSP.enabled") michael@0: || prefName.Equals("security.CRL_download.enabled") michael@0: || prefName.Equals("security.fresh_revocation_info.require") michael@0: || prefName.Equals("security.missing_cert_download.enabled") michael@0: || prefName.Equals("security.OCSP.require") michael@0: || prefName.Equals("security.OCSP.GET.enabled") michael@0: || prefName.Equals("security.ssl.enable_ocsp_stapling") michael@0: || prefName.Equals("security.use_mozillapkix_verification") michael@0: || prefName.Equals("security.use_libpkix_verification") michael@0: || prefName.Equals("security.cert_pinning.enforcement_level")) { michael@0: MutexAutoLock lock(mutex); michael@0: setValidationOptions(false, lock); michael@0: } else if (prefName.Equals("network.ntlm.send-lm-response")) { michael@0: bool sendLM = Preferences::GetBool("network.ntlm.send-lm-response", michael@0: SEND_LM_DEFAULT); michael@0: nsNTLMAuthModule::SetSendLM(sendLM); michael@0: clearSessionCache = false; michael@0: } else { michael@0: clearSessionCache = false; michael@0: } michael@0: if (clearSessionCache) michael@0: SSL_ClearSessionCache(); michael@0: } michael@0: else if (nsCRT::strcmp(aTopic, PROFILE_CHANGE_NET_TEARDOWN_TOPIC) == 0) { michael@0: PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("receiving network teardown topic\n")); michael@0: DoProfileChangeNetTeardown(); michael@0: } michael@0: else if (nsCRT::strcmp(aTopic, PROFILE_CHANGE_NET_RESTORE_TOPIC) == 0) { michael@0: PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("receiving network restore topic\n")); michael@0: DoProfileChangeNetRestore(); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: /*static*/ nsresult michael@0: nsNSSComponent::GetNewPrompter(nsIPrompt** result) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(result); michael@0: *result = nullptr; michael@0: michael@0: if (!NS_IsMainThread()) { michael@0: NS_ERROR("nsSDRContext::GetNewPrompter called off the main thread"); michael@0: return NS_ERROR_NOT_SAME_THREAD; michael@0: } michael@0: michael@0: nsresult rv; michael@0: nsCOMPtr wwatch(do_GetService(NS_WINDOWWATCHER_CONTRACTID, &rv)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: rv = wwatch->GetNewPrompter(0, result); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: /*static*/ nsresult michael@0: nsNSSComponent::ShowAlertWithConstructedString(const nsString& message) michael@0: { michael@0: nsCOMPtr prompter; michael@0: nsresult rv = GetNewPrompter(getter_AddRefs(prompter)); michael@0: if (prompter) { michael@0: nsPSMUITracker tracker; michael@0: if (tracker.isUIForbidden()) { michael@0: NS_WARNING("Suppressing alert because PSM UI is forbidden"); michael@0: rv = NS_ERROR_UNEXPECTED; michael@0: } else { michael@0: rv = prompter->Alert(nullptr, message.get()); michael@0: } michael@0: } michael@0: return rv; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsNSSComponent::ShowAlertFromStringBundle(const char* messageID) michael@0: { michael@0: nsString message; michael@0: nsresult rv; michael@0: michael@0: rv = GetPIPNSSBundleString(messageID, message); michael@0: if (NS_FAILED(rv)) { michael@0: NS_ERROR("GetPIPNSSBundleString failed"); michael@0: return rv; michael@0: } michael@0: michael@0: return ShowAlertWithConstructedString(message); michael@0: } michael@0: michael@0: nsresult nsNSSComponent::LogoutAuthenticatedPK11() michael@0: { michael@0: nsCOMPtr icos = michael@0: do_GetService("@mozilla.org/security/certoverride;1"); michael@0: if (icos) { michael@0: icos->ClearValidityOverride( michael@0: NS_LITERAL_CSTRING("all:temporary-certificates"), michael@0: 0); michael@0: } michael@0: michael@0: nsClientAuthRememberService::ClearAllRememberedDecisions(); michael@0: michael@0: return mShutdownObjectList->doPK11Logout(); michael@0: } michael@0: michael@0: nsresult michael@0: nsNSSComponent::RegisterObservers() michael@0: { michael@0: // Happens once during init only, no mutex protection. michael@0: michael@0: nsCOMPtr observerService(do_GetService("@mozilla.org/observer-service;1")); michael@0: NS_ASSERTION(observerService, "could not get observer service"); michael@0: if (observerService) { michael@0: mObserversRegistered = true; michael@0: PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsNSSComponent: adding observers\n")); michael@0: michael@0: // We are a service. michael@0: // Once we are loaded, don't allow being removed from memory. michael@0: // This makes sense, as initializing NSS is expensive. michael@0: michael@0: // By using false for parameter ownsWeak in AddObserver, michael@0: // we make sure that we won't get unloaded until the application shuts down. michael@0: michael@0: observerService->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false); michael@0: michael@0: observerService->AddObserver(this, PROFILE_CHANGE_TEARDOWN_TOPIC, false); michael@0: observerService->AddObserver(this, PROFILE_BEFORE_CHANGE_TOPIC, false); michael@0: observerService->AddObserver(this, PROFILE_DO_CHANGE_TOPIC, false); michael@0: observerService->AddObserver(this, PROFILE_CHANGE_NET_TEARDOWN_TOPIC, false); michael@0: observerService->AddObserver(this, PROFILE_CHANGE_NET_RESTORE_TOPIC, false); michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsNSSComponent::DeregisterObservers() michael@0: { michael@0: if (!mObserversRegistered) michael@0: return NS_OK; michael@0: michael@0: nsCOMPtr observerService(do_GetService("@mozilla.org/observer-service;1")); michael@0: NS_ASSERTION(observerService, "could not get observer service"); michael@0: if (observerService) { michael@0: mObserversRegistered = false; michael@0: PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsNSSComponent: removing observers\n")); michael@0: michael@0: observerService->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID); michael@0: michael@0: observerService->RemoveObserver(this, PROFILE_CHANGE_TEARDOWN_TOPIC); michael@0: observerService->RemoveObserver(this, PROFILE_BEFORE_CHANGE_TOPIC); michael@0: observerService->RemoveObserver(this, PROFILE_DO_CHANGE_TOPIC); michael@0: observerService->RemoveObserver(this, PROFILE_CHANGE_NET_TEARDOWN_TOPIC); michael@0: observerService->RemoveObserver(this, PROFILE_CHANGE_NET_RESTORE_TOPIC); michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: nsNSSComponent::DoProfileChangeNetTeardown() michael@0: { michael@0: if (mCertVerificationThread) michael@0: mCertVerificationThread->requestExit(); michael@0: mIsNetworkDown = true; michael@0: } michael@0: michael@0: void michael@0: nsNSSComponent::DoProfileChangeTeardown(nsISupports* aSubject) michael@0: { michael@0: mShutdownObjectList->ifPossibleDisallowUI(); michael@0: } michael@0: michael@0: void michael@0: nsNSSComponent::DoProfileBeforeChange(nsISupports* aSubject) michael@0: { michael@0: NS_ASSERTION(mIsNetworkDown, "nsNSSComponent relies on profile manager to wait for synchronous shutdown of all network activity"); michael@0: michael@0: bool needsCleanup = true; michael@0: michael@0: { michael@0: MutexAutoLock lock(mutex); michael@0: michael@0: if (!mNSSInitialized) { michael@0: // Make sure we don't try to cleanup if we have already done so. michael@0: // This makes sure we behave safely, in case we are notified michael@0: // multiple times. michael@0: needsCleanup = false; michael@0: } michael@0: } michael@0: michael@0: if (needsCleanup) { michael@0: ShutdownNSS(); michael@0: } michael@0: mShutdownObjectList->allowUI(); michael@0: } michael@0: michael@0: void michael@0: nsNSSComponent::DoProfileChangeNetRestore() michael@0: { michael@0: // XXX this doesn't work well, since nothing expects null pointers michael@0: deleteBackgroundThreads(); michael@0: createBackgroundThreads(); michael@0: mIsNetworkDown = false; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsNSSComponent::IsNSSInitialized(bool* initialized) michael@0: { michael@0: MutexAutoLock lock(mutex); michael@0: *initialized = mNSSInitialized; michael@0: return NS_OK; michael@0: } michael@0: michael@0: SharedCertVerifier::~SharedCertVerifier() { } michael@0: michael@0: TemporaryRef michael@0: nsNSSComponent::GetDefaultCertVerifier() michael@0: { michael@0: MutexAutoLock lock(mutex); michael@0: MOZ_ASSERT(mNSSInitialized); michael@0: return mDefaultCertVerifier; michael@0: } michael@0: michael@0: namespace mozilla { namespace psm { michael@0: michael@0: TemporaryRef michael@0: GetDefaultCertVerifier() michael@0: { michael@0: static NS_DEFINE_CID(kNSSComponentCID, NS_NSSCOMPONENT_CID); michael@0: michael@0: nsCOMPtr nssComponent(do_GetService(kNSSComponentCID)); michael@0: RefPtr certVerifier; michael@0: if (nssComponent) { michael@0: return nssComponent->GetDefaultCertVerifier(); michael@0: } michael@0: michael@0: return nullptr; michael@0: } michael@0: michael@0: } } // namespace mozilla::psm michael@0: michael@0: NS_IMPL_ISUPPORTS(PipUIContext, nsIInterfaceRequestor) michael@0: michael@0: PipUIContext::PipUIContext() michael@0: { michael@0: } michael@0: michael@0: PipUIContext::~PipUIContext() michael@0: { michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: PipUIContext::GetInterface(const nsIID& uuid, void** result) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(result); michael@0: *result = nullptr; michael@0: michael@0: if (!NS_IsMainThread()) { michael@0: NS_ERROR("PipUIContext::GetInterface called off the main thread"); michael@0: return NS_ERROR_NOT_SAME_THREAD; michael@0: } michael@0: michael@0: if (!uuid.Equals(NS_GET_IID(nsIPrompt))) michael@0: return NS_ERROR_NO_INTERFACE; michael@0: michael@0: nsIPrompt* prompt = nullptr; michael@0: nsresult rv = nsNSSComponent::GetNewPrompter(&prompt); michael@0: *result = prompt; michael@0: return rv; michael@0: } michael@0: michael@0: nsresult michael@0: getNSSDialogs(void** _result, REFNSIID aIID, const char* contract) michael@0: { michael@0: if (!NS_IsMainThread()) { michael@0: NS_ERROR("getNSSDialogs called off the main thread"); michael@0: return NS_ERROR_NOT_SAME_THREAD; michael@0: } michael@0: michael@0: nsresult rv; michael@0: michael@0: nsCOMPtr svc = do_GetService(contract, &rv); michael@0: if (NS_FAILED(rv)) { michael@0: return rv; michael@0: } michael@0: michael@0: rv = svc->QueryInterface(aIID, _result); michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: nsresult michael@0: setPassword(PK11SlotInfo* slot, nsIInterfaceRequestor* ctx) michael@0: { michael@0: nsNSSShutDownPreventionLock locker; michael@0: nsresult rv = NS_OK; michael@0: michael@0: if (PK11_NeedUserInit(slot)) { michael@0: nsITokenPasswordDialogs* dialogs; michael@0: bool canceled; michael@0: NS_ConvertUTF8toUTF16 tokenName(PK11_GetTokenName(slot)); michael@0: michael@0: rv = getNSSDialogs((void**)&dialogs, michael@0: NS_GET_IID(nsITokenPasswordDialogs), michael@0: NS_TOKENPASSWORDSDIALOG_CONTRACTID); michael@0: michael@0: if (NS_FAILED(rv)) goto loser; michael@0: michael@0: { michael@0: nsPSMUITracker tracker; michael@0: if (tracker.isUIForbidden()) { michael@0: rv = NS_ERROR_NOT_AVAILABLE; michael@0: } michael@0: else { michael@0: rv = dialogs->SetPassword(ctx, michael@0: tokenName.get(), michael@0: &canceled); michael@0: } michael@0: } michael@0: NS_RELEASE(dialogs); michael@0: if (NS_FAILED(rv)) goto loser; michael@0: michael@0: if (canceled) { rv = NS_ERROR_NOT_AVAILABLE; goto loser; } michael@0: } michael@0: loser: michael@0: return rv; michael@0: } michael@0: michael@0: namespace mozilla { michael@0: namespace psm { michael@0: michael@0: nsresult michael@0: InitializeCipherSuite() michael@0: { michael@0: NS_ASSERTION(NS_IsMainThread(), "InitializeCipherSuite() can only be accessed in main thread"); michael@0: michael@0: if (NSS_SetDomesticPolicy() != SECSuccess) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: // Disable any ciphers that NSS might have enabled by default michael@0: for (uint16_t i = 0; i < SSL_NumImplementedCiphers; ++i) { michael@0: uint16_t cipher_id = SSL_ImplementedCiphers[i]; michael@0: SSL_CipherPrefSetDefault(cipher_id, false); michael@0: } michael@0: michael@0: // Now only set SSL/TLS ciphers we knew about at compile time michael@0: for (const CipherPref* cp = sCipherPrefs; cp->pref; ++cp) { michael@0: bool cipherEnabled = Preferences::GetBool(cp->pref, cp->enabledByDefault); michael@0: SSL_CipherPrefSetDefault(cp->id, cipherEnabled); michael@0: } michael@0: michael@0: // Enable ciphers for PKCS#12 michael@0: SEC_PKCS12EnableCipher(PKCS12_RC4_40, 1); michael@0: SEC_PKCS12EnableCipher(PKCS12_RC4_128, 1); michael@0: SEC_PKCS12EnableCipher(PKCS12_RC2_CBC_40, 1); michael@0: SEC_PKCS12EnableCipher(PKCS12_RC2_CBC_128, 1); michael@0: SEC_PKCS12EnableCipher(PKCS12_DES_56, 1); michael@0: SEC_PKCS12EnableCipher(PKCS12_DES_EDE3_168, 1); michael@0: SEC_PKCS12SetPreferredCipher(PKCS12_DES_EDE3_168, 1); michael@0: PORT_SetUCS2_ASCIIConversionFunction(pip_ucs2_ascii_conversion_fn); michael@0: michael@0: // Observe preference change around cipher suite setting. michael@0: return CipherSuiteChangeObserver::StartObserve(); michael@0: } michael@0: michael@0: } // namespace psm michael@0: } // namespace mozilla