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

893 lines
21 KiB
C++

/*++
Copyright (c) 2000 Microsoft Corporation
Module Name:
adlstat.cpp
Abstract:
Implementation of AdlStatement and AdlTree class methods
Author:
t-eugenz - August 2000
Environment:
User mode only.
Revision History:
Created - August 2000
--*/
#include "adl.h"
#include <set>
void AdlStatement::ReadFromDacl(IN const PACL pDacl)
/*++
Routine Description:
Empties anything in the current ADL statement, and attempts to fill it with
the ADL representation of the given DACL.
Arguments:
pDacl - The DACL from which to construct the statement
Return Value:
none
--*/
{
//
// Start with cleanup
//
Cleanup();
try
{
ConvertFromDacl(pDacl);
}
catch(exception)
{
throw AdlStatement::ERROR_OUT_OF_MEMORY;
}
_bReady = TRUE;
}
void AdlStatement::ReadFromString(IN const WCHAR *szInput)
/*++
Routine Description:
Empties anything in the current ADL statement, and attempts to fill it with
the parsed version of the ADL statement szInput.
Arguments:
szInput - Input string in the ADL language describing the
permissions
Return Value:
none
--*/
{
//
// Start with cleanup
//
Cleanup();
//
// Manually create first AdlTree, since the parser only creates
// new trees AFTER completing an ADL_STATEMENT. At the end, the
// ParseAdl function itself removes the extra empty tree
// pushed on
//
this->Next();
try
{
ParseAdl(szInput);
}
catch(exception)
{
Cleanup();
throw AdlStatement::ERROR_OUT_OF_MEMORY;
}
//
// If no exceptions thrown, the instance is ready for output
//
_bReady = TRUE;
}
AdlStatement::~AdlStatement()
/*++
Routine Description:
Destructor for the AdlStatement
Uses the private Cleanup() function to deallocate
Arguments:
none
Return Value:
none
--*/
{
this->Cleanup();
}
void AdlStatement::Cleanup()
/*++
Routine Description:
Cleans up any memory used by the parse tree and any allocated tokens
Arguments:
none
Return Value:
none
--*/
{
_bReady = FALSE;
this->_tokError = NULL;
while( !_lTree.empty() )
{
delete _lTree.front();
_lTree.pop_front();
}
while( !_AllocatedTokens.empty() )
{
delete _AllocatedTokens.top();
_AllocatedTokens.pop();
}
}
AdlTree * AdlStatement::Cur()
/*++
Routine Description:
This protected method returns the current AdlTree being filled in by the
parser. It is only used by the ParseAdl function when it fills in the
AdlTree structures
Arguments:
none
Return Value:
AdlTree * - non-const pointer to the AdlTree
--*/
{
return *_iter;
}
void AdlStatement::Next()
/*++
Routine Description:
This protected method constructs a new AdlTree and pushes it on top of the
list (to make it accessable by this->Cur())
It is only used by the ParseAdl function after completing an ADL_STATEMENT
production, and by the AdlStatement constructor (once).
Arguments:
none
Return Value:
none
--*/
{
AdlTree *pAdlTree = new AdlTree();
if( pAdlTree == NULL )
{
throw AdlStatement::ERROR_OUT_OF_MEMORY;
}
try
{
_lTree.push_back(pAdlTree);
_iter = --_lTree.end();
}
catch(...)
{
delete pAdlTree;
throw;
}
}
void AdlStatement::PopEmpty()
/*++
Routine Description:
This protected method pops the extra empty AdlTree added by the ParseAdl
function after completing the last ADL_STATEMENT.
Arguments:
none
Return Value:
none
--*/
{
delete _lTree.back();
_lTree.pop_back();
_iter = -- _lTree.end();
}
void AdlStatement::AddToken(AdlToken *tok)
/*++
Routine Description:
This protected method is used by AdlStatement and friend classes to keep
track of tokens which need to be garbage collected later. Tokens need
to be kept around because they are used in the AdlTrees, and in error
handling.
Arguments:
tok - Pointer to the token to be deleted when ~this is called
Return Value:
none
--*/
{
_AllocatedTokens.push(tok);
}
void AdlStatement::WriteToString(OUT wstring *pSz)
/*++
Routine Description:
This routine prints the AdlStatement structure as a statement in the ADL
language to stdout. This will be replaced when the ADL semantics are
finalized.
Arguments:
none
Return Value:
none
--*/
{
if( _bReady == FALSE )
{
throw AdlStatement::ERROR_NOT_INITIALIZED;
}
list<AdlTree *>::iterator iter, iterEnd;
for(iter = _lTree.begin(), iterEnd = _lTree.end();
iter != iterEnd;
iter++)
{
(*iter)->PrintAdl(pSz, _pControl);
pSz->append(&(_pControl->pLang->CH_NEWLINE), 1);
}
}
void AdlStatement::ValidateParserControl()
/*++
Routine Description:
This validates the ADL_PARSER_CONTROL structure referenced by this
AdlStatement instance
Arguments:
none
Return Value:
none
--*/
{
try
{
//
// Test to verify that all characters are unique
// set.insert returns a pair, with 2nd element being a bool, which
// is true iff an insertion occured. Set cannot have duplicates.
//
set<WCHAR> sChars;
if(
!sChars.insert(_pControl->pLang->CH_NULL).second ||
!sChars.insert(_pControl->pLang->CH_SPACE).second ||
!sChars.insert(_pControl->pLang->CH_TAB).second ||
!sChars.insert(_pControl->pLang->CH_NEWLINE).second ||
!sChars.insert(_pControl->pLang->CH_RETURN).second ||
!sChars.insert(_pControl->pLang->CH_QUOTE).second ||
!sChars.insert(_pControl->pLang->CH_COMMA).second ||
!sChars.insert(_pControl->pLang->CH_SEMICOLON).second ||
!sChars.insert(_pControl->pLang->CH_OPENPAREN).second ||
!sChars.insert(_pControl->pLang->CH_CLOSEPAREN).second ||
!sChars.insert(_pControl->pLang->CH_AT).second ||
!sChars.insert(_pControl->pLang->CH_SLASH).second ||
!sChars.insert(_pControl->pLang->CH_PERIOD).second
)
{
throw AdlStatement::ERROR_INVALID_PARSER_CONTROL;
}
//
// Check all strings for null pointers
//
if(
_pControl->pLang->SZ_TK_AND == NULL ||
_pControl->pLang->SZ_TK_EXCEPT == NULL ||
_pControl->pLang->SZ_TK_ON == NULL ||
_pControl->pLang->SZ_TK_ALLOWED == NULL ||
_pControl->pLang->SZ_TK_AS == NULL ||
_pControl->pLang->SZ_TK_THIS_OBJECT == NULL ||
_pControl->pLang->SZ_TK_CONTAINERS == NULL ||
_pControl->pLang->SZ_TK_OBJECTS == NULL ||
_pControl->pLang->SZ_TK_CONTAINERS_OBJECTS == NULL ||
_pControl->pLang->SZ_TK_NO_PROPAGATE == NULL
)
{
throw AdlStatement::ERROR_INVALID_PARSER_CONTROL;
}
}
catch(exception)
{
throw AdlStatement::ERROR_OUT_OF_MEMORY;
}
}
/******************************************************************************
AdlTree Methods
*****************************************************************************/
//
// An array of these is used to determine the order in which to print
//
#define PRINT_PRINCIPALS 0
#define PRINT_EXPRINCIPALS 1
#define PRINT_ALLOWED 2
#define PRINT_ACCESS 3
#define PRINT_ON 4
#define PRINT_OBJECTS 5
#define PRINT_DEF_SIZE 6
DWORD pdwLangEnglish[6] =
{
PRINT_PRINCIPALS,
PRINT_EXPRINCIPALS,
PRINT_ALLOWED,
PRINT_ACCESS,
PRINT_ON,
PRINT_OBJECTS
};
DWORD pdwLangReverse[6] =
{
PRINT_EXPRINCIPALS,
PRINT_PRINCIPALS,
PRINT_ALLOWED,
PRINT_ACCESS,
PRINT_ON,
PRINT_OBJECTS
};
//
// Append a wchar array to the STL string POUTSTLSTRING, add quotes
// if the input string contains any characters in the wchar
// array SPECIALCHARS
//
#define APPEND_QUOTED_STRING(POUTSTLSTRING, INSTRING, SPECIALCHARS, QUOTECHAR) \
if( wcspbrk( (INSTRING), (SPECIALCHARS) ) ) { \
(POUTSTLSTRING)->append(&(QUOTECHAR), 1); \
(POUTSTLSTRING)->append(INSTRING); \
(POUTSTLSTRING)->append(&(QUOTECHAR), 1); \
} else { \
(POUTSTLSTRING)->append(INSTRING); \
}
void AdlTree::PrintAdl(wstring *pSz, PADL_PARSER_CONTROL pControl)
/*++
Routine Description:
This routine prints the AdlTree structure using one of the pre-defined
language specifications, selected by checking the ADL_PARSER_CONTROL
structure. To add new languages, simply add a new 6 int array as above,
and add it into the switch statement below so it will be recognized.
Arguments:
pSz - An existing wstring to which the ADL statement output will
be appended
pControl - Pointer to the ADL_PARSER_CONTROL structure to define the
printing
Return Value:
none
--*/
{
//
// Iterators for token lists in the AdlTree
//
list<const AdlToken *>::iterator iter;
list<const AdlToken *>::iterator iter_end;
list<const AdlToken *>::iterator iter_tmp;
//
// If a string contains these characters, use quotes
//
WCHAR szSpecialChars[] =
{
pControl->pLang->CH_SPACE,
pControl->pLang->CH_NEWLINE,
pControl->pLang->CH_TAB,
pControl->pLang->CH_RETURN,
pControl->pLang->CH_COMMA,
pControl->pLang->CH_OPENPAREN,
pControl->pLang->CH_CLOSEPAREN,
pControl->pLang->CH_SEMICOLON,
pControl->pLang->CH_AT,
pControl->pLang->CH_SLASH,
pControl->pLang->CH_PERIOD,
pControl->pLang->CH_QUOTE,
0
};
DWORD dwIdx;
DWORD dwTmp;
PDWORD pdwPrintSpec;
//
// Determine which type of grammar to use.
//
switch( pControl->pLang->dwLanguageType )
{
case TK_LANG_ENGLISH:
pdwPrintSpec = pdwLangEnglish;
break;
case TK_LANG_REVERSE:
pdwPrintSpec = pdwLangReverse;
break;
default:
throw AdlStatement::ERROR_INVALID_PARSER_CONTROL;
break;
}
//
// Using that grammar, print the appropriate parts of each
// ADL_STATEMENT production
//
for( dwIdx = 0; dwIdx < PRINT_DEF_SIZE; dwIdx++ )
{
switch(pdwPrintSpec[dwIdx])
{
case PRINT_PRINCIPALS:
for( iter = _lpTokPrincipals.begin(),
iter_end = _lpTokPrincipals.end();
iter != iter_end;
iter++)
{
APPEND_QUOTED_STRING(pSz,
(*iter)->GetValue(),
szSpecialChars,
pControl->pLang->CH_QUOTE);
//
// ISSUE-2000/8/31
// Need to find a way to determine this instead of string comp
//
if( (*iter)->GetOptValue() != NULL &&
_wcsicmp(L"BUILTIN", (*iter)->GetOptValue()))
{
pSz->append(&(pControl->pLang->CH_AT), 1);
APPEND_QUOTED_STRING(pSz,
(*iter)->GetOptValue(),
szSpecialChars,
pControl->pLang->CH_QUOTE);
}
//
// Separate with commas except the last one, there use "and"
//
iter_tmp = iter;
if( ++iter_tmp == iter_end )
{
//
// Do nothing for the last principal
//
}
else if( ++iter_tmp == iter_end )
{
pSz->append(&(pControl->pLang->CH_SPACE), 1);
pSz->append(pControl->pLang->SZ_TK_AND);
pSz->append(&(pControl->pLang->CH_SPACE), 1);
}
else
{
pSz->append(&(pControl->pLang->CH_COMMA), 1);
pSz->append(&(pControl->pLang->CH_SPACE), 1);
}
}
//
// And a trailing space
//
pSz->append(&(pControl->pLang->CH_SPACE), 1);
break;
case PRINT_EXPRINCIPALS:
if( ! _lpTokExPrincipals.empty())
{
pSz->append(&(pControl->pLang->CH_OPENPAREN), 1);
pSz->append(pControl->pLang->SZ_TK_EXCEPT);
pSz->append(&(pControl->pLang->CH_SPACE), 1);
for( iter = _lpTokExPrincipals.begin(),
iter_end = _lpTokExPrincipals.end();
iter != iter_end;
iter++)
{
APPEND_QUOTED_STRING(pSz,
(*iter)->GetValue(),
szSpecialChars,
pControl->pLang->CH_QUOTE);
//
// ISSUE-2000/8/31
// Need to find a way to determine this instead of string comp
//
if( (*iter)->GetOptValue() != NULL &&
_wcsicmp(L"BUILTIN", (*iter)->GetOptValue()))
{
pSz->append(&(pControl->pLang->CH_AT), 1);
APPEND_QUOTED_STRING(pSz,
(*iter)->GetOptValue(),
szSpecialChars,
pControl->pLang->CH_QUOTE);
}
//
// Separate with commas except the last one, there use "and"
//
iter_tmp = iter;
if( ++iter_tmp == iter_end )
{
//
// Do nothing for the last principal
//
}
else if( ++iter_tmp == iter_end )
{
pSz->append(&(pControl->pLang->CH_SPACE), 1);
pSz->append(pControl->pLang->SZ_TK_AND);
pSz->append(&(pControl->pLang->CH_SPACE), 1);
}
else
{
pSz->append(&(pControl->pLang->CH_COMMA), 1);
pSz->append(&(pControl->pLang->CH_SPACE), 1);
}
}
pSz->append(&(pControl->pLang->CH_CLOSEPAREN), 1);
pSz->append(&(pControl->pLang->CH_SPACE), 1);
}
break;
case PRINT_ALLOWED:
pSz->append(pControl->pLang->SZ_TK_ALLOWED);
pSz->append(&(pControl->pLang->CH_SPACE), 1);
break;
case PRINT_ACCESS:
for( iter = _lpTokPermissions.begin(),
iter_end = _lpTokPermissions.end();
iter != iter_end;
iter++)
{
APPEND_QUOTED_STRING(pSz,
(*iter)->GetValue(),
szSpecialChars,
pControl->pLang->CH_QUOTE);
//
// Separate with commas except the last one, there use "and"
//
iter_tmp = iter;
if( ++iter_tmp == iter_end )
{
//
// Do nothing for the last permission
//
}
else if( ++iter_tmp == iter_end )
{
pSz->append(&(pControl->pLang->CH_SPACE), 1);
pSz->append(pControl->pLang->SZ_TK_AND);
pSz->append(&(pControl->pLang->CH_SPACE), 1);
}
else
{
pSz->append(&(pControl->pLang->CH_COMMA), 1);
pSz->append(&(pControl->pLang->CH_SPACE), 1);
}
}
//
// And a trailing space
//
pSz->append(&(pControl->pLang->CH_SPACE), 1);
break;
case PRINT_ON:
pSz->append(pControl->pLang->SZ_TK_ON);
pSz->append(&(pControl->pLang->CH_SPACE), 1);
break;
case PRINT_OBJECTS:
//
// Make sure all bits are defined
//
if( _dwInheritFlags & ~(CONTAINER_INHERIT_ACE |
INHERIT_ONLY_ACE |
NO_PROPAGATE_INHERIT_ACE |
OBJECT_INHERIT_ACE) )
{
throw AdlStatement::ERROR_INVALID_OBJECT;
}
//
// Count the number of object statements, for proper punctuation
//
dwTmp = 0;
if( ! ( _dwInheritFlags & INHERIT_ONLY_ACE) )
{
dwTmp++;
}
if(_dwInheritFlags & ( CONTAINER_INHERIT_ACE || OBJECT_INHERIT_ACE))
{
dwTmp++;
}
if(_dwInheritFlags & NO_PROPAGATE_INHERIT_ACE)
{
dwTmp++;
}
//
// First "this object"
//
if( ! ( _dwInheritFlags & INHERIT_ONLY_ACE) )
{
APPEND_QUOTED_STRING(pSz,
pControl->pLang->SZ_TK_THIS_OBJECT,
szSpecialChars,
pControl->pLang->CH_QUOTE);
//
// Print "and" if 1 more left, "," if two
//
dwTmp--;
if( dwTmp == 2 )
{
pSz->append(&(pControl->pLang->CH_COMMA), 1);
pSz->append(&(pControl->pLang->CH_SPACE), 1);
}
else if( dwTmp == 1)
{
pSz->append(&(pControl->pLang->CH_SPACE), 1);
pSz->append(pControl->pLang->SZ_TK_AND);
pSz->append(&(pControl->pLang->CH_SPACE), 1);
}
}
//
// Then container/object inheritance
//
if( _dwInheritFlags & ( CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE))
{
if( (_dwInheritFlags & OBJECT_INHERIT_ACE)
&& (_dwInheritFlags & CONTAINER_INHERIT_ACE) )
{
APPEND_QUOTED_STRING(pSz,
pControl->pLang->SZ_TK_CONTAINERS_OBJECTS,
szSpecialChars,
pControl->pLang->CH_QUOTE);
}
else if( _dwInheritFlags & CONTAINER_INHERIT_ACE )
{
APPEND_QUOTED_STRING(pSz,
pControl->pLang->SZ_TK_CONTAINERS,
szSpecialChars,
pControl->pLang->CH_QUOTE);
}
else
{
APPEND_QUOTED_STRING(pSz,
pControl->pLang->SZ_TK_OBJECTS,
szSpecialChars,
pControl->pLang->CH_QUOTE);
}
dwTmp--;
//
// Print "and" if 1 more left
// nothing if 0
//
if( dwTmp == 1)
{
pSz->append(&(pControl->pLang->CH_SPACE), 1);
pSz->append(pControl->pLang->SZ_TK_AND);
pSz->append(&(pControl->pLang->CH_SPACE), 1);
}
}
//
// Now no-propagate
//
if(_dwInheritFlags & NO_PROPAGATE_INHERIT_ACE)
{
APPEND_QUOTED_STRING(pSz,
pControl->pLang->SZ_TK_NO_PROPAGATE,
szSpecialChars,
pControl->pLang->CH_QUOTE);
}
break;
default:
//
// Should not get here unless language defs are wrong
//
throw AdlStatement::ERROR_FATAL_PARSER_ERROR;
break;
}
}
//
// And terminate the statement with a semicolon, invariable per grammar
//
pSz->append(&(pControl->pLang->CH_SEMICOLON), 1);
}