windows-nt/Source/XPSP1/NT/shell/osshell/security/aclui/permset.cpp
2020-09-26 16:20:57 +08:00

491 lines
14 KiB
C++

//+-------------------------------------------------------------------------
//
// Microsoft Windows
//
// Copyright (C) Microsoft Corporation, 1996 - 1999
//
// File: permset.cpp
//
// This file contains the implementation for the CPermissionSet class
//
//--------------------------------------------------------------------------
#include "aclpriv.h"
#include "permset.h"
void
CPermissionSet::Reset()
{
TraceEnter(TRACE_PERMSET, "CPermissionSet::Reset");
// Clear the lists
if (m_hPermList != NULL)
{
DSA_Destroy(m_hPermList);
m_hPermList = NULL;
}
DestroyDPA(m_hAdvPermList);
m_hAdvPermList = NULL;
m_fObjectAcesPresent = FALSE;
TraceLeaveVoid();
}
void
CPermissionSet::ResetAdvanced()
{
TraceEnter(TRACE_PERMSET, "CPermissionSet::ResetAdvanced");
DestroyDPA(m_hAdvPermList);
m_hAdvPermList = NULL;
TraceLeaveVoid();
}
BOOL
CPermissionSet::AddAce(LPCGUID pguid, ACCESS_MASK mask, DWORD dwFlags)
{
PERMISSION perm = { mask, dwFlags, 0 };
if (pguid != NULL)
perm.guid = *pguid;
return AddPermission(&perm);
}
BOOL
CPermissionSet::AddPermission(PPERMISSION pPerm)
{
BOOL bObjectTypePresent = FALSE;
TraceEnter(TRACE_PERMSET, "CPermissionSet::AddAce");
TraceAssert(pPerm != NULL);
if (!IsEqualGUID(pPerm->guid, GUID_NULL))
bObjectTypePresent = TRUE;
if (m_hPermList == NULL)
{
m_hPermList = DSA_Create(SIZEOF(PERMISSION), 4);
if (m_hPermList == NULL)
TraceLeaveValue(FALSE);
}
else
{
//
// Try to merge with an existing entry in the list.
//
UINT cItems = DSA_GetItemCount(m_hPermList);
while (cItems > 0)
{
PPERMISSION pPermCompare;
DWORD dwMergeFlags;
DWORD dwMergeResult;
DWORD dwMergeStatus;
--cItems;
pPermCompare = (PPERMISSION)DSA_GetItemPtr(m_hPermList, cItems);
dwMergeFlags = 0;
if (bObjectTypePresent)
dwMergeFlags |= MF_OBJECT_TYPE_1_PRESENT;
if (!IsEqualGUID(pPermCompare->guid, GUID_NULL))
dwMergeFlags |= MF_OBJECT_TYPE_2_PRESENT;
if (!(dwMergeFlags & (MF_OBJECT_TYPE_1_PRESENT | MF_OBJECT_TYPE_2_PRESENT)))
{
// Neither are present, so they are the same
dwMergeFlags |= MF_OBJECT_TYPE_EQUAL;
}
else if (IsEqualGUID(pPermCompare->guid, pPerm->guid))
dwMergeFlags |= MF_OBJECT_TYPE_EQUAL;
dwMergeStatus = MergeAceHelper(pPerm->dwFlags, // #1
pPerm->mask,
pPermCompare->dwFlags, // #2
pPermCompare->mask,
dwMergeFlags,
&dwMergeResult);
if (dwMergeStatus == MERGE_MODIFIED_FLAGS)
{
pPerm->dwFlags = dwMergeResult;
dwMergeStatus = MERGE_OK_1;
}
else if (dwMergeStatus == MERGE_MODIFIED_MASK)
{
pPerm->mask = dwMergeResult;
dwMergeStatus = MERGE_OK_1;
}
if (dwMergeStatus == MERGE_OK_1)
{
//
// The new permission implies the existing permission, so
// the existing one can be removed.
//
DSA_DeleteItem(m_hPermList, cItems);
//
// Keep looking. Maybe we can remove some more entries
// before adding the new one.
//
}
else if (dwMergeStatus == MERGE_OK_2)
{
//
// The existing permission implies the new permission, so
// there is nothing to do here.
//
TraceLeaveValue(TRUE);
}
}
}
// Ok, add the new permission to the list.
DSA_AppendItem(m_hPermList, pPerm);
if (bObjectTypePresent)
m_fObjectAcesPresent = TRUE;
TraceLeaveValue(TRUE);
}
BOOL
CPermissionSet::AddAdvancedAce(PACE_HEADER pAce)
{
TraceEnter(TRACE_PERMSET, "CPermissionSet::AddAdvancedAce");
TraceAssert(pAce != NULL);
// Create list if necessary
if (m_hAdvPermList == NULL)
{
m_hAdvPermList = DPA_Create(4);
if (m_hAdvPermList == NULL)
{
TraceMsg("DPA_Create failed");
TraceLeaveValue(FALSE);
}
}
// This is as big as we need, but sometimes incoming ACEs are extra big.
UINT nAceLen = SIZEOF(KNOWN_OBJECT_ACE) + 2*SIZEOF(GUID) - SIZEOF(DWORD)
+ GetLengthSid(GetAceSid(pAce));
// Use the incoming AceSize only if it's smaller
if (pAce->AceSize < nAceLen)
nAceLen = pAce->AceSize;
// Copy the ACE and add it to the list.
PACE_HEADER pAceCopy = (PACE_HEADER)LocalAlloc(LMEM_FIXED, nAceLen);
if (pAceCopy == NULL)
{
TraceMsg("LocalAlloc failed");
TraceLeaveValue(FALSE);
}
CopyMemory(pAceCopy, pAce, nAceLen);
pAceCopy->AceSize = (USHORT)nAceLen;
DPA_AppendPtr(m_hAdvPermList, pAceCopy);
TraceLeaveValue(TRUE);
}
UINT
CPermissionSet::GetPermCount(BOOL fIncludeAdvAces) const
{
ULONG cAces = 0;
TraceEnter(TRACE_PERMSET, "CPermissionSet::GetPermCount");
if (m_hPermList != NULL)
cAces = DSA_GetItemCount(m_hPermList);
if (fIncludeAdvAces && m_hAdvPermList != NULL)
cAces += DPA_GetPtrCount(m_hAdvPermList);
TraceLeaveValue(cAces);
}
ULONG
CPermissionSet::GetAclLength(ULONG cbSid) const
{
// Return an estimate of the buffer size needed to hold the
// requested ACEs. The size of the ACL header is NOT INCLUDED.
ULONG nAclLength = 0;
ULONG cAces;
ULONG nAceSize = SIZEOF(KNOWN_ACE) - SIZEOF(DWORD) + cbSid;
ULONG nObjectAceSize = SIZEOF(KNOWN_OBJECT_ACE) + SIZEOF(GUID) - SIZEOF(DWORD) + cbSid;
TraceEnter(TRACE_PERMSET, "CPermissionSet::GetAclLength");
if (m_hPermList != NULL)
{
cAces = DSA_GetItemCount(m_hPermList);
if (m_fObjectAcesPresent)
nAclLength += cAces * nObjectAceSize;
else
nAclLength += cAces * nAceSize;
}
if (m_hAdvPermList != NULL)
{
cAces = DPA_GetPtrCount(m_hAdvPermList);
nAclLength += cAces * (nObjectAceSize + SIZEOF(GUID));
}
TraceLeaveValue(nAclLength);
}
BOOL
CPermissionSet::AppendToAcl(PACL pAcl,
PACE_HEADER *ppAcePos, // position to copy first ACE
PSID pSid,
BOOL fAllowAce,
DWORD dwFlags) const
{
PACE_HEADER pAce;
UINT cAces;
DWORD dwSidSize;
DWORD dwAceSize;
PPERMISSION pPerm;
UCHAR uAceType;
PSID psidT;
TraceEnter(TRACE_PERMSET, "CPermissionSet::AppendToAcl");
TraceAssert(pAcl != NULL);
TraceAssert(ppAcePos != NULL);
TraceAssert(pSid != NULL);
if (*ppAcePos == NULL || (ULONG_PTR)*ppAcePos < (ULONG_PTR)FirstAce(pAcl))
*ppAcePos = (PACE_HEADER)FirstAce(pAcl);
TraceAssert((ULONG_PTR)*ppAcePos >= (ULONG_PTR)FirstAce(pAcl) &&
(ULONG_PTR)*ppAcePos <= (ULONG_PTR)ByteOffset(pAcl, pAcl->AclSize));
dwSidSize = GetLengthSid(pSid);
dwAceSize = SIZEOF(KNOWN_ACE) - SIZEOF(DWORD) + dwSidSize;
uAceType = (UCHAR)(fAllowAce ? ACCESS_ALLOWED_ACE_TYPE : ACCESS_DENIED_ACE_TYPE);
cAces = GetPermCount();
while (cAces > 0)
{
BOOL bObjectAce;
pPerm = (PPERMISSION)DSA_GetItemPtr(m_hPermList, --cAces);
if (pPerm == NULL)
continue;
bObjectAce = !IsEqualGUID(pPerm->guid, GUID_NULL);
if (bObjectAce && !(dwFlags & PS_OBJECT))
continue;
else if (!bObjectAce && !(dwFlags & PS_NONOBJECT))
continue;
pAce = *ppAcePos;
// Make sure the buffer is large enough.
if ((ULONG_PTR)ByteOffset(*ppAcePos, dwAceSize) > (ULONG_PTR)ByteOffset(pAcl, pAcl->AclSize))
{
TraceMsg("ACL buffer too small");
TraceAssert(FALSE);
TraceLeaveValue(FALSE);
}
TraceAssert(!IsBadWritePtr(*ppAcePos, dwAceSize));
// Copy the header and mask
pAce->AceType = uAceType;
pAce->AceFlags = (UCHAR)pPerm->dwFlags;
pAce->AceSize = (USHORT)dwAceSize;
((PKNOWN_ACE)pAce)->Mask = pPerm->mask;
// Get the normal SID location
psidT = &((PKNOWN_ACE)pAce)->SidStart;
if (bObjectAce)
{
//
// The Object ACEs that we deal with directly do not have an
// Inherit GUID present. Those ACEs end up in m_hAdvPermList.
//
GUID *pGuid;
// Adjust AceType and AceSize and set the object Flags
pAce->AceType += ACCESS_ALLOWED_OBJECT_ACE_TYPE - ACCESS_ALLOWED_ACE_TYPE;
pAce->AceSize += SIZEOF(KNOWN_OBJECT_ACE) - SIZEOF(KNOWN_ACE) + SIZEOF(GUID);
((PKNOWN_OBJECT_ACE)pAce)->Flags = ACE_OBJECT_TYPE_PRESENT;
// Get the object type guid location
pGuid = RtlObjectAceObjectType(pAce);
// We just set the flag for this, so it can't be NULL
TraceAssert(pGuid);
// Make sure the buffer is large enough.
if ((ULONG_PTR)ByteOffset(pAce, pAce->AceSize) > (ULONG_PTR)ByteOffset(pAcl, pAcl->AclSize))
{
TraceMsg("ACL buffer too small");
TraceAssert(FALSE);
TraceLeaveValue(FALSE);
}
TraceAssert(!IsBadWritePtr(pGuid, SIZEOF(GUID)));
// Copy the object type guid
*pGuid = pPerm->guid;
// Get new SID location
psidT = RtlObjectAceSid(pAce);
// Adjust ACL revision
if (pAcl->AclRevision < ACL_REVISION_DS)
pAcl->AclRevision = ACL_REVISION_DS;
}
// Copy the SID
TraceAssert(!IsBadWritePtr(psidT, dwSidSize));
CopyMemory(psidT, pSid, dwSidSize);
// Move to next ACE position
pAcl->AceCount++;
*ppAcePos = (PACE_HEADER)NextAce(pAce);
}
if ((dwFlags & PS_OBJECT) && m_hAdvPermList != NULL)
{
cAces = DPA_GetPtrCount(m_hAdvPermList);
while (cAces > 0)
{
pAce = (PACE_HEADER)DPA_FastGetPtr(m_hAdvPermList, --cAces);
if (pAce == NULL)
continue;
// Make sure the buffer is large enough.
if ((ULONG_PTR)ByteOffset(*ppAcePos, pAce->AceSize) > (ULONG_PTR)ByteOffset(pAcl, pAcl->AclSize))
{
TraceMsg("ACL buffer too small");
TraceAssert(FALSE);
TraceLeaveValue(FALSE);
}
TraceAssert(!IsBadWritePtr(*ppAcePos, pAce->AceSize));
// Copy the ACE
CopyMemory(*ppAcePos, pAce, pAce->AceSize);
// Adjust ACL revision
if (IsObjectAceType(pAce) && pAcl->AclRevision < ACL_REVISION_DS)
pAcl->AclRevision = ACL_REVISION_DS;
// Move to next ACE position
pAcl->AceCount++;
*ppAcePos = (PACE_HEADER)NextAce(*ppAcePos);
}
}
TraceLeaveValue(TRUE);
}
void
CPermissionSet::ConvertInheritedAces(CPermissionSet &permInherited)
{
UINT cItems;
TraceEnter(TRACE_PERMSET, "CPermissionSet::ConvertInheritedAces");
if (permInherited.m_hPermList != NULL)
{
PPERMISSION pPerm;
cItems = DSA_GetItemCount(permInherited.m_hPermList);
while (cItems)
{
--cItems;
pPerm = (PPERMISSION)DSA_GetItemPtr(permInherited.m_hPermList, cItems);
if (pPerm != NULL)
{
pPerm->dwFlags &= ~INHERITED_ACE;
AddPermission(pPerm);
}
}
}
if (permInherited.m_hAdvPermList != NULL)
{
PACE_HEADER pAceHeader;
cItems = DPA_GetPtrCount(permInherited.m_hAdvPermList);
while (cItems)
{
--cItems;
pAceHeader = (PACE_HEADER)DPA_FastGetPtr(permInherited.m_hAdvPermList, cItems);
if (pAceHeader != NULL)
{
pAceHeader->AceFlags &= ~INHERITED_ACE;
AddAdvancedAce(pAceHeader);
}
}
}
permInherited.Reset();
TraceLeaveVoid();
}
//Removes permission. If bInheritFlag, match inheritance flags before
//removing permission
void
CPermissionSet::RemovePermission(PPERMISSION pPerm, BOOL bInheritFlag )
{
BOOL bObjectAcePresent = FALSE;
TraceEnter(TRACE_PERMSET, "CPermissionSet::RemovePermission");
TraceAssert(pPerm != NULL);
if (m_hPermList)
{
BOOL bNullGuid = IsEqualGUID(pPerm->guid, GUID_NULL);
UINT cItems = DSA_GetItemCount(m_hPermList);
while (cItems > 0)
{
PPERMISSION pPermCompare;
BOOL bNullGuidCompare;
--cItems;
pPermCompare = (PPERMISSION)DSA_GetItemPtr(m_hPermList, cItems);
bNullGuidCompare = IsEqualGUID(pPermCompare->guid, GUID_NULL);
if (bNullGuid || bNullGuidCompare || IsEqualGUID(pPermCompare->guid, pPerm->guid))
{
if( !bInheritFlag || ( (pPermCompare->dwFlags & VALID_INHERIT_FLAGS) == (pPerm->dwFlags & VALID_INHERIT_FLAGS) ) )
{
pPermCompare->mask &= ~pPerm->mask;
if (0 == pPermCompare->mask)
DSA_DeleteItem(m_hPermList, cItems);
else if (!bNullGuidCompare)
bObjectAcePresent = TRUE;
}
}
else if (!bNullGuidCompare)
bObjectAcePresent = TRUE;
}
}
m_fObjectAcesPresent = bObjectAcePresent;
TraceLeaveVoid();
}