//+------------------------------------------------------------------------- // // 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(); }