1010#include " ScopedNSSTypes.h"
1111#include " SharedSSLState.h"
1212#include " mozilla/Assertions.h"
13+ #include " mozilla/TaskQueue.h"
1314#include " mozilla/Telemetry.h"
1415#include " mozilla/TextUtils.h"
1516#include " mozilla/Unused.h"
@@ -38,6 +39,50 @@ using namespace mozilla::psm;
3839
3940#define CERT_OVERRIDE_FILE_NAME " cert_override.txt"
4041
42+ class WriterRunnable : public Runnable {
43+ public:
44+ WriterRunnable (nsCertOverrideService* aService, nsCString& aData,
45+ nsCOMPtr<nsIFile> aFile)
46+ : Runnable(" nsCertOverrideService::WriterRunnable" ),
47+ mCertOverrideService (aService),
48+ mData(aData),
49+ mFile(std::move(aFile)) {}
50+
51+ NS_IMETHOD
52+ Run () override {
53+ mCertOverrideService ->AssertOnTaskQueue ();
54+ nsresult rv;
55+
56+ nsCOMPtr<nsIOutputStream> outputStream;
57+ rv = NS_NewSafeLocalFileOutputStream(
58+ getter_AddRefs (outputStream), mFile ,
59+ PR_CREATE_FILE | PR_TRUNCATE | PR_WRONLY);
60+ NS_ENSURE_SUCCESS (rv, rv);
61+
62+ const char * ptr = mData .get ();
63+ uint32_t remaining = mData .Length ();
64+ uint32_t written = 0 ;
65+ while (remaining > 0 ) {
66+ rv = outputStream->Write (ptr, remaining, &written);
67+ NS_ENSURE_SUCCESS (rv, rv);
68+ remaining -= written;
69+ ptr += written;
70+ }
71+
72+ nsCOMPtr<nsISafeOutputStream> safeStream = do_QueryInterface (outputStream);
73+ MOZ_ASSERT (safeStream);
74+ rv = safeStream->Finish ();
75+ NS_ENSURE_SUCCESS (rv, rv);
76+
77+ return NS_OK;
78+ }
79+
80+ private:
81+ const RefPtr<nsCertOverrideService> mCertOverrideService ;
82+ nsCString mData ;
83+ const nsCOMPtr<nsIFile> mFile ;
84+ };
85+
4186void nsCertOverride::convertBitsToString (OverrideBits ob,
4287 /* out*/ nsACString& str) {
4388 str.Truncate ();
@@ -83,19 +128,44 @@ void nsCertOverride::convertStringToBits(const nsACString& str,
83128}
84129
85130NS_IMPL_ISUPPORTS (nsCertOverrideService, nsICertOverrideService, nsIObserver,
86- nsISupportsWeakReference)
131+ nsISupportsWeakReference, nsIAsyncShutdownBlocker )
87132
88133nsCertOverrideService::nsCertOverrideService()
89- : mDisableAllSecurityCheck(false ), mMutex(" nsCertOverrideService.mutex" ) {}
134+ : mDisableAllSecurityCheck(false ), mMutex(" nsCertOverrideService.mutex" ) {
135+ nsCOMPtr<nsIEventTarget> target =
136+ do_GetService (NS_STREAMTRANSPORTSERVICE_CONTRACTID);
137+ MOZ_ASSERT (target);
138+
139+ mWriterTaskQueue = new TaskQueue (target.forget ());
140+ }
90141
91142nsCertOverrideService::~nsCertOverrideService () = default ;
92143
144+ static nsCOMPtr<nsIAsyncShutdownClient> GetShutdownBarrier () {
145+ nsCOMPtr<nsIAsyncShutdownService> svc =
146+ mozilla::services::GetAsyncShutdownService ();
147+ MOZ_RELEASE_ASSERT (svc);
148+
149+ nsCOMPtr<nsIAsyncShutdownClient> barrier;
150+ nsresult rv = svc->GetProfileBeforeChange (getter_AddRefs (barrier));
151+
152+ MOZ_RELEASE_ASSERT (NS_SUCCEEDED(rv));
153+ MOZ_RELEASE_ASSERT (barrier);
154+ return barrier;
155+ }
156+
93157nsresult nsCertOverrideService::Init () {
94158 if (!NS_IsMainThread()) {
95159 MOZ_ASSERT_UNREACHABLE (" nsCertOverrideService initialized off main thread" );
96160 return NS_ERROR_NOT_SAME_THREAD;
97161 }
98162
163+ nsresult rv = GetShutdownBarrier ()->AddBlocker (
164+ this , NS_LITERAL_STRING(__FILE__), __LINE__,
165+ NS_LITERAL_STRING (" nsCertOverrideService shutdown" ));
166+ // If that failed, we're likely getting initialized during shutdown.
167+ NS_ENSURE_SUCCESS (rv, rv);
168+
99169 nsCOMPtr<nsIObserverService> observerService =
100170 mozilla::services::GetObserverService ();
101171
@@ -253,31 +323,15 @@ nsresult nsCertOverrideService::Write(const MutexAutoLock& aProofOfLock) {
253323 return NS_OK;
254324 }
255325
256- nsresult rv;
257- nsCOMPtr<nsIOutputStream> fileOutputStream;
258- rv = NS_NewSafeLocalFileOutputStream(getter_AddRefs (fileOutputStream),
259- mSettingsFile , -1 , 0600 );
260- if (NS_FAILED(rv)) {
261- NS_ERROR (" failed to open cert_warn_settings.txt for writing" );
262- return rv;
263- }
264-
265- // get a buffered output stream 4096 bytes big, to optimize writes
266- nsCOMPtr<nsIOutputStream> bufferedOutputStream;
267- rv = NS_NewBufferedOutputStream(getter_AddRefs (bufferedOutputStream),
268- fileOutputStream.forget (), 4096 );
269- if (NS_FAILED(rv)) {
270- return rv;
271- }
326+ nsCString output;
272327
273328 static const char kHeader [] =
274329 " # PSM Certificate Override Settings file" NS_LINEBREAK
275330 " # This is a generated file! Do not edit." NS_LINEBREAK;
276331
277332 /* see ::Read for file format */
278333
279- uint32_t unused;
280- bufferedOutputStream->Write (kHeader , sizeof (kHeader ) - 1 , &unused);
334+ output.Append (kHeader );
281335
282336 static const char kTab [] = " \t " ;
283337 for (auto iter = mSettingsTable .Iter (); !iter.Done (); iter.Next ()) {
@@ -291,34 +345,29 @@ nsresult nsCertOverrideService::Write(const MutexAutoLock& aProofOfLock) {
291345 nsAutoCString bits_string;
292346 nsCertOverride::convertBitsToString (settings.mOverrideBits , bits_string);
293347
294- bufferedOutputStream->Write (entry->mHostWithPort .get (),
295- entry->mHostWithPort .Length (), &unused);
296- bufferedOutputStream->Write (kTab , sizeof (kTab ) - 1 , &unused);
297- bufferedOutputStream->Write (sSHA256OIDString , sizeof (sSHA256OIDString ) - 1 ,
298- &unused);
299- bufferedOutputStream->Write (kTab , sizeof (kTab ) - 1 , &unused);
300- bufferedOutputStream->Write (settings.mFingerprint .get (),
301- settings.mFingerprint .Length (), &unused);
302- bufferedOutputStream->Write (kTab , sizeof (kTab ) - 1 , &unused);
303- bufferedOutputStream->Write (bits_string.get (), bits_string.Length (),
304- &unused);
305- bufferedOutputStream->Write (kTab , sizeof (kTab ) - 1 , &unused);
306- bufferedOutputStream->Write (settings.mDBKey .get (), settings.mDBKey .Length (),
307- &unused);
308- bufferedOutputStream->Write (NS_LINEBREAK, NS_LINEBREAK_LEN, &unused);
309- }
310-
311- // All went ok. Maybe except for problems in Write(), but the stream detects
312- // that for us
313- nsCOMPtr<nsISafeOutputStream> safeStream =
314- do_QueryInterface (bufferedOutputStream);
315- MOZ_ASSERT (safeStream, " Expected a safe output stream!" );
316- if (safeStream) {
317- rv = safeStream->Finish ();
318- if (NS_FAILED(rv)) {
319- NS_WARNING (" failed to save cert warn settings file! possible dataloss" );
320- return rv;
321- }
348+ output.Append (entry->mHostWithPort );
349+ output.Append (kTab );
350+ output.Append (sSHA256OIDString );
351+ output.Append (kTab );
352+ output.Append (settings.mFingerprint );
353+ output.Append (kTab );
354+ output.Append (bits_string);
355+ output.Append (kTab );
356+ output.Append (settings.mDBKey );
357+ output.Append (NS_LINEBREAK);
358+ }
359+
360+ // Make a clone of the file to pass to the WriterRunnable.
361+ nsCOMPtr<nsIFile> file;
362+ nsresult rv;
363+ rv = mSettingsFile ->Clone (getter_AddRefs (file));
364+ NS_ENSURE_SUCCESS (rv, rv);
365+
366+ nsCOMPtr<nsIRunnable> runnable = new WriterRunnable (this , output, file);
367+ rv = mWriterTaskQueue ->Dispatch (runnable.forget ());
368+
369+ if (NS_FAILED(rv)) {
370+ return rv;
322371 }
323372
324373 return NS_OK;
@@ -678,3 +727,23 @@ void nsCertOverrideService::GetHostWithPort(const nsACString& aHostName,
678727 }
679728 _retval.Assign (hostPort);
680729}
730+
731+ // nsIAsyncShutdownBlocker implementation
732+ NS_IMETHODIMP
733+ nsCertOverrideService::GetName (nsAString& aName) {
734+ aName = NS_LITERAL_STRING(" nsCertOverrideService: shutdown" );
735+ return NS_OK;
736+ }
737+
738+ NS_IMETHODIMP
739+ nsCertOverrideService::GetState (nsIPropertyBag**) { return NS_OK; }
740+
741+ NS_IMETHODIMP
742+ nsCertOverrideService::BlockShutdown (nsIAsyncShutdownClient*) {
743+ mWriterTaskQueue ->BeginShutdown ();
744+ mWriterTaskQueue ->AwaitShutdownAndIdle ();
745+
746+ nsresult rv = GetShutdownBarrier ()->RemoveBlocker (this );
747+ MOZ_RELEASE_ASSERT (NS_SUCCEEDED(rv));
748+ return NS_OK;
749+ }
0 commit comments