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?

Eryk Sun
  • 33,190
  • 5
  • 92
  • 111
Martin Bammer
  • 537
  • 7
  • 19
  • You have to enable SeTakeOwnershipPrivilege first. (No idea exactly how you do that in Python, but I'm sure it's possible.) – Harry Johnston Dec 11 '16 at 00:42
  • @Harry, use the functions from [win32security](http://docs.activestate.com/activepython/2.7/pywin32/win32security.html) 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. – Eryk Sun Dec 11 '16 at 02:25

2 Answers2

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
Eryk Sun
  • 33,190
  • 5
  • 92
  • 111
  • This is really what I was looking for. Made a slight improvement in my own answer, but nevertheless, I'll upvote yours ;) Thanks ! – Orsiris de Jong Apr 03 '20 at 10:11
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://stackoverflow.com/a/61041460/2635443

Orsiris de Jong
  • 2,819
  • 1
  • 26
  • 48