15

I get an error by a website, on which I use Windows Authentication.

Strange things:

  • Only occurs if user is not yet saved into database (new unknown user)
  • Appears only on live system, everything fine on local development environment

This is what I get in a logging mail:

Source : System.DirectoryServices

Message: The server is not operational.

Trace:
at System.DirectoryServices.DirectoryEntry.Bind(Boolean throwIfFail)
at System.DirectoryServices.DirectoryEntry.Bind()
at System.DirectoryServices.DirectoryEntry.get_AdsObject()
at System.DirectoryServices.DirectorySearcher.FindAll(Boolean findMoreThanOne)
at System.DirectoryServices.DirectorySearcher.FindOne()
at Smarthouse.Labs.DataAccess.UserListManager.SaveUser(String windowsUserName)

This is how I implement DirectorySearch:

private void SaveUser(string windowsUserName)
{
    string[] domainAndUser = windowsUserName.Split('\\');
    string domain = domainAndUser[0];
    string username = domainAndUser[1];

    DirectoryEntry entry = new DirectoryEntry("LDAP://" + domain);
    DirectorySearcher search = new DirectorySearcher(entry);

    try
    {
        // Bind to the native AdsObject to force authentication.
        search.Filter = "(SAMAccountName=" + username + ")";
        search.PropertiesToLoad.Add("cn");
        search.PropertiesToLoad.Add("sn");
        search.PropertiesToLoad.Add("givenName");
        search.PropertiesToLoad.Add("mail");

        SearchResult result = search.FindOne();

        if (result == null)
        {
            throw new Exception("No results found in Windows authentication.");
        }

        User userToSave = new User();
        userToSave.FirstName = (String) result.Properties["givenName"][0];
        userToSave.LastName = (String) result.Properties["sn"][0];
        userToSave.Email = (String) result.Properties["mail"][0];
        userToSave.Username = windowsUserName;
        userToSave.Guid = Guid.NewGuid();

        SaveUser(userToSave);
    }
    catch (Exception ex)
    {
        throw new Exception("Error authenticating user. " + ex.Message, ex);
    }
    finally
    {
        //Dispose service and search to prevent leek in memory
        entry.Dispose();
        search.Dispose();
    }
}

If more code examples are needed just tell me.

jparthj
  • 1,606
  • 3
  • 20
  • 44
Tai Kahar
  • 264
  • 1
  • 3
  • 11

4 Answers4

23

Your problem is that you're using a "plain" domain name to bind - this won't work in LDAP. Actually, if you try to bind to LDAP://MyDomain, what you're really doing is trying to bind to the server called MyDomain.

You need a valid LDAP bind string - something like LDAP://dc=yourdomain,dc=local or something.

To find out what your default LDAP binding context is, use this code snippet:

DirectoryEntry deRoot = new DirectoryEntry("LDAP://RootDSE");

if (deRoot != null)
{
   string defaultNamingContext = deRoot.Properties["defaultNamingContext"].Value.ToString();
}

Once you have that string - use that as your bind string to your LDAP server.

And if you're on .NET 3.5 and up, you should check out the System.DirectoryServices.AccountManagement (S.DS.AM) namespace. Read all about it here:

Basically, you can define a domain context and easily find users and/or groups in AD:

// set up domain context -- no domain name needed, uses default domain 
PrincipalContext ctx = new PrincipalContext(ContextType.Domain);

// find a user
UserPrincipal user = UserPrincipal.FindByIdentity(ctx, username);

if(user != null)
{
   // do something here....     
}

The new S.DS.AM makes it really easy to play around with users and groups in AD!

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
  • I'll have a look on that on monday. Thank you a lot:) Had to fix other stuff on live websites and this is on low prio. – Tai Kahar Oct 28 '11 at 16:38
  • Sorry, that i hadn't time to answer for a while. I tested this one local(right domain) and everything seems to be fine. Hopefully it will also run on live system. – Tai Kahar Nov 15 '11 at 17:05
  • 1
    Finally i thought about it and changed to System.DirectoryServices.AccountManagement. Easy to use and hopefulyl fixes the bug. – Tai Kahar Nov 15 '11 at 17:37
  • Part 1 worked, I don't quite get why though, (bindings? Ad has them?) I'm moving a borderline legacy app to a new domain, the old ldap string was LDAP://[fqdn]/OU=...DC=... , the new one is LDAP://OU=...,DC=... I'm so confused :) At least it worked, thanks. – RandomUs1r Feb 19 '13 at 22:19
1

To add to marc_s's answer above, I needed to search multiple domains. So for each Domain I did the following:

DirectoryEntry deRoot = new DirectoryEntry("LDAP://" +"DomainName"+ "/RootDSE");
string defaultNamingContext = "LDAP://" + deRoot.Properties["defaultNamingContext"].Value.ToString();
DirectoryEntry mySearchRoot = new DirectoryEntry(defaultNamingContext);
DirectorySearcher myDirectorySearcher = new DirectorySearcher(mySearchRoot);
KyleMit
  • 30,350
  • 66
  • 462
  • 664
1

You can use bind strings in the format LDAP://mydomain.com:389. I kept getting "Access is Denied" when trying to use the format LDAP://DC=mydomain,DC=com. Once I switched to the LDAP://mydomain.com:389 format, and bound using the AuthenticationTypes.ServerBind flag when constructing my DirectoryEntry, it worked great. This was in Azure App Service.

r590
  • 689
  • 1
  • 10
  • 15
0

Similar Error Happened to me (though it happened all the time and not in specific cases like pointed out here) because of a wrong Active Directory connection string. i used the corp instead the prod one . Use something that works for another app in your organization if exists.