Bug 1514898 - Disable user impersonation on release. r=rstrong, a=RyanVM
authorAdam Gashlin <agashlin@mozilla.com>
Wed, 09 Jan 2019 19:57:31 +0000
changeset 509392 e620bf5160a4685b2728d28d2bf0eaa1dbf9d786
parent 509391 aaeaf42a092dd7c12c17e6a5850aa9d93129477f
child 509393 f0bb91ca0015991e22c06681f0cd959a390f3a42
push id1905
push userffxbld-merge
push dateMon, 21 Jan 2019 12:33:13 +0000
treeherdermozilla-release@c2fca1944d8c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersrstrong, RyanVM
bugs1514898
milestone65.0
Bug 1514898 - Disable user impersonation on release. r=rstrong, a=RyanVM This introduces a DISABLE_USER_IMPERSONATION define when EARLY_BETA_OR_EARLIER is set, if that is present the maintenance service will not attempt to get an impersonation token for the user's updater process, and the updater will not attempt to use any token it is given. The bulk of the changes are restoring the old failure status codes and the tests that expect them, sorry for the noise that causes. Differential Revision: https://phabricator.services.mozilla.com/D15781
toolkit/components/maintenanceservice/usertoken.cpp
toolkit/components/maintenanceservice/workmonitor.cpp
toolkit/mozapps/update/common/updatehelper.h
toolkit/mozapps/update/tests/unit_base_updater/head_update.js
toolkit/mozapps/update/tests/unit_base_updater/invalidArgInstallDirPathTooLongFailure.js
toolkit/mozapps/update/tests/unit_base_updater/invalidArgInstallDirPathTraversalFailure.js
toolkit/mozapps/update/tests/unit_base_updater/invalidArgInstallWorkingDirPathNotSameFailure_win.js
toolkit/mozapps/update/tests/unit_base_updater/invalidArgStageDirNotInInstallDirFailure_win.js
toolkit/mozapps/update/tests/unit_base_updater/invalidArgWorkingDirPathLocalUNCFailure_win.js
toolkit/mozapps/update/tests/unit_base_updater/invalidArgWorkingDirPathRelativeFailure.js
toolkit/mozapps/update/tests/unit_service_updater/head_update.js
toolkit/mozapps/update/tests/unit_service_updater/invalidArgInstallDirPathTooLongFailureSvc.js
toolkit/mozapps/update/tests/unit_service_updater/invalidArgInstallDirPathTraversalFailureSvc.js
toolkit/mozapps/update/tests/unit_service_updater/invalidArgInstallWorkingDirPathNotSameFailureSvc_win.js
toolkit/mozapps/update/tests/unit_service_updater/invalidArgStageDirNotInInstallDirFailureSvc_win.js
toolkit/mozapps/update/tests/unit_service_updater/invalidArgWorkingDirPathLocalUNCFailureSvc_win.js
toolkit/mozapps/update/tests/unit_service_updater/invalidArgWorkingDirPathRelativeFailureSvc.js
toolkit/mozapps/update/updater/updater.cpp
--- a/toolkit/components/maintenanceservice/usertoken.cpp
+++ b/toolkit/components/maintenanceservice/usertoken.cpp
@@ -563,14 +563,14 @@ GetUserProcessToken(LPCWSTR updaterPath,
     // Found a result!
     rv.swap(hDupToken);
     matchedPid = pid;
   }  // end of while loop over query result rows
 
   if (!rv) {
     LOG_WARN(("Found no matching updater process to impersonate."));
   } else {
-    LOG_WARN(("Successfully matched pid %lu and got impersonation token.",
-              matchedPid));
+    LOG(("Successfully matched pid %lu and got impersonation token.",
+         matchedPid));
   }
 
   return rv.disown();
 }
--- a/toolkit/components/maintenanceservice/workmonitor.cpp
+++ b/toolkit/components/maintenanceservice/workmonitor.cpp
@@ -206,28 +206,31 @@ BOOL StartUpdateProcess(int argc, LPWSTR
     si.wShowWindow = SW_HIDE;
   }
 
   // Add an env var for MOZ_USING_SERVICE so the updater.exe can
   // do anything special that it needs to do for service updates.
   // Search in updater.cpp for more info on MOZ_USING_SERVICE.
   putenv(const_cast<char *>("MOZ_USING_SERVICE=1"));
 
+#ifndef DISABLE_USER_IMPERSONATION
   // Add an env var with a pointer to the impersonation token.
   {
     static const char USER_TOKEN_FMT[] = USER_TOKEN_VAR_NAME "=%p";
     int fmtChars = _scprintf(USER_TOKEN_FMT, userToken.get());
     UniquePtr<char[]> userTokenEnv = MakeUnique<char[]>(fmtChars + 1);
     sprintf_s(userTokenEnv.get(), fmtChars + 1, USER_TOKEN_FMT,
               userToken.get());
     putenv(userTokenEnv.get());
   }
+#endif
 
   // Prepare the attribute list used to inherit the impersonation token handle.
   {
+#ifndef DISABLE_USER_IMPERSONATION
     SIZE_T attributeListSize = 0;
     const DWORD attributeListCount = 1;
     UniquePtr<char[]> attributeListBuf;
 
     if (InitializeProcThreadAttributeList(nullptr, attributeListCount, 0,
                                           &attributeListSize) ||
         (GetLastError() != ERROR_INSUFFICIENT_BUFFER) ||
         !(attributeListBuf = MakeUnique<char[]>(attributeListSize))) {
@@ -247,16 +250,17 @@ BOOL StartUpdateProcess(int argc, LPWSTR
             attributeList.get(), 0, PROC_THREAD_ATTRIBUTE_HANDLE_LIST,
             handlesToInherit, sizeof(handlesToInherit), nullptr, nullptr)) {
       LOG_WARN(("Failed to create attribute list for child process. (%d)",
                 GetLastError()));
       return FALSE;
     }
 
     sie.lpAttributeList = attributeList.get();
+#endif
 
     LOG(("Starting service with cmdline: %ls", cmdLine.get()));
     processStarted =
         CreateProcessW(argv[0], cmdLine.get(), nullptr, nullptr, TRUE,
                        CREATE_DEFAULT_ERROR_MODE | EXTENDED_STARTUPINFO_PRESENT,
                        nullptr, nullptr, &si, &pi);
   }
 
@@ -354,30 +358,38 @@ static bool UpdaterIsValid(LPWSTR update
                            nsAutoHandle &userToken) {
   // Make sure the path to the updater to use for the update is local.
   // We do this check to make sure that file locking is available for
   // race condition security checks.
   BOOL isLocal = FALSE;
   if (!IsLocalFile(updater, isLocal) || !isLocal) {
     LOG_WARN(("Filesystem in path %ls is not supported (%d)", updater,
               GetLastError()));
-    if (!WriteStatusFailure(updateDir, SERVICE_UPDATER_NOT_FIXED_DRIVE,
+    if (
+#ifndef DISABLE_USER_IMPERSONATION
+        !userToken ||
+#endif
+        !WriteStatusFailure(updateDir, SERVICE_UPDATER_NOT_FIXED_DRIVE,
                             userToken)) {
       LOG_WARN(("Could not write update.status service update failure.  (%d)",
                 GetLastError()));
     }
     return false;
   }
 
   nsAutoHandle noWriteLock(CreateFileW(updater, GENERIC_READ, FILE_SHARE_READ,
                                        nullptr, OPEN_EXISTING, 0, nullptr));
   if (INVALID_HANDLE_VALUE == noWriteLock) {
     LOG_WARN(("Could not set no write sharing access on file.  (%d)",
               GetLastError()));
-    if (!WriteStatusFailure(updateDir, SERVICE_COULD_NOT_LOCK_UPDATER,
+    if (
+#ifndef DISABLE_USER_IMPERSONATION
+        !userToken ||
+#endif
+        !WriteStatusFailure(updateDir, SERVICE_COULD_NOT_LOCK_UPDATER,
                             userToken)) {
       LOG_WARN(("Could not write update.status service update failure.  (%d)",
                 GetLastError()));
     }
     return false;
   }
 
   // Verify that the updater.exe that we are executing is the same
@@ -399,17 +411,21 @@ static bool UpdaterIsValid(LPWSTR update
     return false;
   }
 
   if (!updaterIsCorrect) {
     LOG_WARN(
         ("The updaters do not match, updater will not run.\n"
          "Path 1: %ls\nPath 2: %ls",
          updater, installDirUpdater));
-    if (!WriteStatusFailure(updateDir, SERVICE_UPDATER_COMPARE_ERROR,
+    if (
+#ifndef DISABLE_USER_IMPERSONATION
+        !userToken ||
+#endif
+        !WriteStatusFailure(updateDir, SERVICE_UPDATER_COMPARE_ERROR,
                             userToken)) {
       LOG_WARN(("Could not write update.status updater compare failure."));
     }
     return false;
   }
 
   LOG(
       ("updater.exe was compared successfully to the installation directory"
@@ -441,17 +457,21 @@ static bool UpdaterIsValid(LPWSTR update
     FreeLibrary(updaterModule);
   }
 
   if (result) {
     LOG(
         ("The updater.exe application contains the Mozilla"
          " updater identity."));
   } else {
-    if (!WriteStatusFailure(updateDir, SERVICE_UPDATER_IDENTITY_ERROR,
+    if (
+#ifndef DISABLE_USER_IMPERSONATION
+        !userToken ||
+#endif
+        !WriteStatusFailure(updateDir, SERVICE_UPDATER_IDENTITY_ERROR,
                             userToken)) {
       LOG_WARN(("Could not write update.status no updater identity."));
     }
     return false;
   }
 
 #ifndef DISABLE_UPDATER_AUTHENTICODE_CHECK
   return DoesBinaryMatchAllowedCertificates(installDir, updater);
@@ -658,16 +678,20 @@ BOOL ExecuteServiceCommand(int argc, LPW
     } else {
       // The ID is only used by tests, so failure to allocate it isn't fatal.
       LOG(("Executing service command %ls", argv[2]));
     }
   }
 
   BOOL result = FALSE;
   if (!lstrcmpi(argv[2], L"software-update")) {
+    // Null userToken will be treated as "no impersonation needed" in most
+    // cases, except UpdaterIsValid below.
+    nsAutoHandle userToken;
+
     // This check is also performed in updater.cpp and is performed here
     // as well since the maintenance service can be called directly.
     if (argc < 4 || !IsValidFullPath(argv[4])) {
       // Since the status file is written to the patch directory and the patch
       // directory is invalid don't write the status file.
       LOG_WARN(("The patch directory path is not valid for this application."));
       return FALSE;
     }
@@ -691,82 +715,123 @@ BOOL ExecuteServiceCommand(int argc, LPW
       return FALSE;
     }
 
     // This check is also performed in updater.cpp and is performed here
     // as well since the maintenance service can be called directly.
     if (argc < 5 || !IsValidFullPath(argv[5])) {
       LOG_WARN(
           ("The install directory path is not valid for this application."));
+#ifdef DISABLE_USER_IMPERSONATION
+      if (!WriteStatusFailure(argv[4], SERVICE_INVALID_INSTALL_DIR_PATH_ERROR,
+                              userToken)) {
+        LOG_WARN(("Could not write update.status for previous failure."));
+      }
+#else
       // No user token so we can't call
       // WriteStatusFailure(argv[4], SERVICE_INVALID_INSTALL_DIR_PATH_ERROR)
+#endif
       return FALSE;
     }
 
     if (!IsOldCommandline(argc - 3, argv + 3)) {
       // This check is also performed in updater.cpp and is performed here
       // as well since the maintenance service can be called directly.
       if (argc < 6 || !IsValidFullPath(argv[6])) {
         LOG_WARN(
             ("The working directory path is not valid for this application."));
+#ifdef DISABLE_USER_IMPERSONATION
+        if (!WriteStatusFailure(argv[4], SERVICE_INVALID_WORKING_DIR_PATH_ERROR,
+                                userToken)) {
+          LOG_WARN(("Could not write update.status for previous failure."));
+        }
+#else
         // No user token so we can't call
         // WriteStatusFailure(argv[4], SERVICE_INVALID_WORKING_DIR_PATH_ERROR)
+#endif
         return FALSE;
       }
 
       // These checks are also performed in updater.cpp and is performed here
       // as well since the maintenance service can be called directly.
       if (_wcsnicmp(argv[6], argv[5], MAX_PATH) != 0) {
         if (argc < 7 ||
             (wcscmp(argv[7], L"-1") != 0 && !wcsstr(argv[7], L"/replace"))) {
           LOG_WARN(
               ("Installation directory and working directory must be the "
                "same for non-staged updates. Exiting."));
+#ifdef DISABLE_USER_IMPERSONATION
+          if (!WriteStatusFailure(argv[4], SERVICE_INVALID_APPLYTO_DIR_ERROR,
+                                  userToken)) {
+            LOG_WARN(("Could not write update.status for previous failure."));
+          }
+#else
           // No user token so we can't call
           // WriteStatusFailure(argv[4], SERVICE_INVALID_APPLYTO_DIR_ERROR)
+#endif
           return FALSE;
         }
 
         NS_tchar workingDirParent[MAX_PATH];
         NS_tsnprintf(workingDirParent,
                      sizeof(workingDirParent) / sizeof(workingDirParent[0]),
                      NS_T("%s"), argv[6]);
         if (!PathRemoveFileSpecW(workingDirParent)) {
           LOG_WARN(
               ("Couldn't remove file spec when attempting to verify the "
                "working directory path.  (%d)",
                GetLastError()));
+#ifdef DISABLE_USER_IMPERSONATION
+          if (!WriteStatusFailure(argv[4], REMOVE_FILE_SPEC_ERROR, userToken)) {
+            LOG_WARN(("Could not write update.status for previous failure."));
+          }
+#else
           // No user token so we can't call
           // WriteStatusFailure(argv[4], REMOVE_FILE_SPEC_ERROR)
+#endif
           return FALSE;
         }
 
         if (_wcsnicmp(workingDirParent, argv[5], MAX_PATH) != 0) {
           LOG_WARN(
               ("The apply-to directory must be the same as or "
                "a child of the installation directory! Exiting."));
+#ifdef DISABLE_USER_IMPERSONATION
+          if (!WriteStatusFailure(argv[4],
+                                  SERVICE_INVALID_APPLYTO_DIR_STAGED_ERROR,
+                                  userToken)) {
+            LOG_WARN(("Could not write update.status for previous failure."));
+          }
+#else
           // No user token so we can't call
           // WriteStatusFailure(argv[4],
           // SERVICE_INVALID_APPLYTO_DIR_STAGED_ERROR)
+#endif
           return FALSE;
         }
       }
     }
 
     // Use the passed in command line arguments for the update, except for the
     // path to updater.exe. We always look for updater.exe in the installation
     // directory, then we copy that updater.exe to a the directory of the
     // MozillaMaintenance service so that a low integrity process cannot
     // replace the updater.exe at any point and use that for the update.
     // It also makes DLL injection attacks harder.
     WCHAR installDir[MAX_PATH + 1] = {L'\0'};
     if (!GetInstallationDir(argc - 3, argv + 3, installDir)) {
       LOG_WARN(("Could not get the installation directory"));
+#ifdef DISABLE_USER_IMPERSONATION
+      if (!WriteStatusFailure(argv[4], SERVICE_INSTALLDIR_ERROR, userToken)) {
+        LOG_WARN(("Could not write update.status for previous failure."));
+      }
+#else
       // No user token so we can't call
       // WriteStatusFailure(argv[4], SERVICE_INSTALLDIR_ERROR)
+#endif
       return FALSE;
     }
 
     mozilla::UniquePtr<wchar_t[]> updateDir;
     HRESULT permResult = GetCommonUpdateDirectory(
         installDir, SetPermissionsOf::AllFilesAndDirs, updateDir);
     if (FAILED(permResult)) {
       LOG_WARN(
@@ -780,47 +845,63 @@ BOOL ExecuteServiceCommand(int argc, LPW
                                             maintenanceServiceKey)) {
         LOG(("Checking for Maintenance Service registry. key: '%ls'",
              maintenanceServiceKey));
         HKEY baseKey = nullptr;
         if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, maintenanceServiceKey, 0,
                           KEY_READ | KEY_WOW64_64KEY,
                           &baseKey) != ERROR_SUCCESS) {
           LOG_WARN(("The maintenance service registry key does not exist."));
+#ifdef DISABLE_USER_IMPERSONATION
+          if (!WriteStatusFailure(argv[4], SERVICE_INSTALL_DIR_REG_ERROR,
+                                  userToken)) {
+            LOG_WARN(("Could not write update.status for previous failure."));
+          }
+#else
           // No user token so we can't call
           // WriteStatusFailure(argv[4], SERVICE_INSTALL_DIR_REG_ERROR)
+#endif
           return FALSE;
         }
         RegCloseKey(baseKey);
       } else {
+#ifdef DISABLE_USER_IMPERSONATION
+        if (!WriteStatusFailure(argv[4], SERVICE_CALC_REG_PATH_ERROR,
+                                userToken)) {
+          LOG_WARN(("Could not write update.status for previous failure."));
+        }
+#else
         // No user token so we can't call
         // WriteStatusFailure(argv[4], SERVICE_CALC_REG_PATH_ERROR)
+#endif
         return FALSE;
       }
     }
 
     WCHAR installDirUpdater[MAX_PATH + 1] = {L'\0'};
     wcsncpy(installDirUpdater, installDir, MAX_PATH);
     if (!PathAppendSafe(installDirUpdater, L"updater.exe")) {
       LOG_WARN(("Install directory updater could not be determined."));
       result = FALSE;
     }
 
-    nsAutoHandle userToken;
-    // Intentionally passing null userToken.
+    // Intentionally passing null userToken so that UpdaterIsValid will not
+    // write status.
     result = UpdaterIsValid(installDirUpdater, installDir, argv[4], userToken);
 
+#ifndef DISABLE_USER_IMPERSONATION
     if (result) {
       userToken.own(GetUserProcessToken(installDirUpdater, argc - 3,
                                         const_cast<LPCWSTR *>(argv + 3)));
       result = !!userToken;
       if (!userToken) {
         LOG_WARN(("Could not get user process impersonation token"));
       }
     }
+#endif
 
     WCHAR secureUpdaterPath[MAX_PATH + 1] = {L'\0'};
     if (result) {
       result = GetSecureUpdaterPath(secureUpdaterPath);  // Does its own logging
     }
     if (result) {
       LOG(
           ("Passed in path: '%ls' (ignored); "
@@ -829,17 +910,20 @@ BOOL ExecuteServiceCommand(int argc, LPW
            argv[3], installDirUpdater, secureUpdaterPath));
       DeleteSecureUpdater(secureUpdaterPath);
       result = CopyFileW(installDirUpdater, secureUpdaterPath, FALSE);
     }
 
     if (!result) {
       LOG_WARN(
           ("Could not copy path to secure location.  (%d)", GetLastError()));
-      if (!userToken ||
+      if (
+#ifndef DISABLE_USER_IMPERSONATION
+          !userToken ||
+#endif
           !WriteStatusFailure(argv[4], SERVICE_COULD_NOT_COPY_UPDATER,
                               userToken)) {
         LOG_WARN(
             ("Could not write update.status could not copy updater error"));
       }
     } else {
       // We obtained the path and copied it successfully, update the path to
       // use for the service update.
--- a/toolkit/mozapps/update/common/updatehelper.h
+++ b/toolkit/mozapps/update/common/updatehelper.h
@@ -16,16 +16,21 @@ BOOL DoesFallbackKeyExist();
 BOOL IsLocalFile(LPCWSTR file, BOOL &isLocal);
 DWORD StartServiceCommand(int argc, LPCWSTR *argv);
 BOOL IsUnpromptedElevation(BOOL &isUnpromptedElevation);
 
 #define SVC_NAME L"MozillaMaintenance"
 
 #define BASE_SERVICE_REG_KEY L"SOFTWARE\\Mozilla\\MaintenanceService"
 
+// Disable impersonation on release (Bug 1514898)
+#ifndef EARLY_BETA_OR_EARLIER
+#define DISABLE_USER_IMPERSONATION
+#endif
+
 // Environment variable used to pass impersonation token to child
 #define USER_TOKEN_VAR_NAME "MOZ_USER_TOKEN"
 
 // The test only fallback key, as its name implies, is only present on machines
 // that will use automated tests.  Since automated tests always run from a
 // different directory for each test, the presence of this key bypasses the
 // "This is a valid installation directory" check.  This key also stores
 // the allowed name and issuer for cert checks so that the cert check
--- a/toolkit/mozapps/update/tests/unit_base_updater/head_update.js
+++ b/toolkit/mozapps/update/tests/unit_base_updater/head_update.js
@@ -1,8 +1,12 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
 const IS_SERVICE_TEST = false;
 
 /* import-globals-from ../data/xpcshellUtilsAUS.js */
 load("../data/xpcshellUtilsAUS.js");
+
+XPCOMUtils.defineLazyModuleGetters(this, {
+  AppConstants: "resource://gre/modules/AppConstants.jsm",
+});
--- a/toolkit/mozapps/update/tests/unit_base_updater/invalidArgInstallDirPathTooLongFailure.js
+++ b/toolkit/mozapps/update/tests/unit_base_updater/invalidArgInstallDirPathTooLongFailure.js
@@ -1,19 +1,22 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
  */
 
 /* Too long install directory path failure test */
 
 /* The service cannot safely write update.status for this failure. */
-const STATE_AFTER_RUNUPDATE =
-  IS_SERVICE_TEST ? STATE_PENDING_SVC
-                  : STATE_FAILED_INVALID_INSTALL_DIR_PATH_ERROR;
+const STATE_AFTER_RUNUPDATE_BASE = STATE_FAILED_INVALID_INSTALL_DIR_PATH_ERROR;
+const STATE_AFTER_RUNUPDATE_SERVICE = AppConstants.EARLY_BETA_OR_EARLIER
+    ? STATE_PENDING_SVC
+    : STATE_FAILED_SERVICE_INVALID_INSTALL_DIR_PATH_ERROR;
+const STATE_AFTER_RUNUPDATE = IS_SERVICE_TEST ? STATE_AFTER_RUNUPDATE_SERVICE
+                                              : STATE_AFTER_RUNUPDATE_BASE;
 
 function run_test() {
   if (!setupTestCommon()) {
     return;
   }
   gTestFiles = gTestFilesCompleteSuccess;
   gTestDirs = gTestDirsCompleteSuccess;
   setTestFilesAndDirsForFailure();
@@ -47,16 +50,21 @@ function runUpdateFinished() {
   executeSoon(waitForUpdateXMLFiles);
 }
 
 /**
  * Called after the call to waitForUpdateXMLFiles finishes.
  */
 function waitForUpdateXMLFilesFinished() {
   if (IS_SERVICE_TEST) {
-    checkUpdateManager(STATE_NONE, false, STATE_PENDING_SVC, 0, 1);
+    if (AppConstants.EARLY_BETA_OR_EARLIER) {
+      checkUpdateManager(STATE_NONE, false, STATE_PENDING_SVC, 0, 1);
+    } else {
+      checkUpdateManager(STATE_NONE, false, STATE_FAILED,
+                         SERVICE_INVALID_INSTALL_DIR_PATH_ERROR, 1);
+    }
   } else {
     checkUpdateManager(STATE_NONE, false, STATE_FAILED,
                        INVALID_INSTALL_DIR_PATH_ERROR, 1);
   }
 
   waitForFilesInUse();
 }
--- a/toolkit/mozapps/update/tests/unit_base_updater/invalidArgInstallDirPathTraversalFailure.js
+++ b/toolkit/mozapps/update/tests/unit_base_updater/invalidArgInstallDirPathTraversalFailure.js
@@ -1,19 +1,22 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
  */
 
 /* Install directory path traversal failure test */
 
 /* The service cannot safely write update.status for this failure. */
-const STATE_AFTER_RUNUPDATE =
-  IS_SERVICE_TEST ? STATE_PENDING_SVC
-                  : STATE_FAILED_INVALID_INSTALL_DIR_PATH_ERROR;
+const STATE_AFTER_RUNUPDATE_BASE = STATE_FAILED_INVALID_INSTALL_DIR_PATH_ERROR;
+const STATE_AFTER_RUNUPDATE_SERVICE = AppConstants.EARLY_BETA_OR_EARLIER
+    ? STATE_PENDING_SVC
+    : STATE_FAILED_SERVICE_INVALID_INSTALL_DIR_PATH_ERROR;
+const STATE_AFTER_RUNUPDATE = IS_SERVICE_TEST ? STATE_AFTER_RUNUPDATE_SERVICE
+                                              : STATE_AFTER_RUNUPDATE_BASE;
 
 function run_test() {
   if (!setupTestCommon()) {
     return;
   }
   gTestFiles = gTestFilesCompleteSuccess;
   gTestDirs = gTestDirsCompleteSuccess;
   setTestFilesAndDirsForFailure();
@@ -44,16 +47,21 @@ function runUpdateFinished() {
   executeSoon(waitForUpdateXMLFiles);
 }
 
 /**
  * Called after the call to waitForUpdateXMLFiles finishes.
  */
 function waitForUpdateXMLFilesFinished() {
   if (IS_SERVICE_TEST) {
-    checkUpdateManager(STATE_NONE, false, STATE_PENDING_SVC, 0, 1);
+    if (AppConstants.EARLY_BETA_OR_EARLIER) {
+      checkUpdateManager(STATE_NONE, false, STATE_PENDING_SVC, 0, 1);
+    } else {
+      checkUpdateManager(STATE_NONE, false, STATE_FAILED,
+                         SERVICE_INVALID_INSTALL_DIR_PATH_ERROR, 1);
+    }
   } else {
     checkUpdateManager(STATE_NONE, false, STATE_FAILED,
                        INVALID_INSTALL_DIR_PATH_ERROR, 1);
   }
 
   waitForFilesInUse();
 }
--- a/toolkit/mozapps/update/tests/unit_base_updater/invalidArgInstallWorkingDirPathNotSameFailure_win.js
+++ b/toolkit/mozapps/update/tests/unit_base_updater/invalidArgInstallWorkingDirPathNotSameFailure_win.js
@@ -2,19 +2,22 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
  */
 
 /* Different install and working directories for a regular update failure */
 
 /* The service cannot safely write update.status for this failure because the
  * check is done before validating the installed updater. */
-const STATE_AFTER_RUNUPDATE =
-  IS_SERVICE_TEST ? STATE_PENDING_SVC
-                  : STATE_FAILED_INVALID_APPLYTO_DIR_ERROR;
+const STATE_AFTER_RUNUPDATE_BASE = STATE_FAILED_INVALID_APPLYTO_DIR_ERROR;
+const STATE_AFTER_RUNUPDATE_SERVICE = AppConstants.EARLY_BETA_OR_EARLIER
+    ? STATE_PENDING_SVC
+    : STATE_FAILED_SERVICE_INVALID_APPLYTO_DIR_ERROR;
+const STATE_AFTER_RUNUPDATE = IS_SERVICE_TEST ? STATE_AFTER_RUNUPDATE_SERVICE
+                                              : STATE_AFTER_RUNUPDATE_BASE;
 
 function run_test() {
   if (!setupTestCommon()) {
     return;
   }
   gTestFiles = gTestFilesCompleteSuccess;
   gTestDirs = gTestDirsCompleteSuccess;
   setTestFilesAndDirsForFailure();
@@ -39,16 +42,21 @@ function runUpdateFinished() {
   executeSoon(waitForUpdateXMLFiles);
 }
 
 /**
  * Called after the call to waitForUpdateXMLFiles finishes.
  */
 function waitForUpdateXMLFilesFinished() {
   if (IS_SERVICE_TEST) {
-    checkUpdateManager(STATE_NONE, false, STATE_PENDING_SVC, 0, 1);
+    if (AppConstants.EARLY_BETA_OR_EARLIER) {
+      checkUpdateManager(STATE_NONE, false, STATE_PENDING_SVC, 0, 1);
+    } else {
+      checkUpdateManager(STATE_NONE, false, STATE_FAILED,
+                         SERVICE_INVALID_APPLYTO_DIR_ERROR, 1);
+    }
   } else {
     checkUpdateManager(STATE_NONE, false, STATE_FAILED,
                        INVALID_APPLYTO_DIR_ERROR, 1);
   }
 
   waitForFilesInUse();
 }
--- a/toolkit/mozapps/update/tests/unit_base_updater/invalidArgStageDirNotInInstallDirFailure_win.js
+++ b/toolkit/mozapps/update/tests/unit_base_updater/invalidArgStageDirNotInInstallDirFailure_win.js
@@ -2,19 +2,23 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
  */
 
 /* Different install and working directories for a regular update failure */
 
 /* The service cannot safely write update.status for this failure because the
  * check is done before validating the installed updater. */
-const STATE_AFTER_RUNUPDATE =
-  IS_SERVICE_TEST ? STATE_PENDING_SVC
-                  : STATE_FAILED_INVALID_APPLYTO_DIR_STAGED_ERROR;
+const STATE_AFTER_RUNUPDATE_BASE =
+  STATE_FAILED_INVALID_APPLYTO_DIR_STAGED_ERROR;
+const STATE_AFTER_RUNUPDATE_SERVICE = AppConstants.EARLY_BETA_OR_EARLIER
+    ? STATE_PENDING_SVC
+    : STATE_FAILED_SERVICE_INVALID_APPLYTO_DIR_STAGED_ERROR;
+const STATE_AFTER_RUNUPDATE = IS_SERVICE_TEST ? STATE_AFTER_RUNUPDATE_SERVICE
+                                              : STATE_AFTER_RUNUPDATE_BASE;
 
 function run_test() {
   if (!setupTestCommon()) {
     return;
   }
   gTestFiles = gTestFilesCompleteSuccess;
   gTestDirs = gTestDirsCompleteSuccess;
   setTestFilesAndDirsForFailure();
@@ -39,16 +43,21 @@ function runUpdateFinished() {
   executeSoon(waitForUpdateXMLFiles);
 }
 
 /**
  * Called after the call to waitForUpdateXMLFiles finishes.
  */
 function waitForUpdateXMLFilesFinished() {
   if (IS_SERVICE_TEST) {
-    checkUpdateManager(STATE_NONE, false, STATE_PENDING_SVC, 0, 1);
+    if (AppConstants.EARLY_BETA_OR_EARLIER) {
+      checkUpdateManager(STATE_NONE, false, STATE_PENDING_SVC, 0, 1);
+    } else {
+    checkUpdateManager(STATE_NONE, false, STATE_FAILED,
+                       SERVICE_INVALID_APPLYTO_DIR_STAGED_ERROR, 1);
+    }
   } else {
     checkUpdateManager(STATE_NONE, false, STATE_FAILED,
                        INVALID_APPLYTO_DIR_STAGED_ERROR, 1);
   }
 
   waitForFilesInUse();
 }
--- a/toolkit/mozapps/update/tests/unit_base_updater/invalidArgWorkingDirPathLocalUNCFailure_win.js
+++ b/toolkit/mozapps/update/tests/unit_base_updater/invalidArgWorkingDirPathLocalUNCFailure_win.js
@@ -2,19 +2,22 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
  */
 
 /* Working directory path local UNC failure test */
 
 /* The service cannot safely write update.status for this failure because the
  * check is done before validating the installed updater. */
-const STATE_AFTER_RUNUPDATE =
-  IS_SERVICE_TEST ? STATE_PENDING_SVC
-                  : STATE_FAILED_INVALID_WORKING_DIR_PATH_ERROR;
+const STATE_AFTER_RUNUPDATE_BASE = STATE_FAILED_INVALID_WORKING_DIR_PATH_ERROR;
+const STATE_AFTER_RUNUPDATE_SERVICE = AppConstants.EARLY_BETA_OR_EARLIER
+    ? STATE_PENDING_SVC
+    : STATE_FAILED_SERVICE_INVALID_WORKING_DIR_PATH_ERROR;
+const STATE_AFTER_RUNUPDATE = IS_SERVICE_TEST ? STATE_AFTER_RUNUPDATE_SERVICE
+                                              : STATE_AFTER_RUNUPDATE_BASE;
 
 function run_test() {
   if (!setupTestCommon()) {
     return;
   }
   gTestFiles = gTestFilesCompleteSuccess;
   gTestDirs = gTestDirsCompleteSuccess;
   setTestFilesAndDirsForFailure();
@@ -39,16 +42,21 @@ function runUpdateFinished() {
   executeSoon(waitForUpdateXMLFiles);
 }
 
 /**
  * Called after the call to waitForUpdateXMLFiles finishes.
  */
 function waitForUpdateXMLFilesFinished() {
   if (IS_SERVICE_TEST) {
-    checkUpdateManager(STATE_NONE, false, STATE_PENDING_SVC, 0, 1);
+    if (AppConstants.EARLY_BETA_OR_EARLIER) {
+      checkUpdateManager(STATE_NONE, false, STATE_PENDING_SVC, 0, 1);
+    } else {
+      checkUpdateManager(STATE_NONE, false, STATE_FAILED,
+                         SERVICE_INVALID_WORKING_DIR_PATH_ERROR, 1);
+    }
   } else {
     checkUpdateManager(STATE_NONE, false, STATE_FAILED,
                        INVALID_WORKING_DIR_PATH_ERROR, 1);
   }
 
   waitForFilesInUse();
 }
--- a/toolkit/mozapps/update/tests/unit_base_updater/invalidArgWorkingDirPathRelativeFailure.js
+++ b/toolkit/mozapps/update/tests/unit_base_updater/invalidArgWorkingDirPathRelativeFailure.js
@@ -2,19 +2,22 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
  */
 
 /* Relative working directory path failure test */
 
 /* The service cannot safely write update.status for this failure because the
  * check is done before validating the installed updater. */
-const STATE_AFTER_RUNUPDATE =
-  IS_SERVICE_TEST ? STATE_PENDING_SVC
-                  : STATE_FAILED_INVALID_WORKING_DIR_PATH_ERROR;
+const STATE_AFTER_RUNUPDATE_BASE = STATE_FAILED_INVALID_WORKING_DIR_PATH_ERROR;
+const STATE_AFTER_RUNUPDATE_SERVICE = AppConstants.EARLY_BETA_OR_EARLIER
+    ? STATE_PENDING_SVC
+    : STATE_FAILED_SERVICE_INVALID_WORKING_DIR_PATH_ERROR;
+const STATE_AFTER_RUNUPDATE = IS_SERVICE_TEST ? STATE_AFTER_RUNUPDATE_SERVICE
+                                              : STATE_AFTER_RUNUPDATE_BASE;
 
 function run_test() {
   if (!setupTestCommon()) {
     return;
   }
   gTestFiles = gTestFilesCompleteSuccess;
   gTestDirs = gTestDirsCompleteSuccess;
   setTestFilesAndDirsForFailure();
@@ -38,16 +41,21 @@ function runUpdateFinished() {
   executeSoon(waitForUpdateXMLFiles);
 }
 
 /**
  * Called after the call to waitForUpdateXMLFiles finishes.
  */
 function waitForUpdateXMLFilesFinished() {
   if (IS_SERVICE_TEST) {
-    checkUpdateManager(STATE_NONE, false, STATE_PENDING_SVC, 0, 1);
+    if (AppConstants.EARLY_BETA_OR_EARLIER) {
+      checkUpdateManager(STATE_NONE, false, STATE_PENDING_SVC, 0, 1);
+    } else {
+      checkUpdateManager(STATE_NONE, false, STATE_FAILED,
+                         SERVICE_INVALID_WORKING_DIR_PATH_ERROR, 1);
+    }
   } else {
     checkUpdateManager(STATE_NONE, false, STATE_FAILED,
                        INVALID_WORKING_DIR_PATH_ERROR, 1);
   }
 
   waitForFilesInUse();
 }
--- a/toolkit/mozapps/update/tests/unit_service_updater/head_update.js
+++ b/toolkit/mozapps/update/tests/unit_service_updater/head_update.js
@@ -1,8 +1,12 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
 const IS_SERVICE_TEST = true;
 
 /* import-globals-from ../data/xpcshellUtilsAUS.js */
 load("../data/xpcshellUtilsAUS.js");
+
+XPCOMUtils.defineLazyModuleGetters(this, {
+  AppConstants: "resource://gre/modules/AppConstants.jsm",
+});
--- a/toolkit/mozapps/update/tests/unit_service_updater/invalidArgInstallDirPathTooLongFailureSvc.js
+++ b/toolkit/mozapps/update/tests/unit_service_updater/invalidArgInstallDirPathTooLongFailureSvc.js
@@ -1,19 +1,22 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
  */
 
 /* Too long install directory path failure test */
 
 /* The service cannot safely write update.status for this failure. */
-const STATE_AFTER_RUNUPDATE =
-  IS_SERVICE_TEST ? STATE_PENDING_SVC
-                  : STATE_FAILED_INVALID_INSTALL_DIR_PATH_ERROR;
+const STATE_AFTER_RUNUPDATE_BASE = STATE_FAILED_INVALID_INSTALL_DIR_PATH_ERROR;
+const STATE_AFTER_RUNUPDATE_SERVICE = AppConstants.EARLY_BETA_OR_EARLIER
+    ? STATE_PENDING_SVC
+    : STATE_FAILED_SERVICE_INVALID_INSTALL_DIR_PATH_ERROR;
+const STATE_AFTER_RUNUPDATE = IS_SERVICE_TEST ? STATE_AFTER_RUNUPDATE_SERVICE
+                                              : STATE_AFTER_RUNUPDATE_BASE;
 
 function run_test() {
   if (!setupTestCommon()) {
     return;
   }
   gTestFiles = gTestFilesCompleteSuccess;
   gTestDirs = gTestDirsCompleteSuccess;
   setTestFilesAndDirsForFailure();
@@ -47,16 +50,21 @@ function runUpdateFinished() {
   executeSoon(waitForUpdateXMLFiles);
 }
 
 /**
  * Called after the call to waitForUpdateXMLFiles finishes.
  */
 function waitForUpdateXMLFilesFinished() {
   if (IS_SERVICE_TEST) {
-    checkUpdateManager(STATE_NONE, false, STATE_PENDING_SVC, 0, 1);
+    if (AppConstants.EARLY_BETA_OR_EARLIER) {
+      checkUpdateManager(STATE_NONE, false, STATE_PENDING_SVC, 0, 1);
+    } else {
+      checkUpdateManager(STATE_NONE, false, STATE_FAILED,
+                         SERVICE_INVALID_INSTALL_DIR_PATH_ERROR, 1);
+    }
   } else {
     checkUpdateManager(STATE_NONE, false, STATE_FAILED,
                        INVALID_INSTALL_DIR_PATH_ERROR, 1);
   }
 
   waitForFilesInUse();
 }
--- a/toolkit/mozapps/update/tests/unit_service_updater/invalidArgInstallDirPathTraversalFailureSvc.js
+++ b/toolkit/mozapps/update/tests/unit_service_updater/invalidArgInstallDirPathTraversalFailureSvc.js
@@ -1,19 +1,22 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
  */
 
 /* Install directory path traversal failure test */
 
 /* The service cannot safely write update.status for this failure. */
-const STATE_AFTER_RUNUPDATE =
-  IS_SERVICE_TEST ? STATE_PENDING_SVC
-                  : STATE_FAILED_INVALID_INSTALL_DIR_PATH_ERROR;
+const STATE_AFTER_RUNUPDATE_BASE = STATE_FAILED_INVALID_INSTALL_DIR_PATH_ERROR;
+const STATE_AFTER_RUNUPDATE_SERVICE = AppConstants.EARLY_BETA_OR_EARLIER
+    ? STATE_PENDING_SVC
+    : STATE_FAILED_SERVICE_INVALID_INSTALL_DIR_PATH_ERROR;
+const STATE_AFTER_RUNUPDATE = IS_SERVICE_TEST ? STATE_AFTER_RUNUPDATE_SERVICE
+                                              : STATE_AFTER_RUNUPDATE_BASE;
 
 function run_test() {
   if (!setupTestCommon()) {
     return;
   }
   gTestFiles = gTestFilesCompleteSuccess;
   gTestDirs = gTestDirsCompleteSuccess;
   setTestFilesAndDirsForFailure();
@@ -44,16 +47,21 @@ function runUpdateFinished() {
   executeSoon(waitForUpdateXMLFiles);
 }
 
 /**
  * Called after the call to waitForUpdateXMLFiles finishes.
  */
 function waitForUpdateXMLFilesFinished() {
   if (IS_SERVICE_TEST) {
-    checkUpdateManager(STATE_NONE, false, STATE_PENDING_SVC, 0, 1);
+    if (AppConstants.EARLY_BETA_OR_EARLIER) {
+      checkUpdateManager(STATE_NONE, false, STATE_PENDING_SVC, 0, 1);
+    } else {
+      checkUpdateManager(STATE_NONE, false, STATE_FAILED,
+                         SERVICE_INVALID_INSTALL_DIR_PATH_ERROR, 1);
+    }
   } else {
     checkUpdateManager(STATE_NONE, false, STATE_FAILED,
                        INVALID_INSTALL_DIR_PATH_ERROR, 1);
   }
 
   waitForFilesInUse();
 }
--- a/toolkit/mozapps/update/tests/unit_service_updater/invalidArgInstallWorkingDirPathNotSameFailureSvc_win.js
+++ b/toolkit/mozapps/update/tests/unit_service_updater/invalidArgInstallWorkingDirPathNotSameFailureSvc_win.js
@@ -2,19 +2,22 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
  */
 
 /* Different install and working directories for a regular update failure */
 
 /* The service cannot safely write update.status for this failure because the
  * check is done before validating the installed updater. */
-const STATE_AFTER_RUNUPDATE =
-  IS_SERVICE_TEST ? STATE_PENDING_SVC
-                  : STATE_FAILED_INVALID_APPLYTO_DIR_ERROR;
+const STATE_AFTER_RUNUPDATE_BASE = STATE_FAILED_INVALID_APPLYTO_DIR_ERROR;
+const STATE_AFTER_RUNUPDATE_SERVICE = AppConstants.EARLY_BETA_OR_EARLIER
+    ? STATE_PENDING_SVC
+    : STATE_FAILED_SERVICE_INVALID_APPLYTO_DIR_ERROR;
+const STATE_AFTER_RUNUPDATE = IS_SERVICE_TEST ? STATE_AFTER_RUNUPDATE_SERVICE
+                                              : STATE_AFTER_RUNUPDATE_BASE;
 
 function run_test() {
   if (!setupTestCommon()) {
     return;
   }
   gTestFiles = gTestFilesCompleteSuccess;
   gTestDirs = gTestDirsCompleteSuccess;
   setTestFilesAndDirsForFailure();
@@ -39,16 +42,21 @@ function runUpdateFinished() {
   executeSoon(waitForUpdateXMLFiles);
 }
 
 /**
  * Called after the call to waitForUpdateXMLFiles finishes.
  */
 function waitForUpdateXMLFilesFinished() {
   if (IS_SERVICE_TEST) {
-    checkUpdateManager(STATE_NONE, false, STATE_PENDING_SVC, 0, 1);
+    if (AppConstants.EARLY_BETA_OR_EARLIER) {
+      checkUpdateManager(STATE_NONE, false, STATE_PENDING_SVC, 0, 1);
+    } else {
+      checkUpdateManager(STATE_NONE, false, STATE_FAILED,
+                         SERVICE_INVALID_APPLYTO_DIR_ERROR, 1);
+    }
   } else {
     checkUpdateManager(STATE_NONE, false, STATE_FAILED,
                        INVALID_APPLYTO_DIR_ERROR, 1);
   }
 
   waitForFilesInUse();
 }
--- a/toolkit/mozapps/update/tests/unit_service_updater/invalidArgStageDirNotInInstallDirFailureSvc_win.js
+++ b/toolkit/mozapps/update/tests/unit_service_updater/invalidArgStageDirNotInInstallDirFailureSvc_win.js
@@ -2,19 +2,23 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
  */
 
 /* Different install and working directories for a regular update failure */
 
 /* The service cannot safely write update.status for this failure because the
  * check is done before validating the installed updater. */
-const STATE_AFTER_RUNUPDATE =
-  IS_SERVICE_TEST ? STATE_PENDING_SVC
-                  : STATE_FAILED_INVALID_APPLYTO_DIR_STAGED_ERROR;
+const STATE_AFTER_RUNUPDATE_BASE =
+  STATE_FAILED_INVALID_APPLYTO_DIR_STAGED_ERROR;
+const STATE_AFTER_RUNUPDATE_SERVICE = AppConstants.EARLY_BETA_OR_EARLIER
+    ? STATE_PENDING_SVC
+    : STATE_FAILED_SERVICE_INVALID_APPLYTO_DIR_STAGED_ERROR;
+const STATE_AFTER_RUNUPDATE = IS_SERVICE_TEST ? STATE_AFTER_RUNUPDATE_SERVICE
+                                              : STATE_AFTER_RUNUPDATE_BASE;
 
 function run_test() {
   if (!setupTestCommon()) {
     return;
   }
   gTestFiles = gTestFilesCompleteSuccess;
   gTestDirs = gTestDirsCompleteSuccess;
   setTestFilesAndDirsForFailure();
@@ -39,16 +43,21 @@ function runUpdateFinished() {
   executeSoon(waitForUpdateXMLFiles);
 }
 
 /**
  * Called after the call to waitForUpdateXMLFiles finishes.
  */
 function waitForUpdateXMLFilesFinished() {
   if (IS_SERVICE_TEST) {
-    checkUpdateManager(STATE_NONE, false, STATE_PENDING_SVC, 0, 1);
+    if (AppConstants.EARLY_BETA_OR_EARLIER) {
+      checkUpdateManager(STATE_NONE, false, STATE_PENDING_SVC, 0, 1);
+    } else {
+    checkUpdateManager(STATE_NONE, false, STATE_FAILED,
+                       SERVICE_INVALID_APPLYTO_DIR_STAGED_ERROR, 1);
+    }
   } else {
     checkUpdateManager(STATE_NONE, false, STATE_FAILED,
                        INVALID_APPLYTO_DIR_STAGED_ERROR, 1);
   }
 
   waitForFilesInUse();
 }
--- a/toolkit/mozapps/update/tests/unit_service_updater/invalidArgWorkingDirPathLocalUNCFailureSvc_win.js
+++ b/toolkit/mozapps/update/tests/unit_service_updater/invalidArgWorkingDirPathLocalUNCFailureSvc_win.js
@@ -2,19 +2,22 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
  */
 
 /* Working directory path local UNC failure test */
 
 /* The service cannot safely write update.status for this failure because the
  * check is done before validating the installed updater. */
-const STATE_AFTER_RUNUPDATE =
-  IS_SERVICE_TEST ? STATE_PENDING_SVC
-                  : STATE_FAILED_INVALID_WORKING_DIR_PATH_ERROR;
+const STATE_AFTER_RUNUPDATE_BASE = STATE_FAILED_INVALID_WORKING_DIR_PATH_ERROR;
+const STATE_AFTER_RUNUPDATE_SERVICE = AppConstants.EARLY_BETA_OR_EARLIER
+    ? STATE_PENDING_SVC
+    : STATE_FAILED_SERVICE_INVALID_WORKING_DIR_PATH_ERROR;
+const STATE_AFTER_RUNUPDATE = IS_SERVICE_TEST ? STATE_AFTER_RUNUPDATE_SERVICE
+                                              : STATE_AFTER_RUNUPDATE_BASE;
 
 function run_test() {
   if (!setupTestCommon()) {
     return;
   }
   gTestFiles = gTestFilesCompleteSuccess;
   gTestDirs = gTestDirsCompleteSuccess;
   setTestFilesAndDirsForFailure();
@@ -39,16 +42,21 @@ function runUpdateFinished() {
   executeSoon(waitForUpdateXMLFiles);
 }
 
 /**
  * Called after the call to waitForUpdateXMLFiles finishes.
  */
 function waitForUpdateXMLFilesFinished() {
   if (IS_SERVICE_TEST) {
-    checkUpdateManager(STATE_NONE, false, STATE_PENDING_SVC, 0, 1);
+    if (AppConstants.EARLY_BETA_OR_EARLIER) {
+      checkUpdateManager(STATE_NONE, false, STATE_PENDING_SVC, 0, 1);
+    } else {
+      checkUpdateManager(STATE_NONE, false, STATE_FAILED,
+                         SERVICE_INVALID_WORKING_DIR_PATH_ERROR, 1);
+    }
   } else {
     checkUpdateManager(STATE_NONE, false, STATE_FAILED,
                        INVALID_WORKING_DIR_PATH_ERROR, 1);
   }
 
   waitForFilesInUse();
 }
--- a/toolkit/mozapps/update/tests/unit_service_updater/invalidArgWorkingDirPathRelativeFailureSvc.js
+++ b/toolkit/mozapps/update/tests/unit_service_updater/invalidArgWorkingDirPathRelativeFailureSvc.js
@@ -2,19 +2,22 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
  */
 
 /* Relative working directory path failure test */
 
 /* The service cannot safely write update.status for this failure because the
  * check is done before validating the installed updater. */
-const STATE_AFTER_RUNUPDATE =
-  IS_SERVICE_TEST ? STATE_PENDING_SVC
-                  : STATE_FAILED_INVALID_WORKING_DIR_PATH_ERROR;
+const STATE_AFTER_RUNUPDATE_BASE = STATE_FAILED_INVALID_WORKING_DIR_PATH_ERROR;
+const STATE_AFTER_RUNUPDATE_SERVICE = AppConstants.EARLY_BETA_OR_EARLIER
+    ? STATE_PENDING_SVC
+    : STATE_FAILED_SERVICE_INVALID_WORKING_DIR_PATH_ERROR;
+const STATE_AFTER_RUNUPDATE = IS_SERVICE_TEST ? STATE_AFTER_RUNUPDATE_SERVICE
+                                              : STATE_AFTER_RUNUPDATE_BASE;
 
 function run_test() {
   if (!setupTestCommon()) {
     return;
   }
   gTestFiles = gTestFilesCompleteSuccess;
   gTestDirs = gTestDirsCompleteSuccess;
   setTestFilesAndDirsForFailure();
@@ -38,16 +41,21 @@ function runUpdateFinished() {
   executeSoon(waitForUpdateXMLFiles);
 }
 
 /**
  * Called after the call to waitForUpdateXMLFiles finishes.
  */
 function waitForUpdateXMLFilesFinished() {
   if (IS_SERVICE_TEST) {
-    checkUpdateManager(STATE_NONE, false, STATE_PENDING_SVC, 0, 1);
+    if (AppConstants.EARLY_BETA_OR_EARLIER) {
+      checkUpdateManager(STATE_NONE, false, STATE_PENDING_SVC, 0, 1);
+    } else {
+      checkUpdateManager(STATE_NONE, false, STATE_FAILED,
+                         SERVICE_INVALID_WORKING_DIR_PATH_ERROR, 1);
+    }
   } else {
     checkUpdateManager(STATE_NONE, false, STATE_FAILED,
                        INVALID_WORKING_DIR_PATH_ERROR, 1);
   }
 
   waitForFilesInUse();
 }
--- a/toolkit/mozapps/update/updater/updater.cpp
+++ b/toolkit/mozapps/update/updater/updater.cpp
@@ -2537,16 +2537,18 @@ return 0;
 int NS_main(int argc, NS_tchar **argv) {
 #ifdef MOZ_MAINTENANCE_SERVICE
   sUsingService = EnvHasValue("MOZ_USING_SERVICE");
   putenv(const_cast<char *>("MOZ_USING_SERVICE="));
 
 #if XP_WIN
   // Null gUserToken is treated as "no impersonation required".
   gUserToken = nullptr;
+
+#ifndef DISABLE_USER_IMPERSONATION
   if (sUsingService) {
     char *tokenStr = getenv(USER_TOKEN_VAR_NAME);
 
     if (tokenStr) {
       // If a token is provided ensure that we can use it.
 
       if (sscanf_s(tokenStr, "%p", &gUserToken) != 1 || !gUserToken) {
         fprintf(stderr,
@@ -2564,16 +2566,17 @@ int NS_main(int argc, NS_tchar **argv) {
       RevertToSelf();
     } else {
       // If there is no token provided, then even if we are running via the
       // maintenance service we will proceed without impersonation. This
       // prevents failure if an old maintenance service somehow attempts to
       // run a new updater.
     }
   }
+#endif  // DISABLE_USER_IMPERSONATION
 #endif
 #endif
 
   // The callback is the remaining arguments starting at callbackIndex.
   // The argument specified by callbackIndex is the callback executable and the
   // argument prior to callbackIndex is the working directory.
   const int callbackIndex = 6;