1
0
mirror of https://github.com/fwbuilder/fwbuilder synced 2025-06-15 14:47:52 +02:00
fwbuilder/src/pix/pix.cpp
2008-10-09 05:08:41 +00:00

675 lines
20 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 "../../config.h"
#include <fstream>
#include <iostream>
#include <sstream>
#include <map>
#include <algorithm>
#include <functional>
#ifdef _WIN32
# include <direct.h>
#else
# include <unistd.h>
#endif
#include <time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <ctype.h>
#include <assert.h>
#include <cstring>
#include "PolicyCompiler_pix.h"
#include "NATCompiler_pix.h"
#include "OSConfigurator_pix_os.h"
#include "fwcompiler/Preprocessor.h"
#include "fwbuilder/Resources.h"
#include "fwbuilder/FWObjectDatabase.h"
#include "fwbuilder/Firewall.h"
#include "fwbuilder/Interface.h"
#include "fwbuilder/IPv4.h"
#include "fwbuilder/XMLTools.h"
#include "fwbuilder/FWException.h"
#include "fwbuilder/Tools.h"
#ifdef HAVE_GETOPT_H
#include <getopt.h>
#else
#ifdef _WIN32
#include <getopt.h>
#else
#include <stdlib.h>
#endif
#endif
#include "../common/init.cpp"
using namespace std;
using namespace libfwbuilder;
using namespace fwcompiler;
static const char *filename = NULL;
static const char *wdir = NULL;
static const char *fwobjectname = NULL;
static string ofname = "";
static int dl = 0;
static int drp = -1;
static int drn = -1;
static int verbose = 0;
static int test_mode = 0;
static int only_print_inspection_code = 0;
static bool fw_by_id = false;
FWObjectDatabase *objdb = NULL;
class UpgradePredicate: public XMLTools::UpgradePredicate
{
public:
virtual bool operator()(const string &msg) const
{
msg.size(); // to make compiler happy about unused parameter
cout << "Data file has been created in the old version of Firewall Builder.\nLoad it in the GUI to convert it to the new version." << endl;
return false;
}
};
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;
}
};
void usage(const char *name)
{
cout << "Firewall Builder: policy compiler for Cisco PIX firewall (with support for FWSM)" << endl;
cout << "Copyright 2002-2004 NetCitadel, LLC" << endl;
cout << "Version " << VERSION << "-" << RELEASE_NUM << endl;
cout << "Usage: " << name << " [-tvV] [-f filename.xml] [-d destdir] [-o output.fw] firewall_object_name" << endl;
}
int main(int argc, char * const * argv)
{
if (argc<=1)
{
usage(argv0.c_str());
exit(1);
}
int opt;
while( (opt=getopt(argc,argv,"x:ivVf:d:r:tLo:I")) != EOF )
{
switch(opt)
{
case 'i':
fw_by_id = true;
break;
case 'I':
only_print_inspection_code++;
break;
case 'd':
wdir = strdup(optarg);
break;
case 'r':
respath = string(optarg);
break;
case 'f':
filename = strdup(optarg);
break;
case 'o':
ofname = string(optarg);
break;
case 'x':
if (*optarg=='p') {
++optarg;
drp = atoi(optarg);
} else {
if (*optarg=='n') {
++optarg;
drn = atoi(optarg);
} else {
if (isdigit(*optarg)) dl=atoi(optarg); // increase debug level
else {
usage(argv[0]);
exit(1);
}
}
}
break;
case 't':
test_mode++;
break;
case 'v':
verbose++;
break;
case 'V':
usage(argv[0]);
exit(1);
case 'h':
usage(argv[0]);
exit(1);
}
}
/* can use init and init2 only after command line option "-r" has been read */
init(argv);
if((argc-1) != optind)
{
usage(argv[0]);
exit(1);
}
fwobjectname = strdup( argv[optind++] );
if (filename==NULL || fwobjectname==NULL)
{
usage(argv[0]);
exit(1);
}
if (wdir==0) wdir="./";
if (
#ifdef _WIN32
_chdir(wdir)
#else
chdir(wdir)
#endif
) {
cerr << "Can't change working directory to: " << wdir << endl;
exit(1);
}
if (test_mode)
cout << "*** Running in test mode, all errors are ignored" << endl << endl;
try
{
new Resources(respath+FS_SEPARATOR+"resources.xml");
/* create database */
objdb = new FWObjectDatabase();
/* load the data file */
UpgradePredicate upgrade_predicate;
if (verbose) cout << " *** Loading data ...";
#if 0
objdb->setReadOnly( false );
objdb->load( sysfname, &upgrade_predicate, librespath);
objdb->setFileName("");
FWObjectDatabase *ndb = new FWObjectDatabase();
ndb->load(filename, &upgrade_predicate, librespath);
objdb->merge(ndb, NULL);
delete ndb;
objdb->setFileName(filename);
#endif
objdb->setReadOnly( false );
objdb->load( filename, &upgrade_predicate, librespath);
objdb->setFileName(filename);
objdb->reIndex();
if (verbose) cout << " done\n";
/*
* some general sanity checks first
*/
Firewall* fw;
if (fw_by_id)
{
// fwobjectname is actually object id
fw = Firewall::cast(
objdb->findInIndex(objdb->getIntId(fwobjectname)));
fwobjectname = fw->getName().c_str();
}
else
fw = objdb->findFirewallByName(fwobjectname);
if (ofname.empty())
ofname=string(fwobjectname)+".fw";
FWOptions* options=fw->getOptionsObject();
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);
}
if (only_print_inspection_code)
{
OSConfigurator_pix_os *oscnf=NULL;
oscnf = new OSConfigurator_pix_os(objdb , fwobjectname, false);
oscnf->prolog();
cout << oscnf->getProtocolInspectionCommands();
return 0;
}
Helper helper(NULL);
multimap<string, FWObject*> netzone_objects;
std::list<FWObject*> l2=fw->getByType(Interface::TYPENAME);
for (std::list<FWObject*>::iterator i=l2.begin(); i!=l2.end(); ++i)
{
Interface *iface=dynamic_cast<Interface*>(*i);
assert(iface);
/*
* missing labels on interfaces
*/
if (iface->getLabel()=="")
{
string lbl;
if (iface->getSecurityLevel()==0) lbl="outside";
else
{
if (iface->getSecurityLevel()==100) lbl="inside";
else
{
char s[64];
sprintf(s,"dmz%d",iface->getSecurityLevel());
lbl=s;
}
}
iface->setLabel(lbl);
}
if ( iface->isDyn())
{
list<FWObject*> l3=iface->getByType(IPv4::TYPENAME);
if (l3.size()>0)
{
char errstr[256];
for (list<FWObject*>::iterator j=l3.begin(); j!=l3.end(); ++j)
if ( objdb->findAllReferences(*j).size()!=0 )
{
sprintf(errstr,
_("Dynamic interface %s has an IP address that is used in the firewall policy rule.\n"),
iface->getName().c_str() );
throw FWException(errstr);
}
sprintf(errstr,
_("Dynamic interface %s should not have an IP address object attached to it. This IP address object will be ignored.\n"),
iface->getName().c_str() );
cerr << errstr;
for (list<FWObject*>::iterator j=l3.begin(); j!=l3.end(); ++j)
iface->remove(*j);
}
}
/*
* no address
*/
if (iface->isRegular())
{
FWObject *ipv4=iface->getFirstByType(IPv4::TYPENAME);
if (ipv4==NULL)
throw FWException(
"Interface "+
iface->getName()+" ("+iface->getLabel()+") has no address." );
}
/*
* there shouldn't be two interfaces with the same security level
*/
for (std::list<FWObject*>::iterator j=l2.begin(); j!=l2.end(); ++j)
{
Interface *iface2=dynamic_cast<Interface*>(*j);
assert(iface2);
if (iface->getId()==iface2->getId()) continue;
if (iface->getSecurityLevel()==iface2->getSecurityLevel())
throw FWException(
"Security level of each interface should be unique, however interfaces "+
iface->getName()+" ("+iface->getLabel()+")"+
" and "+
iface2->getName()+" ("+iface2->getLabel()+")"+
" have the same security level."
);
}
/*
* in PIX, we need network zones to be defined for all interfaces
*/
string netzone_id=iface->getStr("network_zone");
if (netzone_id=="") {
throw FWException(
"Network zone definition is missing for interface "
+iface->getName()+" ("+iface->getLabel()+")");
}
FWObject *netzone=objdb->findInIndex(
FWObjectDatabase::getIntId(netzone_id));
if (netzone==NULL)
{
throw FWException(
"Network zone points at nonexisting object for interface "
+iface->getName()+" ("+iface->getLabel()+")");
}
/*
* 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_no_cache(netzone,ol);
FWObject *nz=objdb->create(ObjectGroup::TYPENAME);
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)
throw FWException(
"Object "+l->second->getName()
+" is used more than once in network zone of interface '"
+k->first+"'");
else
throw FWException(
"Object "+l->second->getName()
+" is used more than once in network zones of the following interfaces: '"
+k->first+"' and '"+l->first+"'");
}
}
}
/*
* 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() );
*/
char timestr[256];
time_t tm;
tm=time(NULL);
strcpy(timestr,ctime(&tm));
timestr[ strlen(timestr)-1 ]='\0';
#ifdef _WIN32
char* user_name=getenv("USERNAME");
#else
char* user_name=getenv("USER");
#endif
if (user_name==NULL)
throw FWException("Can't figure out your user name, aborting");
Preprocessor* prep=new Preprocessor(objdb , fwobjectname, false);
prep->compile();
/*
* Process firewall options, build OS network configuration script
*/
OSConfigurator *oscnf=NULL;
oscnf = new OSConfigurator_pix_os(objdb , fwobjectname, false);
oscnf->prolog();
oscnf->processFirewallOptions();
/* create compilers and run the whole thing */
NATCompiler_pix *n = new NATCompiler_pix( objdb ,
fwobjectname, false, oscnf );
if (test_mode) n->setTestMode();
n->setDebugLevel( dl );
n->setDebugRule( drn );
n->setVerbose( verbose );
if ( n->prolog() > 0 ) {
n->compile();
n->epilog();
} else
cout << " Nothing to compile in NAT \n" << flush;
PolicyCompiler_pix *c = new PolicyCompiler_pix( objdb ,
fwobjectname ,
false,
oscnf , n);
if (test_mode) c->setTestMode();
c->setDebugLevel( dl );
c->setDebugRule( drp );
c->setVerbose( verbose );
if ( c->prolog() > 0 ) {
c->compile();
c->epilog();
} else
cout << " Nothing to compile in Policy \n" << flush;
#ifdef _WIN32
ofstream ofile(ofname.c_str(), ios::out|ios::binary);
#else
ofstream ofile(ofname.c_str());
#endif
ofile << "!\n\
! This is automatically generated file. DO NOT MODIFY !\n\
!\n\
! Firewall Builder fwb_pix v" << VERSION << "-" << RELEASE_NUM << " \n\
!\n\
! Generated " << timestr
<< " "
<< tzname[0]
<< " by "
<< user_name;
ofile << endl;
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");
ofile << "!" << endl;
ofile << "!"
<< " Compiled for " << platform << " " << vers << endl;
ofile << "!"
<< " Outbound ACLs " << string((outbound_acl_supported)?"supported":"not supported")
<< endl;
if (!outbound_acl_supported)
{
ofile << "!"
<< " Emulate outbound ACLs: " << string((emulate_outb_acls)?"yes":"no")
<< endl;
}
ofile << "!"
<< " Generating outbound ACLs: " << string((generate_outb_acls)?"yes":"no")
<< endl;
ofile << "!"
<< " Assume firewall is part of 'any': " << string((afpa)?"yes":"no")
<< endl;
ofile << "!" << endl;
ofile << "!" << MANIFEST_MARKER << "* " << ofname << endl;
ofile << "!" << endl;
ofile << endl;
ofile << "!" << endl;
ofile << "! Prolog script:" << endl;
ofile << "!" << endl;
string pre_hook= fw->getOptionsObject()->getStr("pix_prolog_script");
ofile << pre_hook << endl;
ofile << "!" << endl;
ofile << "! End of prolog script:" << endl;
ofile << "!" << endl;
ofile << oscnf->getCompiledScript();
ofile << endl;
if (c->haveErrorsAndWarnings())
{
ofile << "! Policy compiler errors and warnings:"
<< endl;
ofile << c->getErrors("! ");
}
ofile << c->getCompiledScript();
ofile << endl;
if (n->haveErrorsAndWarnings())
{
ofile << "! NAT compiler errors and warnings:"
<< endl;
ofile << n->getErrors("! ");
}
ofile << n->getCompiledScript();
ofile << endl;
ofile << "!" << endl;
ofile << "! Epilog script:" << endl;
ofile << "!" << endl;
string post_hook= fw->getOptionsObject()->getStr("pix_epilog_script");
ofile << post_hook << endl;
ofile << endl;
ofile << "! End of epilog script:" << endl;
ofile << "!" << endl;
ofile.close();
cout << _(" Compiled successfully") << endl << flush;
} catch(libfwbuilder::FWException &ex) {
cerr << ex.toString() << endl;
return 1;
} catch (std::string s) {
cerr << s << endl;
return 1;
// } catch (std::exception ex) {
// cerr << "exception: " << ex.what() << endl;
// return 1;
// } catch (...) {
// cerr << "Unsupported exception";
// return 1;
}
return 0;
}