4

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?

Gabriel Luci
  • 38,328
  • 4
  • 55
  • 84
gipinani
  • 14,038
  • 12
  • 56
  • 85
  • 1
    There may be several reasons. The first one is obvious - server is unresponsive or some network issues. The second one is dc throttling policy vs dos attacks. When you create a connection to dc an then dispose it, it may not be closed under the hood. For example, if you make large number of ad requests in a short amount of time you will get this error. In both cases when you restart your service you may be connected to a new dc by dc locator service – oldovets Aug 04 '19 at 19:20
  • 1
    To avoid this I would recommend 2 things. 1. Create and cache one connection to a domain (e. g. to rootdse partition). 2. Connect to a domain controller, not to a domain. If you face this error, just find new dc with force rediscovery option – oldovets Aug 04 '19 at 19:25

1 Answers1

2

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.

Gabriel Luci
  • 38,328
  • 4
  • 55
  • 84
  • Thanks for your answer. I'm 100% sure that the domain used is always the same (other available domains are only meant for administration purpose and normal users do not use them). It seems that our network team did nothing when the problem arose (it started giving problem out of team working hours), so I exclude changes in AD config. I'll try to monitor this with WireShark (when it happens again), but I'm not sure I'll be allowed to install it on our DC – gipinani Aug 02 '19 at 15:00
  • You don't need to install Wireshark on the DC. Install it on the server running this code. – Gabriel Luci Aug 02 '19 at 16:53
  • I am facing a similar issue but the difference is I am setting up a local environment and it's throwing the same exception from PrincipalContext(). Any idea what I need to check ?? – Renascent Nov 11 '21 at 14:11