Bug 1247239 - Allow the maintenance service to copy updater.exe only from the installation directory, and verify it first; r=rstrong

MozReview-Commit-ID: 68tbdamvFf0

--HG--
extra : rebase_source : 3523dd7e04ada5246cdc275ce704ede39103d8c8
This commit is contained in:
Matt Howell 2016-07-08 10:58:21 -07:00
Родитель 84798b84de
Коммит f80a68c7d5
1 изменённых файлов: 144 добавлений и 120 удалений

Просмотреть файл

@ -30,7 +30,7 @@
static const int TIME_TO_WAIT_ON_UPDATER = 15 * 60 * 1000;
wchar_t* MakeCommandLine(int argc, wchar_t** argv);
BOOL WriteStatusFailure(LPCWSTR updateDirPath, int errorCode);
BOOL PathGetSiblingFilePath(LPWSTR destinationBuffer, LPCWSTR siblingFilePath,
BOOL PathGetSiblingFilePath(LPWSTR destinationBuffer, LPCWSTR siblingFilePath,
LPCWSTR newFileName);
/*
@ -287,6 +287,117 @@ StartUpdateProcess(int argc,
return updateWasSuccessful;
}
/**
* Validates a file as an official updater.
*
* @param updater Path to the updater to validate
* @param installDir Path to the application installation
* being updated
* @param updateDir Update applyTo direcotry,
* where logs will be written
*
* @return true if updater is the path to a valid updater
*/
static bool
UpdaterIsValid(LPWSTR updater, LPWSTR installDir, LPWSTR updateDir)
{
// 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)) {
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)) {
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
// as the one in the installation directory which we are updating.
// The installation dir that we are installing to is installDir.
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."));
return false;
}
BOOL updaterIsCorrect;
if (!VerifySameFiles(updater, installDirUpdater, updaterIsCorrect)) {
LOG_WARN(("Error checking if the updaters are the same.\n"
"Path 1: %ls\nPath 2: %ls", updater, installDirUpdater));
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)) {
LOG_WARN(("Could not write update.status updater compare failure."));
}
return false;
}
LOG(("updater.exe was compared successfully to the installation directory"
" updater.exe."));
// Check to make sure the updater.exe module has the unique updater identity.
// This is a security measure to make sure that the signed executable that
// we will run is actually an updater.
bool result = true;
HMODULE updaterModule = LoadLibraryEx(updater, nullptr,
LOAD_LIBRARY_AS_DATAFILE);
if (!updaterModule) {
LOG_WARN(("updater.exe module could not be loaded. (%d)", GetLastError()));
result = false;
} else {
char updaterIdentity[64];
if (!LoadStringA(updaterModule, IDS_UPDATER_IDENTITY,
updaterIdentity, sizeof(updaterIdentity))) {
LOG_WARN(("The updater.exe application does not contain the Mozilla"
" updater identity."));
result = false;
}
if (strcmp(updaterIdentity, UPDATER_IDENTITY_STRING)) {
LOG_WARN(("The updater.exe identity string is not valid."));
result = false;
}
FreeLibrary(updaterModule);
}
if (result) {
LOG(("The updater.exe application contains the Mozilla"
" updater identity."));
} else {
if (!WriteStatusFailure(updateDir, SERVICE_UPDATER_IDENTITY_ERROR)) {
LOG_WARN(("Could not write update.status no updater identity."));
}
return false;
}
#ifndef DISABLE_UPDATER_AUTHENTICODE_CHECK
return DoesBinaryMatchAllowedCertificates(installDir, updater);
#else
return true;
#endif
}
/**
* Processes a software update command
*
@ -324,113 +435,7 @@ ProcessSoftwareUpdateCommand(DWORD argc, LPWSTR *argv)
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_WARN(("Filesystem in path %ls is not supported (%d)",
argv[0], GetLastError()));
if (!WriteStatusFailure(argv[1],
SERVICE_UPDATER_NOT_FIXED_DRIVE)) {
LOG_WARN(("Could not write update.status service update failure. (%d)",
GetLastError()));
}
return FALSE;
}
nsAutoHandle noWriteLock(CreateFileW(argv[0], 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(argv[1],
SERVICE_COULD_NOT_LOCK_UPDATER)) {
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
// as the one in the installation directory which we are updating.
// The installation dir that we are installing to is installDir.
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;
}
BOOL updaterIsCorrect;
if (result && !VerifySameFiles(argv[0], installDirUpdater,
updaterIsCorrect)) {
LOG_WARN(("Error checking if the updaters are the same.\n"
"Path 1: %ls\nPath 2: %ls", argv[0], installDirUpdater));
result = FALSE;
}
if (result && !updaterIsCorrect) {
LOG_WARN(("The updaters do not match, updater will not run.\n"
"Path 1: %ls\nPath 2: %ls", argv[0], installDirUpdater));
result = FALSE;
}
if (result) {
LOG(("updater.exe was compared successfully to the installation directory"
" updater.exe."));
} else {
if (!WriteStatusFailure(argv[1],
SERVICE_UPDATER_COMPARE_ERROR)) {
LOG_WARN(("Could not write update.status updater compare failure."));
}
return FALSE;
}
// Check to make sure the updater.exe module has the unique updater identity.
// This is a security measure to make sure that the signed executable that
// we will run is actually an updater.
HMODULE updaterModule = LoadLibraryEx(argv[0], nullptr,
LOAD_LIBRARY_AS_DATAFILE);
if (!updaterModule) {
LOG_WARN(("updater.exe module could not be loaded. (%d)", GetLastError()));
result = FALSE;
} else {
char updaterIdentity[64];
if (!LoadStringA(updaterModule, IDS_UPDATER_IDENTITY,
updaterIdentity, sizeof(updaterIdentity))) {
LOG_WARN(("The updater.exe application does not contain the Mozilla"
" updater identity."));
result = FALSE;
}
if (strcmp(updaterIdentity, UPDATER_IDENTITY_STRING)) {
LOG_WARN(("The updater.exe identity string is not valid."));
result = FALSE;
}
FreeLibrary(updaterModule);
}
if (result) {
LOG(("The updater.exe application contains the Mozilla"
" updater identity."));
} else {
if (!WriteStatusFailure(argv[1],
SERVICE_UPDATER_IDENTITY_ERROR)) {
LOG_WARN(("Could not write update.status no updater identity."));
}
return TRUE;
}
// Check for updater.exe sign problems
BOOL updaterSignProblem = FALSE;
#ifndef DISABLE_UPDATER_AUTHENTICODE_CHECK
updaterSignProblem = !DoesBinaryMatchAllowedCertificates(installDir,
argv[0]);
#endif
// Only proceed with the update if we have no signing problems
if (!updaterSignProblem) {
if (UpdaterIsValid(argv[0], installDir, argv[1])) {
BOOL updateProcessWasStarted = FALSE;
if (StartUpdateProcess(argc, argv, installDir,
updateProcessWasStarted)) {
@ -584,20 +589,39 @@ ExecuteServiceCommand(int argc, LPWSTR *argv)
BOOL result = FALSE;
if (!lstrcmpi(argv[2], L"software-update")) {
// Use the passed in command line arguments for the update, except for the
// path to updater.exe. We copy updater.exe to a the directory of the
// MozillaMaintenance service so that a low integrity process cannot
// 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 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.
LPWSTR oldUpdaterPath = argv[3];
WCHAR installDir[MAX_PATH + 1] = { L'\0' };
if (!GetInstallationDir(argc - 3, argv + 3, installDir)) {
LOG_WARN(("Could not get the installation directory"));
if (!WriteStatusFailure(argv[1],
SERVICE_INSTALLDIR_ERROR)) {
LOG_WARN(("Could not write update.status for GetInstallationDir failure."));
}
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;
}
result = UpdaterIsValid(installDirUpdater, installDir, argv[5]);
WCHAR secureUpdaterPath[MAX_PATH + 1] = { L'\0' };
result = GetSecureUpdaterPath(secureUpdaterPath); // Does its own logging
if (result) {
result = GetSecureUpdaterPath(secureUpdaterPath); // Does its own logging
}
if (result) {
LOG(("Passed in path: '%ls'; Using this path for updating: '%ls'.",
oldUpdaterPath, secureUpdaterPath));
installDirUpdater, secureUpdaterPath));
DeleteSecureUpdater(secureUpdaterPath);
result = CopyFileW(oldUpdaterPath, secureUpdaterPath, FALSE);
result = CopyFileW(installDirUpdater, secureUpdaterPath, FALSE);
}
if (!result) {
@ -613,16 +637,16 @@ ExecuteServiceCommand(int argc, LPWSTR *argv)
// use for the service update.
argv[3] = secureUpdaterPath;
WCHAR oldUpdaterINIPath[MAX_PATH + 1] = { L'\0' };
WCHAR installDirUpdaterINIPath[MAX_PATH + 1] = { L'\0' };
WCHAR secureUpdaterINIPath[MAX_PATH + 1] = { L'\0' };
if (PathGetSiblingFilePath(secureUpdaterINIPath, secureUpdaterPath,
L"updater.ini") &&
PathGetSiblingFilePath(oldUpdaterINIPath, oldUpdaterPath,
PathGetSiblingFilePath(installDirUpdaterINIPath, installDirUpdater,
L"updater.ini")) {
// This is non fatal if it fails there is no real harm
if (!CopyFileW(oldUpdaterINIPath, secureUpdaterINIPath, FALSE)) {
if (!CopyFileW(installDirUpdaterINIPath, secureUpdaterINIPath, FALSE)) {
LOG_WARN(("Could not copy updater.ini from: '%ls' to '%ls'. (%d)",
oldUpdaterINIPath, secureUpdaterINIPath, GetLastError()));
installDirUpdaterINIPath, secureUpdaterINIPath, GetLastError()));
}
}