0

Well I am using DirectoryEntry and LdapConnection to reset password in a scenario where we have password minimum age and history policy enforced. When someone forgets their password, you want them to be able to reset their password to something which doesn't violate password history. As an alternative solution, it would be possible to use "SetPassword" and reset the password to a generated value and then force the user to change it on their next login. This is not possible in our scenario. Hence, I was following this blog post in technet and trying out LDap extended controls to reset password by honoring password history. In brief, it's just changing to the same password again and again without complaints. My code is as follows:

 private static void PasswordChanger(DirectoryConnection ldapCon, 
        string distinguishedName, 
        string passwordToSet = null)
    {
        // the 'unicodePWD' attribute is used to handle pwd handling requests
        // modification control for the replace operation
        var damReplace = new DirectoryAttributeModification
        {
            Name = "unicodePwd"
        };

        // value to be send with the request
        damReplace.Add(Encoding.Unicode.GetBytes(String.Format("\"{0}\"", passwordToSet)));

        // this is a replace operation
        damReplace.Operation = DirectoryAttributeOperation.Replace;

        // combine modification controls
        var damList = new DirectoryAttributeModification[]
        {
            damReplace
        };

        // init modify request
        var modifyRequest = new ModifyRequest(distinguishedName, damList);

        // the actual extended control OID 
        const string ldapServerPolicyHintsOid = "1.2.840.113556.1.4.2239";

        // build value utilizing berconverter
        var value = BerConverter.Encode("{i}", new object[] { 0x1 });

        // init exetnded control. The variable name represts the actual extended control name.
        var LDAP_SERVER_POLICY_HINTS_OID = new DirectoryControl(ldapServerPolicyHintsOid, 
            value, false, true);

        // add extended control to modify request
        modifyRequest.Controls.Add(LDAP_SERVER_POLICY_HINTS_OID);

            /* send the request into the LDAPConnection and receive the response */
        var result = ldapCon.SendRequest(modifyRequest);
    }

The call to Password changer is enclosed as follows,

                using (var domain = Domain.GetDomain(new DirectoryContext(
                    DirectoryContextType.DirectoryServer,
                    ActiveDirectoryInstance,
                    request.ServiceAccountName,
                    request.ServiceAccountPassword)))
                using (var directoryEntry = domain.GetDirectoryEntry())
                using (var directorySearcher = new DirectorySearcher(directoryEntry))
                using (var conn = new LdapConnection(new LdapDirectoryIdentifier(ActiveDirectoryInstance), 
                    new NetworkCredential(request.ServiceAccountName, 
                        request.ServiceAccountPassword, 
                        ActiveDirectoryInstance), 
                        AuthType.Ntlm))
                {
                    ...
                    ...

                    PasswordChanger(....)
                    ...
                    ...
                }

EDIT:

This is to do with the scenario explained here

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

RE my comment re "As an alternative solution, it would be possible to use "SetPassword" and reset the password to a generated value and then force the user to change it on their next login."

We can't do that in our scenario as we have password history and minimum age restrictions (24h) enabled. Hence I can't use ChangePassword in user context and SetPassword in admin context (as that wouldn't respect password history).

MSI
  • 1,124
  • 8
  • 23
  • 1
    I came across the same scenario and would like to know if you are able to achieve password reset honoring password history using the above code. Please share your findings. Thanks in advance! – Pranay Apr 21 '17 at 07:18

1 Answers1

0

The LDAP_SERVER_POLICY_HINTS_OID control only enforces password history constraints, meaning that you're only preventing password reuse which should be a non-issue if you generate random passwords in the first place.

To test new passwords against password complexity settings, you would need access to the password filters installed on Domain Controllers.

Otherwise you'll need to use SetPassword - it will enforce complexity requirements, but not password history.

Mathias R. Jessen
  • 157,619
  • 12
  • 148
  • 206
  • Thanks for the response. Please see EDIT. We need to call SetPassword or similar by honoring password history. Hence your first para is correct except for the fact that we are not generating random password (minimum age will prevent the user from changing it for a day then). – MSI May 21 '15 at 00:17
  • You could call `SetPassword` without setting "change password on next logon" and then monitor whether the password gets changed or not – Mathias R. Jessen May 21 '15 at 00:29
  • If you set password using SetPassword then yeah the password gets changed but that's not what we are looking for as you can see in my post :). SetPassword can only be invoked under admin context and it also bypasses all password policy restrictions, which defeats the purpose of using it for "Forgot my password" feature. Thats why I was using that extended control feature (policy hint) to use "SetPassword" but with the extended control so that it respects password history policy. – MSI May 21 '15 at 01:25