windows-nt/Source/XPSP1/NT/ds/security/authz/test/adl/adlconvert.cpp
2020-09-26 16:20:57 +08:00

2490 lines
63 KiB
C++

/*++
Copyright (c) 2000 Microsoft Corporation
Module Name:
adlconvert.cpp
Abstract:
The routines to convert between AdlStatement, string in the ADL
language, and a DACL.
Author:
t-eugenz - August 2000
Environment:
User mode only.
Revision History:
Created - August 2000
Semantics finalized - September 2000
--*/
#include "adl.h"
#include "adlconvert.h"
void AdlStatement::ConvertFromDacl(IN const PACL pDacl)
/*++
Routine Description:
Traverses the given ACL, and creates an AdlStatement structure
representative of the DACL.
Algorithm:
First, break up the ACL into 32 stacks, by access mask bits, keeping
track of the inheritance and SID.
Then use the heuristic algorithm in ConvertStacksToPops to get a 'good'
(though not necessarily optimal) sequence of stack pops to perform so as
to produce the optimal set of ADL statements for the DACL.
Finally, perform the sequence of pops and create the ADL statement.
Arguments:
pDacl - The DACL to convert to an AdlStatement
Return Value:
none
--*/
{
DWORD dwIdx;
DWORD dwIdxElems;
AdlToken *pTok = NULL;
AdlStatement::ADL_ERROR_TYPE adlErr = AdlStatement::ERROR_NO_ERROR;
//
// The stack representation of a DACL
//
DWORD pdwStackTop[32];
DWORD pdwStackSize[32];
PBIT_STACK_ELEM pStacks[32];
//
// Mappings from PSID to AdlToken of the name string and from
// pointer into the user-specified language def to the appropriate
// permission token. This allows reuse of name and permission tokens
//
map<const PSID, const AdlToken *> mapSidName;
map<const WCHAR *, const AdlToken *> mapStringTok;
//
// list of pairs <Stack mask, Block Size>, which define the set of pops
// to perform. This is filled in by the decision algorithm. For every
// bit set in the stack mask, a block of the given size will be popped
// into ADL from the stack.
//
list<pair<DWORD, DWORD> > listPops;
list<pair<DWORD, DWORD> >::iterator iterPops;
list<pair<DWORD, DWORD> >::iterator iterPopsEnd;
//
// List of permissions for a given access mask, used as output
// by the access mask -> set of names lookup
//
list<WCHAR *> lPermissions;
list<WCHAR *>::iterator iterPerm;
list<WCHAR *>::iterator iterPermEnd;
//
// Initialize the stacks
//
for( dwIdx = 0; dwIdx < 32; dwIdx++ )
{
pdwStackSize[dwIdx] = 0;
pdwStackTop[dwIdx] = 0;
pStacks[dwIdx] = NULL;
}
//
// First look up all names. If some names don't exist, save time
// by not doing the rest of the conversion below
//
ConvertSidsToNames(pDacl, &mapSidName);
//
// Now convert the ACL to the set of 32 stacks (this needs to be freed
// later)
//
ConvertDaclToStacks(pDacl, _pControl, pdwStackSize, pStacks);
//
// A bit of pre-processing: we will need a map from WCHAR * to
// AdlToken *, so as to reuse AdlTokens for the same permission
// name. The tokens will be garbage-collected later.
//
try
{
for( dwIdx = 0; _pControl->pPermissions[dwIdx].str != NULL; dwIdx++ )
{
pTok = new AdlToken(_pControl->pPermissions[dwIdx].str, 0, 0);
try
{
AddToken(pTok);
}
catch(exception)
{
delete pTok;
throw AdlStatement::ERROR_OUT_OF_MEMORY;
}
mapStringTok[_pControl->pPermissions[dwIdx].str] = pTok;
}
}
catch(exception)
{
adlErr = AdlStatement::ERROR_OUT_OF_MEMORY;
goto error;
}
//
// Now we are ready to actually convert the stacks into an ADL statement
// First we run the recursive algorithm to determine the sequence of
// pop operations on the stacks
//
try
{
ConvertStacksToPops(_pControl,
pStacks,
pdwStackSize,
pdwStackTop,
&listPops);
}
catch(exception)
{
adlErr = AdlStatement::ERROR_OUT_OF_MEMORY;
goto error;
}
catch(AdlStatement::ADL_ERROR_TYPE err)
{
adlErr = err;
goto error;
}
//
// Now we perform the calculated pops
//
try
{
//
// Go through the pops in order, and perform them
//
DWORD dwStacksPopped;
DWORD dwBlockSize;
for( iterPops = listPops.begin(), iterPopsEnd = listPops.end();
iterPops != iterPopsEnd;
iterPops++ )
{
dwStacksPopped = (*iterPops).first;
dwBlockSize = (*iterPops).second;
ASSERT( dwStacksPopped > 0 );
ASSERT( dwBlockSize > 0 );
//
// Create new ADL statement
//
Next();
//
// Set permissions once, mask is same as stacks popped
//
lPermissions.erase(lPermissions.begin(), lPermissions.end());
MapMaskToStrings(dwStacksPopped, &lPermissions);
for( iterPerm = lPermissions.begin(),
iterPermEnd = lPermissions.end();
iterPerm != iterPermEnd;
iterPerm++ )
{
Cur()->AddPermission(mapStringTok[*iterPerm]);
}
//
// Now find the first stack with the block in question
//
for( dwIdx = 0; dwIdx < 32; dwIdx++ )
{
if( dwStacksPopped & ( 0x00000001 << dwIdx ) )
{
break;
}
}
//
// Add the Principals, ExPrincipals, and inheritance flags
// to the ADL statement. The blocks should all be the same,
// so first block is sufficient
//
//
// First the inheritance flags
//
Cur()->OverwriteFlags(pStacks[dwIdx][pdwStackTop[dwIdx]].dwFlags);
//
// Now the principals and exprincipals
//
for( dwIdxElems = 0; dwIdxElems < dwBlockSize; dwIdxElems++ )
{
if( pStacks[dwIdx][pdwStackTop[dwIdx]+dwIdxElems].bAllow
== FALSE )
{
Cur()->AddExPrincipal(
mapSidName[
pStacks[dwIdx][pdwStackTop[dwIdx] + dwIdxElems
].pSid]);
}
else
{
Cur()->AddPrincipal(
mapSidName[
pStacks[dwIdx][pdwStackTop[dwIdx] + dwIdxElems
].pSid]);
}
}
//
// Finally, move the tops of the stacks down to get rid of the
// popped items
//
for( dwIdx = 0; dwIdx < 32; dwIdx++ )
{
if( dwStacksPopped & ( 0x00000001 << dwIdx ) )
{
pdwStackTop[dwIdx] += dwBlockSize;
ASSERT(pdwStackTop[dwIdx] <= pdwStackSize[dwIdx]);
}
}
}
}
catch(exception)
{
adlErr = AdlStatement::ERROR_OUT_OF_MEMORY;
goto error;
}
catch(AdlStatement::ADL_ERROR_TYPE err)
{
adlErr = err;
goto error;
}
error:;
if( pStacks[0] != NULL )
{
//
// Free the chunk of memory allocated by the converion
//
FreeMemory(pStacks[0]);
}
if( adlErr != AdlStatement::ERROR_NO_ERROR )
{
throw adlErr;
}
}
////////////////////////////////////////////////////////////////////////////////
/////////////
///////////// DACL -> ADL conversion algorithm
/////////////
////////////////////////////////////////////////////////////////////////////////
void ConvertStacksToPops(
IN const PADL_PARSER_CONTROL pControl,
IN const PBIT_STACK_ELEM pStacks[32],
IN const DWORD pdwStackSize[32],
IN const DWORD pdwStackTop[32],
OUT list< pair<DWORD, DWORD> > * pListPops
)
/*++
Routine Description:
Recursive heuristic to determine the 'good' conversion from ACL to ADL.
Not necessarily optimal, but computationally feasible.
This finds a sequence of pops which will empty the 32 per-bit stacks
while trying to reduce the number of ADL statements output.
Stacks are empty when the stack tops reach the stack ends.
Algorithm:
---------
While stacks are not empty:
FindOptimalPop with given stack ends
Set temporary stack sizes to the offsets for the optimal pop
ConvertStacksToPops on the temporary stack ends
Store the calculated optimal pop in pListPops
Perform the optimal pops off the stacks
Endwhile
---------
For empty stacks, this just returns.
The output offsets from FindOptimalPop work as temporary stack sizes,
since everything ebove that must be popped off.
WARNING: This is recursive, and uses 268 bytes of stack for locals.
Therefore, a large worst-case ACL will blow the stack (if the recursion
is near the bottom of the stack at every step). Once ADL goes into OS,
this recursion can be rewritten as iteration by keeping a stack of the
StackTop and StackSize in the heap instead of locals, as a finite-state
stack machine.
Arguments:
pControl - The ADL parser spec, used for determining the number
of strings it will take to express a permission
pStacks - The stack representation of the DACL
pdwStackSize - These are offsets to 1 past the last element in the
stacks which should be considered
pdwStackTop - These are offsets to the tops of the stacks to be
considered
pListPops - The STL list containing the sequence of pop operations
to be performed, new pops are appended to this
Return Value:
none
--*/
{
DWORD pdwTmpStackTop[32];
DWORD pdwTmpStackSize[32];
DWORD dwIdx;
DWORD dwStacksPopped;
DWORD dwBlockSize;
//
// Start with top of given stack
//
for( dwIdx = 0; dwIdx < 32; dwIdx++ )
{
pdwTmpStackTop[dwIdx] = pdwStackTop[dwIdx];
}
//
// Stacks are empty when the top of each stack points to 1 past the end
// Therefore, we can just use a memory compare on the tops array and
// the stack sizes to check. Empty stacks have 0 size and top offset.
//
while( memcmp(pdwStackSize, pdwTmpStackTop, sizeof(DWORD) * 32) )
{
//
// The loop should end on empty stacks. Therefore, if this fails,
// we have an internal error. Otherwise, we have our optimal pop.
//
if( FALSE == FindOptimalPop(
pControl,
pStacks,
pdwStackSize,
pdwTmpStackTop,
&dwStacksPopped,
&dwBlockSize,
pdwTmpStackSize
) )
{
throw AdlStatement::ERROR_FATAL_ACL_CONVERT_ERROR;
}
//
// Now recurse, and pop off everything ABOVE the optimal pop
//
ConvertStacksToPops(
pControl,
pStacks,
pdwTmpStackSize,
pdwTmpStackTop,
pListPops
);
//
// Add the optimal pop to the list
//
pListPops->push_back(pair<DWORD, DWORD>(dwStacksPopped, dwBlockSize));
//
// Now update the tops of the stacks by lowering the tops by
// the block size
//
for( dwIdx = 0; dwIdx < 32; dwIdx++ )
{
if( (0x00000001 << dwIdx) & dwStacksPopped )
{
//
// By now we have removed everything ebove the optimal pop, and
// the optimal pop itself. Therefore, go dwBlockSize past the
// beginning of the optimal pop. Stacks other than those
// involved in the optimal pop cannot be effected.
//
pdwTmpStackTop[dwIdx] = pdwTmpStackSize[dwIdx] + dwBlockSize;
}
}
}
}
BOOL FindBlockInStack(
IN const PBIT_STACK_ELEM pBlock,
IN const DWORD dwBlockSize,
IN const PBIT_STACK_ELEM pStack,
IN const DWORD dwStackSize,
IN const DWORD dwStackTop,
OUT PDWORD pdwBlockStart
)
/*++
Routine Description:
Attempts to locate the first block of dwBlockSize which matches a block
of the same size at pBlock in the given stack pStack, between dwStackTop
and dwStackSize - 1. If successful, start offset of the block is stored in
pdwBlockStart.
Arguments:
pBlock - A single block (see GetBlockSize for definition)
for which to look for in pStack
dwBlockSize - The number of elements composing this block
pStack - The stack in which to look for pBlock
dwStackSize - Offset to 1 past the last element in the stack to
consider
dwStackTop - Offset to the effective beginning of the stack
pdwBlockStart - If successful, offset to the beginning of the
found block is returned in this.
Return Value:
TRUE if block found
FALSE otherwise, in which case *pdwBlockStart is undefined
--*/
{
//
// States used by this function
//
#define TMP2_NO_MATCH_STATE 0
#define TMP2_MATCH_STATE 1
DWORD dwState = TMP2_NO_MATCH_STATE;
DWORD dwMatchStartIdx;
DWORD dwIdxStack;
DWORD dwIdxBlock;
ASSERT( dwBlockSize > 0 );
ASSERT( dwStackTop <= dwStackSize );
for( dwIdxStack = dwStackTop, dwIdxBlock = 0;
dwIdxStack < dwStackSize;
dwIdxStack++ )
{
switch(dwState)
{
case TMP2_NO_MATCH_STATE:
//
// If the remaining stack is smaller than the block, no need to
// check further
//
if( dwStackSize - dwIdxStack < dwBlockSize )
{
return FALSE;
}
//
// Check for match start
//
if( pStack[dwIdxStack].bAllow == pBlock[dwIdxBlock].bAllow
&& pStack[dwIdxStack].dwFlags == pBlock[dwIdxBlock].dwFlags
&& EqualSid(pStack[dwIdxStack].pSid, pBlock[dwIdxBlock].pSid) )
{
//
// Special case: block size 1
//
if( dwBlockSize == 1 )
{
*pdwBlockStart = dwIdxStack;
return TRUE;
}
else
{
dwState = TMP2_MATCH_STATE;
dwMatchStartIdx = dwIdxStack;
dwIdxBlock++;
}
}
break;
case TMP2_MATCH_STATE:
//
// If still matched
//
if( pStack[dwIdxStack].bAllow == pBlock[dwIdxBlock].bAllow
&& pStack[dwIdxStack].dwFlags == pBlock[dwIdxBlock].dwFlags
&& EqualSid(pStack[dwIdxStack].pSid, pBlock[dwIdxBlock].pSid) )
{
dwIdxBlock++;
//
// Check for complete match
//
if( dwIdxBlock == dwBlockSize )
{
*pdwBlockStart = dwMatchStartIdx;
return TRUE;
}
}
else
{
//
// Backtrack to the match start
//
dwState = TMP2_NO_MATCH_STATE;
dwIdxBlock = 0;
dwIdxStack = dwMatchStartIdx;
}
break;
default:
throw AdlStatement::ERROR_FATAL_ACL_CONVERT_ERROR;
break;
}
}
//
// If never matched whole block, return FALSE
//
return FALSE;
}
BOOL FindOptimalPop(
IN const PADL_PARSER_CONTROL pControl,
IN const PBIT_STACK_ELEM pStacks[32],
IN const DWORD pdwStackSize[32],
IN const DWORD pdwStackTop[32],
OUT PDWORD pdwStacksPopped,
OUT PDWORD pdwBlockSize,
OUT DWORD pdwPopOffsets[32]
)
/*++
Routine Description:
Attempts to locate the greedy-choice optimal set of blocks to pop off.
Returns the optimal choice in the OUT values on success.
The weight function can be tweaked, and may allow negative values, however
the value of popping a single stack off the top MUST be positive.
Uses this algorithm:
-----------
Start with weight 0
For every non-empty stack:
Get top block of stack
Search all stacks for this block
Compute the weight of this solution based on block size and # of stacks
If the weight is greater than the current best weight:
store the new weight as best weight
store the new solution as best solution
Endif
Endfor
If weight > 0
Return the current best solution
Else
Report failure
Endif
-----------
Arguments:
pControl - The ADL parser spec, used for determining the number
of strings it will take to express a permission
pStacks - The stack representation of the DACL
pdwStackSize - These are offsets to 1 past the last element in the
stacks which should be considered
pdwStackTop - These are offsets to the tops of the stacks to be
considered
pdwStacksPopped - Bitmask for which stacks will be popped, stacks which
are effected by this pop have the appropriate bit set
pdwBlockSize - The size of the block (same for all effected stacks) to
be popped is returned through this.
pdwPopOffsets - The start offsets of the blocks to pop are returned here
Return Value:
TRUE on success, if a pop with weight > 0 has been found
FALSE otherwise, in which case OUT values are undefined
--*/
{
DWORD dwIdxStacks1;
DWORD dwIdxStacks2;
//
// Initial optimal solution
//
LONG iCurrentWeight = 0;
//
// Current solution
//
LONG iTempWeight;
DWORD dwTempStacksPopped;
DWORD dwTempBlockSize;
DWORD pdwTempPops[32];
DWORD dwBlockOffset;
//
// Try the block at the top of every stack
//
for( dwIdxStacks1 = 0; dwIdxStacks1 < 32; dwIdxStacks1++ )
{
//
// Skip empty stacks
//
if( pdwStackSize[dwIdxStacks1] == pdwStackTop[dwIdxStacks1] )
{
continue;
}
dwTempBlockSize = GetStackBlockSize(pStacks[dwIdxStacks1],
pdwStackTop[dwIdxStacks1],
pdwStackSize[dwIdxStacks1]);
//
// If a block size of 0 is detected in a non-empty stack, then
// this is not expressable in ADL, throw the error here
//
if( dwTempBlockSize == 0 )
{
throw AdlStatement::ERROR_INEXPRESSIBLE_ACL;
}
//
// The initial weight is 0, since loop will account for top loop stack
// Same with initial stacks popped
//
iTempWeight = 0;
dwTempStacksPopped = 0;
//
// Now, try to find this block in all stacks
//
for( dwIdxStacks2 = 0; dwIdxStacks2 < 32; dwIdxStacks2++ )
{
//
// Fist assume pop location is at the top (no pop),
// even for empty stacks
//
pdwTempPops[dwIdxStacks2] = pdwStackTop[dwIdxStacks2];
//
// Skip empty stacks
//
if( ! (pdwStackSize[dwIdxStacks2] - pdwStackTop[dwIdxStacks2] > 0) )
{
continue;
}
if( TRUE == FindBlockInStack(
&(pStacks[dwIdxStacks1][pdwStackTop[dwIdxStacks1]]),
dwTempBlockSize,
pStacks[dwIdxStacks2],
pdwStackSize[dwIdxStacks2],
pdwStackTop[dwIdxStacks2],
&dwBlockOffset) )
{
//
// Block was found in this stack
//
pdwTempPops[dwIdxStacks2] = dwBlockOffset;
dwTempStacksPopped |= ( 0x00000001 << dwIdxStacks2);
}
}
//
// Calculate the weight of this solution
//
//
// Weight for number of principals expressed
//
iTempWeight += (WEIGHT_STACK_HEIGHT) * dwTempBlockSize;
//
// Weights for each bit
// also weights (or penalties) for having to pop things
// off above the optimal pop
//
for( dwIdxStacks2 = 0; dwIdxStacks2 < 32; dwIdxStacks2++ )
{
if( dwTempStacksPopped & ( 0x00000001 << dwIdxStacks2 ) )
{
iTempWeight += (WEIGHT_PERM_BIT);
iTempWeight += (WEIGHT_ITEM_ABOVE_POP)
* ( pdwTempPops[dwIdxStacks2]
- pdwStackTop[dwIdxStacks2] );
}
}
//
// Finally the weight/penalty for number of permission names
// needed beyond the 1st one
//
iTempWeight += (WEIGHT_PERMISSION_NAME)
* (NumStringsForMask(pControl, dwTempStacksPopped) - 1 );
//
// If this solution is better than the best so far, save it
//
if( iTempWeight > iCurrentWeight )
{
iCurrentWeight = iTempWeight;
*pdwStacksPopped = dwTempStacksPopped;
*pdwBlockSize = dwTempBlockSize;
for( dwIdxStacks2 = 0; dwIdxStacks2 < 32; dwIdxStacks2++ )
{
pdwPopOffsets[dwIdxStacks2] = pdwTempPops[dwIdxStacks2];
}
}
}
//
// If we have not found any solution, we were passed an empty set of stacks
// Otherwise, the optimal solution is already in the OUT values
//
if( iCurrentWeight > 0 )
{
return TRUE;
}
else
{
return FALSE;
}
}
void ConvertDaclToStacks(
IN const PACL pDacl,
IN const PADL_PARSER_CONTROL pControl,
OUT DWORD pdwStackSize[32],
OUT PBIT_STACK_ELEM pStacks[32]
)
/*++
Routine Description:
Traverses the given ACL, allocates the 32 per-bit stacks, and fills them
with the ACL broken up per-bit. The allocated memory can be freed by
a SINGLE call to AdlStatement::FreeMemory(pStacks[0]), since the block
allocated is a single block.
Arguments:
pDacl - The DACL to convert
pControl - The ADL_PARSER_CONTROL, for permission mapping
pdwStackSize - The sizes of the stacks are returned here
pStacks - The pointers to the per-bit stacks are returned here
pStacks[0] should be freed later using
AdlStatement::FreeMemory
Return Value:
none
--*/
{
DWORD dwIdx;
DWORD dwIdx2;
DWORD dwTmp;
DWORD dwNumBlocksTotal = 0;
DWORD dwFlags;
ACCESS_MASK amMask;
BOOL bAllow;
PVOID pAce;
PSID pSid;
DWORD pdwStackCur[32];
for( dwIdx = 0; dwIdx < 32; dwIdx++ )
{
pdwStackSize[dwIdx] = 0;
pdwStackCur[dwIdx] = 0;
pStacks[dwIdx] = NULL;
}
//
// Determine amount of stack space needed for each stack
//
for( dwIdx = 0; dwIdx < pDacl->AceCount; dwIdx++ )
{
GetAce(pDacl, dwIdx, &pAce);
switch(((PACE_HEADER)pAce)->AceType)
{
case ACCESS_ALLOWED_ACE_TYPE:
amMask = ((PACCESS_ALLOWED_ACE)pAce)->Mask
& ~(pControl->amNeverSet | pControl->amSetAllow);
break;
case ACCESS_DENIED_ACE_TYPE:
amMask = ((PACCESS_DENIED_ACE)pAce)->Mask
& ~(pControl->amNeverSet | pControl->amSetAllow);
break;
default:
throw AdlStatement::ERROR_UNKNOWN_ACE_TYPE;
break;
}
for( dwIdx2 = 0, dwTmp = 0x00000001;
dwIdx2 < 32 ;
dwTmp <<= 1, dwIdx2++ )
{
if( dwTmp & amMask )
{
pdwStackSize[dwIdx2]++;
dwNumBlocksTotal++;
}
}
}
//
// Allocate the 32 stacks of pointers as a single memory chunk
//
pStacks[0] = (PBIT_STACK_ELEM)
new BYTE[dwNumBlocksTotal * sizeof(BIT_STACK_ELEM)];
if( pStacks[0] == NULL )
{
throw AdlStatement::ERROR_OUT_OF_MEMORY;
}
//
// Set the stack pointers to the proper locations in the single memory chunk
//
for( dwIdx = 1, dwTmp = pdwStackSize[0];
dwIdx < 32;
dwIdx++ )
{
if( pdwStackSize[dwIdx] > 0 )
{
pStacks[dwIdx] = &(pStacks[0][dwTmp]);
dwTmp += pdwStackSize[dwIdx];
}
}
ASSERT( dwTmp == dwNumBlocksTotal );
//
// Now go through the ACL again and fill in the stacks and advancing
// the pStacksCur pointers
// Stack sizes are known, so we treat the start of memory at TOP
// of stack
//
// Make sure to strip out the INHERITED_ACE flag from the ACEs,
// and handle the special access masks in the parser control
//
for( dwIdx = 0; dwIdx < pDacl->AceCount; dwIdx++ )
{
GetAce(pDacl, dwIdx, &pAce);
switch(((PACE_HEADER)pAce)->AceType)
{
case ACCESS_ALLOWED_ACE_TYPE:
dwFlags = ((PACCESS_ALLOWED_ACE)pAce)->Header.AceFlags
& ( CONTAINER_INHERIT_ACE | INHERIT_ONLY_ACE
| NO_PROPAGATE_INHERIT_ACE | OBJECT_INHERIT_ACE );
amMask = ((PACCESS_ALLOWED_ACE)pAce)->Mask
& ~(pControl->amNeverSet | pControl->amSetAllow);
bAllow = TRUE;
pSid = &(((PACCESS_ALLOWED_ACE)pAce)->SidStart);
break;
case ACCESS_DENIED_ACE_TYPE:
dwFlags = ((PACCESS_DENIED_ACE)pAce)->Header.AceFlags
& ( CONTAINER_INHERIT_ACE | INHERIT_ONLY_ACE
| NO_PROPAGATE_INHERIT_ACE | OBJECT_INHERIT_ACE );
amMask = ((PACCESS_DENIED_ACE)pAce)->Mask
& ~(pControl->amNeverSet | pControl->amSetAllow);
bAllow = FALSE;
pSid = &(((PACCESS_DENIED_ACE)pAce)->SidStart);
break;
default:
throw AdlStatement::ERROR_UNKNOWN_ACE_TYPE;
break;
}
for( dwIdx2 = 0, dwTmp = 0x00000001;
dwIdx2 < 32;
dwIdx2++, dwTmp <<= 1 )
{
if( dwTmp & amMask )
{
//
// Index should never reach size (1 past bottom) of the stack
//
ASSERT( pdwStackCur[dwIdx2] < pdwStackSize[dwIdx2] );
//
// Fill in the actual structure
//
pStacks[dwIdx2][pdwStackCur[dwIdx2]].bAllow = bAllow;
pStacks[dwIdx2][pdwStackCur[dwIdx2]].pSid = pSid;
pStacks[dwIdx2][pdwStackCur[dwIdx2]].dwFlags = dwFlags;
//
// Top of the stack is the top, but we fill the stack top-first
// So advance toward bottom of stack
//
pdwStackCur[dwIdx2]++;
}
}
}
#if DBG
//
// Now perform an additional check in debug only that all stacks have
// been filled as allocated
//
for( dwIdx = 0; dwIdx < 32; dwIdx++ )
{
ASSERT( pdwStackCur[dwIdx] == pdwStackSize[dwIdx] );
}
#endif
}
DWORD GetStackBlockSize(
IN const PBIT_STACK_ELEM pStack,
IN DWORD dwStartOffset,
IN DWORD dwStackSize
)
/*++
Routine Description:
Finds the size of the maximum 'block' in the current per-bit stack, from
the current position.
A block is defined to be a set of 0 or more consecutive deny per-bit
ACE entries immediately followed by 1 or more consecutive allow per-bit
ACE entries such that the inheritance masks are the same.
Therefore, a DENY without a matching allow is NOT a block. This is detected
when we are in the TMP_READ_DENY_STATE (indicating we have already read at
least one deny) and read either a deny or allow with a non-matching mask.
On the other hand, even a single ALLOW is a valid block. Therefore this
can only fail if dwStartOffset points to a deny.
Arguments:
pStack - The per-bit stack to check
dwStartOffset - Position to begin at (using that ace, not the next one)
dwStackSize - Maximum offset will be dwStackSize - 1, this should
never get called with dwStackSize of 0.
Return Value:
Number of entries in the block if successful
0 if not successful
--*/
{
//
// States used by this function
//
#define TMP_START_STATE 0
#define TMP_READ_DENY_STATE 1
#define TMP_READ_ALLOW_STATE 2
#define TMP_DONE_STATE 3
DWORD dwCurState = TMP_START_STATE;
DWORD dwCurOffset = dwStartOffset;
DWORD dwFlags;
ASSERT( dwStackSize > 0 );
ASSERT( dwStartOffset < dwStackSize );
//
// Returns are inside the loop, they will terminate it
//
while( ( dwCurState != TMP_DONE_STATE ) && ( dwCurOffset < dwStackSize ) )
{
switch( dwCurState )
{
case TMP_START_STATE:
dwFlags = pStack[dwCurOffset].dwFlags;
if( pStack[dwCurOffset].bAllow == FALSE ) // DENY entry
{
dwCurState = TMP_READ_DENY_STATE;
dwCurOffset++;
}
else // Otherwise an ALLOW entry
{
dwCurState = TMP_READ_ALLOW_STATE;
dwCurOffset++;
}
break;
case TMP_READ_DENY_STATE:
//
// If we are in this state, and find an entry with non-matching
// flags, this means no valid block is possible, return 0
//
if( pStack[dwCurOffset].dwFlags != dwFlags )
{
//
// Set end offset to indicate 0 block size and finish
// This indicates there is no valid block
//
dwCurState = TMP_DONE_STATE;
dwCurOffset = dwStartOffset;
}
else
{
if( pStack[dwCurOffset].bAllow == FALSE )
{
//
// Another deny, stay in same state
//
dwCurOffset++;
}
else
{
//
// Allow with matching flags, go into allow state
//
dwCurState = TMP_READ_ALLOW_STATE;
dwCurOffset++;
}
}
break;
case TMP_READ_ALLOW_STATE:
//
// If we are in this state, we have read 0 or more denies and
// at least 1 allow. Therefore, we already have a block, so we
// just need to find its end and return.
//
if( (dwFlags == pStack[dwCurOffset].dwFlags)
&& (pStack[dwCurOffset].bAllow == TRUE) )
{
//
// Another matching allow
//
dwCurOffset++;
}
else
{
//
// End of block found
//
dwCurState = TMP_DONE_STATE;
}
break;
}
}
//
// Two ways to reach this point, hitting the bottom of the stack or
// finding the end of the block (or lack thereof). In both cases, the
// size of the block is dwCurOffset - dwStartOffset. In case of no
// valid block, this will evaluate to 0.
//
return dwCurOffset - dwStartOffset;
}
DWORD NumStringsForMask(
IN const PADL_PARSER_CONTROL pControl,
IN ACCESS_MASK amMask
)
/*++
Routine Description:
Determines the number of permission names which would be required
to express the access mask
Arguments:
pControl - This contains the mapping between permission names and masks
amMask - The mask to represent
Return Value:
DWORD - The number of strings which would be required
--*/
{
ACCESS_MASK amOptional = amMask;
DWORD dwIdx = 0;
DWORD dwNumStrings = 0;
while( amMask != 0
&& (pControl->pPermissions )[dwIdx].str != NULL )
{
//
// If all the bits representing the string are present in the whole mask
// and at least some of the bits have not yet been represented
// by another string, add this string to the list and remove the
// bits from amMask (representing the still required bits)
//
if( ( (amOptional & (pControl->pPermissions )[dwIdx].mask)
== (pControl->pPermissions )[dwIdx].mask )
&& (amMask & (pControl->pPermissions )[dwIdx].mask))
{
amMask &= ~(pControl->pPermissions )[dwIdx].mask;
dwNumStrings++;
}
dwIdx++;
}
//
// If any of the rights are not mapped, throw an exception
//
if( amMask != 0 )
{
throw AdlStatement::ERROR_UNKNOWN_ACCESS_MASK;
}
return dwNumStrings;
}
////////////////////////////////////////////////////////////////////////////////
/////////////
///////////// Conversion from ADL to DACL
/////////////
////////////////////////////////////////////////////////////////////////////////
void AdlStatement::WriteToDacl(OUT PACL * ppDacl)
/*++
Routine Description:
Creates a DACL representative of the AdlStatement structure.
The PACL to the DACL is stored in ppDacl. It should be freed
with the AdlStatement::FreeMemory() function.
The algorithm is very straightforward, it's just a linear conversion
from ADL to a DACL, taking each ADL substatement and creating deny ACEs
for every ExPrincipal and allow ACEs for Principals.
Arguments:
ppDacl - A pointer to the allocated DACL is stored in *pDacl
Return Value:
none
--*/
{
//
// If not initialized, do not output
//
if( _bReady == FALSE )
{
throw AdlStatement::ERROR_NOT_INITIALIZED;
}
//
// Mapping from token *'s to SIDs
//
map<const AdlToken *, PSID> mapTokSid;
//
// Mapping from Adl substatements (AdlTree *'s) to their access mask
// This is before the special treatment masks are applied
//
map<const AdlTree *, ACCESS_MASK> mapTreeMask;
//
// Iterators which are reused
//
list<AdlTree *>::iterator iterTrees;
list<AdlTree *>::iterator iterTreesEnd;
list<const AdlToken *>::iterator iterTokens;
list<const AdlToken *>::iterator iterTokensEnd;
ACCESS_MASK amMask;
stack<PBYTE> stackToFree;
PBYTE pbLastAllocated;
DWORD dwAclSize = sizeof(ACL);
PACL pAcl = NULL;
try {
//
// Do a single LSA lookup, convert all at once
// SIDs will need to be deleted by retrieving them from the map
//
ConvertNamesToSids(&mapTokSid);
//
// Calculate the ACL size
//
for(iterTrees = _lTree.begin(), iterTreesEnd = _lTree.end();
iterTrees != iterTreesEnd;
iterTrees++)
{
//
// Now go through the Principals
//
for(iterTokens = (*iterTrees)->GetPrincipals()->begin(),
iterTokensEnd = (*iterTrees)->GetPrincipals()->end();
iterTokens != iterTokensEnd;
iterTokens ++)
{
dwAclSize += (
sizeof(ACCESS_ALLOWED_ACE)
- sizeof(DWORD)
+ GetLengthSid((*(mapTokSid.find(*iterTokens))).second)
);
}
//
// And the ExPrincipals
//
for(iterTokens = (*iterTrees)->GetExPrincipals()->begin(),
iterTokensEnd = (*iterTrees)->GetExPrincipals()->end();
iterTokens != iterTokensEnd;
iterTokens ++)
{
dwAclSize += (
sizeof(ACCESS_DENIED_ACE)
- sizeof(DWORD)
+ GetLengthSid((*(mapTokSid.find(*iterTokens))).second)
);
}
//
// Calculate the effective permissions ahead of time
//
amMask = 0;
for(iterTokens = (*iterTrees)->GetPermissions()->begin(),
iterTokensEnd = (*iterTrees)->GetPermissions()->end();
iterTokens != iterTokensEnd;
iterTokens ++)
{
amMask |= MapTokenToMask(*iterTokens);
}
//
// And enter the AdlTree *, Mask pair into the map
//
mapTreeMask[*iterTrees] = amMask;
}
//
// Allocate the ACL
//
pAcl = (PACL)new BYTE[dwAclSize];
if( pAcl == NULL )
{
throw AdlStatement::ERROR_OUT_OF_MEMORY;
}
//
// Initialize the ACL
//
if( ! InitializeAcl(pAcl, dwAclSize, ACL_REVISION_DS))
{
throw AdlStatement::ERROR_ACL_API_FAILED;
}
//
// Now go through the substatements and create the ACEs
//
for(iterTrees = _lTree.begin(), iterTreesEnd = _lTree.end();
iterTrees != iterTreesEnd;
iterTrees++)
{
//
// First add the denies for this statement
//
for(iterTokens = (*iterTrees)->GetExPrincipals()->begin(),
iterTokensEnd = (*iterTrees)->GetExPrincipals()->end();
iterTokens != iterTokensEnd;
iterTokens ++)
{
if( ! AddAccessDeniedAceEx(
pAcl,
ACL_REVISION_DS,
(*iterTrees)->GetFlags(),
( mapTreeMask[*iterTrees]
& ~_pControl->amSetAllow)
& ~_pControl->amNeverSet,
mapTokSid[*iterTokens] ))
{
throw AdlStatement::ERROR_ACL_API_FAILED;
}
}
//
// Now go through the Principals, add allows
//
for(iterTokens = (*iterTrees)->GetPrincipals()->begin(),
iterTokensEnd = (*iterTrees)->GetPrincipals()->end();
iterTokens != iterTokensEnd;
iterTokens ++)
{
if( ! AddAccessAllowedAceEx(
pAcl,
ACL_REVISION_DS,
(*iterTrees)->GetFlags(),
(mapTreeMask[*iterTrees]
| _pControl->amSetAllow)
& ~_pControl->amNeverSet,
mapTokSid[*iterTokens] ))
{
throw AdlStatement::ERROR_ACL_API_FAILED;
}
}
}
}
catch(...)
{
if( pAcl != NULL )
{
//
// Memory allocated for ACL
//
delete[] (PBYTE)pAcl;
}
//
// Memory allocated for the SIDs
//
while( !mapTokSid.empty() )
{
delete[] (PBYTE) (*(mapTokSid.begin())).second;
mapTokSid.erase(mapTokSid.begin());
}
//
// and pass the exception along
//
throw;
}
//
// Free the SIDs if done, since they are copied into the ACL
//
while( !mapTokSid.empty() )
{
delete[] (PBYTE) (*(mapTokSid.begin())).second;
mapTokSid.erase(mapTokSid.begin());
}
//
// The DACL is returned, so it should not be deallocated
//
*ppDacl = pAcl;
}
////////////////////////////////////////////////////////////////////////////////
/////////////
///////////// Utility functions
/////////////
////////////////////////////////////////////////////////////////////////////////
bool AdlCompareStruct::operator()(IN const PSID pSid1,
IN const PSID pSid2 ) const
/*++
Routine Description:
This is a less-than function which places a complete ordering on
a set of PSIDs by value, NULL PSIDs are valid. This is used by the
STL map.
Since the number of subauthorities appears before the subauthorities
themselves, that difference will be noticed for two SIDs of different
size before the memcmp tries to access the nonexistant subauthority
in the shorter SID, therefore an access violation will not occur.
Arguments:
pSid1 - First PSID
pSid2 - Second PSID
Return Value:
Returns TRUE iff SID1 < SID2
FALSE otherwise
--*/
{
//
// If both are NULL, false should be returned for complete ordering
//
if(pSid2 == NULL)
{
return false;
}
if(pSid1 == NULL)
{
return true;
}
if( memcmp(pSid1, pSid2, GetLengthSid(pSid1)) < 0 )
{
return true;
}
else
{
return false;
}
}
bool AdlCompareStruct::operator()(IN const WCHAR * sz1,
IN const WCHAR * sz2 ) const
/*++
Routine Description:
operator() compares two null-terminated WCHAR* strings, case-INSENSITIVE.
Arguments:
sz1 - First string
sz2 - Second string
Return Value:
Returns TRUE iff sz1 < sz2
FALSE otherwise
--*/
{
return ( _wcsicmp(sz1, sz2) < 0 );
}
void AdlStatement::MapMaskToStrings(
IN ACCESS_MASK amMask,
IN OUT list<WCHAR *> *pList ) const
/*++
Routine Description:
Converts the given access mask into a list of const WCHAR *'s representing
the possibly overlapping permission strings which, when combined by a
bitwise OR, are equal to the access mask given. Throws exception if
a given access mask cannot be represented by the user-specified permissions.
The WCHAR * pointers are const, and should not be deallocated.
Arguments:
amMask - The mask to represent
pList - An allocated STL list in which to store the pointers
Return Value:
none
--*/
{
ACCESS_MASK amOptional = amMask;
DWORD dwIdx = 0;
while( amMask != 0
&& (_pControl->pPermissions )[dwIdx].str != NULL )
{
//
// If all the bits representing the string are present in the whole mask
// and at least some of the bits have not yet been represented
// by another string, add this string to the list and remove the
// bits from amMask (representing the still required bits)
//
if( ( (amOptional & (_pControl->pPermissions )[dwIdx].mask)
== (_pControl->pPermissions )[dwIdx].mask )
&& (amMask & (_pControl->pPermissions )[dwIdx].mask))
{
amMask &= ~(_pControl->pPermissions )[dwIdx].mask;
pList->push_back((_pControl->pPermissions )[dwIdx].str);
}
dwIdx++;
}
//
// If any of the rights are not mapped, throw an exception
//
if( amMask != 0 )
{
throw AdlStatement::ERROR_UNKNOWN_ACCESS_MASK;
}
}
void AdlStatement::ConvertSidsToNames(
IN const PACL pDacl,
IN OUT map<const PSID, const AdlToken *> * mapSidsNames
)
/*++
Routine Description:
Traverses a DACL, and creates string representations of every SID
found in the DACL. Returns them in the provided map. The newly allocated
tokens will get garbage-collected by the AdlStatement later, no need
to free manually. Since the PSIDs used are the same as in the ACL itself,
we don't need to map by value, since pointer uniqueness is guaranteed here.
Arguments:
pDacl - DACL to traverse
mapSidNames - Where to store the resulting mapping
Return Value:
none
--*/
{
AdlStatement::ADL_ERROR_TYPE adlError = AdlStatement::ERROR_NO_ERROR;
DWORD dwIdx = 0;
LPVOID pAce = NULL;
AdlToken *pTok = NULL;
AdlToken *pTokLastAllocated = NULL;
wstring wsName, wsDomain;
NTSTATUS ntErr = STATUS_SUCCESS;
LSA_HANDLE LsaPolicy;
PLSA_REFERENCED_DOMAIN_LIST RefDomains = NULL;
PLSA_TRANSLATED_NAME Names = NULL;
LSA_OBJECT_ATTRIBUTES LsaObjectAttributes;
//
// Traverse the ACL to get the list of SIDs used
//
PSID *ppSids = NULL;
ppSids = (PSID *) new BYTE[sizeof(PSID) * pDacl->AceCount];
if( ppSids == NULL )
{
adlError = AdlStatement::ERROR_OUT_OF_MEMORY;
goto error;
}
for(dwIdx = 0; dwIdx < pDacl->AceCount; dwIdx++)
{
GetAce(pDacl, dwIdx, &pAce);
switch( ((ACE_HEADER *)pAce)->AceType )
{
case ACCESS_ALLOWED_ACE_TYPE:
ppSids[dwIdx] = &( ((ACCESS_ALLOWED_ACE *)pAce)->SidStart);
break;
case ACCESS_DENIED_ACE_TYPE:
ppSids[dwIdx] = &( ((ACCESS_DENIED_ACE *)pAce)->SidStart);
break;
default:
adlError = AdlStatement::ERROR_UNKNOWN_ACE_TYPE;
goto error;
break;
}
}
//
// Look up all SIDs, getting user and domain names, single LSA call
//
LsaObjectAttributes.Length = sizeof(LSA_OBJECT_ATTRIBUTES);
LsaObjectAttributes.RootDirectory = NULL;
LsaObjectAttributes.ObjectName = NULL;
LsaObjectAttributes.Attributes = 0;
LsaObjectAttributes.SecurityDescriptor = NULL;
LsaObjectAttributes.SecurityQualityOfService = NULL;
ntErr = LsaOpenPolicy(
NULL,
&LsaObjectAttributes,
POLICY_LOOKUP_NAMES,
&LsaPolicy);
if( ntErr != STATUS_SUCCESS )
{
adlError = AdlStatement::ERROR_LSA_FAILED;
goto error;
}
//
// Garbage collect later
//
ntErr = LsaLookupSids(LsaPolicy,
pDacl->AceCount,
ppSids,
&RefDomains,
&Names);
LsaClose(LsaPolicy);
if( ntErr != ERROR_SUCCESS )
{
if( (ntErr == STATUS_SOME_NOT_MAPPED) || (ntErr == STATUS_NONE_MAPPED) )
{
adlError = AdlStatement::ERROR_UNKNOWN_SID;
}
else
{
adlError = AdlStatement::ERROR_LSA_FAILED;
}
goto error;
}
//
// Now traverse the list ppSids, creating matching tokens for the
// SIDs in the ACL.
//
try
{
for(dwIdx = 0; dwIdx < pDacl->AceCount; dwIdx++)
{
pTok = NULL;
//
// LSA Strings not terminated, create terminated version
// LSA buffer sizes in bytes, not wchars
//
assert(Names[dwIdx].DomainIndex >= 0);
wsName.assign(Names[dwIdx].Name.Buffer,
Names[dwIdx].Name.Length / sizeof(WCHAR));
//
// If builtin, no need for domain info
//
if(Names[dwIdx].Use == SidTypeWellKnownGroup)
{
pTok = new AdlToken(wsName.c_str(), 0, 0);
}
else
{
wsDomain.assign(
RefDomains->Domains[Names[dwIdx].DomainIndex].Name.Buffer,
RefDomains->Domains[Names[dwIdx].DomainIndex].Name.Length
/ sizeof(WCHAR));
pTok = new AdlToken(wsName.c_str(), wsDomain.c_str(), 0, 0);
}
if( pTok == NULL )
{
adlError = AdlStatement::ERROR_OUT_OF_MEMORY;
goto error;
}
else
{
//
// This will be deleted immediately if we cannot save the token
// for later deallocation
//
pTokLastAllocated = pTok;
}
AddToken(pTok); // For later garbage collection
//
// No need ot delete immedeately, since no exception thrown
//
pTokLastAllocated = NULL;
(*mapSidsNames)[(ppSids[dwIdx])] = pTok;
}
}
catch(exception)
{
adlError = AdlStatement::ERROR_OUT_OF_MEMORY;
goto error;
}
//
// Done with the SIDs and LSA info, deallocate
//
error:;
if( RefDomains != NULL )
{
LsaFreeMemory(RefDomains);
}
if( Names != NULL )
{
LsaFreeMemory(Names);
}
if( ppSids != NULL )
{
delete[] (PBYTE) ppSids;
}
if( adlError != AdlStatement::ERROR_NO_ERROR )
{
throw adlError;
}
}
ACCESS_MASK AdlStatement::MapTokenToMask(
IN const AdlToken * tokPermission
)
/*++
Routine Description:
This routine maps a string represening a right to the matching access bits
using the user-supplied mapping.
This assumes that there are no pairs with ACCESS_MASK of 0 in the
user-supplied mapping.
Arguments:
tokPermission - The permission token to be looked up
Return Value:
ACCESS_MASK - The corresponding access mask
--*/
{
ACCESS_MASK amMask = 0;
DWORD dwIdx = 0;
//
// The token should never have a second value
//
if( tokPermission->GetOptValue() != NULL )
{
throw AdlStatement::ERROR_FATAL_PARSER_ERROR;
}
while(amMask == 0 && (_pControl->pPermissions)[dwIdx].str != NULL)
{
if(0 == _wcsicmp(tokPermission->GetValue(),
(_pControl->pPermissions)[dwIdx].str ))
{
amMask = (_pControl->pPermissions)[dwIdx].mask;
}
++dwIdx;
}
//
// If mask was not matched, throw exception
//
if(amMask == 0)
{
SetErrorToken(tokPermission);
throw AdlStatement::ERROR_UNKNOWN_PERMISSION;
}
return amMask;
}
void AdlStatement::ConvertNamesToSids(
IN OUT map<const AdlToken *, PSID> * mapTokSid
)
/*++
Routine Description:
This routine traverses all AdlTree's in the AdlStatement, and creates a list
of all usernames used. It then makes a single LSA call, and creates a map
of name AdlToken*'s to PSIDs, for later use by the conversion function.
The newly allocated PSIDs are stored in the map. They should be freed
using the AdlStatement::FreeMemory() function.
On error, any PSIDs that have been added to the map are deleted.
Arguments:
mapTokSid - Allocated map to which the Token,PSID entries should
be added. This MUST be empty. Otherwise, on
error, externally allocated memory would get
freed here.
Return Value:
none
--*/
{
list<AdlTree *>::iterator iterTrees;
list<AdlTree *>::iterator iterTreesEnd;
list<const AdlToken *>::iterator iterTokens;
list<const AdlToken *>::iterator iterTokensEnd;
//
// List of all Principal tokens used, allows for a single tree traversal
//
list<const AdlToken *> listAllPrincipals;
//
// Mapping from PLSA_STRING to AdlToken, for detecting which
// username is invalid
//
map<DWORD, const AdlToken *> mapIdxToken;
//
// Delayed garbage collection
//
stack<PBYTE> stackToFree;
void * pLastAllocated = NULL;
AdlStatement::ADL_ERROR_TYPE adlError = AdlStatement::ERROR_NO_ERROR;
DWORD dwDomainSidSize;
DWORD numNames;
DWORD dwIdx;
PSID pSidTemp;
LSA_HANDLE LsaPolicy;
PLSA_UNICODE_STRING pLsaStrings;
PLSA_REFERENCED_DOMAIN_LIST RefDomains = NULL;
PLSA_TRANSLATED_SID TranslatedSids = NULL;
LSA_OBJECT_ATTRIBUTES LsaObjectAttributes;
LsaObjectAttributes.Length = sizeof(LSA_OBJECT_ATTRIBUTES);
LsaObjectAttributes.RootDirectory = NULL;
LsaObjectAttributes.ObjectName = NULL;
LsaObjectAttributes.Attributes = 0;
LsaObjectAttributes.SecurityDescriptor = NULL;
LsaObjectAttributes.SecurityQualityOfService = NULL;
//
// Verify that the input map is empty as required
//
if( !(*mapTokSid).empty() )
{
throw AdlStatement::ERROR_FATAL_PARSER_ERROR;
}
//
// STL throws exceptions, catch them here
//
try
{
//
// Determine total number of names and place them all in the list
//
for(numNames = 0, iterTrees = _lTree.begin(), iterTreesEnd = _lTree.end();
iterTrees != iterTreesEnd;
iterTrees++)
{
iterTokensEnd = (*iterTrees)->GetPrincipals()->end();
for(iterTokens = (*iterTrees)->GetPrincipals()->begin();
iterTokens != iterTokensEnd; iterTokens ++)
{
numNames++;
listAllPrincipals.push_back(*iterTokens);
}
iterTokensEnd = (*iterTrees)->GetExPrincipals()->end();
for(iterTokens = (*iterTrees)->GetExPrincipals()->begin();
iterTokens != iterTokensEnd; iterTokens ++)
{
numNames++;
listAllPrincipals.push_back(*iterTokens);
}
}
//
// Allocate the needed memory for the LSA name list
//
pLsaStrings = (PLSA_UNICODE_STRING)
new BYTE[numNames * sizeof(LSA_UNICODE_STRING)];
if( pLsaStrings == NULL )
{
adlError = AdlStatement::ERROR_OUT_OF_MEMORY;
goto error;
}
else
{
pLastAllocated = pLsaStrings;
stackToFree.push( (PBYTE)pLsaStrings );
pLastAllocated = NULL;
}
//
// Retrieve name strings here in proper format, DOMAIN\USER
//
for(iterTokens = listAllPrincipals.begin(),
dwIdx = 0,
iterTokensEnd = listAllPrincipals.end();
iterTokens != iterTokensEnd;
iterTokens ++, dwIdx++)
{
//
// Name may be with domain, or just username
//
if( (*iterTokens)->GetOptValue() != NULL )
{
//
// Extra 1 wchar for the '\' character, 2 bytes per wchar
//
pLsaStrings[dwIdx].Length = sizeof(WCHAR) *
( wcslen((*iterTokens)->GetValue()) +
wcslen((*iterTokens)->GetOptValue()) + 1);
}
else
{
pLsaStrings[dwIdx].Length = sizeof(WCHAR) *
(wcslen((*iterTokens)->GetValue()) + 1);
}
pLsaStrings[dwIdx].MaximumLength = pLsaStrings[dwIdx].Length
+ sizeof(WCHAR);
pLsaStrings[dwIdx].Buffer =
(LPTSTR)new BYTE[pLsaStrings[dwIdx].MaximumLength];
if( pLsaStrings[dwIdx].Buffer == NULL )
{
adlError = AdlStatement::ERROR_OUT_OF_MEMORY;
goto error;
}
else
{
pLastAllocated = pLsaStrings[dwIdx].Buffer;
stackToFree.push((PBYTE)(pLsaStrings[dwIdx].Buffer));
pLastAllocated = NULL;
mapIdxToken[dwIdx] = *iterTokens;
}
if( (*iterTokens)->GetOptValue != NULL )
{
wsprintf( (LPTSTR)(pLsaStrings[dwIdx].Buffer),
L"%s%c%s",
(*iterTokens)->GetOptValue(),
_pControl->pLang->CH_SLASH,
(*iterTokens)->GetValue() );
}
else
{
wsprintf( (LPTSTR)(pLsaStrings[dwIdx].Buffer),
L"%s",
(*iterTokens)->GetValue() );
}
}
//
// Open the LSA policy
//
NTSTATUS ntErr;
ntErr = LsaOpenPolicy(
NULL,
&LsaObjectAttributes,
POLICY_LOOKUP_NAMES,
&LsaPolicy);
if( ntErr != STATUS_SUCCESS )
{
adlError = AdlStatement::ERROR_LSA_FAILED;
goto error;
}
//
// Now perform the LsaLookupNames call
//
ntErr = LsaLookupNames(
LsaPolicy,
numNames,
pLsaStrings,
&RefDomains,
&TranslatedSids);
//
// Free the LSA handle
//
LsaClose(LsaPolicy);
//
// Check for any unknown names
//
if( ntErr != STATUS_SUCCESS )
{
adlError = AdlStatement::ERROR_LSA_FAILED;
if( ntErr == STATUS_SOME_NOT_MAPPED || ntErr == STATUS_NONE_MAPPED )
{
adlError = AdlStatement::ERROR_UNKNOWN_USER;
//
// Find first unknown name and return it to user
//
for( dwIdx = 0; dwIdx < numNames; dwIdx++ )
{
if( TranslatedSids[dwIdx].Use == SidTypeInvalid ||
TranslatedSids[dwIdx].Use == SidTypeUnknown )
{
SetErrorToken(mapIdxToken[dwIdx]);
adlError = AdlStatement::ERROR_UNKNOWN_USER;
}
}
}
goto error;
}
//
// Assume all names now mapped if this point is reached
// Traverse all tokens again, pairing them with SIDs
//
for(iterTokens = listAllPrincipals.begin(),
dwIdx = 0,
iterTokensEnd = listAllPrincipals.end();
iterTokens != iterTokensEnd;
iterTokens ++, dwIdx++)
{
//
// Make sure all domains were looked up successfuly
// Invalid SIDs caught earlier
//
assert(TranslatedSids[dwIdx].DomainIndex >= 0);
dwDomainSidSize = GetLengthSid(
RefDomains->Domains[TranslatedSids[dwIdx].DomainIndex].Sid);
//
// One more RID for the user
//
pSidTemp = new BYTE[dwDomainSidSize + sizeof(DWORD)];
if( pSidTemp == NULL )
{
adlError = AdlStatement::ERROR_OUT_OF_MEMORY;
goto error;
}
//
// Copy the domain SID
//
CopySid(dwDomainSidSize + sizeof(DWORD), pSidTemp,
RefDomains->Domains[TranslatedSids[dwIdx].DomainIndex].Sid);
//
// If the SID is not a domain SID, and is valid, then we need to add
// the last RID. If domain SID, then referenced domain is the only
// SID we need, and we already have copied it
//
if( TranslatedSids[dwIdx].Use != SidTypeDomain )
{
((SID *)pSidTemp)->SubAuthority[
((SID *)pSidTemp)->SubAuthorityCount
] = TranslatedSids[dwIdx].RelativeId;
//
// Add 1 more subauthority
//
((SID *)pSidTemp)->SubAuthorityCount++;
}
//
// If this fails, need to allocate the single uninserted SID
// Other SIDs will be deallocated externally
//
pLastAllocated = pSidTemp;
(*mapTokSid)[(*iterTokens)] = pSidTemp;
pLastAllocated = NULL;
}
}
//
// Catch STL exceptions here, if exception is thrown, either the above
// code is wrong, or out of memory. Assume out of memory.
//
catch(exception ex)
{
adlError = AdlStatement::ERROR_OUT_OF_MEMORY;
}
error:;
//
// Garbage collection
//
if( RefDomains != NULL)
{
LsaFreeMemory(RefDomains);
}
if( TranslatedSids != NULL)
{
LsaFreeMemory(TranslatedSids);
}
//
// If the grabage stack threw an exception, deallocate last allocated object
//
if( pLastAllocated != NULL )
{
delete[] (PBYTE)pLastAllocated;
}
while( ! stackToFree.empty() )
{
//
// If popping a stack causes an STL exception, we have bigger problems
// than memory leaks
//
delete[] stackToFree.top();
stackToFree.pop();
}
//
// If any other error code happened earlier, pass it on
//
if( adlError != AdlStatement::ERROR_NO_ERROR )
{
while( !(*mapTokSid).empty() )
{
delete[] (PBYTE) (*((*mapTokSid).begin())).second;
(*mapTokSid).erase( (*mapTokSid).begin() );
}
throw adlError;
}
}