748 lines
20 KiB
C++
748 lines
20 KiB
C++
//*********************************************************
|
|
//
|
|
// Copyright (c) Microsoft. All rights reserved.
|
|
// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
|
|
// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
|
|
// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
|
|
// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
|
|
//
|
|
//*********************************************************
|
|
#include "utility.h"
|
|
#include "CSecInfo.h"
|
|
#include "authz.h"
|
|
#include <stdio.h>
|
|
#include <strsafe.h> // for StringCchCopy
|
|
#include <intsafe.h> // for UShortAdd, UShortMult
|
|
#include "sddl.h"
|
|
#include <string>
|
|
|
|
HRESULT AllocAndCopyString(_In_ PCWSTR source, _Outptr_ PWSTR *dest)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
size_t len;
|
|
|
|
if ( !ARGUMENT_PRESENT(source) )
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
len = wcslen(source);
|
|
// Remember: wcslen excludes the null character when calculating the length.
|
|
// Also, StringCchCopyW takes the total length of the destination buffer
|
|
*dest = (PWSTR)LocalAlloc(LMEM_ZEROINIT, ((len + 1) * sizeof(wchar_t)));
|
|
if ( !(*dest) )
|
|
{
|
|
wprintf(L"LocalAlloc failed.\n");
|
|
hr = E_OUTOFMEMORY;
|
|
goto exit_gracefully;
|
|
}
|
|
|
|
hr = StringCchCopyW(*dest, len + 1, source);
|
|
FailGracefully(hr, L"StringCchCopyW");
|
|
|
|
exit_gracefully:
|
|
|
|
if ( !SUCCEEDED(hr) )
|
|
{
|
|
LocalFree(*dest);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT RemoveAllInheritedAces(PACL *ppAcl)
|
|
{
|
|
BOOL bResult = FALSE;
|
|
DWORD errorCode = S_OK;
|
|
HRESULT hr = S_OK;
|
|
ACL_SIZE_INFORMATION aclInformation;
|
|
DWORD totalCount;
|
|
DWORD aceIndex = 0;
|
|
LPVOID ace = nullptr;
|
|
BYTE aceFlags = 0;
|
|
|
|
if ( !ARGUMENT_PRESENT(*ppAcl) )
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
bResult = GetAclInformation(
|
|
*ppAcl,
|
|
&aclInformation,
|
|
sizeof(aclInformation),
|
|
AclSizeInformation
|
|
);
|
|
FailGracefullyGLE(bResult, L"GetAclInformation");
|
|
|
|
totalCount = aclInformation.AceCount;
|
|
while ( aceIndex < totalCount )
|
|
{
|
|
bResult = GetAce(
|
|
*ppAcl,
|
|
aceIndex,
|
|
&ace
|
|
);
|
|
FailGracefullyGLE(bResult, L"GetAce");
|
|
|
|
aceFlags = ((PACE_HEADER)ace)->AceFlags;
|
|
|
|
if (IS_FLAG_SET(aceFlags,INHERITED_ACE))
|
|
{
|
|
bResult = DeleteAce(
|
|
*ppAcl,
|
|
aceIndex);
|
|
FailGracefullyGLE(bResult, L"DeleteAce");
|
|
|
|
totalCount--;
|
|
}
|
|
else
|
|
{
|
|
aceIndex++;
|
|
}
|
|
}
|
|
|
|
exit_gracefully:
|
|
return hr;
|
|
}
|
|
|
|
// Adds all ACEs from sourceAcl to ppDestAcl.
|
|
// If onlyAddUnique is specified, then this function will ensure that
|
|
// ACEs from sourceAcl aren't added if they already appear in ppDestAcl.
|
|
HRESULT AddAllAcesFromAcl(PACL sourceAcl, PACL *ppDestAcl, bool onlyAddUnique)
|
|
{
|
|
BOOL bResult = 0;
|
|
DWORD errorCode = S_OK;
|
|
HRESULT hr = S_OK;
|
|
ACL_SIZE_INFORMATION aclInformation;
|
|
LPVOID ace;
|
|
bool addIt = true;
|
|
BOOL alreadyExists = FALSE;
|
|
|
|
if ( !ARGUMENT_PRESENT(sourceAcl) || !ARGUMENT_PRESENT(*ppDestAcl) )
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
bResult = GetAclInformation(
|
|
sourceAcl,
|
|
&aclInformation,
|
|
sizeof(aclInformation),
|
|
AclSizeInformation
|
|
);
|
|
|
|
FailGracefullyGLE(bResult, L"GetAclInformation");
|
|
|
|
for ( DWORD i = 0; i < aclInformation.AceCount; i ++ )
|
|
{
|
|
bResult = GetAce(
|
|
sourceAcl,
|
|
i,
|
|
&ace
|
|
);
|
|
|
|
FailGracefullyGLE(bResult, L"GetAce");
|
|
|
|
// If we only want to add unique ACEs, then we
|
|
// need to make sure that this doesn't already exist
|
|
// in the destination.
|
|
addIt = true;
|
|
if ( onlyAddUnique )
|
|
{
|
|
|
|
hr = ACEAlreadyInACL(
|
|
*ppDestAcl,
|
|
ace,
|
|
&alreadyExists,
|
|
false
|
|
);
|
|
|
|
FailGracefully(hr, L"ACEAlreadyInACL");
|
|
|
|
if ( alreadyExists != FALSE )
|
|
{
|
|
addIt = false;
|
|
}
|
|
}
|
|
|
|
if ( addIt )
|
|
{
|
|
hr = AddAceToAcl(ace, ppDestAcl, false);
|
|
FailGracefully(hr, L"AddAceToAcl");
|
|
}
|
|
}
|
|
exit_gracefully:
|
|
|
|
return hr;
|
|
}
|
|
|
|
// This function will remove the noninherited ACEs from
|
|
// ppDestAcl that don't show up in 'acl'. This is needed for
|
|
// SetSecurity - inherited ACEs need to stay, and any ACEs that
|
|
// are being set need to stay, but all others need to go.
|
|
HRESULT RemoveExplicitUniqueAces(PACL acl, PACL *ppDestAcl)
|
|
{
|
|
BOOL bResult = 0;
|
|
DWORD errorCode = S_OK;
|
|
HRESULT hr = S_OK;
|
|
ACL_SIZE_INFORMATION aclInformation;
|
|
DWORD totalCount;
|
|
DWORD aceIndex = 0;
|
|
LPVOID ace;
|
|
BYTE aceFlags;
|
|
BOOL alreadyExists = FALSE;
|
|
|
|
if ( !ARGUMENT_PRESENT(acl) || !ARGUMENT_PRESENT(*ppDestAcl) )
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
bResult = GetAclInformation(
|
|
*ppDestAcl,
|
|
&aclInformation,
|
|
sizeof(aclInformation),
|
|
AclSizeInformation
|
|
);
|
|
FailGracefullyGLE(bResult, L"GetAclInformation");
|
|
|
|
totalCount = aclInformation.AceCount;
|
|
while ( aceIndex < totalCount )
|
|
{
|
|
bResult = GetAce(
|
|
*ppDestAcl,
|
|
aceIndex,
|
|
&ace
|
|
);
|
|
|
|
FailGracefullyGLE(bResult, L"GetAce");
|
|
|
|
aceFlags = ((PACE_HEADER)ace)->AceFlags;
|
|
|
|
// We only care about explcit (i.e. non-inherited) ACEs
|
|
if ( IS_FLAG_SET(aceFlags, INHERITED_ACE) )
|
|
{
|
|
aceIndex++;
|
|
continue;
|
|
}
|
|
|
|
hr = ACEAlreadyInACL(
|
|
acl,
|
|
ace,
|
|
&alreadyExists,
|
|
false
|
|
);
|
|
|
|
FailGracefully(hr, L"ACEAlreadyInACL");
|
|
|
|
// If it's not in 'acl', then it's unique
|
|
if ( alreadyExists == FALSE )
|
|
{
|
|
bResult = DeleteAce(*ppDestAcl, aceIndex);
|
|
FailGracefullyGLE(bResult, L"DeleteAce");
|
|
totalCount--;
|
|
}
|
|
else
|
|
{
|
|
aceIndex++;
|
|
}
|
|
}
|
|
|
|
exit_gracefully:
|
|
return hr;
|
|
}
|
|
|
|
// Go through all of the ACEs in the ACL and see if 'ace' is equal to any of them.
|
|
HRESULT ACEAlreadyInACL(
|
|
_In_ PACL acl,
|
|
_In_ LPVOID ace,
|
|
_Out_ LPBOOL lpbAcePresent,
|
|
_In_ bool forInheritancePurposes)
|
|
{
|
|
BOOL bResult = 0;
|
|
DWORD errorCode = 0;
|
|
HRESULT hr = S_OK;
|
|
ACL_SIZE_INFORMATION aclInformation;
|
|
BYTE aceType;
|
|
WORD aceSize;
|
|
BYTE aceFlags;
|
|
BYTE existingAceType;
|
|
WORD existingAceSize;
|
|
BYTE existingAceFlags;
|
|
DWORD totalCount;
|
|
LPVOID pExistingAce;
|
|
LPVOID sidStart1;
|
|
LPVOID sidStart2;
|
|
DWORD sidLength1;
|
|
DWORD sidLength2;
|
|
int result;
|
|
|
|
if ( !ARGUMENT_PRESENT(acl) || !ARGUMENT_PRESENT(ace) )
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
aceType = ((PACE_HEADER)ace)->AceType;
|
|
aceSize = ((PACE_HEADER)ace)->AceSize;
|
|
aceFlags = ((PACE_HEADER)ace)->AceFlags;
|
|
|
|
if ( forInheritancePurposes )
|
|
{
|
|
// If the ACE wasn't inherited, then we don't care about it
|
|
if ( !IS_FLAG_SET(aceFlags, INHERITED_ACE) )
|
|
{
|
|
*lpbAcePresent = FALSE;
|
|
return S_OK;
|
|
}
|
|
else
|
|
{
|
|
// It was inherited. Don't use that flag for comparison though
|
|
// because it won't appear on the source.
|
|
aceFlags &= ~INHERITED_ACE;
|
|
}
|
|
}
|
|
|
|
bResult = GetAclInformation(
|
|
acl,
|
|
&aclInformation,
|
|
sizeof(aclInformation),
|
|
AclSizeInformation
|
|
);
|
|
FailGracefullyGLE(bResult, L"GetAclInformation");
|
|
|
|
totalCount = aclInformation.AceCount;
|
|
for ( DWORD i = 0; i < totalCount; i ++ )
|
|
{
|
|
bResult = GetAce(
|
|
acl,
|
|
i,
|
|
&pExistingAce
|
|
);
|
|
FailGracefullyGLE(bResult, L"GetAce");
|
|
|
|
existingAceType = ((PACE_HEADER)pExistingAce)->AceType;
|
|
existingAceSize = ((PACE_HEADER)pExistingAce)->AceSize;
|
|
existingAceFlags = ((PACE_HEADER)pExistingAce)->AceFlags;
|
|
|
|
if ( forInheritancePurposes )
|
|
{
|
|
if ( !IsInheritableAce(existingAceFlags) )
|
|
{
|
|
// We only care about inheritable ACEs
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
// Wipe out the inheritance flags that couldn't possibly be on the
|
|
// child. (e.g. when we have a NO_PROPAGATE_INHERIT_ACE, the ACE
|
|
// will make it to the child, but the flag will get unset in
|
|
// AddInheritableAcesFromAcl).
|
|
existingAceFlags &= ~INHERIT_ONLY_ACE;
|
|
|
|
if ( IS_FLAG_SET(existingAceFlags, NO_PROPAGATE_INHERIT_ACE) )
|
|
{
|
|
existingAceFlags &= ~NO_PROPAGATE_INHERIT_ACE;
|
|
|
|
// The child doesn't get the [...]_INHERIT_ACE
|
|
// flag if NO_PROPAGATE was specified.
|
|
existingAceFlags &= ~CONTAINER_INHERIT_ACE;
|
|
existingAceFlags &= ~OBJECT_INHERIT_ACE;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( existingAceFlags == aceFlags &&
|
|
existingAceSize == aceSize &&
|
|
existingAceType == aceType )
|
|
{
|
|
// That was our quick check, now we should compare the
|
|
// actual contents (mask and SID)
|
|
sidStart1 = GetSidStart(aceType, pExistingAce);
|
|
sidStart2 = GetSidStart(aceType, ace);
|
|
sidLength1 = GetLengthSid((PSID)sidStart1);
|
|
sidLength2 = GetLengthSid((PSID)sidStart2);
|
|
|
|
// This is needed even though we just compared the sizes because
|
|
// of callback aces - the size of the ACLs may coincidentally be
|
|
// the same because the sums of the sid length and condition
|
|
// sizes are equal
|
|
if ( sidLength1 != sidLength2 )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
result = memcmp(
|
|
(PSID)sidStart1,
|
|
(PSID)sidStart2,
|
|
aceSize - sizeof(ACE_HEADER) - sizeof(ACCESS_MASK));
|
|
if ( result == 0 )
|
|
{
|
|
*lpbAcePresent = TRUE;
|
|
return S_OK;
|
|
}
|
|
}
|
|
}
|
|
|
|
*lpbAcePresent = FALSE;
|
|
exit_gracefully:
|
|
return hr;
|
|
}
|
|
|
|
// Add an ACE to an ACL, expanding the ACL if needed.
|
|
HRESULT AddAceToAcl(LPVOID pNewAce, PACL *ppAcl, bool bAddToEndOfList)
|
|
{
|
|
DWORD addPosition = bAddToEndOfList ? MAXDWORD : 0;
|
|
BOOL bResult = FALSE;
|
|
DWORD errorCode = S_OK;
|
|
PACL biggerDACL = nullptr;
|
|
HRESULT hr = S_OK;
|
|
ACL_SIZE_INFORMATION aclInformation;
|
|
WORD aceSize;
|
|
DWORD oldSize;
|
|
USHORT uSize;
|
|
DWORD dwNewSizeNeeded;
|
|
|
|
if ( !ARGUMENT_PRESENT(pNewAce) || !ARGUMENT_PRESENT(*ppAcl) )
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
bResult = AddAce(
|
|
*ppAcl,
|
|
ACL_REVISION,
|
|
addPosition,
|
|
pNewAce,
|
|
((PACE_HEADER)pNewAce)->AceSize
|
|
);
|
|
|
|
// See if we failed due to an insufficient buffer size
|
|
if ( !bResult )
|
|
{
|
|
errorCode = GetLastError();
|
|
if ( errorCode == ERROR_INSUFFICIENT_BUFFER )
|
|
{
|
|
// Not enough space. Allocate a bigger ACL.
|
|
|
|
bResult = GetAclInformation(
|
|
*ppAcl,
|
|
&aclInformation,
|
|
sizeof(aclInformation),
|
|
AclSizeInformation
|
|
);
|
|
FailGracefullyGLE(bResult, L"GetAclInformation");
|
|
|
|
aceSize = ((PACE_HEADER)pNewAce)->AceSize;
|
|
oldSize = aclInformation.AclBytesInUse;
|
|
|
|
// Can't overflow WORD boundary
|
|
hr = UShortAdd(aceSize, (USHORT)oldSize, &uSize);
|
|
FailGracefully(hr, L"UShortAdd");
|
|
|
|
// Give us some extra space so that we don't have to keep
|
|
// reallocating.
|
|
hr = UShortMult(uSize, 2, &uSize);
|
|
FailGracefully(hr, L"UShortMult");
|
|
|
|
dwNewSizeNeeded = (DWORD)uSize;
|
|
// Align to a DWORD
|
|
dwNewSizeNeeded = (dwNewSizeNeeded + (sizeof(DWORD) - 1)) & 0xfffffffc;
|
|
|
|
// Make sure the alignment didn't overflow the size of a WORD
|
|
// (the process of aligning can add up to 3)
|
|
if ( dwNewSizeNeeded > USHORT_MAX )
|
|
{
|
|
// It's safe to subtrat now (despite the "size needed" name)
|
|
// because we overestimated earlier by multiplying by 2.
|
|
dwNewSizeNeeded -= 4;
|
|
}
|
|
|
|
biggerDACL = (PACL)LocalAlloc(LPTR, dwNewSizeNeeded);
|
|
if ( !biggerDACL )
|
|
{
|
|
wprintf(L"LocalAlloc failed.\n");
|
|
hr = E_OUTOFMEMORY;
|
|
goto exit_gracefully;
|
|
}
|
|
|
|
// Note: no need to initialize the new ACL
|
|
memcpy(biggerDACL, *ppAcl, oldSize);
|
|
|
|
// This cast is safe because we checked for overflow earlier
|
|
biggerDACL->AclSize = (WORD)dwNewSizeNeeded;
|
|
|
|
*ppAcl = biggerDACL;
|
|
|
|
// Make sure this doesn't get freed later
|
|
biggerDACL = nullptr;
|
|
|
|
bResult = AddAce(
|
|
*ppAcl,
|
|
ACL_REVISION,
|
|
addPosition,
|
|
pNewAce,
|
|
((PACE_HEADER)pNewAce)->AceSize
|
|
);
|
|
FailGracefullyGLE(bResult, L"AddAce");
|
|
}
|
|
else
|
|
{
|
|
return HRESULT_FROM_WIN32(errorCode);
|
|
}
|
|
}
|
|
exit_gracefully:
|
|
LocalFree(biggerDACL);
|
|
|
|
return hr;
|
|
}
|
|
|
|
LPVOID GetSidStart(BYTE aceType, LPVOID ace)
|
|
{
|
|
LONG offset = 0;
|
|
switch (aceType)
|
|
{
|
|
case ACCESS_ALLOWED_ACE_TYPE:
|
|
case ACCESS_DENIED_ACE_TYPE:
|
|
case SYSTEM_AUDIT_ACE_TYPE:
|
|
offset = FIELD_OFFSET(ACCESS_ALLOWED_ACE, SidStart);
|
|
break;
|
|
case SYSTEM_AUDIT_CALLBACK_ACE_TYPE:
|
|
offset = FIELD_OFFSET(SYSTEM_AUDIT_CALLBACK_ACE, SidStart);
|
|
break;
|
|
case ACCESS_ALLOWED_CALLBACK_ACE_TYPE:
|
|
offset = FIELD_OFFSET(ACCESS_ALLOWED_CALLBACK_ACE, SidStart);
|
|
break;
|
|
default:
|
|
return 0;
|
|
}
|
|
|
|
// We use PUCHAR so that we can add an offset to the pointer
|
|
return (PUCHAR)ace + offset;
|
|
}
|
|
|
|
HRESULT ConvertSecurityDescriptor(
|
|
PSECURITY_DESCRIPTOR pSelfRelSD,
|
|
PSECURITY_DESCRIPTOR *ppAbsoluteSD)
|
|
{
|
|
BOOL bResult;
|
|
PACL pDacl = nullptr;
|
|
PACL pSacl = nullptr;
|
|
PSID pOwner = nullptr;
|
|
PSID pPrimaryGroup = nullptr;
|
|
DWORD dwAbsoluteSDSize = 0;
|
|
DWORD dwDaclSize = 0;
|
|
DWORD dwSaclSize = 0;
|
|
DWORD dwOwnerSize = 0;
|
|
DWORD dwPrimaryGroupSize = 0;
|
|
size_t sizeNeeded = 0;
|
|
*ppAbsoluteSD = nullptr;
|
|
|
|
// Confirm that pSelfRelSD is actually self relative
|
|
if(
|
|
!ARGUMENT_PRESENT(pSelfRelSD) ||
|
|
!IS_FLAG_SET(((SECURITY_DESCRIPTOR*)(pSelfRelSD))->Control, SE_SELF_RELATIVE)
|
|
)
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
// Call it once so that the sizes will be set to the correct values. This
|
|
// will return a buffer-too-small error, so we don't check the return value
|
|
MakeAbsoluteSD(
|
|
pSelfRelSD,
|
|
nullptr,
|
|
&dwAbsoluteSDSize,
|
|
nullptr,
|
|
&dwDaclSize,
|
|
nullptr,
|
|
&dwSaclSize,
|
|
nullptr,
|
|
&dwOwnerSize,
|
|
nullptr,
|
|
&dwPrimaryGroupSize
|
|
);
|
|
|
|
sizeNeeded =
|
|
dwAbsoluteSDSize +
|
|
dwDaclSize +
|
|
dwSaclSize +
|
|
dwOwnerSize +
|
|
dwPrimaryGroupSize;
|
|
*ppAbsoluteSD = (PSECURITY_DESCRIPTOR)LocalAlloc(LMEM_FIXED, sizeNeeded);
|
|
|
|
if ( !*ppAbsoluteSD )
|
|
{
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
BYTE* position = (BYTE *)(*ppAbsoluteSD);
|
|
pDacl = reinterpret_cast<PACL>(position += dwAbsoluteSDSize);
|
|
pSacl = reinterpret_cast<PACL>(position += dwDaclSize);
|
|
pOwner = reinterpret_cast<PSID>(position += dwSaclSize);
|
|
pPrimaryGroup = reinterpret_cast<PSID>(position += dwOwnerSize);
|
|
|
|
bResult = MakeAbsoluteSD(
|
|
pSelfRelSD,
|
|
*ppAbsoluteSD,
|
|
&dwAbsoluteSDSize,
|
|
pDacl,
|
|
&dwDaclSize,
|
|
pSacl,
|
|
&dwSaclSize,
|
|
pOwner,
|
|
&dwOwnerSize,
|
|
pPrimaryGroup,
|
|
&dwPrimaryGroupSize
|
|
);
|
|
|
|
if ( !bResult )
|
|
{
|
|
LocalFree(*ppAbsoluteSD);
|
|
return HRESULT_FROM_WIN32(GetLastError());
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
// This goes through every ACE in 'acl', sums the size of the inheritable
|
|
// ACEs, and puts it in dwSizeNeeded.
|
|
HRESULT GetSizeOfAllInheritableAces(PACL acl, DWORD &dwSizeNeeded)
|
|
{
|
|
BOOL bResult;
|
|
DWORD errorCode = S_OK;
|
|
HRESULT hr = S_OK;
|
|
ACL_SIZE_INFORMATION aclInformation;
|
|
DWORD totalCount;
|
|
LPVOID ace;
|
|
BYTE aceFlags;
|
|
|
|
if ( !ARGUMENT_PRESENT(acl) )
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
bResult = GetAclInformation(
|
|
acl,
|
|
&aclInformation,
|
|
sizeof(aclInformation),
|
|
AclSizeInformation
|
|
);
|
|
FailGracefullyGLE(bResult, L"GetAclInformation");
|
|
|
|
totalCount = aclInformation.AceCount;
|
|
|
|
// Start with zero as the size. We'll only initialize this
|
|
// to sizeof(ACL) if we find an inheritable ACE.
|
|
dwSizeNeeded = 0;
|
|
|
|
for ( DWORD aceIndex = 0; aceIndex < totalCount; aceIndex ++ )
|
|
{
|
|
bResult = GetAce(
|
|
acl,
|
|
aceIndex,
|
|
&ace
|
|
);
|
|
FailGracefullyGLE(bResult, L"GetAce");
|
|
|
|
aceFlags = ((PACE_HEADER)ace)->AceFlags;
|
|
|
|
// Only count the inheritable ACEs
|
|
if (IsInheritableAce(aceFlags))
|
|
{
|
|
// Initialize the size now that we've found an inheritable ACE
|
|
if ( dwSizeNeeded == 0 )
|
|
{
|
|
dwSizeNeeded = sizeof(ACL);
|
|
}
|
|
dwSizeNeeded += ((PACE_HEADER)ace)->AceSize;
|
|
}
|
|
}
|
|
exit_gracefully:
|
|
return hr;
|
|
}
|
|
|
|
// Adds any inheritable ACEs from sourceAcl to ppDestAcl.
|
|
HRESULT AddInheritableAcesFromAcl(PACL sourceAcl, PACL *ppDestAcl)
|
|
{
|
|
BOOL bResult = 0;
|
|
DWORD errorCode = S_OK;
|
|
HRESULT hr = S_OK;
|
|
ACL_SIZE_INFORMATION aclInformation;
|
|
LPVOID newAce = nullptr;
|
|
LPVOID ace;
|
|
BYTE aceFlags;
|
|
WORD aceSize;
|
|
|
|
if ( !ARGUMENT_PRESENT(sourceAcl) || !ARGUMENT_PRESENT(*ppDestAcl) )
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
bResult = GetAclInformation(
|
|
sourceAcl,
|
|
&aclInformation,
|
|
sizeof(aclInformation),
|
|
AclSizeInformation
|
|
);
|
|
FailGracefullyGLE(bResult, L"GetAclInformation");
|
|
|
|
for ( DWORD i = 0; i < aclInformation.AceCount; i ++ )
|
|
{
|
|
bResult = GetAce(
|
|
sourceAcl,
|
|
i,
|
|
&ace
|
|
);
|
|
|
|
FailGracefullyGLE(bResult, L"GetAce");
|
|
|
|
aceFlags = ((PACE_HEADER)ace)->AceFlags;
|
|
|
|
// We only care about the inheritable ACEs
|
|
if (IsInheritableAce(aceFlags))
|
|
{
|
|
// We don't need to see if the ACE is already in the child's DACL
|
|
// because we always remove all inherited ACEs before calling this
|
|
// function, so an inheritable ACE can't exist in the child.
|
|
//
|
|
// The ACE that we add needs to have the "ID" flag set,
|
|
// but we don't want to modify the parent's ACE, so we
|
|
// need to make a new ACE.
|
|
aceSize = ((PACE_HEADER)ace)->AceSize;
|
|
newAce = (LPVOID)LocalAlloc(LPTR, aceSize);
|
|
if ( !newAce )
|
|
{
|
|
wprintf(L"LocalAlloc failed.\n");
|
|
hr = E_OUTOFMEMORY;
|
|
goto exit_gracefully;
|
|
}
|
|
|
|
memcpy(newAce, ace, aceSize);
|
|
((PACE_HEADER)newAce)->AceFlags |= INHERITED_ACE;
|
|
|
|
// It's important that we unset the inherit-only flag, or this
|
|
// ACE will be overlooked by AccessCheck (likewise by effective
|
|
// access checks).
|
|
((PACE_HEADER)newAce)->AceFlags &= ~INHERIT_ONLY;
|
|
|
|
// NO_PROPAGATE_INHERIT_ACE means the inheritance only applies to
|
|
// the child, not the grandchildren, so we need to unset the
|
|
// applicable inheritance flags.
|
|
if ( IS_FLAG_SET(aceFlags, NO_PROPAGATE_INHERIT_ACE) )
|
|
{
|
|
((PACE_HEADER)newAce)->AceFlags &= ~NO_PROPAGATE_INHERIT_ACE;
|
|
((PACE_HEADER)newAce)->AceFlags &= ~OBJECT_INHERIT_ACE;
|
|
((PACE_HEADER)newAce)->AceFlags &= ~CONTAINER_INHERIT_ACE;
|
|
}
|
|
|
|
hr = AddAceToAcl(newAce, ppDestAcl, false);
|
|
FailGracefully(hr, L"AddAceToAcl");
|
|
|
|
LocalFree(newAce);
|
|
newAce = nullptr;
|
|
}
|
|
}
|
|
|
|
exit_gracefully:
|
|
|
|
LocalFree(newAce);
|
|
return hr;
|
|
}
|