mirror of
https://github.com/fwbuilder/fwbuilder
synced 2026-03-25 04:37:22 +01:00
* src/res/configlets/linux24/automatic_rules: Generation of the automatic rules (matching ESTABLISHED and INVALID states, backup ssh access and others) now uses configlet. Fixes #883 * src/res/configlets/linux24/automatic_rules: generated script can now include automatic rules to match IPv6 neighbor discovery ICMP6 packets. This is controlled by a checkbox in the iptables "advanced" settings dialog and is off by default. Fixes #878
1708 lines
53 KiB
C++
1708 lines
53 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 "PolicyCompiler_ipt.h"
|
|
#include "OSConfigurator_linux24.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/TagService.h"
|
|
#include "fwbuilder/Policy.h"
|
|
#include "fwbuilder/Network.h"
|
|
#include "fwbuilder/DNSName.h"
|
|
#include "fwbuilder/AddressRange.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/UserService.h"
|
|
|
|
#include "fwbuilder/Inet6AddrMask.h"
|
|
|
|
#include "combinedAddress.h"
|
|
|
|
#include "Configlet.h"
|
|
|
|
#include <QStringList>
|
|
|
|
#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 <cstring>
|
|
#include <algorithm>
|
|
|
|
#include <assert.h>
|
|
|
|
using namespace libfwbuilder;
|
|
using namespace fwcompiler;
|
|
using namespace std;
|
|
|
|
|
|
|
|
/**
|
|
*-----------------------------------------------------------------------
|
|
* Methods for printing
|
|
*/
|
|
|
|
/*
|
|
* Prints single --option with argument and negation "!"
|
|
* taking into account the change that happened in iptables 1.4.3.1
|
|
* that causes warning
|
|
* Using intrapositioned negation (`--option ! this`) is deprecated in favor of extrapositioned (`! --option this`).
|
|
*/
|
|
string PolicyCompiler_ipt::PrintRule::_printSingleOptionWithNegation(
|
|
const string &option, RuleElement *rel, const string &arg)
|
|
{
|
|
ostringstream ostr;
|
|
if (XMLTools::version_compare(version, "1.4.3")>=0)
|
|
{
|
|
ostr << _printSingleObjectNegation(rel);
|
|
ostr << option << " ";
|
|
ostr << arg << " ";
|
|
} else
|
|
{
|
|
ostr << option << " ";
|
|
ostr << _printSingleObjectNegation(rel);
|
|
ostr << arg << " ";
|
|
}
|
|
return ostr.str();
|
|
}
|
|
|
|
void PolicyCompiler_ipt::PrintRule::initializeMinusNTracker()
|
|
{
|
|
PolicyCompiler_ipt *ipt_comp = dynamic_cast<PolicyCompiler_ipt*>(compiler);
|
|
for (list<string>::const_iterator i =
|
|
PolicyCompiler_ipt::getStandardChains().begin();
|
|
i != PolicyCompiler_ipt::getStandardChains().end(); ++i)
|
|
{
|
|
(*(ipt_comp->minus_n_commands))[*i] = true;
|
|
}
|
|
minus_n_tracker_initialized = true;
|
|
}
|
|
|
|
|
|
/*
|
|
* check and create new chain if needed
|
|
*/
|
|
string PolicyCompiler_ipt::PrintRule::_createChain(const string &chain)
|
|
{
|
|
string res;
|
|
PolicyCompiler_ipt *ipt_comp = dynamic_cast<PolicyCompiler_ipt*>(compiler);
|
|
|
|
if (!minus_n_tracker_initialized) initializeMinusNTracker();
|
|
|
|
if ( ipt_comp->minus_n_commands->count(chain)==0 )
|
|
{
|
|
res = string((ipt_comp->ipv6) ? "$IP6TABLES -N " : "$IPTABLES -N ") +
|
|
chain;
|
|
if (ipt_comp->my_table != "filter") res += " -t " + ipt_comp->my_table;
|
|
res += "\n";
|
|
(*(ipt_comp->minus_n_commands))[chain] = true;
|
|
}
|
|
return res;
|
|
}
|
|
|
|
string PolicyCompiler_ipt::PrintRule::_startRuleLine()
|
|
{
|
|
PolicyCompiler_ipt *ipt_comp = dynamic_cast<PolicyCompiler_ipt*>(compiler);
|
|
string res = (ipt_comp->ipv6) ? "$IP6TABLES " : "$IPTABLES ";
|
|
|
|
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 (!compiler->inSingleRuleCompileMode())
|
|
{
|
|
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 || compiler->inSingleRuleCompileMode())
|
|
{
|
|
string comm = rule->getComment();
|
|
if (!comm.empty())
|
|
{
|
|
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;
|
|
|
|
string err = rule->getStr(".error_msg");
|
|
if (!err.empty()) res << "# " << err << endl;
|
|
|
|
return res.str();
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
*-----------------------------------------------------------------------
|
|
*/
|
|
string PolicyCompiler_ipt::PrintRule::_printChain(PolicyRule *rule)
|
|
{
|
|
string s = rule->getStr("ipt_chain");
|
|
if (s.empty()) s = "UNKNOWN";
|
|
// check chain name length per bug report #2507239
|
|
if (s.length() > 30)
|
|
{
|
|
ostringstream str;
|
|
str << "Chain name '" << s << "' ";
|
|
str << "is longer than 30 characters. Rule " << rule->getLabel();
|
|
compiler->abort(rule, str.str());
|
|
}
|
|
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())
|
|
{
|
|
/* syntax "--hashlimit-mode srcip,srcport " (i.e. with options
|
|
separated by commas) tested with iptables 1.3.6
|
|
*/
|
|
QStringList opts;
|
|
bool f;
|
|
|
|
f = ruleopt->getBool("hashlimit_mode_srcip");
|
|
if (f) opts.push_back("srcip");
|
|
|
|
f = ruleopt->getBool("hashlimit_mode_dstip");
|
|
if (f) opts.push_back("dstip");
|
|
|
|
f = ruleopt->getBool("hashlimit_mode_srcport");
|
|
if (f) opts.push_back("srcport");
|
|
|
|
f = ruleopt->getBool("hashlimit_mode_dstport");
|
|
if (f) opts.push_back("dstport");
|
|
|
|
if (!opts.isEmpty())
|
|
ostr << " --" << module_name << "-mode " << opts.join(",").toStdString();
|
|
} else
|
|
// hashlimit_mode is v2.1 option. In v3 we have options
|
|
// hashlimit_mode_srcip
|
|
// hashlimit_mode_dstip
|
|
// hashlimit_mode_srcport
|
|
// hashlimit_mode_dstport
|
|
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)
|
|
{
|
|
PolicyCompiler_ipt *ipt_comp = dynamic_cast<PolicyCompiler_ipt*>(compiler);
|
|
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();
|
|
}
|
|
|
|
// there is no ULOG for ip6tables yet
|
|
if (!ipt_comp->ipv6 && 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");
|
|
ostr << " --set-mark " << rule->getTagValue();
|
|
}
|
|
|
|
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 "";
|
|
|
|
RuleElementItf *itfrel = rule->getItf();
|
|
|
|
/* 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]='+';
|
|
|
|
Interface *rule_iface =
|
|
Interface::cast(compiler->dbcopy->findInIndex(rule->getInterfaceId()));
|
|
|
|
if (rule_iface && rule_iface->isBridgePort() &&
|
|
(version.empty() ||
|
|
XMLTools::version_compare(version, "1.3.0")>=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 << _printSingleOptionWithNegation(" -i", itfrel, iface_name);
|
|
|
|
if (rule->getDirection()==PolicyRule::Outbound)
|
|
ostr << _printSingleOptionWithNegation(" -o", itfrel, 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(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 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 (ipt_comp->ipv6)
|
|
{
|
|
if (s.find("unreachable")!=string::npos)
|
|
{
|
|
if (s.find("net")!=string::npos ||
|
|
s.find("host")!=string::npos)
|
|
str << " --reject-with icmp6-addr-unreachable";
|
|
if (s.find("port")!=string::npos ||
|
|
s.find("proto")!=string::npos)
|
|
str << " --reject-with icmp6-port-unreachable";
|
|
}
|
|
if (s.find("prohibited")!=string::npos)
|
|
{
|
|
str << " --reject-with icmp6-adm-prohibited";
|
|
}
|
|
} else
|
|
{
|
|
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 (XMLTools::version_compare(version, "1.2.9")>=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 &ruleset,
|
|
const string& ,
|
|
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
|
|
* %R - ruleset 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 ((n=s.find("%R"))!=string::npos )
|
|
{
|
|
s.replace(n, 2, ruleset);
|
|
}
|
|
|
|
if (s.length()>29)
|
|
{
|
|
compiler->warning(
|
|
"Log prefix has been truncated to 29 characters");
|
|
s=s.substr(0,29);
|
|
}
|
|
|
|
return _quote( s );
|
|
}
|
|
|
|
string PolicyCompiler_ipt::PrintRule::_printLogPrefix(PolicyRule *rule,
|
|
const string &prefix)
|
|
{
|
|
FWObject *ruleset = rule->getParent();
|
|
|
|
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"),
|
|
ruleset->getName(),
|
|
rule->getLabel(),
|
|
prefix);
|
|
}
|
|
|
|
string PolicyCompiler_ipt::PrintRule::_printLogParameters(PolicyRule *rule)
|
|
{
|
|
PolicyCompiler_ipt *ipt_comp = dynamic_cast<PolicyCompiler_ipt*>(compiler);
|
|
std::ostringstream str;
|
|
string s;
|
|
FWOptions *ruleopt = (rule!=NULL) ?
|
|
rule->getOptionsObject() : compiler->getCachedFwOpt();
|
|
|
|
// there is no ULOG for ip6tables yet
|
|
bool use_ulog = (!ipt_comp->ipv6 &&
|
|
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(Service *srv)
|
|
{
|
|
PolicyCompiler_ipt *ipt_comp = dynamic_cast<PolicyCompiler_ipt*>(compiler);
|
|
string s;
|
|
// CustomService returns protocol name starting with v3.0.4
|
|
// However CustomService can return protocol name "any", which we should
|
|
// just skip.
|
|
if (CustomService::isA(srv))
|
|
{
|
|
// check if the code string for this custom service already includes
|
|
// "-p proto" fragment
|
|
string code = CustomService::cast(srv)->getCodeForPlatform(
|
|
compiler->myPlatformName());
|
|
std::size_t minus_p = code.find("-p ");
|
|
if (minus_p != string::npos) return "";
|
|
string pn = srv->getProtocolName();
|
|
if (pn == "any") return "";
|
|
}
|
|
|
|
if (!srv->isAny() && !TagService::isA(srv) && !UserService::isA(srv))
|
|
{
|
|
string pn = srv->getProtocolName();
|
|
if (pn=="ip" || pn=="any") pn = "all";
|
|
|
|
if (ipt_comp->ipv6)
|
|
{
|
|
if (ICMPService::isA(srv))
|
|
{
|
|
compiler->abort(
|
|
"Can not use ICMPService in ipv6 rule; "
|
|
"use ICMP6Service object instead");
|
|
}
|
|
|
|
if (ICMP6Service::isA(srv))
|
|
{
|
|
s = "-p ipv6-icmp ";
|
|
if (srv->getInt("type")!=-1 &&
|
|
(version.empty() ||
|
|
XMLTools::version_compare(version, "1.3.0")>=0))
|
|
s += " -m icmp6";
|
|
} else
|
|
{
|
|
// ip6tables issues warning for commands using "-p all"
|
|
// Warning: never matched protocol: all. use exension match instead
|
|
// Skip "-p all" if ipv6
|
|
if (pn!="all") s = "-p " + pn + " ";
|
|
}
|
|
} else
|
|
{
|
|
if (ICMP6Service::isA(srv))
|
|
{
|
|
compiler->abort(
|
|
"Can not use ICMP6Service in ipv4 rule; "
|
|
"use ICMPService object instead");
|
|
}
|
|
|
|
if (ICMPService::isA(srv))
|
|
{
|
|
s = "-p icmp ";
|
|
if (version.empty() ||
|
|
XMLTools::version_compare(version, "1.2.9")>=0)
|
|
s += " -m icmp ";
|
|
} else
|
|
{
|
|
s = "-p " + pn + " ";
|
|
}
|
|
}
|
|
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 = TCPUDPService::cast(srv)->getSrcRangeStart();
|
|
int re = TCPUDPService::cast(srv)->getSrcRangeEnd();
|
|
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 = TCPUDPService::cast(srv)->getDstRangeStart();
|
|
int re = TCPUDPService::cast(srv)->getDstRangeEnd();
|
|
str << _printPorts(rs,re);
|
|
}
|
|
return str.str();
|
|
}
|
|
|
|
string PolicyCompiler_ipt::PrintRule::_printICMP(ICMPService *srv)
|
|
{
|
|
std::ostringstream str;
|
|
if (ICMPService::cast(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, PolicyRule *rule)
|
|
{
|
|
PolicyCompiler_ipt *ipt_comp=dynamic_cast<PolicyCompiler_ipt*>(compiler);
|
|
std::ostringstream str;
|
|
if (srv->getBool("fragm") || srv->getBool("short_fragm"))
|
|
{
|
|
if (ipt_comp->ipv6) str << " -m frag --fragmore";
|
|
else str << " -f ";
|
|
}
|
|
|
|
string tos = srv->getTOSCode();
|
|
string dscp = srv->getDSCPCode();
|
|
if (!tos.empty())
|
|
str << " -m tos --tos " << tos;
|
|
else
|
|
if (!dscp.empty())
|
|
{
|
|
if (dscp.find("BE")==0 ||
|
|
dscp.find("EF")==0 ||
|
|
dscp.find("AF")==0 ||
|
|
dscp.find("CS")==0)
|
|
str << " -m dscp --dscp-class " << dscp;
|
|
else
|
|
str << " -m dscp --dscp " << dscp;
|
|
}
|
|
|
|
if (srv->hasIpOptions())
|
|
{
|
|
if (!ipt_comp->ipv6)
|
|
{
|
|
str << " -m ipv4options ";
|
|
if (srv->getBool("any_opt")) str << " --any-opt";
|
|
else
|
|
{
|
|
if (srv->getBool("lsrr")) str << " --lsrr";
|
|
if (srv->getBool("ssrr")) str << " --ssrr";
|
|
if (srv->getBool("rr")) str << " --rr";
|
|
if (srv->getBool("ts")) str << " --ts";
|
|
if (srv->getBool("rtralt")) str << " --ra";
|
|
}
|
|
} else
|
|
{
|
|
compiler->abort(
|
|
rule,
|
|
"IP options match is not supported for IPv6.");
|
|
}
|
|
}
|
|
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)
|
|
{
|
|
PolicyCompiler_ipt *ipt_comp = dynamic_cast<PolicyCompiler_ipt*>(compiler);
|
|
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) ||
|
|
TagService::isA(srv))
|
|
{
|
|
string str=_printSrcPorts( srv );
|
|
if (! str.empty() )
|
|
{
|
|
ostr << _printSingleOptionWithNegation(" --sport", 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();
|
|
|
|
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() )
|
|
{
|
|
if (ipt_comp->newIptables(version))
|
|
ostr << " --sports ";
|
|
else
|
|
ostr << " --source-port ";
|
|
|
|
ostr << str << " ";
|
|
}
|
|
}
|
|
return ostr.str();
|
|
}
|
|
|
|
string PolicyCompiler_ipt::PrintRule::_printDstService(RuleElementSrv *rel)
|
|
{
|
|
PolicyCompiler_ipt *ipt_comp=dynamic_cast<PolicyCompiler_ipt*>(compiler);
|
|
std::ostringstream ostr;
|
|
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=_printDstPorts( srv );
|
|
if (! str.empty() )
|
|
{
|
|
ostr << _printSingleOptionWithNegation(" --dport", rel, str);
|
|
}
|
|
}
|
|
if (TCPService::isA(srv))
|
|
{
|
|
string str=_printTCPFlags(TCPService::cast(srv));
|
|
if (!str.empty())
|
|
{
|
|
ostr << _printSingleOptionWithNegation("", rel, str);
|
|
}
|
|
}
|
|
if (ICMPService::isA(srv) || ICMP6Service::isA(srv))
|
|
{
|
|
string icmp_type_str =
|
|
(ipt_comp->ipv6) ? " --icmpv6-type" : " --icmp-type";
|
|
|
|
string str = _printICMP( ICMPService::cast(srv) );
|
|
if (str.empty() )
|
|
{
|
|
// module icmp6 does not like "--icmp6-type any"
|
|
if ((version.empty() ||
|
|
XMLTools::version_compare(version, "1.2.6")>0) &&
|
|
!ipt_comp->ipv6)
|
|
ostr << icmp_type_str << " any ";
|
|
} else
|
|
{
|
|
ostr << _printSingleOptionWithNegation(icmp_type_str, rel, str);
|
|
}
|
|
}
|
|
if (IPService::isA(srv))
|
|
{
|
|
string str = _printIP(IPService::cast(srv), PolicyRule::cast(rel->getParent()));
|
|
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 "
|
|
<< _printSingleObjectNegation(rel)
|
|
<< "--mark "
|
|
<< TagService::constcast(srv)->getCode() << " ";
|
|
}
|
|
if (UserService::isA(srv))
|
|
{
|
|
ostr << "-m owner "
|
|
<< _printSingleObjectNegation(rel)
|
|
<< "--uid-owner "
|
|
<< UserService::cast(srv)->getUserId() << " ";
|
|
}
|
|
} 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() )
|
|
{
|
|
if (ipt_comp->newIptables(version))
|
|
ostr << " --dports ";
|
|
else
|
|
ostr << " --destination-port ";
|
|
ostr << str << " ";
|
|
}
|
|
}
|
|
return ostr.str();
|
|
}
|
|
|
|
string PolicyCompiler_ipt::PrintRule::_printSrcAddr(RuleElement *rel, Address *o)
|
|
{
|
|
string res;
|
|
if (AddressRange::cast(o)!=NULL)
|
|
{
|
|
AddressRange *ar = AddressRange::cast(o);
|
|
const InetAddr &range_start = ar->getRangeStart();
|
|
const InetAddr &range_end = ar->getRangeEnd();
|
|
|
|
if (range_start != range_end)
|
|
{
|
|
if (!have_m_iprange) { res = "-m iprange "; have_m_iprange = true; }
|
|
res += _printSingleObjectNegation(rel) + "--src-range ";
|
|
res += range_start.toString() + "-" + range_end.toString() + " ";
|
|
} else
|
|
res += "-s " + range_start.toString() + " ";
|
|
|
|
return res;
|
|
}
|
|
return _printSingleOptionWithNegation(" -s", rel, _printAddr(o));
|
|
}
|
|
|
|
string PolicyCompiler_ipt::PrintRule::_printDstAddr(RuleElement *rel, Address *o)
|
|
{
|
|
string res;
|
|
if (AddressRange::cast(o)!=NULL)
|
|
{
|
|
AddressRange *ar = AddressRange::cast(o);
|
|
const InetAddr &range_start = ar->getRangeStart();
|
|
const InetAddr &range_end = ar->getRangeEnd();
|
|
if (range_start != range_end)
|
|
{
|
|
if (!have_m_iprange) { res = "-m iprange "; have_m_iprange = true; }
|
|
res += _printSingleObjectNegation(rel) + "--dst-range ";
|
|
res += range_start.toString() + "-" + range_end.toString() + " ";
|
|
} else
|
|
res += "-d " + range_start.toString() + " ";
|
|
|
|
return res;
|
|
}
|
|
return _printSingleOptionWithNegation(" -d", rel, _printAddr(o));
|
|
}
|
|
|
|
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, ipt_comp->ipv6)
|
|
<< " ";
|
|
return ostr.str();
|
|
}
|
|
|
|
const InetAddr *addr = o->getAddressPtr();
|
|
const InetAddr *mask = o->getNetmaskPtr();
|
|
|
|
if (addr==NULL)
|
|
{
|
|
compiler->warning(
|
|
string("Empty inet address in object ") +
|
|
o->getName() + "(" +
|
|
FWObjectDatabase::getStringId(o->getId())
|
|
+ ")");
|
|
return ostr.str();
|
|
}
|
|
|
|
// Note that mask can be NULL, for example if o is AddressRange.
|
|
if (addr->isAny() && (mask==NULL || 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";
|
|
daysofweek[7]="Sun";
|
|
|
|
int smin, shour, sday, smonth, syear, sdayofweek;
|
|
int emin, ehour, eday, emonth, eyear, edayofweek;
|
|
string days_of_week;
|
|
|
|
Interval *interval = compiler->getFirstWhen(r);
|
|
assert(interval!=NULL);
|
|
|
|
interval->getStartTime( &smin, &shour, &sday, &smonth, &syear, &sdayofweek);
|
|
interval->getEndTime( &emin, &ehour, &eday, &emonth, &eyear, &edayofweek);
|
|
days_of_week = interval->getDaysOfWeek();
|
|
|
|
if (shour<0) shour=0;
|
|
if (smin<0) smin=0;
|
|
|
|
if (ehour<0) ehour=23;
|
|
if (emin<0) emin=59;
|
|
|
|
ostr << "-m time ";
|
|
|
|
bool use_timestart_timestop = true;
|
|
|
|
if (XMLTools::version_compare(version, "1.4.0")>=0)
|
|
{
|
|
// in 1.4.0 date format has changed, it is now ISO 8601
|
|
// http://www.w3.org/TR/NOTE-datetime
|
|
|
|
if (sday>0 && smonth>0 && syear>0)
|
|
{
|
|
ostr << "--datestart "
|
|
<< setw(2) << setfill('0') << syear << "-"
|
|
<< setw(2) << setfill('0') << smonth << "-"
|
|
<< setw(2) << setfill('0') << sday << "T"
|
|
<< setw(2) << setfill('0') << shour << ":"
|
|
<< setw(2) << setfill('0') << smin << ":00Z ";
|
|
use_timestart_timestop = false;
|
|
}
|
|
|
|
if (eday>0 && emonth>0 && eyear>0)
|
|
{
|
|
ostr << "--datestop "
|
|
<< setw(2) << setfill('0') << eyear << "-"
|
|
<< setw(2) << setfill('0') << emonth << "-"
|
|
<< setw(2) << setfill('0') << eday << "T"
|
|
<< setw(2) << setfill('0') << ehour << ":"
|
|
<< setw(2) << setfill('0') << emin << ":00Z ";
|
|
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 (!days_of_week.empty() && days_of_week != "0,1,2,3,4,5,6")
|
|
{
|
|
ostr << " --weekdays ";
|
|
istringstream istr(days_of_week);
|
|
bool first= true;
|
|
while (!istr.eof())
|
|
{
|
|
if (!first) ostr << ',';
|
|
first = false;
|
|
int d;
|
|
istr >> d;
|
|
ostr << daysofweek[d];
|
|
char sep;
|
|
istr >> sep;
|
|
}
|
|
}
|
|
|
|
} else
|
|
{
|
|
/* "old" iptables time module
|
|
|
|
TIME v1.2.11 options:
|
|
--timestart value --timestop value --days listofdays
|
|
timestart value : HH:MM
|
|
timestop value : HH:MM
|
|
listofdays value: a list of days to apply -> ie. Mon,Tue,Wed,Thu,Fri.
|
|
Case sensitive
|
|
|
|
*/
|
|
|
|
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 (!days_of_week.empty() && days_of_week != "0,1,2,3,4,5,6")
|
|
{
|
|
ostr << " --days ";
|
|
istringstream istr(days_of_week);
|
|
bool first= true;
|
|
while (!istr.eof())
|
|
{
|
|
if (!first) ostr << ',';
|
|
first = false;
|
|
int d;
|
|
istr >> d;
|
|
ostr << daysofweek[d];
|
|
char sep;
|
|
istr >> sep;
|
|
}
|
|
}
|
|
|
|
}
|
|
return ostr.str();
|
|
}
|
|
|
|
PolicyCompiler_ipt::PrintRule::PrintRule(const std::string &name) :
|
|
PolicyRuleProcessor(name)
|
|
{
|
|
init = true;
|
|
print_once_on_top = true;
|
|
// use delayed initialization for ipt_comp->minus_n_commands
|
|
// because it requires pointer to the compiler which has not been
|
|
// initialized yet when this constructor is executed.
|
|
minus_n_tracker_initialized = false;
|
|
}
|
|
|
|
/*
|
|
* Initialize some internal variables. Need to do this in a separate
|
|
* method because pointer to the compiler object is set by
|
|
* RuleProcessor::setContext and is not available in constructor.
|
|
*/
|
|
void PolicyCompiler_ipt::PrintRule::initialize()
|
|
{
|
|
// retrieve and save version for _printSingleOptionWithNegation and others
|
|
version = compiler->fw->getStr("version");
|
|
}
|
|
|
|
bool PolicyCompiler_ipt::PrintRule::processNext()
|
|
{
|
|
PolicyCompiler_ipt *ipt_comp=dynamic_cast<PolicyCompiler_ipt*>(compiler);
|
|
PolicyRule *rule =getNext();
|
|
if (rule==NULL) return false;
|
|
|
|
string chain = rule->getStr("ipt_chain");
|
|
if (ipt_comp->chain_usage_counter[chain] > 0)
|
|
{
|
|
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), ipt_comp->ipv6);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
string PolicyCompiler_ipt::PrintRule::PolicyRuleToString(PolicyRule *rule)
|
|
{
|
|
FWOptions *ruleopt = rule->getOptionsObject();
|
|
FWObject *ref;
|
|
|
|
RuleElementSrc *srcrel=rule->getSrc();
|
|
ref=srcrel->front();
|
|
Address *src=Address::cast(FWReference::cast(ref)->getPointer());
|
|
if(src==NULL)
|
|
compiler->abort(rule, string("Broken SRC in ") + rule->getLabel());
|
|
|
|
RuleElementDst *dstrel=rule->getDst();
|
|
ref=dstrel->front();
|
|
Address *dst=Address::cast(FWReference::cast(ref)->getPointer());
|
|
if(dst==NULL)
|
|
compiler->abort(rule, string("Broken DST in ") + rule->getLabel());
|
|
|
|
RuleElementSrv *srvrel=rule->getSrv();
|
|
ref=srvrel->front();
|
|
Service *srv=Service::cast(FWReference::cast(ref)->getPointer());
|
|
if(srv==NULL)
|
|
compiler->abort(rule, string("Broken SRV in ") + rule->getLabel());
|
|
|
|
|
|
std::ostringstream command_line;
|
|
|
|
have_m_iprange = false;
|
|
|
|
command_line << _startRuleLine();
|
|
|
|
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(
|
|
rule,
|
|
"Empty MAC address in rule");
|
|
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";
|
|
command_line << _printSingleOptionWithNegation(" --mac-source",
|
|
srcrel,
|
|
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 << _printSrcAddr(srcrel, src);
|
|
|
|
} else
|
|
command_line << _printSrcAddr(srcrel, src);
|
|
|
|
}
|
|
command_line << _printSrcService(srvrel);
|
|
|
|
if (!dst->isAny())
|
|
command_line << _printDstAddr(dstrel, 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::_commit()
|
|
{
|
|
return "";
|
|
}
|
|
|
|
string PolicyCompiler_ipt::PrintRule::_clampTcpToMssRule()
|
|
{
|
|
PolicyCompiler_ipt *ipt_comp = dynamic_cast<PolicyCompiler_ipt*>(compiler);
|
|
ostringstream res;
|
|
|
|
if ( compiler->getCachedFwOpt()->getBool("clamp_mss_to_mtu"))
|
|
{
|
|
bool ipforw;
|
|
if (ipt_comp->ipv6)
|
|
{
|
|
string s = compiler->getCachedFwOpt()->getStr("linux24_ipv6_forward");
|
|
ipforw = (s.empty() || s=="1" || s=="On" || s=="on");
|
|
// bug #2477775: target TCPMSS is not available in ip6tables
|
|
// before 1.4.0 In fact I am not sure of the minimal required
|
|
// version. According to the netfilter git log, it was added in
|
|
// 1.3.8
|
|
if (XMLTools::version_compare(version, "1.3.8")<0)
|
|
{
|
|
if (ipforw)
|
|
{
|
|
res << "target TCPMSS is not supported by ip6tables before v1.3.8";
|
|
compiler->warning(res.str());
|
|
return "# " + res.str() + "\n\n";
|
|
} else return "";
|
|
}
|
|
} else
|
|
{
|
|
string s = compiler->getCachedFwOpt()->getStr("linux24_ip_forward");
|
|
ipforw = (s.empty() || s=="1" || s=="On" || s=="on");
|
|
}
|
|
|
|
if (ipforw)
|
|
{
|
|
res << _startRuleLine()
|
|
<< "FORWARD -p tcp -m tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu"
|
|
<< _endRuleLine();
|
|
res << endl;
|
|
}
|
|
}
|
|
return res.str();
|
|
}
|
|
|
|
string PolicyCompiler_ipt::PrintRule::_printOptionalGlobalRules()
|
|
{
|
|
PolicyCompiler_ipt *ipt_comp = dynamic_cast<PolicyCompiler_ipt*>(compiler);
|
|
ostringstream res;
|
|
bool isIPv6 = ipt_comp->ipv6;
|
|
|
|
string s = compiler->getCachedFwOpt()->getStr("linux24_ip_forward");
|
|
bool ipforward= (s.empty() || s=="1" || s=="On" || s=="on");
|
|
s = compiler->getCachedFwOpt()->getStr("linux24_ipv6_forward");
|
|
bool ip6forward= (s.empty() || s=="1" || s=="On" || s=="on");
|
|
bool ipforw = ((!ipt_comp->ipv6 && ipforward) ||
|
|
(ipt_comp->ipv6 && ip6forward));
|
|
|
|
Configlet configlet(compiler->fw, "linux24", "automatic_rules");
|
|
configlet.removeComments();
|
|
configlet.collapseEmptyStrings(true);
|
|
|
|
configlet.setVariable("begin_rule", _startRuleLine().c_str());
|
|
configlet.setVariable("end_rule", _endRuleLine().c_str());
|
|
|
|
configlet.setVariable("ipforw", ipforw);
|
|
|
|
configlet.setVariable("accept_established",
|
|
compiler->getCachedFwOpt()->getBool("accept_established") &&
|
|
ipt_comp->my_table=="filter");
|
|
|
|
if ( compiler->getCachedFwOpt()->getBool("mgmt_ssh") &&
|
|
! compiler->getCachedFwOpt()->getStr("mgmt_addr").empty() )
|
|
{
|
|
string addr_str = compiler->getCachedFwOpt()->getStr("mgmt_addr");
|
|
InetAddrMask *inet_addr = NULL;
|
|
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)
|
|
{
|
|
configlet.setVariable("mgmt_access", 1);
|
|
configlet.setVariable("management_address", inet_addr->toString().c_str());
|
|
} else
|
|
{
|
|
QString err("Backup ssh access rule could not be added "
|
|
"because specified address '%1' is invalid");
|
|
compiler->warning(err.arg(addr_str.c_str()).toStdString());
|
|
}
|
|
}
|
|
|
|
configlet.setVariable(
|
|
"drop_new_tcp_with_no_syn",
|
|
! compiler->getCachedFwOpt()->getBool("accept_new_tcp_with_no_syn"));
|
|
|
|
|
|
configlet.setVariable(
|
|
"add_rules_for_ipv6_neighbor_discovery",
|
|
isIPv6 &&
|
|
compiler->getCachedFwOpt()->getBool("add_rules_for_ipv6_neighbor_discovery"));
|
|
|
|
|
|
configlet.setVariable("drop_invalid",
|
|
compiler->getCachedFwOpt()->getBool("drop_invalid") &&
|
|
!compiler->getCachedFwOpt()->getBool("log_invalid"));
|
|
|
|
configlet.setVariable("drop_invalid_and_log",
|
|
compiler->getCachedFwOpt()->getBool("drop_invalid") &&
|
|
compiler->getCachedFwOpt()->getBool("log_invalid"));
|
|
|
|
configlet.setVariable("create_drop_invalid_chain",
|
|
_createChain("drop_invalid").c_str());
|
|
|
|
|
|
if (compiler->getCachedFwOpt()->getBool("log_invalid") &&
|
|
!isIPv6 &&
|
|
compiler->getCachedFwOpt()->getBool("use_ULOG"))
|
|
{
|
|
configlet.setVariable("use_ulog", 1);
|
|
|
|
string s = compiler->getCachedFwOpt()->getStr("ulog_nlgroup");
|
|
configlet.setVariable("use_nlgroup", !s.empty());
|
|
configlet.setVariable("nlgroup", s.c_str());
|
|
|
|
int r = compiler->getCachedFwOpt()->getInt("ulog_cprange");
|
|
configlet.setVariable("use_cprange", r!=0);
|
|
configlet.setVariable("cprange", r);
|
|
|
|
r = compiler->getCachedFwOpt()->getInt("ulog_qthreshold");
|
|
configlet.setVariable("use_qthreshold", r!=0);
|
|
configlet.setVariable("qthreshold", r);
|
|
} else
|
|
configlet.setVariable("not_use_ulog", 1);
|
|
|
|
configlet.setVariable("invalid_match_log_prefix",
|
|
_printLogPrefix("-1",
|
|
"DENY",
|
|
"global",
|
|
"drop_invalid",
|
|
"Policy",
|
|
"BLOCK INVALID",
|
|
"INVALID state -- DENY ").c_str());
|
|
|
|
return configlet.expand().toStdString();
|
|
}
|
|
|
|
string PolicyCompiler_ipt::PrintRule::_quote(const string &s)
|
|
{
|
|
return "\"" + s + "\"";
|
|
}
|
|
|
|
|