Kerberos authentication with SSPI

adaigle picture adaigle · Jul 8, 2013 · Viewed 8.5k times · Source

Note: I manage to make some progress, see the edit for my current question, thanks.


I would like to use libcurl on Windows to access websites with a Kerberos/GSSAPI authentication. I first tried to work it out with MIT Kerberos, but I also require NTLM authentication using SSPI (libcurl doesn't support using both from two different implementation). So I am looking to authenticate in Kerberos using the Windows SSPI library. I managed to compile libcurl with SSPI and SPNEGO support.

Now my problem is that I need to connect to any supplied realm with the provided credentials (it could be the realm of the current user or a different one). From what I understood, I need to call AcquireCredentialsHandle and InitializeSecurityContext from secur32.dll/security.dll to retrieve the Kerberos ticket.

But every time I try to make it work, I:

  1. Don't acquire any tickets from the DC of the provided credentials/realm in the SSPI cache (I'm using kerbtray.exe to see the entries).
    • Should I see a ticket in this cache when using theses methods ?
  2. Using libcurl after having called ImpersonateSecurityContext with a client/server loopback of InitializeSecurityContext/AcceptSecurityContext and looking at the packets in Wireshark, I see that libcurl don't use any of the provided credentials and fallback to NTLM instead (which just cause the authentication to fail)
    • Is the loopback client/server the correct behavior to do (I couldn't find any example of other implementation on the web) ?
    • Should libcurl use the impersonated credentials of the thread assuming the impersonation was successful ?
    • Is libcurl supporting at all NTLM + Kerberos with SSPI (I am not even sure of that...) ?

To facilitate debugging and tests, do you know any tool to add an entry to the SSPI cache, such as kinit from the MIT Kerberos library ? I am using Windows Server 2003 Resource Kit Tools but I couldn't find any such tool...

Any help would be greatly appreciated !


EDIT

Alright, I found out how to make what I want using libcurl. Finally I was assuming I had to do some work with SSPI beforehand, but curl is properly supporting that.

curl will make the authentication to the kerberos realm and store it in the LSA cache when using the properly built version of curl with the SSPI and SPNEGO flag.

When using curl.exe to test, you need to specify the --negotiate argument along with the username/password. However, when using libcurl simply set the CURLOPT_HTTPAUTH option to anything that contains CURLAUTH_GSSNEGOTIATE (such as CURLAUTH_ANY). In my tests, libcurl did the expected Kerberos handshake:

  1. Contact the web server (web.b.com, where B.COM is the kerberos realm of the web server)
  2. Received an unauthorized answer, with the WWW-Authenticate parameter set to Negotiate
  3. Libcurl/SSPI sends a TGS-REQ to the domain controller (DC) of the domain of the current user (let say A.COM)
  4. Somehow, the next TGS-REQ is sent to the domain controller of the web server (B.COM)
  5. Received a Kerberos ticket of the domain B.COM and an entry is added in the LSA cache (viewable with klist.exe or kerbtray.exe)
  6. Libcurl send an HTTP request to the web server with the authorization information (GSS-API)

However and here is my new problem, all this handshake was made with the currently logged credentials, say [email protected]. Since there is a trust between A.COM and B.COM and my user is have access, it works. What I would rather like is to use the provided credentials (user2.B.COM) to login ?

Also, I am not quite sure if it is possible to add an entry in the LSA cache of a different user than the one currently logged ??

How can I make this work by impersonating the user user2.B.COM in a way that libcurl will have access to the ticket associated with this user to authenticate to the web server ?

Answer

adaigle picture adaigle · Jul 12, 2013

Maybe this will help someone in the future, so here's my solution to the problem I listed above.

I used the LogonUser and ImpersonateLoggedOnUser methods to impersonate the thread with the specified user. Using that, curl used the LSA cache associated with the user identity of the thread and managed to access the web server using that identity.

In my setup, I get the following Kerberos packets:

  1. AS-REQ on the domain controller of A.COM with a KDC_ERR_WRONG_REALM response
  2. AS-REQ on the domain controller of B.COM with a KRB5KDC_ERR_PREAUTH_REQUIRED response
  3. AS-REQ on the domain controller of B.COM
  4. TGS-REQ on the domain controller of B.COM
  5. HTTP request with the GSS-API authentication information

Then I can do whatever request I want to the web server on B.COM as long as the impersonation is valid.