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/. */
10 // Used for DNLEN and UNLEN
13 #include "serviceinstall.hxx"
14 #include "servicebase.hxx"
15 #include "updatehelper.h"
17 #include "readstrings.h"
22 #pragma comment(lib, "version.lib")
26 struct AutoServiceHandle
28 AutoServiceHandle(SC_HANDLE handle
):
35 releaseHandle(mHandle
);
38 void releaseHandle(SC_HANDLE handle
)
40 if (handle
!= nullptr)
41 CloseServiceHandle(handle
);
51 return mHandle
!= nullptr;
54 void reset(SC_HANDLE handle
= nullptr)
56 releaseHandle(mHandle
);
66 * A wrapper function to read strings for the maintenance service.
68 * @param path The path of the ini file to read from
69 * @param results The maintenance service strings that were read
70 * @return OK on success
73 ReadMaintenanceServiceStrings(LPCWSTR path
,
74 MaintenanceServiceStringTable
*results
)
76 // Read in the maintenance service description string if specified.
77 const unsigned int kNumStrings
= 1;
78 // TODO: moggi: needs adaptation for LibreOffice
79 const char *kServiceKeys
= "MozillaMaintenanceDescription\0";
80 char serviceStrings
[kNumStrings
][MAX_TEXT_LEN
];
81 int result
= ReadStrings(path
, kServiceKeys
,
82 kNumStrings
, serviceStrings
);
85 serviceStrings
[0][0] = '\0';
87 strncpy(results
->serviceDescription
,
88 serviceStrings
[0], MAX_TEXT_LEN
- 1);
89 results
->serviceDescription
[MAX_TEXT_LEN
- 1] = '\0';
94 * Obtains the version number from the specified PE file's version information
95 * Version Format: A.B.C.D (Example 10.0.0.300)
97 * @param path The path of the file to check the version on
98 * @param A The first part of the version number
99 * @param B The second part of the version number
100 * @param C The third part of the version number
101 * @param D The fourth part of the version number
102 * @return TRUE if successful
105 GetVersionNumberFromPath(LPWSTR path
, DWORD
&A
, DWORD
&B
,
108 DWORD fileVersionInfoSize
= GetFileVersionInfoSizeW(path
, 0);
109 std::unique_ptr
<char[]> fileVersionInfo(new char[fileVersionInfoSize
]);
110 if (!GetFileVersionInfoW(path
, 0, fileVersionInfoSize
,
111 fileVersionInfo
.get()))
113 LOG_WARN(("Could not obtain file info of old service. (%d)",
118 VS_FIXEDFILEINFO
*fixedFileInfo
=
119 reinterpret_cast<VS_FIXEDFILEINFO
*>(fileVersionInfo
.get());
121 if (!VerQueryValueW(fileVersionInfo
.get(), L
"\\",
122 reinterpret_cast<LPVOID
*>(&fixedFileInfo
), &size
))
124 LOG_WARN(("Could not query file version info of old service. (%d)",
129 A
= HIWORD(fixedFileInfo
->dwFileVersionMS
);
130 B
= LOWORD(fixedFileInfo
->dwFileVersionMS
);
131 C
= HIWORD(fixedFileInfo
->dwFileVersionLS
);
132 D
= LOWORD(fixedFileInfo
->dwFileVersionLS
);
137 * Updates the service description with what is stored in updater.ini
138 * at the same path as the currently executing module binary.
140 * @param serviceHandle A handle to an opened service with
141 * SERVICE_CHANGE_CONFIG access right
142 * @param TRUE on success.
145 UpdateServiceDescription(SC_HANDLE serviceHandle
)
147 WCHAR updaterINIPath
[MAX_PATH
+ 1];
148 if (!GetModuleFileNameW(nullptr, updaterINIPath
,
149 sizeof(updaterINIPath
) /
150 sizeof(updaterINIPath
[0])))
152 LOG_WARN(("Could not obtain module filename when attempting to "
153 "modify service description. (%d)", GetLastError()));
157 if (!PathRemoveFileSpecW(updaterINIPath
))
159 LOG_WARN(("Could not remove file spec when attempting to "
160 "modify service description. (%d)", GetLastError()));
164 if (!PathAppendSafe(updaterINIPath
, L
"updater.ini"))
166 LOG_WARN(("Could not append updater.ini filename when attempting to "
167 "modify service description. (%d)", GetLastError()));
171 if (GetFileAttributesW(updaterINIPath
) == INVALID_FILE_ATTRIBUTES
)
173 LOG_WARN(("updater.ini file does not exist, will not modify "
174 "service description. (%d)", GetLastError()));
178 MaintenanceServiceStringTable serviceStrings
;
179 int rv
= ReadMaintenanceServiceStrings(updaterINIPath
, &serviceStrings
);
180 if (rv
!= OK
|| !strlen(serviceStrings
.serviceDescription
))
182 LOG_WARN(("updater.ini file does not contain a maintenance "
183 "service description."));
187 WCHAR serviceDescription
[MAX_TEXT_LEN
];
188 if (!MultiByteToWideChar(CP_UTF8
, 0,
189 serviceStrings
.serviceDescription
, -1,
191 sizeof(serviceDescription
) /
192 sizeof(serviceDescription
[0])))
194 LOG_WARN(("Could not convert description to wide string format. (%d)",
199 SERVICE_DESCRIPTIONW descriptionConfig
;
200 descriptionConfig
.lpDescription
= serviceDescription
;
201 if (!ChangeServiceConfig2W(serviceHandle
,
202 SERVICE_CONFIG_DESCRIPTION
,
205 LOG_WARN(("Could not change service config. (%d)", GetLastError()));
209 LOG(("The service description was updated successfully."));
214 * Determines if the MozillaMaintenance service path needs to be updated
215 * and fixes it if it is wrong.
217 * @param service A handle to the service to fix.
218 * @param currentServicePath The current (possibly wrong) path that is used.
219 * @param servicePathWasWrong Out parameter set to TRUE if a fix was needed.
220 * @return TRUE if the service path is now correct.
223 FixServicePath(SC_HANDLE service
,
224 LPCWSTR currentServicePath
,
225 BOOL
&servicePathWasWrong
)
227 // When we originally upgraded the MozillaMaintenance service we
228 // would uninstall the service on each upgrade. This had an
229 // intermittent error which could cause the service to use the file
230 // maintenanceservice_tmp.exe as the install path. Only a small number
231 // of Nightly users would be affected by this, but we check for this
232 // state here and fix the user if they are affected.
234 // We also fix the path in the case of the path not being quoted.
235 size_t currentServicePathLen
= wcslen(currentServicePath
);
236 bool doesServiceHaveCorrectPath
=
237 currentServicePathLen
> 2 &&
238 !wcsstr(currentServicePath
, L
"maintenanceservice_tmp.exe") &&
239 currentServicePath
[0] == L
'\"' &&
240 currentServicePath
[currentServicePathLen
- 1] == L
'\"';
242 if (doesServiceHaveCorrectPath
)
244 LOG(("The MozillaMaintenance service path is correct."));
245 servicePathWasWrong
= FALSE
;
248 // This is a recoverable situation so not logging as a warning
249 LOG(("The MozillaMaintenance path is NOT correct. It was: %ls",
250 currentServicePath
));
252 servicePathWasWrong
= TRUE
;
253 WCHAR fixedPath
[MAX_PATH
+ 1] = { L
'\0' };
254 wcsncpy(fixedPath
, currentServicePath
, MAX_PATH
);
255 PathUnquoteSpacesW(fixedPath
);
256 if (!PathRemoveFileSpecW(fixedPath
))
258 LOG_WARN(("Couldn't remove file spec. (%d)", GetLastError()));
261 if (!PathAppendSafe(fixedPath
, L
"maintenanceservice.exe"))
263 LOG_WARN(("Couldn't append file spec. (%d)", GetLastError()));
266 PathQuoteSpacesW(fixedPath
);
269 if (!ChangeServiceConfigW(service
, SERVICE_NO_CHANGE
, SERVICE_NO_CHANGE
,
270 SERVICE_NO_CHANGE
, fixedPath
, nullptr, nullptr,
271 nullptr, nullptr, nullptr, nullptr))
273 LOG_WARN(("Could not fix service path. (%d)", GetLastError()));
277 LOG(("Fixed service path to: %ls.", fixedPath
));
282 * Installs or upgrades the SVC_NAME service.
283 * If an existing service is already installed, we replace it with the
284 * currently running process.
286 * @param action The action to perform.
287 * @return TRUE if the service was installed/upgraded
290 SvcInstall(SvcInstallAction action
)
292 // Get a handle to the local computer SCM database with full access rights.
293 AutoServiceHandle
schSCManager(OpenSCManager(nullptr, nullptr,
294 SC_MANAGER_ALL_ACCESS
));
297 LOG_WARN(("Could not open service manager. (%d)", GetLastError()));
301 WCHAR newServiceBinaryPath
[MAX_PATH
+ 1];
302 if (!GetModuleFileNameW(nullptr, newServiceBinaryPath
,
303 sizeof(newServiceBinaryPath
) /
304 sizeof(newServiceBinaryPath
[0])))
306 LOG_WARN(("Could not obtain module filename when attempting to "
307 "install service. (%d)",
312 // Check if we already have the service installed.
313 AutoServiceHandle
schService(OpenServiceW(schSCManager
.get(),
315 SERVICE_ALL_ACCESS
));
316 DWORD lastError
= GetLastError();
317 if (!schService
&& ERROR_SERVICE_DOES_NOT_EXIST
!= lastError
)
319 // The service exists but we couldn't open it
320 LOG_WARN(("Could not open service. (%d)", GetLastError()));
326 // The service exists but it may not have the correct permissions.
327 // This could happen if the permissions were not set correctly originally
328 // or have been changed after the installation. This will reset the
329 // permissions back to allow limited user accounts.
330 if (!SetUserAccessServiceDACL(schService
.get()))
332 LOG_WARN(("Could not reset security ACE on service handle. It might not be "
333 "possible to start the service. This error should never "
334 "happen. (%d)", GetLastError()));
337 // The service exists and we opened it
339 if (!QueryServiceConfigW(schService
.get(), nullptr, 0, &bytesNeeded
) &&
340 GetLastError() != ERROR_INSUFFICIENT_BUFFER
)
342 LOG_WARN(("Could not determine buffer size for query service config. (%d)",
347 // Get the service config information, in particular we want the binary
348 // path of the service.
349 std::unique_ptr
<char[]> serviceConfigBuffer(new char[bytesNeeded
]);
350 if (!QueryServiceConfigW(schService
.get(),
351 reinterpret_cast<QUERY_SERVICE_CONFIGW
*>(serviceConfigBuffer
.get()),
352 bytesNeeded
, &bytesNeeded
))
354 LOG_WARN(("Could open service but could not query service config. (%d)",
358 QUERY_SERVICE_CONFIGW
&serviceConfig
=
359 *reinterpret_cast<QUERY_SERVICE_CONFIGW
*>(serviceConfigBuffer
.get());
361 // Check if we need to fix the service path
362 BOOL servicePathWasWrong
;
363 static BOOL alreadyCheckedFixServicePath
= FALSE
;
364 if (!alreadyCheckedFixServicePath
)
366 if (!FixServicePath(schService
.get(), serviceConfig
.lpBinaryPathName
,
367 servicePathWasWrong
))
369 LOG_WARN(("Could not fix service path. This should never happen. (%d)",
371 // True is returned because the service is pointing to
372 // maintenanceservice_tmp.exe so it actually was upgraded to the
373 // newest installed service.
376 else if (servicePathWasWrong
)
378 // Now that the path is fixed we should re-attempt the install.
379 // This current process' image path is maintenanceservice_tmp.exe.
380 // The service used to point to maintenanceservice_tmp.exe.
381 // The service was just fixed to point to maintenanceservice.exe.
382 // Re-attempting an install from scratch will work as normal.
383 alreadyCheckedFixServicePath
= TRUE
;
384 LOG(("Restarting install action: %d", action
));
385 return SvcInstall(action
);
389 // Ensure the service path is not quoted. We own this memory and know it to
390 // be large enough for the quoted path, so it is large enough for the
391 // unquoted path. This function cannot fail.
392 PathUnquoteSpacesW(serviceConfig
.lpBinaryPathName
);
394 // Obtain the existing maintenanceservice file's version number and
395 // the new file's version number. Versions are in the format of
397 DWORD existingA
, existingB
, existingC
, existingD
;
398 DWORD newA
, newB
, newC
, newD
;
399 BOOL obtainedExistingVersionInfo
=
400 GetVersionNumberFromPath(serviceConfig
.lpBinaryPathName
,
401 existingA
, existingB
,
402 existingC
, existingD
);
403 if (!GetVersionNumberFromPath(newServiceBinaryPath
, newA
,
406 LOG_WARN(("Could not obtain version number from new path"));
410 // Check if we need to replace the old binary with the new one
411 // If we couldn't get the old version info then we assume we should
413 if (ForceInstallSvc
== action
||
414 !obtainedExistingVersionInfo
||
415 (existingA
< newA
) ||
416 (existingA
== newA
&& existingB
< newB
) ||
417 (existingA
== newA
&& existingB
== newB
&&
419 (existingA
== newA
&& existingB
== newB
&&
420 existingC
== newC
&& existingD
< newD
))
423 // We have a newer updater, so update the description from the INI file.
424 UpdateServiceDescription(schService
.get());
432 if (!wcscmp(newServiceBinaryPath
, serviceConfig
.lpBinaryPathName
))
434 LOG(("File is already in the correct location, no action needed for "
435 "upgrade. The path is: \"%ls\"", newServiceBinaryPath
));
441 // Attempt to copy the new binary over top the existing binary.
442 // If there is an error we try to move it out of the way and then
443 // copy it in. First try the safest / easiest way to overwrite the file.
444 if (!CopyFileW(newServiceBinaryPath
,
445 serviceConfig
.lpBinaryPathName
, FALSE
))
447 LOG_WARN(("Could not overwrite old service binary file. "
448 "This should never happen, but if it does the next "
449 "upgrade will fix it, the service is not a critical "
450 "component that needs to be installed for upgrades "
451 "to work. (%d)", GetLastError()));
453 // We rename the last 3 filename chars in an unsafe way. Manually
454 // verify there are more than 3 chars for safe failure in MoveFileExW.
455 const size_t len
= wcslen(serviceConfig
.lpBinaryPathName
);
458 // Calculate the temp file path that we're moving the file to. This
459 // is the same as the proper service path but with a .old extension.
460 LPWSTR oldServiceBinaryTempPath
=
462 memset(oldServiceBinaryTempPath
, 0, (len
+ 1) * sizeof (WCHAR
));
463 wcsncpy(oldServiceBinaryTempPath
, serviceConfig
.lpBinaryPathName
, len
);
464 // Rename the last 3 chars to 'old'
465 wcsncpy(oldServiceBinaryTempPath
+ len
- 3, L
"old", 3);
467 // Move the current (old) service file to the temp path.
468 if (MoveFileExW(serviceConfig
.lpBinaryPathName
,
469 oldServiceBinaryTempPath
,
470 MOVEFILE_REPLACE_EXISTING
| MOVEFILE_WRITE_THROUGH
))
472 // The old binary is moved out of the way, copy in the new one.
473 if (!CopyFileW(newServiceBinaryPath
,
474 serviceConfig
.lpBinaryPathName
, FALSE
))
476 // It is best to leave the old service binary in this condition.
477 LOG_WARN(("The new service binary could not be copied in."
478 " The service will not be upgraded."));
483 LOG(("The new service binary was copied in by first moving the"
484 " old one out of the way."));
487 // Attempt to get rid of the old service temp path.
488 if (DeleteFileW(oldServiceBinaryTempPath
))
490 LOG(("The old temp service path was deleted: %ls.",
491 oldServiceBinaryTempPath
));
495 // The old temp path could not be removed. It will be removed
496 // the next time the user can't copy the binary in or on uninstall.
497 LOG_WARN(("The old temp service path was not deleted."));
502 // It is best to leave the old service binary in this condition.
503 LOG_WARN(("Could not move old service file out of the way from:"
504 " \"%ls\" to \"%ls\". Service will not be upgraded. (%d)",
505 serviceConfig
.lpBinaryPathName
,
506 oldServiceBinaryTempPath
, GetLastError()));
509 delete[] oldServiceBinaryTempPath
;
513 // It is best to leave the old service binary in this condition.
514 LOG_WARN(("Service binary path was less than 3, service will"
515 " not be updated. This should never happen."));
521 LOG(("The new service binary was copied in."));
524 // We made a copy of ourselves to the existing location.
525 // The tmp file (the process of which we are executing right now) will be
526 // left over. Attempt to delete the file on the next reboot.
527 if (MoveFileExW(newServiceBinaryPath
, nullptr,
528 MOVEFILE_DELAY_UNTIL_REBOOT
))
530 LOG(("Deleting the old file path on the next reboot: %ls.",
531 newServiceBinaryPath
));
535 LOG_WARN(("Call to delete the old file path failed: %ls.",
536 newServiceBinaryPath
));
542 // We don't need to copy ourselves to the existing location.
543 // The tmp file (the process of which we are executing right now) will be
544 // left over. Attempt to delete the file on the next reboot.
545 MoveFileExW(newServiceBinaryPath
, nullptr, MOVEFILE_DELAY_UNTIL_REBOOT
);
547 // nothing to do, we already have a newer service installed
551 // If the service does not exist and we are upgrading, don't install it.
552 if (UpgradeSvc
== action
)
554 // The service does not exist and we are upgrading, so don't install it
558 // Quote the path only if it contains spaces.
559 PathQuoteSpacesW(newServiceBinaryPath
);
560 // The service does not already exist so create the service as on demand
561 schService
.reset(CreateServiceW(schSCManager
.get(), SVC_NAME
, SVC_DISPLAY_NAME
,
562 SERVICE_ALL_ACCESS
, SERVICE_WIN32_OWN_PROCESS
,
563 SERVICE_DEMAND_START
, SERVICE_ERROR_NORMAL
,
564 newServiceBinaryPath
, nullptr, nullptr,
565 nullptr, nullptr, nullptr));
568 LOG_WARN(("Could not create Windows service. "
569 "This error should never happen since a service install "
570 "should only be called when elevated. (%d)", GetLastError()));
574 if (!SetUserAccessServiceDACL(schService
.get()))
576 LOG_WARN(("Could not set security ACE on service handle, the service will not "
577 "be able to be started from unelevated processes. "
578 "This error should never happen. (%d)",
582 UpdateServiceDescription(schService
.get());
588 * Stops the Maintenance service.
590 * @return TRUE if successful.
595 // Get a handle to the local computer SCM database with full access rights.
596 AutoServiceHandle
schSCManager(OpenSCManager(nullptr, nullptr,
597 SC_MANAGER_ALL_ACCESS
));
600 LOG_WARN(("Could not open service manager. (%d)", GetLastError()));
605 AutoServiceHandle
schService(OpenServiceW(schSCManager
.get(), SVC_NAME
,
606 SERVICE_ALL_ACCESS
));
609 LOG_WARN(("Could not open service. (%d)", GetLastError()));
613 LOG(("Sending stop request..."));
614 SERVICE_STATUS status
;
615 SetLastError(ERROR_SUCCESS
);
616 if (!ControlService(schService
.get(), SERVICE_CONTROL_STOP
, &status
) &&
617 GetLastError() != ERROR_SERVICE_NOT_ACTIVE
)
619 LOG_WARN(("Error sending stop request. (%d)", GetLastError()));
622 schSCManager
.reset();
625 LOG(("Waiting for service stop..."));
626 DWORD lastState
= WaitForServiceStop(SVC_NAME
, 30);
628 // The service can be in a stopped state but the exe still in use
629 // so make sure the process is really gone before proceeding
630 WaitForProcessExit(L
"maintenanceservice.exe", 30);
631 LOG(("Done waiting for service stop, last service state: %d", lastState
));
633 return lastState
== SERVICE_STOPPED
;
637 * Uninstalls the Maintenance service.
639 * @return TRUE if successful.
644 // Get a handle to the local computer SCM database with full access rights.
645 AutoServiceHandle
schSCManager(OpenSCManager(nullptr, nullptr,
646 SC_MANAGER_ALL_ACCESS
));
649 LOG_WARN(("Could not open service manager. (%d)", GetLastError()));
654 AutoServiceHandle
schService(OpenServiceW(schSCManager
.get(), SVC_NAME
,
655 SERVICE_ALL_ACCESS
));
658 LOG_WARN(("Could not open service. (%d)", GetLastError()));
662 //Stop the service so it deletes faster and so the uninstaller
663 // can actually delete its EXE.
664 DWORD totalWaitTime
= 0;
665 SERVICE_STATUS status
;
666 static const int maxWaitTime
= 1000 * 60; // Never wait more than a minute
667 if (ControlService(schService
.get(), SERVICE_CONTROL_STOP
, &status
))
671 Sleep(status
.dwWaitHint
);
672 totalWaitTime
+= (status
.dwWaitHint
+ 10);
673 if (status
.dwCurrentState
== SERVICE_STOPPED
)
677 else if (totalWaitTime
> maxWaitTime
)
682 while (QueryServiceStatus(schService
.get(), &status
));
685 // Delete the service or mark it for deletion
686 BOOL deleted
= DeleteService(schService
.get());
689 deleted
= (GetLastError() == ERROR_SERVICE_MARKED_FOR_DELETE
);
696 * Sets the access control list for user access for the specified service.
698 * @param hService The service to set the access control list on
699 * @return TRUE if successful
702 SetUserAccessServiceDACL(SC_HANDLE hService
)
704 PACL pNewAcl
= nullptr;
705 PSECURITY_DESCRIPTOR psd
= nullptr;
706 DWORD lastError
= SetUserAccessServiceDACL(hService
, pNewAcl
, psd
);
709 LocalFree((HLOCAL
)pNewAcl
);
713 LocalFree((LPVOID
)psd
);
715 return ERROR_SUCCESS
== lastError
;
719 * Sets the access control list for user access for the specified service.
721 * @param hService The service to set the access control list on
722 * @param pNewAcl The out param ACL which should be freed by caller
723 * @param psd out param security descriptor, should be freed by caller
724 * @return ERROR_SUCCESS if successful
727 SetUserAccessServiceDACL(SC_HANDLE hService
, PACL
&pNewAcl
,
728 PSECURITY_DESCRIPTOR psd
)
730 // Get the current security descriptor needed size
732 if (!QueryServiceObjectSecurity(hService
, DACL_SECURITY_INFORMATION
,
735 if (GetLastError() != ERROR_INSUFFICIENT_BUFFER
)
737 LOG_WARN(("Could not query service object security size. (%d)",
739 return GetLastError();
743 psd
= (PSECURITY_DESCRIPTOR
)LocalAlloc(LPTR
, size
);
746 LOG_WARN(("Could not allocate security descriptor. (%d)",
748 return ERROR_INSUFFICIENT_BUFFER
;
751 // Get the actual security descriptor now
752 if (!QueryServiceObjectSecurity(hService
, DACL_SECURITY_INFORMATION
,
755 LOG_WARN(("Could not allocate security descriptor. (%d)",
757 return GetLastError();
761 // Get the current DACL from the security descriptor.
763 BOOL bDaclPresent
= FALSE
;
764 BOOL bDaclDefaulted
= FALSE
;
765 if ( !GetSecurityDescriptorDacl(psd
, &bDaclPresent
, &pacl
,
768 LOG_WARN(("Could not obtain DACL. (%d)", GetLastError()));
769 return GetLastError();
773 DWORD SIDSize
= SECURITY_MAX_SID_SIZE
;
774 sid
= LocalAlloc(LMEM_FIXED
, SIDSize
);
777 LOG_WARN(("Could not allocate SID memory. (%d)", GetLastError()));
778 return GetLastError();
781 if (!CreateWellKnownSid(WinBuiltinUsersSid
, nullptr, sid
, &SIDSize
))
783 DWORD lastError
= GetLastError();
784 LOG_WARN(("Could not create well known SID. (%d)", lastError
));
789 // Lookup the account name, the function fails if you don't pass in
790 // a buffer for the domain name but it's not used since we're using
791 // the built in account Sid.
792 SID_NAME_USE accountType
;
793 WCHAR accountName
[UNLEN
+ 1] = { L
'\0' };
794 WCHAR domainName
[DNLEN
+ 1] = { L
'\0' };
795 DWORD accountNameSize
= UNLEN
+ 1;
796 DWORD domainNameSize
= DNLEN
+ 1;
797 if (!LookupAccountSidW(nullptr, sid
, accountName
,
799 domainName
, &domainNameSize
, &accountType
))
801 LOG_WARN(("Could not lookup account Sid, will try Users. (%d)",
803 wcsncpy(accountName
, L
"Users", UNLEN
);
806 // We already have the group name so we can get rid of the SID
810 // Build the ACE, BuildExplicitAccessWithName cannot fail so it is not logged.
812 BuildExplicitAccessWithNameW(&ea
, accountName
,
813 SERVICE_START
| SERVICE_STOP
| GENERIC_READ
,
814 SET_ACCESS
, NO_INHERITANCE
);
815 DWORD lastError
= SetEntriesInAclW(1, (PEXPLICIT_ACCESS
)&ea
, pacl
, &pNewAcl
);
816 if (ERROR_SUCCESS
!= lastError
)
818 LOG_WARN(("Could not set entries in ACL. (%d)", lastError
));
822 // Initialize a new security descriptor.
823 SECURITY_DESCRIPTOR sd
;
824 if (!InitializeSecurityDescriptor(&sd
, SECURITY_DESCRIPTOR_REVISION
))
826 LOG_WARN(("Could not initialize security descriptor. (%d)",
828 return GetLastError();
831 // Set the new DACL in the security descriptor.
832 if (!SetSecurityDescriptorDacl(&sd
, TRUE
, pNewAcl
, FALSE
))
834 LOG_WARN(("Could not set security descriptor DACL. (%d)",
836 return GetLastError();
839 // Set the new security descriptor for the service object.
840 if (!SetServiceObjectSecurity(hService
, DACL_SECURITY_INFORMATION
, &sd
))
842 LOG_WARN(("Could not set object security. (%d)",
844 return GetLastError();
847 // Woohoo, raise the roof
848 LOG(("User access was set successfully on the service."));
849 return ERROR_SUCCESS
;