#!/usr/bin/perl
##########################################################################
# $Id: secure,v 1.28 2003/02/18 15:34:53 kirk Exp $
##########################################################################

########################################################
# This was written and is maintained by:
#    Kirk Bauer <kirk@kaybee.org>
#
# Please send all comments, suggestions, bug reports,
#    etc, to kirk@kaybee.org.
########################################################

#$Detail = $ENV{'LOGWATCH_DETAIL_LEVEL'};
$DoLookup = $ENV{'secure_ip_lookup'};
$Ignore = $ENV{'ignore_services'};
$Summarize = $ENV{'summarize_connections'};

my %NameFromIP;
sub LookupIP {
   my ($name, $a1, $a2,$a3,$a4,$PackedAddr,$Addr);
   $Addr = $_[0];
   if ($DoLookup) {
      $name = $NameFromIP{$Addr};
      return ($name . " (" . $Addr . ")") if defined $name;
      ($a1,$a2,$a3,$a4) = split /\./,$Addr;
      $PackedAddr = pack('C4',$a1,$a2,$a3,$a4);
      if ($name = gethostbyaddr ($PackedAddr,2)) {
         $NameFromIP{$Addr} = $name;
         return ($name . " (" . $Addr . ")");
      } else {
         return ($Addr);
      }
   } else {
      return ($Addr);
   }
}

sub sortByIP {
   ($a1,$a2,$a3,$a4) = split /\./,$a;
   ($b1,$b2,$b3,$b4) = split /\./,$b;
   ($a1 <=> $b1) || ($a2 <=> $b2) || ($a3 <=> $b3) || ($a4 <=> $b4);
}

while (defined($ThisLine = <STDIN>)) {
   chomp($ThisLine);
   $ThisLine =~ s/^... .. ..:..:.. [^ ]+ //;
   my $temp = $ThisLine;
   $temp =~ s/^([^[]+).*/$1/;
   if ($Ignore =~ /\Q$temp\E/i) { next; }
   if ( ($Host,$User) = ($ThisLine =~ /^login: FAILED LOGIN \d+ FROM ([^ ]+) FOR ([^,]+),/ ) ) {
      $FailedLogins->{$User}->{$Host}++;
   } elsif ( ($Service,$IP) = ($ThisLine =~ /^([^ ]+)\[\d+\]: connect(ion)? from "?(\d+\.\d+\.\d+\.\d+).*/) ) {
      $Name = LookupIP($IP);
      if ($Summarize =~ /\Q$Service\E/) { 
         $Connections->{$Service}++;
      } else {
         $Connections->{$Service}->{$Name}++;
      }
   } elsif ( ($Service,$IP) = ($ThisLine =~ /^([^ ]+)\[\d+\]: refused connect from (\d+\.\d+\.\d+\.\d+)$/) ) {
      $Name = LookupIP($IP);
      $Refused->{$Service}->{$Name}++;
   } elsif ( ($Service,$Name) = ($ThisLine =~ /^([^ ]+)\[\d+\]: refused connect from (.*)$/) ) {
      $Refused->{$Service}->{$Name}++;
   } elsif ( ($Service,$Name) = ($ThisLine =~ /^([^ ]+)\[\d+\]: connect from ([^\n]+)$/) ) {
      if ($Summarize =~ /\Q$Service\E/) { 
         $Connections->{$Service}++;
      } else {
         $Connections->{$Service}->{$Name}++;
      }
   } elsif ( (undef, $Service, $IP) = ($ThisLine =~ /^(xinetd|xinetd-ipv6)\[\d+\]: START: ([^ ]+) pid=\d+ from=([^ \n]+)$/) ) {
      if ($Summarize =~ /\Q$Service\E/) { 
         $Connections->{$Service}++;
      } else {
         $Name = LookupIP($IP);
         $Connections->{$Service}->{$Name}++;
      }
   } elsif ( (undef, $Service) = ($ThisLine =~ /^(xinetd|xinetd-ipv6)\[\d+\]: EXIT: ([^ ]+) pid=\d+/) ) {
      #ignore...
   } elsif ( $ThisLine =~ s/^([^ ]+)\[\d+\]: warning: can\'t get client address: No route to host$/$1/ ) {
      $NoIP->{$ThisLine}++;
   } elsif ( $ThisLine =~ m/^[^ ]+\[\d+\]: connect from localhost$/ ) {
      #ignore...
   } elsif ( $ThisLine =~ s/^([^ ]+)\[\d+\]: warning: can\'t get client address: Network is unreachable$/$1/ ) {
      $NoIP->{$ThisLine}++;
   } elsif ( $ThisLine =~ s/^([^ ]+)\[\d+\]: warning: can\'t get client address: Connection reset by peer$/$1/ ) {
      $NoIP->{$ThisLine}++;
   } elsif ( $ThisLine =~ s/^([^ ]+)\[\d+\]: warning: can\'t get client address: Connection timed out$/$1/ ) {
      $NoIP->{$ThisLine}++;
   } elsif ( $ThisLine =~ s/^([^ ]+)\[\d+\]: connect from unknown$/$1/ ) {
      $NoIP->{$ThisLine}++;
   } elsif ( ($Service,$Err) = ($ThisLine =~ /^([^ ]+)\[\d+\]: error: (.+)$/) ) {
      $Error{$Service}{$Err}++;
   } elsif ( ($Service,$Err) = ($ThisLine =~ /^([^ ]+): (FAILED LOGIN SESSION FROM [^ ]+ FOR , .*)$/ ) ) {
      $Error{$Service}{$Err}++;
   } elsif ( $ThisLine =~ /warning: can.t get client address: Connection refused/) {
      # Nothing
   } elsif ( $ThisLine =~ /^login: ROOT LOGIN ON tty[0-9]+/) {
      $RootLoginTTY++
   } elsif ( $ThisLine =~ s/^userdel\[\d+\]: delete user `(.+)'/$1/ ) {
      push @DeletedUsers, "   $ThisLine\n";
   } elsif ( $ThisLine =~ s/^useradd\[\d+\]: new user: name=(.+), uid=(\d+).*$/$1 ($2)/ ) {
      push @NewUsers, "   $ThisLine\n";
   } elsif ( $ThisLine =~ s/^userdel\[\d+\]: remove group `(.+)'/$1/ ) {
      push @DeletedGroups, "   $ThisLine\n";
   } elsif ( $ThisLine =~ s/^useradd\[\d+\]: new group: name=(.+), gid=(\d+).*$/$1 ($2)/ ) {
      push @NewGroups, "   $ThisLine\n";
   } elsif ( $ThisLine =~ /^userdel\[\d+\]: delete `.+' from (shadow )?group `.+'$/ ) {
      # Ignore for now
   } elsif ( $ThisLine =~ /^xinetd\[\d+\]: USERID: ([^ ]+) (.+)$/ ) {
      # This is an inetd lookup... $1 is the service (i.e. ftp), $2 is the response
      # I don't think these are important to log at this time
   } elsif ( $ThisLine =~ /^sudo: ([^\s]+) : (command not allowed)?.+ ; COMMAND=(.*)$/ ) {
      # sudo unauthorized commands
        push @SudoList, "$1: $3\n" unless ($2 eq "");
   } elsif ( $ThisLine =~ /^sudo:/) {
      # Ignore other sudo entries as they are handled by the sudo filter
   } elsif ( $ThisLine =~ /^\/usr\/bin\/sudo: ([^\s]+) : (command not allowed)?.+ ; COMMAND=(.*)$/ ) {
      # sudo unauthorized commands
        push @SudoList, "$1: $3\n" unless ($2 eq "");
   } elsif ( $ThisLine =~ /^\/usr\/bin\/sudo:/) {
      # Ignore other sudo entries as they are handled by the sudo filter
   } elsif ( ($service, $from) = ($ThisLine =~ /^xinetd\[\d+\]: FAIL: (.+) address from=([\d.]+)/)) {
      $Refused->{$service}->{$from}++;
   } else {
      # Unmatched entries...
      push @OtherList, "$ThisLine\n";
   }
}

if (@NewUsers) {
   print "\nNew Users:\n@NewUsers\n";
}

if (@DeletedUsers) {
   print "\nDeleted Users:\n@DeletedUsers\n";
}

if (@NewGroups) {
   print "\nNew Groups:\n@NewUsers\n";
}

if (@DeletedGroups) {
   print "\nDeleted Groups:\n@DeletedUsers\n";
}

if (keys %{$Connections}) {
   print "\nConnections:\n";
   foreach $ThisOne (keys %{$Connections}) {
      if ($Summarize =~ /\Q$ThisOne\E/) { 
         print "   Service " . $ThisOne . ": " . $Connections->{$ThisOne} . " Connection(s)\n";
      } else {
         print "   Service " . $ThisOne . ":\n";
         foreach $OtherOne (sort {sortByIP} keys %{$Connections->{$ThisOne}}) {
            print "      " . $OtherOne . ": " . $Connections->{$ThisOne}->{$OtherOne} . " Time(s)\n";
         }
      }
   }
}

if (keys %{$Refused}) {
   print "\nRefused Connections:\n";
   foreach $ThisOne (keys %{$Refused}) {
      print "   Service " . $ThisOne . ":\n";
      foreach $OtherOne (sort {sortByIP} keys %{$Refused->{$ThisOne}}) {
         print "      " . $OtherOne . ": " . $Refused->{$ThisOne}->{$OtherOne} . " Time(s)\n";
      }
   }
}

if (keys %{$FailedLogins}) {
   print "\nFailed logins:\n";
   foreach $ThisOne (keys %{$FailedLogins}) {
      print "   User " . $ThisOne . ":\n";
      foreach $OtherOne (keys %{$FailedLogins->{$ThisOne}}) {
         print "      " . $OtherOne . ": " . $FailedLogins->{$ThisOne}->{$OtherOne} . " Time(s)\n";
      }
   }
}

if (keys %{$NoIP}) {
   print "\nCouldn't get client IPs for connections to:\n";
   foreach $ThisOne (keys %{$NoIP}) {
      print "   " . $ThisOne . ": " . $NoIP->{$ThisOne} . " Time(s)\n";
   }
}

if (keys %Error) {
   print "\nErrors:\n";
   foreach $Service (sort {$a cmp $b} keys %Error) {
      print "   Service " . $Service . ":\n";
      foreach $Err (sort {$a cmp $b} keys %{$Error{$Service}}) {
         print "      " . $Err . ": " . $Error{$Service}{$Err} . " Time(s)\n";
      }
   }
}

if ($RootLoginTTY) {
   print "\nRoot logins on tty\'s: $RootLoginTTY Time(s).\n";
}

if ($#SudoList >= 0) {
   print "\nUnauthorized sudo commands attempted (" . ($#SudoList + 1) . "):\n";
   print @SudoList;
}

if ($#OtherList >= 0) {
   print "\n**Unmatched Entries**\n";
   print @OtherList;
}

exit(0);

