C
clintp
I've got a function that I'm using to create an inheritable ACE. I
want to grant permission on a directory, and have it be inherited by
its children.
The function was largely stolen from MSDN, and it almost works. If I
go into Explorer and pull up the directory, everyhing looks fine -- all
of the correct switches are set, etc... If I look at the children --
no permissions are inherited.
Doing it manually using Explorer's Security tab works fine.
And, in fact, if I take the "broken" ACL on the directory and modify it
in any way using the Security tab and apply the changes... voila, it
gets inherited by its children.
The function follows.
PS: in the if(){} block below, I'm sure that AddAccessAllowedAceEx is
being called. I've dumped the ACE using another tool and the flag bits
look the same in the header whether this function adjusts the
permissions, or the Security tab does it. I'm completely lost.
BOOL AddAccessRights(TCHAR *lpszFileName, TCHAR *lpszAccountName, DWORD
dwAccessMask)
{
// SID variables.
LPVOID pUserSID = NULL;
// File SD variables.
PSECURITY_DESCRIPTOR pFileSD = NULL;
DWORD cbFileSD = 0;
// New SD variables.
SECURITY_DESCRIPTOR newSD;
// ACL variables.
PACL pACL = NULL;
BOOL fDaclPresent;
BOOL fDaclDefaulted;
ACL_SIZE_INFORMATION AclInfo;
// New ACL variables.
PACL pNewACL = NULL;
DWORD cbNewACL = 0;
// Temporary ACE.
LPVOID pTempAce = NULL;
UINT CurrentAceIndex = 0;
UINT newAceIndex = 0;
// Assume function will fail.
BOOL fResult = FALSE;
SetSecurityDescriptorControlFnPtr _SetSecurityDescriptorControl;
AddAccessAllowedAceExFnPtr _AddAccessAllowedAceEx;
SECURITY_INFORMATION secInfo = DACL_SECURITY_INFORMATION;
//
// STEP 1: Get SID of the account name specified.
//
if (! (pUserSID = GetUserSid(lpszAccountName)))
{
fResult = 1;
goto EXITFUNC;
}
//
// STEP 2: Get security descriptor (SD) of the file specified.
//
if (! GetFileSecurity(lpszFileName, secInfo, pFileSD, 0, &cbFileSD))
{
if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
{
err_msg();
fResult = 2;
goto EXITFUNC;
}
}
if (! (pFileSD = malloc(cbFileSD)))
{
fResult = -1;
goto EXITFUNC;
}
if (! GetFileSecurity(lpszFileName, secInfo, pFileSD, cbFileSD,
&cbFileSD))
{
err_msg();
fResult = 2;
goto EXITFUNC;
}
//
// STEP 3: Initialize new SD.
//
if (!InitializeSecurityDescriptor(&newSD,
SECURITY_DESCRIPTOR_REVISION))
{
err_msg();
fResult = 3;
goto EXITFUNC;
}
//
// STEP 4: Get DACL from the old SD.
//
if (!GetSecurityDescriptorDacl(pFileSD, &fDaclPresent, &pACL,
&fDaclDefaulted))
{
err_msg();
fResult = 4;
goto EXITFUNC;
}
//
// STEP 5: Get size information for DACL.
//
AclInfo.AceCount = 0; // Assume NULL DACL.
AclInfo.AclBytesFree = 0;
AclInfo.AclBytesInUse = sizeof(ACL);
if (pACL == NULL)
fDaclPresent = FALSE;
// If not NULL DACL, gather size information from DACL.
if (fDaclPresent)
{
if (!GetAclInformation(pACL, &AclInfo, sizeof(ACL_SIZE_INFORMATION),
AclSizeInformation))
{
err_msg();
fResult = 5;
goto EXITFUNC;
}
}
//
// STEP 6: Compute size needed for the new ACL.
//
cbNewACL = AclInfo.AclBytesInUse + sizeof(ACCESS_ALLOWED_ACE) +
GetLengthSid(pUserSID) - sizeof(DWORD);
//
// STEP 7: Allocate memory for new ACL.
//
if (! (pNewACL = (PACL) malloc(cbNewACL)))
{
fResult = -1;
goto EXITFUNC;
}
//
// STEP 8: Initialize the new ACL.
//
if (!InitializeAcl(pNewACL, cbNewACL, ACL_REVISION2))
{
err_msg();
fResult = 6;
goto EXITFUNC;
}
//
// STEP 9 If DACL is present, copy all the ACEs from the old DACL
// to the new DACL.
//
// The following code assumes that the old DACL is
// already in Windows 2000 preferred order. To conform
// to the new Windows 2000 preferred order, first we will
// copy all non-inherited ACEs from the old DACL to the
// new DACL, irrespective of the ACE type.
//
newAceIndex = 0;
if (fDaclPresent && AclInfo.AceCount)
{
for (CurrentAceIndex = 0; CurrentAceIndex < AclInfo.AceCount;
CurrentAceIndex++)
{
//
// STEP 10: Get an ACE.
//
if (!GetAce(pACL, CurrentAceIndex, &pTempAce))
{
err_msg();
fResult = 7;
goto EXITFUNC;
}
//
// STEP 11: Check if it is a non-inherited ACE.
// If it is an inherited ACE, break from the loop so
// that the new access allowed non-inherited ACE can
// be added in the correct position, immediately after
// all non-inherited ACEs.
//
if (((ACCESS_ALLOWED_ACE *)pTempAce)->Header.AceFlags &
INHERITED_ACE)
break;
//
// STEP 12: Skip adding the ACE, if the SID matches
// with the account specified, as we are going to
// add an access allowed ACE with a different access
// mask.
//
if (EqualSid(pUserSID, &(((ACCESS_ALLOWED_ACE
*)pTempAce)->SidStart)))
continue;
//
// STEP 13: Add the ACE to the new ACL.
//
if (!AddAce(pNewACL, ACL_REVISION, MAXDWORD, pTempAce,
((PACE_HEADER) pTempAce)->AceSize))
{
err_msg();
fResult = 7;
goto EXITFUNC;
}
newAceIndex++;
}
}
//
// STEP 14: Add the access-allowed ACE to the new DACL.
// The new ACE added here will be in the correct position,
// immediately after all existing non-inherited ACEs.
//
_AddAccessAllowedAceEx =
(AddAccessAllowedAceExFnPtr)GetProcAddress(GetModuleHandle(TEXT("advapi32.dll")),"AddAccessAllowedAceEx");
if (_AddAccessAllowedAceEx)
{
fprintf(stderr, "new API\n");
if (!_AddAccessAllowedAceEx(pNewACL, ACL_REVISION2,
CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE, dwAccessMask, pUserSID))
{
err_msg();
fResult = 8;
goto EXITFUNC;
}
}
else
{
if (!AddAccessAllowedAce(pNewACL, ACL_REVISION2, dwAccessMask,
pUserSID))
{
err_msg();
fResult = 8;
goto EXITFUNC;
}
}
//
// STEP 15: To conform to the new Windows 2000 preferred order,
// we will now copy the rest of inherited ACEs from the
// old DACL to the new DACL.
//
if (fDaclPresent && AclInfo.AceCount)
{
for (; CurrentAceIndex < AclInfo.AceCount; CurrentAceIndex++)
{
//
// STEP 16: Get an ACE.
//
if (!GetAce(pACL, CurrentAceIndex, &pTempAce))
{
err_msg();
fResult = 8;
goto EXITFUNC;
}
//
// STEP 17: Add the ACE to the new ACL.
//
if (!AddAce(pNewACL, ACL_REVISION, MAXDWORD, pTempAce,
((PACE_HEADER) pTempAce)->AceSize))
{
err_msg();
fResult = 9;
goto EXITFUNC;
}
}
}
//
// STEP 18: Set the new DACL to the new SD.
//
if (!SetSecurityDescriptorDacl(&newSD, TRUE, pNewACL, FALSE))
{
err_msg();
fResult = 10;
goto EXITFUNC;
}
//
// STEP 19: Copy the old security descriptor control flags
// regarding DACL automatic inheritance for Windows 2000 or
// later where SetSecurityDescriptorControl() API is available
// in advapi32.dll.
//
_SetSecurityDescriptorControl =
(SetSecurityDescriptorControlFnPtr)GetProcAddress(GetModuleHandle(TEXT("advapi32.dll")),"SetSecurityDescriptorControl");
if (_SetSecurityDescriptorControl)
{
SECURITY_DESCRIPTOR_CONTROL controlBitsOfInterest = 0;
SECURITY_DESCRIPTOR_CONTROL controlBitsToSet = 0;
SECURITY_DESCRIPTOR_CONTROL oldControlBits = 0;
DWORD dwRevision = 0;
if (!GetSecurityDescriptorControl(pFileSD, &oldControlBits,
&dwRevision))
{
err_msg();
fResult = 10;
goto EXITFUNC;
}
if (oldControlBits & SE_DACL_AUTO_INHERITED)
{
controlBitsOfInterest = SE_DACL_AUTO_INHERIT_REQ |
SE_DACL_AUTO_INHERITED;
controlBitsToSet = controlBitsOfInterest;
}
else if (oldControlBits & SE_DACL_PROTECTED)
{
controlBitsOfInterest = SE_DACL_PROTECTED;
controlBitsToSet = controlBitsOfInterest;
}
if (controlBitsOfInterest)
{
if (!_SetSecurityDescriptorControl(&newSD, controlBitsOfInterest,
controlBitsToSet))
{
err_msg();
fResult = 11;
goto EXITFUNC;
}
}
}
//
// STEP 20: Set the new SD to the File.
//
if (!SetFileSecurity(lpszFileName, secInfo, &newSD))
{
err_msg();
fResult = 12;
goto EXITFUNC;
}
fResult = 0;
EXITFUNC:
//
// STEP 21: Free allocated memory
//
if (pFileSD) free(pFileSD);
if (pNewACL) free(pNewACL);
return fResult;
}
want to grant permission on a directory, and have it be inherited by
its children.
The function was largely stolen from MSDN, and it almost works. If I
go into Explorer and pull up the directory, everyhing looks fine -- all
of the correct switches are set, etc... If I look at the children --
no permissions are inherited.
Doing it manually using Explorer's Security tab works fine.
And, in fact, if I take the "broken" ACL on the directory and modify it
in any way using the Security tab and apply the changes... voila, it
gets inherited by its children.
The function follows.
PS: in the if(){} block below, I'm sure that AddAccessAllowedAceEx is
being called. I've dumped the ACE using another tool and the flag bits
look the same in the header whether this function adjusts the
permissions, or the Security tab does it. I'm completely lost.
BOOL AddAccessRights(TCHAR *lpszFileName, TCHAR *lpszAccountName, DWORD
dwAccessMask)
{
// SID variables.
LPVOID pUserSID = NULL;
// File SD variables.
PSECURITY_DESCRIPTOR pFileSD = NULL;
DWORD cbFileSD = 0;
// New SD variables.
SECURITY_DESCRIPTOR newSD;
// ACL variables.
PACL pACL = NULL;
BOOL fDaclPresent;
BOOL fDaclDefaulted;
ACL_SIZE_INFORMATION AclInfo;
// New ACL variables.
PACL pNewACL = NULL;
DWORD cbNewACL = 0;
// Temporary ACE.
LPVOID pTempAce = NULL;
UINT CurrentAceIndex = 0;
UINT newAceIndex = 0;
// Assume function will fail.
BOOL fResult = FALSE;
SetSecurityDescriptorControlFnPtr _SetSecurityDescriptorControl;
AddAccessAllowedAceExFnPtr _AddAccessAllowedAceEx;
SECURITY_INFORMATION secInfo = DACL_SECURITY_INFORMATION;
//
// STEP 1: Get SID of the account name specified.
//
if (! (pUserSID = GetUserSid(lpszAccountName)))
{
fResult = 1;
goto EXITFUNC;
}
//
// STEP 2: Get security descriptor (SD) of the file specified.
//
if (! GetFileSecurity(lpszFileName, secInfo, pFileSD, 0, &cbFileSD))
{
if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
{
err_msg();
fResult = 2;
goto EXITFUNC;
}
}
if (! (pFileSD = malloc(cbFileSD)))
{
fResult = -1;
goto EXITFUNC;
}
if (! GetFileSecurity(lpszFileName, secInfo, pFileSD, cbFileSD,
&cbFileSD))
{
err_msg();
fResult = 2;
goto EXITFUNC;
}
//
// STEP 3: Initialize new SD.
//
if (!InitializeSecurityDescriptor(&newSD,
SECURITY_DESCRIPTOR_REVISION))
{
err_msg();
fResult = 3;
goto EXITFUNC;
}
//
// STEP 4: Get DACL from the old SD.
//
if (!GetSecurityDescriptorDacl(pFileSD, &fDaclPresent, &pACL,
&fDaclDefaulted))
{
err_msg();
fResult = 4;
goto EXITFUNC;
}
//
// STEP 5: Get size information for DACL.
//
AclInfo.AceCount = 0; // Assume NULL DACL.
AclInfo.AclBytesFree = 0;
AclInfo.AclBytesInUse = sizeof(ACL);
if (pACL == NULL)
fDaclPresent = FALSE;
// If not NULL DACL, gather size information from DACL.
if (fDaclPresent)
{
if (!GetAclInformation(pACL, &AclInfo, sizeof(ACL_SIZE_INFORMATION),
AclSizeInformation))
{
err_msg();
fResult = 5;
goto EXITFUNC;
}
}
//
// STEP 6: Compute size needed for the new ACL.
//
cbNewACL = AclInfo.AclBytesInUse + sizeof(ACCESS_ALLOWED_ACE) +
GetLengthSid(pUserSID) - sizeof(DWORD);
//
// STEP 7: Allocate memory for new ACL.
//
if (! (pNewACL = (PACL) malloc(cbNewACL)))
{
fResult = -1;
goto EXITFUNC;
}
//
// STEP 8: Initialize the new ACL.
//
if (!InitializeAcl(pNewACL, cbNewACL, ACL_REVISION2))
{
err_msg();
fResult = 6;
goto EXITFUNC;
}
//
// STEP 9 If DACL is present, copy all the ACEs from the old DACL
// to the new DACL.
//
// The following code assumes that the old DACL is
// already in Windows 2000 preferred order. To conform
// to the new Windows 2000 preferred order, first we will
// copy all non-inherited ACEs from the old DACL to the
// new DACL, irrespective of the ACE type.
//
newAceIndex = 0;
if (fDaclPresent && AclInfo.AceCount)
{
for (CurrentAceIndex = 0; CurrentAceIndex < AclInfo.AceCount;
CurrentAceIndex++)
{
//
// STEP 10: Get an ACE.
//
if (!GetAce(pACL, CurrentAceIndex, &pTempAce))
{
err_msg();
fResult = 7;
goto EXITFUNC;
}
//
// STEP 11: Check if it is a non-inherited ACE.
// If it is an inherited ACE, break from the loop so
// that the new access allowed non-inherited ACE can
// be added in the correct position, immediately after
// all non-inherited ACEs.
//
if (((ACCESS_ALLOWED_ACE *)pTempAce)->Header.AceFlags &
INHERITED_ACE)
break;
//
// STEP 12: Skip adding the ACE, if the SID matches
// with the account specified, as we are going to
// add an access allowed ACE with a different access
// mask.
//
if (EqualSid(pUserSID, &(((ACCESS_ALLOWED_ACE
*)pTempAce)->SidStart)))
continue;
//
// STEP 13: Add the ACE to the new ACL.
//
if (!AddAce(pNewACL, ACL_REVISION, MAXDWORD, pTempAce,
((PACE_HEADER) pTempAce)->AceSize))
{
err_msg();
fResult = 7;
goto EXITFUNC;
}
newAceIndex++;
}
}
//
// STEP 14: Add the access-allowed ACE to the new DACL.
// The new ACE added here will be in the correct position,
// immediately after all existing non-inherited ACEs.
//
_AddAccessAllowedAceEx =
(AddAccessAllowedAceExFnPtr)GetProcAddress(GetModuleHandle(TEXT("advapi32.dll")),"AddAccessAllowedAceEx");
if (_AddAccessAllowedAceEx)
{
fprintf(stderr, "new API\n");
if (!_AddAccessAllowedAceEx(pNewACL, ACL_REVISION2,
CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE, dwAccessMask, pUserSID))
{
err_msg();
fResult = 8;
goto EXITFUNC;
}
}
else
{
if (!AddAccessAllowedAce(pNewACL, ACL_REVISION2, dwAccessMask,
pUserSID))
{
err_msg();
fResult = 8;
goto EXITFUNC;
}
}
//
// STEP 15: To conform to the new Windows 2000 preferred order,
// we will now copy the rest of inherited ACEs from the
// old DACL to the new DACL.
//
if (fDaclPresent && AclInfo.AceCount)
{
for (; CurrentAceIndex < AclInfo.AceCount; CurrentAceIndex++)
{
//
// STEP 16: Get an ACE.
//
if (!GetAce(pACL, CurrentAceIndex, &pTempAce))
{
err_msg();
fResult = 8;
goto EXITFUNC;
}
//
// STEP 17: Add the ACE to the new ACL.
//
if (!AddAce(pNewACL, ACL_REVISION, MAXDWORD, pTempAce,
((PACE_HEADER) pTempAce)->AceSize))
{
err_msg();
fResult = 9;
goto EXITFUNC;
}
}
}
//
// STEP 18: Set the new DACL to the new SD.
//
if (!SetSecurityDescriptorDacl(&newSD, TRUE, pNewACL, FALSE))
{
err_msg();
fResult = 10;
goto EXITFUNC;
}
//
// STEP 19: Copy the old security descriptor control flags
// regarding DACL automatic inheritance for Windows 2000 or
// later where SetSecurityDescriptorControl() API is available
// in advapi32.dll.
//
_SetSecurityDescriptorControl =
(SetSecurityDescriptorControlFnPtr)GetProcAddress(GetModuleHandle(TEXT("advapi32.dll")),"SetSecurityDescriptorControl");
if (_SetSecurityDescriptorControl)
{
SECURITY_DESCRIPTOR_CONTROL controlBitsOfInterest = 0;
SECURITY_DESCRIPTOR_CONTROL controlBitsToSet = 0;
SECURITY_DESCRIPTOR_CONTROL oldControlBits = 0;
DWORD dwRevision = 0;
if (!GetSecurityDescriptorControl(pFileSD, &oldControlBits,
&dwRevision))
{
err_msg();
fResult = 10;
goto EXITFUNC;
}
if (oldControlBits & SE_DACL_AUTO_INHERITED)
{
controlBitsOfInterest = SE_DACL_AUTO_INHERIT_REQ |
SE_DACL_AUTO_INHERITED;
controlBitsToSet = controlBitsOfInterest;
}
else if (oldControlBits & SE_DACL_PROTECTED)
{
controlBitsOfInterest = SE_DACL_PROTECTED;
controlBitsToSet = controlBitsOfInterest;
}
if (controlBitsOfInterest)
{
if (!_SetSecurityDescriptorControl(&newSD, controlBitsOfInterest,
controlBitsToSet))
{
err_msg();
fResult = 11;
goto EXITFUNC;
}
}
}
//
// STEP 20: Set the new SD to the File.
//
if (!SetFileSecurity(lpszFileName, secInfo, &newSD))
{
err_msg();
fResult = 12;
goto EXITFUNC;
}
fResult = 0;
EXITFUNC:
//
// STEP 21: Free allocated memory
//
if (pFileSD) free(pFileSD);
if (pNewACL) free(pNewACL);
return fResult;
}