Microsoft KB Archive/274432

{|
 * width="100%"|

BUG: BuildSecurityDescriptor Function Does Not Properly Build SACL

 * }

Q274432

-

The information in this article applies to:


 * Microsoft Win32 Application Programming Interface (API), included with:
 * the operating system: Microsoft Windows NT 4.0

-

SYMPTOMS
The BuildSecurityDescriptor function does not properly build a System Access-Control List (SACL). The SACL that is returned by the BuildSecurityDescriptor function in the built security descriptor contains only the last EXPLICIT_ACCESS information.

RESOLUTION
The low-level access control functions InitializeAcl, AddAuditAccessAce, and SetSecurityDescriptorSacl can be used as a workaround to build the SACL. The sample code in the &quot;More Information&quot; section of this article demonstrates how to use low-level access control functions.

STATUS
Microsoft has confirmed this to be a bug in the Microsoft products listed at the beginning of this article. This bug has been fixed in Microsoft Windows 2000.

MORE INFORMATION
The following sample code reproduces the symptoms of the BuildSecurityDescriptor function when an SACL is built on Windows NT 4.0:

#include 
 * 1) include 
 * 2) include 

void DumpExplicitEntriesInAcl(PACL pAcl) {   DWORD               dwError; ULONG              cCountOfExplicitEntries = 0; PEXPLICIT_ACCESS   pListOfExplicitEntries;

// Find-out how many entries are really there !! dwError = GetExplicitEntriesFromAcl(pAcl,         &cCountOfExplicitEntries,         &pListOfExplicitEntries); if (dwError != ERROR_SUCCESS) {       printf(&quot;GetExplicitEntriesFromAcl failed with error : %u\n\n&quot;, dwError); return; }

printf(&quot;cCountOfExplicitEntries : %d\n&quot;, cCountOfExplicitEntries); printf(&quot;******\n&quot;);

for (ULONG i = 0; i < cCountOfExplicitEntries; i++) {       printf(&quot;++++++\n&quot;); switch (pListOfExplicitEntries[i].Trustee.TrusteeForm) {           case TRUSTEE_IS_SID: printf(&quot;TRUSTEE_IS_SID\n&quot;); break; case TRUSTEE_IS_NAME: printf(&quot;TRUSTEE_IS_NAME\n&quot;); break; }          printf (&quot;grfAccessPermissions : %08x\n&quot;,            pListOfExplicitEntries[i].grfAccessPermissions); printf (&quot;grfInheritance : %08x\n&quot;,           pListOfExplicitEntries[i].grfInheritance); if (pListOfExplicitEntries[i].grfInheritance & CONTAINER_INHERIT_ACE) {           printf(&quot;\tCONTAINER_INHERIT_ACE\n&quot;); }       if (pListOfExplicitEntries[i].grfInheritance & INHERIT_ONLY_ACE) {           printf(&quot;\tINHERIT_ONLY_ACE\n&quot;); }       if (pListOfExplicitEntries[i].grfInheritance & NO_PROPAGATE_INHERIT_ACE) {           printf(&quot;\tNO_PROPAGATE_INHERIT_ACE\n&quot;); }       if (pListOfExplicitEntries[i].grfInheritance & OBJECT_INHERIT_ACE) {           printf(&quot;\tOBJECT_INHERIT_ACE\n&quot;); }       if (pListOfExplicitEntries[i].grfInheritance & SUB_CONTAINERS_AND_OBJECTS_INHERIT) {           printf(&quot;\tSUB_CONTAINERS_AND_OBJECTS_INHERIT\n&quot;); }       else if (pListOfExplicitEntries[i].grfInheritance & SUB_CONTAINERS_ONLY_INHERIT) {           printf(&quot;\tSUB_CONTAINERS_ONLY_INHERIT\n&quot;); }       else if (pListOfExplicitEntries[i].grfInheritance & SUB_OBJECTS_ONLY_INHERIT) {           printf(&quot;\tSUB_OBJECTS_ONLY_INHERIT\n&quot;); }       printf (&quot;grfAccessMode : %d\n&quot;,            pListOfExplicitEntries[i].grfAccessMode); switch (pListOfExplicitEntries[i].grfAccessMode) {           case NOT_USED_ACCESS: printf(&quot;\tNOT_USED_ACCESS\n&quot;); break; case GRANT_ACCESS: printf(&quot;\tGRANT_ACCESS\n&quot;); break; case SET_ACCESS: printf(&quot;\tSET_ACCESS\n&quot;); break; case DENY_ACCESS: printf(&quot;\tDENY_ACCESS\n&quot;); break; case REVOKE_ACCESS: printf(&quot;\tREVOKE_ACCESS\n&quot;); break; case SET_AUDIT_SUCCESS: printf(&quot;\tSET_AUDIT_SUCCESS\n&quot;); break; case SET_AUDIT_FAILURE: printf(&quot;\tSET_AUDIT_FAILURE\n&quot;); break; default: printf(&quot;\tUnknown_ACE_TYPE\n&quot;); break; }       printf(&quot;++++++\n&quot;); }   printf(&quot;******\n&quot;); LocalFree(pListOfExplicitEntries); }

void main(void) {   EXPLICIT_ACCESS pAccess[2]; TRUSTEE pTrustee[2]; char userName[32]; DWORD dwSDSize, dwSize, status;

PSECURITY_DESCRIPTOR pNewSD = NULL;

dwSize = sizeof(userName); GetUserName(userName, &dwSize);

BuildExplicitAccessWithName(&pAccess[0],        userName,        GENERIC_ALL,        SET_AUDIT_FAILURE,        0); BuildExplicitAccessWithName(&pAccess[1],        userName,        GENERIC_ALL,        SET_AUDIT_SUCCESS,        0);

BuildTrusteeWithName(&pTrustee[0], userName); BuildTrusteeWithName(&pTrustee[1], &quot;Administrators&quot;); status = BuildSecurityDescriptor(&pTrustee[0],                           &pTrustee[1],                            0,                            NULL,                            2,                            pAccess,                            NULL,                            &dwSDSize,                            &pNewSD);

if (status == ERROR_SUCCESS && pNewSD) {       BOOL bSaclPresent, bSaclDefaulted; PACL pSacl;

if (GetSecurityDescriptorSacl(pNewSD, &bSaclPresent, &pSacl, &bSaclDefaulted)) {           DumpExplicitEntriesInAcl(pSacl); }       LocalFree(pNewSD); pNewSD = NULL; } } The following sample code demonstrates how low-level access control functions can be used to construct a SACL:

/*

The following sample code demonstrates how to add an access-control entry (ACE) to an object's System Access Control List (SACL). The SACL for an object lets the system know when to make entries in the system security log when the object is accessed. See the documentation in the Platform SDK for additional information on Auditing and Windows NT   Security.

The sample adds a failure audit ACE for &quot;Everyone&quot; with read access. This audit ACE is added to an existing directory called C:\Test. The directory must exist.

The procedure to add an ACE to an object's SACL is similar to that used for adding an ACE to an object's DACL. The exceptions are:

DACL API                     SACL API

GetSecurityDescriptorDacl    GetSecurityDescriptorSacl AddAccessAllowedAce          AddAuditAccessAce SetSecurityDescriptorDacl    SetSecurityDescriptorSacl

The &quot;rules&quot; for ACE composition vary from object type to object type. This sample code generates an Audit ACE that works for a directory/file. A different type of object, for example, a registry key, may require      different access masks and inheritance flags.

This code sample requires the following import library: advapi32.lib


 * 1) include 
 * 2) include 


 * 1) define SD_SIZE (65536 + SECURITY_DESCRIPTOR_MIN_LENGTH)

BOOL EnableSecurityName(void); BOOL AddAuditAce(CHAR *pFileName, DWORD dwAccessMask);

int main(void) {   // Enable the SE_SECURITY_NAME privilege.

if (!EnableSecurityName) return FALSE;

// Do the work to add the ACE to the SACL.

if (!AddAuditAce( &quot;c:\\test&quot;,                                 // Name of directory. FILE_GENERIC_READ | ACCESS_SYSTEM_SECURITY)) // &quot;Read&quot; audit.

//    // The following audit mask combinations duplicate the // different audit settings that you can apply through // WinNT 4.0 Explorer //    //     // FILE_GENERIC_WRITE | ACCESS_SYSTEM_SECURITY // &quot;Write&quot; // FILE_GENERIC_EXECUTE                       // &quot;Execute&quot; // DELETE                                     // &quot;Delete&quot; // WRITE_DAC                                  // &quot;Change Permissions&quot; // WRITE_OWNER                                // &quot;Write Owner&quot; //        printf(&quot;Could not add audit entry to SACL\n&quot;); else printf(&quot;Added audit entry to SACL\n&quot;);

return TRUE; }

BOOL EnableSecurityName(void) {   // A process that tries to read or write a SACL needs // to have and enable the SE_SECURITY_NAME privilege. HANDLE hToken; LUID SecurityNameValue; TOKEN_PRIVILEGES tkp;

if (!OpenProcessToken(GetCurrentProcess, TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)) {       printf(&quot;Error: OpenProcessToken (%lu)\n&quot;, GetLastError); return FALSE; }

if (!LookupPrivilegeValue((LPSTR)NULL, SE_SECURITY_NAME, &SecurityNameValue)) {       printf(&quot;Error: LookupPrivilegeValue (%lu)\n&quot;, GetLastError); return FALSE; }

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

if (!AdjustTokenPrivileges(hToken, FALSE, &tkp, sizeof(TOKEN_PRIVILEGES), (PTOKEN_PRIVILEGES)NULL, (PDWORD)NULL)) {       printf(&quot;Error: AdjustTokenPrivileges (%lu)\n&quot;, GetLastError); return FALSE; }

return TRUE ; }

BOOL AddAuditAce(CHAR *pFileName, DWORD dwAccessMask) {    // SID variables.

SID_IDENTIFIER_AUTHORITY authWorld = SECURITY_WORLD_SID_AUTHORITY; PSID psidWorld = NULL;

// Directory SD variables.

UCHAR         ucSDbuf[SD_SIZE]; PSECURITY_DESCRIPTOR pFileSD=(PSECURITY_DESCRIPTOR)ucSDbuf; DWORD         dwSDLengthNeeded;

// ACL variables.

PACL          pACL = NULL; BOOL          bSaclPresent; BOOL          bSaclDefaulted; ACL_SIZE_INFORMATION AclInfo;

// New ACL variables.

PACL          pNewACL = NULL; DWORD         dwNewACLSize;

// New SD variables.

UCHAR               NewSD[SECURITY_DESCRIPTOR_MIN_LENGTH]; PSECURITY_DESCRIPTOR psdNewSD=(PSECURITY_DESCRIPTOR)NewSD;

// Temporary ACE.

PVOID         pTempAce; UINT          CurrentAceIndex;

// New ACE variables. // Use flags compatible with what explorer uses for a container.

BYTE bAceFlags = CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE; BOOL bReturn = FALSE;

__try {       // Build the &quot;Everyone&quot; SID. if (!AllocateAndInitializeSid( &authWorld, 1,            SECURITY_WORLD_RID, 0,                0,                 0,                 0,                 0,                 0,                 0,             &psidWorld)) {           printf(&quot;Error: AllocateAndInitializeSid (%lu)\n&quot;,GetLastError); __leave; }

// Get security descriptor (SD) for directory.

if (!GetFileSecurity( pFileName, (SECURITY_INFORMATION)(SACL_SECURITY_INFORMATION), pFileSD, SD_SIZE, (LPDWORD)&dwSDLengthNeeded)) {           printf(&quot;Error %d:GetFileSecurity\n&quot;,GetLastError); __leave; }

// Initialize new SD.

if (!InitializeSecurityDescriptor(psdNewSD,SECURITY_DESCRIPTOR_REVISION)) {           printf(&quot;Error: InitializeSecurityDescriptor (%lu)\n&quot;,GetLastError); __leave; }

// Get SACL from SD.

if (!GetSecurityDescriptorSacl( pFileSD, &bSaclPresent, &pACL, &bSaclDefaulted)) {           printf(&quot;Error: GetSecurityDescriptorSacl (%lu)\n&quot;,GetLastError); __leave; }

// Get directory ACL size information.

if (bSaclPresent && pACL) {           if (!GetAclInformation(pACL,&AclInfo,sizeof(ACL_SIZE_INFORMATION), AclSizeInformation)) {               printf(&quot;Error: GetAclInformation (%lu)\n&quot;,GetLastError); __leave; }       }        else {           // If you don't have a SACL, // allow some room for the ACL header and flags. AclInfo.AclBytesInUse = sizeof(ACL) ;

// Set the ACE count. AclInfo.AceCount = 0; }

// Compute size needed for the new ACL.

dwNewACLSize = AclInfo.AclBytesInUse + sizeof(ACCESS_ALLOWED_ACE) + GetLengthSid(psidWorld) - sizeof(DWORD);

// Allocate memory for new ACL.

pNewACL = (PACL)LocalAlloc(LPTR, dwNewACLSize);

// Initialize the new ACL

if (!InitializeAcl(pNewACL, dwNewACLSize, ACL_REVISION2)) {           printf(&quot;Error %d:InitializeAcl\n&quot;,GetLastError); __leave; }       // If SACL is present, copy it to a new SACL.

if (bSaclPresent && pACL) // Only copy if SACL was present. {           printf(&quot;Copying SACL information to new SACL\n&quot;);

// Copy the directory's ACEs to our new ACL.

if (AclInfo.AceCount) {               for(CurrentAceIndex = 0; CurrentAceIndex < AclInfo.AceCount;                    CurrentAceIndex++) {                   // Get an ACE. if (!GetAce(pACL,CurrentAceIndex,&pTempAce)) {                       printf(&quot;Error %d: GetAce\n&quot;,GetLastError); __leave; }

// Add the ACE to the new ACL.

if (!AddAce(pNewACL, ACL_REVISION, MAXDWORD, pTempAce, ((PACE_HEADER)pTempAce)->AceSize)) {                       printf(&quot;Error %d:AddAce\n&quot;,GetLastError); __leave; }               }            }        }

// Add the audit ACE to the new SACL.

printf(&quot;Adding new ACE to SACL\n&quot;);

if (!AddAuditAccessAce( pNewACL, ACL_REVISION2, dwAccessMask, psidWorld, FALSE,        // Do not audit successful access. TRUE))        // Audit unsuccessful access. {           printf(&quot;Error: AddAuditAccessAce (%lu)&quot;,GetLastError); __leave; }

// Get the ace you just added, so you can change the AceFlags.

if (!GetAce( pNewACL, AclInfo.AceCount, // This is the zero-based index of the new ACE. &pTempAce)) {           printf(&quot;Error: GetAce (%lu)\n&quot;,GetLastError); __leave; }       // Set AceFlags member with existing flags so you don't loose the // success/unsuccess audit flags.

((ACCESS_ALLOWED_ACE *)pTempAce)->Header.AceFlags = bAceFlags | ((ACCESS_ALLOWED_ACE *)pTempAce)->Header.AceFlags;

// Set new SACL to the SD.

printf(&quot;Setting SACL to SD\n&quot;);

if (!SetSecurityDescriptorSacl( psdNewSD, TRUE, pNewACL, FALSE)) {           printf(&quot;Error: SetSecurityDescriptorSacl (%lu)&quot;,GetLastError); __leave; }

// Set the SD to the directory.

printf(&quot;Set SD to directory\n&quot;);

if (!SetFileSecurity(pFileName, SACL_SECURITY_INFORMATION,psdNewSD)) {           printf(&quot;Error: SetFileSecurity (%lu)\n&quot;,GetLastError); __leave; }

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

// Free the memory allocated for the new ACL. if (pNewACL) LocalFree((HLOCAL) pNewACL); }

return bReturn; } Additional query words:

Keywords : kbAccCtrl kbAPI kbKernBase kbSDKWin32 kbSecurity kbDSupport kbGrpDSKernBase

Issue type : kbbug

Technology : kbAudDeveloper kbWin32sSearch kbWin32API