mirror of
https://github.com/fwbuilder/fwbuilder
synced 2026-03-21 10:47:16 +01:00
really fixed #869 "compile rule" should also print the comment. Printing rule comment in the compiler output in the single rule compile mode when firewall object is configured to use iptables-restore. Code that prints rule label and comment has been unified for compilers for all firewall platforms.
851 lines
32 KiB
C++
851 lines
32 KiB
C++
/*
|
|
|
|
Firewall Builder
|
|
|
|
Copyright (C) 2009 NetCitadel, LLC
|
|
|
|
Author: Vadim Kurland vadim@vk.crocodile.org
|
|
|
|
$Id$
|
|
|
|
This program is free software which we release under the GNU General Public
|
|
License. You may redistribute and/or modify this program under the terms
|
|
of that license as published by the Free Software Foundation; either
|
|
version 2 of the License, or (at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
To get a copy of the GNU General Public License, write to the Free Software
|
|
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
|
|
*/
|
|
|
|
#include "../../config.h"
|
|
|
|
#include <fstream>
|
|
#include <iostream>
|
|
#include <algorithm>
|
|
#include <functional>
|
|
#include <stdexcept>
|
|
#include <memory>
|
|
|
|
#include <assert.h>
|
|
#include <cstring>
|
|
#include <iomanip>
|
|
|
|
#include "CompilerDriver_pix.h"
|
|
#include "PolicyCompiler_pix.h"
|
|
#include "NATCompiler_pix.h"
|
|
#include "RoutingCompiler_pix.h"
|
|
#include "OSConfigurator_pix_os.h"
|
|
|
|
#include "Helper.h"
|
|
|
|
#include "fwbuilder/Resources.h"
|
|
#include "fwbuilder/FWObjectDatabase.h"
|
|
#include "fwbuilder/XMLTools.h"
|
|
#include "fwbuilder/FWException.h"
|
|
#include "fwbuilder/Firewall.h"
|
|
#include "fwbuilder/Interface.h"
|
|
#include "fwbuilder/Policy.h"
|
|
#include "fwbuilder/NAT.h"
|
|
#include "fwbuilder/Routing.h"
|
|
#include "fwbuilder/IPv4.h"
|
|
#include "fwbuilder/IPv6.h"
|
|
|
|
#include "fwcompiler/Preprocessor.h"
|
|
|
|
#include "fwbuilder/Cluster.h"
|
|
#include "fwbuilder/ClusterGroup.h"
|
|
#include "fwbuilder/Firewall.h"
|
|
#include "fwbuilder/Interface.h"
|
|
#include "fwbuilder/Policy.h"
|
|
#include "fwbuilder/StateSyncClusterGroup.h"
|
|
#include "fwbuilder/FailoverClusterGroup.h"
|
|
|
|
#include <QStringList>
|
|
#include <QFileInfo>
|
|
#include <QFile>
|
|
#include <QTextStream>
|
|
#include <QtDebug>
|
|
|
|
|
|
using namespace std;
|
|
using namespace libfwbuilder;
|
|
using namespace fwcompiler;
|
|
|
|
class sort_by_net_zone {
|
|
string any_address_id;
|
|
public:
|
|
explicit sort_by_net_zone()
|
|
{
|
|
any_address_id = FWObjectDatabase::getStringId(
|
|
FWObjectDatabase::ANY_ADDRESS_ID);
|
|
}
|
|
bool operator()(const FWObject *a, const FWObject *b)
|
|
{
|
|
if (Interface::constcast(a) && Interface::constcast(b))
|
|
{
|
|
string netzone_a=a->getStr("network_zone");
|
|
string netzone_b=b->getStr("network_zone");
|
|
if ( netzone_a==any_address_id) return false;
|
|
if ( netzone_b==any_address_id) return true;
|
|
}
|
|
return false;
|
|
}
|
|
};
|
|
|
|
QString CompilerDriver_pix::assembleManifest(Cluster *cluster, Firewall* fw, bool cluster_member)
|
|
{
|
|
QString script_buffer;
|
|
QTextStream script(&script_buffer, QIODevice::WriteOnly);
|
|
QString ofname = determineOutputFileName(cluster, fw, cluster_member, ".fw");
|
|
script << "!" << MANIFEST_MARKER << "* " << ofname << endl;
|
|
return script_buffer;
|
|
}
|
|
|
|
QString CompilerDriver_pix::printActivationCommands(Firewall*)
|
|
{
|
|
return "";
|
|
}
|
|
|
|
QString CompilerDriver_pix::assembleFwScript(Cluster *cluster,
|
|
Firewall* fw,
|
|
bool cluster_member,
|
|
OSConfigurator *oscnf)
|
|
{
|
|
Configlet script_skeleton(fw, "cisco", "script_skeleton");
|
|
Configlet top_comment(fw, "cisco", "top_comment");
|
|
|
|
FWOptions* options = fw->getOptionsObject();
|
|
options->setStr("prolog_script", options->getStr("pix_prolog_script"));
|
|
options->setStr("epilog_script", options->getStr("pix_epilog_script"));
|
|
|
|
string vers = fw->getStr("version");
|
|
string platform = fw->getStr("platform");
|
|
bool outbound_acl_supported = Resources::platform_res[platform]->getResourceBool(
|
|
string("/FWBuilderResources/Target/options/")+
|
|
"version_"+vers+
|
|
"/pix_outbound_acl_supported");
|
|
bool afpa = options->getBool("pix_assume_fw_part_of_any");
|
|
bool emulate_outb_acls = options->getBool("pix_emulate_out_acl");
|
|
bool generate_outb_acls = options->getBool("pix_generate_out_acl");
|
|
|
|
top_comment.setVariable("outbound_acl_supported", QString((outbound_acl_supported)?"supported":"not supported"));
|
|
top_comment.setVariable("emulate_outb_acls", QString((emulate_outb_acls)?"yes":"no"));
|
|
top_comment.setVariable("generate_outb_acls", QString((generate_outb_acls)?"yes":"no"));
|
|
top_comment.setVariable("afpa", QString((afpa)?"yes":"no"));
|
|
|
|
script_skeleton.setVariable("short_script", options->getBool("short_script"));
|
|
script_skeleton.setVariable("not_short_script", ! options->getBool("short_script"));
|
|
script_skeleton.setVariable("system_configuration_script", system_configuration_script.c_str());
|
|
script_skeleton.setVariable("policy_script", policy_script.c_str());
|
|
script_skeleton.setVariable("nat_script", nat_script.c_str());
|
|
script_skeleton.setVariable("routing_script", routing_script.c_str());
|
|
|
|
assembleFwScriptInternal(cluster, fw, cluster_member, oscnf, &script_skeleton, &top_comment, "!");
|
|
return script_skeleton.expand();
|
|
}
|
|
|
|
string CompilerDriver_pix::run(const std::string &cluster_id,
|
|
const std::string &firewall_id,
|
|
const std::string &single_rule_id)
|
|
{
|
|
Cluster *cluster = NULL;
|
|
if (!cluster_id.empty())
|
|
cluster = Cluster::cast(objdb->findInIndex(objdb->getIntId(cluster_id)));
|
|
|
|
Firewall *fw = Firewall::cast(objdb->findInIndex(objdb->getIntId(firewall_id)));
|
|
assert(fw);
|
|
|
|
// Copy rules from the cluster object
|
|
populateClusterElements(cluster, fw);
|
|
|
|
if (cluster)
|
|
{
|
|
// PIX failover is dfferent from VRRP and other failover protocols
|
|
// in that it does not create new virtual address. Instead, each
|
|
// unit is configured with two ip addresses, one for the active
|
|
// unit and another for standby one. When active unit fails, the
|
|
// other one assumes its address.
|
|
//
|
|
// This matters because when we use cluster object or one of its
|
|
// interfaces in rules, compiler should expand it to the set of
|
|
// addresses that includes addresses of the corresponding
|
|
// interface of both member firewalls. Method
|
|
// CompilerDriver::copyFailoverInterface adds a copy of firewall
|
|
// interface to the cluster object. This works for all firewalls,
|
|
// but for PIX we need to add copies of interfaces from both
|
|
// members.
|
|
//
|
|
FWObjectTypedChildIterator cl_iface = cluster->findByType(Interface::TYPENAME);
|
|
for (; cl_iface != cl_iface.end(); ++cl_iface)
|
|
{
|
|
FailoverClusterGroup *failover_group =
|
|
FailoverClusterGroup::cast(
|
|
(*cl_iface)->getFirstByType(FailoverClusterGroup::TYPENAME));
|
|
if (failover_group)
|
|
{
|
|
FWObject *this_member_interface = NULL;
|
|
list<FWObject*> other_member_interfaces;
|
|
for (FWObjectTypedChildIterator it =
|
|
failover_group->findByType(FWObjectReference::TYPENAME);
|
|
it != it.end(); ++it)
|
|
{
|
|
FWObject *intf = FWObjectReference::getObject(*it);
|
|
assert(intf);
|
|
if (intf->isChildOf(fw)) this_member_interface = intf;
|
|
else other_member_interfaces.push_back(intf);
|
|
}
|
|
|
|
if (!other_member_interfaces.empty())
|
|
{
|
|
for (list<FWObject*>::iterator it=other_member_interfaces.begin();
|
|
it!=other_member_interfaces.end(); ++it)
|
|
{
|
|
cluster->addCopyOf(*it, true);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#if 0
|
|
FWObjectTypedChildIterator iface = fw->findByType(Interface::TYPENAME);
|
|
for (; iface != iface.end(); ++iface)
|
|
{
|
|
(*iface)->dump(true, true);
|
|
}
|
|
#endif
|
|
|
|
|
|
QString ofname = determineOutputFileName(cluster, fw, !cluster_id.empty(), ".fw");
|
|
FWOptions* options = fw->getOptionsObject();
|
|
|
|
QString script_buffer;
|
|
|
|
try
|
|
{
|
|
commonChecks2(cluster, fw);
|
|
|
|
pixClusterConfigurationChecks(cluster, fw);
|
|
|
|
// Note that fwobjectname may be different from the name of the
|
|
// firewall fw This happens when we compile a member of a cluster
|
|
current_firewall_name = fw->getName().c_str();
|
|
|
|
bool pix_acl_basic = options->getBool("pix_acl_basic");
|
|
bool pix_acl_no_clear = options->getBool("pix_acl_no_clear");
|
|
bool pix_acl_substitution = options->getBool("pix_acl_substitution");
|
|
bool pix_add_clear_statements = options->getBool("pix_add_clear_statements");
|
|
|
|
if (!pix_acl_basic && !pix_acl_no_clear && !pix_acl_substitution)
|
|
{
|
|
if ( pix_add_clear_statements ) options->setBool("pix_acl_basic",true);
|
|
else options->setBool("pix_acl_no_clear",true);
|
|
}
|
|
|
|
Helper helper(NULL);
|
|
|
|
multimap<string, FWObject*> netzone_objects;
|
|
|
|
std::list<FWObject*> all_interfaces = fw->getByTypeDeep(Interface::TYPENAME);
|
|
for (std::list<FWObject*>::iterator i=all_interfaces.begin(); i!=all_interfaces.end(); ++i)
|
|
{
|
|
Interface *iface = dynamic_cast<Interface*>(*i);
|
|
assert(iface);
|
|
|
|
if (iface->getOptionsObject()->getBool("cluster_interface")) continue;
|
|
|
|
if ((iface->getOptionsObject()->getStr("type") == "" ||
|
|
iface->getOptionsObject()->getStr("type") == "ethernet") &&
|
|
iface->getByType(Interface::TYPENAME).size() > 0)
|
|
{
|
|
// Parent vlan interface (i.e. trunk)
|
|
if (!iface->isUnprotected())
|
|
{
|
|
QString err(
|
|
"Interface %1 has vlan subinterfaces, it can not "
|
|
"be used for ACL. Marking this interface \"unprotected\" "
|
|
"to exclude it."
|
|
);
|
|
warning(fw, NULL, NULL,
|
|
err.arg(iface->getName().c_str())
|
|
.toStdString());
|
|
iface->setUnprotected(true);
|
|
}
|
|
}
|
|
|
|
// Tests for label, security level and network zone make sense
|
|
// only for interfaces that can be used in ACLs or to bind
|
|
// ACLs to. Unnumbered interfaces can't, so we do not need to
|
|
// run these checks. One example of unnumbered interface is
|
|
// parent interface for vlan subinterfaces.
|
|
if (iface->isUnnumbered()) continue;
|
|
if (iface->isUnprotected()) continue;
|
|
|
|
/*
|
|
* there shouldn't be two interfaces with the same security level and same label
|
|
*
|
|
*/
|
|
for (std::list<FWObject*>::iterator j=all_interfaces.begin(); j!=all_interfaces.end(); ++j)
|
|
{
|
|
Interface *iface2 = dynamic_cast<Interface*>(*j);
|
|
assert(iface2);
|
|
if (iface2->isUnnumbered()) continue;
|
|
if (iface2->isUnprotected()) continue;
|
|
if (iface->getId()==iface2->getId()) continue;
|
|
if (iface->getOptionsObject()->getBool("cluster_interface") ||
|
|
iface2->getOptionsObject()->getBool("cluster_interface"))
|
|
continue;
|
|
|
|
if (iface->getSecurityLevel()==iface2->getSecurityLevel())
|
|
{
|
|
QString err(
|
|
"Security level of each interface should be unique, "
|
|
"however interfaces %1 (%2) and %3 (%4)"
|
|
" have the same security level."
|
|
);
|
|
abort(fw, NULL, NULL,
|
|
err.arg(iface->getName().c_str())
|
|
.arg(iface->getLabel().c_str())
|
|
.arg(iface2->getName().c_str())
|
|
.arg(iface2->getLabel().c_str()).toStdString());
|
|
throw FatalErrorInSingleRuleCompileMode();
|
|
}
|
|
|
|
if (iface->getLabel()==iface2->getLabel())
|
|
{
|
|
QString err(
|
|
"Label of each interface should be unique, "
|
|
"however interfaces %1 (%2) and %3 (%4)"
|
|
" have the same."
|
|
);
|
|
abort(fw, NULL, NULL,
|
|
err.arg(iface->getName().c_str())
|
|
.arg(iface->getLabel().c_str())
|
|
.arg(iface2->getName().c_str())
|
|
.arg(iface2->getLabel().c_str()).toStdString());
|
|
throw FatalErrorInSingleRuleCompileMode();
|
|
}
|
|
}
|
|
|
|
// We only do limited checks for dedicated failover
|
|
// interfaces because they are not used in ACLs or
|
|
// anywhere else in configuration, except in "failover"
|
|
// commands.
|
|
if (iface->isDedicatedFailover()) continue;
|
|
|
|
/*
|
|
* in PIX, we need network zones to be defined for all interfaces
|
|
*/
|
|
string netzone_id = iface->getStr("network_zone");
|
|
if (netzone_id=="")
|
|
{
|
|
QString err("Network zone definition is missing for interface %1 (%2)");
|
|
abort(fw, NULL, NULL,
|
|
err.arg(iface->getName().c_str())
|
|
.arg(iface->getLabel().c_str()).toStdString());
|
|
throw FatalErrorInSingleRuleCompileMode();
|
|
}
|
|
|
|
FWObject *netzone = objdb->findInIndex(
|
|
FWObjectDatabase::getIntId(netzone_id));
|
|
if (netzone==NULL)
|
|
{
|
|
QString err("Network zone points at nonexisting object for interface %1 (%2)");
|
|
abort(fw, NULL, NULL,
|
|
err.arg(iface->getName().c_str())
|
|
.arg(iface->getLabel().c_str()).toStdString());
|
|
throw FatalErrorInSingleRuleCompileMode();
|
|
}
|
|
/*
|
|
* netzone may be a group, in which case we need to expand it
|
|
* (recursively).
|
|
*
|
|
* 1. We create new temporary object (type Group).
|
|
*
|
|
* 2. put it in the database somewhere
|
|
*
|
|
* 3. add all objects that belong to the network zone to this
|
|
* group. We add objects directly, not as a reference.
|
|
*
|
|
* 4. finally replace reference to the old network zone object in the
|
|
* interface with reference to this new group.
|
|
*
|
|
* 5. we store ID of the original network zone object
|
|
* using iface->setStr("orig_netzone_id")
|
|
*
|
|
* This ensures netzones do not contain other groups and do not
|
|
* require any recursive expanding anymore. Since objects were added
|
|
* to netzones directly, we do not need to bother with dereferencing,
|
|
* too.
|
|
*/
|
|
list<FWObject*> ol;
|
|
helper.expand_group_recursive(netzone,ol);
|
|
|
|
FWObject *nz = objdb->createObjectGroup();
|
|
assert(nz!=NULL);
|
|
nz->setName("netzone_"+iface->getLabel());
|
|
objdb->add(nz);
|
|
|
|
for (list<FWObject*>::iterator j=ol.begin(); j!=ol.end(); ++j)
|
|
{
|
|
netzone_objects.insert( pair<string,FWObject*>(iface->getLabel(),*j));
|
|
nz->add(*j);
|
|
}
|
|
iface->setStr("orig_netzone_id", netzone_id );
|
|
iface->setStr("network_zone",
|
|
FWObjectDatabase::getStringId(nz->getId()) );
|
|
}
|
|
|
|
/*
|
|
* the same object (network or host) can not belong to network zones
|
|
* of two different interfaces. Map netzone_objects holds pairs
|
|
* interface_id/object. We just make sure the same object does not
|
|
* appear in two pairs with different interfaces.
|
|
*/
|
|
multimap<string,FWObject*>::iterator k;
|
|
for (k=netzone_objects.begin(); k!=netzone_objects.end(); ++k)
|
|
{
|
|
multimap<string,FWObject*>::iterator l;
|
|
l=k;
|
|
++l;
|
|
for ( ; l!=netzone_objects.end(); ++l)
|
|
{
|
|
if ( l->second->getId() == k->second->getId() )
|
|
{
|
|
if (k->first==l->first)
|
|
{
|
|
QString err("Object %1 is used more than once in network zone of interface %2");
|
|
abort(fw, NULL, NULL,
|
|
err.arg(l->second->getName().c_str())
|
|
.arg(k->first.c_str()).toStdString());
|
|
throw FatalErrorInSingleRuleCompileMode();
|
|
} else
|
|
{
|
|
QString err("Object %1 is used in network zones of "
|
|
"interfaces %2 and %3");
|
|
abort(fw, NULL, NULL,
|
|
err.arg(l->second->getName().c_str())
|
|
.arg(k->first.c_str())
|
|
.arg(l->first.c_str()).toStdString());
|
|
throw FatalErrorInSingleRuleCompileMode();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Now that all checks are done, we can drop copies of cluster
|
|
* interfaces that were added to the firewall by
|
|
* CompilerDriver::populateClusterElements()
|
|
*/
|
|
list<FWObject*> copies_of_cluster_interfaces;
|
|
for (std::list<FWObject*>::iterator i=all_interfaces.begin(); i!=all_interfaces.end(); ++i)
|
|
{
|
|
Interface *iface = Interface::cast(*i);
|
|
assert(iface);
|
|
|
|
if (iface->getOptionsObject()->getBool("cluster_interface"))
|
|
copies_of_cluster_interfaces.push_back(iface);
|
|
}
|
|
while (copies_of_cluster_interfaces.size())
|
|
{
|
|
fw->remove(copies_of_cluster_interfaces.front());
|
|
copies_of_cluster_interfaces.pop_front();
|
|
}
|
|
|
|
all_interfaces = fw->getByTypeDeep(Interface::TYPENAME);
|
|
|
|
for (std::list<FWObject*>::iterator i=all_interfaces.begin(); i!=all_interfaces.end(); ++i)
|
|
{
|
|
Interface *iface = dynamic_cast<Interface*>(*i);
|
|
assert(iface);
|
|
/*
|
|
* missing labels on interfaces
|
|
*
|
|
*/
|
|
if (iface->getLabel()=="")
|
|
{
|
|
string lbl;
|
|
if (iface->isDedicatedFailover())
|
|
{
|
|
// dedicated failover interface misses label. This
|
|
// interface can be used in failover cluster group
|
|
// or state sync group. Assign label depending on
|
|
// the function.
|
|
FWObjectTypedChildIterator it =
|
|
cluster->findByType(StateSyncClusterGroup::TYPENAME);
|
|
StateSyncClusterGroup *state_sync_group =
|
|
StateSyncClusterGroup::cast(*it);
|
|
if (state_sync_group && state_sync_group->hasMember(iface))
|
|
lbl = "state";
|
|
|
|
if (!iface->getOptionsObject()->getStr("failover_group_id").empty())
|
|
lbl = "failover";
|
|
}
|
|
|
|
if (lbl.empty())
|
|
{
|
|
if (iface->getSecurityLevel()==0) lbl="outside";
|
|
else
|
|
{
|
|
if (iface->getSecurityLevel()==100) lbl="inside";
|
|
else
|
|
{
|
|
QString l("dmz%1");
|
|
lbl = l.arg(iface->getSecurityLevel()).toStdString();
|
|
}
|
|
}
|
|
}
|
|
iface->setLabel(lbl);
|
|
}
|
|
|
|
|
|
|
|
}
|
|
/*
|
|
* now sort interfaces by their network zone "width" (that
|
|
* is, more narrow network zone should go first, interface
|
|
* with network zone "any" should be the last)
|
|
*
|
|
std::sort(fw->begin(), fw->end(), sort_by_net_zone() );
|
|
*/
|
|
|
|
std::auto_ptr<Preprocessor> prep(new Preprocessor(objdb , fw, false));
|
|
prep->compile();
|
|
|
|
std::auto_ptr<OSConfigurator> oscnf(new OSConfigurator_pix_os(objdb , fw, false));
|
|
if (inTestMode()) oscnf->setTestMode();
|
|
if (inEmbeddedMode()) oscnf->setEmbeddedMode();
|
|
|
|
oscnf->prolog();
|
|
oscnf->processFirewallOptions();
|
|
|
|
|
|
/* create compilers and run the whole thing */
|
|
|
|
std::auto_ptr<NATCompiler_pix> n(new NATCompiler_pix(objdb, fw, false, oscnf.get()));
|
|
|
|
RuleSet *nat = RuleSet::cast(fw->getFirstByType(NAT::TYPENAME));
|
|
if (nat)
|
|
{
|
|
n->setSourceRuleSet(nat);
|
|
n->setRuleSetName(nat->getName());
|
|
|
|
if (inTestMode()) n->setTestMode();
|
|
if (inEmbeddedMode()) n->setEmbeddedMode();
|
|
n->setSingleRuleCompileMode(single_rule_id);
|
|
n->setDebugLevel( dl );
|
|
if (rule_debug_on) n->setDebugRule( drn );
|
|
n->setVerbose( verbose );
|
|
|
|
if ( n->prolog() > 0 )
|
|
{
|
|
n->compile();
|
|
n->epilog();
|
|
} else
|
|
info(" Nothing to compile in NAT");
|
|
}
|
|
|
|
std::auto_ptr<PolicyCompiler_pix> c(
|
|
new PolicyCompiler_pix(objdb, fw, false, oscnf.get() , n.get()));
|
|
|
|
RuleSet *policy = RuleSet::cast(fw->getFirstByType(Policy::TYPENAME));
|
|
if (policy)
|
|
{
|
|
c->setSourceRuleSet(policy);
|
|
c->setRuleSetName(policy->getName());
|
|
|
|
if (inTestMode()) c->setTestMode();
|
|
if (inEmbeddedMode()) c->setEmbeddedMode();
|
|
c->setSingleRuleCompileMode(single_rule_id);
|
|
c->setDebugLevel( dl );
|
|
if (rule_debug_on) c->setDebugRule( drp );
|
|
c->setVerbose( verbose );
|
|
|
|
if ( c->prolog() > 0 )
|
|
{
|
|
c->compile();
|
|
c->epilog();
|
|
} else
|
|
info(" Nothing to compile in Policy");
|
|
}
|
|
|
|
std::auto_ptr<RoutingCompiler_pix> r(new RoutingCompiler_pix(objdb, fw, false, oscnf.get()));
|
|
|
|
RuleSet *routing = RuleSet::cast(fw->getFirstByType(Routing::TYPENAME));
|
|
if (routing)
|
|
{
|
|
r->setSourceRuleSet(routing);
|
|
r->setRuleSetName(routing->getName());
|
|
|
|
if (inTestMode()) r->setTestMode();
|
|
if (inEmbeddedMode()) r->setEmbeddedMode();
|
|
r->setSingleRuleCompileMode(single_rule_id);
|
|
r->setDebugLevel( dl );
|
|
if (rule_debug_on) r->setDebugRule( drp );
|
|
r->setVerbose( verbose );
|
|
|
|
if ( r->prolog() > 0 )
|
|
{
|
|
r->compile();
|
|
r->epilog();
|
|
} else
|
|
info(" Nothing to compile in Routing");
|
|
}
|
|
|
|
if (haveErrorsAndWarnings())
|
|
{
|
|
all_errors.push_front(getErrors("").c_str());
|
|
}
|
|
|
|
system_configuration_script = oscnf->getCompiledScript();
|
|
policy_script = c->getCompiledScript();
|
|
nat_script = n->getCompiledScript();
|
|
routing_script = r->getCompiledScript();
|
|
|
|
if (c->haveErrorsAndWarnings())
|
|
all_errors.push_back(c->getErrors("C ").c_str());
|
|
if (n->haveErrorsAndWarnings())
|
|
all_errors.push_back(n->getErrors("N ").c_str());
|
|
if (r->haveErrorsAndWarnings())
|
|
all_errors.push_back(r->getErrors("R ").c_str());
|
|
|
|
script_buffer = assembleFwScript(
|
|
cluster, fw, !cluster_id.empty(), oscnf.get());
|
|
|
|
if (single_rule_compile_on)
|
|
{
|
|
return //all_errors.join("\n").toStdString() +
|
|
policy_script + nat_script + routing_script;
|
|
}
|
|
|
|
QFileInfo finfo(ofname);
|
|
if (finfo.isRelative())
|
|
{
|
|
// if ofname is relative, it is relative to the
|
|
// directory the program started in, which can be
|
|
// different from wdir and different from the current dir
|
|
// at this point because we do chdir to the directory
|
|
// defined by the -d command line option
|
|
QFileInfo new_finfo(start_current_dir, ofname);
|
|
ofname = new_finfo.absoluteFilePath();
|
|
}
|
|
|
|
info("Output file name: " + ofname.toStdString());
|
|
QFile fw_file(ofname);
|
|
if (fw_file.open(QIODevice::WriteOnly))
|
|
{
|
|
QTextStream fw_str(&fw_file);
|
|
fw_str << script_buffer;
|
|
fw_file.close();
|
|
fw_file.setPermissions(QFile::ReadOwner | QFile::WriteOwner |
|
|
QFile::ReadGroup | QFile::ReadOther |
|
|
QFile::ExeOwner |
|
|
QFile::ExeGroup |
|
|
QFile::ExeOther );
|
|
|
|
info(" Compiled successfully");
|
|
} else
|
|
{
|
|
QString err(" Failed to open file %1 for writing: %2; Current dir: %3");
|
|
abort(err.arg(fw_file.fileName()).arg(fw_file.error()).arg(QDir::current().path()).toStdString());
|
|
}
|
|
}
|
|
catch (FatalErrorInSingleRuleCompileMode &ex)
|
|
{
|
|
return getErrors("");
|
|
}
|
|
|
|
return "";
|
|
}
|
|
|
|
/*
|
|
* Sanity checks for the cluster configuration. Per ticket #606:
|
|
*
|
|
* - state sync group must have master
|
|
*
|
|
* - one interface must be marked as "dedicated failover" for failover
|
|
* group.
|
|
*
|
|
* - this interface must have failover group with members, one of
|
|
* which must be master
|
|
*
|
|
* - failover interfaces in member firewalls must have ip addresses
|
|
* which should be different but in the same subnet.
|
|
*
|
|
* - possibly another interface can be defined as "dedicated failover"
|
|
* and used in state sync group.
|
|
*
|
|
* - if second interface is used for state sync, it must have ip
|
|
* address in member firewalls (different)
|
|
*
|
|
* - addresses of the dedicated failover interfaces must belong to the
|
|
* same subnet in each pair of failover inetrfaces (failover and state sync)
|
|
*
|
|
* - failover interfaces of both members used in the failover cluster
|
|
* group of the cluster object must have the same name.
|
|
*
|
|
* - The same check should be performed in the state sync group.
|
|
*
|
|
*
|
|
*/
|
|
void CompilerDriver_pix::pixClusterConfigurationChecks(Cluster *cluster,
|
|
Firewall*)
|
|
{
|
|
if (cluster==NULL) return;
|
|
|
|
FWObjectTypedChildIterator it = cluster->findByType(StateSyncClusterGroup::TYPENAME);
|
|
StateSyncClusterGroup *state_sync_group = StateSyncClusterGroup::cast(*it);
|
|
|
|
if (state_sync_group->getStr("master_iface").empty())
|
|
{
|
|
QString err("One of the interfaces in the state synchronization group "
|
|
"must be marked as 'Master'");
|
|
abort(cluster, NULL, NULL, err.toStdString());
|
|
throw FatalErrorInSingleRuleCompileMode();
|
|
}
|
|
|
|
pixClusterGroupChecks(state_sync_group);
|
|
|
|
bool failover_group_inspected = false;
|
|
list<FWObject*> l2 = cluster->getByTypeDeep(Interface::TYPENAME);
|
|
for (list<FWObject*>::iterator i=l2.begin(); i!=l2.end(); ++i)
|
|
{
|
|
Interface *iface = dynamic_cast<Interface*>(*i);
|
|
assert(iface);
|
|
|
|
FailoverClusterGroup *failover_group =
|
|
FailoverClusterGroup::cast(
|
|
iface->getFirstByType(FailoverClusterGroup::TYPENAME));
|
|
if (failover_group)
|
|
{
|
|
for (FWObjectTypedChildIterator it =
|
|
failover_group->findByType(FWObjectReference::TYPENAME);
|
|
it != it.end(); ++it)
|
|
{
|
|
Interface *member_iface = Interface::cast(FWObjectReference::getObject(*it));
|
|
assert(member_iface);
|
|
|
|
pixClusterGroupChecks(failover_group);
|
|
|
|
if (member_iface->isDedicatedFailover())
|
|
{
|
|
failover_group_inspected = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void CompilerDriver_pix::pixClusterGroupChecks(ClusterGroup *cluster_group)
|
|
{
|
|
FWObject *cluster = cluster_group;
|
|
while (cluster && !Cluster::isA(cluster)) cluster = cluster->getParent();
|
|
|
|
FWObject *cluster_interface = NULL;
|
|
FWObject *p = cluster_group->getParent();
|
|
if (Interface::isA(p)) cluster_interface = p;
|
|
|
|
map<QString, const InetAddrMask*> addresses_and_masks;
|
|
|
|
for (FWObjectTypedChildIterator it = cluster_group->findByType(FWObjectReference::TYPENAME);
|
|
it != it.end(); ++it)
|
|
{
|
|
Interface *member_iface = Interface::cast(FWObjectReference::getObject(*it));
|
|
assert(member_iface);
|
|
FWObject *member = member_iface->getParentHost();
|
|
|
|
if (cluster_interface)
|
|
{
|
|
// check consistency of the names.
|
|
// In case of PIX the name of the cluster interface should match
|
|
// names of member interfaces
|
|
if (cluster_interface->getName() != member_iface->getName())
|
|
{
|
|
QString err("Names of interfaces used in state synchronization "
|
|
"or failover group must match the name of the "
|
|
"cluster inetrface. Interface %1:%2 has the name "
|
|
"that is different from the cluster interface name %3");
|
|
|
|
abort(cluster, NULL, NULL,
|
|
err.arg(member->getName().c_str())
|
|
.arg(member_iface->getName().c_str())
|
|
.arg(cluster_interface->getName().c_str()).toStdString());
|
|
throw FatalErrorInSingleRuleCompileMode();
|
|
}
|
|
}
|
|
|
|
if (StateSyncClusterGroup::isA(cluster_group) &&
|
|
!member_iface->isDedicatedFailover())
|
|
{
|
|
QString err("Interface %1 is used in a state synchronization "
|
|
"but is not marked as 'Dedicated Failover' "
|
|
"interface. All interfaces used for the state "
|
|
"synchronization or failover must be marked "
|
|
"'Dedicated Failover'. ");
|
|
|
|
abort(member, NULL, NULL,
|
|
err.arg(member_iface->getName().c_str()).toStdString());
|
|
throw FatalErrorInSingleRuleCompileMode();
|
|
}
|
|
|
|
if (!member_iface->isRegular() || member_iface->countInetAddresses(true)==0)
|
|
{
|
|
QString err("Interface %1 which is used in state synchronization "
|
|
"or failover group does not have an IP address. "
|
|
"All interfaces used for the state "
|
|
"synchronization or failover must have ip addresses.");
|
|
|
|
abort(member, NULL, NULL,
|
|
err.arg(member_iface->getName().c_str()).toStdString());
|
|
throw FatalErrorInSingleRuleCompileMode();
|
|
}
|
|
QString key("%1:%2");
|
|
|
|
FWObjectTypedChildIterator it_addr = member_iface->findByType(IPv4::TYPENAME);
|
|
IPv4* addr = IPv4::cast(*it_addr);
|
|
addresses_and_masks[key.arg(member->getName().c_str()).arg(member_iface->getName().c_str())] =
|
|
addr->getInetAddrMaskObjectPtr();
|
|
}
|
|
|
|
if (addresses_and_masks.size() >= 2)
|
|
{
|
|
QString first_key;
|
|
const InetAddr *first_network_addr = NULL;
|
|
map<QString, const InetAddrMask*>::iterator it;
|
|
for (it=addresses_and_masks.begin(); it!=addresses_and_masks.end(); ++it)
|
|
{
|
|
QString key = it->first;
|
|
const InetAddrMask *am = it->second;
|
|
if (first_network_addr == NULL)
|
|
{
|
|
first_key = key;
|
|
first_network_addr = am->getNetworkAddressPtr();
|
|
} else
|
|
{
|
|
const InetAddr *network_addr = am->getNetworkAddressPtr();
|
|
if (*first_network_addr != *(network_addr))
|
|
{
|
|
QString err("Interfaces used in state synchronization "
|
|
"or failover group must have IP addresses on "
|
|
"the same subnet. Interfaces %1 and %2 have "
|
|
"addresses on different subnets: %3 , %4");
|
|
|
|
abort(cluster, NULL, NULL,
|
|
err.arg(first_key).arg(key)
|
|
.arg(first_network_addr->toString().c_str())
|
|
.arg(network_addr->toString().c_str()).toStdString());
|
|
throw FatalErrorInSingleRuleCompileMode();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|