Verify a Windows Account

R

Reader

I have an application that allows a user to enter a user name, user password, and the domain or machine name. From this information I would like to verify the user account and password is valid. This must work for either a domain or a local machine account. I have tried to find examples from the web and it seems every example that I have found does not work or produces odd results.

I have tested each of these on Windows 2000, Windows XP, Windows 2003. But I have not found a solution that works for all of them consitantly. Actually the three attempts do not even return the same results. Some may return success while another returns false for the same user!!. Then on say XP it is the opposite where one returns false but the other will work and then on some accounts it just does not work at all.

I will even take help in determining why these are not acting the same on all servers.


The authUserLocal does not seem to work all of the time.
static bool authUserLocal(string username, string password)
{
string path = "WinNT://" + Environment.MachineName + ",computer";
DirectoryEntry entry = new DirectoryEntry(path, username, password);
try
{
Object o = entry.NativeGuid;
return true;
}
catch
{
return false;
}

}

static bool authUserAD(string username,string password)
{
DirectoryEntry entry = new DirectoryEntry("LDAP://" + Environment.GetEnvironmentVariable("USERDNSDOMAIN").ToString(),username,password);
try
{
Object o = entry.NativeGuid;
return true;
}
catch
{
return false;
}
}

and this is another attempt..
public class UserLogon
{
[DllImport("advapi32.dll")]
public static extern bool LogonUser(string lpszUsername,
string lpszDomain,
string lpszPassword,
int dwLogonType,
int dwLogonProvider,
ref IntPtr phToken);

//CloseHandle parameters. When you are finished,
//free the memory allocated for the handle.
[DllImport("kernel32.dll",
CharSet=System.Runtime.InteropServices.CharSet.Auto)]
public static extern bool CloseHandle(IntPtr handle);

public static bool logonUser(string username, string domain, string userpassword)
{
//LogonUser parameters

IntPtr tokenHandle = IntPtr.Zero;

try
{
const int LOGON32_PROVIDER_DEFAULT = 0;
const int LOGON32_LOGON_NETWORK = 3;

//Call LogonUser to obtain a
//handle to an access token

bool returnValue = true;

returnValue = LogonUser(username, domain,
userpassword,
LOGON32_LOGON_NETWORK,
LOGON32_PROVIDER_DEFAULT,
ref tokenHandle);

if(false == returnValue)
{
Console.Write("LogonUser failed...");
return false;
}

CloseHandle(tokenHandle);
return true;
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
return true;

}

}


public class ClassAuth
{
[DllImport("C:\\WINNT\\System32\\advapi32.dll")]
public static extern bool LogonUser(String lpszUsername, String lpszDomain, String lpszPassword,
int dwLogonType, int dwLogonProvider, out int phToken);

[DllImport("C:\\WINNT\\System32\\Kernel32.dll")]
public static extern int GetLastError();


public bool SetLogin(string uid,string pwd) //,HttpContext context)
{
try
{
int token1;
bool loggedOn = LogonUser(uid,".",pwd,2,0,out token1);
IntPtr token2 = new IntPtr(token1);

WindowsIdentity wi = new WindowsIdentity(token2);
//WindowsPrincipal wp = new WindowsPrincipal(wi);
//HttpContext.Current.User = wp;
return true;
}
catch(Exception exp)
{
MessageBox.Show( exp.Message );
return false;
}
}

}
 
W

William DePalo [MVP VC++]

Reader said:
I have an application that allows a user to enter a
user name, user password, and the domain or
machine name. From this information I would like to
verify the user account and password is valid. This
must work for either a domain or a local machine
account. I have tried to find examples from the web
and it seems every example that I have found does
not work or produces odd results.

As if you didn't have enough approaches, I put together a quick little hack
in C# that alllows for verification of the password by means of the native
function LogonUser(). I just tested it logging in to the local machine as
well as a domain. Seems to work OK. I'll post it, if you think it will help
you.
I have tested each of these on Windows 2000,
Windows XP, Windows 2003. But I have not found
a solution that works for all of them consitantly.

For one of the methods, at least, this is by design.

Before XP, in order to use the LogonUser() function the owner of the calling
thread must have the SE_TCB_NAME privilege (friendly name "act as part of
the operating system"). The LocalSystem account has the privilege out of the
box but to give other accounts such an elevated privilege is scary. So,
beginning with XP, the privilege is no longer required. This _could_ be the
source of _some_ of the perceived inconsistency.

Regards,
Will
 
R

Reader

If you would please post your test I would like to see if I too will be able
to get it to work.

I'll look into that privilage issue on the 2000 servers that I have here.
The amusing thing is that on my 2000 servers I was only able to verify the
user name and password that I was currently using but if I entered anything
else that would not work. Really strange I know... :p This all could be
just user issues.
 
W

William Stacey [MVP]

I blogged something on this a few days ago
http://spaces.msn.com/members/staceyw/Blog/cns!1pnsZpX0fPvDxLKC6rAAhLsQ!283.entry


--
William Stacey, MVP
http://mvp.support.microsoft.com

I have an application that allows a user to enter a user name, user password, and the domain or machine name. From this information I would like to verify the user account and password is valid. This must work for either a domain or a local machine account. I have tried to find examples from the web and it seems every example that I have found does not work or produces odd results.

I have tested each of these on Windows 2000, Windows XP, Windows 2003. But I have not found a solution that works for all of them consitantly. Actually the three attempts do not even return the same results. Some may return success while another returns false for the same user!!. Then on say XP it is the opposite where one returns false but the other will work and then on some accounts it just does not work at all.

I will even take help in determining why these are not acting the same on all servers.


The authUserLocal does not seem to work all of the time.
static bool authUserLocal(string username, string password)
{
string path = "WinNT://" + Environment.MachineName + ",computer";
DirectoryEntry entry = new DirectoryEntry(path, username, password);
try
{
Object o = entry.NativeGuid;
return true;
}
catch
{
return false;
}

}

static bool authUserAD(string username,string password)
{
DirectoryEntry entry = new DirectoryEntry("LDAP://" + Environment.GetEnvironmentVariable("USERDNSDOMAIN").ToString(),username,password);
try
{
Object o = entry.NativeGuid;
return true;
}
catch
{
return false;
}
}

and this is another attempt..
public class UserLogon
{
[DllImport("advapi32.dll")]
public static extern bool LogonUser(string lpszUsername,
string lpszDomain,
string lpszPassword,
int dwLogonType,
int dwLogonProvider,
ref IntPtr phToken);

//CloseHandle parameters. When you are finished,
//free the memory allocated for the handle.
[DllImport("kernel32.dll",
CharSet=System.Runtime.InteropServices.CharSet.Auto)]
public static extern bool CloseHandle(IntPtr handle);

public static bool logonUser(string username, string domain, string userpassword)
{
//LogonUser parameters

IntPtr tokenHandle = IntPtr.Zero;

try
{
const int LOGON32_PROVIDER_DEFAULT = 0;
const int LOGON32_LOGON_NETWORK = 3;

//Call LogonUser to obtain a
//handle to an access token

bool returnValue = true;

returnValue = LogonUser(username, domain,
userpassword,
LOGON32_LOGON_NETWORK,
LOGON32_PROVIDER_DEFAULT,
ref tokenHandle);

if(false == returnValue)
{
Console.Write("LogonUser failed...");
return false;
}

CloseHandle(tokenHandle);
return true;
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
return true;

}

}


public class ClassAuth
{
[DllImport("C:\\WINNT\\System32\\advapi32.dll")]
public static extern bool LogonUser(String lpszUsername, String lpszDomain, String lpszPassword,
int dwLogonType, int dwLogonProvider, out int phToken);

[DllImport("C:\\WINNT\\System32\\Kernel32.dll")]
public static extern int GetLastError();


public bool SetLogin(string uid,string pwd) //,HttpContext context)
{
try
{
int token1;
bool loggedOn = LogonUser(uid,".",pwd,2,0,out token1);
IntPtr token2 = new IntPtr(token1);

WindowsIdentity wi = new WindowsIdentity(token2);
//WindowsPrincipal wp = new WindowsPrincipal(wi);
//HttpContext.Current.User = wp;
return true;
}
catch(Exception exp)
{
MessageBox.Show( exp.Message );
return false;
}
}

}
 
W

William DePalo [MVP VC++]

Reader said:
If you would please post your test I would like to see if I too will be
able
to get it to work.

OK, sure.

First the disclaimer: I am a C++/Win32 kinda guy. For now, any C#/.Net I do
is strictly for fun. So I make no claim about best practices. My little hack
uses the runtime's Platform Invoke service to get at the Win32 API that I
know well. The C# wrapper class uses some "unsafe" methods.

A very quick scan of the docs seems to indicate that the WindowsIdentity
class may hold some promise and in fact be a better approach.

That said, this program prints the user name under which it is running,
tries to log in to a domain account and if successful switches the thread
context to that of a domain user. It demonstrates that by printing the name
of the user a second time. I tested it here, on Windows XP Pro/SP2, logging
in once to an account on the local machine and once to a domain account.

This is the test program:

using System;

class Class1
{
[STAThread]
static void Main(string[] args)
{
Win32User user;

Console.WriteLine("User is {0}", Win32User.getUserName() );

user = new Win32User("MyDomain", "MyUserName", "MyPassword");

if ( user.logon() )
{
Console.WriteLine("Logon succeeds, begin impersonating ...");
user.beginImpersonation();
Console.WriteLine(" ... {0}", Win32User.getUserName() );
Console.WriteLine("end impersonation, we are now ...");
user.endImpersonation();
Console.WriteLine(" ... {0}", Win32User.getUserName() );

user.logoff();
}

else
Console.WriteLine("Logon fails with error code {0}",
user.getErrorCode() );
}
}

This is the class that wraps the API functions LogonUser(),
ImpersonateLoggedOnUser(), RevertToSelf() etc

using System;
using System.Runtime.InteropServices;

public class Win32User
{
[ DllImport("advapi32.dll", SetLastError = true) ]
public unsafe static extern int LogonUser(String name, String domain,
String password,
int type, int provider, int *phandle);

[ DllImport("advapi32.dll", SetLastError = true) ]
public static extern int RevertToSelf();

[ DllImport("advapi32.dll", SetLastError = true) ]
public static extern int ImpersonateLoggedOnUser(int handle);

[ DllImport("kernel32.dll") ]
public static extern bool CloseHandle(int handle);

[ DllImport("secur32.dll", SetLastError = true) ]
public unsafe static extern int GetUserNameEx(int type, sbyte *user, int
*size);

private int handle;
private int err;
private String dom;
private String nam;
private String pwd;

public const int Interactive = 2;
public const int Batch = 4;
public const int Network = 3;
public const int Service = 5;

// Domain user

public Win32User(String domain, String name, String password)
{
dom = domain;
nam = name;
pwd = password;
handle = 0;
}

// Local machine user

public Win32User(String name, String password): this(null, name, password)
{
}

// Logs on the specified user in the specified mode

unsafe public bool logon(int typ)
{
int status;
bool ok;

if ( handle != 0 )
CloseHandle(handle);

handle = 0;

fixed ( int *ph = &handle )
{
status = LogonUser(nam, dom, pwd, typ, 0, ph);
}

ok = status != 0;

if ( ok )
err = 0;

else
err = Marshal.GetLastWin32Error();

return ok;
}

// logs on interactively

public bool logon()
{
return logon(Win32User.Interactive);
}

// logs off - call this method after logon or a handle leak results

public void logoff()
{
if ( handle != 0 )
CloseHandle(handle);

handle = 0;
}

// causes the calling thread to impersonate the logged on user;
// logon must have been called previously

public bool beginImpersonation()
{
int status;
bool ok;

if ( handle == 0 )
{
ok = false;
err = 1245;
}

else
{
status = ImpersonateLoggedOnUser(handle);

ok = status != 0;

if ( ok )
err = 0;

else
err = Marshal.GetLastWin32Error();
}

return ok;
}

// ends impersonation, reverts to self

public bool endImpersonation()
{
int status;
bool ok;

status = RevertToSelf();

ok = status != 0;

if ( ok )
err = 0;

else
err = Marshal.GetLastWin32Error();

return ok;
}

// returns a SAM compatible string describing the user context of the
calling thread

static unsafe public String getUserName()
{
int status, size;
int *psize;
String result;
sbyte[] name;

size = 1024;
psize = &size;
name = new sbyte[size];

fixed ( sbyte* pname = name )
{
status = GetUserNameEx(2, pname, psize);

if ( status == 0 )
result = null;

else
result = new String(pname, 0, size - 1);
}

return result;
}

// returns the last Win32 error code of a failed method

public int getErrorCode()
{
return err;
}
}

Regards,
Will
 

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

Top