#!/usr/bin/perl
#
# filtermgr
# Script zum dynamischen Verwalten von Filterregeln in Zusammenarbeit
# mit portsentry und iptables
#
# R.Schulze, 27.8.2002

use strict;

###############################################################################

# Basisverzeichnis (da, wo das Script liegt)
my $BASE_DIR="/usr/local/portsentry";

# Datei zum Speichern der IPs und Timestamps
my $DB_FILE="filtermgr.db";

# PortSentrys Block Datei
my $BLOCK_FILE="portsentry.blocked";

# Pfad zu IpTables
my $IPTABLES_CMD="/usr/sbin/iptables";

# Timeout einer Sperrung in Sekunden
my $BLOCK_TIMEOUT=60*10;

###############################################################################

if(!-e $IPTABLES_CMD)
         {
         &LogError("[main]: \"$IPTABLES_CMD\" nicht gefunden.");
         die("\"$IPTABLES_CMD\" nicht gefunden.\nBitte korrekten Pfad zu iptables angeben.\n");
         }

if( (scalar(@ARGV)>0) && ($ARGV[0]=~m/^(\-[adl])$/) )
        {
        if($ARGV[0] eq "-a")
                {
                if( (defined($ARGV[1])) && ($ARGV[1]=~m/^([0-9.]*)$/) )
			{
			&AddEntry($ARGV[1]);
                        }
                else
                        {
                        &PrintUsage();
                        }
                }
        elsif($ARGV[0] eq "-d")
                {
                &DelOldTargets();
                }
        elsif($ARGV[0] eq "-l")
                {
                &PrintTargets();
                }
	else
		{
		&PrintUsage();
		}
        }
else
        {
        PrintUsage();
        }

exit(0);

###############################################################################
# AddEntry
# fuegt der Datenbank einen neuen Eintrag hinzu
###############################################################################

sub AddEntry
{
my $Target=shift;
my %Table;
if(-e "$BASE_DIR/$DB_FILE")
	{
	&ReadDB(\%Table) || return;
	}

if(defined($Table{$Target}))
	{
	return 1;	
	}

$Table{$Target}=time();

&WriteDB(\%Table) || return; 

&BlockTarget($Target);

return 1;
}

############################################################################### BlockTarget
# BlockTarget
# erzeugt eine Regel, die das Ziel blocken soll
###############################################################################

sub BlockTarget
{
my $Target=shift;
my $Cmd="$IPTABLES_CMD -I INPUT -s $Target -j DROP";
if(system($Cmd))
        {
        &LogError("[BlockTarget]: \"$Cmd\" ist fehlgeschlagen.");
	return;
        }
return 1;
}

############################################################################### BlockTarget
# UnBlockTarget
# loescht die Regel, die das Ziel blocken soll
###############################################################################

sub UnBlockTarget
{
my $Target=shift;
my $Cmd="$IPTABLES_CMD -D INPUT -s $Target -j DROP";
if(system($Cmd))
        {
        &LogError("[UnBlockTarget]: \"$Cmd\" ist fehlgeschlagen.");
	return;
	}
return 1;
}

###############################################################################
# DelOldTargets
# loescht alle alten Regeln
###############################################################################

sub DelOldTargets
{
my %Table;
my $Now=time();
(-e "$BASE_DIR/$DB_FILE") || return 1;

&ReadDB(\%Table) || return;

foreach my $IP(keys(%Table))
        {
        if((int($Table{$IP})+$BLOCK_TIMEOUT)<$Now)
                {
                delete($Table{$IP});
		&UnBlockTarget($IP);
		&DeleteTargetFromBlockfiles($IP);
                }
        }

WriteDB(\%Table) || return;

return 1;
}

###############################################################################
# DeleteTargetFromBlockfiles
# loescht einen Eintrag einer IP aus den Blockfiles
###############################################################################

sub DeleteTargetFromBlockfiles
{
my $Target=shift;
my @Files;

opendir(DIR,$BASE_DIR) || return &LogError("[DeleteTargetFromBlockfiles]: opendir ist fehlgeschlagen ($!).\n");
while(my $Entry=readdir(DIR))
	{
	next if $Entry!~m/$BLOCK_FILE/;
	push(@Files,$Entry);
	}
closedir(DIR);

foreach my $File(@Files)
	{
	my @Lines;

	open(BFILE,"+<$BASE_DIR/$File") || return &LogError("[DeleteTargetFromBlockfile]: Konnte \"$BASE_DIR/$File\" nicht zum Lesen/Schreiben oeffnen.($!)\n");
	flock(BFILE,2);

	while(my $Line=<BFILE>)
		{
		$Line=~s/\n//;
		next if ($Line=~m/\/$Target/);
		push(@Lines,$Line);
		}
	seek(BFILE,0,0);
	truncate(BFILE,0);
	foreach(@Lines)
		{
		print BFILE "$_\n";
		}
	close(BFILE);
	}
return 1;
}

###############################################################################
# PrintTargets
# schreibt eine Tabelle mit den geblockten IPs und einigen Infos
###############################################################################

sub PrintTargets
{
my %Table;
&ReadDB(\%Table);

print "Liste der geblockten IPs:\n\n";

foreach my $Entry(keys(%Table))
        {
	my($sec,$min,$hour,$mday,$mon,$year)=(localtime($Table{$Entry}))[0..5];
        $mon++;
        $year+=1900;
        print "- ".$Entry." : geblockt seit $mday\.$mon\.$year, $hour\:$min\:$sec\n";
        }
}


###############################################################################
# PrintUsage
###############################################################################

sub PrintUsage
{
print "\n";
print<<EOU;
Benutzung:
        -a xxx.xxx.xxx.xxx        Blocken der IP xxx.xxx.xxx.xxx
        -d                        Alte Blockregeln loeschen
        -l                        geblockte IPs auflisten
        -h                        diese Hilfe
EOU
}

###############################################################################
# ReadDB
# liest die Datenbank aus und speichert die Daten in einem Hash
###############################################################################

sub ReadDB
{
my $Buffer=shift;
my %Table;
open(FILE,"$BASE_DIR/$DB_FILE") || return &LogError("[ReadDB]: \"$BASE_DIR/$DB_FILE\" konnte nicht zum Lesen geoeffnet werden ($!)");
flock(FILE,1);
while(my $Line=<FILE>)
        {
        $Line=~s/\n//;
        my($IP,$Timestamp)=split(/\:/,$Line);
	if( ($IP=~m/^([0-9\.]+)$/) && ($Timestamp=~m/^([0-9]+)$/) )
        	{
                $Table{$IP}=$Timestamp;
                }
	}
close(FILE);
%$Buffer=%Table;
return 1;
}

###############################################################################
# WriteDB
# schreibt die Daten eines Hashes in die Datenbank
###############################################################################

sub WriteDB
{
my $Table=shift;
open(FILE,">$BASE_DIR/$DB_FILE") || return &LogError("[WriteDB]: \"$BASE_DIR/$DB_FILE\" konnte nicht zum Schreiben geoeffnet werden ($!)");
flock(FILE,2);
while(my($IP,$Timestamp)=each(%$Table))
        {
        print FILE $IP.":".$Timestamp."\n";
        }
close(FILE);
return 1;
}

###############################################################################
# LogError
###############################################################################

sub LogError
{
my $Err=shift;
open(LOG,">>$BASE_DIR/filtermgr.err") || return;
print LOG scalar(localtime(time())).": $Err\n";
close(LOG);
return;
}
