How to use Visual Basic and ADsSecurity.dll to properly order ACEs in an ACL

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.
This article shows how to use ADsSecurity.dll to obtain a security descriptor (SD) for a file, Active Directory object, or registry key. This article demonstrates how, once a SD has been obtained, to retrieve the discretionary access control list (ACL) from that SD and then add an Access Control Entry (ACE) in the proper location. The article provides a simple algorithm along with a Microsoft Visual Basic code example illustrating how to properly order an ACL then write the ACL back to the SD.
More information
The IADsAccessControlList::AddAce method adds the ACE at the top of the ACL. In some cases, adding an ACE at the top will create undesired security access. The Active Directory Service Interfaces (ADSI) property cache on Microsoft Windows Server 2003 and on Microsoft Windows XP will correctly order the DACL before writing it back to the object. Reordering is only required on Microsoft Windows 2000. 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 of the object, such as a property set or property
In the products listed in this article, the IADsAccessControlList interface does not support a method that properly orders an ACL. The ACEs must be sorted into these five groups:
Access-denied on the object
Access-denied on a child or property
Access-allowed on the object
Access-allowed on a child 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 Windows 2000 or Windows NT 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 will not automatically get 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 is 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:
    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.

Steps to use the Visual Basic code that is provided in this article

  1. Register ADsSecurity.dll.

    ADsSecurity.dll is part of the Active Directory Service Interfaces (ADSI) 2.5 Resource Kit. To download the ADSI 2.5 Resource kit, visit the following Microsoft Web site: Use Regsvr32 to register ADsSecurity.dll. If this DLL does not register correctly, this behavior indicates that ADSI is not installed. If you are running the code on a Microsoft Windows NT-based computer or on a Microsoft Windows 98-based computer, install the appropriate Active Directory Client Extension. For more information about how to obtain these clients, see the "References" section.
  2. Start Visual Basic. Then, create a standard EXE project.
  3. View the References for the project. Make sure that the follow are selected:
    • Active DS Type Library
    • ADsSecurity 2.5 Type Library
  4. Make a command button on the form.
  5. Double-click the command button. Then, paste the following code in the Command1_Click command handler.

Visual Basic sample code illustrates how to implement the sorting algorithm

Dim sec As New ADsSecurityDim sd As IADsSecurityDescriptorDim dacl As IADsAccessControlListDim ace As IADsAccessControlEntryDim newAce As New AccessControlEntry'' Declare temporary ACLs for sorting the original' ACL'Dim newdacl As New AccessControlListDim ImpDenyDacl As New AccessControlListDim ImpDenyObjectDacl As New AccessControlListDim InheritedDacl As New AccessControlListDim ImpAllowDacl As New AccessControlListDim impAllowObjectDacl As New AccessControlListPrivate Sub Command1_Click()'' Be sure to register ADsSecurity.Dll using RegSvr32 and' adding the ADsSecurity2.5 Type Library to you references for' the project.'' Using the ADsSecurity object, retrieve a SD for an object.  Just' Replace the LDAP:// path with the path of an object that contains a' SD.  For additional details on valid path strings, review' the information stored in the Platform SDK ADSI samples starting with' "<Platform SDK Root>\Samples\NetDs\ADSI\rtk.htm"'Set sec = CreateObject("ADsSecurity")<BR/>' TODO :  replace the servername and DN of the object you want to modify.Set sd = sec.GetSecurityDescriptor("LDAP://MyDCname/cn=MyUser,cn=Users,dc=MyDom,dc=com")'Displaying the ACE in the DACL --- it's the same way you display ACEs for File, File Share, Registry, Exchange, and Active Directory's ACL.Set dacl = sd.DiscretionaryAclDebug.Print Date & Time & "Initial Values of DACL"For Each ace In dacl   Debug.Print ace.Trustee   Debug.Print Hex(ace.AccessMask)   Debug.Print Hex(ace.AceType)   Debug.Print Hex(ace.AceFlags)   Debug.Print Hex(ace.Flags)NextDebug.Print dacl.AceCount'' Initialize all of the new ACLs'' If you are doing this in VBSscript you will need to use' The following methods of creating the ACL bins instead of' using the Dim As New statements above. ''Set newAce = CreateObject("AccessControlEntry")'Set newdacl = CreateObject("AccessControlList")'Set InheritedDacl = CreateObject("AccessControlList")'Set ImpAllowDacl = CreateObject("AccessControlList")'Set InhAllowDacl = CreateObject("AccessControlList")'Set ImpDenyObjectDacl = CreateObject("AccessControlList")'Set ImpAllowObjectDacl = CreateObject("AccessControlList")''' Create a new ace, this one' sets an extended right on the user object that allows the' trustee to read and write the userAccountControl property of an' user object.'' TODO : Replace the trustee with an appropriate trustee on the domain' in question.'newAce.Trustee = "MyDomain\Myuser"newAce.Flags = ADS_FLAG_OBJECT_TYPE_PRESENTnewAce.ObjectType = "{BF967A68-0DE6-11D0-A285-00AA003049E2}"newAce.AccessMask = ADS_RIGHT_DS_READ_PROP Or ADS_RIGHT_DS_WRITE_PROPnewAce.AceType = ADS_ACETYPE_ACCESS_ALLOWED_OBJECTnewAce.AceFlags = ADS_ACEFLAG_INHERIT_ACE'' Place the new ace in the DACL'dacl.AddAce newAce'' Sift the DACL into 5 bins:' Inherited Aces' Implicit Deny Aces' Implicit Deny Object Aces' Implicit Allow Aces' Implicit Allow object aces'For Each ace In dacl  '  ' Sort the original ACEs into their appropriate  ' ACLs  '  Debug.Print ace.Trustee  If ((ace.AceFlags And ADS_ACEFLAG_INHERITED_ACE) = ADS_ACEFLAG_INHERITED_ACE) Then     '     ' Do not really care about the order of inherited aces. Since we are     ' adding them to the top of a new list, when they are added back     ' to the Dacl for the object, they will be in the same order as     ' they were originally. Just a positive side effect of adding items     ' of a LIFO (Last In First Out) type list.     '     InheritedDacl.AddAce ace  Else     '     ' We have an Implicit ACE, let's put it the proper pool     '     Select Case ace.AceType     Case ADS_ACETYPE_ACCESS_ALLOWED        '        ' We have an implicit allow ace        '        ImpAllowDacl.AddAce ace     Case ADS_ACETYPE_ACCESS_DENIED        '        ' We have an implicit Deny ACE        '        ImpDenyDacl.AddAce ace     Case ADS_ACETYPE_ACCESS_ALLOWED_OBJECT        '        ' We have an object allowed ace        ' Does it apply to a property? or an Object?        '        impAllowObjectDacl.AddAce ace     Case ADS_ACETYPE_ACCESS_DENIED_OBJECT        '        ' We have an object Deny ace        '        ImpDenyObjectDacl.AddAce ace     Case Else        '        ' Missed a bin?        '        Debug.Print "Bad ace...." & Hex(ace.AceType)     End Select  End IfNext'' Combine the ACEs in the proper order' Implicit Deny' Implicit Deny Object' Implicit Allow' Implicit Allow Object' Inherited aces'' Implicit Deny'For Each ace In ImpDenyDacl  newdacl.AddAce aceNext'' Implicit Deny Object'For Each ace In ImpDenyObjectDacl  newdacl.AddAce aceNext'' Implicit Allow'For Each ace In ImpAllowDacl  newdacl.AddAce aceNext'' Implicit Allow Object'For Each ace In impAllowObjectDacl  newdacl.AddAce aceNext'' Inherited Aces'For Each ace In InheritedDacl  newdacl.AddAce aceNextsd.DiscretionaryAcl = newdaclDebug.Print DateFor Each ace In newdacl   Debug.Print ace.Trustee   Debug.Print "Ace Mask: " & Hex(ace.AccessMask)   Debug.Print "Ace Type: " & Hex(ace.AceType)   Debug.Print "Ace Flags: " & Hex(ace.AceFlags)   Debug.Print "Object Type value: " & Hex(ace.Flags)   Debug.Print "Object Guid : " & ace.ObjectTypeNext'' Set the appropriate revision level' for the DACL'newdacl.AclRevision = dacl.AclRevision'' Replace the Security Descriptor'sec.SetSecurityDescriptor sd' If this generates the error -214023559 or 80070539 check your TODO' strings.  This error is most likely a problem with DNS name resolution.				
For additional information, click the following article numbers to view the articles in the Microsoft Knowledge Base:
269175 How to use Visual C++ to properly order ACEs in an ACL
279682 How to use ADsSecurity.dll to add an access control entry to an NTFS folder
For additional information about how to install Active Directory Client Extension, click the following article number to view the article in the Microsoft Knowledge Base:
288358 How to install the Active Directory Client Extension

Article ID: 269159 - Last Review: 06/19/2014 08:34:00 - Revision: 8.0

  • kbdswadsi2003swept kbhowto KB269159