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
GetSecurityInfoand 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
CreateFileWinternally callsNtCreateFilewith theDesiredAccessparameter passed asdwDesiredAccess | FILE_READ_ATTRIBUTES | SYNCHRONIZE. Thus if you passdwDesiredAccessasREAD_CONTROL, then it actually tries to open the file withREAD_CONTROL | FILE_READ_ATTRIBUTES | SYNCHRONIZEaccess.FILE_READ_ATTRIBUTESaccess is granted implicitly by the file system if the caller hasFILE_LIST_DIRECTORYaccess for the parent folder. However,SYNCHRONIZEaccess will not be granted if the file has an empty DACL.One solution here would be to use
NtOpenFileorNtCreateFilein order to control the exact requested access.