How to Validate Domain Credentials

How to validate domain credentials?

C# in .NET 3.5 using System.DirectoryServices.AccountManagement.

 bool valid = false;
using (PrincipalContext context = new PrincipalContext(ContextType.Domain))
{
valid = context.ValidateCredentials( username, password );
}

This will validate against the current domain. Check out the parameterized PrincipalContext constructor for other options.

How to validate domain credentials without considering the Cached Domain Credential

Question already has an answer Why does Active Directory validate last password?

Solution is to use a Kerberos authentication.

The following code shows how you can perform credential validation using only Kerberos. The authentication method at use will not fall back to NTLM in the event of failure.

private const int ERROR_LOGON_FAILURE = 0x31;

private bool ValidateCredentials(string username, string password, string domain)
{
NetworkCredential credentials
= new NetworkCredential(username, password, domain);

LdapDirectoryIdentifier id = new LdapDirectoryIdentifier(domain);

using(LdapConnection connection = new LdapConnection(id, credentials, AuthType.Kerberos))
{
connection.SessionOptions.Sealing = true;
connection.SessionOptions.Signing = true;

try
{
connection.Bind();
}
catch (LdapException lEx)
{
if (ERROR_LOGON_FAILURE == lEx.ErrorCode)
{
return false;
}

throw;
}

return true;
}

Validate credentials for remote domain

Here is my final version of this test function that works with Powershell version older than 5.1.

Function Test-Cred
{
[CmdletBinding()]
[OutputType([String])]

Param (
[Parameter(
Mandatory = $false,
ValueFromPipeLine = $true,
ValueFromPipelineByPropertyName = $true
)]
[Alias(
'PSCredential'
)]
[ValidateNotNull()]
[System.Management.Automation.PSCredential]
[System.Management.Automation.Credential()]
$Credentials
)

# Checking module

Add-Type -AssemblyName System.DirectoryServices.AccountManagement

$ContextType = [System.DirectoryServices.AccountManagement.ContextType]::Domain
$ContextName = 'remote_domain.com'
$Validated = New-Object -TypeName System.DirectoryServices.AccountManagement.PrincipalContext -ArgumentList $ContextType, $ContextName, $($Credentials.UserName),$($Credentials.GetNetworkCredential().Password)
If ($Validated.ConnectedServer)
{
Return "Authenticated"
}
Else
{
Return "Not authenticated"
}
}

Validate user credentials against domain controller in .net

For the sake of completeness, here my solution which seems to do exactly what I want:

public class WinApiDomainAuthenticator
{
[DllImport("advapi32.dll", SetLastError = true)]
public static extern bool LogonUser(string lpszUsername,
string lpszDomain,
string lpszPassword,
int dwLogonType,
int dwLogonProvider,
out IntPtr phToken);

[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
public extern static bool CloseHandle(IntPtr handle);

public static IPrincipal Authenticate(String domainUser, String password)
{
var userToken = IntPtr.Zero;
var creds = new DomainAuthCredentials(domainUser, password);

if (! LogonUser(creds.Username,
creds.Domain,
creds.Password,
(int)LogonType.LOGON32_LOGON_BATCH,
(int)LogonProvider.LOGON32_PROVIDER_DEFAULT, out userToken))
{
var error = new Win32Exception(Marshal.GetLastWin32Error());
throw new SecurityException("Error while authenticating user", error);
}

var identity = new WindowsIdentity(userToken);

if (userToken != IntPtr.Zero)
CloseHandle(userToken);

return ConvertWindowsIdentityToGenericPrincipal(identity);
}

protected static IPrincipal ConvertWindowsIdentityToGenericPrincipal(WindowsIdentity windowsIdentity)
{
if (windowsIdentity == null)
return null;

// Identity in format DOMAIN\Username
var identity = new GenericIdentity(windowsIdentity.Name);

var groupNames = new string[0];
if (windowsIdentity.Groups != null)
{
// Array of Group-Names in format DOMAIN\Group
groupNames = windowsIdentity.Groups
.Select(gId => gId.Translate(typeof(NTAccount)))
.Select(gNt => gNt.ToString())
.ToArray();
}

var genericPrincipal = new GenericPrincipal(identity, groupNames);
return genericPrincipal;
}

protected class DomainAuthCredentials
{
public DomainAuthCredentials(String domainUser, String password)
{
Username = domainUser;
Password = password;
Domain = ".";

if (!domainUser.Contains(@"\"))
return;

var tokens = domainUser.Split(new char[] { '\\' }, 2);
Domain = tokens[0];
Username = tokens[1];
}

public DomainAuthCredentials()
{
Domain = String.Empty;
}

#region Properties

public String Domain { get; set; }
public String Username { get; set; }
public String Password { get; set; }

#endregion
}
}

The LogonType and LogonProvider enums reflect the definitions in "Winbase.h". I settled with LogonType.LOGON32_LOGON_BATCH instead of LogonType.LOGON32_LOGON_NETWORK because samba 3.4.X seems to have trouble with this type.



Related Topics



Leave a reply



Submit