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