Microsoft KB Archive/274432

From BetaArchive Wiki

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 "More Information" 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 <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;
} 

Additional query words:

Keywords : kbAccCtrl kbAPI kbKernBase kbSDKWin32 kbSecurity kbDSupport kbGrpDSKernBase
Issue type : kbbug
Technology : kbAudDeveloper kbWin32sSearch kbWin32API


Last Reviewed: December 2, 2000
© 2001 Microsoft Corporation. All rights reserved. Terms of Use.