On Windows, even if discretionary ACL (DACL) is empty, i.e. no one has permission to the file, file owner can read and write DACL (READ_CONTROL
and WRITE_DAC
access).
So I tried to do the following:
READ_CONTROL
GetSecurityInfo
and the handleHowever, obtaining the handle using CreateFileW
failed with Access is denied
error. Surprisingly, GetFileSecurity
, which is an equivalent of GetSecurityInfo
for files, worked fine.
According to the documentation, GetFileSecurity
requires READ_CONTROL
access.
Why does CreateFileW
fail in the following example?
import sys
import win32security
import win32con
import win32file
import ntsecuritycon
import os
path = sys.argv[1]
with open(path, "w"):
pass # I am the owner of the file
print("Set empty ACL")
sd = win32security.GetFileSecurity(path, win32security.DACL_SECURITY_INFORMATION)
dacl = win32security.ACL()
sd.SetSecurityDescriptorDacl(1, dacl, 0)
win32security.SetFileSecurity(path, win32security.DACL_SECURITY_INFORMATION, sd)
try:
print("Ensure that ACL is empty with GetFileSecurity")
sd = win32security.GetFileSecurity(path, win32security.DACL_SECURITY_INFORMATION)
dacl = sd.GetSecurityDescriptorDacl()
assert 0 == dacl.GetAceCount()
print("Try to ensure that ACL is empty using handle")
handle = win32file.CreateFileW(
path,
ntsecuritycon.READ_CONTROL,
0,
None, # security attributes
win32con.OPEN_EXISTING,
0,
None,
)
sd = win32security.GetSecurityInfo(handle, win32security.SE_FILE_OBJECT, win32security.DACL_SECURITY_INFORMATION)
dacl = sd.GetSecurityDescriptorDacl()
assert 0 == dacl.GetAceCount()
except Exception as e:
print("FAILURE:", e)
finally:
print("Restore inherited ACEs before removing file")
dacl = win32security.ACL()
win32security.SetNamedSecurityInfo(
path,
win32security.SE_FILE_OBJECT,
win32security.DACL_SECURITY_INFORMATION,
None,
None,
dacl,
None
)
os.unlink(path)
Output:
> python acl-test.py file
Set empty ACL
Ensure that ACL is empty with GetFileSecurity
Try to ensure that ACL is empty using handle
FAILURE: (5, 'CreateFileW', 'Access is denied.')
Restore inherited ACEs before removing file
CreateFileW
internally calls NtCreateFile
with the DesiredAccess
parameter passed as dwDesiredAccess | FILE_READ_ATTRIBUTES | SYNCHRONIZE
. Thus if you pass dwDesiredAccess
as READ_CONTROL
, then it actually tries to open the file with READ_CONTROL | FILE_READ_ATTRIBUTES | SYNCHRONIZE
access. FILE_READ_ATTRIBUTES
access is granted implicitly by the file system if the caller has FILE_LIST_DIRECTORY
access for the parent folder. However, SYNCHRONIZE
access will not be granted if the file has an empty DACL.
One solution here would be to use NtOpenFile
or NtCreateFile
in order to control the exact requested access.