Modifying the DACL for a Windows Service Example
A service control program can create or modify the DACL associated with a service to control access. To retrieve the DACL associated with a service object, we can use QueryServiceObjectSecurity() function and to set the DACL, use the SetServiceObjectSecurity() function. Any changes made to the SECURITY_DESCRIPTOR associated with the service object are persistent until the service is removed from the system.
The following code fragment example creates and sets a new DACL for the service. The code merges one access control entry (ACE) to the existing DACL for the service. The new ACE grants the Guest account start, stop, delete, and READ_CONTROL access to the specified service. Access to the service can be modified by the AccessPermissions parameter passed to the BuildExplicitAccessWithName() function. The szSvcName variable is a global variable that contains the name of the service.
// Purpose: Updates the service DACL to grant start, stop, delete, and read control access to the Guest account.
// Parameters: None
// Return value: None
void __stdcall DoUpdateSvcDacl(void)
{
EXPLICIT_ACCESS ea;
SECURITY_DESCRIPTOR sd;
PSECURITY_DESCRIPTOR psd = NULL;
PACL pacl = NULL;
PACL pNewAcl = NULL;
BOOL bDaclPresent = FALSE;
BOOL bDaclDefaulted = FALSE;
DWORD dwError = 0;
DWORD dwSize = 0;
DWORD dwBytesNeeded = 0;
// Get a handle to the SCM database.
schSCManager = OpenSCManager(
NULL, // local computer
NULL, // ServicesActive database
SC_MANAGER_ALL_ACCESS); // full access rights
if (schSCManager == NULL)
{
wprintf(LOpenSCManager() failed, error %u\n, GetLastError());
return;
}
else
wprintf(LOpenSCManager() is OK!\n);
// Get a handle to the service
schService = OpenService(
schSCManager, // SCManager database
szSvcName, // name of service
READ_CONTROL | WRITE_DAC); // access
if (schService == NULL)
{
wprintf(LOpenService() failed, error %u\n, GetLastError());
CloseServiceHandle(schSCManager);
return;
}
else
wprintf(LOpenService() is pretty fine!\n);
// Get the current security descriptor.
if (!QueryServiceObjectSecurity(schService,
DACL_SECURITY_INFORMATION,
&psd, // using NULL does not work on all versions
0,
&dwBytesNeeded))
{
if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
{
dwSize = dwBytesNeeded;
psd = (PSECURITY_DESCRIPTOR)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY, dwSize);
if (psd == NULL)
{
// Note: HeapAlloc does not support GetLastError().
wprintf(LHeapAlloc() failed!\n);
goto dacl_cleanup;
}
if (!QueryServiceObjectSecurity(schService,DACL_SECURITY_INFORMATION, psd, dwSize, &dwBytesNeeded))
{
wprintf(LQueryServiceObjectSecurity() failed, error %u\n, GetLastError());
goto dacl_cleanup;
}
}
else
{
wprintf(LQueryServiceObjectSecurity() failed, error %u\n, GetLastError());
goto dacl_cleanup;
}
}
// Get the DACL.
if (!GetSecurityDescriptorDacl(psd, &bDaclPresent, &pacl, &bDaclDefaulted))
{
wprintf(LGetSecurityDescriptorDacl() failed, error %u\n, GetLastError());
goto dacl_cleanup;
}
else
wprintf(LGetSecurityDescriptorDacl() totally working!\n);
// Build the ACE.
BuildExplicitAccessWithName(&ea, LGUEST,SERVICE_START | SERVICE_STOP | READ_CONTROL | DELETE, SET_ACCESS, NO_INHERITANCE);
dwError = SetEntriesInAcl(1, &ea, pacl, &pNewAcl);
if (dwError != ERROR_SUCCESS)
{
wprintf(LSetEntriesInAcl() failed, error %u\n, dwError);
goto dacl_cleanup;
}
else
wprintf(LSetEntriesInAcl() is no problem!\n);
// Initialize a new security descriptor.
if (!InitializeSecurityDescriptor(&sd,SECURITY_DESCRIPTOR_REVISION))
{
wprintf(LInitializeSecurityDescriptor() failed, error %u\n, GetLastError());
goto dacl_cleanup;
}
else
wprintf(LInitializeSecurityDescriptor() is working perfectly!\n);
// Set the new DACL in the security descriptor.
if (!SetSecurityDescriptorDacl(&sd, TRUE, pNewAcl, FALSE))
{
wprintf(LSetSecurityDescriptorDacl() failed, error %u\n, GetLastError());
goto dacl_cleanup;
}
else
wprintf(LSetSecurityDescriptorDacl() is absolutely working!\n);
// Set the new DACL for the service object.
if (!SetServiceObjectSecurity(schService, DACL_SECURITY_INFORMATION, &sd))
{
wprintf(LSetServiceObjectSecurity() failed, error %u\n, GetLastError());
goto dacl_cleanup;
}
else
wprintf(LSetServiceObjectSecurity() - Service DACL updated successfully\n);
dacl_cleanup:
CloseServiceHandle(schSCManager);
CloseServiceHandle(schService);
if(NULL != pNewAcl)
LocalFree((HLOCAL)pNewAcl);
if(NULL != psd)
HeapFree(GetProcessHeap(), 0, (LPVOID)psd);
}