#!/usr/bin/perl
##########################################################################
# $Id: secure,v 1.46 2004/06/21 14:59:05 kirk Exp $
##########################################################################
# $Log: secure,v $
# Revision 1.46  2004/06/21 14:59:05  kirk
# Added tons of patches from Pawe? Go?aszewski" <blues@ds.pg.gda.pl>
#
# Thanks, as always!
#
# Revision 1.45  2004/06/21 14:27:19  kirk
# Patch from logwatch@iamafreeman.com
#
# Revision 1.44  2004/02/03 04:26:36  kirk
# Solaris patch from Mike Tremaine <mgt@stellarcore.net>
#
# Revision 1.43  2004/02/03 03:55:28  kirk
# Patch from M. B. Heath <malcolm@indeterminate.net>
#
# Revision 1.42  2004/02/03 03:52:20  kirk
# Added mailscanner filter and more Solaris support from Mike Tremaine <mgt@stellarcore.net>
#
# Revision 1.41  2004/02/03 02:45:26  kirk
# Tons of patches, and new 'oidentd' and 'shaperd' filters from
# Pawe? Go?aszewski" <blues@ds.pg.gda.pl>
#
##########################################################################

########################################################
# 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'} || 0;
$DoLookup = $ENV{'secure_ip_lookup'};
$Ignore = $ENV{'ignore_services'};
$Summarize = $ENV{'summarize_connections'};
$ConsoleLock = 0;
$spop3d_opened=0;
$spop3d_errors=0;
use Logwatch ':ip';

while (defined($ThisLine = <STDIN>)) {
   chomp($ThisLine);
   $ThisLine =~ s/^... .. ..:..:.. [^ ]+ //;
   #Solaris ID filter -mgt
   $ThisLine =~ s/\[ID [0-9]+ [a-z]+\.[a-z]+\] //;
   my $temp = $ThisLine;
   $temp =~ s/^([^[]+).*/$1/;
   if ($Ignore =~ /\b\Q$temp\E\b/i) { next; }
   if (
      ( $ThisLine =~ m/^[^ ]+\[\d+\]: connect from localhost$/ ) or
      ( $ThisLine =~ /^\/usr\/bin\/sudo:/) or
      ( $ThisLine =~ /^sudo:/) or
      ( $ThisLine =~ /^halt:/) or
      ( $ThisLine =~ /^reboot:/) or
      ( $ThisLine =~ /^pam_xauth\[\d+\]: call_xauth: child returned \d/) or
      ( $ThisLine =~ /^passwd\[\d+\]:/) or
      ( $ThisLine =~ /warning: can.t get client address: Connection refused/) or
      ( $ThisLine =~ /^xinetd\[\d+\]: USERID: ([^ ]+) (.+)$/ ) or
      ( $ThisLine =~ /^(xinetd|xinetd-ipv6)\[\d+\]: EXIT: /)
   ) {
      # Ignore these entries
   } elsif ($ThisLine =~ /^spop3d/) {
      @line=split(": ",$ThisLine);
      if ($line[1]=~/^session opened for user/) {
         $spop3d_opened++;
         @bzz=split(" ",$line[1]);
         $PopUser= $bzz[4];
         $PopLogin{$PopUser}++;
      } if ($line[1]=~/^authentication failure;/) {
         # authentication failure; logname= uid=0 euid=0 tty=
         # ruser= rhost=  user=xavier
         $spop3d_errors++;
         @bzz=split(" user=",$line[1]);
         $PopErr=$bzz[1];
         $PopErrors{$PopErr}++;
      }
   } elsif ( ($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 ($Ignore =~ /\b\Q$Service\E\b/i) { next; }
      if ($Summarize =~ /\Q$Service\E/) { 
         $Connections->{$Service}++;
      } else {
         $Name = LookupIP($IP);
         $Connections->{$Service}->{$Name}++;
      }
   #Solaris inetd this works if you start "inetd -s -t" then send daemon.notice to authlog -mgt
   } elsif ( ($Service, $IP) = ($ThisLine =~ /^inetd\[\d+\]: (\w+)\[\d+\] from ([^ \n]+) \d+$/) ) {
      if ($Ignore =~ /\b\Q$Service\E\b/i) { next; }
      if ($Summarize =~ /\Q$Service\E/) {
         $Connections->{$Service}++;
      } else {
         $Name = LookupIP($IP);
         $Connections->{$Service}->{$Name}++;
      }
   } elsif ( ($Service,undef,$Name) = ($ThisLine =~ /^([^ ]+)\[\d+\]: warning: ([^ ]+), line \d+: can't verify hostname: getaddrinfo\(([^ ]+), AF_INET\) failed$/) ) {
      $NameVerifyFail{$Service}{$Name}++;
   } elsif ( ($Service,undef,$Name,$IP) = ($ThisLine =~ /^([^ ]+)\[\d+\]: warning: ([^ ]+), line \d+: host name\/name mismatch: ([^ ]+) != ([^ ]+)$/) ) {
      $NameVerifyFail{$Service}{"$Name != $IP"}++;
   } elsif ( ($Display, $User) = ($ThisLine =~ /^xscreensaver\[\d+\]: FAILED LOGIN \d ON DISPLAY \"([^ ]+)\", FOR \"([^ ]+)\"$/) ) {
      $FailedSaver{$User}{$Display}++;
   } elsif ( $ThisLine =~ s/^([^ ]+)\[\d+\]: warning: can\'t get client address: No route to host$/$1/ ) {
      $NoIP->{$ThisLine}++;
   } 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 ( ($Service,$Err) = ($ThisLine =~ /^([^ ]+): (password mismatch for [^ ]+ in [^ ]+):.*$/ ) ) {
      $Error{$Service}{$Err}++;
   } elsif ( $ThisLine =~ /^login: ROOT LOGIN ON tty[0-9]+/) {
      $RootLoginTTY++
   } elsif ( (undef,$User) = ($ThisLine =~ /^login: LOGIN ON (tty|pts\/)[0-9]+ BY ([^ ]+)/ )) {
      $UserLogin{$User}++;
   } elsif ( $ThisLine =~ s/^userdel\[\d+\]: delete user `(.+)'/$1/ ) {
      push @DeletedUsers, "   $ThisLine\n";
   } elsif ( $ThisLine =~ s/^(useradd|adduser)\[\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/^groupdel\[\d+\]: remove group `(.+)'/$1/ ) {
      push @DeletedGroups, "   $ThisLine\n";
   } elsif ( $ThisLine =~ s/^(useradd|adduser)\[\d+\]: new group: name=(.+), gid=(\d+).*$/$1 ($2)/ ) {
      push @NewGroups, "   $ThisLine\n";
   } elsif ( (undef,$User,,undef,$Group) = ($ThisLine =~ /(usermod|useradd)\[\d+\]: add `([^ ]+)' to (shadow |)group `([^ ]+)'/ )) {
      $AddToGroup{$Group}{$User}++;
   } elsif ( $ThisLine =~ s/^groupadd\[\d+\]: new group: name=(.+), gid=(\d+).*$/$1 ($2)/ ) {
      push @NewGroups, "   $ThisLine\n";
   } elsif ( $ThisLine =~ /^userdel\[\d+\]: delete `(.*)' from (shadow |)group `(.*)'\s*$/ ) {
      push @RemoveFromGroup, "    user $1 from group $3\n";
      # 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 =~ /^\/usr\/bin\/sudo: ([^\s]+) : (command not allowed)?.+ ; COMMAND=(.*)$/ ) {
      # sudo unauthorized commands
      push @SudoList, "$1: $3\n" unless ($2 eq "");
   } elsif ( ($service, $from) = ($ThisLine =~ /^xinetd\[\d+\]: FAIL: (.+) (?:address|libwrap) from=([\d.]+)/)) {
      if ($Ignore =~ /\b\Q$service\E\b/i) { next; }    
      $Refused->{$service}->{$from}++;
   } elsif ( ($User) = ($ThisLine =~ /^chage\[\d+\]: changed password expiry for ([^ ]+)/)) {
      $PasswordExpiry{$User}++;
   } elsif ( (undef) = ($ThisLine =~ /^pam_console\[\d+\]: console file lock already in place ([^ ]+)/ )) {
      $ConsoleLock++;
   } elsif ( ($Message) = ($ThisLine =~ /^pam_xauth\[\d+\]: call_xauth: (.+)/)) {
      $XauthMessage{$Message}++;
   } elsif ( ($Group,$NewName) = ($ThisLine =~ /^groupmod\[\d+\]: change group `(.*)' to `(.*)'/)) {
      $GroupRenamed{"$Group -> $NewName"}++;
   } elsif ( ($User,$Home,$NewHome) = ($ThisLine =~ /^usermod\[\d+\]: change user `(.*)' home from `(.*)' to `(.*)'/)) {
      $HomeChange{$User}{"$Home -> $NewHome"}++;
   } elsif ( ($User,$From,$To) = ($ThisLine =~ /^usermod\[\d+\]:change user `(.*)' UID from `(.*)' to `(.*)'/)) {
      $UidChange{"$User: $From -> $To"}++;
   } elsif ( ($User,$From,$To) = ($ThisLine =~ /^usermod\[\d+\]: change user `(.*)' GID from `(.*)' to `(.*)'/)) {
      $GidChange{"$User: $From -> $To"}++;
   # checkpassword-pam
   } elsif ( ($PID) = ($ThisLine =~ /^checkpassword-pam\[(\d+)\]: Reading username and password/)) {
   } elsif ( ($PID,$Username) = ($ThisLine =~ /^checkpassword-pam\[(\d+)\]: Username '([^']+)'/)) {
      $ChkPasswdPam{$PID}{'Username'} = $Username;
   } elsif ( ($PID) = ($ThisLine =~ /^checkpassword-pam\[(\d+)\]: Password read successfully/)) {
   } elsif ( ($PID,$Service) = ($ThisLine =~ /^checkpassword-pam\[(\d+)\]: Initializing PAM library using service name '([^']+)'/)) {
      $ChkPasswdPam{$PID}{'Service'} = $Service;
   } elsif ( ($PID) = ($ThisLine =~ /^checkpassword-pam\[(\d+)\]: Pam library initialization succeeded/)) {
   } elsif ( ($PID) = ($ThisLine =~ /^checkpassword-pam\[(\d+)\]: conversation\(\): msg\[0\], style PAM_PROMPT_ECHO_OFF, msg = "Password: "/)) {
   } elsif ( ($PID) = ($ThisLine =~ /^checkpassword-pam\[(\d+)\]: Authentication passed/)) {
      $ChkPasswdPam{$PID}{'Success'} = 'true';
   } elsif ( ($PID) = ($ThisLine =~ /^checkpassword-pam\[(\d+)\]: Account management succeeded/)) {
   } elsif ( ($PID) = ($ThisLine =~ /^checkpassword-pam\[(\d+)\]: Setting PAM credentials succeeded/)) {
   } elsif ( ($PID) = ($ThisLine =~ /^checkpassword-pam\[(\d+)\]: Terminating PAM library/)) {
   } elsif ( ($PID) = ($ThisLine =~ /^checkpassword-pam\[(\d+)\]: Exiting with status 0/)) {
   } 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@NewGroups\n";
}

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

if (keys %GroupRenamed) {
   print "\nRenamed groups:\n";
   foreach $Group (sort {$a cmp $b} keys %GroupRenamed) {
      print "   $Group\n";
   }
}

if (keys %AddToGroup) {
   print "\nAdded User to group:\n";
   foreach $Group (sort {$a cmp $b} keys %AddToGroup) {
      print "   $Group:\n";
      foreach $User (sort {$a cmp $b} keys %{$AddToGroup{$Group}}) {
         print "      $User\n";
      }
   }
}

if (@RemoveFromGroup) {
   print "\nRemoved From Group:\n@RemoveFromGroup\n";
}

if (keys %HomeChange) {
   print "\nChanged users home directory:\n";
   foreach $User (sort {$a cmp $b} keys %HomeChange) {
      print "   $User:\n";
      # No sorting here - show it by time...
      foreach $Home (keys %{$HomeChange{$User}}) {
         print "      $Home\n";
      }
   }
}

if (keys %UidChange) {
   print "\nChanged users UID:\n";
   foreach $Entry (sort {$a cmp $b} keys %UidChange) {
      print "   $Entry\n";
   }
}

if (keys %GidChange) {
   print "\nChanged users GID:\n";
   foreach $Entry (sort {$a cmp $b} keys %GidChange) {
      print "   $Entry\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 SortIP keys %{$Connections->{$ThisOne}}) {
            print "      " . $OtherOne . ": " . $Connections->{$ThisOne}->{$OtherOne} . " Time(s)\n";
         }
      }
   }
}

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

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

if (keys %{$FailedSaver}) {
   print "\nFailed screensaver disable:\n";
   foreach $User (sort {$a cmp $b} keys %{$FailedSaver}) {
      print "   User $User on displays:\n";
      foreach $Display (sort {$a cmp $b} keys %{$FailedSaver{$User}}) {
         print "      $Display : $FailedSaver{$User}{$Display} Time(s)\n";
      }
   }
}

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

if (keys %NameVerifyFail) {
   print "\nHostname verification failed:\n";
   foreach $Service (sort {$a cmp $b} keys %NameVerifyFail) {
      print "   Service $Service:\n";
      foreach $Name (sort {$a cmp $b} keys %{$NameVerifyFail{$Service}}) {
         print "      $Name: rel5_2_2 $NameVerifyFail{$Service}{$Name} 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 (keys %UserLogin) {
   print "\nUser Login's:\n";
   foreach $User (sort {$a cmp $b} keys %UserLogin) {
      print "   $User : $UserLogin{$User} Time(s)\n";
   }
}

if ($ConsoleLock > 0) {
   print "\nConsole file lock already in place: $ConsoleLock Time(s).\n";
}

if (keys %PasswordExpiry) {
   print "\nChanged password expiry for users:\n";
   foreach $User (sort {$a cmp $b} keys %PasswordExpiry) {
      print "   $User : $PasswordExpiry{$User} Time(s)\n";
   }
}

if (keys %XauthMessage) {
   print "\nReported by call_xauth:\n";
   foreach $Message (sort {$a cmp $b} keys %XauthMessage) {
      print "   $Message : $XauthMessage{$Message} Time(s)\n";
   }
}

if (keys %PopLogin) {
   print "\nspop3d user connections:\n";
   foreach $PopUser (sort {$a cmp $b} keys %PopLogin) {
      print "   $PopUser\:\t$PopLogin{$PopUser} Time(s)\n";
   }
}

if (keys %PopErrors) {
   print "\nspop3d  connection failures:\n";
   foreach $PopErr (sort {$a cmp $b} keys %PopErrors) {
      print "   $PopErr\:\t$PopErrors{$PopErr} Time(s)\n";
   }
}

if ($spop3d_opened > 0) {
   print "\nspop3d connections(sum):\t".$spop3d_opened."\n";
}

if ($spop3d_errors > 0) {
   print "spop3d connection errors:\t".$spop3d_errors."\n";
}

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

if (keys %ChkPasswdPam) {
   print "\ncheckpassword-pam (SUID root PAM client):\n";
   foreach $PID (sort {$a cmp $b} keys %ChkPasswdPam) {
      $ServiceUsernamePair = $ChkPasswdPam{$PID}{'Username'}.' => '.$ChkPasswdPam{$PID}{'Service'};
      if ($ChkPasswdPam{$PID}{'Success'} eq 'true') {
         $Successes{$ServiceUsernamePair}++;
      } else {
         $Failures{$ServiceUsernamePair}++;
      }
   }
   foreach $ServiceUsernamePair (sort {$a cmp $b} keys %Successes) {
      $S = $Successes{$ServiceUsernamePair} ? $Successes{$ServiceUsernamePair} : 0;
      $F = $Failures{$ServiceUsernamePair} ? $Failures{$ServiceUsernamePair} : 0;
      print "   $ServiceUsernamePair : $S success(es), $F failure(s)\n";
   }
}

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

exit(0);

# vi: shiftwidth=3 tabstop=3 et

