1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
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 "nsCertOverrideService.h"
9 #include "NSSCertDBTrustDomain.h"
10 #include "ScopedNSSTypes.h"
11 #include "SharedSSLState.h"
12 #include "mozilla/Assertions.h"
13 #include "mozilla/ScopeExit.h"
14 #include "mozilla/TaskQueue.h"
15 #include "mozilla/Telemetry.h"
16 #include "mozilla/TextUtils.h"
17 #include "mozilla/Tokenizer.h"
18 #include "mozilla/Unused.h"
19 #include "mozilla/dom/ToJSValue.h"
20 #include "nsAppDirectoryServiceDefs.h"
22 #include "nsILineInputStream.h"
23 #ifdef ENABLE_WEBDRIVER
24 # include "nsIMarionette.h"
26 #include "nsIObserver.h"
27 #include "nsIObserverService.h"
28 #include "nsIOutputStream.h"
29 #ifdef ENABLE_WEBDRIVER
30 # include "nsIRemoteAgent.h"
32 #include "nsISafeOutputStream.h"
33 #include "nsIX509Cert.h"
34 #include "nsNSSCertificate.h"
35 #include "nsNSSComponent.h"
36 #include "nsNetUtil.h"
37 #include "nsStreamUtils.h"
38 #include "nsStringBuffer.h"
39 #include "nsThreadUtils.h"
41 using namespace mozilla
;
42 using namespace mozilla::psm
;
44 #define CERT_OVERRIDE_FILE_NAME "cert_override.txt"
46 class WriterRunnable
: public Runnable
{
48 WriterRunnable(nsCertOverrideService
* aService
, nsCString
& aData
,
49 nsCOMPtr
<nsIFile
> aFile
)
50 : Runnable("nsCertOverrideService::WriterRunnable"),
51 mCertOverrideService(aService
),
53 mFile(std::move(aFile
)) {}
57 mCertOverrideService
->AssertOnTaskQueue();
60 auto removeShutdownBlockerOnExit
=
61 MakeScopeExit([certOverrideService
= mCertOverrideService
]() {
62 NS_DispatchToMainThread(NS_NewRunnableFunction(
63 "nsCertOverrideService::RemoveShutdownBlocker",
64 [certOverrideService
] {
65 certOverrideService
->RemoveShutdownBlocker();
69 nsCOMPtr
<nsIOutputStream
> outputStream
;
70 rv
= NS_NewSafeLocalFileOutputStream(
71 getter_AddRefs(outputStream
), mFile
,
72 PR_CREATE_FILE
| PR_TRUNCATE
| PR_WRONLY
);
73 NS_ENSURE_SUCCESS(rv
, rv
);
75 const char* ptr
= mData
.get();
76 uint32_t remaining
= mData
.Length();
78 while (remaining
> 0) {
79 rv
= outputStream
->Write(ptr
, remaining
, &written
);
80 NS_ENSURE_SUCCESS(rv
, rv
);
85 nsCOMPtr
<nsISafeOutputStream
> safeStream
= do_QueryInterface(outputStream
);
86 MOZ_ASSERT(safeStream
);
87 rv
= safeStream
->Finish();
88 NS_ENSURE_SUCCESS(rv
, rv
);
94 const RefPtr
<nsCertOverrideService
> mCertOverrideService
;
96 const nsCOMPtr
<nsIFile
> mFile
;
99 NS_IMPL_ISUPPORTS(nsCertOverride
, nsICertOverride
)
102 nsCertOverride::GetAsciiHost(/*out*/ nsACString
& aAsciiHost
) {
103 aAsciiHost
= mAsciiHost
;
108 nsCertOverride::GetFingerprint(/*out*/ nsACString
& aFingerprint
) {
109 aFingerprint
= mFingerprint
;
114 nsCertOverride::GetPort(/*out*/ int32_t* aPort
) {
120 nsCertOverride::GetHostPort(/*out*/ nsACString
& aHostPort
) {
121 nsCertOverrideService::GetHostWithPort(mAsciiHost
, mPort
, aHostPort
);
126 nsCertOverride::GetOriginAttributes(
127 JSContext
* aCtx
, /*out*/ JS::MutableHandle
<JS::Value
> aValue
) {
128 if (ToJSValue(aCtx
, mOriginAttributes
, aValue
)) {
131 return NS_ERROR_FAILURE
;
134 NS_IMPL_ISUPPORTS(nsCertOverrideService
, nsICertOverrideService
, nsIObserver
,
135 nsISupportsWeakReference
, nsIAsyncShutdownBlocker
)
137 nsCertOverrideService::nsCertOverrideService()
138 : mMutex("nsCertOverrideService.mutex"),
139 mDisableAllSecurityCheck(false),
140 mPendingWriteCount(0) {
141 nsCOMPtr
<nsIEventTarget
> target
=
142 do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID
);
145 mWriterTaskQueue
= TaskQueue::Create(target
.forget(), "CertOverrideService");
148 nsCertOverrideService::~nsCertOverrideService() = default;
150 static nsCOMPtr
<nsIAsyncShutdownClient
> GetShutdownBarrier() {
151 MOZ_ASSERT(NS_IsMainThread());
152 nsCOMPtr
<nsIAsyncShutdownService
> svc
=
153 mozilla::services::GetAsyncShutdownService();
154 MOZ_RELEASE_ASSERT(svc
);
156 nsCOMPtr
<nsIAsyncShutdownClient
> barrier
;
157 nsresult rv
= svc
->GetProfileBeforeChange(getter_AddRefs(barrier
));
159 MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv
));
160 MOZ_RELEASE_ASSERT(barrier
);
164 nsresult
nsCertOverrideService::Init() {
165 if (!NS_IsMainThread()) {
166 MOZ_ASSERT_UNREACHABLE("nsCertOverrideService initialized off main thread");
167 return NS_ERROR_NOT_SAME_THREAD
;
170 nsCOMPtr
<nsIObserverService
> observerService
=
171 mozilla::services::GetObserverService();
173 // If we cannot add ourselves as a profile change observer, then we will not
174 // attempt to read/write any settings file. Otherwise, we would end up
175 // reading/writing the wrong settings file after a profile change.
176 if (observerService
) {
177 observerService
->AddObserver(this, "profile-do-change", true);
178 // simulate a profile change so we read the current profile's settings file
179 Observe(nullptr, "profile-do-change", nullptr);
182 SharedSSLState::NoteCertOverrideServiceInstantiated();
187 nsCertOverrideService::Observe(nsISupports
*, const char* aTopic
,
188 const char16_t
* aData
) {
189 if (!nsCRT::strcmp(aTopic
, "profile-do-change")) {
190 // The profile has already changed.
191 // Now read from the new profile location.
192 // we also need to update the cached file location
194 MutexAutoLock
lock(mMutex
);
196 nsresult rv
= NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR
,
197 getter_AddRefs(mSettingsFile
));
198 if (NS_SUCCEEDED(rv
)) {
199 mSettingsFile
->AppendNative(nsLiteralCString(CERT_OVERRIDE_FILE_NAME
));
201 mSettingsFile
= nullptr;
204 CountPermanentOverrideTelemetry(lock
);
210 void nsCertOverrideService::RemoveAllTemporaryOverrides() {
211 MutexAutoLock
lock(mMutex
);
212 bool removedAny
= false;
213 for (auto iter
= mSettingsTable
.Iter(); !iter
.Done(); iter
.Next()) {
214 nsCertOverrideEntry
* entry
= iter
.Get();
215 if (entry
->mSettings
->mIsTemporary
) {
221 nsCOMPtr
<nsIObserverService
> os
= mozilla::services::GetObserverService();
223 os
->NotifyObservers(nullptr, "net:cancel-all-connections", nullptr);
226 // no need to write, as temporaries are never written to disk
229 static const char sSHA256OIDString
[] = "OID.2.16.840.1.101.3.4.2.1";
230 nsresult
nsCertOverrideService::Read(const MutexAutoLock
& aProofOfLock
) {
231 mMutex
.AssertCurrentThreadOwns();
232 // If we don't have a profile, then we won't try to read any settings file.
233 if (!mSettingsFile
) return NS_OK
;
236 nsCOMPtr
<nsIInputStream
> fileInputStream
;
237 rv
= NS_NewLocalFileInputStream(getter_AddRefs(fileInputStream
),
243 nsCOMPtr
<nsILineInputStream
> lineInputStream
=
244 do_QueryInterface(fileInputStream
, &rv
);
249 nsAutoCString buffer
;
252 // Each line is of the form:
253 // host:port:originAttributes \t sSHA256OIDString \t fingerprint \t
254 // There may be some "bits" identifiers and "dbKey" after the `fingerprint`
255 // field in 'fingerprint \t \t dbKey' format, but these are now ignored.
256 // Lines that don't match this form are silently dropped.
258 while (isMore
&& NS_SUCCEEDED(lineInputStream
->ReadLine(buffer
, &isMore
))) {
259 if (buffer
.IsEmpty() || buffer
.First() == '#') {
263 Tokenizer
parser(buffer
);
264 nsDependentCSubstring host
;
265 if (parser
.CheckChar('[')) { // this is a IPv6 address
266 if (!parser
.ReadUntil(Tokenizer::Token::Char(']'), host
) ||
267 host
.Length() == 0 || !parser
.CheckChar(':')) {
270 } else if (!parser
.ReadUntil(Tokenizer::Token::Char(':'), host
) ||
271 host
.Length() == 0) {
275 if (!parser
.ReadInteger(&port
)) {
278 OriginAttributes attributes
;
279 if (parser
.CheckChar(':')) {
280 nsDependentCSubstring attributesString
;
281 if (!parser
.ReadUntil(Tokenizer::Token::Whitespace(), attributesString
) ||
282 !attributes
.PopulateFromSuffix(attributesString
)) {
285 } else if (!parser
.CheckWhite()) {
288 nsDependentCSubstring algorithm
;
289 if (!parser
.ReadUntil(Tokenizer::Token::Whitespace(), algorithm
) ||
290 algorithm
!= sSHA256OIDString
) {
293 nsDependentCSubstring fingerprint
;
294 if (!parser
.ReadUntil(Tokenizer::Token::Whitespace(), fingerprint
) ||
295 fingerprint
.Length() == 0) {
299 AddEntryToList(host
, port
, attributes
,
300 false, // not temporary
301 fingerprint
, aProofOfLock
);
307 nsresult
nsCertOverrideService::Write(const MutexAutoLock
& aProofOfLock
) {
308 mMutex
.AssertCurrentThreadOwns();
309 MOZ_ASSERT(NS_IsMainThread());
310 if (!NS_IsMainThread()) {
311 return NS_ERROR_NOT_SAME_THREAD
;
314 // If we don't have any profile, then we won't try to write any file
315 if (!mSettingsFile
) {
321 static const char kHeader
[] =
322 "# PSM Certificate Override Settings file" NS_LINEBREAK
323 "# This is a generated file! Do not edit." NS_LINEBREAK
;
325 /* see ::Read for file format */
327 output
.Append(kHeader
);
329 static const char kTab
[] = "\t";
330 for (auto iter
= mSettingsTable
.Iter(); !iter
.Done(); iter
.Next()) {
331 nsCertOverrideEntry
* entry
= iter
.Get();
333 RefPtr
<nsCertOverride
> settings
= entry
->mSettings
;
334 if (settings
->mIsTemporary
) {
338 output
.Append(entry
->mKeyString
);
340 output
.Append(sSHA256OIDString
);
342 output
.Append(settings
->mFingerprint
);
344 // the "bits" string used to go here, but it no longer exists
345 // the "\t dbKey" string used to go here, but it no longer exists
346 output
.Append(NS_LINEBREAK
);
349 // Make a clone of the file to pass to the WriterRunnable.
350 nsCOMPtr
<nsIFile
> file
;
352 rv
= mSettingsFile
->Clone(getter_AddRefs(file
));
353 NS_ENSURE_SUCCESS(rv
, rv
);
355 nsCOMPtr
<nsIRunnable
> runnable
= new WriterRunnable(this, output
, file
);
356 rv
= mWriterTaskQueue
->Dispatch(runnable
.forget());
360 mPendingWriteCount
++;
362 if (mPendingWriteCount
== 1) {
363 rv
= GetShutdownBarrier()->AddBlocker(
364 this, NS_LITERAL_STRING_FROM_CSTRING(__FILE__
), __LINE__
,
365 u
"nsCertOverrideService writing data"_ns
);
366 NS_ENSURE_SUCCESS(rv
, rv
);
372 nsresult
GetCertSha256Fingerprint(nsIX509Cert
* aCert
, nsCString
& aResult
) {
373 nsAutoString fpStrUTF16
;
374 nsresult rv
= aCert
->GetSha256Fingerprint(fpStrUTF16
);
378 aResult
.Assign(NS_ConvertUTF16toUTF8(fpStrUTF16
));
383 nsCertOverrideService::RememberValidityOverride(
384 const nsACString
& aHostName
, int32_t aPort
,
385 const OriginAttributes
& aOriginAttributes
, nsIX509Cert
* aCert
,
387 if (aHostName
.IsEmpty() || !IsAscii(aHostName
) || !aCert
) {
388 return NS_ERROR_INVALID_ARG
;
391 return NS_ERROR_INVALID_ARG
;
393 if (!NS_IsMainThread()) {
394 return NS_ERROR_NOT_SAME_THREAD
;
397 UniqueCERTCertificate
nsscert(aCert
->GetCert());
399 return NS_ERROR_FAILURE
;
403 nsresult rv
= GetCertSha256Fingerprint(aCert
, fpStr
);
409 MutexAutoLock
lock(mMutex
);
410 AddEntryToList(aHostName
, aPort
, aOriginAttributes
, aTemporary
, fpStr
,
421 nsCertOverrideService::RememberValidityOverrideScriptable(
422 const nsACString
& aHostName
, int32_t aPort
,
423 JS::Handle
<JS::Value
> aOriginAttributes
, nsIX509Cert
* aCert
,
424 bool aTemporary
, JSContext
* aCx
) {
425 OriginAttributes attrs
;
426 if (!aOriginAttributes
.isObject() || !attrs
.Init(aCx
, aOriginAttributes
)) {
427 return NS_ERROR_INVALID_ARG
;
430 return RememberValidityOverride(aHostName
, aPort
, attrs
, aCert
, aTemporary
);
434 nsCertOverrideService::HasMatchingOverride(
435 const nsACString
& aHostName
, int32_t aPort
,
436 const OriginAttributes
& aOriginAttributes
, nsIX509Cert
* aCert
,
437 bool* aIsTemporary
, bool* aRetval
) {
438 bool disableAllSecurityCheck
= false;
440 MutexAutoLock
lock(mMutex
);
441 disableAllSecurityCheck
= mDisableAllSecurityCheck
;
443 if (disableAllSecurityCheck
) {
444 *aIsTemporary
= false;
449 if (aHostName
.IsEmpty() || !IsAscii(aHostName
)) {
450 return NS_ERROR_INVALID_ARG
;
452 if (aPort
< -1) return NS_ERROR_INVALID_ARG
;
454 NS_ENSURE_ARG_POINTER(aCert
);
455 NS_ENSURE_ARG_POINTER(aIsTemporary
);
456 NS_ENSURE_ARG_POINTER(aRetval
);
459 RefPtr
<nsCertOverride
> settings(
460 GetOverrideFor(aHostName
, aPort
, aOriginAttributes
));
461 // If there is no corresponding override and the given OriginAttributes isn't
462 // the default, try to look up an override using the default OriginAttributes.
463 if (!settings
&& aOriginAttributes
!= OriginAttributes()) {
464 settings
= GetOverrideFor(aHostName
, aPort
, OriginAttributes());
470 *aIsTemporary
= settings
->mIsTemporary
;
473 nsresult rv
= GetCertSha256Fingerprint(aCert
, fpStr
);
478 *aRetval
= settings
->mFingerprint
.Equals(fpStr
);
482 already_AddRefed
<nsCertOverride
> nsCertOverrideService::GetOverrideFor(
483 const nsACString
& aHostName
, int32_t aPort
,
484 const OriginAttributes
& aOriginAttributes
) {
485 nsAutoCString keyString
;
486 GetKeyString(aHostName
, aPort
, aOriginAttributes
, keyString
);
487 MutexAutoLock
lock(mMutex
);
488 nsCertOverrideEntry
* entry
= mSettingsTable
.GetEntry(keyString
.get());
492 return do_AddRef(entry
->mSettings
);
496 nsCertOverrideService::HasMatchingOverrideScriptable(
497 const nsACString
& aHostName
, int32_t aPort
,
498 JS::Handle
<JS::Value
> aOriginAttributes
, nsIX509Cert
* aCert
,
499 bool* aIsTemporary
, JSContext
* aCx
, bool* aRetval
) {
500 OriginAttributes attrs
;
501 if (!aOriginAttributes
.isObject() || !attrs
.Init(aCx
, aOriginAttributes
)) {
502 return NS_ERROR_INVALID_ARG
;
505 return HasMatchingOverride(aHostName
, aPort
, attrs
, aCert
, aIsTemporary
,
509 nsresult
nsCertOverrideService::AddEntryToList(
510 const nsACString
& aHostName
, int32_t aPort
,
511 const OriginAttributes
& aOriginAttributes
, const bool aIsTemporary
,
512 const nsACString
& fingerprint
, const MutexAutoLock
& aProofOfLock
) {
513 mMutex
.AssertCurrentThreadOwns();
514 nsAutoCString keyString
;
515 GetKeyString(aHostName
, aPort
, aOriginAttributes
, keyString
);
517 nsCertOverrideEntry
* entry
= mSettingsTable
.PutEntry(keyString
.get());
520 NS_ERROR("can't insert a null entry!");
521 return NS_ERROR_OUT_OF_MEMORY
;
524 entry
->mKeyString
= keyString
;
526 RefPtr
<nsCertOverride
> settings(new nsCertOverride());
528 settings
->mAsciiHost
= aHostName
;
529 settings
->mPort
= aPort
;
530 settings
->mOriginAttributes
= aOriginAttributes
;
531 settings
->mIsTemporary
= aIsTemporary
;
532 settings
->mFingerprint
= fingerprint
;
533 entry
->mSettings
= settings
;
539 nsCertOverrideService::ClearValidityOverride(
540 const nsACString
& aHostName
, int32_t aPort
,
541 const OriginAttributes
& aOriginAttributes
) {
542 if (aHostName
.IsEmpty() || !IsAscii(aHostName
)) {
543 return NS_ERROR_INVALID_ARG
;
545 if (!NS_IsMainThread()) {
546 return NS_ERROR_NOT_SAME_THREAD
;
549 if (aPort
== 0 && aHostName
.EqualsLiteral("all:temporary-certificates")) {
550 RemoveAllTemporaryOverrides();
553 nsAutoCString keyString
;
554 GetKeyString(aHostName
, aPort
, aOriginAttributes
, keyString
);
556 MutexAutoLock
lock(mMutex
);
557 mSettingsTable
.RemoveEntry(keyString
.get());
561 nsCOMPtr
<nsINSSComponent
> nss(do_GetService(PSM_COMPONENT_CONTRACTID
));
563 nss
->ClearSSLExternalAndInternalSessionCache();
565 return NS_ERROR_NOT_AVAILABLE
;
568 nsCOMPtr
<nsIObserverService
> os
= mozilla::services::GetObserverService();
570 os
->NotifyObservers(nullptr, "net:cancel-all-connections", nullptr);
576 nsCertOverrideService::ClearValidityOverrideScriptable(
577 const nsACString
& aHostName
, int32_t aPort
,
578 JS::Handle
<JS::Value
> aOriginAttributes
, JSContext
* aCx
) {
579 OriginAttributes attrs
;
580 if (!aOriginAttributes
.isObject() || !attrs
.Init(aCx
, aOriginAttributes
)) {
581 return NS_ERROR_INVALID_ARG
;
584 return ClearValidityOverride(aHostName
, aPort
, attrs
);
588 nsCertOverrideService::ClearAllOverrides() {
589 if (!NS_IsMainThread()) {
590 return NS_ERROR_NOT_SAME_THREAD
;
594 MutexAutoLock
lock(mMutex
);
595 mSettingsTable
.Clear();
599 nsCOMPtr
<nsINSSComponent
> nss(do_GetService(PSM_COMPONENT_CONTRACTID
));
601 nss
->ClearSSLExternalAndInternalSessionCache();
603 return NS_ERROR_NOT_AVAILABLE
;
606 nsCOMPtr
<nsIObserverService
> os
= mozilla::services::GetObserverService();
608 os
->NotifyObservers(nullptr, "net:cancel-all-connections", nullptr);
614 void nsCertOverrideService::CountPermanentOverrideTelemetry(
615 const MutexAutoLock
& aProofOfLock
) {
616 mMutex
.AssertCurrentThreadOwns();
617 uint32_t overrideCount
= 0;
618 for (auto iter
= mSettingsTable
.Iter(); !iter
.Done(); iter
.Next()) {
619 if (!iter
.Get()->mSettings
->mIsTemporary
) {
623 Telemetry::Accumulate(Telemetry::SSL_PERMANENT_CERT_ERROR_OVERRIDES
,
627 static bool IsDebugger() {
628 #ifdef ENABLE_WEBDRIVER
629 nsCOMPtr
<nsIMarionette
> marionette
= do_GetService(NS_MARIONETTE_CONTRACTID
);
631 bool marionetteRunning
= false;
632 marionette
->GetRunning(&marionetteRunning
);
633 if (marionetteRunning
) {
638 nsCOMPtr
<nsIRemoteAgent
> agent
= do_GetService(NS_REMOTEAGENT_CONTRACTID
);
640 bool remoteAgentRunning
= false;
641 agent
->GetRunning(&remoteAgentRunning
);
642 if (remoteAgentRunning
) {
652 nsCertOverrideService::
653 SetDisableAllSecurityChecksAndLetAttackersInterceptMyData(bool aDisable
) {
654 if (!(PR_GetEnv("XPCSHELL_TEST_PROFILE_DIR") || IsDebugger())) {
655 return NS_ERROR_NOT_AVAILABLE
;
659 MutexAutoLock
lock(mMutex
);
660 mDisableAllSecurityCheck
= aDisable
;
663 nsCOMPtr
<nsINSSComponent
> nss(do_GetService(PSM_COMPONENT_CONTRACTID
));
665 nss
->ClearSSLExternalAndInternalSessionCache();
667 return NS_ERROR_NOT_AVAILABLE
;
674 nsCertOverrideService::GetSecurityCheckDisabled(bool* aDisabled
) {
675 MutexAutoLock
lock(mMutex
);
676 *aDisabled
= mDisableAllSecurityCheck
;
681 nsCertOverrideService::GetOverrides(
682 /*out*/ nsTArray
<RefPtr
<nsICertOverride
>>& retval
) {
683 MutexAutoLock
lock(mMutex
);
684 for (auto iter
= mSettingsTable
.Iter(); !iter
.Done(); iter
.Next()) {
685 const RefPtr
<nsICertOverride
> settings
= iter
.Get()->mSettings
;
687 retval
.AppendElement(settings
);
692 void nsCertOverrideService::GetHostWithPort(const nsACString
& aHostName
,
694 nsACString
& aRetval
) {
695 nsAutoCString hostPort
;
696 if (aHostName
.Contains(':')) {
697 // if aHostName is an IPv6 address, add brackets to match the internal
698 // representation, which always stores IPv6 addresses with brackets
699 hostPort
.Append('[');
700 hostPort
.Append(aHostName
);
701 hostPort
.Append(']');
703 hostPort
.Append(aHostName
);
708 if (!hostPort
.IsEmpty()) {
709 hostPort
.Append(':');
710 hostPort
.AppendInt(aPort
);
712 aRetval
.Assign(hostPort
);
715 void nsCertOverrideService::GetKeyString(
716 const nsACString
& aHostName
, int32_t aPort
,
717 const OriginAttributes
& aOriginAttributes
, nsACString
& aRetval
) {
718 nsAutoCString keyString
;
719 GetHostWithPort(aHostName
, aPort
, keyString
);
720 keyString
.Append(':');
721 OriginAttributes
strippedAttributes(aOriginAttributes
);
722 strippedAttributes
.StripAttributes(
723 ~OriginAttributes::STRIP_PRIVATE_BROWSING_ID
);
724 nsAutoCString attributeSuffix
;
725 strippedAttributes
.CreateSuffix(attributeSuffix
);
726 keyString
.Append(attributeSuffix
);
727 aRetval
.Assign(keyString
);
730 // nsIAsyncShutdownBlocker implementation
732 nsCertOverrideService::GetName(nsAString
& aName
) {
733 aName
= u
"nsCertOverrideService: shutdown"_ns
;
738 nsCertOverrideService::GetState(nsIPropertyBag
** aState
) {
740 return NS_ERROR_INVALID_ARG
;
747 nsCertOverrideService::BlockShutdown(nsIAsyncShutdownClient
*) { return NS_OK
; }
749 void nsCertOverrideService::RemoveShutdownBlocker() {
750 MOZ_ASSERT(NS_IsMainThread());
751 MOZ_ASSERT(mPendingWriteCount
> 0);
752 mPendingWriteCount
--;
753 if (mPendingWriteCount
== 0) {
754 nsresult rv
= GetShutdownBarrier()->RemoveBlocker(this);
755 MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv
));