"Fossies" - the Fresh Open Source Software archive

Member "bwm_tools-0.3.0/bwm_firewall/bwm_firewall.c" of archive bwm_tools-0.3.0.tar.gz:


/*
 * 	bwm_firewall.c - BWM Firewall
 * 	Copyright (C) 2003-2006, Linux Based Systems Design
 *
 * 	This program is free software; you can redistribute it and/or modify
 * 	it under the terms of the GNU General Public 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.
 * 	
 * 	You should have received a copy of the GNU General Public License
 * 	along with this program; if not, write to the Free Software
 * 	Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 *
 *  25/05/2003 - Nigel Kukard  <nkukard@lbsd.net>
 *      * Initial design
*/


#include <getopt.h>
#include <glib.h>
#include <errno.h>
#include <fcntl.h>
#include <libxml/parser.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <time.h>
#include <unistd.h>
#include "../config.h"
#include "common.h"
#include "xmlConf.h"



// return current date string like
// Sun Jan  2 21:52:25 2005
static char *date2str(char *buffer)
{
	time_t t = time(NULL);
	sprintf(buffer, "%s", ctime(&t));
	return buffer;
}


// Function to build a list of rules which iptables-restore takes
static GList *createFirewallRules(char *filename, char resetCounters) 
{
	xmlDocPtr doc;
    	xmlNodePtr cur;
	GHashTable *classHash = NULL;
	GHashTable *fwHash = NULL;
	GList *result = NULL;
	struct confACLTable_t *tmpTable; 
	struct confACLChain_t *aChain; 
	char *tableList[] = {"filter","mangle",NULL};
	char *tableName;
	int i;
	char counters[7] = "";
	struct stat fstat;
	

	// Loop with a table	
	void processTable(gpointer p_key, gpointer p_value, gpointer p_user_data)
	{
		char *aRule;
		char *tableName = (char *) p_key;
		struct confACLTable_t *table = (struct confACLTable_t*) p_value;
		
		
		// Create chains...
		void processChains(gpointer p1_key, gpointer p1_value, gpointer p1_user_data)
		{
			struct confACLChain_t *chain = (struct confACLChain_t*) p1_value;
			
			
			aRule = (char *) malloc0(BUFFER_SIZE);

			// Check if we have a default target for the chain or not	
			if (chain->defaultTarget)	
				snprintf(aRule,BUFFER_SIZE,":%s %s%s\n",chain->name,chain->defaultTarget,counters);
			else
				snprintf(aRule,BUFFER_SIZE,":%s -%s\n",chain->name,counters);

			result = g_list_append(result,aRule);		
		}
			
		// Print out rules
		void processRules(gpointer p1_key, gpointer p1_value, gpointer p1_user_data)
		{
			struct confACLChain_t *chain = (struct confACLChain_t*) p1_value;
			
			
			// And each item therein
			void processRuleset(gpointer data, gpointer user_data)
			{
				char *rule = (char *) data;
				
				
				aRule = (char *) malloc0(BUFFER_SIZE);
				snprintf(aRule,BUFFER_SIZE,"%s\n",rule);
				result = g_list_append(result,aRule);
			}
		
			// Loop with all the ruleset items	
			g_list_foreach(chain->ruleset,processRuleset,NULL);
		}

		
		aRule = (char *) malloc0(BUFFER_SIZE);
		snprintf(aRule,BUFFER_SIZE,"*%s\n",tableName);
		result = g_list_append(result,aRule);
		
		// First generate chain names
		g_hash_table_foreach(table->chains,processChains,NULL);
		// Then the rules for the chains
		g_hash_table_foreach(table->chains,processRules,NULL);
		
		aRule = (char *) malloc0(BUFFER_SIZE);
		snprintf(aRule,BUFFER_SIZE,"COMMIT\n");
		result = g_list_append(result,aRule);
	}


    	/* Don't test version, it displays a nasty error
	LIBXML_TEST_VERSION
	*/
	// COMPAT: Do not genrate nodes for formatting spaces
	xmlKeepBlanksDefault(0);

	/* Check if file exists */	
	if (stat(filename,&fstat) != 0)
	{
		fprintf(stderr,"ERROR: Failed to stat configuration path \"%s\": %s",filename,strerror(errno));
		return NULL;
	}
	else
		if (!S_ISREG(fstat.st_mode))
		{
			fprintf(stderr,"ERROR: Specified configuration path \"%s\" is not a regular file",filename);
			return NULL;
		}

	// Build an XML tree from a the file
	doc = xmlParseFile(filename);
    	if (doc == NULL) 
		return(NULL);

	// Check the document is of the right kind
	cur = xmlDocGetRootElement(doc);
	if (cur == NULL) 
	{
		fprintf(stderr,"ERROR: Empty document\n");
		xmlFreeDoc(doc);
		return(NULL);
	}
	
	// Check if we have the right root element & block
	if (xmlStrcmp(cur->name, (const xmlChar *) "firewall")) 
	{
		fprintf(stderr,"ERROR: Document of the wrong type, root node != firewall");
		xmlFreeDoc(doc);
		return(NULL);
	}


	// Init everything...	
	fwHash = g_hash_table_new(g_str_hash,g_str_equal);
	i = 0;
	tableName = tableList[i];
	while (tableName)
	{
		// See if we have a table by this name
		tmpTable = lookupTable(fwHash,tableName);
		// Check if we already did the chain or not
		aChain = lookupChain(tmpTable->chains,"INPUT");
		aChain->defaultTarget = "ACCEPT";
		aChain = lookupChain(tmpTable->chains,"FORWARD");
		aChain->defaultTarget = "ACCEPT";
		aChain = lookupChain(tmpTable->chains,"OUTPUT");
		aChain->defaultTarget = "ACCEPT";
		// Advance...
		i++;
		tableName = tableList[i];
	}

	// Walk the tree.
	cur = cur->xmlChildrenNode;
	while (cur) 
	{
		// Try find sections
		if (!xmlStrcmp(cur->name, (const xmlChar *) "global"))
			classHash = parseGlobal(doc,cur);
		else if (!xmlStrcmp(cur->name, (const xmlChar *) "acl"))
			parseACL(doc,cur,fwHash,classHash);
		else if (!xmlStrcmp(cur->name, (const xmlChar *) "nat"))
			parseNAT(doc,cur,fwHash,classHash);
		else if (!xmlStrcmp(cur->name, (const xmlChar *) "traffic"))
			parseTraffic(doc,cur,fwHash,classHash);
		// Make sure this isn't a comment
		else if (xmlStrcmp(cur->name, (const xmlChar *) "text"))
			fprintf(stderr,"ERROR: Section <%s> not recognised, ignoring",cur->name);
		
	   	// Next plz!! 
		cur = cur->next;
	}


	// Clean up everything else before quitting.
	xmlCleanupParser();

	/* Check if we appending zero counters */
	if (resetCounters)
		sprintf(counters,"%s"," [0:0]");

	// Build rule list
	g_hash_table_foreach(fwHash,processTable,NULL);	

	return(result);
}


// Function to write firewall to file
static int writeFirewall(GList *ruleList, char *filename, char load)
{
	int fd;
	int fuct = 0;
	char *buffer, datetime[32];
	FILE *piptables_restore = NULL;
	

	// Process rule by rule & write to file and/or pipe to iptables directly
	void writeRule(gpointer data, gpointer user_data)
	{
		int res;
		
		
		if (!fuct)
		{
			// Write to file...
			res = write(fd,data,strlen(data));
			if (res < 0)
			{
				fprintf(stderr,"Failed to write to file: %s\n",strerror(errno));
				fuct = 1;
			}
			// Check if we piping to iptables aswell
			if (piptables_restore)
			{
				fprintf(piptables_restore, "%s", (char *) data);
				// Check for IO error
				if (ferror(piptables_restore))
				{
					fprintf(stderr,"Failed to write to file: %s\n",strerror(errno));
					piptables_restore = NULL;
				}
			}
		}
	}

	// Open firewall file	
	fd = open(filename,O_CREAT|O_TRUNC|O_WRONLY,S_IREAD|S_IWRITE);
	if (fd < 0)
	{
		fprintf(stderr,"Failed to open file \"%s\": %s\n",filename,strerror(errno));
		return(-1);
	}

	// Check if we piping direct to iptables aswell
	if (load)
	{
		// iptables-restore should be in path
		piptables_restore = popen("iptables-restore", "w");
		if (!piptables_restore)
			fprintf(stderr, "ERROR: Can not find iptables-restore in $PATH, skipping load\n");
		
		printf("Loading firewall configuration directly into kernel...\n");
	}

	buffer = (char *) malloc0(BUFFER_SIZE);
	// Write out comment to say what version & at what datetime we generated the firewall
	snprintf(buffer,BUFFER_SIZE,"# Generated using BWM Firewall v%s: %s\n",PACKAGE_VERSION,
			date2str((char *) &datetime));
	write(fd,buffer,strlen(buffer));
	
	// Loop with all rules
	g_list_foreach(ruleList,writeRule,NULL);

	// Finally close file descriptor
	close(fd);

	// If we opened iptables-restore close
	if (piptables_restore)
		fclose(piptables_restore);

	return(fuct);
}


// Print out our usage
void printUsage(char **argv)
{
	printf("Usage: %s <options>\n",argv[0]);
	printf("\n");
	printf("Options:\n");
	printf("    -c,  --config=<config_file>     Specify non-default BWM Tools config file\n");
	printf("    -f,  --file[=<output_file>]     Generate iptables-restore file from BWM Tools firewall\n");
	printf("    -l,  --load                     Load BWM Tools firewall directly into kernel\n");
	printf("    -h,  --help                     Display this page\n");
	printf("    -r,  --reset-counters           Reset iptables counters, usable with \"iptables-restore -c\"\n");
	printf("\n");
}


// Obviously we need our main function
int main (int argc, char **argv)
{
	GList *fw;
	int c;
	char *configFile = CONFIG_FILE;
	char *outputFile = IPTABLES_FILE;
	char resetCounters = 0;
	int res = 1; // signal we return error status
	char writeFile = 0;
	char loadFirewall = 0;
	
	
	// Our options
	struct option long_options[] =
	{
		{"config",required_argument,0,'c'},
		{"file",optional_argument,0,'f'},
		{"load",no_argument,0,'l'},
		{"help",no_argument,0,'h'},
		{"reset-counters",no_argument,0,'r'},
		{0,0,0,0}
	};
	

	printf("BWM Firewall v%s - Copyright (c) 2003-2006 Linux Based Systems Design\n\n",PACKAGE_VERSION);

	// Check if we have no args
	if (argc == 1)
	{
		printUsage(argv);
		return(1);
	}
	
	// Loop with options
	while (1)
	{
		int option_index = 0;
		
		// Process
		c = getopt_long(argc,argv,"c:f::lhr",long_options,&option_index);
		if (c == -1)
			break;

		// Check...
		switch (c)
		{
			case 'c':
				configFile = strdup(optarg);
				break;

			case 'f':
				// If we have an option, save it
				if (optarg)
					outputFile = strdup(optarg);
				writeFile = 1;
				break;
			
			case 'l':
				loadFirewall = 1;
				break;
			
			case 'h':
				printUsage(argv);
				return(0);
				
			case 'r':
				resetCounters = 1;
				break;
		}
		
	}
	
	// Let us know about ahy unknown options
	if (optind < argc)
	{
		while (optind < argc)
			fprintf(stderr,"%s: invalid option -- %s\n",argv[0],argv[optind++]);
		return 1;
	}

	// Check if we actually going to do anything
	if (!writeFile && !loadFirewall)
	{
		printf("ERROR: No action to take\n");
		printUsage(argv);
		return 1;
	}
	
	// Load configuration
	printf("Loading configuration from \"%s\"...\n",configFile);	
	fw = createFirewallRules(configFile,resetCounters);

	// Write out firewall
	if (fw)
	{
		printf("Processing firewall/shaping configuration...\n");
		res = writeFirewall(fw,outputFile,loadFirewall);
	}
	
	// Return result code
	return res;
}