You are currently offline, waiting for your internet to reconnect

How to use ADsSecurity.dll to add an access control entry to an NTFS folder

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 you can use the IADsSecurityDescriptor interface that is supported by the ADsSecurity.dll file to add access control entries (ACEs) to an NTFS file system folder's access control list (ACL).

In order for the sample code that is provided in this article to function properly, the latest version of the ADsSecurity.dll file must be registered on the system that is executing the script (the client). The ADsSecurity.dll file is part of the Active Directory Service Interfaces (ADSI) 2.5 Resource Kit. To download the Active Directory Service Interfaces 2.5 Resource Kit, visit the following Microsoft Web site: Use Regsvr32 to register the ADsSecurity.dll file. If this DLL does not correctly register, Active Directory Service Interfaces is not installed.
More information

Script Execution Instructions

  1. Copy and paste the Microsoft Visual Basic Script code that is provided in this article into a .vbs file (such as Test.vbs).
  2. Open a command prompt.
  3. At the command prompt, type: cscript.exe Test.VBS NTFS_PATH [TRUSTEE | -Show]


    • NTFS_PATH is an NTFS file system folder path in the form of \\Computer\Share\Folder.
    • TRUSTEE is the IADsAccessConrolEntry::Trutee in the form of DOMAIN\UserID.
    • -Show instructs the script to list the trustee and access type for each ACE in the folder's ACL. (This parameter is case-sensitive.)
    For example, if you want to add an ACE on folder \\MyComputer\Share1\MyFolder for MyDomain\Me then you would issue this command:
    cscript.exe Test.VBS \\MyComputer\Share1\MyFolder MyDomain\Me
    If you want to view the ACL on file \\MyComputer\Share1\MyFolder, you would issue the command:
    cscript.exe Test.VBS \\MyComputer\Share1\MyFolder -Show

Script Description

This section provides a brief description of each subroutine in the script source.
  1. Constant Definitions
    Because type library information is not readily available inside the scripting environment, several constants must be defined that establish values for the following enumeration types:
    ADS Enumeration TypeDescription
    ADS_RIGHTS_ENUMDefines the possible values for the IADsAccessControlEntry::AccessMask property.
    ADS_ACETYPE_ENUMDefines the possible values for the IADsAccessContronEntry::AceType property.
    ADS_ACEFLAGS_ENUMDefines the possible values for the IADsAccessControlEntry::AceFlags property.
  2. Sub ProcessCommandLine(args, sPath, sTrustee)
    Checks the command line arguments to make sure that the correct number of arguments is present and that they are being placed into the appropriate variables. If the script is executed with the wrong number of arguments, the DisplayUsage routine is called and the script is terminated.
  3. Sub DisplayUsage()
    Provides very simple Help on how to use the script.
  4. Sub DisplayErrorAndBail(oErr, bFlag, sMsg)
    Error processing routing. When the "On Error Resume Next" statement is used in a .vbs file, all error handling is defaulted to the programmer. This routine takes three arguments. The following table outlines the arguments and their usage.
    oErrInVBS-provided error object.
    bFlagInBoolean flag used to determine if the subroutine should
    terminate the script.
    TRUE- The script displays the user-defined error message,
    and then terminates execution.
    FALSE- The script displays the error along with the user-
    defined message, clears the error, and continues.
    sMsgInUser-defined message text that is displayed before
    the error information if an error has occurred.
  5. Sub DeleteEveryoneACE(oSd)
    Takes an IADsSecuriytDescriptor object as input and then enumerates all of the ACEs in its discretionary ACL, searching for the "Everyone" trustee. When an "Everyone" trustee is found, the ACE is deleted. Note that execution is not terminated with the location of the first "Everyone" ACE. Execution continues until all ACEs have been checked because an ACL may contain multiple ACEs for a given trustee.

    This subroutine is an example of how to delete specific ACEs from any ACL by using the IADsAccessControlList::RemoveAce method. Simply search for a given trustee and then use the IADsAccessControlList::RemoveAce method by passing the IADsAccessControlEntry object that is to be deleted.
  6. Sub ReorderDacl(Dacl)
    Takes an IADsAccessControlList object as input, then re-orders ACEs that are contained within it into their appropriate location within the ACL. The comments in the subroutine explain the proper order.

    When an ACE is added to an ACL through the use of the IADsAccessControlList::AddAce method, it could be added to the top, middle, or bottom of the list. Because of this, the programmer becomes responsible for properly ordering the ACL, which explains the need for this subroutine. This is by design, but the behavior could be changed in a future implementation of the IADsAccessControlList::AddAce method.

    The subroutine provides an example of how to properly order the ACL. See the comments that are included in the subroutine for additional details.
  7. Sub DisplayDacl(Dacl)
    Takes an IADsAccessContolList entry as input and displays the access type and trustee for the each ACE in the ACL.
  8. Main Script
    Main body of the script. This is where the action takes place. The command line arguments are interpreted and the subroutines that are described in this section are called in the appropriate order to add an ACE to a file's ACL or display the ACEs in the ACL.

VBS Source Code

'' Define a ADS_RIGHTS_ENUM constants:'  const ADS_RIGHT_DELETE                 = &h10000  const ADS_RIGHT_READ_CONTROL           = &h20000  const ADS_RIGHT_WRITE_DAC              = &h40000  const ADS_RIGHT_WRITE_OWNER            = &h80000  const ADS_RIGHT_SYNCHRONIZE            = &h100000  const ADS_RIGHT_ACCESS_SYSTEM_SECURITY = &h1000000  const ADS_RIGHT_GENERIC_READ           = &h80000000  const ADS_RIGHT_GENERIC_WRITE          = &h40000000  const ADS_RIGHT_GENERIC_EXECUTE        = &h20000000  const ADS_RIGHT_GENERIC_ALL            = &h10000000  const ADS_RIGHT_DS_CREATE_CHILD        = &h1  const ADS_RIGHT_DS_DELETE_CHILD        = &h2  const ADS_RIGHT_ACTRL_DS_LIST          = &h4  const ADS_RIGHT_DS_SELF                = &h8  const ADS_RIGHT_DS_READ_PROP           = &h10  const ADS_RIGHT_DS_WRITE_PROP          = &h20  const ADS_RIGHT_DS_DELETE_TREE         = &h40  const ADS_RIGHT_DS_LIST_OBJECT         = &h80  const ADS_RIGHT_DS_CONTROL_ACCESS      = &h100'++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ' ' ADS_ACETYPE_ENUM' Ace Type definitions'  const ADS_ACETYPE_ACCESS_ALLOWED           = 0  const ADS_ACETYPE_ACCESS_DENIED            = &h1  const ADS_ACETYPE_SYSTEM_AUDIT             = &h2  const ADS_ACETYPE_ACCESS_ALLOWED_OBJECT    = &h5  const ADS_ACETYPE_ACCESS_DENIED_OBJECT     = &h6  const ADS_ACETYPE_SYSTEM_AUDIT_OBJECT      = &h7'++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++'' ADS_ACEFLAGS_ENUM' Ace Flag Constants'  const ADS_ACEFLAG_UNKNOWN                  = &h1  const ADS_ACEFLAG_INHERIT_ACE              = &h2  const ADS_ACEFLAG_NO_PROPAGATE_INHERIT_ACE = &h4  const ADS_ACEFLAG_INHERIT_ONLY_ACE         = &h8  const ADS_ACEFLAG_INHERITED_ACE            = &h10  const ADS_ACEFLAG_VALID_INHERIT_FLAGS      = &h1f  const ADS_ACEFLAG_SUCCESSFUL_ACCESS        = &h40  const ADS_ACEFLAG_FAILED_ACCESS            = &h80'++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++  'On Error Resume Next'------------------------------------------------------------------------' Sub to process the command line'Sub ProcessCommandLine( args, sPath, sTrustee )   if( args.Count < 2 ) then      WScript.Echo "ERROR: Wrong number of arguments."       Call DisplayUsage ( )      WScript.Quit 1   else      sPath = args(0)      sTrustee = args(1)   end ifend sub'========================================================================'------------------------------------------------------------------------' Sub to display the usage for the script'sub DisplayUsage  WScript.Echo "USAGE: cscipt.exe Exclusive_Access.vbs NTFS_PATH [USER_TO_GIVE_ACCESS | -Show]"  WScript.Echo VbCrLf & "Where: NTFS_PATH is a file path in the form of: \\Sever\share\Folder"  WScript.Echo "USER_TO_GIVE_ACCESS is in the form of DOMAIN\UserID"  WScript.Echo "-Show -> displays the ace type and trustee for all ACEs in"  WScript.Echo "       the NTFS_PATH DACL"  WScript.Echoend sub'========================================================================'------------------------------------------------------------------------' Error processing sub.  Takes 3 args, ' 1. is the error object' 2. is a flag, TRUE means terminate if an error was found, '    FALSE- Display an error, clear the error object, continue' 3. A user defined error text to display with the error information'sub DisplayErrorAndBail( oErr, bFlag, sMsg )   if( oErr.Number <> 0 ) then      '      ' We have an error, display the error text and the error number      ' along with the error description      '      WScript.Echo sMsg      WScript.Echo "ERROR Number: " & hex( oErr.Number ) & " has occurred. "      WScript.Echo oErr.Description      oErr.Clear      if( bFlag ) then        WScript.Echo "Terminating script "        WScript.Quit 1      end if    end ifend sub'=========================================================================='--------------------------------------------------------------------------' Delete the EveryOne Ace from the DACL'sub DeleteEveryoneAce ( oSd )  dim oDacl  set oDacl = oSd.DiscretionaryACL  for each ace in oDacl     if( ace.Trustee = "Everyone" ) then         oDacl.RemoveAce ace     end if  next   oSd.DiscretionaryACL = oDaclend Sub'==========================================================================' Sub to reorder an ACL' Comments in the subroutine explain how the ACL should be ordered.' The IADsAccessControlList::AddAce method makes not attempt to properly' order the ACE being added.'Sub ReorderDacl( dacl )   '   ' Initialize all of the new ACLs   '   ' VBS methods of creating the ACL bins   '   Set newdacl = CreateObject("AccessControlList")   Set ImpDenyDacl = CreateObject("AccessControlList")   Set InheritedDacl = CreateObject("AccessControlList")   Set ImpAllowDacl = CreateObject("AccessControlList")   Set InhAllowDacl = CreateObject("AccessControlList")   Set ImpDenyObjectDacl = CreateObject("AccessControlList")   Set ImpAllowObjectDacl = CreateObject("AccessControlList")   '   ' 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       '      If ((ace.AceFlags And ADS_ACEFLAG_INHERITED_ACE) = ADS_ACEFLAG_INHERITED_ACE)Then             '             ' Don't 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 affect of adding items             ' of a LIFO ( Last In First Out) type list.             '         InheritedDacl.AddAce ace       Else         '             ' We have an Implicit ACE, lets 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 a 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 a object Deny ace                   '                   ImpDenyObjectDacl.AddAce ace             Case Else                   '                   ' Missed a bin?                   '                End Select      End If   Next   '   ' 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 ace    Next   '   ' Implicit Deny Object   '   For Each ace In ImpDenyObjectDacl         newdacl.AddAce ace   Next   '   ' Implicit Allow   '   For Each ace In ImpAllowDacl        newdacl.AddAce ace   Next    '   ' Implicit Allow Object   '   For Each ace In impAllowObjectDacl      newdacl.AddAce ace   Next    '   ' Inherited Aces   '   For Each ace In InheritedDacl        newdacl.AddAce ace    Next   '   ' Clean up   '   Set InheritedDacl     = Nothing   Set ImpAllowDacl      = Nothing   Set ImpDenyObjectDacl = Nothing   Set ImpDenyDacl       = Nothing   '   ' Set the appropriate revision level   '  for the DACL   '   newdacl.AclRevision = dacl.AclRevision   '   ' Replace the Security Descriptor   '   Set dacl = nothing   Set dacl = newdaclend Sub'==========================================================================' Sub to display the aces in an acl, Based on "-Show"' as a second argument'sub DisplayDacl( Dacl )  dim sDisplayText  WScript.Echo "Aces: "  for Each Ace in Dacl     sDisplayText = "Trustee: " & ace.Trustee & vbCrLf     sDisplayText = sDisplayText & "Ace Type: "         Select Case ace.AceType             Case ADS_ACETYPE_ACCESS_ALLOWED                   '            ' We have an implicit allow ace                   '                   sDisplayText = sDisplayText & "ACCESS_ALLOWED" & vbCrLf           Case ADS_ACETYPE_ACCESS_DENIED                   '            ' We have a implicit Deny ACE                   '                   sDisplayText = sDisplayText & "ACCESS_DENIED" & vbCrLf              Case ADS_ACETYPE_ACCESS_ALLOWED_OBJECT                   '                   ' We have an object allowed ace                   ' Does it apply to a property? or an Object?                   '                   sDisplayText = sDisplayText & "ACCESS_ALLOWED_OBJECT" & vbCrLf             Case ADS_ACETYPE_ACCESS_DENIED_OBJECT                    '                   ' We have a object Deny ace                   '                   sDisplayText = sDisplayText & "ACCESS_DENIED_OBJECT" & vbCrLf         CASE Default            '            ' This has to be some kind of mistake            '            sDisplayText = sDisplayText & "ACCESS_UNKOWN!" & vbCrLf               End Select         wscript.echo sDisplayText    nextend sub'=========================================================================='--------------------------------------------------------------------------' Beginning of the main script'dim oFSddim oAcedim oAdsSecuritydim argsdim sAceTrustee, sDirPath'' Get the arguments'set Args = WScript.ArgumentsCall ProcessCommandLine( args, sDirPath, sAceTrustee )Set oAdsSecurity = CreateObject("ADsSecurity")Call DisplayErrorAndBail( err, TRUE, "Creating ADsSecurity Object")'' Build the FILE: provider path for the object'sDirPath = "FILE://"&sDirPath'' Retrieve the SD for the file'set oFileSD = oADsSecurity.GetSecurityDescriptor(CStr(sDirPath))if( sAceTrustee = "-Show" ) then   '   ' display the aces in the ACL for the file path   '   set oDacl = oFileSD.DiscretionaryACL   Call DisplayDacl( oDacl )   WScript.Echo "Display of aces for " & sDirpath & " Completed!"else   Call DisplayErrorAndBail( err, TRUE, "Retrieving Security Descriptor for " & sDirPath )   Call DeleteEveryoneAce( oFileSD )   oAdsSecurity.SetSecurityDescriptor oFileSD, CStr(sDirPath)   Call DisplayErrorAndBail( err, TRUE, "Setting Security Descriptor back on file " & sDirPath)   '   ' Create an IADsAccessControlEntry   '   set oAce = CreateObject("AccessControlEntry")     Call DisplayErrorAndBail( err, TRUE, "Creating new ACE...")    oAce.Trustee = sAceTrustee   oAce.AccessMask = ADS_RIGHT_GENERIC_READ Or ADS_RIGHT_GENERIC_EXECUTE _                or ADS_RIGHT_GENERIC_WRITE Or ADS_RIGHT_DELETE   oAce.AceFlags = ADS_ACEFLAG_UNKNOWN Or ADS_ACEFLAG_INHERIT_ACE   oAce.AceType = ADS_ACETYPE_ACCESS_ALLOWED   set oDacl = oFileSD.DiscretionaryAcl   oDacl.AddAce oAce    '   ' Now Reorder the DACL, see comments in ReorderDacl    ' subroutine for rules about reordering the DACL   '   oFileSD.DiscretionaryACL = oDacl   Call ReorderDacl( oDacl )   '   ' Replace the DACL into the Objects Discretionary ACL   '   oFileSD.DiscretionaryACL = oDacl   oADsSecurity.SetSecurityDescriptor oFileSD    Call DisplayErrorAndBail( err, TRUE, "Setting SD back on File " & sDirPath)   WScript.Echo "Ace for " & sAceTrustee & " added to " & sDirPath & " Completed!"end if
For additional information, click the article numbers below to view the articles in the Microsoft Knowledge Base:
269175 How To Use Visual C++ to Properly Order ACEs in an ACL
269159 How To Use Visual Basic and ADsSecurity.dll to Properly Order ACEs in an ACL

Article ID: 279682 - Last Review: 06/19/2014 12:23:00 - Revision: 4.0

  • kbdswadsi2003swept kbhowto KB279682