How To Use Visual C++ to Properly Order ACEs in an ACL

Article translations Article translations
Article ID: 269175 - View products that this article applies to.
This article was previously published under Q269175
Expand all | Collapse all

On This Page

SUMMARY

This article shows how to properly order an access control list (ACL) by using the IADs interface to obtain the object's security descriptor. This article includes C source code that illustrates how to implement the reordering algorithm. Two functions are used to implement the reordering algorithm, ReorderDACL and CombineACLs. ReorderDACL takes as its only argument an IADs interface pointer. The function uses the CombineACLs helper to append one ACL to another.

MORE INFORMATION

The IADsAccessControlList::AddAce method adds the access control entry (ACE) at the top of the ACL. In some cases, adding an ACE at the top will create undesired security access. The proper order of ACEs in an ACL is as follows:
Access-denied ACEs that apply to the object itself
Access-denied ACEs that apply to a child of the object, such as a property set or property
Access-allowed ACEs that apply to the object itself
Access-allowed ACEs that apply to a child object of the object, such as a property set or property
Currently there is no method available through the IADsAccessControlList interface that will properly order an ACL. The ACEs must be sorted into the five groups:
Access-denied on the object
Access-denied on a child object or property
Access-allowed on the object
Access-allowed on a child object or property
All inherited ACEs
The ordering for the inherited ACEs should not be altered. All inherited ACEs are added by the operating system and are ordered appropriately. A programmer can specify that an ACE should be inherited, and the operating system will take care of propagating the ACE to child objects.

There is an exception to the propagation rules. ACEs added to an object's ACL are not automatically applied to existing objects in the tree. It is the responsibility of the programmer to walk the tree and propagate the ACE by adding the ACE to the existing objects. The ACE will be propagated on new objects in the subtree.

Algorithm for Sorting ACEs in an ACL

  1. Obtain the discretionary ACL (DACL) from the security descriptor.
  2. Check the IADsAccessControlEntry::AceFlags to see if the ACE was inherited (check for the ADS_ACEFLAG_INHERITED_ACE bit).
  3. Check the IADsAccessControlEntry::AceType to see what type of access the ACE grants and what the access it granted to (the object itself or properties of the object). The following list outlines the ACE type values and what they mean:
    ADS_ACETYPE_ACCESS_ALLOWED - grants allowed access to the entire object
    ADS_ACETYPE_ACCESS_ALLOWED_OBJECT - indicates the access is allowed to a property or property set
    ADS_ACETYPE_ACCESS_DENIED - denies access to the entire object
    ADS_ACETYPE_ACCESS_DENIED_OBJECT - denies access to a property or property set
  4. Place the ACE in the appropriate temporary DACL based on the IADsAccessControlListEntry::AceType value.
  5. Rebuild the ACL from the separate ACLs in the following order:
    ADS_ACETYPE_ACCESS_DENIED ACE types
    ADS_ACETYPE_ACCESS_DENIED_OBJECT ace types
    ADS_ACETYPE_ACCESS_ALLOWED ACE types
    ADS_ACETYPE_ACCESS_ALLOWED_OBJECT ACE types
    ACEs with ADS_ACEFLAG_INHERITED_ACE flag set in the IADsAccessControlListEntry::AceFlags
  6. Set the new ACL to the same revision level as the old ACL.
  7. Replace the ACL on the security descriptor.

Visual C++ Source Code to Reorder a DACL

// 
// CombineACLs
// 
// Helper function to 
// Combine two ACLs, takes pACL and adds it using the IADsAccessControList::AddAce
// method to the pDestAcl
// 
HRESULT CombineACLs( IADsAccessControlList *pDestACL, IADsAccessControlList *pACL)
{
    VARIANT varACE;
    IEnumVARIANT *pEnum = NULL;
    IDispatch *pDisp = NULL;
    LPUNKNOWN pUnk = NULL;
    HRESULT hr;
    
    ULONG lFetch;

    hr = pACL->get__NewEnum( &pUnk );
    if (SUCCEEDED(hr))
    {
        hr = pUnk->QueryInterface( IID_IEnumVARIANT, (void**) &pEnum );
        if (SUCCEEDED(hr))
        {
            hr = pEnum->Next( 1, &varACE, &lFetch );
            //Loop to read all ACEs on the object.
            while( hr == S_OK )
            {
                //Check if 1 item returned and returned item is an IDispatch pointer.
                if ( (lFetch == 1) && (varACE.vt==VT_DISPATCH))
                {
                    // 
                    // Add ACE's IDispatch to the destination ACL
                    // 
                    hr = pDestACL->AddAce( V_DISPATCH(&varACE));
                    V_DISPATCH(&varACE)->Release();
                }
                //Clean up the VARIANT for the ACE item.
                VariantClear(&varACE);
                //Get the next ACE
                hr = pEnum->Next( 1, &varACE, &lFetch );
            };//End of While loop
        }
        //Clean up
        if (pEnum)
            pEnum->Release();
    }
    if (pUnk)
        pUnk->Release(); 
    return hr;
}

// 
// ReOrderDacl
// Takes a single argument, an IADs interface for the object
// and reorders the DiscresionaryAcl for the Security Descriptor
// 
HRESULT ReOrderDacl( IADs* pObject )
{
    HRESULT hr;
    IADsSecurityDescriptor *pSD;
    IADsAccessControlList *pACL;
    IADsAccessControlList *pAccessDeniedACL;
    IADsAccessControlList *pAccessDeniedObjectACL;
    IADsAccessControlList *pAccessAllowedACL;
    IADsAccessControlList *pAccessAllowedObjectACL;
    IADsAccessControlList *pAllInheritedAcesACL;
    VARIANT var; 
    IDispatch *pDisp;
    // 
    // Check the object pointer, if its valid, retrieve the pSD pointer
    // 
    if (pObject)
    {
        // 
        //Get the nTSecurityDescriptor
        // 
        VariantClear(&var);
        LPOLESTR szAttribute = L"nTSecurityDescriptor";
        hr = pObject->Get(szAttribute,&var);
        if (SUCCEEDED(hr))
        {
            // 
            //Type should be VT_DISPATCH--an IDispatch ptr to the security descriptor object.
            // 
            if (var.vt==VT_DISPATCH)
            { 
                // 
                //Use the V_DISPATCH macro to get the IDispatch pointer from VARIANT structure
                //and QI for IADsSecurityDescriptor ptr.
                // 
                hr = V_DISPATCH( &var )->QueryInterface(IID_IADsSecurityDescriptor,(void**)&pSD);
                if (SUCCEEDED(hr))
                { // Begin working with Security Descriptor
                    // 
                    //Get the DACL
                    // 
                    hr = pSD->get_DiscretionaryAcl(&pDisp);
                    if (SUCCEEDED(hr))
                    {//Begin working with DACL
                        // 
                        //QI for IADsAccessControlList interface
                        // 
                        hr = pDisp->QueryInterface(IID_IADsAccessControlList,(void**)&pACL);
                        if (SUCCEEDED(hr))
                        { // Begin temp ACL creation
                            // 
                            // Request interfaces for all of the temporary ACLs
                            // 
                            hr  = CoCreateInstance( 
                                                    CLSID_AccessControlList,
                                                    NULL,
                                                    CLSCTX_INPROC_SERVER,
                                                    IID_IADsAccessControlList,
                                                    (void **)&pAccessDeniedACL
                                                  );  

                            if( SUCCEEDED(hr) ) hr = CoCreateInstance (
                                                    CLSID_AccessControlList,
                                                    NULL,
                                                    CLSCTX_INPROC_SERVER,
                                                    IID_IADsAccessControlList,
                                                    (void **)&pAccessDeniedObjectACL
                                                    );
                            else goto Error_Handler;

                            if( SUCCEEDED(hr) ) hr = CoCreateInstance (
                                                    CLSID_AccessControlList,
                                                    NULL,
                                                    CLSCTX_INPROC_SERVER,
                                                    IID_IADsAccessControlList,
                                                    (void **)&pAccessAllowedACL
                                                    );
                            else goto Error_Handler;
                            if( SUCCEEDED(hr) ) hr = CoCreateInstance (
                                                    CLSID_AccessControlList,
                                                    NULL,
                                                    CLSCTX_INPROC_SERVER,
                                                    IID_IADsAccessControlList,
                                                    (void **)&pAccessAllowedObjectACL
                                                    );
                            else goto Error_Handler;
                            if( SUCCEEDED(hr) ) hr = CoCreateInstance (
                                                    CLSID_AccessControlList,
                                                    NULL,
                                                    CLSCTX_INPROC_SERVER,
                                                    IID_IADsAccessControlList,
                                                    (void **)&pAllInheritedAcesACL
                                                    );
                            else goto Error_Handler;
                            if( SUCCEEDED(hr) )
                            {  // Begin sorting list
                                // 
                                // All of the temporary ACLs are ready.
                                // Sort the pACL list into the appropriate 
                                // ACLs
                                // 
                                // Setup the enumeration interfaces and the enumeration loop
                                // 
                                VARIANT varACE;
                                IEnumVARIANT *pEnum = NULL;
                                LPUNKNOWN pUnk = NULL;
                                IADsAccessControlEntry *pACE;
                                IDispatch *pDispatch;
                                long lAceType,lAceFlags;
                                long lAclRevision;

                                ULONG lFetch;

                                hr = pACL->get__NewEnum( &pUnk );
                                if (SUCCEEDED(hr))
                                {
                                    hr = pUnk->QueryInterface( IID_IEnumVARIANT, (void**) &pEnum );
                                    if (SUCCEEDED(hr))
                                    {
                                        hr = pEnum->Next( 1, &varACE, &lFetch );
                                        // 
                                        //Loop to read all ACEs on the object.
                                        // 
                                        while( hr == S_OK )
                                        {
                                            // 
                                            //Check if 1 item returned and returned item is an IDispatch pointer.
                                            // 
                                            if ( (lFetch == 1) && (varACE.vt==VT_DISPATCH))
                                            {
                                                // 
                                                // Add ACE's IDispatch to the destination ACL
                                                // 
                                                hr = V_DISPATCH(&varACE)->QueryInterface(IID_IADsAccessControlEntry,(void**)&pACE);
                                                if( SUCCEEDED(hr))
                                                {
                                                    // 
                                                    // We have an ACE pointer, determine its Destination ACL
                                                    // 
                                                    pACE->get_AceType( &lAceType );
                                                    pACE->get_AceFlags( &lAceFlags);
                                                    if( lAceFlags & ADS_ACEFLAG_INHERITED_ACE  )
                                                    {
                                                        // 
                                                        // All inherited aces go into one bin.  They are set by 
                                                        // OS and cannot be added by a user, so they
                                                        // are in order already.
                                                        // 
                                                        pAllInheritedAcesACL->AddAce( V_DISPATCH(&varACE) );
                                                    }
                                                    else
                                                    {
                                                        switch( lAceType )
                                                        {
                                                        case ADS_ACETYPE_ACCESS_DENIED:
                                                            pAccessDeniedACL->AddAce( V_DISPATCH(&varACE));
                                                            break;
                                                        case ADS_ACETYPE_ACCESS_DENIED_OBJECT:
                                                            pAccessDeniedObjectACL->AddAce( V_DISPATCH(&varACE));
                                                            break;
                                                        case ADS_ACETYPE_ACCESS_ALLOWED:
                                                            pAccessAllowedACL->AddAce( V_DISPATCH(&varACE));
                                                            break;
                                                        case ADS_ACETYPE_ACCESS_ALLOWED_OBJECT:
                                                            pAccessAllowedObjectACL->AddAce( V_DISPATCH(&varACE));
                                                            break;
                                                        default:
                                                            break;
                                                        }
                                                    }
                                                }
                                                V_DISPATCH(&varACE)->Release();
                                            }// End of While Loop
                                            // 
                                            //Clean up the VARIANT for the ACE item.
                                            // 
                                            VariantClear(&varACE);
                                            // 
                                            //Get the next ACE
                                            // 
                                            hr = pEnum->Next( 1, &varACE, &lFetch );
                                        }
                                        // 
                                        //Clean up the enumeration interface
                                        // 
                                        if (pEnum)
                                        pEnum->Release();
                                    }
                                    // 
                                    // Cleanup the IUnknown for the enumeration interface
                                    // 
                                    if (pUnk)
                                        pUnk->Release();
                                }
                                // 
                                // Combine the ACLs
                                // The order is as follows:
                                //  Access Denied for the object
                                //  Access Denied to a property or property set
                                //  Access Allowed for the object
                                //  Access Allowed for a property or property set
                                // 
                                //  Since the Access Denied aces are first, just add the 
                                //  other ACLs to the bottom of the pAccessDeniedACL pointer
                                //  using the helper function CombineACLs
                                // 
                                CombineACLs( pAccessDeniedACL, pAccessDeniedObjectACL);
                                CombineACLs( pAccessDeniedACL, pAccessAllowedACL);
                                CombineACLs( pAccessDeniedACL, pAccessAllowedObjectACL);
                                CombineACLs( pAccessDeniedACL, pAllInheritedAcesACL);
                                // 
                                // Set the revision level to the old ACLs revision level
                                // and obtain an IDispatch pointer to write ACL back into
                                // the Discretionary ACL
                                // 
                                pACL->get_AclRevision( &lAclRevision );
                                pAccessDeniedACL->put_AclRevision( lAclRevision );
                                pAccessDeniedACL->QueryInterface(IID_IDispatch,(void **)&pDispatch);
                                // 
                                //Write the DACL
                                // 
                                hr = pSD->put_DiscretionaryAcl(pDispatch);
                                if (SUCCEEDED(hr))
                                {        
                                    LPOLESTR szAttribute = L"nTSecurityDescriptor";
                                    // 
                                    // Write the ntSecurityDescriptor property to the property cache.
                                    // Var still contains the IDispatch pointer to the ntSecurityDescriptor
                                    // used to obtain pSD pointer
                                    // 
                                    hr = pObject->Put(szAttribute, var);
                                    if (SUCCEEDED(hr))
                                    {
                                        // 
                                        //Call SetInfo to flush the client side changes to the server
                                        // 
                                        hr = pObject->SetInfo();
                                    }
                                }
                                // 
                                // Clean up
                                // 
                                pAccessDeniedACL->Release();
                                pAccessDeniedObjectACL->Release();
                                pAccessAllowedACL->Release();
                                pAccessAllowedObjectACL->Release();
                                if( pDispatch) pDispatch->Release();
                                pDisp->Release();
                           }// End sorting ACLs
                           pACL->Release();
                        }//End ACL Creation
                    }//End working with DACL
                    pSD->Release();
                }// End working with Security Descriptor
            }
            
        }
    }
    return hr;

Error_Handler:
    if( pAccessAllowedACL ) pAccessAllowedACL->Release();
    if( pAccessAllowedObjectACL ) pAccessAllowedObjectACL->Release();
    if( pAccessDeniedACL ) pAccessDeniedACL->Release();
    if( pAccessDeniedObjectACL ) pAccessDeniedObjectACL->Release();
    if( pAllInheritedAcesACL ) pAllInheritedAcesACL->Release();
    if( pSD) pSD->Release();
    if( pACL ) pACL->Release();
    return hr;
}
				

REFERENCES

For additional information, click the article numbers below to view the articles in the Microsoft Knowledge Base:
269159 How To Use Visual Basic and ADsSecurity.dll to Properly Order ACEs in an ACL
279682 How To Use ADsSecurity.dll to Add an Access Control Entry to an NTFS Folder

Properties

Article ID: 269175 - Last Review: July 13, 2004 - Revision: 3.1
APPLIES TO
  • Microsoft Active Directory Service Interfaces 2.5
  • Microsoft Active Directory Service Interfaces 2.5
  • Microsoft Active Directory Client Extension
Keywords: 
kbdswadsi2003swept kbhowto KB269175
Retired KB Content Disclaimer
This article was written about products for which Microsoft no longer offers support. Therefore, this article is offered "as is" and will no longer be updated.

Give Feedback

 

Contact us for more help

Contact us for more help
Connect with Answer Desk for expert help.
Get more support from smallbusiness.support.microsoft.com