Question

I'm testing the behavior of the .net LDAP client when a bad password is provided enough times to trigger a lockout.

I see this odd behavior: it seems that if the process successfully connected at any time, then it is able to re-connect even after purposefully triggering a lockout.

Here's a shorthand version of my binding method:

private DirectoryEntry Bind(string userId, string password)
{
    var entry = new DirectoryEntry(BasePath, userId, password);
    // If the password is bad, the attempt to access entry.NativeObject throws an exception. 
    var obj = entry.NativeObject;  // causes the bind to occur
    return entry;
}

My test proceeds as follows:

private void TestLockout()
{
    // attempt with bad pw enough times to trigger a lockout.
    for (int i=0; i < 5; i++)
    {
        try
        {
            // i.ToString() is a purposefully bad pw
            Bind("testuser", i.ToString());
        }
        catch
        {
        }
    }
    // Now make sure that an attempt with a good pw fails due to lockout
    var bindSuccess = true;
    try
    {
        Bind("testuser", "correctpassword");
    }
    catch
    {
        bindSuccess = false;  
    }
    // the output should be "false"
    Console.WriteLine("Bind result is " + bindSuccess.ToString();
}

This works fine as is. However, if a call to Bind() with a good password precedes the test, I get different results.

IOW, this:

Bind("testuser", "correctpassword");  // succeeds
TestLockout(); // does not give the correct result

The following happens.

a) TestLockout produces incorrect output, because the final Bind succeeds and it should not.
b) Yet, I know that the user was locked out, because of subsequent inspection.

So it appears that some component is tracking whether the current process ever connected successfully. I need to have a way to clear this condition. This authentication code will be executing in a long-running service process and it is not acceptable to authenticate a user when they are really locked out.

Was it helpful?

Solution

This is related to the fact that DirectoryEntry is using ADSI. ADSI has internal LDAP connection pool built based on the BasePath, Username and Password.

If you tried to bind with the correct password before the account was locked out, an LDAP connection was successfully made using that correct password and cached in the connection pool. Then, you made your account locked out and tried to bind to the Active Directory using the same BasePath, Username and Password. DirectoryEntry at this time will not establish a new LDAP connection but reuse the previous one. You can prove that by looking at the network trace.

To fix it, you can dispose your DirectoryEntry when you don't need it. Once your DirectoryEntry is disposed, ADSI should be smart enough to close the LDAP connection that it no longer requires. In your sample code, it looks like you never need it again. So, this should fix the problem.

private void Bind(string userId, string password)
{
    using (var entry = new DirectoryEntry(BasePath, userId, password))
    {
        // If the password is bad, the attempt to access entry.NativeObject throws an exception. 
        var obj = entry.NativeObject;  // causes the bind to occur
    }
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top