BUG: Service Auditing Fails on Windows 2000 (301037)



The information in this article applies to:

  • Microsoft Win32 Application Programming Interface (API), when used with:
    • the operating system: Microsoft Windows 2000

This article was previously published under Q301037

SYMPTOMS

A service is a securable object that should be auditable. However, on Windows 2000 prior to Service Pack 2, access to a service object does not generate the expected audit events in the security log.

STATUS

Microsoft has confirmed that this is a bug in the Microsoft products that are listed at the beginning of this article.

This bug was corrected in Windows 2000 Service Pack (SP) 2.

MORE INFORMATION

Access to a securable object can be audited on Windows NT and Windows 2000 as long as the following is true:
  1. An administrator has enabled the Audit object access entry within the system's Local Security Policy.
  2. The object's security descriptor has a system access-control list (SACL) that contains the necessary access control entries (ACEs) specifying which users should be audited.
On Windows NT 4.0 and Windows 2000 with SP2, auditing works as detailed below for service objects:
  • When a service handle is opened, an audit event with ID 560 is logged in the security log.
  • When a service handle is closed, an audit event with ID 562 is logged in the security log.

Sample Code

The following sample code can be used to audit both successful and failed access to a service. This code adds a SACL to the security descriptor for the service. The SACL contains an ACE for the Everyone group.

#include <windows.h>
#include <stdio.h>

void ReportError(char* apiName) {

   LPVOID lpMsgBuf;
   char   szMsg[4096];

   FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER
         | FORMAT_MESSAGE_FROM_SYSTEM
         | FORMAT_MESSAGE_IGNORE_INSERTS,
         NULL, GetLastError(),
         MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
         (LPTSTR) &lpMsgBuf, 0, NULL );

   printf(szMsg, "%s failed with error %ld\n%s", apiName, 
         GetLastError(), lpMsgBuf);
   
   LocalFree(lpMsgBuf);
}

BOOL EnableSecurityName() {

   // A process that tries to read or write a SACL needs
   // to have and enable the SE_SECURITY_NAME privilege.
   
   LUID   SecurityNameValue;
   HANDLE hToken;
   TOKEN_PRIVILEGES tp;

   if (!OpenProcessToken(GetCurrentProcess(),
         TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)) {
      ReportError("OpenProcessToken");
      return FALSE;
   }

   if (!LookupPrivilegeValue(NULL, SE_SECURITY_NAME,
         &SecurityNameValue)) {
      ReportError("LookupPrivilegeValue");
      return FALSE;
   }

   tp.PrivilegeCount = 1;
   tp.Privileges[0].Luid = SecurityNameValue;
   tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;

   if (!AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(tp),
         NULL, NULL)) {
      ReportError("AdjustTokenPrivileges");
      return FALSE;
   }

   return TRUE ;
}

BOOL AddEveryoneAceToServiceSacl(SC_HANDLE hObjectHandle,
      DWORD dwAccessMask) {

   BOOL  bReturn   = FALSE;
   PSID  psidWorld = NULL;
   PACL  pNewACL   = NULL;
   DWORD dwNewACLSize;
   UCHAR NewSD[SECURITY_DESCRIPTOR_MIN_LENGTH];

   SID_IDENTIFIER_AUTHORITY authWorld = SECURITY_WORLD_SID_AUTHORITY;

   __try {

      // Build the "Everyone" SID
      if (!AllocateAndInitializeSid(&authWorld, 1, SECURITY_WORLD_RID,
            0, 0, 0, 0, 0, 0, 0, &psidWorld)) {
         ReportError("AllocateAndInitializeSid");
         __leave;
      }

      // Initialize new SD
      if (!InitializeSecurityDescriptor(&NewSD,
            SECURITY_DESCRIPTOR_REVISION)) {
         ReportError("InitializeSecurityDescriptor");
         __leave;
      }

      // Compute size needed for the new ACL
      dwNewACLSize = sizeof(ACL) + sizeof(ACCESS_ALLOWED_ACE) 
            + GetLengthSid(psidWorld) - sizeof(DWORD);
 
      // Allocate memory for new ACL
      pNewACL = (PACL) HeapAlloc(GetProcessHeap(), 0, dwNewACLSize);
      if (!pNewACL) {
         ReportError("HeapAlloc");
         __leave;
      }

      // Initialize the new ACL
      if (!InitializeAcl(pNewACL, dwNewACLSize, ACL_REVISION2)) {
         ReportError("InitializeAcl");
         __leave;
      }
 
      // Add the audit ACE to the new SACL
      if (!AddAuditAccessAce(pNewACL, ACL_REVISION2,
            dwAccessMask, psidWorld, TRUE /* Audit Success */,
            TRUE /* Audit Failure */)) {
         ReportError("AddAuditAccessAce");
         __leave;
      }

      // Add the SACL to the new SD
      if (!SetSecurityDescriptorSacl(&NewSD, TRUE, pNewACL, FALSE)) {
         ReportError("SetSecurityDescriptorSacl");
         __leave;
      }

      // Set the security on the service using the new SD
      if (!SetServiceObjectSecurity(hObjectHandle,
            SACL_SECURITY_INFORMATION, &NewSD)) {
         ReportError("SetServiceObjectSecurity");
         __leave;
      }

      bReturn = TRUE;
   }
   __finally
   {
      // Free the allocated SID.
      if (psidWorld) FreeSid(psidWorld);

      // Free the memory allocated for the new ACL.
      if (pNewACL) HeapFree(GetProcessHeap(), 0, pNewACL);
   }

   return bReturn;
}

void main(int argc, char *argv[]) {

   SC_HANDLE hSCM     = NULL;
   SC_HANDLE hService = NULL;

   if (argc != 2) {
     printf("Usage: %s <ServiceName>\n", argv[0]);
     return;
   }

   __try {

      // Enable the SE_SECURITY_NAME privilege.
      if (!EnableSecurityName())
         __leave;

      hSCM = OpenSCManager(NULL, 0, SC_MANAGER_ALL_ACCESS);
      if (!hSCM) {
         ReportError("OpenSCManager");
         __leave;
      }

      hService = OpenService(hSCM, argv[1],
            SERVICE_ALL_ACCESS | ACCESS_SYSTEM_SECURITY);
      if (!hService) {
         ReportError("OpenService");
         __leave;
      }

      // Add the ACE to the SACL.
      if (!AddEveryoneAceToServiceSacl(hService,
            GENERIC_ALL | ACCESS_SYSTEM_SECURITY))
         printf("Could not add audit entry to SACL\n");
      else
         printf("Added audit entry to SACL\n");

      // NOTE
      // 
      // The following audit mask combinations duplicate the
      // different audit settings that you can apply to files
      // using Explorer in Windows NT 4.0
      // 
      // Read                FILE_GENERIC_READ | ACCESS_SYSTEM_SECURITY
      // Write               FILE_GENERIC_WRITE | ACCESS_SYSTEM_SECURITY
      // Execute             FILE_GENERIC_EXECUTE
      // Delete              DELETE
      // Change Permissions  WRITE_DAC
      // Write Owner         WRITE_OWNER

   } __finally {

      if (hService)  CloseServiceHandle(hService);

      if (hSCM)      CloseServiceHandle(hSCM);
   }

}
				

Modification Type:MajorLast Reviewed:11/3/2003
Keywords:kbACL kbAPI kbbug kbnofix KbSECHack kbSecurity KbSECVulnerability kbService KB301037