ldap: identify accounts with expired passwords

Title:ldap: identify accounts with expired passwords
Author:Douglas O’Leary <dkoleary@olearycomputers.com>
Description:ldap: identify accounts with expired passwords
Date created:02/13/14
Date updated:12/20/14
Disclaimer:Standard: Use the information that follows at your own risk. If you screw up a system, don’t blame it on me...

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);