How do I validate Active Directory creds over LDAP + SSL?

.net, active-directory, c#, directoryservices, ldap

Solution

I was able to validate credentials using the `System.DirectoryServices.Protocols` namespace, thanks to a co-worker. Here's the code:

// See http://support.microsoft.com/kb/218185 for full list of LDAP error codes
const int ldapErrorInvalidCredentials = 0x31;

const string server = "sd.example.com:636";
const string domain = "sd.example.com";

try
{
    using (var ldapConnection = new LdapConnection(server))
    {
        var networkCredential = new NetworkCredential(_username, _password, domain);
        ldapConnection.SessionOptions.SecureSocketLayer = true;
        ldapConnection.AuthType = AuthType.Negotiate;
        ldapConnection.Bind(networkCredential);
    }

    // If the bind succeeds, the credentials are valid
    return true;
}
catch (LdapException ldapException)
{
    // Invalid credentials throw an exception with a specific error code
    if (ldapException.ErrorCode.Equals(ldapErrorInvalidCredentials))
    {
        return false;
    }

    throw;
}

I'm not thrilled with using a try/catch block to control decisioning logic, but it's what works. :/

Problem

I'm trying to use the .NET 3.5 `System.DirectoryServices.AccountManagement` namespace to validate user credentials against our Active Directory LDAP server over an SSL encrypted LDAP connection. Here's the sample code: ``` using (var pc = new PrincipalContext(ContextType.Domain, "sd.example.com:389", "DC=sd,DC=example,DC=com", ContextOptions.Negotiate)) { return pc.ValidateCredentials(_username, _password); } ``` This code works fine over unsecured LDAP (port 389), however I'd rather not transmit a user/pass combination in clear text. But when I change to LDAP + SSL (port 636), I get the following exception: ``` System.DirectoryServices.Protocols.DirectoryOperationException: The server cannot handle directory requests. at System.DirectoryServices.Protocols.ErrorChecking.CheckAndSetLdapError(Int32 error) at System.DirectoryServices.Protocols.LdapSessionOptions.FastConcurrentBind() at System.DirectoryServices.AccountManagement.CredentialValidator.BindLdap(NetworkCredential creds, ContextOptions contextOptions) at System.DirectoryServices.AccountManagement.CredentialValidator.Validate(String userName, String password) at System.DirectoryServices.AccountManagement.PrincipalContext.ValidateCredentials(String userName, String password) at (my code) ``` Port 636 works for other activities, such as looking up non-password information for that LDAP/AD entry... ``` UserPrincipal.FindByIdentity(pc, IdentityType.SamAccountName, _username) ``` ...so I know it's not my LDAP server's SSL setup, since it works over SSL for other lookups. Has anyone gotten the `ValidateCredentials(...)` call to work over SSL? Can you explain how? Or is there another/better way to securely validate AD/LDAP credentials?

Original source