impersonation works on local xp not on web server

S

smyers

The program I have written to change a password by impersonating an
admin has worked successfully on my local XP, but when transferred to
the Windows 2000 server the impersonation fails. I have tried
everything I can think of even to the extent of making sure the W2k
box has the SE_TCB_NAME privilege and installing a server
certification and the program is still not functioning properly on the
server. I believe that the LogonUser() function is taking in the
correct values with the exception that the token has no value...it
continually is showing an output of 0 when I debug on the server. Any
suggestions would be greatly appreciated. Here is some code:
using System;
using System.DirectoryServices;
using System.Runtime.InteropServices;
using System.Security;
using System.Security.Permissions;
using System.Security.Principal;
using System.Configuration;
using System.Diagnostics;


namespace BoardShareData
{
public class UserSecurity
{
public UserSecurity()
{

}

#region Impersonate Info
private const int LOGON32_LOGON_INTERACTIVE = 2;
private const int LOGON32_PROVIDER_DEFAULT = 0;

WindowsImpersonationContext impersonationContext;

[DllImport("advapi32.dll")]
private static extern bool LogonUser(
String lpszUsername,
String lpszDomain,
String lpszPassword,
int dwLogonType,
int dwLogonProvider,
ref IntPtr phToken );

[DllImport("advapi32.dll")]
private static extern bool DuplicateToken(
IntPtr ExistingTokenHandle,
int ImpersonationLevel,
ref IntPtr DuplicateTokenHandle );

#endregion

#region Private Methods
/// <summary>
/// Sets up the impersonation
/// </summary>
/// <returns></returns>
private bool _impersonateValidUser()
{
string adminUser, adminPwd, domain;
// values come from web.config file
domain = Environment.MachineName.ToString();
adminUser = ConfigurationSettings.AppSettings["pwdAdminUser"].ToString();
adminPwd = ConfigurationSettings.AppSettings["pwdAdminPwd"].ToString();
#if (DEBUG)
// debug environment
adminUser = ConfigurationSettings.AppSettings["pwdAdminUser-debug"].ToString();
adminPwd = ConfigurationSettings.AppSettings["pwdAdminPwd-debug"].ToString();
#endif

WindowsIdentity tempWindowsIdentity;
IntPtr token = IntPtr.Zero;
IntPtr tokenDuplicate = IntPtr.Zero;

// check to make sure user has rights to change the password
if(LogonUser(adminUser, domain, adminPwd, LOGON32_LOGON_INTERACTIVE,
LOGON32_PROVIDER_DEFAULT, ref token) != false)
{
if(DuplicateToken(token, 2, ref tokenDuplicate) != false)
{
tempWindowsIdentity = new WindowsIdentity(tokenDuplicate);
impersonationContext = tempWindowsIdentity.Impersonate();
if (impersonationContext != null)
return true;
else
return false;
}
else
return false;
}
else
return false;
}

/// <summary>
/// Undoes the impersonation
/// </summary>
private void _undoImpersonation()
{
impersonationContext.Undo();
}
#endregion

#region Public Methods
// change password for a given user
public void ChangePassword(string userName, string userPwd)
{
if (this._impersonateValidUser())
{
// valid admin user, impersonation is working
// impersonation set up, change the password
string adStr = "WinNT://" + Environment.MachineName.ToString();

DirectoryEntry ad = new DirectoryEntry(adStr);
DirectoryEntry user;
// find user and set password
try
{
user = ad.Children.Find(userName, "User");
user.Invoke("SetPassword", new object[] {userPwd});
user.CommitChanges();
// done with impersonation
this._undoImpersonation();
}
catch (Exception exp)
{
// error encountered, undo impersonation
this._undoImpersonation();
throw exp;
}
}
else
{
// impersonation did not work, some type of error handling here to
let user know what happened
Console.Write("You are not a valid user");
}
}

#endregion
}
}
 
J

Joe Kaplan \(MVP - ADSI\)

Are you trying to change passwords against AD? If so, there is a much
easier way to do this using LDAP that doesn't require impersonation. I can
help with the AD if that applies.

If you must impersonate, you should use the canonical example provided by
Microsoft here:
http://msdn.microsoft.com/library/d...ImpersonationContextClassTopic.asp?frame=true

HTH,

Joe K.

The program I have written to change a password by impersonating an
admin has worked successfully on my local XP, but when transferred to
the Windows 2000 server the impersonation fails. I have tried
everything I can think of even to the extent of making sure the W2k
box has the SE_TCB_NAME privilege and installing a server
certification and the program is still not functioning properly on the
server. I believe that the LogonUser() function is taking in the
correct values with the exception that the token has no value...it
continually is showing an output of 0 when I debug on the server. Any
suggestions would be greatly appreciated. Here is some code:
using System;
using System.DirectoryServices;
using System.Runtime.InteropServices;
using System.Security;
using System.Security.Permissions;
using System.Security.Principal;
using System.Configuration;
using System.Diagnostics;


namespace BoardShareData
{
public class UserSecurity
{
public UserSecurity()
{

}

#region Impersonate Info
private const int LOGON32_LOGON_INTERACTIVE = 2;
private const int LOGON32_PROVIDER_DEFAULT = 0;

WindowsImpersonationContext impersonationContext;

[DllImport("advapi32.dll")]
private static extern bool LogonUser(
String lpszUsername,
String lpszDomain,
String lpszPassword,
int dwLogonType,
int dwLogonProvider,
ref IntPtr phToken );

[DllImport("advapi32.dll")]
private static extern bool DuplicateToken(
IntPtr ExistingTokenHandle,
int ImpersonationLevel,
ref IntPtr DuplicateTokenHandle );

#endregion

#region Private Methods
/// <summary>
/// Sets up the impersonation
/// </summary>
/// <returns></returns>
private bool _impersonateValidUser()
{
string adminUser, adminPwd, domain;
// values come from web.config file
domain = Environment.MachineName.ToString();
adminUser = ConfigurationSettings.AppSettings["pwdAdminUser"].ToString();
adminPwd = ConfigurationSettings.AppSettings["pwdAdminPwd"].ToString();
#if (DEBUG)
// debug environment
adminUser = ConfigurationSettings.AppSettings["pwdAdminUser-debug"].ToString();
adminPwd = ConfigurationSettings.AppSettings["pwdAdminPwd-debug"].ToString();
#endif

WindowsIdentity tempWindowsIdentity;
IntPtr token = IntPtr.Zero;
IntPtr tokenDuplicate = IntPtr.Zero;

// check to make sure user has rights to change the password
if(LogonUser(adminUser, domain, adminPwd, LOGON32_LOGON_INTERACTIVE,
LOGON32_PROVIDER_DEFAULT, ref token) != false)
{
if(DuplicateToken(token, 2, ref tokenDuplicate) != false)
{
tempWindowsIdentity = new WindowsIdentity(tokenDuplicate);
impersonationContext = tempWindowsIdentity.Impersonate();
if (impersonationContext != null)
return true;
else
return false;
}
else
return false;
}
else
return false;
}

/// <summary>
/// Undoes the impersonation
/// </summary>
private void _undoImpersonation()
{
impersonationContext.Undo();
}
#endregion

#region Public Methods
// change password for a given user
public void ChangePassword(string userName, string userPwd)
{
if (this._impersonateValidUser())
{
// valid admin user, impersonation is working
// impersonation set up, change the password
string adStr = "WinNT://" + Environment.MachineName.ToString();

DirectoryEntry ad = new DirectoryEntry(adStr);
DirectoryEntry user;
// find user and set password
try
{
user = ad.Children.Find(userName, "User");
user.Invoke("SetPassword", new object[] {userPwd});
user.CommitChanges();
// done with impersonation
this._undoImpersonation();
}
catch (Exception exp)
{
// error encountered, undo impersonation
this._undoImpersonation();
throw exp;
}
}
else
{
// impersonation did not work, some type of error handling here to
let user know what happened
Console.Write("You are not a valid user");
}
}

#endregion
}
}
 
S

smyers

I am trying to change passwords against AD. Any help would be appreciated.
Thanks in advance!
Sam

Joe Kaplan \(MVP - ADSI\) said:
Are you trying to change passwords against AD? If so, there is a much
easier way to do this using LDAP that doesn't require impersonation. I can
help with the AD if that applies.

If you must impersonate, you should use the canonical example provided by
Microsoft here:
http://msdn.microsoft.com/library/d...ImpersonationContextClassTopic.asp?frame=true

HTH,

Joe K.

The program I have written to change a password by impersonating an
admin has worked successfully on my local XP, but when transferred to
the Windows 2000 server the impersonation fails. I have tried
everything I can think of even to the extent of making sure the W2k
box has the SE_TCB_NAME privilege and installing a server
certification and the program is still not functioning properly on the
server. I believe that the LogonUser() function is taking in the
correct values with the exception that the token has no value...it
continually is showing an output of 0 when I debug on the server. Any
suggestions would be greatly appreciated. Here is some code:
using System;
using System.DirectoryServices;
using System.Runtime.InteropServices;
using System.Security;
using System.Security.Permissions;
using System.Security.Principal;
using System.Configuration;
using System.Diagnostics;


namespace BoardShareData
{
public class UserSecurity
{
public UserSecurity()
{

}

#region Impersonate Info
private const int LOGON32_LOGON_INTERACTIVE = 2;
private const int LOGON32_PROVIDER_DEFAULT = 0;

WindowsImpersonationContext impersonationContext;

[DllImport("advapi32.dll")]
private static extern bool LogonUser(
String lpszUsername,
String lpszDomain,
String lpszPassword,
int dwLogonType,
int dwLogonProvider,
ref IntPtr phToken );

[DllImport("advapi32.dll")]
private static extern bool DuplicateToken(
IntPtr ExistingTokenHandle,
int ImpersonationLevel,
ref IntPtr DuplicateTokenHandle );

#endregion

#region Private Methods
/// <summary>
/// Sets up the impersonation
/// </summary>
/// <returns></returns>
private bool _impersonateValidUser()
{
string adminUser, adminPwd, domain;
// values come from web.config file
domain = Environment.MachineName.ToString();
adminUser = ConfigurationSettings.AppSettings["pwdAdminUser"].ToString();
adminPwd = ConfigurationSettings.AppSettings["pwdAdminPwd"].ToString();
#if (DEBUG)
// debug environment
adminUser = ConfigurationSettings.AppSettings["pwdAdminUser-debug"].ToString();
adminPwd = ConfigurationSettings.AppSettings["pwdAdminPwd-debug"].ToString();
#endif

WindowsIdentity tempWindowsIdentity;
IntPtr token = IntPtr.Zero;
IntPtr tokenDuplicate = IntPtr.Zero;

// check to make sure user has rights to change the password
if(LogonUser(adminUser, domain, adminPwd, LOGON32_LOGON_INTERACTIVE,
LOGON32_PROVIDER_DEFAULT, ref token) != false)
{
if(DuplicateToken(token, 2, ref tokenDuplicate) != false)
{
tempWindowsIdentity = new WindowsIdentity(tokenDuplicate);
impersonationContext = tempWindowsIdentity.Impersonate();
if (impersonationContext != null)
return true;
else
return false;
}
else
return false;
}
else
return false;
}

/// <summary>
/// Undoes the impersonation
/// </summary>
private void _undoImpersonation()
{
impersonationContext.Undo();
}
#endregion

#region Public Methods
// change password for a given user
public void ChangePassword(string userName, string userPwd)
{
if (this._impersonateValidUser())
{
// valid admin user, impersonation is working
// impersonation set up, change the password
string adStr = "WinNT://" + Environment.MachineName.ToString();

DirectoryEntry ad = new DirectoryEntry(adStr);
DirectoryEntry user;
// find user and set password
try
{
user = ad.Children.Find(userName, "User");
user.Invoke("SetPassword", new object[] {userPwd});
user.CommitChanges();
// done with impersonation
this._undoImpersonation();
}
catch (Exception exp)
{
// error encountered, undo impersonation
this._undoImpersonation();
throw exp;
}
}
else
{
// impersonation did not work, some type of error handling here to
let user know what happened
Console.Write("You are not a valid user");
}
}

#endregion
}
}
 
J

Joe Kaplan \(MVP - ADSI\)

Sorry for the delay. Below is a .NET ChangePassword routine I put together
for someone else. It basically attempts to find the user in AD by their
username using their old password to authenticate to AD. If it succeeds, it
tries the ChangePassword function.

One thing to watch out for is that ChangePassword only wants to work on a
128 secure SSL connection to the DC. Make sure you have your DC configured
with a valid certificate and you bind to it with the DNS name that the cert
is issued to.

HTH,

Joe K.

Private Sub ChangePassword(ByVal username As String, ByVal oldPassword
As String, ByVal newPassword As String)

Dim dcDNS As String = "yourdc.com" 'use this if you want to supply a
server name
Dim rootDN As String
Dim rootDSE As DirectoryEntry
Dim searchRoot As DirectoryEntry
Dim userEntry As DirectoryEntry
Dim searcher As DirectorySearcher
Dim results As SearchResultCollection
Dim result As SearchResult

Try
'note the authenicationtypes here
'you need to either use SecureSocketsLayer for ChangePasswor
rootDSE = New DirectoryEntry(String.Format("LDAP://{0}/rootDSE",
dcDNS), username, oldPassword, AuthenticationTypes.Secure Or
AuthenticationTypes.SecureSocketsLayer Or AuthenticationTypes.ServerBind)
rootDN =
DirectCast(rootDSE.Properties("defaultNamingContext").Value, String)
searchRoot = New DirectoryEntry(String.Format("LDAP://{0}/{1}",
dcDNS, rootDN), username, oldPassword, AuthenticationTypes.Secure Or
AuthenticationTypes.SecureSocketsLayer Or AuthenticationTypes.ServerBind)
searcher = New DirectorySearcher(searchRoot)
searcher.Filter = String.Format("sAMAccountName={0}", username)
searcher.SearchScope = SearchScope.Subtree
searcher.CacheResults = False

'I use FindAll here because FindOne leaks memory if it does not
find anything
'in .NET 1.0 and 1.1
results = searcher.FindAll()
For Each result In results
'only use this method on .NET 1.1 or higher
'otherwise, get the adsPath value and build a new
DirectoryEntry with the supplied credentials
userEntry = result.GetDirectoryEntry()
Exit For 'this is redundant because sAMAccountName is unique
in the domain, but it is done for clarity
Next

If userEntry Is Nothing Then
Throw New InvalidOperationException("User not found in this
domain.")
End If

userEntry.Invoke("ChangePassword", New Object() {oldPassword,
newPassword})
userEntry.CommitChanges()

Catch ex As System.Reflection.TargetInvocationException
Throw ex.InnerException

Finally 'these prevent other memory leaks
If Not userEntry Is Nothing Then userEntry.Dispose()
If Not results Is Nothing Then results.Dispose()
If Not searcher Is Nothing Then searcher.Dispose()
If Not searchRoot Is Nothing Then searchRoot.Dispose()
If Not rootDSE Is Nothing Then rootDSE.Dispose()
End Try
End Sub
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,744
Messages
2,569,484
Members
44,903
Latest member
orderPeak8CBDGummies

Latest Threads

Top