Knowledge Base

HOWTO: Call LsaLogonUser to Obtain Primary Level Access Tokens

PSS ID Number: 137578

Article Last Modified on 5/7/2003


The information in this article applies to:


This article was previously published under Q137578

SUMMARY

One of the primary uses of the LSA authentication facility is to obtain primary access tokens through LsaLogonUser(). The resulting access token can be used by the winlogon replaceable component Gina. This article demonstrates how to obtain an access token programmatically by using LSA APIs.

MORE INFORMATION

The following steps show how to obtain an access token through LsaLogonUser() from the MSV1_0 authentication package:
  1. Enable the SeTcbPrivilege. This privilege is necessary in order to be able to register the logon process and look up the appropriate authentication package.
  2. Call LsaRegisterLogonProcess() to establish a connection to the LSA, which will be used in subsequent authentication services.
  3. Call LsaLookupAuthenticationPackage() to obtain the package ID of an authentication package. This ID can then be used in subsequent authentication services. For the default MSV1_0 authentication package, the PackageName to look up is:

    MICROSOFT_AUTHENTICATION_PACKAGE_V1_0

  4. Disable the SeTcbPrivilege.
  5. Prepare an OriginName string, which will identify the origin of the logon attempt. For example, "TTY1" to indicate terminal 1, or "LAN Manager - remote node JAZZ" might indicate a network logon attempt through LAN Manager from a remote node called "JAZZ".
  6. Prepare SourceContext information identifying the source component (for example, session manager) and context that may be useful to that component. This information will be included in the token and can later be retrieved.
  7. Prepare an optional TOKEN_GROUPS structure containing supplementary security IDs to add to the access token during the call to LsaLogonUser.
  8. Prepare an authentication buffer that is suitable for the target authentication package. The authentication buffer is expected to include identification and authentication information such as username and password. The format of this buffer is dependent on the target authentication package. For the default MSV1_0 package, the authentication buffer must be built such that each element of the buffer is contained within a single contiguous memory block.
  9. Call LsaLogonUser() with data obtained from steps 2-8 to obtain an access token representing the supplied credentials.
  10. When the authentication services are no longer needed, close the handle obtained from step 2.
  11. When the access token is no longer needed, close the access token handle obtained from step 9.

Sample Code

The following is sample code for LsaLogonUser, which can be used to generate primary level access tokens.

The LogonType parameter can be:

Batch
Service
Interactive

This sample is not suitable for generating network-impersonation tokens. The Interactive logontype is appropriate for Gina and processes that need to call CreateProcessAsUser().

Once the package has been successfully initialized via RegisterLogonProcess(), you are free to obtain access tokens through MyLogonUser() by passing the provided credentials. The handle and ID of the authentication package are saved in a global variable to allow the caller the ability to continually call MyLogonUser() until DeRegisterLogonProcess() is called.

The sample code should be linked with the following import libraries if the application is targeted only for Windows 2000 and later:

Mssdk\Lib\Advapi32.lib
Mssdk\Lib\Kernel32.lib
Mssdk\Lib\Secur32.lib

The sample code should be linked with the following import libraries if the application is targeted for Windows NT 4.0 and earlier. Applications compiled using these import libraries will run on Windows 2000 and later as well.

Mssdk\Lib\Advapi32.lib
Mssdk\Lib\Kernel32.lib
Ntddk\Libfre\I386\Lsadll.lib
Ntddk\Libfre\I386\Ntdll.lib

Some of the structures used in the sample code are declared only in the latest ntsecapi.h file. So, this sample code can be compiled successfully only with the headers and libraries that come with the latest Microsoft Platform SDK.

The latest Microsoft Platform SDK can be downloaded from the following Microsoft Web site:
The import libraries Lsadll.lib and Ntdll.lib for Windows NT 4.0 and earlier can be obtained by installing the Microsoft Windows DDK from:
#include <windows.h>
#include <stdio.h>
#include <ntsecapi.h>

BOOL
SetCurrentPrivilege(
    LPCTSTR Privilege,      // Privilege to enable/disable
    BOOL bEnablePrivilege   // to enable or disable privilege
);

NTSTATUS RegisterLogonProcess(void);

void DeRegisterLogonProcess(void);

NTSTATUS MyLogonUser(
    LPWSTR UserName,
    LPWSTR Domain,
    LPWSTR Password,
    SECURITY_LOGON_TYPE LogonType,
    PHANDLE pLogonToken,
    PMSV1_0_INTERACTIVE_PROFILE *pProfileBuffer);

void DisplayProfile(
    PMSV1_0_INTERACTIVE_PROFILE ProfileBuffer);

void DisplaySystemTime(
    LPSTR Text,
    LPFILETIME ft);

void InitString(PLSA_STRING LsaString, LPSTR String);

#define RTN_OK 0
#define RTN_USAGE 1
#define RTN_ERROR 13

#define AUTH_PACKAGE "MICROSOFT_AUTHENTICATION_PACKAGE_V1_0"

// 
// if you have the ddk, include ntstatus.h
// 
#ifndef STATUS_SUCCESS
#define STATUS_SUCCESS                  ((NTSTATUS)0x00000000L)
#define STATUS_UNSUCCESSFUL             ((NTSTATUS)0xC0000001L)
#endif

// 
// cache LsaHandle and the authentication package Id
// 
HANDLE gLsaHandle = INVALID_HANDLE_VALUE;
ULONG gAuthPackageId = 0;

int wmain(int argc, wchar_t *argv[])
{
    HANDLE hToken;
    PMSV1_0_INTERACTIVE_PROFILE Profile = NULL;
    NTSTATUS Status;
    LPWSTR wUserName;
    LPWSTR wDomain;
    LPWSTR wPassword;

    if (argc != 4)
    {
        fprintf(stderr,
        "Usage: %ls <UserName> <DomainName> <Password>\n", argv[0]);
        return RTN_USAGE;
    }

    wUserName = argv[1];
    wDomain = argv[2];
    wPassword = argv[3];

    // 
    // enable the SeTcbPrivilege
    // 
    if (!SetCurrentPrivilege(SE_TCB_NAME, TRUE))
    {
        fprintf(stderr,
            "SeTcbPrivilege could not be enabled! (rc=%lu)\n",
            GetLastError());
        return RTN_ERROR;
    }

    // 
    // Prepare to use the authentication package
    // 
    Status=RegisterLogonProcess();

    // 
    // disable the SeTcbPrivilege
    // 
    SetCurrentPrivilege(SE_TCB_NAME, FALSE);

    if (Status != STATUS_SUCCESS)
    {
        fprintf(stderr,
            "RegisterLogonProcess error! (ntstatus=0x%lx)\n", Status);
        return RTN_ERROR;
    }

    // 
    // we've successfully setup a link to the authentication package
    // we can now logon via MyLogonUser until we DeRegister the
    // authentication package
    // 

    Status = MyLogonUser(wUserName, wDomain, wPassword,
                Interactive, &hToken, &Profile);

    if (Status == STATUS_SUCCESS)
    {

        // 
        // impersonate the user we just got a token for
        // 
        if (ImpersonateLoggedOnUser(hToken))
        {
            CHAR szName[255];
            DWORD cbName=255;

            // 
            // indicate who we logged on, to verify success
            // 
            GetUserNameA(szName, &cbName);
            fprintf(stdout,"Successfully logged on user = %s\n",
                szName);

            RevertToSelf();

            // 
            // Display the Profile
            // 
            DisplayProfile(Profile);

            // 
            // free Profile buffer allocated by Lsa
            // 
            if (Profile)
                LsaFreeReturnBuffer(Profile);
        }
        else
        {
            fprintf(stdout,"ImpersonateLoggedOnUser error (rc=%lu)\n",
            GetLastError());
        }

        // 
        // close the process token
        // 
        CloseHandle(hToken);

    }
    else
    {
        fprintf(stderr,
            "MyLogonUser error! (ntstatus=0x%lx)\n", Status);
    }

    // 
    // we are done with the authentication package
    // 
    DeRegisterLogonProcess();

    return RTN_OK;
}

NTSTATUS RegisterLogonProcess(void)
{
    CHAR szFileName[128];
    LSA_STRING LogonProcessName;
    LSA_STRING PackageName;
    LSA_OPERATIONAL_MODE SecurityMode;
    NTSTATUS Status;

    GetModuleFileNameA(NULL, szFileName, 127);

    // 
    // Prepare to use authentication services
    // 

    InitString(&LogonProcessName, szFileName);

    if ((Status=LsaRegisterLogonProcess(
            &LogonProcessName,
            &gLsaHandle,       // global LsaHandle
            &SecurityMode
            )) != STATUS_SUCCESS)
        return Status;

    // 
    // Obtain ID used to reference the authentication package
    // 

    InitString(&PackageName, AUTH_PACKAGE);

    if ((Status=LsaLookupAuthenticationPackage(
        gLsaHandle,
        &PackageName,
        &gAuthPackageId     // global Authentication Package Id
        )) != STATUS_SUCCESS)
    {

        // 
        // if this failed, cleanup
        // 
        DeRegisterLogonProcess();

    }
    
    return Status;
}

void DeRegisterLogonProcess(void)
{
    // 
    // Close and invalidate the handle
    // 

    CloseHandle(gLsaHandle);

    gLsaHandle = INVALID_HANDLE_VALUE;
}

NTSTATUS MyLogonUser(
    LPWSTR UserName,
    LPWSTR Domain,
    LPWSTR Password,
    SECURITY_LOGON_TYPE LogonType,
    PHANDLE pLogonToken,
    PMSV1_0_INTERACTIVE_PROFILE *pProfileBuffer)
{
    HANDLE hHeap=GetProcessHeap(); // cache the process heap handle
    USHORT cbUserName;  // number of bytes for username
    USHORT cbDomain;    // number of bytes for domain
    USHORT cbPassword;  // number of bytes for password
    LSA_STRING OriginName;
    LUID LogonLuid;
    TOKEN_SOURCE SourceContext;
    QUOTA_LIMITS Quotas;
    PMSV1_0_INTERACTIVE_LOGON MsvAuthInfo;
    ULONG ProfileBufferLength;
    PVOID AuthInfoBuf;
    ULONG AuthInfoSize;
    PTOKEN_GROUPS TokenGroups;
    SID_IDENTIFIER_AUTHORITY IdentifierAuthority=
    SECURITY_LOCAL_SID_AUTHORITY;
    PSID LogonSid;
    PSID LocalSid;
    NTSTATUS Status;
    NTSTATUS SubStatus;

    // 
    // invalidate pointers to dynamically allocated resources
    // 
    LocalSid = NULL;
    LogonSid = NULL;
    AuthInfoBuf = NULL;

    // 
    // Set logon origin
    // change to represent your product name
    // 

    InitString(&OriginName, "nospam@microsoft.com");

    // 
    // Initialize source context structure
    // 

    strncpy(SourceContext.SourceName, "Source  ",
        sizeof(SourceContext.SourceName));

    if (!AllocateLocallyUniqueId(&SourceContext.SourceIdentifier))
    {
        return STATUS_UNSUCCESSFUL;
    }

    __try
    {

        // 
        // the following code is specific to the interactive logontype
        // 

        // 
        // Create Logon Sid and Local Sid
        // 
        if (!AllocateLocallyUniqueId(&LogonLuid))
        {
            Status = STATUS_UNSUCCESSFUL;
            __leave;
        }

        // 
        // NOTE: for Gina, winlogon passes us a Logon Sid, which we could use
        // here instead
        // 

        if (!AllocateAndInitializeSid(
            &IdentifierAuthority,
            3,
            SECURITY_LOGON_IDS_RID,
            LogonLuid.HighPart,
            LogonLuid.LowPart,
            0, 0, 0, 0, 0,
            &LogonSid))
        {
            Status = STATUS_NO_MEMORY;
            __leave;
        }

        if (!AllocateAndInitializeSid(
            &IdentifierAuthority,
            1,
            SECURITY_LOCAL_RID,
            0, 0, 0, 0, 0, 0, 0,
            &LocalSid))
        {
            Status = STATUS_NO_MEMORY;
            __leave;
        }

        // 
        // Create logon token groups containing Logon and Local Sid
        // for a gina call to LsaLogonUser, we are given a logon Sid,
        // so we could skip creating the LogonSid below, and just add what
        // we were passed in by gina
        // 

        #define TOKEN_GROUP_COUNT 2

        TokenGroups = (PTOKEN_GROUPS)HeapAlloc(hHeap, 0,
                        sizeof(TOKEN_GROUPS) +
                        (TOKEN_GROUP_COUNT - ANYSIZE_ARRAY) *
                        sizeof(SID_AND_ATTRIBUTES));

        if (TokenGroups == NULL)
        {
            Status = STATUS_NO_MEMORY;
            __leave;
        }

        TokenGroups->GroupCount = TOKEN_GROUP_COUNT;

        TokenGroups->Groups[0].Sid = LogonSid;
        TokenGroups->Groups[0].Attributes =
        SE_GROUP_MANDATORY | SE_GROUP_ENABLED |
        SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_LOGON_ID;

        TokenGroups->Groups[1].Sid = LocalSid;
        TokenGroups->Groups[1].Attributes =
        SE_GROUP_MANDATORY | SE_GROUP_ENABLED |
        SE_GROUP_ENABLED_BY_DEFAULT;

        // 
        // Build logon authentication package valid for the following
        // logons: batch, interactive, service
        // 
        // the package is passed in an LPC call, so it needs to reside in
        // contiguous memory.
        // 

        cbUserName = (USHORT)(wcslen(UserName) * sizeof(WCHAR));
        cbDomain = (USHORT)(wcslen(Domain) * sizeof(WCHAR));
        cbPassword = (USHORT)(wcslen(Password) * sizeof(WCHAR));

        AuthInfoSize = sizeof(MSV1_0_INTERACTIVE_LOGON) +
                        cbUserName + sizeof(WCHAR) +
                        cbDomain + sizeof(WCHAR) +
                        cbPassword + sizeof(WCHAR) ;

        AuthInfoBuf = HeapAlloc(hHeap,
                                HEAP_ZERO_MEMORY,
                                AuthInfoSize);

        MsvAuthInfo = (PMSV1_0_INTERACTIVE_LOGON)AuthInfoBuf;

        if (MsvAuthInfo == NULL)
        {
            Status = STATUS_NO_MEMORY;
            __leave;
        }

        // 
        // The authentication buffer is interactivelogon type for
        // batch, interactive, service
        // 
        MsvAuthInfo->MessageType = MsV1_0InteractiveLogon;

        // 
        // Copy the user name into the authentication buffer
        // 

        MsvAuthInfo->UserName.Length = cbUserName;
        MsvAuthInfo->UserName.MaximumLength = cbUserName + sizeof(WCHAR);
        MsvAuthInfo->UserName.Buffer = (PWSTR)(MsvAuthInfo + 1);
        wcscpy(MsvAuthInfo->UserName.Buffer, UserName);

        // 
        // Copy the domain name into the authentication buffer
        // 

        MsvAuthInfo->LogonDomainName.Length = cbDomain;
        MsvAuthInfo->LogonDomainName.MaximumLength = cbDomain + sizeof(WCHAR);

        MsvAuthInfo->LogonDomainName.Buffer = (PWSTR)
                ((PBYTE)(MsvAuthInfo->UserName.Buffer) +
                MsvAuthInfo->UserName.MaximumLength);

        wcscpy(MsvAuthInfo->LogonDomainName.Buffer, Domain);

        // 
        // Copy the password into the authentication buffer
        // 

        MsvAuthInfo->Password.Length = cbPassword;
        MsvAuthInfo->Password.MaximumLength = cbPassword + sizeof(WCHAR);
        MsvAuthInfo->Password.Buffer = (PWSTR)
                ((PBYTE)(MsvAuthInfo->LogonDomainName.Buffer) +
                MsvAuthInfo->LogonDomainName.MaximumLength);

        wcscpy(MsvAuthInfo->Password.Buffer, Password);

        Status = LsaLogonUser(
                    gLsaHandle,             // global LsaHandle
                    &OriginName,
                    LogonType,              // logon type
                    gAuthPackageId,         // global authentication package ID
                    AuthInfoBuf,
                    AuthInfoSize,
                    TokenGroups,
                    &SourceContext,
                    (PVOID *)pProfileBuffer,
                    &ProfileBufferLength,
                    &LogonLuid,
                    pLogonToken,
                    &Quotas,
                    &SubStatus);

    } // try
    __finally
    {

        cbUserName = cbDomain = cbPassword = 0; // sensitive information

        // 
        // free token group list
        // 

        if (TokenGroups)
            HeapFree(hHeap, 0, TokenGroups);

        // 
        // zero and free authentication buffer
        // 

        if (AuthInfoBuf)
        {
            ZeroMemory(AuthInfoBuf, AuthInfoSize);  // sensitive buffer
            HeapFree(hHeap, 0, AuthInfoBuf);
        }

        // 
        // free Sids
        // 

        if (LocalSid)
            FreeSid(LocalSid);

        // 
        // TODO add a parameter to this function for the LogonSid
        // We would then call FreeSid from outside this function when we
        // are done with the logon Sid.  This is appropriate for Gina, etc.
        // 
        // 
        // NOTE: THIS LOGIC WILL BE DIFFERENT FOR A GINA IF WE USE THE
        // SID WHICH GINA PREPARED.
        // 
        if (Status != STATUS_SUCCESS) // only free this if we failed
        {
            if (LogonSid)
                FreeSid(LogonSid);
        }

    } // finally

    return Status;
}

void DisplayProfile(PMSV1_0_INTERACTIVE_PROFILE ProfileBuffer)
{
    fprintf(stdout,   "LogonCount         = 0x%x\n",
        ProfileBuffer->LogonCount);

    fprintf(stdout,   "BadPasswordCount   = 0x%x\n",
        ProfileBuffer->BadPasswordCount);

    DisplaySystemTime("LogonTime         ",
        (LPFILETIME)&ProfileBuffer->LogonTime);

    DisplaySystemTime("LogoffTime        ",
        (LPFILETIME)&ProfileBuffer->LogoffTime);

    DisplaySystemTime("KickOffTime       ",
        (LPFILETIME)&ProfileBuffer->KickOffTime);

    DisplaySystemTime("PasswordLastSet   ",
        (LPFILETIME)&ProfileBuffer->PasswordLastSet);

    DisplaySystemTime("PasswordCanChange ",
        (LPFILETIME)&ProfileBuffer->PasswordCanChange);

    DisplaySystemTime("PasswordMustChange",
        (LPFILETIME)&ProfileBuffer->PasswordMustChange);

    fprintf(stdout,   "LogonScript        = %ls\n",
        ProfileBuffer->LogonScript.Buffer);

    fprintf(stdout,   "HomeDirectory      = %ls\n",
        ProfileBuffer->HomeDirectory.Buffer);

    fprintf(stdout,   "FullName           = %ls\n",
        ProfileBuffer->FullName.Buffer);

    fprintf(stdout,   "ProfilePath        = %ls\n",
        ProfileBuffer->ProfilePath.Buffer);

    fprintf(stdout,   "HomeDirectoryDrive = %ls\n",
        ProfileBuffer->HomeDirectoryDrive.Buffer);

    fprintf(stdout,   "LogonServer        = %ls\n",
        ProfileBuffer->LogonServer.Buffer);

    fprintf(stdout,   "UserFlags          = 0x%lx\n",
        ProfileBuffer->UserFlags);
}

void DisplaySystemTime(
    LPSTR Text,
    LPFILETIME ft)
{
    SYSTEMTIME utc;
    SYSTEMTIME st;

    FileTimeToSystemTime(ft, &utc);
    SystemTimeToTzSpecificLocalTime(NULL, &utc, &st);

    fprintf(stdout,"%s = %02u-%02u-%u %02u:%02u:%02u.%02u\n",
        Text, st.wMonth, st.wDay, st.wYear,
        st.wHour, st.wMinute, st.wSecond, st.wMilliseconds);
}

void InitString(
    PLSA_STRING LsaString,
    LPSTR String)
{
    DWORD StringLength;

    if (String == NULL)
    {
        LsaString->Buffer = NULL;
        LsaString->Length = 0;
        LsaString->MaximumLength = 0;

        return;
    }

    StringLength = strlen(String);
    LsaString->Buffer = String;
    LsaString->Length = (USHORT) StringLength * sizeof(CHAR);
    LsaString->MaximumLength =
    (USHORT) (StringLength + 1) * sizeof(CHAR);
}

BOOL SetCurrentPrivilege(
    LPCTSTR Privilege,      // Privilege to enable/disable
    BOOL bEnablePrivilege)  // to enable or disable privilege
{
    HANDLE hToken;
    TOKEN_PRIVILEGES tp;
    LUID luid;
    TOKEN_PRIVILEGES tpPrevious;
    DWORD cbPrevious=sizeof(TOKEN_PRIVILEGES);
    BOOL bSuccess=FALSE;

    if (!LookupPrivilegeValue(NULL, Privilege, &luid))
        return FALSE;

    if (!OpenProcessToken(
            GetCurrentProcess(),
            TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES,
            &hToken
            ))
        return FALSE;

    // 
    // first pass.  get current privilege setting
    // 
    tp.PrivilegeCount           = 1;
    tp.Privileges[0].Luid       = luid;
    tp.Privileges[0].Attributes = 0;

    AdjustTokenPrivileges(
        hToken,
        FALSE,
        &tp,
        sizeof(TOKEN_PRIVILEGES),
        &tpPrevious,
        &cbPrevious);

    if (GetLastError() == ERROR_SUCCESS)
    {
        // 
        // second pass.  set privilege based on previous setting
        // 
        tpPrevious.PrivilegeCount     = 1;
        tpPrevious.Privileges[0].Luid = luid;

        if (bEnablePrivilege) {
            tpPrevious.Privileges[0].Attributes |=
            (SE_PRIVILEGE_ENABLED);
        }
        else
        {
            tpPrevious.Privileges[0].Attributes ^=
            (SE_PRIVILEGE_ENABLED &
            tpPrevious.Privileges[0].Attributes);
        }

        AdjustTokenPrivileges(
            hToken,
            FALSE,
            &tpPrevious,
            cbPrevious,
            NULL,
            NULL);

        if (GetLastError() == ERROR_SUCCESS)
            bSuccess=TRUE;
    }

    CloseHandle(hToken);

    return bSuccess;
}
				

Additional query words: prodnt winlogon BseSecurity BseMisc CodeSam

Keywords: kbAPI kbhowto kbKernBase kbLSA kbSecurity KB137578
Technology: kbAudDeveloper kbOSWin2000 kbOSWinNT400 kbOSWinNTSearch kbOSWinSearch kbOSWinXP kbOSWinXPSearch kbWin32API kbWin32sSearch