LsaLogonUser in a WCP context to sign in into windows is not working

114 views Asked by At

When I boot my computer, I am attempting to have sign in into Windows without having to enter credentials. These credentials are available and entered from other means (which excludes the solution of using the winlogon default registry values), in a "single sign-on" kind of deal. Anyways, I am therefore attempting to go about it by creating a Windows Credential Provider (WCP). I got this to run. In this WCP I am applying the LsaLogonUser function with MSV1_0_INTERACTIVE_LOGON. Everything returns the result of success but it does not make the computer to actually log on. I might've misunderstood how it all works so I was wondering if anyone know why it doesn't work. My thoughts is that the issue potentially is that the session only lasts as long as the scope does. That is, as long as the token exists. So when the LogonUI (by Winlogon) process finishes it's execution, it is automatically signed out or something. What am I missing?

Here is a big chunk of code of how it executes:

void login(std::wstring domain, std::wstring username, std::wstring secret)
{
    //Get a handle to LSA
    HANDLE hLSA = nullptr;
    NTSTATUS status = LsaConnectUntrusted(&hLSA);
    if (status != 0)
    {
        int winError = LsaNtStatusToWinError(status);
        LLLOG(L"Error calling LsaConnectUntrusted. Error code: " + std::to_wstring(winError) );
        return;
    }
    if (!hLSA)
    {
        LLLOG(L"hLSA is NULL");
        return;
    }

    //Build LsaLogonUser parameters
    LSA_STRING originName = {};
    char originNameStr[] = "WCP";
    originName.Buffer = originNameStr;
    originName.Length = (USHORT)strlen(originNameStr);
    originName.MaximumLength = originName.Length;

    ULONG authPackage = 0;
    PLSA_STRING authPackageName = new LSA_STRING();
    char authPackageBuf[] = MSV1_0_PACKAGE_NAME;
    authPackageName->Buffer = authPackageBuf;
    authPackageName->Length = (USHORT)strlen(authPackageBuf);
    authPackageName->MaximumLength = (USHORT)strlen(authPackageBuf);
    status = LsaLookupAuthenticationPackage(hLSA, authPackageName, &authPackage);
    if (status != 0)
    {
        int winError = LsaNtStatusToWinError(status);
        LLLOG(L"Call to LsaLookupAuthenticationPackage failed. Error code: " + std::to_wstring(winError));
        return;
    }
    
    DWORD authBufferSize = 0;
    PVOID authBuffer = CreateNtlmLogonStructure(domain, username, secret, &authBufferSize);
    LLLOG(L"authBufferSize: " + std::to_wstring(authBufferSize));

    //Get TokenSource
    HANDLE hProcess = GetCurrentProcess();
    HANDLE procToken = nullptr;
    BOOL success = OpenProcessToken(hProcess, TOKEN_ALL_ACCESS, &procToken);
    if (!success)
    {
        DWORD errorCode = GetLastError();
        LLLOG(L"Call to OpenProcessToken failed. Errorcode: " + std::to_wstring(errorCode));
        return;
    }

    TOKEN_SOURCE tokenSource = {};
    DWORD realSize = 0;
    success = GetTokenInformation(procToken, TokenSource, &tokenSource, sizeof(tokenSource), &realSize);
    if (!success)
    {
        LLLOG(L"Call to GetTokenInformation failed.");
        return;
    }

    //
    PVOID profileBuffer = NULL;
    ULONG profileBufferSize = 0;
    LUID loginId;
    HANDLE token = NULL;
    QUOTA_LIMITS quotaLimits;
    NTSTATUS subStatus = 0;

    status = LsaLogonUser(
        hLSA,
        &originName,
        Interactive,
        authPackage,
        authBuffer,
        authBufferSize,
        0,
        &tokenSource,
        &profileBuffer,
        &profileBufferSize,
        &loginId,
        &token,
        &quotaLimits,
        &subStatus);

    if (status != 0)
    {
        NTSTATUS winError = LsaNtStatusToWinError(status);
        LLLOG(L"Error calling LsaLogonUser. Error code: " + std::to_wstring(winError));
        return;
    }

    LLLOG(L"Success!");

    LsaFreeReturnBuffer(profileBuffer);
    CloseHandle(token);
    HeapFree(GetProcessHeap(), 0, authBuffer);
    LLLOG(L"Cleanup complete.");

    return;
}

//size will be set to the size of the structure created
PVOID CreateNtlmLogonStructure(std::wstring domain, std::wstring username, std::wstring password, DWORD* size)
{
    size_t wcharSize = sizeof(wchar_t);
    size_t totalSize = sizeof(MSV1_0_INTERACTIVE_LOGON) + ((domain.length() + username.length() + password.length()) * wcharSize);
    MSV1_0_INTERACTIVE_LOGON* ntlmLogon = (PMSV1_0_INTERACTIVE_LOGON)(new BYTE[totalSize]);
    size_t offset = sizeof(MSV1_0_INTERACTIVE_LOGON);

    ntlmLogon->MessageType = MsV1_0InteractiveLogon;
    offset += WriteUnicodeString(domain, &(ntlmLogon->LogonDomainName), ntlmLogon, offset);
    offset += WriteUnicodeString(username, &(ntlmLogon->UserName), ntlmLogon, offset);
    offset += WriteUnicodeString(password, &(ntlmLogon->Password), ntlmLogon, offset);

    *size = (DWORD)totalSize;
    return ntlmLogon;
}

size_t WriteUnicodeString(std::wstring str, UNICODE_STRING* uniStr, PVOID baseAddress, size_t offset)
{
    const wchar_t* buffer = str.c_str();
    size_t size = str.length() * sizeof(wchar_t);
    uniStr->Length = (USHORT)size;
    uniStr->MaximumLength = (USHORT)size;
    uniStr->Buffer = (PWSTR)((UINT_PTR)baseAddress + offset);
    memcpy((PVOID)((UINT_PTR)baseAddress + offset), str.c_str(), size);
    return size;
}
1

There are 1 answers

0
Nehluxhes On

Right now your session will end as soon as you call CloseHandle(token);

But your mistake is that you are not supposed to call LsaLogonUser inside a CP. Instead you are supposed to pass your MSV1_0_INTERACTIVE_LOGON serialization to your ICredentialProviderCredential::GetSerialization implementation to pass it to winlogon, and then that's winlogon itself which will call LsaLogonUser (or similar) for you, exiting the Logon screen at the same time.