# HG changeset patch # User carl # Date 1133154721 28800 # Node ID 8fe310e5cd44e1b823c9a75c43d0d66c72cea54e # Parent 6e88da080f08d216a70856f41873681c4204a0a4 initial coding diff -r 6e88da080f08 -r 8fe310e5cd44 src/syslog2iptables.cpp --- a/src/syslog2iptables.cpp Thu Nov 24 10:31:09 2005 -0800 +++ b/src/syslog2iptables.cpp Sun Nov 27 21:12:01 2005 -0800 @@ -199,9 +199,9 @@ CONFIG *conf = new_conf(); if (conf) { conf->dump(); - for (int i=0; i<30; i++) { + for (int i=0; i<2; i++) { conf->read(); - sleep(1); + conf->sleep(5); } delete conf; return 0; diff -r 6e88da080f08 -r 8fe310e5cd44 src/syslogconfig.cpp --- a/src/syslogconfig.cpp Thu Nov 24 10:31:09 2005 -0800 +++ b/src/syslogconfig.cpp Sun Nov 27 21:12:01 2005 -0800 @@ -20,22 +20,179 @@ #include "includes.h" #include +#include +#include +#include +#include static char* syslogconfig_version="$Id$"; -char *token_cisco; +char *token_bucket; char *token_file; +char *token_ignore; char *token_include; +char *token_index; char *token_lbrace; -char *token_parser; +char *token_pattern; char *token_rbrace; char *token_semi; -char *token_ssh; +char *token_slash; +char *token_threshold; + +struct ltint +{ + bool operator()(const int s1, const int s2) const + { + return (unsigned)s1 < (unsigned)s2; + } +}; 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; + +class IPR { + ip_buckets violations; +public: + void add(int ip, int bucket, CONFIG &con); + void changed(CONFIG &con); + void leak(int amount, CONFIG &con); +}; + +IPR recorder; + + +//////////////////////////////////////////////// +// +void IPR::add(int ip, int bucket, CONFIG &con) { + if (con.looking(ip)) { + ip_buckets::iterator i = violations.find(ip); + if (i == violations.end()) violations[ip] = bucket; + else { + (*i).second += bucket; + if ((*i).second > con.get_threshold()) changed(con); + } + } +} + + +void IPR::leak(int amount, CONFIG &con) { + 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; + violations.erase(i++); + } + else { + (*i).second -= amount; + i++; + } + } + if (ch) changed(con); +} + + +void IPR::changed(CONFIG &con) { + my_syslog("**** dump"); + for (ip_buckets::iterator i=violations.begin(); i!=violations.end(); i++) { + int ip = (*i).first; + int n = (*i).second; + in_addr ad; + ad.s_addr = htonl(ip); + char buf[maxlen]; + snprintf(buf, maxlen, "%s with %d count", inet_ntoa(ad), n); + my_syslog(buf); + } +} +//////////////////////////////////////////////// +// +int ip_address(char *have); +int ip_address(char *have) { + int ipaddr = 0; + in_addr ip; + if (inet_aton(have, &ip)) ipaddr = ip.s_addr; + else { + struct hostent *host = gethostbyname(have); + if (host && host->h_addrtype == AF_INET) memcpy(&ipaddr, host->h_addr, sizeof(ipaddr)); + } + return ntohl(ipaddr); +} + + +//////////////////////////////////////////////// +// +PATTERN::PATTERN(TOKEN &tok, char *pattern_, int index_, int bucket_) { + pattern = pattern_; + index = index_; + bucket = bucket_; + if (pattern) { + int rc = regcomp(&re, pattern, REG_ICASE | REG_EXTENDED); + if (rc) { + char bu[maxlen]; + regerror(rc, &re, bu, maxlen); + char buf[maxlen]; + snprintf(buf, sizeof(buf), "pattern %s not valid - %s", pattern, bu); + tok.token_error(buf); + pattern = NULL; + } + } +} + + +PATTERN::~PATTERN() { + regfree(&re); +} + + +bool PATTERN::process(char *buf, CONFIG &con) { + if (pattern) { + const int nmatch = index+1; + regmatch_t match[nmatch]; + if (0 == regexec(&re, buf, nmatch, match, 0)) { + int s = match[index].rm_so; + int e = match[index].rm_eo; + // char bu[maxlen]; + // 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); + } + return true; + } + } + } + return false; +} + + +void PATTERN::dump(int level) { + char indent[maxlen]; + int i = min(maxlen-1, level*4); + memset(indent, ' ', i); + 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 }; \n", indent); +} + + +//////////////////////////////////////////////// +// CONFIG::CONFIG() { reference_count = 0; generation = 0; @@ -48,6 +205,7 @@ SYSLOGCONFIG *c = *i; delete c; } + ignore.clear(); } @@ -56,7 +214,23 @@ } +void CONFIG::add_pair(IPPAIR pair) { + ignore.push_back(pair); +} + + void CONFIG::dump() { + printf(" threshold %d; \n\n", threshold); + + printf(" ignore { \n"); + for (ippair_list::iterator i=ignore.begin(); i!=ignore.end(); i++) { + IPPAIR &p = *i; + in_addr ip; + ip.s_addr = htonl(p.first); + printf(" %s/%d; \n", inet_ntoa(ip), p.cidr); + } + printf(" }; \n\n"); + for (syslogconfig_list::iterator i=syslogconfigs.begin(); i!=syslogconfigs.end(); i++) { SYSLOGCONFIGP c = *i; c->dump(0); @@ -65,16 +239,36 @@ void CONFIG::read() { + while (true) { + bool have = false; for (syslogconfig_list::iterator i=syslogconfigs.begin(); i!=syslogconfigs.end(); i++) { SYSLOGCONFIGP c = *i; - c->read(); + have |= c->read(*this); + } + break; //!! + if (!have) break; } } -SYSLOGCONFIG::SYSLOGCONFIG(TOKEN &tok, char *file_name_, parser_style parser_) { +void CONFIG::sleep(int duration) { + ::sleep(duration); + recorder.leak(duration, *this); +} + + +bool CONFIG::looking(int ip) { + for (ippair_list::iterator i=ignore.begin(); i!=ignore.end(); i++) { + IPPAIR &p = *i; + if ((p.first <= ip) && (ip <= p.last)) return false; + } + return true; +} + +//////////////////////////////////////////////// +// +SYSLOGCONFIG::SYSLOGCONFIG(TOKEN &tok, char *file_name_) { file_name = file_name_; - parser = parser_; fd = open(file_name, O_RDONLY); len = 0; if (fd == -1) { @@ -83,7 +277,7 @@ tok.token_error(buf); } else { - lseek(fd, 0, SEEK_END); + //lseek(fd, 0, SEEK_END); //!! } } @@ -91,31 +285,45 @@ SYSLOGCONFIG::~SYSLOGCONFIG() { if (fd != -1) close(fd); fd = -1; + for (pattern_list::iterator i=patterns.begin(); i!=patterns.end(); i++) { + PATTERN *p = *i; + delete p; + } } -void SYSLOGCONFIG::read() { - if (failed()) return; - int n = ::read(fd, buf, buflen-len); - if (n > 0) { +void SYSLOGCONFIG::add_pattern(PATTERNP pat) { + patterns.push_back(pat); +} + + +bool SYSLOGCONFIG::read(CONFIG &con) { + if (failed()) return false; + int n = ::read(fd, buf+len, buflen-len); + bool have = (n > 0); + if (have) { len += n; while (true) { char *p = (char*)memchr(buf, '\n', len); if (!p) break; n = p-buf; *p = '\0'; - process(); // process null terminated string + process(con); // process null terminated string len -= n+1; memmove(buf, p+1, len); } // no in a full buffer if (len == buflen) len = 0; } + return have; } -void SYSLOGCONFIG::process() { - my_syslog(buf); +void SYSLOGCONFIG::process(CONFIG &con) { + for (pattern_list::iterator i=patterns.begin(); i!=patterns.end(); i++) { + PATTERN *p = *i; + if (p->process(buf, con)) break; + } } @@ -126,13 +334,9 @@ indent[i] = '\0'; char buf[maxlen]; printf("%s file \"%s\" {\n", indent, file_name); - switch (parser) { - case cisco: - printf("%s parser cisco; \n", indent); - break; - case ssh: - printf("%s parser ssh; \n", indent); - break; + for (pattern_list::iterator i=patterns.begin(); i!=patterns.end(); i++) { + PATTERN *p = *i; + p->dump(level); } printf("%s }; \n", indent); } @@ -182,38 +386,128 @@ //////////////////////////////////////////////// // -bool parse_syslogconfig(TOKEN &tok, CONFIG &dc); -bool parse_syslogconfig(TOKEN &tok, CONFIG &dc) { - char *name = tok.next(); - parser_style parser; +bool parse_pattern(TOKEN &tok, SYSLOGCONFIG &con); +bool parse_pattern(TOKEN &tok, SYSLOGCONFIG &con) { + char *pat = tok.next(); + int ind, buc; if (!tsa(tok, token_lbrace)) return false; while (true) { char *have = tok.next(); - if (have == token_parser) { + if (!have) break; + if (have == token_rbrace) break; + if (have == token_index) { have = tok.next(); - if (have == token_cisco) parser = cisco; - else if (have == token_ssh) parser = ssh; - else { - tok.token_error("cisco/ssh", have); - return false; - } + ind = atoi(have); if (!tsa(tok, token_semi)) return false; } - else if (have == token_rbrace) { - break; // done + else if (have == token_bucket) { + have = tok.next(); + buc = atoi(have); + if (!tsa(tok, token_semi)) return false; } else { - tok.token_error("file keyword", have); + tok.token_error("index/bucket", have); return false; } } if (!tsa(tok, token_semi)) return false; - SYSLOGCONFIGP con = new SYSLOGCONFIG(tok, name, parser); + PATTERNP patt = new PATTERN(tok, pat, ind, buc); + con.add_pattern(patt); + return true; +} + + +//////////////////////////////////////////////// +// +bool parse_ignore(TOKEN &tok, CONFIG &dc); +bool parse_ignore(TOKEN &tok, CONFIG &dc) { + if (!tsa(tok, token_lbrace)) return false; + while (true) { + char *have = tok.next(); + if (!have) break; + if (have == token_rbrace) break; + int ipaddr = ip_address(have); + if (ipaddr == 0) { + tok.token_error("ip address", have); + return false; + } + if (!tsa(tok, token_slash)) return false; + have = tok.next(); + int mask = atoi(have); + if ((mask < 8) || (mask > 32)) { + tok.token_error("cidr 8..32 value", have); + return false; + } + if (!tsa(tok, token_semi)) return false; + IPPAIR pair; + const int masks[33] = {0xffffffff, // 0 + 0x7fffffff, // 1 + 0x3fffffff, // 2 + 0x1fffffff, // 3 + 0x0fffffff, // 4 + 0x07ffffff, // 5 + 0x03ffffff, // 6 + 0x01ffffff, // 7 + 0x00ffffff, // 8 + 0x007fffff, // 9 + 0x003fffff, // 10 + 0x001fffff, // 11 + 0x000fffff, // 12 + 0x0007ffff, // 13 + 0x0003ffff, // 14 + 0x0001ffff, // 15 + 0x0000ffff, // 16 + 0x00007fff, // 17 + 0x00003fff, // 18 + 0x00001fff, // 19 + 0x00000fff, // 20 + 0x000007ff, // 21 + 0x000003ff, // 22 + 0x000001ff, // 23 + 0x000000ff, // 24 + 0x0000007f, // 25 + 0x0000003f, // 26 + 0x0000001f, // 27 + 0x0000000f, // 28 + 0x00000007, // 29 + 0x00000003, // 30 + 0x00000001, // 31 + 0x00000000}; // 32 + pair.first = ipaddr; + pair.last = ipaddr | masks[mask]; + pair.cidr = mask; + dc.add_pair(pair); + } + if (!tsa(tok, token_semi)) return false; + return true; +} + + +//////////////////////////////////////////////// +// +bool parse_syslogconfig(TOKEN &tok, CONFIG &dc); +bool parse_syslogconfig(TOKEN &tok, CONFIG &dc) { + char *name = tok.next(); + if (!tsa(tok, token_lbrace)) return false; + SYSLOGCONFIGP con = new SYSLOGCONFIG(tok, name); if (con->failed()) { delete con; return false; } dc.add_syslogconfig(con); + while (true) { + char *have = tok.next(); + if (!have) break; + if (have == token_rbrace) break; + if (have == token_pattern) { + if (!parse_pattern(tok, *con)) return false; + } + else { + tok.token_error("pattern", have); + return false; + } + } + if (!tsa(tok, token_semi)) return false; return true; } @@ -227,15 +521,20 @@ while (true) { char *have = tok.next(); if (!have) break; - if (have == token_file) { - if (!parse_syslogconfig(tok, dc)) { - tok.token_error("load_conf() failed to parse syslogconfig"); - return false; + if (have == token_threshold) { + have = tok.next(); + dc.set_threshold(atoi(have)); + if (!tsa(tok, token_semi)) return false; } - else count++; + else if (have == token_ignore) { + if (!parse_ignore(tok, dc)) return false; + } + else if (have == token_file) { + if (!parse_syslogconfig(tok, dc)) return false; + count++; } else { - tok.token_error(token_file, have); + tok.token_error("threshold/ignore/file", have); return false; } } @@ -248,12 +547,15 @@ // init the tokens // void token_init() { - token_cisco = register_string("cisco"); + token_bucket = register_string("bucket"); token_file = register_string("file"); + token_ignore = register_string("ignore"); token_include = register_string("include"); + token_index = register_string("index"); token_lbrace = register_string("{"); - token_parser = register_string("parser"); + token_pattern = register_string("pattern"); token_rbrace = register_string("}"); token_semi = register_string(";"); - token_ssh = register_string("ssh"); + token_slash = register_string("/"); + token_threshold = register_string("threshold"); } diff -r 6e88da080f08 -r 8fe310e5cd44 src/syslogconfig.h --- a/src/syslogconfig.h Thu Nov 24 10:31:09 2005 -0800 +++ b/src/syslogconfig.h Sun Nov 27 21:12:01 2005 -0800 @@ -23,60 +23,94 @@ #include "tokenizer.h" #include +#include class SYSLOGCONFIG; +class CONFIG; + +struct IPPAIR { + int first; + int last; + int cidr; +}; + +class PATTERN { + 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 +public: + ~PATTERN(); + PATTERN(TOKEN &tok, char *pattern_, int index_, int bucket_); + bool process(char *buf, CONFIG &con); + void dump(int level); +}; typedef SYSLOGCONFIG * SYSLOGCONFIGP; +typedef PATTERN * PATTERNP; typedef list syslogconfig_list; -enum parser_style {cisco, ssh}; +typedef list ippair_list; +typedef list pattern_list; const int buflen = 1024; class SYSLOGCONFIG { char * file_name; // name of the syslog file - parser_style parser; + pattern_list patterns; // owns the patterns int fd; int len; // bytes in the buffer char buf[buflen]; public: - SYSLOGCONFIG(TOKEN &tok, char *file_name_, parser_style parser_); + SYSLOGCONFIG(TOKEN &tok, char *file_name_); ~SYSLOGCONFIG(); + void add_pattern(PATTERNP pat); + bool failed() { return (fd == -1); }; + bool read(CONFIG &con); + void process(CONFIG &con); void dump(int level); - bool failed() { return (fd == -1); }; - void read(); - void process(); }; -struct CONFIG { +class CONFIG { +public: // the only mutable stuff once it has been loaded from the config file int reference_count; // protected by the global config_mutex // all the rest is constant after loading from the config file int generation; time_t load_time; string_set config_files; - syslogconfig_list syslogconfigs; // owns all the syslogconfigs, not just top level syslogconfigs + int threshold; + ippair_list ignore; // owns all the ippairs + syslogconfig_list syslogconfigs; // owns all the syslogconfigs CONFIG(); ~CONFIG(); + void set_threshold(int threshold_) { threshold = threshold_; }; + int get_threshold() { return threshold; }; void add_syslogconfig(SYSLOGCONFIGP con); + void add_pair(IPPAIR pair); void dump(); void read(); + void sleep(int duration); + bool looking(int ip); }; void discard(string_set &s); char* register_string(string_set &s, char *name); char* register_string(char *name); -CONFIG *parse_config(char *fn); +int ip_address(char *have); bool load_conf(CONFIG &dc, char *fn); void token_init(); -extern char *token_cisco; +extern char *token_bucket; extern char *token_file; +extern char *token_ignore; extern char *token_include; +extern char *token_index; extern char *token_lbrace; -extern char *token_parser; +extern char *token_pattern; extern char *token_rbrace; extern char *token_semi; -extern char *token_ssh; +extern char *token_slash; +extern char *token_threshold; #endif diff -r 6e88da080f08 -r 8fe310e5cd44 src/tokenizer.cpp --- a/src/tokenizer.cpp Thu Nov 24 10:31:09 2005 -0800 +++ b/src/tokenizer.cpp Sun Nov 27 21:12:01 2005 -0800 @@ -89,7 +89,7 @@ { s_single, s_term, s_string, s_single, s_eol, }, // 0x2C , { s_single, s_token, s_string, s_single, s_eol, }, // 0x2D - { s_single, s_token, s_string, s_single, s_eol, }, // 0x2E . - { s_slash, s_token, s_string, s_slash, s_eol, }, // 0x2F / + { s_slash, s_term, s_string, s_slash, s_eol, }, // 0x2F / { s_token, s_token, s_string, s_token, s_eol, }, // 0x30 0 { s_token, s_token, s_string, s_token, s_eol, }, // 0x31 1 { s_token, s_token, s_string, s_token, s_eol, }, // 0x32 2 @@ -523,7 +523,7 @@ void TOKEN::token_error(const char *want, const char *have) { token_error(); - token_error("expecting %s, found %s \n", want, have); + token_error("expecting %s, found %s", want, have); } @@ -535,7 +535,7 @@ if (i != filenames.begin()) { char *fn = (*i); int li = (*j); - token_error("\n included from line %d in file %s -- ", li, fn); + token_error(" included from line %d in file %s -- ", li, fn); } } } diff -r 6e88da080f08 -r 8fe310e5cd44 syslog2iptables.conf --- a/syslog2iptables.conf Thu Nov 24 10:31:09 2005 -0800 +++ b/syslog2iptables.conf Sun Nov 27 21:12:01 2005 -0800 @@ -1,7 +1,26 @@ -file "/var/log/cisco.log" { - parser cisco; +threshold 600; + +ignore { + 127.0.0.0/8; // localhost + 205.147.40.32/26; // 510sg + 205.147.0.100/24; // digilink + 205.147.39.128/25; // ams }; -file "/var/log/messages" { - parser ssh; +file "/var/log/cisco.log" { + pattern "Internet_Firewall denied (tcp|udp) ([^(]*)" { + index 2; // zero based + bucket 200; + }; }; + +file "messages.log" { + pattern "sshd.pam_unix.*authentication failure.*rhost=(.*) user=" { + index 1; // zero based + bucket 300; + }; + pattern "sshd.pam_unix.*authentication failure.*rhost=(.*)$" { + index 1; // zero based + bucket 300; + }; +};