MSDN defines FILE_READ_ATTRIBUTES
as "The right to read file attributes."
MSDN defines FILE_READ_EA
as "The right to read extended file attributes."
As far as I understand, if these rights are missing, we lose access to file attributes. However, if I take these rights away from a file, GetFileAttributes
continues to return file attributes.
So what does the absence of FILE_READ_ATTRIBUTES
and FILE_READ_EA
affect?
To run my code, set the variable trusteeSID
to the correct value and the variable objectName
to the correct path to the file
#include <aclapi.h>
#include <windows.h>
#include <sddl.h>
#include <wil/resource.h>
#include <securitybaseapi.h>
#include <map>
#include <iostream>
DWORD GetSecurityDescriptor(HANDLE handle, std::unique_ptr<BYTE[]>& pSD)
{
DWORD dwSDLen;
std::unique_ptr<BYTE[]> buffer;
if (GetKernelObjectSecurity(handle, DACL_SECURITY_INFORMATION, NULL, 0, &dwSDLen) == 0)
{
DWORD err = GetLastError();
if (err == ERROR_INSUFFICIENT_BUFFER)
{
buffer.reset(new(std::nothrow) BYTE[dwSDLen]);
if (!buffer)
{
return ERROR_NOT_ENOUGH_MEMORY;
}
if (GetKernelObjectSecurity(handle, DACL_SECURITY_INFORMATION, buffer.get(), dwSDLen, &dwSDLen) == 0)
{
return GetLastError();
}
pSD = std::move(buffer);
}
else
{
return err;
}
}
return ERROR_SUCCESS;
}
DWORD ToAbsoluteSD(const std::unique_ptr<BYTE[]>& selfRelativeSD, std::unique_ptr<BYTE[]>& absoluteSD)
{
DWORD absSDSize = 0;
DWORD dwDaclSize = 0;
DWORD dwSaclSize = 0;
DWORD dwOwnerSize = 0;
DWORD dwPrimaryGroupSize = 0;
DWORD result = MakeAbsoluteSD(selfRelativeSD.get(), NULL, &absSDSize,
NULL, &dwDaclSize,
NULL, &dwSaclSize,
NULL, &dwOwnerSize,
NULL, &dwPrimaryGroupSize);
if (result != 0)
{
return ERROR_INSUFFICIENT_BUFFER;
}
DWORD err = GetLastError();
if (err == ERROR_INSUFFICIENT_BUFFER)
{
std::unique_ptr<BYTE[]> absSD(new(std::nothrow) BYTE[absSDSize]);
std::unique_ptr<BYTE[]> dacl(new(std::nothrow) BYTE[dwDaclSize]);
std::unique_ptr<BYTE[]> sacl(new(std::nothrow) BYTE[dwSaclSize]);
std::unique_ptr<BYTE[]> owner(new(std::nothrow) BYTE[dwOwnerSize]);
std::unique_ptr<BYTE[]> primaryGroup(new(std::nothrow) BYTE[dwPrimaryGroupSize]);
if (!absSD || !dacl || !sacl || !owner || !primaryGroup)
{
return ERROR_NOT_ENOUGH_MEMORY;
}
result = MakeAbsoluteSD(selfRelativeSD.get(), absSD.get(), &absSDSize,
(PACL)dacl.get(), &dwDaclSize,
(PACL)sacl.get(), &dwSaclSize,
(PSID)owner.get(), &dwOwnerSize,
(PSID)primaryGroup.get(), &dwPrimaryGroupSize);
if (result == 0)
{
return GetLastError();
}
absoluteSD = std::move(absSD);
}
else
{
return err;
}
return ERROR_SUCCESS;
}
DWORD SetPermissions(const std::wstring& fileName, PSID pSID, bool allowed, ACCESS_MASK accessRights, bool set)
{
if (fileName.size() == 0)
{
return ERROR_INVALID_PARAMETER;
}
if (!pSID)
{
return ERROR_INVALID_PARAMETER;
}
wil::unique_handle hFile(CreateFile(fileName.c_str(), READ_CONTROL | WRITE_DAC, 0, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL));
if (!hFile)
{
return GetLastError();
}
std::unique_ptr<BYTE[]> pSD;
DWORD result = GetSecurityDescriptor(hFile.get(), pSD);
if (result != ERROR_SUCCESS)
{
return result;
}
PACL dacl = nullptr;
BOOL present;
BOOL defaulted;
if (GetSecurityDescriptorDacl(pSD.get(), &present, &dacl, &defaulted) == 0)
{
return GetLastError();
}
if (!present)
{
return ERROR_NOT_SUPPORTED;
}
bool aceType = allowed ? ACCESS_ALLOWED_ACE_TYPE : ACCESS_DENIED_ACE_TYPE;
// if DACL present and not NULL
if (dacl)
{
for (WORD i = 0; i < dacl->AceCount; i++)
{
PACE_HEADER header;
if (GetAce(dacl, i, (PVOID*)&header) != 0)
{
if (header->AceType == aceType)
{
auto ace = (ACCESS_ALLOWED_ACE*)header; // have the same binary layout as ACCESS_DENIED_ACE
if (EqualSid((PSID)&ace->SidStart, pSID) != 0)
{
set ? ace->Mask |= accessRights : ace->Mask &= ~accessRights;
if (SetKernelObjectSecurity(hFile.get(), DACL_SECURITY_INFORMATION, pSD.get()) == 0)
{
return GetLastError();
}
return ERROR_SUCCESS;
}
}
}
}
}
EXPLICIT_ACCESS ea;
PACL pNewDACL = NULL;
// Initialize an EXPLICIT_ACCESS structure for the new ACE.
ZeroMemory(&ea, sizeof(EXPLICIT_ACCESS));
ea.grfAccessPermissions = accessRights;
ea.grfAccessMode = allowed ? SET_ACCESS : DENY_ACCESS;
ea.grfInheritance = NO_INHERITANCE;
ea.Trustee.TrusteeForm = TRUSTEE_IS_SID;
ea.Trustee.ptstrName = (PWSTR)pSID;
result = SetEntriesInAcl(1, &ea, dacl, &pNewDACL);
if (result != ERROR_SUCCESS) {
return result;
}
std::unique_ptr<BYTE[]> absoluteSD;
result = ToAbsoluteSD(pSD, absoluteSD);
if (result != ERROR_SUCCESS)
{
return result;
}
if (SetSecurityDescriptorDacl(absoluteSD.get(), TRUE, pNewDACL, FALSE) == 0)
{
return GetLastError();
}
if (SetKernelObjectSecurity(hFile.get(), DACL_SECURITY_INFORMATION, absoluteSD.get()) == 0)
{
return GetLastError();
}
return ERROR_SUCCESS;
}
int main()
{
wchar_t trusteeSID[] = L"S-1-5-21-#-#-#-#";
PSID pSid;
if (!ConvertStringSidToSid(trusteeSID, &pSid)) {
std::cout << "ConvertStringSidToSid failed\n";
return 0;
}
wchar_t objectName[] = L"C:\\Users\\Username\\Desktop\\acl.txt";
// deny FILE_READ_ATTRIBUTES and FILE_READ_EA
std::cout << "Res: " << SetPermissions(objectName, pSid, false, FILE_READ_ATTRIBUTES | FILE_READ_EA, true) << std::endl;
// check file attributes
std::cout << "Is valid attributes: " << (GetFileAttributes(objectName) != INVALID_FILE_ATTRIBUTES) << std::endl;
LocalFree(pSid);
}