use Exporter qw(import);
-our @EXPORT_OK = qw(Init CheckAction Update);
+our @EXPORT_OK = qw(Init CheckAction GenerateIgnoreList Update);
# Hash which stores all supported commands from the queue.
my %commands = (
'block' => \&CallBlock,
'unblock' => \&CallUnblock,
'flush' => \&CallFlush,
+ 'reload' => \&main::Reload,
+ 'reload-ignore-list' => \&main::ReloadIgnoreList,
+ 'logrotate' => \&main::Logrotate,
);
# Hash to store addresses and their current count.
# when the block for this address can be released.
my %blockhash = ();
+# Hash to store user-defined IP addresses and/or subnets which should be
+# ignored in case any events should be repored for them.
+my %ignorehash = ();
+
+# Array to store localhost related IP addresses.
+# They are always white-listed to prevent guardian from blocking
+# any local traffic.
+my @localhost_addresses = ("127.0.0.1", "::1");
+
# This object will contain the reference to the logger object after calling Init.
my $logger;
my $module_name = "Guardian::" . $self->{FirewallEngine};
eval "use $module_name; 1" or die "Could not load a module for firewall engine: $self->{FirewallEngine}!";
+ # Check if an IgnoreFile has been configured.
+ if (exists($self->{IgnoreFile})) {
+ # Call function to handle the ignore mechanism.
+ &GenerateIgnoreList($self->{IgnoreFile});
+ } else {
+ # Whitelist local addresses.
+ %ignorehash = &_whitelist_localhost();
+ }
+
# Return the class object.
return $self;
}
+#
+## The "Update" Block settings function.
+#
+## This object based function is called to update various class settings.
+#
+sub Update (\%) {
+ my $self = shift;
+
+ # Dereference the given hash-ref and store
+ # the values into a new temporary hash.
+ my %settings = %{ $_[0] };
+
+ # Skip settings update if some essential settings are missing.
+ unless ((exists($settings{BlockCount})) && (exists($settings{BlockTime}))) {
+ $logger->Log("err", "Values for BlockCount or BlockTime are missing, keeping previously configured settings.");
+
+ # Return unmodified class object.
+ return $self;
+ }
+
+ # Change settings.
+ $self->{BlockCount} = $settings{BlockCount};
+ $self->{BlockTime} = $settings{BlockTime};
+
+ # Return modified class object.
+ return $self;
+}
+
#
## The main "CheckAction" function.
#
return;
}
- # Check if the given address is valid.
- unless(&Guardian::Base::IsValidAddressOrNetwork($address)) {
- # Log error message.
- $logger->Log("err", "Invalid IP address: $address");
- return;
+ # Check if the given event contains an address.
+ if ($address) {
+ # Convert and validate the given address.
+ my $bin_address = &Guardian::Base::IPOrNet2Int($address);
+
+ # Abort if the given address could not be converted because it is not valid.
+ unless ($bin_address) {
+ $logger->Log("err", "Invalid IP address: $address");
+ return;
+ }
+
+ # Check if address should be ignored.
+ if(&_IsOnIgnoreList($bin_address)) {
+ # Log message.
+ $logger->Log("info", "Ignoring event for $address, because it is part of the ignore list.");
+ return;
+ }
}
# Call required handler.
# Drop address from blockhash.
delete ($blockhash{$address});
+ # Drop address from counthash if the address has been unblocked
+ # by the user. This happens when the called module is "Socket".
+ if ($module eq "Socket") {
+ delete ($counthash{$address});
+ }
+
+ # Everything worked well, return nothing.
+ return undef;
+}
+
+#
+## CallFlush function.
+#
+## This function is responsible for calling the used firewall
+## engine to do a flush of the used firewall chain. This will
+## clean the entire firewall chain.
+#
+sub CallFlush ($) {
+ my $self = shift;
+
+ # Log the call for flushing.
+ $logger->Log("info", "Flush has been called...");
+
+ # Call flush.
+ my $error = &DoFlush();
+
+ # If an error message is returned, something went wrong.
+ if ($error) {
+ # Exit function and return the error message.
+ return $error;
+ } else {
+ # Flush successfully has been performed.
+ $logger->Log("debug", "Flush successfully has been performed...");
+ }
+
+ # Flush blockhash.
+ %blockhash = ();
+
# Everything worked well, return nothing.
return undef;
}
+#
+## GenerateIgnoreList function.
+#
+## This function is responsible for generating/updating the
+## IgnoreHash which contains all ignored IP addresses and
+## networks.
+#
+sub GenerateIgnoreList($) {
+ my $file = shift;
+ my @include_files;
+
+ # Reset current ignore hash and add
+ # localhost related IP addresses.
+ %ignorehash = &_whitelist_localhost();
+
+ # Check if the given IgnoreFile could be opened.
+ unless(-e $file) {
+ $logger->Log("err", "The configured IgnoreFile \($file\) could not be opened. Skipped!");
+ return;
+ }
+
+ # Open the given IgnoreFile.
+ open (IGNORE, $file);
+
+ # Read-in the file line by line.
+ while (<IGNORE>) {
+ # Skip comments.
+ next if (/\#/);
+
+ # Skip blank lines.
+ next if (/^\s*$/);
+
+ # Remove any newlines.
+ chomp;
+
+ # Check for an include instruction.
+ if ($_ =~ /^Include_File = (.*)/) {
+ my $include_file = $1;
+
+ # Check if the parsed include file exists and is read-able.
+ if (-e $include_file) {
+ # Add file to the array of files wich will be included.
+ push(@include_files, $include_file);
+
+ # Write out log message.
+ $logger->Log("debug", "Addresses from $include_file will be included...");
+ } else {
+ # Log missing file.
+ $logger->Log("err", "$include_file will not be included. File does not exist!");
+ }
+ } else {
+ # Check if the line contains a valid single address or network and
+ # convert it into binary format. Store the result/start and
+ # end values in a temporary array.
+ my @values = &Guardian::Base::IPOrNet2Int($_);
+
+ # If the function returned any values, the line contained a valid
+ # single address or network which successfully has been converted into
+ # binary format.
+ if (@values) {
+ # Assign the array as value to the ignorehash.
+ $ignorehash{$_} = [@values];
+ } else {
+ # Log invalid entry.
+ $logger->Log("err", "IgnoreFile contains an invalid address/network: $_");
+
+ # Skip line.
+ next;
+ }
+ }
+ }
+
+ # Close filehandle for the IgnoreFile.
+ close (IGNORE);
+
+ # Check if any files should be included.
+ if (@include_files) {
+ # Loop through the array of files which should be included.
+ foreach my $file (@include_files) {
+ # Open the file.
+ open(INCLUDE, $file);
+
+ # Read-in file line by line.
+ while(<INCLUDE>) {
+ # Skip any comments.
+ next if (/\#/);
+
+ # Skip any blank lines.
+ next if (/^\s*$/);
+
+ # Chomp any newlines.
+ chomp;
+
+ # Check if the line contains a valid single address or network and
+ # convert it into binary format. Store the result/start and
+ # end values in a temporary array.
+ my @values = &Guardian::Base::IPOrNet2Int($_);
+
+ # If the function returned any values, the line contained a valid
+ # single address or network which successfully has been converted into
+ # binary format.
+ if (@values) {
+ # Assign the array as value to the ignorehash.
+ $ignorehash{$_} = [@values];
+ } else {
+ # Log invalid entry.
+ $logger->Log("err", "$file contains an invalid address/network: $_");
+
+ # Skip line.
+ next;
+ }
+ }
+
+ # Close filehandle.
+ close(INCLUDE);
+ }
+ }
+
+ # Get amount of current elements in hash.
+ my $amount = scalar(keys(%ignorehash));
+
+ # Write out log message.
+ $logger->Log("debug", "Ignore list currently contains $amount entries.");
+}
+
+#
+## Private function to check if an address is part of the Ignore Hash.
+#
+## This function checks if a passed IP address in binary format (!),
+## directly or as part of an ignored network is stored in the ignore hash.
+#
+sub _IsOnIgnoreList ($) {
+ my $bin_address = shift;
+
+ # Loop through the ignore hash and grab the stored values.
+ foreach my $key ( keys %ignorehash ) {
+ # Dereference values array.
+ my @values = @{$ignorehash{$key}};
+
+ # Obtain amount of items for the current value array.
+ my $items = scalar @values;
+
+ # Whether the amount equals one, the given binary address just
+ # needs to be compared against a single address.
+ if ($items eq "1") {
+ my ($ignored_address) = @values;
+
+ # Simple check if the stored and the given binary address
+ # are the same.
+ if ($bin_address eq $ignored_address) {
+ # The given address is part of the ignore list.
+ $logger->Log("debug", "Address $key found on the ignore list.");
+
+ # Return "1" (True).
+ return 1;
+ }
+ }
+
+ # If the amount equals two, for passed binary address needs to
+ # be checked if it is part of the ignored network range.
+ elsif ($items eq "2") {
+ my ($first_address, $last_address) = @values;
+
+ # Check if the passed binary address is bigger than
+ # the first address and smaler than the last address
+ # (between) the stored network range.
+ if (($bin_address >= $first_address) && ($bin_address <= $last_address)) {
+ # The address is part of an ignored network.
+ $logger->Log("debug", "Address is found inside the ignored network $key.");
+
+ # Return "1" (True).
+ return 1;
+ }
+
+ # If the amount is not eighter one or two, the current entry of the ignorehash seems
+ # to be corrupted. Skip and log it.
+ } else {
+ # Write log message about this corruped item in the ignore hash.
+ $logger->Log("err", "Invalid item in the Ignore Hash: $key - @values");
+
+ # Skip this element of the ignore hash.
+ next;
+ }
+ }
+
+ # If we got here, the given address is not part of the ignore hash.
+ # Return nothing (False).
+ return;
+}
+
+#
+## The _whitelist_localhost function.
+#
+## This tiny private function simple generates and returns a hash which contains
+## the clear and binary converted addresses for all array-stored
+## (@localhost_addresses) in an ignorelist compatible format.
+#
+sub _whitelist_localhost () {
+ my %temphash;
+
+ # Loop through the array of localhost related addresses.
+ foreach my $address (@localhost_addresses) {
+ # Validate and convert the addresss into binary format.
+ my @values = &Guardian::Base::IPOrNet2Int($address);
+
+ # Check if any values are returned.
+ if (@values) {
+ # Store the converted binary values in the temporary hash.
+ $temphash{$address} = [@values];
+ }
+ }
+
+ # Return the temporary hash.
+ return %temphash;
+}
+
1;