Bug 748948 - r=rstrong, a=akeybl
authorBrian R. Bondy <netzen@gmail.com>
Wed, 02 May 2012 15:40:33 -0400
changeset 92169 023b3a0e655b33e066af547964f9b9b88d9b8b9c
parent 92168 b906d4ad61245343a9ebbb678f22c2ea2ecb5e41
child 92170 4c55969f64f9d4972267ba49a7c4e5177ea4de16
push idunknown
push userunknown
push dateunknown
reviewersrstrong, akeybl
bugs748948
milestone13.0
Bug 748948 - r=rstrong, a=akeybl
toolkit/components/maintenanceservice/workmonitor.cpp
toolkit/mozapps/update/common/errors.h
toolkit/mozapps/update/common/updatehelper.cpp
toolkit/mozapps/update/common/updatehelper.h
toolkit/mozapps/update/nsUpdateService.js
toolkit/mozapps/update/updater/updater.cpp
--- a/toolkit/components/maintenanceservice/workmonitor.cpp
+++ b/toolkit/components/maintenanceservice/workmonitor.cpp
@@ -284,16 +284,44 @@ ProcessSoftwareUpdateCommand(DWORD argc,
         !WriteStatusFailure(argv[1], 
                             SERVICE_NOT_ENOUGH_COMMAND_LINE_ARGS)) {
       LOG(("Could not write update.status service update failure."
            "Last error: %d\n", GetLastError()));
     }
     return FALSE;
   }
 
+  // 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(argv[0], isLocal) || !isLocal) {
+    LOG(("Filesystem in path %ls is not supported"
+         "Last error: %d\n", argv[0], GetLastError()));
+    if (!WriteStatusFailure(argv[1], 
+                            SERVICE_UPDATER_NOT_FIXED_DRIVE)) {
+      LOG(("Could not write update.status service update failure."
+           "Last error: %d\n", GetLastError()));
+    }
+    return FALSE;
+  }
+
+  nsAutoHandle noWriteLock(CreateFileW(argv[0], GENERIC_READ, FILE_SHARE_READ, 
+                                       NULL, OPEN_EXISTING, 0, NULL));
+  if (INVALID_HANDLE_VALUE == noWriteLock) {
+      LOG(("Could not set no write sharing access on file."
+           "Last error: %d\n", GetLastError()));
+    if (!WriteStatusFailure(argv[1], 
+                            SERVICE_COULD_NOT_LOCK_UPDATER)) {
+      LOG(("Could not write update.status service update failure."
+           "Last error: %d\n", GetLastError()));
+    }
+    return FALSE;
+  }
+
   // Verify that the updater.exe that we are executing is the same
   // as the one in the installation directory which we are updating.
   // The installation dir that we are installing to is argv[2].
   WCHAR installDirUpdater[MAX_PATH + 1];
   wcsncpy(installDirUpdater, argv[2], MAX_PATH);
   if (!PathAppendSafe(installDirUpdater, L"updater.exe")) {
     LOG(("Install directory updater could not be determined.\n"));
     result = FALSE;
--- a/toolkit/mozapps/update/common/errors.h
+++ b/toolkit/mozapps/update/common/errors.h
@@ -66,25 +66,27 @@
 #define CERT_LOAD_ERROR 17
 #define CERT_HANDLING_ERROR 18
 #define CERT_VERIFY_ERROR 19
 #define ARCHIVE_NOT_OPEN 20
 #define COULD_NOT_READ_PRODUCT_INFO_BLOCK_ERROR 21
 #define MAR_CHANNEL_MISMATCH_ERROR 22
 #define VERSION_DOWNGRADE_ERROR 23
 
-// Error codes 24-30 are related to the maintenance service
+// Error codes 24-32 are related to the maintenance service
 // and so are Windows only
 #define SERVICE_UPDATER_COULD_NOT_BE_STARTED 24
 #define SERVICE_NOT_ENOUGH_COMMAND_LINE_ARGS 25
 #define SERVICE_UPDATER_SIGN_ERROR 26
 #define SERVICE_UPDATER_COMPARE_ERROR 27
 #define SERVICE_UPDATER_IDENTITY_ERROR 28
 #define SERVICE_STILL_APPLYING_ON_SUCCESS 29
 #define SERVICE_STILL_APPLYING_ON_FAILURE 30
+#define SERVICE_UPDATER_NOT_FIXED_DRIVE 31
+#define SERVICE_COULD_NOT_LOCK_UPDATER 32
 
 // The following error codes are only used by updater.exe
 // when a fallback key exists and XPCShell tests are being run.
 #define FALLBACKKEY_UNKNOWN_ERROR 100
 #define FALLBACKKEY_REGPATH_ERROR 101
 #define FALLBACKKEY_NOKEY_ERROR 102
 #define FALLBACKKEY_SERVICE_NO_STOP_ERROR 103
 #define FALLBACKKEY_LAUNCH_ERROR 104
--- a/toolkit/mozapps/update/common/updatehelper.cpp
+++ b/toolkit/mozapps/update/common/updatehelper.cpp
@@ -647,8 +647,28 @@ DoesFallbackKeyExist()
                     KEY_READ | KEY_WOW64_64KEY, 
                     &testOnlyFallbackKey) != ERROR_SUCCESS) {
     return FALSE;
   }
 
   RegCloseKey(testOnlyFallbackKey);
   return TRUE;
 }
+
+/**
+ * Determines if the file system for the specified file handle is local
+ * @param file path to check the filesystem type for, must be at most MAX_PATH
+ * @param isLocal out parameter which will hold TRUE if the drive is local
+ * @return TRUE if the call succeeded
+*/
+BOOL
+IsLocalFile(LPCWSTR file, BOOL &isLocal)
+{
+  WCHAR rootPath[MAX_PATH + 1];
+  if (wcslen(file) > MAX_PATH) {
+    return FALSE;
+  }
+
+  wcscpy(rootPath, file);
+  PathStripToRootW(rootPath);
+  isLocal = GetDriveTypeW(rootPath) == DRIVE_FIXED;
+  return TRUE;
+}
--- a/toolkit/mozapps/update/common/updatehelper.h
+++ b/toolkit/mozapps/update/common/updatehelper.h
@@ -42,10 +42,11 @@ BOOL LaunchWinPostProcess(const WCHAR *i
 BOOL StartServiceUpdate(int argc, LPWSTR *argv);
 BOOL GetUpdateDirectoryPath(LPWSTR path);
 DWORD LaunchServiceSoftwareUpdateCommand(int argc, LPCWSTR *argv);
 BOOL WriteStatusFailure(LPCWSTR updateDirPath, int errorCode);
 BOOL WriteStatusPending(LPCWSTR updateDirPath);
 DWORD WaitForServiceStop(LPCWSTR serviceName, DWORD maxWaitSeconds);
 DWORD WaitForProcessExit(LPCWSTR filename, DWORD maxSeconds);
 BOOL DoesFallbackKeyExist();
+BOOL IsLocalFile(LPCWSTR file, BOOL &isLocal);
 
 #define SVC_NAME L"MozillaMaintenance"
--- a/toolkit/mozapps/update/nsUpdateService.js
+++ b/toolkit/mozapps/update/nsUpdateService.js
@@ -145,16 +145,18 @@ const ELEVATION_CANCELED = 9;
 // Windows service specific errors
 const SERVICE_UPDATER_COULD_NOT_BE_STARTED = 24;
 const SERVICE_NOT_ENOUGH_COMMAND_LINE_ARGS = 25;
 const SERVICE_UPDATER_SIGN_ERROR           = 26;
 const SERVICE_UPDATER_COMPARE_ERROR        = 27;
 const SERVICE_UPDATER_IDENTITY_ERROR       = 28;
 const SERVICE_STILL_APPLYING_ON_SUCCESS    = 29;
 const SERVICE_STILL_APPLYING_ON_FAILURE    = 30;
+const SERVICE_UPDATER_NOT_FIXED_DRIVE      = 31;
+const SERVICE_COULD_NOT_LOCK_UPDATER       = 32;
 
 const CERT_ATTR_CHECK_FAILED_NO_UPDATE  = 100;
 const CERT_ATTR_CHECK_FAILED_HAS_UPDATE = 101;
 const BACKGROUNDCHECK_MULTIPLE_FAILURES = 110;
 
 const DOWNLOAD_CHUNK_SIZE           = 300000; // bytes
 const DOWNLOAD_BACKGROUND_INTERVAL  = 600;    // seconds
 const DOWNLOAD_FOREGROUND_INTERVAL  = 0;
@@ -1410,17 +1412,20 @@ UpdateService.prototype = {
         }
 
         if (update.errorCode == SERVICE_UPDATER_COULD_NOT_BE_STARTED ||
             update.errorCode == SERVICE_NOT_ENOUGH_COMMAND_LINE_ARGS ||
             update.errorCode == SERVICE_UPDATER_SIGN_ERROR ||
             update.errorCode == SERVICE_UPDATER_COMPARE_ERROR ||
             update.errorCode == SERVICE_UPDATER_IDENTITY_ERROR ||
             update.errorCode == SERVICE_STILL_APPLYING_ON_SUCCESS ||
-            update.errorCode == SERVICE_STILL_APPLYING_ON_FAILURE) {
+            update.errorCode == SERVICE_STILL_APPLYING_ON_FAILURE ||
+            update.errorCode == SERVICE_UPDATER_NOT_FIXED_DRIVE ||
+            update.errorCode == SERVICE_COULD_NOT_LOCK_UPDATER) {
+
           var failCount = getPref("getIntPref", 
                                   PREF_APP_UPDATE_SERVICE_ERRORS, 0);
           var maxFail = getPref("getIntPref", 
                                 PREF_APP_UPDATE_SERVICE_MAX_ERRORS, 
                                 DEFAULT_SERVICE_MAX_ERRORS);
 
           // As a safety, when the service reaches maximum failures, it will
           // disable itself and fallback to using the normal update mechanism
--- a/toolkit/mozapps/update/updater/updater.cpp
+++ b/toolkit/mozapps/update/updater/updater.cpp
@@ -1812,16 +1812,24 @@ int NS_main(int argc, NS_tchar **argv)
       }
 
       PRUnichar *cmdLine = MakeCommandLine(argc - 1, argv + 1);
       if (!cmdLine) {
         CloseHandle(elevatedFileHandle);
         return 1;
       }
 
+      // Make sure the path to the updater to use for the update is on local.
+      // We do this check to make sure that file locking is available for
+      // race condition security checks.
+      if (useService) {
+        BOOL isLocal = FALSE;
+        useService = IsLocalFile(argv[0], isLocal) && isLocal;
+      }
+      
       // Make sure the service registry entries for the instsallation path
       // are available.  If not don't use the service.
       if (useService) {
         WCHAR maintenanceServiceKey[MAX_PATH + 1];
         if (CalculateRegistryPathFromFilePath(argv[2], maintenanceServiceKey)) {
           HKEY baseKey;
           if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, 
                             maintenanceServiceKey, 0,