Add functionality for whitelisting single addresses or network ranges.
authorStefan Schantl <stefan.schantl@ipfire.org>
Wed, 3 Feb 2016 07:34:12 +0000 (08:34 +0100)
committerStefan Schantl <stefan.schantl@ipfire.org>
Wed, 3 Feb 2016 07:34:12 +0000 (08:34 +0100)
This commit adds the posibilty for generating a ignore list based on a
set of IP addresses or networks (IPv4/IPv6 are supported) which are provided
by an so called ignore file.

The path and the filename can be configured by using the "IgnoreFile = /path/to/somefile"
option inside the config file of guardian. Using this feature is completely optional and
only be activated if an ignore file has been specified.

Signed-off-by: Stefan Schantl <stefan.schantl@ipfire.org>
modules/Base.pm
modules/Events.pm

index 96ea256..194cb96 100644 (file)
@@ -135,6 +135,60 @@ sub IsValidAddressOrNetwork ($) {
        return 1;
 }
 
+#
+## Address/Network to binary format caluculator function.
+#
+## This function is used to convert a given single IP address
+## or network into a binary format.
+#
+## The used Net::IP module is not able to directly detect
+## single addresses or network ranges. Only an element which may be
+## a single address or a whole network can be assigned, for which a
+## lot of different values can be calculated. In case the input has
+## been a single address, the module will calculate the same binary
+## address (intip) and last address for the network range (last_int)
+## because internally it uses a /32 bit prefix for IPv4 and a /128 prefix
+## on IPv6 addresses.
+#
+## So a single address can be detected by just comparing both calculated
+## addresses if they are equal.
+#
+sub IPOrNet2Int($) {
+       my $address = shift;
+
+       # Assign and validate the given address, or directly return
+       # nothing (False) and exit the function.
+       my $ip = new Net::IP ($address) || return;
+
+       # Convert the given address into integer format.
+       my $first .= $ip->intip();
+
+       # Calculate last address for the given network.
+       my $last .= $ip->last_int();
+
+       # Check whether the first address equals the last address.
+       # If this is true, a single IP address has been passed.
+       if ($first eq $last) {
+               # Return the binary converted single address.
+               return $first;
+       }
+
+       # If both addresses are not equal a network has been passed.
+       #
+       # Check if the converted first address is less than the calculated last
+       # address of the network.
+       elsif ($first < $last) {
+               # Return the binary converted first and last address of
+               # the given network.
+               return $first, $last;
+       }
+
+       # If we got here, something strange happend, return nothing (False).
+       else {
+               return;
+       }
+}
+
 #
 ## Function for fileposition initialization.
 #
index b60a572..fa3be24 100644 (file)
@@ -21,6 +21,10 @@ my %counthash = ();
 # 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 = ();
+
 # This object will contain the reference to the logger object after calling Init.
 my $logger;
 
@@ -55,6 +59,12 @@ sub Init (%) {
        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});
+       }
+
        # Return the class object.
        return $self;
 }
@@ -77,13 +87,22 @@ sub CheckAction ($$) {
                 return;
         }
 
-       # Check if the given address is valid.
-       unless(&Guardian::Base::IsValidAddressOrNetwork($address)) {
-               # Log error message.
+       # 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.
        my $error = $commands{$command}->($self, $address, $module, $message);
 
@@ -263,4 +282,122 @@ sub CallUnblock ($) {
        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;
+
+       # 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 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);
+}
+
+#
+## Private function to check if an address is part of the Ignore Hash.
+#
+## This function requires 
+#
+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;
+}
+
 1;