toolkit/components/maintenanceservice/serviceinstall.cpp

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.

     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 <windows.h>
     6 #include <aclapi.h>
     7 #include <stdlib.h>
     8 #include <shlwapi.h>
    10 // Used for DNLEN and UNLEN
    11 #include <lm.h>
    13 #include <nsWindowsHelpers.h>
    14 #include "mozilla/Scoped.h"
    16 #include "serviceinstall.h"
    17 #include "servicebase.h"
    18 #include "updatehelper.h"
    19 #include "shellapi.h"
    20 #include "readstrings.h"
    21 #include "errors.h"
    23 #pragma comment(lib, "version.lib")
    25 /**
    26  * A wrapper function to read strings for the maintenance service.
    27  *
    28  * @param path    The path of the ini file to read from
    29  * @param results The maintenance service strings that were read
    30  * @return OK on success
    31 */
    32 static int
    33 ReadMaintenanceServiceStrings(LPCWSTR path, 
    34                               MaintenanceServiceStringTable *results)
    35 {
    36   // Read in the maintenance service description string if specified.
    37   const unsigned int kNumStrings = 1;
    38   const char *kServiceKeys = "MozillaMaintenanceDescription\0";
    39   char serviceStrings[kNumStrings][MAX_TEXT_LEN];
    40   int result = ReadStrings(path, kServiceKeys, 
    41                            kNumStrings, serviceStrings);
    42   if (result != OK) {
    43     serviceStrings[0][0] = '\0';
    44   }
    45   strncpy(results->serviceDescription, 
    46           serviceStrings[0], MAX_TEXT_LEN - 1);
    47   results->serviceDescription[MAX_TEXT_LEN - 1] = '\0';
    48   return result;
    49 }
    51 /**
    52  * Obtains the version number from the specified PE file's version information
    53  * Version Format: A.B.C.D (Example 10.0.0.300)
    54  *  
    55  * @param  path The path of the file to check the version on
    56  * @param  A    The first part of the version number
    57  * @param  B    The second part of the version number
    58  * @param  C    The third part of the version number
    59  * @param  D    The fourth part of the version number
    60  * @return TRUE if successful
    61  */
    62 static BOOL
    63 GetVersionNumberFromPath(LPWSTR path, DWORD &A, DWORD &B, 
    64                          DWORD &C, DWORD &D) 
    65 {
    66   DWORD fileVersionInfoSize = GetFileVersionInfoSizeW(path, 0);
    67   mozilla::ScopedDeleteArray<char> fileVersionInfo(new char[fileVersionInfoSize]);
    68   if (!GetFileVersionInfoW(path, 0, fileVersionInfoSize,
    69                            fileVersionInfo.get())) {
    70       LOG_WARN(("Could not obtain file info of old service.  (%d)", 
    71                 GetLastError()));
    72       return FALSE;
    73   }
    75   VS_FIXEDFILEINFO *fixedFileInfo = 
    76     reinterpret_cast<VS_FIXEDFILEINFO *>(fileVersionInfo.get());
    77   UINT size;
    78   if (!VerQueryValueW(fileVersionInfo.get(), L"\\", 
    79     reinterpret_cast<LPVOID*>(&fixedFileInfo), &size)) {
    80       LOG_WARN(("Could not query file version info of old service.  (%d)", 
    81                 GetLastError()));
    82       return FALSE;
    83   }  
    85   A = HIWORD(fixedFileInfo->dwFileVersionMS);
    86   B = LOWORD(fixedFileInfo->dwFileVersionMS);
    87   C = HIWORD(fixedFileInfo->dwFileVersionLS);
    88   D = LOWORD(fixedFileInfo->dwFileVersionLS);
    89   return TRUE;
    90 }
    92 /**
    93  * Updates the service description with what is stored in updater.ini
    94  * at the same path as the currently executing module binary.
    95  *
    96  * @param serviceHandle A handle to an opened service with 
    97  *                      SERVICE_CHANGE_CONFIG access right
    98  * @param TRUE on succcess.
    99 */
   100 BOOL
   101 UpdateServiceDescription(SC_HANDLE serviceHandle)
   102 {
   103   WCHAR updaterINIPath[MAX_PATH + 1];
   104   if (!GetModuleFileNameW(nullptr, updaterINIPath, 
   105                           sizeof(updaterINIPath) /
   106                           sizeof(updaterINIPath[0]))) {
   107     LOG_WARN(("Could not obtain module filename when attempting to "
   108               "modify service description.  (%d)", GetLastError()));
   109     return FALSE;
   110   }
   112   if (!PathRemoveFileSpecW(updaterINIPath)) {
   113     LOG_WARN(("Could not remove file spec when attempting to "
   114               "modify service description.  (%d)", GetLastError()));
   115     return FALSE;
   116   }
   118   if (!PathAppendSafe(updaterINIPath, L"updater.ini")) {
   119     LOG_WARN(("Could not append updater.ini filename when attempting to "
   120               "modify service description.  (%d)", GetLastError()));
   121     return FALSE;
   122   }
   124   if (GetFileAttributesW(updaterINIPath) == INVALID_FILE_ATTRIBUTES) {
   125     LOG_WARN(("updater.ini file does not exist, will not modify "
   126               "service description.  (%d)", GetLastError()));
   127     return FALSE;
   128   }
   130   MaintenanceServiceStringTable serviceStrings;
   131   int rv = ReadMaintenanceServiceStrings(updaterINIPath, &serviceStrings);
   132   if (rv != OK || !strlen(serviceStrings.serviceDescription)) {
   133     LOG_WARN(("updater.ini file does not contain a maintenance "
   134               "service description."));
   135     return FALSE;
   136   }
   138   WCHAR serviceDescription[MAX_TEXT_LEN];
   139   if (!MultiByteToWideChar(CP_UTF8, 0, 
   140                            serviceStrings.serviceDescription, -1,
   141                            serviceDescription,
   142                            sizeof(serviceDescription) / 
   143                            sizeof(serviceDescription[0]))) {
   144     LOG_WARN(("Could not convert description to wide string format.  (%d)",
   145               GetLastError()));
   146     return FALSE;
   147   }
   149   SERVICE_DESCRIPTIONW descriptionConfig;
   150   descriptionConfig.lpDescription = serviceDescription;
   151   if (!ChangeServiceConfig2W(serviceHandle, 
   152                              SERVICE_CONFIG_DESCRIPTION, 
   153                              &descriptionConfig)) {
   154     LOG_WARN(("Could not change service config.  (%d)", GetLastError()));
   155     return FALSE;
   156   }
   158   LOG(("The service description was updated successfully."));
   159   return TRUE;
   160 }
   162 /**
   163  * Determines if the MozillaMaintenance service path needs to be updated
   164  * and fixes it if it is wrong.
   165  *
   166  * @param service             A handle to the service to fix.
   167  * @param currentServicePath  The current (possibly wrong) path that is used.
   168  * @param servicePathWasWrong Out parameter set to TRUE if a fix was needed.
   169  * @return TRUE if the service path is now correct.
   170 */
   171 BOOL
   172 FixServicePath(SC_HANDLE service,
   173                LPCWSTR currentServicePath,
   174                BOOL &servicePathWasWrong)
   175 {
   176   // When we originally upgraded the MozillaMaintenance service we
   177   // would uninstall the service on each upgrade.  This had an
   178   // intermittent error which could cause the service to use the file
   179   // maintenanceservice_tmp.exe as the install path.  Only a small number
   180   // of Nightly users would be affected by this, but we check for this
   181   // state here and fix the user if they are affected.
   182   //
   183   // We also fix the path in the case of the path not being quoted.
   184   size_t currentServicePathLen = wcslen(currentServicePath);
   185   bool doesServiceHaveCorrectPath =
   186     currentServicePathLen > 2 &&
   187     !wcsstr(currentServicePath, L"maintenanceservice_tmp.exe") &&
   188     currentServicePath[0] == L'\"' &&
   189     currentServicePath[currentServicePathLen - 1] == L'\"';
   191   if (doesServiceHaveCorrectPath) {
   192     LOG(("The MozillaMaintenance service path is correct."));
   193     servicePathWasWrong = FALSE;
   194     return TRUE;
   195   }
   196   // This is a recoverable situation so not logging as a warning
   197   LOG(("The MozillaMaintenance path is NOT correct. It was: %ls",
   198        currentServicePath));
   200   servicePathWasWrong = TRUE;
   201   WCHAR fixedPath[MAX_PATH + 1] = { L'\0' };
   202   wcsncpy(fixedPath, currentServicePath, MAX_PATH);
   203   PathUnquoteSpacesW(fixedPath);
   204   if (!PathRemoveFileSpecW(fixedPath)) {
   205     LOG_WARN(("Couldn't remove file spec.  (%d)", GetLastError()));
   206     return FALSE;
   207   }
   208   if (!PathAppendSafe(fixedPath, L"maintenanceservice.exe")) {
   209     LOG_WARN(("Couldn't append file spec.  (%d)", GetLastError()));
   210     return FALSE;
   211   }
   212   PathQuoteSpacesW(fixedPath);
   215   if (!ChangeServiceConfigW(service, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE,
   216                             SERVICE_NO_CHANGE, fixedPath, nullptr, nullptr,
   217                             nullptr, nullptr, nullptr, nullptr)) {
   218     LOG_WARN(("Could not fix service path.  (%d)", GetLastError()));
   219     return FALSE;
   220   }
   222   LOG(("Fixed service path to: %ls.", fixedPath));
   223   return TRUE;
   224 }
   226 /**
   227  * Installs or upgrades the SVC_NAME service.
   228  * If an existing service is already installed, we replace it with the
   229  * currently running process.
   230  *
   231  * @param  action The action to perform.
   232  * @return TRUE if the service was installed/upgraded
   233  */
   234 BOOL
   235 SvcInstall(SvcInstallAction action)
   236 {
   237   // Get a handle to the local computer SCM database with full access rights.
   238   nsAutoServiceHandle schSCManager(OpenSCManager(nullptr, nullptr, 
   239                                                  SC_MANAGER_ALL_ACCESS));
   240   if (!schSCManager) {
   241     LOG_WARN(("Could not open service manager.  (%d)", GetLastError()));
   242     return FALSE;
   243   }
   245   WCHAR newServiceBinaryPath[MAX_PATH + 1];
   246   if (!GetModuleFileNameW(nullptr, newServiceBinaryPath, 
   247                           sizeof(newServiceBinaryPath) / 
   248                           sizeof(newServiceBinaryPath[0]))) {
   249     LOG_WARN(("Could not obtain module filename when attempting to "
   250               "install service.  (%d)",
   251               GetLastError()));
   252     return FALSE;
   253   }
   255   // Check if we already have the service installed.
   256   nsAutoServiceHandle schService(OpenServiceW(schSCManager, 
   257                                               SVC_NAME, 
   258                                               SERVICE_ALL_ACCESS));
   259   DWORD lastError = GetLastError();
   260   if (!schService && ERROR_SERVICE_DOES_NOT_EXIST != lastError) {
   261     // The service exists but we couldn't open it
   262     LOG_WARN(("Could not open service.  (%d)", GetLastError()));
   263     return FALSE;
   264   }
   266   if (schService) {
   267     // The service exists but it may not have the correct permissions.
   268     // This could happen if the permissions were not set correctly originally
   269     // or have been changed after the installation.  This will reset the 
   270     // permissions back to allow limited user accounts.
   271     if (!SetUserAccessServiceDACL(schService)) {
   272       LOG_WARN(("Could not reset security ACE on service handle. It might not be "
   273                 "possible to start the service. This error should never "
   274                 "happen.  (%d)", GetLastError()));
   275     }
   277     // The service exists and we opened it
   278     DWORD bytesNeeded;
   279     if (!QueryServiceConfigW(schService, nullptr, 0, &bytesNeeded) && 
   280         GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
   281       LOG_WARN(("Could not determine buffer size for query service config.  (%d)",
   282                 GetLastError()));
   283       return FALSE;
   284     }
   286     // Get the service config information, in particular we want the binary 
   287     // path of the service.
   288     mozilla::ScopedDeleteArray<char> serviceConfigBuffer(new char[bytesNeeded]);
   289     if (!QueryServiceConfigW(schService, 
   290         reinterpret_cast<QUERY_SERVICE_CONFIGW*>(serviceConfigBuffer.get()), 
   291         bytesNeeded, &bytesNeeded)) {
   292       LOG_WARN(("Could open service but could not query service config.  (%d)",
   293                 GetLastError()));
   294       return FALSE;
   295     }
   296     QUERY_SERVICE_CONFIGW &serviceConfig = 
   297       *reinterpret_cast<QUERY_SERVICE_CONFIGW*>(serviceConfigBuffer.get());
   299     // Check if we need to fix the service path
   300     BOOL servicePathWasWrong;
   301     static BOOL alreadyCheckedFixServicePath = FALSE;
   302     if (!alreadyCheckedFixServicePath) {
   303       if (!FixServicePath(schService, serviceConfig.lpBinaryPathName,
   304                           servicePathWasWrong)) {
   305         LOG_WARN(("Could not fix service path. This should never happen.  (%d)",
   306                   GetLastError()));
   307         // True is returned because the service is pointing to
   308         // maintenanceservice_tmp.exe so it actually was upgraded to the
   309         // newest installed service.
   310         return TRUE;
   311       } else if (servicePathWasWrong) {
   312         // Now that the path is fixed we should re-attempt the install.
   313         // This current process' image path is maintenanceservice_tmp.exe.
   314         // The service used to point to maintenanceservice_tmp.exe.
   315         // The service was just fixed to point to maintenanceservice.exe.
   316         // Re-attempting an install from scratch will work as normal.
   317         alreadyCheckedFixServicePath = TRUE;
   318         LOG(("Restarting install action: %d", action));
   319         return SvcInstall(action);
   320       }
   321     }
   323     // Ensure the service path is not quoted. We own this memory and know it to
   324     // be large enough for the quoted path, so it is large enough for the
   325     // unquoted path.  This function cannot fail.
   326     PathUnquoteSpacesW(serviceConfig.lpBinaryPathName);
   328     // Obtain the existing maintenanceservice file's version number and
   329     // the new file's version number.  Versions are in the format of
   330     // A.B.C.D.
   331     DWORD existingA, existingB, existingC, existingD;
   332     DWORD newA, newB, newC, newD; 
   333     BOOL obtainedExistingVersionInfo = 
   334       GetVersionNumberFromPath(serviceConfig.lpBinaryPathName, 
   335                                existingA, existingB, 
   336                                existingC, existingD);
   337     if (!GetVersionNumberFromPath(newServiceBinaryPath, newA, 
   338                                  newB, newC, newD)) {
   339       LOG_WARN(("Could not obtain version number from new path"));
   340       return FALSE;
   341     }
   343     // Check if we need to replace the old binary with the new one
   344     // If we couldn't get the old version info then we assume we should 
   345     // replace it.
   346     if (ForceInstallSvc == action ||
   347         !obtainedExistingVersionInfo || 
   348         (existingA < newA) ||
   349         (existingA == newA && existingB < newB) ||
   350         (existingA == newA && existingB == newB && 
   351          existingC < newC) ||
   352         (existingA == newA && existingB == newB && 
   353          existingC == newC && existingD < newD)) {
   355       // We have a newer updater, so update the description from the INI file.
   356       UpdateServiceDescription(schService);
   358       schService.reset();
   359       if (!StopService()) {
   360         return FALSE;
   361       }
   363       if (!wcscmp(newServiceBinaryPath, serviceConfig.lpBinaryPathName)) {
   364         LOG(("File is already in the correct location, no action needed for "
   365              "upgrade.  The path is: \"%ls\"", newServiceBinaryPath));
   366         return TRUE;
   367       }
   369       BOOL result = TRUE;
   371       // Attempt to copy the new binary over top the existing binary.
   372       // If there is an error we try to move it out of the way and then
   373       // copy it in.  First try the safest / easiest way to overwrite the file.
   374       if (!CopyFileW(newServiceBinaryPath, 
   375                      serviceConfig.lpBinaryPathName, FALSE)) {
   376         LOG_WARN(("Could not overwrite old service binary file. "
   377                   "This should never happen, but if it does the next "
   378                   "upgrade will fix it, the service is not a critical "
   379                   "component that needs to be installed for upgrades "
   380                   "to work.  (%d)", GetLastError()));
   382         // We rename the last 3 filename chars in an unsafe way.  Manually
   383         // verify there are more than 3 chars for safe failure in MoveFileExW.
   384         const size_t len = wcslen(serviceConfig.lpBinaryPathName);
   385         if (len > 3) {
   386           // Calculate the temp file path that we're moving the file to. This 
   387           // is the same as the proper service path but with a .old extension.
   388           LPWSTR oldServiceBinaryTempPath = 
   389             new WCHAR[len + 1];
   390           memset(oldServiceBinaryTempPath, 0, (len + 1) * sizeof (WCHAR));
   391           wcsncpy(oldServiceBinaryTempPath, serviceConfig.lpBinaryPathName, len);
   392           // Rename the last 3 chars to 'old'
   393           wcsncpy(oldServiceBinaryTempPath + len - 3, L"old", 3);
   395           // Move the current (old) service file to the temp path.
   396           if (MoveFileExW(serviceConfig.lpBinaryPathName, 
   397                           oldServiceBinaryTempPath, 
   398                           MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH)) {
   399             // The old binary is moved out of the way, copy in the new one.
   400             if (!CopyFileW(newServiceBinaryPath, 
   401                            serviceConfig.lpBinaryPathName, FALSE)) {
   402               // It is best to leave the old service binary in this condition.
   403               LOG_WARN(("The new service binary could not be copied in."
   404                         " The service will not be upgraded."));
   405               result = FALSE;
   406             } else {
   407               LOG(("The new service binary was copied in by first moving the"
   408                    " old one out of the way."));
   409             }
   411             // Attempt to get rid of the old service temp path.
   412             if (DeleteFileW(oldServiceBinaryTempPath)) {
   413               LOG(("The old temp service path was deleted: %ls.",
   414                    oldServiceBinaryTempPath));
   415             } else {
   416               // The old temp path could not be removed.  It will be removed
   417               // the next time the user can't copy the binary in or on uninstall.
   418               LOG_WARN(("The old temp service path was not deleted."));
   419             }
   420           } else {
   421             // It is best to leave the old service binary in this condition.
   422             LOG_WARN(("Could not move old service file out of the way from:"
   423                       " \"%ls\" to \"%ls\". Service will not be upgraded.  (%d)",
   424                       serviceConfig.lpBinaryPathName,
   425                       oldServiceBinaryTempPath, GetLastError()));
   426             result = FALSE;
   427           }
   428           delete[] oldServiceBinaryTempPath;
   429         } else {
   430             // It is best to leave the old service binary in this condition.
   431             LOG_WARN(("Service binary path was less than 3, service will"
   432                       " not be updated.  This should never happen."));
   433             result = FALSE;
   434         }
   435       } else {
   436         LOG(("The new service binary was copied in."));
   437       }
   439       // We made a copy of ourselves to the existing location.
   440       // The tmp file (the process of which we are executing right now) will be
   441       // left over.  Attempt to delete the file on the next reboot.
   442       if (MoveFileExW(newServiceBinaryPath, nullptr,
   443                       MOVEFILE_DELAY_UNTIL_REBOOT)) {
   444         LOG(("Deleting the old file path on the next reboot: %ls.",
   445              newServiceBinaryPath));
   446       } else {
   447         LOG_WARN(("Call to delete the old file path failed: %ls.",
   448                   newServiceBinaryPath));
   449       }
   451       return result;
   452     }
   454     // We don't need to copy ourselves to the existing location.
   455     // The tmp file (the process of which we are executing right now) will be
   456     // left over.  Attempt to delete the file on the next reboot.
   457     MoveFileExW(newServiceBinaryPath, nullptr, MOVEFILE_DELAY_UNTIL_REBOOT);
   459     // nothing to do, we already have a newer service installed
   460     return TRUE; 
   461   }
   463   // If the service does not exist and we are upgrading, don't install it.
   464   if (UpgradeSvc == action) {
   465     // The service does not exist and we are upgrading, so don't install it
   466     return TRUE;
   467   }
   469   // Quote the path only if it contains spaces.
   470   PathQuoteSpacesW(newServiceBinaryPath);
   471   // The service does not already exist so create the service as on demand
   472   schService.own(CreateServiceW(schSCManager, SVC_NAME, SVC_DISPLAY_NAME,
   473                                 SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS,
   474                                 SERVICE_DEMAND_START, SERVICE_ERROR_NORMAL,
   475                                 newServiceBinaryPath, nullptr, nullptr,
   476                                 nullptr, nullptr, nullptr));
   477   if (!schService) {
   478     LOG_WARN(("Could not create Windows service. "
   479               "This error should never happen since a service install "
   480               "should only be called when elevated.  (%d)", GetLastError()));
   481     return FALSE;
   482   } 
   484   if (!SetUserAccessServiceDACL(schService)) {
   485     LOG_WARN(("Could not set security ACE on service handle, the service will not "
   486               "be able to be started from unelevated processes. "
   487               "This error should never happen.  (%d)",
   488               GetLastError()));
   489   }
   491   UpdateServiceDescription(schService);
   493   return TRUE;
   494 }
   496 /**
   497  * Stops the Maintenance service.
   498  *
   499  * @return TRUE if successful.
   500  */
   501 BOOL
   502 StopService()
   503 {
   504   // Get a handle to the local computer SCM database with full access rights.
   505   nsAutoServiceHandle schSCManager(OpenSCManager(nullptr, nullptr, 
   506                                                  SC_MANAGER_ALL_ACCESS));
   507   if (!schSCManager) {
   508     LOG_WARN(("Could not open service manager.  (%d)", GetLastError()));
   509     return FALSE;
   510   }
   512   // Open the service
   513   nsAutoServiceHandle schService(OpenServiceW(schSCManager, SVC_NAME, 
   514                                               SERVICE_ALL_ACCESS));
   515   if (!schService) {
   516     LOG_WARN(("Could not open service.  (%d)", GetLastError()));
   517     return FALSE;
   518   } 
   520   LOG(("Sending stop request..."));
   521   SERVICE_STATUS status;
   522   SetLastError(ERROR_SUCCESS);
   523   if (!ControlService(schService, SERVICE_CONTROL_STOP, &status) &&
   524       GetLastError() != ERROR_SERVICE_NOT_ACTIVE) {
   525     LOG_WARN(("Error sending stop request.  (%d)", GetLastError()));
   526   }
   528   schSCManager.reset();
   529   schService.reset();
   531   LOG(("Waiting for service stop..."));
   532   DWORD lastState = WaitForServiceStop(SVC_NAME, 30);
   534   // The service can be in a stopped state but the exe still in use
   535   // so make sure the process is really gone before proceeding
   536   WaitForProcessExit(L"maintenanceservice.exe", 30);
   537   LOG(("Done waiting for service stop, last service state: %d", lastState));
   539   return lastState == SERVICE_STOPPED;
   540 }
   542 /**
   543  * Uninstalls the Maintenance service.
   544  *
   545  * @return TRUE if successful.
   546  */
   547 BOOL
   548 SvcUninstall()
   549 {
   550   // Get a handle to the local computer SCM database with full access rights.
   551   nsAutoServiceHandle schSCManager(OpenSCManager(nullptr, nullptr, 
   552                                                  SC_MANAGER_ALL_ACCESS));
   553   if (!schSCManager) {
   554     LOG_WARN(("Could not open service manager.  (%d)", GetLastError()));
   555     return FALSE;
   556   }
   558   // Open the service
   559   nsAutoServiceHandle schService(OpenServiceW(schSCManager, SVC_NAME, 
   560                                               SERVICE_ALL_ACCESS));
   561   if (!schService) {
   562     LOG_WARN(("Could not open service.  (%d)", GetLastError()));
   563     return FALSE;
   564   } 
   566   //Stop the service so it deletes faster and so the uninstaller
   567   // can actually delete its EXE.
   568   DWORD totalWaitTime = 0;
   569   SERVICE_STATUS status;
   570   static const int maxWaitTime = 1000 * 60; // Never wait more than a minute
   571   if (ControlService(schService, SERVICE_CONTROL_STOP, &status)) {
   572     do {
   573       Sleep(status.dwWaitHint);
   574       totalWaitTime += (status.dwWaitHint + 10);
   575       if (status.dwCurrentState == SERVICE_STOPPED) {
   576         break;
   577       } else if (totalWaitTime > maxWaitTime) {
   578         break;
   579       }
   580     } while (QueryServiceStatus(schService, &status));
   581   }
   583   // Delete the service or mark it for deletion
   584   BOOL deleted = DeleteService(schService);
   585   if (!deleted) {
   586     deleted = (GetLastError() == ERROR_SERVICE_MARKED_FOR_DELETE);
   587   }
   589   return deleted;
   590 }
   592 /**
   593  * Sets the access control list for user access for the specified service.
   594  *
   595  * @param  hService The service to set the access control list on
   596  * @return TRUE if successful
   597  */
   598 BOOL
   599 SetUserAccessServiceDACL(SC_HANDLE hService)
   600 {
   601   PACL pNewAcl = nullptr;
   602   PSECURITY_DESCRIPTOR psd = nullptr;
   603   DWORD lastError = SetUserAccessServiceDACL(hService, pNewAcl, psd);
   604   if (pNewAcl) {
   605     LocalFree((HLOCAL)pNewAcl);
   606   }
   607   if (psd) {
   608     LocalFree((LPVOID)psd);
   609   }
   610   return ERROR_SUCCESS == lastError;
   611 }
   613 /**
   614  * Sets the access control list for user access for the specified service.
   615  *
   616  * @param  hService  The service to set the access control list on
   617  * @param  pNewAcl   The out param ACL which should be freed by caller
   618  * @param  psd       out param security descriptor, should be freed by caller
   619  * @return ERROR_SUCCESS if successful
   620  */
   621 DWORD
   622 SetUserAccessServiceDACL(SC_HANDLE hService, PACL &pNewAcl, 
   623                          PSECURITY_DESCRIPTOR psd)
   624 {
   625   // Get the current security descriptor needed size
   626   DWORD needed = 0;
   627   if (!QueryServiceObjectSecurity(hService, DACL_SECURITY_INFORMATION, 
   628                                   &psd, 0, &needed)) {
   629     if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
   630       LOG_WARN(("Could not query service object security size.  (%d)",
   631                 GetLastError()));
   632       return GetLastError();
   633     }
   635     DWORD size = needed;
   636     psd = (PSECURITY_DESCRIPTOR)LocalAlloc(LPTR, size);
   637     if (!psd) {
   638       LOG_WARN(("Could not allocate security descriptor.  (%d)",
   639                 GetLastError()));
   640       return ERROR_INSUFFICIENT_BUFFER;
   641     }
   643     // Get the actual security descriptor now
   644     if (!QueryServiceObjectSecurity(hService, DACL_SECURITY_INFORMATION, 
   645                                     psd, size, &needed)) {
   646       LOG_WARN(("Could not allocate security descriptor.  (%d)",
   647                 GetLastError()));
   648       return GetLastError();
   649     }
   650   }
   652   // Get the current DACL from the security descriptor.
   653   PACL pacl = nullptr;
   654   BOOL bDaclPresent = FALSE;
   655   BOOL bDaclDefaulted = FALSE;
   656   if ( !GetSecurityDescriptorDacl(psd, &bDaclPresent, &pacl, 
   657                                   &bDaclDefaulted)) {
   658     LOG_WARN(("Could not obtain DACL.  (%d)", GetLastError()));
   659     return GetLastError();
   660   }
   662   PSID sid;
   663   DWORD SIDSize = SECURITY_MAX_SID_SIZE;
   664   sid = LocalAlloc(LMEM_FIXED, SIDSize);
   665   if (!sid) {
   666     LOG_WARN(("Could not allocate SID memory.  (%d)", GetLastError()));
   667     return GetLastError();
   668   }
   670   if (!CreateWellKnownSid(WinBuiltinUsersSid, nullptr, sid, &SIDSize)) {
   671     DWORD lastError = GetLastError();
   672     LOG_WARN(("Could not create well known SID.  (%d)", lastError));
   673     LocalFree(sid);
   674     return lastError;
   675   }
   677   // Lookup the account name, the function fails if you don't pass in
   678   // a buffer for the domain name but it's not used since we're using
   679   // the built in account Sid.
   680   SID_NAME_USE accountType;
   681   WCHAR accountName[UNLEN + 1] = { L'\0' };
   682   WCHAR domainName[DNLEN + 1] = { L'\0' };
   683   DWORD accountNameSize = UNLEN + 1;
   684   DWORD domainNameSize = DNLEN + 1;
   685   if (!LookupAccountSidW(nullptr, sid, accountName, 
   686                          &accountNameSize, 
   687                          domainName, &domainNameSize, &accountType)) {
   688     LOG_WARN(("Could not lookup account Sid, will try Users.  (%d)",
   689               GetLastError()));
   690     wcsncpy(accountName, L"Users", UNLEN);
   691   }
   693   // We already have the group name so we can get rid of the SID
   694   FreeSid(sid);
   695   sid = nullptr;
   697   // Build the ACE, BuildExplicitAccessWithName cannot fail so it is not logged.
   698   EXPLICIT_ACCESS ea;
   699   BuildExplicitAccessWithNameW(&ea, accountName, 
   700                               SERVICE_START | SERVICE_STOP | GENERIC_READ, 
   701                               SET_ACCESS, NO_INHERITANCE);
   702   DWORD lastError = SetEntriesInAclW(1, (PEXPLICIT_ACCESS)&ea, pacl, &pNewAcl);
   703   if (ERROR_SUCCESS != lastError) {
   704     LOG_WARN(("Could not set entries in ACL.  (%d)", lastError));
   705     return lastError;
   706   }
   708   // Initialize a new security descriptor.
   709   SECURITY_DESCRIPTOR sd;
   710   if (!InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION)) {
   711     LOG_WARN(("Could not initialize security descriptor.  (%d)",
   712               GetLastError()));
   713     return GetLastError();
   714   }
   716   // Set the new DACL in the security descriptor.
   717   if (!SetSecurityDescriptorDacl(&sd, TRUE, pNewAcl, FALSE)) {
   718     LOG_WARN(("Could not set security descriptor DACL.  (%d)",
   719               GetLastError()));
   720     return GetLastError();
   721   }
   723   // Set the new security descriptor for the service object.
   724   if (!SetServiceObjectSecurity(hService, DACL_SECURITY_INFORMATION, &sd)) {
   725     LOG_WARN(("Could not set object security.  (%d)",
   726               GetLastError()));
   727     return GetLastError();
   728   }
   730   // Woohoo, raise the roof
   731   LOG(("User access was set successfully on the service."));
   732   return ERROR_SUCCESS;
   733 }

mercurial