C# / ASP.NET / AD - "The specified directory service attribute or value does not exist."

10,680

I've hit an issue like this once. It may be because you can't retrieve LDAP NativeObject property for authentication. If the exception is thrown right after the object obj = entry.NativeObject; call, check if user has permissions on the domain.

Either debug through your code to see if it is indeed the NativeObject binding that is failing. Or Put a try/catch block around the binding in your IsAuthenticated() function like below. You should see the custom error thrown if it's caused by the issue I'm describing.

try
{   //Bind to the native AdsObject to force authentication.         
    Object obj = entry.NativeObject;
}
catch (System.Runtime.InteropServices.COMException e)
{
    if (e.ErrorCode == -2147016694) // -2147016694 - The specified directory service attribute or value does not exist.
    {
        throw new Exception("Can't retrieve LDAP NativeObject property");
    }
    throw;
}
Share:
10,680
Dale Jay
Author by

Dale Jay

Updated on June 04, 2022

Comments

  • Dale Jay
    Dale Jay almost 2 years

    When submitting valid credentials to Active Directory via a ASP.NET web form, the following error message is returned: "The specified directory service attribute or value does not exist."

    Code for LDAP Authentication:

    using System;
    using System.Text;
    using System.Collections;
    using System.DirectoryServices;
    
    namespace FormsAuth
    {
        public class LdapAuthentication
        {
            private string _path;
            private string _filterAttribute;
    
            public LdapAuthentication(string path)
            {
                _path = path;
            }
    
            public bool IsAuthenticated(string domain, string username, string pwd)
            {
                string domainAndUsername = "CBHC" + @"\" + username;
                DirectoryEntry entry = new DirectoryEntry(_path, domainAndUsername, pwd);
    
                try
                {
                    //Bind to the native AdsObject to force authentication.
                    object obj = entry.NativeObject;
    
                    DirectorySearcher search = new DirectorySearcher(entry);
    
                    search.Filter = "(SAMAccountName=" + username + ")";
                    search.PropertiesToLoad.Add("cn");
                    SearchResult result = search.FindOne();
    
                    if (null == result)
                    {
                        return false;
                    }
    
                    //Update the new path to the user in the directory.
                    _path = result.Path;
                    _filterAttribute = (string)result.Properties["cn"][0];
                }
                catch (Exception ex)
                {
                    throw new Exception("Error authenticating user. " + ex.Message);
                }
    
                return true;
            }
    
            public string GetGroups()
            {
                DirectorySearcher search = new DirectorySearcher(_path);
                search.Filter = "(cn=" + _filterAttribute + ")";
                search.PropertiesToLoad.Add("memberOf");
                StringBuilder groupNames = new StringBuilder();
    
                try
                {
                    SearchResult result = search.FindOne();
                    int propertyCount = result.Properties["memberOf"].Count;
                    string dn;
                    int equalsIndex, commaIndex;
    
                    for (int propertyCounter = 0; propertyCounter < propertyCount; propertyCounter++)
                    {
                        dn = (string)result.Properties["memberOf"][propertyCounter];
                        equalsIndex = dn.IndexOf("=", 1);
                        commaIndex = dn.IndexOf(",", 1);
                        if (-1 == equalsIndex)
                        {
                            return null;
                        }
                        groupNames.Append(dn.Substring((equalsIndex + 1), (commaIndex - equalsIndex) - 1));
                        groupNames.Append("|");
                    }
                }
                catch (Exception ex)
                {
                    throw new Exception("Error obtaining group names. " + ex.Message);
                }
                return groupNames.ToString();
            }
        } 
    }
    

    Code on login page:

    <script runat=server>
    void Login_Click(object sender, EventArgs e)
    {
        string adPath = "LDAP://server/DC=domain,DC=loc"; //Path to your LDAP directory server
      LdapAuthentication adAuth = new LdapAuthentication(adPath);
      try
      {
        if(true == adAuth.IsAuthenticated("CBHC",txtUsername.Text, txtPassword.Text))
        {
          string groups = adAuth.GetGroups();
    
          //Create the ticket, and add the groups.
          bool isCookiePersistent = chkPersist.Checked;
          FormsAuthenticationTicket authTicket = new FormsAuthenticationTicket(1, 
                    txtUsername.Text,DateTime.Now, DateTime.Now.AddMinutes(60), isCookiePersistent, groups);
    
          //Encrypt the ticket.
          string encryptedTicket = FormsAuthentication.Encrypt(authTicket);
    
          //Create a cookie, and then add the encrypted ticket to the cookie as data.
          HttpCookie authCookie = new HttpCookie(FormsAuthentication.FormsCookieName, encryptedTicket);
    
          if(true == isCookiePersistent)
          authCookie.Expires = authTicket.Expiration;
    
          //Add the cookie to the outgoing cookies collection.
          Response.Cookies.Add(authCookie);
    
          //You can redirect now.
    
          Response.Redirect(FormsAuthentication.GetRedirectUrl(txtUsername.Text, false));
        }
        else
        {
          errorLabel.Text = "Authentication did not succeed. Check user name and password.";
        }
      }
      catch(Exception ex)
      {
        errorLabel.Text = "Error authenticating. " + ex.Message;
      }
    }
    </script>
    

    Authentication settings on Web.config for form on IIS:

    <authentication mode="Windows">
          <forms loginUrl="logon.aspx" name="adAuthCookie" timeout="10" path="/" />
        </authentication>
        <authorization>
          <deny users="?" />
          <allow users="*" />
        </authorization>
        <identity impersonate="true" />
    

    Word of note: This does not occur when running the site in Debug; in that situation, it authenticates perfectly and moves on to the Default page. It only occurs when contacting the page live on the IIS server.

  • Dale Jay
    Dale Jay over 10 years
    Sadly I don’t think this applies to us… We only have one domain and are not trying to authenticate across multiple.
  • tokafew420
    tokafew420 over 10 years
    Not sure how you came to the conclusion that I'm talking about multiple domains. What I mean to say is that, if the user you are trying to authenticate does not have read access to the Active Directory Domain, then you will get the exception: -2147016694 The specified directory service attribute or value does not exist.
  • Dale Jay
    Dale Jay over 10 years
    Sorry. Asked somebody else to interpret your previous answer as my AD knowledge is minimal and I am not a domain admin. This user (me) is able to access other AD-authenticated servers / programs / etc. on the domain.
  • Dale Jay
    Dale Jay over 10 years
    Yes. It returns that error code.
  • tokafew420
    tokafew420 over 10 years
    I noticed that you have <authentication mode="Windows">. You should use <authentication mode="Forms">. Also follow the instructions in the "To configure IIS for anonymous authentication" section in the following Active Directory Authentication from ASP .NET
  • Dale Jay
    Dale Jay over 10 years
    Thanks, TonyV. Gave that a go. No luck.