1090 lines
36 KiB
C++
1090 lines
36 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 "CSecInfo.h"
|
|
#include "authz.h"
|
|
#include <stdio.h>
|
|
#include "sddl.h"
|
|
#include <string>
|
|
#include "utility.h"
|
|
#include <assert.h>
|
|
|
|
CSecInfo::CSecInfo() :
|
|
m_cRef(0),
|
|
m_AccessTable(g_siForumsAccess),
|
|
m_AccessTableCount(ARRAYSIZE(g_siForumsAccess)),
|
|
m_DefaultAccess(0), // full control by default
|
|
m_defaultSecurityDescriptorSddl(L"O:WDG:BAD:AI(A;CIIO;FA;;;WD)(A;;FA;;;BA)"
|
|
L"S:AI(AU;SAFACIIO;FA;;;WD)"),
|
|
m_editingResource(0),
|
|
m_resources(0),
|
|
m_dwSIFlags(0),
|
|
m_bFailedToConstruct(false)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
PWSTR* allSDs;
|
|
PCWSTR noDaclOrSacl = L"O:WDG:BA";
|
|
int parentIndex;
|
|
PSECURITY_DESCRIPTOR pSelfRelativeSD;
|
|
ULONG securityDescriptorSize = 0;
|
|
BOOL bResult;
|
|
|
|
m_resources = (PRESOURCE*)LocalAlloc(
|
|
LPTR,
|
|
NUMBER_OF_RESOURCES * sizeof(PRESOURCE*));
|
|
if ( !m_resources )
|
|
{
|
|
m_bFailedToConstruct = true;
|
|
return;
|
|
}
|
|
|
|
allSDs = (PWSTR *)LocalAlloc(LPTR, NUMBER_OF_RESOURCES * sizeof(PWSTR*));
|
|
if ( !allSDs )
|
|
{
|
|
m_bFailedToConstruct = true;
|
|
return;
|
|
}
|
|
|
|
for ( int i = 0; i < NUMBER_OF_RESOURCES; i++ )
|
|
{
|
|
hr = AllocAndCopyString(noDaclOrSacl, &allSDs[i]);
|
|
if ( !SUCCEEDED(hr) )
|
|
{
|
|
m_bFailedToConstruct = true;
|
|
return;
|
|
}
|
|
}
|
|
|
|
m_resources[CONTOSO_FORUMS] = new Resource(L"Contoso forums", FORUM, allSDs[CONTOSO_FORUMS], NONEXISTENT_OBJECT);
|
|
m_resources[SPORTS] = new Resource(L"Sports", SECTION, allSDs[SPORTS], CONTOSO_FORUMS);
|
|
m_resources[FAVORITE_TEAM] = new Resource(L"Favorite team", TOPIC, allSDs[FAVORITE_TEAM], SPORTS);
|
|
m_resources[UPCOMING_EVENTS] = new Resource(L"Upcoming events", TOPIC, allSDs[UPCOMING_EVENTS], SPORTS);
|
|
m_resources[MOVIES] = new Resource(L"Movies", SECTION, allSDs[MOVIES], CONTOSO_FORUMS);
|
|
m_resources[NEW_RELEASES] = new Resource(L"2012 releases", TOPIC, allSDs[NEW_RELEASES], MOVIES);
|
|
m_resources[CLASSICS] = new Resource(L"Classics", TOPIC, allSDs[CLASSICS], MOVIES);
|
|
m_resources[HOBBIES] = new Resource(L"Hobbies", SECTION, allSDs[HOBBIES], CONTOSO_FORUMS);
|
|
m_resources[LEARNING_TO_COOK] = new Resource(L"Learning to cook", TOPIC, allSDs[LEARNING_TO_COOK], HOBBIES);
|
|
m_resources[SNOWBOARDING] = new Resource(L"Snowboarding", TOPIC, allSDs[SNOWBOARDING], HOBBIES);
|
|
|
|
// Associate parents with their children
|
|
for ( int i = 0; i < NUMBER_OF_RESOURCES; i++ )
|
|
{
|
|
parentIndex = m_resources[i]->GetParentIndex();
|
|
if ( parentIndex != NONEXISTENT_OBJECT )
|
|
{
|
|
m_resources[parentIndex]->AddChild(i);
|
|
}
|
|
}
|
|
|
|
m_objectTypeList.Level = ACCESS_OBJECT_GUID;
|
|
m_objectTypeList.Sbz = 0;
|
|
m_objectTypeList.ObjectType = nullptr;
|
|
|
|
// Initialize to a sane value even if we won't be editing the grandparent
|
|
SetCurrentObject(0);
|
|
|
|
bResult = ConvertStringSecurityDescriptorToSecurityDescriptor(
|
|
m_defaultSecurityDescriptorSddl,
|
|
SDDL_REVISION_1,
|
|
&pSelfRelativeSD,
|
|
&securityDescriptorSize
|
|
);
|
|
if ( bResult == FALSE )
|
|
{
|
|
wprintf(L"Error calling ConvertStringSecurityDescriptorToSecurityDescriptor"
|
|
L": %d\n", GetLastError());
|
|
m_bFailedToConstruct = true;
|
|
return;
|
|
}
|
|
|
|
// Call SetSecurity on the forums root so that
|
|
// everything gets an inherited DACL.
|
|
hr = SetSecurity(DACL_SECURITY_INFORMATION, pSelfRelativeSD);
|
|
LocalFree(pSelfRelativeSD);
|
|
if ( !SUCCEEDED(hr) )
|
|
{
|
|
wprintf(L"Error calling SetSecurity: %d\n", GetLastError());
|
|
m_bFailedToConstruct = true;
|
|
return;
|
|
}
|
|
}
|
|
|
|
CSecInfo::~CSecInfo()
|
|
{
|
|
if ( m_resources != nullptr )
|
|
{
|
|
for ( int i = 0; i < NUMBER_OF_RESOURCES; i++ )
|
|
{
|
|
delete m_resources[i];
|
|
}
|
|
LocalFree(m_resources);
|
|
}
|
|
|
|
assert(m_cRef == 0);
|
|
}
|
|
|
|
/* IUnknown */
|
|
IFACEMETHODIMP_(ULONG)
|
|
CSecInfo::AddRef(void)
|
|
{
|
|
return ::InterlockedIncrement(&m_cRef);
|
|
}
|
|
|
|
/* IUnknown */
|
|
IFACEMETHODIMP_(ULONG)
|
|
CSecInfo::Release(void)
|
|
{
|
|
return ::InterlockedDecrement(&m_cRef);
|
|
}
|
|
|
|
/* IUnknown */
|
|
IFACEMETHODIMP
|
|
CSecInfo::QueryInterface(_In_ REFIID riid, _Outptr_ void ** ppv)
|
|
{
|
|
if ( !ARGUMENT_PRESENT(ppv) )
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
if (::IsEqualIID(riid, IID_IUnknown))
|
|
{
|
|
*ppv = this;
|
|
}
|
|
else if(::IsEqualIID(riid, IID_ISecurityInformation))
|
|
{
|
|
*ppv = static_cast<ISecurityInformation *>(this);
|
|
}
|
|
else if(::IsEqualIID(riid, IID_ISecurityInformation3))
|
|
{
|
|
*ppv = static_cast<ISecurityInformation3 *>(this);
|
|
}
|
|
else if( ::IsEqualIID(riid, IID_IEffectivePermission2))
|
|
{
|
|
*ppv = static_cast<IEffectivePermission2 *>(this);
|
|
}
|
|
else if( ::IsEqualIID(riid, IID_ISecurityObjectTypeInfo))
|
|
{
|
|
*ppv = static_cast<ISecurityObjectTypeInfo *>(this);
|
|
}
|
|
else
|
|
{
|
|
*ppv = nullptr;
|
|
return E_NOINTERFACE;
|
|
}
|
|
|
|
AddRef();
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CSecInfo::ConvertStringToAbsSD(
|
|
_In_ PWSTR stringSD,
|
|
_Outptr_ PSECURITY_DESCRIPTOR *ppSD)
|
|
{
|
|
BOOL bResult = 0;
|
|
DWORD errorCode = 0;
|
|
HRESULT hr = S_OK;
|
|
PSECURITY_DESCRIPTOR pSelfRelativeSD;
|
|
ULONG securityDescriptorSize = 0;
|
|
|
|
// Start by getting an SD from our string
|
|
bResult = ConvertStringSecurityDescriptorToSecurityDescriptor(
|
|
stringSD,
|
|
SDDL_REVISION_1,
|
|
&pSelfRelativeSD,
|
|
&securityDescriptorSize
|
|
);
|
|
FailGracefullyGLE(
|
|
bResult,
|
|
L"ConvertStringSecurityDescriptorToSecurityDescriptor");
|
|
|
|
hr = ConvertSecurityDescriptor(pSelfRelativeSD, ppSD);
|
|
exit_gracefully:
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CSecInfo::OrderDacl(int childIndex, PACL *ppAcl)
|
|
{
|
|
BOOL bResult;
|
|
HRESULT hr = S_OK;
|
|
DWORD errorCode = S_OK;
|
|
Resource *resourceToOrder;
|
|
PINHERITED_FROM pInheritArray;
|
|
PACL pOrderedAcl = nullptr;
|
|
ACL_SIZE_INFORMATION aclInformation;
|
|
ACL_SIZE_INFORMATION aclInformation2;
|
|
DWORD totalCount;
|
|
DWORD totalCount2;
|
|
DWORD dwSizeNeeded;
|
|
int parentIndex;
|
|
int grandparentIndex;
|
|
int numPasses;
|
|
LPVOID ace;
|
|
|
|
if ( childIndex == 0 )
|
|
{
|
|
// The base object (forums) can only have explicit ACEs since there's
|
|
// nothing above it (GetInheritSource would fail for the forums
|
|
// anyway because we return not impl)
|
|
return S_OK;
|
|
}
|
|
|
|
if ( !ARGUMENT_PRESENT(*ppAcl) )
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
resourceToOrder = m_resources[childIndex];
|
|
|
|
// This call has to be made on the child, not the thing we're currently editing
|
|
hr = GetInheritSourceHelper(
|
|
childIndex,
|
|
DACL_SECURITY_INFORMATION,
|
|
*ppAcl,
|
|
&pInheritArray);
|
|
FailGracefully(hr, L"GetInheritSourceHelper");
|
|
|
|
bResult = GetAclInformation(
|
|
*ppAcl,
|
|
&aclInformation,
|
|
sizeof(aclInformation),
|
|
AclSizeInformation
|
|
);
|
|
FailGracefullyGLE(bResult, L"GetAclInformation");
|
|
|
|
totalCount = aclInformation.AceCount;
|
|
dwSizeNeeded = aclInformation.AclBytesFree + aclInformation.AclBytesInUse;
|
|
pOrderedAcl = (PACL)LocalAlloc(LPTR, dwSizeNeeded);
|
|
if ( !pOrderedAcl )
|
|
{
|
|
wprintf(L"LocalAlloc failed.\n");
|
|
hr = E_OUTOFMEMORY;
|
|
goto exit_gracefully;
|
|
}
|
|
|
|
bResult = InitializeAcl(
|
|
pOrderedAcl,
|
|
dwSizeNeeded,
|
|
ACL_REVISION
|
|
);
|
|
FailGracefullyGLE(bResult, L"InitializeAcl");
|
|
|
|
parentIndex = resourceToOrder->GetParentIndex();
|
|
grandparentIndex =
|
|
parentIndex == NONEXISTENT_OBJECT
|
|
? NONEXISTENT_OBJECT
|
|
: m_resources[parentIndex]->GetParentIndex();
|
|
|
|
// Do two passes for each set of ACEs: explicit, parent, grandparent.
|
|
// One pass is for deny ACEs, one is for allow.
|
|
// This gives us a total of 6 passes for TOPICs (because they have a
|
|
// grandparent), 4 passes for SECTIONs (because they only have a
|
|
// parent), and 2 passes for FORUMs.
|
|
numPasses = 2;
|
|
if ( parentIndex > -1 ) numPasses += 2;
|
|
if ( grandparentIndex > -1 ) numPasses += 2;
|
|
|
|
for (int pass = 0; pass < numPasses; pass ++ )
|
|
{
|
|
for ( DWORD aceIndex = 0; aceIndex < totalCount; aceIndex ++ )
|
|
{
|
|
bResult = GetAce(
|
|
*ppAcl,
|
|
aceIndex,
|
|
&ace
|
|
);
|
|
FailGracefullyGLE(bResult, L"GetAce");
|
|
|
|
BYTE aceType = ((PACE_HEADER)ace)->AceType;
|
|
if (
|
|
// Pass 0: explicit deny ACEs
|
|
( pass == 0 &&
|
|
pInheritArray[aceIndex].GenerationGap == 0 &&
|
|
aceType == ACCESS_DENIED_ACE_TYPE ) ||
|
|
|
|
// Pass 1: explicit allow ACEs
|
|
( pass == 1 &&
|
|
pInheritArray[aceIndex].GenerationGap == 0 &&
|
|
IsAccessAllowedAce(aceType) ) ||
|
|
|
|
// Pass 2: inherited-from-parent deny ACEs
|
|
( pass == 2 &&
|
|
pInheritArray[aceIndex].GenerationGap == 1 &&
|
|
aceType == ACCESS_DENIED_ACE_TYPE ) ||
|
|
|
|
// Pass 3: inherited-from-parent allow ACEs
|
|
( pass == 3 &&
|
|
pInheritArray[aceIndex].GenerationGap == 1 &&
|
|
IsAccessAllowedAce(aceType) ) ||
|
|
|
|
// Pass 3: inherited-from-grandparent deny ACEs
|
|
( pass == 4 &&
|
|
pInheritArray[aceIndex].GenerationGap == 2 &&
|
|
aceType == ACCESS_DENIED_ACE_TYPE ) ||
|
|
|
|
// Pass 3: inherited-from-grandparent allow ACEs
|
|
( pass == 5 &&
|
|
pInheritArray[aceIndex].GenerationGap == 2 &&
|
|
IsAccessAllowedAce(aceType) )
|
|
)
|
|
{
|
|
// We COULD just use AddAce, because we're guaranteed not to
|
|
// overflow the ACL's size (since we allocated it to exactly
|
|
// the same size and we're going to add all of the ACEs)
|
|
hr = AddAceToAcl(ace, &pOrderedAcl, true);
|
|
FailGracefully(hr, L"AddAceToAcl");
|
|
}
|
|
}
|
|
}
|
|
|
|
bResult = GetAclInformation(
|
|
*ppAcl,
|
|
&aclInformation2,
|
|
sizeof(aclInformation2),
|
|
AclSizeInformation
|
|
);
|
|
FailGracefullyGLE(bResult, L"GetAclInformation");
|
|
|
|
totalCount2 = aclInformation2.AceCount;
|
|
|
|
// Sanity check: ensure that we didn't leave out any ACEs
|
|
if ( totalCount != totalCount2 )
|
|
{
|
|
wprintf(L"A different amount of ACEs exists in the ACL now. "
|
|
L"Before: %d after: %d\n", totalCount, totalCount2);
|
|
hr = E_FAIL;
|
|
goto exit_gracefully;
|
|
}
|
|
|
|
// Sanity check: ensure that the ACLs are the same size
|
|
if ( aclInformation.AclBytesFree != aclInformation2.AclBytesFree ||
|
|
aclInformation.AclBytesInUse != aclInformation2.AclBytesInUse )
|
|
{
|
|
wprintf(L"Either AclBytesFree or AclBytesInUse doesn't match up\n");
|
|
hr = E_FAIL;
|
|
goto exit_gracefully;
|
|
}
|
|
|
|
// Now point the unordered ACL to the ordered one
|
|
LocalFree(*ppAcl);
|
|
*ppAcl = pOrderedAcl;
|
|
pOrderedAcl = nullptr;
|
|
|
|
exit_gracefully:
|
|
LocalFree(pOrderedAcl);
|
|
return hr;
|
|
}
|
|
|
|
// pSD represents the security descriptor of the parent
|
|
HRESULT CSecInfo::SetSecurityOfChildren(
|
|
int parentIndex,
|
|
SECURITY_INFORMATION si,
|
|
PSECURITY_DESCRIPTOR pSD)
|
|
{
|
|
BOOL bResult = 0;
|
|
DWORD errorCode = 0;
|
|
HRESULT hr = S_OK;
|
|
BOOL bAclPresent = 0;
|
|
PACL acl = nullptr;
|
|
PACL pChildAcl = nullptr;
|
|
BOOL bAclDefaulted = 0;
|
|
Resource *parentResource = m_resources[parentIndex];
|
|
std::list<int> childIndices = parentResource->GetChildIndices();
|
|
Resource *childResource;
|
|
PSECURITY_DESCRIPTOR childAbsoluteSD;
|
|
DWORD dwRevision;
|
|
SECURITY_DESCRIPTOR_CONTROL sdControl;
|
|
DWORD dwSizeNeeded = 0;
|
|
int childIndex;
|
|
PWSTR stringSD;
|
|
ULONG stringSDLen = 0;
|
|
std::list<int>::iterator it;
|
|
|
|
for(it = childIndices.begin(); it != childIndices.end(); ++it)
|
|
{
|
|
childIndex = *it;
|
|
childResource = m_resources[childIndex];
|
|
|
|
hr = ConvertStringToAbsSD(childResource->GetSD(), &childAbsoluteSD);
|
|
FailGracefully(hr, L"ConvertStringToAbsSD");
|
|
|
|
if ( IS_FLAG_SET(si, DACL_SECURITY_INFORMATION) )
|
|
{
|
|
|
|
// First of all, if the child has the protected bit set,
|
|
// then they aren't interested in inheriting anything.
|
|
bResult = GetSecurityDescriptorControl(
|
|
childAbsoluteSD,
|
|
&sdControl,
|
|
&dwRevision
|
|
);
|
|
FailGracefullyGLE(bResult, L"GetSecurityDescriptorControl");
|
|
|
|
// No need to set the security on the children if it's a protected DACL
|
|
if ( IS_FLAG_SET(sdControl, SE_DACL_PROTECTED) )
|
|
{
|
|
return S_OK;
|
|
}
|
|
|
|
// Get the DACL of the parent
|
|
bResult = GetSecurityDescriptorDacl(
|
|
pSD,
|
|
&bAclPresent,
|
|
&acl,
|
|
&bAclDefaulted
|
|
);
|
|
FailGracefullyGLE(bResult, L"GetSecurityDescriptorDacl");
|
|
|
|
// If there was no supplied ACL, then there's nothing
|
|
// for the child to inherit
|
|
if ( !bAclPresent )
|
|
{
|
|
return S_OK;
|
|
}
|
|
|
|
// Now we know there's a DACL, but we don't know if there are
|
|
// any inheritable ACEs.
|
|
hr = GetSizeOfAllInheritableAces(acl, dwSizeNeeded);
|
|
FailGracefully(hr, L"GetSizeOfAllInheritableAces");
|
|
|
|
// At this point, we know that the parent has a DACL.
|
|
// We need to get the child's DACL too because
|
|
// we may need to delete or add entries
|
|
bResult = GetSecurityDescriptorDacl(
|
|
childAbsoluteSD,
|
|
&bAclPresent,
|
|
&pChildAcl,
|
|
&bAclDefaulted
|
|
);
|
|
FailGracefullyGLE(bResult, L"GetSecurityDescriptorDacl");
|
|
|
|
if ( pChildAcl != nullptr )
|
|
{
|
|
// Keep only the explicit ACEs. This way we don't need to do a
|
|
// differential thing and find out which inherited ACEs went
|
|
// away or which ones were added. We just wipe them all out
|
|
// and then add all the inheritable ACEs from the parent.
|
|
hr = RemoveAllInheritedAces(&pChildAcl);
|
|
FailGracefully(hr, L"RemoveAllInheritedAces");
|
|
}
|
|
|
|
// There are no inheritable ACEs, so we're done here.
|
|
if ( dwSizeNeeded == 0 )
|
|
{
|
|
return S_OK;
|
|
}
|
|
|
|
// At this point, we know that there is a parent DACL
|
|
// and that it contains inheritable entries. If the child's ACL
|
|
// is null, then we need to initialize it.
|
|
if ( pChildAcl == nullptr )
|
|
{
|
|
// We know how much space we need, so we can allocate it.
|
|
// First though, align it to a DWORD (this is necessary)
|
|
dwSizeNeeded = (dwSizeNeeded + (sizeof(DWORD) - 1)) & 0xfffffffc;
|
|
|
|
pChildAcl = (PACL)LocalAlloc(LPTR, dwSizeNeeded);
|
|
if ( pChildAcl == nullptr )
|
|
{
|
|
wprintf(L"LocalAlloc failed.\n");
|
|
hr = E_OUTOFMEMORY;
|
|
goto exit_gracefully;
|
|
}
|
|
|
|
bResult = InitializeAcl(pChildAcl, dwSizeNeeded, ACL_REVISION);
|
|
FailGracefullyGLE(bResult, L"InitializeAcl");
|
|
}
|
|
|
|
hr = AddInheritableAcesFromAcl(acl, &pChildAcl);
|
|
FailGracefully(hr, L"AddInheritableAcesFromAcl");
|
|
|
|
// Now, the ACL is fully formed, so set it on the child
|
|
hr = OrderDacl(
|
|
childIndex,
|
|
&pChildAcl
|
|
);
|
|
FailGracefully(hr, L"OrderDacl");
|
|
|
|
bResult = SetSecurityDescriptorDacl(
|
|
childAbsoluteSD,
|
|
true,
|
|
pChildAcl,
|
|
false
|
|
);
|
|
FailGracefullyGLE(bResult, L"SetSecurityDescriptorDacl");
|
|
|
|
bResult = SetSecurityDescriptorControl(
|
|
childAbsoluteSD,
|
|
SE_DACL_AUTO_INHERITED,
|
|
SE_DACL_AUTO_INHERITED
|
|
);
|
|
FailGracefullyGLE(bResult, L"SetSecurityDescriptorControl");
|
|
}
|
|
|
|
// Finally, convert the SD back to a string
|
|
bResult = ConvertSecurityDescriptorToStringSecurityDescriptor(
|
|
childAbsoluteSD,
|
|
SDDL_REVISION_1,
|
|
OWNER_SECURITY_INFORMATION |
|
|
GROUP_SECURITY_INFORMATION |
|
|
DACL_SECURITY_INFORMATION |
|
|
LABEL_SECURITY_INFORMATION |
|
|
ATTRIBUTE_SECURITY_INFORMATION |
|
|
SCOPE_SECURITY_INFORMATION,
|
|
&stringSD,
|
|
&stringSDLen
|
|
);
|
|
FailGracefullyGLE(
|
|
bResult,
|
|
L"ConvertSecurityDescriptorToStringSecurityDescriptor");
|
|
|
|
childResource->FreeSD();
|
|
childResource->SetSD(stringSD);
|
|
stringSD = nullptr;
|
|
|
|
// Now, call SetSecurityOfChildren on the child
|
|
hr = SetSecurityOfChildren(childIndex, si, childAbsoluteSD);
|
|
FailGracefully(hr, L"SetSecurityOfChildren");
|
|
}
|
|
exit_gracefully:
|
|
return hr;
|
|
}
|
|
|
|
// ISecurityInformation3
|
|
IFACEMETHODIMP
|
|
CSecInfo::GetFullResourceName(_Outptr_ LPWSTR *ppszResourceName)
|
|
{
|
|
*ppszResourceName = m_resources[m_editingResource]->GetName();
|
|
|
|
return S_OK;
|
|
}
|
|
IFACEMETHODIMP
|
|
CSecInfo::OpenElevatedEditor(_In_ HWND hWnd, _In_ SI_PAGE_TYPE uPage)
|
|
{
|
|
UNREFERENCED_PARAMETER(hWnd);
|
|
UNREFERENCED_PARAMETER(uPage);
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
// IEffectivePermission2
|
|
IFACEMETHODIMP CSecInfo::ComputeEffectivePermissionWithSecondarySecurity (THIS_
|
|
_In_ PSID pSid,
|
|
_In_opt_ PSID pDeviceSid,
|
|
_In_ PCWSTR pszServerName,
|
|
_Inout_updates_(dwSecurityObjectCount) PSECURITY_OBJECT pSecurityObjects,
|
|
_In_ DWORD dwSecurityObjectCount,
|
|
_In_opt_ PTOKEN_GROUPS pUserGroups,
|
|
_When_(pUserGroups != nullptr && *pAuthzUserGroupsOperations != AUTHZ_SID_OPERATION_REPLACE_ALL, _In_reads_(pUserGroups->GroupCount))
|
|
_In_opt_ PAUTHZ_SID_OPERATION pAuthzUserGroupsOperations,
|
|
_In_opt_ PTOKEN_GROUPS pDeviceGroups,
|
|
_When_(pDeviceGroups != nullptr && *pAuthzDeviceGroupsOperations != AUTHZ_SID_OPERATION_REPLACE_ALL, _In_reads_(pDeviceGroups->GroupCount))
|
|
_In_opt_ PAUTHZ_SID_OPERATION pAuthzDeviceGroupsOperations,
|
|
_In_opt_ PAUTHZ_SECURITY_ATTRIBUTES_INFORMATION pAuthzUserClaims,
|
|
_When_(pAuthzUserClaims != nullptr && *pAuthzUserClaimsOperations != AUTHZ_SECURITY_ATTRIBUTE_OPERATION_REPLACE_ALL, _In_reads_(pAuthzUserClaims->AttributeCount))
|
|
_In_opt_ PAUTHZ_SECURITY_ATTRIBUTE_OPERATION pAuthzUserClaimsOperations,
|
|
_In_opt_ PAUTHZ_SECURITY_ATTRIBUTES_INFORMATION pAuthzDeviceClaims,
|
|
_When_(pAuthzDeviceClaims != nullptr && *pAuthzDeviceClaimsOperations != AUTHZ_SECURITY_ATTRIBUTE_OPERATION_REPLACE_ALL, _In_reads_(pAuthzDeviceClaims->AttributeCount))
|
|
_In_opt_ PAUTHZ_SECURITY_ATTRIBUTE_OPERATION pAuthzDeviceClaimsOperations,
|
|
_Inout_updates_(dwSecurityObjectCount) PEFFPERM_RESULT_LIST pEffpermResultLists)
|
|
{
|
|
// The server name passed in should always be null
|
|
UNREFERENCED_PARAMETER(pszServerName);
|
|
|
|
BOOL bResult;
|
|
DWORD errorCode = S_OK;
|
|
HRESULT hr = S_OK;
|
|
DWORD dwFlags = 0;
|
|
|
|
// AuthZ context representing the client.
|
|
AUTHZ_CLIENT_CONTEXT_HANDLE hAuthzUserContext = nullptr;
|
|
|
|
// AuthZ context representing the device.
|
|
AUTHZ_CLIENT_CONTEXT_HANDLE hAuthzDeviceContext = nullptr;
|
|
|
|
// AuthZ context representing the combination of client and device. If no
|
|
// device SID is passed in to this function, then it only represents
|
|
// the user context.
|
|
AUTHZ_CLIENT_CONTEXT_HANDLE hAuthzCompoundContext = nullptr;
|
|
|
|
// Access request specifies the desired access mask, principal self sid,
|
|
// the object type list strucutre (if any).
|
|
AUTHZ_ACCESS_REQUEST request;
|
|
AUTHZ_AUDIT_EVENT_HANDLE hAuditEvent = nullptr;
|
|
PSECURITY_DESCRIPTOR pSD;
|
|
PSECURITY_DESCRIPTOR *pOptionalSecurityDescriptorArray = nullptr;
|
|
DWORD dwOptionalSecurityDescriptorCount = 0;
|
|
AUTHZ_ACCESS_REPLY reply;
|
|
reply.Error = nullptr;
|
|
reply.GrantedAccessMask = nullptr;
|
|
bool needToFreeAccMask = true;
|
|
PAUTHZ_ACCESS_CHECK_RESULTS_HANDLE phAccessCheckResults = nullptr;
|
|
AUTHZ_RESOURCE_MANAGER_HANDLE hAuthzResourceManager = nullptr;
|
|
|
|
bool madeResourceManager = false;
|
|
LUID identifier;
|
|
|
|
// There is no concept of shares or CAPs in this resource manager,
|
|
// so the only security object in pSecurityObjects will be
|
|
// the SD in question. The following checks aren't necessary when
|
|
// considering the sample code.
|
|
if ( dwSecurityObjectCount != 1 )
|
|
{
|
|
wprintf(L"Unexpected effective permissions argument data: "
|
|
L"dwSecurityObjectCount==%d\n", dwSecurityObjectCount);
|
|
hr = E_FAIL;
|
|
goto exit_gracefully;
|
|
}
|
|
|
|
if ( pSecurityObjects[0].Id != SECURITY_OBJECT_ID_OBJECT_SD )
|
|
{
|
|
wprintf(L"Unexpected pSecurityObjects[0].Id: %d\n", pSecurityObjects[0].Id);
|
|
hr = E_FAIL;
|
|
goto exit_gracefully;
|
|
}
|
|
|
|
if ( !ARGUMENT_PRESENT(pSid) )
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
bResult = AuthzInitializeResourceManager(
|
|
AUTHZ_RM_FLAG_NO_AUDIT,
|
|
nullptr,
|
|
nullptr,
|
|
nullptr,
|
|
L"SDK Sample Resource Manager",
|
|
&hAuthzResourceManager
|
|
);
|
|
FailGracefullyGLE(bResult, L"AuthzInitializeResourceManager");
|
|
madeResourceManager = true;
|
|
|
|
// This is never interpreted by AuthZ
|
|
identifier.HighPart = 0;
|
|
identifier.LowPart = 0;
|
|
|
|
bResult = AuthzInitializeContextFromSid(
|
|
0,
|
|
pSid, // use the SID passed in to this function
|
|
hAuthzResourceManager,
|
|
nullptr, // token will never expire (this isn't enforced anyway)
|
|
identifier, // never interpreted by authz
|
|
nullptr,
|
|
&hAuthzUserContext
|
|
);
|
|
FailGracefullyGLE(bResult, L"AuthzInitializeContextFromSid");
|
|
|
|
// Set up the different contexts
|
|
if ( pDeviceSid != nullptr )
|
|
{
|
|
bResult = AuthzInitializeContextFromSid(
|
|
0,
|
|
pDeviceSid, // use the device SID passed in to this function
|
|
hAuthzResourceManager,
|
|
nullptr, // token will never expire (this isn't enforced anyway)
|
|
identifier, // never interpreted by authz
|
|
nullptr,
|
|
&hAuthzDeviceContext
|
|
);
|
|
|
|
FailGracefullyGLE(bResult, L"AuthzInitializeContextFromSid (device)");
|
|
|
|
bResult = AuthzInitializeCompoundContext(
|
|
hAuthzUserContext,
|
|
hAuthzDeviceContext,
|
|
&hAuthzCompoundContext
|
|
);
|
|
FailGracefullyGLE(bResult, L"AuthzInitializeCompoundContext");
|
|
|
|
// Add device claims
|
|
if ( pAuthzDeviceClaims != nullptr )
|
|
{
|
|
bResult = AuthzModifyClaims(
|
|
hAuthzCompoundContext,
|
|
AuthzContextInfoDeviceClaims,
|
|
pAuthzDeviceClaimsOperations,
|
|
pAuthzDeviceClaims
|
|
);
|
|
FailGracefullyGLE(bResult, L"AuthzModifyClaims (device claims)");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hAuthzCompoundContext = hAuthzUserContext;
|
|
}
|
|
|
|
// Add user claims
|
|
if ( pAuthzUserClaims != nullptr )
|
|
{
|
|
bResult = AuthzModifyClaims(
|
|
hAuthzCompoundContext,
|
|
AuthzContextInfoUserClaims,
|
|
pAuthzUserClaimsOperations,
|
|
pAuthzUserClaims
|
|
);
|
|
FailGracefullyGLE(bResult, L"AuthzModifyClaims (user claims)");
|
|
}
|
|
|
|
// Add "what-if" device groups
|
|
if ( pDeviceGroups != nullptr )
|
|
{
|
|
bResult = AuthzModifySids(
|
|
hAuthzCompoundContext,
|
|
AuthzContextInfoDeviceSids,
|
|
pAuthzDeviceGroupsOperations,
|
|
pDeviceGroups
|
|
);
|
|
FailGracefullyGLE(bResult, L"AuthzModifySids (device groups)");
|
|
}
|
|
|
|
// Add "what-if" user groups
|
|
if ( pUserGroups != nullptr && pAuthzUserGroupsOperations != nullptr )
|
|
{
|
|
bResult = AuthzModifySids(
|
|
hAuthzCompoundContext,
|
|
AuthzContextInfoGroupsSids,
|
|
pAuthzUserGroupsOperations,
|
|
pUserGroups
|
|
);
|
|
FailGracefullyGLE(bResult, L"AuthzModifySids (user groups)");
|
|
}
|
|
|
|
pSD = (PSECURITY_DESCRIPTOR)pSecurityObjects[0].pData;
|
|
|
|
request.DesiredAccess = MAXIMUM_ALLOWED;
|
|
request.PrincipalSelfSid = nullptr;
|
|
request.ObjectTypeList = nullptr;
|
|
request.ObjectTypeListLength = 0;
|
|
request.OptionalArguments = nullptr;
|
|
|
|
reply.ResultListLength = 1;
|
|
reply.SaclEvaluationResults = nullptr;
|
|
reply.GrantedAccessMask = (PACCESS_MASK)LocalAlloc(
|
|
LPTR,
|
|
sizeof(ACCESS_MASK) * reply.ResultListLength);
|
|
if ( !reply.GrantedAccessMask )
|
|
{
|
|
wprintf(L"LocalAlloc failed.\n");
|
|
hr = E_OUTOFMEMORY;
|
|
goto exit_gracefully;
|
|
}
|
|
reply.Error = (PDWORD)LocalAlloc(
|
|
LPTR,
|
|
sizeof(DWORD) * reply.ResultListLength);
|
|
if ( !reply.Error )
|
|
{
|
|
wprintf(L"LocalAlloc failed.\n");
|
|
hr = E_OUTOFMEMORY;
|
|
goto exit_gracefully;
|
|
}
|
|
|
|
// Finally, call access check. This is the heart of this function.
|
|
bResult = AuthzAccessCheck(
|
|
dwFlags, // deep copy the SD (default)
|
|
hAuthzCompoundContext,
|
|
&request,
|
|
hAuditEvent,
|
|
pSD,
|
|
pOptionalSecurityDescriptorArray,
|
|
dwOptionalSecurityDescriptorCount,
|
|
&reply,
|
|
phAccessCheckResults
|
|
);
|
|
|
|
FailGracefullyGLE(bResult, L"AuthzAccessCheck");
|
|
needToFreeAccMask = false;
|
|
|
|
// Only one security object is passed into this function
|
|
// (the object's SD), and we ensured that at the beginning.
|
|
pEffpermResultLists[0].fEvaluated = true;
|
|
pEffpermResultLists[0].pGrantedAccessList = reply.GrantedAccessMask;
|
|
|
|
// We don't support object ACEs, so cObjectTypeListLength has to be 1
|
|
// pObjectTypeList has to be the null GUID, and pGrantedAccessList
|
|
// should be a single DWORD
|
|
pEffpermResultLists[0].cObjectTypeListLength = 1;
|
|
|
|
pEffpermResultLists[0].pObjectTypeList = &m_objectTypeList;
|
|
|
|
exit_gracefully:
|
|
|
|
if ( madeResourceManager )
|
|
{
|
|
AuthzFreeResourceManager(hAuthzResourceManager);
|
|
}
|
|
|
|
if ( phAccessCheckResults != nullptr )
|
|
{
|
|
AuthzFreeHandle(*phAccessCheckResults);
|
|
phAccessCheckResults = nullptr;
|
|
}
|
|
|
|
if (hAuthzUserContext != nullptr)
|
|
{
|
|
AuthzFreeContext(hAuthzUserContext);
|
|
hAuthzUserContext = nullptr;
|
|
}
|
|
|
|
if (hAuthzDeviceContext != nullptr)
|
|
{
|
|
AuthzFreeContext(hAuthzDeviceContext);
|
|
hAuthzDeviceContext = nullptr;
|
|
|
|
if (hAuthzCompoundContext != nullptr)
|
|
{
|
|
AuthzFreeContext(hAuthzCompoundContext);
|
|
hAuthzCompoundContext = nullptr;
|
|
}
|
|
}
|
|
|
|
LocalFree(reply.Error);
|
|
|
|
if ( needToFreeAccMask )
|
|
{
|
|
LocalFree(reply.GrantedAccessMask);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
// ISecurityObjectTypeInfo
|
|
STDMETHODIMP
|
|
CSecInfo::GetInheritSource(
|
|
SECURITY_INFORMATION si,
|
|
PACL acl,
|
|
PINHERITED_FROM *ppInheritArray)
|
|
{
|
|
return GetInheritSourceHelper(m_editingResource, si, acl, ppInheritArray);
|
|
}
|
|
|
|
HRESULT CSecInfo::GetInheritSourceHelper(
|
|
int childIndex,
|
|
SECURITY_INFORMATION si,
|
|
PACL acl,
|
|
PINHERITED_FROM *ppInheritArray)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
BOOL bResult = FALSE;
|
|
DWORD errorCode = S_OK;
|
|
ACL_SIZE_INFORMATION aclInformation;
|
|
Resource *childResource = m_resources[childIndex];
|
|
Resource *parentResource = nullptr;
|
|
int indexOfParent = childResource->GetParentIndex();
|
|
DWORD totalCount;
|
|
LPVOID ace;
|
|
BYTE aceFlags;
|
|
BOOL alreadyExists = FALSE;
|
|
PSECURITY_DESCRIPTOR pParentSD;
|
|
int grandparentIndex;
|
|
PSECURITY_DESCRIPTOR pGrandparentSD;
|
|
Resource *grandparentResource;
|
|
ULONG defaultSecurityDescriptorSize = 0;
|
|
BOOL bDaclPresent = 0;
|
|
BOOL bDaclDefaulted = 0;
|
|
PACL pGrandparentDacl = nullptr;
|
|
PACL pParentDacl = nullptr;
|
|
|
|
if ( !ARGUMENT_PRESENT(acl) )
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
// If there's no parent, just return E_NOTIMPL
|
|
if ( indexOfParent == NONEXISTENT_OBJECT )
|
|
{
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
*ppInheritArray = (PINHERITED_FROM)LocalAlloc(
|
|
LPTR,
|
|
acl->AceCount * sizeof(INHERITED_FROM) );
|
|
if ( !(*ppInheritArray) )
|
|
{
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
// Iterate over all of the ACEs.
|
|
bResult = GetAclInformation(
|
|
acl,
|
|
&aclInformation,
|
|
sizeof(aclInformation),
|
|
AclSizeInformation
|
|
);
|
|
FailGracefullyGLE(bResult, L"GetAclInformation");
|
|
|
|
totalCount = aclInformation.AceCount;
|
|
|
|
for ( DWORD aceIndex = 0; aceIndex < totalCount; aceIndex ++ )
|
|
{
|
|
bResult = GetAce(
|
|
acl,
|
|
aceIndex,
|
|
&ace
|
|
);
|
|
FailGracefullyGLE(bResult, L"GetAce");
|
|
|
|
aceFlags = ((PACE_HEADER)ace)->AceFlags;
|
|
|
|
if ( indexOfParent != NONEXISTENT_OBJECT )
|
|
{
|
|
parentResource = m_resources[indexOfParent];
|
|
}
|
|
|
|
// If we're a SECTION, then it could only have come from the FORUM
|
|
// (assuming it wasn't orphaned). If we're a TOPIC, then it could
|
|
// either be the parent SECTION or the grandparent FORUM.
|
|
if (IS_FLAG_SET(aceFlags,INHERITED_ACE))
|
|
{
|
|
if ( childResource->GetType() == SECTION )
|
|
{
|
|
// can't just assume that this ACE will be on the parent...
|
|
// need to check it; it may be an orphan
|
|
bResult = ConvertStringSecurityDescriptorToSecurityDescriptor(
|
|
parentResource->GetSD(),
|
|
SDDL_REVISION_1,
|
|
&pParentSD,
|
|
&defaultSecurityDescriptorSize);
|
|
FailGracefullyGLE(
|
|
bResult,
|
|
L"ConvertStringSecurityDescriptorToSecurityDescriptor");
|
|
|
|
alreadyExists = FALSE;
|
|
|
|
if ( IS_FLAG_SET(si, DACL_SECURITY_INFORMATION) )
|
|
{
|
|
bResult = GetSecurityDescriptorDacl(
|
|
pParentSD,
|
|
&bDaclPresent,
|
|
&pParentDacl,
|
|
&bDaclDefaulted
|
|
);
|
|
FailGracefullyGLE(bResult, L"GetSecurityDescriptorDacl");
|
|
|
|
hr = ACEAlreadyInACL(
|
|
pParentDacl,
|
|
ace,
|
|
&alreadyExists,
|
|
true
|
|
);
|
|
FailGracefully(hr, L"ACEAlreadyInACL");
|
|
}
|
|
|
|
if ( alreadyExists != FALSE )
|
|
{
|
|
(*ppInheritArray)[aceIndex].GenerationGap = 1;
|
|
(*ppInheritArray)[aceIndex].AncestorName = parentResource->GetName();
|
|
}
|
|
else
|
|
{
|
|
(*ppInheritArray)[aceIndex].GenerationGap = 0;
|
|
(*ppInheritArray)[aceIndex].AncestorName = nullptr;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// We're a TOPIC. Check to see if this ACE is in the parent.
|
|
// If it isn't, then we check the grandparent.
|
|
// We can't skip that check because it may be possible that
|
|
// this ACE was orphaned, so it may not exist on any ancestor
|
|
grandparentIndex = parentResource->GetParentIndex();
|
|
PSECURITY_DESCRIPTOR pParentSD;
|
|
ULONG defaultSecurityDescriptorSize = 0;
|
|
bResult = ConvertStringSecurityDescriptorToSecurityDescriptor(
|
|
parentResource->GetSD(),
|
|
SDDL_REVISION_1,
|
|
&pParentSD,
|
|
&defaultSecurityDescriptorSize);
|
|
FailGracefullyGLE(
|
|
bResult,
|
|
L"ConvertStringSecurityDescriptorToSecurityDescriptor");
|
|
|
|
if ( IS_FLAG_SET(si, DACL_SECURITY_INFORMATION) )
|
|
{
|
|
bResult = GetSecurityDescriptorDacl(
|
|
pParentSD,
|
|
&bDaclPresent,
|
|
&pParentDacl,
|
|
&bDaclDefaulted
|
|
);
|
|
FailGracefullyGLE(bResult, L"GetSecurityDescriptorDacl");
|
|
|
|
hr = ACEAlreadyInACL(
|
|
pParentDacl,
|
|
ace,
|
|
&alreadyExists,
|
|
true
|
|
);
|
|
FailGracefully(hr, L"ACEAlreadyInACL");
|
|
}
|
|
|
|
|
|
if ( alreadyExists != FALSE )
|
|
{
|
|
(*ppInheritArray)[aceIndex].GenerationGap = 1;
|
|
(*ppInheritArray)[aceIndex].AncestorName = parentResource->GetName();
|
|
}
|
|
else
|
|
{
|
|
// The parent didn't have it, so check the grandparent.
|
|
grandparentResource = m_resources[grandparentIndex];
|
|
defaultSecurityDescriptorSize = 0;
|
|
bResult = ConvertStringSecurityDescriptorToSecurityDescriptor(
|
|
grandparentResource->GetSD(),
|
|
SDDL_REVISION_1,
|
|
&pGrandparentSD,
|
|
&defaultSecurityDescriptorSize);
|
|
FailGracefullyGLE(
|
|
bResult,
|
|
L"ConvertStringSecurityDescriptorToSecurityDescriptor");
|
|
|
|
alreadyExists = FALSE;
|
|
if ( IS_FLAG_SET(si, DACL_SECURITY_INFORMATION) )
|
|
{
|
|
bResult = GetSecurityDescriptorDacl(
|
|
pGrandparentSD,
|
|
&bDaclPresent,
|
|
&pGrandparentDacl,
|
|
&bDaclDefaulted
|
|
);
|
|
FailGracefullyGLE(bResult, L"GetSecurityDescriptorDacl");
|
|
|
|
hr = ACEAlreadyInACL(
|
|
pGrandparentDacl,
|
|
ace,
|
|
&alreadyExists,
|
|
true
|
|
);
|
|
}
|
|
|
|
if ( alreadyExists != FALSE )
|
|
{
|
|
// It came from the grandparent
|
|
(*ppInheritArray)[aceIndex].GenerationGap = 2;
|
|
(*ppInheritArray)[aceIndex].AncestorName = grandparentResource->GetName();
|
|
}
|
|
else
|
|
{
|
|
// This ACE did not come from a [grand]parent
|
|
(*ppInheritArray)[aceIndex].GenerationGap = 0;
|
|
(*ppInheritArray)[aceIndex].AncestorName = nullptr;
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// This ACE did not come from a [grand]parent
|
|
(*ppInheritArray)[aceIndex].GenerationGap = 0;
|
|
(*ppInheritArray)[aceIndex].AncestorName = nullptr;
|
|
}
|
|
}
|
|
exit_gracefully:
|
|
|
|
return hr;
|
|
}
|