BUG: BuildSecurityDescriptor() Function Does Not Properly Build SACL (274432)
The information in this article applies to:
- Microsoft Win32 Application Programming Interface (API), when used with:
- the operating system: Microsoft Windows NT 4.0
This article was previously published under Q274432 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 "More Information" section of this article demonstrates how to use low-level access control functions.
STATUSMicrosoft has confirmed that this is a bug in the Microsoft products that are 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 <windows.h>
#include <aclapi.h>
#include <stdio.h>
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("GetExplicitEntriesFromAcl failed with error : %u\n\n", dwError);
return;
}
printf("cCountOfExplicitEntries : %d\n", cCountOfExplicitEntries);
printf("******\n");
for (ULONG i = 0; i < cCountOfExplicitEntries; i++)
{
printf("++++++\n");
switch (pListOfExplicitEntries[i].Trustee.TrusteeForm)
{
case TRUSTEE_IS_SID:
printf("TRUSTEE_IS_SID\n");
break;
case TRUSTEE_IS_NAME:
printf("TRUSTEE_IS_NAME\n");
break;
}
printf ("grfAccessPermissions : %08x\n",
pListOfExplicitEntries[i].grfAccessPermissions);
printf ("grfInheritance : %08x\n",
pListOfExplicitEntries[i].grfInheritance);
if (pListOfExplicitEntries[i].grfInheritance & CONTAINER_INHERIT_ACE)
{
printf("\tCONTAINER_INHERIT_ACE\n");
}
if (pListOfExplicitEntries[i].grfInheritance & INHERIT_ONLY_ACE)
{
printf("\tINHERIT_ONLY_ACE\n");
}
if (pListOfExplicitEntries[i].grfInheritance & NO_PROPAGATE_INHERIT_ACE)
{
printf("\tNO_PROPAGATE_INHERIT_ACE\n");
}
if (pListOfExplicitEntries[i].grfInheritance & OBJECT_INHERIT_ACE)
{
printf("\tOBJECT_INHERIT_ACE\n");
}
if (pListOfExplicitEntries[i].grfInheritance & SUB_CONTAINERS_AND_OBJECTS_INHERIT)
{
printf("\tSUB_CONTAINERS_AND_OBJECTS_INHERIT\n");
}
else if (pListOfExplicitEntries[i].grfInheritance & SUB_CONTAINERS_ONLY_INHERIT)
{
printf("\tSUB_CONTAINERS_ONLY_INHERIT\n");
}
else if (pListOfExplicitEntries[i].grfInheritance & SUB_OBJECTS_ONLY_INHERIT)
{
printf("\tSUB_OBJECTS_ONLY_INHERIT\n");
}
printf ("grfAccessMode : %d\n",
pListOfExplicitEntries[i].grfAccessMode);
switch (pListOfExplicitEntries[i].grfAccessMode)
{
case NOT_USED_ACCESS:
printf("\tNOT_USED_ACCESS\n");
break;
case GRANT_ACCESS:
printf("\tGRANT_ACCESS\n");
break;
case SET_ACCESS:
printf("\tSET_ACCESS\n");
break;
case DENY_ACCESS:
printf("\tDENY_ACCESS\n");
break;
case REVOKE_ACCESS:
printf("\tREVOKE_ACCESS\n");
break;
case SET_AUDIT_SUCCESS:
printf("\tSET_AUDIT_SUCCESS\n");
break;
case SET_AUDIT_FAILURE:
printf("\tSET_AUDIT_FAILURE\n");
break;
default:
printf("\tUnknown_ACE_TYPE\n");
break;
}
printf("++++++\n");
}
printf("******\n");
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], "Administrators");
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 "Everyone" 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 "rules" 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
*/
#include <windows.h>
#include <stdio.h>
#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(
"c:\\test", // Name of directory.
FILE_GENERIC_READ | ACCESS_SYSTEM_SECURITY)) // "Read" 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 // "Write"
// FILE_GENERIC_EXECUTE // "Execute"
// DELETE // "Delete"
// WRITE_DAC // "Change Permissions"
// WRITE_OWNER // "Write Owner"
//
printf("Could not add audit entry to SACL\n");
else
printf("Added audit entry to SACL\n");
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("Error: OpenProcessToken (%lu)\n", GetLastError());
return FALSE;
}
if (!LookupPrivilegeValue((LPSTR)NULL,
SE_SECURITY_NAME,
&SecurityNameValue))
{
printf("Error: LookupPrivilegeValue (%lu)\n", 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("Error: AdjustTokenPrivileges (%lu)\n", 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 "Everyone" SID.
if (!AllocateAndInitializeSid(
&authWorld,
1,
SECURITY_WORLD_RID,
0,
0,
0,
0,
0,
0,
0,
&psidWorld))
{
printf("Error: AllocateAndInitializeSid (%lu)\n",GetLastError());
__leave;
}
// Get security descriptor (SD) for directory.
if (!GetFileSecurity(
pFileName,
(SECURITY_INFORMATION)(SACL_SECURITY_INFORMATION),
pFileSD,
SD_SIZE,
(LPDWORD)&dwSDLengthNeeded))
{
printf("Error %d:GetFileSecurity\n",GetLastError());
__leave;
}
// Initialize new SD.
if (!InitializeSecurityDescriptor(psdNewSD,SECURITY_DESCRIPTOR_REVISION))
{
printf("Error: InitializeSecurityDescriptor (%lu)\n",GetLastError());
__leave;
}
// Get SACL from SD.
if (!GetSecurityDescriptorSacl(
pFileSD,
&bSaclPresent,
&pACL,
&bSaclDefaulted))
{
printf("Error: GetSecurityDescriptorSacl (%lu)\n",GetLastError());
__leave;
}
// Get directory ACL size information.
if (bSaclPresent && pACL)
{
if (!GetAclInformation(pACL,&AclInfo,sizeof(ACL_SIZE_INFORMATION),
AclSizeInformation))
{
printf("Error: GetAclInformation (%lu)\n",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("Error %d:InitializeAcl\n",GetLastError());
__leave;
}
// If SACL is present, copy it to a new SACL.
if (bSaclPresent && pACL) // Only copy if SACL was present.
{
printf("Copying SACL information to new SACL\n");
// 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("Error %d: GetAce\n",GetLastError());
__leave;
}
// Add the ACE to the new ACL.
if (!AddAce(pNewACL, ACL_REVISION, MAXDWORD, pTempAce,
((PACE_HEADER)pTempAce)->AceSize))
{
printf("Error %d:AddAce\n",GetLastError());
__leave;
}
}
}
}
// Add the audit ACE to the new SACL.
printf("Adding new ACE to SACL\n");
if (!AddAuditAccessAce(
pNewACL,
ACL_REVISION2,
dwAccessMask,
psidWorld,
FALSE, // Do not audit successful access.
TRUE)) // Audit unsuccessful access.
{
printf("Error: AddAuditAccessAce (%lu)",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("Error: GetAce (%lu)\n",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("Setting SACL to SD\n");
if (!SetSecurityDescriptorSacl(
psdNewSD,
TRUE,
pNewACL,
FALSE))
{
printf("Error: SetSecurityDescriptorSacl (%lu)",GetLastError());
__leave;
}
// Set the SD to the directory.
printf("Set SD to directory\n");
if (!SetFileSecurity(pFileName, SACL_SECURITY_INFORMATION,psdNewSD))
{
printf("Error: SetFileSecurity (%lu)\n",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;
}
Modification Type: | Major | Last Reviewed: | 11/3/2003 |
---|
Keywords: | kbACL kbAPI kbbug kbKernBase kbSecurity KB274432 |
---|
|