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: #include "nsClientAuthRemember.h" michael@0: michael@0: #include "nsIX509Cert.h" michael@0: #include "mozilla/RefPtr.h" michael@0: #include "nsCRT.h" michael@0: #include "nsNetUtil.h" michael@0: #include "nsNSSCertHelper.h" michael@0: #include "nsIObserverService.h" michael@0: #include "nsNetUtil.h" michael@0: #include "nsISupportsPrimitives.h" michael@0: #include "nsPromiseFlatString.h" michael@0: #include "nsThreadUtils.h" michael@0: #include "nsStringBuffer.h" michael@0: #include "cert.h" michael@0: #include "nspr.h" michael@0: #include "pk11pub.h" michael@0: #include "certdb.h" michael@0: #include "sechash.h" michael@0: #include "SharedSSLState.h" michael@0: michael@0: using namespace mozilla; michael@0: using namespace mozilla::psm; michael@0: michael@0: NS_IMPL_ISUPPORTS(nsClientAuthRememberService, michael@0: nsIObserver, michael@0: nsISupportsWeakReference) michael@0: michael@0: nsClientAuthRememberService::nsClientAuthRememberService() michael@0: : monitor("nsClientAuthRememberService.monitor") michael@0: { michael@0: } michael@0: michael@0: nsClientAuthRememberService::~nsClientAuthRememberService() michael@0: { michael@0: RemoveAllFromMemory(); michael@0: } michael@0: michael@0: nsresult michael@0: nsClientAuthRememberService::Init() michael@0: { michael@0: if (!NS_IsMainThread()) { michael@0: NS_ERROR("nsClientAuthRememberService::Init called off the main thread"); michael@0: return NS_ERROR_NOT_SAME_THREAD; michael@0: } michael@0: michael@0: nsCOMPtr observerService = michael@0: mozilla::services::GetObserverService(); michael@0: if (observerService) { michael@0: observerService->AddObserver(this, "profile-before-change", true); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsClientAuthRememberService::Observe(nsISupports *aSubject, michael@0: const char *aTopic, michael@0: const char16_t *aData) michael@0: { michael@0: // check the topic michael@0: if (!nsCRT::strcmp(aTopic, "profile-before-change")) { michael@0: // The profile is about to change, michael@0: // or is going away because the application is shutting down. michael@0: michael@0: ReentrantMonitorAutoEnter lock(monitor); michael@0: RemoveAllFromMemory(); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: void nsClientAuthRememberService::ClearRememberedDecisions() michael@0: { michael@0: ReentrantMonitorAutoEnter lock(monitor); michael@0: RemoveAllFromMemory(); michael@0: } michael@0: michael@0: void nsClientAuthRememberService::ClearAllRememberedDecisions() michael@0: { michael@0: RefPtr svc = michael@0: PublicSSLState()->GetClientAuthRememberService(); michael@0: svc->ClearRememberedDecisions(); michael@0: michael@0: svc = PrivateSSLState()->GetClientAuthRememberService(); michael@0: svc->ClearRememberedDecisions(); michael@0: } michael@0: michael@0: void michael@0: nsClientAuthRememberService::RemoveAllFromMemory() michael@0: { michael@0: mSettingsTable.Clear(); michael@0: } michael@0: michael@0: nsresult michael@0: nsClientAuthRememberService::RememberDecision(const nsACString & aHostName, michael@0: CERTCertificate *aServerCert, CERTCertificate *aClientCert) michael@0: { michael@0: // aClientCert == nullptr means: remember that user does not want to use a cert michael@0: NS_ENSURE_ARG_POINTER(aServerCert); michael@0: if (aHostName.IsEmpty()) michael@0: return NS_ERROR_INVALID_ARG; michael@0: michael@0: nsAutoCString fpStr; michael@0: nsresult rv = GetCertFingerprintByOidTag(aServerCert, SEC_OID_SHA256, fpStr); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: { michael@0: ReentrantMonitorAutoEnter lock(monitor); michael@0: if (aClientCert) { michael@0: nsNSSCertificate pipCert(aClientCert); michael@0: char *dbkey = nullptr; michael@0: rv = pipCert.GetDbKey(&dbkey); michael@0: if (NS_SUCCEEDED(rv) && dbkey) { michael@0: AddEntryToList(aHostName, fpStr, michael@0: nsDependentCString(dbkey)); michael@0: } michael@0: if (dbkey) { michael@0: PORT_Free(dbkey); michael@0: } michael@0: } michael@0: else { michael@0: nsCString empty; michael@0: AddEntryToList(aHostName, fpStr, empty); michael@0: } michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsClientAuthRememberService::HasRememberedDecision(const nsACString & aHostName, michael@0: CERTCertificate *aCert, michael@0: nsACString & aCertDBKey, michael@0: bool *_retval) michael@0: { michael@0: if (aHostName.IsEmpty()) michael@0: return NS_ERROR_INVALID_ARG; michael@0: michael@0: NS_ENSURE_ARG_POINTER(aCert); michael@0: NS_ENSURE_ARG_POINTER(_retval); michael@0: *_retval = false; michael@0: michael@0: nsresult rv; michael@0: nsAutoCString fpStr; michael@0: rv = GetCertFingerprintByOidTag(aCert, SEC_OID_SHA256, fpStr); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: nsAutoCString hostCert; michael@0: GetHostWithCert(aHostName, fpStr, hostCert); michael@0: nsClientAuthRemember settings; michael@0: michael@0: { michael@0: ReentrantMonitorAutoEnter lock(monitor); michael@0: nsClientAuthRememberEntry *entry = mSettingsTable.GetEntry(hostCert.get()); michael@0: if (!entry) michael@0: return NS_OK; michael@0: settings = entry->mSettings; // copy michael@0: } michael@0: michael@0: aCertDBKey = settings.mDBKey; michael@0: *_retval = true; michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsClientAuthRememberService::AddEntryToList(const nsACString &aHostName, michael@0: const nsACString &fingerprint, michael@0: const nsACString &db_key) michael@0: michael@0: { michael@0: nsAutoCString hostCert; michael@0: GetHostWithCert(aHostName, fingerprint, hostCert); michael@0: michael@0: { michael@0: ReentrantMonitorAutoEnter lock(monitor); michael@0: nsClientAuthRememberEntry *entry = mSettingsTable.PutEntry(hostCert.get()); michael@0: michael@0: if (!entry) { michael@0: NS_ERROR("can't insert a null entry!"); michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: } michael@0: michael@0: entry->mHostWithCert = hostCert; michael@0: michael@0: nsClientAuthRemember &settings = entry->mSettings; michael@0: settings.mAsciiHost = aHostName; michael@0: settings.mFingerprint = fingerprint; michael@0: settings.mDBKey = db_key; michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: nsClientAuthRememberService::GetHostWithCert(const nsACString & aHostName, michael@0: const nsACString & fingerprint, michael@0: nsACString& _retval) michael@0: { michael@0: nsAutoCString hostCert(aHostName); michael@0: hostCert.AppendLiteral(":"); michael@0: hostCert.Append(fingerprint); michael@0: michael@0: _retval.Assign(hostCert); michael@0: }