Reputation: 12425
The Windows SSPI API is an interface into the Windows security service that allows you to authenticate clients and servers to each other. One of the major uses of the API is to provide Windows Integrated Authentication aka Single Sign-on - applications are able to automatically authenticate users to servers by using the user's credentials from when they logged on.
The normal flow for this process:
AcquireCredentialsHandle()
to get a handle to the existing credentials the user created when they logged on.AcquireCredentialsHandle()
to participate in the SSPI authentication cycle with a server, using the InitializeSecurityContext()
API.InitializeSecurityContext()
(client side) and AcceptSecurityContext()
(server side) APIs to verify each others' tokens and continue the authentication cycle.This is the normal flow for this process when using the API as part of single sign-on.
The signature for AcquireCredentialsHandle()
is:
SECURITY_STATUS SEC_Entry AcquireCredentialsHandle(
_In_ SEC_CHAR *pszPrincipal,
_In_ SEC_CHAR *pszPackage,
_In_ ULONG fCredentialUse,
_In_ PLUID pvLogonID,
_In_ PVOID pAuthData,
_In_ SEC_GET_KEY_FN pGetKeyFn,
_In_ PVOID pvGetKeyArgument,
_Out_ PCredHandle phCredential,
_Out_ PTimeStamp ptsExpiry
);
When used in the typical Windows Integrated Authentication case as above, the pAuthData
parameter is usually not provided - a null reference is provided instead.
I wish to be able to use AcquireCredentialsHandle()
in a manner where I provide a username and password to it directly. It would seem to handle this, since the pAuthData
parameter (which was null above) can be a reference to the SEC_WINNT_AUTH_IDENTITY
structure, which allows you to specify a username and password outright.
I've attempted to invoke AcquireCredentialsHandle()
this way, providing it a filled out SEC_WINNT_AUTH_IDENTITY
structure with my username and password. However, every time I invoke it, I get back success, even if I use made-up usernames or passwords. As a sanity check, I tried invoking LogonUser()
with the same credentials and they either worked or failed, as expected, depending on whether I gave it a valid username/password combo.
What am I doing wrong? Why does AcquireCredentialsHandle()
always return success even with absolutely incorrect credentials?
Below shows the basics of the code I'm using to invoke AcquireCredentialsHandle()
:
public class SuppliedCredential : Credential
{
public SuppliedCredential( string securityPackage, CredentialUse use, string username, string domain, string password ) :
base( securityPackage )
{
Init( use, username, domain, password );
}
private void Init( CredentialUse use, string username, string domain, string password )
{
// Copy off for the call, since this.SecurityPackage is a property.
string packageName = this.SecurityPackage;
TimeStamp rawExpiry = new TimeStamp();
SecurityStatus status = SecurityStatus.InternalError;
AuthIdentity auth = new AuthIdentity();
auth.User = username;
auth.UserLength = username.Length;
auth.Domain = domain;
auth.DomainLength = domain.Length;
auth.Password = password;
auth.PasswordLength = password.Length;
auth.Flags = 0x2; // unicode
this.Handle = new SafeCredentialHandle();
RuntimeHelpers.PrepareConstrainedRegions();
try { }
finally
{
status = CredentialNativeMethods.AcquireCredentialsHandle_2(
"",
packageName,
use,
IntPtr.Zero,
ref auth,
IntPtr.Zero,
IntPtr.Zero,
ref this.Handle.rawHandle,
ref rawExpiry
);
}
if( status != SecurityStatus.OK )
{
throw new SSPIException( "Failed to call AcquireCredentialHandle", status );
}
this.Expiry = rawExpiry.ToDateTime();
}
}
...
[StructLayout( LayoutKind.Sequential )]
public struct AuthIdentity
{
[MarshalAs(UnmanagedType.LPWStr)]
public string User;
public int UserLength;
[MarshalAs(UnmanagedType.LPWStr)]
public string Domain;
public int DomainLength;
[MarshalAs(UnmanagedType.LPWStr)]
public string Password;
public int PasswordLength;
public int Flags;
};
...
[ReliabilityContract( Consistency.WillNotCorruptState, Cer.MayFail )]
[DllImport( "Secur32.dll", EntryPoint = "AcquireCredentialsHandle", CharSet = CharSet.Unicode )]
internal static extern SecurityStatus AcquireCredentialsHandle_2(
string principleName,
string packageName,
CredentialUse credentialUse,
IntPtr loginId,
ref AuthIdentity packageData,
IntPtr getKeyFunc,
IntPtr getKeyData,
ref RawSspiHandle credentialHandle,
ref TimeStamp expiry
);
Upvotes: 3
Views: 3340
Reputation: 12425
AcquireCredentialsHandle()
returns true even if the credentials are bogus; only when the client attempts to invoke InitializeSecurityContext()
does the API validate the username and password. AcquireCredentialsHandle()
just performs parameter validation (pointers values are valid, structures are filled out correctly, parameters make sense with each other, etc). Since I correctly provided incorrect parameters, AcquireCredentialsHandle()
didn't care.
...
To summarize, the normal cycle for an client application to participate in SSPI authentication is the following:
AcquireCredentialsHandle()
.InitializeSecurityContext()
, which returns some token to send to the server.AcceptSecurityContext()
, returning some token to send back to the client.InitializeSecurityContext()
.In the above situation, credentials acquired from calling AcquireCredentialsHandle()
with a provided SEC_WINNT_AUTH_IDENTITY
structure (in turn filled out with a valid username, domain, and password) aren't validated on the client side until the client invokes InitializeSecurityContext()
the first time, before it sends its first token to the server.
In response to a similar question, Dave Christiansen (a Microsoft employee?) posted the following in the newsgroup 'microsoft.public.platformsdk.security' on 19-Sept-2003:
How are you determining that the credentials are those of the local user? SSPI is sometimes tricky this way. What package are you using (Sounds like NTLM, Kerberos, or Negotiate if you're using SEC_WINNT_AUTH_IDENTITY)?
Note that AcquireCredentialsHandle will succeed even if the credentials given are incorrect (e.g. wrong password), because it doesn't actually use them until you call InitializeSecurityContext.
Upvotes: 3