Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions ee/wcp/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -330,6 +330,8 @@ set(PROJECT_INSALL_DIR "${CMAKE_CURRENT_BINARY_DIR}/ak_cred_provider/Release")
add_subdirectory(cefexe)
set_property(TARGET ak_cef PROPERTY MSVC_RUNTIME_LIBRARY MultiThreaded)

add_subdirectory(ak_lsa)

# Display configuration settings.
PRINT_CEF_CONFIG()

Expand Down
2 changes: 1 addition & 1 deletion ee/wcp/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ include ../../common.mk

OUT_TARGET := wcp

TARGETS := ak_cred_provider ak_common cefexe cefsimple
TARGETS := ak_cred_provider ak_lsa ak_common cefexe cefsimple

CLANG_FORMAT := "C:\Program Files\LLVM\bin\clang-format.exe"
FORMAT_FIND_ARGS := -iname '*.h' -o -iname '*.cpp' -o -iname '*.hpp'
Expand Down
37 changes: 37 additions & 0 deletions ee/wcp/ak_lsa/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
project(ak_lsa)

set(CMAKE_BUILD_TYPE Release)
set(CMAKE_CXX_STANDARD 20)

set(SRCS
PrepareProfile.cpp
PrepareToken.cpp
Main.cpp
)

find_library(CREDUI_LIB_PATH Credui.lib)
find_library(SECUR32_LIB_PATH Secur32.lib)
find_library(SHLWAPI_LIB_PATH Shlwapi.lib)

add_library(${PROJECT_NAME} SHARED
${SRCS}
)
target_compile_definitions(${PROJECT_NAME} PUBLIC UNICODE _UNICODE SECURITY_WIN32)

include_directories(
include
)

include_directories(${PROJECT_NAME} PUBLIC ..)

target_link_libraries(${PROJECT_NAME}
${CREDUI_LIB_PATH}
${SECUR32_LIB_PATH}
${SHLWAPI_LIB_PATH}
authentik_sys_bridge
ak_common
spdlog
)
set_property(TARGET ak_lsa PROPERTY MSVC_RUNTIME_LIBRARY MultiThreaded)
set_property(TARGET authentik_sys PROPERTY MSVC_RUNTIME_LIBRARY MultiThreaded)
set_property(TARGET authentik_sys_bridge PROPERTY MSVC_RUNTIME_LIBRARY MultiThreaded)
292 changes: 292 additions & 0 deletions ee/wcp/ak_lsa/Main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,292 @@
#include "PrepareToken.hpp"
#include "PrepareProfile.hpp"
#include "Utils.hpp"
#include "ak_common/include/ak_log.h"
#include "ak_common/include/ak_sentry.h"
#include "spdlog/spdlog.h"

// exported symbols
#pragma comment(linker, "/export:SpLsaModeInitialize")

LSA_SECPKG_FUNCTION_TABLE FunctionTable;

NTSTATUS NTAPI SpInitialize(_In_ ULONG_PTR PackageId, _In_ SECPKG_PARAMETERS* Parameters,
_In_ LSA_SECPKG_FUNCTION_TABLE* functionTable) {
ak_setup_logs("lsa");
ak_setup_sentry("lsa");

spdlog::debug("SpInitialize");

spdlog::debug(" PackageId: %u", PackageId);
spdlog::debug(" Version: %u", Parameters->Version);
{
ULONG state = Parameters->MachineState;
spdlog::debug(" MachineState:");
if (state & SECPKG_STATE_ENCRYPTION_PERMITTED) {
state &= ~SECPKG_STATE_ENCRYPTION_PERMITTED;
spdlog::debug(" - ENCRYPTION_PERMITTED");
}
if (state & SECPKG_STATE_STRONG_ENCRYPTION_PERMITTED) {
state &= ~SECPKG_STATE_STRONG_ENCRYPTION_PERMITTED;
spdlog::debug(" - STRONG_ENCRYPTION_PERMITTED");
}
if (state & SECPKG_STATE_DOMAIN_CONTROLLER) {
state &= ~SECPKG_STATE_DOMAIN_CONTROLLER;
spdlog::debug(" - DOMAIN_CONTROLLER");
}
if (state & SECPKG_STATE_WORKSTATION) {
state &= ~SECPKG_STATE_WORKSTATION;
spdlog::debug(" - WORKSTATION");
}
if (state & SECPKG_STATE_STANDALONE) {
state &= ~SECPKG_STATE_STANDALONE;
spdlog::debug(" - STANDALONE");
}
if (state) {
// print resudual flags not already covered
spdlog::debug(" * Unknown flags: 0x%X", state);
}
}
spdlog::debug(" SetupMode: %u", Parameters->SetupMode);
// parameters not logged
Parameters->DomainSid;
Parameters->DomainName;
Parameters->DnsDomainName;
Parameters->DomainGuid;

FunctionTable = *functionTable; // copy function pointer table

spdlog::debug(" return STATUS_SUCCESS");
return STATUS_SUCCESS;
}

NTSTATUS NTAPI SpShutDown() {
ak_teardown_sentry();
spdlog::debug("SpShutDown");
spdlog::debug(" return STATUS_SUCCESS");
ak_teardown_logs();
return STATUS_SUCCESS;
}

NTSTATUS NTAPI SpGetInfo(_Out_ SecPkgInfoW* PackageInfo) {
spdlog::debug("SpGetInfo");

// return security package metadata
PackageInfo->fCapabilities = SECPKG_FLAG_LOGON // supports LsaLogonUser
| SECPKG_FLAG_CLIENT_ONLY; // no server auth support
PackageInfo->wVersion = SECURITY_SUPPORT_PROVIDER_INTERFACE_VERSION;
PackageInfo->wRPCID = SECPKG_ID_NONE; // no DCE/RPC support
PackageInfo->cbMaxToken = 0;
PackageInfo->Name = (wchar_t*)L"ak_lsa";
PackageInfo->Comment = (wchar_t*)L"authentik Token Authentication";

spdlog::debug(" return STATUS_SUCCESS");
return STATUS_SUCCESS;
}

/* Authenticate a user logon attempt.
Returns STATUS_SUCCESS if the login attempt succeeded. */
NTSTATUS LsaApLogonUser(_In_ PLSA_CLIENT_REQUEST ClientRequest, _In_ SECURITY_LOGON_TYPE LogonType,
_In_reads_bytes_(SubmitBufferSize) VOID* ProtocolSubmitBuffer,
_In_ VOID* ClientBufferBase, _In_ ULONG SubmitBufferSize,
_Outptr_result_bytebuffer_(*ProfileBufferSize) VOID** ProfileBuffer,
_Out_ ULONG* ProfileBufferSize, _Out_ LUID* LogonId,
_Out_ NTSTATUS* SubStatus,
_Out_ LSA_TOKEN_INFORMATION_TYPE* TokenInformationType,
_Outptr_ VOID** TokenInformation, _Out_ LSA_UNICODE_STRING** AccountName,
_Out_ LSA_UNICODE_STRING** AuthenticatingAuthority) {
spdlog::debug("LsaApLogonUser");

{
// clear output arguments first in case of failure
*ProfileBuffer = nullptr;
*ProfileBufferSize = 0;
*LogonId = {};
*SubStatus = 0;
*TokenInformationType = {};
*TokenInformation = nullptr;
*AccountName = nullptr;
if (AuthenticatingAuthority) *AuthenticatingAuthority = nullptr;
}

// input arguments
spdlog::debug(" LogonType: {}", (int)LogonType); // Interactive=2, RemoteInteractive=10
ClientBufferBase;
spdlog::debug(" ProtocolSubmitBuffer size: {}", (int)SubmitBufferSize);

// deliberately restrict supported logontypes
if ((LogonType != Interactive) && (LogonType != RemoteInteractive)) {
spdlog::debug(" return STATUS_NOT_IMPLEMENTED (unsupported LogonType)");
return STATUS_NOT_IMPLEMENTED;
}

// authentication credentials passed by client
auto* logonInfo = (MSV1_0_INTERACTIVE_LOGON*)ProtocolSubmitBuffer;
{
if (SubmitBufferSize < sizeof(MSV1_0_INTERACTIVE_LOGON)) {
spdlog::debug(" ERROR: SubmitBufferSize too small");
return STATUS_INVALID_PARAMETER;
}

// make relative pointers absolute to ease later access
logonInfo->LogonDomainName.Buffer =
(wchar_t*)((BYTE*)logonInfo + (size_t)logonInfo->LogonDomainName.Buffer);
logonInfo->UserName.Buffer = (wchar_t*)((BYTE*)logonInfo + (size_t)logonInfo->UserName.Buffer);
logonInfo->Password.Buffer = (wchar_t*)((BYTE*)logonInfo + (size_t)logonInfo->Password.Buffer);
}

if (!ValidateToken(logonInfo)) {
spdlog::debug(" ValidateToken: failed");
return STATUS_ACCOUNT_RESTRICTION;
}
// assign output arguments

{
wchar_t computerName[MAX_COMPUTERNAME_LENGTH + 1] = {};
DWORD computerNameSize = ARRAYSIZE(computerName);
if (!GetComputerNameW(computerName, &computerNameSize)) {
spdlog::debug(" return STATUS_INTERNAL_ERROR (GetComputerNameW failed)");
return STATUS_INTERNAL_ERROR;
}

// assign "ProfileBuffer" output argument
*ProfileBufferSize = GetProfileBufferSize(computerName, *logonInfo);
FunctionTable.AllocateClientBuffer(ClientRequest, *ProfileBufferSize,
ProfileBuffer); // will update *ProfileBuffer

std::vector<BYTE> profileBuffer =
PrepareProfileBuffer(computerName, *logonInfo, (BYTE*)*ProfileBuffer);
FunctionTable.CopyToClientBuffer(ClientRequest, (ULONG)profileBuffer.size(), *ProfileBuffer,
profileBuffer.data()); // copy to caller process
}

{
// assign "LogonId" output argument
if (!AllocateLocallyUniqueId(LogonId)) {
spdlog::debug(" ERROR: AllocateLocallyUniqueId failed");
return STATUS_FAIL_FAST_EXCEPTION;
}
NTSTATUS status = FunctionTable.CreateLogonSession(LogonId);
if (status != STATUS_SUCCESS) {
spdlog::debug(" ERROR: CreateLogonSession failed with err: 0x%x", status);
return status;
}

spdlog::debug(" LogonId: High=0x%x , Low=0x%x", LogonId->HighPart, LogonId->LowPart);
}

*SubStatus = STATUS_SUCCESS; // reason for error

{
// Assign "TokenInformation" output argument
LSA_TOKEN_INFORMATION_V2* tokenInfo = nullptr;
NTSTATUS subStatus = 0;
NTSTATUS status = UserNameToToken(&logonInfo->UserName, &tokenInfo, &subStatus);
if (status != STATUS_SUCCESS) {
spdlog::debug("ERROR: UserNameToToken failed with err: 0x%x", status);
*SubStatus = subStatus;
return status;
}

*TokenInformationType = LsaTokenInformationV1;
*TokenInformation = tokenInfo;
}

{
// assign "AccountName" output argument
std::wstring username = ToWstring(logonInfo->UserName).c_str();
spdlog::debug(" AccountName: {}", utf8_encode(username));
*AccountName = CreateLsaUnicodeString(logonInfo->UserName.Buffer,
logonInfo->UserName.Length); // mandatory
}

if (AuthenticatingAuthority) {
// assign "AuthenticatingAuthority" output argument
*AuthenticatingAuthority =
(LSA_UNICODE_STRING*)FunctionTable.AllocateLsaHeap(sizeof(LSA_UNICODE_STRING));

if (logonInfo->LogonDomainName.Length > 0) {
std::wstring authority = ToWstring(logonInfo->LogonDomainName).c_str();
spdlog::debug(" AuthenticatingAuthority: {}", utf8_encode(authority));
*AuthenticatingAuthority = CreateLsaUnicodeString(logonInfo->LogonDomainName.Buffer,
logonInfo->LogonDomainName.Length);
} else {
spdlog::debug(" AuthenticatingAuthority: <empty>");
**AuthenticatingAuthority = {
.Length = 0,
.MaximumLength = 0,
.Buffer = nullptr,
};
}
}

spdlog::debug(" return STATUS_SUCCESS");
return STATUS_SUCCESS;
}

void LsaApLogonTerminated(_In_ LUID* LogonId) {
spdlog::debug("LsaApLogonTerminated");
spdlog::debug(" LogonId: High=0x%x , Low=0x%x", LogonId->HighPart, LogonId->LowPart);
spdlog::debug(" return");
}

SECPKG_FUNCTION_TABLE SecurityPackageFunctionTable = {
.InitializePackage = nullptr,
.LogonUser = LsaApLogonUser,
.CallPackage = nullptr,
.LogonTerminated = LsaApLogonTerminated,
.CallPackageUntrusted = nullptr,
.CallPackagePassthrough = nullptr,
.LogonUserEx = nullptr,
.LogonUserEx2 = nullptr,
.Initialize = SpInitialize,
.Shutdown = SpShutDown,
.GetInfo = SpGetInfo,
.AcceptCredentials = nullptr,
.AcquireCredentialsHandle = nullptr,
.QueryCredentialsAttributes = nullptr,
.FreeCredentialsHandle = nullptr,
.SaveCredentials = nullptr,
.GetCredentials = nullptr,
.DeleteCredentials = nullptr,
.InitLsaModeContext = nullptr,
.AcceptLsaModeContext = nullptr,
.DeleteContext = nullptr,
.ApplyControlToken = nullptr,
.GetUserInfo = nullptr,
.GetExtendedInformation = nullptr,
.QueryContextAttributes = nullptr,
.AddCredentialsW = nullptr,
.SetExtendedInformation = nullptr,
.SetContextAttributes = nullptr,
.SetCredentialsAttributes = nullptr,
.ChangeAccountPassword = nullptr,
.QueryMetaData = nullptr,
.ExchangeMetaData = nullptr,
.GetCredUIContext = nullptr,
.UpdateCredentials = nullptr,
.ValidateTargetInfo = nullptr,
.PostLogonUser = nullptr,
.GetRemoteCredGuardLogonBuffer = nullptr,
.GetRemoteCredGuardSupplementalCreds = nullptr,
.GetTbalSupplementalCreds = nullptr,
.LogonUserEx3 = nullptr,
.PreLogonUserSurrogate = nullptr,
.PostLogonUserSurrogate = nullptr,
.ExtractTargetInfo = nullptr,
};

/** LSA calls SpLsaModeInitialize() when loading SSP/AP DLLs. */
extern "C" NTSTATUS NTAPI SpLsaModeInitialize(_In_ ULONG LsaVersion, _Out_ ULONG* PackageVersion,
_Out_ SECPKG_FUNCTION_TABLE** ppTables,
_Out_ ULONG* pcTables) {
spdlog::debug("SpLsaModeInitialize");
spdlog::debug(" LsaVersion %u", LsaVersion);

*PackageVersion = SECPKG_INTERFACE_VERSION;
*ppTables = &SecurityPackageFunctionTable;
*pcTables = 1;

spdlog::debug(" return STATUS_SUCCESS");
return STATUS_SUCCESS;
}
Loading
Loading