Bug 1205843 - Part 2: Disable maintenance service on WinXP < SP3
authorMatt Howell <mhowell@mozilla.com>
Tue, 27 Oct 2015 15:17:07 -0700
changeset 491486 e2ffa9afadd208a89eca91aa4339d33954525af8
parent 491485 805c01131911b415dbe6dcb17ceb2d423e167e15
child 491487 554128f7e7de86fc65c4c151344d46c2770eaf40
push id47343
push userbmo:dothayer@mozilla.com
push dateWed, 01 Mar 2017 22:58:58 +0000
bugs1205843
milestone44.0a1
Bug 1205843 - Part 2: Disable maintenance service on WinXP < SP3
browser/installer/windows/nsis/installer.nsi
browser/installer/windows/nsis/stub.nsi
toolkit/mozapps/update/nsUpdateService.js
--- a/browser/installer/windows/nsis/installer.nsi
+++ b/browser/installer/windows/nsis/installer.nsi
@@ -435,22 +435,26 @@ Section "-Application" APP_IDX
   ; If the maintenance service page was not displayed then 
   ; InstallMaintenanceService will be equal to "".
   ${If} $InstallMaintenanceService == ""
     Call IsUserAdmin
     Pop $R0
     ${If} $R0 == "true"
     ; Only proceed if we have HKLM write access
     ${AndIf} $TmpVal == "HKLM"
-    ; On Windows 2000 we do not install the maintenance service.
-    ${AndIf} ${AtLeastWinXP}
-      ; The user is an admin so we should default to install service yes
-      StrCpy $InstallMaintenanceService "1"
+      ; On Windows < XP SP3 we do not install the maintenance service.
+      ${If} ${IsWinXP}
+      ${AndIf} ${AtMostServicePack} 2
+        StrCpy $InstallMaintenanceService "0"
+      ${Else}
+        ; The user is an admin, so we should default to installing the service.
+        StrCpy $InstallMaintenanceService "1"
+      ${EndIf}
     ${Else}
-      ; The user is not admin so we should default to install service no
+      ; The user is not admin, so we can't install the service.
       StrCpy $InstallMaintenanceService "0"
     ${EndIf}
   ${EndIf}
 
   ${If} $InstallMaintenanceService == "1"
     ; The user wants to install the maintenance service, so execute
     ; the pre-packaged maintenance service installer. 
     ; This option can only be turned on if the user is an admin so there
@@ -911,20 +915,21 @@ Function preComponents
   ; If the service already exists, don't show this page
   ServicesHelper::IsInstalled "MozillaMaintenance"
   Pop $R9
   ${If} $R9 == 1
     ; The service already exists so don't show this page.
     Abort
   ${EndIf}
 
-  ; On Windows 2000 we do not install the maintenance service.
-  ${Unless} ${AtLeastWinXP}
+  ; On Windows < XP SP3 we do not install the maintenance service.
+  ${If} ${IsWinXP}
+  ${AndIf} ${AtMostServicePack} 2
     Abort
-  ${EndUnless}
+  ${EndIf}
 
   ; Don't show the custom components page if the
   ; user is not an admin
   Call IsUserAdmin
   Pop $R9
   ${If} $R9 != "true"
     Abort
   ${EndIf}
--- a/browser/installer/windows/nsis/stub.nsi
+++ b/browser/installer/windows/nsis/stub.nsi
@@ -1016,19 +1016,29 @@ Function createOptions
   ; The uxtheme must be disabled on checkboxes in order to override the system
   ; font color.
   System::Call 'uxtheme::SetWindowTheme(i $CheckboxSendPing, w " ", w " ")'
   SetCtlColors $CheckboxSendPing ${COMMON_TEXT_COLOR_NORMAL} ${COMMON_BKGRD_COLOR}
   SendMessage $CheckboxSendPing ${WM_SETFONT} $FontNormal 0
   ${NSD_Check} $CheckboxSendPing
 
 !ifdef MOZ_MAINTENANCE_SERVICE
-  ; Only show the maintenance service checkbox if we have write access to HKLM
+  ; We can only install the maintenance service if the user is an admin.
   Call IsUserAdmin
   Pop $0
+  
+  ; Only show the maintenance service checkbox if we're on XP SP3 or higher;
+  ;  we don't ever want to install it on XP without at least SP3 installed.
+  ${If} $0 == "true"
+  ${AndIf} ${IsWinXP}
+  ${AndIf} ${AtMostServicePack} 2
+    StrCpy $0 "false"
+  ${EndIf}
+  
+  ; Only show the maintenance service checkbox if we have write access to HKLM
   ClearErrors
   WriteRegStr HKLM "Software\Mozilla" "${BrandShortName}InstallerTest" \
                    "Write Test"
   ${If} ${Errors}
   ${OrIf} $0 != "true"
     StrCpy $CheckboxInstallMaintSvc "0"
   ${Else}
     DeleteRegValue HKLM "Software\Mozilla" "${BrandShortName}InstallerTest"
--- a/toolkit/mozapps/update/nsUpdateService.js
+++ b/toolkit/mozapps/update/nsUpdateService.js
@@ -866,20 +866,96 @@ function releaseSDCardMountLock() {
 
 /**
  * Determines if the service should be used to attempt an update
  * or not.
  *
  * @return  true if the service should be used for updates.
  */
 function shouldUseService() {
-  if (AppConstants.MOZ_MAINTENANCE_SERVICE && isServiceInstalled()) {
-    return getPref("getBoolPref",
-                   PREF_APP_UPDATE_SERVICE_ENABLED, false);
+  // This function will return true if the mantenance service should be used if
+  // all of the following conditions are met:
+  // 1) This build was done with the maintenance service enabled
+  // 2) The maintenance service is installed
+  // 3) The pref for using the service is enabled
+  // 4) The Windows version is XP Service Pack 3 or above (for SHA-2 support)
+  // The maintenance service requires SHA-2 support because we sign our binaries
+  // with a SHA-2 certificate and the certificate is verified before the binary
+  // is launched.
+  if (!AppConstants.MOZ_MAINTENANCE_SERVICE || !isServiceInstalled() ||
+      !getPref("getBoolPref", PREF_APP_UPDATE_SERVICE_ENABLED, false) ||
+      !AppConstants.isPlatformAndVersionAtLeast("win", "5.1") /* WinXP */) {
+    return false;
+  }
+
+  // If it's newer than XP, then the service pack doesn't matter.
+  if (Services.sysinfo.getProperty("version") != "5.1") {
+    return true;
   }
+
+  // If the Windows version is XP, we also need to check the service pack.
+  // We'll return false if only < SP3 is installed, or if we can't tell.
+  // Check the service pack level by calling GetVersionEx via ctypes.
+  const BYTE = ctypes.uint8_t;
+  const WORD = ctypes.uint16_t;
+  const DWORD = ctypes.uint32_t;
+  const WCHAR = ctypes.char16_t;
+  const BOOL = ctypes.int;
+  // This structure is described at:
+  // http://msdn.microsoft.com/en-us/library/ms724833%28v=vs.85%29.aspx
+  const SZCSDVERSIONLENGTH = 128;
+  const OSVERSIONINFOEXW = new ctypes.StructType('OSVERSIONINFOEXW',
+  [
+    {dwOSVersionInfoSize: DWORD},
+    {dwMajorVersion: DWORD},
+    {dwMinorVersion: DWORD},
+    {dwBuildNumber: DWORD},
+    {dwPlatformId: DWORD},
+    {szCSDVersion: ctypes.ArrayType(WCHAR, SZCSDVERSIONLENGTH)},
+    {wServicePackMajor: WORD},
+    {wServicePackMinor: WORD},
+    {wSuiteMask: WORD},
+    {wProductType: BYTE},
+    {wReserved: BYTE}
+  ]);
+
+  let kernel32 = false;
+  try {
+    kernel32 = ctypes.open("Kernel32");
+  } catch (e) {
+    Cu.reportError("Unable to open kernel32! " + e);
+    return false;
+  }
+
+  if (kernel32) {
+    try {
+      try {
+        let GetVersionEx = kernel32.declare("GetVersionExW",
+                                            ctypes.default_abi,
+                                            BOOL,
+                                            OSVERSIONINFOEXW.ptr);
+        let winVer = OSVERSIONINFOEXW();
+        winVer.dwOSVersionInfoSize = OSVERSIONINFOEXW.size;
+
+        if (0 !== GetVersionEx(winVer.address())) {
+          return winVer.wServicePackMajor >= 3;
+        } else {
+          Cu.reportError("Unknown failure in GetVersionEX (returned 0)");
+          return false;
+        }
+      } catch (e) {
+        Cu.reportError("Error getting service pack information. Exception: " + e);
+        return false;
+      }
+    } finally {
+      kernel32.close();
+    }
+  }
+
+  // If the service pack check couldn't be done, assume we can't use the service.
   return false;
 }
 
 /**
  * Determines if the service is is installed.
  *
  * @return  true if the service is installed.
  */