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 "mozilla/Assertions.h"
11 #include "mozilla/ScopeExit.h"
12 #include "mozilla/TaskQueue.h"
13 #include "mozilla/Telemetry.h"
14 #include "mozilla/TextUtils.h"
15 #include "mozilla/Tokenizer.h"
16 #include "mozilla/Unused.h"
17 #include "mozilla/dom/ToJSValue.h"
18 #include "nsAppDirectoryServiceDefs.h"
20 #include "nsILineInputStream.h"
21 #ifdef ENABLE_WEBDRIVER
22 # include "nsIMarionette.h"
24 #include "nsIObserver.h"
25 #include "nsIObserverService.h"
26 #include "nsIOutputStream.h"
27 #ifdef ENABLE_WEBDRIVER
28 # include "nsIRemoteAgent.h"
30 #include "nsISafeOutputStream.h"
31 #include "nsIX509Cert.h"
32 #include "nsNSSCertificate.h"
33 #include "nsNSSComponent.h"
34 #include "nsNetUtil.h"
35 #include "nsStreamUtils.h"
36 #include "nsThreadUtils.h"
38 using namespace mozilla
;
39 using namespace mozilla::psm
;
41 #define CERT_OVERRIDE_FILE_NAME "cert_override.txt"
43 class WriterRunnable
: public Runnable
{
45 WriterRunnable(nsCertOverrideService
* aService
, nsCString
& aData
,
46 nsCOMPtr
<nsIFile
> aFile
)
47 : Runnable("nsCertOverrideService::WriterRunnable"),
48 mCertOverrideService(aService
),
50 mFile(std::move(aFile
)) {}
54 mCertOverrideService
->AssertOnTaskQueue();
57 auto removeShutdownBlockerOnExit
=
58 MakeScopeExit([certOverrideService
= mCertOverrideService
]() {
59 NS_DispatchToMainThread(NS_NewRunnableFunction(
60 "nsCertOverrideService::RemoveShutdownBlocker",
61 [certOverrideService
] {
62 certOverrideService
->RemoveShutdownBlocker();
66 nsCOMPtr
<nsIOutputStream
> outputStream
;
67 rv
= NS_NewSafeLocalFileOutputStream(
68 getter_AddRefs(outputStream
), mFile
,
69 PR_CREATE_FILE
| PR_TRUNCATE
| PR_WRONLY
);
70 NS_ENSURE_SUCCESS(rv
, rv
);
72 const char* ptr
= mData
.get();
73 uint32_t remaining
= mData
.Length();
75 while (remaining
> 0) {
76 rv
= outputStream
->Write(ptr
, remaining
, &written
);
77 NS_ENSURE_SUCCESS(rv
, rv
);
82 nsCOMPtr
<nsISafeOutputStream
> safeStream
= do_QueryInterface(outputStream
);
83 MOZ_ASSERT(safeStream
);
84 rv
= safeStream
->Finish();
85 NS_ENSURE_SUCCESS(rv
, rv
);
91 const RefPtr
<nsCertOverrideService
> mCertOverrideService
;
93 const nsCOMPtr
<nsIFile
> mFile
;
96 NS_IMPL_ISUPPORTS(nsCertOverride
, nsICertOverride
)
99 nsCertOverride::GetAsciiHost(/*out*/ nsACString
& aAsciiHost
) {
100 aAsciiHost
= mAsciiHost
;
105 nsCertOverride::GetFingerprint(/*out*/ nsACString
& aFingerprint
) {
106 aFingerprint
= mFingerprint
;
111 nsCertOverride::GetPort(/*out*/ int32_t* aPort
) {
117 nsCertOverride::GetHostPort(/*out*/ nsACString
& aHostPort
) {
118 nsCertOverrideService::GetHostWithPort(mAsciiHost
, mPort
, aHostPort
);
123 nsCertOverride::GetOriginAttributes(
124 JSContext
* aCtx
, /*out*/ JS::MutableHandle
<JS::Value
> aValue
) {
125 if (ToJSValue(aCtx
, mOriginAttributes
, aValue
)) {
128 return NS_ERROR_FAILURE
;
131 NS_IMPL_ISUPPORTS(nsCertOverrideService
, nsICertOverrideService
, nsIObserver
,
132 nsISupportsWeakReference
, nsIAsyncShutdownBlocker
)
134 nsCertOverrideService::nsCertOverrideService()
135 : mMutex("nsCertOverrideService.mutex"),
136 mDisableAllSecurityCheck(false),
137 mPendingWriteCount(0) {
138 nsCOMPtr
<nsIEventTarget
> target
=
139 do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID
);
142 mWriterTaskQueue
= TaskQueue::Create(target
.forget(), "CertOverrideService");
145 nsCertOverrideService::~nsCertOverrideService() = default;
147 static nsCOMPtr
<nsIAsyncShutdownClient
> GetShutdownBarrier() {
148 MOZ_ASSERT(NS_IsMainThread());
149 nsCOMPtr
<nsIAsyncShutdownService
> svc
=
150 mozilla::services::GetAsyncShutdownService();
151 MOZ_RELEASE_ASSERT(svc
);
153 nsCOMPtr
<nsIAsyncShutdownClient
> barrier
;
154 nsresult rv
= svc
->GetProfileBeforeChange(getter_AddRefs(barrier
));
156 MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv
));
157 MOZ_RELEASE_ASSERT(barrier
);
161 nsresult
nsCertOverrideService::Init() {
162 if (!NS_IsMainThread()) {
163 MOZ_ASSERT_UNREACHABLE("nsCertOverrideService initialized off main thread");
164 return NS_ERROR_NOT_SAME_THREAD
;
167 nsCOMPtr
<nsIObserverService
> observerService
=
168 mozilla::services::GetObserverService();
170 // If we cannot add ourselves as a profile change observer, then we will not
171 // attempt to read/write any settings file. Otherwise, we would end up
172 // reading/writing the wrong settings file after a profile change.
173 if (observerService
) {
174 observerService
->AddObserver(this, "last-pb-context-exited", false);
175 observerService
->AddObserver(this, "profile-do-change", true);
176 // simulate a profile change so we read the current profile's settings file
177 Observe(nullptr, "profile-do-change", nullptr);
184 nsCertOverrideService::Observe(nsISupports
*, const char* aTopic
,
185 const char16_t
* aData
) {
186 if (!nsCRT::strcmp(aTopic
, "profile-do-change")) {
187 // The profile has already changed.
188 // Now read from the new profile location.
189 // we also need to update the cached file location
191 MutexAutoLock
lock(mMutex
);
193 nsresult rv
= NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR
,
194 getter_AddRefs(mSettingsFile
));
195 if (NS_SUCCEEDED(rv
)) {
196 mSettingsFile
->AppendNative(nsLiteralCString(CERT_OVERRIDE_FILE_NAME
));
198 mSettingsFile
= nullptr;
201 CountPermanentOverrideTelemetry(lock
);
202 } else if (!nsCRT::strcmp(aTopic
, "last-pb-context-exited")) {
203 ClearValidityOverride("all:temporary-certificates"_ns
, 0,
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
;
398 nsresult rv
= GetCertSha256Fingerprint(aCert
, fpStr
);
404 MutexAutoLock
lock(mMutex
);
405 AddEntryToList(aHostName
, aPort
, aOriginAttributes
, aTemporary
, fpStr
,
416 nsCertOverrideService::RememberValidityOverrideScriptable(
417 const nsACString
& aHostName
, int32_t aPort
,
418 JS::Handle
<JS::Value
> aOriginAttributes
, nsIX509Cert
* aCert
,
419 bool aTemporary
, JSContext
* aCx
) {
420 OriginAttributes attrs
;
421 if (!aOriginAttributes
.isObject() || !attrs
.Init(aCx
, aOriginAttributes
)) {
422 return NS_ERROR_INVALID_ARG
;
425 return RememberValidityOverride(aHostName
, aPort
, attrs
, aCert
, aTemporary
);
429 nsCertOverrideService::HasMatchingOverride(
430 const nsACString
& aHostName
, int32_t aPort
,
431 const OriginAttributes
& aOriginAttributes
, nsIX509Cert
* aCert
,
432 bool* aIsTemporary
, bool* aRetval
) {
433 bool disableAllSecurityCheck
= false;
435 MutexAutoLock
lock(mMutex
);
436 disableAllSecurityCheck
= mDisableAllSecurityCheck
;
438 if (disableAllSecurityCheck
) {
439 *aIsTemporary
= false;
444 if (aHostName
.IsEmpty() || !IsAscii(aHostName
)) {
445 return NS_ERROR_INVALID_ARG
;
447 if (aPort
< -1) return NS_ERROR_INVALID_ARG
;
449 NS_ENSURE_ARG_POINTER(aCert
);
450 NS_ENSURE_ARG_POINTER(aIsTemporary
);
451 NS_ENSURE_ARG_POINTER(aRetval
);
454 RefPtr
<nsCertOverride
> settings(
455 GetOverrideFor(aHostName
, aPort
, aOriginAttributes
));
456 // If there is no corresponding override and the given OriginAttributes isn't
457 // the default, try to look up an override using the default OriginAttributes.
458 if (!settings
&& aOriginAttributes
!= OriginAttributes()) {
459 settings
= GetOverrideFor(aHostName
, aPort
, OriginAttributes());
465 *aIsTemporary
= settings
->mIsTemporary
;
468 nsresult rv
= GetCertSha256Fingerprint(aCert
, fpStr
);
473 *aRetval
= settings
->mFingerprint
.Equals(fpStr
);
477 already_AddRefed
<nsCertOverride
> nsCertOverrideService::GetOverrideFor(
478 const nsACString
& aHostName
, int32_t aPort
,
479 const OriginAttributes
& aOriginAttributes
) {
480 nsAutoCString keyString
;
481 GetKeyString(aHostName
, aPort
, aOriginAttributes
, keyString
);
482 MutexAutoLock
lock(mMutex
);
483 nsCertOverrideEntry
* entry
= mSettingsTable
.GetEntry(keyString
.get());
487 return do_AddRef(entry
->mSettings
);
491 nsCertOverrideService::HasMatchingOverrideScriptable(
492 const nsACString
& aHostName
, int32_t aPort
,
493 JS::Handle
<JS::Value
> aOriginAttributes
, nsIX509Cert
* aCert
,
494 bool* aIsTemporary
, JSContext
* aCx
, bool* aRetval
) {
495 OriginAttributes attrs
;
496 if (!aOriginAttributes
.isObject() || !attrs
.Init(aCx
, aOriginAttributes
)) {
497 return NS_ERROR_INVALID_ARG
;
500 return HasMatchingOverride(aHostName
, aPort
, attrs
, aCert
, aIsTemporary
,
504 nsresult
nsCertOverrideService::AddEntryToList(
505 const nsACString
& aHostName
, int32_t aPort
,
506 const OriginAttributes
& aOriginAttributes
, const bool aIsTemporary
,
507 const nsACString
& fingerprint
, const MutexAutoLock
& aProofOfLock
) {
508 mMutex
.AssertCurrentThreadOwns();
509 nsAutoCString keyString
;
510 GetKeyString(aHostName
, aPort
, aOriginAttributes
, keyString
);
512 nsCertOverrideEntry
* entry
= mSettingsTable
.PutEntry(keyString
.get());
515 NS_ERROR("can't insert a null entry!");
516 return NS_ERROR_OUT_OF_MEMORY
;
519 entry
->mKeyString
= keyString
;
521 RefPtr
<nsCertOverride
> settings(new nsCertOverride());
523 settings
->mAsciiHost
= aHostName
;
524 settings
->mPort
= aPort
;
525 settings
->mOriginAttributes
= aOriginAttributes
;
526 settings
->mIsTemporary
= aIsTemporary
;
527 settings
->mFingerprint
= fingerprint
;
528 entry
->mSettings
= settings
;
534 nsCertOverrideService::ClearValidityOverride(
535 const nsACString
& aHostName
, int32_t aPort
,
536 const OriginAttributes
& aOriginAttributes
) {
537 if (aHostName
.IsEmpty() || !IsAscii(aHostName
)) {
538 return NS_ERROR_INVALID_ARG
;
540 if (!NS_IsMainThread()) {
541 return NS_ERROR_NOT_SAME_THREAD
;
544 if (aPort
== 0 && aHostName
.EqualsLiteral("all:temporary-certificates")) {
545 RemoveAllTemporaryOverrides();
548 nsAutoCString keyString
;
549 GetKeyString(aHostName
, aPort
, aOriginAttributes
, keyString
);
551 MutexAutoLock
lock(mMutex
);
552 mSettingsTable
.RemoveEntry(keyString
.get());
556 nsCOMPtr
<nsINSSComponent
> nss(do_GetService(PSM_COMPONENT_CONTRACTID
));
558 nss
->ClearSSLExternalAndInternalSessionCache();
560 return NS_ERROR_NOT_AVAILABLE
;
563 nsCOMPtr
<nsIObserverService
> os
= mozilla::services::GetObserverService();
565 os
->NotifyObservers(nullptr, "net:cancel-all-connections", nullptr);
571 nsCertOverrideService::ClearValidityOverrideScriptable(
572 const nsACString
& aHostName
, int32_t aPort
,
573 JS::Handle
<JS::Value
> aOriginAttributes
, JSContext
* aCx
) {
574 OriginAttributes attrs
;
575 if (!aOriginAttributes
.isObject() || !attrs
.Init(aCx
, aOriginAttributes
)) {
576 return NS_ERROR_INVALID_ARG
;
579 return ClearValidityOverride(aHostName
, aPort
, attrs
);
583 nsCertOverrideService::ClearAllOverrides() {
584 if (!NS_IsMainThread()) {
585 return NS_ERROR_NOT_SAME_THREAD
;
589 MutexAutoLock
lock(mMutex
);
590 mSettingsTable
.Clear();
594 nsCOMPtr
<nsINSSComponent
> nss(do_GetService(PSM_COMPONENT_CONTRACTID
));
596 nss
->ClearSSLExternalAndInternalSessionCache();
598 return NS_ERROR_NOT_AVAILABLE
;
601 nsCOMPtr
<nsIObserverService
> os
= mozilla::services::GetObserverService();
603 os
->NotifyObservers(nullptr, "net:cancel-all-connections", nullptr);
609 void nsCertOverrideService::CountPermanentOverrideTelemetry(
610 const MutexAutoLock
& aProofOfLock
) {
611 mMutex
.AssertCurrentThreadOwns();
612 uint32_t overrideCount
= 0;
613 for (auto iter
= mSettingsTable
.Iter(); !iter
.Done(); iter
.Next()) {
614 if (!iter
.Get()->mSettings
->mIsTemporary
) {
618 Telemetry::Accumulate(Telemetry::SSL_PERMANENT_CERT_ERROR_OVERRIDES
,
622 static bool IsDebugger() {
623 #ifdef ENABLE_WEBDRIVER
624 nsCOMPtr
<nsIMarionette
> marionette
= do_GetService(NS_MARIONETTE_CONTRACTID
);
626 bool marionetteRunning
= false;
627 marionette
->GetRunning(&marionetteRunning
);
628 if (marionetteRunning
) {
633 nsCOMPtr
<nsIRemoteAgent
> agent
= do_GetService(NS_REMOTEAGENT_CONTRACTID
);
635 bool remoteAgentRunning
= false;
636 agent
->GetRunning(&remoteAgentRunning
);
637 if (remoteAgentRunning
) {
647 nsCertOverrideService::
648 SetDisableAllSecurityChecksAndLetAttackersInterceptMyData(bool aDisable
) {
649 if (!(PR_GetEnv("XPCSHELL_TEST_PROFILE_DIR") || IsDebugger())) {
650 return NS_ERROR_NOT_AVAILABLE
;
654 MutexAutoLock
lock(mMutex
);
655 mDisableAllSecurityCheck
= aDisable
;
658 nsCOMPtr
<nsINSSComponent
> nss(do_GetService(PSM_COMPONENT_CONTRACTID
));
660 nss
->ClearSSLExternalAndInternalSessionCache();
662 return NS_ERROR_NOT_AVAILABLE
;
669 nsCertOverrideService::GetSecurityCheckDisabled(bool* aDisabled
) {
670 MutexAutoLock
lock(mMutex
);
671 *aDisabled
= mDisableAllSecurityCheck
;
676 nsCertOverrideService::GetOverrides(
677 /*out*/ nsTArray
<RefPtr
<nsICertOverride
>>& retval
) {
678 MutexAutoLock
lock(mMutex
);
679 for (auto iter
= mSettingsTable
.Iter(); !iter
.Done(); iter
.Next()) {
680 const RefPtr
<nsICertOverride
> settings
= iter
.Get()->mSettings
;
682 retval
.AppendElement(settings
);
687 void nsCertOverrideService::GetHostWithPort(const nsACString
& aHostName
,
689 nsACString
& aRetval
) {
690 nsAutoCString hostPort
;
691 if (aHostName
.Contains(':')) {
692 // if aHostName is an IPv6 address, add brackets to match the internal
693 // representation, which always stores IPv6 addresses with brackets
694 hostPort
.Append('[');
695 hostPort
.Append(aHostName
);
696 hostPort
.Append(']');
698 hostPort
.Append(aHostName
);
703 if (!hostPort
.IsEmpty()) {
704 hostPort
.Append(':');
705 hostPort
.AppendInt(aPort
);
707 aRetval
.Assign(hostPort
);
710 void nsCertOverrideService::GetKeyString(
711 const nsACString
& aHostName
, int32_t aPort
,
712 const OriginAttributes
& aOriginAttributes
, nsACString
& aRetval
) {
713 nsAutoCString keyString
;
714 GetHostWithPort(aHostName
, aPort
, keyString
);
715 keyString
.Append(':');
716 OriginAttributes
strippedAttributes(aOriginAttributes
);
717 strippedAttributes
.StripAttributes(
718 ~OriginAttributes::STRIP_PRIVATE_BROWSING_ID
);
719 nsAutoCString attributeSuffix
;
720 strippedAttributes
.CreateSuffix(attributeSuffix
);
721 keyString
.Append(attributeSuffix
);
722 aRetval
.Assign(keyString
);
725 // nsIAsyncShutdownBlocker implementation
727 nsCertOverrideService::GetName(nsAString
& aName
) {
728 aName
= u
"nsCertOverrideService: shutdown"_ns
;
733 nsCertOverrideService::GetState(nsIPropertyBag
** aState
) {
735 return NS_ERROR_INVALID_ARG
;
742 nsCertOverrideService::BlockShutdown(nsIAsyncShutdownClient
*) { return NS_OK
; }
744 void nsCertOverrideService::RemoveShutdownBlocker() {
745 MOZ_ASSERT(NS_IsMainThread());
746 MOZ_ASSERT(mPendingWriteCount
> 0);
747 mPendingWriteCount
--;
748 if (mPendingWriteCount
== 0) {
749 nsresult rv
= GetShutdownBarrier()->RemoveBlocker(this);
750 MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv
));