GuidesScripting NTFS Permissions with ADSI (Part 3)

Scripting NTFS Permissions with ADSI (Part 3)

ServerWatch content and product recommendations are editorially independent. We may make money when you click on links to our partners. Learn More.





by Marcin Policht

In his latest article in a series discussing a scripting approach to modifying Security Descriptors, Marcin Policht discusses how to make modifications to Security Descriptors that control permissions to files and folders on NTFS-based partitions.

This is the third article in the series discussing a scripting approach to modifying Security Descriptors. In the first two articles, I introduced the concept of the Security Descriptor and described its components. Next, I described a way to extract information about it through ADSI-based scripting. In this article, you

will find out how to make modifications to Security Descriptors that control permissions to files and folders on NTFS-based partitions. As

before, keep in mind that in order for the script to work, you will need to obtain ADsSecurity.DLL file (which is included in the ADSI Software

Development Kit, freely downloadable from the Microsoft Web Site), copy it to the computer where the script will be run, and register it using

REGSVR32.EXE (refer to the first article of the series for details).

After you have registered ADsSecurity.DLL, copy the script to a text file and and save it as ACLs.vbs. This version combines the capability

included in the script presented in the previous article, which allowed displaying permissions with the ability to set permissions.

After saving the script, you can execute it by typing appropriate command at the Command Prompt:

To display the Security Descriptor (Trustees, ACE Flags, and Access Control Entries) for a file or folder, you would use the following

syntax:

cscript //nologo ACLs.wsf ACTION=SHOW TARGET=[File|Folder] [ACCOUNT=DomainAccount]

where

  • SHOW is used to indicate type of action (displaying Access Control Entries)
  • File|Folder is a full path to the file or folder
  • DomainAccount is an optional argument that allows you to limit the listing to a specific user or group account (in the format domainaccount)

To set ACE of a file or folder for a particular user or group, you would type the following:

cscript //nologo ACLs.wsf ACTION=SET TARGET=[File|Folder] ACCOUNT=DomainAccount PERM=[Read|Change|Full|NoAccess]

where

  • SET is used to indicate the type of action (setting Access Control Entry)
  • File|Folder is a full path to the file or folder on which permissions will be set
  • DomainAccount is a mandatory argument and specifies the user or group account for which permissions to file/folder will be set
  • Read|Change|Full|NoAccess is the type of permissions set on the target file or folder (can be any one of these four)

You can find out the proper syntax simply by double-clicking on the file. The same message box is displayed if you happen to provide the wrong

number of arguments. Here are some examples of a proper use of the script:

To display the full content of security descriptor for a file c:boot.ini, you would run:

cscript //nologo ACLs.wsf ACTION=SHOW TARGET=c:boot.ini

If you wanted to limit the display of ACEs to a single trustee only (e.g. MPolicht account from the SWYNK domain), you would execute:

cscript //nologo ACLs.wsf ACTION=SHOW TARGET=c:boot.ini ACCOUNT=SWYNKMPolicht

And the following command would grant FULL CONTROL permissions on c:boot.ini to a trustee (SWYNKMPolicht in this case):

cscript //nologo ACLs.wsf ACTION=SET TARGET=c:boot.ini ACCOUNT=SWYNKMPolicht PERM=FULL

The target parameter for both SHOW and SET actions can be either a file or folder. When used with a file, SHOW (and SET) will display (and modify)

permissions on the target file. When applied to folders, SHOW will display permissions on the folder only; SET however, will modify permissions

on the folder and all of its content (including subfolders and their content, recursively). Note that the permissions will be replaced by the one

you provide on the command line (i.e. Read, Change, Full, or No Access) but only for the account you specify with the ACCOUNT parameter.

Permissions for any other user or group accounts will remain unaffected.

It is also important to realize that, when performing SET action on a folder, permissions are assigned explicitly (not through

inheritance). In other words, the script will traverse each file and subfolder within the target folder and assign permissions directly to each.

This is different from the default Windows 2000 behavior, which assigns explicit permissions only to the parent folder (while effective

permissions on its content are determined based on inheritance settings).

Any new files or subfolders created in the target folder, do, however, properly inherit permissions from their parent.

The script consists of the following subroutines:

  • GetArguments – Reading command line arguments
  • DisplayUsage – Displaying MessageBox listing proper syntax for script execution
  • DisplayACLs – Displaying a list of Access Control Entries for the target file or folder (explained in the previous article)
  • RecurseACLs – Recursing through the folder hierarchy in order to allow changing permissions for all child files and folders
  • SetACLs – Setting appropriate access control entry on a file or folder for the account specified as the input argument (this is the critical

    part of the script, which actually modifies the ACE)

  • ReorderDACL – Rearranging the sequence of Access Control Entries in the Access Control List. This step is necessary, since access control

    entries are supposed to follow a certain order — those denying access should always precede those that allow it. Unfortunately, the process used to

    set ACEs (in SetACLs sub) does not maintain this ordering, hence the additional cleanup.

    
    Option Explicit
    
    '**************************************************
    '*** Constant Declarations
    
    '**************************************************
    '*** Access Control Entry Inheritance Flags
    '*** Possible values for the IADsAccessControlEntry::AceFlags property.  
    
    const ADS_ACEFLAG_UNKNOWN                  	= &h1
    
    '*** child objects will inherit ACE of current object
    const ADS_ACEFLAG_INHERIT_ACE 			= &h2	
    '*** prevents ACE inherited by the object from further propagation
    const ADS_ACEFLAG_NO_PROPAGATE_INHERIT_ACE 	= &h4
    '*** indicates ACE used only for inheritance (it does not affect permissions on object itself)
    const ADS_ACEFLAG_INHERIT_ONLY_ACE 		= &h8
    '*** indicates that ACE was inherited	
    const ADS_ACEFLAG_INHERITED_ACE 		= &h10
    '*** indicates that inherit flags are valid (provides confirmation of valid settings)
    const ADS_ACEFLAG_VALID_INHERIT_FLAGS 		= &h1f
    '*** for auditing success in system audit ACE
    const ADS_ACEFLAG_SUCCESSFUL_ACCESS 		= &h40
    '*** for auditing failure in system audit ACE
    const ADS_ACEFLAG_FAILED_ACCESS 		= &h80
    
    '**************************************************
    '*** Access Control Entry Type Values
    '*** Possible values for the IADsAccessContronEntry::AceType property.  
    
    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
    
    '**************************************************
    '*** Access Control Entry Permission Type Values
    '*** Possible values for the IADsAccessControlEntry::AccessMask property.  
    
    '*** permission to read data from file or list contents of directory
    '*** corresponds to List Folder / Read Data permissions in Windows UI
    const FILE_READ_DATA 				= &h1		'bit number 0
    const FILE_LIST_DIRECTORY 			= &h1		'bit number 0
    
    '*** permission to write data to file or create file in directory
    '*** corresponds to Create Files / Write Data permissions in Windows UI
    const FILE_WRITE_DATA 				= &h2		'bit number 1
    const FILE_ADD_FILE 				= &h2		'bit number 1
    
    '*** permission to append data to file or to create subdirectory
    '*** corresponds to Create Folders / Append Data permissions in Windows UI
    const FILE_APPEND_DATA				= &h4		'bit number 2
    const FILE_ADD_SUBDIRECTORY 			= &h4		'bit number 2
    
    '*** permission to read extended attributes
    '*** corresponds to Read Extended Attributes permissions in Windows UI
    const FILE_READ_EA 				= &h8		'bit number 3
    
    '*** permission to write extended attributes
    '*** corresponds to Write Extended Attributes permissions in Windows UI
    const FILE_WRITE_EA				= &h10		'bit number 4
    
    '*** permission to execute file or traverse directory
    '*** corresponds to Traverse Folder / Execute File permissions in Windows UI
    const FILE_EXECUTE 				= &h20		'bit number 5
    const FILE_TRAVERSE 				= &h20		'bit number 5
    
    '*** permission to delete directory and all files it contains
    '*** corresponds to Delete Subfolders and Files permissions in Windows UI
    const FILE_DELETE_CHILD 			= &h40		'bit number 6
    
    '*** permission to read file or folder attributes
    '*** corresponds to Read Attributes permissions in Windows UI
    const FILE_READ_ATTRIBUTES 			= &h80		'bit number 7
    
    '*** permission to change file or folder attributes
    '*** corresponds to Write Attributes permissions in Windows UI
    const FILE_WRITE_ATTRIBUTES			= &h100		'bit number 8
    
    '*** permission to delete file or folder
    '*** corresponds to Delete permissions in Windows UI
    const DELETE 					= &h10000	'bit number 16
    
    '*** permission to read security descriptor
    '*** corresponds to Read Permissions permissions in Windows UI
    const READ_CONTROL				= &h20000	'bit number 17
    
    '*** permission to change discretionary ACL
    '*** corresponds to Change Permissions permissions in Windows UI
    const WRITE_DAC 				= &h40000	'bit number 18
    
    '*** permission to assign owner
    '*** corresponds to Take Ownership permissions in Windows UI
    const WRITE_OWNER				= &h80000	'bit number 19
    
    '*** permission to synchronize
    '*** bit indicating permission to perform synchronize operation, used sometimes during file access
    '*** this permission is automaticaly granted with read and write access and revoked when read or 
    '*** write access is denied. It is not displayed in the list of permissions in Windows UI
    const SYNCHRONIZE 				= &h100000	'bit number 20
    
    const ADS_RIGHT_GENERIC_ALL            		= &h10000000	'bit number 28
    const ADS_RIGHT_GENERIC_EXECUTE        		= &h20000000	'bit number 29
    const ADS_RIGHT_GENERIC_WRITE          		= &h40000000	'bit number 30
    const ADS_RIGHT_GENERIC_READ           		= &h80000000	'bit number 31
    
    '**************************************************
    '*** Variable Declarations
    
    Dim adsFILE_FULL	'value representing sum of access mask corresponding to full file access
    Dim adsFOLDER_FULL	'value representing sum of access mask corresponding to full folder access
    Dim adsFILE_CHANGE	'value representing sum of access mask corresponding to change file access
    Dim adsFOLDER_CHANGE	'value representing sum of access mask corresponding to change folder access
    Dim adsFILE_READ	'value representing sum of access mask corresponding to read file access
    Dim adsFOLDER_READ	'value representing sum of access mask corresponding to read folder access
    Dim adsFILE_NOACCESS	'value representing sum of access mask corresponding to denied full file access
    Dim adsFOLDER_NOACCESS	'value representing sum of access mask corresponding to denied full folder access
    
    adsFILE_FULL		= FILE_READ_DATA Or FILE_WRITE_DATA Or FILE_APPEND_DATA Or _
    			FILE_READ_EA Or FILE_WRITE_EA Or FILE_EXECUTE Or FILE_DELETE_CHILD Or _
    			FILE_READ_ATTRIBUTES Or FILE_WRITE_ATTRIBUTES Or DELETE Or _
    			READ_CONTROL Or WRITE_DAC Or WRITE_OWNER Or SYNCHRONIZE
    adsFOLDER_FULL 		= FILE_LIST_DIRECTORY Or FILE_ADD_FILE Or FILE_ADD_SUBDIRECTORY Or _
    			FILE_READ_EA Or FILE_WRITE_EA Or FILE_TRAVERSE Or FILE_DELETE_CHILD Or _
    			FILE_READ_ATTRIBUTES Or FILE_WRITE_ATTRIBUTES Or DELETE Or _
    			READ_CONTROL Or WRITE_DAC Or WRITE_OWNER Or SYNCHRONIZE
    adsFILE_CHANGE 		= FILE_READ_DATA Or FILE_WRITE_DATA Or FILE_APPEND_DATA Or _
    			FILE_READ_EA Or FILE_WRITE_EA Or FILE_EXECUTE Or _
    			FILE_READ_ATTRIBUTES Or FILE_WRITE_ATTRIBUTES Or DELETE Or _
    			READ_CONTROL Or SYNCHRONIZE
    adsFOLDER_CHANGE	= FILE_LIST_DIRECTORY Or FILE_ADD_FILE Or FILE_ADD_SUBDIRECTORY Or _
    			FILE_READ_EA Or FILE_WRITE_EA Or FILE_TRAVERSE Or _
    			FILE_READ_ATTRIBUTES Or FILE_WRITE_ATTRIBUTES Or DELETE Or _
    			READ_CONTROL Or SYNCHRONIZE
    adsFILE_READ 		= FILE_READ_DATA Or FILE_READ_EA Or FILE_EXECUTE Or _
    			FILE_READ_ATTRIBUTES Or READ_CONTROL Or SYNCHRONIZE
    adsFOLDER_READ		= FILE_LIST_DIRECTORY Or FILE_READ_EA Or FILE_TRAVERSE Or _
    			FILE_READ_ATTRIBUTES Or READ_CONTROL Or SYNCHRONIZE
    adsFILE_NOACCESS 	= FILE_READ_DATA Or FILE_WRITE_DATA Or FILE_APPEND_DATA Or _
    			FILE_READ_EA Or FILE_WRITE_EA Or FILE_EXECUTE Or FILE_DELETE_CHILD Or _
    			FILE_READ_ATTRIBUTES Or FILE_WRITE_ATTRIBUTES Or DELETE Or _
    			READ_CONTROL Or WRITE_DAC Or WRITE_OWNER
    adsFOLDER_NOACCESS 	= FILE_LIST_DIRECTORY Or FILE_ADD_FILE Or FILE_ADD_SUBDIRECTORY Or _
    			FILE_READ_EA Or FILE_WRITE_EA Or FILE_TRAVERSE Or FILE_DELETE_CHILD Or _
    			FILE_READ_ATTRIBUTES Or FILE_WRITE_ATTRIBUTES Or DELETE Or _
    			READ_CONTROL Or WRITE_DAC Or WRITE_OWNER
    
    Dim sAction		'type of action to perform (show or set)
    Dim sPermission		'permission type (read, change, full, or no access)
    Dim sAccount		'user or group account for which permissions are shown/set
    Dim sTarget		'target file or folder path
    Dim iTarget		'integer indicating type of target 0 - file, 1 - folder
    Dim iOffset		'value used for display only (left justifying displayed values)
    
    Dim oADSSecurity	'object representing ADsSecurity class
    Dim oFSO		'object representing Scripting.FileSystemObject ProgID
    
    '**************************************************
    '*** Retrieve script arguments
    
    Call GetArguments(Wscript.Arguments, sAction, sTarget, sAccount, sPermission)
    
    '**************************************************
    '*** Set variables
    
    iOffset 		= 20
    Set oFSO 		= CreateObject("Scripting.FileSystemObject")
    Set oADSSecurity 	= CreateObject("ADsSecurity")
    
    If oFSO.FileExists(sTarget) Then 
    	iTarget = 0
    ElseIf oFSO.FolderExists(sTarget) Then
    	iTarget = 1
    Else
    	Call MsgBox("Target File or Folder does not exist", vbOKOnly, "Incorrect argument")
    	WScript.Quit
    End If
    
    Select Case UCase(sAction)
    	Case "SHOW"	
    			Call DisplayACLs(sTarget, sAccount)
    	Case "SET"	
    			Call RecurseACLs(sTarget, sAccount, sPermission)
    	Case Else
    			Call DisplayUsage("ERROR: Incorrect ACTION type")			
    End Select
    
    Wscript.Quit
    
    '**************************************************
    '*** Subroutine reading command line arguments
    '
    Sub GetArguments(oArgs, sAction, sTarget, sAccount, sPermission)
    
    Dim iCount
    
    For iCount=0 To oArgs.Count - 1
    	Select Case UCase(Split(WScript.Arguments(iCount), "=")(0))
    		Case "ACTION" 	sAction 	= Split(WScript.Arguments(iCount), "=")(1)	
    		Case "TARGET"	sTarget 	= Split(WScript.Arguments(iCount), "=")(1)
    		Case "ACCOUNT" 	sAccount	= Split(WScript.Arguments(iCount), "=")(1)
    		Case "PERM" 	sPermission 	= Split(WScript.Arguments(iCount), "=")(1)
    	End Select
    Next
    
    If sAction = "" or sTarget = "" or (sAction = "SET" and (sTarget = "" or sAccount = "" or sPermission = "")) Then
    	Call DisplayUsage("ERROR: Missing argument(s)")
    	WScript.Quit
    End If
    
    end sub	
    
    
    '**************************************************
    '*** Subroutine displaying usage of the script from the command line
    '
    sub DisplayUsage(sHeader)
    
    Dim sMsg
    
    	sMsg = "To display permissions on file/folder (ACCOUNT parameter is optional) run:"
    	sMsg = sMsg & VbCrLf & _
    		"cscript //nologo ACLs.vbs ACTION=SHOW TARGET=[File|Folder] [ACCOUNT=DomainAccount]"
    	sMsg = sMsg & VbCrLf & vbCrLf & "To set permissions on file/folder (ACCOUNT parameter is mandatory) run:"
    	sMsg = sMsg & VbCrLf & _
    		"cscript //nologo ACLs.vbs ACTION=SET TARGET=[File|Folder] 
    		ACCOUNT=DomainAccount PERM=[Read|Change|Full|NoAccess]"
    	sMsg = sMsg & VbCrLf & vbCrLf & "Where:"
    	sMsg = sMsg & VbCrLf & String(7," ") & "ACTION is set to SHOW to display permissions"
    	sMsg = sMsg & VbCrLf & String(7," ") & "TARGET is full path to the file or folder"
    	sMsg = sMsg & VbCrLf & String(7," ") & "ACCOUNT is user or group account in the DOMAINAccountName format"
    	sMsg = sMsg & VbCrLf & String(7," ") & "PERM specifies type of permissions to be set"
    
    	Call MsgBox(sMsg, vbOKOnly, sHeader)
    
    end sub
    
    '**************************************************
    '*** Subroutine displaying list of Trustees, AcL Types, and ACL Masks
    '
    Sub DisplayACLs(sTarget, sAccount)
    
    Dim oACE		'object representing an Access Control Entry
    Dim sMsg, sAccessMask	'strings containing message to be displayed
    Dim hAccessMask		'number representing Access Mask value
    Dim oTargetSD		'object representing security descriptor of target file or folder
    Dim oDACL		'object representing Discretionary Access Control List
    
    set oTargetSD = oADsSecurity.GetSecurityDescriptor("FILE://" & Cstr(sTarget))
    set oDACL = oTargetSD.DiscretionaryACL
    
    For Each oACE in oDACL
    	If sAccount = "" or UCase(sAccount) = UCase(oACE.Trustee) Then 
    		sMsg = vbCrLf & "Trustee:" & String(iOffset - Len("Trustee:"), Chr(32)) & oACE.Trustee & vbCrLf
    		sMsg = sMsg & "ACE Type:" & String(iOffset - Len("ACE Type:"), Chr(32)) 
    		Select Case oACE.AceType    
    			Case ADS_ACETYPE_ACCESS_ALLOWED       
    				'Implicit Allow ACE
    				sMsg = sMsg & "ACCESS_ALLOWED"
    			Case ADS_ACETYPE_ACCESS_DENIED       
    				'Implicit Deny ACE
    				sMsg = sMsg & "ACCESS_DENIED"
    			Case ADS_ACETYPE_ACCESS_ALLOWED_OBJECT       
    				'Object Allowed ACE
    				sMsg = sMsg & "ACCESS_ALLOWED_OBJECT"    
    			Case ADS_ACETYPE_ACCESS_DENIED_OBJECT        
    				'Object Deny ACE
    				sMsg = sMsg & "ACCESS_DENIED_OBJECT"
    		End Select 
    		Wscript.Echo sMsg
    
    		sAccessMask = ""
    		hAccessMask = 0
    		If iTarget = 0 Then
    			If (oACE.AccessMask AND FILE_READ_DATA) Then
    				sAccessMask = String(iOffset, Chr(32)) & "FILE_READ_DATA" & vbCrLf
    				hAccessMask = hAccessMask + FILE_READ_DATA
    			End If
    			If (oACE.AccessMask AND FILE_WRITE_DATA) Then
    				sAccessMask = sAccessMask & String(iOffset, Chr(32)) & "FILE_WRITE_DATA" & vbCrLf
    				hAccessMask = hAccessMask + FILE_WRITE_DATA
    			End If
    			If (oACE.AccessMask AND FILE_APPEND_DATA) Then
    				sAccessMask = sAccessMask & String(iOffset, Chr(32)) & "FILE_APPEND_DATA" & vbCrLf
    				hAccessMask = hAccessMask + FILE_APPEND_DATA
    			End If
    			If (oACE.AccessMask AND FILE_READ_EA) Then
    				sAccessMask = sAccessMask & String(iOffset, Chr(32)) & "FILE_READ_EA" & vbCrLf
    				hAccessMask = hAccessMask + FILE_READ_EA
    			End If
    			If (oACE.AccessMask AND FILE_WRITE_EA) Then
    				sAccessMask = sAccessMask & String(iOffset, Chr(32)) & "FILE_WRITE_EA" & vbCrLf
    				hAccessMask = hAccessMask + FILE_WRITE_EA
    			End If
    			If (oACE.AccessMask AND FILE_EXECUTE) Then
    				sAccessMask = sAccessMask & String(iOffset, Chr(32)) & "FILE_EXECUTE" & vbCrLf
    				hAccessMask = hAccessMask + FILE_EXECUTE
    			End If
    			If (oACE.AccessMask AND FILE_DELETE_CHILD) Then
    				sAccessMask = sAccessMask & String(iOffset, Chr(32)) & "FILE_DELETE_CHILD" & vbCrLf
    				hAccessMask = hAccessMask + FILE_DELETE_CHILD
    			End If
    			If (oACE.AccessMask AND FILE_READ_ATTRIBUTES) Then
    				sAccessMask = sAccessMask & String(iOffset, Chr(32)) & "FILE_READ_ATTRIBUTES" & vbCrLf
    				hAccessMask = hAccessMask + FILE_READ_ATTRIBUTES
    			End If
    			If (oACE.AccessMask AND FILE_WRITE_ATTRIBUTES) Then
    				sAccessMask = sAccessMask & String(iOffset, Chr(32)) & "FILE_WRITE_ATTRIBUTES" & vbCrLf
    				hAccessMask = hAccessMask + FILE_WRITE_ATTRIBUTES
    			End If
    			If (oACE.AccessMask AND DELETE) Then
    				sAccessMask = sAccessMask & String(iOffset, Chr(32)) & "DELETE" & vbCrLf
    				hAccessMask = hAccessMask + DELETE
    			End If
    			If (oACE.AccessMask AND READ_CONTROL) Then
    				sAccessMask = sAccessMask & String(iOffset, Chr(32)) & "READ_CONTROL" & vbCrLf
    				hAccessMask = hAccessMask + READ_CONTROL
    			End If
    			If (oACE.AccessMask AND WRITE_DAC) Then
    				sAccessMask = sAccessMask & String(iOffset, Chr(32)) & "WRITE_DAC" & vbCrLf
    				hAccessMask = hAccessMask + WRITE_DAC
    			End If
    			If (oACE.AccessMask AND WRITE_OWNER) Then
    				sAccessMask = sAccessMask & String(iOffset, Chr(32)) & "WRITE_OWNER" & vbCrLf
    				hAccessMask = hAccessMask + WRITE_OWNER
    			End If
    			If (oACE.AccessMask AND SYNCHRONIZE) Then
    				sAccessMask = sAccessMask & String(iOffset, Chr(32)) & "SYNCHRONIZE" & vbCrLf
    				hAccessMask = hAccessMask + SYNCHRONIZE
    			End If
    		End If
    
    		If iTarget = 1 Then
    			If (oACE.AccessMask AND FILE_LIST_DIRECTORY) Then
    				sAccessMask = String(iOffset, Chr(32)) & "FILE_LIST_DIRECTORY" & vbCrLf
    				hAccessMask = hAccessMask + FILE_LIST_DIRECTORY
    			End If
    			If (oACE.AccessMask AND FILE_ADD_FILE) Then
    				sAccessMask = sAccessMask & String(iOffset, Chr(32)) & "FILE_ADD_FILE" & vbCrLf
    				hAccessMask = hAccessMask + FILE_ADD_FILE
    			End If
    			If (oACE.AccessMask AND FILE_ADD_SUBDIRECTORY) Then
    				sAccessMask = sAccessMask & String(iOffset, Chr(32)) & "FILE_ADD_SUBDIRECTORY" & vbCrLf
    				hAccessMask = hAccessMask + FILE_ADD_SUBDIRECTORY
    			End If
    			If (oACE.AccessMask AND FILE_READ_EA) Then
    				sAccessMask = sAccessMask & String(iOffset, Chr(32)) & "FILE_READ_EA" & vbCrLf
    				hAccessMask = hAccessMask + FILE_READ_EA
    			End If
    			If (oACE.AccessMask AND FILE_WRITE_EA) Then
    				sAccessMask = sAccessMask & String(iOffset, Chr(32)) & "FILE_WRITE_EA" & vbCrLf
    				hAccessMask = hAccessMask + FILE_WRITE_EA
    			End If
    			If (oACE.AccessMask AND FILE_TRAVERSE) Then
    				sAccessMask = sAccessMask & String(iOffset, Chr(32)) & "FILE_TRAVERSE" & vbCrLf
    				hAccessMask = hAccessMask + FILE_TRAVERSE
    			End If
    			If (oACE.AccessMask AND FILE_DELETE_CHILD) Then
    				sAccessMask = sAccessMask & String(iOffset, Chr(32)) & "FILE_DELETE_CHILD" & vbCrLf
    				hAccessMask = hAccessMask + FILE_DELETE_CHILD
    			End If
    			If (oACE.AccessMask AND FILE_READ_ATTRIBUTES) Then
    				sAccessMask = sAccessMask & String(iOffset, Chr(32)) & "FILE_READ_ATTRIBUTES" & vbCrLf
    				hAccessMask = hAccessMask + FILE_READ_ATTRIBUTES
    			End If
    			If (oACE.AccessMask AND FILE_WRITE_ATTRIBUTES) Then
    				sAccessMask = sAccessMask & String(iOffset, Chr(32)) & "FILE_WRITE_ATTRIBUTES" & vbCrLf
    				hAccessMask = hAccessMask + FILE_WRITE_ATTRIBUTES
    			End If
    			If (oACE.AccessMask AND DELETE) Then
    				sAccessMask = sAccessMask & String(iOffset, Chr(32)) & "DELETE" & vbCrLf
    				hAccessMask = hAccessMask + DELETE
    			End If
    			If (oACE.AccessMask AND READ_CONTROL) Then
    				sAccessMask = sAccessMask & String(iOffset, Chr(32)) & "READ_CONTROL" & vbCrLf
    				hAccessMask = hAccessMask + READ_CONTROL
    			End If
    			If (oACE.AccessMask AND WRITE_DAC) Then
    				sAccessMask = sAccessMask & String(iOffset, Chr(32)) & "WRITE_DAC" & vbCrLf
    				hAccessMask = hAccessMask + WRITE_DAC
    			End If
    			If (oACE.AccessMask AND WRITE_OWNER) Then
    				sAccessMask = sAccessMask & String(iOffset, Chr(32)) & "WRITE_OWNER" & vbCrLf
    				hAccessMask = hAccessMask + WRITE_OWNER
    			End If
    			If (oACE.AccessMask AND SYNCHRONIZE) Then
    				sAccessMask = sAccessMask & String(iOffset, Chr(32)) & "SYNCHRONIZE" & vbCrLf
    				hAccessMask = hAccessMask + SYNCHRONIZE
    			End If
    		End If
    		sMsg = "ACE Permissions:" & String(iOffset - Len("ACE Permissions:"), Chr(32)) 
    		Select Case hAccessMask
    			Case adsFILE_FULL 	Wscript.Echo sMsg & "FULL CONTROL"
    			Case adsFOLDER_FULL 	Wscript.Echo sMsg & "FULL CONTROL"
    			Case adsFILE_CHANGE 	Wscript.Echo sMsg & "CHANGE"
    			Case adsFOLDER_CHANGE 	Wscript.Echo sMsg & "CHANGE"
    			Case adsFILE_READ 	Wscript.Echo sMsg & "READ"
    			Case adsFOLDER_READ 	Wscript.Echo sMsg & "READ"
    			Case adsFILE_NOACCESS 	Wscript.Echo sMsg & "NO ACCESS"
    			Case adsFOLDER_NOACCESS Wscript.Echo sMsg & "NO ACCESS"
    			Case Else		WScript.Echo sMsg & "" & oACE.AccessMask
    						WScript.Echo sAccessMask
    		End Select
    
    		sMsg = "ACE Flags:" & String(iOffset - Len("ACE Flags:"), Chr(32)) 
    		If (oACE.AceFlags AND ADS_ACEFLAG_INHERIT_ACE) Then
    			WScript.Echo sMsg & "ADS_ACEFLAG_INHERIT_ACE"
    		End If
    		If (oACE.AceFlags AND ADS_ACEFLAG_NO_PROPAGATE_INHERIT_ACE) Then 
    			WScript.Echo sMsg & "ADS_ACEFLAG_NO_PROPAGATE_INHERIT_ACE"
    		End If
    		If (oACE.AceFlags AND ADS_ACEFLAG_INHERIT_ONLY_ACE) Then
    			WScript.Echo sMsg & "ADS_ACEFLAG_INHERIT_ONLY_ACE"
    		End If
    		If (oACE.AceFlags AND ADS_ACEFLAG_INHERITED_ACE) Then
    			WScript.Echo sMsg & "ADS_ACEFLAG_INHERITED_ACE"
    		End If
    		If (oACE.AceFlags AND ADS_ACEFLAG_VALID_INHERIT_FLAGS) Then
    			WScript.Echo sMsg & "ADS_ACEFLAG_VALID_INHERIT_FLAGS"
    		End If
    		If (oACE.AceFlags AND ADS_ACEFLAG_SUCCESSFUL_ACCESS) Then
    			WScript.Echo sMsg & "ADS_ACEFLAG_SUCCESSFUL_ACCESS"
    		End If
    		If (oACE.AceFlags AND ADS_ACEFLAG_FAILED_ACCESS) Then
    			WScript.Echo sMsg & "ADS_ACEFLAG_FAILED_ACCESS"
    		End If
    		If (oACE.AceFlags AND ADS_ACEFLAG_UNKNOWN) Then
    			WScript.Echo sMsg & "ADS_ACEFLAG_UNKNOWN"
    		End If
    	End If
    Next
    
    WScript.Echo "Total ACE entries:" &_
    		String(iOffset - Len("Total ACE entries:"), Chr(32)) & oDACL.AceCount
    WScript.Echo "ACL revision:" &_ 
    		String(iOffset - Len("ACL revision:"), Chr(32)) & oDACL.ACLRevision
    
    End Sub
    
    '**************************************************
    '*** Subroutine recursing through content of folder (when setting permissions)
    '*** necessary to propagate permissions set on parent folder (per Q266461)
    '
    Public Sub RecurseACLs(sTarget, sAccount, sPermission)
    
    Dim oTarget		'object representing target folder or file
    Dim oSubfolders		'collection representing subfolders of target folder
    Dim oFiles		'collection representing files in target folder
    Dim oSubfolder		'object representing a subfolder (used for enumeration)
    Dim oFile		'object representing a file (used for enumeration)
    
    If oFSO.FileExists(sTarget) Then
    	Call SetACLs(sTarget, sAccount, sPermission)
    Else
    	Set oTarget = oFSO.GetFolder(sTarget)
    	WScript.Echo "Processing folder " & sTarget
    	Call SetACLs(oTarget.Path, sAccount, sPermission)
    
    	Set oFiles = oTarget.Files
    
    	'*** Apply permissions to files
    	For Each oFile in oFiles
    		WScript.Echo "Processing file " & oFile.Path
    		Call SetACLs(oFile.Path, sAccount, sPermission)
    	Next  
    
    	Set oSubFolders = oTarget.SubFolders
    
    	'*** Apply permissions to subfolders
    	For Each oSubFolder in oSubFolders
    		Call RecurseACLs(oSubFolder.Path, sAccount, sPermission)
    	Next
    
    	Set oSubFolders = Nothing
    	Set oFiles = Nothing
    
    End If
    
    End Sub
    
    '**************************************************
    '*** Subroutine applying permissions for specified account 
    '
    Sub SetACLs(sTarget, sAccount, sPermission)
    
    Dim hMask		'value representing Access Mask
    Dim hType		'value representing Access Type
    Dim oSID		'object representing SID of a Security Principal
    Dim oACE		'object representing ACE of target object
    Dim hSID		'hexadecimal representation of SID
    Dim oTarget		'object representing target folder or file
    Dim oTargetSD		'object representing security descriptor of target file or folder
    Dim oDACL		'object representing Discretionary Access Control List
    
    Select Case UCase(sPermission)
    	Case "FULL"
    		hType = ADS_ACETYPE_ACCESS_ALLOWED
    		If iTarget = 0 Then
    			hMask = adsFILE_FULL
    		Else
    			hMask = adsFOLDER_FULL
    		End If
    	Case "CHANGE"
    		hType = ADS_ACETYPE_ACCESS_ALLOWED
    		If iTarget = 0 Then
    			hMask = adsFILE_CHANGE
    		Else
    			hMask = adsFOLDER_CHANGE
    		End If
    	Case "READ"
    		hType = ADS_ACETYPE_ACCESS_ALLOWED
    		If iTarget = 0 Then
    			hMask = adsFILE_READ
    		Else
    			hMask = adsFOLDER_READ
    		End If
    	Case "NOACCESS"
    		hType = ADS_ACETYPE_ACCESS_DENIED
    		If iTarget = 0 Then
    			hMask = adsFILE_NOACCESS
    		Else
    			hMask = adsFOLDER_NOACCESS
    		End If
    	Case Else
    		Call DisplayUsage("ERROR: Incorrect permission type.")
    		WScript.Quit
    End Select
    
    set oTargetSD = oADsSecurity.GetSecurityDescriptor("FILE://" & Cstr(sTarget))
    set oDACL = oTargetSD.DiscretionaryACL
    
    For Each oACE in oDACL
    	If UCase(oACE.Trustee) = UCase(sAccount) Then
    		oDACL.RemoveACE oACE
    	End If
    Next
    
    Set oACE = CreateObject("AccessControlEntry")
    oACE.Trustee = sAccount
    oAce.AceType = hType
    oAce.AccessMask = hMask
    oACE.AceFlags = ADS_ACEFLAG_INHERIT_ACE Or ADS_ACEFLAG_UNKNOWN
    oDACL.AddAce oACE
    
    Call ReorderDACL(oDACL)
    
    oTargetSD.DiscretionaryACL = oDACL
    oADsSecurity.SetSecurityDescriptor oTargetSD
    
    End Sub 
    
    '**************************************************
    '*** Subroutine reordering the ACLs (per Q279682)
    '*** ACEs need to be properly ordered, since AddAce method does not perform ordering. 
    '*** If an access-allowed ACE appears before access-denied, a trustee will be granted access.
    
    '*** The preferred order of ACEs in a DACL is described in MSDN Library (at msdn.microsoft.com). 
    '*** For Windows 2000, ACEs should be arranged into two main groups - non-inherited and inherited.
    '*** Non-inherited ACEs should be listed first, followed by the inherited ones. Within each group
    '*** (non-inherited and inherited), ACEs are arranged in the following fashion:
    '*** - access-denied ACEs that apply to the object itself
    '*** - access-denied ACEs that apply to subobjects of the object (including its properties)
    '*** - access-allowed ACEs that apply to the object itself
    '*** - access-allowed ACEs that apply to subobjects of the object (including its properties) 
    '***
    '*** Since the script does not affect inherited ACEs (it sets permission directly on target object)
    '*** they do not have to be rearranged. We only need to rearrange non-inherited ACEs
    
    Sub ReorderDACL(oDACL)
    
    Dim oNewDACL			'object used to temporarily store DACL (during ordering)
    Dim oInheritedDACL		'object representing list of all Inherited ACEs
    Dim oDenyDACL			'object representing list of non-Inherited Deny ACEs
    Dim oAllowDACL			'object representing list of non-Inherited Allow ACEs
    Dim oACE			'object representing ACE (used for enumeration)
    
    '**************************************************
    '*** Create Access Control List objects 
    
    Set oNewDACL = CreateObject("AccessControlList")
    Set oInheritedDACL = CreateObject("AccessControlList")
    Set oAllowDACL = CreateObject("AccessControlList")
    Set oDenyDACL = CreateObject("AccessControlList")
    
    '**************************************************
    '*** Add individual ACEs into each of the lists
    '*** based on the ACE Flags and ACE Type values
    
    For Each oACE In oDACL 
    	If ((oACE.AceFlags AND ADS_ACEFLAG_INHERITED_ACE) = ADS_ACEFLAG_INHERITED_ACE) Then	 
    
    	'**************************************************
    	'*** as explained, no sorting is needed for Inherited ACEs, they are simply
    	'*** added to the list and retrieved at the end of the sub in the same order
    		oInheritedDACL.AddAce oACE 
    
    	Else
    
    	'**************************************************
    	'*** non-Inherited ACEs need to be placed in their respective list to be re-ordered
    
    		Select Case oACE.AceType	 
    			Case ADS_ACETYPE_ACCESS_ALLOWED		 
    				oAllowDACL.AddAce oACE	 
    			Case ADS_ACETYPE_ACCESS_DENIED		 
    				oDenyDACL.AddAce oACE    
    		End Select
    	End If
    Next
    
    '**************************************************
    '*** Recreate the Access Control List following the appropriate order
    '*** - non-Inherited Deny ACEs
    '*** - non-Inherited Allow ACEs
    '*** - Inherited ACEs
    
    For Each oACE In oDenyDACL 
    	 oNewDACL.AddAce oACE 
    Next
    For Each oACE In oAllowDACL
    	  oNewDACL.AddAce oACE
    Next 
    For Each oACE In oInheritedDACL
    	  oNewDACL.AddAce oACE 
    Next
    
    Set oInheritedDACL = Nothing
    Set oDenyDACL = Nothing
    Set oAllowDACL = Nothing
    
    '**************************************************
    '*** Set appropriate DACL revision level
    
    oNewDACL.AclRevision = oDACL.AclRevision
    
    '**************************************************
    '*** Reset the original DACL
    Set oDACL = Nothing
    Set oDACL = oNewDACL
    
    end Sub
    
    
    

    Get the Free Newsletter!

    Subscribe to Daily Tech Insider for top news, trends & analysis

    Latest Posts

    Related Stories