Microsoft KB Archive/279682

{|
 * width="100%"|

HOWTO: Use ADsSecurity.dll to Add an Access Control Entry to an NTFS Folder

 * }

Q279682

-

The information in this article applies to:


 * Microsoft Active Directory Service Interfaces, version 2.5
 * Windows Script Host, version 2.0
 * Microsoft Active Directory Services Interface, System Component
 * Microsoft Active Directory Services Interface, Microsoft Active Directory Client

-

SUMMARY
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). To obtain the most current version of ADsSecurity.dll, you should install the most current version of the Microsoft Platform Software Development Kit (Platform SDK) and use RegSvr32 to register the .dll file on the client.

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

where:

 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: 

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:
 * 1) 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.
 * 1) Sub DisplayUsage

Provides very simple Help on how to use the script.
 * 1) Sub DisplayErrorAndBail(oErr, bFlag, sMsg)

Error processing routing. When the &quot;On Error Resume Next&quot; 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.
 * 1) Sub DeleteEveryoneACE(oSd)

Takes an IADsSecuriytDescriptor object as input and then enumerates all of the ACEs in its discretionary ACL, searching for the &quot;Everyone&quot; trustee. When an &quot;Everyone&quot; trustee is found, the ACE is deleted. Note that execution is not terminated with the location of the first &quot;Everyone&quot; 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.
 * 1) 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.
 * 1) Sub DisplayDacl(Dacl)

Takes an IADsAccessContolList entry as input and displays the access type and trustee for the each ACE in the ACL.
 * 1) 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
<pre class="CODESAMP">' ' 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 &quot;ERROR: Wrong number of arguments.&quot; Call DisplayUsage WScript.Quit 1 else sPath = args(0) sTrustee = args(1) end if end sub '======================================================================== ' ' Sub to display the usage for the script ' sub DisplayUsage WScript.Echo &quot;USAGE: cscipt.exe Exclusive_Access.vbs NTFS_PATH [USER_TO_GIVE_ACCESS | -Show]&quot; WScript.Echo VbCrLf & &quot;Where: NTFS_PATH is a file path in the form of: \\Sever\share\Folder&quot; WScript.Echo &quot;USER_TO_GIVE_ACCESS is in the form of DOMAIN\UserID&quot; WScript.Echo &quot;-Show -> displays the ace type and trustee for all ACEs in&quot; WScript.Echo &quot;      the NTFS_PATH DACL&quot; WScript.Echo end 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 &quot;ERROR Number: &quot; & hex( oErr.Number ) & &quot; has occurred. &quot; WScript.Echo oErr.Description oErr.Clear if( bFlag ) then WScript.Echo &quot;Terminating script &quot; WScript.Quit 1 end if   end if end 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 = &quot;Everyone&quot; ) then oDacl.RemoveAce ace end if next oSd.DiscretionaryACL = oDacl end 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(&quot;AccessControlList&quot;) Set ImpDenyDacl = CreateObject(&quot;AccessControlList&quot;) Set InheritedDacl = CreateObject(&quot;AccessControlList&quot;) Set ImpAllowDacl = CreateObject(&quot;AccessControlList&quot;) Set InhAllowDacl = CreateObject(&quot;AccessControlList&quot;) Set ImpDenyObjectDacl = CreateObject(&quot;AccessControlList&quot;) Set ImpAllowObjectDacl = CreateObject(&quot;AccessControlList&quot;) '  ' 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 = newdacl end Sub '========================================================================== ' Sub to display the aces in an acl, Based on &quot;-Show&quot; ' as a second argument ' sub DisplayDacl( Dacl ) dim sDisplayText WScript.Echo &quot;Aces: &quot; for Each Ace in Dacl sDisplayText = &quot;Trustee: &quot; & ace.Trustee & vbCrLf sDisplayText = sDisplayText & &quot;Ace Type: &quot; Select Case ace.AceType Case ADS_ACETYPE_ACCESS_ALLOWED '           ' We have an implicit allow ace '                  sDisplayText = sDisplayText & &quot;ACCESS_ALLOWED&quot; & vbCrLf Case ADS_ACETYPE_ACCESS_DENIED '           ' We have a implicit Deny ACE '                  sDisplayText = sDisplayText & &quot;ACCESS_DENIED&quot; & vbCrLf Case ADS_ACETYPE_ACCESS_ALLOWED_OBJECT '                  ' We have an object allowed ace ' Does it apply to a property? or an Object? '                  sDisplayText = sDisplayText & &quot;ACCESS_ALLOWED_OBJECT&quot; & vbCrLf Case ADS_ACETYPE_ACCESS_DENIED_OBJECT '                  ' We have a object Deny ace '                  sDisplayText = sDisplayText & &quot;ACCESS_DENIED_OBJECT&quot; & vbCrLf CASE Default '           ' This has to be some kind of mistake '           sDisplayText = sDisplayText & &quot;ACCESS_UNKOWN!&quot; & vbCrLf End Select wscript.echo sDisplayText next end sub '========================================================================== '-- ' Beginning of the main script ' dim oFSd dim oAce dim oAdsSecurity dim args dim sAceTrustee, sDirPath ' ' Get the arguments ' set Args = WScript.Arguments Call ProcessCommandLine( args, sDirPath, sAceTrustee ) Set oAdsSecurity = CreateObject(&quot;ADsSecurity&quot;) Call DisplayErrorAndBail( err, TRUE, &quot;Creating ADsSecurity Object&quot;) ' ' Build the FILE: provider path for the object ' sDirPath = &quot;FILE://&quot;&sDirPath ' ' Retrieve the SD for the file ' set oFileSD = oADsSecurity.GetSecurityDescriptor(CStr(sDirPath)) if( sAceTrustee = &quot;-Show&quot; ) then '  ' display the aces in the ACL for the file path '  set oDacl = oFileSD.DiscretionaryACL Call DisplayDacl( oDacl ) WScript.Echo &quot;Display of aces for &quot; & sDirpath & &quot; Completed!&quot; else Call DisplayErrorAndBail( err, TRUE, &quot;Retrieving Security Descriptor for &quot; & sDirPath ) Call DeleteEveryoneAce( oFileSD ) oAdsSecurity.SetSecurityDescriptor oFileSD, CStr(sDirPath) Call DisplayErrorAndBail( err, TRUE, &quot;Setting Security Descriptor back on file &quot; & sDirPath) '  ' Create an IADsAccessControlEntry '  set oAce = CreateObject(&quot;AccessControlEntry&quot;) Call DisplayErrorAndBail( err, TRUE, &quot;Creating new ACE...&quot;) 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, &quot;Setting SD back on File &quot; & sDirPath) WScript.Echo &quot;Ace for &quot; & sAceTrustee & &quot; added to &quot; & sDirPath & &quot; Completed!&quot; end if