How To Control Access to a Windows NT, Windows 2000, and Windows XP Service (180116)
The information in this article applies to:
- Microsoft Win32 Application Programming Interface (API), when used with:
- the operating system: Microsoft Windows NT 4.0
- the operating system: Microsoft Windows 2000
- the operating system: Microsoft Windows XP
This article was previously published under Q180116 SUMMARY
Microsoft Windows NT, Windows 2000, and Windows XP Services are securable objects in that they are associated with a Security Descriptor and a Discretionary Access Control List (DACL). The DACL associated with the Service has sole control over access to the Service. No special user rights or privileges are needed to manipulate a Service. An application requests a handle to a Service via the OpenService or CreateService API. If the user has the requested access to the Service object, a valid handle is returned. If the requested access is denied by the system, an error code 5 or "access denied" is returned. The following specific access types are defined for a Service:
Access Description
---------------------------------------------------------------
SERVICE_CHANGE_CONFIG Change Service configuration
information
SERVICE_ENUMERATE_DEPENDENTS Enumerate all Services dependent
on the Service
SERVICE_INTERROGATE Ask the Service to report status
immediately
SERVICE_PAUSE_CONTINUE Pause or continue
SERVICE_QUERY_CONFIG Query Service configuration
information
SERVICE_QUERY_STATUS Query the status
SERVICE_START Start
SERVICE_STOP Stop
SERVICE_USER_DEFINED_CONTROL Send a user defined control
Other access types defined for a Service include READ_CONTROL, WRITE_OWNER,
WRITE_DAC, and DELETE. These access types apply to all securable objects.
Please refer to the Platform SDK Documentation for more information.
When installing a Service through the CreateService API, the operating
system creates a DACL for the Service. The following DACL is created:
User or Group Access
---------------------------------------------------------------
The Everyone Group SERVICE_ENUMERATE_DEPENDENTS
SERVICE_INTERROGATE
SERVICE_QUERY_CONFIG
SERVICE_QUERY_STATUS
SERVICE_USER_DEFINED_CONTROL
READ_CONTROL
The Power Users Group SERVICE_ENUMERATE_DEPENDENTS
LocalSystem (System) SERVICE_INTERROGATE
SERVICE_PAUSE_CONTINUE
SERVICE_QUERY_CONFIG
SERVICE_QUERY_STATUS
SERVICE_START
SERVICE_STOP
SERVICE_USER_DEFINED_CONTROL
READ_CONTROL
The Administrators Group SERVICE_CHANGE_CONFIG
The Server Operators Group SERVICE_ENUMERATE_DEPENDENTS
SERVICE_INTERROGATE
SERVICE_PAUSE_CONTINUE
SERVICE_QUERY_CONFIG
SERVICE_QUERY_STATUS
SERVICE_START
SERVICE_STOP
SERVICE_USER_DEFINED_CONTROL
READ_CONTROL
WRITE_OWNER
WRITE_DAC
DELETE
MORE INFORMATION
An application can create or modify the DACL associated with a Service
object to control access. Unfortunately, the CreateService API does not
allow you to pass a SECURITY_ATTRIBUTES or SECURITY_DESCRIPTOR structure.
The DACL associated with a Service object can be obtained via the
QueryServiceObjectSecurity API and can be set via the
SetServiceObjectSecurity API. Any changes made to the SECURITY_DESCRIPTOR
associated with the Service object are persistent until the Service is
removed from the system.
The following sample code creates and sets a new DACL for the Service
specified in the command line. The sample 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 BuildExplicitAccessWithName().
Sample Code
Libraries required: ADVAPI32.LIB
#include <windows.h>
#include <aclapi.h>
#include <stdio.h>
#include <tchar.h>
void DisplayError(DWORD dwError, LPTSTR pszAPI)
{
LPVOID lpvMessageBuffer;
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM,
NULL, dwError,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR)&lpvMessageBuffer, 0, NULL);
//Now display this string.
_tprintf(TEXT("ERROR: API = %s.\n"), pszAPI);
_tprintf(TEXT(" error code = %u.\n"), dwError);
_tprintf(TEXT(" message = %s.\n"),
(LPTSTR)lpvMessageBuffer);
// Free the buffer allocated by the system.
LocalFree(lpvMessageBuffer);
ExitProcess(dwError);
}
void _tmain(int argc, TCHAR *argv[])
{
BOOL bDaclPresent = FALSE;
BOOL bDaclDefaulted = FALSE;
DWORD dwError = 0;
DWORD dwSize = 0;
EXPLICIT_ACCESS ea;
PACL pacl = NULL;
PACL pNewAcl = NULL;
PSECURITY_DESCRIPTOR psd;
SC_HANDLE schManager = NULL;
SC_HANDLE schService = NULL;
SECURITY_DESCRIPTOR sd;
if (argc != 2){
_tprintf(TEXT("Usage: %s [service name]\n"), argv[0]);
return;
}
//
// Obtain a handle to the Service Controller.
//
schManager = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT);
if (schManager == NULL)
DisplayError(GetLastError(), TEXT("OpenSCManager"));
//
// Obtain a handle to the service.
//
schService = OpenService(schManager, argv[1],
READ_CONTROL | WRITE_DAC);
if (schService == NULL)
DisplayError(GetLastError(), TEXT("OpenService"));
//
// Get the current security descriptor.
//
if (!QueryServiceObjectSecurity(schService,
DACL_SECURITY_INFORMATION, psd, 0, &dwSize)){
if (GetLastError() == ERROR_INSUFFICIENT_BUFFER){
psd = (PSECURITY_DESCRIPTOR)HeapAlloc(GetProcessHeap(),
HEAP_ZERO_MEMORY, dwSize);
if (psd == NULL){
DisplayError(0, TEXT("HeapAlloc"));
// note HeapAlloc does not support GetLastError()
}
if (!QueryServiceObjectSecurity(schService,
DACL_SECURITY_INFORMATION, psd, dwSize, &dwSize))
DisplayError(GetLastError(),
TEXT("QueryServiceObjectSecurity"));
}
else
DisplayError(GetLastError(),
TEXT("QueryServiceObjectSecurity"));
}
//
// Get the DACL.
//
if (!GetSecurityDescriptorDacl(psd, &bDaclPresent, &pacl,
&bDaclDefaulted))
DisplayError(GetLastError(), TEXT("GetSecurityDescriptorDacl"));
//
// Build the ACE.
//
BuildExplicitAccessWithName(&ea, TEXT("GUEST"),
SERVICE_START | SERVICE_STOP | READ_CONTROL | DELETE,
SET_ACCESS, NO_INHERITANCE);
dwError = SetEntriesInAcl(1, &ea, pacl, &pNewAcl);
if (dwError != ERROR_SUCCESS)
DisplayError(dwError, TEXT("SetEntriesInAcl"));
//
// Initialize a NEW Security Descriptor.
//
if (!InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION))
DisplayError(GetLastError(),
TEXT("InitializeSecurityDescriptor"));
//
// Set the new DACL in the Security Descriptor.
//
if (!SetSecurityDescriptorDacl(&sd, TRUE, pNewAcl, FALSE))
DisplayError(GetLastError(), TEXT("SetSecurityDescriptorDacl"));
//
// Set the new DACL for the service object.
//
if (!SetServiceObjectSecurity(schService, DACL_SECURITY_INFORMATION,
&sd))
DisplayError(GetLastError(), TEXT("SetServiceObjectSecurity"));
//
// Close the handles.
//
if (!CloseServiceHandle(schManager))
DisplayError(GetLastError(), TEXT("CloseServiceHandle"));
if (!CloseServiceHandle(schService))
DisplayError(GetLastError(), TEXT("CloseServiceHandle"));
//
// Free buffers.
//
LocalFree((HLOCAL)pNewAcl);
HeapFree(GetProcessHeap(), 0, (LPVOID)psd);
}
Modification Type: | Minor | Last Reviewed: | 6/29/2004 |
---|
Keywords: | kbACL kbAPI kbcode kbhowto kbKernBase kbSecurity kbService KB180116 |
---|
|