From cfe5a22044951bb6046b1580bed45b85cc9a4dad Mon Sep 17 00:00:00 2001 From: Stefan Schantl Date: Fri, 22 Jan 2016 13:26:55 +0100 Subject: [PATCH] Allow to configure the used parser for a monitored file. This commit adds support to assign the used parser for a configured monitored file. The information which parser should be used directly will be obtained from the configuration option. For example "Monitor_Snort = /path/to/snort/alert/file" will monitor the specified file as before, but will try to use the parser called "snort" (parsers internal completely are handled in lower case format) to parser any messages which are written to that file. This will remove the static filename <-> parser structure, which will allow a better implementation of parsers or customized parsers in the future and a better portability to other systems. Signed-off-by: Stefan Schantl --- guardian | 23 +++++++++---- modules/Base.pm | 87 +++++++++++++++++++++++++++++++++++++++-------- modules/Parser.pm | 39 ++++++++++++++++----- 3 files changed, 121 insertions(+), 28 deletions(-) diff --git a/guardian b/guardian index 07e0c14..57255c5 100644 --- a/guardian +++ b/guardian @@ -90,9 +90,13 @@ $SIG{__DIE__} = sub { $logger->Log("err", "@_"); }; # Initialize the event handler. my $events = Guardian::Events->Init(%mainsettings); -# Shared hash between the main process and all threads. It will store all +# Hash to store the currently monitored files and their configured +# parsers. +my %monitored_files = (); + +# Shared hash between the main process and all threads. It will store the # monitored files and their current file position. -my %monitored_files :shared = (); +my %file_positions :shared = (); # Create the main queue. It is used to store and process all events which are # reported and enqueued by the worker threads. @@ -187,6 +191,10 @@ sub Init () { sub Worker ($) { my $file = $_[0]; + # Obtain the parser name which should be used to parser any + # messages of this file. + my $parser = $monitored_files{$file}; + # Signal handler to kill worker. $SIG{'KILL'} = sub { threads->exit(); }; @@ -210,7 +218,7 @@ sub Worker ($) { my @message = (); # Obtain fileposition from hash. - my $fileposition = $monitored_files{$file}; + my $fileposition = $file_positions{$file}; # Open the file. open (FILE, $file) or die "Could not open $file. $!"; @@ -229,10 +237,10 @@ sub Worker ($) { { # Lock shared hash. - lock(%monitored_files); + lock(%file_positions); # Update fileposition. - $monitored_files{$file} = tell(FILE); + $file_positions{$file} = tell(FILE); } # Close file. @@ -240,7 +248,7 @@ sub Worker ($) { # Send filename and message to the parser, # which will return if an action has to be performed. - my @action = &Guardian::Parser::Parser("$file", @message); + my @action = &Guardian::Parser::Parser("$parser", @message); # Send the action to the main process and put it into # the queue. @@ -328,6 +336,9 @@ sub SignalHandler { ## be added to the array of running workers. # sub StartWorkers () { + # Init/Update hash which contains the cursor position of EOF. + %file_positions = &Guardian::Base::FilePositions(\%monitored_files, \%file_positions); + # Loop through the hash which contains the monitored files and start # a worker thread for each single one. foreach my $file (keys %monitored_files) { diff --git a/modules/Base.pm b/modules/Base.pm index 0e43823..fbda49a 100644 --- a/modules/Base.pm +++ b/modules/Base.pm @@ -4,7 +4,7 @@ use warnings; use Exporter qw(import); -our @EXPORT_OK = qw(GenerateMonitoredFiles); +our @EXPORT_OK = qw(GenerateMonitoredFiles FilePositions); # ## Function for fileposition initialization. @@ -40,13 +40,13 @@ sub InitFileposition ($) { # ## This function is responsible for creating the hash of which files should be ## monitored by guardian. In order to do this, all options from the given hash of -## main settings will be parsed and all files to monitor extracted and stored into -## a temporary hash. +## main settings will be parsed and all files to monitor and their configured parsers +## get extracted, validated and stored into a temporary hash. # -## Next step will be to check if the the file allready is part of the existing hash -## of monitored files and if true to use the stored value of the current cursor position. -## If this check fails, the responsible function to initialize the cursor position of -## EOF (end of file) will be called and stored. +## Next step will be to cleanup files which have been monitored in the past but have been +## requested for beeing unmonitored for now. To do this, a check if the the file name is +## part of the existing hash of monitored files and if true to transfer the data into the +## new temporary hash which get returned by the function. # sub GenerateMonitoredFiles (\%\%) { # Dereference the given hash-refs and store @@ -57,14 +57,28 @@ sub GenerateMonitoredFiles (\%\%) { # Private hash for storing the new monitored files. my %new_monitored_files = (); - # Loop through the temporary hash which contains the main settings - # and search for files which should be monitored. Compare if the file - # already was part of the Add them to the - # private new hash of monitored files which will be returned. + # Loop through the temporary hash which contains the main settings. + # Search for files which should be monitored and extract the requested + # parser. Compare if the file already was a part of the hash which contains + # the monitored files and add them to the private new hash of monitored + # files which will be returned. foreach my $config_option (keys %mainsettings) { # Skip option if it does not look like "Monitor_XYZ". next unless($config_option =~ m/^Monitor_/); + # Splitt monitor instruction into 2 parts, to grab the + # requested parser module. + my ($start, $parser) = split (/_/, $config_option); + + # Convert parser name into lower case format. + # Internally the parser module name is completely handled + # in this way. This also prevents from any problems related + # how the parser name has been spelled in the config file. + $parser = lc($parser); + + # Check if the configured parser is available and valid. + next unless(&Guardian::Parser::IsSupportedParser($parser)); + # Get the configured file for this option. my $file = $mainsettings{$config_option}; @@ -75,9 +89,9 @@ sub GenerateMonitoredFiles (\%\%) { # of monitored files. unless(exists($current_monitored_files{$file})) { # Add the file, init and store the fileposition. - $new_monitored_files{$file} = &InitFileposition($file); - } else { - # Add the file and grab the fileposition from the existing hash. + $new_monitored_files{$file} = $parser; + } else { + # Copy file and parser information to the new hash. $new_monitored_files{$file} = $current_monitored_files{$file}; } } @@ -86,4 +100,49 @@ sub GenerateMonitoredFiles (\%\%) { return %new_monitored_files; } +# +## The FilePositions function. +# +## This function is responsible for creating and/or updating the hash which +## stores the current cursor position of the end of file (EOF) of all +## monitored files. +# +## The function requires the hash of currently monitored files and the old hash +## of the current file positions in order to work properly. +# +sub FilePositions (\%\%) { + # Dereference the given hash-refs and store + # them into a new temporary hashes. + my %monitored_files = %{ $_[0] }; + my %current_file_positions = %{ $_[1] }; + + # Private hash for storing the new monitored files. + my %new_file_positions = (); + + # Loop through the hash of monitored files. + # Compare if the file allready has been a part of the hash + # which contains the file positions and transfer the stored + # cursor position into the temporary hash which will be returned. + # + # Otherwise, call the responsible function to obtain the current + # end of file (EOF) and store it. + foreach my $file (keys %monitored_files) { + # Check if the filename is allready part of the hash + # of file positions. + if (exists($current_file_positions{$file})) { + # Copy file position into temporary hash. + $new_file_positions{$file} = $current_file_positions{$file}; + } else { + # Call function to obtain the file position. + my $position = &InitFileposition($file); + + # Add filename and position to the temporary hash. + $new_file_positions{$file} = $position; + } + } + + # Return the new_file_positions hash. + return %new_file_positions; +} + 1; diff --git a/modules/Parser.pm b/modules/Parser.pm index ad55d20..9c3fc87 100644 --- a/modules/Parser.pm +++ b/modules/Parser.pm @@ -4,12 +4,12 @@ use warnings; use Exporter qw(import); -our @EXPORT_OK = qw(Parser); +our @EXPORT_OK = qw(IsSupportedParser Parser); -# This hash contains all supported logfiles and which function -# has to be called to parse them in the right way. +# This hash contains all supported parsers and which function +# has to be called to parse messages in the right way. my %logfile_parsers = ( - "/var/log/snort/alert" => \&message_parser_snort, + "snort" => \&message_parser_snort, ); # @@ -20,20 +20,43 @@ my %logfile_parsers = ( ## any action should be performed. # sub Parser ($$) { - my ($file, @message) = @_; + my ($parser, @message) = @_; # If no responsible message parser could be found, just return nothing. - unless (exists($logfile_parsers{$file})) { + unless (exists($logfile_parsers{$parser})) { return; } - # Call responsible logfile parser. - my $action = $logfile_parsers{$file}->(@message); + # Call responsible message parser. + my $action = $logfile_parsers{$parser}->(@message); # Return which action should be performed. return "count $action"; } +# +## IsSupportedParser function. +# +## This very tiny function checks if a given parser name is available and +## therefore a supported parser. +# +## To perform these check, the function is going to lookup if a key in the +## hash of supported parsers is available +# +sub IsSupportedParser ($) { + my $parser = $_[0]; + + # Check if a key for the given parser exists in the hash of logfile_parsers. + if(exists($logfile_parsers{$parser})) { + # Found a valid parser, so return nothing. + return 1; + } + + # Return "False" if we got here, and therefore no parser + # is available. + return; +} + # ## The Snort message parser. # -- 2.39.2