You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2286 lines
79 KiB
2286 lines
79 KiB
//+-------------------------------------------------------------------
|
|
//
|
|
// Microsoft Windows
|
|
// Copyright (C) Microsoft Corporation, 1992 - 1995.
|
|
//
|
|
// File: cacl.cxx
|
|
//
|
|
// Contents: class providing merging of an access list and an ACL
|
|
//
|
|
// Classes: CAclBuilder
|
|
//
|
|
// History: Feb-94 Created DaveMont
|
|
//
|
|
//--------------------------------------------------------------------
|
|
#include <aclpch.hxx>
|
|
#pragma hdrstop
|
|
|
|
#define NAME_NOT_FOUND L"Name Not Found"
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
// This is rather an odd object. It's output is either an ACL or a list
|
|
// (array) of AccessEntries. As input it takes an (optional) ACL, and
|
|
// (optionally) multiple lists of AccessEntries. The object maintains the
|
|
// SID, user name, mask, etc. in AccountAccess object.
|
|
//
|
|
// The following 3 methods are used to input access information:
|
|
//
|
|
// SetAcl -- can only be called once
|
|
// ClearAll -- used when a call to replace all access rights is made
|
|
// AddAccessEntries -- adds a list of access entries to the object
|
|
//
|
|
// The following method generates an ACL from the information within the object:
|
|
//
|
|
// GetAcl -- allocates and generates a new ACL
|
|
//
|
|
// The following method generates a list of access entries from the information
|
|
// within the object. This list will match an ACL generated by GetAcl
|
|
//
|
|
// GetAccessEntries -- allocates and generates a list of AccessEntries
|
|
//
|
|
// The following method calculates the effective rights for a trustee based
|
|
// on the access control information within the object (but not on privileges
|
|
// the trustee may have):
|
|
//
|
|
// GetEffectiveRights-- returns the effective access rights for trustee
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: ctor, public
|
|
//
|
|
// Synopsis: initializes class member variables
|
|
//
|
|
// Arguments: IN - [system] - the machine where the object is
|
|
// IN - [fSaveNamesAndSids] - TRUE if this object is to be transacted
|
|
// IN - [fUsedByProviderIndependentApi] - if TRUE, SYNCHRONIZE and
|
|
// READ_CONTROL are appended to any allowed masks, thus allowing
|
|
// PROV_OBJECT_READ, ...WRITE and ...EXECUTE to be non-overlapping.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
CAcl::CAcl(LPWSTR system,
|
|
IS_CONTAINER fdir,
|
|
BOOL fSaveNamesAndSids,
|
|
BOOL fUsedByProviderIndependentApi)
|
|
:_fused_by_provider_independent_api(fUsedByProviderIndependentApi),
|
|
_aclrevision(ACL_REVISION2),
|
|
_capabilities(0),
|
|
_pcaaacl(NULL),
|
|
_pcaaaes(NULL),
|
|
_pcaaaclindex(0),
|
|
_pcaaaesindex(0),
|
|
_pcacli(NULL),
|
|
_pcaeli(NULL),
|
|
_system(system),
|
|
_fdir(fdir),
|
|
_fsave_names_and_sids(fSaveNamesAndSids)
|
|
{
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: dtor, public
|
|
//
|
|
// Synopsis: frees class member variables
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
CAcl::~CAcl()
|
|
{
|
|
//
|
|
// have to delete the individual account accesses
|
|
//
|
|
if (_pcaaacl)
|
|
{
|
|
for (ULONG idx = 0; idx < _pcaaaclindex; idx++ )
|
|
{
|
|
delete _pcaaacl[idx];
|
|
}
|
|
AccFree(_pcaaacl);
|
|
}
|
|
if (_pcaaaes)
|
|
{
|
|
for (ULONG idx = 0; idx < _pcaaaesindex; idx++ )
|
|
{
|
|
delete _pcaaaes[idx];
|
|
}
|
|
AccFree(_pcaaaes);
|
|
}
|
|
if (_pcacli)
|
|
{
|
|
delete _pcacli;
|
|
}
|
|
if (_pcaeli)
|
|
{
|
|
delete _pcaeli;
|
|
}
|
|
}
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: new, public
|
|
//
|
|
// Synopsis:
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
void * CAcl::operator new(size_t size)
|
|
{
|
|
return(RtlAllocateHeap(RtlProcessHeap(), HEAP_ZERO_MEMORY, (ULONG)size));
|
|
}
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: delete, public
|
|
//
|
|
// Synopsis:
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
void CAcl::operator delete(void *p, size_t size)
|
|
{
|
|
RtlFreeHeap(RtlProcessHeap(), 0, p);
|
|
}
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: SetAcl, public
|
|
//
|
|
// Synopsis: Takes an input ACL and converts it into AccountAccess objects.
|
|
//
|
|
// Arguments: IN - [pacl] - the acl from the object
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
DWORD CAcl::SetAcl(PACL pacl)
|
|
{
|
|
DWORD status;
|
|
|
|
//
|
|
// new the iterators if required
|
|
//
|
|
status = _InitIterators();
|
|
if (status != NO_ERROR)
|
|
{
|
|
return(status);
|
|
}
|
|
//
|
|
// initialize the ACL, if it exists
|
|
//
|
|
_pcacli->Init(pacl);
|
|
|
|
//
|
|
// allocate space for the ACL's array of account accesses
|
|
// (if there are any ACEs)
|
|
//
|
|
|
|
if (_pcacli->NumberEntries() > 0)
|
|
{
|
|
//
|
|
// allocate for the account accesses for the acl
|
|
//
|
|
if ( NULL != (_pcaaacl = (CAccountAccess **)AccAlloc(
|
|
(_pcacli->NumberEntries() )* sizeof(void *))))
|
|
{
|
|
//
|
|
// initialize the ACLs AccountAccesses (old AAs)
|
|
//
|
|
|
|
for (_pcacli->FirstAce(); _pcacli->MoreAces(); _pcacli->NextAce() )
|
|
{
|
|
status = _AddEntry( _pcacli,
|
|
&(_pcaaacl[_pcaaaclindex]),
|
|
&_pcaaaclindex );
|
|
if (status != NO_ERROR)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
} else
|
|
{
|
|
status = ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
//
|
|
// all done with the acl iterator now, clear it.
|
|
//
|
|
_pcacli->Init(NULL);
|
|
}
|
|
return(status);
|
|
}
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: ClearAll, public
|
|
//
|
|
// Synopsis: clears the account accesses for the ACL and the access entries
|
|
// (preparatory to doing a replace all API call)
|
|
//
|
|
// Arguments: none
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
DWORD CAcl::ClearAll()
|
|
{
|
|
DWORD status;
|
|
|
|
//
|
|
// new the iterators if required
|
|
//
|
|
status = _InitIterators();
|
|
if (status != NO_ERROR)
|
|
{
|
|
return(status);
|
|
}
|
|
//
|
|
// clear out the acl, if it exists
|
|
//
|
|
_pcacli->Init(NULL);
|
|
|
|
if (_pcaaacl)
|
|
{
|
|
for (ULONG idx = 0; idx < _pcaaaclindex; idx++ )
|
|
{
|
|
delete _pcaaacl[idx];
|
|
}
|
|
AccFree(_pcaaacl);
|
|
_pcaaacl = NULL;
|
|
_pcaaaclindex = 0;
|
|
}
|
|
status = ClearAccessEntries();
|
|
|
|
return(status);
|
|
}
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: ClearAccessEntries, public
|
|
//
|
|
// Synopsis: clears the access entries AccountAccess objects. (revert)
|
|
//
|
|
// Arguments: none
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
DWORD CAcl::ClearAccessEntries()
|
|
{
|
|
if (_pcaaaes)
|
|
{
|
|
for (ULONG idx = 0; idx < _pcaaaesindex; idx++ )
|
|
{
|
|
delete _pcaaaes[idx];
|
|
}
|
|
AccFree(_pcaaaes);
|
|
_pcaaaes = NULL;
|
|
_pcaaaesindex = 0;
|
|
}
|
|
return(NO_ERROR);
|
|
}
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: AddAccessEntries, public
|
|
//
|
|
// Synopsis: converts the access entries and ACL into AccountAccess objects,
|
|
//
|
|
// Arguments: IN - [ccount] - the number of access entries
|
|
// IN - [pae] - the array of access entries
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
DWORD CAcl::AddAccessEntries( ULONG ccount,
|
|
PACCESS_ENTRY pae)
|
|
{
|
|
|
|
DWORD status = NO_ERROR;
|
|
|
|
//
|
|
// new the iterators if required
|
|
//
|
|
status = _InitIterators();
|
|
if (status != NO_ERROR)
|
|
{
|
|
return(status);
|
|
}
|
|
//
|
|
// initialize the iterator thru the access entries
|
|
//
|
|
_pcaeli->Init(ccount, pae);
|
|
|
|
//
|
|
// do something if there are any access entries
|
|
//
|
|
if (_pcaeli->NumberEntries() > 0)
|
|
{
|
|
//
|
|
// if we have already processed some access entries into
|
|
// AccountAccesses, then we need to make room for more
|
|
// ie. do a re-alloc
|
|
//
|
|
if (_pcaaaes != NULL)
|
|
{
|
|
CAccountAccess **pcaaaestmp;
|
|
|
|
if (NULL != (pcaaaestmp = (CAccountAccess **)AccAlloc(
|
|
(_pcaaaesindex +
|
|
_pcaeli->NumberEntries() )* sizeof(void *)) ))
|
|
{
|
|
//
|
|
// after allocating enough space for the old and new access
|
|
// entries, move the old AccountAccesses into the new space,
|
|
//
|
|
for (ULONG aeindex = 0; aeindex < _pcaaaesindex; aeindex++)
|
|
{
|
|
status = _pcaaaes[aeindex]->Clone(&(pcaaaestmp[aeindex]));
|
|
if (status != NO_ERROR)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
//
|
|
// free the old list and its contents (AccountAccesses)
|
|
//
|
|
if (status == NO_ERROR)
|
|
{
|
|
for (ULONG idx = 0; idx < _pcaaaesindex; idx++ )
|
|
{
|
|
delete _pcaaaes[idx];
|
|
}
|
|
AccFree(_pcaaaes);
|
|
_pcaaaes = pcaaaestmp;
|
|
} else
|
|
//
|
|
// if something failed, free the new list and what ever contents
|
|
// got initialized
|
|
//
|
|
{
|
|
for (ULONG idx = 0; idx < aeindex; idx++ )
|
|
{
|
|
delete pcaaaestmp[idx];
|
|
}
|
|
AccFree(pcaaaestmp);
|
|
}
|
|
}else
|
|
{
|
|
status = ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
} else
|
|
{
|
|
//
|
|
// allocate memory for the account accesses for the access entries
|
|
//
|
|
if (NULL == (_pcaaaes = (CAccountAccess **)AccAlloc(
|
|
(_pcaeli->NumberEntries() )* sizeof(void *)) ))
|
|
{
|
|
status = ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
|
|
}
|
|
if (status == NO_ERROR)
|
|
{
|
|
//
|
|
// save the number of old AccountAccesses so any that are for the
|
|
// same trustee as a new access entry can be overridden (marked
|
|
// unused).
|
|
//
|
|
ULONG countold = _pcaaaesindex;
|
|
//
|
|
// create AccountAccesses for the new access entries
|
|
//
|
|
for (_pcaeli->FirstAe(); _pcaeli->MoreAes(); _pcaeli->NextAe() )
|
|
{
|
|
status = _AddEntry( _pcaeli,
|
|
&(_pcaaaes[_pcaaaesindex]),
|
|
&_pcaaaesindex );
|
|
if (status != NO_ERROR)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
} else
|
|
{
|
|
status = ERROR_INVALID_PARAMETER;
|
|
}
|
|
return(status);
|
|
}
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: BuildAcl, public
|
|
//
|
|
// Synopsis: converts AccountAccess objects into ACEs and builds an ACL
|
|
// this is a two pass operation, the first pass calculates
|
|
// the size required for the ACL, and resolves any conficts
|
|
// between the ACL and access entries. The second pass
|
|
// builds the ACL.
|
|
//
|
|
// Arguments: OUT - [pacl] - the returned, built acl
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
DWORD CAcl::BuildAcl(PACL *pacl)
|
|
{
|
|
DWORD status = NO_ERROR;
|
|
NTSTATUS ntstatus;
|
|
ULONG cacl_size, cace_count;
|
|
ULONG aceindex = 0;
|
|
ULONG newidx, oldidx;
|
|
|
|
//
|
|
// pass1 calculates the size and number of entries for an ACL or list of
|
|
// access entries based on the current contents of this class
|
|
//
|
|
status = _Pass1(&cacl_size, &cace_count, TRUE); // TRUE = build ACL
|
|
|
|
//
|
|
// now for pass2, building the ACL. The first thing to do is to
|
|
// allocate space for the ACL, then add any access entries denies
|
|
// then add any ACL denies, then add any access entries allows,
|
|
// then add any ACL allows. If any ACL denies are found after the
|
|
// first ACL allow, an error is returned. In this case the caller must
|
|
// use a replace all option to clear out the old ACL.
|
|
//
|
|
if (status == NO_ERROR)
|
|
{
|
|
*pacl = (PACL)AccAlloc( cacl_size);
|
|
if (*pacl == NULL)
|
|
{
|
|
status = ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
}
|
|
|
|
if (status == NO_ERROR)
|
|
{
|
|
ntstatus = RtlCreateAcl( *pacl,
|
|
cacl_size,
|
|
ACL_REVISION2 );
|
|
if (!NT_SUCCESS(ntstatus))
|
|
{
|
|
status = RtlNtStatusToDosError(ntstatus);
|
|
}
|
|
}
|
|
|
|
if (status == NO_ERROR)
|
|
{
|
|
//
|
|
// Add any access entries denies
|
|
//
|
|
for (ULONG newidx = 0; newidx < _pcaaaesindex ; newidx++ )
|
|
{
|
|
if (_pcaaaes[newidx]->AccessMode() == DENY_ACCESS)
|
|
{
|
|
if (_pcaaaes[newidx]->MultipleTrusteeOperation() ==
|
|
TRUSTEE_IS_IMPERSONATE)
|
|
{
|
|
status = ERROR_INVALID_PARAMETER;
|
|
break;
|
|
}
|
|
if (!NT_SUCCESS(ntstatus = RtlAddAccessDeniedAce(*pacl,
|
|
ACL_REVISION2,
|
|
_pcaaaes[newidx]->AccessMask(),
|
|
_pcaaaes[newidx]->Sid())))
|
|
{
|
|
status = RtlNtStatusToDosError(ntstatus);
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// set the inheritance
|
|
//
|
|
status = _SetAceFlags( aceindex,
|
|
*pacl,
|
|
_pcaaaes[newidx] );
|
|
if (status != NO_ERROR)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
//
|
|
// increment the count of added aces (so can set any
|
|
// inheritance)
|
|
//
|
|
aceindex++;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (status == NO_ERROR)
|
|
{
|
|
//
|
|
// loop thru the ACL AccountAccesses until the first allow
|
|
//
|
|
for (oldidx = 0; oldidx < _pcaaaclindex ; oldidx++ )
|
|
{
|
|
//
|
|
// add any denys found
|
|
//
|
|
if (_pcaaacl[oldidx]->AccessMode() == DENY_ACCESS)
|
|
{
|
|
if (!NT_SUCCESS(ntstatus = RtlAddAccessDeniedAce(
|
|
*pacl,
|
|
ACL_REVISION2,
|
|
_pcaaacl[oldidx]->AccessMask(),
|
|
_pcaaacl[oldidx]->Sid())))
|
|
{
|
|
status = RtlNtStatusToDosError(ntstatus);
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// set the inheritance
|
|
//
|
|
status = _SetAceFlags( aceindex,
|
|
*pacl,
|
|
_pcaaacl[oldidx] );
|
|
if (status != NO_ERROR)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
//
|
|
// increment the count of added aces (so can set any
|
|
// inheritance)
|
|
//
|
|
aceindex++;
|
|
|
|
//
|
|
// accessmode will be set to SE_AUDIT_BOTH in the case
|
|
// where some other utility has put an an ACE to
|
|
// audit both success and failure; this dll will
|
|
// insert two aces when a request is made to set
|
|
// both success and failure auditing
|
|
//
|
|
}
|
|
else if ( (_pcaaacl[oldidx]->AccessMode() ==
|
|
SET_ACCESS) ||
|
|
(_pcaaacl[oldidx]->AccessMode() ==
|
|
SET_AUDIT_SUCCESS) ||
|
|
(_pcaaacl[oldidx]->AccessMode() ==
|
|
SET_AUDIT_FAILURE) ||
|
|
(_pcaaacl[oldidx]->AccessMode() ==
|
|
SE_AUDIT_BOTH))
|
|
{
|
|
//
|
|
// break at the first allowed ACE
|
|
//
|
|
break;
|
|
}
|
|
} // for
|
|
}
|
|
|
|
if (status == NO_ERROR)
|
|
{
|
|
//
|
|
// loop thru the access entries AccountAccesses
|
|
//
|
|
for (newidx = 0; newidx < _pcaaaesindex ; newidx++ )
|
|
{
|
|
//
|
|
// add any grants or sets found
|
|
//
|
|
if ( (_pcaaaes[newidx]->AccessMode() ==
|
|
SET_ACCESS) ||
|
|
(_pcaaaes[newidx]->AccessMode() ==
|
|
GRANT_ACCESS) )
|
|
{
|
|
if (_pcaaaes[newidx]->MultipleTrusteeOperation() ==
|
|
TRUSTEE_IS_IMPERSONATE)
|
|
{
|
|
//
|
|
// note that no mask translations additions are
|
|
// done on compound ace masks
|
|
//
|
|
if (!NT_SUCCESS(ntstatus = RtlAddCompoundAce(
|
|
*pacl,
|
|
ACL_REVISION3,
|
|
COMPOUND_ACE_IMPERSONATION,
|
|
_pcaaaes[newidx]->AccessMask(),
|
|
_pcaaaes[newidx]->ImpersonateSid(),
|
|
_pcaaaes[newidx]->Sid())))
|
|
{
|
|
status = RtlNtStatusToDosError(ntstatus);
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!NT_SUCCESS(ntstatus = RtlAddAccessAllowedAce(
|
|
*pacl,
|
|
ACL_REVISION2,
|
|
_pcaaaes[newidx]->AccessMask(),
|
|
_pcaaaes[newidx]->Sid())))
|
|
{
|
|
status = RtlNtStatusToDosError(ntstatus);
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// set the inheritance
|
|
//
|
|
status = _SetAceFlags( aceindex,
|
|
*pacl,
|
|
_pcaaaes[newidx] );
|
|
|
|
if (status != NO_ERROR)
|
|
{
|
|
break;
|
|
}
|
|
//
|
|
// increment the count of added aces (so can set
|
|
// any inheritance)
|
|
//
|
|
aceindex++;
|
|
}
|
|
|
|
//
|
|
// else add any audit entries
|
|
//
|
|
else if ( (_pcaaaes[newidx]->AccessMode() ==
|
|
SET_AUDIT_SUCCESS) ||
|
|
(_pcaaaes[newidx]->AccessMode() ==
|
|
SET_AUDIT_FAILURE) )
|
|
{
|
|
//
|
|
// allow synchronize and read control if
|
|
// used by provider independent API
|
|
//
|
|
ACCESS_MASK tmpmask;
|
|
|
|
if (_fused_by_provider_independent_api)
|
|
{
|
|
tmpmask = _pcaaaes[newidx]->AccessMask() |
|
|
SYNCHRONIZE | READ_CONTROL;
|
|
} else
|
|
{
|
|
tmpmask = _pcaaaes[newidx]->AccessMask();
|
|
}
|
|
|
|
if (!NT_SUCCESS(ntstatus = RtlAddAuditAccessAce(
|
|
*pacl,
|
|
ACL_REVISION2,
|
|
tmpmask,
|
|
_pcaaaes[newidx]->Sid(),
|
|
(_pcaaaes[newidx]->AccessMode() ==
|
|
SET_AUDIT_SUCCESS),
|
|
(_pcaaaes[newidx]->AccessMode() ==
|
|
SET_AUDIT_FAILURE))))
|
|
{
|
|
status = RtlNtStatusToDosError(ntstatus);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// set the inheritance
|
|
//
|
|
status = _SetAceFlags( aceindex,
|
|
*pacl,
|
|
_pcaaaes[newidx] );
|
|
}
|
|
|
|
if (status != NO_ERROR)
|
|
{
|
|
break;
|
|
}
|
|
|
|
//
|
|
// increment the count of added aces (so can set
|
|
// any inheritance)
|
|
//
|
|
aceindex++;
|
|
}
|
|
|
|
} // for
|
|
}
|
|
|
|
if (status == NO_ERROR)
|
|
{
|
|
//
|
|
// loop thru the rest of the ACLs AccountAccesses
|
|
//
|
|
for (; oldidx < _pcaaaclindex ; oldidx++ )
|
|
{
|
|
//
|
|
// add any sets found, error if any denys found
|
|
//
|
|
if (_pcaaacl[oldidx]->AccessMode() == SET_ACCESS)
|
|
{
|
|
if (_pcaaacl[oldidx]->MultipleTrusteeOperation() ==
|
|
TRUSTEE_IS_IMPERSONATE)
|
|
{
|
|
//
|
|
// add any impersonate aces
|
|
//
|
|
if (!NT_SUCCESS(ntstatus = RtlAddCompoundAce( *pacl,
|
|
ACL_REVISION3,
|
|
COMPOUND_ACE_IMPERSONATION,
|
|
_pcaaacl[oldidx]->AccessMask(),
|
|
_pcaaacl[oldidx]->ImpersonateSid(),
|
|
_pcaaacl[oldidx]->Sid())))
|
|
{
|
|
status = RtlNtStatusToDosError(ntstatus);
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// add an access allowed ACE
|
|
//
|
|
if (!NT_SUCCESS(ntstatus = RtlAddAccessAllowedAce(
|
|
*pacl,
|
|
ACL_REVISION2,
|
|
_pcaaacl[oldidx]->AccessMask(),
|
|
_pcaaacl[oldidx]->Sid())))
|
|
{
|
|
status = RtlNtStatusToDosError(ntstatus);
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// set the inheritance
|
|
//
|
|
status = _SetAceFlags( aceindex,
|
|
*pacl,
|
|
_pcaaacl[oldidx] );
|
|
if (status != NO_ERROR)
|
|
{
|
|
break;
|
|
}
|
|
|
|
//
|
|
// increment the count of added aces (so can
|
|
// set any inheritance)
|
|
//
|
|
aceindex++;
|
|
|
|
}
|
|
//
|
|
// add any audit aces
|
|
//
|
|
else if ( (_pcaaacl[oldidx]->AccessMode() == SET_AUDIT_SUCCESS) ||
|
|
(_pcaaacl[oldidx]->AccessMode() == SET_AUDIT_FAILURE) ||
|
|
(_pcaaacl[oldidx]->AccessMode() == SE_AUDIT_BOTH))
|
|
{
|
|
//
|
|
// allow synchronize and read control if
|
|
// used by provider independent API
|
|
// also note that if an audit both ace
|
|
// existed before, and is not being modified,
|
|
// no changes are made to the ace
|
|
//
|
|
|
|
ACCESS_MASK tmpmask;
|
|
|
|
if (_fused_by_provider_independent_api)
|
|
{
|
|
tmpmask = _pcaaacl[oldidx]->AccessMask() |
|
|
SYNCHRONIZE | READ_CONTROL;
|
|
} else
|
|
{
|
|
tmpmask = _pcaaacl[oldidx]->AccessMask();
|
|
}
|
|
|
|
//
|
|
// add an audit ace
|
|
//
|
|
if (!NT_SUCCESS(ntstatus = RtlAddAuditAccessAce(
|
|
*pacl,
|
|
ACL_REVISION2,
|
|
tmpmask,
|
|
_pcaaacl[oldidx]->Sid(),
|
|
(_pcaaacl[oldidx]->AccessMode() ==
|
|
SET_AUDIT_SUCCESS),
|
|
(_pcaaacl[oldidx]->AccessMode() ==
|
|
SET_AUDIT_FAILURE))))
|
|
{
|
|
status = RtlNtStatusToDosError(ntstatus);
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// set the inheritance
|
|
//
|
|
status = _SetAceFlags( aceindex,
|
|
*pacl,
|
|
_pcaaacl[oldidx] );
|
|
if (status != NO_ERROR)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// increment the count of added aces (so can
|
|
// set any inheritance)
|
|
//
|
|
aceindex++;
|
|
}
|
|
|
|
//
|
|
// unless the entry was marked as not used, it is
|
|
// out of order
|
|
//
|
|
else if (_pcaaacl[oldidx]->AccessMode() !=
|
|
NOT_USED_ACCESS)
|
|
|
|
{
|
|
status = ERROR_INVALID_ACL;
|
|
break;
|
|
}
|
|
} // for
|
|
|
|
}
|
|
|
|
if (status != NO_ERROR)
|
|
{
|
|
if (*pacl != NULL)
|
|
{
|
|
AccFree(*pacl);
|
|
}
|
|
}
|
|
|
|
return(status);
|
|
}
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: BuildAccessEntries, public
|
|
//
|
|
// Synopsis: converts the ACL and access entries AccountAccesses into
|
|
// access entries. This is also a 2 pass operation, the first
|
|
// pass calculates the size and the second pass resolves conflicts,
|
|
// allocates memory and builds the access entries to return
|
|
//
|
|
// Arguments: OUT - [csize] - the returned size of the access entries
|
|
// OUT - [ccount] - the returned count of access entries
|
|
// OUT - [pae] - the returned list of access entries, this buffer
|
|
// and each trustee name in it must be freed using
|
|
// AccFree (or see below)
|
|
// IN - [fAbsolute] - If TRUE the returned access entries are in a
|
|
// single buffer and must be freed with a single
|
|
// call to AccFree
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
DWORD CAcl::BuildAccessEntries(PULONG csize,
|
|
PULONG ccount,
|
|
PACCESS_ENTRY *pae,
|
|
BOOL fAbsolute)
|
|
{
|
|
DWORD status = NO_ERROR;
|
|
|
|
//
|
|
// pass1 resolve conflicts and get the size of the access entries based on
|
|
// the current contents of this class
|
|
//
|
|
status = _Pass1(csize, ccount, FALSE); // FALSE:build access entries
|
|
if (status == NO_ERROR)
|
|
{
|
|
//
|
|
// allocate space for the new entries
|
|
//
|
|
if ( NULL != (*pae = (PACCESS_ENTRY)AccAlloc(*csize) ) )
|
|
{
|
|
//
|
|
// get a pointer to where the names will be put
|
|
//
|
|
LPWSTR nameptr = (LPWSTR)((PBYTE)(*pae) + sizeof(ACCESS_ENTRY) *
|
|
*ccount);
|
|
|
|
ULONG cintcount = 0;
|
|
//
|
|
// loop thru the access entries AccountAccesses
|
|
//
|
|
for (ULONG newidx = 0; newidx < _pcaaaesindex ; newidx++ )
|
|
{
|
|
//
|
|
// add any denys found
|
|
//
|
|
if (_pcaaaes[newidx]->AccessMode() == DENY_ACCESS)
|
|
{
|
|
_BuildAccessEntry(_pcaaaes[newidx],
|
|
&nameptr,
|
|
&((*pae)[cintcount++]),
|
|
fAbsolute);
|
|
}
|
|
}
|
|
|
|
//
|
|
// loop thru the ACL AccountAccesses until the first allow
|
|
//
|
|
for (ULONG oldidx = 0; oldidx < _pcaaaclindex ; oldidx++ )
|
|
{
|
|
//
|
|
// add any denys found
|
|
//
|
|
if (_pcaaacl[oldidx]->AccessMode() == DENY_ACCESS)
|
|
{
|
|
_BuildAccessEntry(_pcaaacl[oldidx],
|
|
&nameptr,
|
|
&((*pae)[cintcount++]),
|
|
fAbsolute);
|
|
|
|
} else if ( (_pcaaacl[oldidx]->AccessMode() ==
|
|
SET_ACCESS) ||
|
|
(_pcaaacl[oldidx]->AccessMode() ==
|
|
SET_AUDIT_SUCCESS) ||
|
|
(_pcaaacl[oldidx]->AccessMode() ==
|
|
SET_AUDIT_FAILURE) ||
|
|
(_pcaaacl[oldidx]->AccessMode() ==
|
|
SE_AUDIT_BOTH))
|
|
{
|
|
//
|
|
// break at the first allowed ACE
|
|
//
|
|
break;
|
|
}
|
|
}
|
|
//
|
|
// loop thru the access entries AccountAccesses
|
|
//
|
|
for (newidx = 0; newidx < _pcaaaesindex ; newidx++)
|
|
{
|
|
//
|
|
// add any grants or sets found
|
|
//
|
|
if ( (_pcaaaes[newidx]->AccessMode() ==
|
|
SET_ACCESS) ||
|
|
(_pcaaaes[newidx]->AccessMode() ==
|
|
GRANT_ACCESS) ||
|
|
(_pcaaaes[newidx]->AccessMode() ==
|
|
SET_AUDIT_SUCCESS) ||
|
|
(_pcaaaes[newidx]->AccessMode() ==
|
|
SET_AUDIT_FAILURE) )
|
|
{
|
|
_BuildAccessEntry(_pcaaaes[newidx],
|
|
&nameptr,
|
|
&((*pae)[cintcount++]),
|
|
fAbsolute);
|
|
}
|
|
}
|
|
//
|
|
// loop thru the rest of the ACLs AccountAccesses
|
|
//
|
|
for (; oldidx < _pcaaaclindex ; oldidx++)
|
|
{
|
|
//
|
|
// add any sets found, error if any denys found
|
|
//
|
|
if ( (_pcaaacl[oldidx]->AccessMode() ==
|
|
SET_ACCESS) ||
|
|
(_pcaaacl[oldidx]->AccessMode() ==
|
|
SET_AUDIT_SUCCESS) ||
|
|
(_pcaaacl[oldidx]->AccessMode() ==
|
|
SET_AUDIT_FAILURE))
|
|
{
|
|
_BuildAccessEntry(_pcaaacl[oldidx],
|
|
&nameptr,
|
|
&((*pae)[cintcount++]),
|
|
fAbsolute);
|
|
//
|
|
// unless the entry was marked as not used, it is
|
|
// out of order
|
|
//
|
|
} else if (_pcaaacl[oldidx]->AccessMode() ==
|
|
SE_AUDIT_BOTH)
|
|
{
|
|
_BuildDualAuditEntries(_pcaaacl[oldidx],
|
|
&nameptr,
|
|
*pae,
|
|
&cintcount,
|
|
fAbsolute);
|
|
|
|
} else if (_pcaaacl[oldidx]->AccessMode() !=
|
|
NOT_USED_ACCESS)
|
|
|
|
{
|
|
status = ERROR_INVALID_ACL;
|
|
AccFree(*pae);
|
|
break;
|
|
}
|
|
}
|
|
ASSERT(*ccount == cintcount);
|
|
} else
|
|
{
|
|
status = ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
}
|
|
return(status);
|
|
}
|
|
#if 0
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: GetEffectiveRights, public
|
|
//
|
|
// Synopsis: grovels the CACLs AccountAccess objects to determine the trustees
|
|
// effective access rights
|
|
//
|
|
// Arguments: IN - [ptrustee] - the name of the trustee to get effective right
|
|
// OUT - [accessmask] - effective rights for the trustee
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
DWORD CAcl::GetEffectiveRights( PTRUSTEE ptrustee,
|
|
PACCESS_MASK accessmask)
|
|
{
|
|
DWORD status = NO_ERROR;
|
|
CAccountAccess caa;
|
|
|
|
//
|
|
// initialize the account access for the trustee depending on type
|
|
// of trustee
|
|
//
|
|
if (ptrustee->TrusteeForm == TRUSTEE_IS_SID)
|
|
{
|
|
status = caa.Init( (PSID)ptrustee->ptstrName,
|
|
_system,
|
|
SET_ACCESS,
|
|
0,
|
|
0,
|
|
FALSE);
|
|
}
|
|
else if (ptrustee->TrusteeForm == TRUSTEE_IS_NAME)
|
|
{
|
|
status = caa.Init( ptrustee->ptstrName,
|
|
_system,
|
|
SET_ACCESS,
|
|
0,
|
|
0,
|
|
FALSE);
|
|
}
|
|
else
|
|
{
|
|
status = ERROR_NOT_SUPPORTED;
|
|
}
|
|
|
|
if (status == NO_ERROR)
|
|
{
|
|
//
|
|
// a class to do the member check
|
|
//
|
|
CMemberCheck cmc(&caa);
|
|
|
|
//
|
|
// initialized the class
|
|
//
|
|
status = cmc.Init();
|
|
if (status == NO_ERROR)
|
|
{
|
|
ACCESS_MASK allowmask = 0, denymask = 0;
|
|
|
|
for (ULONG newidx = 0; newidx < _pcaaaesindex ; newidx++ )
|
|
{
|
|
//
|
|
// add any denys found
|
|
//
|
|
if (_pcaaaes[newidx]->AccessMode() == DENY_ACCESS)
|
|
{
|
|
status = _ComputeEffective( _pcaaaes[newidx],
|
|
&cmc,
|
|
&allowmask,
|
|
&denymask);
|
|
if (status != NO_ERROR)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (status == NO_ERROR)
|
|
{
|
|
//
|
|
// loop thru the ACL AccountAccesses until the first allow
|
|
//
|
|
for (ULONG oldidx = 0; oldidx < _pcaaaclindex ; oldidx++ )
|
|
{
|
|
//
|
|
// add any denys found
|
|
//
|
|
if (_pcaaacl[oldidx]->AccessMode() == DENY_ACCESS)
|
|
{
|
|
status = _ComputeEffective( _pcaaacl[oldidx],
|
|
&cmc,
|
|
&allowmask,
|
|
&denymask );
|
|
if (status != NO_ERROR)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
else if (_pcaaacl[oldidx]->AccessMode() == SET_ACCESS)
|
|
{
|
|
//
|
|
// break at the first allowed ACE
|
|
//
|
|
break;
|
|
}
|
|
else if ( (_pcaaacl[oldidx]->AccessMode() ==
|
|
SET_AUDIT_SUCCESS) ||
|
|
(_pcaaacl[oldidx]->AccessMode() ==
|
|
SET_AUDIT_FAILURE) ||
|
|
(_pcaaacl[oldidx]->AccessMode() ==
|
|
SE_AUDIT_BOTH) )
|
|
{
|
|
//
|
|
// we should not encounter any audit aces when
|
|
// we are checking for effective access rights
|
|
//
|
|
status = ERROR_INVALID_ACL;
|
|
break;
|
|
}
|
|
}
|
|
if (status == NO_ERROR)
|
|
{
|
|
//
|
|
// loop thru the access entries AccountAccesses
|
|
//
|
|
for (newidx = 0; newidx < _pcaaaesindex ; newidx++ )
|
|
{
|
|
//
|
|
// add any grants, or sets found
|
|
//
|
|
if ( (_pcaaaes[newidx]->AccessMode() ==
|
|
SET_ACCESS) ||
|
|
(_pcaaaes[newidx]->AccessMode() ==
|
|
GRANT_ACCESS) )
|
|
{
|
|
status = _ComputeEffective( _pcaaaes[newidx],
|
|
&cmc,
|
|
&allowmask,
|
|
&denymask );
|
|
if (status != NO_ERROR)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
else if ( (_pcaaaes[newidx]->AccessMode() ==
|
|
SET_AUDIT_SUCCESS) ||
|
|
(_pcaaaes[newidx]->AccessMode() ==
|
|
SET_AUDIT_FAILURE) ||
|
|
(_pcaaaes[newidx]->AccessMode() ==
|
|
SE_AUDIT_BOTH) )
|
|
{
|
|
//
|
|
// we should not encounter any audit aces when
|
|
// we are checking for effective access rights
|
|
//
|
|
status = ERROR_INVALID_ACL;
|
|
break;
|
|
}
|
|
}
|
|
if (status == NO_ERROR)
|
|
{
|
|
//
|
|
// loop thru the rest of the ACLs AccountAccesses
|
|
//
|
|
for (; oldidx < _pcaaaclindex ; oldidx++ )
|
|
{
|
|
//
|
|
// add any sets or audits found, error if any
|
|
// denys found
|
|
//
|
|
if ( (_pcaaacl[oldidx]->AccessMode() ==
|
|
SET_ACCESS) )
|
|
{
|
|
status = _ComputeEffective( _pcaaacl[oldidx],
|
|
&cmc,
|
|
&allowmask,
|
|
&denymask );
|
|
if (status != NO_ERROR)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
else if (_pcaaacl[oldidx]->AccessMode() !=
|
|
NOT_USED_ACCESS)
|
|
|
|
{
|
|
//
|
|
// unless the entry was marked as not used, it is
|
|
// out of order
|
|
//
|
|
status = ERROR_INVALID_ACL;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (status == NO_ERROR)
|
|
{
|
|
//
|
|
// set the effective rights to be the alloweds minus the denieds
|
|
// (because of required ordering)
|
|
//
|
|
*accessmask = allowmask & ~denymask;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
return(status);
|
|
}
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: GetAuditedRights, public
|
|
//
|
|
// Synopsis: grovels the CACLs AccountAccess objects to determine the trustees
|
|
// audited rights
|
|
//
|
|
// Arguments: IN - [ptrustee] - the name of the trustee to get effective right
|
|
// OUT - [successmask] - success audited rights for the trustee
|
|
// OUT - [failuremask] - failure audited rights for the trustee
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
DWORD CAcl::GetAuditedRights(PTRUSTEE ptrustee,
|
|
PACCESS_MASK successmask,
|
|
PACCESS_MASK failuremask)
|
|
{
|
|
DWORD status;
|
|
CAccountAccess caa;
|
|
|
|
|
|
*successmask = 0;
|
|
*failuremask = 0;
|
|
|
|
//
|
|
// initialize the account access for the trustee depending on form of
|
|
// trustee
|
|
//
|
|
if (ptrustee->TrusteeForm == TRUSTEE_IS_SID)
|
|
{
|
|
status = caa.Init( (PSID)ptrustee->ptstrName,
|
|
_system,
|
|
SET_ACCESS,
|
|
0,
|
|
0,
|
|
FALSE );
|
|
}
|
|
else if (ptrustee->TrusteeForm == TRUSTEE_IS_NAME)
|
|
{
|
|
status = caa.Init( ptrustee->ptstrName,
|
|
_system,
|
|
SET_ACCESS,
|
|
0,
|
|
0,
|
|
FALSE );
|
|
}
|
|
else
|
|
{
|
|
status = ERROR_NOT_SUPPORTED;
|
|
}
|
|
|
|
if (status == NO_ERROR)
|
|
{
|
|
//
|
|
// a class to do the member check
|
|
//
|
|
CMemberCheck cmc(&caa);
|
|
|
|
//
|
|
// initialized the class
|
|
//
|
|
status = cmc.Init();
|
|
if (status == NO_ERROR)
|
|
{
|
|
//
|
|
// loop thru the access entry accesses
|
|
//
|
|
for (ULONG newidx = 0; newidx < _pcaaaesindex ; newidx++ )
|
|
{
|
|
//
|
|
// no access convered by ace with inherit only flag on
|
|
//
|
|
if (0 == (_pcaaaes[newidx]->AceFlags() & INHERIT_ONLY_ACE ) )
|
|
{
|
|
BOOL fresult;
|
|
|
|
//
|
|
// check if the trustee is a member of the account access id
|
|
//
|
|
status = cmc.IsMemberOf( _pcaaaes[newidx],
|
|
&fresult );
|
|
if (status == NO_ERROR)
|
|
{
|
|
if (fresult == TRUE)
|
|
{
|
|
//
|
|
// then decide which type to add it to
|
|
//
|
|
if ( (_pcaaaes[newidx]->AccessMode() ==
|
|
SET_AUDIT_SUCCESS) ||
|
|
(_pcaaaes[newidx]->AccessMode() ==
|
|
SE_AUDIT_BOTH) )
|
|
{
|
|
*successmask |=_pcaaaes[newidx]->AccessMask();
|
|
}
|
|
|
|
if ( (_pcaaaes[newidx]->AccessMode() ==
|
|
SET_AUDIT_FAILURE) ||
|
|
(_pcaaaes[newidx]->AccessMode() ==
|
|
SE_AUDIT_BOTH) )
|
|
{
|
|
*failuremask |=_pcaaaes[newidx]->AccessMask();
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (status == NO_ERROR)
|
|
{
|
|
//
|
|
// loop thru the ACL AccountAccesses
|
|
//
|
|
for (ULONG oldidx = 0; oldidx < _pcaaaclindex ; oldidx++ )
|
|
{
|
|
//
|
|
// no access convered by ace with inherit only flag on
|
|
//
|
|
if (0 == (_pcaaacl[oldidx]->AceFlags() & INHERIT_ONLY_ACE ) )
|
|
{
|
|
BOOL fresult;
|
|
|
|
//
|
|
// check if the trustee is a member of the account
|
|
// access id
|
|
//
|
|
status = cmc.IsMemberOf( _pcaaacl[oldidx],
|
|
&fresult );
|
|
if (status == NO_ERROR)
|
|
{
|
|
if (fresult == TRUE)
|
|
{
|
|
//
|
|
// then decide which type to add it to
|
|
//
|
|
if ( (_pcaaacl[oldidx]->AccessMode() ==
|
|
SET_AUDIT_SUCCESS) ||
|
|
(_pcaaacl[oldidx]->AccessMode() ==
|
|
SE_AUDIT_BOTH) )
|
|
{
|
|
*successmask |=_pcaaacl[oldidx]->AccessMask();
|
|
}
|
|
if ( (_pcaaacl[oldidx]->AccessMode() ==
|
|
SET_AUDIT_FAILURE) ||
|
|
(_pcaaacl[oldidx]->AccessMode() ==
|
|
SE_AUDIT_BOTH) )
|
|
{
|
|
*failuremask |=_pcaaacl[oldidx]->AccessMask();
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return(status);
|
|
}
|
|
#endif
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: _InitIterators, private
|
|
//
|
|
// Synopsis: resolves any conficts between the ACL and access entries,
|
|
// and calculates the size required for an ACL or access entries
|
|
// to return
|
|
//
|
|
// Arguments: none
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
DWORD CAcl::_InitIterators()
|
|
{
|
|
DWORD status = NO_ERROR;
|
|
//
|
|
// new the iterators if required
|
|
//
|
|
if (_pcacli == NULL)
|
|
{
|
|
if (NULL == (_pcacli = new CAclIterator()))
|
|
{
|
|
status = ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
}
|
|
|
|
if (status == NO_ERROR)
|
|
{
|
|
if (_pcaeli == NULL)
|
|
{
|
|
if (NULL == (_pcaeli = new CAesIterator()))
|
|
{
|
|
status = ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
}
|
|
}
|
|
return(status);
|
|
}
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: _Pass1, private
|
|
//
|
|
// Synopsis: resolves any conficts between the ACL and access entries,
|
|
// and calculates the size required for an ACL or access entries
|
|
// to return
|
|
//
|
|
// Arguments: [OUT] cSize - the size required for an ACL or access entries
|
|
// [OUT] cCount - the count of ACEs or access entries
|
|
// [IN] fBuildAcl - if FALSE, calculate size for access entries
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
DWORD CAcl::_Pass1(PULONG cSize, PULONG cCount, BOOL fBuildAcl)
|
|
{
|
|
|
|
DWORD status = NO_ERROR;
|
|
BOOL useentry;
|
|
|
|
//
|
|
// initialize the size.
|
|
//
|
|
*cSize = fBuildAcl ? sizeof(ACL) : 0;
|
|
*cCount = 0;
|
|
|
|
//
|
|
// assume that we are using all the (remaining) ACL entries
|
|
// (ie. whats one more loop)
|
|
//
|
|
for (ULONG aclindex = 0; aclindex < _pcaaaclindex; aclindex++)
|
|
{
|
|
//
|
|
// don't process any entries marked not used
|
|
//
|
|
if (_pcaaacl[aclindex]->AccessMode() != NOT_USED_ACCESS)
|
|
{
|
|
status = _UseEntry( _pcaaacl[aclindex],
|
|
cSize,
|
|
cCount,
|
|
fBuildAcl );
|
|
if (status != NO_ERROR)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
//
|
|
// if there are any access entries, resolve any conflicts
|
|
//
|
|
for (ULONG aeindex = 0; aeindex < _pcaaaesindex; aeindex++)
|
|
{
|
|
//
|
|
// don't process any entries already marked not used.
|
|
// (perhaps from a previous pass)
|
|
//
|
|
if (_pcaaaes[aeindex]->AccessMode() != NOT_USED_ACCESS)
|
|
{
|
|
//
|
|
// first check for matching SIDs in the ACL
|
|
//
|
|
status = _CheckEntryList( _pcaaaes[aeindex],
|
|
_pcaaacl,
|
|
_pcaaaclindex,
|
|
cSize,
|
|
cCount,
|
|
fBuildAcl );
|
|
if (status != NO_ERROR)
|
|
{
|
|
break;
|
|
}
|
|
//
|
|
// if the entry is still needed (access not already provided
|
|
// by a previous entry), check the access entries list for matches
|
|
//
|
|
if (_pcaaaes[aeindex]->AccessMode() != NOT_USED_ACCESS)
|
|
{
|
|
//
|
|
// then check for matching SIDS in any previous access entries
|
|
//
|
|
status = _CheckEntryList( _pcaaaes[aeindex],
|
|
_pcaaaes,
|
|
aeindex,
|
|
cSize,
|
|
cCount,
|
|
fBuildAcl );
|
|
if (status != NO_ERROR)
|
|
{
|
|
break;
|
|
}
|
|
//
|
|
// all done checking, if we still want the entry, use it
|
|
//
|
|
|
|
if ( (_pcaaaes[aeindex]->AccessMode() != NOT_USED_ACCESS) &&
|
|
(_pcaaaes[aeindex]->AccessMode() != REVOKE_ACCESS) )
|
|
{
|
|
status = _UseEntry( _pcaaaes[aeindex],
|
|
cSize,
|
|
cCount,
|
|
fBuildAcl );
|
|
if (status != NO_ERROR)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return(status);
|
|
}
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: _CheckEntryList, private
|
|
//
|
|
// Synopsis: resolves any conficts between the ACL and access entries,
|
|
// and calculates the size required for an ACL or access entries
|
|
// to return
|
|
//
|
|
// Arguments: [IN] pCAA - the Account Access class to check for matches against
|
|
// the list of account accessses
|
|
// [IN] plistCAA - the list of account accesses to search thru
|
|
// [IN] clistlength - the number of entries in the list
|
|
// [IN/OUT] cSize - the size required for an ACL or access entries
|
|
// [IN/OUT] cCount - the size required for an ACL or access entries
|
|
// [IN] fBuildAcl - if FALSE, calculate size for access entries
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
DWORD CAcl::_CheckEntryList(CAccountAccess *pCAA,
|
|
CAccountAccess **plistCAA,
|
|
ULONG clistlength,
|
|
PULONG cSize,
|
|
PULONG cCount,
|
|
BOOL fBuildAcl)
|
|
{
|
|
DWORD status = NO_ERROR;
|
|
|
|
BOOL dontuseentry = FALSE;
|
|
|
|
for (ULONG index = 0; index < clistlength; index++)
|
|
{
|
|
//
|
|
// don't process any entries already marked not used.
|
|
// (perhaps from a previous pass)
|
|
//
|
|
if (plistCAA[index]->AccessMode() != NOT_USED_ACCESS)
|
|
{
|
|
//
|
|
// check for matching ids (and matching modes, see
|
|
// accesscontrolcdd.doc for a full explaination of this)
|
|
// this impersonate stuff is specifically for DS objects,
|
|
// and the kind of ace management they do (specifically, see
|
|
// notes on DS object specific permissions, and query access
|
|
//
|
|
if ( (RtlEqualSid( pCAA->Sid(), plistCAA[index]->Sid())) &&
|
|
( ( (pCAA->AccessMode() == DENY_ACCESS) ||
|
|
(plistCAA[index]->AccessMode() == DENY_ACCESS) ) ||
|
|
( (pCAA->MultipleTrusteeOperation() != TRUSTEE_IS_IMPERSONATE) &&
|
|
(plistCAA[index]->MultipleTrusteeOperation() != TRUSTEE_IS_IMPERSONATE) ||
|
|
( (pCAA->MultipleTrusteeOperation() == TRUSTEE_IS_IMPERSONATE) &&
|
|
(plistCAA[index]->MultipleTrusteeOperation() == TRUSTEE_IS_IMPERSONATE) ))))
|
|
{
|
|
|
|
//
|
|
// do things based on the type of the new entry
|
|
//
|
|
switch (pCAA->AccessMode())
|
|
{
|
|
case GRANT_ACCESS:
|
|
case DENY_ACCESS:
|
|
//
|
|
// merge the entries
|
|
//
|
|
status = _MergeEntries(pCAA,
|
|
plistCAA[index],
|
|
cSize,
|
|
cCount,
|
|
fBuildAcl);
|
|
break;
|
|
case SET_ACCESS:
|
|
case REVOKE_ACCESS:
|
|
case SET_AUDIT_SUCCESS:
|
|
case SET_AUDIT_FAILURE:
|
|
//
|
|
// don't use the old entry (since the new one takes
|
|
// precedence)
|
|
//
|
|
|
|
status = _RemoveEntry(plistCAA[index],
|
|
cSize,
|
|
cCount,
|
|
fBuildAcl);
|
|
break;
|
|
default:
|
|
ASSERT( (pCAA->AccessMode() != GRANT_ACCESS ) ||
|
|
(pCAA->AccessMode() != DENY_ACCESS ) ||
|
|
(pCAA->AccessMode() != SET_ACCESS ) ||
|
|
(pCAA->AccessMode() != REVOKE_ACCESS ) ||
|
|
(pCAA->AccessMode() != SET_AUDIT_SUCCESS ) ||
|
|
(pCAA->AccessMode() != SET_AUDIT_FAILURE ) );
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return(status);
|
|
}
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: _UseEntry, private
|
|
//
|
|
// Synopsis: uses the entry for the ACL or access entries being built
|
|
//
|
|
// Arguments: [IN] pCAA - the Account Access class to use
|
|
// [IN/OUT] cSize - the size required for an ACL or access entries
|
|
// [IN/OUT] cCount - the size required for an ACL or access entries
|
|
// [IN] fBuildAcl - if FALSE, calculate size for access entries
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
DWORD CAcl::_UseEntry(CAccountAccess *pCAA,
|
|
PULONG cSize,
|
|
PULONG cCount,
|
|
BOOL fBuildAcl)
|
|
{
|
|
DWORD status = NO_ERROR;
|
|
|
|
if (fBuildAcl)
|
|
{
|
|
//
|
|
// increment the count (of aces in this case)
|
|
// and grow the size
|
|
//
|
|
(*cSize) += _GetAceSize(pCAA);
|
|
|
|
} else
|
|
{
|
|
//
|
|
// get the size of the access entry
|
|
//
|
|
ULONG centrysize;
|
|
|
|
status = _GetAccessEntrySize( pCAA,
|
|
¢rysize);
|
|
if (status == NO_ERROR)
|
|
{
|
|
(*cSize) += centrysize;
|
|
if (pCAA->AccessMode() == SE_AUDIT_BOTH)
|
|
{
|
|
//
|
|
// if it is an audit both (success and failure) ACE,
|
|
// add two access entries
|
|
//
|
|
(*cSize) += centrysize;
|
|
(*cCount)++;
|
|
}
|
|
}
|
|
}
|
|
//
|
|
// increment the count (of access entries)
|
|
//
|
|
(*cCount)++;
|
|
return(status);
|
|
}
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: _RemoveEntry, private
|
|
//
|
|
// Synopsis: removes the entry from those to be used for the ACL or access
|
|
// entries being built
|
|
//
|
|
// Arguments: [IN] pCAA - the Account Access class to not use
|
|
// [IN/OUT] cSize - the size required for an ACL or access entries
|
|
// [IN/OUT] cCount - the size required for an ACL or access entries
|
|
// [IN] fBuildAcl - if FALSE, calculate size for access entries
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
DWORD CAcl::_RemoveEntry(CAccountAccess *pCAA,
|
|
PULONG cSize,
|
|
PULONG cCount,
|
|
BOOL fBuildAcl)
|
|
{
|
|
DWORD status = NO_ERROR;
|
|
|
|
ASSERT(pCAA->AccessMode() != NOT_USED_ACCESS);
|
|
|
|
if (fBuildAcl)
|
|
{
|
|
//
|
|
// increment the count (of aces in this case)
|
|
// and grow the size
|
|
//
|
|
(*cSize) -= _GetAceSize(pCAA);
|
|
} else
|
|
{
|
|
//
|
|
// get the size of the access entry
|
|
//
|
|
ULONG centrysize;
|
|
|
|
status = _GetAccessEntrySize( pCAA,
|
|
¢rysize );
|
|
if (status == NO_ERROR)
|
|
{
|
|
(*cSize) -= centrysize;
|
|
}
|
|
}
|
|
//
|
|
// increment the count (of access entries)
|
|
//
|
|
(*cCount)--;
|
|
pCAA->SetAccessMode(NOT_USED_ACCESS);
|
|
return(status);
|
|
}
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: _MergeEntries, private
|
|
//
|
|
// Synopsis: this merge function checks two access entries with matching
|
|
// SIDs for inheritance and mask intersections. It is assumed that
|
|
// the new access entry is either DENY_ACCESS or GRANT_ACCESS,
|
|
// and the old entry is either DENY_ACCESS, GRANT_ACCESS, or SET_ACCESS
|
|
//
|
|
// Arguments: [IN] pnewCAA - the new Account Access class to merge with the
|
|
// old one
|
|
// [IN] poldCAA - the old Account Access class
|
|
// [IN/OUT] cSize - the size required for an ACL or access entries
|
|
// [IN/OUT] cCount - the size required for an ACL or access entries
|
|
// [IN] fBuildAcl - if FALSE, calculate size for access entries
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
DWORD CAcl::_MergeEntries(CAccountAccess *pnewCAA,
|
|
CAccountAccess *poldCAA,
|
|
PULONG cSize,
|
|
PULONG cCount,
|
|
BOOL fBuildAcl)
|
|
{
|
|
|
|
DWORD status = NO_ERROR;
|
|
BOOL leave_old_ace = FALSE;
|
|
|
|
//
|
|
// bogus while loop for fake gotos
|
|
//
|
|
while(1)
|
|
{
|
|
//
|
|
// can't set inheritance on objects
|
|
//
|
|
if ( ( (_fdir == ACCESS_TO_UNKNOWN ) ||
|
|
(_fdir == ACCESS_TO_OBJECT ) ) &&
|
|
(0 != pnewCAA->AceFlags()) )
|
|
{
|
|
status = ERROR_INVALID_PARAMETER;
|
|
break;
|
|
} else if (_fdir == ACCESS_TO_CONTAINER )
|
|
{
|
|
//
|
|
// check for inheritance intersections
|
|
//
|
|
if (0 == (pnewCAA->AceFlags() & poldCAA->AceFlags()))
|
|
{
|
|
//
|
|
// if disjoint inheritance, the entries really don't match
|
|
//
|
|
break;
|
|
|
|
} else if (pnewCAA->AceFlags() != poldCAA->AceFlags())
|
|
{
|
|
//
|
|
// if the entries inheritance intersects in some way, return an
|
|
// error (can only set or revoke access in this case)
|
|
//
|
|
status = ERROR_INVALID_PARAMETER;
|
|
break;
|
|
}
|
|
}
|
|
//
|
|
// now to merge the masks, switch on mask intersection type, and
|
|
// then on entry types,
|
|
// NOTE that SYNCHRONIZE and READ_CONTROL are always added on the
|
|
// mask compares, however
|
|
// it is only actually applied on the SET_ACCESS ACEs, so that, for
|
|
// example, READ can be allowed, and WRITE can be denied (as opposed to
|
|
// not granted).
|
|
//
|
|
if (0 == ((pnewCAA->AccessMask() | SYNCHRONIZE | READ_CONTROL) &
|
|
(poldCAA->AccessMask() | SYNCHRONIZE | READ_CONTROL)))
|
|
{
|
|
//
|
|
// disjoint
|
|
//
|
|
if ( (pnewCAA->AccessMode() == poldCAA->AccessMode()) ||
|
|
( (pnewCAA->AccessMode() != DENY_ACCESS) &&
|
|
(poldCAA->AccessMode() != DENY_ACCESS) ) )
|
|
{
|
|
//
|
|
// equivalent modes
|
|
//
|
|
pnewCAA->SetAccessMask(pnewCAA->AccessMask() |
|
|
poldCAA->AccessMask());
|
|
status = _RemoveEntry(poldCAA,
|
|
cSize,
|
|
cCount,
|
|
fBuildAcl);
|
|
}
|
|
} else if ((pnewCAA->AccessMask() | SYNCHRONIZE | READ_CONTROL) ==
|
|
(poldCAA->AccessMask() | SYNCHRONIZE | READ_CONTROL) )
|
|
{
|
|
//
|
|
// equal
|
|
//
|
|
status = _RemoveEntry(poldCAA,
|
|
cSize,
|
|
cCount,
|
|
fBuildAcl);
|
|
|
|
} else if ( (poldCAA->AccessMask() | pnewCAA->AccessMask() |
|
|
SYNCHRONIZE | READ_CONTROL) ==
|
|
(poldCAA->AccessMask() | SYNCHRONIZE | READ_CONTROL))
|
|
{
|
|
//
|
|
// new is subset of old
|
|
//
|
|
if ( (pnewCAA->AccessMode() == poldCAA->AccessMode()) ||
|
|
( (pnewCAA->AccessMode() != DENY_ACCESS) &&
|
|
(poldCAA->AccessMode() != DENY_ACCESS) ) )
|
|
{
|
|
//
|
|
// equivalent modes
|
|
//
|
|
pnewCAA->SetAccessMode(NOT_USED_ACCESS);
|
|
} else
|
|
{
|
|
//
|
|
// one is deny, one is allow
|
|
//
|
|
poldCAA->SetAccessMask(poldCAA->AccessMask() &
|
|
~(pnewCAA->AccessMask()));
|
|
}
|
|
} else if ( (poldCAA->AccessMask() | pnewCAA->AccessMask() |
|
|
SYNCHRONIZE | READ_CONTROL) ==
|
|
(pnewCAA->AccessMask() | SYNCHRONIZE | READ_CONTROL))
|
|
{
|
|
//
|
|
// old is subset of new
|
|
//
|
|
status = _RemoveEntry(poldCAA,
|
|
cSize,
|
|
cCount,
|
|
fBuildAcl);
|
|
} else
|
|
{
|
|
//
|
|
// overlapping
|
|
//
|
|
if ( ( (pnewCAA->AccessMode() | SYNCHRONIZE | READ_CONTROL) ==
|
|
(poldCAA->AccessMode() | SYNCHRONIZE | READ_CONTROL) ) ||
|
|
( (pnewCAA->AccessMode() != DENY_ACCESS) &&
|
|
(poldCAA->AccessMode() != DENY_ACCESS) ) )
|
|
{
|
|
//
|
|
// equivalent modes
|
|
//
|
|
pnewCAA->SetAccessMask(pnewCAA->AccessMask() |
|
|
poldCAA->AccessMask());
|
|
status = _RemoveEntry(poldCAA,
|
|
cSize,
|
|
cCount,
|
|
fBuildAcl);
|
|
} else
|
|
{
|
|
//
|
|
// one is deny, one is allow
|
|
//
|
|
poldCAA->SetAccessMask(poldCAA->AccessMask() &
|
|
~(pnewCAA->AccessMask()));
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
return(status);
|
|
}
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: _BuildAccessEntry, private
|
|
//
|
|
// Synopsis: converts an AccountAccess into an access entry
|
|
//
|
|
// Arguments: [IN] pCAA - the AccountAccess being converted to an access entry
|
|
// [IN] nameptr - where to put the trustee name
|
|
// [OUT] pAccessEntry - access entry to be stuffed
|
|
// [IN] fAbsolute - if TRUE, trustee names are pointers
|
|
//
|
|
//+---------------------------------------------------------------------------
|
|
void CAcl::_BuildAccessEntry(CAccountAccess *pCAA,
|
|
LPWSTR *nameptr,
|
|
PACCESS_ENTRY pAccessEntry,
|
|
BOOL fAbsolute)
|
|
{
|
|
|
|
pAccessEntry->AccessMode = pCAA->AccessMode();
|
|
pAccessEntry->InheritType = (pCAA->AceFlags() & ACLBUILD_VALID_ACE_FLAGS);
|
|
pAccessEntry->AccessMask = pCAA->AccessMask();
|
|
|
|
//
|
|
// now to make the trustee, first the single one
|
|
//
|
|
if (NULL != pCAA->Name())
|
|
{
|
|
wcscpy(*nameptr, pCAA->Name());
|
|
} else
|
|
{
|
|
wcscpy(*nameptr, NAME_NOT_FOUND);
|
|
}
|
|
BuildTrusteeWithName(&(pAccessEntry->Trustee), *nameptr);
|
|
//
|
|
// return the real (simplified) trustee type
|
|
//
|
|
switch (pCAA->SidType())
|
|
{
|
|
case SidTypeUser:
|
|
pAccessEntry->Trustee.TrusteeType = TRUSTEE_IS_USER;
|
|
break;
|
|
case SidTypeGroup:
|
|
case SidTypeDomain:
|
|
case SidTypeAlias:
|
|
case SidTypeWellKnownGroup:
|
|
pAccessEntry->Trustee.TrusteeType = TRUSTEE_IS_GROUP;
|
|
break;
|
|
case SidTypeDeletedAccount:
|
|
case SidTypeInvalid:
|
|
case SidTypeUnknown:
|
|
default:
|
|
pAccessEntry->Trustee.TrusteeType = TRUSTEE_IS_UNKNOWN;
|
|
break;
|
|
}
|
|
//
|
|
// increment to the next trustee
|
|
//
|
|
*nameptr = (LPWSTR)((PBYTE)(*nameptr) + (wcslen(*nameptr) + 1) * sizeof(WCHAR));
|
|
|
|
//
|
|
// now if there is a multiple trustee
|
|
//
|
|
if (pCAA->MultipleTrusteeOperation() == TRUSTEE_IS_IMPERSONATE)
|
|
{
|
|
//
|
|
// make space for the impersonate trustee
|
|
//
|
|
PTRUSTEE pImpTrustee = (PTRUSTEE)*nameptr;
|
|
(*nameptr) = (LPWSTR)((PBYTE)*nameptr + sizeof(TRUSTEE));
|
|
//
|
|
// figure out the sid size
|
|
// why not make the server a name based trustee??
|
|
// answer: because it is not easier
|
|
//
|
|
PSID psid = pCAA->ImpersonateSid();
|
|
ULONG sidsize = RtlLengthSid(psid);
|
|
//
|
|
// copy the sid, and increment the buffer pointer
|
|
//
|
|
RtlCopySid(sidsize, *nameptr,psid);
|
|
//
|
|
// connect the trustee together with the sid
|
|
//
|
|
BuildTrusteeWithSid(pImpTrustee, *nameptr);
|
|
//
|
|
// move the buffer pointer past the sid
|
|
//
|
|
(*nameptr) = (LPWSTR)((PBYTE)*nameptr + sidsize);
|
|
//
|
|
// finally turn the original trustee into an impersonate trustee
|
|
// with the other one
|
|
//
|
|
BuildImpersonateTrustee(&(pAccessEntry->Trustee), pImpTrustee);
|
|
}
|
|
}
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: _BuildDualAuditEntries, private
|
|
//
|
|
// Synopsis: converts an ACEs AccountAccess for auditing both success
|
|
// and failed opens into two access entries
|
|
//
|
|
// Arguments: [IN] pCAA - the AccountAccess being converted to an access entry
|
|
// [IN] nameptr - where to put the trustee name
|
|
// [IN/OUT] pae - list of access entries to be stuffed
|
|
// [IN/OUT] ccount - index into access entry list to start stuffing
|
|
// [IN] fAbsolute - if TRUE, trustee names are pointers
|
|
//
|
|
//+---------------------------------------------------------------------------
|
|
void CAcl::_BuildDualAuditEntries(CAccountAccess *pCAA,
|
|
LPWSTR *nameptr,
|
|
PACCESS_ENTRY pae,
|
|
DWORD *ccount,
|
|
BOOL fAbsolute)
|
|
{
|
|
//
|
|
// first add an access entry for the successful open
|
|
_BuildAccessEntry(pCAA,
|
|
nameptr,
|
|
&(pae[*ccount]),
|
|
fAbsolute);
|
|
pae[(*ccount)++].AccessMode = SET_AUDIT_SUCCESS;
|
|
|
|
//
|
|
// then add an entry for the failed open
|
|
//
|
|
_BuildAccessEntry(pCAA,
|
|
nameptr,
|
|
&(pae[*ccount]),
|
|
fAbsolute);
|
|
pae[(*ccount)++].AccessMode = SET_AUDIT_FAILURE;
|
|
}
|
|
#if 0
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: _ComputeEffective, private
|
|
//
|
|
// Synopsis: computes effective rights from an AccountAccess, including
|
|
// doing a check for group membership
|
|
//
|
|
// Arguments: [IN] pCAA - the AccountAccess to test against
|
|
// [IN] cMC - the member check class
|
|
// [IN OUT] AllowedMask - the mask to add allowed accesses to
|
|
// [IN OUT] DeniedMask - the mask to add denied accesses to
|
|
//
|
|
//+---------------------------------------------------------------------------
|
|
DWORD CAcl::_ComputeEffective(CAccountAccess *pCAA,
|
|
CMemberCheck *cMC,
|
|
PACCESS_MASK AllowMask,
|
|
PACCESS_MASK DenyMask)
|
|
{
|
|
DWORD status = NO_ERROR;
|
|
//
|
|
// no access convered by ace with inherit only flag on
|
|
//
|
|
if (0 == (pCAA->AceFlags() & INHERIT_ONLY_ACE ) )
|
|
{
|
|
BOOL fresult;
|
|
|
|
//
|
|
// check of the trustee is a member of the account access id
|
|
//
|
|
status = cMC->IsMemberOf( pCAA,
|
|
&fresult );
|
|
if (status == NO_ERROR)
|
|
{
|
|
if (fresult == TRUE)
|
|
{
|
|
//
|
|
// if a denied, then add to the deny mask
|
|
//
|
|
if (pCAA->AccessMode() == DENY_ACCESS)
|
|
{
|
|
*DenyMask |= pCAA->AccessMask();
|
|
//
|
|
// if a set, then add to the allowed mask
|
|
//
|
|
} else if ( (pCAA->AccessMode() == SET_ACCESS) ||
|
|
(pCAA->AccessMode() == GRANT_ACCESS) )
|
|
{
|
|
*AllowMask |= pCAA->AccessMask();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return(status);
|
|
}
|
|
#endif
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: _SetAceFlags, private
|
|
//
|
|
// Synopsis: sets the ACE (inheritance) flag on an ACE in an ACL being
|
|
// constructed1
|
|
//
|
|
// Arguments: IN - [AceIndex] - the index of the ace
|
|
// IN/OUT - [pacl] - the acl
|
|
// IN - [pcaa] - the account access for the ace index
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
DWORD CAcl::_SetAceFlags(ULONG AceIndex, PACL pacl, CAccountAccess *pcaa)
|
|
{
|
|
NTSTATUS ntstatus = STATUS_SUCCESS;
|
|
if (0 != pcaa->AceFlags())
|
|
{
|
|
//
|
|
// get the ACE and set the ace flags
|
|
//
|
|
PACE_HEADER pah;
|
|
if (NT_SUCCESS(ntstatus = RtlGetAce(pacl,
|
|
AceIndex,
|
|
(void **)&pah)))
|
|
{
|
|
pah->AceFlags |= (BYTE)(pcaa->AceFlags() & VALID_INHERIT_FLAGS);
|
|
} else
|
|
{
|
|
return(RtlNtStatusToDosError(ntstatus));
|
|
}
|
|
}
|
|
return(ERROR_SUCCESS);
|
|
}
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: _GetAceSize, private
|
|
//
|
|
// Synopsis: gets the size required for an ACE
|
|
//
|
|
// Arguments: IN - [pcaa] - returns the size required for the ACE
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
ULONG CAcl::_GetAceSize(CAccountAccess *pcaa)
|
|
{
|
|
if (pcaa->AccessMode() == REVOKE_ACCESS)
|
|
{
|
|
return(0);
|
|
} else if (pcaa->MultipleTrusteeOperation() == TRUSTEE_IS_IMPERSONATE)
|
|
{
|
|
return(RtlLengthSid(pcaa->Sid()) +
|
|
sizeof(ACE_HEADER) +
|
|
sizeof(ACCESS_MASK) +
|
|
sizeof(ULONG) + // sizeof compound ace type & reserved short
|
|
RtlLengthSid(pcaa->ImpersonateSid()) );
|
|
} else
|
|
{
|
|
return(RtlLengthSid(pcaa->Sid()) +
|
|
sizeof(ACE_HEADER) +
|
|
sizeof(ACCESS_MASK));
|
|
}
|
|
}
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: _GetAccessEntrySize, private
|
|
//
|
|
// Synopsis: gets the size required for an access entry
|
|
//
|
|
// Arguments: IN - [pcaa] - the size required for an access entry
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
DWORD CAcl::_GetAccessEntrySize(CAccountAccess *pcaa, PULONG cAccessEntrySize)
|
|
{
|
|
DWORD status;
|
|
LPWSTR name;
|
|
|
|
status = pcaa->LookupName(&name);
|
|
if (status == NO_ERROR)
|
|
{
|
|
*cAccessEntrySize = (name == NULL ?
|
|
(wcslen(NAME_NOT_FOUND) + 1) * sizeof(WCHAR) +
|
|
sizeof(ACCESS_ENTRY) :
|
|
(wcslen(name) + 1) * sizeof(WCHAR) +
|
|
sizeof(ACCESS_ENTRY));
|
|
|
|
if (pcaa->MultipleTrusteeOperation() == TRUSTEE_IS_IMPERSONATE)
|
|
{
|
|
(*cAccessEntrySize) += (sizeof(TRUSTEE) +
|
|
RtlLengthSid(pcaa->ImpersonateSid()));
|
|
}
|
|
}
|
|
return(status);
|
|
}
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: _AddEntry , private
|
|
//
|
|
// Synopsis: adds an entry (either an ACE or an access entry) to the CACL
|
|
// objects list of AccountAccess objects
|
|
//
|
|
// Arguments: IN - [ci] - the iterator class
|
|
// OUT - [pcaa] - the account access class for the entry
|
|
// OUT - [pcaaindex] the index of the entry
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
DWORD CAcl::_AddEntry(CIterator *ci,
|
|
CAccountAccess **pcaa,
|
|
PULONG pcaaindex)
|
|
{
|
|
|
|
DWORD status;
|
|
|
|
|
|
//
|
|
// allocate space for the account access class
|
|
//
|
|
if (NULL != (*pcaa = new CAccountAccess()))
|
|
{
|
|
//
|
|
// initialize the account access
|
|
//
|
|
status = ci->InitAccountAccess( *pcaa,
|
|
_system,
|
|
_fdir,
|
|
_fsave_names_and_sids );
|
|
if (status == NO_ERROR)
|
|
{
|
|
(*pcaaindex)++;
|
|
|
|
}
|
|
else
|
|
{
|
|
delete *pcaa;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
status = ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
|
|
|
|
//
|
|
// Map ERROR_BAD_INHERITANCE_ACL to NO_ERROR. That error is returned
|
|
// when an ACE is inherit-only, so we don't want it in the ACL entry.
|
|
// We do, however, want to skip the ACE so it is good that hte
|
|
// index is not incremented.
|
|
//
|
|
//
|
|
// JINHUANG: InitAccountAccess is changed so that INHERIT_ONLY ace
|
|
// is also added to the list. So this error should never be returned
|
|
//
|
|
|
|
if (status == ERROR_BAD_INHERITANCE_ACL)
|
|
{
|
|
status = NO_ERROR;
|
|
}
|
|
|
|
return(status);
|
|
}
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: _CheckForDuplicateEntries, private
|
|
//
|
|
// Synopsis: checks for duplicate AccountAccess objects in the CACLs list of
|
|
// AccountAccess objects.
|
|
//
|
|
// Arguments: IN - [pcaa] - the list of account access objects
|
|
// IN - [curindex] - the account access just added (at the end)
|
|
// IN - [countold] - the number of account accesses in the list
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
DWORD CAcl::_CheckForDuplicateEntries(CAccountAccess **pcaa,
|
|
ULONG curindex,
|
|
ULONG countold)
|
|
{
|
|
|
|
DWORD status = NO_ERROR;
|
|
|
|
|
|
//
|
|
// loop thru the existing AccountAccesses to see if the latest matches
|
|
//
|
|
for (ULONG idx = 0; idx < countold; idx++)
|
|
{
|
|
if (RtlEqualSid(pcaa[idx]->Sid(), pcaa[curindex]->Sid()))
|
|
{
|
|
pcaa[idx]->SetAccessMode(NOT_USED_ACCESS);
|
|
}
|
|
}
|
|
//
|
|
// loop thru the new AccountAccesses to see if the latest matches
|
|
// (they start at countold, which is where idx is left at after
|
|
// the last loop)
|
|
//
|
|
for (; idx < curindex; idx++)
|
|
{
|
|
if (RtlEqualSid(pcaa[idx]->Sid(), pcaa[curindex]->Sid()))
|
|
{
|
|
status =ERROR_INVALID_PARAMETER;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return(status);
|
|
}
|