1
0
mirror of https://github.com/fwbuilder/fwbuilder synced 2026-07-01 05:19:34 +02:00
Files
fwbuilder/src/cisco_lib/NATCompiler_pix.cpp
2018-10-04 16:20:02 +02:00

1434 lines
44 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 "NATCompiler_pix.h"
#include "NamedObjectsAndGroupsSupport.h"
#include "NamedObjectsManager.h"
#include "fwbuilder/AddressRange.h"
#include "fwbuilder/AddressTable.h"
#include "fwbuilder/Cluster.h"
#include "fwbuilder/FWObjectDatabase.h"
#include "fwbuilder/FailoverClusterGroup.h"
#include "fwbuilder/ICMPService.h"
#include "fwbuilder/IPv4.h"
#include "fwbuilder/IPv6.h"
#include "fwbuilder/InetAddr.h"
#include "fwbuilder/Interface.h"
#include "fwbuilder/Library.h"
#include "fwbuilder/NAT.h"
#include "fwbuilder/Network.h"
#include "fwbuilder/Resources.h"
#include "fwbuilder/RuleElement.h"
#include "fwbuilder/TCPService.h"
#include "fwbuilder/UDPService.h"
#include <algorithm>
#include <functional>
#include <iostream>
#include <cstring>
#include <assert.h>
#include <QString>
using namespace libfwbuilder;
using namespace fwcompiler;
using namespace std;
string NATCompiler_pix::myPlatformName() { return "pix"; }
string _print_addr(const InetAddr* addr)
{
if (addr) return addr->toString();
return "NULL";
}
NATCompiler_pix::NATCompiler_pix(FWObjectDatabase *_db,
Firewall *fw,
bool ipv6_policy,
OSConfigurator *_oscnf) :
NATCompiler(_db, fw, ipv6_policy, _oscnf) , helper(this)
{
}
NATCompiler_pix::~NATCompiler_pix()
{
std::map<int,NATCmd*>::iterator it1;
for (it1=nat_commands.begin(); it1!=nat_commands.end(); ++it1)
{
delete it1->second;
}
nat_commands.clear();
std::map<int,StaticCmd*>::iterator it2;
for (it2=static_commands.begin(); it2!=static_commands.end(); ++it2)
{
delete it2->second;
}
static_commands.clear();
nonat_rules.clear();
first_nonat_rule_id.clear();
}
bool StaticCmd::operator==(const StaticCmd &other)
{
return (*oaddr == *(other.oaddr) &&
*iaddr == *(other.iaddr) &&
*osrv == *(other.osrv) &&
*tsrv == *(other.tsrv) &&
i_iface->getId() == other.i_iface->getId() &&
o_iface->getId() == other.o_iface->getId());
}
/*
* Do not expand interfaces in ODst and TSrc
*
*/
void NATCompiler_pix::_expand_addr_recursive_pix(Rule *rule,
FWObject *re,
FWObject *s,
list<FWObject*> &ol,
bool expand_cluster_interfaces_fully)
{
bool odst_or_tsrc = (re->getTypeName() == RuleElementODst::TYPENAME ||
re->getTypeName() == RuleElementTSrc::TYPENAME);
list<FWObject*> addrlist;
for (FWObject::iterator i1=s->begin(); i1!=s->end(); ++i1)
{
FWObject *o = FWReference::getObject(*i1);
assert(o);
Address *addr = Address::cast(o);
// this condition includes Host, Firewall and Interface
if (addr && !addr->hasInetAddress())
{
addrlist.push_back(o);
continue;
}
// IPv4, IPv6, Network, NetworkIPv6
if (addr && addr->hasInetAddress() && MatchesAddressFamily(o))
{
addrlist.push_back(o);
continue;
}
if (o->getId() == FWObjectDatabase::ANY_ADDRESS_ID ||
MultiAddress::cast(o)!=nullptr ||
Interface::cast(o) ||
physAddress::cast(o))
{
addrlist.push_back(o);
continue;
}
}
if (addrlist.empty())
{
if (RuleElement::cast(s)==nullptr) ol.push_back(s);
}
else
{
for (list<FWObject*>::iterator i2=addrlist.begin();
i2!=addrlist.end(); ++i2)
{
Interface *i2itf = Interface::cast(*i2);
if (i2itf)
{
// if this is ODst or TSrc, just use interface
if (odst_or_tsrc)
{
ol.push_back(i2itf);
continue;
}
_expand_interface(rule, i2itf, ol, expand_cluster_interfaces_fully);
continue;
}
_expand_addr_recursive_pix(rule, re, *i2, ol, expand_cluster_interfaces_fully);
}
}
}
void NATCompiler_pix::_expand_addr_recursive(Rule *rule, FWObject *re,
list<FWObject*> &ol,
bool expand_cluster_interfaces_fully)
{
_expand_addr_recursive_pix(rule, re, re, ol, expand_cluster_interfaces_fully);
}
void NATCompiler_pix::_expand_interface(Rule *rule,
Interface *iface,
std::list<FWObject*> &ol,
bool expand_cluster_interfaces_fully)
{
Compiler::_expand_interface(rule, iface, ol, expand_cluster_interfaces_fully);
}
string NATCompiler_pix::getNATACLname(Rule *rule,int nat_id)
{
int n=-1;
string res;
do
{
n++;
ostringstream os;
os << rule->getUniqueId() << "." << nat_id << "." << n;
res=os.str();
} while (nat_acl_names.count(res)!=0);
return res;
}
string NATCompiler_pix::getNATACLname(Rule *rule,string suffix)
{
int n=-1;
string res;
do
{
n++;
ostringstream os;
os << rule->getUniqueId();
if (!suffix.empty()) os << "." << suffix;
os << "." << n;
res=os.str();
} while (nat_acl_names.count(res)!=0);
return res;
}
int NATCompiler_pix::prolog()
{
global_pool_no = 1;
NAT *final_ruleset = new NAT();
final_ruleset->setName("Final NAT Rule Set");
persistent_objects->add( final_ruleset );
final_ruleset_id = final_ruleset->getId();
return NATCompiler::prolog();
}
string NATCompiler_pix::debugPrintRule(Rule *r)
{
NATRule *rule=NATRule::cast(r);
#ifndef NDEBUG
RuleElementItfInb *itf_in_re = rule->getItfInb(); assert(itf_in_re!=nullptr);
RuleElementItfOutb *itf_out_re = rule->getItfOutb(); assert(itf_out_re!=nullptr);
#endif
ostringstream os;
switch (rule->getRuleType())
{
case NATRule::NONAT:
os << "NONAT Type: " << rule->getInt("nonat_type");
break;
case NATRule::SNAT:
{
if ( ! rule->exists("nat_cmd") ) break;
NATCmd *natcmd = nat_commands[ rule->getInt("nat_cmd") ];
if (natcmd != nullptr)
{
os <<" NATCmd: ";
os << " rule=[" << natcmd->rule_label << "]";
os << " id=" << natcmd->nat_id;
os << " rule=" << natcmd->rule_label;
os << " nat_acl_name=" << natcmd->nat_acl_name;
os << " (" << nat_acl_names[natcmd->nat_acl_name] << ")";
os << " o_src=" << _print_addr(natcmd->o_src->getAddressPtr());
os << " o_dst=" << _print_addr(natcmd->o_dst->getAddressPtr());
os << " o_srv=" << natcmd->o_srv->getName();
os << " t_addr=" << _print_addr(natcmd->t_addr->getAddressPtr());
os << " ignore_global=" << string((natcmd->ignore_global)?"1":"0");
os << " ignore_nat=" << string((natcmd->ignore_nat)?"1":"0");
os << " ignore_nat_and_print_acl="
<< string((natcmd->ignore_nat_and_print_acl)?"1":"0");
os << " use_nat_0_0="
<< string((rule->getBool("use_nat_0_0"))?"1":"0");
}
}
break;
case NATRule::DNAT:
{
if ( ! rule->exists("sc_cmd") ) break;
StaticCmd *scmd=static_commands[ rule->getInt("sc_cmd") ];
if (scmd!=nullptr)
{
string iaddr_str = _print_addr(scmd->iaddr->getAddressPtr());
string oaddr_str = _print_addr(scmd->oaddr->getAddressPtr());
os << " StaticCmd:";
os << " ignore=" << scmd->ignore_scmd_and_print_acl;
os << " acl=" << scmd->acl_name;
os << " (" << nat_acl_names[scmd->acl_name] << ")";
os << " iaddr=" << iaddr_str;
os << " oaddr=" << oaddr_str;
os << " osrc=" << _print_addr(scmd->osrc->getAddressPtr());
os << " osrv=" << scmd->osrv->getName();
os << " tsrv=" << scmd->tsrv->getName();
}
}
break;
default: ; // TODO: should actually be always_assert
}
return NATCompiler::debugPrintRule(rule) +
" " +
" (type=" + rule->getRuleTypeAsString() + ") " +
"use_nat_0_0=" + string((rule->getBool("use_nat_0_0"))?"1":"0") + " " +
os.str();
}
/*
* store final nat rules in final rule set object in
* persistent_obejcts. Note that we can't add the same rules since an
* object can not be placed in two different places in the tree, so we
* have to add copies.
*/
bool NATCompiler_pix::storeProcessedRules::processNext()
{
NATCompiler_pix *pix_comp = dynamic_cast<NATCompiler_pix*>(compiler);
FWObject *final_ruleset = compiler->persistent_objects->getRoot()->findInIndex(
pix_comp->final_ruleset_id);
slurp();
if (tmp_queue.size()==0) return false;
for (deque<Rule*>::iterator k=tmp_queue.begin(); k!=tmp_queue.end(); ++k)
{
NATRule *rule = NATRule::cast( *k );
NATRule *r = compiler->dbcopy->createNATRule();
final_ruleset->add(r);
r->duplicate(rule);
}
return true;
}
bool NATCompiler_pix::VerifyRules::processNext()
{
NATRule *rule=getNext(); if (rule==nullptr) return false;
string version = compiler->fw->getStr("version");
if (rule->getRuleType()==NATRule::SDNAT)
{
compiler->abort(
rule,
"Rules that translate both source and destination are not supported.");
return true;
}
bool version_lt_63= (
compiler->fw->getStr("platform")=="pix" &&
libfwbuilder::XMLTools::version_compare(version, "6.3")<0); // fwsm is always above 6.3 - its OS is based on 6.3
RuleElementOSrc *osrc=rule->getOSrc(); assert(osrc);
RuleElementODst *odst=rule->getODst(); assert(odst);
RuleElementOSrv *osrv=rule->getOSrv(); assert(osrv);
RuleElementTSrc *tsrc=rule->getTSrc(); assert(tsrc);
RuleElementTDst *tdst=rule->getTDst(); assert(tdst);
RuleElementTSrv *tsrv=rule->getTSrv(); assert(tsrv);
if (rule->getRuleType()==NATRule::LB)
{
compiler->abort(
rule,
"Load balancing rules are not supported.");
return true;
}
if (rule->getRuleType()==NATRule::NONAT && (!osrv->isAny() || !tsrv->isAny()))
{
compiler->abort(
rule,
"'no nat' rules should have no services");
return true;
}
if (osrc->getNeg() ||
odst->getNeg() ||
osrv->getNeg() ||
tsrc->getNeg() ||
tdst->getNeg() ||
tsrv->getNeg())
{
compiler->abort(
rule,
"Negation is not supported in NAT rules.");
return true;
}
if (rule->getRuleType()==NATRule::SNAT)
{
// if ( tsrc->size()!=1)
// compiler->abort("There should be no more than one object in translated source in the rule "+rule->getLabel());
if ( ! odst->isAny() && version_lt_63) // can do on fwsm
{
compiler->warning(
rule,
"Original destination is ignored in 'nat' NAT rules "
"when compiling for PIX v6.2 and earlier.");
odst->clearChildren();
odst->setAnyElement();
}
}
if (rule->getRuleType()==NATRule::DNAT)
{
if ( odst->size()!=1 && version_lt_63)
{
compiler->abort(
rule,
"There should be no more than one object in original destination");
return true;
}
if ( ! osrc->isAny() && version_lt_63)
compiler->warning(
rule,
"Original source is ignored in 'static' NAT rules "
"when compiling for PIX v6.2 and earlier.");
}
if (osrv->size()!=1 && !tsrv->isAny())
{
compiler->abort(
rule,
"Can not translate multiple services into one service in one rule. ");
return true;
}
if (tsrv->size()!=1)
{
compiler->abort(
rule,
"Translated service should be 'Original' or should contain single object.");
return true;
}
if ( Group::cast( compiler->getFirstTSrv(rule) )!=nullptr)
{
compiler->abort(
rule,
"Can not use group in translated service.");
return true;
}
if (rule->getRuleType()==NATRule::SNetnat && !tsrc->isAny() )
{
Network *a1=Network::cast(compiler->getFirstOSrc(rule));
Network *a2=Network::cast(compiler->getFirstTSrc(rule));
if ( a1==nullptr || a2==nullptr ||
a1->getNetmaskPtr()->getLength()!=a2->getNetmaskPtr()->getLength() )
{
compiler->abort(
rule,
"Original and translated source should both be networks of the same size");
return true;
}
}
if (rule->getRuleType()==NATRule::DNetnat && !tsrc->isAny() )
{
Network *a1=Network::cast(compiler->getFirstODst(rule));
Network *a2=Network::cast(compiler->getFirstTDst(rule));
if ( a1==nullptr || a2==nullptr ||
a1->getNetmaskPtr()->getLength()!=a2->getNetmaskPtr()->getLength() )
{
compiler->abort(
rule,
"Original and translated destination should both be networks of the same size.");
return true;
}
}
if (rule->getRuleType()==NATRule::SNetnat) rule->setRuleType(NATRule::SNAT);
if (rule->getRuleType()==NATRule::DNetnat) rule->setRuleType(NATRule::DNAT);
tmp_queue.push_back(rule);
return true;
}
bool NATCompiler_pix::AssignInterface::processNext()
{
Helper helper(compiler);
NATRule *rule = getNext(); if (rule==nullptr) return false;
RuleElement *itf_re;
Address *a1 = nullptr;
Address *a2 = nullptr;
if (rule->getRuleType()==NATRule::SNAT || rule->getRuleType()==NATRule::SDNAT)
{
a1 = compiler->getFirstOSrc(rule);
a2 = compiler->getFirstTSrc(rule);
}
if (rule->getRuleType()==NATRule::DNAT)
{
a1 = compiler->getFirstODst(rule);
a2 = compiler->getFirstTDst(rule);
}
if (rule->getRuleType()==NATRule::NONAT)
{
a1 = compiler->getFirstOSrc(rule);
a2 = compiler->getFirstODst(rule);
}
assert(a1!=nullptr && a2!=nullptr);
int org_intf_id = helper.findInterfaceByNetzone(a1);
int trn_intf_id = helper.findInterfaceByNetzone(a2);
FWObject *iface_org = compiler->dbcopy->findInIndex(org_intf_id);
FWObject *iface_trn = compiler->dbcopy->findInIndex(trn_intf_id);
if ( org_intf_id==-1 )
{
QString err("Object '%1' does not belong to any known network zone.");
compiler->abort(rule, err.arg(a1->getName().c_str()).toStdString());
return true;
}
if ( trn_intf_id==-1 )
{
QString err("Object '%1' does not belong to any known network zone.");
compiler->abort(rule, err.arg(a2->getName().c_str()).toStdString());
return true;
}
itf_re = rule->getItfInb(); assert(itf_re!=nullptr);
if (itf_re->isAny() && ! itf_re->hasRef(iface_org)) itf_re->addRef(iface_org);
itf_re = rule->getItfOutb(); assert(itf_re!=nullptr);
if (itf_re->isAny() && ! itf_re->hasRef(iface_trn)) itf_re->addRef(iface_trn);
if (org_intf_id == trn_intf_id)
{
QString err("Objects used in Original Source and Translated Source "
"of the rule dictate that the same interface '%1' is going "
"to be used as real and mapped interface in the generated "
"nat command.");
compiler->warning(
rule,
err.arg(
Interface::cast(iface_org)->getLabel().c_str()).toStdString());
}
tmp_queue.push_back(rule);
return true;
}
bool NATCompiler_pix::verifyInterfaces::processNext()
{
NATRule *rule=getNext(); if (rule==nullptr) return false;
tmp_queue.push_back(rule);
return true;
}
bool NATCompiler_pix::verifyRuleElements::processNext()
{
NATRule *rule=getNext(); if (rule==nullptr) return false;
#ifndef NDEBUG
Address *osrc=compiler->getFirstOSrc(rule); assert(osrc);
#endif
Address *odst=compiler->getFirstODst(rule); assert(odst);
Service *osrv=compiler->getFirstOSrv(rule); assert(osrv);
#ifndef NDEBUG
Address *tsrc=compiler->getFirstTSrc(rule); assert(tsrc);
#endif
Address *tdst=compiler->getFirstTDst(rule); assert(tdst);
Service *tsrv=compiler->getFirstTSrv(rule); assert(tsrv);
string version = compiler->fw->getStr("version");
if (rule->getRuleType()==NATRule::SNAT)
{
if ((! osrv->isAny() || ! tsrv->isAny()) &&
libfwbuilder::XMLTools::version_compare(version, "6.3")<0)
{
compiler->abort(
rule,
"only PIX v6.3 and later recognizes services in global NAT.");
return true;
}
}
if (rule->getRuleType()==NATRule::DNAT)
{
if ((AddressRange::cast(odst) || AddressRange::cast(tdst)) &&
libfwbuilder::XMLTools::version_compare(version, "8.3")<0)
{
compiler->abort(
rule,
"Address ranges are not supported in original destination or "
"translated destination ");
return true;
}
if (Network::isA(odst) && Network::isA(tdst))
{
InetAddr n1 = (Interface::cast(odst)) ?
InetAddr(InetAddr::getAllOnes()) : (*(odst->getNetmaskPtr()));
InetAddr n2 = (Interface::cast(tdst)) ?
InetAddr(InetAddr::getAllOnes()) : (*(tdst->getNetmaskPtr()));
if ( !(n1==n2) )
{
compiler->abort(
rule,
"Original and translated destination must be of the same "
"size");
return true;
}
}
if (osrv->getTypeName()!=tsrv->getTypeName())
{
compiler->abort(
rule,
"Original and translated services must be of "
"the same type.");
return true;
}
if (ICMPService::isA(osrv))
{
compiler->abort(
rule,
"ICMP services are not supported in static NAT. ");
return true;
}
if (TCPService::isA(osrv) || UDPService::isA(osrv))
{
int drs=TCPUDPService::cast(osrv)->getDstRangeStart();
int dre=TCPUDPService::cast(osrv)->getDstRangeEnd();
if (drs!=dre)
{
compiler->abort(
rule,
"TCP or UDP service with a port range is not "
"supported in NAT.");
return true;
}
}
if (TCPService::isA(tsrv) || UDPService::isA(tsrv))
{
int drs=TCPUDPService::cast(tsrv)->getDstRangeStart();
int dre=TCPUDPService::cast(tsrv)->getDstRangeEnd();
if (drs!=dre)
{
compiler->abort(
rule,
"TCP or UDP service with a port range is not "
"supported in NAT.");
return true;
}
}
}
tmp_queue.push_back(rule);
return true;
}
bool NATCompiler_pix::fillTranslatedSrv::processNext()
{
NATRule *rule=getNext(); if (rule==nullptr) return false;
tmp_queue.push_back(rule);
Service *osrv_o=compiler->getFirstOSrv(rule);
Service *tsrv_o=compiler->getFirstTSrv(rule);
if ( ! osrv_o->isAny() && tsrv_o->isAny() )
{
RuleElementTSrv *tsrv=rule->getTSrv();
tsrv->addRef(osrv_o);
}
return true;
}
/**
* unlike standard inspector addressRanges in the base class NATCompiler,
* this one does not expand address ranges in TSrc and TDst
*/
bool NATCompiler_pix::ExpandAddressRanges::processNext()
{
NATRule *rule=getNext(); if (rule==nullptr) return false;
tmp_queue.push_back(rule);
RuleElement *rel;
rel=rule->getOSrc(); assert(rel);
compiler->_expandAddressRanges(rule,rel);
rel=rule->getODst(); assert(rel);
compiler->_expandAddressRanges(rule,rel);
#if 0
// if we want to support NAT rules with address ranges. For example,
// could compile these as a bunch of individual host translations
switch (rule->getRuleType())
{
case NATRule::SNAT:
rel=rule->getTSrc(); assert(rel);
compiler->_expandAddressRanges(rule,rel);
break;
case NATRule::DNAT:
rel=rule->getTDst(); assert(rel);
compiler->_expandAddressRanges(rule,rel);
break;
}
#endif
return true;
}
/*
* I assume that there is always only one object in ODst, TSrc and TDst
* rule elements. This should have been assured by inspector VerifyRules
*/
bool NATCompiler_pix::ReplaceFirewallObjectsODst::processNext()
{
NATRule *rule=getNext(); if (rule==nullptr) return false;
tmp_queue.push_back(rule);
list<FWObject*> cl;
RuleElementODst *rel;
Address *obj=nullptr;
switch (rule->getRuleType()) {
case NATRule::Masq:
// case NATRule::Redirect:
return true;
case NATRule::DNAT:
rel=rule->getODst(); assert(rel);
obj=compiler->getFirstODst(rule); assert(obj!=nullptr);
if (obj->getId()==compiler->getFwId() )
{
list<FWObject*> l2=compiler->fw->getByType(Interface::TYPENAME);
for (list<FWObject*>::iterator i=l2.begin(); i!=l2.end(); ++i)
{
Interface *iface = Interface::cast(*i);
if (! iface->isLoopback() && iface->getSecurityLevel()==0 )
cl.push_back(iface);
}
if ( ! cl.empty() )
{
// while (rel->size())
// rel->remove( rel->front() );
rel->clearChildren();
for (FWObject::iterator i1=cl.begin(); i1!=cl.end(); ++i1)
{
rel->addRef( *i1 );
}
}
}
default: ; // TODO: should actually be always_assert
}
return true;
}
bool NATCompiler_pix::ReplaceFirewallObjectsTSrc::processNext()
{
Helper helper(compiler);
NATRule *rule=getNext(); if (rule==nullptr) return false;
list<FWObject*> cl;
RuleElementTSrc *rel;
Address *obj=nullptr;
switch (rule->getRuleType()) {
case NATRule::Masq:
case NATRule::Redirect:
{
tmp_queue.push_back(rule);
return true;
}
case NATRule::SNAT:
{
int osrc_level=100;
Address *osrc=nullptr;
Interface *osrc_iface=nullptr;
if ( ! rule->getOSrc()->isAny())
{
osrc=compiler->getFirstOSrc(rule); assert(osrc!=nullptr);
osrc_iface = Interface::cast(
compiler->dbcopy->findInIndex( helper.findInterfaceByNetzone(osrc)));
osrc_level = osrc_iface->getSecurityLevel();
}
rel = rule->getTSrc();
assert(rel);
if (rel->size() == 0)
{
compiler->abort(rule, "Empty TSrc");
return true;
}
obj = compiler->getFirstTSrc(rule);
assert(obj!=nullptr);
if (obj->getId()==compiler->getFwId() )
{
/* if ODst is 'any', pick all interfaces with security level _less_ than
* level of the interface OSrc is associated with. If ODst is not 'any',
* find interface it is associated with and use only it.
*/
if (rule->getODst()->isAny())
{
list<FWObject*> l2=compiler->fw->getByType(Interface::TYPENAME);
for (list<FWObject*>::iterator i=l2.begin(); i!=l2.end(); ++i)
{
Interface *iface = Interface::cast(*i);
if (iface->getSecurityLevel()<osrc_level )
cl.push_back(iface);
}
} else
{
Address *odst = compiler->getFirstODst(rule); assert(odst!=nullptr);
FWObject *odst_iface =
compiler->dbcopy->findInIndex(
helper.findInterfaceByNetzone(odst ) );
if (odst_iface!=nullptr) cl.push_back(odst_iface);
}
if ( ! cl.empty() )
{
// while (rel->size())
// rel->remove( rel->front() );
rel->clearChildren();
for (FWObject::iterator i1=cl.begin(); i1!=cl.end(); ++i1)
{
rel->addRef( *i1 );
}
}
}
}
break;
default: ; // TODO: should actually be always_assert
}
tmp_queue.push_back(rule);
return true;
}
void NATCompiler_pix::UseFirewallInterfaces::scanInterfaces(RuleElement *rel)
{
FWObject *o= rel->front();
if (FWReference::cast(o)!=nullptr) o=FWReference::cast(o)->getPointer();
Address *obj=Address::cast(o);
if(obj==nullptr)
{
compiler->abort(rel->getParent(),
"Broken rule element "+
rel->getTypeName()+
" in rule "+
NATRule::cast(rel->getParent())->getLabel()+
" ( found object with type "+
string((o!=nullptr)?o->getTypeName():"<nullptr>") +
")");
return;
}
const InetAddr *obj_addr = obj->getAddressPtr();
if (obj_addr==nullptr) return;
list<FWObject*> l2=compiler->fw->getByType(Interface::TYPENAME);
for (list<FWObject*>::iterator i=l2.begin(); i!=l2.end(); ++i)
{
Interface *iface=Interface::cast(*i);
const InetAddr *iface_addr = iface->getAddressPtr();
if (iface_addr == nullptr) continue;
if (*iface_addr == *obj_addr)
{
rel->removeRef(obj);
rel->addRef(iface);
return;
}
}
}
bool NATCompiler_pix::UseFirewallInterfaces::processNext()
{
NATRule *rule=getNext(); if (rule==nullptr) return false;
tmp_queue.push_back(rule);
RuleElement *rel;
rel=rule->getODst(); assert(rel);
if (!rel->isAny()) scanInterfaces(rel);
rel=rule->getTSrc(); assert(rel);
if (!rel->isAny()) scanInterfaces(rel);
return true;
}
bool NATCompiler_pix::processNONATRules::processNext()
{
Helper helper(compiler);
NATCompiler_pix *pix_comp = dynamic_cast<NATCompiler_pix*>(compiler);
NATRule *rule=getNext(); if (rule==nullptr) return false;
tmp_queue.push_back(rule);
if (rule->getRuleType()==NATRule::NONAT)
{
Address *osrc=compiler->getFirstOSrc(rule); assert(osrc);
Address *odst=compiler->getFirstODst(rule); assert(odst);
Interface *osrc_iface = Interface::cast(
compiler->dbcopy->findInIndex(helper.findInterfaceByNetzone(osrc)));
Interface *odst_iface = Interface::cast(
compiler->dbcopy->findInIndex(helper.findInterfaceByNetzone(odst)));
int osrc_level = osrc_iface->getSecurityLevel();
int odst_level = odst_iface->getSecurityLevel();
/*
* PIX has two types of NONAT rules, one is when connection goes from
* low security interface to the high security interface and another
* for the opposite
*/
if (osrc_level>odst_level)
{
rule->setInt("nonat_type", NONAT_NAT0);
nonat n0;
// n0.i_iface = osrc_iface;
// n0.o_iface = odst_iface;
RuleElement *itf_re = rule->getItfInb();
assert(itf_re!=nullptr);
if ( ! itf_re->hasRef(osrc_iface)) itf_re->addRef(osrc_iface);
itf_re = rule->getItfOutb();
assert(itf_re!=nullptr);
if ( ! itf_re->hasRef(odst_iface)) itf_re->addRef(odst_iface);
n0.src = osrc;
n0.dst = odst;
n0.acl_name = "nat0."+osrc_iface->getLabel();
n0.last = true;
pix_comp->nonat_rules[rule->getId()] = n0;
pix_comp->registerACL(n0.acl_name);
if (pix_comp->first_nonat_rule_id.count(osrc_iface->getId()) == 0)
pix_comp->first_nonat_rule_id[osrc_iface->getId()] =
rule->getId();
} else
{
rule->setInt("nonat_type", NONAT_STATIC);
Interface *osrc_iface = Interface::cast(
compiler->dbcopy->findInIndex(helper.findInterfaceByNetzone(osrc)));
Interface *odst_iface = Interface::cast(
compiler->dbcopy->findInIndex(helper.findInterfaceByNetzone(odst)));
RuleElement *itf_re = rule->getItfInb();
assert(itf_re!=nullptr);
if ( ! itf_re->hasRef(osrc_iface)) itf_re->addRef(osrc_iface);
itf_re = rule->getItfOutb();
assert(itf_re!=nullptr);
if ( ! itf_re->hasRef(odst_iface)) itf_re->addRef(odst_iface);
}
}
return true;
}
bool NATCompiler_pix::createNATCmd::processNext()
{
// Helper helper(compiler);
NATCompiler_pix *pix_comp = dynamic_cast<NATCompiler_pix*>(compiler);
NATRule *rule = getNext(); if (rule==nullptr) return false;
string version = compiler->fw->getStr("version");
if (rule->getRuleType()==NATRule::SNAT)
{
Address *osrc = compiler->getFirstOSrc(rule); assert(osrc);
Address *odst = compiler->getFirstODst(rule); assert(osrc);
Service *osrv = compiler->getFirstOSrv(rule); assert(osrv);
Address *tsrc = compiler->getFirstTSrc(rule); assert(tsrc);
RuleElementItfInb *itf_in_re = rule->getItfInb();
RuleElementItfOutb *itf_out_re = rule->getItfOutb();
Interface *i_iface = Interface::cast(
FWObjectReference::getObject(itf_in_re->front()));
Interface *o_iface = Interface::cast(
FWObjectReference::getObject(itf_out_re->front()));
NATCmd *natcmd = new NATCmd();
natcmd->nat_id = nat_id_counter;
natcmd->rule_label = rule->getLabel();
natcmd->o_src = osrc;
natcmd->o_dst = odst;
natcmd->o_srv = osrv;
natcmd->t_addr = tsrc;
natcmd->i_iface = i_iface; // inbound interface
natcmd->o_iface = o_iface; // outbound interface
natcmd->nat_acl_name = pix_comp->getNATACLname(rule,"");
pix_comp->registerACL(natcmd->nat_acl_name);
if (Interface::cast(tsrc)!=nullptr || o_iface->isDyn())
{
natcmd->type = INTERFACE;
} else
{
if (Network::cast(tsrc))
{
natcmd->type = NETWORK_ADDRESS;
} else {
if (AddressRange::cast(tsrc)) natcmd->type = ADDRESS_RANGE;
else natcmd->type = SINGLE_ADDRESS;
}
}
natcmd->ignore_nat = natcmd->ignore_nat_and_print_acl =
natcmd->ignore_global = false;
natcmd->use_nat_0_0 = rule->getBool("use_nat_0_0");
/*
* "nat ... outside" is only supported in PIX 6.2
*/
natcmd->outside =
( i_iface->getSecurityLevel() < o_iface->getSecurityLevel());
if (natcmd->outside && compiler->fw->getStr("platform")=="pix" &&
libfwbuilder::XMLTools::version_compare(version, "6.2")<0 )
{
compiler->abort(
rule,
"Bi-Directional NAT of source addresses is only "
"supported in PIX 6.2 and newer.");
return true;
}
/*
* map is sorted container, this means that objects are going to be arranged
* in nat_commands in the order of the key.
*/
pix_comp->nat_commands[nat_id_counter]= natcmd;
rule->setInt("nat_cmd",nat_id_counter);
nat_id_counter++;
}
tmp_queue.push_back(rule);
return true;
}
bool NATCompiler_pix::createStaticCmd::processNext()
{
NATCompiler_pix *pix_comp=dynamic_cast<NATCompiler_pix*>(compiler);
NATRule *rule=getNext(); if (rule==nullptr) return false;
tmp_queue.push_back(rule);
if (rule->getRuleType()==NATRule::DNAT)
{
Address *osrc = compiler->getFirstOSrc(rule); assert(osrc);
Address *odst = compiler->getFirstODst(rule); assert(odst);
Service *osrv = compiler->getFirstOSrv(rule); assert(osrv);
Address *tdst = compiler->getFirstTDst(rule); assert(tdst);
Service *tsrv = compiler->getFirstTSrv(rule); assert(tsrv);
RuleElementItfInb *itf_in_re = rule->getItfInb();
RuleElementItfOutb *itf_out_re = rule->getItfOutb();
Interface *i_iface = Interface::cast(
FWObjectReference::getObject(itf_in_re->front()));
Interface *o_iface = Interface::cast(
FWObjectReference::getObject(itf_out_re->front()));
StaticCmd *scmd = new StaticCmd();
scmd->acl_name = pix_comp->getNATACLname(rule,"");
pix_comp->registerACL(scmd->acl_name);
scmd->rule=rule->getLabel();
// source and destination addresses are swapped here because
// access lists used for NAT should have 'real' addresses in source
scmd->iaddr=tdst;
scmd->oaddr=odst;
scmd->osrc= osrc;
scmd->osrv= osrv;
scmd->tsrv= tsrv;
scmd->ignore_scmd_and_print_acl=false;
scmd->i_iface = i_iface;
scmd->o_iface = o_iface;
pix_comp->static_commands[sc_id_counter]=scmd;
rule->setInt("sc_cmd",sc_id_counter);
sc_id_counter++;
}
return true;
}
bool NATCompiler_pix::clearOSrc::processNext()
{
// NATCompiler_pix *pix_comp=dynamic_cast<NATCompiler_pix*>(compiler);
NATRule *rule=getNext(); if (rule==nullptr) return false;
if (rule->getBool("clear_osrc"))
{
RuleElementOSrc *osrc=rule->getOSrc();
osrc->clearChildren();
osrc->setAnyElement();
}
tmp_queue.push_back(rule);
return true;
}
bool NATCompiler_pix::processMultiAddressObjectsInRE::processNext()
{
NATRule *rule=getNext(); if (rule==nullptr) 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)!=nullptr) o=FWReference::cast(o)->getPointer();
MultiAddress *atrt = MultiAddress::cast(o);
if (atrt!=nullptr && atrt->isRunTime())
{
compiler->abort(
rule,
"Run-time AddressTable and DNSName objects are not supported.");
return true;
}
}
tmp_queue.push_back(rule);
return true;
}
void NATCompiler_pix::compile()
{
info(" Compiling NAT rules for " + fw->getName());
Compiler::compile();
add( new Begin( "Begin processing"));
add( new printTotalNumberOfRules());
add( new singleRuleFilter());
add(new expandGroupsInItfInb("expand groups in inbound Interface"));
add(new replaceClusterInterfaceInItfInb(
"replace cluster interfaces with member interfaces in "
"the inbound Interface rule element"));
add(new ItfInbNegation("process negation in inbound Itf"));
add(new expandGroupsInItfOutb("expand groups in outbound Interface"));
add(new replaceClusterInterfaceInItfOutb(
"replace cluster interfaces with member interfaces in "
"the outbound Interface rule element"));
add(new ItfOutbNegation("process negation in outbound Itf"));
add( new ConvertToAtomicForItfInb("convert to atomic for inbound interface") );
add( new ConvertToAtomicForItfOutb("convert to atomic for outbound interface"));
if (fw->getOptionsObject()->getBool( "pix_optimize_default_nat"))
add (new optimizeDefaultNAT(
"optimize commands 'nat (interface) 0.0.0.0 0.0.0.0'"));
add( new recursiveGroupsInOSrc("check for recursive groups in OSRC"));
add( new recursiveGroupsInODst("check for recursive groups in ODST"));
add( new recursiveGroupsInOSrv("check for recursive groups in OSRV"));
add( new recursiveGroupsInTSrc("check for recursive groups in TSRC"));
add( new recursiveGroupsInTDst("check for recursive groups in TDST"));
add( new recursiveGroupsInTSrv("check for recursive groups in TSRV"));
add( new emptyGroupsInOSrc("check for empty groups in OSRC"));
add( new emptyGroupsInODst("check for empty groups in ODST"));
add( new emptyGroupsInOSrv("check for empty groups in OSRV"));
add( new emptyGroupsInTSrc("check for empty groups in TSRC"));
add( new emptyGroupsInTDst("check for empty groups in TDST"));
add( new emptyGroupsInTSrv("check for empty groups in TSRV"));
add( new ExpandGroups("expand groups"));
/*
* We do not support ipv6 yet
*/
add( new DropIPv6RulesWithWarning(
"drop ipv6 rules",
"Rule has been suppressed because it contains IPv6 objects and "
"Firewall Builder does not support IPv6 for this platform"));
add( new eliminateDuplicatesInOSRC("eliminate duplicates in OSRC"));
add( new eliminateDuplicatesInODST("eliminate duplicates in ODST"));
add( new eliminateDuplicatesInOSRV("eliminate duplicates in OSRV"));
add( new processMultiAddressObjectsInOSrc(
"process MultiAddress objects in OSrc"));
add( new processMultiAddressObjectsInODst(
"process MultiAddress objects in ODst"));
add( new classifyNATRule("determine NAT rule types"));
add( new VerifyRules("verify rules" ));
// ReplaceFirewallObjectsODst, ReplaceFirewallObjectsODst and
// UseFirewallInterfaces assume there is one object in ODst,
// TSrc and TDst rule elements. This should have been assured
// by inspector VerifyRules
add( new ReplaceFirewallObjectsODst("replace fw object in ODst" ));
add( new ReplaceFirewallObjectsTSrc("replace fw object in TSrc" ));
add( new UseFirewallInterfaces(
"replace host objects with firewall's interfaces if the have the same address"));
// ExpandMultipleAddresses acts on different rule elements
// depending on the rule type.
// Also using overloaded virtual function _expand_interface
add( new ExpandMultipleAddresses("expand multiple addresses"));
add( new MACFiltering( "check for MAC address filtering"));
add( new ExpandAddressRanges("expand address range objects"));
add( new checkForUnnumbered("check for unnumbered interfaces"));
add( new ConvertToAtomic("convert to atomic rules" ));
add( new AssignInterface("assign rules to interfaces" ));
add( new verifyInterfaces("verify interfaces assignment" ));
add( new fillTranslatedSrv("fill translated service element" ));
add( new verifyRuleElements(
"verify rule elements for static NAT rules"));
add( new processNONATRules("process NONAT" ));
if (fw->getOptionsObject()->getBool("pix_optimize_default_nat"))
add (new clearOSrc ("clear OSrc" ));
add( new SpecialServicesOSrv( "check for special services" ));
add( new createNATCmd ("create NAT commands" ));
add( new createStaticCmd ("create static commands" ));
add( new mergeNATCmd ("merge NAT commands" ));
add( new SuppressDuplicateNONATStatics(
"suppress duplicate NONAT statics" ));
add( new checkForObjectsWithErrors(
"check if we have objects with errors in rule elements"));
//add( new PrintClearCommands( "Clear ACLs" ));
add( new PrintRule ("generate PIX code" ));
add( new storeProcessedRules ("store processed rules" ));
add( new simplePrintProgress ());
bool pix_check_duplicate_nat =
fw->getOptionsObject()->getBool("pix_check_duplicate_nat");
bool pix_check_overlapping_global_pools =
fw->getOptionsObject()->getBool("pix_check_overlapping_global_pools");
bool pix_check_overlapping_statics =
fw->getOptionsObject()->getBool("pix_check_overlapping_statics");
bool pix_check_overlapping_global_statics =
fw->getOptionsObject()->getBool("pix_check_overlapping_global_statics");
if ( pix_check_duplicate_nat ||
pix_check_overlapping_global_pools ||
pix_check_overlapping_statics ||
pix_check_overlapping_global_statics )
{
add( new createNewCompilerPass(" Detecting nat problems ..."));
if ( pix_check_duplicate_nat )
add( new DetectDuplicateNAT(" Detect duplicate nat entries"));
if ( pix_check_overlapping_global_pools )
add( new DetectGlobalPoolProblems(
" Detect global pool overlapping" ));
if ( pix_check_overlapping_statics )
add( new DetectOverlappingStatics(
" Detect overlapping statics" ));
if ( pix_check_overlapping_global_statics )
add( new DetectOverlappingGlobalPoolsAndStaticRules(
" Detect overlapping global pools and statics" ));
add( new simplePrintProgress ( ));
}
runRuleProcessors();
}
void NATCompiler_pix::regroup()
{
list<string> commands;
map<string,list<string> > script;
commands.push_back("THE_REST");
commands.push_back("access-list ");
commands.push_back("global ");
commands.push_back("nat ");
commands.push_back("static ");
string acl, agrp, icmp, telnet, ssh;
string new_output;
char buf[1024];
istringstream in(output.str());
while (in)
{
in.getline(buf, 1023, '\n');
strcat(buf,"\n");
if (buf[0]=='!') continue;
string slot="THE_REST";
for (list<string>::iterator i=commands.begin(); i!=commands.end(); ++i)
{
if (strncmp(buf, (*i).c_str(), (*i).size())==0)
{
slot= *i;
break;
}
}
script[slot].push_back(buf);
}
output.str("");
for (list<string>::iterator i=commands.begin(); i!=commands.end(); ++i)
{
for (list<string>::iterator j=script[*i].begin(); j!=script[*i].end(); ++j)
output << *j;
output << "! \n";
output << "! \n";
}
}
void NATCompiler_pix::epilog()
{
if ( fw->getOptionsObject()->getBool("pix_regroup_commands"))
{
info(" Regrouping commands");
regroup();
}
}
string NATCompiler_pix::printClearCommands()
{
ostringstream output;
string version = fw->getStr("version");
string platform = fw->getStr("platform");
if ( !fw->getOptionsObject()->getBool("pix_acl_no_clear") &&
!inSingleRuleCompileMode())
{
output << Resources::platform_res[platform]->getResourceStr(
string("/FWBuilderResources/Target/options/") +
"version_" + version + "/pix_commands/clear_xlate") << endl;
output << Resources::platform_res[platform]->getResourceStr(
string("/FWBuilderResources/Target/options/") +
"version_" + version + "/pix_commands/clear_static") << endl;
output << Resources::platform_res[platform]->getResourceStr(
string("/FWBuilderResources/Target/options/") +
"version_" + version + "/pix_commands/clear_global") << endl;
output << Resources::platform_res[platform]->getResourceStr(
string("/FWBuilderResources/Target/options/") +
"version_" + version + "/pix_commands/clear_nat") << endl;
}
return output.str();
}
/*
* This includes commands that should be added first, such as commit mode
* for FWSM, setting up temporary access list etc.
*/
string NATCompiler_pix::printPreambleCommands()
{
return "";
}
class MergeConflictRes : public FWObjectDatabase::ConflictResolutionPredicate
{
public:
MergeConflictRes() { }
virtual bool askUser(FWObject*, FWObject*) {return false;}
};
void NATCompiler_pix::setNamedObjectsManager(NamedObjectsManager *mgr)
{
named_objects_manager = mgr;
}