toolkit/components/maintenanceservice/workmonitor.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

     1 /* This Source Code Form is subject to the terms of the Mozilla Public
     2  * License, v. 2.0. If a copy of the MPL was not distributed with this
     3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     5 #include <shlobj.h>
     6 #include <shlwapi.h>
     7 #include <wtsapi32.h>
     8 #include <userenv.h>
     9 #include <shellapi.h>
    11 #pragma comment(lib, "wtsapi32.lib")
    12 #pragma comment(lib, "userenv.lib")
    13 #pragma comment(lib, "shlwapi.lib")
    14 #pragma comment(lib, "ole32.lib")
    15 #pragma comment(lib, "rpcrt4.lib")
    17 #include "nsWindowsHelpers.h"
    19 #include "workmonitor.h"
    20 #include "serviceinstall.h"
    21 #include "servicebase.h"
    22 #include "registrycertificates.h"
    23 #include "uachelper.h"
    24 #include "updatehelper.h"
    25 #include "errors.h"
    27 // Wait 15 minutes for an update operation to run at most.
    28 // Updates usually take less than a minute so this seems like a 
    29 // significantly large and safe amount of time to wait.
    30 static const int TIME_TO_WAIT_ON_UPDATER = 15 * 60 * 1000;
    31 char16_t* MakeCommandLine(int argc, char16_t **argv);
    32 BOOL WriteStatusFailure(LPCWSTR updateDirPath, int errorCode);
    33 BOOL PathGetSiblingFilePath(LPWSTR destinationBuffer,  LPCWSTR siblingFilePath, 
    34                             LPCWSTR newFileName);
    36 /* 
    37  * Read the update.status file and sets isApplying to true if
    38  * the status is set to applying
    39  *
    40  * @param  updateDirPath The directory where update.status is stored
    41  * @param  isApplying Out parameter for specifying if the status
    42  *         is set to applying or not.
    43  * @return TRUE if the information was filled.
    44 */
    45 static BOOL
    46 IsStatusApplying(LPCWSTR updateDirPath, BOOL &isApplying)
    47 {
    48   isApplying = FALSE;
    49   WCHAR updateStatusFilePath[MAX_PATH + 1] = {L'\0'};
    50   wcsncpy(updateStatusFilePath, updateDirPath, MAX_PATH);
    51   if (!PathAppendSafe(updateStatusFilePath, L"update.status")) {
    52     LOG_WARN(("Could not append path for update.status file"));
    53     return FALSE;
    54   }
    56   nsAutoHandle statusFile(CreateFileW(updateStatusFilePath, GENERIC_READ,
    57                                       FILE_SHARE_READ | 
    58                                       FILE_SHARE_WRITE | 
    59                                       FILE_SHARE_DELETE,
    60                                       nullptr, OPEN_EXISTING, 0, nullptr));
    62   if (INVALID_HANDLE_VALUE == statusFile) {
    63     LOG_WARN(("Could not open update.status file"));
    64     return FALSE;
    65   }
    67   char buf[32] = { 0 };
    68   DWORD read;
    69   if (!ReadFile(statusFile, buf, sizeof(buf), &read, nullptr)) {
    70     LOG_WARN(("Could not read from update.status file"));
    71     return FALSE;
    72   }
    74   LOG(("updater.exe returned status: %s", buf));
    76   const char kApplying[] = "applying";
    77   isApplying = strncmp(buf, kApplying, 
    78                        sizeof(kApplying) - 1) == 0;
    79   return TRUE;
    80 }
    82 /**
    83  * Determines whether we're staging an update.
    84  *
    85  * @param argc    The argc value normally sent to updater.exe
    86  * @param argv    The argv value normally sent to updater.exe
    87  * @param boolean True if we're staging an update
    88  */
    89 static bool
    90 IsUpdateBeingStaged(int argc, LPWSTR *argv)
    91 {
    92   // PID will be set to -1 if we're supposed to stage an update.
    93   return argc == 4 && !wcscmp(argv[3], L"-1");
    94 }
    96 /**
    97  * Gets the installation directory from the arguments passed to updater.exe.
    98  *
    99  * @param argcTmp    The argc value normally sent to updater.exe
   100  * @param argvTmp    The argv value normally sent to updater.exe
   101  * @param aResultDir Buffer to hold the installation directory.
   102  */
   103 static BOOL
   104 GetInstallationDir(int argcTmp, LPWSTR *argvTmp, WCHAR aResultDir[MAX_PATH + 1])
   105 {
   106   if (argcTmp < 2) {
   107     return FALSE;
   108   }
   109   wcsncpy(aResultDir, argvTmp[2], MAX_PATH);
   110   WCHAR* backSlash = wcsrchr(aResultDir, L'\\');
   111   // Make sure that the path does not include trailing backslashes
   112   if (backSlash && (backSlash[1] == L'\0')) {
   113     *backSlash = L'\0';
   114   }
   115   bool backgroundUpdate = IsUpdateBeingStaged(argcTmp, argvTmp);
   116   bool replaceRequest = (argcTmp >= 4 && wcsstr(argvTmp[3], L"/replace"));
   117   if (backgroundUpdate || replaceRequest) {
   118     return PathRemoveFileSpecW(aResultDir);
   119   }
   120   return TRUE;
   121 }
   123 /**
   124  * Runs an update process as the service using the SYSTEM account.
   125  *
   126  * @param  argc           The number of arguments in argv
   127  * @param  argv           The arguments normally passed to updater.exe
   128  *                        argv[0] must be the path to updater.exe
   129  * @param  processStarted Set to TRUE if the process was started.
   130  * @return TRUE if the update process was run had a return code of 0.
   131  */
   132 BOOL
   133 StartUpdateProcess(int argc,
   134                    LPWSTR *argv,
   135                    LPCWSTR installDir,
   136                    BOOL &processStarted)
   137 {
   138   LOG(("Starting update process as the service in session 0."));
   139   STARTUPINFO si = {0};
   140   si.cb = sizeof(STARTUPINFO);
   141   si.lpDesktop = L"winsta0\\Default";
   142   PROCESS_INFORMATION pi = {0};
   144   // The updater command line is of the form:
   145   // updater.exe update-dir apply [wait-pid [callback-dir callback-path args]]
   146   LPWSTR cmdLine = MakeCommandLine(argc, argv);
   148   // If we're about to start the update process from session 0,
   149   // then we should not show a GUI.  This only really needs to be done
   150   // on Vista and higher, but it's better to keep everything consistent
   151   // across all OS if it's of no harm.
   152   if (argc >= 2 ) {
   153     // Setting the desktop to blank will ensure no GUI is displayed
   154     si.lpDesktop = L"";
   155     si.dwFlags |= STARTF_USESHOWWINDOW;
   156     si.wShowWindow = SW_HIDE;
   157   }
   159   // We move the updater.ini file out of the way because we will handle 
   160   // executing PostUpdate through the service.  We handle PostUpdate from
   161   // the service because there are some per user things that happen that
   162   // can't run in session 0 which we run updater.exe in.
   163   // Once we are done running updater.exe we rename updater.ini back so
   164   // that if there were any errors the next updater.exe will run correctly.
   165   WCHAR updaterINI[MAX_PATH + 1];
   166   WCHAR updaterINITemp[MAX_PATH + 1];
   167   BOOL selfHandlePostUpdate = FALSE;
   168   // We use the updater.ini from the same directory as the updater.exe
   169   // because of background updates.
   170   if (PathGetSiblingFilePath(updaterINI, argv[0], L"updater.ini") &&
   171       PathGetSiblingFilePath(updaterINITemp, argv[0], L"updater.tmp")) {
   172     selfHandlePostUpdate = MoveFileExW(updaterINI, updaterINITemp, 
   173                                        MOVEFILE_REPLACE_EXISTING);
   174   }
   176   // Add an env var for MOZ_USING_SERVICE so the updater.exe can
   177   // do anything special that it needs to do for service updates.
   178   // Search in updater.cpp for more info on MOZ_USING_SERVICE.
   179   putenv(const_cast<char*>("MOZ_USING_SERVICE=1"));
   180   LOG(("Starting service with cmdline: %ls", cmdLine));
   181   processStarted = CreateProcessW(argv[0], cmdLine, 
   182                                   nullptr, nullptr, FALSE, 
   183                                   CREATE_DEFAULT_ERROR_MODE, 
   184                                   nullptr, 
   185                                   nullptr, &si, &pi);
   186   // Empty value on putenv is how you remove an env variable in Windows
   187   putenv(const_cast<char*>("MOZ_USING_SERVICE="));
   189   BOOL updateWasSuccessful = FALSE;
   190   if (processStarted) {
   191     // Wait for the updater process to finish
   192     LOG(("Process was started... waiting on result."));
   193     DWORD waitRes = WaitForSingleObject(pi.hProcess, TIME_TO_WAIT_ON_UPDATER);
   194     if (WAIT_TIMEOUT == waitRes) {
   195       // We waited a long period of time for updater.exe and it never finished
   196       // so kill it.
   197       TerminateProcess(pi.hProcess, 1);
   198     } else {
   199       // Check the return code of updater.exe to make sure we get 0
   200       DWORD returnCode;
   201       if (GetExitCodeProcess(pi.hProcess, &returnCode)) {
   202         LOG(("Process finished with return code %d.", returnCode));
   203         // updater returns 0 if successful.
   204         updateWasSuccessful = (returnCode == 0);
   205       } else {
   206         LOG_WARN(("Process finished but could not obtain return code."));
   207       }
   208     }
   209     CloseHandle(pi.hProcess);
   210     CloseHandle(pi.hThread);
   212     // Check just in case updater.exe didn't change the status from
   213     // applying.  If this is the case we report an error.
   214     BOOL isApplying = FALSE;
   215     if (IsStatusApplying(argv[1], isApplying) && isApplying) {
   216       if (updateWasSuccessful) {
   217         LOG(("update.status is still applying even know update "
   218              " was successful."));
   219         if (!WriteStatusFailure(argv[1], 
   220                                 SERVICE_STILL_APPLYING_ON_SUCCESS)) {
   221           LOG_WARN(("Could not write update.status still applying on"
   222                     " success error."));
   223         }
   224         // Since we still had applying we know updater.exe didn't do its
   225         // job correctly.
   226         updateWasSuccessful = FALSE;
   227       } else {
   228         LOG_WARN(("update.status is still applying and update was not successful."));
   229         if (!WriteStatusFailure(argv[1], 
   230                                 SERVICE_STILL_APPLYING_ON_FAILURE)) {
   231           LOG_WARN(("Could not write update.status still applying on"
   232                     " success error."));
   233         }
   234       }
   235     }
   236   } else {
   237     DWORD lastError = GetLastError();
   238     LOG_WARN(("Could not create process as current user, "
   239               "updaterPath: %ls; cmdLine: %ls.  (%d)",
   240               argv[0], cmdLine, lastError));
   241   }
   243   // Now that we're done with the update, restore back the updater.ini file
   244   // We use it ourselves, and also we want it back in case we had any type 
   245   // of error so that the normal update process can use it.
   246   if (selfHandlePostUpdate) {
   247     MoveFileExW(updaterINITemp, updaterINI, MOVEFILE_REPLACE_EXISTING);
   249     // Only run the PostUpdate if the update was successful
   250     if (updateWasSuccessful && argc > 2) {
   251       LPCWSTR updateInfoDir = argv[1];
   252       bool backgroundUpdate = IsUpdateBeingStaged(argc, argv);
   254       // Launch the PostProcess with admin access in session 0.  This is
   255       // actually launching the post update process but it takes in the 
   256       // callback app path to figure out where to apply to.
   257       // The PostUpdate process with user only access will be done inside
   258       // the unelevated updater.exe after the update process is complete
   259       // from the service.  We don't know here which session to start
   260       // the user PostUpdate process from.
   261       // Note that we don't need to do this if we're just staging the
   262       // update in the background, as the PostUpdate step runs when
   263       // performing the replacing in that case.
   264       if (!backgroundUpdate) {
   265         LOG(("Launching post update process as the service in session 0."));
   266         if (!LaunchWinPostProcess(installDir, updateInfoDir, true, nullptr)) {
   267           LOG_WARN(("The post update process could not be launched."
   268                     " installDir: %ls, updateInfoDir: %ls",
   269                     installDir, updateInfoDir));
   270         }
   271       }
   272     }
   273   }
   275   free(cmdLine);
   276   return updateWasSuccessful;
   277 }
   279 /**
   280  * Processes a software update command
   281  *
   282  * @param  argc           The number of arguments in argv
   283  * @param  argv           The arguments normally passed to updater.exe
   284  *                        argv[0] must be the path to updater.exe
   285  * @return TRUE if the update was successful.
   286  */
   287 BOOL
   288 ProcessSoftwareUpdateCommand(DWORD argc, LPWSTR *argv)
   289 {
   290   BOOL result = TRUE;
   291   if (argc < 3) {
   292     LOG_WARN(("Not enough command line parameters specified. "
   293               "Updating update.status."));
   295     // We can only update update.status if argv[1] exists.  argv[1] is
   296     // the directory where the update.status file exists.
   297     if (argc < 2 || 
   298         !WriteStatusFailure(argv[1], 
   299                             SERVICE_NOT_ENOUGH_COMMAND_LINE_ARGS)) {
   300       LOG_WARN(("Could not write update.status service update failure.  (%d)",
   301                 GetLastError()));
   302     }
   303     return FALSE;
   304   }
   306   WCHAR installDir[MAX_PATH + 1] = {L'\0'};
   307   if (!GetInstallationDir(argc, argv, installDir)) {
   308     LOG_WARN(("Could not get the installation directory"));
   309     if (!WriteStatusFailure(argv[1],
   310                             SERVICE_INSTALLDIR_ERROR)) {
   311       LOG_WARN(("Could not write update.status for GetInstallationDir failure."));
   312     }
   313     return FALSE;
   314   }
   316   // Make sure the path to the updater to use for the update is local.
   317   // We do this check to make sure that file locking is available for
   318   // race condition security checks.
   319   BOOL isLocal = FALSE;
   320   if (!IsLocalFile(argv[0], isLocal) || !isLocal) {
   321     LOG_WARN(("Filesystem in path %ls is not supported (%d)",
   322               argv[0], GetLastError()));
   323     if (!WriteStatusFailure(argv[1], 
   324                             SERVICE_UPDATER_NOT_FIXED_DRIVE)) {
   325       LOG_WARN(("Could not write update.status service update failure.  (%d)",
   326                 GetLastError()));
   327     }
   328     return FALSE;
   329   }
   331   nsAutoHandle noWriteLock(CreateFileW(argv[0], GENERIC_READ, FILE_SHARE_READ, 
   332                                        nullptr, OPEN_EXISTING, 0, nullptr));
   333   if (INVALID_HANDLE_VALUE == noWriteLock) {
   334       LOG_WARN(("Could not set no write sharing access on file.  (%d)",
   335                 GetLastError()));
   336     if (!WriteStatusFailure(argv[1], 
   337                             SERVICE_COULD_NOT_LOCK_UPDATER)) {
   338       LOG_WARN(("Could not write update.status service update failure.  (%d)",
   339                 GetLastError()));
   340     }
   341     return FALSE;
   342   }
   344   // Verify that the updater.exe that we are executing is the same
   345   // as the one in the installation directory which we are updating.
   346   // The installation dir that we are installing to is installDir.
   347   WCHAR installDirUpdater[MAX_PATH + 1] = { L'\0' };
   348   wcsncpy(installDirUpdater, installDir, MAX_PATH);
   349   if (!PathAppendSafe(installDirUpdater, L"updater.exe")) {
   350     LOG_WARN(("Install directory updater could not be determined."));
   351     result = FALSE;
   352   }
   354   BOOL updaterIsCorrect;
   355   if (result && !VerifySameFiles(argv[0], installDirUpdater, 
   356                                  updaterIsCorrect)) {
   357     LOG_WARN(("Error checking if the updaters are the same.\n"
   358               "Path 1: %ls\nPath 2: %ls", argv[0], installDirUpdater));
   359     result = FALSE;
   360   }
   362   if (result && !updaterIsCorrect) {
   363     LOG_WARN(("The updaters do not match, updater will not run.")); 
   364     result = FALSE;
   365   }
   367   if (result) {
   368     LOG(("updater.exe was compared successfully to the installation directory"
   369          " updater.exe."));
   370   } else {
   371     if (!WriteStatusFailure(argv[1], 
   372                             SERVICE_UPDATER_COMPARE_ERROR)) {
   373       LOG_WARN(("Could not write update.status updater compare failure."));
   374     }
   375     return FALSE;
   376   }
   378   // Check to make sure the updater.exe module has the unique updater identity.
   379   // This is a security measure to make sure that the signed executable that
   380   // we will run is actually an updater.
   381   HMODULE updaterModule = LoadLibraryEx(argv[0], nullptr, 
   382                                         LOAD_LIBRARY_AS_DATAFILE);
   383   if (!updaterModule) {
   384     LOG_WARN(("updater.exe module could not be loaded. (%d)", GetLastError()));
   385     result = FALSE;
   386   } else {
   387     char updaterIdentity[64];
   388     if (!LoadStringA(updaterModule, IDS_UPDATER_IDENTITY, 
   389                      updaterIdentity, sizeof(updaterIdentity))) {
   390       LOG_WARN(("The updater.exe application does not contain the Mozilla"
   391                 " updater identity."));
   392       result = FALSE;
   393     }
   395     if (strcmp(updaterIdentity, UPDATER_IDENTITY_STRING)) {
   396       LOG_WARN(("The updater.exe identity string is not valid."));
   397       result = FALSE;
   398     }
   399     FreeLibrary(updaterModule);
   400   }
   402   if (result) {
   403     LOG(("The updater.exe application contains the Mozilla"
   404           " updater identity."));
   405   } else {
   406     if (!WriteStatusFailure(argv[1], 
   407                             SERVICE_UPDATER_IDENTITY_ERROR)) {
   408       LOG_WARN(("Could not write update.status no updater identity."));
   409     }
   410     return TRUE;
   411   }
   413   // Check for updater.exe sign problems
   414   BOOL updaterSignProblem = FALSE;
   415 #ifndef DISABLE_UPDATER_AUTHENTICODE_CHECK
   416   updaterSignProblem = !DoesBinaryMatchAllowedCertificates(installDir,
   417                                                            argv[0]);
   418 #endif
   420   // Only proceed with the update if we have no signing problems
   421   if (!updaterSignProblem) {
   422     BOOL updateProcessWasStarted = FALSE;
   423     if (StartUpdateProcess(argc, argv, installDir,
   424                            updateProcessWasStarted)) {
   425       LOG(("updater.exe was launched and run successfully!"));
   426       LogFlush();
   428       // Don't attempt to update the service when the update is being staged.
   429       if (!IsUpdateBeingStaged(argc, argv)) {
   430         // We might not execute code after StartServiceUpdate because
   431         // the service installer will stop the service if it is running.
   432         StartServiceUpdate(installDir);
   433       }
   434     } else {
   435       result = FALSE;
   436       LOG_WARN(("Error running update process. Updating update.status  (%d)",
   437                 GetLastError()));
   438       LogFlush();
   440       // If the update process was started, then updater.exe is responsible for
   441       // setting the failure code.  If it could not be started then we do the 
   442       // work.  We set an error instead of directly setting status pending 
   443       // so that the app.update.service.errors pref can be updated when 
   444       // the callback app restarts.
   445       if (!updateProcessWasStarted) {
   446         if (!WriteStatusFailure(argv[1], 
   447                                 SERVICE_UPDATER_COULD_NOT_BE_STARTED)) {
   448           LOG_WARN(("Could not write update.status service update failure.  (%d)",
   449                     GetLastError()));
   450         }
   451       }
   452     }
   453   } else {
   454     result = FALSE;
   455     LOG_WARN(("Could not start process due to certificate check error on "
   456               "updater.exe. Updating update.status.  (%d)", GetLastError()));
   458     // When there is a certificate check error on the updater.exe application,
   459     // we want to write out the error.
   460     if (!WriteStatusFailure(argv[1], 
   461                             SERVICE_UPDATER_SIGN_ERROR)) {
   462       LOG_WARN(("Could not write pending state to update.status.  (%d)",
   463                 GetLastError()));
   464     }
   465   }
   467   return result;
   468 }
   470 /**
   471  * Obtains the updater path alongside a subdir of the service binary.
   472  * The purpose of this function is to return a path that is likely high
   473  * integrity and therefore more safe to execute code from.
   474  *
   475  * @param serviceUpdaterPath Out parameter for the path where the updater
   476  *                           should be copied to.
   477  * @return TRUE if a file path was obtained.
   478  */
   479 BOOL
   480 GetSecureUpdaterPath(WCHAR serviceUpdaterPath[MAX_PATH + 1])
   481 {
   482   if (!GetModuleFileNameW(nullptr, serviceUpdaterPath, MAX_PATH)) {
   483     LOG_WARN(("Could not obtain module filename when attempting to "
   484               "use a secure updater path.  (%d)", GetLastError()));
   485     return FALSE;
   486   }
   488   if (!PathRemoveFileSpecW(serviceUpdaterPath)) {
   489     LOG_WARN(("Couldn't remove file spec when attempting to use a secure "
   490               "updater path.  (%d)", GetLastError()));
   491     return FALSE;
   492   }
   494   if (!PathAppendSafe(serviceUpdaterPath, L"update")) {
   495     LOG_WARN(("Couldn't append file spec when attempting to use a secure "
   496               "updater path.  (%d)", GetLastError()));
   497     return FALSE;
   498   }
   500   CreateDirectoryW(serviceUpdaterPath, nullptr);
   502   if (!PathAppendSafe(serviceUpdaterPath, L"updater.exe")) {
   503     LOG_WARN(("Couldn't append file spec when attempting to use a secure "
   504               "updater path.  (%d)", GetLastError()));
   505     return FALSE;
   506   }
   508   return TRUE;
   509 }
   511 /**
   512  * Deletes the passed in updater path and the associated updater.ini file.
   513  *
   514  * @param serviceUpdaterPath The path to delete.
   515  * @return TRUE if a file was deleted.
   516  */
   517 BOOL
   518 DeleteSecureUpdater(WCHAR serviceUpdaterPath[MAX_PATH + 1])
   519 {
   520   BOOL result = FALSE;
   521   if (serviceUpdaterPath[0]) {
   522     result = DeleteFileW(serviceUpdaterPath);
   523     if (!result && GetLastError() != ERROR_PATH_NOT_FOUND &&
   524         GetLastError() != ERROR_FILE_NOT_FOUND) {
   525       LOG_WARN(("Could not delete service updater path: '%ls'.",
   526                 serviceUpdaterPath));
   527     }
   529     WCHAR updaterINIPath[MAX_PATH + 1] = { L'\0' };
   530     if (PathGetSiblingFilePath(updaterINIPath, serviceUpdaterPath,
   531                                L"updater.ini")) {
   532       result = DeleteFileW(updaterINIPath);
   533       if (!result && GetLastError() != ERROR_PATH_NOT_FOUND &&
   534           GetLastError() != ERROR_FILE_NOT_FOUND) {
   535         LOG_WARN(("Could not delete service updater INI path: '%ls'.",
   536                   updaterINIPath));
   537       }
   538     }
   539   }
   540   return result;
   541 }
   543 /**
   544  * Executes a service command.
   545  *
   546  * @param argc The number of arguments in argv
   547  * @param argv The service command line arguments, argv[0] and argv[1]
   548  *             and automatically included by Windows.  argv[2] is the
   549  *             service command.
   550  *             
   551  * @return FALSE if there was an error executing the service command.
   552  */
   553 BOOL
   554 ExecuteServiceCommand(int argc, LPWSTR *argv)
   555 {
   556   if (argc < 3) {
   557     LOG_WARN(("Not enough command line arguments to execute a service command"));
   558     return FALSE;
   559   }
   561   // The tests work by making sure the log has changed, so we put a 
   562   // unique ID in the log.
   563   RPC_WSTR guidString = RPC_WSTR(L"");
   564   GUID guid;
   565   HRESULT hr = CoCreateGuid(&guid);
   566   if (SUCCEEDED(hr)) {
   567     UuidToString(&guid, &guidString);
   568   }
   569   LOG(("Executing service command %ls, ID: %ls",
   570        argv[2], reinterpret_cast<LPCWSTR>(guidString)));
   571   RpcStringFree(&guidString);
   573   BOOL result = FALSE;
   574   if (!lstrcmpi(argv[2], L"software-update")) {
   576     // Use the passed in command line arguments for the update, except for the
   577     // path to updater.exe.  We copy updater.exe to a the directory of the
   578     // MozillaMaintenance service so that a low integrity process cannot
   579     // replace the updater.exe at any point and use that for the update.
   580     // It also makes DLL injection attacks harder.
   581     LPWSTR oldUpdaterPath = argv[3];
   582     WCHAR secureUpdaterPath[MAX_PATH + 1] = { L'\0' };
   583     result = GetSecureUpdaterPath(secureUpdaterPath); // Does its own logging
   584     if (result) {
   585       LOG(("Passed in path: '%ls'; Using this path for updating: '%ls'.",
   586            oldUpdaterPath, secureUpdaterPath));
   587       DeleteSecureUpdater(secureUpdaterPath);
   588       result = CopyFileW(oldUpdaterPath, secureUpdaterPath, FALSE);
   589     }
   591     if (!result) {
   592       LOG_WARN(("Could not copy path to secure location.  (%d)",
   593                 GetLastError()));
   594       if (argc > 4 && !WriteStatusFailure(argv[4],
   595                                           SERVICE_COULD_NOT_COPY_UPDATER)) {
   596         LOG_WARN(("Could not write update.status could not copy updater error"));
   597       }
   598     } else {
   600       // We obtained the path and copied it successfully, update the path to
   601       // use for the service update.
   602       argv[3] = secureUpdaterPath;
   604       WCHAR oldUpdaterINIPath[MAX_PATH + 1] = { L'\0' };
   605       WCHAR secureUpdaterINIPath[MAX_PATH + 1] = { L'\0' };
   606       if (PathGetSiblingFilePath(secureUpdaterINIPath, secureUpdaterPath,
   607                                  L"updater.ini") &&
   608           PathGetSiblingFilePath(oldUpdaterINIPath, oldUpdaterPath,
   609                                  L"updater.ini")) {
   610         // This is non fatal if it fails there is no real harm
   611         if (!CopyFileW(oldUpdaterINIPath, secureUpdaterINIPath, FALSE)) {
   612           LOG_WARN(("Could not copy updater.ini from: '%ls' to '%ls'.  (%d)",
   613                     oldUpdaterINIPath, secureUpdaterINIPath, GetLastError()));
   614         }
   615       }
   617       result = ProcessSoftwareUpdateCommand(argc - 3, argv + 3);
   618       DeleteSecureUpdater(secureUpdaterPath);
   619     }
   621     // We might not reach here if the service install succeeded
   622     // because the service self updates itself and the service
   623     // installer will stop the service.
   624     LOG(("Service command %ls complete.", argv[2]));
   625   } else {
   626     LOG_WARN(("Service command not recognized: %ls.", argv[2]));
   627     // result is already set to FALSE
   628   }
   630   LOG(("service command %ls complete with result: %ls.",
   631        argv[1], (result ? L"Success" : L"Failure")));
   632   return TRUE;
   633 }

mercurial