When calling InitializeSecurityContext
, what value do i pass to the TargetName
parameter?
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
.
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?
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.
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 authenticationsecurity 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.
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"
"avatopia.com"
)"avatopia.com"
)"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:
Update 4
What i'm trying to do: figure out if a username/password is valid.
"ian"
"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 machineSo i need to specify if i want to:
stackoverflow.com
), or"."
)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:
i'm going to try to login to the domain TURBOENCABULATOR
. i specified i want to try to authenticate against the TURBOENCABULATOR
domain by prefixing my username as:
TURBOENCABULATOR\ian
Note: i highly doubt the network has a domain called turboencabulator, since the name itself only comes from Rockwell automation. The attempt to login will almost certainly fail. But how does Windows check them?
How does Windows attempt to validate these credentials? How does Windows validate the credentials:
Does Windows use the Security Support Package Interface? Assuming windows uses Negotiate or Kerberos for authentication, what does Windows pass as the pszTarget
parameter? Almost certainly the credentials i enter will not be valid. How will Windows determine if they are valid? What API will Windows call to validate the credentails?
Windows is able to validate credentails. I want to also validate credentials.
Perhaps instead of trying to connect to the TURBOENCABULATOR
domain, i try to connect to the turboencabulator.com
domain by prepending the domain to my username as turboencabulator.com\ian
:
Same question applies. How does Windows validate credentials? i want to do what Windows does. Assuming Windows uses kerberos for authorization, what does Windows pass as the pszTargetName
parameter in SSPI?
Perhaps instead of trying to connect to the turboencabulator.com
domain, i try to connect to the turboencabulator.net
domain:
Note that in this example i've appended the domain name to my username, rather than prepending it.
Perhaps instead of trying to connect to the turboencabulator.net
domain, i try to validate the user as a local (machine) account by prefixing my username with .\
as:
How does Windows validate the username and password against the local account database? Does it use SSPI with Negotiate package? If so what value does it pass as the pszTargetName
?
People are talking about web servers, http, team foundation server. i really don't know where they're getting that from. Or they talk about editing a user in active directory to ensure something is present - i don't see why i need to edit anything: Windows doesn't edit anything.
What TargetName
do i used when calling InitializeSecurityContext
in order to validate a set of credentials?
Here's a chapter from the Application Verifier documentation about why they have a test if someone is mistakenly using NTLM:
Why the NTLM Plug-in is Needed
NTLM is an outdated authentication protocol with flaws that potentially compromise the security of applications and the operating system. The most important shortcoming is the lack of server authentication, which could allow an attacker to trick users into connecting to a spoofed server. As a corollary of missing server authentication, applications using NTLM can also be vulnerable to a type of attack known as a “reflection” attack. This latter allows an attacker to hijack a user’s authentication conversation to a legitimate server and use it to authenticate the attacker to the user’s computer. NTLM’s vulnerabilities and ways of exploiting them are the target of increasing research activity in the security community.
Although Kerberos has been available for many years many applications are still written to use NTLM only. This needlessly reduces the security of applications. Kerberos cannot however replace NTLM in all scenarios – principally those where a client needs to authenticate to systems that are not joined to a domain (a home network perhaps being the most common of these). The Negotiate security package allows a backwards-compatible compromise that uses Kerberos whenever possible and only reverts to NTLM when there is no other option. Switching code to use Negotiate instead of NTLM will significantly increase the security for our customers while introducing few or no application compatibilities. Negotiate by itself is not a silver bullet – there are cases where an attacker can force downgrade to NTLM but these are significantly more difficult to exploit. However, one immediate improvement is that applications written to use Negotiate correctly are automatically immune to NTLM reflection attacks.
By way of a final word of caution against use of NTLM: in future versions of Windows it will be possible to disable the use of NTLM at the operating system. If applications have a hard dependency on NTLM they will simply fail to authenticate when NTLM is disabled.
How the Plug-in Works
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.
NTLM Stops
5000 – Application Has Explicitly Selected NTLM Package
Severity – Error
The application or subsystem explicitly selects NTLM instead of Negotiate in the call to AcquireCredentialsHandle. Even though it may be possible for the client and server to authenticate using Kerberos this is prevented by the explicit selection of NTLM.
How to Fix this Error
The fix for this error is to select the Negotiate package in place of NTLM. How this is done will depend on the particular Network subsystem being used by the client or server. Some examples are given below. You should consult the documentation on the particular library or API set that you are using.
APIs(parameter) Used by Application Incorrect Value Correct Value ===================================== =============== ======================== AcquireCredentialsHandle (pszPackage) “NTLM” NEGOSSP_NAME “Negotiate”
The TargetName
is the username that you want to prove your identity to.
ian@stackoverflow.com
steve@stackoverflow.com
steve@stackoverflow.com
In the case of server, the username might be something like:
IISServiceAccount@stackoverflow.com
MSSqlServer@stackoverflow.com
The Negotiate
authentication package will attempt to use Kerberos
. If it cannot, it will attempt to fallback to NTLM
.
Kerberos
.Kerberos
you must supply a TargetNameKerberos
is fundamentally unable to functionThe question becomes, given all the parties involved:
what TargetName do i specify?
This is where it's important to know what TargetName means to Kerberos:
steve@stackoverflow.local
steve@stackoverflow.local
is the only one able to decrypt itsteve@stackoverflow.local
because i specified steve@stackoverflow.local
in the TargetNameThat'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:
InitializeSecurityContext(credHandle, context, "steve@stackoverflow.local", ...);
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"
.
ian@stackoverflow.com
: valid usernamesteve@stackoverflow.com
: valid usernamespn/HOSTNAME
: Unknown userIn the case above i had to know that the "server" code will be running as steve@stackoverflow.local
.
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?
iisagent@stackoverflow.local
?sqldaemon@stackoverflow.local
?Fortunately(?), Kerberos created aliases (called Service Principle Names - or SPNs):
http://bugtracker.stackoverflow.local
iisagent@stackoverflow.local
iisagent@stackoverflow.local
HTTP/bugtracker.stackoverflow.local
HTTP/bugtracker.stackoverflow.local
→ iisagent@stackoverflow.local
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:
HTTP/[servername]
MSSQLSvc/[servername]:1433
SMTPSVC/[servername]
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.
You can see the SPN associated with a machine account in Active Directory Users and Computers by finding the machine user account,and looking at it's servicePrincipleName
attribute. E.g.:
Attribute | Value |
---|---|
distinguishedName | CN=Vader,CN=Computers,DC=stackoverflow,DC=com |
dNSHostName | VADER.stackoverflow.com |
sAMAccountName | VADER$ |
servicePrincipleName | HOST/VADER HOST/VADER.stackoverflow.com MSSQLSvc/Vader.stackoverflow.com MSSQLSvc/Vader.stackoverflow.com:1433 RestrictedKrbHost/VADER RestrictedKrbHost/VADER.stackoverflow.com TERMSRV/VADER TERMSRV/Vader.stackoverflow.com WSMAN/Vader WSMAN/Vader.stackoverflow.com DNS/Vader.stackoverflow.com GC/Vader.stackoverflow.com/stackoverflow.com ldap/VADER ldap/Vader.stackoverflow.com ldap/Vader.stackoverflow.com/STACKOVERFLOW |
And you can query for the userPrincipleName using:
SELECT userPrincipalName FROM "LDAP://dc=stackoverflow,dc=com"
WHERE servicePrincipalName = 'HTTP/ticketsystem.stackoverflow.com'
userPrincipleName
-----------------
vader.avatopia.com
1 row(s) affected
It's roughly equivalent to attempting to specify "stackoverflow.com", rather than simply using "35.186.238.101".
SSPI was designed as a generic wrapper around different security algorithms. The way to use the API is pretty simple:
InitializeSecurityContext
and is given a blobAcceptSecurityContext(blob)
, and is given a blob backInitializeSecurityContext(blob)
, and is given back a blobAcceptSecurityContext(blob)
, and is given a blob backBoth sides keep going back and forth until the function stops returning a blob that needs to be sent to the other side:
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.
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.
You can even use a carrier pidgeon, Skype, or E-mail if you like.
User aliases, also known as Service Principle Names, or SPNs are added to your Active Directory database. Windows Server comes with a handy MMC snap-in to view the AD database:
Go find the server in AD:
Hydrogen is the name of a machine. But machines get their own user accounts in Active Directory:
Hydrogen
Hydrogen$
And then in the servicePrincipleName
attribute, you will see a list of SPNs for this computer account (i.e. Hydrogen$@contoso.local
):
So rather than passing:
Hydrogen$.contoso.local
You can pass:
MSSQLSvc/Hydrogen.contoso.local
And it will resovle it to the username Hydrogen$
for you.