fix for bug #390433: [Vista] Users that are not a member of the administrators group receive update notification. r=rstrong
authorsspitzer@mozilla.org
Thu, 20 Sep 2007 09:54:00 -0700
changeset 6144 c07da4701e5f4249c1033f2212792907bad7afc9
parent 6142 099d324f65accdd7c7430aead273047e36ba5a03
child 6145 525c23d801892d60addea1319244f551b8f10c70
push idunknown
push userunknown
push dateunknown
reviewersrstrong
bugs390433
milestone1.9a8pre
fix for bug #390433: [Vista] Users that are not a member of the administrators group receive update notification. r=rstrong
toolkit/mozapps/update/src/nsUpdateService.js.in
toolkit/xre/nsAppRunner.cpp
toolkit/xre/nsIWinAppHelper.idl
--- a/toolkit/mozapps/update/src/nsUpdateService.js.in
+++ b/toolkit/mozapps/update/src/nsUpdateService.js.in
@@ -1494,32 +1494,80 @@ UpdateService.prototype = {
         upDirFile.create(nsILocalFile.NORMAL_FILE_TYPE, PERMS_FILE);
         upDirFile.remove(false);
       }
 #ifdef XP_WIN
       var sysInfo =
         Components.classes["@mozilla.org/system-info;1"]
                   .getService(Components.interfaces.nsIPropertyBag2);
 
+      // Example windowsVersion:  Windows XP == 5.1
+      var windowsVersion = sysInfo.getProperty("version");
+      LOG("UpdateService", "canUpdate?  windowsVersion = " + windowsVersion);
+
+      // For Vista, updates can be performed to a location requiring 
+      // admin privileges by requesting elevation via the UAC prompt when 
+      // launching updater.exe if the appDir is under the Program Files 
+      // directory (e.g. C:\Program Files\) and UAC is turned on and 
+      // we can elevate (e.g. user has a split token)
+      //
+      // Note: this does note attempt to handle the case where UAC is
+      // turned on and the installation directory is in a restricted
+      // location that requires admin privileges to update other than 
+      // Program Files.
+
+      var userCanElevate = false;
+
+      if (parseFloat(windowsVersion) >= 6) {
+        try {
+          var fileLocator = 
+            Components.classes["@mozilla.org/file/directory_service;1"]
+                      .getService(Components.interfaces.nsIProperties);
+          // KEY_UPDROOT will fail and throw an exception if
+          // appDir is not under the Program Files, so we rely on that
+          var dir = fileLocator.get(KEY_UPDROOT, Components.interfaces.nsIFile);
+          // appDir is under Program Files, so check if the user can elevate
+          userCanElevate = 
+            gApp.QueryInterface(Components.interfaces.nsIWinAppHelper)
+                .userCanElevate;
+          LOG("UpdateService", 
+              "canUpdate?  on Vista, userCanElevate = " + userCanElevate);
+        }
+        catch (ex) {
+          // When the installation directory is not under Program Files,
+          // fall through to checking if write access to the 
+          // installation directory is available.
+          LOG("UpdateService", 
+              "canUpdate?  on Vista, appDir is not under the Program Files");
+        }
+      }
+
       // On Windows, we no longer store the update under the app dir
       // if the app dir is under C:\Program Files.
       //
-      // If we are on Windows, but not Vista, we need to check that
+      // If we are on Windows (including Vista, if we can't elevate)
+      // we need to check that
       // we can create and remove files from the actual app directory
-      // (like C:\Program Files\Mozilla Firefox).  If we can't,
-      // because this user is not an adminstrator, for example
-      // canUpdate() should return false (like it used to).
+      // (like C:\Program Files\Mozilla Firefox).  If we can't
+      // (because this user is not an adminstrator, for example)
+      // canUpdate() should return false.
       //
-      // For Vista, don't perform this check because non-admin users
-      // can update firefox (by granting the updater access via the
-      // UAC prompt)
-      var windowsVersion = sysInfo.getProperty("version");
-      LOG("UpdateService", "canUpdate?  version = " + windowsVersion);
-      // Example windowsVersion:  Windows XP == 5.1
-      if (parseFloat(windowsVersion) < 6) {
+      // For Vista, we perform this check to enable updating the 
+      // application when the user has write access to the installation 
+      // directory under the following scenarios:
+      // 1) the installation directory is not under Program Files 
+      //    (e.g. C:\Program Files)
+      // 2) UAC is turned off
+      // 3) UAC is turned on and the user is not an admin 
+      //    (e.g. the user does not have a split token)
+      // 4) UAC is turned on and the user is already elevated,
+      //    so they can't be elevated again.
+      if (!userCanElevate) {
+        // if we're unable to create the test file
+        // the code below will throw an exception 
         var actualAppDir = getDir(KEY_APPDIR, []);
         var actualAppDirFile = actualAppDir.clone();
         actualAppDirFile.append(FILE_PERMS_TEST);
         LOG("UpdateService", "canUpdate?  testing " + actualAppDirFile.path);
         if (!actualAppDirFile.exists()) {
           actualAppDirFile.create(nsILocalFile.NORMAL_FILE_TYPE, PERMS_FILE);
           actualAppDirFile.remove(false);
         }
--- a/toolkit/xre/nsAppRunner.cpp
+++ b/toolkit/xre/nsAppRunner.cpp
@@ -750,16 +750,61 @@ nsXULAppInfo::PostUpdate(nsILocalFile *a
   rv = LaunchAppHelperWithArgs(upgradeArgc, upgradeArgv);
   
   if (pathArg)
     PR_smprintf_free(pathArg);
 
   free(upgradeArgv);
   return rv;
 }
+
+// Matches the enum in WinNT.h for the Vista SDK but renamed so that we can
+// safely build with the Vista SDK and without it.
+typedef enum 
+{
+  VistaTokenElevationTypeDefault = 1,
+  VistaTokenElevationTypeFull,
+  VistaTokenElevationTypeLimited
+} VISTA_TOKEN_ELEVATION_TYPE;
+
+// avoid collision with TokeElevationType enum in WinNT.h
+// of the Vista SDK
+#define VistaTokenElevationType static_cast< TOKEN_INFORMATION_CLASS >( 18 )
+
+NS_IMETHODIMP
+nsXULAppInfo::GetUserCanElevate(PRBool *aUserCanElevate)
+{
+  HANDLE hToken;
+
+  VISTA_TOKEN_ELEVATION_TYPE elevationType;
+  DWORD dwSize; 
+
+  if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken) ||
+      !GetTokenInformation(hToken, VistaTokenElevationType, &elevationType,
+                           sizeof(elevationType), &dwSize)) {
+    *aUserCanElevate = PR_FALSE;
+  } 
+  else {
+    // The possible values returned for elevationType and their meanings are:
+    //   TokenElevationTypeDefault: The token does not have a linked token 
+    //     (e.g. UAC disabled or a standard user, so they can't be elevated)
+    //   TokenElevationTypeFull: The token is linked to an elevated token 
+    //     (e.g. UAC is enabled and the user is already elevated so they can't
+    //      be elevated again)
+    //   TokenElevationTypeLimited: The token is linked to a limited token 
+    //     (e.g. UAC is enabled and the user is not elevated, so they can be
+    //	    elevated)
+    *aUserCanElevate = (elevationType == VistaTokenElevationTypeLimited);
+  }
+
+  if (hToken)
+    CloseHandle(hToken);
+
+  return NS_OK;
+}
 #endif
 
 #ifdef MOZ_CRASHREPORTER
 NS_IMETHODIMP
 nsXULAppInfo::AnnotateCrashReport(const nsACString& key,
                                   const nsACString& data)
 {
   return CrashReporter::AnnotateCrashReport(key, data);
--- a/toolkit/xre/nsIWinAppHelper.idl
+++ b/toolkit/xre/nsIWinAppHelper.idl
@@ -41,14 +41,15 @@
  * a special process that gets created with elevated privileges.
  *
  * @status UNSTABLE - This interface is not frozen and will probably change in
  *                    future releases.
  */
 
 interface nsILocalFile;
 
-[scriptable, uuid(575249a7-de7a-4602-a997-b7ad2b3b6dab)]
+[scriptable, uuid(b0fb682a-8287-4b0f-b628-65bb206c073f)]
 interface nsIWinAppHelper : nsISupports
 {
   void postUpdate(in nsILocalFile logFile);
   void fixReg();
+  readonly attribute boolean userCanElevate;
 };