#!/usr/bin/perl -s
#
# A hack to reset pam_tally counts which have reached or exceeded a nominated
# value, after a nominated time. Put it in root's cron.
#
# Usage: * * * * * pam_tally_autoreset.pl --count=3 --delay=30
#
# Resets counts at or above [count] once [delay] minutes have passed.
#
# (c) 2003 Tim Baverstock.
# License: GPL, see http://www.gnu.org.

die "Usage: $0 --count=n --delay=n\n" unless ${-count} and ${-delay};

my $STATUS="/var/log/faillog.resets";
my $PAM_TALLY="pam_tally";
my %UserTime;
my $now=time;

# Read outstanding blocked accounts (First run should create $STATUS)

open RESETS, $STATUS or warn "$0: Unable to read $STATUS: $!\n";
while ( <RESETS> ) {
  my ( $user, $time ) = split;
  $UserTime{$user}=$time;
}
close RESETS;

# Add any new blocked accounts

open TALLY, "pam_tally|" or die "$0: Unable to pipe from pam_tally: $!\n";
while ( <TALLY> ) {
# User warwick    (500)   has 0
  /^User\s+(\w+)\s+\(\d+\)\s+has\s(\d+)/ or warn "$0: Can't parse $_\n";
  my ( $user, $count ) = ( $1, $2 );
  if ( $count >= ${-count} and not exists $UserTime{$user} ) {
    $UserTime{$user} = $now;
  }
}
close TALLY;

# Scan blocked accounts for ones to unblock, and rewrite outstanding file

open RESETS, ">$STATUS" or die "$0: Unable to write $STATUS: $!\n";
for ( keys %UserTime ) {
  if ( $now - ${-delay}*60 >= $UserTime{$_} ) {
    system "$PAM_TALLY --user=$_ --reset >/dev/null";
  }
  else {
    print RESETS "$_ $UserTime{$_}\n";
  }
}
close RESETS;

# Hide contents of $STATUS

chmod 600, $STATUS;


