Reputation: 1175
Self service password reset site. For certain operations, I am impersonating a technical user that has account operator privileges in the domain. It works perfectly on my laptop, I can change a user's password, unlock the account or query the domain for all the locked accounts.
It even worked on the server until some 2 weeks ago. I tried to investigate for changes in our environment bot no one is aware of any change that could have had an effect on this.
The best part is that I have no error messages at all. Marshal.GetLastWin32Error()
is returning zero, which is basically "everything went OK".
Here's the code where the impersonation happens:
#region accountManagement
[DllImport("advapi32.dll")]
public static extern int LogonUserA(String lpszUserName, String lpszDomain, String lpszPassword, int dwLogonType, int dwLogonProvider, ref IntPtr phToken);
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern int DuplicateToken(IntPtr hToken, int impersonationLevel, ref IntPtr hNewToken);
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern bool RevertToSelf();
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
public static extern bool CloseHandle(IntPtr handle);
WindowsImpersonationContext impersonationContext;
private bool impersonateValidUser(String userName, String domain, String password)
{
const int LOGON32_LOGON_INTERACTIVE = 2;
const int LOGON32_LOGON_NETWORK = 3;
const int LOGON32_LOGON_BATCH = 4;
const int LOGON32_LOGON_SERVICE = 5;
const int LOGON32_LOGON_UNLOCK = 7;
const int LOGON32_LOGON_NETWORK_CLEARTEXT = 8;
const int LOGON32_LOGON_NEW_CREDENTIALS = 9;
const int LOGON32_PROVIDER_DEFAULT = 0;
WindowsIdentity tempWindowsIdentity;
IntPtr token = IntPtr.Zero;
IntPtr tokenDuplicate = IntPtr.Zero;
if (RevertToSelf())
{
// Int32 result = LogonUserA(userName, domain, password, LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, ref token);
// Response.Write(">>> " + result.ToString());
if (LogonUserA(userName, domain, password, LOGON32_LOGON_UNLOCK, LOGON32_PROVIDER_DEFAULT, ref token) != 0)
{
if (DuplicateToken(token, 2, ref tokenDuplicate) != 0)
{
tempWindowsIdentity = new WindowsIdentity(tokenDuplicate);
impersonationContext = tempWindowsIdentity.Impersonate();
if (impersonationContext != null)
{
CloseHandle(token);
CloseHandle(tokenDuplicate);
return true;
}
}
}
}
if (token != IntPtr.Zero)
CloseHandle(token);
if (tokenDuplicate != IntPtr.Zero)
CloseHandle(tokenDuplicate);
return false;
}
private void undoImpersonation()
{
impersonationContext.Undo();
}
#endregion
And here is the piece that calls it (irrelevant parts removed):
String iuUser = System.Configuration.ConfigurationManager.AppSettings["domain.accountop.user"];
String iuPass = System.Configuration.ConfigurationManager.AppSettings["domain.accountop.pass"];
String iuDomn = System.Configuration.ConfigurationManager.AppSettings["domain.name"];
if (impersonateValidUser(iuUser, iuDomn, iuPass))
{
try
{
email = user.Properties["mail"].Value.ToString();
user.Invoke("SetPassword", new object[] { pw1 });
user.Properties["LockOutTime"].Value = 0; //unlock account
user.CommitChanges();
user.Close();
undoImpersonation();
// clear form and redirect
pPopupSuccess.Visible = true;
hfSuccess.Value = "The account is unlocked now and the password has been reset successfully.";
}
catch (Exception ex)
{
Exception innerException = ex.InnerException;
DirectoryServicesCOMException exds = (DirectoryServicesCOMException)innerException;
String errorMessage = "<p><strong>Your password probably did not meet the requirements.</strong></p>";
errorMessage += "<p>" + System.Configuration.ConfigurationManager.AppSettings["domain.pwreqmessage"] + "</p>";
errorMessage += "<strong>Detailed error message:</strong><br />";
errorMessage += ex.Message;
errorMessage += "<br />";
errorMessage += ex.StackTrace;
if (innerException != null) {
errorMessage = errorMessage + innerException.Message;
}
pPopupError.Visible = true;
hfErrorMessage.Value = errorMessage;
}
}
else
{
// The impersonation failed. Include a fail-safe mechanism here.
pPopupError.Visible = true;
hfErrorMessage.Value = "<p>Impersonation error. Failed to elevate the rights to account operator. Please report this error to the Help Desk.</p>";
hfErrorMessage.Value += "<p>" + Marshal.GetLastWin32Error().ToString() + "</p>";
}
What I keep getting is NOT an exception at the middle, but my own message at the very end of the second code piece saying the impersonation was not successful. And Marshal.GetLastWin32Error() just returs zero.
What can go wrong here and how can I get more information on what's happening? What can be different on the server that makes this code fail, while it's running OK on my dev PC?
Upvotes: 0
Views: 806
Reputation: 1175
Sorry, it was a #PEBKAC. Windows security log suggested that
The user has not been granted the requested logon type at this machine.
So the answer is "check your windows events!"
This is strange because I had been using this server with this technical account. So I double-checked the user and put it back to the Administrators group of the server. I think it is more than I really need, also a security risk, but I will do some experiments soon to see what is the minimum amount of rights locally to keep it working.
Upvotes: 0