diff --git a/build/pgo/certs/cert9.db b/build/pgo/certs/cert9.db index 7011091aacc0..c4f59cdf389f 100644 Binary files a/build/pgo/certs/cert9.db and b/build/pgo/certs/cert9.db differ diff --git a/build/pgo/certs/key4.db b/build/pgo/certs/key4.db index d454ed584a74..cd34021f5de6 100644 Binary files a/build/pgo/certs/key4.db and b/build/pgo/certs/key4.db differ diff --git a/build/pgo/certs/mochitest.client b/build/pgo/certs/mochitest.client index a7bb340e7c7e..3142b38d35b4 100644 Binary files a/build/pgo/certs/mochitest.client and b/build/pgo/certs/mochitest.client differ diff --git a/build/pgo/server-locations.txt b/build/pgo/server-locations.txt index 7ff79dffc3a7..3a253a40cceb 100644 --- a/build/pgo/server-locations.txt +++ b/build/pgo/server-locations.txt @@ -117,7 +117,6 @@ https://untrusted.example.com:443 privileged,cert=untrusted https://expired.example.com:443 privileged,cert=expired https://requestclientcert.example.com:443 privileged,clientauth=request https://requireclientcert.example.com:443 privileged,clientauth=require -https://requireclientcert-2.example.com:443 privileged,clientauth=require https://mismatch.expired.example.com:443 privileged,cert=expired https://mismatch.untrusted.example.com:443 privileged,cert=untrusted https://untrusted-expired.example.com:443 privileged,cert=untrustedandexpired diff --git a/security/manager/locales/en-US/security/certificates/certManager.ftl b/security/manager/locales/en-US/security/certificates/certManager.ftl index ec2c79524d97..9a8850f2b031 100644 --- a/security/manager/locales/en-US/security/certificates/certManager.ftl +++ b/security/manager/locales/en-US/security/certificates/certManager.ftl @@ -184,9 +184,6 @@ delete-email-cert-impact = If you delete a person’s e-mail certificate, you wi cert-with-serial = .value = Certificate with serial number: { $serialNumber } -# Used to indicate that the user chose not to send a client authentication certificate to a server that requested one in a TLS handshake. -send-no-client-certificate = Send no client certificate - ## Add Security Exception dialog add-exception-branded-warning = You are about to override how { -brand-short-name } identifies this site. add-exception-invalid-header = This site attempts to identify itself with invalid information. diff --git a/security/manager/pki/resources/content/certManager.js b/security/manager/pki/resources/content/certManager.js index a2da735a9ff6..4140e91e22b0 100644 --- a/security/manager/pki/resources/content/certManager.js +++ b/security/manager/pki/resources/content/certManager.js @@ -49,7 +49,7 @@ var clientAuthRememberService; var richlist; var rememberedDecisionsRichList = { - async buildRichList() { + buildRichList() { let rememberedDecisions = clientAuthRememberService.getDecisions(); let oldItems = richlist.querySelectorAll("richlistitem"); @@ -59,12 +59,10 @@ var rememberedDecisionsRichList = { let frag = document.createDocumentFragment(); for (let decision of rememberedDecisions) { - let richlistitem = await this._richBoxAddItem(decision); + let richlistitem = this._richBoxAddItem(decision); frag.appendChild(richlistitem); } richlist.appendChild(frag); - - richlist.addEventListener("select", () => this.setButtonState()); }, _createItem(item) { @@ -83,7 +81,7 @@ var rememberedDecisionsRichList = { return innerHbox; }, - async _richBoxAddItem(item) { + _richBoxAddItem(item) { let richlistitem = document.createXULElement("richlistitem"); richlistitem.setAttribute("entryKey", item.entryKey); @@ -93,29 +91,20 @@ var rememberedDecisionsRichList = { hbox.setAttribute("flex", "1"); hbox.setAttribute("equalsize", "always"); + let tmpCert = certdb.findCertByDBKey(item.dbKey); + hbox.appendChild(this._createItem(item.asciiHost)); - if (item.dbKey == "") { - let noCertSpecified = await document.l10n.formatValue( - "send-no-client-certificate" - ); - hbox.appendChild(this._createItem(noCertSpecified)); + hbox.appendChild(this._createItem(tmpCert.commonName)); - hbox.appendChild(this._createItem("")); - } else { - let tmpCert = certdb.findCertByDBKey(item.dbKey); - - hbox.appendChild(this._createItem(tmpCert.commonName)); - - hbox.appendChild(this._createItem(tmpCert.serialNumber)); - } + hbox.appendChild(this._createItem(tmpCert.serialNumber)); richlistitem.appendChild(hbox); return richlistitem; }, - async deleteSelectedRichListItem() { + deleteSelectedRichListItem() { let selectedItem = richlist.selectedItem; let index = richlist.selectedIndex; if (index < 0) { @@ -126,8 +115,7 @@ var rememberedDecisionsRichList = { selectedItem.attributes.entryKey.value ); - await this.buildRichList(); - this.setButtonState(); + this.buildRichList(); }, viewSelectedRichListItem() { @@ -137,27 +125,12 @@ var rememberedDecisionsRichList = { return; } - if (selectedItem.attributes.dbKey.value != "") { - let cert = certdb.findCertByDBKey(selectedItem.attributes.dbKey.value); - viewCertHelper(window, cert); - } - }, - - setButtonState() { - let rememberedDeleteButton = document.getElementById( - "remembered_deleteButton" - ); - let rememberedViewButton = document.getElementById("remembered_viewButton"); - - rememberedDeleteButton.disabled = richlist.selectedIndex < 0; - rememberedViewButton.disabled = - richlist.selectedItem == null - ? true - : richlist.selectedItem.attributes.dbKey.value == ""; + let cert = certdb.findCertByDBKey(selectedItem.attributes.dbKey.value); + viewCertHelper(window, cert); }, }; -async function LoadCerts() { +function LoadCerts() { certdb = Cc["@mozilla.org/security/x509certdb;1"].getService( Ci.nsIX509CertDB ); @@ -193,9 +166,7 @@ async function LoadCerts() { richlist = document.getElementById("rememberedList"); - await rememberedDecisionsRichList.buildRichList(); - - rememberedDecisionsRichList.setButtonState(); + rememberedDecisionsRichList.buildRichList(); enableBackupAllButton(); } diff --git a/security/manager/ssl/DataStorageList.h b/security/manager/ssl/DataStorageList.h index 989772c1f386..235300a4d678 100644 --- a/security/manager/ssl/DataStorageList.h +++ b/security/manager/ssl/DataStorageList.h @@ -14,7 +14,6 @@ // to something faster. DATA_STORAGE(AlternateServices) -DATA_STORAGE(ClientAuthRememberList) DATA_STORAGE(SecurityPreloadState) DATA_STORAGE(SiteSecurityServiceState) DATA_STORAGE(TRRBlacklist) diff --git a/security/manager/ssl/nsClientAuthRemember.cpp b/security/manager/ssl/nsClientAuthRemember.cpp index 2fb1ef193b82..2c71596b15ec 100644 --- a/security/manager/ssl/nsClientAuthRemember.cpp +++ b/security/manager/ssl/nsClientAuthRemember.cpp @@ -7,7 +7,6 @@ #include "nsClientAuthRemember.h" #include "mozilla/BasePrincipal.h" -#include "mozilla/DataStorage.h" #include "mozilla/RefPtr.h" #include "nsCRT.h" #include "nsNSSCertHelper.h" @@ -23,17 +22,13 @@ #include "sechash.h" #include "SharedSSLState.h" -#include "nsJSUtils.h" - using namespace mozilla; using namespace mozilla::psm; -NS_IMPL_ISUPPORTS(nsClientAuthRememberService, nsIClientAuthRememberService) +NS_IMPL_ISUPPORTS(nsClientAuthRememberService, nsIClientAuthRememberService, + nsIObserver) NS_IMPL_ISUPPORTS(nsClientAuthRemember, nsIClientAuthRememberRecord) -const nsCString nsClientAuthRemember::SentinelValue = - "no client certificate"_ns; - NS_IMETHODIMP nsClientAuthRemember::GetAsciiHost(/*out*/ nsACString& aAsciiHost) { aAsciiHost = mAsciiHost; @@ -58,18 +53,24 @@ nsClientAuthRemember::GetEntryKey(/*out*/ nsACString& aEntryKey) { return NS_OK; } +nsClientAuthRememberService::nsClientAuthRememberService() + : monitor("nsClientAuthRememberService.monitor") {} + +nsClientAuthRememberService::~nsClientAuthRememberService() { + RemoveAllFromMemory(); +} + nsresult nsClientAuthRememberService::Init() { if (!NS_IsMainThread()) { NS_ERROR("nsClientAuthRememberService::Init called off the main thread"); return NS_ERROR_NOT_SAME_THREAD; } - mClientAuthRememberList = - mozilla::DataStorage::Get(DataStorageClass::ClientAuthRememberList); - nsresult rv = mClientAuthRememberList->Init(nullptr); - - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; + nsCOMPtr observerService = + mozilla::services::GetObserverService(); + if (observerService) { + observerService->AddObserver(this, "profile-before-change", false); + observerService->AddObserver(this, "last-pb-context-exited", false); } return NS_OK; @@ -77,8 +78,10 @@ nsresult nsClientAuthRememberService::Init() { NS_IMETHODIMP nsClientAuthRememberService::ForgetRememberedDecision(const nsACString& key) { - mClientAuthRememberList->Remove(PromiseFlatCString(key), - mozilla::DataStorage_Persistent); + { + ReentrantMonitorAutoEnter lock(monitor); + mSettingsTable.RemoveEntry(PromiseFlatCString(key).get()); + } nsNSSComponent::ClearSSLExternalAndInternalSessionCacheNative(); return NS_OK; @@ -87,56 +90,57 @@ nsClientAuthRememberService::ForgetRememberedDecision(const nsACString& key) { NS_IMETHODIMP nsClientAuthRememberService::GetDecisions( nsTArray>& results) { - nsTArray decisions; - mClientAuthRememberList->GetAll(&decisions); - - for (const mozilla::psm::DataStorageItem& decision : decisions) { - if (decision.type() == DataStorageType::DataStorage_Persistent) { - RefPtr tmp = - new nsClientAuthRemember(decision.key(), decision.value()); - - results.AppendElement(tmp); + ReentrantMonitorAutoEnter lock(monitor); + for (auto iter = mSettingsTable.Iter(); !iter.Done(); iter.Next()) { + if (!nsClientAuthRememberService::IsPrivateBrowsingKey( + iter.Get()->mEntryKey)) { + results.AppendElement(iter.Get()->mSettings); } } return NS_OK; } +NS_IMETHODIMP +nsClientAuthRememberService::Observe(nsISupports* aSubject, const char* aTopic, + const char16_t* aData) { + // check the topic + if (!nsCRT::strcmp(aTopic, "profile-before-change")) { + // The profile is about to change, + // or is going away because the application is shutting down. + + ReentrantMonitorAutoEnter lock(monitor); + RemoveAllFromMemory(); + } else if (!nsCRT::strcmp(aTopic, "last-pb-context-exited")) { + ReentrantMonitorAutoEnter lock(monitor); + ClearPrivateDecisions(); + } + + return NS_OK; +} + NS_IMETHODIMP nsClientAuthRememberService::ClearRememberedDecisions() { - mClientAuthRememberList->Clear(); - nsNSSComponent::ClearSSLExternalAndInternalSessionCacheNative(); + ReentrantMonitorAutoEnter lock(monitor); + RemoveAllFromMemory(); return NS_OK; } -NS_IMETHODIMP -nsClientAuthRememberService::DeleteDecisionsByHost( - const nsACString& aHostName, JS::Handle aOriginAttributes, - JSContext* aCx) { - OriginAttributes attrs; - if (!aOriginAttributes.isObject() || !attrs.Init(aCx, aOriginAttributes)) { - return NS_ERROR_INVALID_ARG; - } - DataStorageType storageType = GetDataStorageType(attrs); - - nsTArray decisions; - mClientAuthRememberList->GetAll(&decisions); - - for (const mozilla::psm::DataStorageItem& decision : decisions) { - if (decision.type() == storageType) { - RefPtr tmp = - new nsClientAuthRemember(decision.key(), decision.value()); - nsAutoCString asciiHost; - tmp->GetAsciiHost(asciiHost); - if (asciiHost.Equals(aHostName)) { - mClientAuthRememberList->Remove(decision.key(), decision.type()); - } +nsresult nsClientAuthRememberService::ClearPrivateDecisions() { + ReentrantMonitorAutoEnter lock(monitor); + for (auto iter = mSettingsTable.Iter(); !iter.Done(); iter.Next()) { + if (nsClientAuthRememberService::IsPrivateBrowsingKey( + iter.Get()->mEntryKey)) { + iter.Remove(); } } - nsNSSComponent::ClearSSLExternalAndInternalSessionCacheNative(); return NS_OK; } +void nsClientAuthRememberService::RemoveAllFromMemory() { + mSettingsTable.Clear(); +} + NS_IMETHODIMP nsClientAuthRememberService::RememberDecision( const nsACString& aHostName, const OriginAttributes& aOriginAttributes, @@ -154,16 +158,19 @@ nsClientAuthRememberService::RememberDecision( return rv; } - if (aClientCert) { - RefPtr pipCert(new nsNSSCertificate(aClientCert)); - nsAutoCString dbkey; - rv = pipCert->GetDbKey(dbkey); - if (NS_SUCCEEDED(rv)) { - AddEntryToList(aHostName, aOriginAttributes, fpStr, dbkey); + { + ReentrantMonitorAutoEnter lock(monitor); + if (aClientCert) { + RefPtr pipCert(new nsNSSCertificate(aClientCert)); + nsAutoCString dbkey; + rv = pipCert->GetDbKey(dbkey); + if (NS_SUCCEEDED(rv)) { + AddEntryToList(aHostName, aOriginAttributes, fpStr, dbkey); + } + } else { + nsCString empty; + AddEntryToList(aHostName, aOriginAttributes, fpStr, empty); } - } else { - AddEntryToList(aHostName, aOriginAttributes, fpStr, - nsClientAuthRemember::SentinelValue); } return NS_OK; @@ -178,7 +185,6 @@ nsClientAuthRememberService::HasRememberedDecision( NS_ENSURE_ARG_POINTER(aCert); NS_ENSURE_ARG_POINTER(aRetVal); *aRetVal = false; - aCertDBKey.Truncate(); nsresult rv; nsAutoCString fpStr; @@ -187,18 +193,13 @@ nsClientAuthRememberService::HasRememberedDecision( nsAutoCString entryKey; GetEntryKey(aHostName, aOriginAttributes, fpStr, entryKey); - DataStorageType storageType = GetDataStorageType(aOriginAttributes); - - nsCString listEntry = mClientAuthRememberList->Get(entryKey, storageType); - if (listEntry.IsEmpty()) { - return NS_OK; + { + ReentrantMonitorAutoEnter lock(monitor); + nsClientAuthRememberEntry* entry = mSettingsTable.GetEntry(entryKey.get()); + if (!entry) return NS_OK; + entry->mSettings->GetDbKey(aCertDBKey); + *aRetVal = true; } - - if (!listEntry.Equals(nsClientAuthRemember::SentinelValue)) { - aCertDBKey = listEntry; - } - *aRetVal = true; - return NS_OK; } @@ -207,12 +208,20 @@ nsresult nsClientAuthRememberService::AddEntryToList( const nsACString& aFingerprint, const nsACString& aDBKey) { nsAutoCString entryKey; GetEntryKey(aHostName, aOriginAttributes, aFingerprint, entryKey); - DataStorageType storageType = GetDataStorageType(aOriginAttributes); - nsCString tmpDbKey(aDBKey); - nsresult rv = mClientAuthRememberList->Put(entryKey, tmpDbKey, storageType); - if (NS_FAILED(rv)) { - return rv; + { + ReentrantMonitorAutoEnter lock(monitor); + nsClientAuthRememberEntry* entry = mSettingsTable.PutEntry(entryKey.get()); + + if (!entry) { + NS_ERROR("can't insert a null entry!"); + return NS_ERROR_OUT_OF_MEMORY; + } + + entry->mEntryKey = entryKey; + + entry->mSettings = + new nsClientAuthRemember(aHostName, aFingerprint, aDBKey, entryKey); } return NS_OK; @@ -222,13 +231,11 @@ void nsClientAuthRememberService::GetEntryKey( const nsACString& aHostName, const OriginAttributes& aOriginAttributes, const nsACString& aFingerprint, nsACString& aEntryKey) { nsAutoCString hostCert(aHostName); - hostCert.Append(','); - hostCert.Append(aFingerprint); - hostCert.Append(','); - nsAutoCString suffix; aOriginAttributes.CreateSuffix(suffix); hostCert.Append(suffix); + hostCert.Append(':'); + hostCert.Append(aFingerprint); aEntryKey.Assign(hostCert); } @@ -244,11 +251,3 @@ bool nsClientAuthRememberService::IsPrivateBrowsingKey( } return OriginAttributes::IsPrivateBrowsing(suffix); } - -DataStorageType nsClientAuthRememberService::GetDataStorageType( - const OriginAttributes& aOriginAttributes) { - if (aOriginAttributes.mPrivateBrowsingId > 0) { - return DataStorage_Private; - } - return DataStorage_Persistent; -} diff --git a/security/manager/ssl/nsClientAuthRemember.h b/security/manager/ssl/nsClientAuthRemember.h index 9ad6fb9cb8ca..182d7f60b60d 100644 --- a/security/manager/ssl/nsClientAuthRemember.h +++ b/security/manager/ssl/nsClientAuthRemember.h @@ -10,7 +10,6 @@ #include #include "mozilla/Attributes.h" -#include "mozilla/DataStorage.h" #include "mozilla/HashFunctions.h" #include "mozilla/ReentrantMonitor.h" #include "nsIClientAuthRememberService.h" @@ -31,39 +30,74 @@ class nsClientAuthRemember final : public nsIClientAuthRememberRecord { NS_DECL_THREADSAFE_ISUPPORTS NS_DECL_NSICLIENTAUTHREMEMBERRECORD - nsClientAuthRemember(const nsCString& aEntryKey, const nsCString& aDBKey) { + nsClientAuthRemember(const nsACString& aAsciiHost, + const nsACString& aFingerprint, const nsACString& aDBKey, + const nsACString& aEntryKey) { + mAsciiHost = aAsciiHost; + mFingerprint = aFingerprint; + mDBKey = aDBKey; mEntryKey = aEntryKey; - if (!aDBKey.Equals(nsClientAuthRemember::SentinelValue)) { - mDBKey = aDBKey; - } - - nsTArray fields = {&mAsciiHost, &mFingerprint}; - - auto fieldsIter = fields.begin(); - auto splitter = aEntryKey.Split(','); - auto splitterIter = splitter.begin(); - for (; fieldsIter != fields.end() && splitterIter != splitter.end(); - ++fieldsIter, ++splitterIter) { - (*fieldsIter)->Assign(*splitterIter); - } } nsCString mAsciiHost; nsCString mFingerprint; nsCString mDBKey; nsCString mEntryKey; - static const nsCString SentinelValue; protected: ~nsClientAuthRemember() = default; }; -class nsClientAuthRememberService final : public nsIClientAuthRememberService { +// hash entry class +class nsClientAuthRememberEntry final : public PLDHashEntryHdr { + public: + // Hash methods + typedef const char* KeyType; + typedef const char* KeyTypePointer; + + // do nothing with aHost - we require mHead to be set before we're live! + explicit nsClientAuthRememberEntry(KeyTypePointer aHostWithCertUTF8) {} + + nsClientAuthRememberEntry(nsClientAuthRememberEntry&& aToMove) + : PLDHashEntryHdr(std::move(aToMove)), + mSettings(std::move(aToMove.mSettings)), + mEntryKey(std::move(aToMove.mEntryKey)) {} + + ~nsClientAuthRememberEntry() = default; + + KeyType GetKey() const { return EntryKeyPtr(); } + + KeyTypePointer GetKeyPointer() const { return EntryKeyPtr(); } + + bool KeyEquals(KeyTypePointer aKey) const { + return !strcmp(EntryKeyPtr(), aKey); + } + + static KeyTypePointer KeyToPointer(KeyType aKey) { return aKey; } + + static PLDHashNumber HashKey(KeyTypePointer aKey) { + return mozilla::HashString(aKey); + } + + enum { ALLOW_MEMMOVE = false }; + + // get methods + inline const nsCString& GetEntryKey() const { return mEntryKey; } + + inline KeyTypePointer EntryKeyPtr() const { return mEntryKey.get(); } + + nsCOMPtr mSettings; + nsCString mEntryKey; +}; + +class nsClientAuthRememberService final : public nsIObserver, + public nsIClientAuthRememberService { public: NS_DECL_THREADSAFE_ISUPPORTS + NS_DECL_NSIOBSERVER NS_DECL_NSICLIENTAUTHREMEMBERSERVICE - nsClientAuthRememberService() = default; + nsClientAuthRememberService(); nsresult Init(); @@ -75,12 +109,14 @@ class nsClientAuthRememberService final : public nsIClientAuthRememberService { static bool IsPrivateBrowsingKey(const nsCString& entryKey); protected: - ~nsClientAuthRememberService() = default; + ~nsClientAuthRememberService(); - static mozilla::DataStorageType GetDataStorageType( - const OriginAttributes& aOriginAttributes); + mozilla::ReentrantMonitor monitor; + nsTHashtable mSettingsTable; - RefPtr mClientAuthRememberList; + void RemoveAllFromMemory(); + + nsresult ClearPrivateDecisions(); nsresult AddEntryToList(const nsACString& aHost, const OriginAttributes& aOriginAttributes, diff --git a/security/manager/ssl/nsIClientAuthRememberService.idl b/security/manager/ssl/nsIClientAuthRememberService.idl index d59a80062028..d3029e89ea2c 100644 --- a/security/manager/ssl/nsIClientAuthRememberService.idl +++ b/security/manager/ssl/nsIClientAuthRememberService.idl @@ -54,9 +54,4 @@ interface nsIClientAuthRememberService : nsISupports [must_use] void clearRememberedDecisions(); - - [implicit_jscontext] - void deleteDecisionsByHost(in ACString aHostName, - in jsval aOriginAttributes); - }; diff --git a/security/manager/ssl/nsNSSComponent.cpp b/security/manager/ssl/nsNSSComponent.cpp index d601e4bba77e..ba6d480afe90 100644 --- a/security/manager/ssl/nsNSSComponent.cpp +++ b/security/manager/ssl/nsNSSComponent.cpp @@ -2488,7 +2488,14 @@ nsresult nsNSSComponent::LogoutAuthenticatedPK11() { icos->ClearValidityOverride("all:temporary-certificates"_ns, 0); } - nsNSSComponent::ClearSSLExternalAndInternalSessionCacheNative(); + nsCOMPtr svc = + do_GetService(NS_CLIENTAUTHREMEMBERSERVICE_CONTRACTID); + + if (svc) { + nsresult rv = svc->ClearRememberedDecisions(); + + Unused << NS_WARN_IF(NS_FAILED(rv)); + } nsCOMPtr os = mozilla::services::GetObserverService(); if (os) { diff --git a/security/manager/ssl/nsNSSIOLayer.cpp b/security/manager/ssl/nsNSSIOLayer.cpp index 78da68acb6e0..85992b44ad68 100644 --- a/security/manager/ssl/nsNSSIOLayer.cpp +++ b/security/manager/ssl/nsNSSIOLayer.cpp @@ -2342,12 +2342,7 @@ void ClientAuthDataRunnable::RunOnTargetThread() { if (NS_WARN_IF(NS_FAILED(rv))) { return; } - if (found) { - // An empty dbKey indicates that the user chose not to use a certificate - // and chose to remember this decision - if (rememberedDBKey.IsEmpty()) { - return; - } + if (found && !rememberedDBKey.IsEmpty()) { nsCOMPtr certdb = do_GetService(NS_X509CERTDB_CONTRACTID); if (NS_WARN_IF(!certdb)) { return; diff --git a/security/manager/ssl/tests/mochitest/browser/browser_clientAuthRememberService.js b/security/manager/ssl/tests/mochitest/browser/browser_clientAuthRememberService.js index 74e32619e623..f2d79ea9a7b3 100644 --- a/security/manager/ssl/tests/mochitest/browser/browser_clientAuthRememberService.js +++ b/security/manager/ssl/tests/mochitest/browser/browser_clientAuthRememberService.js @@ -11,7 +11,6 @@ var cert; var cert2; var cert3; -var sdr = Cc["@mozilla.org/security/sdr;1"].getService(Ci.nsISecretDecoderRing); var certDB = Cc["@mozilla.org/security/x509certdb;1"].getService( Ci.nsIX509CertDB ); @@ -31,51 +30,6 @@ function findCertByCommonName(commonName) { return null; } -async function testHelper(connectURL, expectedURL) { - let win = await BrowserTestUtils.openNewBrowserWindow(); - - await SpecialPowers.pushPrefEnv({ - set: [["security.default_personal_cert", "Ask Every Time"]], - }); - - await BrowserTestUtils.loadURI(win.gBrowser.selectedBrowser, connectURL); - - await BrowserTestUtils.browserLoaded( - win.gBrowser.selectedBrowser, - false, - expectedURL, - true - ); - let loadedURL = win.gBrowser.selectedBrowser.documentURI.spec; - Assert.ok( - loadedURL.startsWith(expectedURL), - `Expected and actual URLs should match (got '${loadedURL}', expected '${expectedURL}')` - ); - - await win.close(); - - // This clears the TLS session cache so we don't use a previously-established - // ticket to connect and bypass selecting a client auth certificate in - // subsequent tests. - sdr.logout(); -} - -async function openRequireClientCert() { - gClientAuthDialogs.chooseCertificateCalled = false; - await testHelper( - "https://requireclientcert.example.com:443", - "https://requireclientcert.example.com/" - ); -} - -async function openRequireClientCert2() { - gClientAuthDialogs.chooseCertificateCalled = false; - await testHelper( - "https://requireclientcert-2.example.com:443", - "https://requireclientcert-2.example.com/" - ); -} - // Mock implementation of nsIClientAuthRememberService const gClientAuthRememberService = { forgetRememberedDecision(key) { @@ -110,35 +64,6 @@ const gClientAuthRememberService = { QueryInterface: ChromeUtils.generateQI(["nsIClientAuthRememberService"]), }; -const gClientAuthDialogs = { - _chooseCertificateCalled: false, - - get chooseCertificateCalled() { - return this._chooseCertificateCalled; - }, - - set chooseCertificateCalled(value) { - this._chooseCertificateCalled = value; - }, - - chooseCertificate( - hostname, - port, - organization, - issuerOrg, - certList, - selectedIndex, - rememberClientAuthCertificate - ) { - rememberClientAuthCertificate.value = true; - this.chooseCertificateCalled = true; - selectedIndex.value = 0; - return true; - }, - - QueryInterface: ChromeUtils.generateQI([Ci.nsIClientAuthDialogs]), -}; - add_task(async function testRememberedDecisionsUI() { cert = findCertByCommonName("Mochitest client"); cert2 = await readCertificate("pgo-ca-all-usages.pem", ",,"); @@ -152,6 +77,10 @@ add_task(async function testRememberedDecisionsUI() { gClientAuthRememberService ); + registerCleanupFunction(() => { + MockRegistrar.unregister(clientAuthRememberServiceCID); + }); + let win = await openCertManager(); let listItems = win.document @@ -207,51 +136,4 @@ add_task(async function testRememberedDecisionsUI() { win.document.getElementById("certmanager").acceptDialog(); await BrowserTestUtils.windowClosed(win); - - MockRegistrar.unregister(clientAuthRememberServiceCID); -}); - -add_task(async function testDeletingRememberedDecisions() { - let clientAuthDialogsCID = MockRegistrar.register( - "@mozilla.org/nsClientAuthDialogs;1", - gClientAuthDialogs - ); - let cars = Cc["@mozilla.org/security/clientAuthRememberService;1"].getService( - Ci.nsIClientAuthRememberService - ); - - await openRequireClientCert(); - Assert.ok( - gClientAuthDialogs.chooseCertificateCalled, - "chooseCertificate should have been called if visiting 'requireclientcert.example.com' for the first time" - ); - - await openRequireClientCert(); - Assert.ok( - !gClientAuthDialogs.chooseCertificateCalled, - "chooseCertificate should not have been called if visiting 'requireclientcert.example.com' for the second time" - ); - - await openRequireClientCert2(); - Assert.ok( - gClientAuthDialogs.chooseCertificateCalled, - "chooseCertificate should have been called if visiting'requireclientcert-2.example.com' for the first time" - ); - - let originAttributes = { privateBrowsingId: 0 }; - cars.deleteDecisionsByHost("requireclientcert.example.com", originAttributes); - - await openRequireClientCert(); - Assert.ok( - gClientAuthDialogs.chooseCertificateCalled, - "chooseCertificate should have been called after removing all remembered decisions for 'requireclientcert.example.com'" - ); - - await openRequireClientCert2(); - Assert.ok( - !gClientAuthDialogs.chooseCertificateCalled, - "chooseCertificate should not have been called if visiting 'requireclientcert-2.example.com' for the second time" - ); - - MockRegistrar.unregister(clientAuthDialogsCID); }); diff --git a/security/manager/ssl/tests/mochitest/browser/browser_clientAuth_connection.js b/security/manager/ssl/tests/mochitest/browser/browser_clientAuth_connection.js index 044dd5ef03c9..791a2db9855e 100644 --- a/security/manager/ssl/tests/mochitest/browser/browser_clientAuth_connection.js +++ b/security/manager/ssl/tests/mochitest/browser/browser_clientAuth_connection.js @@ -21,9 +21,6 @@ const DialogState = { }; var sdr = Cc["@mozilla.org/security/sdr;1"].getService(Ci.nsISecretDecoderRing); -let cars = Cc["@mozilla.org/security/clientAuthRememberService;1"].getService( - Ci.nsIClientAuthRememberService -); var gExpectedClientCertificateChoices; @@ -163,17 +160,10 @@ add_task(async function setup() { * If the connection is expected to load successfully, the URL that * should load. If the connection is expected to fail and result in an * error page, |undefined|. - * @param {Boolean} expectCallingChooseCertificate - * Determines whether we expect chooseCertificate to be called. * @param {Object} options * Optional options object to pass on to the window that gets opened. */ -async function testHelper( - prefValue, - expectedURL, - expectCallingChooseCertificate, - options = undefined -) { +async function testHelper(prefValue, expectedURL, options = undefined) { gClientAuthDialogs.chooseCertificateCalled = false; await SpecialPowers.pushPrefEnv({ set: [["security.default_personal_cert", prefValue]], @@ -199,7 +189,7 @@ async function testHelper( ); Assert.equal( gClientAuthDialogs.chooseCertificateCalled, - expectCallingChooseCertificate, + prefValue == "Ask Every Time", "chooseCertificate should have been called if we were expecting it to be called" ); @@ -217,12 +207,11 @@ add_task(async function testCertChosenAutomatically() { gClientAuthDialogs.state = DialogState.ASSERT_NOT_CALLED; await testHelper( "Select Automatically", - "https://requireclientcert.example.com/", - false + "https://requireclientcert.example.com/" ); // This clears all saved client auth certificate state so we don't influence // subsequent tests. - cars.clearRememberedDecisions(); + sdr.logoutAndTeardown(); }); // Test that if the user doesn't choose a certificate, the connection fails and @@ -231,38 +220,16 @@ add_task(async function testCertNotChosenByUser() { gClientAuthDialogs.state = DialogState.RETURN_CERT_NOT_SELECTED; await testHelper( "Ask Every Time", - "about:neterror?e=nssFailure2&u=https%3A//requireclientcert.example.com/", - true + "about:neterror?e=nssFailure2&u=https%3A//requireclientcert.example.com/" ); - cars.clearRememberedDecisions(); + sdr.logoutAndTeardown(); }); // Test that if the user chooses a certificate the connection suceeeds. add_task(async function testCertChosenByUser() { gClientAuthDialogs.state = DialogState.RETURN_CERT_SELECTED; - await testHelper( - "Ask Every Time", - "https://requireclientcert.example.com/", - true - ); - cars.clearRememberedDecisions(); -}); - -// Test that the cancel decision is remembered correctly -add_task(async function testEmptyCertChosenByUser() { - gClientAuthDialogs.state = DialogState.RETURN_CERT_NOT_SELECTED; - gClientAuthDialogs.rememberClientAuthCertificate = true; - await testHelper( - "Ask Every Time", - "about:neterror?e=nssFailure2&u=https%3A//requireclientcert.example.com/", - true - ); - await testHelper( - "Ask Every Time", - "about:neterror?e=nssFailure2&u=https%3A//requireclientcert.example.com/", - false - ); - cars.clearRememberedDecisions(); + await testHelper("Ask Every Time", "https://requireclientcert.example.com/"); + sdr.logoutAndTeardown(); }); // Test that if the user chooses a certificate in a private browsing window, @@ -276,32 +243,18 @@ add_task(async function testEmptyCertChosenByUser() { add_task(async function testClearPrivateBrowsingState() { gClientAuthDialogs.rememberClientAuthCertificate = true; gClientAuthDialogs.state = DialogState.RETURN_CERT_SELECTED; - await testHelper( - "Ask Every Time", - "https://requireclientcert.example.com/", - true, - { - private: true, - } - ); - await testHelper( - "Ask Every Time", - "https://requireclientcert.example.com/", - true, - { - private: true, - } - ); - await testHelper( - "Ask Every Time", - "https://requireclientcert.example.com/", - true - ); - // NB: we don't `cars.clearRememberedDecisions()` in between the two calls to + await testHelper("Ask Every Time", "https://requireclientcert.example.com/", { + private: true, + }); + await testHelper("Ask Every Time", "https://requireclientcert.example.com/", { + private: true, + }); + await testHelper("Ask Every Time", "https://requireclientcert.example.com/"); + // NB: we don't `sdr.logoutAndTeardown()` in between the two calls to // `testHelper` because that would clear all client auth certificate state and // obscure what we're testing (that Firefox properly clears the relevant state // when the last private window closes). - cars.clearRememberedDecisions(); + sdr.logoutAndTeardown(); }); // Test that 3rd party certificates are taken into account when filtering client @@ -329,12 +282,8 @@ add_task(async function testCertFilteringWithIntermediate() { nssComponent.addEnterpriseIntermediate(intermediateBytes); gExpectedClientCertificateChoices = 4; gClientAuthDialogs.state = DialogState.RETURN_CERT_SELECTED; - await testHelper( - "Ask Every Time", - "https://requireclientcert.example.com/", - true - ); - cars.clearRememberedDecisions(); + await testHelper("Ask Every Time", "https://requireclientcert.example.com/"); + sdr.logoutAndTeardown(); // This will reset the added intermediate. await SpecialPowers.pushPrefEnv({ set: [["security.enterprise_roots.enabled", true]], diff --git a/toolkit/components/cleardata/ClearDataService.jsm b/toolkit/components/cleardata/ClearDataService.jsm index 9f7374de1540..06e9dd8e9e09 100644 --- a/toolkit/components/cleardata/ClearDataService.jsm +++ b/toolkit/components/cleardata/ClearDataService.jsm @@ -928,11 +928,6 @@ const SecuritySettingsCleaner = { ); } } - let cars = Cc[ - "@mozilla.org/security/clientAuthRememberService;1" - ].getService(Ci.nsIClientAuthRememberService); - - cars.deleteDecisionsByHost(aHost, aOriginAttributes); aResolve(); }); @@ -946,10 +941,6 @@ const SecuritySettingsCleaner = { Ci.nsISiteSecurityService ); sss.clearAll(); - let cars = Cc[ - "@mozilla.org/security/clientAuthRememberService;1" - ].getService(Ci.nsIClientAuthRememberService); - cars.clearRememberedDecisions(); aResolve(); }); },