#!/usr/bin/perl -w
# 
# script to monitor ldap password and account expiry
# will send an email to each user whose password or account expires within 
# the amount of days defined by the users shadowWarning attribute
#
# will send the mail to the users mail attribute.
#
#
# 09/26/05 - added account filtering to block any accounts that end with 
#  a $ and certain other ones
#
# 09/26/05 - script is now pretty chatty when working,  can comment out 
#  prints or just direct it all to null or a file.  probably best in a file 
#  to check what/if errors occured.
#

use strict;
use warnings;

use Net::LDAP;		# libnet-ldap-perl
use Net::SMTP;

use constant SERVER => 'ldap.com';
use constant LDAPUSER => 'cn=nss,ou=Admins,dcxxxx';
use constant LDAPPASS => 'ldap';
use constant LDAPBASE => 'dc=xxx';
use constant LDAPADMIN => 'admin@somehwere.com';

# epoch multiplier is 86400
# epoch is today since epoch in days (not seconds)
my $epoch = int(time()/60/60/24);

open TMPFILE, ">/tmp/tmpfile" or die "couldn't open file";

# find out why ldap->bind doesn't like variables....

# connect and bind
my $ldap = Net::LDAP->new( SERVER, timeout=>10);
$ldap->bind ( LDAPUSER, password => LDAPPASS,version => 3 );     

# grab all accounts
my $mesg = $ldap->search(	filter=>"(objectClass=posixAccount)",
			base=> LDAPBASE,
			attrs=> ['uid', 'sn', 'mail', 'shadowLastChange', 
				'shadowMax', 'shadowWarning', 'shadowExpire', 'shadowInactive'] );

# create array from results			
my @entries = $mesg->entries;
foreach my $entry (@entries) {
        
	# filter out $ accounts and root,nobody
	next if ($entry->get_value("uid") =~ /\$$/);
	next if ($entry->get_value("uid") =~ /root/);
	next if ($entry->get_value("uid") =~ /nobody/);
	
	print "dn: " . $entry->dn() . "\n";
	my @attrs = $entry->attributes();
	
	foreach my $attr (@attrs) {
   	printf("\t%s: %s\n", $attr, $entry->get_value($attr));
   }
	
	# the number of days before password will expire
	my $pw_days_left = $entry->get_value("shadowLastChange")+$entry->get_value("shadowMax") -$epoch;
	my $acct_days_left = $entry->get_value("shadowExpire") - $epoch;
	
	print "password days left is $pw_days_left\n";
	print "account days left is $acct_days_left\n";
	
	# check password expiry
	if ($pw_days_left <= $entry->get_value("shadowWarning"))	{
		if ($pw_days_left > 0)	{
			# notify user about password expiry, user still in grace period
			print "user must change your password in " . $pw_days_left . " days!";
			print TMPFILE "password expiry:\t" . $entry->get_value("uid") . "\tin" . "\t" . $pw_days_left . " days\n";
			# send warning email to said user
			&password_warning($entry->get_value("mail"), $pw_days_left);
		}
		# if less then 0 then user has passed the expiry point, dead account?
		if ($pw_days_left < 0)	{
			# user has been notified a number of times and done nothing
			print "account is dead\n";
			print TMPFILE "password expired:\t" . $entry->get_value("uid") . "\t\t" . $pw_days_left*(-1) . " days ago, no email sent.\n";
			
		}
	}	

	# check account expiry
	if ($acct_days_left <= $entry->get_value("shadowWarning"))	{
		if ($acct_days_left > 0 )	{
			# user still in grace peroid,  send notice
			print "users account is about to expire in " . ($entry->get_value("shadowExpire") - $epoch) . " days";
			print TMPFILE "Account expiry:\t\t" . $entry->get_value("uid") . "\tin" . "\t" . $acct_days_left . " days\n";
			&acct_expire_warning($entry->get_value("mail"), $acct_days_left);
		}
		if ($acct_days_left < 0 )	{
			# user's account has expired and done nothing about it. send no email but note it
			print "Your account has expired " . $acct_days_left*(-1) . " days ago\n";
			print TMPFILE "Account expired:\t" . $entry->get_value("uid") . "\t\t" . $acct_days_left*(-1) . " days ago, no email sent\n";
		}
	}

}	

# close the filehandle as is write only
close TMPFILE;

# reopen so we can read from it
open TMPFILE, "/tmp/tmpfile" or die "couldn't open file";

# send summary email to root list

my $smtp = Net::SMTP->new('mail.somewhere.com');
# $smtp->mail = name that the email appears to be from
# might need the hello to not get blacklisted at spamhaus
# $smtp->hello ("somewhere.com");
$smtp->mail('from@somewhere.com');
$smtp->to(LDAPADMIN);
$smtp->data();
$smtp->datasend("To: ".LDAPADMIN."\n");
$smtp->datasend("Subject: Password/Account expiry summary\n");
$smtp->datasend("\n");

# read in tmp file and add to email
while (<TMPFILE>) {
        $smtp->datasend($_);
}
$smtp->dataend();
$smtp->quit;

close TMPFILE;

# could add something to delete the TMPFILE.....

sub password_warning	{
	# should get 2 variables first one is mail address, second one is days left before password dies
	# will send mail to the address supplied with the following message
	# should maybe verify that email address was suppplied,  bad things may happen without one.
#	print $_[0] . " " . $_[1];
	
	# edit the password message below
my $msg="
Please change your password now.\n
In order to change your password:\n
1. Start an internet browser.
2. Go to: https://www/passchange/ (Note that this website is NOT accessible from the internet.)
3. If you get a popup window asking for a username/password, click cancel to dismiss it. Only enter your username/password in the form on the webpage itself.
4. Follow the directions on the webpage. \n\n


\n\t\tPlease do not reply to this email. \n
\t\tThank you for your co-operation.";

$smtp = Net::SMTP->new('mail.somewhere.com');
$smtp->hello ("somewhere.com");
# smtp->mail = name that the email appears to be from
$smtp->mail('password@mail.somewhere.com');
$smtp->to($_[0]);
$smtp->data();
$smtp->datasend("To: " . $_[0] . "\n");
$smtp->datasend("Subject: Your password expires in " . $_[1] . " days\n");
$smtp->datasend("\n");
$smtp->datasend($msg);
$smtp->dataend();

$smtp->quit;
	
}

sub acct_expire_warning	{
	# should get 2 variables first one is mail address, second one is 
	# days left before account expires

my $msg="
Your account is going to expire soon.  This generally means you're screwed as I haven't made any easy way for you to stop this from happening.  Bow to CS power";
my $smtp = Net::SMTP->new('mail.somewhere.com');
$smtp->hello ("somewhere.com");
$smtp->mail('password@mail.somewhere.com');
$smtp->to($_[0]);
$smtp->data();
$smtp->datasend("To: " . $_[0] . "\n");
$smtp->datasend("Subject: Your account expires in " . $_[1] . " days\n");
$smtp->datasend("\n");
#$smtp->datasend("A simple test message\n");
$smtp->datasend($msg);
$smtp->dataend();
$smtp->quit;
}

