# HG changeset patch # User carl # Date 1133486257 28800 # Node ID 2737ab01659a06153b0d63ea02956841b7c15e1b # Parent 8fe310e5cd44e1b823c9a75c43d0d66c72cea54e initial coding diff -r 8fe310e5cd44 -r 2737ab01659a TODO --- 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. diff -r 8fe310e5cd44 -r 2737ab01659a src/syslog2iptables.cpp --- 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 #include #include -#include -#include #include /* header for waitpid() and various macros */ #include /* 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 { diff -r 8fe310e5cd44 -r 2737ab01659a src/syslogconfig.cpp --- 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 #include #include +#include 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 ip_buckets; +typedef map 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 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); } diff -r 8fe310e5cd44 -r 2737ab01659a src/syslogconfig.h --- 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 #include +#include +#include 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); }; diff -r 8fe310e5cd44 -r 2737ab01659a syslog2iptables.conf --- 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;