Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Searching for entries with expired ppolicy controlled passwords

73 views
Skip to first unread message

Nick Urbanik

unread,
Nov 30, 2011, 5:21:55 PM11/30/11
to perl...@perl.org
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?
--
Nick Urbanik http://nicku.org 808-71011 nick.u...@optusnet.com.au
GPG: 7FFA CDC7 5A77 0558 DC7A 790A 16DF EC5B BB9D 2C24 ID: BB9D2C24
I disclaim, therefore I am.

Chris Ridd

unread,
Dec 1, 2011, 2:12:24 AM12/1/11
to Nick Urbanik, perl...@perl.org

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! :-)

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.

Chris

Nick Urbanik

unread,
Dec 1, 2011, 6:40:22 PM12/1/11
to Chris Ridd, perl...@perl.org
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.

Chris Ridd

unread,
Dec 2, 2011, 9:25:02 AM12/2/11
to Nick Urbanik, perl...@perl.org

On 1 Dec 2011, at 23:40, Nick Urbanik wrote:

> 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.

The multiple policy areas does make it a bit messier, but I think you're probably doing things correctly unless there's some bit of schema I've forgotten.

I suspect the server *doesn't* actually know which ones have expired. It is probably lazy and only evaluates the expiredness when a user actually tries to bind, or a bound user tries some operation and they've expired in the meantime. But I doubt there's some overall list of who is expired.

Chris

Buchan Milne

unread,
Dec 13, 2011, 10:50:41 AM12/13/11
to perl...@perl.org, Nick Urbanik, Chris Ridd
On Friday, 2 December 2011 01:40:22 Nick Urbanik wrote:
> 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.

I haven't had time to do all I wanted with this, but I developed a simple
Catalyst app for managing users for an open-source project, covering
registration, some user self-service, some minimal user/group management. I
wanted to add some features regarding password expiry, locked out users etc.,
but the password change / reset feature does use/support ppolicy.

I should probably also revert some of the theming changes and host it in a
more neutral place, but you can find it here:

http://svnweb.mageia.org/soft/identity/CatDap/trunk/

The 'stable' instance runs here:
https://identity.mageia.org/

I can probably hook up a demo one somewhere if you don't want to register on
this one to see what it looks like after login ...

I hope to be able to spend some time on it this month to address needs of my
$dayjob.

There is a long list of issues that still need to be addressed:
https://wiki.mageia.org/en/Web_Identity
but it is useful in its current form, and I did most development just to get
us to the point where we could centralise all user accessible apps (such as
the wiki where this page is) in one place.

I would be interested to know what you are doing/using and whether it is open
source or not.

> >>
> >> 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.


That statement is a bit unfair. The script to search for users whose passwords
have expired does search on the ppolicy attributes directly (there seems to be
no other way), but the password changing script available from the same
location uses only Net::LDAP::Control::PasswordPolicy (and doesn't rely on any
specific attributes).

At present I don't believe there is any way to use the ppolicy overlay to list
users with expired passwords. I believe there was talk of having a compare
operation with PasswordPolicy control attached be useful for determining if a
specific user's password had expired, but this would still require one
operation per user, whereas one search per policy is sufficient if we use the
ppolicy attributes.
You don't show what search you are doing, but you should construct a search
filter based on the current date, the expiry/warn time of your policy. This is
what my code does (granted, a lot of the code in this script is ugly, but I
think the approach is correct).

It has something like this:
mesg = $ldap->search(base => $config{'basedn'},
filter => '(objectclass=pwdPolicy)',
attrs =>
['cn','pwdMaxAge','pwdExpireWarning','pwdGraceAuthnLimit'],
);
$mesg->code && carp $mesg->error;
foreach my $entry ($mesg->entries)

[...]

if ( $policydn eq $config{'defaultpolicy'} ) {
$filter = "(&(|(!(pwdPolicySubEntry=*))
(pwdPolicySubEntry=$config{'defaultpolicy'}))(pwdChangedTime<=$filterwarn)
(pwdChangedTime>=$filterexpire))";
} else {
$filter = "(&(pwdPolicySubEntry=$policydn)
(pwdChangedTime<=$filterwarn)(pwdChangedTime>=$filterexpire))"
}
$mesg = $ldap->search(base => $config{'basedn'},
filter => $filter,
attrs => ['cn', 'uid', 'mail', 'pwdChangedTime',
'pwdGraceUseTime'],
);

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

Not really. It has an overlay that intercepts specific operations (bind,
passmod, modify) and evaluates the password policy only for these operations.
It doesn't maintain a list of which DNs have expired or similar.

> it would be great if I had a
> way of asking it to filter them out for me.

Ah, but you can to some degree ... although you do need to search once per
policy (most of my environments have two to 5 policies).


Regards,
Buchan
0 new messages