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

2024-10-02 18:21:27 发布

您现在位置:Python中文网/ 问答频道 /正文

我想能够授权或拒绝写访问一个特定的目录在WindowsXP和更多。在

我尝试了以下方法,但都不起作用:

  • os.chmod():只能指定文件只读属性,请参见Python's doc
  • win32api.SetFileAttribute()FILE_ATTRIBUTE_READONLY:只读的文件。[…]此属性在目录上不适用,请参见MSDN's SetFileAttribute

看起来我唯一的选择是访问和更新目录的“Security info”,我已经尝试了几个小时来完成这项工作,但没有取得太大成功(我真的不熟悉Win32 API)。在

有什么办法吗?在


Tags: 文件方法目录doc属性osattributefile
2条回答

所以,我四处打探,试图了解到底发生了什么,我设法找到了与@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))

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

^{pr2}$

这个例子显示了我系统上的默认ACL+第一个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解决方案相反,我使用了一个“拒绝”ACE,拒绝写访问(而Vyktor添加了一个“允许只读”ACE)。在

我会错过一个合适的方法来删除这个ACE,这样我就可以在这个目录中再写一次了,但是我还没有真正去看。有一点很重要,“Denied”ACE的优先级高于“Allowed”ACE,因此我尝试了一种简单的方法,即使用与在dacl.AddAccessDeniedAceEx()完全相同的参数使用{},但后者的优先级高于前一个,因此我仍然无法写入目录。在

这是一件很有挑战性的事情。我从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的数量。在

^{}函数将^{} header作为tuple返回:

  • ^{}-两个整数AceTypeAceFlags-反复试验表明,AceFlags设置为11意味着继承特权,3不是继承的
  • ^{}-详细list here^{}
  • SID

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

^{pr2}$

然后,您可以通过调用^{}[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)

我从脚本前半部分的列表中获取了编号32032127和{}(在运行脚本之前,我在Explorer->;右键单击->;Properties->;Security->;Advanced中设置了权限):

{1美元^

只是借用了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——我在^{}中没有找到的常数)
  • 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

...

相关问题 更多 >