Resetting a Password vs. Changing a Password Active Directory makes a distinct difference between resetting a user's password and changing it. Resetting a user's password is an administrative function and does not require the user's existing password. You simply specify the new password and the user's account is instantly changed. This is used in the case where the user has forgotten his password or where the password is simply not available. Since this is an administrative function, your program or web application will need to perform this call under administrative priviledges. This means you will likely need to impersonate a user with administrative rights. Changing a user's password requires the user's existing password. Active Directory will first encrypt the existing password you supply and compare it against the encrypted password in the Active Directory database. If the passwords match, the new password that you supply will be set. If the passwords do not match, an exception will be thrown. How to Retrieve a User's Password from Active Directory  | Upon first thought of changing a user's password in Active Directory, you may assume you need to lookup the user's current password, compare it with what they type into a form, and if it matches, set the new password. However, you simply can't do this. It is impossible to retrieve a user's password from Active Directory. But, then how do we compare the password to change it? You don't actually do the comparing. Instead, Active Directory does the comparing by invoking the ChangePassword() function. As mentioned above, Active Directory will compare the encrypted version of the existing password against that in the database. It will let you know via an exception whether it has succeeded or failed in changing the password. |
Changing a Password in Active Directory with C# ASP .NET This is the case where you have the user's existing password. You want to verify the password they enter is correct, and if so, change the password in Active Directory. First, you will get the DirectoryEntry object and then invoke the ChangePassword function. // Connect to Active Directory and get the DirectoryEntry object. // Note, ADPath is an Active Directory path pointing to a user. You would have created this // path by calling a GetUser() function, which searches AD for the specified user // and returns its DirectoryEntry object or path. See here. DirectoryEntry oDE; oDE = new DirectoryEntry(ADPath, ADUser, ADPassword, AuthenticationTypes.Secure); try { // Change the password. oDE.Invoke("ChangePassword", new object[]{strOldPassword, strNewPassword}); } catch (Exception excep) { Debug.WriteLine("Error changing password. Reason: " + excep.Message); }
Resetting a Password in Active Directory with C# ASP .NET This is the case where you do not have the user's existing password or he has forgotten it. First, you will get the DirectoryEntry object, as in the above example. However, now you will invoke the SetPassword() function. This requires administrative access and you will likely need to use impersonation in your web application while making this call: // Connect to Active Directory and get the DirectoryEntry object. DirectoryEntry oDE; oDE = new DirectoryEntry(ADPath, ADUser, ADPassword, AuthenticationTypes.Secure); try { // Impersonate a user with administrative rights. // ... // // Reset the password. oDE.Invoke("SetPassword", new object[] {NewPassword}); // Remove impersonation and restore anonymous Internet guest rights. // ... // } catch (Exception excep) { Debug.WriteLine("Error changing password. Reason: " + excep.Message); } A Note on Windows Impersonation  | Windows impersonation is a somewhat complex process involving setting up a security token and calling Logonuser() to change the security context of the calling process, so that all function calls are executed under an administrative user context. After completing the call, impersonation is removed and the security context is set back to the anonymous Internet guest in IIS. This would be an article of its own, although you can find code available to utilize impersonation. If you are creating a desktop application, you may not require impersonation. Important functions used within impersonation are LogonUser, DuplicateToken, System.Runtime.InteropServices, System.Security.Principal.WindowsImpersonationContext, and WindowsIdentity.Impersonate |
A More Fault-Tolerant Password Reset Method In a C# .NET web application, you may find that you still receive exceptions upon resetting a password, even with impersonation enabled. This is due to certain security flaws in the .NET runtime and IIS security context. Resetting passwords can be risky business for Windows, so security is at the top of the list. To get around this, you can actually create a small console application that executes a SetPassword function. You pass the console application the DisplayName of the Active Directory user to modify and the new password to set. In your web application code for resetting the password, you modify the exception block to call this console application if the original SetPassword failed. The console application will execute in its own security context (calling its own impersonate user function) and reset the password. The code for the console application follows the same as the example shown above for resetting a password. The only difference is that you will read command line parameters. Here is an example of calling the console application from your web application: // // If the original SetPassword() fails: // try { // This is probably a permissions error, so try running the command line exe tool to set the password instead. string strCmd = strStartPath + "\\MySetPasswordTool.exe"; string strArgs = "-p \"" + DisplayName + "\" " + Password; // We must remove impersonation because the EXE does that itself. We will resume impersonation after this completes. MyImpersonator.Close(); Process process = new Process(); process.StartInfo.UseShellExecute = false; process.StartInfo.RedirectStandardOutput = true; process.StartInfo.RedirectStandardError = true; process.StartInfo.CreateNoWindow = true; process.StartInfo.FileName = strCmd; process.StartInfo.Arguments = strArgs; process.StartInfo.WorkingDirectory = ""; process.Start(); process.WaitForExit(); string output = process.StandardOutput.ReadToEnd(); process.Close(); if (output.IndexOf("SUCCESS") == -1) { throw new Exception(output); } } catch (Exception excep) { throw excep; } |