Auditing ACLs of files and folders

Author Message
59cobalt

  • Total Posts : 969
  • Scores: 91
  • Reward points : 0
  • Joined: 7/17/2011
  • Status: offline
Auditing ACLs of files and folders Tuesday, August 16, 2011 2:57 AM (permalink)
0
Every once in a while customers run into issues with permissions on their file servers. Manually analyzing permissions is quite tedious, even when using standard tools like cacls or xcacls. The output of XCACLS.vbs isn't any better, but since it's a script, I thought about modifying it to suit my needs. After taking an actual look at the script I abandoned that thought.

I came across ntfsacls, a nice little freeware utility, which is much closer to what I desire. However, it's still not exactly there. For instance, I'd have to run the command twice to get the permissions on the root directory and only the non-inherited permissions below the root. I don't find SDDL all that readable, too. And when using the simplified format, I got the impression that some items weren't shown accurately. I didn't look further into that last point, though, so my impression may be wrong.

Bottom line: I wasn't able to find a tool that would traverse a directory tree for me, and show me the permissions of the root folder plus all non-inherited permissions below the root in a format that I consider readable. After this question from toddW got me started, I thus wrote my own script for auditing ACLs on files and folders. Here's what I got so far (the script can also be downloaded here):
'! A script for auditing permissions of files and folders.
'!
'! @author  Ansgar Wiechers <ansgar.wiechers@planetcobalt.net>
'! @date    2011-08-16
'! @version 0.9

'! @todo add handling of SACLs

Option Explicit

' ControlFlags
' see <http://msdn.microsoft.com/en-us/library/aa394402.aspx>
Private Const SE_OWNER_DEFAULTED         = &h0001
Private Const SE_GROUP_DEFAULTED         = &h0002
Private Const SE_DACL_PRESENT            = &h0004
Private Const SE_DACL_DEFAULTED          = &h0008
Private Const SE_SACL_PRESENT            = &h0010
Private Const SE_SACL_DEFAULTED          = &h0020
Private Const SE_DACL_AUTO_INHERIT_REQ   = &h0100
Private Const SE_SACL_AUTO_INHERIT_REQ   = &h0200
Private Const SE_DACL_AUTO_INHERITED     = &h0400
Private Const SE_SACL_AUTO_INHERITED     = &h0800
Private Const SE_DACL_PROTECTED          = &h1000
Private Const SE_SACL_PROTECTED          = &h2000
Private Const SE_SELF_RELATIVE           = &h8000

' AccessMask flags
' see <http://msdn.microsoft.com/en-us/library/aa394063.aspx>
Private Const FILE_READ_DATA             = &h000001
Private Const FILE_LIST_DIRECTORY        = &h000001
Private Const FILE_WRITE_DATA            = &h000002
Private Const FILE_ADD_FILE              = &h000002
Private Const FILE_APPEND_DATA           = &h000004
Private Const FILE_ADD_SUBDIRECTORY      = &h000004
Private Const FILE_READ_EA               = &h000008
Private Const FILE_WRITE_EA              = &h000010
Private Const FILE_EXECUTE               = &h000020
Private Const FILE_TRAVERSE              = &h000020
Private Const FILE_DELETE_CHILD          = &h000040
Private Const FILE_READ_ATTRIBUTES       = &h000080
Private Const FILE_WRITE_ATTRIBUTES      = &h000100
Private Const DELETE                     = &h010000
Private Const READ_CONTROL               = &h020000
Private Const WRITE_DAC                  = &h040000
Private Const WRITE_OWNER                = &h080000
Private Const SYNCHRONIZE                = &h100000

' AceFlags
' see <http://msdn.microsoft.com/en-us/library/aa394063.aspx>
Private Const OBJECT_INHERIT_ACE         = &h01
Private Const CONTAINER_INHERIT_ACE      = &h02
Private Const NO_PROPAGATE_INHERIT_ACE   = &h04
Private Const INHERIT_ONLY_ACE           = &h08
Private Const INHERITED_ACE              = &h10
Private Const SUCCESSFUL_ACCESS_ACE_FLAG = &h40
Private Const FAILED_ACCESS_ACE_FLAG     = &h80

' AceType
' see <http://msdn.microsoft.com/en-us/library/aa394063.aspx>
Private Const ACCESS_ALLOWED = 0
Private Const ACCESS_DENIED  = 1
Private Const AUDIT          = 2

' simple permissions (for simplicity reasons defined as literal values, not as
' combinations of AccessMask flags)
Private Const FULL_CONTROL       = &h001f01ff
Private Const MODIFY             = &h001301bf
Private Const READ_WRITE_EXECUTE = &h001201bf
Private Const READ_EXECUTE       = &h001200a9
Private Const READ_WRITE         = &h0012019f
Private Const READ_ONLY          = &h00120089
Private Const WRITE_ONLY         = &h00100116

' maximum length of a string displaying aceFlags
Private Const MAXLEN_ACEFLAGS_STR = 16

Private accessMask : Set accessMask = CreateObject("Scripting.Dictionary")
 accessMask.Add 0                     , "-"
 accessMask.Add FILE_READ_DATA        , "r"
 accessMask.Add FILE_WRITE_DATA       , "w"
 accessMask.Add FILE_APPEND_DATA      , "a"
 accessMask.Add FILE_READ_EA          , "r"
 accessMask.Add FILE_WRITE_EA         , "w"
 accessMask.Add FILE_EXECUTE          , "x"
 accessMask.Add FILE_DELETE_CHILD     , "c"
 accessMask.Add FILE_READ_ATTRIBUTES  , "r"
 accessMask.Add FILE_WRITE_ATTRIBUTES , "w"
 accessMask.Add DELETE                , "d"
 accessMask.Add READ_CONTROL          , "r"
 accessMask.Add WRITE_DAC             , "w"
 accessMask.Add WRITE_OWNER           , "o"
 accessMask.Add SYNCHRONIZE           , "s"

Private aceFlags : Set aceFlags = CreateObject("Scripting.Dictionary")
 aceFlags.Add 0                       , ""
 aceFlags.Add OBJECT_INHERIT_ACE      , "(OI)"
 aceFlags.Add CONTAINER_INHERIT_ACE   , "(CI)"
 aceFlags.Add NO_PROPAGATE_INHERIT_ACE, "(NP)"
 aceFlags.Add INHERIT_ONLY_ACE        , "(IO)"
 aceFlags.Add INHERITED_ACE           , "(I)"

Private aceType : Set aceType = CreateObject("Scripting.Dictionary")
 aceType.Add ACCESS_ALLOWED, "A"
 aceType.Add ACCESS_DENIED , "D"
 aceType.Add AUDIT         , "I"

Private simplePermissions : Set simplePermissions = CreateObject("Scripting.Dictionary")
 simplePermissions.Add FULL_CONTROL      , "F  "
 simplePermissions.Add MODIFY            , "M  "
 simplePermissions.Add READ_WRITE_EXECUTE, "RWX"
 simplePermissions.Add READ_EXECUTE      , "RX "
 simplePermissions.Add READ_WRITE        , "RW "
 simplePermissions.Add READ_ONLY         , "R  "
 simplePermissions.Add WRITE_ONLY        , "W  "

' global configuration flags
Private showOwner, showInheritedPermissions, showSimplePermissions, showFiles, recurse

Private fso : Set fso = CreateObject("Scripting.FileSystemObject")

Main WScript.Arguments

'! Main procedure to handle commandline arguments and start the auditing.
'!
'! @param  args   The commandline arguments passed to the script.
Sub Main(args)
 Dim arg, path

 ' check if script is run with cscript.exe, abort otherwise
 If LCase(Right(WScript.FullName, 11)) <> "cscript.exe" Then
 MsgBox WScript.ScriptName & " must be run with cscript.exe!" _
 , vbOKOnly Or vbCritical, WScript.ScriptName
 WScript.Quit 1
 End If

 ' evaluate commandline options
 If args.Named.Exists("?") Then PrintUsage
 showOwner = args.Named.Exists("o")
 showInheritedPermissions = args.Named.Exists("i")
 showSimplePermissions = args.Named.Exists("s")
 showFiles = args.Named.Exists("f")
 recurse = args.Named.Exists("r")

 For Each arg In args.Unnamed
 path = fso.GetAbsolutePathName(arg)
 If fso.FileExists(path) Then
 ' arg is a file
 PrintSecurityInformation fso.GetFile(path), "", ""
 ElseIf fso.FolderExists(path) Then
 ' arg is a folder
 PrintSecurityInformation fso.GetFolder(path), "", ""
 Else
 ' Print an error if arg doesn't exist and continue.
 WScript.StdErr.WriteLine "File or folder '" & path & "' does not exist."
 End If
 Next
End Sub

'! Print security information on the given object. The object can be either a
'! folder or a file. The amount of information displayed depends on the global
'! flags showOwner, showInheritedPermissions and showSimplePermissions.
'!
'! If the object is a folder and the flags recurse or showFiles are set to
'! True, security information on the subfolders or files is printed as well.
'!
'! @param  obj          The folder or file for which security information
'!                      should be printed.
'! @param  parentPrefix Indention prefix for the information on the parent
'!                      folder of the current file/folder.
'! @param  myPrefix     Additional prefix for the current file/folder.
Private Sub PrintSecurityInformation(obj, ByVal parentPrefix, ByVal myPrefix)
 Dim record, sd, owner, group, ace
 Dim indentString, i, sf, f

 record = parentPrefix & myPrefix & obj.Name

 ' Adjust the indention string according to whether the current object has
 ' subfolders or has files that should be displayed.
 If myPrefix = "+-" Then myPrefix = "| " ' "connect" subsequent object unless
 If myPrefix = "`-" Then myPrefix = "  " ' it's the last object (which has no successor)
 indentString = parentPrefix & myPrefix & "    "

 If TypeName(obj) = "Folder" Then
 record = record & "\" ' append a "\" to folder names
 ' "connect" subsequent objects through the ACEs for the current object
 If (showFiles And obj.Files.Count > 0) Or obj.SubFolders.Count > 0 Then
 indentString = parentPrefix & myPrefix & "|   "
 End If
 End If

 Set sd = GetSecurityDescriptor(obj.Path)

 ' display owner information
 owner = FormatTrustee(sd.Owner)
 If Not IsNull(sd.Group) Then owner = owner & " (" & FormatTrustee(sd.Group) & ")"
 If showOwner Then record = record & vbTab & owner

 ' display DACLs
 If IsSet(sd.ControlFlags, SE_DACL_PRESENT) Then
 For Each ace In sd.DACL
 If showInheritedPermissions Or Not IsInherited(ace) Then record = record _
 & vbNewLine & indentString & FormatACE(ace)
 Next
 End If

 ' Display SACLs
 '~ If IsSet(sd.ControlFlags, SE_SACL_PRESENT) Then
 '~ For Each ace In sd.SACL
 '~ ' do stuff
 '~ Next
 '~ End If

 WScript.StdOut.WriteLine record

 ' If the given object is a folder and recurse is True or showFiles is True,
 ' print security information of the contained folders or files respectively.
 If TypeName(obj) = "Folder" Then
 If recurse Then
 i = 0
 For Each sf In obj.SubFolders
 i = i + 1
 If i = obj.SubFolders.Count And (Not showFiles Or obj.Files.Count = 0) Then
 PrintSecurityInformation sf, parentPrefix & myPrefix, "`-"
 Else
 PrintSecurityInformation sf, parentPrefix & myPrefix, "+-"
 End If
 Next
 End If

 If showFiles Then
 i = 0
 For Each f In obj.Files
 i = i + 1
 If i = obj.Files.Count Then
 PrintSecurityInformation f, parentPrefix & myPrefix, "`-"
 Else
 PrintSecurityInformation f, parentPrefix & myPrefix, "+-"
 End If
 Next
 End If
 End If
End Sub

'! Get the security descriptor of the given file or folder. The function does
'! NOT check whether the object exists, so the caller MUST ensure this.
'!
'! @param  path   A relative or absolute path to a file or folder.
'! @return The security descriptor object of the file/folder.
'!
'! @see <http://msdn.microsoft.com/en-us/library/aa390773.aspx> (GetSecurityDescriptor Method of the Win32_LogicalFileSecuritySetting Class)
'! @see <http://msdn.microsoft.com/en-us/library/aa394577.aspx> (WMI Security Descriptor Objects)
Private Function GetSecurityDescriptor(ByVal path)
 Dim wmiFileSecSetting, wmiSD

 Set GetSecurityDescriptor = Nothing

 path = fso.GetAbsolutePathName(path)
 If fso.FileExists(path) Or fso.FolderExists(path) Then
 On Error Resume Next
 Set wmiFileSecSetting = GetObject("winmgmts:Win32_LogicalFileSecuritySetting.path='" _
 & Replace(path, "\", "\\") & "'")
 wmiFileSecSetting.GetSecurityDescriptor wmiSD
 If Err.Number = 0 Then
 Set GetSecurityDescriptor = wmiSD
 Else
 WScript.StdErr.WriteLine Err.Description & " (" & Hex(Err.Number) & ")"
 End If
 On Error Goto 0
 End If
End Function

'! Return a formatted string representing the given ACE. An ACE is presented
'! either in simple or in full form, depending on the showSimple paramenter.
'!
'! - Simple: A RX  Trustee
'! - Full:   D rwaxdc rw rw rwo [(OI)(CI)(IO)(NP)] Trustee
'!
'! First is always the ACE type (A = Allow, D = Deny, I = Audit). Last is
'! always the name of the trustee (user, group or security principal) to whom
'! the ACE applies.
'!
'! When showSimplePermissions is set to True, permissions can be either full
'! control (F), modify (M), read & write & execute (RWX), read & execute (RX),
'! read & write (RW), read-only (R), write-only (W). All other permissions are
'! displayed as "(S)" (special permissions).
'!
'! When showSimple is False, the second group shows the flags for read (r),
'! write (w), append (a), execute (x), delete (d) and delete child (c)
'! permissions. The next two groups specify read/write permissions for
'! attributes and extended attributes respectively. The fourth group shows
'! the rights to read permissions (r), write permissions (w) and take
'! ownership (o), followed by a group with inheritance settings. This fifth
'! group may be empty if permissions to the object are neither inherited nor
'! inheritable).
'!
'! @param  ace        The ACE.
'! @return A string representation of the given ACE.
Private Function FormatACE(ByVal ace)
 Dim inheritance

 FormatACE = aceType(ace.AceType) & " "
 If showSimplePermissions Then
 ' When displaying simple permissions, all access masks that aren't exact
 ' matches will be displayed as "(S)" (special permissions).
 If simplePermissions.Exists(ace.AccessMask) Then
 FormatACE = FormatACE & simplePermissions(ace.AccessMask)
 Else
 FormatACE = FormatACE & "(S)"
 End If
 Else
 ' Display full permissions and inheritance settings.
 inheritance = aceFlags(ace.AceFlags And OBJECT_INHERIT_ACE) _
 & aceFlags(ace.AceFlags And CONTAINER_INHERIT_ACE) _
 & aceFlags(ace.AceFlags And INHERIT_ONLY_ACE) _
 & aceFlags(ace.AceFlags And NO_PROPAGATE_INHERIT_ACE)

 FormatACE = FormatACE _
 & accessMask(ace.AccessMask And FILE_READ_DATA) _
 & accessMask(ace.AccessMask And FILE_WRITE_DATA) _
 & accessMask(ace.AccessMask And FILE_APPEND_DATA) _
 & accessMask(ace.AccessMask And FILE_EXECUTE) _
 & accessMask(ace.AccessMask And DELETE) _
 & accessMask(ace.AccessMask And FILE_DELETE_CHILD) & " " _
 & accessMask(ace.AccessMask And FILE_READ_ATTRIBUTES) _
 & accessMask(ace.AccessMask And FILE_WRITE_ATTRIBUTES) & " " _
 & accessMask(ace.AccessMask And FILE_READ_EA) _
 & accessMask(ace.AccessMask And FILE_WRITE_EA) & " " _
 & accessMask(ace.AccessMask And READ_CONTROL) _
 & accessMask(ace.AccessMask And WRITE_DAC) _
 & accessMask(ace.AccessMask And WRITE_OWNER) & " " _
 & inheritance & String(MAXLEN_ACEFLAGS_STR - Len(inheritance), " ")
 End If
 FormatACE = FormatACE & " " & FormatTrustee(ace.Trustee)
End Function

'! Format user and group names. If the domain is an FQDN, the format
'! USER@DOMAIN is used, otherwise the format DOMAIN\USER.
'!
'! @param  trustee  A Win32_Trustee data structure.
'! @return The formatted user/group name for the given trustee.
'!
'! @see <http://msdn.microsoft.com/en-us/library/aa394501.aspx>
Private Function FormatTrustee(ByVal trustee)
 If InStr(trustee.Domain, ".") > 0 Then
 FormatTrustee = trustee.Name & "@" & trustee.Domain
 ElseIf IsNull(trustee.Domain) Or trustee.Domain = "" Then
 FormatTrustee = trustee.Name
 Else
 FormatTrustee = trustee.Domain & "\" & trustee.Name
 End If
End Function

'! Check if a flag is set in the given value.
'!
'! @param  val  An integer value.
'! @param  flag The flag to check.
'! @return True if the flag is set, otherwise False.
Private Function IsSet(ByVal val, ByVal flag)
 IsSet = ((val And flag) = flag)
End Function

'! Check if the given ACE is inherited from a parent object.
'!
'! @param  ace  The ACE.
'! @return True if the ACE is inherited, otherwise False.
Private Function IsInherited(ByVal ace)
 IsInherited = IsSet(ace.AceFlags, INHERITED_ACE)
End Function

'! Print usage information and exit.
Private Sub PrintUsage
 WScript.Echo "Display security information of a given file, folder or directory tree." & vbNewLine & vbNewLine _
 & "Usage:" & vbTab & WScript.ScriptName & " [/f] [/i] [/o] [/s] [/r] FILE/FOLDER [FILE/FOLDER ...]" & vbNewLine _
 & vbTab & WScript.ScriptName & " /?" & vbNewLine & vbNewLine _
 & vbTab & "/?" & vbTab & "Print this help and exit." & vbNewLine _
 & vbTab & "/f" & vbTab & "Show security information of files as well (not only folders)." & vbNewLine _
 & vbTab & "/i" & vbTab & "Show inherited permissions." & vbNewLine _
 & vbTab & "/o" & vbTab & "Show owner." & vbNewLine _
 & vbTab & "/r" & vbTab & "Recurse into subfolders." & vbNewLine _
 & vbTab & "/s" & vbTab & "Show simple permissions."
 WScript.Quit 0
End Sub
Comments? Suggestions?
 
#1
    59cobalt

    • Total Posts : 969
    • Scores: 91
    • Reward points : 0
    • Joined: 7/17/2011
    • Status: offline
    Re:Auditing ACLs of files and folders Thursday, September 15, 2011 10:13 AM (permalink)
    0
    After several changes ("simple permissions" became the default, permissions of the root object are always displayed, SACLs are displayed, SIDs are displayed for non-resolvable trustee names, ...) I have now finished version 1.0. Feedback would be much appreciated.
     
    #2

      Online Bookmarks Sharing: Share/Bookmark

      Jump to:

      Current active users

      There are 0 members and 1 guests.

      Icon Legend and Permission

      • New Messages
      • No New Messages
      • Hot Topic w/ New Messages
      • Hot Topic w/o New Messages
      • Locked w/ New Messages
      • Locked w/o New Messages
      • Read Message
      • Post New Thread
      • Reply to message
      • Post New Poll
      • Submit Vote
      • Post reward post
      • Delete my own posts
      • Delete my own threads
      • Rate post

      2000-2012 ASPPlayground.NET Forum Version 3.9