Windows Service LdapException: The LDAP server is unavailable

gipinani picture gipinani · Aug 2, 2019 · Viewed 7.5k times · Source

I have a Windows Service that provides data through WCF to a Windows Forms application. The service takes care also of user authentication, validating user password using LDAP over the company Active Directory servers.

The problem is that it works for weeks (even months), than something happens and the LDAP user authentication fails with the following exception until I restart the service:

System.DirectoryServices.AccountManagement.PrincipalServerDownException: The server could not be contacted. 
---> System.DirectoryServices.Protocols.LdapException: The LDAP server is unavailable.     
at System.DirectoryServices.Protocols.LdapConnection.Connect()     
at System.DirectoryServices.Protocols.LdapConnection.SendRequestHelper(DirectoryRequest request, Int32& messageID)     
at System.DirectoryServices.Protocols.LdapConnection.SendRequest(DirectoryRequest request, TimeSpan requestTimeout)     
at System.DirectoryServices.Protocols.LdapConnection.SendRequest(DirectoryRequest request)     
at System.DirectoryServices.AccountManagement.PrincipalContext.ReadServerConfig(String serverName, ServerProperties& properties)    
 --- End of inner exception stack trace ---     
 at System.DirectoryServices.AccountManagement.PrincipalContext.ReadServerConfig(String serverName, ServerProperties& properties)     
 at System.DirectoryServices.AccountManagement.PrincipalContext.DoServerVerifyAndPropRetrieval()     
 at System.DirectoryServices.AccountManagement.PrincipalContext..ctor(ContextType contextType, String name, String container, ContextOptions options, String userName, String password)     
 at System.DirectoryServices.AccountManagement.PrincipalContext..ctor(ContextType contextType, String name, String userName, String password)     
 at SMSTModel.Authentication.ActiveDirectory.IsUserAllowed(String username, String password)

The service restart fixes the problem.

public static bool IsUserAllowed(string username, string password)
{
    String localDomain = Domain.GetComputerDomain().Name;
    string userDomain = null;
    string user = username;
    if (user.Contains(@"\"))
    {
        userDomain = user.Substring(0, user.IndexOf("\\"));
        user = user.Substring(user.IndexOf("\\") + 1);
    }

    userDomain = userDomain != null ? userDomain : localDomain;
    using (PrincipalContext pc = new PrincipalContext(ContextType.Domain, userDomain, user, password))
    {
        bool credOk = pc.ValidateCredentials(user, password);
        if (!credOk)
            return false;
        using (UserPrincipal userP = UserPrincipal.FindByIdentity(pc, user))
        {
            if (userP != null)
            {
                using (PrincipalContext pc1 = new PrincipalContext(ContextType.Domain, localDomain))
                {
                    using (GroupPrincipal groupPrincipal = new GroupPrincipal(pc1))
                    {
                        groupPrincipal.Name = "APP_*";
                        using (PrincipalSearcher principalSearcher = new PrincipalSearcher(groupPrincipal))
                            foreach (Principal found in principalSearcher.FindAll())
                            {
                                if (found.Name == "APP_Group" && found is GroupPrincipal && userP.IsMemberOf((GroupPrincipal)found))
                                {
                                    return true;
                                }
                            }
                    }
                }
            }
        }
    }

    return false;
}

Any idea on why it happens and how to fix it?

Answer

Gabriel Luci picture Gabriel Luci · Aug 2, 2019

It looks like the exception is happening here:

using (PrincipalContext pc = new PrincipalContext(ContextType.Domain, userDomain, user, password))

So do you know what the userDomain being used is? It seems like you might have more than one domain in your environment, so does it happen with all of your domains, or only one?

In our environment, I've seen cases where our AD admins decommission a domain controller, but for some reason that server still shows up in DNS. In other words, if I do a DNS lookup in the command line:

nslookup example.com

one of the IPs is for a decommissioned DC.

That's a possibility in your case. If it picked a bad IP address, then restarting the application would make it do another DNS lookup, which might return a different IP address at the top of the list and things would work again.

To get to the bottom of this, you will really have to observe what is going on at the time it stops working. If you haven't already, install Wireshark on your server. When it stops working, use Wireshark to look for traffic using port 389 (the default LDAP port) and see which IP it's trying to connect to.