HOWTO: Control Access to a Windows NT Service

ID: Q180116

The information in this article applies to:

SUMMARY

Windows NT 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);
   }

Keywords          : kbcode kbAPI kbKernBase kbService kbGrpKernBase 
Version           : WINNT:4.0
Platform          : winnt
Issue type        : kbhowto

Last Reviewed: August 26, 1998