/*++ 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 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::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 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::iterator iter; list::iterator iter_end; list::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); }