MSDN Docs

System Access Control List (SACL)

Overview

A System Access Control List (SACL) is a type of access control list that defines the audit policy for an object. While DACLs control who can access an object, SACLs specify which access attempts should be recorded in the security audit log.

SACLs are associated with security descriptors and can be applied to most Windows kernel objects such as files, registry keys, processes, and threads.

Structure

Each SACL consists of one or more ACE (Access Control Entry) structures. The relevant fields are:

FieldDescription
ACE TypeIndicates audit success, failure, or both (e.g., SYSTEM_AUDIT_ACE_TYPE).
ACE FlagsInheritance flags and object type flags.
Access MaskSpecifies the rights being audited (e.g., FILE_READ_DATA).
SidSecurity identifier of the principal being audited.

When to Use SACL

  • Compliance requirements (PCI DSS, HIPAA) that demand audit trails.
  • Forensics – tracking who accessed or attempted to access sensitive objects.
  • Debugging security‑related issues in kernel‑mode drivers.

Relevant API Functions

Below are the most common Win32 and Native APIs for working with SACLs.

BOOL GetSecurityInfo(
    HANDLE handle,
    SE_OBJECT_TYPE ObjectType,
    SECURITY_INFORMATION SecurityInfo,
    PSID *ppsidOwner,
    PSID *ppsidGroup,
    PACL *ppDacl,
    PACL *ppSacl,
    PSECURITY_DESCRIPTOR *ppSecurityDescriptor
);

DWORD SetSecurityInfo(
    HANDLE handle,
    SE_OBJECT_TYPE ObjectType,
    SECURITY_INFORMATION SecurityInfo,
    PSID psidOwner,
    PSID psidGroup,
    PACL pDacl,
    PACL pSacl
);

NTSTATUS NtQuerySecurityObject(
    HANDLE Handle,
    SECURITY_INFORMATION SecurityInformation,
    PSECURITY_DESCRIPTOR SecurityDescriptor,
    ULONG Length,
    PULONG ReturnLength
);

Code Example – Adding a SACL to a File

This example adds a SYSTEM_AUDIT_ACE_TYPE that logs both successful and failed reads on C:\Logs\audit.txt for the Everyone group.

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

int wmain()
{
    LPCWSTR filePath = L"C:\\Logs\\audit.txt";
    PSID pEveryoneSID = NULL;
    PACL pOldSacl = NULL, pNewSacl = NULL;
    PSECURITY_DESCRIPTOR pSD = NULL;
    EXPLICIT_ACCESSW ea = {0};

    // Create a SID for the Everyone group.
    SID_IDENTIFIER_AUTHORITY SIDAuthWorld = SECURITY_WORLD_SID_AUTHORITY;
    if (!AllocateAndInitializeSid(&SIDAuthWorld, 1,
                                  SECURITY_WORLD_RID,
                                  0,0,0,0,0,0,0,
                                  &pEveryoneSID))
    {
        wprintf(L"AllocateAndInitializeSid Error %u\\n", GetLastError());
        return 1;
    }

    // Get the current SACL.
    DWORD dwRes = GetSecurityInfo(
        CreateFileW(filePath, READ_CONTROL, 0, NULL,
                    OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL),
        SE_FILE_OBJECT,
        SACL_SECURITY_INFORMATION,
        NULL, NULL, NULL, &pOldSacl,
        &pSD);
    if (dwRes != ERROR_SUCCESS) {
        wprintf(L"GetSecurityInfo error %u\\n", dwRes);
        return 1;
    }

    // Set up an audit ACE.
    ea.grfAccessPermissions = FILE_GENERIC_READ;
    ea.grfAccessMode = NO_ACCESS;
    ea.grfInheritance = SUB_CONTAINERS_AND_OBJECTS_INHERIT;
    ea.Trustee.TrusteeForm = TRUSTEE_IS_SID;
    ea.Trustee.TrusteeType = TRUSTEE_IS_WELL_KNOWN_GROUP;
    ea.Trustee.ptstrName   = (LPWSTR)pEveryoneSID;

    // Add the audit entry.
    dwRes = SetEntriesInAclW(1, &ea, pOldSacl, &pNewSacl);
    if (dwRes != ERROR_SUCCESS) {
        wprintf(L"SetEntriesInAcl error %u\\n", dwRes);
        return 1;
    }

    // Apply the updated SACL.
    dwRes = SetSecurityInfo(
        CreateFileW(filePath, WRITE_DAC, 0, NULL,
                    OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL),
        SE_FILE_OBJECT,
        SACL_SECURITY_INFORMATION,
        NULL, NULL, NULL, pNewSacl);
    if (dwRes != ERROR_SUCCESS) {
        wprintf(L"SetSecurityInfo error %u\\n", dwRes);
        return 1;
    }

    wprintf(L"SACL successfully added.\\n");
    return 0;
}