============================================== ldap: identify accounts with expired passwords ============================================== Overview: ========= Accounts *expire* based on time limits specified in the corporate security policy and implemented via the ldap password policy overlay. The script attempts to retrieve the pwdmaxage parameter from the default password policy. If the script can't find it, the script assumes 90 days - a fairly standard default max password age. Configuration: ============== Three main things to configure in the script: * The global variables on/about line 34. * The filter for the password policy on/about line 53. * The pwdmaxage attribute, if you're not using openldap. Sample runs: ============ * Against a single user: :: # ./expired doleary User Last changed Exp date ======================================================== doleary 01/19/14 18:19 04/19/14 18:19 * Against all users: :: # ./expired | head User Last changed Exp date ======================================================== a 08/09/13 14:17 11/07/13 14:17 aa 01/21/14 21:12 04/21/14 21:12 aaa 01/19/14 17:49 04/19/14 17:49 aaaa 01/20/14 19:54 04/20/14 19:54 aaab 07/09/13 20:30 10/07/13 20:30 aaac 11/14/13 21:33 02/12/14 21:33 aaad 02/05/14 14:47 05/06/14 14:47 aab 12/26/13 12:28 03/26/14 12:28 Script: ======= :: #!/usr/bin/perl ##################################################################################### # expired: Identifies if an account, or all accounts is/are expired. # Author: Doug O'Leary # Created: 02/05/14 ##################################################################################### use Net::LDAP; use Time::Local; use Getopt::Long; sub calc_dates { my ($pwdchange, $exp_secs) = @_; my ($last, $next); if ($pwdchange =~ /\d+/) { my ($year, $mon, $day, $hr, $min, $sec) = $pwdchange =~ m{(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2}).*}; my $local = timelocal($sec,$min,$hr,$day,$mon-1,$year-1900); my ($esec,$emin,$ehr,$eday,$emon,$eyear) = localtime($local + $exp_secs); $last = sprintf("%02d/%02d/%02d %02d:%02d", $mon, $day, $year%100, $hr, $min); $next = sprintf("%02d/%02d/%02d %02d:%02d", $emon+1, $eday, $eyear%100, $hr, $min); } else { $next = $last = 'not recorded'; } return ($last, $next); } ##################################################################################### our $rootdn = 'cn=admin,dc=oci,dc=com'; our $rootpw = 'not_really_my_pwd'; our $config = "cn=config"; our $base = 'dc=oci,dc=com'; our $userdn = "ou=users,$base"; our $groupdn = "ou=groups,$base"; our $lsvr = 'ldaps://ldapsvr.olearycomputers.com'; my ($uid, $pwdchange,$filter, %users, $exp_secs); (defined(@ARGV)) ? ($filter = "uid=$ARGV[0]") : ($filter = 'uid=*'); my $ldap = Net::LDAP->new($lsvr) or die "Can't bind to ldap!\n"; $ldap->bind( dn => $rootdn, password => $rootpw, ); my ($mesg) = $ldap->search( base => $base, filter => 'cn=default', attrs => ['pwdmaxage'], ); (defined($mesg->entry(0))) ? ($exp_secs = $mesg->entry(0)->get_value('pwdmaxage')) : ($exp_secs = 7776000); $exp_secs =~ s/(\d+).*/\1/g; my ($mesg) = $ldap->search( scope => 'one', base => $userdn, filter => $filter, attrs => [ 'uid','pwdChangedTime' ], ); printf("%-25s %-14s %s\n", "User", "Last changed", "Exp date"); print "=" x 56 . "\n"; foreach my $entry ($mesg->all_entries) { $uid = ${$entry->get('uid')}[0]; (defined($entry->get('pwdchangedtime'))) ? ($pwdchange = ${$entry->get('pwdchangedtime')}[0]) : ($pwdchange = 'not recorded'); my ($last, $next) = calc_dates($pwdchange, $exp_secs); $users{$uid} = [ $last, $next ]; } foreach my $uid (sort keys %users) { printf("%-25s %-14s %s\n", $uid, $users{$uid}[0], $users{$uid}[1]); } __DATA__ printf("%-25s %02d/%02d/%02d %02d:%02d %02d/%02d/%02d %02d:%02d\n", $uid, $emon+1, $eday, $eyear%100, $ehr, $emin);