RyanFrantz

OpenLDAP: Implementing the Password Policy Overlay

Sometimes I tend to be verbose. Click here to get to the meat of the post. Otherwise, read on.

It's been some time since I configured OpenLDAP but I decided to use it on a new project I'm working on. Prior to this new effort, I used a simple database table to store user credentials because my requirements were, well, simple. Now, I need to extend the management capabilities of my project and delegate access according to group membership. In addition, I want to implement basic password management functions such as minimum password length, aging, etc. OpenLDAP's password policy overlay is perfect for this second criterion.

Only, something is missing: documentation. How I loathe missing documentation. The only thing worse is incorrect, incomplete, or out-of-date documentation. That's exactly what I found myself with when attempting to configure the password policy overlay for OpenLDAP using the the dynamic configuration backend: cn=config.

For more information on overlays, see http://www.openldap.org/doc/admin24/overlays.html. You'll note, unfortunately, that the documentation for the password policy overlay still references the slapd.conf-method of configuring that overlay.

NOTE: I used slaptest to convert a temporary instance of slapd.conf into the cn=config backend objects I needed. Perhaps I'll post more on that process later.
I dig the cn=config methodology. It makes sense to me and I've had no issues implementing custom backends for new directory instances. However, there is a dearth of clear, concise information available on how to configure the password policy overlay using the dynamic cn=config backend. Until now.

Assumptions

To get started, I'll tell you what my assumptions are and what we'll end up with:
  1. I'll assume (onerously, conceitedly) you're working on the same platform as me (AWS Micro Instance) running a generic Linux AMI.
  2. I'll assume you're familiar with the ldap* and slap* commands.
  3. I'll assume that you like the name of my directory instance: snafoobar.com.
  4. You'll end up with a working, simple password policy overlay that is used to create a default password policy for one of your directory instances.

The Meat

For this example, the configuration lives in /etc/openldap/slapd.d/cn=config/. Our directory instance is specified in a file named olcDatabase={3}snafoobar.com.ldif:

/etc/openldap/slapd.d/cn=config/
|
`-- olcDatabase={3}snafoobar.com.ldif

File Preparation

To create and configure the password policy overlay, start by creating a few files:
The resulting file hierarchy is as follows:

/etc/openldap/slapd.d/cn=config/
|
|-- cn=module{0}.ldif
|-- olcDatabase={3}snafoobar.com
| `-- olcOverlay={0}ppolicy.ldif
`-- olcDatabase={3}snafoobar.com.ldif

cn=module{0}.ldif

cn=module{0}.ldif specifies which shared library LDAP should load to support the password policy overlay. Its contents should be as follows:

dn: cn=module{0}
objectClass: olcModuleList
cn: module{0}
olcModuleLoad: {0}ppolicy.la
structuralObjectClass: olcModuleList

olcOverlay={0}ppolicy.ldif

olcOverlay={0}ppolicy.ldif should have the following attributes defined:

dn: olcOverlay={0}ppolicy
objectClass: olcOverlayConfig
objectClass: olcPPolicyConfig
olcOverlay: {0}ppolicy
olcPPolicyDefault: cn=passwordDefault,ou=policies,dc=snafoobar,dc=com
olcPPolicyHashCleartext: FALSE
olcPPolicyUseLockout: FALSE
olcPPolicyForwardUpdates: FALSE
structuralObjectClass: olcPPolicyConfig

NOTE: I've already created the ou=policies,dc=snafoobar,dc=com organizational unit in my directory instance. You'll need to do the same to follow this example.

Restart slapd

Do it. Restart slapd. I'll wait.

passwordPolicy.ldif

You'll need to create an LDIF file that will contain the default password policy definition. I didn't mention this above as this file is not part of the cn=config hierarchy. Create a file named passwordPolicy.ldif (anywhere, except within the cn=config/ hierarchy) and add the following contents:

dn: cn=passwordDefault,ou=policies,dc=snafoobar,dc=com
objectClass: pwdPolicy
objectClass: person
objectClass: top
cn: passwordDefault
sn: passwordDefault
pwdAttribute: userPassword
pwdCheckQuality: 0
pwdMinAge: 0
pwdMaxAge: 0
pwdMinLength: 5
pwdInHistory: 5
pwdMaxFailure: 3
pwdFailureCountInterval: 0
pwdLockout: TRUE
pwdLockoutDuration: 0
pwdAllowUserChange: TRUE
pwdExpireWarning: 0
pwdGraceAuthNLimit: 0
pwdMustChange: FALSE
pwdSafeModify: TRUE

NOTE: I'm leaving it as an exercise to you, the reader, to determine what the above attributes are and what is appropriate for your environment. I need these attributes configured with these values. You may require something different.

Add the entry into the directory instance (my LDIF lives in ~/ldif/):

[root@awsInstance ~/ldif]# ldapadd -c -f passwordPolicy.ldif -D "cn=root,dc=snafoobar,dc=com" -x -w cleverBoysNeverTell
adding new entry "cn=passwordDefault,ou=policies,dc=snafoobar,dc=org"


If you want to confirm that the new entry exists and a default password policy has been implemented, execute an ldapsearch:

[root@awsInstance ~/ldif]# ldapsearch -x -D cn=root,dc=snafoobar,dc=com -H ldap:// -b "dc=snafoobar,dc=com" -w cleverBoysNeverTell
# extended LDIF
#
# LDAPv3
# base with scope subtree
# filter: (objectclass=*)
# requesting: ALL
#
...

# policies, snafoobar.com
dn: ou=policies,dc=snafoobar,dc=com
ou: policies
objectClass: organizationalUnit
description: Default directory policies

# passwordDefault, policies, snafoobar.com
dn: cn=passwordDefault,ou=policies,dc=snafoobar,dc=com
objectClass: pwdPolicy
objectClass: person
objectClass: top
cn: passwordDefault
sn: passwordDefault
pwdAttribute: userPassword
pwdCheckQuality: 0
pwdMinAge: 0
pwdMaxAge: 0
pwdMinLength: 5
pwdInHistory: 5
pwdMaxFailure: 3
pwdFailureCountInterval: 0
pwdLockout: TRUE
pwdLockoutDuration: 0
pwdAllowUserChange: TRUE
pwdExpireWarning: 0
pwdGraceAuthNLimit: 0
pwdMustChange: FALSE
pwdSafeModify: TRUE
...

Fini!

You should now have an LDAP instance with a default password policy defined. Test it out by modifying user accounts' passwords (i.e. via your favorite LDAP browser or custom code).