Taking directory ownership on Windows with Python results in "Access denied" error
Asked Answered
S

2

2

I'm trying to take ownership of a directory with the following code:

sd = win32security.SECURITY_DESCRIPTOR()
sd.SetSecurityDescriptorOwner(curUser, False)
win32security.SetFileSecurity("C:/ProgramData/Test", 
    win32security.OWNER_SECURITY_INFORMATION, sd)

The SetFileSecurity call fails with an "Access denied" error.

The access rights for the current user have been removed from this directory. In Explorer I can see it, but when I try to open it, I first have to take ownership as an administrator. That works in Explorer, but the above code is executed with elevated permissions, and for some reason it still fails. Any suggestions?

Stace answered 10/12, 2016 at 23:6 Comment(2)
You have to enable SeTakeOwnershipPrivilege first. (No idea exactly how you do that in Python, but I'm sure it's possible.)Impetuosity
@Harry, use the functions from win32security module (e.g. OpenProcessToken, LookupPrivilegeValue, AdjustTokenPrivileges), which are basically like what you'd do in C, except using Python lists and tuples instead of C arrays and structs.Apps
A
2

You can forcefully take ownership by enabling SeTakeOwnerShipPrivilege, but of course only if your access token has this privilege (e.g. an elevated administrator). Also, you can forcefully assign ownership to another security principal, such as SYSTEM, by enabling SeRestorePrivilege. Without the latter, the assignment may fail with the error code ERROR_INVALID_OWNER.

The following set_file_owner function has a force option that attempts to temporarily enable both of these privileges.

import win32api
import win32security

def set_file_owner(path, sid=None, force=False):
    try:
        hToken = win32security.OpenThreadToken(win32api.GetCurrentThread(),
                    win32security.TOKEN_ALL_ACCESS, True)
    except win32security.error:
        hToken = win32security.OpenProcessToken(win32api.GetCurrentProcess(),
                    win32security.TOKEN_ALL_ACCESS)
    if sid is None:
        sid = win32security.GetTokenInformation(hToken,
                win32security.TokenOwner)
    prev_state = ()
    if force:
        new_state = [(win32security.LookupPrivilegeValue(None, name),
                      win32security.SE_PRIVILEGE_ENABLED)
                        for name in (win32security.SE_TAKE_OWNERSHIP_NAME,
                                     win32security.SE_RESTORE_NAME)]
        prev_state = win32security.AdjustTokenPrivileges(hToken, False,
                        new_state)
    try:
        sd = win32security.SECURITY_DESCRIPTOR()
        sd.SetSecurityDescriptorOwner(sid, False)
        win32security.SetFileSecurity(path, 
            win32security.OWNER_SECURITY_INFORMATION, sd)
    finally:
        if prev_state:
            win32security.AdjustTokenPrivileges(hToken, False, prev_state)

def get_file_owner(path):
    sd = win32security.GetFileSecurity(path,
            win32security.OWNER_SECURITY_INFORMATION)
    sid = sd.GetSecurityDescriptorOwner()
    return win32security.LookupAccountSid(None, sid)

Example

if __name__ == '__main__':
    import os
    import tempfile
    import subprocess

    username = os.environ['UserName']
    test_path = tempfile.mkdtemp()
    print('Test path: {}'.format(test_path))
    subprocess.call(['icacls.exe', test_path, '/setowner', 'SYSTEM'],
                     creationflags=8)
    owner = get_file_owner(test_path)
    print('Initial owner: {}\\{}'.format(owner[1], owner[0]))
    try:
        print('Denying write owner access')
        subprocess.call(['icacls.exe', test_path, '/deny',
                         '{}:(WO)'.format(username)], creationflags=8)
        try:
            set_file_owner(test_path)
        except win32security.error:
            print('Trying with force=True')
            try:
                set_file_owner(test_path, force=True)
            except win32security.error:
                pass
    finally:
        owner = get_file_owner(test_path)
        print('Final owner: {}\\{}'.format(owner[1], owner[0]))
        os.rmdir(test_path)

Output

Test path: C:\Users\eryksun\AppData\Local\Temp\tmpizgemrdz
Initial owner: NT AUTHORITY\SYSTEM
Denying write owner access
Trying with force=True
Final owner: BUILTIN\Administrators
Apps answered 11/12, 2016 at 16:14 Comment(1)
This is really what I was looking for. Made a slight improvement in my own answer, but nevertheless, I'll upvote yours ;) Thanks !Linette
L
0

I've based on Eryk's work to make a full recursive ACL and onwership handling routing for NTFS files under Windows here: https://mcmap.net/q/441263/-how-to-set-folder-permissions-in-windows

Linette answered 3/4, 2020 at 10:10 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.