Bug 1935611 - Fix libyuv/libpng link failed for loongarch64. r=glandium,tnikkel,ng
[gecko.git] / security / manager / ssl / nsClientAuthRemember.cpp
bloba5f1e54e4884252859d54d22f72fa1455a43bdcb
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 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/. */
7 #include "nsClientAuthRemember.h"
9 #include "mozilla/BasePrincipal.h"
10 #include "mozilla/RefPtr.h"
11 #include "nsCRT.h"
12 #include "nsINSSComponent.h"
13 #include "nsPrintfCString.h"
14 #include "nsNSSComponent.h"
15 #include "nsIDataStorage.h"
16 #include "nsIObserverService.h"
17 #include "nsNetUtil.h"
18 #include "nsPromiseFlatString.h"
19 #include "nsThreadUtils.h"
20 #include "cert.h"
21 #include "nspr.h"
22 #include "pk11pub.h"
23 #include "certdb.h"
24 #include "sechash.h"
26 #include "nsJSUtils.h"
28 #ifdef XP_MACOSX
29 # include <CoreFoundation/CoreFoundation.h>
30 # include <Security/Security.h>
31 # include "KeychainSecret.h" // for ScopedCFType
32 #endif // XP_MACOSX
34 using namespace mozilla;
35 using namespace mozilla::psm;
37 NS_IMPL_ISUPPORTS(nsClientAuthRememberService, nsIClientAuthRememberService)
38 NS_IMPL_ISUPPORTS(nsClientAuthRemember, nsIClientAuthRememberRecord)
40 NS_IMETHODIMP
41 nsClientAuthRemember::GetAsciiHost(/*out*/ nsACString& aAsciiHost) {
42 aAsciiHost = mAsciiHost;
43 return NS_OK;
46 NS_IMETHODIMP
47 nsClientAuthRemember::GetDbKey(/*out*/ nsACString& aDBKey) {
48 aDBKey = mDBKey;
49 return NS_OK;
52 NS_IMETHODIMP
53 nsClientAuthRemember::GetEntryKey(/*out*/ nsACString& aEntryKey) {
54 aEntryKey.Assign(mAsciiHost);
55 aEntryKey.Append(',');
56 // This used to include the SHA-256 hash of the server certificate.
57 aEntryKey.Append(',');
58 aEntryKey.Append(mOriginAttributesSuffix);
59 return NS_OK;
62 nsresult nsClientAuthRememberService::Init() {
63 if (!NS_IsMainThread()) {
64 NS_ERROR("nsClientAuthRememberService::Init called off the main thread");
65 return NS_ERROR_NOT_SAME_THREAD;
68 nsCOMPtr<nsIDataStorageManager> dataStorageManager(
69 do_GetService("@mozilla.org/security/datastoragemanager;1"));
70 if (!dataStorageManager) {
71 return NS_ERROR_FAILURE;
73 nsresult rv =
74 dataStorageManager->Get(nsIDataStorageManager::ClientAuthRememberList,
75 getter_AddRefs(mClientAuthRememberList));
76 if (NS_FAILED(rv)) {
77 return rv;
79 if (!mClientAuthRememberList) {
80 return NS_ERROR_FAILURE;
83 return NS_OK;
86 NS_IMETHODIMP
87 nsClientAuthRememberService::ForgetRememberedDecision(const nsACString& key) {
88 nsresult rv = mClientAuthRememberList->Remove(
89 PromiseFlatCString(key), nsIDataStorage::DataType::Persistent);
90 if (NS_FAILED(rv)) {
91 return rv;
93 nsCOMPtr<nsINSSComponent> nssComponent(do_GetService(NS_NSSCOMPONENT_CID));
94 if (!nssComponent) {
95 return NS_ERROR_NOT_AVAILABLE;
97 return nssComponent->ClearSSLExternalAndInternalSessionCache();
100 NS_IMETHODIMP
101 nsClientAuthRememberService::GetDecisions(
102 nsTArray<RefPtr<nsIClientAuthRememberRecord>>& results) {
103 nsTArray<RefPtr<nsIDataStorageItem>> decisions;
104 nsresult rv = mClientAuthRememberList->GetAll(decisions);
105 if (NS_FAILED(rv)) {
106 return rv;
109 for (const auto& decision : decisions) {
110 nsIDataStorage::DataType type;
111 rv = decision->GetType(&type);
112 if (NS_FAILED(rv)) {
113 return rv;
115 if (type == nsIDataStorage::DataType::Persistent) {
116 nsAutoCString key;
117 rv = decision->GetKey(key);
118 if (NS_FAILED(rv)) {
119 return rv;
121 nsAutoCString value;
122 rv = decision->GetValue(value);
123 if (NS_FAILED(rv)) {
124 return rv;
126 RefPtr<nsIClientAuthRememberRecord> tmp =
127 new nsClientAuthRemember(key, value);
129 results.AppendElement(tmp);
133 return NS_OK;
136 NS_IMETHODIMP
137 nsClientAuthRememberService::ClearRememberedDecisions() {
138 nsresult rv = mClientAuthRememberList->Clear();
139 if (NS_FAILED(rv)) {
140 return rv;
142 nsCOMPtr<nsINSSComponent> nssComponent(do_GetService(NS_NSSCOMPONENT_CID));
143 if (!nssComponent) {
144 return NS_ERROR_NOT_AVAILABLE;
146 return nssComponent->ClearSSLExternalAndInternalSessionCache();
149 NS_IMETHODIMP
150 nsClientAuthRememberService::DeleteDecisionsByHost(
151 const nsACString& aHostName, JS::Handle<JS::Value> aOriginAttributes,
152 JSContext* aCx) {
153 OriginAttributes attrs;
154 if (!aOriginAttributes.isObject() || !attrs.Init(aCx, aOriginAttributes)) {
155 return NS_ERROR_INVALID_ARG;
157 nsIDataStorage::DataType storageType = GetDataStorageType(attrs);
159 nsTArray<RefPtr<nsIDataStorageItem>> decisions;
160 nsresult rv = mClientAuthRememberList->GetAll(decisions);
161 if (NS_FAILED(rv)) {
162 return rv;
165 for (const auto& decision : decisions) {
166 nsIDataStorage::DataType type;
167 nsresult rv = decision->GetType(&type);
168 if (NS_FAILED(rv)) {
169 return rv;
171 if (type == storageType) {
172 nsAutoCString key;
173 rv = decision->GetKey(key);
174 if (NS_FAILED(rv)) {
175 return rv;
177 nsAutoCString value;
178 rv = decision->GetValue(value);
179 if (NS_FAILED(rv)) {
180 return rv;
182 RefPtr<nsIClientAuthRememberRecord> tmp =
183 new nsClientAuthRemember(key, value);
184 nsAutoCString asciiHost;
185 tmp->GetAsciiHost(asciiHost);
186 if (asciiHost.Equals(aHostName)) {
187 rv = mClientAuthRememberList->Remove(key, type);
188 if (NS_FAILED(rv)) {
189 return rv;
194 nsCOMPtr<nsINSSComponent> nssComponent(do_GetService(NS_NSSCOMPONENT_CID));
195 if (!nssComponent) {
196 return NS_ERROR_NOT_AVAILABLE;
198 return nssComponent->ClearSSLExternalAndInternalSessionCache();
201 NS_IMETHODIMP
202 nsClientAuthRememberService::RememberDecisionScriptable(
203 const nsACString& aHostName, JS::Handle<JS::Value> aOriginAttributes,
204 nsIX509Cert* aClientCert, JSContext* aCx) {
205 OriginAttributes attrs;
206 if (!aOriginAttributes.isObject() || !attrs.Init(aCx, aOriginAttributes)) {
207 return NS_ERROR_INVALID_ARG;
209 return RememberDecision(aHostName, attrs, aClientCert);
212 NS_IMETHODIMP
213 nsClientAuthRememberService::RememberDecision(
214 const nsACString& aHostName, const OriginAttributes& aOriginAttributes,
215 nsIX509Cert* aClientCert) {
216 if (aHostName.IsEmpty()) {
217 return NS_ERROR_INVALID_ARG;
220 // aClientCert == nullptr means: remember that user does not want to use a
221 // cert
222 if (aClientCert) {
223 nsAutoCString dbkey;
224 nsresult rv = aClientCert->GetDbKey(dbkey);
225 if (NS_FAILED(rv)) {
226 return rv;
228 return AddEntryToList(aHostName, aOriginAttributes, dbkey);
230 return AddEntryToList(aHostName, aOriginAttributes,
231 nsClientAuthRemember::SentinelValue);
234 #ifdef XP_MACOSX
235 // On macOS, users can add "identity preference" items in the keychain. These
236 // can be added via the Keychain Access tool. These specify mappings from
237 // URLs/wildcards like "*.mozilla.org" to specific client certificates. This
238 // function retrieves the preferred client certificate for a hostname by
239 // querying a system API that checks for these identity preferences.
240 nsresult CheckForPreferredCertificate(const nsACString& aHostName,
241 nsACString& aCertDBKey) {
242 aCertDBKey.Truncate();
243 // SecIdentityCopyPreferred seems to expect a proper URI which it can use
244 // for prefix and wildcard matches.
245 // We don't have the full URL but we can turn the hostname into a URI with
246 // an authority section, so that it matches against macOS identity preferences
247 // like `*.foo.com`. If we know that this connection is always going to be
248 // https, then we should put that in the URI as well, so that it matches
249 // identity preferences like `https://foo.com/` as well. If we can plumb
250 // the path or the full URL into this function we could also match identity
251 // preferences like `https://foo.com/bar/` but for now we cannot.
252 nsPrintfCString fakeUrl("//%s/", PromiseFlatCString(aHostName).get());
253 ScopedCFType<CFStringRef> host(::CFStringCreateWithCString(
254 kCFAllocatorDefault, fakeUrl.get(), kCFStringEncodingUTF8));
255 if (!host) {
256 return NS_ERROR_UNEXPECTED;
258 ScopedCFType<SecIdentityRef> identity(
259 ::SecIdentityCopyPreferred(host.get(), NULL, NULL));
260 if (!identity) {
261 // No preferred identity for this hostname, leave aCertDBKey empty and
262 // return
263 return NS_OK;
265 SecCertificateRef certRefRaw = NULL;
266 OSStatus copyResult =
267 ::SecIdentityCopyCertificate(identity.get(), &certRefRaw);
268 ScopedCFType<SecCertificateRef> certRef(certRefRaw);
269 if (copyResult != errSecSuccess || certRef.get() == NULL) {
270 return NS_ERROR_UNEXPECTED;
272 ScopedCFType<CFDataRef> der(::SecCertificateCopyData(certRef.get()));
273 if (!der) {
274 return NS_ERROR_UNEXPECTED;
277 nsTArray<uint8_t> derArray(::CFDataGetBytePtr(der.get()),
278 ::CFDataGetLength(der.get()));
279 nsCOMPtr<nsIX509Cert> cert(new nsNSSCertificate(std::move(derArray)));
280 return cert->GetDbKey(aCertDBKey);
282 #endif
284 void nsClientAuthRememberService::Migrate() {
285 auto migrated = mMigrated.Lock();
286 if (*migrated) {
287 return;
289 *migrated = true;
291 nsTArray<RefPtr<nsIDataStorageItem>> decisions;
292 nsresult rv = mClientAuthRememberList->GetAll(decisions);
293 if (NS_FAILED(rv)) {
294 return;
296 for (const auto& decision : decisions) {
297 nsIDataStorage::DataType type;
298 if (NS_FAILED(decision->GetType(&type))) {
299 continue;
301 if (type != nsIDataStorage::DataType::Persistent) {
302 continue;
304 nsAutoCString key;
305 if (NS_FAILED(decision->GetKey(key))) {
306 continue;
308 nsAutoCString value;
309 if (NS_FAILED(decision->GetValue(value))) {
310 continue;
312 RefPtr<nsClientAuthRemember> entry(new nsClientAuthRemember(key, value));
313 nsAutoCString newKey;
314 if (NS_FAILED(entry->GetEntryKey(newKey))) {
315 continue;
317 if (newKey != key) {
318 if (NS_FAILED(mClientAuthRememberList->Remove(
319 key, nsIDataStorage::DataType::Persistent))) {
320 continue;
322 if (NS_FAILED(mClientAuthRememberList->Put(
323 newKey, value, nsIDataStorage::DataType::Persistent))) {
324 continue;
330 NS_IMETHODIMP
331 nsClientAuthRememberService::HasRememberedDecision(
332 const nsACString& aHostName, const OriginAttributes& aOriginAttributes,
333 nsACString& aCertDBKey, bool* aRetVal) {
334 NS_ENSURE_ARG_POINTER(aRetVal);
335 if (aHostName.IsEmpty()) {
336 return NS_ERROR_INVALID_ARG;
339 *aRetVal = false;
340 aCertDBKey.Truncate();
342 Migrate();
344 nsAutoCString entryKey;
345 RefPtr<nsClientAuthRemember> entry(
346 new nsClientAuthRemember(aHostName, aOriginAttributes));
347 nsresult rv = entry->GetEntryKey(entryKey);
348 if (NS_FAILED(rv)) {
349 return rv;
351 nsIDataStorage::DataType storageType = GetDataStorageType(aOriginAttributes);
353 nsAutoCString listEntry;
354 rv = mClientAuthRememberList->Get(entryKey, storageType, listEntry);
355 if (NS_FAILED(rv) && rv != NS_ERROR_NOT_AVAILABLE) {
356 return rv;
358 if (NS_SUCCEEDED(rv) && !listEntry.IsEmpty()) {
359 if (!listEntry.Equals(nsClientAuthRemember::SentinelValue)) {
360 aCertDBKey = listEntry;
362 *aRetVal = true;
363 return NS_OK;
366 #ifdef XP_MACOSX
367 rv = CheckForPreferredCertificate(aHostName, aCertDBKey);
368 if (NS_FAILED(rv)) {
369 return rv;
371 if (!aCertDBKey.IsEmpty()) {
372 *aRetVal = true;
373 return NS_OK;
375 #endif
377 return NS_OK;
380 NS_IMETHODIMP
381 nsClientAuthRememberService::HasRememberedDecisionScriptable(
382 const nsACString& aHostName, JS::Handle<JS::Value> aOriginAttributes,
383 nsACString& aCertDBKey, JSContext* aCx, bool* aRetVal) {
384 OriginAttributes attrs;
385 if (!aOriginAttributes.isObject() || !attrs.Init(aCx, aOriginAttributes)) {
386 return NS_ERROR_INVALID_ARG;
388 return HasRememberedDecision(aHostName, attrs, aCertDBKey, aRetVal);
391 nsresult nsClientAuthRememberService::AddEntryToList(
392 const nsACString& aHostName, const OriginAttributes& aOriginAttributes,
393 const nsACString& aDBKey) {
394 nsAutoCString entryKey;
395 RefPtr<nsClientAuthRemember> entry(
396 new nsClientAuthRemember(aHostName, aOriginAttributes));
397 nsresult rv = entry->GetEntryKey(entryKey);
398 if (NS_FAILED(rv)) {
399 return rv;
401 nsIDataStorage::DataType storageType = GetDataStorageType(aOriginAttributes);
403 nsCString tmpDbKey(aDBKey);
404 rv = mClientAuthRememberList->Put(entryKey, tmpDbKey, storageType);
405 if (NS_FAILED(rv)) {
406 return rv;
409 return NS_OK;
412 bool nsClientAuthRememberService::IsPrivateBrowsingKey(
413 const nsCString& entryKey) {
414 const int32_t separator = entryKey.Find(":");
415 nsCString suffix;
416 if (separator >= 0) {
417 entryKey.Left(suffix, separator);
418 } else {
419 suffix = entryKey;
421 return OriginAttributes::IsPrivateBrowsing(suffix);
424 nsIDataStorage::DataType nsClientAuthRememberService::GetDataStorageType(
425 const OriginAttributes& aOriginAttributes) {
426 if (aOriginAttributes.IsPrivateBrowsing()) {
427 return nsIDataStorage::DataType::Private;
429 return nsIDataStorage::DataType::Persistent;