Re: Searching for entries with expired ppolicy controlled passwords

[email protected] (Nick Urbanik) Fri, 2 Dec 2011 10:40:22 +1100
Newsgroups perl.ldap
Message-ID <[email protected]>
Dear Chris,

On 01/12/11 07:12 +0000, Chris Ridd wrote:
>
>On 30 Nov 2011, at 22:21, Nick Urbanik wrote:
>
>> Dear Folks,
>>
>> I am writing Perl software to manage our OpenLDAP cluster using the
>> slapo.ppolicy overlay to manage password policy.  I'm hoping to get
>> this into production very soon.
>>
>> My problem: how to efficiently search for users who have expired?
>>
>> I have seen the code written by Buchan Milne at
>> http://staff.telkomsa.net/~bgmilne/ldap/, but he is munging with the
>> operational attributes directly.  The code I've written so far has
>> mostly avoided working with these directly; I've been using
>> Net::LDAP::Control::PasswordPolicy so far, and am trying to understand
>> how to construct a suitable filter using this control.
>>
>> Can anyone point me in the right direction?
>
>The PP control is very much meant for telling a user about *his*
>password status. If you combined it with proxy auth, you could
>imagine writing something that iterated through every user, and did
>some kind of operation (something simple like whoami perhaps)
>proxying as that user and requesting PP status back.
>
>That may give you what you want. It may even work! :-)

Well, I have thousands of users, and simply want to create a filter
that will fetch the unexpired users who match various other criteria.

>I suspect Buchan's approach is better as it doesn't involve iterating
>through each user and the attributes it is using are defined in the
>same draft as the PP control, i.e. reasonably standardised.

The problem is that I cannot construct one filter that works for
everyone, because I have multiple policies; I have code like this:

sub _read_ppolicy_times {
     my ( $self ) = @_;
     my $ldap = _ldapopen $self or return;
     my $search = _search $self, $ldap, $POLICY_DN, '(objectclass=pwdPolicy)',
         'sub', [ qw( cn pwdMaxAge pwdExpireWarning pwdGraceAuthnLimit ) ]
             or return;
     if ( $search->code() or $search->count() == 0 ) {
         _log_trans_fail $self, $ERR_LDAP_SEARCH_FAIL, 'cannot find ANY ppolicy';
         return;
     }
     my %expires;
     foreach my $entry ( $search->entries() ) {
         my $pwd_max_age = $entry->get_value( 'pwdMaxAge' ) or next;

         $expires{ $entry->get_value( 'cn' ) }
             = strftime '%Y%m%d%H%M%SZ', gmtime( time - $pwd_max_age );
     }
     return \%expires;
}

And then I conduct a search, and loop through the entries removing
those that are expired, like this:

     foreach my $entry ( $search->entries() ) {
         my $ppolicy = _dns_to_cns $entry->get_value( 'pwdPolicySubentry' );
         my $last_changed = $entry->get_value( 'pwdChangedTime' );

         if ( exists $expires->{$ppolicy} ) {
             next if $no_expired and $expires->{$ppolicy} > $last_changed;
         }
         else {
             $self->log( "Cannot find '$ppolicy' in " . Dumper( $expires ) )
         }
         push @entries, $entry;
     }

This is ugly, especially when I want to have other code to fetch only
users who *have* expired.  Do I really need to do this sort of thing?
The server knows which ones have expired; it would be great if I had a
way of asking it to filter them out for me.
-- 
Nick Urbanik http://nicku.org 808-71011 [email protected]
GPG: 7FFA CDC7 5A77 0558 DC7A 790A 16DF EC5B BB9D 2C24  ID: BB9D2C24
I disclaim, therefore I am.