Reputation: 2924
I'm trying to make a folder read-only using WinAPI and then making it read/write again. I try to do this by denying write access (for the read-only part) and granting all access (for the read/write part). The second part works, but for the first part, when I try to open the folder, it tells me I don't have permission to access it and I can't even see what files the folder has in it.
This is the code (from MSDN and other sources):
#include <windows.h>
#include <stdio.h>
#include <accctrl.h>
#include <aclapi.h>
#include <stdio.h>
#pragma comment(lib, "advapi32.lib")
BOOL SetPrivilege(
HANDLE hToken, // access token handle
LPCTSTR lpszPrivilege, // name of privilege to enable/disable
BOOL bEnablePrivilege // to enable or disable privilege
)
{
TOKEN_PRIVILEGES tp;
LUID luid;
if (!LookupPrivilegeValue(
NULL, // lookup privilege on local system
lpszPrivilege, // privilege to lookup
&luid)) // receives LUID of privilege
{
printf("LookupPrivilegeValue error: %u\n", GetLastError());
return FALSE;
}
tp.PrivilegeCount = 1;
tp.Privileges[0].Luid = luid;
if (bEnablePrivilege)
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
else
tp.Privileges[0].Attributes = 0;
// Enable the privilege or disable all privileges.
if (!AdjustTokenPrivileges(
hToken,
FALSE,
&tp,
sizeof(TOKEN_PRIVILEGES),
(PTOKEN_PRIVILEGES)NULL,
(PDWORD)NULL))
{
printf("AdjustTokenPrivileges error: %u\n", GetLastError());
return FALSE;
}
if (GetLastError() == ERROR_NOT_ALL_ASSIGNED)
{
printf("The token does not have the specified privilege. \n");
return FALSE;
}
return TRUE;
}
BOOL TakeOwnership(LPTSTR lpszOwnFile, bool bAllowAccess)
{
BOOL bRetval = FALSE;
HANDLE hToken = NULL;
PSID pSIDAdmin = NULL;
PSID pSIDEveryone = NULL;
PACL pACL = NULL;
SID_IDENTIFIER_AUTHORITY SIDAuthWorld =
SECURITY_WORLD_SID_AUTHORITY;
SID_IDENTIFIER_AUTHORITY SIDAuthNT = SECURITY_NT_AUTHORITY;
const int NUM_ACES = 1;
EXPLICIT_ACCESS ea[NUM_ACES];
DWORD dwRes;
ZeroMemory(&ea, NUM_ACES * sizeof(EXPLICIT_ACCESS));
// Set read access for Everyone.
ea[0].grfAccessPermissions = (bAllowAccess ? GENERIC_ALL : GENERIC_WRITE);
ea[0].grfAccessMode = (bAllowAccess ? GRANT_ACCESS : DENY_ACCESS);
ea[0].grfInheritance = NO_INHERITANCE;
ea[0].Trustee.TrusteeForm = TRUSTEE_IS_NAME;
ea[0].Trustee.TrusteeType = TRUSTEE_IS_WELL_KNOWN_GROUP;
ea[0].Trustee.ptstrName = _T("EVERYONE");// (LPTSTR)pSIDEveryone;
dwRes = SetEntriesInAcl(NUM_ACES,
ea,
NULL,
&pACL);
if (ERROR_SUCCESS != dwRes)
{
printf("Failed SetEntriesInAcl with error %d\n", dwRes);
goto Cleanup;
}
// Try to modify the object's DACL.
dwRes = SetNamedSecurityInfo(
lpszOwnFile, // name of the object
SE_FILE_OBJECT, // type of object
DACL_SECURITY_INFORMATION, // change only the object's DACL
NULL, NULL, // do not change owner or group
pACL, // DACL specified
NULL); // do not change SACL
if (ERROR_SUCCESS == dwRes)
{
printf("Successfully changed DACL\n");
bRetval = TRUE;
// No more processing needed.
goto Cleanup;
}
if (dwRes != ERROR_ACCESS_DENIED)
{
printf("First SetNamedSecurityInfo call failed: %u\n",
dwRes);
goto Cleanup;
}
// If the preceding call failed because access was denied,
// enable the SE_TAKE_OWNERSHIP_NAME privilege, create a SID for
// the Administrators group, take ownership of the object, and
// disable the privilege. Then try again to set the object's DACL.
// Open a handle to the access token for the calling process.
if (!OpenProcessToken(GetCurrentProcess(),
TOKEN_ADJUST_PRIVILEGES,
&hToken))
{
printf("OpenProcessToken failed: %u\n", GetLastError());
goto Cleanup;
}
// Enable the SE_TAKE_OWNERSHIP_NAME privilege.
if (!SetPrivilege(hToken, SE_TAKE_OWNERSHIP_NAME, TRUE))
{
printf("You must be logged on as Administrator.\n");
goto Cleanup;
}
// Disable the SE_TAKE_OWNERSHIP_NAME privilege.
if (!SetPrivilege(hToken, SE_TAKE_OWNERSHIP_NAME, FALSE))
{
printf("Failed SetPrivilege call unexpectedly.\n");
goto Cleanup;
}
// Try again to modify the object's DACL,
// now that we are the owner.
dwRes = SetNamedSecurityInfo(
lpszOwnFile, // name of the object
SE_FILE_OBJECT, // type of object
DACL_SECURITY_INFORMATION, // change only the object's DACL
NULL, NULL, // do not change owner or group
pACL, // DACL specified
NULL); // do not change SACL
if (dwRes == ERROR_SUCCESS)
{
printf("Successfully changed DACL\n");
bRetval = TRUE;
}
else
{
printf("Second SetNamedSecurityInfo call failed: %u\n",
dwRes);
}
Cleanup:
if (pSIDAdmin)
FreeSid(pSIDAdmin);
if (pSIDEveryone)
FreeSid(pSIDEveryone);
if (pACL)
LocalFree(pACL);
if (hToken)
CloseHandle(hToken);
return bRetval;
}
int main(int argc, char* argv[])
{
bool allow = false; //true
TakeOwnership(argv[1], allow);
return 0;
}
Can anyone tell me what I'm doing wrong?
Upvotes: 1
Views: 451
Reputation: 13318
Your problem is you're not actually ever granted yourself read access to the directory. What your code is doing is effectively setting a DACL which looks like:
DENY GENERIC_WRITE to Everyone
Now when you try and list the directory the application tries to open with say GENERIC_READ access. The OS will perform the access check, it looks at the first entry in the DACL, doesn't see a match between the Read request and the Write mask so skips it. The OS has now run out of access control entries, so the only thing it can do is deny you access. To fix this you'll need a second ACE so that it looks like:
DENY GENERIC_WRITE to Everyone
GRANT GENERIC_READ to Everyone
You should be able to do that with something like:
const int NUM_ACES = 2;
EXPLICIT_ACCESS ea[NUM_ACES];
DWORD dwRes;
ZeroMemory(&ea, NUM_ACES * sizeof(EXPLICIT_ACCESS));
DWORD dwMask = FILE_WRITE_DATA | FILE_WRITE_ATTRIBUTES | FILE_WRITE_EA | FILE_APPEND_DATA;
ea[0].grfAccessPermissions = (bAllowAccess ? GENERIC_ALL : dwMask);
ea[0].grfAccessMode = (bAllowAccess ? GRANT_ACCESS : DENY_ACCESS);
ea[0].grfInheritance = NO_INHERITANCE;
ea[0].Trustee.TrusteeForm = TRUSTEE_IS_NAME;
ea[0].Trustee.TrusteeType = TRUSTEE_IS_WELL_KNOWN_GROUP;
ea[0].Trustee.ptstrName = _T("EVERYONE");
ea[1].grfAccessPermissions = GENERIC_READ;
ea[1].grfAccessMode = GRANT_ACCESS;
ea[1].grfInheritance = NO_INHERITANCE;
ea[1].Trustee.TrusteeForm = TRUSTEE_IS_NAME;
ea[1].Trustee.TrusteeType = TRUSTEE_IS_WELL_KNOWN_GROUP;
ea[1].Trustee.ptstrName = _T("EVERYONE");
Upvotes: 1