changeset 4:2737ab01659a

initial coding
author carl
date Thu, 01 Dec 2005 17:17:37 -0800
parents 8fe310e5cd44
children 276c4edc8521
files TODO src/syslog2iptables.cpp src/syslogconfig.cpp src/syslogconfig.h syslog2iptables.conf
diffstat 5 files changed, 155 insertions(+), 66 deletions(-) [+]
line wrap: on
line diff
--- a/TODO	Sun Nov 27 21:12:01 2005 -0800
+++ b/TODO	Thu Dec 01 17:17:37 2005 -0800
@@ -0,0 +1,5 @@
+1) Configurable limit on size of drop table
+2) If size exceeded, mail to root but not more often than once per 6 hours.
+   and only use the larger count items?
+3) Configure an export file and command to allow pushing the filters
+   to other systems.
--- a/src/syslog2iptables.cpp	Sun Nov 27 21:12:01 2005 -0800
+++ b/src/syslog2iptables.cpp	Thu Dec 01 17:17:37 2005 -0800
@@ -30,8 +30,6 @@
 #include <unistd.h>
 #include <pthread.h>
 #include <syslog.h>
-#include <sys/types.h>
-#include <sys/stat.h>
 #include <sys/wait.h>	/* header for waitpid() and various macros */
 #include <signal.h> 	/* header for signal functions */
 #include "includes.h"
@@ -196,14 +194,34 @@
 	if (check) {
 		use_syslog	 = false;
 		debug_syslog = 10;
-		CONFIG *conf = new_conf();
-		if (conf) {
-			conf->dump();
-			for (int i=0; i<2; i++) {
-				conf->read();
-				conf->sleep(5);
+		config = new_conf();
+		if (config) {
+			config->dump();
+			{
+				// just for testing
+				// initialize the thread sync objects
+				pthread_mutex_init(&config_mutex, 0);
+				pthread_mutex_init(&syslog_mutex, 0);
+
+				time_t t = time(NULL);
+				CONFIG *c;
+				pthread_mutex_lock(&config_mutex);
+					c = config;
+					c->reference_count++;
+				pthread_mutex_unlock(&config_mutex);
+				while (true) {
+					if (c != config) {
+						pthread_mutex_lock(&config_mutex);
+							c->reference_count--;
+							c = config;
+							c->reference_count++;
+						pthread_mutex_unlock(&config_mutex);
 			}
-			delete conf;
+					c->read();
+					c->sleep(10, t);
+				}
+			}
+			delete config;
 			return 0;
 		}
 		else {
--- a/src/syslogconfig.cpp	Sun Nov 27 21:12:01 2005 -0800
+++ b/src/syslogconfig.cpp	Thu Dec 01 17:17:37 2005 -0800
@@ -24,8 +24,10 @@
 #include <netinet/in.h>
 #include <arpa/inet.h>
 #include <netdb.h>
+#include <limits.h>
 
 static char* syslogconfig_version="$Id$";
+static char* iptables = "/sbin/iptables";
 
 char *token_bucket;
 char *token_file;
@@ -47,14 +49,19 @@
   }
 };
 
+struct bucket {
+	int  count;
+	bool latch; // true iff ever count>threshold
+};
+
 string_set	all_strings;	// owns all the strings, only modified by the config loader thread
 const int maxlen = 1000;	// used for snprintf buffers
-typedef map<int, int, ltint>   ip_buckets;
+typedef map<int, bucket, ltint>   ip_buckets;
 
 class IPR {
 	ip_buckets	violations;
 public:
-	void add(int ip, int bucket, CONFIG &con);
+	void add(int ip, int amount, CONFIG &con);
 	void changed(CONFIG &con);
 	void leak(int amount, CONFIG &con);
 };
@@ -64,13 +71,26 @@
 
 ////////////////////////////////////////////////
 //
-void IPR::add(int ip, int bucket, CONFIG &con) {
+void IPR::add(int ip, int amount, CONFIG &con) {
 	if (con.looking(ip)) {
 		ip_buckets::iterator i = violations.find(ip);
-		if (i == violations.end()) violations[ip] = bucket;
+		if (i == violations.end()) {
+			bucket b;
+			b.count = amount;
+			b.latch = false;
+			violations[ip] = b;
+		}
 		else {
-			(*i).second += bucket;
-			if ((*i).second > con.get_threshold()) changed(con);
+			bucket &b = (*i).second;
+			if (b.count < (INT_MAX-amount)) {
+				int t = con.get_threshold();
+				int c = b.count;
+				b.count += amount;
+				if ((!b.latch) && (c < t) && (t <= b.count)) {
+					b.latch = true;
+					changed(con);
+				}
+			}
 		}
 	}
 }
@@ -80,18 +100,18 @@
 	bool ch = false;
 	for (ip_buckets::iterator i=violations.begin(); i!=violations.end(); ) {
 		int ip = (*i).first;
-		int n  = (*i).second;
-		in_addr ad;
-		ad.s_addr = htonl(ip);
-		char buf[maxlen];
-		snprintf(buf, maxlen, "leak %s with %d count", inet_ntoa(ad), n);
-		my_syslog(buf);
-		if (n <= amount) {
-			ch = true;
+		bucket &b = (*i).second;
+	 // in_addr ad;
+	 // ad.s_addr = htonl(ip);
+	 // char buf[maxlen];
+	 // snprintf(buf, maxlen, "leak %s with %d count", inet_ntoa(ad), n);
+	 // my_syslog(buf);
+		if (b.count <= amount) {
+			ch |= b.latch;
 			violations.erase(i++);
 		}
 		else {
-			(*i).second -= amount;
+			b.count -= amount;
 			i++;
 		}
 	}
@@ -100,17 +120,21 @@
 
 
 void IPR::changed(CONFIG &con) {
-	my_syslog("**** dump");
+	char buf[maxlen];
+	snprintf(buf, maxlen, "%s -F INPUT", iptables);
+	my_syslog(" ");
+	my_syslog(buf);
 	for (ip_buckets::iterator i=violations.begin(); i!=violations.end(); i++) {
 		int ip = (*i).first;
-		int n  = (*i).second;
+		bucket &b = (*i).second;
+		if (b.count > con.get_threshold()) {
 		in_addr ad;
 		ad.s_addr = htonl(ip);
-		char buf[maxlen];
-		snprintf(buf, maxlen, "%s with %d count", inet_ntoa(ad), n);
+			snprintf(buf, maxlen, "count=%d %s -A INPUT --src %s --jump DROP", b.count, iptables, inet_ntoa(ad));
 		my_syslog(buf);
 	}
 }
+}
 
 
 ////////////////////////////////////////////////
@@ -130,10 +154,10 @@
 
 ////////////////////////////////////////////////
 //
-PATTERN::PATTERN(TOKEN &tok, char *pattern_, int index_, int bucket_) {
+PATTERN::PATTERN(TOKEN &tok, char *pattern_, int index_, int amount_) {
 	pattern = pattern_;
 	index	= index_;
-	bucket	= bucket_;
+	amount	= amount_;
 	if (pattern) {
 		int rc = regcomp(&re, pattern, REG_ICASE | REG_EXTENDED);
 		if (rc) {
@@ -164,12 +188,10 @@
 		//	snprintf(bu, maxlen, "re match from %d to %d", s, e);
 		//	my_syslog(bu);
 			if (s != -1) {
-				my_syslog(buf);
 				buf[e] = '\0';
 				int ip = ip_address(buf+s);
 				if (ip) {
-					my_syslog(buf+s);
-					recorder.add(ip, bucket, con);
+					recorder.add(ip, amount, con);
 				}
 				return true;
 			}
@@ -186,7 +208,7 @@
 	indent[i] = '\0';
 	printf("%s pattern \"%s\" {; \n", indent, pattern);
 	printf("%s     index %d; \n", indent, index);
-	printf("%s     bucket %d; \n", indent, bucket);
+	printf("%s     bucket %d; \n", indent, amount);
 	printf("%s }; \n", indent);
 }
 
@@ -245,15 +267,16 @@
 			SYSLOGCONFIGP c = *i;
 			have |= c->read(*this);
 		}
-		break;	//!!
 		if (!have) break;
 	}
 }
 
 
-void CONFIG::sleep(int duration) {
+void CONFIG::sleep(int duration, time_t &previous) {
 	::sleep(duration);
-	recorder.leak(duration, *this);
+	time_t now = time(NULL);
+	recorder.leak(now-previous, *this);
+	previous = now;
 }
 
 
@@ -268,23 +291,14 @@
 ////////////////////////////////////////////////
 //
 SYSLOGCONFIG::SYSLOGCONFIG(TOKEN &tok, char *file_name_) {
+	tokp	  = &tok;
 	file_name = file_name_;
-	fd		  = open(file_name, O_RDONLY);
-	len 	  = 0;
-	if (fd == -1) {
-		char buf[maxlen];
-		snprintf(buf, sizeof(buf), "syslog file %s not readable", file_name);
-		tok.token_error(buf);
-	}
-	else {
-		//lseek(fd, 0, SEEK_END);	//!!
-	}
+	open(true);
 }
 
 
 SYSLOGCONFIG::~SYSLOGCONFIG() {
-	if (fd != -1) close(fd);
-	fd = -1;
+	close();
 	for (pattern_list::iterator i=patterns.begin(); i!=patterns.end(); i++) {
 		PATTERN *p = *i;
 		delete p;
@@ -292,13 +306,33 @@
 }
 
 
-void SYSLOGCONFIG::add_pattern(PATTERNP pat) {
-	patterns.push_back(pat);
+void SYSLOGCONFIG::open(bool msg) {
+	fd		  = ::open(file_name, O_RDONLY);
+	len 	  = 0;
+	if (fd == -1) {
+		if (msg) {
+			char buf[maxlen];
+			snprintf(buf, sizeof(buf), "syslog file %s not readable", file_name);
+			tokp->token_error(buf);
+		}
+	}
+	else {
+		lseek(fd, 0, SEEK_END);
+		if (fstat(fd, &openfdstat)) {
+			close();
+			char buf[maxlen];
+			snprintf(buf, sizeof(buf), "syslog file %s cannot stat after open", file_name);
+			tokp->token_error(buf);
+		}
+	}
 }
 
 
 bool SYSLOGCONFIG::read(CONFIG &con) {
+	if (failed()) {
+		open(false);
 	if (failed()) return false;
+	}
 	int n = ::read(fd, buf+len, buflen-len);
 	bool have = (n > 0);
 	if (have) {
@@ -315,10 +349,36 @@
 		// no <lf> in a full buffer
 		if (len == buflen) len = 0;
 	}
+	else {
+		// check for file close
+		struct stat filenamest;
+		if (0 == stat(file_name, &filenamest)) {
+			if ((filenamest.st_dev != openfdstat.st_dev) ||
+				(filenamest.st_ino != openfdstat.st_ino)) {
+				close();
+			}
+		}
+		else {
+			// filename no longer exists
+			close();
+		}
+
+	}
 	return have;
 }
 
 
+void SYSLOGCONFIG::close() {
+	if (fd != -1) ::close(fd);
+	fd = -1;
+}
+
+
+void SYSLOGCONFIG::add_pattern(PATTERNP pat) {
+	patterns.push_back(pat);
+}
+
+
 void SYSLOGCONFIG::process(CONFIG &con) {
 	for (pattern_list::iterator i=patterns.begin(); i!=patterns.end(); i++) {
 		PATTERN *p = *i;
@@ -336,7 +396,7 @@
 	printf("%s file \"%s\" {\n", indent, file_name);
 	for (pattern_list::iterator i=patterns.begin(); i!=patterns.end(); i++) {
 		PATTERN *p = *i;
-		p->dump(level);
+		p->dump(level+1);
 	}
 	printf("%s }; \n", indent);
 }
--- a/src/syslogconfig.h	Sun Nov 27 21:12:01 2005 -0800
+++ b/src/syslogconfig.h	Thu Dec 01 17:17:37 2005 -0800
@@ -24,6 +24,8 @@
 #include "tokenizer.h"
 #include <map>
 #include <regex.h>
+#include <sys/types.h>
+#include <sys/stat.h>
 
 
 class SYSLOGCONFIG;
@@ -39,10 +41,10 @@
 	char *			pattern;	// owned by the string table
 	regex_t 		re;
 	int 			index;		// zero based substring of the regex match that contains the ip address or hostname
-	int 			bucket; 	// count to add to the ip address leaky bucket
+	int 			amount; 	// count to add to the ip address leaky bucket
 public:
 	~PATTERN();
-	PATTERN(TOKEN &tok, char *pattern_, int index_, int bucket_);
+	PATTERN(TOKEN &tok, char *pattern_, int index_, int amount_);
 	bool	process(char *buf, CONFIG &con);
 	void	dump(int level);
 };
@@ -55,17 +57,21 @@
 const int buflen = 1024;
 
 class SYSLOGCONFIG {
+	TOKEN * 		tokp;
 	char *			file_name;	// name of the syslog file
 	pattern_list	patterns;	// owns the patterns
 	int 			fd;
+	struct stat 	openfdstat;
 	int 			len;		// bytes in the buffer
 	char			buf[buflen];
 public:
 	SYSLOGCONFIG(TOKEN &tok, char *file_name_);
 	~SYSLOGCONFIG();
+	bool	failed()	{ return (fd == -1); };
+	void	open(bool msg);
+	bool	read(CONFIG &con);
+	void	close();
 	void	add_pattern(PATTERNP pat);
-	bool	failed()	{ return (fd == -1); };
-	bool	read(CONFIG &con);
 	void	process(CONFIG &con);
 	void	dump(int level);
 };
@@ -90,7 +96,7 @@
 	void	add_pair(IPPAIR pair);
 	void	dump();
 	void	read();
-	void	sleep(int duration);
+	void	sleep(int duration, time_t &previous);
 	bool	looking(int ip);
 };
 
--- a/syslog2iptables.conf	Sun Nov 27 21:12:01 2005 -0800
+++ b/syslog2iptables.conf	Thu Dec 01 17:17:37 2005 -0800
@@ -7,14 +7,14 @@
     205.147.39.128/25;  // ams
 };
 
-file "/var/log/cisco.log" {
+file "mycisco.log" {
     pattern "Internet_Firewall denied (tcp|udp) ([^(]*)" {
         index 2;    // zero based
         bucket 200;
     };
 };
 
-file "messages.log" {
+file "mymessages.log" {
     pattern "sshd.pam_unix.*authentication failure.*rhost=(.*) user=" {
         index 1;    // zero based
         bucket 300;