tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

OSReauthenticator.cpp (19723B)


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