1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 *
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7 #include "nsClientAuthRemember.h"
8
9 #include "mozilla/BasePrincipal.h"
10 #include "mozilla/RefPtr.h"
11 #include "nsCRT.h"
12 #include "nsNSSCertHelper.h"
13 #include "nsIObserverService.h"
14 #include "nsNetUtil.h"
15 #include "nsPromiseFlatString.h"
16 #include "nsThreadUtils.h"
17 #include "nsStringBuffer.h"
18 #include "cert.h"
19 #include "nspr.h"
20 #include "pk11pub.h"
21 #include "certdb.h"
22 #include "sechash.h"
23 #include "SharedSSLState.h"
24
25 using namespace mozilla;
26 using namespace mozilla::psm;
27
NS_IMPL_ISUPPORTS(nsClientAuthRememberService,nsIClientAuthRememberService,nsIObserver)28 NS_IMPL_ISUPPORTS(nsClientAuthRememberService, nsIClientAuthRememberService,
29 nsIObserver)
30 NS_IMPL_ISUPPORTS(nsClientAuthRemember, nsIClientAuthRememberRecord)
31
32 NS_IMETHODIMP
33 nsClientAuthRemember::GetAsciiHost(/*out*/ nsACString& aAsciiHost) {
34 aAsciiHost = mAsciiHost;
35 return NS_OK;
36 }
37
38 NS_IMETHODIMP
GetFingerprint(nsACString & aFingerprint)39 nsClientAuthRemember::GetFingerprint(/*out*/ nsACString& aFingerprint) {
40 aFingerprint = mFingerprint;
41 return NS_OK;
42 }
43
44 NS_IMETHODIMP
GetDbKey(nsACString & aDBKey)45 nsClientAuthRemember::GetDbKey(/*out*/ nsACString& aDBKey) {
46 aDBKey = mDBKey;
47 return NS_OK;
48 }
49
50 NS_IMETHODIMP
GetEntryKey(nsACString & aEntryKey)51 nsClientAuthRemember::GetEntryKey(/*out*/ nsACString& aEntryKey) {
52 aEntryKey = mEntryKey;
53 return NS_OK;
54 }
55
nsClientAuthRememberService()56 nsClientAuthRememberService::nsClientAuthRememberService()
57 : monitor("nsClientAuthRememberService.monitor") {}
58
~nsClientAuthRememberService()59 nsClientAuthRememberService::~nsClientAuthRememberService() {
60 RemoveAllFromMemory();
61 }
62
Init()63 nsresult nsClientAuthRememberService::Init() {
64 if (!NS_IsMainThread()) {
65 NS_ERROR("nsClientAuthRememberService::Init called off the main thread");
66 return NS_ERROR_NOT_SAME_THREAD;
67 }
68
69 nsCOMPtr<nsIObserverService> observerService =
70 mozilla::services::GetObserverService();
71 if (observerService) {
72 observerService->AddObserver(this, "profile-before-change", false);
73 observerService->AddObserver(this, "last-pb-context-exited", false);
74 }
75
76 return NS_OK;
77 }
78
79 NS_IMETHODIMP
ForgetRememberedDecision(const nsACString & key)80 nsClientAuthRememberService::ForgetRememberedDecision(const nsACString& key) {
81 {
82 ReentrantMonitorAutoEnter lock(monitor);
83 mSettingsTable.RemoveEntry(PromiseFlatCString(key).get());
84 }
85
86 nsNSSComponent::ClearSSLExternalAndInternalSessionCacheNative();
87 return NS_OK;
88 }
89
90 NS_IMETHODIMP
GetDecisions(nsTArray<RefPtr<nsIClientAuthRememberRecord>> & results)91 nsClientAuthRememberService::GetDecisions(
92 nsTArray<RefPtr<nsIClientAuthRememberRecord>>& results) {
93 ReentrantMonitorAutoEnter lock(monitor);
94 for (auto iter = mSettingsTable.Iter(); !iter.Done(); iter.Next()) {
95 if (!nsClientAuthRememberService::IsPrivateBrowsingKey(
96 iter.Get()->mEntryKey)) {
97 results.AppendElement(iter.Get()->mSettings);
98 }
99 }
100
101 return NS_OK;
102 }
103
104 NS_IMETHODIMP
Observe(nsISupports * aSubject,const char * aTopic,const char16_t * aData)105 nsClientAuthRememberService::Observe(nsISupports* aSubject, const char* aTopic,
106 const char16_t* aData) {
107 // check the topic
108 if (!nsCRT::strcmp(aTopic, "profile-before-change")) {
109 // The profile is about to change,
110 // or is going away because the application is shutting down.
111
112 ReentrantMonitorAutoEnter lock(monitor);
113 RemoveAllFromMemory();
114 } else if (!nsCRT::strcmp(aTopic, "last-pb-context-exited")) {
115 ReentrantMonitorAutoEnter lock(monitor);
116 ClearPrivateDecisions();
117 }
118
119 return NS_OK;
120 }
121
122 NS_IMETHODIMP
ClearRememberedDecisions()123 nsClientAuthRememberService::ClearRememberedDecisions() {
124 ReentrantMonitorAutoEnter lock(monitor);
125 RemoveAllFromMemory();
126 return NS_OK;
127 }
128
ClearPrivateDecisions()129 nsresult nsClientAuthRememberService::ClearPrivateDecisions() {
130 ReentrantMonitorAutoEnter lock(monitor);
131 for (auto iter = mSettingsTable.Iter(); !iter.Done(); iter.Next()) {
132 if (nsClientAuthRememberService::IsPrivateBrowsingKey(
133 iter.Get()->mEntryKey)) {
134 iter.Remove();
135 }
136 }
137 return NS_OK;
138 }
139
RemoveAllFromMemory()140 void nsClientAuthRememberService::RemoveAllFromMemory() {
141 mSettingsTable.Clear();
142 }
143
144 NS_IMETHODIMP
RememberDecision(const nsACString & aHostName,const OriginAttributes & aOriginAttributes,CERTCertificate * aServerCert,CERTCertificate * aClientCert)145 nsClientAuthRememberService::RememberDecision(
146 const nsACString& aHostName, const OriginAttributes& aOriginAttributes,
147 CERTCertificate* aServerCert, CERTCertificate* aClientCert) {
148 // aClientCert == nullptr means: remember that user does not want to use a
149 // cert
150 NS_ENSURE_ARG_POINTER(aServerCert);
151 if (aHostName.IsEmpty()) {
152 return NS_ERROR_INVALID_ARG;
153 }
154
155 nsAutoCString fpStr;
156 nsresult rv = GetCertFingerprintByOidTag(aServerCert, SEC_OID_SHA256, fpStr);
157 if (NS_FAILED(rv)) {
158 return rv;
159 }
160
161 {
162 ReentrantMonitorAutoEnter lock(monitor);
163 if (aClientCert) {
164 RefPtr<nsNSSCertificate> pipCert(new nsNSSCertificate(aClientCert));
165 nsAutoCString dbkey;
166 rv = pipCert->GetDbKey(dbkey);
167 if (NS_SUCCEEDED(rv)) {
168 AddEntryToList(aHostName, aOriginAttributes, fpStr, dbkey);
169 }
170 } else {
171 nsCString empty;
172 AddEntryToList(aHostName, aOriginAttributes, fpStr, empty);
173 }
174 }
175
176 return NS_OK;
177 }
178
179 NS_IMETHODIMP
HasRememberedDecision(const nsACString & aHostName,const OriginAttributes & aOriginAttributes,CERTCertificate * aCert,nsACString & aCertDBKey,bool * aRetVal)180 nsClientAuthRememberService::HasRememberedDecision(
181 const nsACString& aHostName, const OriginAttributes& aOriginAttributes,
182 CERTCertificate* aCert, nsACString& aCertDBKey, bool* aRetVal) {
183 if (aHostName.IsEmpty()) return NS_ERROR_INVALID_ARG;
184
185 NS_ENSURE_ARG_POINTER(aCert);
186 NS_ENSURE_ARG_POINTER(aRetVal);
187 *aRetVal = false;
188
189 nsresult rv;
190 nsAutoCString fpStr;
191 rv = GetCertFingerprintByOidTag(aCert, SEC_OID_SHA256, fpStr);
192 if (NS_FAILED(rv)) return rv;
193
194 nsAutoCString entryKey;
195 GetEntryKey(aHostName, aOriginAttributes, fpStr, entryKey);
196 {
197 ReentrantMonitorAutoEnter lock(monitor);
198 nsClientAuthRememberEntry* entry = mSettingsTable.GetEntry(entryKey.get());
199 if (!entry) return NS_OK;
200 entry->mSettings->GetDbKey(aCertDBKey);
201 *aRetVal = true;
202 }
203 return NS_OK;
204 }
205
AddEntryToList(const nsACString & aHostName,const OriginAttributes & aOriginAttributes,const nsACString & aFingerprint,const nsACString & aDBKey)206 nsresult nsClientAuthRememberService::AddEntryToList(
207 const nsACString& aHostName, const OriginAttributes& aOriginAttributes,
208 const nsACString& aFingerprint, const nsACString& aDBKey) {
209 nsAutoCString entryKey;
210 GetEntryKey(aHostName, aOriginAttributes, aFingerprint, entryKey);
211
212 {
213 ReentrantMonitorAutoEnter lock(monitor);
214 nsClientAuthRememberEntry* entry = mSettingsTable.PutEntry(entryKey.get());
215
216 if (!entry) {
217 NS_ERROR("can't insert a null entry!");
218 return NS_ERROR_OUT_OF_MEMORY;
219 }
220
221 entry->mEntryKey = entryKey;
222
223 entry->mSettings =
224 new nsClientAuthRemember(aHostName, aFingerprint, aDBKey, entryKey);
225 }
226
227 return NS_OK;
228 }
229
GetEntryKey(const nsACString & aHostName,const OriginAttributes & aOriginAttributes,const nsACString & aFingerprint,nsACString & aEntryKey)230 void nsClientAuthRememberService::GetEntryKey(
231 const nsACString& aHostName, const OriginAttributes& aOriginAttributes,
232 const nsACString& aFingerprint, nsACString& aEntryKey) {
233 nsAutoCString hostCert(aHostName);
234 nsAutoCString suffix;
235 aOriginAttributes.CreateSuffix(suffix);
236 hostCert.Append(suffix);
237 hostCert.Append(':');
238 hostCert.Append(aFingerprint);
239
240 aEntryKey.Assign(hostCert);
241 }
242
IsPrivateBrowsingKey(const nsCString & entryKey)243 bool nsClientAuthRememberService::IsPrivateBrowsingKey(
244 const nsCString& entryKey) {
245 const int32_t separator = entryKey.Find(":", false, 0, -1);
246 nsCString suffix;
247 if (separator >= 0) {
248 entryKey.Left(suffix, separator);
249 } else {
250 suffix = entryKey;
251 }
252 return OriginAttributes::IsPrivateBrowsing(suffix);
253 }
254