#!/usr/bin/perl
use strict;
use Net::SMTP;
use File::Glob;
use POSIX qw(strftime);

# Written by Nathan Shanks v.06
# Mike Wimpy helped a ton with this script!  Thanks!

# Mail Setup
my $mailserver="<your mail server>";
my $smtpto='reports@dshield.org';
my $smtpfrom='<your email address>';
my ($smtp, @ok_addr);
 # Make sure to include your user ID
my $userID = "<your dshield ID>";

my @fileList = File::Glob::bsd_glob("/var/log/security.*.bz2");
push( @fileList, "/var/log/security" );

# Misc options
my $data = "/var/log/security";
my $lastTimeStamp="0";

my $lastTimeStampFile="/tmp/dshield_lastrun.tmp";

 # IP Addresses you want to exclude
my @exip;
push( @exip, "10.0.0.0/8" );
;

# Don't touch these values
my $year = strftime("%Y", localtime);
 # Taking the time zone off the local system
my $timezone = substr( strftime("%z", localtime), 0, 3 ) . ":" . substr( strftime("%z", localtime), 3, 2 );
my @data;
my ($month2,$day2,$time2);
my ($month,$day,$time,$messsage,$rule,$action,$proto,$srcip,$srcport,$destip,$destport);
my (%hash1, %hash2, %month);

$month{Jan} = "01";
$month{Feb} = "02";
$month{Mar} = "03";
$month{Apr} = "04";
$month{May} = "05";
$month{Jun} = "06";
$month{Jul} = "07";
$month{Aug} = "08";
$month{Sep} = "09";
$month{Oct} = "10";
$month{Nov} = "11";
$month{Dec} = "12";

# Check to see the last file I sent
if (-e $lastTimeStampFile)
{
         open (DSHIELD, $lastTimeStampFile) or die "CAN NOT OPEN OR CREATE $lastTimeStampFile ($!)";
         while (<DSHIELD>)
         {
                  my $line = $_;
                  $line =~ s/\n//g;

                  if( length( $line ) == 14 )
                  {
                           $lastTimeStamp = $line;
                  }
         }
         close( DSHIELD );
}

open (EXTRA, ">>/tmp/dshieldmissed.tmp") or die "UNABLE TO OPEN $data ($!)";

foreach my $curFile ( @fileList )
{
         my $modtime = (stat($curFile))[9];
         my $modtimeString = strftime("%Y%m%d%H%M%S", localtime($modtime));
         if( $modtimeString gt $lastTimeStamp )
         {
                  $data = $curFile;

                  # Open the security log (use the $data to declare static log)
                  if( $data =~ /.bz2/ )
                  {
                           open( SECURITY, "-|", "bzcat $data" );
                  }
                  else
                  {
                           open (SECURITY, "$data") or die "UNABLE TO OPEN $data ($!)";
                  }


                  print "Reading $data:";

                  MAINLOOP: while (<SECURITY>)
                  {
                           # The big regular expression to pull all the data out
                           if (($month,$day,$time,$messsage,$rule,$action,$proto,$srcip,$srcport,$destip,$destport)= /(\w{3})\s+(\d+)\s+(\d{2}:\d{2}:\d{2})\s+(\w+\s+\w+:\s+\w{4}:)\s(\d+)\s+(\w+)\s+(\w+)\s(\d{1,3}.\d{1,3}.\d{1,3}.\d{1,3}):(\d{1,5})\s+(\d{1,3}.\d{1,3}.\d{1,3}.\d{1,3}):(\d{1,5})\s+(?:in|out)\s+via\s+\w{2,3}\d+/)
                           {
                                    if ($action =~ /deny/i ) #only send denies
                                    {
                                             foreach my $subnet( @exip )# Remove IP's you don't want to include
                                             {
                                                      #print("ipInSubnet( $srcip, $subnet ) || ipInSubnet( $destip, $subnet )\n");
                                                      if( ipInSubnet( $srcip, $subnet ) == 1 || ipInSubnet( $destip, $subnet ) == 1 )
                                                      {
                                                               #print("Found $srcip or $destip in $subnet. Skipping");
                                                               next MAINLOOP;
                                                      }
                                             }

                                            if( length($day) == 1 )
                                            {
                                            $day = "0"."$day";
                                            }

                                            my $curTimeStamp="$year$month{$month}$day$time";
                                            $curTimeStamp =~ s/://g;

                                            if ( $curTimeStamp le $lastTimeStamp )
                                            {
                                                     #do nothing
                                            }
                                            else
                                            {
                                                     # check if its a duplicate if not store it and if it is increase the count
                                                     if ($hash1{$proto,$srcip,$srcport,$destip,$destport} eq 0)
                                                     {
                                                              $hash1{$proto,$srcip,$srcport,$destip,$destport} = 1;
                                                              $hash2{$proto,$srcip,$srcport,$destip,$destport} = "$year-$month{$month}-$day $time $timezone\t$userID\t$hash1{$proto,$srcip,$srcport,$destip,$destport}\t$srcip\t$srcport\t$destip\t$destport\t$proto";
                                                     }
                                                     else
                                                     {
                                                              $hash1{$proto,$srcip,$srcport,$destip,$destport}++;
                                                              $hash2{$proto,$srcip,$srcport,$destip,$destport} = "$year-$month{$month}-$day $time $timezone\t$userID\t$hash1{$proto,$srcip,$srcport,$destip,$destport}\t$srcip\t$srcport\t$destip\t$destport\t$proto";
                                                     }
                                            }
                                    }
                           }
                           elsif (($month,$day,$time,$messsage,$rule,$action,$proto,$srcport,$destport,$srcip,$destip)= /(\w{3})\s+(\d+)\s+(\d{2}:\d{2}:\d{2})\s+(\w+\s+\w+:\s+\w{4}:)\s+(\d+)\s+(\w+)\s+(ICMP):(\d+).(\d+)\s+(\d{1,3}.\d{1,3}.\d{1,3}.\d{1,3})\s+(\d{1,3}.\d{1,3}.\d{1,3}.\d{1,3})\s+(?:in|out)\s+via\s+\w{2,3}\d+/)
                           {
                                    if ($action =~ /deny/i )#only send denies
                                    {
                                             foreach my $subnet( @exip )# Remove IP's you don't want to include
                                             {
                                                      #print("ipInSubnet( $srcip, $subnet ) || ipInSubnet( $destip, $subnet )\n");
                                                      if( ipInSubnet( $srcip, $subnet ) == 1 || ipInSubnet( $destip, $subnet ) == 1 )
                                                      {
                                                               #print("Found $srcip or $destip in $subnet. Skipping");
                                                               next MAINLOOP;
                                                      }
                                             }

                                             if( length($day) == 1 )
                                             {
                                                      $day = "0"."$day";
                                             }

                                             my $curTimeStamp="$year$month{$month}$day$time";
                                             $curTimeStamp =~ s/://g;

                                             if ( $curTimeStamp le $lastTimeStamp )
                                             {
                                                     #do nothing
                                             }
                                             else
                                             {
                                                      # check if its a duplicate if not store it and if it is increase the count
                                                      if ($hash1{$proto,$srcip,$srcport,$destip,$destport} eq 0)
                                                      {
                                                               $hash1{$proto,$srcip,$srcport,$destip,$destport} = 1;
                                                               $hash2{$proto,$srcip,$srcport,$destip,$destport} = "$year-$month{$month}-$day $time $timezone\t$userID\t$hash1{$proto,$srcip,$srcport,$destip,$destport}\t$srcip\t$srcport\t$destip\t$destport\t$proto";
                                                      }
                                                      else
                                                      {
                                                               $hash1{$proto,$srcip,$srcport,$destip,$destport}++;
                                                               $hash2{$proto,$srcip,$srcport,$destip,$destport} = "$year-$month{$month}-$day $time $timezone\t$userID\t$hash1{$proto,$srcip,$srcport,$destip,$destport}\t$srcip\t$srcport\t$destip\t$destport\t$proto";
                                                      }
                                             }
                                    }
                           }
                           else
                           {
                                   #print EXTRA "$_\n";
                           }
                  }


                  print "done\n";
                  close SECURITY;

         }
}

print "Producing Output:";
open (OUTPUT, "> /tmp/output.tmp") or die "CAN NOT OPEN OR CREATE output.tmp ($!)";

# Write your output to a file that can be mailed
foreach (values %hash2)
{
         push(@data, "$_\n");
         print OUTPUT "$_\n";
}
close(OUTPUT);
close(EXTRA);

print "done\n";

if (@data ne 0)
{
         print "Sendmail Email:";
         &mail;
         print "done\n";
}
else
{
         print "No data to send email not sent.\n";
}

open (DSHIELD, ">$lastTimeStampFile") or die "CAN NOT OPEN OR CREATE $lastTimeStampFile ($!)";
$lastTimeStamp = "$year$month{$month}$day$time";
$lastTimeStamp =~ s/://g;
print(DSHIELD "$lastTimeStamp\n");
close( DSHIELD );

sub mail
{
         # Define the Subject line
         my $subject="FORMAT DSHIELD USERID $userID TZ $timezone";

         # Build a mail server connection
         $smtp = Net::SMTP->new($mailserver,Timeout=>60);

         # From and to
         $smtp->mail($smtpfrom);
         $smtp->to($smtpto);
         @ok_addr = $smtp->recipient($smtpto, {SkipBad=>1});

         # Plugin the data and transmit
         $smtp->data();
         $smtp->datasend("From: $smtpfrom\n");
         $smtp->datasend("Subject: $subject\n");
         $smtp->datasend(@data);
         $smtp->dataend();
         $smtp->quit;
}

sub ipInSubnet
{
    my $ip = shift;
    my $subnet = shift;
    my $subnetBin;
    my $ipBin;
    my $mask;
    my $match = 0;

    if( $subnet =~ /(\d{1,3}).(\d{1,3}).(\d{1,3}).(\d{1,3})\/(\d+)/ )
    {
        $subnetBin = $1<<24 | $2<<16 | $3<<8 | $4;
        $mask = 0xFFFFFFFF << (32-$5);
        $subnetBin &= $mask;
        #printf("0x%.8X\n", $subnetBin);
    }

    if( $ip =~ /(\d{1,3}).(\d{1,3}).(\d{1,3}).(\d{1,3})/ )
    {
        $ipBin = $1<<24 | $2<<16 | $3<<8 | $4;
        $ipBin &= $mask;
        #printf("0x%.8X\n", $ipBin);
    }

    if( $subnetBin == $ipBin )
    {
        #print("Match!\n");
        $match =1;
    }

    return $match;
}
