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 "OSReauthenticator.h"
8 
9 #include "OSKeyStore.h"
10 #include "nsNetCID.h"
11 #include "mozilla/dom/Promise.h"
12 #include "mozilla/Logging.h"
13 #include "mozilla/Maybe.h"
14 #include "mozilla/Preferences.h"
15 #include "nsComponentManagerUtils.h"
16 #include "nsIBaseWindow.h"
17 #include "nsIDocShell.h"
18 #include "nsISupportsUtils.h"
19 #include "nsIWidget.h"
20 #include "nsPIDOMWindow.h"
21 #include "nsServiceManagerUtils.h"
22 #include "nsThreadUtils.h"
23 #include "mozilla/ipc/IPCTypes.h"
24 
25 NS_IMPL_ISUPPORTS(OSReauthenticator, nsIOSReauthenticator)
26 
27 extern mozilla::LazyLogModule gCredentialManagerSecretLog;
28 
29 using mozilla::LogLevel;
30 using mozilla::Maybe;
31 using mozilla::Preferences;
32 using mozilla::WindowsHandle;
33 using mozilla::dom::Promise;
34 
35 #define PREF_BLANK_PASSWORD "security.osreauthenticator.blank_password"
36 #define PREF_PASSWORD_LAST_CHANGED_LO \
37   "security.osreauthenticator.password_last_changed_lo"
38 #define PREF_PASSWORD_LAST_CHANGED_HI \
39   "security.osreauthenticator.password_last_changed_hi"
40 
41 #if defined(XP_WIN)
42 #  include <combaseapi.h>
43 #  include <ntsecapi.h>
44 #  include <wincred.h>
45 #  include <windows.h>
46 #  include "nsIWindowsRegKey.h"  // Must be included after <windows.h> for HKEY definition
47 #  define SECURITY_WIN32
48 #  include <security.h>
49 #  include <shlwapi.h>
50 #  if !defined(__MINGW32__)
51 #    include <Lm.h>
52 #    undef ACCESS_READ  // nsWindowsRegKey defines its own ACCESS_READ
53 #  endif                // !defined(__MINGW32__)
54 struct HandleCloser {
55   typedef HANDLE pointer;
operator ()HandleCloser56   void operator()(HANDLE h) {
57     if (h != INVALID_HANDLE_VALUE) {
58       CloseHandle(h);
59     }
60   }
61 };
62 struct BufferFreer {
63   typedef LPVOID pointer;
64   ULONG mSize;
BufferFreerBufferFreer65   explicit BufferFreer(ULONG size) : mSize(size) {}
operator ()BufferFreer66   void operator()(LPVOID b) {
67     SecureZeroMemory(b, mSize);
68     CoTaskMemFree(b);
69   }
70 };
71 struct LsaDeregistrator {
72   typedef HANDLE pointer;
operator ()LsaDeregistrator73   void operator()(HANDLE h) {
74     if (h != INVALID_HANDLE_VALUE) {
75       LsaDeregisterLogonProcess(h);
76     }
77   }
78 };
79 typedef std::unique_ptr<HANDLE, HandleCloser> ScopedHANDLE;
80 typedef std::unique_ptr<LPVOID, BufferFreer> ScopedBuffer;
81 typedef std::unique_ptr<HANDLE, LsaDeregistrator> ScopedLsaHANDLE;
82 
83 constexpr int64_t Int32Modulo = 2147483648;
84 
85 // Get the token info holding the sid.
GetTokenInfo(ScopedHANDLE & token)86 std::unique_ptr<char[]> GetTokenInfo(ScopedHANDLE& token) {
87   DWORD length = 0;
88   // https://docs.microsoft.com/en-us/windows/desktop/api/securitybaseapi/nf-securitybaseapi-gettokeninformation
89   mozilla::Unused << GetTokenInformation(token.get(), TokenUser, nullptr, 0,
90                                          &length);
91   if (!length || GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
92     MOZ_LOG(gCredentialManagerSecretLog, LogLevel::Debug,
93             ("Unable to obtain current token info."));
94     return nullptr;
95   }
96   std::unique_ptr<char[]> token_info(new char[length]);
97   if (!GetTokenInformation(token.get(), TokenUser, token_info.get(), length,
98                            &length)) {
99     MOZ_LOG(gCredentialManagerSecretLog, LogLevel::Debug,
100             ("Unable to obtain current token info (second call, possible "
101              "system error."));
102     return nullptr;
103   }
104   return token_info;
105 }
106 
GetUserTokenInfo()107 std::unique_ptr<char[]> GetUserTokenInfo() {
108   // Get current user sid to make sure the same user got logged in.
109   HANDLE token;
110   if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &token)) {
111     // Couldn't get a process token. This will fail any unlock attempts later.
112     MOZ_LOG(gCredentialManagerSecretLog, LogLevel::Debug,
113             ("Unable to obtain process token."));
114     return nullptr;
115   }
116   ScopedHANDLE scopedToken(token);
117   return GetTokenInfo(scopedToken);
118 }
119 
GetPasswordLastChanged(const WCHAR * username)120 Maybe<int64_t> GetPasswordLastChanged(const WCHAR* username) {
121 #  if defined(__MINGW32__)
122   // NetUserGetInfo requires Lm.h which is not provided in MinGW builds
123   return mozilla::Nothing();
124 #  else
125   LPUSER_INFO_1 user_info = NULL;
126   DWORD passwordAgeInSeconds = 0;
127 
128   NET_API_STATUS ret =
129       NetUserGetInfo(NULL, username, 1, reinterpret_cast<LPBYTE*>(&user_info));
130 
131   if (ret == NERR_Success) {
132     // Returns seconds since last password change.
133     passwordAgeInSeconds = user_info->usri1_password_age;
134     NetApiBufferFree(user_info);
135   } else {
136     return mozilla::Nothing();
137   }
138 
139   // Return the time that the password was changed so we can use this
140   // for future comparisons.
141   return mozilla::Some(PR_Now() - passwordAgeInSeconds * PR_USEC_PER_SEC);
142 #  endif
143 }
144 
IsAutoAdminLogonEnabled()145 bool IsAutoAdminLogonEnabled() {
146   // https://support.microsoft.com/en-us/help/324737/how-to-turn-on-automatic-logon-in-windows
147   nsresult rv;
148   nsCOMPtr<nsIWindowsRegKey> regKey =
149       do_CreateInstance("@mozilla.org/windows-registry-key;1", &rv);
150   if (NS_FAILED(rv)) {
151     return false;
152   }
153 
154   rv = regKey->Open(
155       nsIWindowsRegKey::ROOT_KEY_LOCAL_MACHINE,
156       nsLiteralString(
157           u"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon"),
158       nsIWindowsRegKey::ACCESS_READ);
159   if (NS_FAILED(rv)) {
160     return false;
161   }
162 
163   nsAutoString value;
164   rv = regKey->ReadStringValue(u"AutoAdminLogon"_ns, value);
165   if (NS_FAILED(rv)) {
166     return false;
167   }
168   regKey->Close();
169 
170   return value.Equals(u"1"_ns);
171 }
172 
IsRequireSignonEnabled()173 bool IsRequireSignonEnabled() {
174   // https://docs.microsoft.com/en-us/windows-hardware/customize/power-settings/no-subgroup-settings-prompt-for-password-on-resume
175   nsresult rv;
176   nsCOMPtr<nsIWindowsRegKey> regKey =
177       do_CreateInstance("@mozilla.org/windows-registry-key;1", &rv);
178   if (NS_FAILED(rv)) {
179     return true;
180   }
181 
182   rv = regKey->Open(nsIWindowsRegKey::ROOT_KEY_LOCAL_MACHINE,
183                     u"System\\CurrentControlSet\\Control\\Power\\User\\Power"
184                     "Schemes"_ns,
185                     nsIWindowsRegKey::ACCESS_READ);
186   if (NS_FAILED(rv)) {
187     return true;
188   }
189 
190   nsAutoString activePowerScheme;
191   rv = regKey->ReadStringValue(u"ActivePowerScheme"_ns, activePowerScheme);
192   if (NS_FAILED(rv)) {
193     return true;
194   }
195   regKey->Close();
196 
197   rv = regKey->Open(nsIWindowsRegKey::ROOT_KEY_LOCAL_MACHINE,
198                     u"System\\CurrentControlSet\\Control\\Power\\User\\Power"
199                     "Schemes\\"_ns +
200                         activePowerScheme +
201                         u"\\0e796bdb-100d-47d6-a2d5-f7d2daa51f51"_ns,
202                     nsIWindowsRegKey::ACCESS_READ);
203   if (NS_FAILED(rv)) {
204     return true;
205   }
206 
207   uint32_t value;
208   rv = regKey->ReadIntValue(u"ACSettingIndex"_ns, &value);
209   if (NS_FAILED(rv)) {
210     return true;
211   }
212   regKey->Close();
213 
214   return !!value;
215 }
216 
217 // Use the Windows credential prompt to ask the user to authenticate the
218 // currently used account.
ReauthenticateUserWindows(const nsAString & aMessageText,const nsAString & aCaptionText,const WindowsHandle & hwndParent,bool & reauthenticated,bool & isBlankPassword,int64_t & prefLastChanged,bool & isAutoAdminLogonEnabled,bool & isRequireSignonEnabled)219 static nsresult ReauthenticateUserWindows(
220     const nsAString& aMessageText, const nsAString& aCaptionText,
221     const WindowsHandle& hwndParent,
222     /* out */ bool& reauthenticated,
223     /* inout */ bool& isBlankPassword,
224     /* inout */ int64_t& prefLastChanged,
225     /* out */ bool& isAutoAdminLogonEnabled,
226     /* out */ bool& isRequireSignonEnabled) {
227   reauthenticated = false;
228   isAutoAdminLogonEnabled = false;
229   isRequireSignonEnabled = true;
230 
231   // Check if the user has a blank password before proceeding
232   DWORD usernameLength = CREDUI_MAX_USERNAME_LENGTH + 1;
233   WCHAR username[CREDUI_MAX_USERNAME_LENGTH + 1] = {0};
234 
235   if (!GetUserNameEx(NameSamCompatible, username, &usernameLength)) {
236     MOZ_LOG(gCredentialManagerSecretLog, LogLevel::Debug,
237             ("Error getting username"));
238     return NS_ERROR_FAILURE;
239   }
240 
241 #  ifdef OS_DOMAINMEMBER
242   bool isDomainMember = IsOS(OS_DOMAINMEMBER);
243 #  else
244   // Bug 1633097
245   bool isDomainMember = false;
246 #  endif
247   if (!isDomainMember) {
248     const WCHAR* usernameNoDomain = username;
249     // Don't include the domain portion of the username when calling LogonUser.
250     LPCWSTR backslash = wcschr(username, L'\\');
251     if (backslash) {
252       usernameNoDomain = backslash + 1;
253     }
254 
255     Maybe<int64_t> lastChanged = GetPasswordLastChanged(usernameNoDomain);
256     if (lastChanged.isSome()) {
257       bool shouldCheckAgain = lastChanged.value() > prefLastChanged;
258       // Update the value stored in preferences
259       prefLastChanged = lastChanged.value();
260 
261       if (shouldCheckAgain) {
262         HANDLE logonUserHandle = INVALID_HANDLE_VALUE;
263         bool result =
264             LogonUser(usernameNoDomain, L".", L"", LOGON32_LOGON_INTERACTIVE,
265                       LOGON32_PROVIDER_DEFAULT, &logonUserHandle);
266         if (result) {
267           CloseHandle(logonUserHandle);
268         }
269         // ERROR_ACCOUNT_RESTRICTION: Indicates a referenced user name and
270         // authentication information are valid, but some user account
271         // restriction has prevented successful authentication (such as
272         // time-of-day restrictions).
273         reauthenticated = isBlankPassword =
274             (result || GetLastError() == ERROR_ACCOUNT_RESTRICTION);
275       } else if (isBlankPassword) {
276         reauthenticated = true;
277       }
278 
279       if (reauthenticated) {
280         return NS_OK;
281       }
282     } else {
283       isBlankPassword = false;
284     }
285   } else {
286     // Update any preferences, assuming domain members do not have blank
287     // passwords
288     isBlankPassword = false;
289   }
290 
291   isAutoAdminLogonEnabled = IsAutoAdminLogonEnabled();
292 
293   isRequireSignonEnabled = IsRequireSignonEnabled();
294 
295   // Is used in next iteration if the previous login failed.
296   DWORD err = 0;
297   std::unique_ptr<char[]> userTokenInfo = GetUserTokenInfo();
298 
299   // CredUI prompt.
300   CREDUI_INFOW credui = {};
301   credui.cbSize = sizeof(credui);
302   credui.hwndParent = reinterpret_cast<HWND>(hwndParent);
303   const nsString& messageText = PromiseFlatString(aMessageText);
304   credui.pszMessageText = messageText.get();
305   const nsString& captionText = PromiseFlatString(aCaptionText);
306   credui.pszCaptionText = captionText.get();
307   credui.hbmBanner = nullptr;  // ignored
308 
309   while (!reauthenticated) {
310     HANDLE lsa = INVALID_HANDLE_VALUE;
311     // Get authentication handle for future user authentications.
312     // https://docs.microsoft.com/en-us/windows/desktop/api/ntsecapi/nf-ntsecapi-lsaconnectuntrusted
313     if (LsaConnectUntrusted(&lsa) != ERROR_SUCCESS) {
314       MOZ_LOG(gCredentialManagerSecretLog, LogLevel::Debug,
315               ("Error acquiring lsa. Authentication attempts will fail."));
316       return NS_ERROR_FAILURE;
317     }
318     ScopedLsaHANDLE scopedLsa(lsa);
319 
320     if (!userTokenInfo || lsa == INVALID_HANDLE_VALUE) {
321       MOZ_LOG(gCredentialManagerSecretLog, LogLevel::Debug,
322               ("Error setting up login and user token."));
323       return NS_ERROR_FAILURE;
324     }
325 
326     ULONG authPackage = 0;
327     ULONG outCredSize = 0;
328     LPVOID outCredBuffer = nullptr;
329 
330     // Get user's Windows credentials.
331     // https://docs.microsoft.com/en-us/windows/desktop/api/wincred/nf-wincred-creduipromptforwindowscredentialsw
332     err = CredUIPromptForWindowsCredentialsW(
333         &credui, err, &authPackage, nullptr, 0, &outCredBuffer, &outCredSize,
334         nullptr, CREDUIWIN_ENUMERATE_CURRENT_USER);
335     ScopedBuffer scopedOutCredBuffer(outCredBuffer, BufferFreer(outCredSize));
336     if (err == ERROR_CANCELLED) {
337       MOZ_LOG(gCredentialManagerSecretLog, LogLevel::Debug,
338               ("Error getting authPackage for user login, user cancel."));
339       return NS_OK;
340     }
341     if (err != ERROR_SUCCESS) {
342       MOZ_LOG(gCredentialManagerSecretLog, LogLevel::Debug,
343               ("Error getting authPackage for user login."));
344       return NS_ERROR_FAILURE;
345     }
346 
347     // Verify the credentials.
348     TOKEN_SOURCE source;
349     PCHAR contextName = const_cast<PCHAR>("Mozilla");
350     size_t nameLength =
351         std::min(TOKEN_SOURCE_LENGTH, static_cast<int>(strlen(contextName)));
352     // Note that the string must not be longer than TOKEN_SOURCE_LENGTH.
353     memcpy(source.SourceName, contextName, nameLength);
354     // https://docs.microsoft.com/en-us/windows/desktop/api/securitybaseapi/nf-securitybaseapi-allocatelocallyuniqueid
355     if (!AllocateLocallyUniqueId(&source.SourceIdentifier)) {
356       MOZ_LOG(gCredentialManagerSecretLog, LogLevel::Debug,
357               ("Error allocating ID for logon process."));
358       return NS_ERROR_FAILURE;
359     }
360 
361     NTSTATUS substs;
362     void* profileBuffer = nullptr;
363     ULONG profileBufferLength = 0;
364     QUOTA_LIMITS limits = {0};
365     LUID luid;
366     HANDLE token = INVALID_HANDLE_VALUE;
367     LSA_STRING name;
368     name.Buffer = contextName;
369     name.Length = strlen(name.Buffer);
370     name.MaximumLength = name.Length;
371     // https://docs.microsoft.com/en-us/windows/desktop/api/ntsecapi/nf-ntsecapi-lsalogonuser
372     NTSTATUS sts = LsaLogonUser(
373         scopedLsa.get(), &name, (SECURITY_LOGON_TYPE)Interactive, authPackage,
374         scopedOutCredBuffer.get(), outCredSize, nullptr, &source,
375         &profileBuffer, &profileBufferLength, &luid, &token, &limits, &substs);
376     ScopedHANDLE scopedToken(token);
377     LsaFreeReturnBuffer(profileBuffer);
378     if (sts == ERROR_SUCCESS) {
379       MOZ_LOG(gCredentialManagerSecretLog, LogLevel::Debug,
380               ("User logged in successfully."));
381     } else {
382       err = LsaNtStatusToWinError(sts);
383       MOZ_LOG(gCredentialManagerSecretLog, LogLevel::Debug,
384               ("Login failed with %lx (%lx).", sts, err));
385       continue;
386     }
387 
388     // The user can select any user to log-in on the authentication prompt.
389     // Make sure that the logged in user is the current user.
390     std::unique_ptr<char[]> logonTokenInfo = GetTokenInfo(scopedToken);
391     if (!logonTokenInfo) {
392       MOZ_LOG(gCredentialManagerSecretLog, LogLevel::Debug,
393               ("Error getting logon token info."));
394       return NS_ERROR_FAILURE;
395     }
396     PSID logonSID =
397         reinterpret_cast<TOKEN_USER*>(logonTokenInfo.get())->User.Sid;
398     PSID userSID = reinterpret_cast<TOKEN_USER*>(userTokenInfo.get())->User.Sid;
399     if (EqualSid(userSID, logonSID)) {
400       MOZ_LOG(gCredentialManagerSecretLog, LogLevel::Debug,
401               ("Login successfully (correct user)."));
402       reauthenticated = true;
403       break;
404     } else {
405       err = ERROR_LOGON_FAILURE;
406     }
407   }
408   return NS_OK;
409 }
410 #endif  // XP_WIN
411 
ReauthenticateUser(const nsAString & prompt,const nsAString & caption,const WindowsHandle & hwndParent,bool & reauthenticated,bool & isBlankPassword,int64_t & prefLastChanged,bool & isAutoAdminLogonEnabled,bool & isRequireSignonEnabled)412 static nsresult ReauthenticateUser(const nsAString& prompt,
413                                    const nsAString& caption,
414                                    const WindowsHandle& hwndParent,
415                                    /* out */ bool& reauthenticated,
416                                    /* inout */ bool& isBlankPassword,
417                                    /* inout */ int64_t& prefLastChanged,
418                                    /* out */ bool& isAutoAdminLogonEnabled,
419                                    /* out */ bool& isRequireSignonEnabled) {
420   reauthenticated = false;
421 #if defined(XP_WIN)
422   return ReauthenticateUserWindows(
423       prompt, caption, hwndParent, reauthenticated, isBlankPassword,
424       prefLastChanged, isAutoAdminLogonEnabled, isRequireSignonEnabled);
425 #elif defined(XP_MACOSX)
426   return ReauthenticateUserMacOS(prompt, reauthenticated, isBlankPassword);
427 #endif  // Reauthentication is not implemented for this platform.
428   return NS_OK;
429 }
430 
BackgroundReauthenticateUser(RefPtr<Promise> & aPromise,const nsAString & aMessageText,const nsAString & aCaptionText,const WindowsHandle & hwndParent,bool isBlankPassword,int64_t prefLastChanged)431 static void BackgroundReauthenticateUser(RefPtr<Promise>& aPromise,
432                                          const nsAString& aMessageText,
433                                          const nsAString& aCaptionText,
434                                          const WindowsHandle& hwndParent,
435                                          bool isBlankPassword,
436                                          int64_t prefLastChanged) {
437   nsAutoCString recovery;
438   bool reauthenticated;
439   bool isAutoAdminLogonEnabled;
440   bool isRequireSignonEnabled;
441   nsresult rv = ReauthenticateUser(
442       aMessageText, aCaptionText, hwndParent, reauthenticated, isBlankPassword,
443       prefLastChanged, isAutoAdminLogonEnabled, isRequireSignonEnabled);
444 
445   nsTArray<int32_t> prefLastChangedUpdates;
446 #if defined(XP_WIN)
447   // Increase the lastChanged time to account for clock skew.
448   prefLastChanged += PR_USEC_PER_SEC;
449   // Need to split the 64bit integer to its hi and lo bits before sending it
450   // back to JS.
451   int32_t prefLastChangedHi = prefLastChanged / Int32Modulo;
452   int32_t prefLastChangedLo = prefLastChanged % Int32Modulo;
453   prefLastChangedUpdates.AppendElement(prefLastChangedHi);
454   prefLastChangedUpdates.AppendElement(prefLastChangedLo);
455 #endif
456 
457   nsTArray<int32_t> results;
458   results.AppendElement(reauthenticated);
459   results.AppendElement(isBlankPassword);
460 #if defined(XP_WIN)
461   results.AppendElement(isAutoAdminLogonEnabled);
462   results.AppendElement(isRequireSignonEnabled);
463 #endif
464   nsCOMPtr<nsIRunnable> runnable(NS_NewRunnableFunction(
465       "BackgroundReauthenticateUserResolve",
466       [rv, results = std::move(results),
467        prefLastChangedUpdates = std::move(prefLastChangedUpdates),
468        aPromise = std::move(aPromise)]() {
469         if (NS_FAILED(rv)) {
470           aPromise->MaybeReject(rv);
471         } else {
472           aPromise->MaybeResolve(results);
473         }
474 
475         nsresult rv = Preferences::SetBool(PREF_BLANK_PASSWORD, results[1]);
476         if (NS_FAILED(rv)) {
477           return;
478         }
479         if (prefLastChangedUpdates.Length() > 1) {
480           rv = Preferences::SetInt(PREF_PASSWORD_LAST_CHANGED_HI,
481                                    prefLastChangedUpdates[0]);
482           if (NS_FAILED(rv)) {
483             return;
484           }
485           Preferences::SetInt(PREF_PASSWORD_LAST_CHANGED_LO,
486                               prefLastChangedUpdates[1]);
487         }
488       }));
489   NS_DispatchToMainThread(runnable.forget());
490 }
491 
492 NS_IMETHODIMP
AsyncReauthenticateUser(const nsAString & aMessageText,const nsAString & aCaptionText,mozIDOMWindow * aParentWindow,JSContext * aCx,Promise ** promiseOut)493 OSReauthenticator::AsyncReauthenticateUser(const nsAString& aMessageText,
494                                            const nsAString& aCaptionText,
495                                            mozIDOMWindow* aParentWindow,
496                                            JSContext* aCx,
497                                            Promise** promiseOut) {
498   NS_ENSURE_ARG_POINTER(aCx);
499 
500   RefPtr<Promise> promiseHandle;
501   nsresult rv = GetPromise(aCx, promiseHandle);
502   if (NS_FAILED(rv)) {
503     return rv;
504   }
505 
506   WindowsHandle hwndParent = 0;
507   if (aParentWindow) {
508     nsPIDOMWindowInner* win = nsPIDOMWindowInner::From(aParentWindow);
509     nsIDocShell* docShell = win->GetDocShell();
510     if (docShell) {
511       nsCOMPtr<nsIBaseWindow> baseWindow = do_QueryInterface(docShell);
512       if (baseWindow) {
513         nsCOMPtr<nsIWidget> widget;
514         baseWindow->GetMainWidget(getter_AddRefs(widget));
515         if (widget) {
516           hwndParent = reinterpret_cast<WindowsHandle>(
517               widget->GetNativeData(NS_NATIVE_WINDOW));
518         }
519       }
520     }
521   }
522 
523   int64_t prefLastChanged = 0;
524   bool isBlankPassword = false;
525 #if defined(XP_WIN)
526   // These preferences are only supported on Windows.
527   // Preferences are read/write main-thread only.
528   int32_t prefLastChangedLo;
529   int32_t prefLastChangedHi;
530   rv = Preferences::GetBool(PREF_BLANK_PASSWORD, &isBlankPassword);
531   if (NS_FAILED(rv)) {
532     return rv;
533   }
534   rv = Preferences::GetInt(PREF_PASSWORD_LAST_CHANGED_LO, &prefLastChangedLo);
535   if (NS_FAILED(rv)) {
536     return rv;
537   }
538   rv = Preferences::GetInt(PREF_PASSWORD_LAST_CHANGED_HI, &prefLastChangedHi);
539   if (NS_FAILED(rv)) {
540     return rv;
541   }
542   prefLastChanged = prefLastChangedHi * Int32Modulo + prefLastChangedLo;
543 #endif
544 
545   nsCOMPtr<nsIRunnable> runnable(NS_NewRunnableFunction(
546       "BackgroundReauthenticateUser",
547       [promiseHandle, aMessageText = nsAutoString(aMessageText),
548        aCaptionText = nsAutoString(aCaptionText), hwndParent, isBlankPassword,
549        prefLastChanged]() mutable {
550         BackgroundReauthenticateUser(promiseHandle, aMessageText, aCaptionText,
551                                      hwndParent, isBlankPassword,
552                                      prefLastChanged);
553       }));
554 
555   nsCOMPtr<nsIEventTarget> target(
556       do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID));
557   if (!target) {
558     return NS_ERROR_FAILURE;
559   }
560   rv = target->Dispatch(runnable, NS_DISPATCH_NORMAL);
561   if (NS_WARN_IF(NS_FAILED(rv))) {
562     return rv;
563   }
564 
565   promiseHandle.forget(promiseOut);
566   return NS_OK;
567 }
568