I am looking for an example to get a folder ACL permissions with Python 27 . I need the result to be like: domain\username - FullControl, domain\username Modify
Thank you !
Here's WMI example that uses Tim Golden's wmi module. It selects an instance of Win32_LogicalFileSecuritySetting
for a given path. It calls the GetSecurityDescriptor
method to get a Win32_SecurityDescriptor
. I use this to create Ace
and FileSecurity
namedtuple instances. I've added a few methods to test the access granted, denied, or audited by an ACE and output the data in a format that's similar to what icacls uses.
I'm also including ctypes code to enable SeSecurityPrivilege
, which is required in order to read the system access control list (SACL).
Imports and constants
import os
import wmi
import collections
import ctypes
from ctypes import wintypes
SE_OWNER_DEFAULTED = 0x0001
SE_GROUP_DEFAULTED = 0x0002
SE_DACL_PRESENT = 0x0004
SE_DACL_DEFAULTED = 0x0008
SE_SACL_PRESENT = 0x0010
SE_SACL_DEFAULTED = 0x0020
SE_DACL_AUTO_INHERIT_REQ = 0x0100
SE_SACL_AUTO_INHERIT_REQ = 0x0200
SE_DACL_AUTO_INHERITED = 0x0400
SE_SACL_AUTO_INHERITED = 0x0800
SE_DACL_PROTECTED = 0x1000
SE_SACL_PROTECTED = 0x2000
SE_SELF_RELATIVE = 0x8000
OBJECT_INHERIT_ACE = 0x01
CONTAINER_INHERIT_ACE = 0x02
NO_PROPAGATE_INHERIT_ACE = 0x04
INHERIT_ONLY_ACE = 0x08
INHERITED_ACE = 0x10
SUCCESSFUL_ACCESS_ACE_FLAG = 0x40
FAILED_ACCESS_ACE_FLAG = 0x80
ACCESS_ALLOWED_ACE_TYPE = 0
ACCESS_DENIED_ACE_TYPE = 1
SYSTEM_AUDIT_ACE_TYPE = 2
DELETE = 0x00010000 # DE
READ_CONTROL = 0x00020000 # RC
WRITE_DAC = 0x00040000 # WDAC
WRITE_OWNER = 0x00080000 # WO
SYNCHRONIZE = 0x00100000 # S
ACCESS_SYSTEM_SECURITY = 0x01000000 # AS
GENERIC_READ = 0x80000000 # GR
GENERIC_WRITE = 0x40000000 # GW
GENERIC_EXECUTE = 0x20000000 # GE
GENERIC_ALL = 0x10000000 # GA
FILE_READ_DATA = 0x00000001 # RD
FILE_LIST_DIRECTORY = 0x00000001
FILE_WRITE_DATA = 0x00000002 # WD
FILE_ADD_FILE = 0x00000002
FILE_APPEND_DATA = 0x00000004 # AD
FILE_ADD_SUBDIRECTORY = 0x00000004
FILE_READ_EA = 0x00000008 # REA
FILE_WRITE_EA = 0x00000010 # WEA
FILE_EXECUTE = 0x00000020 # X
FILE_TRAVERSE = 0x00000020
FILE_DELETE_CHILD = 0x00000040 # DC
FILE_READ_ATTRIBUTES = 0x00000080 # RA
FILE_WRITE_ATTRIBUTES = 0x00000100 # WA
FILE_GENERIC_READ = (FILE_READ_DATA |
FILE_READ_EA |
FILE_READ_ATTRIBUTES |
READ_CONTROL |
SYNCHRONIZE)
FILE_GENERIC_WRITE = (FILE_WRITE_DATA |
FILE_APPEND_DATA |
FILE_WRITE_EA |
FILE_WRITE_ATTRIBUTES |
READ_CONTROL |
SYNCHRONIZE)
FILE_GENERIC_EXECUTE = (FILE_EXECUTE |
FILE_READ_ATTRIBUTES |
READ_CONTROL |
SYNCHRONIZE)
FILE_ALL_ACCESS = 0x001F01FF
FILE_MODIIFY_ACCESS = FILE_ALL_ACCESS & ~(FILE_DELETE_CHILD |
WRITE_DAC |
WRITE_OWNER)
FILE_READ_EXEC_ACCESS = FILE_GENERIC_READ | FILE_GENERIC_EXECUTE
FILE_DELETE_ACCESS = DELETE | SYNCHRONIZE
Classes
_Ace = collections.namedtuple('_Ace',
'ace_type flags mask mapped_mask sid trustee')
class Ace(_Ace):
def __new__(cls, ace_type, flags, mask, sid, trustee):
mapped_mask = cls._map_generic(mask)
return super(Ace, cls).__new__(cls, ace_type, flags,
mask, mapped_mask, sid, trustee)
@staticmethod
def _map_generic(mask):
if mask & GENERIC_READ:
mask = (mask & ~GENERIC_READ) | FILE_GENERIC_READ
if mask & GENERIC_WRITE:
mask = (mask & ~GENERIC_WRITE) | FILE_GENERIC_WRITE
if mask & GENERIC_EXECUTE:
mask = (mask & ~GENERIC_EXECUTE) | FILE_GENERIC_EXECUTE
if mask & GENERIC_ALL:
mask = (mask & ~GENERIC_ALL) | FILE_ALL_ACCESS
return mask
def inherited(self): # I
return bool(self.flags & INHERITED_ACE)
def object_inherit(self): # OI
return bool(self.flags & OBJECT_INHERIT_ACE)
def container_inherit(self): # CI
return bool(self.flags & CONTAINER_INHERIT_ACE)
def inherit_only(self): # IO
return bool(self.flags & INHERIT_ONLY_ACE)
def no_propagate(self): # NP
return bool(self.flags & NO_PROPAGATE_INHERIT_ACE)
def no_access(self): # N
return self.mapped_mask == 0
def full_access(self): # F
return self.mapped_mask == FILE_ALL_ACCESS
def modify_access(self): # M
return self.mapped_mask == FILE_MODIIFY_ACCESS
def read_exec_access(self): # RX
return self.mapped_mask == FILE_READ_EXEC_ACCESS
def read_only_access(self): # R
return self.mapped_mask == FILE_GENERIC_READ
def write_only_access(self): # W
return self.mapped_mask == FILE_GENERIC_WRITE
def delete_access(self): # D
return self.mapped_mask == FILE_DELETE_ACCESS
def get_file_rights(self):
if self.no_access(): return ['N']
if self.full_access(): return ['F']
if self.modify_access(): return ['M']
if self.read_exec_access(): return ['RX']
if self.read_only_access(): return ['R']
if self.write_only_access(): return ['W']
if self.delete_access(): return ['D']
rights = []
for right, name in ((DELETE, 'DE'), (READ_CONTROL, 'RC'),
(WRITE_DAC, 'WDAC'), (WRITE_OWNER, 'WO'),
(SYNCHRONIZE, 'S'),
(ACCESS_SYSTEM_SECURITY, 'AS'),
(GENERIC_READ, 'GR'), (GENERIC_WRITE, 'GW'),
(GENERIC_EXECUTE, 'GE'), (GENERIC_ALL, 'GA'),
(FILE_READ_DATA, 'RD'), (FILE_WRITE_DATA, 'WD'),
(FILE_APPEND_DATA, 'AD'), (FILE_READ_EA, 'REA'),
(FILE_WRITE_EA, 'WEA'), (FILE_EXECUTE, 'X'),
(FILE_DELETE_CHILD, 'DC'),
(FILE_READ_ATTRIBUTES, 'RA'),
(FILE_WRITE_ATTRIBUTES, 'WA')):
if self.mask & right:
rights.append(name)
return rights
def granted_access(self, mask):
return bool(self.mapped_mask & self._map_generic(mask))
def __str__(self):
trustee = self.trustee if self.trustee else self.sid
access = []
if self.ace_type == ACCESS_DENIED_ACE_TYPE:
access.append('(DENY)')
elif self.ace_type == SYSTEM_AUDIT_ACE_TYPE:
access.append('(AUDIT)')
if self.inherited(): access.append('(I)')
if self.object_inherit(): access.append('(OI)')
if self.container_inherit(): access.append('(CI)')
if self.inherit_only(): access.append('(IO)')
if self.no_propagate(): acccess.append('(NP)')
access.append('(%s)' % ','.join(self.get_file_rights()))
return '%s:%s' % (trustee, ''.join(access))
_FileSecurity = collections.namedtuple('_FileSecurity',
'path owner_permissions owner group '
'owner_sid group_sid flags dacl sacl')
class FileSecurity(_FileSecurity):
def __str__(self):
owner = self.owner if self.owner else self.owner_sid
group = self.group if self.group else self.group_sid
items = ['Path: %s' % self.path,
'Owner: %s' % owner,
'Group: %s' % group]
if self.dacl:
items += ['DACL: %s' %
'\n '.join(str(x) for x in self.dacl)]
if self.sacl:
items += ['SACL: %s' %
'\n '.join(str(x) for x in self.sacl)]
return '\n'.join(items)
Functions
def list_acl(wmi_acl):
acl = []
for entry in wmi_acl:
trustee = entry.Trustee.Name
if trustee and entry.Trustee.Domain:
trustee = '%s\\%s' % (entry.Trustee.Domain, trustee)
mask = entry.AccessMask
if mask < 0:
mask += 2 ** 32
ace = Ace(entry.AceType, entry.AceFlags, mask,
entry.Trustee.SIDString, trustee)
acl.append(ace)
return acl
# Win32_LogicalFileSecuritySetting
# https://msdn.microsoft.com/en-us/library/aa394180
WQL_LFSS = 'SELECT * FROM Win32_LogicalFileSecuritySetting WHERE Path="%s"'
wmi_ns = wmi.WMI()
def get_file_security(path):
path = os.path.abspath(path)
os.stat(path) # ensure path exists
lfss = wmi_ns.query(WQL_LFSS % (path,))[0]
sd = lfss.GetSecurityDescriptor()[0]
owner = sd.Owner.Name
if owner and sd.Owner.Domain:
owner = '%s\\%s' % (sd.Owner.Domain, owner)
group = sd.Group.Name
if group and sd.Group.Domain:
group = '%s\\%s' % (sd.Group.Domain, group)
dacl = sacl = ()
if sd.ControlFlags & SE_DACL_PRESENT:
dacl = tuple(list_acl(sd.DACL))
if sd.ControlFlags & SE_SACL_PRESENT:
sacl = tuple(list_acl(sd.SACL))
return FileSecurity(lfss.Path,
lfss.OwnerPermissions,
owner, group,
sd.Owner.SIDString,
sd.Group.SIDString,
sd.ControlFlags,
dacl, sacl)
Accessing the SACL requires SeSecurityPrivilege
. Here's some ctypes code to enable a privilege:
kernel32 = ctypes.WinDLL('kernel32', use_last_error=True)
advapi32 = ctypes.WinDLL('advapi32', use_last_error=True)
ERROR_NOT_ALL_ASSIGNED = 0x0514
SE_PRIVILEGE_ENABLED = 0x00000002
TOKEN_ALL_ACCESS = 0x000F0000 | 0x01FF
class LUID(ctypes.Structure):
_fields_ = (('LowPart', wintypes.DWORD),
('HighPart', wintypes.LONG))
class LUID_AND_ATTRIBUTES(ctypes.Structure):
_fields_ = (('Luid', LUID),
('Attributes', wintypes.DWORD))
class TOKEN_PRIVILEGES(ctypes.Structure):
_fields_ = (('PrivilegeCount', wintypes.DWORD),
('Privileges', LUID_AND_ATTRIBUTES * 1))
def __init__(self, PrivilegeCount=1, *args):
super(TOKEN_PRIVILEGES, self).__init__(PrivilegeCount, *args)
PDWORD = ctypes.POINTER(wintypes.DWORD)
PHANDLE = ctypes.POINTER(wintypes.HANDLE)
PLUID = ctypes.POINTER(LUID)
PTOKEN_PRIVILEGES = ctypes.POINTER(TOKEN_PRIVILEGES)
def errcheck_bool(result, func, args):
if not result:
raise ctypes.WinError(ctypes.get_last_error())
return args
kernel32.CloseHandle.argtypes = (wintypes.HANDLE,)
kernel32.GetCurrentProcess.errcheck = errcheck_bool
kernel32.GetCurrentProcess.restype = wintypes.HANDLE
# https://msdn.microsoft.com/en-us/library/aa379295
advapi32.OpenProcessToken.errcheck = errcheck_bool
advapi32.OpenProcessToken.argtypes = (
wintypes.HANDLE, # _In_ ProcessHandle
wintypes.DWORD, # _In_ DesiredAccess
PHANDLE) # _Out_ TokenHandle
# https://msdn.microsoft.com/en-us/library/aa379180
advapi32.LookupPrivilegeValueW.errcheck = errcheck_bool
advapi32.LookupPrivilegeValueW.argtypes = (
wintypes.LPCWSTR, # _In_opt_ lpSystemName
wintypes.LPCWSTR, # _In_ lpName
PLUID) # _Out_ lpLuid
# https://msdn.microsoft.com/en-us/library/aa375202
advapi32.AdjustTokenPrivileges.errcheck = errcheck_bool
advapi32.AdjustTokenPrivileges.argtypes = (
wintypes.HANDLE, # _In_ TokenHandle
wintypes.BOOL, # _In_ DisableAllPrivileges
PTOKEN_PRIVILEGES, # _In_opt_ NewState
wintypes.DWORD, # _In_ BufferLength
PTOKEN_PRIVILEGES, # _Out_opt_ PreviousState
PDWORD) # _Out_opt_ ReturnLength
def enable_privilege(privilege):
hToken = wintypes.HANDLE()
luid = LUID()
tp = TOKEN_PRIVILEGES()
advapi32.LookupPrivilegeValueW(None, privilege, ctypes.byref(luid))
tp.Privileges[0].Luid = luid
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED
advapi32.OpenProcessToken(kernel32.GetCurrentProcess(),
TOKEN_ALL_ACCESS,
ctypes.byref(hToken))
try:
advapi32.AdjustTokenPrivileges(hToken, False,
ctypes.byref(tp),
ctypes.sizeof(tp),
None, None)
if ctypes.get_last_error() == ERROR_NOT_ALL_ASSIGNED:
raise ctypes.WinError(ERROR_NOT_ALL_ASSIGNED)
finally:
kernel32.CloseHandle(hToken)
def disable_privilege(privilege):
hToken = wintypes.HANDLE()
luid = LUID()
tp = TOKEN_PRIVILEGES()
advapi32.LookupPrivilegeValueW(None, privilege, ctypes.byref(luid))
tp.Privileges[0].Luid = luid
tp.Privileges[0].Attributes = 0
advapi32.OpenProcessToken(kernel32.GetCurrentProcess(),
TOKEN_ALL_ACCESS,
ctypes.byref(hToken))
try:
advapi32.AdjustTokenPrivileges(hToken, False,
ctypes.byref(tp),
ctypes.sizeof(tp),
None, None)
if ctypes.get_last_error() == ERROR_NOT_ALL_ASSIGNED:
raise ctypes.WinError(ERROR_NOT_ALL_ASSIGNED)
finally:
kernel32.CloseHandle(hToken)
For example, I added an audit ACE to the "Program Files" directory to log any attempt by anyone to change the permissions or owner of the directory. This ACE type is stored in the system access control list (SACL).
>>> enable_privilege('SeSecurityPrivilege')
>>> print get_file_security('C:\\Program Files')
Path : C:\Program Files
Owner: NT SERVICE\TrustedInstaller
Group: NT SERVICE\TrustedInstaller
DACL : NT SERVICE\TrustedInstaller:(F)
NT SERVICE\TrustedInstaller:(CI)(IO)(F)
NT AUTHORITY\SYSTEM:(M)
NT AUTHORITY\SYSTEM:(OI)(CI)(IO)(F)
BUILTIN\Administrators:(M)
BUILTIN\Administrators:(OI)(CI)(IO)(F)
BUILTIN\Users:(RX)
BUILTIN\Users:(OI)(CI)(IO)(RX)
CREATOR OWNER:(OI)(CI)(IO)(F)
APPLICATION PACKAGE AUTHORITY\ALL APPLICATION PACKAGES:(RX)
APPLICATION PACKAGE AUTHORITY\ALL APPLICATION PACKAGES:(OI)(CI)(IO)(RX)
SACL : Everyone:(AUDIT)(WDAC,WO)
The following shows how a deny ACE appears:
>>> f = open('tempfile', 'w'); f.close()
>>> os.system('icacls tempfile /deny Guests:(M)')
processed file: tempfile
Successfully processed 1 files; Failed processing 0 files
0
>>> print get_file_security('tempfile')
Path : C:\Temp\tempfile
Owner: BUILTIN\Administrators
Group: THISPC\None
DACL : BUILTIN\Guests:(DENY)(M)
BUILTIN\Administrators:(I)(F)
NT AUTHORITY\SYSTEM:(I)(F)
BUILTIN\Users:(I)(RX)
NT AUTHORITY\Authenticated Users:(I)(M)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With