
########################################################
# Please file all bug reports, patches, and feature
# requests under:
#      https://sourceforge.net/p/logwatch/_list/tickets
# Help requests and discusion can be filed under:
#      https://sourceforge.net/p/logwatch/discussion/
########################################################

#######################################################
## Copyright (c) 2008 Kirk Bauer
## Covered under the included MIT/X-Consortium License:
##    http://www.opensource.org/licenses/mit-license.php
## All modifications and contributions by other persons to
## this script are assumed to have been donated to the
## Logwatch project and thus assume the above copyright
## and licensing terms.  If you want to make contributions
## under your own copyright or a different license this
## must be explicitly stated in the contribution an the
## Logwatch project reserves the right to not accept such
## contributions.  If you have made significant
## contributions to this script and want to claim
## copyright please contact logwatch-devel@lists.sourceforge.net.
#########################################################

##########################################################################
# TO-DO
# We really should search for specific strings (authentication failure,
# bad username, check pass, password changed, session opened/closed,
# account expired, etc., using the service name as a variable in the hash,
# instead of having to add a test for every new service.
###########################################################################

use strict;

use Logwatch ':sort';

my $Detail = $ENV{'LOGWATCH_DETAIL_LEVEL'} || 0;

my ($service, $line, %data);

while ($line = <STDIN>) {
   chomp $line;
   $service = $line;
   # for fedora and others
   if ($line =~ s/^... .. ..:..:.. .+ .+\(pam_unix\)\[\d+\]: //) {
      $service =~ s/^... .. ..:..:.. .+ (.+)\(pam_unix\)\[\d+\]: .*$/$1/;
   # new fedora (fc5) version
   } elsif ( $line =~ s/^... .. ..:..:.. .+ pam_unix\(.+:.+\): //  ) {
      $service =~ s/^... .. ..:..:.. .+ pam_unix\((.+):.+\): .*$/$1/;
   # fedora with pam_sss
   } elsif ( $line =~ s/^... .. ..:..:.. .+ pam_sss\(.+:.+\): //  ) {
      $service =~ s/^... .. ..:..:.. .+ pam_sss\((.+):.+\): .*$/$1/;
   # for debian sarge - "normal" lines
   } elsif ($line =~ s/^... .. ..:..:.. .+ [^ :]+: \(pam_unix\) //) {
      $service =~ s/^... .. ..:..:.. .+ ([^\s:\[\]]+)(?:\[[0-9]+\]|): \(pam_unix\) .*$/$1/;
   #for debian sarge - kdm - why can't they log in standard-compliant way?
   } elsif ( $line =~ s/^... .. ..:..:.. .+ [^\s:\[\]]+: [0-9:\[\]\.]+ \(pam_unix\) //) {
      $service =~ s/^... .. ..:..:.. .+ ([^\s:\[\]]+): [0-9:\[\]\.]+ \(pam_unix\) .*$/$1/;
   #for debian woody
   } elsif ( $line =~ s/^... .. ..:..:.. .+ PAM_unix\[\d+\]: \((.*?)\) // ) {
      $service =~ s/^... .. ..:..:.. .+ PAM_unix\[\d+\]: \(([^ ]*)\) .*/$1/;
   # for Ubuntu 7.10
   } elsif ( $line =~ s/^... .. ..:..:.. .+ \S+\[\d+\]: pam_unix_\S+\(.+:.+\): //  ) {
      $service =~ s/^... .. ..:..:.. .+ \S+\[\d+\]: pam_unix_\S+\((.+):.+\): .*$/$1/;
   # for debian and others ?
   } elsif ($line =~ s/^... .. ..:..:.. \S+ \S+\[\d+\]: PAM //) {
      $service =~ s/^... .. ..:..:.. \S+ (\S+)\[\d+\]: PAM .*/$1/;
   } else {
      next;
   }
   # handle password expiring globally
   if ($line =~ /^password for user (.+) will expire in (\d+) days/) {
      $data{"all"}{'Password Expiring'}{"$1 in $2 days"}++;
      next;
   }
   #lowercase the service
   $service = lc($service);
   if ( grep $_ eq $service, qw/ssh sshd login ftp vsftpd proftpd rsh remote rlogin rexec systemd-user/) {
      if ($line =~ s/^session opened for user (.+) by \(uid=\d+\)/$1/) {
	 ($Detail >= 5) && $data{$service}{'Sessions Opened'}{$line}++;
      } elsif ($line =~ s/^session opened for user ([^ ]*) by ([^ ]*)\(uid=\d+\)/$1 by $2/) {
         ($Detail >= 5) && $data{$service}{'Sessions Opened'}{$line}++;
      } elsif ($line =~ s/^session opened for user (.+) by LOGIN\(uid=\d+\)/$1/) {
	 $data{$service}{'Sessions Opened'}{$line}++;
      } elsif ($line =~ /session closed for user/) {
      } elsif ($line =~ /^service\(sshd\) ignoring max retries/) {
	# ignore these lines
      } elsif ($line =~ s/^authentication failure; .*rhost=(\S*)\s+user=(\S*)$/$2 ($1)/) {
 	 $data{$service}{'Authentication Failures'}{$line}++;
      } elsif ($line =~ s/^authentication failure; .*rhost=(\S*)\s*$/unknown ($1)/) {
         $data{$service}{'Authentication Failures'}{$line}++;
      } elsif ($line =~ s/^authentication failure; logname=(\S*) uid=(\d+) .*user=(\S*)$/$1($2) -> $3/) {
         $data{$service}{'Authentication Failures'}{$line}++;
      } elsif ($line =~ s/^authentication failure; logname=(\S*) .*rhost=(\S*)\s+user=(\S*)$/($3 or $1)($2): /) {
         $data{$service}{'Authentication Failures'}{$line}++;
      } elsif ($line =~ s/^(\d+) more authentication failures?; .*rhost=(\S*)\s+user=(\S*)$/$3 ($2)/) {
         $data{$service}{'Authentication Failures'}{$line} += $1;
      } elsif ($line =~ s/^(\d+) more authentication failures?; .*rhost=(\S*)\s*$/unknown ($2)/) {
         $data{$service}{'Authentication Failures'}{$line} += $1;
      } elsif ($line =~ /check pass; user unknown/) {
         $data{$service}{'Invalid Users'}{'Unknown Account'}++;
      } elsif ($line =~ s/^password changed for (.+)/$1(by sshd)/) {
         ($Detail >= 5) && $data{passwd}{'Password changed'}{$line}++;
      } elsif ($line =~ s/^account (.+) has expired \((?:failed to change password|account expired)\)$/$1/) {
         $data{$service}{'Expired Accounts'}{$line}++;
      } elsif ($line =~ s/bad username \[(.*)\]/$1/) {
         $data{$service}{'Invalid Users'}{"Bad User: $line"}++;
      } elsif ($line =~ s/^authentication success; logname=(\S*) uid=(\d+) .*user=(\S*)$/$1($2) -> $3/) {
         ($Detail >= 5) && $data{$service}{'Authentication Success'}{$line}++;
      } else {
         $data{$service}{'Unknown Entries'}{$line}++;
      }
   } elsif (grep $_ eq $service, qw/su sudo su-l polkit-1/) {
      if ( my ($logname, $uid, $ruser, $user) = ($line =~ /^authentication failure; logname=(\S*)\s+uid=(\d+) (?:.*ruser=(\S*)\s+)?.*user=(\S*)$/)) {
         $line = ($logname or $ruser)."($uid) -> $user";
         $data{$service}{'Authentication Failures'}{$line}++;
      } elsif ($line =~ /session closed for user/) {
         # ignore this line
      } elsif ($line =~ /conversation failed/) {
         # ignore this line. An other line will describe the reason.
      } elsif (my ($nam, $byid) = ($line =~ /session opened for user (.+) by (.+)$/)) {
         # resolve uid to name if possible
         my $onam;
         if ($byid =~ s/^\(uid=(\d+)\)$/$1/) {
           $onam = getpwuid($byid) or $byid;
         } elsif ($byid =~ s/^(\S+)\(uid=\d+\)$/$1/) {
           $onam = $byid;
         } else {
           $onam = $byid;
         }
         $data{$service}{'Sessions Opened'}{"$onam -> $nam"}++;
      } elsif ($line =~ s/auth could not identify password for \[(.*)\]/$1/) {
         $data{$service}{'Not Identify Password For'}{$line}++;
      } elsif ($line =~ /^account root has password changed in future/) {
         #I'm not sure whether this info could not be reported
      } else {
         $data{$service}{'Unknown Entries'}{$line}++;
      }
   } elsif (grep $_ eq $service, qw/passwd propassd/) {
      if ($line =~ s/^password changed for (.+)/$1/) {
         ($Detail >= 5) && $data{$service}{'Password changed'}{$line}++;
      }
   } elsif (grep $_ eq $service, qw/gdm gdm-password gdm-welcome gdm-launch-environment kdm kcheckpass xdm imap dovecot cups/) {
      if ($line =~ s/^session opened for user (.+) by (?:\(unknown\)|\w+)?\(uid=\d+\)/$1/) {
         ($Detail >= 5) && $data{$service}{'Sessions Opened'}{$line}++;
      } elsif ($line =~ s/^authentication failure;.* user=(.+)$/$1/) {
         $data{$service}{'Authentication Failures'}{$line}++;
      } elsif ($line =~ s/^authentication failure;.* ruser=(.+) rhost=.+$/$1/) {
         $data{$service}{'Authentication Failures'}{$line}++;
      } elsif ($line =~ /check pass; user unknown/) {
         $data{$service}{'Invalid Users'}{'Unknown Account'}++;
      } elsif ($line =~ /session closed for user/) {
         # ignore this line
      } elsif ($line =~ s/^authentication success; logname=(\S*) uid=(\d+) .*user=(\S*)$/$1($2) -> $3/) {
         ($Detail >= 5) && $data{$service}{'Authentication Success'}{$line}++;
      } elsif ($line =~ /received for user.*Permission denied/) {
	# ignore this line - paired with authentication failure
      } else {
         $data{$service}{'Unknown Entries'}{$line}++;
      }

   } elsif (grep $_ eq $service, qw/spop3d pop/) {
	   if ($line =~ s/^session opened for user (.+)/$1/) {
		   $data{$service}{'Sessions Opened'}{$line}++;
	   } elsif ($line =~ /session closed for user/) {
		   # ignore this line
      } elsif ($line =~ s/^authentication failure; .*user=(.+)$/$1/) {
		   $data{$service}{'Authentication Failures'}{$line}++;
      } elsif ($line =~ s/^account (.+) has expired (failed to change password)$/$1/) {
         $data{$service}{'Expired Accounts'}{$line}++;
	   } else {
		   $data{$service}{'Unknown Entries'}{$line}++;
	   }
   } elsif ($service eq 'tpop3d') {
      if ($line =~ s/^authentication failure; .*rhost=(.+)  user=(.+)$/$2 ($1)/) {
         $data{$service}{'Authentication Failures'}{$line}++;
      } else {
         $data{$service}{'Unknown Entries'}{$line}++;
      }
   } elsif (grep $_ eq $service, qw/pure-ftpd vsftpd/) {
      if ($line =~ s/^session opened for user (.+)/$1/) {
         $data{$service}{'Sessions Opened'}{$line}++;
      } elsif ($line =~ s/^check pass; (.+)/$1/) {
         $data{$service}{'Password Failures'}{$line}++;
      } elsif ($line =~ s/^authentication failure; .*user=(.+)$/$1/) {
         $data{$service}{'Authentication Failures'}{$line}++;
      } else {
         $data{$service}{'Unknown Entries'}{$line}++;
      }
   } elsif (grep $_ eq $service, qw/xscreensaver gnome-screensaver kscreensaver/) {
      if ($line =~ s/^authentication failure; .*uid=(\d+) euid=(\d+) tty=(.+) ruser= rhost=  user=(.+)$/$4($1,$2) on display $3/) {
         $data{$service}{'Authentication Failures'}{$line}++;
      }
   } elsif ($service =~ /^(?:\/[\w\/]+\/|f)?crond?$/ ) {
	   if ($line =~ s/^session opened for user (.+) by \(uid=\d+\)/$1/) {
		   ($Detail >= 5) && $data{$service}{'Sessions Opened'}{$line}++;
	   } elsif ($line =~ /session closed for user/) {
		   # ignore this line
	   } elsif ($line =~ /^account root has password changed in future/) {
	          #I'm not sure whether this info could not be reported
           } elsif ($line =~ /^adding faulty module: (.+)/) {
                  $data{$service}{'Faulty modules'}{$1}++; 
           } elsif ($line =~ /^unable to dlopen\(.+\): (.+)$/) {
                  $data{$service}{'Unable to dlopen'}{$1}++; 
	   } else {
		   $data{$service}{'Unknown Entries'}{$line}++;
	   }
   } elsif ($service eq 'cyrus') {
      if ($line =~ /check pass; user unknown/) {
         $data{$service}{'Invalid Users'}{'Unknown Account'}++;
      } elsif ($line =~ /authentication failure; logname= uid=0 euid=0 tty= ruser= rhost=/) {
         # ignore this line
      } else {
         $data{$service}{'Unknown Entries'}{$line}++;
      }
   } elsif (grep $_ eq $service, qw/samba smbd/) {
      if ($line =~ s/^session opened for user (\S+) by (.+)/$1/) {
         ($Detail >= 5) && $data{$service}{'Sessions Opened'}{$line}++;
      } elsif ($line =~ s/^session closed for user (.+)/$1/) {
         ($Detail >= 8) && $data{$service}{'Sessions Closed'}{$line}++;
      } else {
         $data{$service}{'Unknown Entries'}{$line}++;
      }
   } elsif (grep $_ eq $service, qw/runuser runuser-l/) {
     if ($line =~/^session (opened)?(\/)?(closed)? for user [\w\.\-]+/) {
     } else {
         $data{$service}{'Unknown Entries'}{$line}++;
     }
   } elsif ($service eq 'atd') {
     if ($line =~/^session (opened)?(\/)?(closed)? for user [\w\.\-]+/) {
     } elsif ($line =~ /^account root has password changed in future/) {
       #I'm not sure whether this info could not be reported
     } else {
         $data{$service}{'Unknown Entries'}{$line}++;
     }
   } elsif ($service eq 'system-config-date') {
      if ($line =~ s/auth could not identify password for \[(.*)\]/$1/) {
         $data{$service}{'Not Identify Password For'}{$line}++;
      } else {
         $data{$service}{'Unknown Entries'}{$line}++;
      }
   } elsif ($service eq 'smtp') {
      if ($line =~ s/^authentication failure; logname=(\S*) uid=(\d+).*user=(\S*)$/$1($2) -> $3/) {
         $data{$service}{'Authentication Failures'}{$line}++;
      } elsif ($line =~ /authentication failure; logname= uid=0 euid=0 tty= ruser= rhost=/) {
         # ignore this line
      } elsif ($line =~ /check pass; user unknown/) {
         $data{$service}{'Invalid Users'}{'Unknown Account'}++;
      } else  {
         $data{$service}{'Unknown Entries'}{$line}++;
      }
   } else {
      $data{$service}{'Unknown Entries'}{$line}++;
   }
}

foreach my $service (sort {$a cmp $b} keys %data) {
   print "$service:\n";
   foreach my $type (sort {$a cmp $b} keys %{$data{$service}}) {
      print "   $type:\n";
      my $sort = CountOrder(%{$data{$service}{$type}});
      foreach my $entry (sort $sort keys %{$data{$service}{$type}}) {
         print "      $entry: $data{$service}{$type}{$entry} Time(s)\n";
      }
   }
   print "\n";
}

exit(0);

# vi: shiftwidth=3 tabstop=3 syntax=perl et
# Local Variables:
# mode: perl
# perl-indent-level: 3
# indent-tabs-mode: nil
# End:
