Use DsBindWithCred. Note that this function fails with Access Denied even when the credentials are technically valid, such as the account being locked out. You will have to use the LogonUser function if you want that level of detail, but each call will count as a login attempt.
using System.Runtime.InteropServices;
using System.ComponentModel;
using System.Text;
public class PInvoke
{
public static bool TestCreds(string usernamePossiblyWithDomain,
SecureString password,
string dnsDomainName)
{
string username, usernameDomain;
ParseUserName(usernamePossiblyWithDomain, out username, out usernameDomain);
IntPtr pPass = Marshal.SecureStringToGlobalAllocUnicode(password);
try
{
IntPtr hDS = IntPtr.Zero;
IntPtr authID = MakePassCreds(username, usernameDomain, pPass);
//if you're really paranoid, you can uncomment the next two lines
//to zero out the memory as soon as possible
//Marshal.ZeroFreeGlobalAllocUnicode(pPass);
//pPass = IntPtr.Zero;
try
{
int lastErr = DsBindWithCred(null, dnsDomainName, authID, ref hDS);
switch(lastErr)
{
case 0: return true; //ERROR_SUCCESS
case 5: return false; //ERROR_ACCESS_DENIED
default: throw new Win32Exception(lastErr);
}
}
finally
{
if(hDS != IntPtr.Zero) DsUnBind(ref hDS);
if(authID != IntPtr.Zero) DsFreePasswordCredentials(authID);
}
}
finally
{
if(pPass != IntPtr.Zero) Marshal.ZeroFreeGlobalAllocUnicode(pPass);
}
}
[DllImport("credui.dll", CharSet = CharSet.Unicode)]
protected static extern int CredUIParseUserName(string pszUserName,
StringBuilder pszUser, int ulUserMaxChars,
StringBuilder pszDomain, int ulDomainMaxChars);
public static void ParseUserName(string usernamePossiblyWithDomain,
out string username, out string domain)
{
int MaxUserChars = 256, maxDomainChars = 256;
StringBuilder sbUser = new StringBuilder(maxUserChars);
StringBuilder sbDomain = new StringBuilder(maxDomainChars);
int lastErr = CredUIParseUserName(usernamePossiblyWithDomain, sbUser,
maxUserChars - 1, sbDomain, maxDomainChars - 1);
if(lastErr != 0) throw new Win32Exception(lastErr);
username = sbUser.ToString();
domain = sbDomain.ToString();
}
[DllImport("ntdsapi.dll", CharSet = CharSet.Unicode)]
protected static extern int DsMakePasswordCredentials(
string User, string Domain, IntPtr Password, ref IntPtr pAuthIdentity);
[DllImport("ntdsapi.dll")]
public static extern int DsFreePasswordCredentials(IntPtr AuthIdentity);
//caller is responsible for calling DsFreePasswordCredentials on the return val
public static IntPtr MakePassCreds(string username, string domain, IntPtr pPass)
{
IntPtr auth = IntPtr.Zero;
int lastErr = DsMakePasswordCredentials(username, domain, pPass, ref auth);
if(lastErr != 0) throw new Win32Exception(lastErr);
return auth;
}
[DllImport("ntdsapi.dll", CharSet = CharSet.Unicode)]
protected static extern int DsBindWithCred(string DomainControllerName,
string DnsDomainName, IntPtr AuthIdentity, ref IntPtr phDS);
[DllImport("ntdsapi.dll")]
public static extern int DsUnBind(ref IntPtr phDS);
}