Changing Active Directory passwords via LDAP using PHP
The background
A user's password is stored in the unicodePwd attribute of the user object in the Active Directory. This attribute cannot be read and can only be set under certain LDAP operations:
- An atomic modify request containing an delete operation with the current password and an add operation with the new password
- If bound with sufficient privileges an LDAP replace can be used to set a new password without supplying the current password.
Additionally, the following conditions must also be met:
- The operations must be performed using LDAPS
- The LDAPS connection must use at least 127-bit encryption
The problems
Firstly, you need to ensure that the Active Directory domain controller you intend to use to perform the change supports LDAPS.
The second problem is that the LDAP support in the PHP for LDAP_MODIFY only allows one attribute to modified at a time, which as mentioned previously is not suitable for changing the unicodePwd entry.
The Solution
The way around this is to generate an LDIF entry an pass this to the OpenLDAP ldapmodify command.
dn: <User DN> changetype: modify delete: unicodePwd unicodePwd:: <Base64 encoded old password> - add: unicodePwd unicodePwd:: <Base64 encoded new password> -
Use the above LDIF file with the following ldapmodify command:
/usr/bin/ldapmodify -f <LDIF file> -H ldaps://ad.mydomain -D '<User DN>' -x -w <Users old password>
An example
function EncodePwd($pw) { $newpw = ''; $pw = "\"" . $pw . "\""; $len = strlen($pw); for ($i = 0; $i < $len; $i++) $newpw .= "{$pw{$i}}\000"; $newpw = base64_encode($newpw); return $newpw; } $newpw64 = EncodePwd($newpw); $oldpw64 = EncodePwd($oldpw); $ldif=<<<EOT dn: $userdn changetype: modify delete: unicodePwd unicodePwd:: $oldpw64 - add: unicodePwd unicodePwd:: $newpw64 - EOT; $cmd = sprintf("/usr/bin/ldapmodify -H %s -D '%s' -x -w %s", $adserver, $userdn, $oldpw); if (($fh = popen($cmd, 'w')) === false ) die("Open failed: ${php_errormsg}\n"); fwrite($fh, "$ldif\n"); pclose($fh);