1
0
mirror of https://github.com/fwbuilder/fwbuilder synced 2026-03-22 11:17:31 +01:00
fwbuilder/src/pflib/PolicyCompiler_ipf.cpp

612 lines
20 KiB
C++

/*
Firewall Builder
Copyright (C) 2002 NetCitadel, LLC
Author: Vadim Kurland vadim@vk.crocodile.org
$Id$
This program is free software which we release under the GNU General Public
License. You may redistribute and/or modify this program under the terms
of that license as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
To get a copy of the GNU General Public License, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "config.h"
#include "PolicyCompiler_ipf.h"
#include "fwcompiler/Compiler.h"
#include "fwbuilder/AddressTable.h"
#include "fwbuilder/FWObjectDatabase.h"
#include "fwbuilder/Firewall.h"
#include "fwbuilder/ICMPService.h"
#include "fwbuilder/IPService.h"
#include "fwbuilder/Interface.h"
#include "fwbuilder/Library.h"
#include "fwbuilder/Policy.h"
#include "fwbuilder/RuleElement.h"
#include "fwbuilder/TCPService.h"
#include "fwbuilder/UDPService.h"
#include <iostream>
#include <assert.h>
using namespace libfwbuilder;
using namespace fwcompiler;
using namespace std;
string PolicyCompiler_ipf::myPlatformName() { return "ipf"; }
int PolicyCompiler_ipf::prolog()
{
int n= PolicyCompiler_pf::prolog();
anytcp = dbcopy->createTCPService();
anytcp->setId(FWObjectDatabase::generateUniqueId()); //ANY_TCP_OBJ_ID);
persistent_objects->add(anytcp,false);
anyudp=dbcopy->createUDPService();
anyudp->setId(FWObjectDatabase::generateUniqueId()); //ANY_UDP_OBJ_ID);
persistent_objects->add(anyudp,false);
anyicmp=dbcopy->createICMPService();
anyicmp->setId(FWObjectDatabase::generateUniqueId()); //ANY_ICMP_OBJ_ID);
persistent_objects->add(anyicmp,false);
return n;
}
bool PolicyCompiler_ipf::expandAnyService::processNext()
{
PolicyCompiler_ipf *pcomp=dynamic_cast<PolicyCompiler_ipf*>(compiler);
PolicyRule *rule=getNext(); if (rule==NULL) return false;
RuleElementSrv *srv=rule->getSrv();
FWOptions *ruleopt =rule->getOptionsObject();
if (srv->isAny() && ! ruleopt->getBool("stateless") && rule->getAction()==PolicyRule::Accept) {
PolicyRule *r = compiler->dbcopy->createPolicyRule();
compiler->temp_ruleset->add(r);
r->duplicate(rule);
RuleElementSrv *nsrv=r->getSrv();
nsrv->clearChildren();
nsrv->addRef(pcomp->anyicmp); //compiler->dbcopy->findInIndex(ANY_ICMP_OBJ_ID));
tmp_queue.push_back(r);
r = compiler->dbcopy->createPolicyRule();
compiler->temp_ruleset->add(r);
r->duplicate(rule);
nsrv=r->getSrv();
nsrv->clearChildren();
nsrv->addRef(pcomp->anytcp); //compiler->dbcopy->findInIndex(ANY_TCP_OBJ_ID));
tmp_queue.push_back(r);
r = compiler->dbcopy->createPolicyRule();
compiler->temp_ruleset->add(r);
r->duplicate(rule);
nsrv=r->getSrv();
nsrv->clearChildren();
nsrv->addRef(pcomp->anyudp); //compiler->dbcopy->findInIndex(ANY_UDP_OBJ_ID));
tmp_queue.push_back(r);
r = compiler->dbcopy->createPolicyRule();
compiler->temp_ruleset->add(r);
r->duplicate(rule);
FWOptions *ruleopt =r->getOptionsObject();
ruleopt->setBool("stateless",true);
tmp_queue.push_back(r);
} else
tmp_queue.push_back(rule);
return true;
}
bool PolicyCompiler_ipf::doSrcNegation::processNext()
{
PolicyRule *rule=getNext(); if (rule==NULL) return false;
RuleElementSrc *src=rule->getSrc();
if (src->getNeg()) {
RuleElementSrc *nsrc;
PolicyRule *r;
FWOptions *ruleopt;
r= compiler->dbcopy->createPolicyRule();
compiler->temp_ruleset->add(r);
r->duplicate(rule);
r->setAction(PolicyRule::Continue);
r->setLogging(false);
nsrc=r->getSrc();
nsrc->setNeg(false);
r->setBool("quick",false);
r->setBool("skip_check_for_duplicates",true);
ruleopt = r->getOptionsObject();
ruleopt->setBool("stateless", true);
tmp_queue.push_back(r);
r= compiler->dbcopy->createPolicyRule();
compiler->temp_ruleset->add(r);
r->duplicate(rule);
nsrc=r->getSrc();
nsrc->setNeg(false);
nsrc->clearChildren();
nsrc->setAnyElement();
r->setBool("quick",true);
r->setBool("skip_check_for_duplicates",true);
tmp_queue.push_back(r);
return true;
}
tmp_queue.push_back(rule);
return true;
}
bool PolicyCompiler_ipf::doDstNegation::processNext()
{
PolicyRule *rule=getNext(); if (rule==NULL) return false;
RuleElementDst *dst=rule->getDst();
if (dst->getNeg()) {
RuleElementDst *ndst;
PolicyRule *r;
FWOptions *ruleopt;
r= compiler->dbcopy->createPolicyRule();
compiler->temp_ruleset->add(r);
r->duplicate(rule);
r->setAction(PolicyRule::Continue);
r->setLogging(false);
ndst=r->getDst();
ndst->setNeg(false);
r->setBool("quick",false);
r->setBool("skip_check_for_duplicates",true);
ruleopt = r->getOptionsObject();
ruleopt->setBool("stateless", true);
tmp_queue.push_back(r);
r= compiler->dbcopy->createPolicyRule();
compiler->temp_ruleset->add(r);
r->duplicate(rule);
ndst=r->getDst();
ndst->setNeg(false);
ndst->clearChildren();
ndst->setAnyElement();
r->setBool("quick",true);
r->setBool("skip_check_for_duplicates",true);
tmp_queue.push_back(r);
return true;
}
tmp_queue.push_back(rule);
return true;
}
bool PolicyCompiler_ipf::doSrvNegation::processNext()
{
PolicyRule *rule=getNext(); if (rule==NULL) return false;
RuleElementSrv *srv=rule->getSrv();
if (srv->getNeg())
{
compiler->abort(rule, "Negation in Srv is not implemented");
return false;
}
tmp_queue.push_back(rule);
return true;
}
void PolicyCompiler_ipf::specialCaseWithDynInterface::dropDynamicInterface(RuleElement *re)
{
list<FWObject*> cl;
for (list<FWObject*>::iterator i1=re->begin(); i1!=re->end(); ++i1)
{
FWObject *o = *i1;
FWObject *obj = o;
if (FWReference::cast(o)!=NULL) obj=FWReference::cast(o)->getPointer();
Interface *ifs =Interface::cast( obj );
if (ifs!=NULL && !ifs->isRegular()) continue;
cl.push_back(obj);
}
if (!cl.empty())
{
re->clearChildren();
for (list<FWObject*>::iterator i1=cl.begin(); i1!=cl.end(); ++i1)
re->addRef( (*i1) );
}
}
bool PolicyCompiler_ipf::specialCaseWithDynInterface::processNext()
{
PolicyRule *rule=getNext(); if (rule==NULL) return false;
dropDynamicInterface( rule->getDst() );
dropDynamicInterface( rule->getSrc() );
tmp_queue.push_back(rule);
return true;
}
PolicyCompiler_ipf::calculateSkip::calculateSkip(const std::string &n) : PolicyRuleProcessor(n)
{
}
bool PolicyCompiler_ipf::calculateSkip::processNext()
{
// PolicyRule *rule;
slurp();
if (tmp_queue.size()==0) return false;
/*
* first, we scan all rules and build a hash that maps attribute
* "skip_label" to rule number. Attribute "skip_label" is set in
* optimize1, after which we could have split some rules, so this
* attrbiute may not be unique. We want to skip to the first rule
* marked with the same skip label if there are few with the same
* label. The simplest way to find the first one with the same label
* is to scan rules in reverse order, that is from the bottom up.
*/
int N=tmp_queue.size()-1; // The last rule number is N
for (deque<Rule*>::reverse_iterator k=tmp_queue.rbegin(); k!=tmp_queue.rend(); ++k)
{
PolicyRule *r = PolicyRule::cast( *k );
if (!r->getStr("skip_label").empty()) allrules[r->getStr("skip_label")]=N;
r->setInt("rule_num",N);
N--;
}
for (deque<Rule*>::iterator k=tmp_queue.begin(); k!=tmp_queue.end(); ++k)
{
PolicyRule *r = PolicyRule::cast( *k );
string rl=r->getLabel();
int current_position=r->getPosition();
if (r->getAction()==PolicyRule::Skip)
{
assert(!r->getStr("skip_to").empty());
int to=allrules[r->getStr("skip_to")];
int n =r->getInt("rule_num");
r->setInt("no_to_skip",to-n-1);
}
/* Action 'Continue' means we need to jump to the next rule in the
* GUI. We scan rules down from the current one, looking for the first
* rule that corresponds to the next rule in the GUI.
*/
if (r->getAction()==PolicyRule::Continue)
{
r->setAction(PolicyRule::Skip);
r->setBool("quick",false);
deque<Rule*>::iterator j=k;
++j;
int n=0;
for ( ; j!=tmp_queue.end(); ++j)
{
PolicyRule *r2 = PolicyRule::cast( *j );
if (r2->getPosition()!=current_position) break;
/* 'skip' only skips rules with the same setting of 'in' or 'out',
* that is the same direction
*/
if (r2->getDirection()==r->getDirection()) ++n;
}
r->setInt("no_to_skip",n);
}
}
return true;
}
bool PolicyCompiler_ipf::checkForKeepState::processNext()
{
PolicyRule *rule=getNext(); if (rule==NULL) return false;
tmp_queue.push_back(rule);
Service *srv=compiler->getFirstSrv(rule); assert(srv);
FWOptions *ruleopt =rule->getOptionsObject();
if (! ICMPService::isA(srv) &&
! UDPService::isA(srv) &&
! TCPService::isA(srv) ) ruleopt->setBool("stateless",true);
return true;
}
bool PolicyCompiler_ipf::eliminateDuplicateRules::processNext()
{
PolicyCompiler *pcomp = dynamic_cast<PolicyCompiler*>(compiler);
PolicyRule *rule = getNext(); if (rule==NULL) return false;
// Note that if rule has "any" in Interface column, it is
// implemented as reference to the AnyNetwork object. In this case
// Compiler::getFirstItf() returns NULL.
Interface *intf_rule = compiler->getFirstItf(rule);
int intf_id_rule = (intf_rule) ? intf_rule->getId() : -1;
if ( ! rule->getBool("skip_check_for_duplicates"))
{
for (deque<PolicyRule*>::iterator i=rules_seen_so_far.begin(); i!=rules_seen_so_far.end(); ++i)
{
PolicyRule *r=(*i);
if (r->getBool("skip_check_for_duplicates") ) continue;
if (r->getAction()==PolicyRule::Continue ||
r->getAction()==PolicyRule::Skip) continue;
Interface *intf_r = compiler->getFirstItf(r);
int intf_id_r = (intf_r) ? intf_r->getId() : -1;
if (intf_id_r==intf_id_rule &&
r->getAction()==rule->getAction() &&
r->getLogging()==rule->getLogging() &&
pcomp->cmpRules(*r,*rule) )
{
// cout << "---------------------------------------" << endl;
// cout << pcomp->debugPrintRule(r) << endl;
// cout << pcomp->debugPrintRule(rule) << endl;
return true;
}
}
}
tmp_queue.push_back(rule);
rules_seen_so_far.push_back(rule);
return true;
}
bool PolicyCompiler_ipf::processMultiAddressObjectsInRE::processNext()
{
PolicyRule *rule=getNext(); if (rule==NULL) return false;
RuleElement *re=RuleElement::cast( rule->getFirstByType(re_type) );
for (FWObject::iterator i=re->begin(); i!=re->end(); i++)
{
FWObject *o= *i;
if (FWReference::cast(o)!=NULL) o=FWReference::cast(o)->getPointer();
MultiAddressRunTime *atrt = MultiAddressRunTime::cast(o);
if (atrt!=NULL && atrt->getSubstitutionTypeName()==AddressTable::TYPENAME)
compiler->abort(
rule,
"Run-time AddressTable objects are not supported.");
}
tmp_queue.push_back(rule);
return true;
}
bool PolicyCompiler_ipf::SplitDirectionIpfilter::processNext()
{
PolicyRule *rule=getNext(); if (rule==NULL) return false;
if (rule->getDirection()==PolicyRule::Both)
{
PolicyRule *r= compiler->dbcopy->createPolicyRule();
compiler->temp_ruleset->add(r);
r->duplicate(rule);
r->setDirection(PolicyRule::Inbound);
tmp_queue.push_back(r);
r= compiler->dbcopy->createPolicyRule();
compiler->temp_ruleset->add(r);
r->duplicate(rule);
r->setDirection(PolicyRule::Outbound);
tmp_queue.push_back(r);
} else
tmp_queue.push_back(rule);
return true;
}
void PolicyCompiler_ipf::compile()
{
info(" Compiling policy for " + fw->getName());
Compiler::compile();
addDefaultPolicyRule();
bool check_for_recursive_groups=true;
if ( fw->getOptionsObject()->getBool ("check_shading") &&
! inSingleRuleCompileMode())
{
add( new Begin ("Detecting rule shadowing" ) );
add( new printTotalNumberOfRules( ) );
add( new ItfNegation("process negation in Itf" ) );
add( new InterfacePolicyRules(
"process interface policy rules and store interface ids"));
add( new recursiveGroupsInSrc("check for recursive groups in SRC"));
add( new recursiveGroupsInDst("check for recursive groups in DST"));
add( new recursiveGroupsInSrv("check for recursive groups in SRV"));
check_for_recursive_groups=false;
add( new ExpandGroups("expand groups") );
add( new eliminateDuplicatesInSRC("eliminate duplicates in SRC") );
add( new eliminateDuplicatesInDST("eliminate duplicates in DST") );
add( new eliminateDuplicatesInSRV("eliminate duplicates in SRV") );
add( new swapMultiAddressObjectsInSrc(
" swap MultiAddress -> MultiAddressRunTime in Src") );
add( new swapMultiAddressObjectsInDst(
" swap MultiAddress -> MultiAddressRunTime in Dst") );
add( new ExpandMultipleAddressesInSrc(
"expand objects with multiple addresses in SRC" ) );
add( new ExpandMultipleAddressesInDst(
"expand objects with multiple addresses in DST" ) );
add( new ConvertToAtomic("convert to atomic rules" ) );
add( new checkForObjectsWithErrors(
"check if we have objects with errors in rule elements"));
add( new DetectShadowing("Detect shadowing" ) );
add( new simplePrintProgress() );
runRuleProcessors();
deleteRuleProcessors();
}
add( new Begin());
add( new printTotalNumberOfRules() );
add( new singleRuleFilter());
// add( new MACFiltering( "verify for MAC address filtering" ) );
add( new setQuickFlag("set 'quick' flag") );
if (check_for_recursive_groups)
{
add( new recursiveGroupsInSrc("check for recursive groups in SRC"));
add( new recursiveGroupsInDst("check for recursive groups in DST"));
add( new recursiveGroupsInSrv("check for recursive groups in SRV"));
}
add( new emptyGroupsInSrc("check for empty groups in SRC") );
add( new emptyGroupsInDst("check for empty groups in DST") );
add( new emptyGroupsInSrv("check for empty groups in SRV") );
add( new ItfNegation("process negation in Itf" ) );
add( new InterfacePolicyRules(
"process interface policy rules and store interface ids") );
add( new doSrcNegation("process negation in Src") );
add( new doDstNegation("process negation in Dst") );
add( new doSrvNegation("process negation in Srv") );
add( new ExpandGroups( "expand groups") );
add( new CheckForTCPEstablished(
"check for TCPService objects with flag \"established\"") );
add( new CheckForUnsupportedUserService("check for user service") );
add( new eliminateDuplicatesInSRC("eliminate duplicates in SRC") );
add( new eliminateDuplicatesInDST("eliminate duplicates in DST") );
add( new eliminateDuplicatesInSRV("eliminate duplicates in SRV") );
add( new swapMultiAddressObjectsInSrc(
" swap MultiAddress -> MultiAddressRunTime in Src") );
add( new swapMultiAddressObjectsInDst(
" swap MultiAddress -> MultiAddressRunTime in Dst") );
add( new processMultiAddressObjectsInSrc(
"process MultiAddress objects in Src") );
add( new processMultiAddressObjectsInDst(
"process MultiAddress objects in Dst") );
add( new splitIfFirewallInSrc("split rule if firewall is in Src") );
add( new splitIfFirewallInDst("split rule if firewall is in Dst") );
add( new fillDirection("determine directions") );
add( new SplitDirectionIpfilter("split rules with direction 'both'" ) );
add( new ExpandMultipleAddresses(
"expand objects with multiple addresses") );
add( new checkForDynamicInterfacesOfOtherObjects(
"check for dynamic interfaces of other hosts and firewalls" ));
add( new MACFiltering("verify for MAC address filtering" ) );
add( new checkForUnnumbered("check for unnumbered interfaces") );
add( new specialCaseWithDynInterface(
"check for a special cases with dynamic interface") );
add( new addressRanges("expand address range objects") );
add( new groupServicesByProtocol("split rules with different protocols") );
add( new separateTCPWithFlags("separate TCP services with flags" ) );
add( new separateSrcPort("split on TCP and UDP with source ports"));
add( new verifyCustomServices(
"verify custom services for this platform") );
add( new SpecialServices("check for special services" ) );
add( new expandAnyService("expand ANY service for stateful rules") );
/*
* it may make sense to do optimization even before we expand groups
* (before ExpandGroups). Need to test this idea.
*/
if ( fw->getOptionsObject()->getBool ("optimize") )
{
add( new optimizeSrc("optimization in SRC") );
add( new optimizeDst("optimization in DST") );
add( new optimizeSrv("optimization in SRV") );
}
add( new ConvertToAtomic("convert to atomic rules") );
add( new checkForZeroAddr("check for zero addresses" ) );
if ( fw->getOptionsObject()->getBool ("eliminate_duplicates") )
add( new eliminateDuplicateRules("eliminate duplicate rules" ) );
add( new calculateSkip("calculate argument for skip") );
add( new checkForKeepState("check for 'keep state'") );
add( new checkForObjectsWithErrors(
"check if we have objects with errors in rule elements"));
add( new PrintRule("generate ipf code") );
add( new simplePrintProgress() );
runRuleProcessors();
}
string PolicyCompiler_ipf::debugPrintRule(Rule *r)
{
PolicyRule *rule=PolicyRule::cast(r);
// FWOptions *ruleopt =rule->getOptionsObject();
ostringstream s;
s << PolicyCompiler::debugPrintRule(rule) << " ";
RuleElementItf *intf_re = rule->getItf();
string rule_interfaces;
int intf_count = 0;
for (FWObject::iterator it=intf_re->begin(); it!=intf_re->end(); ++it)
{
FWObject *o = *it;
if (FWReference::cast(o)!=NULL) o = FWReference::cast(o)->getPointer();
rule_interfaces += " " + o->getName();
intf_count++;
}
if (intf_count > 0)
{
s << " intf: ";
if (intf_count > 1) s << "{ ";
s << rule_interfaces;
if (intf_count > 1) s << " }";
}
else
s << " intf: ?";
s << " ";
if (r->getBool("skip_check_for_duplicates")) s << "skip_check_for_duplicates ";
if (r->getStr("skip_label")!="") s << "skip_label: " << r->getStr("skip_label") << " ";
if (r->getStr("skip_to")!="") s << "skip_to: " << r->getStr("skip_to") << " ";
if (r->getInt("no_to_skip")!=-1) s << "no_to_skip: " << r->getInt("no_to_skip");
s << " " << FWObjectDatabase::getStringId(r->getId()) << " (" << r->getId() << ")";
return s.str();
}
void PolicyCompiler_ipf::epilog()
{
}