michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* vim: set ts=2 et sw=2 tw=80: */ 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: #include "SharedSSLState.h" michael@0: #include "nsClientAuthRemember.h" michael@0: #include "nsComponentManagerUtils.h" michael@0: #include "nsICertOverrideService.h" michael@0: #include "nsIObserverService.h" michael@0: #include "mozilla/Services.h" michael@0: #include "nsThreadUtils.h" michael@0: #include "nsCRT.h" michael@0: #include "nsServiceManagerUtils.h" michael@0: #include "nsRecentBadCerts.h" michael@0: #include "PSMRunnable.h" michael@0: #include "PublicSSL.h" michael@0: #include "ssl.h" michael@0: #include "nsNetCID.h" michael@0: #include "mozilla/Atomics.h" michael@0: #include "mozilla/unused.h" michael@0: michael@0: using mozilla::psm::SyncRunnableBase; michael@0: using mozilla::Atomic; michael@0: using mozilla::unused; michael@0: michael@0: namespace { michael@0: michael@0: static Atomic sCertOverrideSvcExists(false); michael@0: static Atomic sCertDBExists(false); michael@0: michael@0: class MainThreadClearer : public SyncRunnableBase michael@0: { michael@0: public: michael@0: MainThreadClearer() : mShouldClearSessionCache(false) {} michael@0: michael@0: void RunOnTargetThread() { michael@0: // In some cases it's possible to cause PSM/NSS to initialize while XPCOM shutdown michael@0: // is in progress. We want to avoid this, since they do not handle the situation well, michael@0: // hence the flags to avoid instantiating the services if they don't already exist. michael@0: michael@0: bool certOverrideSvcExists = sCertOverrideSvcExists.exchange(false); michael@0: if (certOverrideSvcExists) { michael@0: sCertOverrideSvcExists = true; michael@0: nsCOMPtr icos = do_GetService(NS_CERTOVERRIDE_CONTRACTID); 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: michael@0: bool certDBExists = sCertDBExists.exchange(false); michael@0: if (certDBExists) { michael@0: sCertDBExists = true; michael@0: nsCOMPtr certdb = do_GetService(NS_X509CERTDB_CONTRACTID); michael@0: if (certdb) { michael@0: nsCOMPtr badCerts; michael@0: certdb->GetRecentBadCerts(true, getter_AddRefs(badCerts)); michael@0: if (badCerts) { michael@0: badCerts->ResetStoredCerts(); michael@0: } michael@0: } michael@0: } michael@0: michael@0: // This needs to be checked on the main thread to avoid racing with NSS michael@0: // initialization. michael@0: mShouldClearSessionCache = mozilla::psm::PrivateSSLState() && michael@0: mozilla::psm::PrivateSSLState()->SocketCreated(); michael@0: } michael@0: bool mShouldClearSessionCache; michael@0: }; michael@0: michael@0: } // anonymous namespace michael@0: michael@0: namespace mozilla { michael@0: michael@0: void ClearPrivateSSLState() michael@0: { michael@0: // This only works if it is called on the socket transport michael@0: // service thread immediately after closing all private SSL michael@0: // connections. michael@0: #ifdef DEBUG michael@0: nsresult rv; michael@0: nsCOMPtr sts michael@0: = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv); michael@0: MOZ_ASSERT(NS_SUCCEEDED(rv)); michael@0: bool onSTSThread; michael@0: rv = sts->IsOnCurrentThread(&onSTSThread); michael@0: MOZ_ASSERT(NS_SUCCEEDED(rv) && onSTSThread); michael@0: #endif michael@0: michael@0: RefPtr runnable = new MainThreadClearer; michael@0: runnable->DispatchToMainThreadAndWait(); michael@0: michael@0: // If NSS isn't initialized, this throws an assertion. We guard it by checking if michael@0: // the session cache might even have anything worth clearing. michael@0: if (runnable->mShouldClearSessionCache) { michael@0: SSL_ClearSessionCache(); michael@0: } michael@0: } michael@0: michael@0: namespace psm { michael@0: michael@0: namespace { michael@0: class PrivateBrowsingObserver : public nsIObserver { michael@0: public: michael@0: NS_DECL_ISUPPORTS michael@0: NS_DECL_NSIOBSERVER michael@0: PrivateBrowsingObserver(SharedSSLState* aOwner) : mOwner(aOwner) {} michael@0: virtual ~PrivateBrowsingObserver() {} michael@0: private: michael@0: SharedSSLState* mOwner; michael@0: }; michael@0: michael@0: SharedSSLState* gPublicState; michael@0: SharedSSLState* gPrivateState; michael@0: } // anonymous namespace michael@0: michael@0: NS_IMPL_ISUPPORTS(PrivateBrowsingObserver, nsIObserver) michael@0: michael@0: NS_IMETHODIMP michael@0: PrivateBrowsingObserver::Observe(nsISupports *aSubject, michael@0: const char *aTopic, michael@0: const char16_t *aData) michael@0: { michael@0: if (!nsCRT::strcmp(aTopic, "last-pb-context-exited")) { michael@0: mOwner->ResetStoredData(); michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: SharedSSLState::SharedSSLState() michael@0: : mClientAuthRemember(new nsClientAuthRememberService) michael@0: , mMutex("SharedSSLState::mMutex") michael@0: , mSocketCreated(false) michael@0: , mOCSPStaplingEnabled(false) michael@0: { michael@0: mIOLayerHelpers.Init(); michael@0: mClientAuthRemember->Init(); michael@0: } michael@0: michael@0: SharedSSLState::~SharedSSLState() michael@0: { michael@0: } michael@0: michael@0: void michael@0: SharedSSLState::NotePrivateBrowsingStatus() michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread(), "Not on main thread"); michael@0: mObserver = new PrivateBrowsingObserver(this); michael@0: nsCOMPtr obsSvc = mozilla::services::GetObserverService(); michael@0: obsSvc->AddObserver(mObserver, "last-pb-context-exited", false); michael@0: } michael@0: michael@0: void michael@0: SharedSSLState::ResetStoredData() michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread(), "Not on main thread"); michael@0: mClientAuthRemember->ClearRememberedDecisions(); michael@0: mIOLayerHelpers.clearStoredData(); michael@0: } michael@0: michael@0: void michael@0: SharedSSLState::NoteSocketCreated() michael@0: { michael@0: MutexAutoLock lock(mMutex); michael@0: mSocketCreated = true; michael@0: } michael@0: michael@0: bool michael@0: SharedSSLState::SocketCreated() michael@0: { michael@0: MutexAutoLock lock(mMutex); michael@0: return mSocketCreated; michael@0: } michael@0: michael@0: /*static*/ void michael@0: SharedSSLState::GlobalInit() michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread(), "Not on main thread"); michael@0: gPublicState = new SharedSSLState(); michael@0: gPrivateState = new SharedSSLState(); michael@0: gPrivateState->NotePrivateBrowsingStatus(); michael@0: } michael@0: michael@0: /*static*/ void michael@0: SharedSSLState::GlobalCleanup() michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread(), "Not on main thread"); michael@0: michael@0: if (gPrivateState) { michael@0: gPrivateState->Cleanup(); michael@0: delete gPrivateState; michael@0: gPrivateState = nullptr; michael@0: } michael@0: michael@0: if (gPublicState) { michael@0: gPublicState->Cleanup(); michael@0: delete gPublicState; michael@0: gPublicState = nullptr; michael@0: } michael@0: } michael@0: michael@0: /*static*/ void michael@0: SharedSSLState::NoteCertOverrideServiceInstantiated() michael@0: { michael@0: sCertOverrideSvcExists = true; michael@0: } michael@0: michael@0: /*static*/ void michael@0: SharedSSLState::NoteCertDBServiceInstantiated() michael@0: { michael@0: sCertDBExists = true; michael@0: } michael@0: michael@0: void michael@0: SharedSSLState::Cleanup() michael@0: { michael@0: mIOLayerHelpers.Cleanup(); michael@0: } michael@0: michael@0: SharedSSLState* michael@0: PublicSSLState() michael@0: { michael@0: return gPublicState; michael@0: } michael@0: michael@0: SharedSSLState* michael@0: PrivateSSLState() michael@0: { michael@0: return gPrivateState; michael@0: } michael@0: michael@0: } // namespace psm michael@0: } // namespace mozilla