Changing Active Directory user password with PHP script

20,814

Solution 1

You may not change a password using this method unless you connect over SSL/TLS. If you Google or Bing for the word unicodePwd, which you already knew because you included it in your post, one of the first if not the first result will be the MSDN documentation for unicodePwd, which states within the first three sentences:

This attribute is written by an LDAP Modify under the following restricted conditions. Windows 2000 operating system servers require that the client have a 128-bit (or better) SSL/TLS-encrypted connection to the DC in order to modify this attribute. On Windows Server 2003 operating system, Windows Server 2008 operating system, Windows Server 2008 R2 operating system, Windows Server 2012 operating system, Windows Server 2012 R2 operating system, and Windows Server 2016 Technical Preview operating system, the DC also permits modification of the unicodePwd attribute on a connection protected by 128-bit (or better) Simple Authentication and Security Layer (SASL)-layer encryption instead of SSL/TLS. In Windows Server 2008, Windows Server 2008 R2, Windows Server 2012, Windows Server 2012 R2, and Windows Server 2016 Technical Preview, if the fAllowPasswordOperationsOverNonSecureConnection heuristic of the dSHeuristics attribute (section 6.1.1.2.4.1.2) is true and Active Directory is operating as AD LDS, then the DC permits modification of the unicodePwd attribute over a connection that is neither SSL/TLS-encrypted nor SASL-encrypted. The unicodePwd attribute is never returned by an LDAP search.

If you just perform a simple search for unicodePwd, again one of the very first results you'll get is STEP BY STEP CODE on how to do this:

https://support.microsoft.com/en-us/kb/269190

Solution 2

Realise I'm a year late to this party; but having discovered this post whilst troubleshooting a similar problem...

Suspect ldaps is/was providing a certificate that wasn't trusted by the server hosting this php script (linux?); OP mentions having changed the code to use ldaps and getting to exit('Could not connect to LDAP server'); but connecting OK via Apache Directory Studio which may be on a machine that DOES trust the certificate (ie a domain joined workstation that trusts the DC).

To fix this proper, investigate your Private Key Infrastructure (a big topic, start here : https://social.technet.microsoft.com/wiki/contents/articles/2980.ldap-over-ssl-ldaps-certificate.aspx)

Alternatively, just tell the php server to trust the LDAP self-signed cert as see: Authenticating a self-signed certificate for LDAPS connection

To verify this is the case before embarking (ie not recommended in production) consider configuring LDAP on the php host (assumed linux server) to ignore errors caused by the certificate authority/trust by doing putting

TLS_REQCERT never

at the end of /etc/ldap/ldap.conf (restart apache / webserver after change)

Solution 3

As Ryan Ries has noted, you must make a secure connection in order to change a password, but the code you've posted does not do so.

The problematic code is:

$ldap = ldap_connect('localhost');

As you can see, this makes a non-secure connection.

To make a secure connection, you need to specify an LDAPS URI:

$ldap = ldap_connect('ldaps://localhost');

Solution 4

You're creating the unicode password wrong. Here is code that works at least for me on Server 2012.

// create the unicode password
$newpassword = "\"" . $newpassword . "\"";
$len = strlen($newpassword);
for ($i = 0; $i < $len; $i++) $newpass .= "{$newpassword{$i}}\000";
$entry["unicodePwd"] = $newpass;

// Modify the password
if (ldap_mod_replace($ldap, $userDn, $entry))
{
  exit("Successfully updated password!");
}

Notice how I encode the " with the new password, that was the trick that got it working for me.

Share:
20,814
Mohammed Noureldin
Author by

Mohammed Noureldin

Austrian software engineer with a focus not only on coding, but also on every single detail of software engineering, equipped with a diversity of promising and useful skills, like deep knowledge in different programming languages and frameworks, hands-on embedded systems, and a thorough DevOps experience. Always interested in the latest cutting-edge technologies, and recently started gaining deep knowledge and experience in the field of machine learning and artificial intelligence. My huge passion for technology motivated me to self-educate myself in these different technical fields, and the vision to the future keeps pulling me to develop myself continuously. Additionally, a pharmacist holds a doctoral degree (Ph.D.) in pharmacology from the University of Graz in Austria. Besides being a husband to a beautiful doctor, but not a father yet. To keep myself fit (though in the being time I am not really committed), swimming, table tennis, and cycling are my favorites. Occasionally I like to play some computer or board games. If You Are Working On Something That You Really Care About, You Don’t Have To Be Pushed. The Vision Pulls You. – Steve Jobs

Updated on July 09, 2022

Comments

  • Mohammed Noureldin
    Mohammed Noureldin almost 2 years

    I am trying to get a very simple PHP script to change a user password in my Active Directory domain.

    Here is the script I found some where online:

    <?php
    $uid = 'Mohammed Noureldin';
    $newPassword = '5omeGoodP@ssword';
    $bindDn = 'CN=Administrator,OU=UsersOU,DC=example,DC=local';
    $bindPassword = 'An0therGoodP@ssword';
    $baseDn = 'OU=UsersOU,DC=example,DC=local';
    $protocolVersion = 3;
    
    $ldap = ldap_connect('localhost');
    if (!ldap_set_option($ldap, LDAP_OPT_PROTOCOL_VERSION, $protocolVersion))
    {
        exit('Failed to set protocol version to '.$protocolVersion);
    }
    // bind anonymously so that we can verify if the server really is running
    ldap_bind($ldap);
    if (ldap_errno($ldap) !== 0)
    {
        exit('Could not connect to LDAP server');
    }
    
    // now bind with the correct username and password
    ldap_bind($ldap, $bindDn, $bindPassword);
    if (ldap_errno($ldap) !== 0)
    {
        exit('ERROR: '.ldap_error($ldap));
    }
    
    $searchResults = ldap_search($ldap, $baseDn, 'cn='.$uid);
    // no matching records
    if ($searchResults === false)
    {
        exit('No user found');
    }
    
    if (!is_resource($searchResults))
    {
        exit('Error in search results.');
    }
    // create the unicode password
    $len = strlen($newPassword);
    $newPass = '"';
    for ($i = 0; $i < $len; $i++)
    {
        $newPass .= "{$newPassword{$i}}\000";
    }
    $newPass .= '"';
    
    $entry = ldap_first_entry($ldap, $searchResults);
    if (!is_resource($entry))
    {
        exit('Couldn\'t get entry');
    }
    $userDn = ldap_get_dn($ldap, $entry);
    if (!$userDn)
    {
    exit('Errrrrrrrrr1');
    
    }
    if (!ldap_modify($ldap, $userDn, array('unicodePwd' => $newPass)))
    {
    exit(ldap_errno($ldap)." ". ldap_error($ldap));
    
    }
    ?>
    

    The output of this PHP page was this error message:

    53 Server is unwilling to perform

    And the script simply didn't work (the password of the user was NOT changed).

    I know the main principle that AD stores the passwords in unicodePwd field (if that is still the case till now), and I knew that I have to use secure connection and I am using it (hopfully it is correctly setup).

    I googled about that error message but I couldn't find any functional solution.

    I also tried some other scripts but this one was the best till now because the others gave me some errors in some previous steps (for example binding).

    I really appreciate any help to solve that problem, or even another functional script may be a good idea! Thanks in advance.