// -------------------------------------------------------------------------- // 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: // // 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(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(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(LocalAlloc(LMEM_FIXED, sizeof(SECURITY_DESCRIPTOR) + dwACLSize)); if (pBuffer != NULL) { PSECURITY_DESCRIPTOR pSD; PACL pACL; pSD = reinterpret_cast(pBuffer); pACL = reinterpret_cast(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: // // Returns: // // Purpose: Initializes the CAccessControlList object. // // History: 1999-10-05 vtan created // -------------------------------------------------------------------------- CAccessControlList::CAccessControlList (void) : _pACL(NULL) { } // -------------------------------------------------------------------------- // CAccessControlList::~CAccessControlList // // Arguments: // // Returns: // // Purpose: Releases resources used by the CAccessControlList object. // // History: 1999-10-05 vtan created // -------------------------------------------------------------------------- CAccessControlList::~CAccessControlList (void) { ReleaseMemory(_pACL); } // -------------------------------------------------------------------------- // CAccessControlList::operator PACL // // Arguments: // // 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(_ACEArray.Get(i)); dwSizeOfAllACEs += pACE->Header.AceSize; } dwACLSize = sizeof(ACL) + dwSizeOfAllACEs; _pACL = pACL = static_cast(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(_ACEArray.Get(i)); TBOOL(AddAccessAllowedAceEx(pACL, ACL_REVISION, pACE->Header.AceFlags, pACE->Mask, reinterpret_cast(&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(LocalAlloc(LMEM_FIXED, dwACESize)); if (pACE != NULL) { pACE->Header.AceType = ACCESS_ALLOWED_ACE_TYPE; pACE->Header.AceFlags = ucInheritence; pACE->Header.AceSize = static_cast(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(pvData); if (EqualSid(reinterpret_cast(const_cast((&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: // // 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: // // Returns: // // 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(&pAce)) != FALSE) { ASSERTMSG(pAce->Header.AceType == ACCESS_ALLOWED_ACE_TYPE, "Expect only access allowed ACEs in CSecuredObject::MakeIndividualACEs"); status = accessControlList.Add(reinterpret_cast(&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); }