Resetting Passwords in Active Directory with C# ASP .NET

modified

Introduction

One of the most common functions to perform in Active Directory from a desktop application or C# ASP .NET application is resetting user passwords. Due to the security features of Windows, resetting a password can be a little tricker than you think. After throwing in the security features of IIS and Windows Server, changing passwords from a web application seems almost impossible. However, by following a few important guidelines, you can have your web application easily resetting user passwords in Active Directory.

Note, if you are not yet familiar with connecting to Active Directory in C# .NET, please read our article on Using Active Directory with C# .NET.

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

Changing a Password in 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.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 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.
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);
}

In the above code, 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.

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:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 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

Impersonation in C# ASP .NET with Active Directory

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:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
//
// 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;
}

Conclusion

Working with Active Directory passwords in a C# ASP .NET application can greatly enhance an application and empower its users. By remembering the security rules of .NET, Windows, and IIS, manipulating passwords can be a straight forward process. Always remember to enclose all Active Directory functions within a try catch block in order to handle errors. Note that many Active Directory functions require impersonation as a user with administrative rights.

Primary Objects creates advanced C# ASP .NET web applications, many of which include advanced active directory technology. If you are in need of a custom web application with these features, please contact us.

About the Author

This article was written by Kory Becker, software developer and architect, skilled in a range of technologies, including web application development, machine learning, artificial intelligence, and data science.

Share