title: Win32: How to validate credentials against Active Directory?
body: <p>It has been <a href="https://stackoverflow.com/questions/290548/c-validate-a-username-and-password-against-active-directory">asked</a>, and <a href="https://stackoverflow.com/questions/326818/c-how-to-validate-domain-credentials">answered for .NET</a>, but now it's time to get an answer for native Win32 code:</p>
  
  <p><strong>How do i validate a Windows username and password?</strong></p>
  
  <p>i <a href="https://stackoverflow.com/questions/326818/c-how-to-validate-domain-credentials">asked this question before for managed code</a>. Now it's time for the native solution.</p>
  
  <hr>
  
  <p>It needs to be pointed the pitfalls with some of the more commonly proposed solutions:</p>
  
  <h2>Invalid Method 1. Query Active Directory with Impersonation</h2>
  
  <p>A lot of people <a href="https://stackoverflow.com/questions/290548/c-validate-a-username-and-password-against-active-directory/290580#290580">suggest querying the Active Directory</a> for something. If an exception is thrown, then you know the credentials are not valid - as is suggested in <a href="https://stackoverflow.com/questions/290548/c-validate-a-username-and-password-against-active-directory/290580#290580">this stackoverflow question</a>.</p>
  
  <p>There are <a href="http://www.pcreview.co.uk/forums/fyi-easy-way-validate-ad-credentials-win2k-using-c-t1374121.html" rel="noreferrer">some serious drawbacks to this approach</a> however:</p>
  
  <blockquote>
    <ul>
    <li><p>You are not only authenticating a domain account, but you are also doing an implicit authorization check. That is, you are reading properties from the AD using an impersonation token. What if the otherwise valid account has no rights to read from the AD? By default all users have read access, but domain policies can be set to disable access permissions for restricted accounts (and or groups).</p></li>
    <li><p>Binding against the AD has a serious overhead, the AD schema cache has to be loaded at the client (ADSI cache in the ADSI provider used by DirectoryServices). This is both network, and AD server, resource consuming - and is too expensive for a simple operation like authenticating a user account.</p></li>
    <li><p>You're relying on an exception failure for a non-exceptional case, and assuming that means invalid username and password. Other problems (e.g. network failure, AD connectivity failure, memory allocation error, etc) are then mis-intrepreted as authentication failure.</p></li>
    </ul>
  </blockquote>
  
  <p>The use of the <a href="http://msdn.microsoft.com/en-us/library/system.directoryservices.directoryentry.aspx" rel="noreferrer"><code>DirectoryEntry</code></a> class is .NET is an example of an incorrect way to verify credentials:</p>
  
  <p><strong>Invalid Method 1a - .NET</strong></p>
  
  <pre><code>DirectoryEntry entry = new DirectoryEntry("persuis", "iboyd", "Tr0ub4dor&amp;3");
  object nativeObject = entry.NativeObject;
  </code></pre>
  
  <p><strong>Invalid Method 1b - .NET #2</strong></p>
  
  <pre><code>public static Boolean CheckADUserCredentials(String accountName, String password, String domain)
  {
      Boolean result;
  
      using (DirectoryEntry entry = new DirectoryEntry("LDAP://" + domain, accountName, password))
      {
          using (DirectorySearcher searcher = new DirectorySearcher(entry))
          {
              String filter = String.Format("(&amp;(objectCategory=user)(sAMAccountName={0}))", accountName);
              searcher.Filter = filter;
              try
              {
                  SearchResult adsSearchResult = searcher.FindOne();
                  result = true;
              }
              catch (DirectoryServicesCOMException ex)
              {
                  const int SEC_E_LOGON_DENIED = -2146893044; //0x8009030C;
                  if (ex.ExtendedError == SEC_E_LOGON_DENIED)
                  {
                      // Failed to authenticate. 
                      result = false;
                  }
                  else
                  {
                      throw;
                  }
              }
          }
      }
  </code></pre>
  
  <p>As well as querying Active Directory through an ADO connection:</p>
  
  <p><strong>Invalid Method 1c - Native Query</strong></p>
  
  <pre><code>connectionString = "Provider=ADsDSOObject;
         User ID=iboyd;Password=Tr0ub4dor&amp;3;
         Encrypt Password=True;Mode=Read;
         Bind Flags=0;ADSI Flag=-2147483648';"
  
  SELECT userAccountControl 
  FROM 'LDAP://persuis/DC=stackoverflow,DC=com'
  WHERE objectClass='user' and sAMAccountName = 'iboyd'
  </code></pre>
  
  <p>These both fail even when your credentials are <strong>valid</strong>, but you do not have permission to view your directory entry:</p>
  
  <p><img src="https://i.stack.imgur.com/UDoGv.png" alt="enter image description here"></p>
  
  <h2>Invalid Method 2. LogonUser Win32 API</h2>
  
  <p><a href="https://i.stack.imgur.com/UDoGv.png" rel="noreferrer">Others</a> have suggested using the <a href="http://msdn.microsoft.com/en-us/library/aa378184.aspx" rel="noreferrer">LogonUser()</a> API function. This sounds nice, but unfortunatly the calling user sometimes needs a permission ususally only given to the operating system itself:</p>
  
  <blockquote>
    <p>The process calling LogonUser requires
    the SE_TCB_NAME privilege. If the
    calling process does not have this
    privilege, LogonUser fails and
    GetLastError returns
    ERROR_PRIVILEGE_NOT_HELD. </p>
    
    <p>In some
    cases, the process that calls
    LogonUser must also have the
    SE_CHANGE_NOTIFY_NAME privilege
    enabled; otherwise, LogonUser fails
    and GetLastError returns
    ERROR_ACCESS_DENIED. This privilege is
    not required for the local system
    account or accounts that are members
    of the administrators group. By
    default, SE_CHANGE_NOTIFY_NAME is
    enabled for all users, but some
    administrators may disable it for
    everyone.</p>
  </blockquote>
  
  <p>Handing out the "<em>Act as a part of the operating system</em>" privelage is not something you want to do willy-nilly - as Microsoft points out in a <a href="http://support.microsoft.com/kb/180548" rel="noreferrer">knowledge base article</a>:</p>
  
  <blockquote>
    <p>...the process that is calling
    LogonUser must have the SE_TCB_NAME
    privilege (in User Manager, this is
    the "<em>Act as part of the Operating
    System</em>" right). The SE_TCB_NAME
    privilege is very powerful and
    <strong>should not be granted to any arbitrary user just so that they can
    run an application</strong> that needs to
    validate credentials.</p>
  </blockquote>
  
  <p>Additionally, a call to LogonUser() will fail if a blank password is specified.</p>
  
  <hr>
  
  <h2>Valid .NET 3.5 Method - <code>PrincipalContext</code></h2>
  
  <p>There is a validation method, only available in .NET 3.5 and newer, that allows authentication by a user without performing an authorization check:</p>
  
  <pre><code>// create a "principal context" - e.g. your domain (could be machine, too)
  using(PrincipalContext pc = new PrincipalContext(ContextType.Domain, "stackoverflow.com"))
  {
      // validate the credentials
      bool isValid = pc.ValidateCredentials("iboyd", "Tr0ub4dor&amp;3")
  }
  </code></pre>
  
  <p>Unfortunately this code is only available in .NET 3.5 and later.</p>
  
  <p>It's time to find the <em>native</em> equivalent.</p>