antiduh
antiduh

Reputation: 12425

How to invoke SSPI's 'AcquireCredentialsHandle' with supplied credentials?

Background

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:

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.

Problem

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

Answers (1)

antiduh
antiduh

Reputation: 12425

In short

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.

...

In long

To summarize, the normal cycle for an client application to participate in SSPI authentication is the following:

  • Client calls some form of AcquireCredentialsHandle().
  • Client invoke InitializeSecurityContext(), which returns some token to send to the server.
  • Server takes the received token and invokes AcceptSecurityContext(), returning some token to send back to the client.
  • The client receives the token and invokes InitializeSecurityContext().
  • ... the loop continues until the authentication cycle has completed between both ends.

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

Related Questions