diff --git a/toolkit/components/maintenanceservice/maintenanceservice.cpp b/toolkit/components/maintenanceservice/maintenanceservice.cpp index a4cd9b72822..d071e78119d 100644 --- a/toolkit/components/maintenanceservice/maintenanceservice.cpp +++ b/toolkit/components/maintenanceservice/maintenanceservice.cpp @@ -39,12 +39,12 @@ #include #include #include +#include #include "serviceinstall.h" #include "maintenanceservice.h" #include "servicebase.h" #include "workmonitor.h" -#include "shlobj.h" SERVICE_STATUS gSvcStatus = { 0 }; SERVICE_STATUS_HANDLE gSvcStatusHandle = NULL; diff --git a/toolkit/components/maintenanceservice/servicebase.cpp b/toolkit/components/maintenanceservice/servicebase.cpp index 05b92165d41..dbe0e87cea5 100644 --- a/toolkit/components/maintenanceservice/servicebase.cpp +++ b/toolkit/components/maintenanceservice/servicebase.cpp @@ -36,6 +36,84 @@ * ***** END LICENSE BLOCK ***** */ #include "servicebase.h" +#include "nsWindowsHelpers.h" // Shared code between applications and updater.exe #include "nsWindowsRestart.cpp" + +/** + * Verifies if 2 files are byte for byte equivalent. + * + * @param file1Path The first file to verify. + * @param file2Path The second file to verify. + * @param sameContent Out parameter, TRUE if the files are equal + * @return TRUE If there was no error checking the files. + */ +BOOL +VerifySameFiles(LPCWSTR file1Path, LPCWSTR file2Path, BOOL &sameContent) +{ + sameContent = FALSE; + nsAutoHandle file1(CreateFileW(file1Path, GENERIC_READ, FILE_SHARE_READ, + NULL, OPEN_EXISTING, 0, NULL)); + if (!file1) { + return FALSE; + } + nsAutoHandle file2(CreateFileW(file2Path, GENERIC_READ, FILE_SHARE_READ, + NULL, OPEN_EXISTING, 0, NULL)); + if (!file2) { + return FALSE; + } + + DWORD fileSize1 = GetFileSize(file1, NULL); + DWORD fileSize2 = GetFileSize(file2, NULL); + if (INVALID_FILE_SIZE == fileSize1 || INVALID_FILE_SIZE == fileSize2) { + return FALSE; + } + + if (fileSize1 != fileSize2) { + // sameContent is already set to FALSE + return TRUE; + } + + char buf1[COMPARE_BLOCKSIZE]; + char buf2[COMPARE_BLOCKSIZE]; + DWORD numBlocks = fileSize1 / COMPARE_BLOCKSIZE; + DWORD leftOver = fileSize1 % COMPARE_BLOCKSIZE; + DWORD readAmount; + for (DWORD i = 0; i < numBlocks; i++) { + if (!ReadFile(file1, buf1, COMPARE_BLOCKSIZE, &readAmount, NULL) || + readAmount != COMPARE_BLOCKSIZE) { + return FALSE; + } + + if (!ReadFile(file2, buf2, COMPARE_BLOCKSIZE, &readAmount, NULL) || + readAmount != COMPARE_BLOCKSIZE) { + return FALSE; + } + + if (memcmp(buf1, buf2, COMPARE_BLOCKSIZE)) { + // sameContent is already set to FALSE + return TRUE; + } + } + + if (leftOver) { + if (!ReadFile(file1, buf1, leftOver, &readAmount, NULL) || + readAmount != leftOver) { + return FALSE; + } + + if (!ReadFile(file2, buf2, leftOver, &readAmount, NULL) || + readAmount != leftOver) { + return FALSE; + } + + if (memcmp(buf1, buf2, leftOver)) { + // sameContent is already set to FALSE + return TRUE; + } + } + + sameContent = TRUE; + return TRUE; +} diff --git a/toolkit/components/maintenanceservice/servicebase.h b/toolkit/components/maintenanceservice/servicebase.h index d2e9453fffe..f837b183bd3 100644 --- a/toolkit/components/maintenanceservice/servicebase.h +++ b/toolkit/components/maintenanceservice/servicebase.h @@ -39,3 +39,9 @@ #include "updatelogging.h" BOOL PathAppendSafe(LPWSTR base, LPCWSTR extra); +BOOL VerifySameFiles(LPCWSTR file1Path, LPCWSTR file2Path, BOOL &sameContent); + +// 32KiB for comparing files at a time seems reasonable. +// The bigger the better for speed, but this will be used +// on the stack so I don't want it to be too big. +#define COMPARE_BLOCKSIZE 32768 diff --git a/toolkit/components/maintenanceservice/workmonitor.cpp b/toolkit/components/maintenanceservice/workmonitor.cpp index 3871c69f3e0..90237a575fb 100644 --- a/toolkit/components/maintenanceservice/workmonitor.cpp +++ b/toolkit/components/maintenanceservice/workmonitor.cpp @@ -71,6 +71,7 @@ BOOL PathGetSiblingFilePath(LPWSTR destinationBuffer, LPCWSTR siblingFilePath, const int SERVICE_UPDATER_COULD_NOT_BE_STARTED = 16000; const int SERVICE_NOT_ENOUGH_COMMAND_LINE_ARGS = 16001; const int SERVICE_UPDATER_SIGN_ERROR = 16002; +const int SERVICE_UPDATER_COMPARE_ERROR = 16003; /** * Runs an update process as the service using the SYSTEM account. @@ -342,6 +343,41 @@ ProcessWorkItem(LPCWSTR monitoringBasePath, int argcTmp = 0; LPWSTR* argvTmp = CommandLineToArgvW(cmdlineBufferWide, &argcTmp); + // 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 argvTmp[2]. + WCHAR installDirUpdater[MAX_PATH + 1]; + wcsncpy(installDirUpdater, argvTmp[2], MAX_PATH); + if (!PathAppendSafe(installDirUpdater, L"updater.exe")) { + LOG(("Install directory updater could not be determined.\n")); + result = FALSE; + } + + BOOL updaterIsCorrect; + if (result && !VerifySameFiles(updaterPath, installDirUpdater, + updaterIsCorrect)) { + LOG(("Error checking if the updaters are the same.\n")); + result = FALSE; + } + + if (result && !updaterIsCorrect) { + LOG(("The updaters do not match, udpater will not run.\n")); + result = FALSE; + } + + if (result) { + LOG(("updater.exe was compared successfully to the installation directory" + " updater.exe.\n")); + } else { + SetEvent(serviceRunningEvent); + if (argcTmp < 2 || + !WriteStatusFailure(argvTmp[1], + SERVICE_UPDATER_COMPARE_ERROR)) { + LOG(("Could not write update.status updater compare failure.\n")); + } + return TRUE; + } + // Check for updater.exe sign problems BOOL updaterSignProblem = FALSE; #ifndef DISABLE_UPDATER_AUTHENTICODE_CHECK diff --git a/toolkit/mozapps/update/nsUpdateService.js b/toolkit/mozapps/update/nsUpdateService.js index 11af1527748..24937b3a768 100644 --- a/toolkit/mozapps/update/nsUpdateService.js +++ b/toolkit/mozapps/update/nsUpdateService.js @@ -146,6 +146,7 @@ const ELEVATION_CANCELED = 9; const SERVICE_UPDATER_COULD_NOT_BE_STARTED = 16000; const SERVICE_NOT_ENOUGH_COMMAND_LINE_ARGS = 16001; const SERVICE_UPDATER_SIGN_ERROR = 16002; +const SERVICE_UPDATER_COMPARE_ERROR = 16003; const CERT_ATTR_CHECK_FAILED_NO_UPDATE = 100; const CERT_ATTR_CHECK_FAILED_HAS_UPDATE = 101; @@ -1435,7 +1436,8 @@ 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_SIGN_ERROR || + update.errorCode == SERVICE_UPDATER_COMPARE_ERROR) { var failCount = getPref("getIntPref", PREF_APP_UPDATE_SERVICE_ERRORS, 0); var maxFail = getPref("getIntPref",