【问题标题】:Python - Get windows folder ACL permissionsPython - 获取 windows 文件夹 ACL 权限
【发布时间】:2016-04-14 10:15:36
【问题描述】:

我正在寻找使用 Python 27 获取文件夹 ACL 权限的示例。 我需要的结果是:域\用户名 - FullControl,域\用户名修改

谢谢!

【问题讨论】:

    标签: python windows permissions acl


    【解决方案1】:

    这是使用 Tim Golden 的 wmi module 的 WMI 示例。它为给定路径选择Win32_LogicalFileSecuritySetting 的实例。它调用GetSecurityDescriptor 方法来获取Win32_SecurityDescriptor。我用它来创建AceFileSecurity namedtuple 实例。我添加了一些方法来测试 ACE 授予、拒绝或审核的访问权限,并以类似于 icacls 使用的格式输出数据。

    我还包含 ctypes 代码以启用 SeSecurityPrivilege,这是读取系统访问控制列表 (SACL) 所必需的。

    导入和常量

    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
    

    _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)
    

    函数

    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)
    

    访问 SACL 需要 SeSecurityPrivilege。下面是一些启用权限的 ctypes 代码:

    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)
    

    例如,我在“程序文件”目录中添加了一个审计 ACE,以记录任何人更改目录权限或所有者的任何尝试。此 ACE 类型存储在系统访问控制列表 (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)
    

    以下显示拒绝 ACE 的显示方式:

    >>> 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)
    

    【讨论】:

    • 谢谢。但是有什么简单的方法可以做到这一点吗?对于这么简单的任务,这个例子看起来相当复杂。
    • 如果您正在编写一个从头开始的程序,那么使用 wmi 是一种简单的方法。我本可以通过 ctypes 使用 Windows API 来完成所有工作,但这会花费更长的时间。如果您只想要文本输出,使用 subprocess 调用 icacls.exe 并转储 DACL 会简单得多,但是(1)这通常不是很有用,(2)它不是 Stack Overflow 的主题;这是Super User 的主题。
    • 多么可怕的传说 ;) 我正在尝试编写一个处理继承的 python set_acls() 函数。谢谢,这真的很有帮助。
    猜你喜欢
    • 2014-05-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-05-15
    • 2023-03-21
    • 2011-05-21
    • 2017-08-29
    • 1970-01-01
    相关资源
    最近更新 更多