tripwire-open-source/src/parser/policy.y

811 lines
25 KiB
Plaintext

//
// The developer of the original code and/or files is Tripwire, Inc.
// Portions created by Tripwire, Inc. are copyright (C) 2000-2017 Tripwire,
// Inc. Tripwire is a registered trademark of Tripwire, Inc. All rights
// reserved.
//
// This program is free software. The contents of this file are subject
// to the terms of the GNU General Public License as published by the
// Free Software Foundation; either version 2 of the License, or (at your
// option) any later version. You may redistribute it and/or modify it
// only in compliance with the GNU General Public License.
//
// This program is distributed in the hope that it will be useful.
// However, this program is distributed AS-IS WITHOUT ANY
// WARRANTY; INCLUDING THE IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS
// FOR A PARTICULAR PURPOSE. Please see the GNU General Public License
// for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
// USA.
//
// Nothing in the GNU General Public License or any other license to use
// the code or files shall permit you to use Tripwire's trademarks,
// service marks, or other intellectual property without Tripwire's
// prior written consent.
//
// If you have any questions, please contact Tripwire, Inc. at either
// info@tripwire.org or www.tripwire.org.
//
/*
///////////////////////////////////////////////////////////////////////////////
// policy.y -- yacc grammar for parsing policy language
//
INTERESTING STUFF:
---------------------
* all whitespace is removed from input
* token "@@end" is taken as the logical end of policy file
* "->" is now the fconame/specmask separator
* attribute lists now contain comma-separated attributes
* quoted strings use C++-style escaping (char (e.g. \n, \"), hex (e.g. \x8E230A), oct(e.g. \271) )
* default spec mask has name _T("default")
TODO: UNICODE
*/
/*
* NOTE: prefix all token names with TWP_ to prevent #define name collisions
* (e.g., STRING is probably already defined by somebody...)
*/
/*
* debugging hints:
*
* - if you see an error like this: "policy.y: line 308: Newline in string started at line 307"
* you are using a C++ style comment, that has a "n't" in it somewhere -- like this:
* // tell parent that we don't have any attributes
* either change it to C comment, or change "don't" to "do not".
* unbelievable, isn't it? MKS Yacc is a piece of crap.
*
* ghk 8/28/98
*
*/
%token TWP_COMMA
%token TWP_LPAREN
%token TWP_RPAREN
%token TWP_COMMENT
%token <mpString> TWP_STRING
%token TWP_SEMICOLON
%token TWP_LBRACE
%token TWP_RBRACE
%token TWP_DEFAULT
%token TWP_PIPE
%token TWP_DOLLAR
%token TWP_BANG
%token TWP_PLUS
%token TWP_MINUS
%token TWP_DQUOTE
%token TWP_RARROW
%token TWP_SECTION
%token TWP_IFHOST
%token TWP_ELSE
%token TWP_ENDIF
%token TWP_ECHO
%token TWP_ERROR
/* TODO: change TWP_XXX to TWP_ID_XXX for keyword */
%token TWP_DEFINE
%token TWP_UNDEF
%token TWP_ELSE
%token TWP_ENDIF
%token TWP_CONTENTS
%token TWP_DBASEVERSION
%token TWP_ANDAND
%token TWP_OROR
%token TWP_BSLASH
%token TWP_ESCCHAR
%token TWP_QSTRING
%token TWP_EQUALS
%token <mpString>TWP_GLOBAL_STRING
%{
#include "stdtwparser.h"
#include "core/debug.h"
#include "parserhelper.h"
#include "genreparseinfo.h"
#include "policyparser.h"
#include "fco/genreswitcher.h"
#include "fco/twfactory.h"
#include "fco/fconameinfo.h"
#include "fco/parsergenreutil.h"
#include "fco/fconametranslator.h"
#include "core/usernotify.h"
#ifndef YYNEWLINE
# include "parser/yylex.h"
# include "parser/yyparse.h"
#endif
// global variables
int yaccdebuglevel;
// in MKS Yacc in C++ mode, we need to explicitly declare yylavl
YYSTYPE yylval;
static TSTRING ConcatenateStrings( const std::list<TSTRING>* plist )
{
TSTRING strRet;
for( std::list<TSTRING>::const_iterator i = plist->begin(); i != plist->end(); i++ )
strRet += *i;
return strRet;
}
static std::list<TSTRING>* MergeIntoList( std::list<TSTRING>* plistLHS, const std::list<TSTRING>* plistRHS )
{
for( std::list<TSTRING>::const_iterator i = plistRHS->begin(); i != plistRHS->end(); i++ )
plistLHS->push_back( *i );
return plistLHS;
}
%}
/* this defines the lval values that are returned by each grammar rule */
%union {
class cParseString* mpString; // wrapper around TSTRING
class cParseStringList* mpStringList;
class cParseRule* mpNode; // representation of FCOSpec
class cParseSpecMask* mpSpecMask; // representation of specmask
class cParseSpecMaskList* mpSpecMaskList; // ...and a collection
class cParseNamedAttr* mpAttr; // representation of an attribute
class cParseNamedAttrList* mpAttrList; // ...and a collection
};
%type <mpNode> spec_masks
%type <mpAttr> attribute
%type <mpAttrList> attribute_list
%type <mpAttrList> attribute_list_with_opt_trailing_comma
%type <mpAttrList> opt_spec_attributes
/*****************************************
TODO: implement this when everthing else is done and the details have been hammered out
%type <mpSpecMask> wildcard_spec_mask
%type <mpSpecMask> default_spec_mask
%type <mpSpecMaskList> opt_wildcard_spec_mask_list
%type <mpString> fco_wildcard
*****************************************/
%type <mpStringList> fco_name
%type <mpString> attribute_name
%type <mpString> attribute_value
%type <mpString> string
%type <mpStringList> multi_string
%type <mpStringList> global_multi_string
%type <mpString> variable
%type <mpString> variable_name
%type <mpString> global_string
%type <mpString> host_name
%type <mpStringList> host_name_list
%type <mpString> prop_vector
%start policy
%%
/*-----------------------------------------------------------------------------
* policy rules
*
* these productions are responsible for creating the list of rules. each completed rule
* adds a cParseRule to either the "omit list" or the "rule list"
*
*/
policy
: opt_statement_list
;
opt_statement_list
: statement_list
| /* empty */
;
statement_list
: statement_list statement
| statement
;
statement
: variable_assignment TWP_SEMICOLON { cParserHelper::IncrementScopeStatementCount(); }
| global_variable_assignment TWP_SEMICOLON { cParserHelper::IncrementScopeStatementCount(); }
| rule TWP_SEMICOLON { cParserHelper::IncrementScopeStatementCount(); }
| TWP_SEMICOLON
| directive_block
| scoped_block
;
scoped_block
: TWP_LPAREN attribute_list_with_opt_trailing_comma TWP_RPAREN TWP_LBRACE
{
cDebug d("Parse::scope");
// create new scope
cAttrScopeInfo* pScopeInfo = new cAttrScopeInfo;
// init scope info
pScopeInfo->SetAttributes( $2 );
// push onto scope stack
cParserHelper::PushScope( pScopeInfo );
d.TraceDebug("push\n");
}
opt_statement_list TWP_RBRACE
{
if( 0 == cParserHelper::GetScopeStatementCount() && iUserNotify::GetInstance()->GetVerboseLevel() == iUserNotify::V_VERBOSE )
{
eParserNoStatementsInScope e;
e.SetFatality( false );
cParserHelper::GetErrorBucket()->AddError( e );
}
cDebug d("Parse::scope");
cParserHelper::PopScope();
d.TraceDebug("pop\n");
}
;
directive_block
: TWP_IFHOST host_name_list
{
cDebug d("Parse::#ifhost");
/* only look at this if we're not ignoring */
if( cPreprocessor::GetState() == cPreprocessor::STATE_ACCEPT )
{
if( cParserUtil::AnyOfTheseHostsExists( $2 ) )
{
cPreprocessor::PushState( cPreprocessor::STATE_ACCEPT );
}
else
{
cPreprocessor::PushState( cPreprocessor::STATE_IGNORE );
}
d.TraceDebug(_T("\n"));
}
else
{
cPreprocessor::PushState( cPreprocessor::STATE_IGNORE );
d.TraceDebug(_T("ignoring ifhost\n"));
}
}
opt_statement_list opt_else_host TWP_ENDIF
{
cDebug d("Parse::#endif");
cPreprocessor::PopState();
delete $2;
}
| TWP_SECTION string
{
cDebug d("Parse::#section");
if( ! ( cPreprocessor::AtTopLevel() && cParserHelper::AtTopScope() ) )
throw eParserSectionInsideBlock( *$2 );
cParserHelper::SetSection( *$2 );
delete $2;
}
| TWP_ERROR string
{
if( cPreprocessor::GetState() == cPreprocessor::STATE_ACCEPT && !cParserHelper::ParseOnly() )
{
iUserNotify::GetInstance()->Notify( iUserNotify::V_NORMAL, $2->c_str());
iUserNotify::GetInstance()->Notify( iUserNotify::V_NORMAL, _T("\n") );
YYRETURN(-1);
}
delete $2;
}
| TWP_ECHO string
{
if( cPreprocessor::GetState() == cPreprocessor::STATE_ACCEPT && !cParserHelper::ParseOnly() )
{
iUserNotify::GetInstance()->Notify( iUserNotify::V_NORMAL, $2->c_str());
iUserNotify::GetInstance()->Notify( iUserNotify::V_NORMAL, _T("\n") );
}
delete $2;
}
;
host_name_list
: host_name_list TWP_OROR host_name
{
$$ = $1;
$$->push_back( *$3 );
}
| host_name
{
$$ = new cParseStringList;
$$->push_back( *$1 );
}
;
opt_else_host
: TWP_ELSE
{
cDebug d("Parse::#else");
if( cPreprocessor::GetLastState() == cPreprocessor::STATE_ACCEPT )
{
cPreprocessor::ToggleTopState();
d.TraceDebug(_T("\n"));
}
else
{
d.TraceDebug(_T("ignoring #else\n"));
}
}
opt_statement_list
| /* empty */
;
variable_assignment
: variable_name TWP_EQUALS multi_string
{
cDebug d("Parse::variable_assignment");
if( cPreprocessor::GetState() == cPreprocessor::STATE_IGNORE )
{
d.TraceDebug(_T(" ignoring variable assignment\n"));
}
else
{
TSTRING strVal = ConcatenateStrings( $3 );
d.TraceDebug("*** assigning <%s> to <%s>\n", $1->c_str(), strVal.c_str() );
cParserHelper::GetGenreInfo()->InsertVariable( *$1, strVal );
}
delete $1;
delete $3;
}
;
global_variable_assignment
: global_string TWP_EQUALS global_multi_string
{
cDebug d("Parse::global variable_assignment");
if( cPreprocessor::GetState() == cPreprocessor::STATE_IGNORE )
{
d.TraceDebug(_T(" ignoring global variable assignment\n"));
}
else
{
TSTRING strVal = ConcatenateStrings( $3 );
d.TraceDebug("*** assigning <%s> to <%s>\n", $1->c_str(), strVal.c_str() );
cParserHelper::InsertGlobalVariable( *$1, strVal );
}
delete $1;
delete $3;
}
;
rule
: fco_name TWP_RARROW spec_masks
{
cDebug d("Parse::rule(fco_name TWP_RARROW spec_masks)");
if( cPreprocessor::GetState() == cPreprocessor::STATE_IGNORE )
{
d.TraceDebug(_T(" ignoring rule\n"));
}
else
{
// bind to children
cParseStringList *pnamelist = $1;
cParseRule *pnode = $3;
cFCOName fcoName;
// create the fco name from string
iParserGenreUtil* pGU = iTWFactory::GetInstance()->CreateParserGenreUtil();
pGU->InterpretFCOName( *pnamelist, fcoName );
if( ! pGU->IsAbsolutePath( iTWFactory::GetInstance()->GetNameTranslator()->ToStringRaw( fcoName ) ) )
throw eParserRelativePath( iTWFactory::GetInstance()->GetNameTranslator()->ToStringDisplay( fcoName ) );
else if( cParserHelper::GetGenreInfo()->RulePointAlreadyDefined( fcoName ) )
throw eParserPointAlreadyDefined( iTWFactory::GetInstance()->GetNameTranslator()->ToStringDisplay( fcoName ) );
// assign parse node the name, attributes
pnode->SetName( fcoName );
d.TraceDebug("*** rule(%s: %d specifiers, default=%s) (%d named attributes) (is%s case sensitive)\n",
pnode->GetName().AsString().c_str(), pnode->GetNumSpecMasks(),
pnode->GetDefSpecMask().GetPropVectorString().c_str(),
pnode->GetNumNamedAttrs(),
iTWFactory::GetInstance()->GetNameInfo()->IsCaseSensitive() ? "" : " not" );
pnode->Dump();
// add to our lists
cParserHelper::GetGenreInfo()->AddStopPoint( fcoName );
cParserHelper::GetGenreInfo()->AddRule( pnode );
}
delete $1;
/* do not delete $3 */
}
| TWP_BANG fco_name /* stop point */
{
cDebug d("Parse::rule(!fconame)");
if( cPreprocessor::GetState() == cPreprocessor::STATE_IGNORE )
{
d.TraceDebug(_T(" ignoring rule\n"));
}
else
{
// create the fco name from string
cFCOName fcoName;
iParserGenreUtil* pGU = iTWFactory::GetInstance()->CreateParserGenreUtil();
pGU->InterpretFCOName( *$2, fcoName );
if( ! pGU->IsAbsolutePath( iTWFactory::GetInstance()->GetNameTranslator()->ToStringRaw( fcoName ) ) )
throw eParserRelativePath( iTWFactory::GetInstance()->GetNameTranslator()->ToStringDisplay( fcoName ) );
if( cParserHelper::GetGenreInfo()->RulePointAlreadyDefined( fcoName ) )
throw eParserPointAlreadyDefined( iTWFactory::GetInstance()->GetNameTranslator()->ToStringDisplay( fcoName ) );
d.TraceDebug( "*** omit(%s)\n", fcoName.AsString().c_str() );
// add to stop list
cParserHelper::GetGenreInfo()->AddStopPoint( fcoName );
}
delete $2;
}
;
spec_masks
: prop_vector opt_spec_attributes
{
if( cPreprocessor::GetState() == cPreprocessor::STATE_ACCEPT )
{
iParserGenreUtil* pGU = iTWFactory::GetInstance()->CreateParserGenreUtil();
TSTRING strDefault = _T("default");
cParseRule* pNode = new cParseRule( cParseSpecMask( strDefault, *$1, pGU ) );
pNode->SetAttrList( $2 );
$2->MergeNoOverwrite( cParserHelper::GetGlobalAttrList() );
$$ = pNode;
}
delete $1;
/* do not delete $2 */
/* do not delete pNode */
}
/******************************************
*
TODO: implement this when everthing else is done and the details have been hammered out
| TWP_LBRACE opt_wildcard_spec_mask_list default_spec_mask TWP_RBRACE
{
cParseRule* pNode = new cParseRule( $3 );
pNode->SetSpecMaskList( $2 );
$$ = pNode;
}
| TWP_LBRACE opt_wildcard_spec_mask_list TWP_RBRACE
{
ASSERT( false );
}
*
******************************************/
;
opt_spec_attributes
: TWP_LPAREN attribute_list_with_opt_trailing_comma TWP_RPAREN
{
$$ = $2;
}
| /* empty */
{
$$ = new cParseNamedAttrList;
}
;
/******************************************
*
TODO: implement this when everthing else is done and the details have been hammered out
opt_wildcard_spec_mask_list
: opt_wildcard_spec_mask_list wildcard_spec_mask
{
$$ = $1;
if( $$ == NULL )
{
$$ = new cParseSpecMaskList; ASSERT( $$ );
}
$$->Add( $2 );
}
| { $$ = NULL; }
;
*
******************************************/
attribute_list_with_opt_trailing_comma
: attribute_list opt_comma
{
$$ = $1;
}
;
attribute_list
: attribute_list TWP_COMMA attribute
{
cDebug d("Parse::attribute_list");
ASSERT( $1 && $3 );
// add attribute to list
$1->Add( $3 );
d.TraceDebug("added attribute (%s=%s)\n", $3->GetName().c_str(), $3->GetValue().c_str());
$$ = $1;
/* do not delete $3 */
}
| attribute
{
cDebug d("Parse::attribute_list");
cParseNamedAttrList *pAttrList = new cParseNamedAttrList; ASSERT( pAttrList );
pAttrList->Add( $1 );
d.TraceDebug("added attribute (%s=%s)\n", $1->GetName().c_str(), $1->GetValue().c_str());
$$ = pAttrList;
}
;
attribute
: attribute_name TWP_EQUALS attribute_value
{
cDebug d("Parse::attribute");
cParseNamedAttr *pAttr = new cParseNamedAttr( *$1, *$3 );
ASSERT(pAttr);
d.TraceDebug("attribute (%s=%s)\n", $1->c_str(), $3->c_str());
$$ = pAttr;
/* don't need strings anymore */
delete $1;
delete $3;
}
;
/******************************************
*
TODO: implement this when everthing else is done and the details have been hammered out
wildcard_spec_mask
: fco_wildcard TWP_RARROW prop_vector opt_spec_attributes TWP_SEMICOLON
{
cDebug d("Parse::wildcard_spec_mask");
// create node, and attach specmask string and attributes
// the parent will merge this into the rest of the specmasks, and then delete it
cParseSpecMask *pSpecMask = new cParseSpecMask( *$1, *$3 ); ASSERT( pSpecMask );
// attach any named attributes
pSpecMask->SetAttrList( $4 );
d.TraceAlways("condition(%s), value(%s), attributes(%d)\n",
$1->c_str(),
$3->c_str(),
pSpecMask->GetNumAttrs());
$$ = pSpecMask;
}
;
default_spec_mask
: TWP_DEFAULT TWP_RARROW prop_vector opt_spec_attributes TWP_SEMICOLON
{
cDebug d("Parse::default_spec_mask");
// create node, and attach specmask string and attributes
// the parent will merge this into the rest of the specmasks, and then delete it
cParseSpecMask *pDefMask = new cParseSpecMask(TSTRING("default"), *$3); ASSERT(pDefMask);
// attach any named attributes
pDefMask->SetAttrList( $4 );
d.TraceAlways("propvector(%s), num attributes(%d)\n", pDefMask->GetPropVector().c_str(),
pDefMask->GetNumAttrs());
$$ = pDefMask;
}
;
*
******************************************/
opt_comma
: TWP_COMMA
| /* empty */
;
variable
: TWP_DOLLAR TWP_LPAREN variable_name TWP_RPAREN
{
cDebug d( " parser::variable" );
cParseString* pVarValue = new cParseString;
if( cPreprocessor::GetState() == cPreprocessor::STATE_ACCEPT )
{
// we want to replace the keyword with its value, (e.g. replacing <dollar><lparen>ReadOnly<rparen>
// with "pingus" (or whatever readonly means). So it is a rote replacement.
if( ! cParserHelper::GetGenreInfo()->LookupVariable( *$3, *pVarValue ) )
throw eParserUseUndefVar( *$3 );
// return the lval
d.TraceDetail("--(STRING)--> got variable (%s) mapped to (%s)\n",
$3->c_str(),
pVarValue->c_str());
}
$$ = pVarValue;
/* don't need string anymore */
delete $3;
}
;
/******************************************
*
TODO: implement this when everthing else is done and the details have been hammered out
fco_wildcard
: TWP_STRING
{
// like "*.exe"
cDebug d(" parser::string(fco_wildcard)");
d.TraceNever("--(STRING)--> got string (%s)\n", $1);
$$ = $1;
}
;
*
*******************************************/
prop_vector
: multi_string
{
$$ = new cParseString;
*$$ = ConcatenateStrings( $1 );
delete $1;
}
;
attribute_name
: string
{
cDebug d(" parser::string(attribute_name)");
$$ = $1;
}
;
attribute_value
: multi_string
{
$$ = new cParseString;
cDebug d(" parser::multi_string(attribute_value)");
*$$ = ConcatenateStrings( $1 );
delete $1;
}
;
fco_name
: multi_string
{
cDebug d(" parser::multi_string(fco_name)");
$$ = $1;
}
|
multi_string TWP_PIPE multi_string
{
#if IS_WIN32
ASSERT( 0 == _tcscmp( _T("NTREG"), cGenreSwitcher::GetInstance()->GenreToString( cGenreSwitcher::GetInstance()->CurrentGenre() ) ) );
#endif
$1->push_back( _T("|") );
MergeIntoList( $1, $3 );
$$ = $1;
delete $3;
}
;
host_name
: string
{
/*not multistring because no eol delimiter */
cDebug d(" parser::multi_string(host_name)");
$$ = $1;
}
;
variable_name
: TWP_STRING
{
cDebug d(" parser::string(variable_name)");
$$ = $1;
}
;
multi_string
: multi_string string
{
$$->push_back( *$2 );
delete $2;
}
| string
{
$$ = new cParseStringList;
$$->push_back( *$1 );
delete $1;
}
;
global_multi_string
: global_multi_string global_string
{
$$->push_back( *$2 );
delete $2;
}
| global_string
{
$$ = new cParseStringList;
$$->push_back( *$1 );
delete $1;
}
;
string
: TWP_STRING
{
cDebug d(" parser::string(normal)");
d.TraceNever("--(STRING)--> got string (%s)\n", $1);
$$ = $1;
}
| variable
{
cDebug d(" parser::string(normal)");
d.TraceNever("--(STRING)--> got string (%s)\n", $1);
$$ = $1;
}
;
global_string
: TWP_GLOBAL_STRING
{
cDebug d(" parser::string(normal)");
d.TraceNever("--(STRING)--> got string (%s)\n", $1);
$$ = $1;
}
;
%%