summaryrefslogtreecommitdiffstats
path: root/security/manager/ssl/nsClientAuthRemember.cpp
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--security/manager/ssl/nsClientAuthRemember.cpp276
1 files changed, 276 insertions, 0 deletions
diff --git a/security/manager/ssl/nsClientAuthRemember.cpp b/security/manager/ssl/nsClientAuthRemember.cpp
new file mode 100644
index 0000000000..a61b98ce8b
--- /dev/null
+++ b/security/manager/ssl/nsClientAuthRemember.cpp
@@ -0,0 +1,276 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsClientAuthRemember.h"
+
+#include "mozilla/BasePrincipal.h"
+#include "mozilla/DataStorage.h"
+#include "mozilla/RefPtr.h"
+#include "nsCRT.h"
+#include "nsNSSCertHelper.h"
+#include "nsIObserverService.h"
+#include "nsNetUtil.h"
+#include "nsPromiseFlatString.h"
+#include "nsThreadUtils.h"
+#include "nsStringBuffer.h"
+#include "cert.h"
+#include "nspr.h"
+#include "pk11pub.h"
+#include "certdb.h"
+#include "sechash.h"
+#include "SharedSSLState.h"
+
+#include "nsJSUtils.h"
+
+using namespace mozilla;
+using namespace mozilla::psm;
+
+NS_IMPL_ISUPPORTS(nsClientAuthRememberService, nsIClientAuthRememberService)
+NS_IMPL_ISUPPORTS(nsClientAuthRemember, nsIClientAuthRememberRecord)
+
+const nsCString nsClientAuthRemember::SentinelValue =
+ "no client certificate"_ns;
+
+NS_IMETHODIMP
+nsClientAuthRemember::GetAsciiHost(/*out*/ nsACString& aAsciiHost) {
+ aAsciiHost = mAsciiHost;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsClientAuthRemember::GetFingerprint(/*out*/ nsACString& aFingerprint) {
+ aFingerprint = mFingerprint;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsClientAuthRemember::GetDbKey(/*out*/ nsACString& aDBKey) {
+ aDBKey = mDBKey;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsClientAuthRemember::GetEntryKey(/*out*/ nsACString& aEntryKey) {
+ aEntryKey = mEntryKey;
+ return NS_OK;
+}
+
+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;
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsClientAuthRememberService::ForgetRememberedDecision(const nsACString& key) {
+ mClientAuthRememberList->Remove(PromiseFlatCString(key),
+ mozilla::DataStorage_Persistent);
+
+ nsCOMPtr<nsINSSComponent> nssComponent(do_GetService(NS_NSSCOMPONENT_CID));
+ if (!nssComponent) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+ return nssComponent->ClearSSLExternalAndInternalSessionCache();
+}
+
+NS_IMETHODIMP
+nsClientAuthRememberService::GetDecisions(
+ nsTArray<RefPtr<nsIClientAuthRememberRecord>>& results) {
+ nsTArray<mozilla::psm::DataStorageItem> decisions;
+ mClientAuthRememberList->GetAll(&decisions);
+
+ for (const mozilla::psm::DataStorageItem& decision : decisions) {
+ if (decision.type() == DataStorageType::DataStorage_Persistent) {
+ RefPtr<nsIClientAuthRememberRecord> tmp =
+ new nsClientAuthRemember(decision.key(), decision.value());
+
+ results.AppendElement(tmp);
+ }
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsClientAuthRememberService::ClearRememberedDecisions() {
+ mClientAuthRememberList->Clear();
+ nsCOMPtr<nsINSSComponent> nssComponent(do_GetService(NS_NSSCOMPONENT_CID));
+ if (!nssComponent) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+ return nssComponent->ClearSSLExternalAndInternalSessionCache();
+}
+
+NS_IMETHODIMP
+nsClientAuthRememberService::DeleteDecisionsByHost(
+ const nsACString& aHostName, JS::Handle<JS::Value> aOriginAttributes,
+ JSContext* aCx) {
+ OriginAttributes attrs;
+ if (!aOriginAttributes.isObject() || !attrs.Init(aCx, aOriginAttributes)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+ DataStorageType storageType = GetDataStorageType(attrs);
+
+ nsTArray<mozilla::psm::DataStorageItem> decisions;
+ mClientAuthRememberList->GetAll(&decisions);
+
+ for (const mozilla::psm::DataStorageItem& decision : decisions) {
+ if (decision.type() == storageType) {
+ RefPtr<nsIClientAuthRememberRecord> tmp =
+ new nsClientAuthRemember(decision.key(), decision.value());
+ nsAutoCString asciiHost;
+ tmp->GetAsciiHost(asciiHost);
+ if (asciiHost.Equals(aHostName)) {
+ mClientAuthRememberList->Remove(decision.key(), decision.type());
+ }
+ }
+ }
+ nsCOMPtr<nsINSSComponent> nssComponent(do_GetService(NS_NSSCOMPONENT_CID));
+ if (!nssComponent) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+ return nssComponent->ClearSSLExternalAndInternalSessionCache();
+}
+
+static nsresult GetCertSha256Fingerprint(CERTCertificate* aNssCert,
+ nsCString& aResult) {
+ nsCOMPtr<nsIX509Cert> cert(nsNSSCertificate::Create(aNssCert));
+ nsAutoString fpStrUTF16;
+ nsresult rv = cert->GetSha256Fingerprint(fpStrUTF16);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ aResult.Assign(NS_ConvertUTF16toUTF8(fpStrUTF16));
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsClientAuthRememberService::RememberDecision(
+ const nsACString& aHostName, const OriginAttributes& aOriginAttributes,
+ CERTCertificate* aServerCert, CERTCertificate* aClientCert) {
+ // aClientCert == nullptr means: remember that user does not want to use a
+ // cert
+ NS_ENSURE_ARG_POINTER(aServerCert);
+ if (aHostName.IsEmpty()) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ nsAutoCString fpStr;
+ nsresult rv = GetCertSha256Fingerprint(aServerCert, fpStr);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ if (aClientCert) {
+ RefPtr<nsNSSCertificate> pipCert(new nsNSSCertificate(aClientCert));
+ nsAutoCString dbkey;
+ rv = pipCert->GetDbKey(dbkey);
+ if (NS_SUCCEEDED(rv)) {
+ AddEntryToList(aHostName, aOriginAttributes, fpStr, dbkey);
+ }
+ } else {
+ AddEntryToList(aHostName, aOriginAttributes, fpStr,
+ nsClientAuthRemember::SentinelValue);
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsClientAuthRememberService::HasRememberedDecision(
+ const nsACString& aHostName, const OriginAttributes& aOriginAttributes,
+ CERTCertificate* aCert, nsACString& aCertDBKey, bool* aRetVal) {
+ if (aHostName.IsEmpty()) return NS_ERROR_INVALID_ARG;
+
+ NS_ENSURE_ARG_POINTER(aCert);
+ NS_ENSURE_ARG_POINTER(aRetVal);
+ *aRetVal = false;
+ aCertDBKey.Truncate();
+
+ nsAutoCString fpStr;
+ nsresult rv = GetCertSha256Fingerprint(aCert, fpStr);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ nsAutoCString entryKey;
+ GetEntryKey(aHostName, aOriginAttributes, fpStr, entryKey);
+ DataStorageType storageType = GetDataStorageType(aOriginAttributes);
+
+ nsCString listEntry = mClientAuthRememberList->Get(entryKey, storageType);
+ if (listEntry.IsEmpty()) {
+ return NS_OK;
+ }
+
+ if (!listEntry.Equals(nsClientAuthRemember::SentinelValue)) {
+ aCertDBKey = listEntry;
+ }
+ *aRetVal = true;
+
+ return NS_OK;
+}
+
+nsresult nsClientAuthRememberService::AddEntryToList(
+ const nsACString& aHostName, const OriginAttributes& aOriginAttributes,
+ 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;
+ }
+
+ return NS_OK;
+}
+
+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);
+
+ aEntryKey.Assign(hostCert);
+}
+
+bool nsClientAuthRememberService::IsPrivateBrowsingKey(
+ const nsCString& entryKey) {
+ const int32_t separator = entryKey.Find(":", false, 0, -1);
+ nsCString suffix;
+ if (separator >= 0) {
+ entryKey.Left(suffix, separator);
+ } else {
+ suffix = entryKey;
+ }
+ return OriginAttributes::IsPrivateBrowsing(suffix);
+}
+
+DataStorageType nsClientAuthRememberService::GetDataStorageType(
+ const OriginAttributes& aOriginAttributes) {
+ if (aOriginAttributes.mPrivateBrowsingId > 0) {
+ return DataStorage_Private;
+ }
+ return DataStorage_Persistent;
+}