# HG changeset patch # User Carl Byington # Date 1232841140 28800 # Node ID 206448c00b55aae7cf45294ea638000a4e8eebc1 # Parent 75361069c6ef4e839b2389b290383be9086a65cf Allow multiple contexts with independent add/remove commands. diff -r 75361069c6ef -r 206448c00b55 ChangeLog --- a/ChangeLog Wed Dec 24 18:40:54 2008 -0800 +++ b/ChangeLog Sat Jan 24 15:52:20 2009 -0800 @@ -1,3 +1,6 @@ +1.12 2009-01-24 + Allow multiple contexts with independent add/remove commands. + 1.11 2008-05-29 Fixes to compile on Fedora 9 and for const correctness. diff -r 75361069c6ef -r 206448c00b55 NEWS --- a/NEWS Wed Dec 24 18:40:54 2008 -0800 +++ b/NEWS Sat Jan 24 15:52:20 2009 -0800 @@ -1,3 +1,4 @@ +1.12 2009-01-24 Allow multiple contexts with independent add/remove commands. 1.11 2008-05-29 Fixes to compile on Fedora 9 and for const correctness. 1.10 2008-03-22 Add fixes for Solaris from sm-archive. 1.9 2008-03-21 Add default config for bounce floods; fedora packaging diff -r 75361069c6ef -r 206448c00b55 configure.in --- a/configure.in Wed Dec 24 18:40:54 2008 -0800 +++ b/configure.in Sat Jan 24 15:52:20 2009 -0800 @@ -1,6 +1,6 @@ AC_PREREQ(2.59) -AC_INIT(syslog2iptables,1.11,carl@five-ten-sg.com) +AC_INIT(syslog2iptables,1.12,carl@five-ten-sg.com) AC_CONFIG_SRCDIR([config.h.in]) AC_CONFIG_HEADER([config.h]) diff -r 75361069c6ef -r 206448c00b55 src/syslogconfig.cpp --- a/src/syslogconfig.cpp Wed Dec 24 18:40:54 2008 -0800 +++ b/src/syslogconfig.cpp Sat Jan 24 15:52:20 2009 -0800 @@ -16,6 +16,7 @@ const char *token_add; const char *token_bucket; +const char *token_context; const char *token_file; const char *token_ignore; const char *token_include; @@ -28,40 +29,38 @@ const char *token_semi; const char *token_slash; const char *token_threshold; - -struct ltint -{ - bool operator()(const int s1, const int s2) const - { - return (unsigned)s1 < (unsigned)s2; - } -}; - -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 +recorder_map recorders; // all the recorders are named const int maxlen = 1000; // used for snprintf buffers -typedef map ip_buckets; - -class IPR { - ip_buckets violations; -public: - void add(int ip, int amount, CONFIG &con, const char *file_name, int pattern_index, const char *message); - void leak(int amount, CONFIG &con); - void free_all(CONFIG &con); - void update(int ip, bool added, const char *file_name, int pattern_index, const char *message); - void changed(CONFIG &con, int ip, bool added); -}; - -IPR recorder; //////////////////////////////////////////////// // -void IPR::add(int ip, int amount, CONFIG &con, const char *file_name, int pattern_index, const char *message) { + +IPR::IPR() { + reference_count = 0; +} + +IPR* IPR::find(const char* name) { + recorder_map::iterator m = recorders.find(name); + if (m == recorders.end()) recorders[name] = new IPR; + recorders[name]->reference(1); + return recorders[name]; +} + + +void IPR::release(const char* name) { + recorder_map::iterator m = recorders.find(name); + IPR* i = (*m).second; + int r = i->reference(-1); + if (r == 0) { + delete i; + recorders.erase(m); + } +} + + +void IPR::add(int ip, int amount, CONTEXT &con, const char *file_name, int pattern_index, const char *message) { if (con.looking(ip)) { ip_buckets::iterator i = violations.find(ip); if (i == violations.end()) { @@ -91,7 +90,7 @@ } -void IPR::leak(int amount, CONFIG &con) { +void IPR::leak(int amount, CONTEXT &con) { for (ip_buckets::iterator i=violations.begin(); i!=violations.end(); ) { int ip = (*i).first; bucket &b = (*i).second; @@ -110,7 +109,7 @@ } -void IPR::free_all(CONFIG &con) { +void IPR::free_all(CONTEXT &con) { if (debug_syslog > 2) { my_syslog("syslog2iptables shutting down"); } @@ -141,7 +140,7 @@ } -void IPR::changed(CONFIG &con, int ip, bool added) { +void IPR::changed(CONTEXT &con, int ip, bool added) { int t = con.get_threshold(); char buf[maxlen]; if (added) { @@ -203,7 +202,7 @@ } -bool PATTERN::process(char *buf, CONFIG &con, const char *file_name, int pattern_index) { +bool PATTERN::process(char *buf, CONTEXT &con, const char *file_name, int pattern_index) { if (pattern) { const int nmatch = index+1; regmatch_t match[nmatch]; @@ -217,7 +216,7 @@ buf[e] = '\0'; int ip = ip_address(buf+s); if (ip) { - recorder.add(ip, amount, con, file_name, pattern_index, message); + con.recorder->add(ip, amount, con, file_name, pattern_index, message); } return true; } @@ -242,58 +241,65 @@ //////////////////////////////////////////////// // -CONFIG::CONFIG() { - reference_count = 0; - generation = 0; - load_time = 0; +CONTEXT::CONTEXT(const char *nam) { + name = nam; threshold = 500; add_command = "/sbin/iptables -I INPUT --src %s --jump DROP"; remove_command = "/sbin/iptables -D INPUT --src %s --jump DROP"; + recorder = IPR::find(name); } -CONFIG::~CONFIG() { +//////////////////////////////////////////////// +// +CONTEXT::~CONTEXT() { + ignore.clear(); for (syslogconfig_list::iterator i=syslogconfigs.begin(); i!=syslogconfigs.end(); i++) { SYSLOGCONFIG *c = *i; delete c; } - ignore.clear(); + IPR::release(name); } -void CONFIG::add_syslogconfig(SYSLOGCONFIGP con) { +void CONTEXT::add_syslogconfig(SYSLOGCONFIGP con) { syslogconfigs.push_back(con); } -void CONFIG::add_pair(IPPAIR pair) { +void CONTEXT::add_pair(IPPAIR pair) { ignore.push_back(pair); } -void CONFIG::dump() { - printf(" threshold %d; \n\n", threshold); +void CONTEXT::dump() { + string indents(" "); + const char *indent = indents.c_str(); - printf(" add_command \"%s\"; \n", add_command); - printf(" remove_command \"%s\"; \n\n", remove_command); + printf("context %s {\n", name); + printf("%s threshold %d; \n\n", indent, threshold); - printf(" ignore { \n"); + printf("%s add_command \"%s\"; \n", indent, add_command); + printf("%s remove_command \"%s\"; \n\n", indent, remove_command); + + printf("%s ignore { \n", indent); 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("%s %s/%d; \n", indent, inet_ntoa(ip), p.cidr); } - printf(" }; \n\n"); + printf("%s }; \n\n", indent); for (syslogconfig_list::iterator i=syslogconfigs.begin(); i!=syslogconfigs.end(); i++) { SYSLOGCONFIGP c = *i; - c->dump(0); + c->dump(1); } + printf("}; \n\n"); } -void CONFIG::read() { +void CONTEXT::read(CONFIG &con) { while (true) { bool have = false; for (syslogconfig_list::iterator i=syslogconfigs.begin(); i!=syslogconfigs.end(); i++) { @@ -305,19 +311,18 @@ } -void CONFIG::sleep(int duration, time_t &previous) { - ::sleep(duration); - time_t now = time(NULL); - recorder.leak(now-previous, *this); - previous = now; +void CONTEXT::free_all() { + recorder->free_all(*this); } -void CONFIG::free_all() { - recorder.free_all(*this); +void CONTEXT::leak(int delta) { + recorder->leak(delta, *this); + } -bool CONFIG::looking(int ip) { + +bool CONTEXT::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; @@ -327,6 +332,58 @@ //////////////////////////////////////////////// // +CONFIG::CONFIG() { + reference_count = 0; + generation = 0; + load_time = 0; +} + + +CONFIG::~CONFIG() { + for (context_list::iterator i=contexts.begin(); i!=contexts.end(); i++) { + CONTEXT *c = *i; + delete c; + } +} + + +void CONFIG::dump() { + for (context_list::iterator i=contexts.begin(); i!=contexts.end(); i++) { + CONTEXTP c = *i; + c->dump(); + } +} + + +void CONFIG::read() { + for (context_list::iterator i=contexts.begin(); i!=contexts.end(); i++) { + CONTEXT *c = *i; + c->read(*this); + } +} + + +void CONFIG::sleep(int duration, time_t &previous) { + ::sleep(duration); + time_t now = time(NULL); + for (context_list::iterator i=contexts.begin(); i!=contexts.end(); i++) { + CONTEXT *c = *i; + c->leak(now-previous); + } + previous = now; +} + + +void CONFIG::free_all() { + for (context_list::iterator i=contexts.begin(); i!=contexts.end(); i++) { + CONTEXT *c = *i; + c->free_all(); + } +} + + +//////////////////////////////////////////////// +// SYSLOGCONFIG::SYSLOGCONFIG(TOKEN &tok, const char *file_name_) { tokp = &tok; file_name = file_name_; @@ -374,7 +431,7 @@ } -bool SYSLOGCONFIG::read(CONFIG &con) { +bool SYSLOGCONFIG::read(CONTEXT &con) { if (failed()) { open(false); if (failed()) return false; @@ -428,7 +485,7 @@ } -void SYSLOGCONFIG::process(CONFIG &con) { +void SYSLOGCONFIG::process(CONTEXT &con) { int pi=0; for (pattern_list::iterator i=patterns.begin(); i!=patterns.end(); i++) { PATTERN *p = *i; @@ -504,8 +561,8 @@ //////////////////////////////////////////////// // -bool parse_pattern(TOKEN &tok, SYSLOGCONFIG &con); -bool parse_pattern(TOKEN &tok, SYSLOGCONFIG &con) { +bool parse_pattern(TOKEN &tok, SYSLOGCONFIG &con, CONTEXT &me); +bool parse_pattern(TOKEN &tok, SYSLOGCONFIG &con, CONTEXT &me) { const char *pat = tok.next(); int ind = 0; int buc = 0; @@ -543,8 +600,8 @@ //////////////////////////////////////////////// // -bool parse_ignore(TOKEN &tok, CONFIG &dc); -bool parse_ignore(TOKEN &tok, CONFIG &dc) { +bool parse_ignore(TOKEN &tok, CONFIG &dc, CONTEXT &me); +bool parse_ignore(TOKEN &tok, CONFIG &dc, CONTEXT &me) { if (!tsa(tok, token_lbrace)) return false; while (true) { const char *have = tok.next(); @@ -600,7 +657,36 @@ pair.first = ipaddr; pair.last = ipaddr | masks[mask]; pair.cidr = mask; - dc.add_pair(pair); + me.add_pair(pair); + } + if (!tsa(tok, token_semi)) return false; + return true; +} + + +//////////////////////////////////////////////// +// +bool parse_syslogconfig(TOKEN &tok, CONFIG &dc, CONTEXT &me); +bool parse_syslogconfig(TOKEN &tok, CONFIG &dc, CONTEXT &me) { + const char *name = tok.next(); + if (!tsa(tok, token_lbrace)) return false; + SYSLOGCONFIGP con = new SYSLOGCONFIG(tok, name); + if (con->failed()) { + delete con; + return false; + } + me.add_syslogconfig(con); + while (true) { + const char *have = tok.next(); + if (!have) break; + if (have == token_rbrace) break; + if (have == token_pattern) { + if (!parse_pattern(tok, *con, me)) return false; + } + else { + tok.token_error("pattern", have); + return false; + } } if (!tsa(tok, token_semi)) return false; return true; @@ -609,29 +695,47 @@ //////////////////////////////////////////////// // -bool parse_syslogconfig(TOKEN &tok, CONFIG &dc); -bool parse_syslogconfig(TOKEN &tok, CONFIG &dc) { +bool parse_context(TOKEN &tok, CONFIG &dc, CONTEXTP parent); +bool parse_context(TOKEN &tok, CONFIG &dc, CONTEXTP parent) { const char *name = tok.next(); if (!tsa(tok, token_lbrace)) return false; - SYSLOGCONFIGP con = new SYSLOGCONFIG(tok, name); - if (con->failed()) { + CONTEXTP con = new CONTEXT(name); + + while (true) { + const char *have = tok.next(); + if (!have) break; + if (have == token_rbrace) break; // done + if (have == token_threshold) { + have = tok.next(); + con->set_threshold(atoi(have)); + if (!tsa(tok, token_semi)) return false; + } + else if (have == token_ignore) { + if (!parse_ignore(tok, dc, *con)) return false; + } + else if (have == token_add) { + have = tok.next(); + con->set_add(have); + if (!tsa(tok, token_semi)) return false; + } + else if (have == token_remove) { + have = tok.next(); + con->set_remove(have); + if (!tsa(tok, token_semi)) return false; + } + else if (have == token_file) { + if (!parse_syslogconfig(tok, dc, *con)) return false; + } + else { + tok.token_error("threshold/ignore/add_command/remove_command/file", have); + return false; + } + } + if (!tsa(tok, token_semi)) { delete con; return false; } - dc.add_syslogconfig(con); - while (true) { - const 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; + dc.add_context(con); return true; } @@ -645,35 +749,20 @@ while (true) { const char *have = tok.next(); if (!have) break; - if (have == token_threshold) { - have = tok.next(); - dc.set_threshold(atoi(have)); - if (!tsa(tok, token_semi)) return false; - } - else if (have == token_ignore) { - if (!parse_ignore(tok, dc)) return false; + if (have == token_context) { + if (!parse_context(tok, dc, NULL)) { + tok.token_error("load_conf() failed to parse context"); + return false; } - else if (have == token_add) { - have = tok.next(); - dc.set_add(have); - if (!tsa(tok, token_semi)) return false; - } - else if (have == token_remove) { - have = tok.next(); - dc.set_remove(have); - if (!tsa(tok, token_semi)) return false; - } - else if (have == token_file) { - if (!parse_syslogconfig(tok, dc)) return false; - count++; + else count++; } else { - tok.token_error("threshold/ignore/add_command/remove_command/file", have); + tok.token_error(token_context, have); return false; } } - tok.token_error("load_conf() found %d syslog files in %s", count, fn); - return (!dc.syslogconfigs.empty()); + tok.token_error("load_conf() found %d contexts in %s", count, fn); + return (!dc.contexts.empty()); } @@ -683,6 +772,7 @@ void token_init() { token_add = register_string("add_command"); token_bucket = register_string("bucket"); + token_context = register_string("context"); token_file = register_string("file"); token_ignore = register_string("ignore"); token_include = register_string("include"); diff -r 75361069c6ef -r 206448c00b55 src/syslogconfig.h --- a/src/syslogconfig.h Wed Dec 24 18:40:54 2008 -0800 +++ b/src/syslogconfig.h Sat Jan 24 15:52:20 2009 -0800 @@ -8,6 +8,7 @@ class SYSLOGCONFIG; +class CONTEXT; class CONFIG; struct IPPAIR { @@ -25,12 +26,46 @@ public: ~PATTERN(); PATTERN(TOKEN &tok, const char *pattern_, int index_, int amount_, const char *msg_); - bool process(char *buf, CONFIG &con, const char *file_name, int pattern_index); + bool process(char *buf, CONTEXT &con, const char *file_name, int pattern_index); void dump(int level); }; +struct ltint +{ + bool operator()(const int s1, const int s2) const + { + return (unsigned)s1 < (unsigned)s2; + } +}; + +struct bucket { + int count; + bool latch; // true iff ever count>threshold +}; + +typedef map ip_buckets; + +class IPR { + int reference_count; // number of contexts using this recorder + ip_buckets violations; +public: + IPR(); + int reference(int delta) {reference_count += delta; return reference_count;}; + void add(int ip, int amount, CONTEXT &con, const char *file_name, int pattern_index, const char *message); + void leak(int amount, CONTEXT &con); + void free_all(CONTEXT &con); + void update(int ip, bool added, const char *file_name, int pattern_index, const char *message); + void changed(CONTEXT &con, int ip, bool added); + static IPR* find(const char* name); + static void release(const char* name); +}; + + typedef SYSLOGCONFIG * SYSLOGCONFIGP; typedef PATTERN * PATTERNP; +typedef CONTEXT * CONTEXTP; +typedef map recorder_map; +typedef list context_list; typedef list syslogconfig_list; typedef list ippair_list; typedef list pattern_list; @@ -49,13 +84,40 @@ ~SYSLOGCONFIG(); bool failed() { return (fd == -1); }; void open(bool msg); - bool read(CONFIG &con); + bool read(CONTEXT &con); void close(); void add_pattern(PATTERNP pat); - void process(CONFIG &con); + void process(CONTEXT &con); void dump(int level); }; + +class CONTEXT { +public: + const char * name; // name of this context + int threshold; + ippair_list ignore; // owns all the ippairs + const char * add_command; // owned by the string table + const char * remove_command; // "" + IPR * recorder; // used to record violations + syslogconfig_list syslogconfigs; // owns all the syslogconfigs + + CONTEXT(const char *nam); + ~CONTEXT(); + void set_add(const char *add) { add_command = add; }; + void set_remove(const char *remove) { remove_command = remove; }; + 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(CONFIG &con); + void free_all(); + void leak(int delta); + bool looking(int ip); +}; + + class CONFIG { public: // the only mutable stuff once it has been loaded from the config file @@ -64,25 +126,15 @@ int generation; time_t load_time; string_set config_files; - int threshold; - ippair_list ignore; // owns all the ippairs - const char * add_command; // owned by the string table - const char * remove_command; // "" - syslogconfig_list syslogconfigs; // owns all the syslogconfigs + context_list contexts; CONFIG(); ~CONFIG(); - void set_add(const char *add) { add_command = add; }; - void set_remove(const char *remove) { remove_command = remove; }; - void set_threshold(int threshold_) { threshold = threshold_; }; - int get_threshold() { return threshold; }; - void add_syslogconfig(SYSLOGCONFIGP con); - void add_pair(IPPAIR pair); + void add_context(CONTEXTP con) {contexts.push_back(con);} ; void dump(); void read(); void sleep(int duration, time_t &previous); void free_all(); - bool looking(int ip); }; void discard(string_set &s); @@ -95,6 +147,7 @@ extern const char *token_add; extern const char *token_bucket; +extern const char *token_context; extern const char *token_file; extern const char *token_ignore; extern const char *token_include; diff -r 75361069c6ef -r 206448c00b55 syslog2iptables.conf --- a/syslog2iptables.conf Wed Dec 24 18:40:54 2008 -0800 +++ b/syslog2iptables.conf Sat Jan 24 15:52:20 2009 -0800 @@ -1,3 +1,24 @@ +context dns { + threshold 1100; + + add_command "/sbin/iptables -I INPUT --protocol udp --destination-port 53 --src %s --jump DROP"; + remove_command "/sbin/iptables -D INPUT --protocol udp --destination-port 53 --src %s --jump DROP"; + + ignore { + 127.0.0.0/8; // localhost + }; + + file "/var/log/messages" { + pattern "named.*client (.*)#.*query.*cache.*denied" { + index 1; // zero based + bucket 400; + message "DNS attack"; + }; + }; +}; + + +context general { threshold 550; add_command "/sbin/iptables -I INPUT --src %s --jump DROP"; @@ -121,3 +142,5 @@ // message "ssh failed password"; // }; // }; +}; + diff -r 75361069c6ef -r 206448c00b55 syslog2iptables.spec.in --- a/syslog2iptables.spec.in Wed Dec 24 18:40:54 2008 -0800 +++ b/syslog2iptables.spec.in Sat Jan 24 15:52:20 2009 -0800 @@ -69,6 +69,9 @@ %changelog +* Sat Jan 24 2009 Carl Byington - 1.12-1 +- Allow multiple contexts with independent add/remove commands. + * Thu May 29 2008 Carl Byington - 1.11-1 - Fix to compile on Fedora 9 and for const correctness.