Latest Posts

Detecting password age from either Active Directory or OpenLDAP/Samba

Posted on January 03, 2009, under ColdFusion | 15596 Views

So you have that fancy ColdFusion application of yours using ldap authentication back to either an active directory or OpenLDAP/Samba server. Everyone is happy, shared authentication works perfectly. Life is great, you're the hero. Suddenly you start getting messages from users, some of them can't login. It worked for last 3 months now it doesn't.

A hidden danger is that there are often have account policies such as password ages and minimum password lengths. Local windows users are prompted warning them about an upcoming expiration so why shouldn't our applications provide that courtesy? Turns out that when we are provided with two pieces of information, the date the password was last set and the maximum password age, we can. We'll leave the minimum length discussions for another day (Hi CJ).

Getting Started

First thing we need is the date the password was last set. In AD this is called pwdLastSet and is stored as the number of nanoseconds since January 1, 1601. (No, I'm not kidding - 1601). OpenLDAP/samba makes it easier using the number of seconds since January 1, 1970 and stores it in sambaPwdLastSet. All we have to do is return either of those in our ldap calls and we have one piece of the puzzle. Simple dateadd/diff converts that into days and from there we just check to see if that values falls within the account policies.

AD

One note - if the pwdLastSet is 0, the user must change their password. You may need to account for that in your application.
<cftry>
   <cfldap action="QUERY"
      name="chkLdapAuth"
      filter="(&(objectclass=user)(samaccountname=#arguments.username#))"
      attributes="cn,pwdLastSet,userAccountControl"
      start="cn=users,dc=acme,dc=com"
      server="192.168.1.1"
      username="#arguments.username#"
      password="#arguments.password#"
      port="389">



   <cfset pwdAge = dateDiff("d",DateAdd("n", chkLdapAuth.pwdLastSet / (600000000),"1/1/1601"),now())>

   <cfcatch> </cfcatch>
</cftry>

OpenLDAP

<cfldap action="QUERY"
      name="ChkLdap"
      attributes="cn,sambaPwdLastSet"
      start="dc=acme,dc=com"
      scope="SUBTREE"
      filter="uid=#arguments.username#"
      server="#ldapsrv#"
      port="389"
      username="uid=#arguments.Username#,ou=Users,dc=acme,dc=com"
      password="#arguments.Password#">



      <cfset pwdAge = dateDiff("d",dateFormat(dateAdd("s",chkLdapAuth.sambaPwdLastSet,"1/1/1970 0:0:00")),now())>

Wrapping up and an example function

I've briefly covered how to adding a warning or forcing users to change their passwords based on their password age for both active directory and OpenLdap/Samba users. Hopefully this code will help by providing the motivation and pieces to get you started on adding this function into your applications. Enjoy 2009!

<!---
      you could probably do some math to figure out the server type and adjust that way,
      it was easier for me to just pass in the type and go from there
   ---->
   <cffunction name="calculatePWDage" hint="returns number of days since password last set">
      <cfargument name="timestamp" required="true">
      <cfargument name="directoryType" required="false" default="AD">
      
      <cfif not isnumeric(arguments.timestamp)>
         <cfreturn 0>
      </cfif>

      <cfif arguments.directoryType eq "openldap">
      
            <cfreturn dateDiff("d",dateFormat(dateAdd("s",arguments.timestamp,"1/1/1970 0:0:00")),now())>
      
         <cfelseif arguments.domain eq "AD">
      
            <cfreturn dateDiff("d",DateAdd("n", arguments.timestamp / (600000000),"1/1/1601"),now())>
      </cfif>
      
      <cfreturn 0>
      
   </cffunction>