
##########################################################################
# freeradius logwatch filter
#	written by Jonas Marczona 28.12.2011 
#
## 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.
##########################################################################

$^W=1;
use strict;

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

my $DebugCounter = 0;

if ( $Debug >= 5 ) {
   print STDERR "\n\nDEBUG: Inside freeradius Filter \n\n";
   $DebugCounter = 1;
}

my %OtherList = ();

my %loginsOk = ();
my %wrongPassword = ();
my %wrongUser = ();
my %wrong_ip = ();
my %invalidUser = ();
my %discards = ();
my %warnings = ();
my %givingUps = ();
my $killedChilds = 0;
my $requests = 0;
my $requests_duration = 0;

my $ThisLine;
while (defined($ThisLine = <STDIN>)) {
   if ( $Debug >= 5 ) {
      print STDERR "DEBUG($DebugCounter): $ThisLine";
      $DebugCounter++;
   }
   chomp($ThisLine);

   if ( ( $ThisLine =~ /^F-TICKS/ ) ||
        ( $ThisLine =~ /^Access-Request from/ ) ||
        ( $ThisLine =~ /^ \.\.\. (?:closing|adding new) socket/ ) ||
        ( $ThisLine =~ /^(?:SSL|TLS|rlm_(?:unix|eap|sql|radutmp)|    TLS_accept|  \[ldap\])/ ) ||
        ( $ThisLine =~ /^Ready to process requests\.$/ ) ||
        ( $ThisLine =~ /^Exiting normally\.$/ ) ||
        ( $ThisLine =~ /^Loaded virtual server/ ) ||
        ( $ThisLine =~ /^HUP - / ) ||
        ( $ThisLine =~ /^Received HUP signal\.$/ ) ||
        ( $ThisLine =~ /^ ?Module: Reloaded module/ ) 
      ) {
      # ignore
   }

   # TD: Login OK: [user@example.com] (from client radius port 0)
   # TD: Login OK: [user@example.com] (from client radius port 9 cli 00-11-22-33-44-AA;eduroam via TLS tunnel)
   elsif ( my ($user) = ($ThisLine =~ m/^(?:Auth: )?Login OK: \[(.+)\] \(from client [^ ]* port \d{1,10}(?: cli [-0-9a-fA-F.:]+)?(?:;\w+)?(?: via TLS tunnel)?\)/) ) {
      $loginsOk{$user}++;

   }

   # TD: Login incorrect (  [ldap] User not found): [user@example.com] (from client radius port 13 cli 38-16-dd-aa-bb-cc via TLS tunnel)
   # TD: Login incorrect (mschap: External script says Logon failure (0xc000006d)): [user@example.com] (from client radius port 13 cli aa-bb-cc-11-22-33 via TLS tunnel)
   # TD: Login incorrect (TLS Alert write:fatal:handshake failure): [user@example.com] (from client radius port 13 cli aa-bb-cc-11-22-33)
   # TD: 
   elsif ( my ($user, $ip) = ( $ThisLine =~ m/^(?:Auth: )?Login incorrect(?: \(.+\))?: \[(.*)\] \(from client [^ ]* port \d{1,10}(?: cli ([-0-9a-fA-F.:]+)(?:;\w+)?)?(?: via TLS tunnel)?\)/) ) {
      if (! $ip) { $ip = "*not named*"; }
      $wrongUser{$ip}{$user}++;
      $wrong_ip{$ip}++;

   }

   # TD: Login incorrect: [user@example.com] (from client radius port 175143 cli cc08.e051.a240)
   # TD: Login incorrect: [user@example.com] (from client radius1 port 0) 
   elsif ( my ($user, $ip) = ($ThisLine =~ m/^(?:Auth: )?Login incorrect: \[(.+)\] \(from client [^ ]* port \d{1,10}(?: cli ([-0-9a-fA-F.:]+))?(?: via TLS tunnel)?\)/) ) {
      if (! $ip) { $ip = "*not named*"; }
      $wrongPassword{$ip}{$user}++;
      $wrong_ip{$ip}++;
   }

   # TD: Invalid user (  [ldap] Access Attribute denies access): [user@example.com] (from client radius port 13 cli aa-bb-cc-dd-ee-11 via TLS tunnel)
   # TD: Invalid user: [user@example.com] (from client <host> port 13 cli aa-bb-cc-dd-ee-11)
   elsif ( my ($reason, $user, $ip) = ($ThisLine =~ m/^(?:Auth: )?Invalid user(?: \(\s*(.+)\))?: \[(.+)\] \(from client [^ ]* port \d{1,10}(?: cli ([-0-9a-fA-F.:]+))?(?: via TLS tunnel)?\)/) ) {
      if (! $ip) { $ip = "*not named*"; }
      if (! $reason) { $reason = "*no reason*"; }
      $invalidUser{$reason}{$user}++;
   }

   # TD: Discarding duplicate request from client <host> port 47609 - ID: 182 due to unfinished request 12713766
   # TD: Discarding conflicting packet from client <host> port 42221 - ID: 85 due to recent request 9008535.
   elsif ( my ($reason, $client) = ($ThisLine =~ /Discarding (duplicate request|conflicting packet) from client (\S+) port \d+ - ID: \d+ due to (unfinished|recent) request/) ) {
      $discards{$reason}{$client}++;
   }

   # TD: Received conflicting packet from client radius2 port 60612 - ID: 30 due to unfinished request 1136681.  Giving up on old request.
   elsif ( my ($client) = ($ThisLine =~ /Received conflicting packet from client ([^ ]+) port \d{1,10} - ID: \d+ due to unfinished request \d+/) ) {
      $givingUps{$client}++;
   }

   # TD: Child PID 57436 is taking too much time: forcing failure and killing child.
   elsif ( $ThisLine =~ m/Child PID \d+ is taking too much time: forcing failure and killing child/ ) {
      $killedChilds++;
   }

   # TD: Request 67678577 has been waiting in the processing queue for 378 seconds.  Check that all databases are running properly!
   elsif ($ThisLine =~ m/^Request \d+ has been waiting in the processing queue for (\d+) seconds/) {
      $requests++;
      $requests_duration += $1;
   }

   # TD: WARNING: Unresponsive child for request 4737598, in component accounting module unix
   # TD: WARNING: Child is hung for request 4737598 in component accounting module unix.
   elsif ( $ThisLine =~ m/^WARNING: (Unresponsive child|Child is hung) for request \d+,? in component ([<>\w]+) module ?([<>\w]*)/ ) {
      $warnings{"$1 in component:"}{"$2 [module: $3]"}++;
   }

   # TD: WARNING: Allowing fast client radius2 port 60612 - ID: 102 for recent request 9035637.
   elsif ( $ThisLine =~ m/^WARNING: (Allowing fast client) ([^ ]+) port \d{1,10} - ID: \d+/ ) {
      $warnings{"${1}s:"}{$2}++;
   }

   else {
      # Report any unmatched entries...
      $OtherList{$ThisLine}++;
   }
}


#################################
# Output section
################################

if ($requests > 0) {
   printf "Long running requests: Check that all databases are running properly!\n";
   printf "  %-40s : %5d\n", 'Long running requests', $requests;
   printf "  %-40s : %5d s\n", 'avg queue time per long running request', $requests_duration / $requests;
   print "\n";
}


sub compPerIp {
  return $wrong_ip{$b} <=> $wrong_ip{$a};
}

if (keys %loginsOk) {
   if ($Detail >= 10) {
      print "\nSuccessful logins:\n";
      foreach my $user (sort {$loginsOk{$b} <=> $loginsOk{$a}} keys %loginsOk) {
        printf "  %-40s : %5d time(s)\n", $user, $loginsOk{$user};
      }
   } elsif ($Detail >= 6) {
      my $loginsOkSum = 0;
      foreach my $user (keys %loginsOk) {
         $loginsOkSum += $loginsOk{$user};
      }
      printf "\n%-42s : %5d time(s)\n", "Successful logins", $loginsOkSum;
   }
}

if ($Detail >= 8) {
   if (keys %wrong_ip) {
      print "\nSum of failed logins per ip (wrong password or user)\n";
      foreach my $ip (sort compPerIp keys %wrong_ip) {
         printf "  %-40s : %5d time(s)\n", $ip, $wrong_ip{$ip};
      } 
   }
}

if (keys %wrongUser) {
   if ($Detail >= 6) {
      print "\nFailed logins - wrong user name:\n";
      foreach my $ip (sort compPerIp keys %wrongUser) {
         printf "  %-40s\n",  $ip;
         my $users = $wrongUser{$ip};
         foreach my $user (sort {$users->{$b} <=> $users->{$a}} keys %$users) {
            printf "    %-38s : %5d time(s)\n", $user, $users->{$user};
         }
      }
   } else {
      my $userSum = 0;
      foreach my $ip (keys %wrongUser) {
         my $users = $wrongUser{$ip};
         foreach my $user (keys %$users) {
            $userSum += $users->{$user};
         }
      }
      printf "\n%-42s : %5d time(s)\n", "Failed logins - wrong user name", $userSum;
   }
}

if (keys %wrongPassword) {
   if ($Detail >= 6) {
      print "\nFailed logins - wrong password:\n";
      foreach my $ip (sort compPerIp keys %wrongPassword) {
         my $users = $wrongPassword{$ip};
         printf "  %-40s\n", $ip ;
         foreach my $user (sort {$users->{$b} <=> $users->{$a}} keys %$users) {
            #print "    $user ", $users->{$user}, " time(s)\n";
            printf "    %-38s : %5d time(s)\n", $user, $users->{$user};
         }
      }
   } else {
      my $wrongPasswordSum = 0;
      foreach my $ip (%wrongPassword) {
         my $users = $wrongPassword{$ip};
         foreach my $user (keys %$users) {
            $wrongPasswordSum += $users->{$user};
         }
      }
      printf "\n%-42s : %5d time(s)\n", "Failed logins - wrong password", $wrongPasswordSum;
   }
}

if (keys %invalidUser) {
   if ($Detail >= 6) {
      print "\nInvalid User:\n";
      foreach my $reason (keys %invalidUser) {
         my $users = $invalidUser{$reason};
         printf "  %-40s\n", $reason;
         foreach my $user (sort {$users->{$b} <=> $users->{$a}} keys %$users) {
            printf "    %-38s : %5d time(s)\n", $user, $users->{$user};
         }
      }
   } else {
      my $invalidUserSum = 0;
      foreach my $reason (keys %invalidUser) {
         my $users = $invalidUser{$reason};
         foreach my $user (keys %$users) {
            $invalidUserSum += $users->{$user};
         }
      }
      printf "\n%-42s : %5d time(s)\n", "Invalid Users", $invalidUserSum;
   }
}

if (keys %discards) {
   print "\nDiscards:\n";
   foreach my $reason (keys %discards) {
      my $clients = $discards{$reason};
      printf "  %-40s\n", $reason;
      foreach my $client (keys %$clients) {
         printf "    %-38s : %5d time(s)\n", $client, $clients->{$client};
      }
   }
}

if (keys %givingUps) {
  print "\nGiving up on old requests:\n";
  foreach my $client (keys %givingUps) {
     printf "  %-40s : %5d time(s)\n", $client, $givingUps{$client};
  }
}

if ($killedChilds) {
   printf "\n%-42s : %5d time(s)\n", "Killed Childs (taking too much time)", $killedChilds;
}

if (%warnings) {
   print "\nWarnings:\n";
   foreach my $warning (keys %warnings) {
      my $components = $warnings{$warning};
      printf "  %-40s\n", $warning;
      foreach my $component (keys %$components) {
         printf "    %-38s : %5d time(s)\n", $component, $components->{$component};
      }
   }
}

if (keys %OtherList) {
   print "\n**** Unmatched entries ****\n";
   foreach (keys %OtherList) {
      print "    $_ : $OtherList{$_} Time(s)\n";
   }
}

exit(0);

# vi: shiftwidth=3 tabstop=3 syntax=perl et

