title: How to perform Windows Authentication?
body: <p>SQL Server, File and Printer Sharing, Exchange, and a number of other applications are able to authenticate the user based on their Windows identity.</p>
  <p>How do they do this? In particular, how can i do this?</p>
  <p>As a concrete example, complete the native Windows code inside the following method:</p>
  <pre><code>Boolean IsCurrentUserValidForDomain(String domainName)
  {
     //TODO: Ask Stackoverflow to fill in the code here
  }
  </code></pre>
  <p>I can get us started off:</p>
  <pre><code>Boolean IsCurrentUserValidForDomain(String domainName)
  {
      //Get the security token associated with the thread
      TOKEN userToken;
  
      // Get the calling thread's access token.
      if (!OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, true, out userToken)
      {
         if (GetLastError != ERROR_NO_TOKEN)
            throw new Exception(&quot;Could not get current thread security token&quot;);
  
         // Retry against process token since no thread token exists.
         if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, out userToken)           
            throw new Exception(&quot;Could not get current process security token&quot;);
      }
  
      //We now have the security token of the running user (userToken)
  
      //From this, we can get the SID of the user
      PSID sidUser = null;
  
      DWORD cbBuf = 0;
      Boolean bsuccess = GetTokenInformation(hToken, TokenUser, null, 0, ref cbBuf);
      PTOKEN_USER ptiUser = null;
      while ((!bSuccess) &amp;&amp; (GetLastError() = ERROR_INSUFFICIENT_BUFFER))
      {
         ReallocMem(ref ptiUser, cbBuf);
         bSuccess = GetTokenInformation(hToken, TokenUser, ptiUser, cbBuf, ref cbBuf);
      }
      sidUser = ptiUser.User.Sid;
  
      //Now that we have the user's SID, we can get the SID of their domain
      PSID sidDomain = null;
      GetWindowsAccountDomainSid(sidUser, null, ref cbBuff);
      ReallocMem(sidDomain, cbBuff);
      GetWindowsAccountDomainSid(sidUser, sidDomain, ref cbBuff);
  
      //We now have
      //TOKEN userToken: security token of the running user
      //PSID sidUser (S-1-5-21-2154378322-3929449213-1104335884-1006)
      //PSID sidDomain (S-1-5-21-2154378322-3929449213-1104335884)
  
      //TODO: ask stackoverflow if anything i've computed so far can help 
      //answer the question
  
      //TODO: Ask Stackoverflow to fill in the code here
  }
  
  
      
  </code></pre>
  <hr />
  <p><strong>Note:</strong> everything from here down is <em>&quot;shows research effort&quot;</em>. You can stop reading now. I only have it to document my own research effort (some of which can be very useful in other situations). I also want to preëmpt some of the more common, and insecure, approaches (which is a trap i fell into myself). If i can help anyone else avoid the same trap - all the better.</p>
  <h2>Background</h2>
  <p>When a user connects to SQL Server, they have an option of using <em>Integrated Authentication</em>:</p>
  <blockquote>
  <p>Integrated security uses the current Windows identity established on the operating system thread to access the SQL Server database</p>
  </blockquote>
  <p>and more from SQL Server:</p>
  <blockquote>
  <p>When a user connects through a Windows user account, SQL Server validates the account name and password using the Windows principal token in the operating system. This means that the user identity is confirmed by Windows. SQL Server does not ask for the password, and does not perform the identity validation.</p>
  </blockquote>
  <p><strong>Result:</strong> I can login to SQL Server without having to type a username and password</p>
  <p>And if i connect to a remote network share, my own user credentials are used to validate that i am a user on the remote server. I can browse to the remote machine and the connection implicitly used my logged in user account to validate access.</p>
  <p><strong>Result</strong>: I can connect to a network share without having to type a username and password.</p>
  <p>How does SQL Server validate me as a user?<br />
  How does File and Printer Sharing validate me as a user?</p>
  <p>Lets say i am writing my own database engine, and i want to support <strong>&quot;Windows Authentication&quot;</strong> how would i do it?</p>
  <p>Lets say my SQL database engine is running on a non-domain joined PC, with a local user account. I <strong>can</strong> get various pieces of information about them</p>
  <h2>Local user</h2>
  <ul>
  <li><a href="https://msdn.microsoft.com/en-us/library/windows/desktop/ms724432%28v=vs.85%29.aspx?f=255&amp;MSPPError=-2147217396" rel="nofollow noreferrer"><strong>GetUsername</strong></a>: <code>ginger</code></li>
  <li><strong>GetUsernameEx(NameSamCompatible)</strong>: <code>HYDROGEN\Ginger</code> <em>(hydrogen is the name of the machine)</em></li>
  <li><a href="https://msdn.microsoft.com/en-us/library/windows/desktop/aa446671(v=vs.85).aspx" rel="nofollow noreferrer"><strong>GetTokenInformation(TokenUser)</strong></a>: UserSID = <code>S-1-5-21-2154378322-3929449213-1104335884-1006</code></li>
  <li><a href="https://msdn.microsoft.com/en-us/library/windows/desktop/aa379166(v=vs.85).aspx" rel="nofollow noreferrer"><strong>LookupAccountSid</strong></a>
  <ul>
  <li>username: <code>Ginger</code></li>
  <li>domain: <code>HYDROGEN</code></li>
  </ul>
  </li>
  <li><a href="https://msdn.microsoft.com/en-us/library/windows/desktop/aa446676(v=vs.85).aspx" rel="nofollow noreferrer"><strong>GetWindowsAccountDomainSid</strong></a>: <code>S-1-5-21-2154378322-3929449213-1104335884</code></li>
  </ul>
  <p>Lets say my SQL database engine is running on a domain joined PC, with a domain user account (and to make it interesting, the user is from a different domain than the machine-joined domain). I <strong>can</strong> get various pieces of information about the user:</p>
  <ul>
  <li><a href="https://msdn.microsoft.com/en-us/library/windows/desktop/ms724432%28v=vs.85%29.aspx?f=255&amp;MSPPError=-2147217396" rel="nofollow noreferrer"><strong>GetUsername</strong></a>: <code>forest</code></li>
  <li><strong>GetUsernameEx(NameSamCompatible)</strong>: <code>CONTOSO\forest</code> <em>(contoso is the legacy, SAM Compatible, NetBIOS name of the domain. The actual domain name is contoso.com)</em></li>
  <li><a href="https://msdn.microsoft.com/en-us/library/windows/desktop/aa446671(v=vs.85).aspx" rel="nofollow noreferrer"><strong>GetTokenInformation(TokenUser)</strong></a>: UserSID = <code>S-1-5-21-1708537768-854245398-2146844275-3110</code></li>
  <li><a href="https://msdn.microsoft.com/en-us/library/windows/desktop/aa379166(v=vs.85).aspx" rel="nofollow noreferrer"><strong>LookupAccountSid</strong></a>
  <ul>
  <li>username: <code>forest</code></li>
  <li>domain: <code>CONTOSO</code></li>
  </ul>
  </li>
  <li><a href="https://msdn.microsoft.com/en-us/library/windows/desktop/aa446676(v=vs.85).aspx" rel="nofollow noreferrer"><strong>GetWindowsAccountDomainSid</strong></a>: <code>S-1-5-21-1708537768-854245398-2146844275</code></li>
  </ul>
  <p>Is there enough information in here to correctly implement <a href="https://en.wikipedia.org/wiki/Single_sign-on" rel="nofollow noreferrer"><strong>Windows Authentication</strong></a>?</p>
  <p>How does SQL Server do it? How does Explorer do it? How does Internet Explorer along with IIS do it?</p>
  <p>Aren't there some buzzwords, like <a href="https://en.wikipedia.org/wiki/Ticket_Granting_Ticket" rel="nofollow noreferrer">Ticket-Granting-Ticket</a> i have to include?</p>
  <h2>Bad Ideas</h2>
  <p>I had some bad, insecure, ideas. I figured why not just take name returned from <strong>GetUsernameEx</strong></p>
  <pre><code>CONTOSO\forest
  </code></pre>
  <p>and split it into two parts:</p>
  <pre><code>Username: forest
  Domain:   CONTOSO
  </code></pre>
  <p>That way i <strong>know</strong> that the user really is the user <strong>forest</strong> from the <strong>CONTOSO</strong> domain. I know it really is <strong>contoso\forest</strong> because Windows validated their credentials at login.</p>
  <p>Except no. Because the user on their standalone, non-domain joined, notebook, can change the name of their workgroup from <code>HYDROGEN</code> to <code>CONTOSO</code>. Now when i read their username:</p>
  <pre><code>CONTOSO\forest
  </code></pre>
  <p>I will believe they are:</p>
  <blockquote>
  <p>forest of the <strong>CONTOSO</strong> domain</p>
  </blockquote>
  <p>when in reality they are:</p>
  <blockquote>
  <p>forest of a standalone machine</p>
  </blockquote>
  <h2>Ok, so use the SID</h2>
  <p>Since i cannot trust the <em>&quot;domain name&quot;</em> returned by various Windows functions, then i could use the user's <strong>SID</strong>:</p>
  <blockquote>
  <p>domain user contoso\forest: <code>S-1-5-21-1708537768-854245398-2146844275-3110</code></p>
  </blockquote>
  <p>There's no way a user on a standalone PC can fake that, right? Right? :(</p>
  <p>Yes, they can:</p>
  <blockquote>
  <p>local user hydrogen\ginger: <code>S-1-5-21-1708537768-854245398-2146844275-3110</code></p>
  </blockquote>
  <p>You see where i'm going with this? I'm trying to <strong>invent</strong> a way to perform authentication - and failing. Meanwhile Windows and SQL Server teams both already solved this problem twenty years ago. Dave Cutler designed this system in 1994, and knew exactly what i should be doing.</p>
  <p>I just don't know what that something is.</p>
  <h2>User SIDs are not used for authentication</h2>
  <p>In researching this, i discovered some interesting concepts. Such as the SID of a domain is the <em>machine</em> SID of the first machine to become the domain's controller. I also discovered that users in that domain are a suffix of the domain SID:</p>
  <blockquote>
  <p><a href="http://blogs.msdn.com/b/aaron_margosis/archive/2009/11/05/machine-sids-and-domain-sids.aspx" rel="nofollow noreferrer">Machine SIDs and Domain SIDs</a></p>
  <pre><code>| Machine SID for computer DEMOSYSTEM | S-1-5-21-3419697060-3810377854-678604692      |
  | DEMOSYSTEM\Administrator            | S-1-5-21-3419697060-3810377854-678604692-500  |
  | DEMOSYSTEM\Guest                    | S-1-5-21-3419697060-3810377854-678604692-501  |
  | DEMOSYSTEM\CustomAccount1           | S-1-5-21-3419697060-3810377854-678604692-1000 |
  | DEMOSYSTEM\CustomAccount2           | S-1-5-21-3419697060-3810377854-678604692-1001 |
  </code></pre>
  <p>On a workgroup system, local accounts and groups are all there are.  Authentication to a remote system using a local account requires a user name and password known to the remote system, and that SIDs are not used. The only way anything resembling single sign on happens with local accounts is that if the remote system has the same user name and password that the caller is using.  SIDs are not transmitted and are not used for remote authentication.</p>
  </blockquote>
  <p>This is an important point, and you have to realize that duplicated SIDs are <strong>perfectly valid</strong>. SIDs must be unique <em>within the authority</em> in which they are used. So while DEMOSYSTEM must have only one local account with the SID S-1-5-21-3419697060-3810377854-678604692-1000, it doesn’t matter if another computer uses the same SID to refer to a local account of its own.</p>
  <p>This makes sense, and reinforces the idea that it is <strong>credentials</strong> that authorize a user, not their SID.</p>
  <h2>Which is why another of my ideas sucks</h2>
  <p>SQL Server stored Windows logins by their SID in <code>syslogins</code> table:</p>
  <pre><code>sid                                                         name            isntuser
  ----------------------------------------------------------  --------------  ---------
  0x010500000000000515000000A837D66516C0EA32733EF67F260C0000  CONTOSO\forest  1
  </code></pre>
  <p>My SQL Database engine cannot read the SID of the current user and check if it exists in my <code>syslogins</code> table, because there can be more than one user with the same sid connecting to my database engine over TCP port 1434.</p>
  <h1>Windows stores credentials somewhere</h1>
  <p>I was reading about <em>&quot;Pass the Hash&quot;</em> attacks against NTML and Kerberos. <a href="http://blog.varonis.com/closer-look-pass-hash-part-one/" rel="nofollow noreferrer">There was one interesting snippet</a>:</p>
  <blockquote>
  <p>Windows caches the hashed passwords in memory to implement Single Sign On or SSO, which is an essential feature of Windows enterprise environments.</p>
  </blockquote>
  <p>I just have to figure out how to convince Windows to tell me if the user really is <code>domain\BillG</code>.</p>
  <h1>Windows Authentication in Chrome and Windows Explorer</h1>
  <p>Chrome is able to transparently supply my Windows credentails to someone asking for them. If i were to request on a server, the server will deny me access (401) and indicate that i should use <code>Negotiate</code> authentication:</p>
  <pre><code>HTTP/1.1 401 Unauthorized
  WWW-Authenticate: Negotiate
  </code></pre>
  <p>My client then <em>performs some magic</em>, and re-issues the request, this time with my identity attached:</p>
  <pre><code>GET http://contoso.com/foo HTTP/1.1
  Authorization: Negotiate YIIFzwYGKwYBBQUCoIIFwzCCBb+gMDAuBgkqhkiC9xIBAgIGCSqGSIb3EgECAgYKKwYBBAGCNwICHgYKKwYBBAGCNwICCqKCBYkEggWFYIIFgQYJKoZIhvcSAQICAQBuggVwMIIFbKADAgEFoQMCAQ6iBwMFACAAAACjggP4YYID9DCCA/CgAwIBBaEOGwxBVkFUT1BJQS5DT02iJTAjoAMCAQKhHDAaGwRIVFRQGxJ2YWRlci5hdmF0b3BpYS5jb22jggOwMIIDrKADAgEXoQMCATCiggOeBIIDmjMA0SnAUdqmbf8+UXZHsipRqPKt2yxqQaFia8hBF3TuQVDBgqGk8yL+CoDGnvkyGqpZK3UBsS/EuXP4Z+/0y49ZyDDQnDFqcJpF5ZY87t+u/kYQy+dr42GxEYQIjb096AQzDZio0dRWqbHleS5DlR7wCEaJ+a0CG6/vLEXL6tT20aj3avFibZc++5OKhynoxtyh10tJO3iwun2usJT+p1IfTD9yVDhfplMchLBgyp803+6IUwzm0zcwcqt7R1KnCv1i+baw3e/dhkIJz8cnoh1oNuivSXf4zOqlvp8FDlQMQEGqa9OA7LBmhg1rWDTOdyB4E9oZtVG8ipHyFYzDcyvIpWOMf9S68TTE78TgEhWjVq7g6BoH+O6IW14QIItxVk1GbSd2Ke9n9We0pbMjRxiZIMqyOvvFBgU5NlUUksdlG/yv0BTai7SILbVfNPsVwHeus//UfKQenX6YEnKUVi+XutY0kjLyp6l1L3Ce/ovkpDVmmYFebfdIT8Xbya9Zksa2nF8+7OL5S7I0tZaZUBL2Bzca9VJiGioRFvpgBXxKiChv71SukROreic+ylxHOfOWwXsEa0+ISHV6Uvhd44y3UA2VKtI3xoF8+3SZ184hIZ4fbahkfrBa1Zu5FqQ9M0rxAPgmsBZ2PwuMDWWLtraK7gJsAh+DxXGAaSTiPWaRhms59mfetBmzSnkzWBCr63G8rL71TiDgevoxhv0FP5s1JmWzWsnluJ95f9fphItuiDRI0C1358LMai9B1ZFWf9CRooeMAH4YUuL4SZ0r61/zQVnWFF1ngyt/ko/9UQ3mErLFeA/9Oq6BYfI/ExhVl9VVue0irM1vk09pIdUMS9MvQdW7YCg/C9LtOiJVpYw/aEVakn74l7TM71bIfjucDddDCBNuup41bWy5Nqkci8AHEMyoVyG9BxHmTm8NZ3FSujl+MeDAANKSt3a6P2k0C/W4Mley76ZoAGf6IYXf/9THQucvQGkasUkIN6PwIZIaxEdVt1BXiVXu1ADgt2/+0UB8rzYq+kt53R16rjev4Exvt7jpHIWUxjbDTxo2CvW0+Eh+mFyMj3CS2xQlhjrU2Q9ADQqA8wf8H88Dzp4PPWPxJnB4tC+Ecd9ZYlQwal00UX6aN47+dKPYDCp4piq6dvr2BhpzpsXxyR8QOZRKqAoXXLmb4Y1eGFWiUqH56J3Wju5h+cyzhMq+otpI4s77lfIecM41HccPrTKkggFZMIIBVaADAgEXooIBTASCAUigcKId1qR+UzSz8R00q+0o2M4+2dLnNW2vPU+uLeG9SqLJgJWsgBWUGtt6TRvPLF/GoHxP+sqST8fKJf0EHfycGfH/VJR6bnfpQYCWCgWRHjfdUpll51G/xKYqJYyy5xtNQvtKkzp+IB6CVKe1q3wopAY+uDsUk9XUvaIbUtHDEcWDATwi8BKGggVunw/idxKaZjaRmRko/Nsj5p38fiBk+OCN3yKDNSFCTDn+HUiCoCbDsv03zt2EO1eTJUPxXNhqJUjZMKYodgcsLMzNhSiyySH+kvgQZci3b8LGY1sCHMXopaL0Ysu4QgPD8UDD7dIBZ0ORmGf9srdZMgKjLIoEhXOmg+y5kqJpoPAwQaooHDizKQ8bmhFX2pOp7NjXoJ/wRvTB98seUNlDXDl5ySrt7P3Xf1Ybj7PpgMuqJykou2lKxirVhYYJ
  </code></pre>
  <p>Both IE, and Chrome, are able to turn around and do <em>something</em> can be used to prove i am who i am. How Windows Authentication is not documented, but there are some hints. From <a href="https://msdn.microsoft.com/en-us/library/ms995329.aspx" rel="nofollow noreferrer">MSDN: HTTP-Based Cross-Platform Authentication via the Negotiate Protocol Part I - Network Infrastructure</a>:</p>
  <p><img src="https://i.stack.imgur.com/S4D6O.png" alt="enter image description here" /></p>
  <ol>
  <li>When the logged-on user requests a resource from the web server, it sends the initial HTTP GET verb.</li>
  <li>The web server, running the SPNEGO Token Handler code, requires authentication and issues a 401 Access Denied, WWW-Authenticate: Negotiate response.</li>
  <li>The client calls <strong><code>AcquireCredentialsHandle()</code></strong> and <strong><code>InitializeSecurityContext()</code></strong> with the SPN to build the Security Context that requests the session ticket from the TGS/KDC.</li>
  <li>The TGS/KDC supplies the client with the necessary Kerberos Ticket (assuming the client is authorized) wrapped in a SPNEGO Token.</li>
  <li>The client re-sends the HTTP GET request + the Negotiate SPNEGO Token in an Authorization: Negotiate base64(token) header.</li>
  <li>The web server's SPNEGO Token Handler code accepts and processes the token through GSS API, authenticates the user and responds with the requested URL.</li>
  </ol>
  <p><img src="https://i.stack.imgur.com/FpKlK.png" alt="enter image description here" /></p>
  <p>So there is some code that the client can use to generate a proof that they are who they say they are. Which means there is some way for me, on the client, to generate proof that domain user is the domain user that they say they are.</p>
  <p>I, of course, don't need to wrap up the ticket from Kerberos in a encrypted, base-64'd, SPNEGO blob. I just need the correct API calls, in the right order, to know that i am who i say i am.</p>