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:
- Set an empty DACL on a file
- Obtain a handle to the file for
READ_CONTROL
- Obtain security descriptor using
GetSecurityInfo
and the handle - Check that the DACL is actually empty
However, 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 callsNtCreateFile
with theDesiredAccess
parameter passed asdwDesiredAccess | FILE_READ_ATTRIBUTES | SYNCHRONIZE
. Thus if you passdwDesiredAccess
asREAD_CONTROL
, then it actually tries to open the file withREAD_CONTROL | FILE_READ_ATTRIBUTES | SYNCHRONIZE
access.FILE_READ_ATTRIBUTES
access is granted implicitly by the file system if the caller hasFILE_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
orNtCreateFile
in order to control the exact requested access.