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).