What TargetName to use when calling InitializeSecurityContext (Negotiate)?

Short Answer

The TargetName is the username that the "server" code will be running as.

Background

The Negotiate authentication package will attempt to use Kerberos. If it cannot, it will attempt to fallback to NTLM.

  • You don't want to use NTLM; it is old, deprecated, weak, broken, and should not be used.
  • You want to use Kerberos.
  • In order to use Kerberos you must supply a TargetName
  • Without a TargetName, Kerberos is fundamentally unable the function

The question becomes, given all the parties involved:

  • me (Ian)
  • authenticating with Steve

what TargetName do i specify?

enter image description here

This is where it's important to know what TargetName means to Kerberos:

That's how Steve knows the blob is valid, it was encrypted so only he can decrypt it.

I have to tell Kerberos who i will be giving the encrypted blob to, so the domain controller knows who to encrypt it for.

So in the above list of possible names, three values work:

enter image description here

InitializeSecurityContext(credHandle, context, "[email protected]", ...);    
InitializeSecurityContext(credHandle, context, "stackoverflow.local\steve", ...);    
InitializeSecurityContext(credHandle, context, "steve", ...); //if we're in the same forest

So you can see why my earlier attempts to call InitializeSecurityContext all failed:

InitializeSecurityContextA(credHandle, context, null, ...);
InitializeSecurityContextA(credHandle, context, "", ...);
InitializeSecurityContextA(credHandle, context, "spn/HOSTNAME", ...);
InitializeSecurityContextA(credHandle, context, "spn/HOSTNAME.DOMAIN.COM", ...);
InitializeSecurityContextA(credHandle, context, "cargocult/PROGRAMMING", ...);
InitializeSecurityContextA(credHandle, context, "http/TFS.DOMAIN.COM", ...);
InitializeSecurityContextA(credHandle, context, "http/HOSTNAME", ...);
InitializeSecurityContextA(credHandle, context, "qwertyasdf", ...);
InitializeSecurityContextA(credHandle, context, "AuthSamp", ...);

Because i wasn't specifying Steve as the TargetName; i was specifying something non-sensical:

spn/HOSTNAME

In fairness, people did keep telling me to pass "spn/HOSTNAME".

Extra confusion because flexibility (welcome to SPN)

SPNs Short version

  • when using Kerberos you must specify a username as your TargetName
  • an SPN is an "alias" for a username
  • therefore you can specify an SPN as your TargetName

SPNs Long Version

In the case above i had to know that the "server" code will be running as [email protected].

That's a pain. I mean it's fine when i know it's steve. But if i'm talking to a remote machine, i have to find out the user account that the code is running as?

  • i have to figure out that IIS is running as [email protected]?
  • i have to figure out that SQL Server is running as [email protected]?
  • and what if the service is running as Local Service; that isn't a domain user at all?

Fortunately(?), Kerberos created aliases (called Service Principle Names - or SPNs):

  • if i need to authenticate to the web server http://bugtracker.stackoverflow.local
  • and IIS service is running under the domain account [email protected]
  • rather than have to specify the targetname of [email protected]
  • i can specify HTTP/bugtracker.stackoverflow.local
  • that's because IIS registered an alias with the domain controller
  • HTTP/bugtracker.stackoverflow.local[email protected]

All this requires that you know the SPN if you wish to use it as a TargetName. Various standard Microsoft products register SPNs when they install:

  • IIS: HTTP/[servername]
  • SQL Server: MSSQLSvc/[servername]:1433
  • SMTP: SMTPSVC/[servername]
  • File sharing: HOST/[servername]

These are all undocumented, and make your life hell when one isn't configured correctly.

But by no means do you have to supply a SPN. An SPN is simply an alias designed to make your life easier more difficult.

It's roughly equivalent to attempting to specify "stackoverflow.com", rather than simply using "35.186.238.101".

Bonus Chatter - How does SSPI work?

SSPI was designed as a generic wrapper around different security algorithms. The way to use the API is pretty simple:

  • Client: calls InitializeSecurityContext and is given a blob
  • client sends that blob to the server
  • Server: calls AcceptSecurityContext(blob), and is given a blob back
  • server sends that blob back to the client
  • Client: calls InitializeSecurityContext(blob), and is given back a blob
  • client sends that blob back to the server
  • Server: calls AcceptSecurityContext(blob), and is given a blob back
  • ...keep repeating until told to stop...

Both sides keep going back and forth until the function stops returning a blob that needs to be sent to the other side:

enter image description here

And so the with SSPI you do this ping-ponging back and forth until you're told to stop. And so they were able to shoe-horn every authentication scheme into that ping-pong-until-told-to-stop high level abstraction.

How do I transmit the blobs?

You transmit the blobs over whatever communication channel you're using.

If you're talking to a remote server over TCP/IP, then you'd probably use that:

// Open connection to server
sockConnect(162.210.196.166, 1433);

blob = null;

Boolean bContinue = InitializeSecurityContext(ref blob);

while (bContinue)
{
   sockWrite(blob); //send the blob to the server

   blob = sockRead(); //wait for the server to return a blob 

   bContinue = InitializeSecurityContext(ref blob);
}

If you're doing it over http:

blob = null;
Boolean bContinue = InitializeSecurityContext(ref blob);

while (bContinue)
{
    http = new HttpRequest("http://4chan.org/default.aspx");
    http.AddHeader("X-SSPI-Blob", blob.ToBase64());
    http.Send();

    blob = http.ReasponseHeader["X-SSPI-Blob"];
    if (blob.IsEmpty())
       break;

    bContinue = InitializeSecurityContext(ref blob);
}

The SSPI API doesn't care you to get the blob transmitted back and forth - just that you have to transmit it back and forth.

  • using connecting to SQL Server, SQL client driver does the transmitting over the database connection
  • using http, the browser sends the blobs in request and response header

You can even use a carrier pidgeon, Skype, or E-mail if you like.


Ian, I think we still don't understand what you are trying to do exactly. In order to help you providing us more information on what you are trying to do, here is a little bit background about SSPI. You may already know this but just to make sure we are on the same page.

SSPI is generally used for authenticating a user over the network. Client calls the AcquireCredentialsHandle to obtain a credentials handle and then create a security context by calling InitializeSecurityContext. Pass the security buffer to server. Note that SSPI doesn't dictate how you pass the security buffer. You can use http, tcp, named pipe whatever you like. Once the server receive the security buffer. Similarly, it calls the AcquireCredentialsHandle first. Then it passes the received security buffer into AcceptSecurityContext and generate new security buffer. In some cases, the newly generated security buffer needs to send back to the client and client passes that into InitializeSecurityContext and generates another new security context again. This SSPI handshaking process continues until InitializeSecurityContext and AcceptSecurityContext both returns SEC_E_OK

Although SSPI was designed for authentication over the network, many applications are actually doing loopback SSPI handshaking, which means both client and server are on the same box. This is really just a special case of the network authentication. The end result of a loopback SSPI handshaking is a authenticated SSPI security context. With this authenticated SSPI, application can do QueryContextAttributes and ImpersonateSecurityContext. Since you seem to have no idea what targetName means, I am guessing you are trying to do the loop back handshaking. I might be wrong though but you need to tell us what you are trying to do.

To understand why targetName is needed in Kerberos but not in NTLM, you need to understand some more underlying implementation.

There are two different ways to acquire a credentials handle. Normally, people specify to use the current security context. (i.e. the account that you used to log onto your machine). You can also provide another set of username/password. Different security package has different meanings on the term credentials. NTLM means that it's going to save a hash of your password. Kerberos means that it's going to save a Ticket Granting Ticket (TGT). To the SSPI programmer, you don't need to worry about this.

Now, when the client passes in the acquired credentials handle into InitializeSecurityContext, similarly, different security package is going to do different things. NTLM is going to generate a NEGOTIATE packet on the first InitializeSecurityContext call. No other machines are involved in the process of generating the NEGOTITATE packet. Kerberos package is very different. It's going to talk to KDC to request a service ticket for the requested service. The service is identified by Service Principal Name (SPN) in Kerberos. I cannot cover all the details here. The net net is that service request for NTLM is untargeted while the service request for Kerberos is targeted. You can use the same NTLM NEGOTIATE packet for different services using NTLM authentication method. However, you need to use different Kerberos service tickets for different services using Kerberos authentication method. That's why when calling InitializeSecurityContext for Kerberos / Negotiate, you need to provide the targetName.

When KDC receives the request of a service ticket, it does a search on its LDAP database and find out which account is associated with the specified servicePrincipalName. The account can be AD user account or AD computer account. The KDC will use the target service account's master key (generated by the account password) to encrypt a session key. This encrypted session key will be passed from the client to the server later on.

Now, remember I said the server also needs to do AcquireCredentialsHandle and I said there are two major approaches to get the credentials handle? I guess your are using the first approach to acquire the credentials handles. That means it is going to use the current security context. In a normal network authentication case, this can be illustrated by the following example. If your sever is a HTTP server, it's going to be the service account of your IIS server. IIS server is going to use its service account master key to decrypt the encrypted session key. Once the session key is obtained, client and server continues the communication using the session key to do the encryption and decryption.

If it is a loop back SSPI scenario, it's trickier. If you are running domain\jane and doing loop back on yourself. You need to specify a SPN for domain\jane. What's the SPN for domain\jane. If you check the AD user object, there is none by default. You need to manually fix it.

There is one thing that used to work for me but it's undocumented. You can specify the user's UPN (i.e. [email protected]) as the SPN. This works for me. You can try it.

If that doesn't work, another way to fix it is to use the second approach to do the server part AcquireCredentialsHandle. Instead of using domain\jane credentials handle, you specify another service account credentials. You can make sure that service account has a correct SPN set. Then, you can use that service account's SPN in your InitializeSecurityContext. Of course, that also means you need to hard code your service account's password in the code. You need to be careful and make sure you completely lock down this service account so that even though the password is stolen, your AD environment is not at big risk.


I am a couple years late to this party... Yesterday, I came across your question while researching my own SSPI issue. This morning as I continued my research, I came across an article by By Keith Brown, from the April 2001 MSDN Magazine, that seems to offer a solution to your question:

Security Briefs - The Security Support Provider Interface Revisited (archive)

by Keith Brown
From the April 2001 issue of MSDN Magazine.

The "Figures" referenced in the article (including the example code) is located here (archive)

The article contains example code which reveals the targetName (for the purpose of password validation) should be a string in the form "Machine\User" or "Domain\User".

I realize you likely found a solution to this issue a long time ago. Furthermore, I cannot certify that the author's code functions correctly on modern Windows platforms (I suspect it would, but I have not validated the behavior)

Hopefully the MSDN article will also be a useful resource to others.


It depends a bit on the SPN you're trying to authenticate against. We do NTLM/SPNEGO authentication to HTTP servers (only), and the guidance suggests that HTTP/HTTPS server should register an SPN as http/HOSTNAME. So when we authenticate, we just prepend http/ to the upper-cased hostname. For example, we pass:

http/TFS.DOMAIN.COM

as the target to InitializeSecurityContext, where TFS.DOMAIN.COM is the upper-cased hostname that the user typed to access their TFS server.

We do not try to do any DNS lookups or FQDN matching. If the user simply types http://foo/ then our SPN is http/FOO. This means that the server admin must have configured http/FOO as an SPN.

It's not impossible that a server admin configures a machine, call it FOO and sets up the SPN http/FOO, then exposes this machine on the internet as extranet.domain.com. In that case, they should also configure http/EXTRANET.DOMAIN.COM as an SPN. This can get tricky with load balancers, etc, but this should be the server admin's responsibility.