1
0
mirror of https://github.com/fwbuilder/fwbuilder synced 2026-03-23 19:57:21 +01:00
fwbuilder/src/cisco_lib/PolicyCompiler_iosacl_writers.cpp
Vadim Kurland 33fac22504 * PolicyCompiler_iosacl_writers.cpp (PrintRule::_printTCPFlags):
Implemented TCP flag matching per #2865044: "Add TCP options
support for IOS ACL". Uses extended ACL option "match-all" that
supports list of TCP flags that should be set and cleared. This
requires IOS v12.4 or later even though Cisco documentation seems
to indicate this option was introduced in 12.3(4)T. Fixes #455
2009-11-08 06:21:39 +00:00

580 lines
17 KiB
C++

/*
Firewall Builder
Copyright (C) 2007 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 "PolicyCompiler_iosacl.h"
#include "fwbuilder/Firewall.h"
#include "fwbuilder/AddressRange.h"
#include "fwbuilder/RuleElement.h"
#include "fwbuilder/IPService.h"
#include "fwbuilder/ICMPService.h"
#include "fwbuilder/ICMP6Service.h"
#include "fwbuilder/TCPService.h"
#include "fwbuilder/UDPService.h"
#include "fwbuilder/CustomService.h"
#include "fwbuilder/Policy.h"
#include "fwbuilder/FWOptions.h"
#include "fwbuilder/FWObjectDatabase.h"
#include "fwbuilder/Interface.h"
#include "fwbuilder/IPv4.h"
#include "fwbuilder/IPv6.h"
#include "fwbuilder/Network.h"
#include "fwbuilder/Management.h"
#include "fwbuilder/Resources.h"
#include "fwbuilder/XMLTools.h"
#include <iostream>
#if __GNUC__ > 3 || \
(__GNUC__ == 3 && (__GNUC_MINOR__ > 2 || (__GNUC_MINOR__ == 2 ) ) ) || \
_MSC_VER
# include <streambuf>
#else
# include <streambuf.h>
#endif
#include <iomanip>
#include <fstream>
#include <sstream>
#include <algorithm>
#include <functional>
#include <assert.h>
#include <QStringList>
using namespace libfwbuilder;
using namespace fwcompiler;
using namespace std;
bool PolicyCompiler_iosacl::ClearACLs::processNext()
{
PolicyCompiler_iosacl *iosacl_comp=dynamic_cast<PolicyCompiler_iosacl*>(compiler);
string vers = compiler->fw->getStr("version");
string platform = compiler->fw->getStr("platform");
string clearACLcmd = Resources::platform_res[platform]->getResourceStr(
string("/FWBuilderResources/Target/options/")+
"version_"+vers+"/iosacl_commands/clear_acl");
slurp();
if (tmp_queue.size()==0) return false;
if ( compiler->fw->getOptionsObject()->getBool("iosacl_acl_basic") )
{
compiler->output << clearACLcmd << endl;
}
if (compiler->fw->getOptionsObject()->getBool("iosacl_acl_substitution"))
{
for (map<string,ciscoACL*>::iterator i=iosacl_comp->acls.begin();
i!=iosacl_comp->acls.end(); ++i)
{
ciscoACL *acl=(*i).second;
compiler->output << clearACLcmd << " " << acl->workName() << endl;
}
compiler->output << endl;
}
if ( !compiler->fw->getOptionsObject()->getBool("iosacl_acl_no_clear") )
{
string clearICMPcmd = Resources::platform_res[platform]->getResourceStr(
string("/FWBuilderResources/Target/options/")+
"version_"+vers+"/iosacl_commands/clear_icmp");
string clearTelnetcmd = Resources::platform_res[platform]->getResourceStr(
string("/FWBuilderResources/Target/options/")+
"version_"+vers+"/iosacl_commands/clear_telnet");
string clearSSHcmd = Resources::platform_res[platform]->getResourceStr(
string("/FWBuilderResources/Target/options/")+
"version_"+vers+"/iosacl_commands/clear_ssh");
//compiler->output << clearICMPcmd << endl;
//compiler->output << clearTelnetcmd << endl;
//compiler->output << clearSSHcmd << endl;
}
return true;
}
void PolicyCompiler_iosacl::PrintCompleteACLs::printRulesForACL::operator()(
Rule* rule)
{
// print rule if it belongs to ACL <acl>
PolicyRule *prule = PolicyRule::cast(rule);
string acl_name = prule->getStr("acl");
assert (acl_name!="");
ciscoACL *rule_acl = iosacl_comp->acls[acl_name];
assert(rule_acl!=NULL);
if (acl == rule_acl)
{
*output << print_acl_p->_printRule(prule);
}
}
bool PolicyCompiler_iosacl::PrintCompleteACLs::processNext()
{
PolicyCompiler_iosacl *iosacl_comp=dynamic_cast<PolicyCompiler_iosacl*>(compiler);
string vers = compiler->fw->getStr("version");
string platform = compiler->fw->getStr("platform");
string xml_element = "clear_ip_acl";
if (iosacl_comp->ipv6) xml_element = "clear_ipv6_acl";
string clearACLCmd = Resources::platform_res[platform]->getResourceStr(
string("/FWBuilderResources/Target/options/")+
"version_"+vers+"/iosacl_commands/" + xml_element);
assert( !clearACLCmd.empty());
slurp();
if (tmp_queue.size()==0) return false;
if ( compiler->fw->getOptionsObject()->getBool("iosacl_acl_basic") ||
compiler->fw->getOptionsObject()->getBool("iosacl_acl_substitution"))
{
for (map<string,ciscoACL*>::iterator i=iosacl_comp->acls.begin();
i!=iosacl_comp->acls.end(); ++i)
{
ciscoACL *acl=(*i).second;
compiler->output << clearACLCmd << " " << acl->workName() << endl;
}
compiler->output << endl;
}
string addr_family_prefix = "ip";
if (iosacl_comp->ipv6) addr_family_prefix = "ipv6";
for (map<string,ciscoACL*>::iterator i=iosacl_comp->acls.begin();
i!=iosacl_comp->acls.end(); ++i)
{
ciscoACL *acl=(*i).second;
compiler->output << addr_family_prefix
<< " access-list ";
if (!iosacl_comp->ipv6) compiler->output << "extended ";
compiler->output<< acl->workName() << endl;
std::for_each(tmp_queue.begin(), tmp_queue.end(),
printRulesForACL(iosacl_comp,
this, acl, &(compiler->output)));
compiler->output << "exit" << endl;
compiler->output << endl;
}
return true;
}
string PolicyCompiler_iosacl::PrintRule::_printRule(PolicyRule *rule)
{
PolicyCompiler_iosacl *iosacl_comp =
dynamic_cast<PolicyCompiler_iosacl*>(compiler);
//FWOptions *ruleopt =rule->getOptionsObject();
bool write_comments =
compiler->fw->getOptionsObject()->getBool("iosacl_include_comments");
ostringstream ruleout;
ostringstream aclstr;
string rl=rule->getLabel();
if (write_comments && !compiler->inSingleRuleCompileMode())
{
if (rl!=current_rule_label1)
{
ruleout << "! " << endl;
ruleout << "! Rule " << rl << endl;
string comm=rule->getComment();
string::size_type c1,c2;
c1=0;
while ( (c2=comm.find('\n',c1))!=string::npos ) {
ruleout << "! " << comm.substr(c1,c2-c1) << endl;
c1=c2+1;
}
ruleout << "! " << comm.substr(c1) << endl;
ruleout << "! " << endl;
current_rule_label1=rl;
}
}
string err = rule->getStr(".error_msg");
if (!err.empty()) ruleout << "! " << err << endl;
/*
* all three rule elements contain exactly one object, which can
* be either group (in case processor CreateObjectGroups created
* object group for it) or a regular object
*/
RuleElementSrc *src=rule->getSrc();
RuleElementDst *dst=rule->getDst();
RuleElementSrv *srv=rule->getSrv();
assert(src->size()==1);
assert(dst->size()==1);
assert(srv->size()==1);
FWObject *srcobj=src->front();
FWObject *dstobj=dst->front();
FWObject *srvobj=srv->front();
assert(srcobj);
assert(dstobj);
assert(srvobj);
if (FWReference::cast(srcobj)!=NULL)
{
srcobj=FWReference::cast(srcobj)->getPointer();
assert(srcobj);
}
if (FWReference::cast(dstobj)!=NULL)
{
dstobj=FWReference::cast(dstobj)->getPointer();
assert(dstobj);
}
if (FWReference::cast(srvobj)!=NULL)
{
srvobj=FWReference::cast(srvobj)->getPointer();
assert(srvobj);
}
string acl_name=rule->getStr("acl");
assert (acl_name!="");
ciscoACL *acl = iosacl_comp->acls[acl_name];
assert(acl!=NULL);
/*
* Assemble ACL command in aclstr
*/
aclstr << _printAction(rule);
aclstr << _printProtocol(Service::cast(srvobj));
aclstr << _printAddr( compiler->getFirstSrc(rule) );
aclstr << _printSrcService( compiler->getFirstSrv(rule) );
aclstr << _printAddr( compiler->getFirstDst(rule) );
aclstr << _printDstService( compiler->getFirstSrv(rule) );
aclstr << _printLog( rule );
// "fragments" should be the last option in the access-list command
aclstr << _printIPServiceOptions(rule);
// aclstr << endl;
if (compiler->fw->getOptionsObject()->getBool("iosacl_use_acl_remarks"))
{
ruleout << acl->addRemark(rule->getLabel(), rule->getComment());
}
ruleout << acl->addLine(aclstr.str());
return ruleout.str();
}
string PolicyCompiler_iosacl::PrintRule::_printAction(PolicyRule *rule)
{
ostringstream str;
switch (rule->getAction()) {
case PolicyRule::Accept: str << "permit "; break;
case PolicyRule::Deny: str << "deny "; break;
case PolicyRule::Reject: str << "deny "; break;
default: str << rule->getActionAsString() << " ";
}
return str.str();
}
string PolicyCompiler_iosacl::PrintRule::_printACL(PolicyRule *rule)
{
// PolicyCompiler_iosacl *iosacl_comp=dynamic_cast<PolicyCompiler_iosacl*>(compiler);
string acl_name=rule->getStr("acl");
assert (acl_name!="");
return acl_name+" ";
}
string PolicyCompiler_iosacl::PrintRule::_printLog(PolicyRule *rule)
{
if (rule->getLogging())
{
FWOptions *ruleopt =rule->getOptionsObject();
if (ruleopt->getBool("iosacl_log_input")) return "log-input ";
return "log ";
}
return "";
}
string PolicyCompiler_iosacl::PrintRule::_printSrcService(Service *srv)
{
ostringstream str;
if (TCPService::isA(srv) || UDPService::isA(srv))
{
int rs=TCPUDPService::cast(srv)->getSrcRangeStart();
int re=TCPUDPService::cast(srv)->getSrcRangeEnd();
if (rs<0) rs=0;
if (re<0) re=0;
if (rs>0 || re>0) {
if (rs==re) str << "eq " << rs << " ";
else
if (rs==0 && re!=0) str << "lt " << re << " ";
else
if (rs!=0 && re==65535) str << "gt " << rs << " ";
else
str << "range " << rs << " " << re << " ";
}
}
return str.str();
}
string PolicyCompiler_iosacl::PrintRule::_printIPServiceOptions(PolicyRule *r)
{
Service *srv = compiler->getFirstSrv(r);
const IPService *ip;
if ((ip=IPService::constcast(srv))!=NULL)
{
string version = compiler->fw->getStr("version");
if (srv->getBool("fragm") || srv->getBool("short_fragm"))
return "fragments ";
if (ip->hasIpOptions() && XMLTools::version_compare(version, "12.4")<0)
compiler->abort(r, "IP options match requires IOS v12.4 or later.");
if (ip->getBool("lsrr")) return "option lsr";
if (ip->getBool("ssrr")) return "option ssr";
if (ip->getBool("rr")) return "option record-route";
if (ip->getBool("rtralt")) return "option router-alert";
if (ip->getBool("any_opt")) return "option any-options ";
string tos = ip->getTOSCode();
string dscp = ip->getDSCPCode();
if (!dscp.empty()) return string("dscp ") + dscp;
else
if (!tos.empty()) return string("tos ") + tos;
}
return "";
}
string PolicyCompiler_iosacl::PrintRule::_printDstService(Service *srv)
{
ostringstream str;
if (TCPService::isA(srv) || UDPService::isA(srv))
{
int rs=TCPUDPService::cast(srv)->getDstRangeStart();
int re=TCPUDPService::cast(srv)->getDstRangeEnd();
if (rs<0) rs=0;
if (re<0) re=0;
if (rs>0 || re>0) {
if (rs==re) str << "eq " << rs << " ";
else
if (rs==0 && re!=0) str << "lt " << re << " ";
else
if (rs!=0 && re==65535) str << "gt " << rs << " ";
else
str << "range " << rs << " " << re << " ";
}
}
if (TCPService::isA(srv))
{
if (srv->getBool("established")) str << "established ";
else str << _printTCPFlags(TCPService::cast(srv));
}
if ((ICMPService::isA(srv) || ICMP6Service::isA(srv)) && srv->getInt("type")!=-1)
str << srv->getStr("type") << " ";
if (CustomService::isA(srv))
str << CustomService::cast(srv)->getCodeForPlatform(
compiler->myPlatformName() ) << " ";
return str.str();
}
string PolicyCompiler_iosacl::PrintRule::getTcpFlagName(const TCPService::TCPFlag f)
{
switch (f)
{
case TCPService::URG: return "urg";
case TCPService::ACK: return "ack";
case TCPService::PSH: return "psh";
case TCPService::RST: return "rst";
case TCPService::SYN: return "syn";
case TCPService::FIN: return "fin";
default: return "";
}
return "";
}
string PolicyCompiler_iosacl::PrintRule::_printTCPFlags(TCPService *srv)
{
if (srv->inspectFlags())
{
// We check the version and call compiler->abort() if its
// wrong in SpecialServices rule processor. Here we should just execute.
string version = compiler->fw->getStr("version");
if (XMLTools::version_compare(version, "12.4")>=0)
{
std::set<TCPService::TCPFlag> flags = srv->getAllTCPFlags();
std::set<TCPService::TCPFlag> masks = srv->getAllTCPFlagMasks();
std::set<TCPService::TCPFlag>::iterator mit = masks.begin();
QStringList match_specs;
for (; mit!=masks.end(); mit++)
{
if (flags.count(*mit) > 0)
match_specs.push_back(QString("+%1").arg(getTcpFlagName(*mit).c_str()));
else
match_specs.push_back(QString("-%1").arg(getTcpFlagName(*mit).c_str()));
}
if (!match_specs.empty())
match_specs.push_front("match-all");
return match_specs.join(" ").toStdString() + " ";
}
}
return "";
}
string PolicyCompiler_iosacl::PrintRule::_printProtocol(Service *srv)
{
PolicyCompiler_iosacl *iosacl_comp = dynamic_cast<PolicyCompiler_iosacl*>(
compiler);
string addr_family_prefix = "ip ";
if (iosacl_comp->ipv6) addr_family_prefix = "ipv6 ";
string proto = srv->getProtocolName();
if (ICMP6Service::isA(srv)) proto = "icmp";
if (CustomService::isA(srv))
{
// special case standard CusctomService objects "ESTABLISHED"
// and "ESTABLISHED ipv6": these require protocol "tcp" but
// protocol is set in the Custom Service object for all
// platforms at once, so we can't have protocol defined only
// for iosacl to be used here.
string srv_code = CustomService::cast(srv)->getCodeForPlatform(
compiler->myPlatformName());
if (srv_code == "established") proto = "tcp";
}
if (proto=="ip") return addr_family_prefix;
return proto + " ";
}
string PolicyCompiler_iosacl::PrintRule::_printAddr(Address *o)
{
PolicyCompiler_iosacl *iosacl_comp = dynamic_cast<PolicyCompiler_iosacl*>(compiler);
if (Interface::cast(o)!=NULL)
{
Interface *interface_ = Interface::cast(o);
if (interface_->isDyn())
{
return string("interface ") + interface_->getLabel() + " ";
}
}
ostringstream str;
const InetAddr *srcaddr = o->getAddressPtr();
if (srcaddr)
{
const InetAddr srcmask = *(o->getNetmaskPtr());
if (srcaddr->isAny() && srcmask.isAny())
{
str << "any ";
} else
{
if (Interface::cast(o)==NULL &&
Interface::cast(o->getParent())==NULL &&
o->dimension() > 1 &&
!srcmask.isHostMask())
{
if (iosacl_comp->ipv6)
{
str << srcaddr->toString()
<< "/"
<< srcmask.getLength() << " ";
} else
{
str << srcaddr->toString() << " ";
// cisco uses "wildcards" instead of netmasks
//long nm = srcmask.to32BitInt();
//struct in_addr na;
//na.s_addr = ~nm;
InetAddr nnm( ~srcmask );
str << nnm.toString() << " ";
}
} else
{
str << "host " << srcaddr->toString() << " ";
}
}
return str.str();
}
ostringstream errstr;
errstr << "Object "
<< o->getName()
<< " (id="
<< o->getId()
<< ") "
<< " has no ip address and can not be used "
<< "in the rule.";
compiler->abort(errstr.str());
return ""; // to make compiler happy
}
/*
* the following additional attributes should have been defined by now:
*
* "acl" - string, name of the access list
* choices are: outside-in, outside-out, inside-in, indside-out,
* dmz-in, dmz-out etc.
* General rule for the acl name: "iface_name-{in,out}"
*/
bool PolicyCompiler_iosacl::PrintRule::processNext()
{
PolicyRule *rule=getNext(); if (rule==NULL) return false;
tmp_queue.push_back(rule);
compiler->output << _printRule(rule);
return true;
}