1
0
mirror of https://github.com/fwbuilder/fwbuilder synced 2026-03-23 19:57:21 +01:00
fwbuilder/src/ipt/PolicyCompiler_PrintRule.cpp

1483 lines
44 KiB
C++

/*
Firewall Builder
Copyright (C) 2002 NetCitadel, LLC
Author: Vadim Kurland vadim@vk.crocodile.org
$Id: PolicyCompiler_PrintRule.cpp 1456 2007-12-13 16:38:34Z vk $
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_ipt.h"
#include "OSConfigurator_linux24.h"
#include "fwbuilder/RuleElement.h"
#include "fwbuilder/IPService.h"
#include "fwbuilder/ICMPService.h"
#include "fwbuilder/TCPService.h"
#include "fwbuilder/UDPService.h"
#include "fwbuilder/CustomService.h"
#include "fwbuilder/TagService.h"
#include "fwbuilder/Policy.h"
#include "fwbuilder/Network.h"
#include "fwbuilder/DNSName.h"
#include "fwbuilder/FWObjectDatabase.h"
#include "fwbuilder/RuleElement.h"
#include "fwbuilder/Policy.h"
#include "fwbuilder/Interface.h"
#include "fwbuilder/IPv4.h"
#include "fwbuilder/Firewall.h"
#include "fwbuilder/Resources.h"
#include "fwbuilder/AddressTable.h"
#include "fwbuilder/Inet6AddrMask.h"
#include "combinedAddress.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 <assert.h>
using namespace libfwbuilder;
using namespace fwcompiler;
using namespace std;
/**
*-----------------------------------------------------------------------
* Methods for printing
*/
/*
* check and create new chain if needed
*/
string PolicyCompiler_ipt::PrintRule::_createChain(const string &chain,
bool ipv6)
{
string res;
PolicyCompiler_ipt *ipt_comp=dynamic_cast<PolicyCompiler_ipt*>(compiler);
if ( ! chains[chain] )
{
res = string((ipv6) ? "$IP6TABLES -N " : "$IPTABLES -N ") + chain;
if (ipt_comp->my_table != "filter") res += " -t " + ipt_comp->my_table;
res += "\n";
chains[chain]=true;
}
return res;
}
string PolicyCompiler_ipt::PrintRule::_startRuleLine(bool ipv6)
{
string res = (ipv6) ? "$IP6TABLES " : "$IPTABLES ";
PolicyCompiler_ipt *ipt_comp=dynamic_cast<PolicyCompiler_ipt*>(compiler);
if (ipt_comp->my_table != "filter") res += "-t " + ipt_comp->my_table + " ";
res += "-A ";
return res;
}
string PolicyCompiler_ipt::PrintRule::_endRuleLine()
{
return string("\n");
}
string PolicyCompiler_ipt::PrintRule::_printRuleLabel(PolicyRule *rule)
{
ostringstream res;
bool nocomm = Resources::os_res[compiler->fw->getStr("host_OS")]->
Resources::getResourceBool(
"/FWBuilderResources/Target/options/suppress_comments");
string rl=rule->getLabel();
if (rl!=current_rule_label)
{
if (!nocomm)
{
res << "# " << endl;
res << "# Rule " << rl << endl;
res << "# " << endl;
}
res << "echo " << _quote(string("Rule ")+rl) << endl;
res << "# " << endl;
/* do not put comment in the script if it is intended for linksys */
if (!nocomm)
{
string comm=rule->getComment();
string::size_type c1,c2;
c1=0;
while ( (c2=comm.find('\n',c1))!=string::npos ) {
res << "# " << comm.substr(c1,c2-c1) << endl;
c1=c2+1;
}
res << "# " << comm.substr(c1) << endl;
res << "# " << endl;
}
current_rule_label=rl;
}
return res.str();
}
/**
*-----------------------------------------------------------------------
*/
string PolicyCompiler_ipt::PrintRule::_printChain(PolicyRule *rule)
{
string s=rule->getStr("ipt_chain");
if (s.empty()) s="UNKNOWN";
s= s + " ";
return s;
}
string PolicyCompiler_ipt::PrintRule::_printModules(PolicyRule *rule)
{
std::ostringstream ostr;
string target=rule->getStr("ipt_target");
if (target.empty()) target="UNKNOWN";
FWOptions *ruleopt =rule->getOptionsObject();
int lim = 0;
/*
* Here is what do we do with limits:
*
* Limit set globally in 'Firewall' tab of the firewall dialog
* applies only to logging
*
* Limit set in the rule options dialog applies only to this
* rule's target.
*
* this is so as of 1.0.11 ( 28/06/03 ) --vk
*/
if (target=="LOG")
{
FWOptions *compopt=compiler->getCachedFwOpt();
if ((lim=compopt->getInt("limit_value"))>0)
{
ostr << " -m limit --limit " << lim;
string ls=compopt->getStr("limit_suffix");
if (!ls.empty()) ostr << ls;
int lb=compopt->getInt("limit_burst");
if (lb>0) ostr << " --limit-burst " << lb;
}
} else {
if (ruleopt!=NULL && (lim=ruleopt->getInt("limit_value"))>0)
{
ostr << " -m limit --limit " << lim;
string ls=ruleopt->getStr("limit_suffix");
if (!ls.empty()) ostr << ls;
int lb=ruleopt->getInt("limit_burst");
if (lb>0) ostr << " --limit-burst " << lb;
}
}
if (ruleopt!=NULL && (lim=ruleopt->getInt("connlimit_value"))>0)
{
ostr << " -m connlimit --connlimit-above " << lim;
int ml=ruleopt->getInt("connlimit_masklen");
if (ml>0) ostr << " --connlimit-mask " << ml;
}
if (ruleopt!=NULL && (lim=ruleopt->getInt("hashlimit_value"))>0)
{
string module_name = "hashlimit";
if (ruleopt->getBool("hashlimit_dstlimit"))
module_name = "dstlimit";
ostr << " -m " << module_name << " --" << module_name << " " << lim;
string ls = ruleopt->getStr("hashlimit_suffix");
if (!ls.empty()) ostr << ls;
int lb=ruleopt->getInt("hashlimit_burst");
if (lb>0) ostr << " --" << module_name << "-burst " << lb;
ls=ruleopt->getStr("hashlimit_mode");
if (!ls.empty()) ostr << " --" << module_name << "-mode " << ls;
string hl_name = ruleopt->getStr("hashlimit_name");
if (hl_name.empty())
{
std::ostringstream hn;
hn << "htable_rule_" << rule->getPosition();
hl_name = hn.str();
}
ostr << " --" << module_name << "-name " << hl_name;
int arg = ruleopt->getInt("hashlimit_size");
if (arg>0) ostr << " --" << module_name << "-htable-size " << arg;
arg = ruleopt->getInt("hashlimit_max");
if (arg>0) ostr << " --" << module_name << "-htable-max " << arg;
arg = ruleopt->getInt("hashlimit_expire");
if (arg>0) ostr << " --" << module_name << "-htable-expire " << arg;
arg = ruleopt->getInt("hashlimit_gcinterval");
if (arg>0) ostr << " --" << module_name << "-htable-gcinterval " << arg;
}
return ostr.str();
}
string PolicyCompiler_ipt::PrintRule::_printTarget(PolicyRule *rule)
{
std::ostringstream ostr;
string target=rule->getStr("ipt_target");
if (target.empty()) target="UNKNOWN";
FWOptions *ruleopt =rule->getOptionsObject();
if (target=="CUSTOM")
{
ostr << ruleopt->getStr("custom_str");
return ostr.str();
}
if ( compiler->getCachedFwOpt()->getBool("use_ULOG") &&
target=="LOG") target="ULOG";
ostr << " -j " << target << " ";
if (target=="REJECT")
ostr << _printActionOnReject(rule);
if (target=="LOG" || target=="ULOG")
ostr << _printLogParameters(rule);
if (target=="MARK")
{
ostr << " --set-mark " << ruleopt->getStr("tagvalue");
}
if (target=="CONNMARK")
{
ostr << ruleopt->getStr("CONNMARK_arg");
}
if (target=="CLASSIFY")
{
ostr << " --set-class " << ruleopt->getStr("classify_str");
}
if (target=="ROUTE")
{
string a;
a = ruleopt->getStr("ipt_iif");
if (!a.empty()) ostr << " --iif " << a;
a = ruleopt->getStr("ipt_oif");
if (!a.empty()) ostr << " --oif " << a;
a = ruleopt->getStr("ipt_gw");
if (!a.empty()) ostr << " --gw " << a;
bool c = ruleopt->getBool("ipt_continue");
if (c) ostr << " --continue";
c = ruleopt->getBool("ipt_tee");
if (c) ostr << " --tee";
}
return ostr.str();
}
string PolicyCompiler_ipt::PrintRule::_printMultiport(PolicyRule *rule)
{
RuleElementSrv *srvrel=rule->getSrv();
string s;
if(srvrel->size()>1 && rule->getBool("ipt_multiport"))
s= " -m multiport ";
return s;
}
string PolicyCompiler_ipt::PrintRule::_printDirectionAndInterface(PolicyRule *rule)
{
std::ostringstream ostr;
string iface_name = rule->getInterfaceStr();
if (iface_name.empty() || iface_name=="nil" ) return "";
/* if interface name ends with '*', this is a wildcard
* interface. Iptables supports wildcard interfaces but uses '+' as a
* wildcard symbol */
string::size_type n;
if ( (n=iface_name.find("*"))!=string::npos) iface_name[n]='+';
string version=compiler->fw->getStr("version");
Interface *rule_iface =
compiler->getCachedFwInterface(rule->getInterfaceId());
if (rule_iface && rule_iface->isBridgePort() && version == "1.3.0")
{
if (rule->getDirection()==PolicyRule::Inbound)
ostr << " -m physdev --physdev-in " << iface_name;
if (rule->getDirection()==PolicyRule::Outbound)
ostr << " -m physdev --physdev-out " << iface_name;
} else
{
if (rule->getDirection()==PolicyRule::Inbound)
ostr << " -i " << iface_name;
if (rule->getDirection()==PolicyRule::Outbound)
ostr << " -o " << iface_name;
}
// if (rule->getDirection()==PolicyRule::Both)
// compiler->output << "-i " << rule_iface->getName()
// << " -o " << rule_iface->getName();
ostr << " ";
return ostr.str();
}
string PolicyCompiler_ipt::PrintRule::_printActionOnReject(libfwbuilder::PolicyRule *rule)
{
std::ostringstream str;
PolicyCompiler_ipt *ipt_comp=dynamic_cast<PolicyCompiler_ipt*>(compiler);
// RuleElementSrv *srvrel=rule->getSrv();
Service *srv =compiler->getFirstSrv(rule); assert(srv);
string version=compiler->fw->getStr("version");
string s=ipt_comp->getActionOnReject(rule);
if (!s.empty())
{
if (ipt_comp->isActionOnRejectTCPRST(rule)) str << " --reject-with tcp-reset";
if (s.find("ICMP")!=string::npos)
{
if (s.find("unreachable")!=string::npos)
{
if (s.find("net")!=string::npos) str << " --reject-with icmp-net-unreachable";
if (s.find("host")!=string::npos) str << " --reject-with icmp-host-unreachable";
if (s.find("port")!=string::npos) str << " --reject-with icmp-port-unreachable";
if (s.find("proto")!=string::npos) str << " --reject-with icmp-proto-unreachable";
}
if (s.find("prohibited")!=string::npos)
{
if (s.find("net")!=string::npos) str << " --reject-with icmp-net-prohibited";
if (s.find("host")!=string::npos) str << " --reject-with icmp-host-prohibited";
if ((version=="1.2.9" || version=="1.3.0") &&
s.find("admin")!=string::npos) str << " --reject-with icmp-admin-prohibited";
}
}
}
str << " ";
return str.str();
}
string PolicyCompiler_ipt::PrintRule::_printGlobalLogParameters()
{
return _printLogParameters(NULL);
}
string PolicyCompiler_ipt::PrintRule::_printLogPrefix(const string &rule_num,
const string &action,
const string &interf,
const string &chain,
const string &rule_label,
const string &prefix)
{
string s=prefix;
/* deal with our logging macros:
* %N - rule number ('2', or '2/3' for rule in a branch)
* %A - action
* %I - interface name
* %C - chain name
*/
string::size_type n;
if ((n=s.find("%N"))!=string::npos )
{
s.replace(n,2,rule_num);
}
if ((n=s.find("%A"))!=string::npos )
{
s.replace(n,2,action);
}
if ((n=s.find("%I"))!=string::npos )
{
s.replace(n,2,interf);
}
if ((n=s.find("%C"))!=string::npos )
{
s.replace(n,2,chain);
}
if (s.length()>29)
{
compiler->warning(_("Log prefix has been truncated to 29 characters in rule ")+rule_label);
s=s.substr(0,29);
}
return _quote( s );
}
string PolicyCompiler_ipt::PrintRule::_printLogPrefix(PolicyRule *rule,
const string &prefix)
{
char action[64];
strncpy(action,rule->getStr("stored_action").c_str(),sizeof(action));
for (char *cptr=action; *cptr; cptr++) *cptr=toupper(*cptr);
string rule_iface = rule->getInterfaceStr();
if (rule_iface=="") rule_iface = "global";
std::ostringstream s1;
int pos=rule->getPosition();
// parent_rule_num is set by processor "Branching" for branch rules
string ppos = rule->getStr("parent_rule_num");
if (ppos != "")
s1 << ppos << "/";
s1 << pos;
return _printLogPrefix(s1.str(),
action,
rule_iface,
rule->getStr("ipt_chain"),
rule->getLabel(),
prefix);
}
string PolicyCompiler_ipt::PrintRule::_printLogParameters(libfwbuilder::PolicyRule *rule)
{
std::ostringstream str;
string s;
// int l;
FWOptions *ruleopt =(rule!=NULL)?rule->getOptionsObject():compiler->getCachedFwOpt();
bool use_ulog=compiler->getCachedFwOpt()->getBool("use_ULOG");
if (use_ulog)
{
s=ruleopt->getStr("ulog_nlgroup");
if (s.empty()) s=compiler->getCachedFwOpt()->getStr("ulog_nlgroup");
if (!s.empty())
str << " --ulog-nlgroup " << s;
s=ruleopt->getStr("log_prefix");
if (s.empty()) s=compiler->getCachedFwOpt()->getStr("log_prefix");
if (!s.empty())
str << " --ulog-prefix " << _printLogPrefix(rule,s);
int r=compiler->getCachedFwOpt()->getInt("ulog_cprange");
if (r!=0) str << " --ulog-cprange " << r << " ";
r=compiler->getCachedFwOpt()->getInt("ulog_qthreshold");
if (r!=0) str << " --ulog-qthreshold " << r << " ";
} else
{
bool numeric_levels;
numeric_levels=compiler->getCachedFwOpt()->getBool("use_numeric_log_levels");
s=ruleopt->getStr("log_level");
if (s.empty()) s=compiler->getCachedFwOpt()->getStr("log_level");
if (!s.empty())
{
if ( numeric_levels )
{
if (s=="alert") s="1";
if (s=="crit") s="2";
if (s=="error") s="3";
if (s=="warning") s="4";
if (s=="notice") s="5";
if (s=="info") s="6";
if (s=="debug") s="7";
}
str << " --log-level " << s;
}
s=ruleopt->getStr("log_prefix");
if (s.empty()) s=compiler->getCachedFwOpt()->getStr("log_prefix");
if (!s.empty())
str << " --log-prefix " << _printLogPrefix(rule,s);
if (ruleopt->getBool("log_tcp_seq") || compiler->getCachedFwOpt()->getBool("log_tcp_seq"))
str << " --log-tcp-sequence ";
if (ruleopt->getBool("log_tcp_opt") || compiler->getCachedFwOpt()->getBool("log_tcp_opt"))
str << " --log-tcp-options ";
if (ruleopt->getBool("log_ip_opt") || compiler->getCachedFwOpt()->getBool("log_ip_opt"))
str << " --log-ip-options ";
}
return str.str();
}
string PolicyCompiler_ipt::PrintRule::_printLimit(libfwbuilder::PolicyRule *rule)
{
std::ostringstream str;
string s;
int l, lb;
FWOptions *ruleopt =rule->getOptionsObject();
FWOptions *compopt =compiler->getCachedFwOpt();
if ( (ruleopt!=NULL && (l=ruleopt->getInt("limit_value"))>0) ||
(l=compopt->getInt("limit_value"))>0 )
{
str << " -m limit --limit " << l;
if (ruleopt!=NULL) s=ruleopt->getStr("limit_suffix");
if (s.empty()) s=compopt->getStr("limit_suffix");
if (!s.empty()) str << s;
lb=-1;
if (ruleopt!=NULL) lb=ruleopt->getInt("limit_burst");
if (lb<0) lb=compopt->getInt("limit_burst");
if (lb>0) str << " --limit-burst " << lb;
}
return str.str();
}
string PolicyCompiler_ipt::PrintRule::_printProtocol(libfwbuilder::Service *srv)
{
string version=compiler->fw->getStr("version");
string s;
if (! srv->isAny() && !CustomService::isA(srv) && !TagService::isA(srv))
{
string pn=srv->getProtocolName();
if (pn=="ip") pn="all";
s= "-p " + pn + " ";
if (pn == "icmp")
{
if (version.empty() || version=="1.2.9" || version=="1.3.0")
{
s += " -m icmp ";
}
} else
{
if (pn == "tcp") s += "-m tcp ";
if (pn == "udp") s += "-m udp ";
}
}
return s;
}
string PolicyCompiler_ipt::PrintRule::_printPorts(int rs,int re)
{
std::ostringstream str;
compiler->normalizePortRange(rs,re);
if (rs>0 || re>0) {
if (rs==re) str << rs;
else
if (rs==0 && re!=0) str << ":" << re;
else
str << rs << ":" << re;
}
return str.str();
}
string PolicyCompiler_ipt::PrintRule::_printSrcPorts(Service *srv)
{
std::ostringstream str;
if (TCPService::isA(srv) || UDPService::isA(srv))
{
int rs=srv->getInt("src_range_start");
int re=srv->getInt("src_range_end");
str << _printPorts(rs,re);
}
return str.str();
}
string PolicyCompiler_ipt::PrintRule::_printDstPorts(Service *srv)
{
std::ostringstream str;
if (TCPService::isA(srv) || UDPService::isA(srv))
{
int rs=srv->getInt("dst_range_start");
int re=srv->getInt("dst_range_end");
str << _printPorts(rs,re);
}
return str.str();
}
string PolicyCompiler_ipt::PrintRule::_printICMP(ICMPService *srv)
{
std::ostringstream str;
if (ICMPService::isA(srv) && srv->getInt("type")!=-1) {
str << srv->getStr("type");
if (srv->getInt("code")!=-1)
str << "/" << srv->getStr("code") << " ";
}
return str.str();
}
string PolicyCompiler_ipt::PrintRule::_printIP(IPService *srv)
{
std::ostringstream str;
if (IPService::isA(srv) ) {
if (srv->getBool("fragm") || srv->getBool("short_fragm"))
str << " -f ";
if (srv->getBool("lsrr") ||
srv->getBool("ssrr") ||
srv->getBool("rr") ||
srv->getBool("ts") ) str << " -m ipv4options ";
if (srv->getBool("lsrr")) str << " --lsrr";
if (srv->getBool("ssrr")) str << " --ssrr";
if (srv->getBool("rr")) str << " --rr";
if (srv->getBool("ts")) str << " --ts";
}
return str.str();
}
string PolicyCompiler_ipt::PrintRule::_printTCPFlags(libfwbuilder::TCPService *srv)
{
string str;
if (srv->inspectFlags())
{
TCPService::TCPFlag f1[2]={ TCPService::SYN };
TCPService::TCPFlag f2[7]={ TCPService::URG,
TCPService::ACK,
TCPService::PSH,
TCPService::RST,
TCPService::SYN,
TCPService::FIN };
std::set<TCPService::TCPFlag> none;
std::set<TCPService::TCPFlag> syn( f1, f1+1 );
std::set<TCPService::TCPFlag> all_masks( f2 , f2+6 );
if (srv->getAllTCPFlags()==syn && srv->getAllTCPFlagMasks()==all_masks)
str=" --tcp-flags SYN,RST,ACK SYN ";
else
{
str=" --tcp-flags ";
bool first=true;
if (srv->getAllTCPFlagMasks()==all_masks) str+="ALL";
else
{
if (srv->getTCPFlagMask(TCPService::URG)) { if (!first) str+=","; str+="URG"; first=false; }
if (srv->getTCPFlagMask(TCPService::ACK)) { if (!first) str+=","; str+="ACK"; first=false; }
if (srv->getTCPFlagMask(TCPService::PSH)) { if (!first) str+=","; str+="PSH"; first=false; }
if (srv->getTCPFlagMask(TCPService::RST)) { if (!first) str+=","; str+="RST"; first=false; }
if (srv->getTCPFlagMask(TCPService::SYN)) { if (!first) str+=","; str+="SYN"; first=false; }
if (srv->getTCPFlagMask(TCPService::FIN)) { if (!first) str+=","; str+="FIN"; first=false; }
}
str+=" ";
if (srv->getAllTCPFlags()==none) str+="NONE";
else
{
first=true;
if (srv->getTCPFlag(TCPService::URG)) { if (!first) str+=","; str+="URG"; first=false; }
if (srv->getTCPFlag(TCPService::ACK)) { if (!first) str+=","; str+="ACK"; first=false; }
if (srv->getTCPFlag(TCPService::PSH)) { if (!first) str+=","; str+="PSH"; first=false; }
if (srv->getTCPFlag(TCPService::RST)) { if (!first) str+=","; str+="RST"; first=false; }
if (srv->getTCPFlag(TCPService::SYN)) { if (!first) str+=","; str+="SYN"; first=false; }
if (srv->getTCPFlag(TCPService::FIN)) { if (!first) str+=","; str+="FIN"; first=false; }
}
}
}
return str;
}
/*
* we made sure that all services in rel represent the same protocol
*/
string PolicyCompiler_ipt::PrintRule::_printSrcService(RuleElementSrv *rel)
{
std::ostringstream ostr;
/* I do not want to use rel->getFirst because it traverses the tree to
* find the object. I'd rather use a cached copy in the compiler
*/
FWObject *o=rel->front();
if (o && FWReference::cast(o)!=NULL) o=FWReference::cast(o)->getPointer();
Service *srv= Service::cast(o);
if (rel->size()==1) {
if (UDPService::isA(srv) || TCPService::isA(srv)) {
string str=_printSrcPorts( srv );
if (! str.empty() )
{
ostr << " --sport ";
ostr << _printSingleObjectNegation(rel) << str << " ";
}
}
} else {
/* use multiport */
string str;
bool first=true;
for (FWObject::iterator i=rel->begin(); i!=rel->end(); i++) {
FWObject *o= *i;
// if (FWReference::cast(o)!=NULL) o=FWReference::cast(o)->getPointer();
if (FWReference::cast(o)!=NULL) o=FWReference::cast(o)->getPointer();
Service *s=Service::cast( o );
assert(s);
if (UDPService::isA(srv) || TCPService::isA(srv)) {
if (!first) str+=",";
str+= _printSrcPorts( s );
if (!str.empty()) first=false;
}
}
if ( !str.empty() )
{
string v=compiler->fw->getStr("version");
if (v.empty() || v=="ge_1.2.6" || v=="1.2.9" || v=="1.3.0")
ostr << " --sports ";
else
ostr << " --source-port ";
ostr << str << " ";
}
}
return ostr.str();
}
string PolicyCompiler_ipt::PrintRule::_printDstService(RuleElementSrv *rel)
{
std::ostringstream ostr;
FWObject *o=rel->front();
string version=compiler->fw->getStr("version");
if (o && FWReference::cast(o)!=NULL) o=FWReference::cast(o)->getPointer();
Service *srv= Service::cast(o);
if (rel->size()==1)
{
if (UDPService::isA(srv) || TCPService::isA(srv))
{
string str=_printDstPorts( srv );
if (! str.empty() )
{
ostr << " --dport ";
ostr << _printSingleObjectNegation(rel) << str << " ";
}
}
if (TCPService::isA(srv))
{
string str=_printTCPFlags(TCPService::cast(srv));
if (!str.empty())
{
ostr << _printSingleObjectNegation(rel)
<< str << " ";
}
}
if (ICMPService::isA(srv))
{
string str=_printICMP( ICMPService::cast(srv) );
if (str.empty() )
{
if (version.empty() || version=="1.2.9" || version=="1.3.0")
ostr << " --icmp-type any ";
} else
{
ostr << " --icmp-type "
<< _printSingleObjectNegation(rel)
<< str << " ";
}
}
if (IPService::isA(srv))
{
string str=_printIP( IPService::cast(srv) );
if (! str.empty() )
{
ostr << _printSingleObjectNegation(rel)
<< str << " ";
}
}
if (CustomService::isA(srv))
{
ostr << _printSingleObjectNegation(rel) << " "
<< CustomService::cast(srv)->getCodeForPlatform( compiler->myPlatformName() ) << " ";
}
if (TagService::isA(srv))
{
ostr << "-m mark --mark "
<< TagService::cast(srv)->getCode() << " ";
}
} else
{
/* use multiport */
string str;
for (FWObject::iterator i=rel->begin(); i!=rel->end(); i++)
{
FWObject *o= *i;
if (FWReference::cast(o)!=NULL) o=FWReference::cast(o)->getPointer();
Service *s=Service::cast( o );
assert(s);
if (UDPService::isA(srv) || TCPService::isA(srv))
{
string str1 = _printDstPorts( s );
if (str!="" && str1!="") str+=",";
str+=str1;
}
}
if ( !str.empty() )
{
string v=compiler->fw->getStr("version");
if (v.empty() || v=="ge_1.2.6" || v=="1.2.9" || v=="1.3.0")
ostr << " --dports ";
else
ostr << " --destination-port ";
ostr << str << " ";
}
}
return ostr.str();
}
string PolicyCompiler_ipt::PrintRule::_printAddr(Address *o)
{
PolicyCompiler_ipt *ipt_comp=dynamic_cast<PolicyCompiler_ipt*>(compiler);
std::ostringstream ostr;
MultiAddressRunTime *atrt = MultiAddressRunTime::cast(o);
if (atrt!=NULL)
{
if (atrt->getSubstitutionTypeName()==AddressTable::TYPENAME)
{
ostr << "$" << ipt_comp->getAddressTableVarName(atrt) << " ";
return ostr.str();
}
if (atrt->getSubstitutionTypeName()==DNSName::TYPENAME)
{
return atrt->getSourceName();
}
// at this time we only support two types of MultiAddress
// objects: AddressTable and DNSName. Both should be converted
// to MultiAddressRunTime at this point. If we get some other
// kind of MultiAddressRunTime object, we do not know what to do
// with it so we stop.
assert(atrt==NULL);
}
if (Interface::cast(o)!=NULL)
{
Interface *iface=Interface::cast(o);
if (iface->isDyn())
ostr << "$" << ipt_comp->getInterfaceVarName(iface) << " ";
return ostr.str();
}
const InetAddr *addr = o->getAddressPtr();
const InetAddr *mask = o->getNetmaskPtr();
if (addr->isAny() && mask->isAny())
{
ostr << "0/0 ";
} else
{
ostr << addr->toString();
if (Interface::cast(o)==NULL &&
Address::cast(o)->dimension() > 1 &&
!mask->isHostMask())
{
ostr << "/" << mask->getLength();
}
ostr << " ";
}
return ostr.str();
}
string PolicyCompiler_ipt::PrintRule::_printSingleObjectNegation(RuleElement *rel)
{
if (rel->getBool("single_object_negation")) return "! ";
else return "";
}
string PolicyCompiler_ipt::PrintRule::_printTimeInterval(PolicyRule *r)
{
std::ostringstream ostr;
RuleElementInterval* ri=r->getWhen();
if (ri==NULL || ri->isAny()) return "";
std::map<int,std::string> daysofweek;
daysofweek[0]="Sun";
daysofweek[1]="Mon";
daysofweek[2]="Tue";
daysofweek[3]="Wed";
daysofweek[4]="Thu";
daysofweek[5]="Fri";
daysofweek[6]="Sat";
bool first;
int smin, shour, sday, smonth, syear, sdayofweek;
int emin, ehour, eday, emonth, eyear, edayofweek;
Interval *interval=compiler->getFirstWhen(r);
assert(interval!=NULL);
interval->getStartTime( &smin, &shour, &sday, &smonth, &syear, &sdayofweek);
interval->getEndTime( &emin, &ehour, &eday, &emonth, &eyear, &edayofweek);
ostr << "-m time ";
if (shour<0) shour=0;
if (smin<0) smin=0;
if (ehour<0) ehour=23;
if (emin<0) emin=59;
bool use_timestart_timestop = true;
if (sday>0 && smonth>0 && syear>0)
{
ostr << "--datestart "
<< setw(2) << setfill('0') << syear << ":"
<< setw(2) << setfill('0') << smonth << ":"
<< setw(2) << setfill('0') << sday << ":"
<< setw(2) << setfill('0') << shour << ":"
<< setw(2) << setfill('0') << smin << ":00 ";
use_timestart_timestop = false;
}
if (eday>0 && emonth>0 && eyear>0)
{
ostr << "--datestop "
<< setw(2) << setfill('0') << syear << ":"
<< setw(2) << setfill('0') << smonth << ":"
<< setw(2) << setfill('0') << sday << ":"
<< setw(2) << setfill('0') << ehour << ":"
<< setw(2) << setfill('0') << emin << ":00 ";
use_timestart_timestop = false;
}
if (use_timestart_timestop )
{
ostr << " --timestart "
<< setw(2) << setfill('0') << shour << ":"
<< setw(2) << setfill('0') << smin << " ";
ostr << " --timestop "
<< setw(2) << setfill('0') << ehour << ":"
<< setw(2) << setfill('0') << emin << " ";
if (sdayofweek<0) sdayofweek=0;
if (sdayofweek>6) sdayofweek=6;
// if both start and end day are -1, need to
// generate "sun,mon,tue,wed,thu,fri,sat"
if (edayofweek<0) edayofweek=6;
if (edayofweek>6) edayofweek=6;
ostr << " --days ";
first=true;
bool inside_interval = false;
int day=0;
while (1)
{
if (!inside_interval && day==sdayofweek) inside_interval=true;
if (inside_interval)
{
if (!first) ostr << ",";
first=false;
ostr << daysofweek[day];
// if sdayofweek==edayofweek print one day
if (day==edayofweek) break;
}
if (++day>6) day=0;
}
}
return ostr.str();
}
PolicyCompiler_ipt::PrintRule::PrintRule(const std::string &name) : PolicyRuleProcessor(name)
{
init=true;
print_once_on_top=true;
chains["INPUT"] =true;
chains["OUTPUT"] =true;
chains["FORWARD"] =true;
chains["PREROUTING"] =true;
chains["POSTROUTING"] =true;
chains["RETURN"] =true;
chains["LOG"] =true;
chains["ACCEPT"] =true;
chains["DROP"] =true;
chains["REJECT"] =true;
chains["MARK"] =true;
chains["CONNMARK"] =true;
chains["QUEUE"] =true;
chains["CLASSIFY"] =true;
chains["CUSTOM"] =true;
chains["ROUTE"] =true;
}
bool PolicyCompiler_ipt::PrintRule::processNext()
{
PolicyRule *rule =getNext();
if (rule==NULL) return false;
tmp_queue.push_back(rule);
compiler->output << _printRuleLabel(rule);
compiler->output << _createChain(rule->getStr("ipt_chain"));
compiler->output << _createChain(rule->getStr("ipt_target"));
compiler->output
<< dynamic_cast<OSConfigurator_linux24*>(compiler->osconfigurator)->printRunTimeWrappers( rule, PolicyRuleToString(rule) );
return true;
}
string PolicyCompiler_ipt::PrintRule::PolicyRuleToString(PolicyRule *rule)
{
FWOptions *ruleopt = rule->getOptionsObject();
FWObject *ref;
bool isIPv6 = rule->getBool("ipv6_rule");
RuleElementSrc *srcrel=rule->getSrc();
ref=srcrel->front();
Address *src=Address::cast(FWReference::cast(ref)->getPointer());
if(src==NULL)
throw FWException(_("Broken SRC in ")+rule->getLabel());
RuleElementDst *dstrel=rule->getDst();
ref=dstrel->front();
Address *dst=Address::cast(FWReference::cast(ref)->getPointer());
if(dst==NULL)
throw FWException(_("Broken DST in ")+rule->getLabel());
RuleElementSrv *srvrel=rule->getSrv();
ref=srvrel->front();
Service *srv=Service::cast(FWReference::cast(ref)->getPointer());
if(srv==NULL)
throw FWException(_("Broken SRV in ")+rule->getLabel());
std::ostringstream command_line;
command_line << _startRuleLine(isIPv6);
command_line << _printChain(rule);
command_line << _printDirectionAndInterface(rule);
command_line << _printProtocol(srv);
command_line << _printMultiport(rule);
if (!src->isAny())
{
if (physAddress::isA(src) || combinedAddress::isA(src))
{
string physaddress = "";
if (physAddress::isA(src))
{
physaddress = physAddress::cast(src)->getPhysAddress();
if (physaddress.empty())
{
compiler->warning("Empty MAC address in rule " +
rule->getLabel());
physaddress = "00:00:00:00:00:00";
}
}
if (combinedAddress::isA(src))
physaddress = combinedAddress::cast(src)->getPhysAddress();
/* physAddress component of combinedAddress can be empty. For example
* this happens when an object with both IP and MAC addresses is found
* in "source" and rule is determined to go into OUTPUT chain. On the
* other hand, if physAddress object has no MAC address, it is always
* an error.
*/
if (!physaddress.empty())
{
command_line << " -m mac --mac-source "
<< _printSingleObjectNegation(srcrel);
command_line << physaddress;
}
/*
* fool-proof: this is last resort check for situation when user
* created IPv4 object for the interface but left it with empty
* address ( 0.0.0.0 ).
*
* note that combinedAddress inherits IPv4 and therefore
* combinedAddress::hasInetAddress returns true;
*
*/
if (src->hasInetAddress() && !src->getAddressPtr()->isAny())
{
command_line << " -s " << _printSingleObjectNegation(srcrel);
command_line << _printAddr(src);
}
} else
{
command_line << " -s " << _printSingleObjectNegation(srcrel);
command_line << _printAddr(src);
}
}
command_line << _printSrcService(srvrel);
if (!dst->isAny())
{
command_line << " -d " << _printSingleObjectNegation(dstrel);
command_line << _printAddr(dst);
}
command_line << _printDstService(srvrel);
/* keeping state does not apply to deny/reject
however some rules need state check even if action is Deny
autoupgrade transformation 2.1.11 -> 2.1.12 adds rule option
'stateless=True' for rules with action NOT 'Accept', 'Tag' or
'Route'. No need to check action here, just rely on this option
and internal flag 'force_state_check' (05/07/07 --vk)
*/
if (!ruleopt->getBool("stateless") || rule->getBool("force_state_check") )
{
/*
* But not, when the line already contains a state matching
*/
if (command_line.str().find("-m state --state", 0) == string::npos)
command_line << " -m state --state NEW ";
}
command_line << _printTimeInterval(rule);
command_line << _printModules(rule);
command_line << _printTarget(rule);
command_line << _endRuleLine();
// command_line << endl;
return command_line.str();
}
string PolicyCompiler_ipt::PrintRule::_declareTable()
{
return "";
}
string PolicyCompiler_ipt::PrintRule::_flushAndSetDefaultPolicy()
{
// PolicyCompiler_ipt *ipt_comp = dynamic_cast<PolicyCompiler_ipt*>(compiler);
FWOptions *fwopt = compiler->getCachedFwOpt();
ostringstream res;
// if (ipt_comp->my_table=="filter")
// {
res << "$IPTABLES -P OUTPUT DROP" << endl;
res << "$IPTABLES -P INPUT DROP" << endl;
res << "$IPTABLES -P FORWARD DROP" << endl;
/*
* need to flush all tables and chains before setting up any rules
*/
res << "\n\
cat /proc/net/ip_tables_names | while read table; do\n\
$IPTABLES -t $table -L -n | while read c chain rest; do\n\
if test \"X$c\" = \"XChain\" ; then\n\
$IPTABLES -t $table -F $chain\n\
fi\n\
done\n\
$IPTABLES -t $table -X\n\
done\n";
res << endl;
res << endl;
/*
* test if ip6tables is installed and if it works. It may be installed
* on the system but fail because ipv6 is not compiled into the
* kernel.
*/
res << "$IP6TABLES -L -n > /dev/null 2>&1 && {" << endl;
res << " $IP6TABLES -P OUTPUT DROP" << endl;
res << " $IP6TABLES -P INPUT DROP" << endl;
res << " $IP6TABLES -P FORWARD DROP" << endl;
res << "\n\
cat /proc/net/ip6_tables_names | while read table; do\n\
$IP6TABLES -t $table -L -n | while read c chain rest; do\n\
if test \"X$c\" = \"XChain\" ; then\n\
$IP6TABLES -t $table -F $chain\n\
fi\n\
done\n\
$IP6TABLES -t $table -X\n\
done\n\
\n\
\n";
res << "}";
res << endl;
res << endl;
// }
return res.str();
}
string PolicyCompiler_ipt::PrintRule::_commit()
{
return "";
}
string PolicyCompiler_ipt::PrintRule::_printOptionalGlobalRules(bool isIPv6)
{
PolicyCompiler_ipt *ipt_comp = dynamic_cast<PolicyCompiler_ipt*>(compiler);
ostringstream res;
/*
* bug #1092141: "irritating FORWARD rule for established connections"
* Need rules in FORWARD chain only if ip forwarding is on or set to
* "no change"
*/
bool ipforward = false;
string s = compiler->getCachedFwOpt()->getStr("linux24_ip_forward");
ipforward= (s.empty() || s=="1" || s=="On" || s=="on");
if ( compiler->getCachedFwOpt()->getBool("clamp_mss_to_mtu") && ipforward)
{
res << _startRuleLine(isIPv6)
<< "FORWARD -p tcp -m tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu"
<< _endRuleLine();
res << endl;
}
if ( compiler->getCachedFwOpt()->getBool("accept_established") &&
ipt_comp->my_table=="filter")
{
res << _startRuleLine(isIPv6)
<< "INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT"
<< _endRuleLine();
res << _startRuleLine(isIPv6)
<< "OUTPUT -m state --state ESTABLISHED,RELATED -j ACCEPT"
<< _endRuleLine();
if (ipforward)
res << _startRuleLine(isIPv6)
<< "FORWARD -m state --state ESTABLISHED,RELATED -j ACCEPT"
<< _endRuleLine();
res << endl;
}
/*
* it helps to add backup ssh access rule as early as possible so that
* ssh session opened from the management station won't break after
* all chains are flushed. The installation process may stall if
* stdout buffer gets filled with diagnostic or progress output from
* this script printed after chains are flushed but before a rule
* permitting ssh is installed. This may happen if script debugging is
* on or there are many NAT rules (so it prints a lot of "Rule NN
* (NAT)" lines).
*/
if ( compiler->getCachedFwOpt()->getBool("mgmt_ssh") &&
! compiler->getCachedFwOpt()->getStr("mgmt_addr").empty() )
{
string addr_str = compiler->getCachedFwOpt()->getStr("mgmt_addr");
InetAddrMask *inet_addr;
bool addr_is_good = true;
if (isIPv6)
{
// check if given address is ipv6
try
{
inet_addr = new Inet6AddrMask(addr_str);
} catch(const FWException &ex) {
// address does not parse as ipv6, skip this rule.
addr_is_good = false;
}
} else
{
// check if given address parses as ipv4
try
{
inet_addr = new InetAddrMask(addr_str);
} catch(const FWException &ex) {
// address does not parse
addr_is_good = false;
}
}
if (addr_is_good)
{
res << "# backup ssh access" << endl;
res << "#" << endl;
/* bug #1106701: 'backup ssh access' and statefulness interation
* Need to add rules with ESTABLISHED and RELATED to make sure backup ssh access
* works even when global rule that accepts ESTABLISHED and RELATED is disabled
*/
res << _startRuleLine(isIPv6) << "INPUT -p tcp -m tcp -s "
<< inet_addr->toString()
<< " --dport 22 -m state --state NEW,ESTABLISHED -j ACCEPT"
<< _endRuleLine();
res << _startRuleLine(isIPv6) << "OUTPUT -p tcp -m tcp -d "
<< inet_addr->toString()
<< " --sport 22 -m state --state ESTABLISHED,RELATED -j ACCEPT"
<< _endRuleLine();
res << endl;
}
}
if ( ! compiler->getCachedFwOpt()->getBool("accept_new_tcp_with_no_syn") )
{
res << "# drop TCP sessions opened prior firewall restart"
<< endl;
res << "#" << endl;
res << _startRuleLine(isIPv6)
<< "INPUT -p tcp -m tcp ! --tcp-flags SYN,RST,ACK SYN -m state --state NEW -j DROP"
<< _endRuleLine();
res << _startRuleLine(isIPv6)
<< "OUTPUT -p tcp -m tcp ! --tcp-flags SYN,RST,ACK SYN -m state --state NEW -j DROP"
<< _endRuleLine();
if (ipforward)
res << _startRuleLine(isIPv6)
<< "FORWARD -p tcp -m tcp ! --tcp-flags SYN,RST,ACK SYN -m state --state NEW -j DROP"
<< _endRuleLine();
res << endl;
}
if ( compiler->getCachedFwOpt()->getBool("drop_invalid") )
{
res << "# drop packets that do not match any valid state "
<< endl;
res << "#" << endl;
if ( !compiler->getCachedFwOpt()->getBool("log_invalid"))
{
res << _startRuleLine(isIPv6)
<< "OUTPUT -m state --state INVALID -j DROP"
<< _endRuleLine();
res << _startRuleLine(isIPv6)
<< "INPUT -m state --state INVALID -j DROP"
<< _endRuleLine();
if (ipforward)
res << _startRuleLine(isIPv6)
<< "FORWARD -m state --state INVALID -j DROP"
<< _endRuleLine();
} else
{
res << _createChain("drop_invalid", isIPv6);
res << _startRuleLine(isIPv6)
<< "OUTPUT -m state --state INVALID -j drop_invalid"
<< _endRuleLine();
res << _startRuleLine(isIPv6)
<< "INPUT -m state --state INVALID -j drop_invalid"
<< _endRuleLine();
if (ipforward)
res << _startRuleLine(isIPv6)
<< "FORWARD -m state --state INVALID -j drop_invalid"
<< _endRuleLine();
res << _startRuleLine(isIPv6);
if (compiler->getCachedFwOpt()->getBool("use_ULOG"))
{
string s = compiler->getCachedFwOpt()->getStr("ulog_nlgroup");
res << "drop_invalid -j ULOG ";
if (!s.empty())
res << "--ulog-nlgroup " << s << " ";
int r;
if ((r = compiler->getCachedFwOpt()->getInt("ulog_cprange"))!=0)
res << "--ulog-cprange " << r << " ";
if ((r = compiler->getCachedFwOpt()->getInt("ulog_qthreshold"))!=0)
res << " --ulog-qthreshold " << r << " ";
res << "--ulog-prefix ";
} else {
res << "drop_invalid -j LOG "
<< "--log-level debug --log-prefix ";
}
string s = compiler->getCachedFwOpt()->getStr("log_prefix");
if (s.empty())
s = "INVALID state -- DENY ";
res << _printLogPrefix("-1", "DENY","global","drop_invalid","BLOCK INVALID",s)
<< _endRuleLine()
<< _startRuleLine(isIPv6) << "drop_invalid -j DROP" << _endRuleLine();
}
res << endl;
}
return res.str();
}
string PolicyCompiler_ipt::PrintRule::_quote(const string &s)
{
return "\"" + s + "\"";
}