Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
859 views
in Technique[技术] by (71.8m points)

security - What TargetName to use when calling InitializeSecurityContext (Negotiate)?

The Question

When calling InitializeSecurityContext, what value do i pass to the TargetName parameter?

Revised Background

I'm calling the function InitializeSecurityContext:

InitializeSecurityContextA(
      @pAS.hcred,           //[in] credentials
      phContext,            //[in] optional] Context handle structure
      pszTargetName,        //[in, optional] Target name
      0,                    //[in] context requirements
      0,                    //[in] reserved1, must be zero
      SECURITY_NATIVE_DREP, //[in] target data representation
      pInput,               //[in] optional] SecBufferDescription
      0,                    //[in] reserved2, must be zero
      @pAS.hctxt,           //[in, out] pointer to context handle structure
      @OutBuffDesc,         //[in, out] pointer to SecBufferDesc
      ContextAttributes,    //[out] context attributes
      @lifetime);           //[out] expiration timestamp

What do i pass to pszTargetName?

I've tried

  • null: InitializeSecurityContextA(@pAS.hcred, phContext, null, ...);
  • "": InitializeSecurityContextA(@pAS.hcred, phContext, "", ...);
  • "spn/HOSTNAME": InitializeSecurityContextA(@pAS.hcred, phContext, "spn/HOSTNAME", ...);
  • spn/HOSTNAME.DOMAIN.COM: InitializeSecurityContextA(@pAS.hcred, phContext, "spn/HOSTNAME.DOMAIN.COM", ...);
  • "cargocult/PROGRAMMING": InitializeSecurityContextA(@pAS.hcred, phContext, "cargocult/PROGRAMMING", ...);
  • "http/TFS.DOMAIN.COM": InitializeSecurityContextA(@pAS.hcred, phContext, "http/TFS.DOMAIN.COM", ...);
  • "http/HOSTNAME": InitializeSecurityContextA(@pAS.hcred, phContext, "http/HOSTNAME", ...);
  • "qwertyasdf": InitializeSecurityContextA(@pAS.hcred, phContext, "qwertyasdf", ...);

  • "AuthSamp": InitializeSecurityContextA(@pAS.hcred, phContext, "AuthSamp", ...);

They all either fail, or downgrade to NTLM.

Note: My machine is domain joined, but the domain is not named domain.com, or even hostname.domain.com, or even qwertyasdf. So i'm not surprised that those attempts fail. But people said try things like http/HOSTNAME, so i put in http/HOSTNAME.

Background

The InitializeSecurityContext (Negotiate) function has an optional TargetName parameter:

pszTargetName [in, optional]

A pointer to a null-terminated string that indicates the service principal name (SPN) or the security context of the destination server.
Applications must supply a valid SPN to help mitigate replay attacks.

What is this supposed to be?

More Background

i am trying to validate a set of user's credentials, e.g.:

Boolean ValidateCredentials(String username, String password, String domain)
{
   ...
}

Validating a set of user's credentials requires using the SSPI API. The first function to call is InitializeSecurityContext. One of the parameters to InitializeSecurityContext is a "TargetName" string.

i've tried leaving it null, but the Application Verifier triggers a breakpoint, writing out the error:

VERIFIER STOP 00005003: pid 0xF08:
InitializeSecurityContext uses NULL target or malformed target for Kerberos service.
Please see pszTargetName for the value of the target.
00000000 : Not used.
00000000 : Not

At this point it would be helpful to remember that the Negotiate provider will attempt to use Kerberos, but fallback to NTLM. In the case of Negotiate, Kerberos or NTLM, the TargetName parameter is documented to be:

Service principal name (SPN) or the security context of the destination server.

But then what should i pass?

i tried doing what the SSPI Knowledge Base article does, nothing (i.e. pass NULL):

How to validate user credentials on Microsoft operating systems

ss = _InitializeSecurityContext(
        &pAS->hcred,
        pAS->fInitialized ? &pAS->hctxt : NULL, 
        NULL,        //<-------pszTargetName
        0, 
        0,
        SECURITY_NATIVE_DREP, 
        pAS->fInitialized ? &sbdIn : NULL,
        0, 
        &pAS->hctxt, 
        &sbdOut, 
        &fContextAttr, 
        &tsExpiry);

But nothing (i.e. NULL) doesn't work.

Note: The KB article was massivly rewritten in 2007. In its original 1999 incarnation they passed "AuthSamp" as the target, but that also fails.

Bonus Chatter:

service principal name
(SPN) The name by which a client uniquely identifies an instance of a service. If you install multiple instances of a service on computers throughout a forest, each instance must have its own SPN. A given service instance can have multiple SPNs if there are multiple names that clients might use for authentication

security context
The security attributes or rules that are currently in effect. For example, the current user logged on to the computer or the personal identification number entered by the smart card user. For SSPI, a security context is an opaque data structure that contains security data relevant to a connection, such as a session key or an indication of the duration of the session.

Bonus Chatter 2

From the application verifier documentation:

The Verifier plug detects the following errors:

  • The NTLM package is directly specified in the call to AcquireCredentialsHandle (or higher level wrapper API).

  • The target name in the call to InitializeSecurityContext is NULL.

  • The target name in the call to InitializeSecurityContext is not a properly-formed SPN, UPN or NetBIOS-style domain name.

  • The latter two cases will force Negotiate to fall back to NTLM either directly (the first case) or indirectly (the domain controller will return a “principal not found” error in the second case causing Negotiate to fall back).

  • The plug-in also logs warnings when it detects downgrades to NTLM; for example, when an SPN is not found by the Domain Controller. These are only logged as warnings since they are often legitimate cases – for example, when authenticating to a system that is not domain-joined.

In my case the domain i am validating against is null (since i don't know the machine's domain name, or even if there is a domain). But the results are the same if the hard-code my development machine's domain name.

Update 3

Values of pszTargetName that trigger AppVerifier error, but logon succeeds:

  • null
  • ""
  • "AuthSamp"
  • "qwertyasdf"
  • *the name of the domain i'm validating against (e.g. "avatopia.com")
  • *the name of the domain the machine is joined to (e.g. "avatopia.com")
  • *the name of the domain the user account is located in (e.g. "avatopia.com")

Values of pszTargetName that do not trigger an AppVerifier error, but logon fails:

  • "http/HOSTNAME"
  • "http/TFS.DOMAIN.COM"
  • "frob/GROBBER"
  • "cargocult/PROGRAMMING"
  • "spn/HOSTNAME"
  • "spn/HOSTNAME.DOMAIN.COM"

Values of pszTargetname that do not trigger an AppVerifier error, and logon succeeds:

  • none

Update 4

What i'm trying to do: figure out if a username/password is valid.

  • i have a username: e.g. "ian"
  • i have a password: e.g. "pass1"

Now there's the further wrinkle that the account ian could be a local account or a domain account. And you need to decide if ian is a local or domain account before you can ask. This is because ian can have two accounts:

  • ian on domain stackoverflow.com
  • ian on local machine

So i need to specify if i want to:

  • ask a particular domain (e.g. stackoverflow.com), or
  • ask the local machine (which i'll represent as ".")

Now we can come up with a cross reference:

Username  Password  Domain             Machine on domain?  Validate as
========  ========  =================  ==================  ==============
iboyd     pass1     .                  No                  Local account
iboyd     pass1     (empty)            No                  Local account
iboyd     pass1     stackoverflow.com  No                  Domain account

iboyd     pass1     .                  Yes                 Local account
iboyd     pass1     (empty)            Yes                 Domain account
iboyd     pass1     stackoverflow.com  Yes                 Domain account

Update 5

It might help to explain what i'm trying to do, then maybe how to do it will become easier. Lets say i walk into a random office building downtown, walk into a random cubicle, and type in a random username and password:

enter image description here

i'm going t


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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 domainjane and doing loop back on yourself. You need to specify a SPN for domainjane. What's the SPN for domainjane. 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 domainjane 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.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...