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.
STATUSMicrosoft 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:
- An administrator has enabled the Audit object access entry
within the system's Local Security Policy.
- 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: | Major | Last Reviewed: | 11/3/2003 |
---|
Keywords: | kbACL kbAPI kbbug kbnofix KbSECHack kbSecurity KbSECVulnerability kbService KB301037 |
---|
|