diff --git a/policy/logging.bro b/policy/logging.bro index 90f42a8874..0bb9f6df83 100644 --- a/policy/logging.bro +++ b/policy/logging.bro @@ -93,9 +93,14 @@ export { # Defines rotation parameters per (id, path) tuple. const rotation_control: table[Writer, string] of Log::RotationControl &default=[] &redef; + ### Function. + + const no_filter: Filter = [$name=""]; # Sentinel. + global create_stream: function(id: Log::ID, stream: Log::Stream) : bool; global add_filter: function(id: Log::ID, filter: Log::Filter) : bool; global remove_filter: function(id: Log::ID, name: string) : bool; + global get_filter: function(id: Log::ID, name: string) : Filter; # Returns no_filter if not found. global write: function(id: Log::ID, columns: any) : bool; global set_buf: function(id: Log::ID, buffered: bool): bool; global flush: function(id: Log::ID): bool; @@ -103,6 +108,9 @@ export { global remove_default_filter: function(id: ID) : bool; } +# We keep a script-level copy of all filters so that we can directly manipulate them. +global filters: table[ID, string] of Filter; + @load logging.bif # Needs Log::Filter and Log::Stream defined. module Log; @@ -123,14 +131,24 @@ function create_stream(id: Log::ID, stream: Log::Stream) : bool function add_filter(id: Log::ID, filter: Log::Filter) : bool { + filters[id, filter$name] = filter; return Log::__add_filter(id, filter); } function remove_filter(id: Log::ID, name: string) : bool { + delete filters[id, name]; return Log::__remove_filter(id, name); } +function get_filter(id: Log::ID, name: string) : Filter + { + if ( [id, name] in filters ) + return filters[id, name]; + + return no_filter; + } + function write(id: Log::ID, columns: any) : bool { return Log::__write(id, columns); diff --git a/src/LogMgr.cc b/src/LogMgr.cc index 8f44c4a520..76feb0e0ea 100644 --- a/src/LogMgr.cc +++ b/src/LogMgr.cc @@ -492,6 +492,10 @@ bool LogMgr::AddFilter(EnumVal* id, RecordVal* fval) filter->path_val = new StringVal(path.c_str()); } + // Remove any filter with the same name we might already have. + RemoveFilter(id, filter->name); + + // Add the new one. stream->filters.push_back(filter); #ifdef DEBUG @@ -514,14 +518,17 @@ bool LogMgr::AddFilter(EnumVal* id, RecordVal* fval) return true; } -bool LogMgr::RemoveFilter(EnumVal* id, StringVal* filter) +bool LogMgr::RemoveFilter(EnumVal* id, StringVal* name) + { + return RemoveFilter(id, name->AsString()->CheckString()); + } + +bool LogMgr::RemoveFilter(EnumVal* id, string name) { Stream* stream = FindStream(id); if ( ! stream ) return false; - string name = filter->AsString()->CheckString(); - for ( list::iterator i = stream->filters.begin(); i != stream->filters.end(); ++i ) { if ( (*i)->name == name ) diff --git a/src/LogMgr.h b/src/LogMgr.h index 6a0c16159f..3a356b4e88 100644 --- a/src/LogMgr.h +++ b/src/LogMgr.h @@ -68,7 +68,8 @@ public: // actual BiFs just forward here. bool CreateStream(EnumVal* id, RecordVal* stream); bool AddFilter(EnumVal* id, RecordVal* filter); - bool RemoveFilter(EnumVal* id, StringVal* filter); + bool RemoveFilter(EnumVal* id, StringVal* name); + bool RemoveFilter(EnumVal* id, string name); bool Write(EnumVal* id, RecordVal* columns); bool SetBuf(EnumVal* id, bool enabled); // Changes the state for all writers for that stream. bool Flush(EnumVal* id); // Flushes all writers for the stream. @@ -100,6 +101,7 @@ private: void InstallRotationTimer(WriterInfo* winfo); void Rotate(WriterInfo* info); RecordVal* LookupRotationControl(EnumVal* writer, string path); + Filter* FindFilter(EnumVal* id, StringVal* filter); vector streams; // Indexed by stream enum. }; diff --git a/testing/btest/Baseline/logging.adapt-filter/ssh-new-default.log b/testing/btest/Baseline/logging.adapt-filter/ssh-new-default.log new file mode 100644 index 0000000000..e0f76c1dfc --- /dev/null +++ b/testing/btest/Baseline/logging.adapt-filter/ssh-new-default.log @@ -0,0 +1,3 @@ +# t id.orig_h id.orig_p id.resp_h id.resp_p status country +1299633982.99326 1.2.3.4 1234 2.3.4.5 80 success unknown +1299633982.99326 1.2.3.4 1234 2.3.4.5 80 failure US diff --git a/testing/btest/logging/adapt-filter.bro b/testing/btest/logging/adapt-filter.bro new file mode 100644 index 0000000000..393f46eba9 --- /dev/null +++ b/testing/btest/logging/adapt-filter.bro @@ -0,0 +1,35 @@ + +# @TEST-EXEC: bro %INPUT +# @TEST-EXEC: btest-diff ssh-new-default.log +# @TEST-EXEC: test '!' -e ssh.log + +module SSH; + +@load logging + +export { + # Create a new ID for our log stream + redef enum Log::ID += { SSH }; + + # Define a record with all the columns the log file can have. + # (I'm using a subset of fields from ssh-ext for demonstration.) + type Log: record { + t: time; + id: conn_id; # Will be rolled out into individual columns. + status: string &optional; + country: string &default="unknown"; + }; +} + +event bro_init() +{ + Log::create_stream(SSH, [$columns=Log]); + + local filter = Log::get_filter(SSH, "default"); + filter$path= "ssh-new-default"; + Log::add_filter(SSH, filter); + + local cid = [$orig_h=1.2.3.4, $orig_p=1234/tcp, $resp_h=2.3.4.5, $resp_p=80/tcp]; + Log::write(SSH, [$t=network_time(), $id=cid, $status="success"]); + Log::write(SSH, [$t=network_time(), $id=cid, $status="failure", $country="US"]); +}