I'm continuing my quest to find the Holy Grail - or, really, just to write some C code that does some very basic ACL modifications on files. And, I'm continuing to bang my head against this wall with various challenges. The most recent one is that the "Trustee" returned by the GetAce()
function doesn't seem to be a recognizable Trustee, even though I know exactly what it should be.
At a high level, what I'm currently trying to do is remove an Explicit ACE on a specific file that is DENYing access to the well-known Everyone group. Interestingly, if, instead of GetAce()
, I use GetExplicitEntriesFromAcl()
, I am able to get a recognizable SID and it compares correctly to the Everyone SID using EqualSid()
; however, I don't know how to take the information from GetExplicitEntriesFromAcl()
and then use that to remove the ACE from the ACL completely, because I don't get the absolute index of the ACE at that point, and there doesn't seem to be a RemoveExplicitEntriesFromAcl()
-type function.
Current code, using GetAce()
is below, but I'm open to other suggestions on how to reliably locate and remove a DENY ACE for the Everyone group.
BOOL removeAce(LPTSTR filePath) {
PACL existingAcl;
PSECURITY_DESCRIPTOR securityDescriptor;
PSID everyoneSid;
// Get PSID for well-known "Everyone" group
SID_IDENTIFIER_AUTHORITY sidAuthority = SECURITY_WORLD_SID_AUTHORITY;
if (!AllocateAndInitializeSid(&sidAuthority, 1, SECURITY_WORLD_RID, 0, 0, 0, 0, 0, 0, 0, &everyoneSid))
return false;
// Make sure we really have a file path
if (filePath == NULL || wcscmp(filePath, L"") == 0)
return false;
// Retrieve the ACL for the file, or bail out.
if (GetNamedSecurityInfo(filePath, SE_FILE_OBJECT, DACL_SECURITY_INFORMATION, NULL, NULL, &existingAcl, NULL, &securityDescriptor) != ERROR_SUCCESS)
return false;
EXPLICIT_ACCESS* aclEntries;
ULONG numEntries = existingAcl->AceCount;
// Loop through all ACEs
for (DWORD i = 0; i < numEntries; i ) {
// Going to cast as a ACCESS_DENIED_ACE entry.
ACCESS_DENIED_ACE* entry;
// Try to get the ACE at current index, or continue to next one.
if (!GetAce(existingAcl, i, (LPVOID*) &entry))
continue;
// We're only interested in DENY entries.
if (entry->Header.AceType != ACCESS_DENIED_ACE_TYPE)
continue;
// Cast a couple of things to make it easier below
PSID thisSid = (PSID)entry->SidStart;
EXPLICIT_ACCESS* eaEntry = (EXPLICIT_ACCESS*)entry;
/* This is where I start to have problems - I put this code
* in strictly for debug purposes, and it always hits the
* default case, indicating that the TrusteeForm is either
* some unknown type, or I'm doing something insanely
* wrong and pointing to the wrong block of memory
* somewhere.
*/
switch (thisEntry->Trustee.TrusteeForm) {
case TRUSTEE_IS_SID:
OutputDebugString(L"We have a SID.\n");
break;
case TRUSTEE_IS_NAME:
OutputDebugString(L"We have a name.\n");
break;
case TRUSTEE_IS_OBJECTS_AND_SID:
OutputDebugString(L"We have objects and a SID.\n");
break;
case TRUSTEE_IS_OBJECTS_AND_NAME:
OutputDebugString(L"We have objects and a name.\n");
break;
default:
OutputDebugString(L"We have an alien Trustee type.\n");
}
/* This is the ultimate goal - being able to match the Trustee
* of the current ACE being evaluated with the Everyone SID, but
* this if statement never evaluates to True, and, so far, it
* seems to be because the TrusteeForm isn't recognized as a SID
* - or anything else for that matter.
*/
if (eaEntry->Trustee.TrusteeForm == TRUSTEE_IS_SID
&& EqualSid((PSID)entry->SidStart, everyoneSid))
OutputDebugString(L"This is our EVERYONE entry.\n");
}
return true;
}
CodePudding user response:
SidStart
is not a pointer to a SID; it is the first 4 bytes of a SID. You need to access it like so:
PSID thisSid = (PSID)&(entry->SidStart);