windows-nt/Source/XPSP1/NT/shell/lib/generic/access.cpp
2020-09-26 16:20:57 +08:00

563 lines
19 KiB
C++

// --------------------------------------------------------------------------
// Module Name: Access.cpp
//
// Copyright (c) 1999, Microsoft Corporation
//
// This file contains a few classes that assist with ACL manipulation on
// objects to which a handle has already been opened. This handle must have
// (obvisouly) have WRITE_DAC access.
//
// History: 1999-10-05 vtan created
// 2000-02-01 vtan moved from Neptune to Whistler
// --------------------------------------------------------------------------
#include "StandardHeader.h"
#include "Access.h"
#include "StatusCode.h"
// --------------------------------------------------------------------------
// CSecurityDescriptor::CSecurityDescriptor
//
// Arguments: iCount = Count of ACCESS_CONTROLS passed in.
// pAccessControl = Pointer to ACCESS_CONTROLS.
//
// Returns: <none>
//
// Purpose: Allocates and assigns the PSECURITY_DESCRIPTOR that
// corresponds to the description given by the parameters. The
// caller must release the memory allocated via LocalFree.
//
// History: 2000-10-05 vtan created
// --------------------------------------------------------------------------
PSECURITY_DESCRIPTOR CSecurityDescriptor::Create (int iCount, const ACCESS_CONTROL *pAccessControl)
{
PSECURITY_DESCRIPTOR pSecurityDescriptor;
PSID *pSIDs;
pSecurityDescriptor = NULL;
// Allocate an array of PSIDs required to hold all the SIDs to add.
pSIDs = reinterpret_cast<PSID*>(LocalAlloc(LPTR, iCount * sizeof(PSID)));
if (pSIDs != NULL)
{
bool fSuccessfulAllocate;
int i;
const ACCESS_CONTROL *pAC;
for (fSuccessfulAllocate = true, pAC = pAccessControl, i = 0; fSuccessfulAllocate && (i < iCount); ++pAC, ++i)
{
fSuccessfulAllocate = (AllocateAndInitializeSid(pAC->pSIDAuthority,
static_cast<BYTE>(pAC->iSubAuthorityCount),
pAC->dwSubAuthority0,
pAC->dwSubAuthority1,
pAC->dwSubAuthority2,
pAC->dwSubAuthority3,
pAC->dwSubAuthority4,
pAC->dwSubAuthority5,
pAC->dwSubAuthority6,
pAC->dwSubAuthority7,
&pSIDs[i]) != FALSE);
}
if (fSuccessfulAllocate)
{
DWORD dwACLSize;
unsigned char *pBuffer;
// Calculate the size of the ACL required by totalling the ACL header
// struct and the 2 ACCESS_ALLOWED_ACE structs with the SID sizes.
// Add the SECURITY_DESCRIPTOR struct size as well.
dwACLSize = sizeof(ACL) + ((sizeof(ACCESS_ALLOWED_ACE) - sizeof(DWORD)) * 3);
for (i = 0; i < iCount; ++i)
{
dwACLSize += GetLengthSid(pSIDs[i]);
}
// Allocate the buffer for everything and portion off the buffer to
// the right place.
pBuffer = static_cast<unsigned char*>(LocalAlloc(LMEM_FIXED, sizeof(SECURITY_DESCRIPTOR) + dwACLSize));
if (pBuffer != NULL)
{
PSECURITY_DESCRIPTOR pSD;
PACL pACL;
pSD = reinterpret_cast<PSECURITY_DESCRIPTOR>(pBuffer);
pACL = reinterpret_cast<PACL>(pBuffer + sizeof(SECURITY_DESCRIPTOR));
// Initialize the ACL. Fill in the ACL.
// Initialize the SECURITY_DESCRIPTOR. Set the security descriptor.
if ((InitializeAcl(pACL, dwACLSize, ACL_REVISION) != FALSE) &&
AddAces(pACL, pSIDs, iCount, pAccessControl) &&
(InitializeSecurityDescriptor(pSD, SECURITY_DESCRIPTOR_REVISION) != FALSE) &&
(SetSecurityDescriptorDacl(pSD, TRUE, pACL, FALSE) != FALSE))
{
pSecurityDescriptor = pSD;
}
else
{
(HLOCAL)LocalFree(pBuffer);
}
}
}
for (i = iCount - 1; i >= 0; --i)
{
if (pSIDs[i] != NULL)
{
(void*)FreeSid(pSIDs[i]);
}
}
(HLOCAL)LocalFree(pSIDs);
}
return(pSecurityDescriptor);
}
// --------------------------------------------------------------------------
// CSecurityDescriptor::AddAces
//
// Arguments: pACL = PACL to add ACEs to.
// pSIDs = Pointer to SIDs.
// iCount = Count of ACCESS_CONTROLS passed in.
// pAccessControl = Pointer to ACCESS_CONTROLS.
//
// Returns: bool
//
// Purpose: Adds access allowed ACEs to the given ACL.
//
// History: 2000-10-05 vtan created
// --------------------------------------------------------------------------
bool CSecurityDescriptor::AddAces (PACL pACL, PSID *pSIDs, int iCount, const ACCESS_CONTROL *pAC)
{
bool fResult;
int i;
for (fResult = true, i = 0; fResult && (i < iCount); ++pSIDs, ++pAC, ++i)
{
fResult = (AddAccessAllowedAce(pACL, ACL_REVISION, pAC->dwAccessMask, *pSIDs) != FALSE);
}
return(fResult);
}
// --------------------------------------------------------------------------
// CAccessControlList::CAccessControlList
//
// Arguments: <none>
//
// Returns: <none>
//
// Purpose: Initializes the CAccessControlList object.
//
// History: 1999-10-05 vtan created
// --------------------------------------------------------------------------
CAccessControlList::CAccessControlList (void) :
_pACL(NULL)
{
}
// --------------------------------------------------------------------------
// CAccessControlList::~CAccessControlList
//
// Arguments: <none>
//
// Returns: <none>
//
// Purpose: Releases resources used by the CAccessControlList object.
//
// History: 1999-10-05 vtan created
// --------------------------------------------------------------------------
CAccessControlList::~CAccessControlList (void)
{
ReleaseMemory(_pACL);
}
// --------------------------------------------------------------------------
// CAccessControlList::operator PACL
//
// Arguments: <none>
//
// Returns: PACL
//
// Purpose: If the ACL has been constructed returns that value. If not
// then the ACL is constructed from the ACEs and then returned.
//
// History: 1999-10-05 vtan created
// --------------------------------------------------------------------------
CAccessControlList::operator PACL (void)
{
PACL pACL;
if (_pACL == NULL)
{
int i;
DWORD dwACLSize, dwSizeOfAllACEs;
pACL = NULL;
dwSizeOfAllACEs = 0;
// Walk thru all the ACEs to calculate the total size
// required for the ACL.
for (i = _ACEArray.GetCount() - 1; i >= 0; --i)
{
ACCESS_ALLOWED_ACE *pACE;
pACE = static_cast<ACCESS_ALLOWED_ACE*>(_ACEArray.Get(i));
dwSizeOfAllACEs += pACE->Header.AceSize;
}
dwACLSize = sizeof(ACL) + dwSizeOfAllACEs;
_pACL = pACL = static_cast<ACL*>(LocalAlloc(LMEM_FIXED, dwACLSize));
if (pACL != NULL)
{
TBOOL(InitializeAcl(pACL, dwACLSize, ACL_REVISION));
// Construct the ACL in reverse order of the ACEs. This
// allows CAccessControlList::Add to actually insert the
// granted access at the head of the list which is usually
// the desired result. The order of the ACEs is important!
for (i = _ACEArray.GetCount() - 1; i >= 0; --i)
{
ACCESS_ALLOWED_ACE *pACE;
pACE = static_cast<ACCESS_ALLOWED_ACE*>(_ACEArray.Get(i));
TBOOL(AddAccessAllowedAceEx(pACL, ACL_REVISION, pACE->Header.AceFlags, pACE->Mask, reinterpret_cast<PSID>(&pACE->SidStart)));
}
}
}
else
{
pACL = _pACL;
}
return(pACL);
}
// --------------------------------------------------------------------------
// CAccessControlList::Add
//
// Arguments: pSID = SID to grant access to.
// dwMask = Level of access to grant.
// ucInheritence = Type of inheritence for this access.
//
// Returns: NTSTATUS
//
// Purpose: Adds the given SID, access and inheritence type as an ACE to
// the list of ACEs to build into an ACL. The ACE array is
// allocated in blocks of 16 pointers to reduce repeated calls
// to allocate memory if many ACEs are added.
//
// History: 1999-10-05 vtan created
// --------------------------------------------------------------------------
NTSTATUS CAccessControlList::Add (PSID pSID, ACCESS_MASK dwMask, UCHAR ucInheritence)
{
NTSTATUS status;
DWORD dwSIDLength, dwACESize;
ACCESS_ALLOWED_ACE *pACE;
dwSIDLength = GetLengthSid(pSID);
dwACESize = sizeof(ACCESS_ALLOWED_ACE) - sizeof(DWORD) + dwSIDLength;
pACE = static_cast<ACCESS_ALLOWED_ACE*>(LocalAlloc(LMEM_FIXED, dwACESize));
if (pACE != NULL)
{
pACE->Header.AceType = ACCESS_ALLOWED_ACE_TYPE;
pACE->Header.AceFlags = ucInheritence;
pACE->Header.AceSize = static_cast<USHORT>(dwACESize);
pACE->Mask = dwMask;
CopyMemory(&pACE->SidStart, pSID, dwSIDLength);
status = _ACEArray.Add(pACE);
if (STATUS_NO_MEMORY == status)
{
(HLOCAL)LocalFree(pACE);
}
}
else
{
status = STATUS_NO_MEMORY;
}
return(status);
}
// --------------------------------------------------------------------------
// CAccessControlList::Remove
//
// Arguments: pSID = SID to revoke access from.
//
// Returns: NTSTATUS
//
// Purpose: Removes all references to the given SID from the ACE list.
//
// History: 1999-10-05 vtan created
// --------------------------------------------------------------------------
NTSTATUS CAccessControlList::Remove (PSID pSID)
{
NTSTATUS status;
// Set up for an iteration of the array.
_searchSID = pSID;
_iFoundIndex = -1;
status = _ACEArray.Iterate(this);
while (NT_SUCCESS(status) && (_iFoundIndex >= 0))
{
// When the SIDs are found to match remove this entry.
// ALL matching SID entries are removed!
status = _ACEArray.Remove(_iFoundIndex);
if (NT_SUCCESS(status))
{
_iFoundIndex = -1;
status = _ACEArray.Iterate(this);
}
}
return(status);
}
// --------------------------------------------------------------------------
// CAccessControlList::Callback
//
// Arguments: pvData = Pointer to the array index data.
// iElementIndex = Index into the array.
//
// Returns: NTSTATUS
//
// Purpose: Callback from the CDynamicArray::Iterate function. This
// method can be used to process the array contents by index or
// by content when iterating thru the array. Return an error
// status to stop the iteration and have that value returned to
// the caller of CDynamicArray::Iterate.
//
// Converts the pointer into a pointer to an ACCESS_ALLOWED_ACE.
// The compares the SID in that ACE to the desired search SID.
// Saves the index if found.
//
// History: 1999-11-15 vtan created
// --------------------------------------------------------------------------
NTSTATUS CAccessControlList::Callback (const void *pvData, int iElementIndex)
{
const ACCESS_ALLOWED_ACE *pACE;
pACE = *reinterpret_cast<const ACCESS_ALLOWED_ACE* const*>(pvData);
if (EqualSid(reinterpret_cast<PSID>(const_cast<unsigned long*>((&pACE->SidStart))), _searchSID) != FALSE)
{
_iFoundIndex = iElementIndex;
}
return(STATUS_SUCCESS);
}
// --------------------------------------------------------------------------
// CSecuredObject::CSecuredObject
//
// Arguments: hObject = Optional HANDLE to the object to secure.
// seObjectType = Type of object specified in handle.
//
// Returns: <none>
//
// Purpose: Sets the optionally given HANDLE into the member variables.
// The HANDLE is duplicated so the caller must release their
// HANDLE.
//
// In order for this class to work the handle you pass it MUST
// have DUPLICATE access as well as READ_CONTROL and WRITE_DAC.
//
// History: 1999-10-05 vtan created
// --------------------------------------------------------------------------
CSecuredObject::CSecuredObject (HANDLE hObject, SE_OBJECT_TYPE seObjectType) :
_hObject(hObject),
_seObjectType(seObjectType)
{
}
// --------------------------------------------------------------------------
// CSecuredObject::~CSecuredObject
//
// Arguments: <none>
//
// Returns: <none>
//
// Purpose: Release our HANDLE reference.
//
// History: 1999-10-05 vtan created
// --------------------------------------------------------------------------
CSecuredObject::~CSecuredObject (void)
{
}
// --------------------------------------------------------------------------
// CSecuredObject::Allow
//
// Arguments: pSID = SID to grant access to.
// dwMask = Level of access to grant.
// ucInheritence = Type of inheritence for this access.
//
// Returns: NTSTATUS
//
// Purpose: Get the DACL for the object. Add the desired access. Set the
// DACL for the object.
//
// History: 1999-10-05 vtan created
// --------------------------------------------------------------------------
NTSTATUS CSecuredObject::Allow (PSID pSID, ACCESS_MASK dwMask, UCHAR ucInheritence) const
{
NTSTATUS status;
CAccessControlList accessControlList;
status = GetDACL(accessControlList);
if (NT_SUCCESS(status))
{
status = accessControlList.Add(pSID, dwMask, ucInheritence);
if (NT_SUCCESS(status))
{
status = SetDACL(accessControlList);
}
}
return(status);
}
// --------------------------------------------------------------------------
// CSecuredObject::Remove
//
// Arguments: pSID = SID to revoke access from.
//
// Returns: NTSTATUS
//
// Purpose: Get the DACL for the object. Remove the desired access. Set
// the DACL for the object.
//
// History: 1999-10-05 vtan created
// --------------------------------------------------------------------------
NTSTATUS CSecuredObject::Remove (PSID pSID) const
{
NTSTATUS status;
CAccessControlList accessControlList;
status = GetDACL(accessControlList);
if (NT_SUCCESS(status))
{
status = accessControlList.Remove(pSID);
if (NT_SUCCESS(status))
{
status = SetDACL(accessControlList);
}
}
return(status);
}
// --------------------------------------------------------------------------
// CSecuredObject::GetDACL
//
// Arguments: accessControlList = CAccessControlList that gets the
// decomposed DACL into ACEs.
//
// Returns: NTSTATUS
//
// Purpose: Gets the object's DACL and walk the individual ACEs and add
// this access to the CAccessControlList object given. The access
// is walked backward to allow CAccessControlList::Add to add to
// end of the list but actually add to the head of the list.
//
// History: 1999-10-05 vtan created
// --------------------------------------------------------------------------
NTSTATUS CSecuredObject::GetDACL (CAccessControlList& accessControlList) const
{
NTSTATUS status;
DWORD dwResult;
PACL pDACL;
PSECURITY_DESCRIPTOR pSD;
status = STATUS_SUCCESS;
pSD = NULL;
pDACL = NULL;
dwResult = GetSecurityInfo(_hObject,
_seObjectType,
DACL_SECURITY_INFORMATION,
NULL,
NULL,
&pDACL,
NULL,
&pSD);
if ((ERROR_SUCCESS == dwResult) && (pDACL != NULL))
{
int i;
ACCESS_ALLOWED_ACE *pAce;
for (i = pDACL->AceCount - 1; NT_SUCCESS(status) && (i >= 0); --i)
{
if (GetAce(pDACL, i, reinterpret_cast<void**>(&pAce)) != FALSE)
{
ASSERTMSG(pAce->Header.AceType == ACCESS_ALLOWED_ACE_TYPE, "Expect only access allowed ACEs in CSecuredObject::MakeIndividualACEs");
status = accessControlList.Add(reinterpret_cast<PSID>(&pAce->SidStart), pAce->Mask, pAce->Header.AceFlags);
}
}
}
ReleaseMemory(pSD);
return(status);
}
// --------------------------------------------------------------------------
// CSecuredObject::SetDACL
//
// Arguments: accessControlList = CAccessControlList that contains all
// ACEs to build into an ACL.
//
// Returns: NTSTATUS
//
// Purpose: Builds the ACL for the given ACE list and sets the DACL into
// the object handle.
//
// History: 1999-10-05 vtan created
// --------------------------------------------------------------------------
NTSTATUS CSecuredObject::SetDACL (CAccessControlList& accessControlList) const
{
NTSTATUS status;
DWORD dwResult;
dwResult = SetSecurityInfo(_hObject,
_seObjectType,
DACL_SECURITY_INFORMATION,
NULL,
NULL,
accessControlList,
NULL);
if (ERROR_SUCCESS == dwResult)
{
status = STATUS_SUCCESS;
}
else
{
status = CStatusCode::StatusCodeOfErrorCode(dwResult);
}
return(status);
}