如何使用Python授权/拒绝对Windows上的目录的写访问?

时间:2014-10-20 12:18:30

标签: python windows winapi authorization

我希望能够授权或拒绝对Windows XP上特定目录的写访问权限。

我尝试了以下操作,但它们都不起作用:

  • os.chmod():只能指定文件只读属性,请参阅Python's doc
  • win32api.SetFileAttribute() FILE_ATTRIBUTE_READONLY:只读文件。 [...]目录不支持此属性,请参阅MSDN's SetFileAttribute

看起来我唯一的选择是访问和更新目录的“Security info”,我已经尝试了几个小时来完成这项工作而没有取得多大成功(我真的很陌生使用Win32 API)。

关于如何做到这一点的任何想法?

2 个答案:

答案 0 :(得分:5)

这只是挑战性的事情。我从this really great answer开始,它可以帮助你做类似的事情。

您可以首先列出目录的ACL,这可以使用以下代码完成:

import win32security
import ntsecuritycon as con

FILENAME = r'D:\tmp\acc_test' 

sd = win32security.GetFileSecurity(FILENAME, win32security.DACL_SECURITY_INFORMATION)
dacl = sd.GetSecurityDescriptorDacl()

ace_count = dacl.GetAceCount()
print('Ace count:', ace_count)

for i in range(0, ace_count):
    rev, access, usersid = dacl.GetAce(i)
    user, group, type  = win32security.LookupAccountSid('', usersid)
    print('User: {}/{}'.format(group, user), rev, access)

您可以找到返回ACE数量的方法PyACL.GetAceCount()

GetAce(i)函数将ACCESS_ALLOWED_ACE header作为tuple返回:

现在您可以阅读旧的ACE,而deleting old ones非常简单:

for i in range(0, ace_count):
    dacl.DeleteAce(0)

之后,您只需拨打AddAccessAllowedAceEx() [MSDN]即可添加权限:

userx, domain, type = win32security.LookupAccountName ("", "your.user")
usery, domain, type = win32security.LookupAccountName ("", "other.user")

dacl.AddAccessAllowedAceEx(win32security.ACL_REVISION, 3, 2032127, userx) # Full control
dacl.AddAccessAllowedAceEx(win32security.ACL_REVISION, 3, 1179785, usery) # Read only

sd.SetSecurityDescriptorDacl(1, dacl, 0)   # may not be necessary
win32security.SetFileSecurity(FILENAME, win32security.DACL_SECURITY_INFORMATION, sd)

我从脚本前半部分的列表中获取了数字320321271179785(在运行脚本之前,我在 Explorer中设置了权限 - >右键单击 - >属性 - >安全 - >高级):

Explorer->Right Click->Properties->Security->Advanced

只是从http://technet.microsoft.com/

借来的说明性图片
User: DOMAIN/user (0, 3) 2032127
User: DOMAIN/user2 (0, 3) 1179785

但它对应于:

  • 3 - > OBJECT_INHERIT_ACE|CONTAINER_INHERIT_ACE
  • 2032127 - > FILE_ALL_ACCESS(实际上是con.FILE_ALL_ACCESS = 2032639,但是一旦你将其应用到文件中并将其读回来,你将获得 2032127 ;差异是 512 - 0x0200 - 我在ntsecuritycon.py/file security permissions
  • 中找不到的常数
  • 1179785 - > FILE_GENERIC_READ

您也可以删除访问权限,更改权限或将其删除,但这应该是非常可靠的开始。


TL; DR - 代码

import win32security
import ntsecuritycon as con

FILENAME = r'D:\tmp\acc_test'

userx, domain, type = win32security.LookupAccountName ("", "your.user")
usery, domain, type = win32security.LookupAccountName ("", "other.user")

sd = win32security.GetFileSecurity(FILENAME, win32security.DACL_SECURITY_INFORMATION)
dacl = sd.GetSecurityDescriptorDacl()

ace_count = dacl.GetAceCount()
print('Ace count:', ace_count)

# Listing
for i in range(0, ace_count):
    rev, access, usersid = dacl.GetAce(i)
    user, group, type  = win32security.LookupAccountSid('', usersid)

    print('User: {}/{}'.format(group, user), rev, access)

# Removing the old ones
for i in range(0, ace_count):
    dacl.DeleteAce(0)

# Add full control for user x
dacl.AddAccessAllowedAceEx(win32security.ACL_REVISION, 
    con.OBJECT_INHERIT_ACE|con.CONTAINER_INHERIT_ACE, con.FILE_ALL_ACCESS, userx)

# Add read only access for user y
dacl.AddAccessAllowedAceEx(win32security.ACL_REVISION, 
    con.OBJECT_INHERIT_ACE|con.CONTAINER_INHERIT_ACE, con.FILE_GENERIC_READ, usery)

sd.SetSecurityDescriptorDacl(1, dacl, 0)   # may not be necessary
win32security.SetFileSecurity(FILENAME, win32security.DACL_SECURITY_INFORMATION, sd)

用于完整ACE列表的迷你实用程序

我刚刚编写了用于解析所有文件ACE的小脚本:

import win32security
import ntsecuritycon as con
import sys


# List of all file masks that are interesting
ACCESS_MASKS = ['FILE_READ_DATA', 'FILE_LIST_DIRECTORY', 'FILE_WRITE_DATA', 'FILE_ADD_FILE', 
                     'FILE_APPEND_DATA', 'FILE_ADD_SUBDIRECTORY', 'FILE_CREATE_PIPE_INSTANCE', 'FILE_READ_EA',
                     'FILE_WRITE_EA', 'FILE_EXECUTE', 'FILE_TRAVERSE', 'FILE_DELETE_CHILD', 
                     'FILE_READ_ATTRIBUTES', 'FILE_WRITE_ATTRIBUTES', 'FILE_ALL_ACCESS', 'FILE_GENERIC_READ',
                     'FILE_GENERIC_WRITE', 'FILE_GENERIC_EXECUTE'] 

# List of all inheritance flags
ACE_FLAGS = ['OBJECT_INHERIT_ACE', 'CONTAINER_INHERIT_ACE', 'NO_PROPAGATE_INHERIT_ACE', 'INHERIT_ONLY_ACE']

# List of all ACE types
ACE_TYPES = ['ACCESS_MIN_MS_ACE_TYPE', 'ACCESS_ALLOWED_ACE_TYPE', 'ACCESS_DENIED_ACE_TYPE', 'SYSTEM_AUDIT_ACE_TYPE',
             'SYSTEM_ALARM_ACE_TYPE', 'ACCESS_MAX_MS_V2_ACE_TYPE', 'ACCESS_ALLOWED_COMPOUND_ACE_TYPE',
             'ACCESS_MAX_MS_V3_ACE_TYPE', 'ACCESS_MIN_MS_OBJECT_ACE_TYPE', 'ACCESS_ALLOWED_OBJECT_ACE_TYPE',
             'ACCESS_DENIED_OBJECT_ACE_TYPE', 'SYSTEM_AUDIT_OBJECT_ACE_TYPE', 'SYSTEM_ALARM_OBJECT_ACE_TYPE',
             'ACCESS_MAX_MS_OBJECT_ACE_TYPE', 'ACCESS_MAX_MS_V4_ACE_TYPE', 'ACCESS_MAX_MS_ACE_TYPE',
             'ACCESS_ALLOWED_CALLBACK_ACE_TYPE', 'ACCESS_DENIED_CALLBACK_ACE_TYPE', 'ACCESS_ALLOWED_CALLBACK_OBJECT_ACE_TYPE',
             'ACCESS_DENIED_CALLBACK_OBJECT_ACE_TYPE', 'SYSTEM_AUDIT_CALLBACK_ACE_TYPE', 'SYSTEM_ALARM_CALLBACK_ACE_TYPE',
             'SYSTEM_AUDIT_CALLBACK_OBJECT_ACE_TYPE', 'SYSTEM_ALARM_CALLBACK_OBJECT_ACE_TYPE', 'SYSTEM_MANDATORY_LABEL_ACE_TYPE',
             'ACCESS_MAX_MS_V5_ACE_TYPE']

################################################################################
def get_ace_types_str(ace_type):
    ''' Yields all matching ACE types as strings
    '''
    for t in ACE_TYPES:
        if getattr(con, t) == ace_type:
            yield t

################################################################################
def get_ace_flags_str(ace_flag):
    ''' Yields all matching ACE flags as strings 
    '''
    for t in ACE_FLAGS:
        attr = getattr(con, t)
        if (attr & ace_flag) == attr:
            yield t

################################################################################
def get_access_mask_str(access_mask):
    ''' Yields all matching ACE flags as strings 
    '''
    for t in ACCESS_MASKS:
        attr = getattr(con, t)
        if (attr & access_mask) == attr:
            yield t

################################################################################
def list_file_ace(filename):
    ''' Method for listing of file ACEs
    '''

    # Load data
    sd = win32security.GetFileSecurity(filename, win32security.DACL_SECURITY_INFORMATION)
    dacl = sd.GetSecurityDescriptorDacl()     

    # Print ACE count
    ace_count = dacl.GetAceCount()
    print('File', filename, 'has', ace_count, 'ACEs')

    # Go trough individual ACEs
    for i in range(0, ace_count):
        (ace_type, ace_flag), access_mask, usersid = dacl.GetAce(i)
        user, group, usertype = win32security.LookupAccountSid('', usersid)

        print('\tUser: {}\\{}'.format(group, user))    
        print('\t\tACE Type ({}):'.format(ace_type), '; '.join(get_ace_types_str(ace_type))) 
        print('\t\tACE Flags ({}):'.format(ace_flag), ' | '.join(get_ace_flags_str(ace_flag)))
        print('\t\tAccess Mask ({}):'.format(access_mask), ' | '.join(get_access_mask_str(access_mask)))
        print()


################################################################################
# Execute with some defaults
if __name__ == '__main__':
    for filename in sys.argv[1:]:
        list_file_ace(filename)
        print()

打印出如下字符串:

D:\tmp>acc_list.py D:\tmp D:\tmp\main.bat
File D:\tmp has 8 ACEs
        User: BUILTIN\Administrators
                ACE Type (0): ACCESS_MIN_MS_ACE_TYPE; ACCESS_ALLOWED_ACE_TYPE
                ACE Flags (0):
                Access Mask (2032127): FILE_READ_DATA | FILE_LIST_DIRECTORY | FILE_WRITE_DATA | FILE_ADD_FILE | FILE_APPEND_DATA | FILE_ADD_SUBDIRECTORY | FILE_CREATE_PIPE_INSTANCE | FILE_READ_EA | FILE_WRITE_EA | FILE_EXECUTE | FILE_TRAVERSE | FILE_DELETE_CHILD | FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES | FILE_GENERIC_READ | FILE_GENERIC_WRITE | FILE_GENERIC_EXECUTE

...

答案 1 :(得分:0)

所以,通过喋喋不休并试图了解发生了什么,我找到了与@Vyktor之前发布的非常相似的东西。

我使用this example找到了一些帮助。

所以,我做的第一件事是尝试理解Windows在我用GUI手动更改安全信息时设置的标志,我构建了一组函数来帮助我:

import os

import win32con
import win32security
import win32process
import ntsecuritycon

d = "toto"
f = os.path.join(d, "foo")


def build_flags_map(*attrs, **kw):
    mod = kw.get('mod', win32con)

    r = {}
    for attr in attrs:
        value = getattr(mod, attr)
        r[value] = attr
    return r


ACE_TYPE = build_flags_map('ACCESS_ALLOWED_ACE_TYPE', 'ACCESS_DENIED_ACE_TYPE')

ACCESS_MASK = build_flags_map(
    'GENERIC_WRITE', 'GENERIC_ALL', 'GENERIC_EXECUTE', 'GENERIC_READ',
    'WRITE_OWNER', 'DELETE', 'READ_CONTROL', 'SYNCHRONIZE', 'WRITE_DAC',
    'ACCESS_SYSTEM_SECURITY')

ACCESS_MASK_FILES = build_flags_map(
        'FILE_ADD_FILE', 'FILE_READ_DATA', 'FILE_LIST_DIRECTORY',
        'FILE_WRITE_DATA', 'FILE_ADD_FILE', 'FILE_APPEND_DATA',
        'FILE_ADD_SUBDIRECTORY', 'FILE_CREATE_PIPE_INSTANCE', 'FILE_READ_EA',
        'FILE_WRITE_EA', 'FILE_EXECUTE', 'FILE_TRAVERSE', 'FILE_DELETE_CHILD',
        'FILE_READ_ATTRIBUTES', 'FILE_WRITE_ATTRIBUTES', 'FILE_ALL_ACCESS',
        'FILE_GENERIC_READ', 'FILE_GENERIC_WRITE', 'FILE_GENERIC_EXECUTE',
        mod=ntsecuritycon,
    )

ACE_FLAGS = build_flags_map(
    'CONTAINER_INHERIT_ACE', 'INHERITED_ACE', 'FAILED_ACCESS_ACE_FLAG',
    'INHERIT_ONLY_ACE', 'OBJECT_INHERIT_ACE',
    mod=win32security)


def display_flags(map, value):
    r = []
    for flag, name in map.items():
        if flag & value:
            r.append(name)
            value = value - flag

    if value != 0:
        # We didn't specified all the flags in the mapping :(
        r.append('(flags left 0x%x)' % value)

    return r' | '.join(r)


def show_acls(path):

    process_handler = win32process.GetCurrentProcess()
    thread_handler = win32security.OpenProcessToken(
            process_handler,
            win32security.TOKEN_ALL_ACCESS)
    current_sid = win32security.GetTokenInformation(thread_handler, win32security.TokenUser)[0]

    desc = win32security.GetNamedSecurityInfo(
            path,
            win32security.SE_FILE_OBJECT,
            win32security.DACL_SECURITY_INFORMATION)    

    dacl = desc.GetSecurityDescriptorDacl()

    print("%d ACE on %s" % (dacl.GetAceCount(), path))
    for i in range(0, dacl.GetAceCount()):

        ace = dacl.GetAce(i)
        (ace_type, ace_flags), ace_mask, ace_sid = ace

        if ace_sid == current_sid:
            user = "me"
        else:
            user = str(ace_sid)

        print("  User: %s =>\n"
              "      ACE type: %s\n"
              "      ACE flags: %s\n"
              "      ACE mask: %s\n"
              "      Raw ACE: %r\n" % (
              user,
              ACE_TYPE[ace_type],
              display_flags(ACE_FLAGS, ace_flags),
              display_flags(ACCESS_MASK_FILES, ace_mask),
              ace))

从那里,我得到以下信息:

7 ACE on toto
  User: me =>
      ACE type: ACCESS_DENIED_ACE_TYPE
      ACE flags: CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE
      ACE mask: FILE_ADD_FILE | FILE_CREATE_PIPE_INSTANCE | FILE_WRITE_ATTRIBUTES | FILE_WRITE_EA
      Raw ACE: ((1, 3), 278, <PySID object at 0x00B02148>)

  User: me =>
      ACE type: ACCESS_ALLOWED_ACE_TYPE
      ACE flags: INHERITED_ACE
      ACE mask: FILE_TRAVERSE | FILE_LIST_DIRECTORY | FILE_ADD_FILE | FILE_CREATE_PIPE_INSTANCE | FILE_READ_EA | FILE_DELETE_CHILD | FILE_READ_ATTRIBUTES | FILE_GENERIC_EXECUTE | FILE_WRITE_EA | FILE_ALL_ACCESS | (flags left 0x-12039f)
      Raw ACE: ((0, 16), 2032127, <PySID object at 0x00A6FBF0>)

  User: me =>
      ACE type: ACCESS_ALLOWED_ACE_TYPE
      ACE flags: INHERITED_ACE | CONTAINER_INHERIT_ACE | INHERIT_ONLY_ACE | OBJECT_INHERIT_ACE
      ACE mask: (flags left 0x10000000)
      Raw ACE: ((0, 27), 268435456, <PySID object at 0x00B02148>)

  User: PySID:S-1-5-18 =>
      ACE type: ACCESS_ALLOWED_ACE_TYPE
      ACE flags: INHERITED_ACE
      ACE mask: FILE_TRAVERSE | FILE_LIST_DIRECTORY | FILE_ADD_FILE | FILE_CREATE_PIPE_INSTANCE | FILE_READ_EA | FILE_DELETE_CHILD | FILE_READ_ATTRIBUTES | FILE_GENERIC_EXECUTE | FILE_WRITE_EA | FILE_ALL_ACCESS | (flags left 0x-12039f)
      Raw ACE: ((0, 16), 2032127, <PySID object at 0x00A6FBF0>)

  User: PySID:S-1-5-18 =>
      ACE type: ACCESS_ALLOWED_ACE_TYPE
      ACE flags: INHERITED_ACE | CONTAINER_INHERIT_ACE | INHERIT_ONLY_ACE | OBJECT_INHERIT_ACE
      ACE mask: (flags left 0x10000000)
      Raw ACE: ((0, 27), 268435456, <PySID object at 0x00B02148>)

  User: PySID:S-1-5-32-544 =>
      ACE type: ACCESS_ALLOWED_ACE_TYPE
      ACE flags: INHERITED_ACE
      ACE mask: FILE_TRAVERSE | FILE_LIST_DIRECTORY | FILE_ADD_FILE | FILE_CREATE_PIPE_INSTANCE | FILE_READ_EA | FILE_DELETE_CHILD | FILE_READ_ATTRIBUTES | FILE_GENERIC_EXECUTE | FILE_WRITE_EA | FILE_ALL_ACCESS | (flags left 0x-12039f)
      Raw ACE: ((0, 16), 2032127, <PySID object at 0x00A6FBF0>)

  User: PySID:S-1-5-32-544 =>
      ACE type: ACCESS_ALLOWED_ACE_TYPE
      ACE flags: INHERITED_ACE | CONTAINER_INHERIT_ACE | INHERIT_ONLY_ACE | OBJECT_INHERIT_ACE
      ACE mask: (flags left 0x10000000)
      Raw ACE: ((0, 27), 268435456, <PySID object at 0x00B02148>)

此示例显示了我在系统上的默认ACL +第一个,我自己创建的那个,并且拒绝在目录上写入。

所以,使用之前的例子,我构建了这个:

def pipe_str_flags(map, *flags):
    r = 0
    reverse_map = dict((value, key) for key, value in map.items())
    for flag in flags:
        r = r | reverse_map[flag]
    return r

def forbid_write(path):
    security_info = win32security.DACL_SECURITY_INFORMATION

    process_handler = win32process.GetCurrentProcess()
    thread_handler = win32security.OpenProcessToken(
            process_handler,
            win32security.TOKEN_ALL_ACCESS)

    desc = win32security.GetNamedSecurityInfo(
            path,
            win32security.SE_FILE_OBJECT,
            security_info)    

    current_sid = win32security.GetTokenInformation(thread_handler, win32security.TokenUser)[0]
    dacl = desc.GetSecurityDescriptorDacl()

    mask = pipe_str_flags(ACCESS_MASK_FILES,
            'FILE_ADD_FILE',
            'FILE_CREATE_PIPE_INSTANCE',
            'FILE_WRITE_ATTRIBUTES',
            'FILE_WRITE_EA')

    ace_flags = pipe_str_flags(ACE_FLAGS,
            'CONTAINER_INHERIT_ACE',
            'OBJECT_INHERIT_ACE')

    dacl.AddAccessDeniedAceEx(
            dacl.GetAclRevision(),
            ace_flags,
            mask,
            current_sid)

    win32security.SetNamedSecurityInfo(
        path,
        win32security.SE_FILE_OBJECT,
        security_info,
        None,
        None,
        dacl,
        None)

与@Vyktor解决方案相反,我使用&#34;拒绝&#34; ACE,拒绝写访问(而Vyktor添加了&#34;允许只读&#34; ACE)。

我错过了一个正确的方法来删除这个ACE,所以我可以在这个目录中再次写,但我还没看到。有一件重要的事情是&#34;拒绝&#34; ACE优先于&#34;允许&#34; ACE,所以我尝试了使用dacl.AddAccessAllowedAceEx()的天真方式,其参数与我在dacl.AddAccessDeniedAceEx()上使用的参数完全相同,但后者优先于前者,所以我仍然可以写进目录。