From cf053e7a204bb5512ad6ef9991c2ae509613fb19 Mon Sep 17 00:00:00 2001 From: Jens Langhammer Date: Sat, 10 Jan 2026 22:38:18 +0000 Subject: [PATCH 1/8] init --- ee/wcp/CMakeLists.txt | 2 +- ee/wcp/ak_lsa/CMakeLists.txt | 25 +++ ee/wcp/ak_lsa/Main.cpp | 283 +++++++++++++++++++++++++++++++ ee/wcp/ak_lsa/PrepareProfile.cpp | 74 ++++++++ ee/wcp/ak_lsa/PrepareProfile.hpp | 11 ++ ee/wcp/ak_lsa/PrepareToken.cpp | 141 +++++++++++++++ ee/wcp/ak_lsa/PrepareToken.hpp | 12 ++ ee/wcp/ak_lsa/Utils.hpp | 70 ++++++++ 8 files changed, 617 insertions(+), 1 deletion(-) create mode 100644 ee/wcp/ak_lsa/CMakeLists.txt create mode 100644 ee/wcp/ak_lsa/Main.cpp create mode 100644 ee/wcp/ak_lsa/PrepareProfile.cpp create mode 100644 ee/wcp/ak_lsa/PrepareProfile.hpp create mode 100644 ee/wcp/ak_lsa/PrepareToken.cpp create mode 100644 ee/wcp/ak_lsa/PrepareToken.hpp create mode 100644 ee/wcp/ak_lsa/Utils.hpp diff --git a/ee/wcp/CMakeLists.txt b/ee/wcp/CMakeLists.txt index fb564ee8..20d3517c 100644 --- a/ee/wcp/CMakeLists.txt +++ b/ee/wcp/CMakeLists.txt @@ -271,7 +271,6 @@ find_package(OpenSSL REQUIRED) # Display the set and detected paths message(INFO " > OPENSSL_ROOT_DIR: ${OPENSSL_ROOT_DIR}") -message(INFO "") message(INFO " > OPENSSL_CRYPTO_LIBRARY: ${OPENSSL_CRYPTO_LIBRARY}") message(INFO " > OPENSSL_CRYPTO_LIBRARIES: ${OPENSSL_CRYPTO_LIBRARIES}") message(INFO " > OPENSSL_SSL_LIBRARY: ${OPENSSL_SSL_LIBRARY}") @@ -376,6 +375,7 @@ endif() # Set the project output directory for subprojects, such as `cefexe`, to copy their binaries to set(PROJECT_INSALL_DIR "${CMAKE_CURRENT_BINARY_DIR}/ak_cred_provider/Release") add_subdirectory(cefexe) +add_subdirectory(ak_lsa) # Copy cefexe executable and OpenSSL library file to the common build directory file(COPY "${OPENSSL_ROOT_DIR}/libcrypto-3-x64.dll" DESTINATION "${PROJECT_INSALL_DIR}") diff --git a/ee/wcp/ak_lsa/CMakeLists.txt b/ee/wcp/ak_lsa/CMakeLists.txt new file mode 100644 index 00000000..d30c2fd7 --- /dev/null +++ b/ee/wcp/ak_lsa/CMakeLists.txt @@ -0,0 +1,25 @@ +project(ak_lsa) + +set(CMAKE_CXX_STANDARD 20) + +set(SRCS + PrepareProfile.cpp + PrepareToken.cpp + Main.cpp +) + +add_library (${PROJECT_NAME} SHARED + ${SRCS} +) + +add_compile_definitions(SECURITY_WIN32) + +target_include_directories(${PROJECT_NAME} PUBLIC ..) + +target_link_libraries(${PROJECT_NAME} + ak_cred_provider +) + +# Copy binary and resource files to the target output directory. + +COPY_FILES("${PROJECT_NAME}" "${PROJECT_NAME}.dll" "${CMAKE_CURRENT_BINARY_DIR}/Release" "${PROJECT_INSALL_DIR}") diff --git a/ee/wcp/ak_lsa/Main.cpp b/ee/wcp/ak_lsa/Main.cpp new file mode 100644 index 00000000..ec946a95 --- /dev/null +++ b/ee/wcp/ak_lsa/Main.cpp @@ -0,0 +1,283 @@ +#include "PrepareToken.hpp" +#include "PrepareProfile.hpp" +#include "Utils.hpp" + +// 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) { + LogMessage("SpInitialize"); + + LogMessage(" PackageId: %u", PackageId); + LogMessage(" Version: %u", Parameters->Version); + { + ULONG state = Parameters->MachineState; + LogMessage(" MachineState:"); + if (state & SECPKG_STATE_ENCRYPTION_PERMITTED) { + state &= ~SECPKG_STATE_ENCRYPTION_PERMITTED; + LogMessage(" - ENCRYPTION_PERMITTED"); + } + if (state & SECPKG_STATE_STRONG_ENCRYPTION_PERMITTED) { + state &= ~SECPKG_STATE_STRONG_ENCRYPTION_PERMITTED; + LogMessage(" - STRONG_ENCRYPTION_PERMITTED"); + } + if (state & SECPKG_STATE_DOMAIN_CONTROLLER) { + state &= ~SECPKG_STATE_DOMAIN_CONTROLLER; + LogMessage(" - DOMAIN_CONTROLLER"); + } + if (state & SECPKG_STATE_WORKSTATION) { + state &= ~SECPKG_STATE_WORKSTATION; + LogMessage(" - WORKSTATION"); + } + if (state & SECPKG_STATE_STANDALONE) { + state &= ~SECPKG_STATE_STANDALONE; + LogMessage(" - STANDALONE"); + } + if (state) { + // print resudual flags not already covered + LogMessage(" * Unknown flags: 0x%X", state); + } + } + LogMessage(" SetupMode: %u", Parameters->SetupMode); + // parameters not logged + Parameters->DomainSid; + Parameters->DomainName; + Parameters->DnsDomainName; + Parameters->DomainGuid; + + FunctionTable = *functionTable; // copy function pointer table + + LogMessage(" return STATUS_SUCCESS"); + return STATUS_SUCCESS; +} + +NTSTATUS NTAPI SpShutDown() { + LogMessage("SpShutDown"); + LogMessage(" return STATUS_SUCCESS"); + return STATUS_SUCCESS; +} + +NTSTATUS NTAPI SpGetInfo(_Out_ SecPkgInfoW* PackageInfo) { + LogMessage("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"; + + LogMessage(" 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 +) { + LogMessage("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 + LogMessage(" LogonType: %i", LogonType); // Interactive=2, RemoteInteractive=10 + ClientBufferBase; + LogMessage(" ProtocolSubmitBuffer size: %i", SubmitBufferSize); + + // deliberately restrict supported logontypes + if ((LogonType != Interactive) && (LogonType != RemoteInteractive)) { + LogMessage(" 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)) { + LogMessage(" 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); + } + + // assign output arguments + + { + wchar_t computerName[MAX_COMPUTERNAME_LENGTH + 1] = {}; + DWORD computerNameSize = ARRAYSIZE(computerName); + if (!GetComputerNameW(computerName, &computerNameSize)) { + LogMessage(" 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 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)) { + LogMessage(" ERROR: AllocateLocallyUniqueId failed"); + return STATUS_FAIL_FAST_EXCEPTION; + } + NTSTATUS status = FunctionTable.CreateLogonSession(LogonId); + if (status != STATUS_SUCCESS) { + LogMessage(" ERROR: CreateLogonSession failed with err: 0x%x", status); + return status; + } + + LogMessage(" 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) { + LogMessage("ERROR: UserNameToToken failed with err: 0x%x", status); + *SubStatus = subStatus; + return status; + } + + *TokenInformationType = LsaTokenInformationV1; + *TokenInformation = tokenInfo; + } + + { + // assign "AccountName" output argument + LogMessage(" AccountName: %ls", ToWstring(logonInfo->UserName).c_str()); + *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) { + LogMessage(" AuthenticatingAuthority: %ls", ToWstring(logonInfo->LogonDomainName).c_str()); + *AuthenticatingAuthority = CreateLsaUnicodeString(logonInfo->LogonDomainName.Buffer, logonInfo->LogonDomainName.Length); + } else { + LogMessage(" AuthenticatingAuthority: "); + **AuthenticatingAuthority = { + .Length = 0, + .MaximumLength = 0, + .Buffer = nullptr, + }; + } + } + + LogMessage(" return STATUS_SUCCESS"); + return STATUS_SUCCESS; +} + +void LsaApLogonTerminated(_In_ LUID* LogonId) { + LogMessage("LsaApLogonTerminated"); + LogMessage(" LogonId: High=0x%x , Low=0x%x", LogonId->HighPart, LogonId->LowPart); + LogMessage(" 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 +) { + LogMessage("SpLsaModeInitialize"); + LogMessage(" LsaVersion %u", LsaVersion); + + *PackageVersion = SECPKG_INTERFACE_VERSION; + *ppTables = &SecurityPackageFunctionTable; + *pcTables = 1; + + LogMessage(" return STATUS_SUCCESS"); + return STATUS_SUCCESS; +} diff --git a/ee/wcp/ak_lsa/PrepareProfile.cpp b/ee/wcp/ak_lsa/PrepareProfile.cpp new file mode 100644 index 00000000..e0dfdbbf --- /dev/null +++ b/ee/wcp/ak_lsa/PrepareProfile.cpp @@ -0,0 +1,74 @@ +#include +#include +#include "PrepareProfile.hpp" +#include "Utils.hpp" + +static LARGE_INTEGER InfiniteFuture() { + LARGE_INTEGER val{ + .LowPart = 0xFFFFFFFF, // unsigned + .HighPart = 0x7FFFFFFF, // signed + }; + return val; +} + +static LARGE_INTEGER CurrentTime() { + FILETIME time{}; + GetSystemTimeAsFileTime(&time); + return LARGE_INTEGER { + .LowPart = time.dwLowDateTime, + .HighPart = (LONG)time.dwHighDateTime, + }; +} + +ULONG GetProfileBufferSize(const std::wstring& computername, const MSV1_0_INTERACTIVE_LOGON& logonInfo) { + return sizeof(MSV1_0_INTERACTIVE_PROFILE) + logonInfo.UserName.Length + (ULONG)(2 * computername.size()); +} + +std::vector PrepareProfileBuffer(const std::wstring& computername, const MSV1_0_INTERACTIVE_LOGON& logonInfo, BYTE* hostProfileAddress) { + std::vector profileBuffer(GetProfileBufferSize(computername, logonInfo), (BYTE)0); + auto* profile = (MSV1_0_INTERACTIVE_PROFILE*)profileBuffer.data(); + size_t offset = sizeof(MSV1_0_INTERACTIVE_PROFILE); // offset to string parameters + + profile->MessageType = MsV1_0InteractiveProfile; + profile->LogonCount = 0; // unknown + profile->BadPasswordCount = 0; + profile->LogonTime = CurrentTime(); + profile->LogoffTime = InfiniteFuture(); // logoff reminder + profile->KickOffTime = InfiniteFuture(); // forced logoff + profile->PasswordLastSet.QuadPart = 0; // 1. January 1601 + profile->PasswordCanChange = InfiniteFuture(); // password change reminder + profile->PasswordMustChange = InfiniteFuture(); // password change required + profile->LogonScript; // observed to be empty + profile->HomeDirectory; // observed to be empty + { + // set "UserName" + memcpy(/*dst*/profileBuffer.data() + offset, /*src*/logonInfo.UserName.Buffer, logonInfo.UserName.MaximumLength); + + LSA_UNICODE_STRING tmp = { + .Length = logonInfo.UserName.Length, + .MaximumLength = logonInfo.UserName.MaximumLength, + .Buffer = (wchar_t*)(hostProfileAddress + offset), + }; + profile->FullName = tmp; + + offset += profile->FullName.MaximumLength; + } + profile->ProfilePath; // observed to be empty + profile->HomeDirectoryDrive; // observed to be empty + { + // set "LogonServer" + memcpy(/*dst*/profileBuffer.data() + offset, /*src*/computername.data(), computername.size()); + + LSA_UNICODE_STRING tmp = { + .Length = (USHORT)(2 * computername.size()), + .MaximumLength = (USHORT)(2 * computername.size()), + .Buffer = (wchar_t*)(hostProfileAddress + offset), + }; + profile->LogonServer = tmp; + + offset += profile->LogonServer.MaximumLength; + } + profile->UserFlags = 0; + + return profileBuffer; +} diff --git a/ee/wcp/ak_lsa/PrepareProfile.hpp b/ee/wcp/ak_lsa/PrepareProfile.hpp new file mode 100644 index 00000000..efc6b8c3 --- /dev/null +++ b/ee/wcp/ak_lsa/PrepareProfile.hpp @@ -0,0 +1,11 @@ + +#pragma once +#include +#include +#include // for MSV1_0_INTERACTIVE_LOGON +#include // for PLSA_CLIENT_REQUEST + + +ULONG GetProfileBufferSize(const std::wstring& computername, const MSV1_0_INTERACTIVE_LOGON& logonInfo); + +std::vector PrepareProfileBuffer(const std::wstring& computername, const MSV1_0_INTERACTIVE_LOGON& logonInfo, BYTE* hostProfileAddress); diff --git a/ee/wcp/ak_lsa/PrepareToken.cpp b/ee/wcp/ak_lsa/PrepareToken.cpp new file mode 100644 index 00000000..cb0f9748 --- /dev/null +++ b/ee/wcp/ak_lsa/PrepareToken.cpp @@ -0,0 +1,141 @@ +#include "PrepareToken.hpp" +#include +#include "Utils.hpp" + +#pragma comment(lib, "Netapi32.lib") + + +static bool NameToSid(const wchar_t* username, PSID* userSid) { + DWORD lengthSid = 0; + SID_NAME_USE Use = {}; + DWORD referencedDomainNameLen = 0; + BOOL res = LookupAccountNameW(nullptr, username, nullptr, &lengthSid, nullptr, &referencedDomainNameLen, &Use); + + *userSid = (PSID)FunctionTable.AllocateLsaHeap(lengthSid); + wchar_t* referencedDomainName = (wchar_t*)FunctionTable.AllocateLsaHeap(sizeof(wchar_t) * referencedDomainNameLen); // throwaway string + res = LookupAccountNameW(nullptr, username, *userSid, &lengthSid, referencedDomainName, &referencedDomainNameLen, &Use); + if (!res) { + DWORD err = GetLastError(); + LogMessage(" LookupAccountNameW failed (err %u)", err); + return false; + } + + FunctionTable.FreeLsaHeap(referencedDomainName); + return true; +} + +static void GetPrimaryGroupSidFromUserSid(PSID userSID, PSID* primaryGroupSID) { + // duplicate the user sid + *primaryGroupSID = (PSID)FunctionTable.AllocateLsaHeap(GetLengthSid(userSID)); + CopySid(GetLengthSid(userSID), *primaryGroupSID, userSID); + + // replace the last subauthority by DOMAIN_GROUP_RID_USERS + // https://learn.microsoft.com/en-us/windows-server/identity/ad-ds/manage/understand-security-identifiers (last SubAuthority = RID + // https://learn.microsoft.com/nb-no/windows/win32/secauthz/well-known-sids + UCHAR SubAuthorityCount = *GetSidSubAuthorityCount(*primaryGroupSID); + *GetSidSubAuthority(*primaryGroupSID, SubAuthorityCount - 1) = DOMAIN_GROUP_RID_USERS; +} + +static bool GetGroups(const wchar_t* UserName, GROUP_USERS_INFO_1** lpGroupInfo, DWORD* pTotalEntries) { + DWORD NumberOfEntries = 0; + DWORD status = NetUserGetGroups(NULL, UserName, 1, (BYTE**)lpGroupInfo, MAX_PREFERRED_LENGTH, &NumberOfEntries, pTotalEntries); + if (status != NERR_Success) { + LogMessage("ERROR: NetUserGetGroups failed with error %u", status ); + return false; + } + return true; +} + +static bool GetLocalGroups(const wchar_t* UserName, GROUP_USERS_INFO_0** lpGroupInfo, DWORD* pTotalEntries) { + DWORD NumberOfEntries = 0; + DWORD status = NetUserGetLocalGroups(NULL, UserName, 0, 0, (BYTE**)lpGroupInfo, MAX_PREFERRED_LENGTH, &NumberOfEntries, pTotalEntries); + if (status != NERR_Success) { + LogMessage("ERROR: NetUserGetLocalGroups failed with error %u", status); + return false; + } + return true; +} + + +NTSTATUS UserNameToToken( + __in LSA_UNICODE_STRING* AccountName, + __out LSA_TOKEN_INFORMATION_V1** Token, + __out PNTSTATUS SubStatus +) { + const LARGE_INTEGER Forever { + .LowPart = 0xFFFFFFFF, // unsigned + .HighPart = 0x7FFFFFFF, // signed + }; + + // convert username to zero-terminated string + std::wstring username = ToWstring(*AccountName); + + auto* token = (LSA_TOKEN_INFORMATION_V1*)FunctionTable.AllocateLsaHeap(sizeof(LSA_TOKEN_INFORMATION_V1)); + + token->ExpirationTime = Forever; + + PSID userSid = nullptr; + { + // configure "User" + if (!NameToSid(username.c_str(), &userSid)) + return STATUS_FAIL_FAST_EXCEPTION; + + LogMessage(" User.User: %ls", username.c_str()); + token->User.User = { + .Sid = userSid, + .Attributes = 0, + }; + } + + { + // configure "Groups" + DWORD NumberOfGroups = 0; + GROUP_USERS_INFO_1* pGroupInfo = nullptr; + if (!GetGroups(username.c_str(), &pGroupInfo, &NumberOfGroups)) { + return STATUS_FAIL_FAST_EXCEPTION; + } + LogMessage(" NumberOfGroups: %u", NumberOfGroups); + + DWORD NumberOfLocalGroups = 0; + GROUP_USERS_INFO_0* pLocalGroupInfo = nullptr; + if (!GetLocalGroups(username.c_str(), &pLocalGroupInfo, &NumberOfLocalGroups)) { + return STATUS_FAIL_FAST_EXCEPTION; + } + LogMessage(" NumberOfLocalGroups: %u", NumberOfLocalGroups); + + TOKEN_GROUPS* tokenGroups = (TOKEN_GROUPS*)FunctionTable.AllocateLsaHeap(FIELD_OFFSET(TOKEN_GROUPS, Groups[NumberOfGroups + NumberOfLocalGroups])); + tokenGroups->GroupCount = NumberOfGroups + NumberOfLocalGroups; + for (size_t i = 0; i < NumberOfGroups; i++) { + NameToSid(pGroupInfo[i].grui1_name, &tokenGroups->Groups[i].Sid); + + tokenGroups->Groups[i].Attributes = pGroupInfo[i].grui1_attributes; + } + for (size_t i = 0; i < NumberOfLocalGroups; i++) { + NameToSid(pLocalGroupInfo[i].grui0_name, &tokenGroups->Groups[NumberOfGroups + i].Sid); + + // get the attributes of group since pLocalGroupInfo doesn't contain attributes + if (*GetSidSubAuthority(tokenGroups->Groups[NumberOfGroups + i].Sid, 0) != SECURITY_BUILTIN_DOMAIN_RID) + tokenGroups->Groups[NumberOfGroups + i].Attributes = SE_GROUP_ENABLED | SE_GROUP_ENABLED_BY_DEFAULT; + else + tokenGroups->Groups[NumberOfGroups + i].Attributes = 0; + } + + token->Groups = tokenGroups; + } + + GetPrimaryGroupSidFromUserSid(userSid, &token->PrimaryGroup.PrimaryGroup); + + // TOKEN_PRIVILEGES Privileges not currently configured + token->Privileges = nullptr; + + // PSID Owner not currently configured + token->Owner.Owner = (PSID)nullptr; + + // PACL DefaultDacl not currently configured + token->DefaultDacl.DefaultDacl = nullptr; + + // assign outputs + *Token = token; + *SubStatus = STATUS_SUCCESS; + return STATUS_SUCCESS; +} diff --git a/ee/wcp/ak_lsa/PrepareToken.hpp b/ee/wcp/ak_lsa/PrepareToken.hpp new file mode 100644 index 00000000..a1a23f26 --- /dev/null +++ b/ee/wcp/ak_lsa/PrepareToken.hpp @@ -0,0 +1,12 @@ +#pragma once +#include +#include +#include +#include +#include // for LSA_STRING +#include // for LSA_DISPATCH_TABLE + + +NTSTATUS UserNameToToken(__in LSA_UNICODE_STRING* AccountName, + __out LSA_TOKEN_INFORMATION_V1** Token, + __out PNTSTATUS SubStatus); diff --git a/ee/wcp/ak_lsa/Utils.hpp b/ee/wcp/ak_lsa/Utils.hpp new file mode 100644 index 00000000..fe1fd624 --- /dev/null +++ b/ee/wcp/ak_lsa/Utils.hpp @@ -0,0 +1,70 @@ +#pragma once +#include +#include + +extern LSA_SECPKG_FUNCTION_TABLE FunctionTable; + + +inline void LogMessage(const char* message, ...) { + // append to log file + FILE* file = nullptr; + fopen_s(&file, "C:\\ak_lsa.txt", "a+"); + if (!file) + return; + { + // print variadic message + va_list args; + va_start(args, message); + _vfprintf_l(file, message, NULL, args); + va_end(args); + } + fprintf(file, "\n"); + fclose(file); +} + +/** Allocate and create a new LSA_STRING object. + Assumes that "FunctionTable" is initialized. */ +inline LSA_STRING* CreateLsaString(const std::string& msg) { + auto msg_len = (USHORT)msg.size(); // exclude null-termination + + assert(FunctionTable.AllocateLsaHeap); + auto* obj = (LSA_STRING*)FunctionTable.AllocateLsaHeap(sizeof(LSA_STRING)); + obj->Buffer = (char*)FunctionTable.AllocateLsaHeap(msg_len); + memcpy(/*dst*/obj->Buffer, /*src*/msg.c_str(), msg_len); + obj->Length = msg_len; + obj->MaximumLength = msg_len; + return obj; +} + +/** Allocate and create a new LSA_UNICODE_STRING object. + Assumes that "FunctionTable" is initialized. */ +inline LSA_UNICODE_STRING* CreateLsaUnicodeString(const wchar_t* msg, USHORT msg_len_bytes) { + assert(FunctionTable.AllocateLsaHeap); + auto* obj = (LSA_UNICODE_STRING*)FunctionTable.AllocateLsaHeap(sizeof(LSA_UNICODE_STRING)); + obj->Buffer = (wchar_t*)FunctionTable.AllocateLsaHeap(msg_len_bytes); + memcpy(/*dst*/obj->Buffer, /*src*/msg, msg_len_bytes); + obj->Length = msg_len_bytes; + obj->MaximumLength = msg_len_bytes; + return obj; +} + +inline LSA_UNICODE_STRING* CreateLsaUnicodeString(const std::wstring& msg) { + return CreateLsaUnicodeString(msg.c_str(), (USHORT)msg.size()*sizeof(wchar_t)); +} + +inline std::wstring ToWstring(LSA_UNICODE_STRING& lsa_str) { + if (lsa_str.Length == 0) + return L""; + return std::wstring(lsa_str.Buffer, lsa_str.Length/2); +} + +inline void AssignLsaUnicodeString(const LSA_UNICODE_STRING& source, LSA_UNICODE_STRING& dest) { + assert(FunctionTable.AllocateLsaHeap); + if (dest.Buffer) + FunctionTable.FreeLsaHeap(dest.Buffer); + + dest.Buffer = (wchar_t*)FunctionTable.AllocateLsaHeap(source.Length); + memcpy(/*dst*/dest.Buffer, /*src*/source.Buffer, source.Length); + dest.Length = source.Length; + dest.MaximumLength = source.Length; +} From 2422491afeb92edec84865ce40cb96a7fe0c8147 Mon Sep 17 00:00:00 2001 From: Jens Langhammer Date: Sat, 10 Jan 2026 23:07:31 +0000 Subject: [PATCH 2/8] format and fix build --- ee/wcp/Makefile | 15 +- ee/wcp/ak_cred_provider/CMakeLists.txt | 17 +- ee/wcp/ak_lsa/CMakeLists.txt | 18 +- ee/wcp/ak_lsa/Main.cpp | 393 ++++++++++++------------- ee/wcp/ak_lsa/PrepareProfile.cpp | 113 +++---- ee/wcp/ak_lsa/PrepareProfile.hpp | 12 +- ee/wcp/ak_lsa/PrepareToken.cpp | 226 +++++++------- ee/wcp/ak_lsa/PrepareToken.hpp | 4 +- ee/wcp/ak_lsa/Utils.hpp | 78 +++-- 9 files changed, 443 insertions(+), 433 deletions(-) diff --git a/ee/wcp/Makefile b/ee/wcp/Makefile index a7f280ab..bdc13fcd 100644 --- a/ee/wcp/Makefile +++ b/ee/wcp/Makefile @@ -5,6 +5,7 @@ include ../../common.mk TARGET := wcp CLANG_FORMAT := "C:\Program Files\LLVM\bin\clang-format.exe" +FORMAT_FIND_ARGS := -iname '*.h' -o -iname '*.cpp' -o -iname '*.hpp' .PHONY: bump bump: @@ -16,15 +17,17 @@ clean: .PHONY: lint lint: - find "${PWD}/ak_cred_provider" -iname '*.h' -o -iname '*.cpp' | xargs ${CLANG_FORMAT} --dry-run --Werror --verbose -i - find "${PWD}/cefexe" -iname '*.h' -o -iname '*.cpp' | xargs ${CLANG_FORMAT} --dry-run --Werror --verbose -i - find "${PWD}/cefsimple" -iname '*.h' -o -iname '*.cpp' | xargs ${CLANG_FORMAT} --dry-run --Werror --verbose -i + find "${PWD}/ak_cred_provider" ${FORMAT_FIND_ARGS} | xargs ${CLANG_FORMAT} --dry-run --Werror --verbose -i + find "${PWD}/ak_lsa" ${FORMAT_FIND_ARGS} | xargs ${CLANG_FORMAT} --dry-run --Werror --verbose -i + find "${PWD}/cefexe" ${FORMAT_FIND_ARGS} | xargs ${CLANG_FORMAT} --dry-run --Werror --verbose -i + find "${PWD}/cefsimple" ${FORMAT_FIND_ARGS} | xargs ${CLANG_FORMAT} --dry-run --Werror --verbose -i .PHONY: lint-fix lint-fix: - find "${PWD}/ak_cred_provider" -iname '*.h' -o -iname '*.cpp' | xargs ${CLANG_FORMAT} --verbose -i - find "${PWD}/cefexe" -iname '*.h' -o -iname '*.cpp' | xargs ${CLANG_FORMAT} --verbose -i - find "${PWD}/cefsimple" -iname '*.h' -o -iname '*.cpp' | xargs ${CLANG_FORMAT} --verbose -i + find "${PWD}/ak_cred_provider" ${FORMAT_FIND_ARGS} | xargs ${CLANG_FORMAT} --verbose -i + find "${PWD}/ak_lsa" ${FORMAT_FIND_ARGS} | xargs ${CLANG_FORMAT} --verbose -i + find "${PWD}/cefexe" ${FORMAT_FIND_ARGS} | xargs ${CLANG_FORMAT} --verbose -i + find "${PWD}/cefsimple" ${FORMAT_FIND_ARGS} | xargs ${CLANG_FORMAT} --verbose -i .PHONY: build build: diff --git a/ee/wcp/ak_cred_provider/CMakeLists.txt b/ee/wcp/ak_cred_provider/CMakeLists.txt index 7c010e5c..1894a855 100644 --- a/ee/wcp/ak_cred_provider/CMakeLists.txt +++ b/ee/wcp/ak_cred_provider/CMakeLists.txt @@ -12,7 +12,7 @@ project( # TODO: Add tests and install targets if needed. -set(CMAKE_CXX_STANDARD 11) +set(CMAKE_CXX_STANDARD 20) # Add source to this project's executable. set(SRCS @@ -42,20 +42,17 @@ include_directories( ../ ) -if (CMAKE_VERSION VERSION_GREATER 3.12) - set_property(TARGET ${PROJECT_NAME} PROPERTY CXX_STANDARD 20) -endif() if (MSVC) - target_compile_definitions(${PROJECT_NAME} PUBLIC UNICODE _UNICODE) - target_sources(${PROJECT_NAME} PRIVATE - ${CMAKE_CURRENT_SOURCE_DIR}/${PROJECT_NAME}.def + target_compile_definitions(${PROJECT_NAME} PUBLIC UNICODE _UNICODE) + target_sources(${PROJECT_NAME} PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/${PROJECT_NAME}.def ) endif() target_link_libraries(${PROJECT_NAME} PUBLIC - ${CREDUI_LIB_PATH} - ${SECUR32_LIB_PATH} - ${SHLWAPI_LIB_PATH} + ${CREDUI_LIB_PATH} + ${SECUR32_LIB_PATH} + ${SHLWAPI_LIB_PATH} cefsimple authentik_sys_bridge spdlog diff --git a/ee/wcp/ak_lsa/CMakeLists.txt b/ee/wcp/ak_lsa/CMakeLists.txt index d30c2fd7..5378ec04 100644 --- a/ee/wcp/ak_lsa/CMakeLists.txt +++ b/ee/wcp/ak_lsa/CMakeLists.txt @@ -1,5 +1,6 @@ project(ak_lsa) +set(CMAKE_BUILD_TYPE Release) set(CMAKE_CXX_STANDARD 20) set(SRCS @@ -8,16 +9,23 @@ set(SRCS Main.cpp ) -add_library (${PROJECT_NAME} SHARED +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) -add_compile_definitions(SECURITY_WIN32) - -target_include_directories(${PROJECT_NAME} PUBLIC ..) +include_directories(${PROJECT_NAME} PUBLIC ..) target_link_libraries(${PROJECT_NAME} - ak_cred_provider + ${CREDUI_LIB_PATH} + ${SECUR32_LIB_PATH} + ${SHLWAPI_LIB_PATH} + authentik_sys_bridge + spdlog ) # Copy binary and resource files to the target output directory. diff --git a/ee/wcp/ak_lsa/Main.cpp b/ee/wcp/ak_lsa/Main.cpp index ec946a95..ebd4a9a8 100644 --- a/ee/wcp/ak_lsa/Main.cpp +++ b/ee/wcp/ak_lsa/Main.cpp @@ -7,214 +7,213 @@ LSA_SECPKG_FUNCTION_TABLE FunctionTable; - -NTSTATUS NTAPI SpInitialize(_In_ ULONG_PTR PackageId, _In_ SECPKG_PARAMETERS* Parameters, _In_ LSA_SECPKG_FUNCTION_TABLE* functionTable) { - LogMessage("SpInitialize"); - - LogMessage(" PackageId: %u", PackageId); - LogMessage(" Version: %u", Parameters->Version); - { - ULONG state = Parameters->MachineState; - LogMessage(" MachineState:"); - if (state & SECPKG_STATE_ENCRYPTION_PERMITTED) { - state &= ~SECPKG_STATE_ENCRYPTION_PERMITTED; - LogMessage(" - ENCRYPTION_PERMITTED"); - } - if (state & SECPKG_STATE_STRONG_ENCRYPTION_PERMITTED) { - state &= ~SECPKG_STATE_STRONG_ENCRYPTION_PERMITTED; - LogMessage(" - STRONG_ENCRYPTION_PERMITTED"); - } - if (state & SECPKG_STATE_DOMAIN_CONTROLLER) { - state &= ~SECPKG_STATE_DOMAIN_CONTROLLER; - LogMessage(" - DOMAIN_CONTROLLER"); - } - if (state & SECPKG_STATE_WORKSTATION) { - state &= ~SECPKG_STATE_WORKSTATION; - LogMessage(" - WORKSTATION"); - } - if (state & SECPKG_STATE_STANDALONE) { - state &= ~SECPKG_STATE_STANDALONE; - LogMessage(" - STANDALONE"); - } - if (state) { - // print resudual flags not already covered - LogMessage(" * Unknown flags: 0x%X", state); - } +NTSTATUS NTAPI SpInitialize(_In_ ULONG_PTR PackageId, _In_ SECPKG_PARAMETERS* Parameters, + _In_ LSA_SECPKG_FUNCTION_TABLE* functionTable) { + LogMessage("SpInitialize"); + + LogMessage(" PackageId: %u", PackageId); + LogMessage(" Version: %u", Parameters->Version); + { + ULONG state = Parameters->MachineState; + LogMessage(" MachineState:"); + if (state & SECPKG_STATE_ENCRYPTION_PERMITTED) { + state &= ~SECPKG_STATE_ENCRYPTION_PERMITTED; + LogMessage(" - ENCRYPTION_PERMITTED"); } - LogMessage(" SetupMode: %u", Parameters->SetupMode); - // parameters not logged - Parameters->DomainSid; - Parameters->DomainName; - Parameters->DnsDomainName; - Parameters->DomainGuid; - - FunctionTable = *functionTable; // copy function pointer table - - LogMessage(" return STATUS_SUCCESS"); - return STATUS_SUCCESS; + if (state & SECPKG_STATE_STRONG_ENCRYPTION_PERMITTED) { + state &= ~SECPKG_STATE_STRONG_ENCRYPTION_PERMITTED; + LogMessage(" - STRONG_ENCRYPTION_PERMITTED"); + } + if (state & SECPKG_STATE_DOMAIN_CONTROLLER) { + state &= ~SECPKG_STATE_DOMAIN_CONTROLLER; + LogMessage(" - DOMAIN_CONTROLLER"); + } + if (state & SECPKG_STATE_WORKSTATION) { + state &= ~SECPKG_STATE_WORKSTATION; + LogMessage(" - WORKSTATION"); + } + if (state & SECPKG_STATE_STANDALONE) { + state &= ~SECPKG_STATE_STANDALONE; + LogMessage(" - STANDALONE"); + } + if (state) { + // print resudual flags not already covered + LogMessage(" * Unknown flags: 0x%X", state); + } + } + LogMessage(" SetupMode: %u", Parameters->SetupMode); + // parameters not logged + Parameters->DomainSid; + Parameters->DomainName; + Parameters->DnsDomainName; + Parameters->DomainGuid; + + FunctionTable = *functionTable; // copy function pointer table + + LogMessage(" return STATUS_SUCCESS"); + return STATUS_SUCCESS; } NTSTATUS NTAPI SpShutDown() { - LogMessage("SpShutDown"); - LogMessage(" return STATUS_SUCCESS"); - return STATUS_SUCCESS; + LogMessage("SpShutDown"); + LogMessage(" return STATUS_SUCCESS"); + return STATUS_SUCCESS; } NTSTATUS NTAPI SpGetInfo(_Out_ SecPkgInfoW* PackageInfo) { - LogMessage("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"; - - LogMessage(" return STATUS_SUCCESS"); - return STATUS_SUCCESS; + LogMessage("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"; + + LogMessage(" 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 -) { - LogMessage("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 - LogMessage(" LogonType: %i", LogonType); // Interactive=2, RemoteInteractive=10 - ClientBufferBase; - LogMessage(" ProtocolSubmitBuffer size: %i", SubmitBufferSize); - - // deliberately restrict supported logontypes - if ((LogonType != Interactive) && (LogonType != RemoteInteractive)) { - LogMessage(" return STATUS_NOT_IMPLEMENTED (unsupported LogonType)"); - return STATUS_NOT_IMPLEMENTED; +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) { + LogMessage("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 + LogMessage(" LogonType: %i", LogonType); // Interactive=2, RemoteInteractive=10 + ClientBufferBase; + LogMessage(" ProtocolSubmitBuffer size: %i", SubmitBufferSize); + + // deliberately restrict supported logontypes + if ((LogonType != Interactive) && (LogonType != RemoteInteractive)) { + LogMessage(" 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)) { + LogMessage(" ERROR: SubmitBufferSize too small"); + return STATUS_INVALID_PARAMETER; } - // authentication credentials passed by client - auto* logonInfo = (MSV1_0_INTERACTIVE_LOGON*)ProtocolSubmitBuffer; - { - if (SubmitBufferSize < sizeof(MSV1_0_INTERACTIVE_LOGON)) { - LogMessage(" 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); - } - - // assign output arguments - - { - wchar_t computerName[MAX_COMPUTERNAME_LENGTH + 1] = {}; - DWORD computerNameSize = ARRAYSIZE(computerName); - if (!GetComputerNameW(computerName, &computerNameSize)) { - LogMessage(" 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 profileBuffer = PrepareProfileBuffer(computerName, *logonInfo, (BYTE*)*ProfileBuffer); - FunctionTable.CopyToClientBuffer(ClientRequest, (ULONG)profileBuffer.size(), *ProfileBuffer, profileBuffer.data()); // copy to caller process + // 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); + } + + // assign output arguments + + { + wchar_t computerName[MAX_COMPUTERNAME_LENGTH + 1] = {}; + DWORD computerNameSize = ARRAYSIZE(computerName); + if (!GetComputerNameW(computerName, &computerNameSize)) { + LogMessage(" return STATUS_INTERNAL_ERROR (GetComputerNameW failed)"); + return STATUS_INTERNAL_ERROR; } - { - // assign "LogonId" output argument - if (!AllocateLocallyUniqueId(LogonId)) { - LogMessage(" ERROR: AllocateLocallyUniqueId failed"); - return STATUS_FAIL_FAST_EXCEPTION; - } - NTSTATUS status = FunctionTable.CreateLogonSession(LogonId); - if (status != STATUS_SUCCESS) { - LogMessage(" ERROR: CreateLogonSession failed with err: 0x%x", status); - return status; - } - - LogMessage(" LogonId: High=0x%x , Low=0x%x", LogonId->HighPart, LogonId->LowPart); + // assign "ProfileBuffer" output argument + *ProfileBufferSize = GetProfileBufferSize(computerName, *logonInfo); + FunctionTable.AllocateClientBuffer(ClientRequest, *ProfileBufferSize, + ProfileBuffer); // will update *ProfileBuffer + + std::vector 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)) { + LogMessage(" ERROR: AllocateLocallyUniqueId failed"); + return STATUS_FAIL_FAST_EXCEPTION; } - - *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) { - LogMessage("ERROR: UserNameToToken failed with err: 0x%x", status); - *SubStatus = subStatus; - return status; - } - - *TokenInformationType = LsaTokenInformationV1; - *TokenInformation = tokenInfo; + NTSTATUS status = FunctionTable.CreateLogonSession(LogonId); + if (status != STATUS_SUCCESS) { + LogMessage(" ERROR: CreateLogonSession failed with err: 0x%x", status); + return status; } - { - // assign "AccountName" output argument - LogMessage(" AccountName: %ls", ToWstring(logonInfo->UserName).c_str()); - *AccountName = CreateLsaUnicodeString(logonInfo->UserName.Buffer, logonInfo->UserName.Length); // mandatory + LogMessage(" 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) { + LogMessage("ERROR: UserNameToToken failed with err: 0x%x", status); + *SubStatus = subStatus; + return status; } - if (AuthenticatingAuthority) { - // assign "AuthenticatingAuthority" output argument - *AuthenticatingAuthority = (LSA_UNICODE_STRING*)FunctionTable.AllocateLsaHeap(sizeof(LSA_UNICODE_STRING)); - - if (logonInfo->LogonDomainName.Length > 0) { - LogMessage(" AuthenticatingAuthority: %ls", ToWstring(logonInfo->LogonDomainName).c_str()); - *AuthenticatingAuthority = CreateLsaUnicodeString(logonInfo->LogonDomainName.Buffer, logonInfo->LogonDomainName.Length); - } else { - LogMessage(" AuthenticatingAuthority: "); - **AuthenticatingAuthority = { - .Length = 0, - .MaximumLength = 0, - .Buffer = nullptr, - }; - } + *TokenInformationType = LsaTokenInformationV1; + *TokenInformation = tokenInfo; + } + + { + // assign "AccountName" output argument + LogMessage(" AccountName: %ls", ToWstring(logonInfo->UserName).c_str()); + *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) { + LogMessage(" AuthenticatingAuthority: %ls", ToWstring(logonInfo->LogonDomainName).c_str()); + *AuthenticatingAuthority = CreateLsaUnicodeString(logonInfo->LogonDomainName.Buffer, + logonInfo->LogonDomainName.Length); + } else { + LogMessage(" AuthenticatingAuthority: "); + **AuthenticatingAuthority = { + .Length = 0, + .MaximumLength = 0, + .Buffer = nullptr, + }; } + } - LogMessage(" return STATUS_SUCCESS"); - return STATUS_SUCCESS; + LogMessage(" return STATUS_SUCCESS"); + return STATUS_SUCCESS; } void LsaApLogonTerminated(_In_ LUID* LogonId) { - LogMessage("LsaApLogonTerminated"); - LogMessage(" LogonId: High=0x%x , Low=0x%x", LogonId->HighPart, LogonId->LowPart); - LogMessage(" return"); + LogMessage("LsaApLogonTerminated"); + LogMessage(" LogonId: High=0x%x , Low=0x%x", LogonId->HighPart, LogonId->LowPart); + LogMessage(" return"); } SECPKG_FUNCTION_TABLE SecurityPackageFunctionTable = { @@ -264,20 +263,16 @@ SECPKG_FUNCTION_TABLE SecurityPackageFunctionTable = { }; /** 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 -) { - LogMessage("SpLsaModeInitialize"); - LogMessage(" LsaVersion %u", LsaVersion); - - *PackageVersion = SECPKG_INTERFACE_VERSION; - *ppTables = &SecurityPackageFunctionTable; - *pcTables = 1; - - LogMessage(" return STATUS_SUCCESS"); - return STATUS_SUCCESS; +extern "C" NTSTATUS NTAPI SpLsaModeInitialize(_In_ ULONG LsaVersion, _Out_ ULONG* PackageVersion, + _Out_ SECPKG_FUNCTION_TABLE** ppTables, + _Out_ ULONG* pcTables) { + LogMessage("SpLsaModeInitialize"); + LogMessage(" LsaVersion %u", LsaVersion); + + *PackageVersion = SECPKG_INTERFACE_VERSION; + *ppTables = &SecurityPackageFunctionTable; + *pcTables = 1; + + LogMessage(" return STATUS_SUCCESS"); + return STATUS_SUCCESS; } diff --git a/ee/wcp/ak_lsa/PrepareProfile.cpp b/ee/wcp/ak_lsa/PrepareProfile.cpp index e0dfdbbf..68f4bf3f 100644 --- a/ee/wcp/ak_lsa/PrepareProfile.cpp +++ b/ee/wcp/ak_lsa/PrepareProfile.cpp @@ -4,71 +4,76 @@ #include "Utils.hpp" static LARGE_INTEGER InfiniteFuture() { - LARGE_INTEGER val{ - .LowPart = 0xFFFFFFFF, // unsigned - .HighPart = 0x7FFFFFFF, // signed - }; - return val; + LARGE_INTEGER val{ + .LowPart = 0xFFFFFFFF, // unsigned + .HighPart = 0x7FFFFFFF, // signed + }; + return val; } static LARGE_INTEGER CurrentTime() { - FILETIME time{}; - GetSystemTimeAsFileTime(&time); - return LARGE_INTEGER { - .LowPart = time.dwLowDateTime, - .HighPart = (LONG)time.dwHighDateTime, - }; + FILETIME time{}; + GetSystemTimeAsFileTime(&time); + return LARGE_INTEGER{ + .LowPart = time.dwLowDateTime, + .HighPart = (LONG)time.dwHighDateTime, + }; } -ULONG GetProfileBufferSize(const std::wstring& computername, const MSV1_0_INTERACTIVE_LOGON& logonInfo) { - return sizeof(MSV1_0_INTERACTIVE_PROFILE) + logonInfo.UserName.Length + (ULONG)(2 * computername.size()); +ULONG GetProfileBufferSize(const std::wstring& computername, + const MSV1_0_INTERACTIVE_LOGON& logonInfo) { + return sizeof(MSV1_0_INTERACTIVE_PROFILE) + logonInfo.UserName.Length + + (ULONG)(2 * computername.size()); } -std::vector PrepareProfileBuffer(const std::wstring& computername, const MSV1_0_INTERACTIVE_LOGON& logonInfo, BYTE* hostProfileAddress) { - std::vector profileBuffer(GetProfileBufferSize(computername, logonInfo), (BYTE)0); - auto* profile = (MSV1_0_INTERACTIVE_PROFILE*)profileBuffer.data(); - size_t offset = sizeof(MSV1_0_INTERACTIVE_PROFILE); // offset to string parameters +std::vector PrepareProfileBuffer(const std::wstring& computername, + const MSV1_0_INTERACTIVE_LOGON& logonInfo, + BYTE* hostProfileAddress) { + std::vector profileBuffer(GetProfileBufferSize(computername, logonInfo), (BYTE)0); + auto* profile = (MSV1_0_INTERACTIVE_PROFILE*)profileBuffer.data(); + size_t offset = sizeof(MSV1_0_INTERACTIVE_PROFILE); // offset to string parameters - profile->MessageType = MsV1_0InteractiveProfile; - profile->LogonCount = 0; // unknown - profile->BadPasswordCount = 0; - profile->LogonTime = CurrentTime(); - profile->LogoffTime = InfiniteFuture(); // logoff reminder - profile->KickOffTime = InfiniteFuture(); // forced logoff - profile->PasswordLastSet.QuadPart = 0; // 1. January 1601 - profile->PasswordCanChange = InfiniteFuture(); // password change reminder - profile->PasswordMustChange = InfiniteFuture(); // password change required - profile->LogonScript; // observed to be empty - profile->HomeDirectory; // observed to be empty - { - // set "UserName" - memcpy(/*dst*/profileBuffer.data() + offset, /*src*/logonInfo.UserName.Buffer, logonInfo.UserName.MaximumLength); + profile->MessageType = MsV1_0InteractiveProfile; + profile->LogonCount = 0; // unknown + profile->BadPasswordCount = 0; + profile->LogonTime = CurrentTime(); + profile->LogoffTime = InfiniteFuture(); // logoff reminder + profile->KickOffTime = InfiniteFuture(); // forced logoff + profile->PasswordLastSet.QuadPart = 0; // 1. January 1601 + profile->PasswordCanChange = InfiniteFuture(); // password change reminder + profile->PasswordMustChange = InfiniteFuture(); // password change required + profile->LogonScript; // observed to be empty + profile->HomeDirectory; // observed to be empty + { + // set "UserName" + memcpy(/*dst*/ profileBuffer.data() + offset, /*src*/ logonInfo.UserName.Buffer, + logonInfo.UserName.MaximumLength); - LSA_UNICODE_STRING tmp = { - .Length = logonInfo.UserName.Length, - .MaximumLength = logonInfo.UserName.MaximumLength, - .Buffer = (wchar_t*)(hostProfileAddress + offset), - }; - profile->FullName = tmp; + LSA_UNICODE_STRING tmp = { + .Length = logonInfo.UserName.Length, + .MaximumLength = logonInfo.UserName.MaximumLength, + .Buffer = (wchar_t*)(hostProfileAddress + offset), + }; + profile->FullName = tmp; - offset += profile->FullName.MaximumLength; - } - profile->ProfilePath; // observed to be empty - profile->HomeDirectoryDrive; // observed to be empty - { - // set "LogonServer" - memcpy(/*dst*/profileBuffer.data() + offset, /*src*/computername.data(), computername.size()); + offset += profile->FullName.MaximumLength; + } + profile->ProfilePath; // observed to be empty + profile->HomeDirectoryDrive; // observed to be empty + { + // set "LogonServer" + memcpy(/*dst*/ profileBuffer.data() + offset, /*src*/ computername.data(), computername.size()); - LSA_UNICODE_STRING tmp = { - .Length = (USHORT)(2 * computername.size()), - .MaximumLength = (USHORT)(2 * computername.size()), - .Buffer = (wchar_t*)(hostProfileAddress + offset), - }; - profile->LogonServer = tmp; + LSA_UNICODE_STRING tmp = { + .Length = (USHORT)(2 * computername.size()), + .MaximumLength = (USHORT)(2 * computername.size()), + .Buffer = (wchar_t*)(hostProfileAddress + offset), + }; + profile->LogonServer = tmp; - offset += profile->LogonServer.MaximumLength; - } - profile->UserFlags = 0; + offset += profile->LogonServer.MaximumLength; + } + profile->UserFlags = 0; - return profileBuffer; + return profileBuffer; } diff --git a/ee/wcp/ak_lsa/PrepareProfile.hpp b/ee/wcp/ak_lsa/PrepareProfile.hpp index efc6b8c3..9ab73cca 100644 --- a/ee/wcp/ak_lsa/PrepareProfile.hpp +++ b/ee/wcp/ak_lsa/PrepareProfile.hpp @@ -2,10 +2,12 @@ #pragma once #include #include -#include // for MSV1_0_INTERACTIVE_LOGON -#include // for PLSA_CLIENT_REQUEST +#include // for MSV1_0_INTERACTIVE_LOGON +#include // for PLSA_CLIENT_REQUEST +ULONG GetProfileBufferSize(const std::wstring& computername, + const MSV1_0_INTERACTIVE_LOGON& logonInfo); -ULONG GetProfileBufferSize(const std::wstring& computername, const MSV1_0_INTERACTIVE_LOGON& logonInfo); - -std::vector PrepareProfileBuffer(const std::wstring& computername, const MSV1_0_INTERACTIVE_LOGON& logonInfo, BYTE* hostProfileAddress); +std::vector PrepareProfileBuffer(const std::wstring& computername, + const MSV1_0_INTERACTIVE_LOGON& logonInfo, + BYTE* hostProfileAddress); diff --git a/ee/wcp/ak_lsa/PrepareToken.cpp b/ee/wcp/ak_lsa/PrepareToken.cpp index cb0f9748..9f81e263 100644 --- a/ee/wcp/ak_lsa/PrepareToken.cpp +++ b/ee/wcp/ak_lsa/PrepareToken.cpp @@ -4,138 +4,144 @@ #pragma comment(lib, "Netapi32.lib") - static bool NameToSid(const wchar_t* username, PSID* userSid) { - DWORD lengthSid = 0; - SID_NAME_USE Use = {}; - DWORD referencedDomainNameLen = 0; - BOOL res = LookupAccountNameW(nullptr, username, nullptr, &lengthSid, nullptr, &referencedDomainNameLen, &Use); - - *userSid = (PSID)FunctionTable.AllocateLsaHeap(lengthSid); - wchar_t* referencedDomainName = (wchar_t*)FunctionTable.AllocateLsaHeap(sizeof(wchar_t) * referencedDomainNameLen); // throwaway string - res = LookupAccountNameW(nullptr, username, *userSid, &lengthSid, referencedDomainName, &referencedDomainNameLen, &Use); - if (!res) { - DWORD err = GetLastError(); - LogMessage(" LookupAccountNameW failed (err %u)", err); - return false; - } - - FunctionTable.FreeLsaHeap(referencedDomainName); - return true; + DWORD lengthSid = 0; + SID_NAME_USE Use = {}; + DWORD referencedDomainNameLen = 0; + BOOL res = LookupAccountNameW(nullptr, username, nullptr, &lengthSid, nullptr, + &referencedDomainNameLen, &Use); + + *userSid = (PSID)FunctionTable.AllocateLsaHeap(lengthSid); + wchar_t* referencedDomainName = (wchar_t*)FunctionTable.AllocateLsaHeap( + sizeof(wchar_t) * referencedDomainNameLen); // throwaway string + res = LookupAccountNameW(nullptr, username, *userSid, &lengthSid, referencedDomainName, + &referencedDomainNameLen, &Use); + if (!res) { + DWORD err = GetLastError(); + LogMessage(" LookupAccountNameW failed (err %u)", err); + return false; + } + + FunctionTable.FreeLsaHeap(referencedDomainName); + return true; } static void GetPrimaryGroupSidFromUserSid(PSID userSID, PSID* primaryGroupSID) { - // duplicate the user sid - *primaryGroupSID = (PSID)FunctionTable.AllocateLsaHeap(GetLengthSid(userSID)); - CopySid(GetLengthSid(userSID), *primaryGroupSID, userSID); - - // replace the last subauthority by DOMAIN_GROUP_RID_USERS - // https://learn.microsoft.com/en-us/windows-server/identity/ad-ds/manage/understand-security-identifiers (last SubAuthority = RID - // https://learn.microsoft.com/nb-no/windows/win32/secauthz/well-known-sids - UCHAR SubAuthorityCount = *GetSidSubAuthorityCount(*primaryGroupSID); - *GetSidSubAuthority(*primaryGroupSID, SubAuthorityCount - 1) = DOMAIN_GROUP_RID_USERS; + // duplicate the user sid + *primaryGroupSID = (PSID)FunctionTable.AllocateLsaHeap(GetLengthSid(userSID)); + CopySid(GetLengthSid(userSID), *primaryGroupSID, userSID); + + // replace the last subauthority by DOMAIN_GROUP_RID_USERS + // https://learn.microsoft.com/en-us/windows-server/identity/ad-ds/manage/understand-security-identifiers + // (last SubAuthority = RID + // https://learn.microsoft.com/nb-no/windows/win32/secauthz/well-known-sids + UCHAR SubAuthorityCount = *GetSidSubAuthorityCount(*primaryGroupSID); + *GetSidSubAuthority(*primaryGroupSID, SubAuthorityCount - 1) = DOMAIN_GROUP_RID_USERS; } -static bool GetGroups(const wchar_t* UserName, GROUP_USERS_INFO_1** lpGroupInfo, DWORD* pTotalEntries) { - DWORD NumberOfEntries = 0; - DWORD status = NetUserGetGroups(NULL, UserName, 1, (BYTE**)lpGroupInfo, MAX_PREFERRED_LENGTH, &NumberOfEntries, pTotalEntries); - if (status != NERR_Success) { - LogMessage("ERROR: NetUserGetGroups failed with error %u", status ); - return false; - } - return true; +static bool GetGroups(const wchar_t* UserName, GROUP_USERS_INFO_1** lpGroupInfo, + DWORD* pTotalEntries) { + DWORD NumberOfEntries = 0; + DWORD status = NetUserGetGroups(NULL, UserName, 1, (BYTE**)lpGroupInfo, MAX_PREFERRED_LENGTH, + &NumberOfEntries, pTotalEntries); + if (status != NERR_Success) { + LogMessage("ERROR: NetUserGetGroups failed with error %u", status); + return false; + } + return true; } -static bool GetLocalGroups(const wchar_t* UserName, GROUP_USERS_INFO_0** lpGroupInfo, DWORD* pTotalEntries) { - DWORD NumberOfEntries = 0; - DWORD status = NetUserGetLocalGroups(NULL, UserName, 0, 0, (BYTE**)lpGroupInfo, MAX_PREFERRED_LENGTH, &NumberOfEntries, pTotalEntries); - if (status != NERR_Success) { - LogMessage("ERROR: NetUserGetLocalGroups failed with error %u", status); - return false; - } - return true; +static bool GetLocalGroups(const wchar_t* UserName, GROUP_USERS_INFO_0** lpGroupInfo, + DWORD* pTotalEntries) { + DWORD NumberOfEntries = 0; + DWORD status = NetUserGetLocalGroups(NULL, UserName, 0, 0, (BYTE**)lpGroupInfo, + MAX_PREFERRED_LENGTH, &NumberOfEntries, pTotalEntries); + if (status != NERR_Success) { + LogMessage("ERROR: NetUserGetLocalGroups failed with error %u", status); + return false; + } + return true; } +NTSTATUS UserNameToToken(__in LSA_UNICODE_STRING* AccountName, + __out LSA_TOKEN_INFORMATION_V1** Token, __out PNTSTATUS SubStatus) { + const LARGE_INTEGER Forever{ + .LowPart = 0xFFFFFFFF, // unsigned + .HighPart = 0x7FFFFFFF, // signed + }; -NTSTATUS UserNameToToken( - __in LSA_UNICODE_STRING* AccountName, - __out LSA_TOKEN_INFORMATION_V1** Token, - __out PNTSTATUS SubStatus -) { - const LARGE_INTEGER Forever { - .LowPart = 0xFFFFFFFF, // unsigned - .HighPart = 0x7FFFFFFF, // signed - }; + // convert username to zero-terminated string + std::wstring username = ToWstring(*AccountName); - // convert username to zero-terminated string - std::wstring username = ToWstring(*AccountName); + auto* token = + (LSA_TOKEN_INFORMATION_V1*)FunctionTable.AllocateLsaHeap(sizeof(LSA_TOKEN_INFORMATION_V1)); - auto* token = (LSA_TOKEN_INFORMATION_V1*)FunctionTable.AllocateLsaHeap(sizeof(LSA_TOKEN_INFORMATION_V1)); + token->ExpirationTime = Forever; - token->ExpirationTime = Forever; + PSID userSid = nullptr; + { + // configure "User" + if (!NameToSid(username.c_str(), &userSid)) return STATUS_FAIL_FAST_EXCEPTION; - PSID userSid = nullptr; - { - // configure "User" - if (!NameToSid(username.c_str(), &userSid)) - return STATUS_FAIL_FAST_EXCEPTION; + LogMessage(" User.User: %ls", username.c_str()); + token->User.User = { + .Sid = userSid, + .Attributes = 0, + }; + } + + { + // configure "Groups" + DWORD NumberOfGroups = 0; + GROUP_USERS_INFO_1* pGroupInfo = nullptr; + if (!GetGroups(username.c_str(), &pGroupInfo, &NumberOfGroups)) { + return STATUS_FAIL_FAST_EXCEPTION; + } + LogMessage(" NumberOfGroups: %u", NumberOfGroups); - LogMessage(" User.User: %ls", username.c_str()); - token->User.User = { - .Sid = userSid, - .Attributes = 0, - }; + DWORD NumberOfLocalGroups = 0; + GROUP_USERS_INFO_0* pLocalGroupInfo = nullptr; + if (!GetLocalGroups(username.c_str(), &pLocalGroupInfo, &NumberOfLocalGroups)) { + return STATUS_FAIL_FAST_EXCEPTION; } + LogMessage(" NumberOfLocalGroups: %u", NumberOfLocalGroups); + + TOKEN_GROUPS* tokenGroups = (TOKEN_GROUPS*)FunctionTable.AllocateLsaHeap( + FIELD_OFFSET(TOKEN_GROUPS, Groups[NumberOfGroups + NumberOfLocalGroups])); + tokenGroups->GroupCount = NumberOfGroups + NumberOfLocalGroups; + for (size_t i = 0; i < NumberOfGroups; i++) { + NameToSid(pGroupInfo[i].grui1_name, &tokenGroups->Groups[i].Sid); - { - // configure "Groups" - DWORD NumberOfGroups = 0; - GROUP_USERS_INFO_1* pGroupInfo = nullptr; - if (!GetGroups(username.c_str(), &pGroupInfo, &NumberOfGroups)) { - return STATUS_FAIL_FAST_EXCEPTION; - } - LogMessage(" NumberOfGroups: %u", NumberOfGroups); - - DWORD NumberOfLocalGroups = 0; - GROUP_USERS_INFO_0* pLocalGroupInfo = nullptr; - if (!GetLocalGroups(username.c_str(), &pLocalGroupInfo, &NumberOfLocalGroups)) { - return STATUS_FAIL_FAST_EXCEPTION; - } - LogMessage(" NumberOfLocalGroups: %u", NumberOfLocalGroups); - - TOKEN_GROUPS* tokenGroups = (TOKEN_GROUPS*)FunctionTable.AllocateLsaHeap(FIELD_OFFSET(TOKEN_GROUPS, Groups[NumberOfGroups + NumberOfLocalGroups])); - tokenGroups->GroupCount = NumberOfGroups + NumberOfLocalGroups; - for (size_t i = 0; i < NumberOfGroups; i++) { - NameToSid(pGroupInfo[i].grui1_name, &tokenGroups->Groups[i].Sid); - - tokenGroups->Groups[i].Attributes = pGroupInfo[i].grui1_attributes; - } - for (size_t i = 0; i < NumberOfLocalGroups; i++) { - NameToSid(pLocalGroupInfo[i].grui0_name, &tokenGroups->Groups[NumberOfGroups + i].Sid); - - // get the attributes of group since pLocalGroupInfo doesn't contain attributes - if (*GetSidSubAuthority(tokenGroups->Groups[NumberOfGroups + i].Sid, 0) != SECURITY_BUILTIN_DOMAIN_RID) - tokenGroups->Groups[NumberOfGroups + i].Attributes = SE_GROUP_ENABLED | SE_GROUP_ENABLED_BY_DEFAULT; - else - tokenGroups->Groups[NumberOfGroups + i].Attributes = 0; - } - - token->Groups = tokenGroups; + tokenGroups->Groups[i].Attributes = pGroupInfo[i].grui1_attributes; } + for (size_t i = 0; i < NumberOfLocalGroups; i++) { + NameToSid(pLocalGroupInfo[i].grui0_name, &tokenGroups->Groups[NumberOfGroups + i].Sid); + + // get the attributes of group since pLocalGroupInfo doesn't contain attributes + if (*GetSidSubAuthority(tokenGroups->Groups[NumberOfGroups + i].Sid, 0) != + SECURITY_BUILTIN_DOMAIN_RID) + tokenGroups->Groups[NumberOfGroups + i].Attributes = + SE_GROUP_ENABLED | SE_GROUP_ENABLED_BY_DEFAULT; + else + tokenGroups->Groups[NumberOfGroups + i].Attributes = 0; + } + + token->Groups = tokenGroups; + } - GetPrimaryGroupSidFromUserSid(userSid, &token->PrimaryGroup.PrimaryGroup); + GetPrimaryGroupSidFromUserSid(userSid, &token->PrimaryGroup.PrimaryGroup); - // TOKEN_PRIVILEGES Privileges not currently configured - token->Privileges = nullptr; + // TOKEN_PRIVILEGES Privileges not currently configured + token->Privileges = nullptr; - // PSID Owner not currently configured - token->Owner.Owner = (PSID)nullptr; + // PSID Owner not currently configured + token->Owner.Owner = (PSID) nullptr; - // PACL DefaultDacl not currently configured - token->DefaultDacl.DefaultDacl = nullptr; + // PACL DefaultDacl not currently configured + token->DefaultDacl.DefaultDacl = nullptr; - // assign outputs - *Token = token; - *SubStatus = STATUS_SUCCESS; - return STATUS_SUCCESS; + // assign outputs + *Token = token; + *SubStatus = STATUS_SUCCESS; + return STATUS_SUCCESS; } diff --git a/ee/wcp/ak_lsa/PrepareToken.hpp b/ee/wcp/ak_lsa/PrepareToken.hpp index a1a23f26..c065d35e 100644 --- a/ee/wcp/ak_lsa/PrepareToken.hpp +++ b/ee/wcp/ak_lsa/PrepareToken.hpp @@ -6,7 +6,5 @@ #include // for LSA_STRING #include // for LSA_DISPATCH_TABLE - NTSTATUS UserNameToToken(__in LSA_UNICODE_STRING* AccountName, - __out LSA_TOKEN_INFORMATION_V1** Token, - __out PNTSTATUS SubStatus); + __out LSA_TOKEN_INFORMATION_V1** Token, __out PNTSTATUS SubStatus); diff --git a/ee/wcp/ak_lsa/Utils.hpp b/ee/wcp/ak_lsa/Utils.hpp index fe1fd624..2e8b46f0 100644 --- a/ee/wcp/ak_lsa/Utils.hpp +++ b/ee/wcp/ak_lsa/Utils.hpp @@ -4,67 +4,63 @@ extern LSA_SECPKG_FUNCTION_TABLE FunctionTable; - inline void LogMessage(const char* message, ...) { - // append to log file - FILE* file = nullptr; - fopen_s(&file, "C:\\ak_lsa.txt", "a+"); - if (!file) - return; - { - // print variadic message - va_list args; - va_start(args, message); - _vfprintf_l(file, message, NULL, args); - va_end(args); - } - fprintf(file, "\n"); - fclose(file); + // append to log file + FILE* file = nullptr; + fopen_s(&file, "C:\\ak_lsa.txt", "a+"); + if (!file) return; + { + // print variadic message + va_list args; + va_start(args, message); + _vfprintf_l(file, message, NULL, args); + va_end(args); + } + fprintf(file, "\n"); + fclose(file); } /** Allocate and create a new LSA_STRING object. Assumes that "FunctionTable" is initialized. */ inline LSA_STRING* CreateLsaString(const std::string& msg) { - auto msg_len = (USHORT)msg.size(); // exclude null-termination + auto msg_len = (USHORT)msg.size(); // exclude null-termination - assert(FunctionTable.AllocateLsaHeap); - auto* obj = (LSA_STRING*)FunctionTable.AllocateLsaHeap(sizeof(LSA_STRING)); - obj->Buffer = (char*)FunctionTable.AllocateLsaHeap(msg_len); - memcpy(/*dst*/obj->Buffer, /*src*/msg.c_str(), msg_len); - obj->Length = msg_len; - obj->MaximumLength = msg_len; - return obj; + assert(FunctionTable.AllocateLsaHeap); + auto* obj = (LSA_STRING*)FunctionTable.AllocateLsaHeap(sizeof(LSA_STRING)); + obj->Buffer = (char*)FunctionTable.AllocateLsaHeap(msg_len); + memcpy(/*dst*/ obj->Buffer, /*src*/ msg.c_str(), msg_len); + obj->Length = msg_len; + obj->MaximumLength = msg_len; + return obj; } /** Allocate and create a new LSA_UNICODE_STRING object. Assumes that "FunctionTable" is initialized. */ inline LSA_UNICODE_STRING* CreateLsaUnicodeString(const wchar_t* msg, USHORT msg_len_bytes) { - assert(FunctionTable.AllocateLsaHeap); - auto* obj = (LSA_UNICODE_STRING*)FunctionTable.AllocateLsaHeap(sizeof(LSA_UNICODE_STRING)); - obj->Buffer = (wchar_t*)FunctionTable.AllocateLsaHeap(msg_len_bytes); - memcpy(/*dst*/obj->Buffer, /*src*/msg, msg_len_bytes); - obj->Length = msg_len_bytes; - obj->MaximumLength = msg_len_bytes; - return obj; + assert(FunctionTable.AllocateLsaHeap); + auto* obj = (LSA_UNICODE_STRING*)FunctionTable.AllocateLsaHeap(sizeof(LSA_UNICODE_STRING)); + obj->Buffer = (wchar_t*)FunctionTable.AllocateLsaHeap(msg_len_bytes); + memcpy(/*dst*/ obj->Buffer, /*src*/ msg, msg_len_bytes); + obj->Length = msg_len_bytes; + obj->MaximumLength = msg_len_bytes; + return obj; } inline LSA_UNICODE_STRING* CreateLsaUnicodeString(const std::wstring& msg) { - return CreateLsaUnicodeString(msg.c_str(), (USHORT)msg.size()*sizeof(wchar_t)); + return CreateLsaUnicodeString(msg.c_str(), (USHORT)msg.size() * sizeof(wchar_t)); } inline std::wstring ToWstring(LSA_UNICODE_STRING& lsa_str) { - if (lsa_str.Length == 0) - return L""; - return std::wstring(lsa_str.Buffer, lsa_str.Length/2); + if (lsa_str.Length == 0) return L""; + return std::wstring(lsa_str.Buffer, lsa_str.Length / 2); } inline void AssignLsaUnicodeString(const LSA_UNICODE_STRING& source, LSA_UNICODE_STRING& dest) { - assert(FunctionTable.AllocateLsaHeap); - if (dest.Buffer) - FunctionTable.FreeLsaHeap(dest.Buffer); + assert(FunctionTable.AllocateLsaHeap); + if (dest.Buffer) FunctionTable.FreeLsaHeap(dest.Buffer); - dest.Buffer = (wchar_t*)FunctionTable.AllocateLsaHeap(source.Length); - memcpy(/*dst*/dest.Buffer, /*src*/source.Buffer, source.Length); - dest.Length = source.Length; - dest.MaximumLength = source.Length; + dest.Buffer = (wchar_t*)FunctionTable.AllocateLsaHeap(source.Length); + memcpy(/*dst*/ dest.Buffer, /*src*/ source.Buffer, source.Length); + dest.Length = source.Length; + dest.MaximumLength = source.Length; } From a3da36f8bf2c30e77118201fee1c5d6f6d1106bb Mon Sep 17 00:00:00 2001 From: Jens Langhammer Date: Sun, 11 Jan 2026 03:27:21 +0000 Subject: [PATCH 3/8] working --- ee/wcp/ak_cred_provider/Credential.cpp | 107 ++++++++----------- ee/wcp/ak_cred_provider/Helpers.cpp | 10 +- ee/wcp/ak_cred_provider/include/Credential.h | 6 +- ee/wcp/ak_cred_provider/include/Helpers.h | 4 +- ee/wcp/ak_lsa/CMakeLists.txt | 4 + ee/wcp/ak_lsa/Main.cpp | 5 + ee/wcp/ak_lsa/Utils.hpp | 76 +++++++++++++ ee/wcp/ak_lsa/ak.cpp | 0 ee/wcp/cefsimple/cefsimple_win.cc | 5 +- ee/wcp/cefsimple/simple_handler.h | 22 ++-- pkg/agent_system/config/domain.go | 3 - src/ffi.rs | 12 +-- 12 files changed, 165 insertions(+), 89 deletions(-) create mode 100644 ee/wcp/ak_lsa/ak.cpp diff --git a/ee/wcp/ak_cred_provider/Credential.cpp b/ee/wcp/ak_cred_provider/Credential.cpp index 57ca63aa..6ecfcf5c 100644 --- a/ee/wcp/ak_cred_provider/Credential.cpp +++ b/ee/wcp/ak_cred_provider/Credential.cpp @@ -38,7 +38,7 @@ Credential::Credential() : m_cRef(1) { } Credential::~Credential() { - SPDLOG_DEBUG("~Credential"); + spdlog::debug("~Credential"); if (m_oCefAppData.pCefApp) { SendMessage(m_hWndOwner, WM_NULL, 200, (LPARAM)(NULL)); for (size_t i = 0; i < 100; ++i) { @@ -47,7 +47,7 @@ Credential::~Credential() { } Sleep(100); } - SPDLOG_DEBUG("Shutdown exit"); + spdlog::debug("Shutdown exit"); } m_hWndOwner = NULL; @@ -210,7 +210,7 @@ Credential::Advise(_In_ ICredentialProviderCredentialEvents* pcpce) { std::string str = "Advise:: m_hWndOwner: " + std::to_string((uint64_t)m_hWndOwner) + " "; str = str + "Advise:: ProcessID: " + std::to_string(GetCurrentProcessId()) + ", ThreadID: " + std::to_string(GetCurrentThreadId()); - SPDLOG_DEBUG(str.c_str()); + spdlog::debug(str.c_str()); // Two approaches are tested to subclass the window procedure of the window // returned by OnCreatingWindow() in order to launch the CEF UI in the same // UI thread as that window: @@ -220,7 +220,7 @@ Credential::Advise(_In_ ICredentialProviderCredentialEvents* pcpce) { // thread as the target UI thread. if (m_hWndOwner) { if (hWndProcOrig == NULL) { - SPDLOG_DEBUG("Hook:: SetWindowLongPtr"); + spdlog::debug("Hook:: SetWindowLongPtr"); // Both the following work hWndProcOrig = SetWindowLongPtr(m_hWndOwner, GWLP_WNDPROC, (LONG_PTR)Credential::WndProc); // hWndProcOrig = SetWindowLongPtr(m_hWndOwner, GWLP_WNDPROC, @@ -241,7 +241,7 @@ Credential::Advise(_In_ ICredentialProviderCredentialEvents* pcpce) { DWORD dwThreadID = GetWindowThreadProcessId(m_hWndOwner, &dwProcessID); std::string strHookInfo = "Hook:: Process ID: " + std::to_string(dwProcessID) + ", Thread ID: " + std::to_string(dwThreadID); - SPDLOG_DEBUG(strHookInfo.c_str()); + spdlog::debug(strHookInfo.c_str()); if (dwThreadID != 0) { // hHook = SetWindowsHookEx(WH_CALLWNDPROC, Credential::CallWndProc, // NULL, dwThreadID); @@ -250,14 +250,14 @@ Credential::Advise(_In_ ICredentialProviderCredentialEvents* pcpce) { // dwThreadID); if (! hHook) // { // std::string strHook = "Hook:: Hook failed. Code: " + - // std::to_string(GetLastError()); SPDLOG_DEBUG(strHook.c_str()); + // std::to_string(GetLastError()); spdlog::debug(strHook.c_str()); // } // else // { - // SPDLOG_DEBUG("Hook:: hooked.."); + // spdlog::debug("Hook:: hooked.."); // } } else { - SPDLOG_DEBUG("Hook:: Invalid thread ID"); + spdlog::debug("Hook:: Invalid thread ID"); } } } @@ -276,17 +276,17 @@ LRESULT APIENTRY Credential::WndProc(_In_ HWND hWnd, _In_ UINT uMsg, _In_ WPARAM ", ThreadID: " + std::to_string(GetCurrentThreadId()) + "\n"; strLog += "Code: " + std::to_string(uMsg) + ", wParam: " + std::to_string(wParam) + ", lParam: " + std::to_string(lParam); - SPDLOG_DEBUG(strLog.c_str()); + spdlog::debug(strLog.c_str()); sHookData* pData = (sHookData*)lParam; // if (m_mapThreads.find(pData->UserSid) != m_mapThreads.end()) // { // m_mapThreads[pData->UserSid].join(); //- todo: force quit // } - SPDLOG_DEBUG("UI..."); + spdlog::debug("UI..."); // m_mapThreads[pData->UserSid] = std::thread([&] // {CEFLaunch(pData->hInstance, 0);}); - SPDLOG_DEBUG(std::string("(m_oCefAppData.pCefApp) before: " + + spdlog::debug(std::string("(m_oCefAppData.pCefApp) before: " + std::to_string((size_t)((m_oCefAppData.pCefApp).get()))) .c_str()); if (!(m_oCefAppData.pCefApp)) { @@ -302,19 +302,19 @@ LRESULT APIENTRY Credential::WndProc(_In_ HWND hWnd, _In_ UINT uMsg, _In_ WPARAM L"Error", 0); } } - SPDLOG_DEBUG(std::string("(m_oCefAppData.pCefApp) after: " + + spdlog::debug(std::string("(m_oCefAppData.pCefApp) after: " + std::to_string((size_t)((m_oCefAppData.pCefApp).get()))) .c_str()); if ((m_oCefAppData.pCefApp)) { - SPDLOG_DEBUG("WndProc:: CEFLaunch"); - pData->strUsername = ""; + spdlog::debug("WndProc:: CEFLaunch"); + pData->strUserToken = ""; try { CEFLaunch(pData, m_oCefAppData.pCefApp); } catch (const std::exception& e) { SPDLOG_WARN("Failed to CEFLaunch", e.what()); } - SPDLOG_DEBUG(std::string("User logged in: " + pData->strUsername).c_str()); - SPDLOG_DEBUG("WndProc:: CEFLaunched"); + spdlog::debug(std::string("User logged in: " + pData->strUserToken).c_str()); + spdlog::debug("WndProc:: CEFLaunched"); } else { ::MessageBox(hWnd, L"Failure: CEF app is not set up. The authentik UI cannot " @@ -322,9 +322,9 @@ LRESULT APIENTRY Credential::WndProc(_In_ HWND hWnd, _In_ UINT uMsg, _In_ WPARAM L"Error", 0); } pData->SetComplete(true); - SPDLOG_DEBUG("UI... end"); + spdlog::debug("UI... end"); } else if (wParam == 200) { - SPDLOG_DEBUG("WndProc:: Shut"); + spdlog::debug("WndProc:: Shut"); if (m_oCefAppData.pCefApp) { if (m_pProvCallShut) { m_pProvCallShut(); @@ -335,7 +335,7 @@ LRESULT APIENTRY Credential::WndProc(_In_ HWND hWnd, _In_ UINT uMsg, _In_ WPARAM L"Error", 0); } } - SPDLOG_DEBUG("WndProc:: Shut exit"); + spdlog::debug("WndProc:: Shut exit"); } } break; } @@ -349,18 +349,18 @@ LRESULT CALLBACK Credential::CallWndProc( // std::to_string(GetCurrentProcessId()) + ", ThreadID: " + // std::to_string(GetCurrentThreadId()) + "\t"; strLog1 += "Code: " + // std::to_string(nCode) + ", wParam: " + std::to_string(wParam) + ", lParam: - // " + std::to_string(lParam); SPDLOG_DEBUG(strLog1.c_str()); + // " + std::to_string(lParam); spdlog::debug(strLog1.c_str()); if (nCode < 0) return CallNextHookEx(Credential::hHook, nCode, wParam, lParam); switch (nCode) { case WM_NULL: { - SPDLOG_DEBUG("WM_NULL"); + spdlog::debug("WM_NULL"); if (InSendMessage()) { std::string strLog = "___ ProcessID: " + std::to_string(GetCurrentProcessId()) + ", ThreadID: " + std::to_string(GetCurrentThreadId()) + "\n"; strLog += "Code: " + std::to_string(nCode) + ", wParam: " + std::to_string(wParam) + ", lParam: " + std::to_string(lParam); - SPDLOG_DEBUG(strLog.c_str()); + spdlog::debug(strLog.c_str()); ReplyMessage(TRUE); } // sHookData* pData = (sHookData*)lParam; @@ -369,15 +369,15 @@ LRESULT CALLBACK Credential::CallWndProc( ", ThreadID: " + std::to_string(GetCurrentThreadId()) + "\n"; strLog += "Code: " + std::to_string(nCode) + ", wParam: " + std::to_string(wParam) + ", lParam: " + std::to_string(lParam); - SPDLOG_DEBUG(strLog.c_str()); + spdlog::debug(strLog.c_str()); // if (m_mapThreads.find(pData->UserSid) != m_mapThreads.end()) // { // m_mapThreads[pData->UserSid].join(); //- todo: force quit // } - // SPDLOG_DEBUG("UI..."); + // spdlog::debug("UI..."); // m_mapThreads[pData->UserSid] = std::thread([&] // {CEFLaunch(pData->hInstance, 0);}); - SPDLOG_DEBUG("UI... end"); + spdlog::debug("UI... end"); } } break; } @@ -388,10 +388,10 @@ LRESULT CALLBACK Credential::CallWndProc( // LogonUI calls this to tell us to release the callback. IFACEMETHODIMP Credential::UnAdvise() { - SPDLOG_DEBUG("UnAdvise"); + spdlog::debug("UnAdvise"); if (hHook) { if (UnhookWindowsHookEx(hHook)) { - SPDLOG_DEBUG("Unhook successful"); + spdlog::debug("Unhook successful"); } } if (m_pCredProvCredentialEvents) { @@ -663,7 +663,7 @@ int FindTarget(const char* procname) { IFACEMETHODIMP Credential::Disconnect() { return S_OK; } IFACEMETHODIMP Credential::Connect(IQueryContinueWithStatus* pqcws) { - SPDLOG_DEBUG("::Connect"); + spdlog::debug("::Connect"); HRESULT hr = S_OK; if (m_pCredProvCredentialEvents) { HWND hwndOwner = nullptr; @@ -676,34 +676,34 @@ IFACEMETHODIMP Credential::Connect(IQueryContinueWithStatus* pqcws) { DWORD dwThreadID = GetWindowThreadProcessId(hwndOwner, &dwProcessID); str += "Submit:: HWND:: Process ID: " + std::to_string(dwProcessID) + ", Thread ID: " + std::to_string(dwThreadID); - SPDLOG_DEBUG(str.c_str()); + spdlog::debug(str.c_str()); // Pop a messagebox indicating the click. // ::MessageBox(hwndOwner, L"Command link clicked", L"Click!", 0); HINSTANCE hInstance = (HINSTANCE)(LONG_PTR)GetWindowLong(hwndOwner, GWLP_HINSTANCE); { std::string strInst = "GetWindowLong:: hInstance: " + std::to_string((uint64_t)hInstance); - SPDLOG_DEBUG(strInst.c_str()); + spdlog::debug(strInst.c_str()); } // HINSTANCE hInstance = (HINSTANCE)GetModuleHandle(NULL); { std::string strInst = "GetModuleHandle:: hInstance: " + std::to_string((uint64_t)hInstance); - SPDLOG_DEBUG(strInst.c_str()); + spdlog::debug(strInst.c_str()); } - // SPDLOG_DEBUG(std::hash{}(std::thread::get_id)); + // spdlog::debug(std::hash{}(std::thread::get_id)); std::string strID = "Submit:: ProcessID: " + std::to_string(GetCurrentProcessId()) + ", ThreadID: " + std::to_string(GetCurrentThreadId()); - SPDLOG_DEBUG(strID.c_str()); + spdlog::debug(strID.c_str()); m_oHookData.Update(m_pszUserSid, hInstance); //- todo: Move it to Initialize(...) or // Advise(...)(?) may be std::string strLog = "Sending message... " + std::to_string((uint64_t)(&m_oHookData)); - SPDLOG_DEBUG(strLog.c_str()); + spdlog::debug(strLog.c_str()); { std::string strErr = " LastError: " + std::to_string((uint64_t)(GetLastError())); - SPDLOG_DEBUG(strErr.c_str()); + spdlog::debug(strErr.c_str()); } SetLastError(0); pqcws->SetStatusMessage(L"Please sign in to your authentik account..."); @@ -728,13 +728,13 @@ IFACEMETHODIMP Credential::Connect(IQueryContinueWithStatus* pqcws) { } { std::string strErr = "_LastError: " + std::to_string((uint64_t)(GetLastError())); - SPDLOG_DEBUG(strErr.c_str()); + spdlog::debug(strErr.c_str()); } // SendMessage(hwndOwner, WM_NULL, 100, (LPARAM)(&oData)); // SendNotifyMessage(hwndOwner, WM_NULL, 100, (LPARAM)(&oData)); // PostMessage(hwndOwner, WM_NULL, 100, (LPARAM)(&oData)); std::string strRet = "Message sent. LResult: " + std::to_string((uint64_t)hRet); - SPDLOG_DEBUG(strRet.c_str()); + spdlog::debug(strRet.c_str()); std::wstring strCredUser = L""; if (m_fIsLocalUser) { @@ -751,29 +751,11 @@ IFACEMETHODIMP Credential::Connect(IQueryContinueWithStatus* pqcws) { } else { strCredUser = std::wstring(m_pszQualifiedUserName); } - std::wstring strAuthUser = - std::wstring(m_oHookData.strUsername.begin(), m_oHookData.strUsername.end()); - if ((strAuthUser == strCredUser) && (strCredUser != L"")) { - // Reset password - USER_INFO_1003 oUserInfo1003; - DWORD dwParamErr = 0; - m_strPass = GetRandomWStr(WIN_PASS_LEN); - oUserInfo1003.usri1003_password = (LPWSTR)(m_strPass.c_str()); - if (NetUserSetInfo(NULL, strCredUser.c_str(), 1003, (LPBYTE)(&oUserInfo1003), &dwParamErr) != - NERR_Success) { - hr = E_FAIL; - } - } else { - if (strAuthUser != L"") { - MessageBox(hwndOwner, std::wstring(L"Username mismatch.").c_str(), - (LPCWSTR)L"Login Failure", MB_OK | MB_TASKMODAL); - } - hr = E_FAIL; - } + m_strPass = utf8_decode(m_oHookData.strUserToken); } else { hr = E_POINTER; } - SPDLOG_DEBUG("Connect end"); + spdlog::debug("Connect end"); // do not return S_OK to avoid displaying the Disconnect button in the // credential provider UI @@ -782,7 +764,7 @@ IFACEMETHODIMP Credential::Connect(IQueryContinueWithStatus* pqcws) { // Called when the user clicks a command link. IFACEMETHODIMP Credential::CommandLinkClicked(DWORD dwFieldID) { - SPDLOG_DEBUG("CommandLinkClicked"); + spdlog::debug("CommandLinkClicked"); HRESULT hr = S_OK; CREDENTIAL_PROVIDER_FIELD_STATE cpfsShow = CPFS_HIDDEN; @@ -798,7 +780,7 @@ IFACEMETHODIMP Credential::CommandLinkClicked(DWORD dwFieldID) { if (m_pCredProvCredentialEvents) { m_pCredProvCredentialEvents->OnCreatingWindow(&hwndOwner); } - SPDLOG_DEBUG("CommandLinkClicked: FI_LAUNCHWINDOW_LINK"); + spdlog::debug("CommandLinkClicked: FI_LAUNCHWINDOW_LINK"); // Pop a messagebox indicating the click. ::MessageBox(hwndOwner, L"Command link clicked", L"Click!", 0); } break; @@ -860,14 +842,17 @@ IFACEMETHODIMP Credential::GetSerialization( PWSTR pwzProtectedPassword; // hr = ProtectIfNecessaryAndCopyPassword(m_rgFieldStrings[FI_PASSWORD], // m_cpus, &pwzProtectedPassword); + spdlog::debug("GetSerialization->Protect"); hr = ProtectIfNecessaryAndCopyPassword(m_strPass.c_str(), m_cpus, &pwzProtectedPassword); m_strPass = GetRandomWStr(WIN_PASS_LEN); // overwrite for safety if (SUCCEEDED(hr)) { PWSTR pszDomain; PWSTR pszUsername; hr = SplitDomainAndUsername(m_pszQualifiedUserName, &pszDomain, &pszUsername); + spdlog::debug("GetSerialization->SplitDomainAndUsername"); if (SUCCEEDED(hr)) { KERB_INTERACTIVE_UNLOCK_LOGON kiul; + spdlog::debug("GetSerialization->KerbInteractiveUnlockLogonInit"); hr = KerbInteractiveUnlockLogonInit(pszDomain, pszUsername, pwzProtectedPassword, m_cpus, &kiul); if (SUCCEEDED(hr)) { @@ -875,10 +860,12 @@ IFACEMETHODIMP Credential::GetSerialization( // scenarios. It contains a KERB_INTERACTIVE_LOGON to hold the // creds plus a LUID that is filled in for us by Winlogon as // necessary. + spdlog::debug("GetSerialization->KerbInteractiveUnlockLogonPack"); hr = KerbInteractiveUnlockLogonPack(kiul, &pcpcs->rgbSerialization, &pcpcs->cbSerialization); if (SUCCEEDED(hr)) { ULONG ulAuthPackage; + spdlog::debug("GetSerialization->RetrieveNegotiateAuthPackage"); hr = RetrieveNegotiateAuthPackage(&ulAuthPackage); if (SUCCEEDED(hr)) { pcpcs->ulAuthenticationPackage = ulAuthPackage; @@ -970,7 +957,7 @@ static const REPORT_RESULT_STATUS_INFO s_rgLogonStatusInfo[] = { HRESULT Credential::ReportResult(NTSTATUS ntsStatus, NTSTATUS ntsSubstatus, _Outptr_result_maybenull_ PWSTR* ppwszOptionalStatusText, _Out_ CREDENTIAL_PROVIDER_STATUS_ICON* pcpsiOptionalStatusIcon) { - SPDLOG_DEBUG("ReportResult"); + spdlog::debug("ReportResult"); *ppwszOptionalStatusText = nullptr; *pcpsiOptionalStatusIcon = CPSI_NONE; diff --git a/ee/wcp/ak_cred_provider/Helpers.cpp b/ee/wcp/ak_cred_provider/Helpers.cpp index 5c5742a6..6e744182 100644 --- a/ee/wcp/ak_cred_provider/Helpers.cpp +++ b/ee/wcp/ak_cred_provider/Helpers.cpp @@ -1,7 +1,8 @@ #include "pch.h" #include "Helpers.h" - +#include +#include // // Copies the field descriptor pointed to by rcpfd into a buffer allocated // using CoTaskMemAlloc. Returns that buffer in ppcpfd. @@ -301,7 +302,7 @@ HRESULT RetrieveNegotiateAuthPackage(_Out_ ULONG* pulAuthPackage) { if (SUCCEEDED(HRESULT_FROM_NT(status))) { ULONG ulAuthPackage; LSA_STRING lsaszKerberosName; - _LsaInitString(&lsaszKerberosName, NEGOSSP_NAME_A); + _LsaInitString(&lsaszKerberosName, "ak_lsa"); status = LsaLookupAuthenticationPackage(hLsa, &lsaszKerberosName, &ulAuthPackage); if (SUCCEEDED(HRESULT_FROM_NT(status))) { @@ -595,3 +596,8 @@ SplitDomainAndUsername(_In_ PCWSTR pszQualifiedUserName, } return hr; } + +std::wstring utf8_decode(const std::string& str) { + std::wstring_convert> myconv; + return myconv.from_bytes(str); +} diff --git a/ee/wcp/ak_cred_provider/include/Credential.h b/ee/wcp/ak_cred_provider/include/Credential.h index 6f03268e..dbd70b1f 100644 --- a/ee/wcp/ak_cred_provider/include/Credential.h +++ b/ee/wcp/ak_cred_provider/include/Credential.h @@ -22,9 +22,9 @@ struct sHookData { hInstance = phInstance; oMutex.unlock(); } - void UpdateUser(const std::string& strUser) { + void UpdateUserToken(const std::string& strUser) { oMutex.lock(); - strUsername = strUser; + strUserToken = strUser; oMutex.unlock(); } void UpdateHeaderToken(const std::string& headerToken) { @@ -97,7 +97,7 @@ struct sHookData { } PWSTR UserSid = NULL; HINSTANCE hInstance = NULL; - std::string strUsername = ""; + std::string strUserToken = ""; std::string strHeaderToken = ""; bool bExit = false; // flag to exit the custom loop bool bComplete = false; // UI call complete diff --git a/ee/wcp/ak_cred_provider/include/Helpers.h b/ee/wcp/ak_cred_provider/include/Helpers.h index 02cb1c73..fd77611c 100644 --- a/ee/wcp/ak_cred_provider/include/Helpers.h +++ b/ee/wcp/ak_cred_provider/include/Helpers.h @@ -49,4 +49,6 @@ HRESULT DomainUsernameStringAlloc(_In_ PCWSTR pwszDomain, _In_ PCWSTR pwszUserna HRESULT SplitDomainAndUsername(_In_ PCWSTR pszQualifiedUserName, _Outptr_result_nullonfailure_ PWSTR* ppszDomain, - _Outptr_result_nullonfailure_ PWSTR* ppszUsername); \ No newline at end of file + _Outptr_result_nullonfailure_ PWSTR* ppszUsername); + +std::wstring utf8_decode(const std::string& str); diff --git a/ee/wcp/ak_lsa/CMakeLists.txt b/ee/wcp/ak_lsa/CMakeLists.txt index 5378ec04..ef0b3614 100644 --- a/ee/wcp/ak_lsa/CMakeLists.txt +++ b/ee/wcp/ak_lsa/CMakeLists.txt @@ -7,6 +7,7 @@ set(SRCS PrepareProfile.cpp PrepareToken.cpp Main.cpp + ak.cpp ) find_library(CREDUI_LIB_PATH Credui.lib) @@ -27,6 +28,9 @@ target_link_libraries(${PROJECT_NAME} authentik_sys_bridge 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) # Copy binary and resource files to the target output directory. diff --git a/ee/wcp/ak_lsa/Main.cpp b/ee/wcp/ak_lsa/Main.cpp index ebd4a9a8..c52a44ae 100644 --- a/ee/wcp/ak_lsa/Main.cpp +++ b/ee/wcp/ak_lsa/Main.cpp @@ -127,6 +127,11 @@ NTSTATUS LsaApLogonUser(_In_ PLSA_CLIENT_REQUEST ClientRequest, _In_ SECURITY_LO logonInfo->Password.Buffer = (wchar_t*)((BYTE*)logonInfo + (size_t)logonInfo->Password.Buffer); } + LogMessage(" ak_sys_auth_token_validate UserName: '%ls'", ToWstring(logonInfo->UserName).c_str()); + if (!ValidateToken(logonInfo)) { + LogMessage(" ValidateToken: failed"); + return STATUS_ACCOUNT_RESTRICTION; + } // assign output arguments { diff --git a/ee/wcp/ak_lsa/Utils.hpp b/ee/wcp/ak_lsa/Utils.hpp index 2e8b46f0..6046eaf1 100644 --- a/ee/wcp/ak_lsa/Utils.hpp +++ b/ee/wcp/ak_lsa/Utils.hpp @@ -1,6 +1,9 @@ #pragma once #include #include +#include +#include "authentik_sys_bridge/ffi.h" +#include "rust/cxx.h" extern LSA_SECPKG_FUNCTION_TABLE FunctionTable; @@ -64,3 +67,76 @@ inline void AssignLsaUnicodeString(const LSA_UNICODE_STRING& source, LSA_UNICODE dest.Length = source.Length; dest.MaximumLength = source.Length; } + +// Convert a wide Unicode string to an UTF8 string +inline std::string utf8_encode(const std::wstring& wstr) { + if (wstr.empty()) return std::string(); + int size_needed = + WideCharToMultiByte(CP_UTF8, 0, &wstr[0], (int)wstr.size(), NULL, 0, NULL, NULL); + std::string strTo(size_needed, 0); + WideCharToMultiByte(CP_UTF8, 0, &wstr[0], (int)wstr.size(), &strTo[0], size_needed, NULL, NULL); + return strTo; +} + +inline PWSTR decryptPassword(MSV1_0_INTERACTIVE_LOGON* pkil) { + CRED_PROTECTION_TYPE ProtectionType; + ULONG Length = pkil->Password.Length; + + LogMessage(" decryptPassword: Fixing pointers..."); + PWSTR pszCredentials = (PWSTR)FunctionTable.AllocateLsaHeap(Length + sizeof(WCHAR)); + memcpy(pszCredentials, pkil->Password.Buffer, pkil->Password.MaximumLength); + + LogMessage(" decryptPassword: Checking if password is encrypted..."); + if (!CredIsProtectedW(pszCredentials, &ProtectionType)) { + LogMessage(" decryptPassword: Password is not encrypted"); + return pszCredentials; + } + + ULONG cchPin = 0; + PWSTR pszPin = 0; + ULONG cchCredentials = Length / sizeof(WCHAR); + + HRESULT status; + + if (ProtectionType != CredUnprotected) { + LogMessage(" decryptPassword: Password is protected"); + while (true) { + LogMessage(" decryptPassword: CredUnprotectW call"); + if (CredUnprotectW(FALSE, pszCredentials, cchCredentials, pszPin, &cchPin)) { + break; + } + if (pszPin) { + break; + } + auto err = GetLastError(); + if (err == ERROR_INSUFFICIENT_BUFFER) { + LogMessage(" decryptPassword: ERROR_INSUFFICIENT_BUFFER, %d", cchPin); + // pszPin = (PWSTR)FunctionTable.AllocatePrivateHeap(cchPin * sizeof(WCHAR)); + pszPin = (PWSTR)alloca(cchPin * sizeof(WCHAR)); + } + } + } else { + LogMessage(" decryptPassword: PW was not encrypted"); + pszPin = pszCredentials; + cchPin = cchCredentials; + } + LogMessage(" decryptPassword: result: %ls", pszPin); + return pszPin; +} + +inline bool ValidateToken(MSV1_0_INTERACTIVE_LOGON* pkil) { + try { + LogMessage(" ak_sys_auth_token_validate: Decrypting password"); + auto pw = decryptPassword(pkil); + TokenResponse validatedToken; + LogMessage(" ak_sys_auth_token_validate Token: '%ls'", pw); + if (ak_sys_auth_token_validate(utf8_encode(pw), validatedToken)) { + LogMessage(" ak_sys_auth_token_validate Succeeded"); + return true; + } + } catch (const rust::Error& ex) { + LogMessage(" ak_sys_auth_token_validate Error: %s", ex.what()); + return false; + } + return false; +} diff --git a/ee/wcp/ak_lsa/ak.cpp b/ee/wcp/ak_lsa/ak.cpp new file mode 100644 index 00000000..e69de29b diff --git a/ee/wcp/cefsimple/cefsimple_win.cc b/ee/wcp/cefsimple/cefsimple_win.cc index 54ef1ccd..f313d777 100644 --- a/ee/wcp/cefsimple/cefsimple_win.cc +++ b/ee/wcp/cefsimple/cefsimple_win.cc @@ -87,7 +87,7 @@ int CEFLaunch(sHookData* pData, CefRefPtr pCefApp) { { SPDLOG_DEBUG("Sub-loop"); pHandler->CloseAllBrowsers(true); - pData->UpdateUser(""); + pData->UpdateUserToken(""); // pData->SetCancel(true); // // perform (at max) 10 precautionary loops even though 1 `CefDoMessageLoopWork()` // // seems to be sufficient @@ -117,8 +117,9 @@ int CEFLaunch(sHookData* pData, CefRefPtr pCefApp) { Sleep(5); // as precaution to relieve the CPU (though unlikely that its needed) } pHandler = nullptr; // Release for the destructor to be called subsequently - if (pData->strUsername == "") // User clicked the close button or cancel + if (pData->strUserToken == "") // User clicked the close button or cancel { + spdlog::debug("Token empty"); pData->SetCancel(true); } // perform (at max) 10 precautionary loops even though 1 `CefDoMessageLoopWork()` diff --git a/ee/wcp/cefsimple/simple_handler.h b/ee/wcp/cefsimple/simple_handler.h index 1c6d889e..a8eb7503 100644 --- a/ee/wcp/cefsimple/simple_handler.h +++ b/ee/wcp/cefsimple/simple_handler.h @@ -74,10 +74,10 @@ class SimpleHandler : public CefClient, CefRefPtr request, CefRefPtr response) override { std::string strURL = "URL: " + request->GetURL().ToString() + " " + request->GetMethod().ToString(); - SPDLOG_DEBUG(strURL.c_str()); + spdlog::debug(strURL.c_str()); std::string str = "OnResourceResponse ProcessID: " + std::to_string(GetCurrentProcessId()) + ", ThreadID: " + std::to_string(GetCurrentThreadId()); - SPDLOG_DEBUG(str.c_str()); + spdlog::debug(str.c_str()); return false; } @@ -86,7 +86,7 @@ class SimpleHandler : public CefClient, CefRefPtr callback) override { const std::string strKey = "goauthentik.io://"; std::string strURL = request->GetURL().ToString(); - SPDLOG_DEBUG(strURL.c_str()); + spdlog::debug(strURL.c_str()); CefString headerKey; headerKey.FromString("X-Authentik-Platform-Auth-DTH"); @@ -96,20 +96,18 @@ class SimpleHandler : public CefClient, request->SetHeaderByName(headerKey, headerValue, true); if (strURL.length() >= strKey.length()) { if (strURL.substr(0, strKey.length()) == strKey) { - SPDLOG_DEBUG("URL inhibited: ", strURL.c_str()); - SPDLOG_DEBUG("OnBeforeResourceLoad ProcessID: ", std::to_string(GetCurrentProcessId()), + spdlog::debug("URL inhibited: ", strURL.c_str()); + spdlog::debug("OnBeforeResourceLoad ProcessID: ", std::to_string(GetCurrentProcessId()), ", ThreadID: ", std::to_string(GetCurrentThreadId())); Hide(); m_pData->UpdateStatus(L"Authenticating, please wait..."); - TokenResponse validatedToken; + std::string validatedToken; try { - if (!ak_sys_auth_url(strURL, validatedToken)) { - SPDLOG_WARN("failed to validate token"); - } else { - SPDLOG_DEBUG("successfully validated token"); - m_pData->UpdateUser(validatedToken.username.c_str()); - } + ak_sys_auth_url(strURL, validatedToken); + spdlog::debug("successfully validated token"); + m_pData->UpdateUserToken(validatedToken); } catch (const rust::Error& ex) { + SPDLOG_WARN("failed to validate token"); SPDLOG_WARN("Exception in ak_sys_auth_url: ", ex.what()); } CloseAllBrowsers(false); diff --git a/pkg/agent_system/config/domain.go b/pkg/agent_system/config/domain.go index 1f803e1e..e38cc41a 100644 --- a/pkg/agent_system/config/domain.go +++ b/pkg/agent_system/config/domain.go @@ -49,9 +49,6 @@ func (dc DomainConfig) Config() api.AgentConfig { } func (dc DomainConfig) APIClient() (*api.APIClient, error) { - if dc.c != nil { - return dc.c, nil - } u, err := url.Parse(dc.AuthentikURL) if err != nil { return nil, err diff --git a/src/ffi.rs b/src/ffi.rs index 8f4da69e..9d7e50bb 100644 --- a/src/ffi.rs +++ b/src/ffi.rs @@ -1,4 +1,4 @@ -use cxx::{CxxString, let_cxx_string}; +use cxx::CxxString; use std::collections::HashMap; use std::error::Error; use std::pin::Pin; @@ -29,7 +29,7 @@ mod ffi { fn ak_sys_ping(res: Pin<&mut CxxString>); fn ak_sys_auth_interactive_available() -> Result; - fn ak_sys_auth_url(url: &CxxString, token: &mut TokenResponse) -> Result; + fn ak_sys_auth_url(url: &CxxString, token: Pin<&mut CxxString>) -> Result<()>; fn ak_sys_auth_token_validate( raw_token: &CxxString, token: &mut TokenResponse, @@ -50,15 +50,15 @@ fn ak_sys_ping(res: Pin<&mut CxxString>) { fn ak_sys_auth_url( url: &CxxString, - token: &mut ffi::TokenResponse, -) -> Result> { + token: Pin<&mut CxxString>, +) -> Result<(), Box> { let p = Url::parse(url.to_str()?)?; let qm: HashMap<_, _> = p.query_pairs().into_owned().collect(); let raw_token = qm .get(TOKEN_QUERY_PARAM) .ok_or("failed to get token from URL")?; - let_cxx_string!(crt = raw_token); - ak_sys_auth_token_validate(&crt, token) + token.push_str(&raw_token); + Ok(()) } fn ak_sys_auth_token_validate( From 229d05ec89d08c2f1a6410e611c33f1daf8eaeca Mon Sep 17 00:00:00 2001 From: Jens Langhammer Date: Sun, 11 Jan 2026 14:38:39 +0000 Subject: [PATCH 4/8] add common library --- ee/wcp/CMakeLists.txt | 5 ++ ee/wcp/Makefile | 28 +++--- ee/wcp/ak_common/CMakeLists.txt | 27 ++++++ .../ak_log.cpp | 15 ++-- .../ak_sentry.cpp | 8 +- ee/wcp/ak_common/include/ak_log.h | 16 ++++ .../include/ak_sentry.h | 4 +- .../include/ak_version.h | 2 +- ee/wcp/ak_common/include/strings.h | 6 ++ ee/wcp/ak_common/strings.cpp | 13 +++ ee/wcp/ak_cred_provider/CMakeLists.txt | 9 +- ee/wcp/ak_cred_provider/Credential.cpp | 8 +- ee/wcp/ak_cred_provider/Dll.cpp | 11 +-- ee/wcp/ak_cred_provider/Helpers.cpp | 9 +- ee/wcp/ak_cred_provider/Provider.cpp | 6 +- ee/wcp/ak_cred_provider/ak_cred_provider.def | 4 - ee/wcp/ak_cred_provider/include/Common.h | 3 +- ee/wcp/ak_cred_provider/include/ak_log.h | 7 -- ee/wcp/ak_lsa/CMakeLists.txt | 1 + ee/wcp/ak_lsa/Main.cpp | 86 +++++++++++-------- ee/wcp/ak_lsa/PrepareToken.cpp | 19 ++-- ee/wcp/ak_lsa/Utils.hpp | 51 +++-------- ee/wcp/cefexe/CMakeLists.txt | 1 + ee/wcp/cefexe/cefexe.cpp | 12 +-- ee/wcp/cefsimple/cefsimple_win.cc | 4 +- ee/wcp/cefsimple/simple_handler.h | 2 +- ee/wcp/libcef_dll/CMakeLists.txt | 3 +- 27 files changed, 198 insertions(+), 162 deletions(-) create mode 100644 ee/wcp/ak_common/CMakeLists.txt rename ee/wcp/{ak_cred_provider => ak_common}/ak_log.cpp (70%) rename ee/wcp/{ak_cred_provider => ak_common}/ak_sentry.cpp (89%) create mode 100644 ee/wcp/ak_common/include/ak_log.h rename ee/wcp/{ak_cred_provider => ak_common}/include/ak_sentry.h (71%) rename ee/wcp/{ak_cred_provider => ak_common}/include/ak_version.h (71%) create mode 100644 ee/wcp/ak_common/include/strings.h create mode 100644 ee/wcp/ak_common/strings.cpp delete mode 100644 ee/wcp/ak_cred_provider/include/ak_log.h diff --git a/ee/wcp/CMakeLists.txt b/ee/wcp/CMakeLists.txt index 20d3517c..e0aa3caf 100644 --- a/ee/wcp/CMakeLists.txt +++ b/ee/wcp/CMakeLists.txt @@ -358,6 +358,9 @@ set_property(TARGET sentry PROPERTY MSVC_RUNTIME_LIBRARY MultiThreaded) # Define CEF-based targets. # +add_subdirectory(ak_common) +set_property(TARGET ak_common PROPERTY MSVC_RUNTIME_LIBRARY MultiThreaded) + # Include the ak_cred_provider target. # Comes from the libcef_dll/CMakeLists.txt file in the binary distribution # directory. @@ -375,6 +378,8 @@ endif() # Set the project output directory for subprojects, such as `cefexe`, to copy their binaries to 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) # Copy cefexe executable and OpenSSL library file to the common build directory file(COPY "${OPENSSL_ROOT_DIR}/libcrypto-3-x64.dll" DESTINATION "${PROJECT_INSALL_DIR}") diff --git a/ee/wcp/Makefile b/ee/wcp/Makefile index bdc13fcd..9deff4b0 100644 --- a/ee/wcp/Makefile +++ b/ee/wcp/Makefile @@ -2,42 +2,38 @@ include ../../common.mk .ONESHELL: -TARGET := wcp +OUT_TARGET := wcp + +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' .PHONY: bump bump: - sed -i 's/#define AK_WCP_VERSION ".*"/#define AK_WCP_VERSION ${VERSION}/g' ${PWD}/ak_cred_provider/include/ak_version.h + sed -i 's/#define AK_VERSION ".*"/#define AK_VERSION ${VERSION}/g' ${PWD}/ak_common/include/ak_version.h .PHONY: clean clean: - cd "${TOP}" && rm -rf "bin/${TARGET}" + cd "${TOP}" && rm -rf "bin/${OUT_TARGET}" .PHONY: lint lint: - find "${PWD}/ak_cred_provider" ${FORMAT_FIND_ARGS} | xargs ${CLANG_FORMAT} --dry-run --Werror --verbose -i - find "${PWD}/ak_lsa" ${FORMAT_FIND_ARGS} | xargs ${CLANG_FORMAT} --dry-run --Werror --verbose -i - find "${PWD}/cefexe" ${FORMAT_FIND_ARGS} | xargs ${CLANG_FORMAT} --dry-run --Werror --verbose -i - find "${PWD}/cefsimple" ${FORMAT_FIND_ARGS} | xargs ${CLANG_FORMAT} --dry-run --Werror --verbose -i + $(foreach target,$(TARGETS),$(shell find "${PWD}/${target}" ${FORMAT_FIND_ARGS} | xargs ${CLANG_FORMAT} --dry-run --Werror --verbose -i)) .PHONY: lint-fix lint-fix: - find "${PWD}/ak_cred_provider" ${FORMAT_FIND_ARGS} | xargs ${CLANG_FORMAT} --verbose -i - find "${PWD}/ak_lsa" ${FORMAT_FIND_ARGS} | xargs ${CLANG_FORMAT} --verbose -i - find "${PWD}/cefexe" ${FORMAT_FIND_ARGS} | xargs ${CLANG_FORMAT} --verbose -i - find "${PWD}/cefsimple" ${FORMAT_FIND_ARGS} | xargs ${CLANG_FORMAT} --verbose -i + $(foreach target,$(TARGETS),$(shell find "${PWD}/${target}" ${FORMAT_FIND_ARGS} | xargs ${CLANG_FORMAT} --verbose -i)) .PHONY: build build: ${TOP}/hack/windows/setup.sh - mkdir -p ${TOP}/bin/${TARGET} - mkdir -p ${TOP}/cache/${TARGET} + mkdir -p ${TOP}/bin/${OUT_TARGET} + mkdir -p ${TOP}/cache/${OUT_TARGET} # To avoid the OpenSSL build using linux `link.exe` mv /usr/bin/link.exe /usr/bin/link_bak || true - powershell.exe -noprofile -executionpolicy bypass -file ./build.ps1 ${PWD} ${TOP} ${TARGET} - cp -r ${TOP}/cache/${TARGET}/ak_cred_provider/Release/* ${TOP}/bin/${TARGET}/ + powershell.exe -noprofile -executionpolicy bypass -file ./build.ps1 ${PWD} ${TOP} ${OUT_TARGET} + cp -r ${TOP}/cache/${OUT_TARGET}/ak_cred_provider/Release/* ${TOP}/bin/${OUT_TARGET}/ ifdef SENTRY_AUTH_TOKEN - @$(call sentry_upload_symbols,"${TOP}/bin/${TARGET}/") + @$(call sentry_upload_symbols,"${TOP}/bin/${OUT_TARGET}/") endif diff --git a/ee/wcp/ak_common/CMakeLists.txt b/ee/wcp/ak_common/CMakeLists.txt new file mode 100644 index 00000000..5d4ed4a7 --- /dev/null +++ b/ee/wcp/ak_common/CMakeLists.txt @@ -0,0 +1,27 @@ +project(ak_common) + +set(CMAKE_BUILD_TYPE Release) +set(CMAKE_CXX_STANDARD 20) + +set(SRCS + ak_log.cpp + ak_sentry.cpp + strings.cpp +) + +add_library(${PROJECT_NAME} STATIC + ${SRCS} +) + +# include_directories(${PROJECT_NAME} PUBLIC ..) +target_include_directories(${PROJECT_NAME} PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/include +) + +target_link_libraries(${PROJECT_NAME} + authentik_sys_bridge + spdlog + sentry +) +set_property(TARGET authentik_sys PROPERTY MSVC_RUNTIME_LIBRARY MultiThreaded) +set_property(TARGET authentik_sys_bridge PROPERTY MSVC_RUNTIME_LIBRARY MultiThreaded) diff --git a/ee/wcp/ak_cred_provider/ak_log.cpp b/ee/wcp/ak_common/ak_log.cpp similarity index 70% rename from ee/wcp/ak_cred_provider/ak_log.cpp rename to ee/wcp/ak_common/ak_log.cpp index ad67ed8d..4a23db6f 100644 --- a/ee/wcp/ak_cred_provider/ak_log.cpp +++ b/ee/wcp/ak_common/ak_log.cpp @@ -1,7 +1,5 @@ -#include "pch.h" - -#include "ak_log.h" -#include "ak_version.h" +#include "include/ak_log.h" +#include "include/ak_version.h" #include "spdlog/async.h" #include "spdlog/sinks/rotating_file_sink.h" #include "spdlog/sinks/win_eventlog_sink.h" @@ -13,7 +11,7 @@ bool g_logSetup; const auto _ak_log_max_size = 1024 * 1024 * 50; const auto _ak_log_max_files = 3; -void SetupLogs(const char* logger_name) { +void ak_setup_logs(const char* logger_name) { const auto logger = spdlog::rotating_logger_mt( logger_name, std::string(AK_PROGRAM_DATA).append("\\logs\\").append(logger_name).append(".log").c_str(), @@ -21,6 +19,11 @@ void SetupLogs(const char* logger_name) { spdlog::set_level(spdlog::level::debug); spdlog::flush_every(std::chrono::seconds(5)); spdlog::set_default_logger(logger); - SPDLOG_INFO("authentik Platform Credential Provider Version {}", AK_WCP_VERSION); + SPDLOG_INFO("authentik Platform {} Version {}", logger_name, AK_VERSION); g_logSetup = true; } + +void ak_teardown_logs() { + if (!g_logSetup) return; + spdlog::shutdown(); +} diff --git a/ee/wcp/ak_cred_provider/ak_sentry.cpp b/ee/wcp/ak_common/ak_sentry.cpp similarity index 89% rename from ee/wcp/ak_cred_provider/ak_sentry.cpp rename to ee/wcp/ak_common/ak_sentry.cpp index b38b154d..7b795173 100644 --- a/ee/wcp/ak_cred_provider/ak_sentry.cpp +++ b/ee/wcp/ak_common/ak_sentry.cpp @@ -1,4 +1,4 @@ -#include "ak_version.h" +#include "include/ak_version.h" #include #define SENTRY_BUILD_STATIC 1 @@ -17,12 +17,12 @@ static void ak_sentry_log_callback(sentry_level_t level, const char* message, va spdlog::get("sentry")->debug(formatted_message); } -void SentrySetup(const char* component) { +void ak_setup_sentry(const char* component) { if (g_sentrySetup) return; spdlog::register_logger(spdlog::default_logger()->clone("sentry")); std::string release = - std::string("ak-platform-wcp-").append(component).append("@").append(AK_WCP_VERSION); + std::string("ak-platform-wcp-").append(component).append("@").append(AK_VERSION); sentry_options_t* options = sentry_options_new(); sentry_options_set_database_path(options, std::string(AK_PROGRAM_DATA).append("\\wcp-sentry\\").c_str()); @@ -38,4 +38,4 @@ void SentrySetup(const char* component) { g_sentrySetup = true; } -void SentryShutdown() { sentry_shutdown(); } +void ak_teardown_sentry() { sentry_shutdown(); } diff --git a/ee/wcp/ak_common/include/ak_log.h b/ee/wcp/ak_common/include/ak_log.h new file mode 100644 index 00000000..8f5547f3 --- /dev/null +++ b/ee/wcp/ak_common/include/ak_log.h @@ -0,0 +1,16 @@ +// BISMILLAAHIRRAHMAANIRRAHEEM + +#pragma once +#include +#include + +// #ifdef __cplusplus +// extern "C" { +// #endif + +void ak_setup_logs(const char* logger_name); +void ak_teardown_logs(); + +// #ifdef __cplusplus +// } +// #endif diff --git a/ee/wcp/ak_cred_provider/include/ak_sentry.h b/ee/wcp/ak_common/include/ak_sentry.h similarity index 71% rename from ee/wcp/ak_cred_provider/include/ak_sentry.h rename to ee/wcp/ak_common/include/ak_sentry.h index 4a68829c..8c5a5942 100644 --- a/ee/wcp/ak_cred_provider/include/ak_sentry.h +++ b/ee/wcp/ak_common/include/ak_sentry.h @@ -2,8 +2,8 @@ #include -void SentrySetup(const char* component); -void SentryShutdown(); +void ak_setup_sentry(const char* component); +void ak_teardown_sentry(); static void ak_sentry_log_callback(sentry_level_t level, const char* message, va_list args, void* userdata); diff --git a/ee/wcp/ak_cred_provider/include/ak_version.h b/ee/wcp/ak_common/include/ak_version.h similarity index 71% rename from ee/wcp/ak_cred_provider/include/ak_version.h rename to ee/wcp/ak_common/include/ak_version.h index 735fd51b..7045367d 100644 --- a/ee/wcp/ak_cred_provider/include/ak_version.h +++ b/ee/wcp/ak_common/include/ak_version.h @@ -1,5 +1,5 @@ #pragma once -#define AK_WCP_VERSION "0.35.6" +#define AK_VERSION "0.35.6" #define AK_PROGRAM_DATA "C:\\ProgramData\\Authentik Security Inc" diff --git a/ee/wcp/ak_common/include/strings.h b/ee/wcp/ak_common/include/strings.h new file mode 100644 index 00000000..f6490f81 --- /dev/null +++ b/ee/wcp/ak_common/include/strings.h @@ -0,0 +1,6 @@ +#include +#include +#include + +std::wstring utf8_decode(const std::string& str); +std::string utf8_encode(const std::wstring& str); diff --git a/ee/wcp/ak_common/strings.cpp b/ee/wcp/ak_common/strings.cpp new file mode 100644 index 00000000..cee7523a --- /dev/null +++ b/ee/wcp/ak_common/strings.cpp @@ -0,0 +1,13 @@ +#include +#include +#include + +std::wstring utf8_decode(const std::string& str) { + std::wstring_convert> myconv; + return myconv.from_bytes(str); +} + +std::string utf8_encode(const std::wstring& str) { + std::wstring_convert> myconv; + return myconv.to_bytes(str); +} diff --git a/ee/wcp/ak_cred_provider/CMakeLists.txt b/ee/wcp/ak_cred_provider/CMakeLists.txt index 1894a855..b2738b39 100644 --- a/ee/wcp/ak_cred_provider/CMakeLists.txt +++ b/ee/wcp/ak_cred_provider/CMakeLists.txt @@ -16,10 +16,8 @@ set(CMAKE_CXX_STANDARD 20) # Add source to this project's executable. set(SRCS - "ak_sentry.cpp" "ClassFactory.cpp" "Credential.cpp" - "ak_log.cpp" "Dll.cpp" "GUIDs.cpp" "Helpers.cpp" @@ -32,7 +30,7 @@ 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 +add_library(${PROJECT_NAME} SHARED ${SRCS} ) @@ -49,14 +47,13 @@ if (MSVC) ) endif() -target_link_libraries(${PROJECT_NAME} PUBLIC +target_link_libraries(${PROJECT_NAME} ${CREDUI_LIB_PATH} ${SECUR32_LIB_PATH} ${SHLWAPI_LIB_PATH} + ak_common cefsimple authentik_sys_bridge - spdlog - sentry ) # TODO: Add tests and install targets if needed. diff --git a/ee/wcp/ak_cred_provider/Credential.cpp b/ee/wcp/ak_cred_provider/Credential.cpp index 6ecfcf5c..c1ede0fe 100644 --- a/ee/wcp/ak_cred_provider/Credential.cpp +++ b/ee/wcp/ak_cred_provider/Credential.cpp @@ -287,8 +287,8 @@ LRESULT APIENTRY Credential::WndProc(_In_ HWND hWnd, _In_ UINT uMsg, _In_ WPARAM // {CEFLaunch(pData->hInstance, 0);}); spdlog::debug(std::string("(m_oCefAppData.pCefApp) before: " + - std::to_string((size_t)((m_oCefAppData.pCefApp).get()))) - .c_str()); + std::to_string((size_t)((m_oCefAppData.pCefApp).get()))) + .c_str()); if (!(m_oCefAppData.pCefApp)) { if (m_pProvCallSet) { m_oCefAppData.SetInit(false); @@ -303,8 +303,8 @@ LRESULT APIENTRY Credential::WndProc(_In_ HWND hWnd, _In_ UINT uMsg, _In_ WPARAM } } spdlog::debug(std::string("(m_oCefAppData.pCefApp) after: " + - std::to_string((size_t)((m_oCefAppData.pCefApp).get()))) - .c_str()); + std::to_string((size_t)((m_oCefAppData.pCefApp).get()))) + .c_str()); if ((m_oCefAppData.pCefApp)) { spdlog::debug("WndProc:: CEFLaunch"); pData->strUserToken = ""; diff --git a/ee/wcp/ak_cred_provider/Dll.cpp b/ee/wcp/ak_cred_provider/Dll.cpp index 22b834b7..d808f4d7 100644 --- a/ee/wcp/ak_cred_provider/Dll.cpp +++ b/ee/wcp/ak_cred_provider/Dll.cpp @@ -4,7 +4,8 @@ #include "Dll.h" -#include "ak_sentry.h" +#include "ak_common/include/ak_sentry.h" +#include "ak_common/include/ak_log.h" #include "include/cef_command_line.h" #include "include/cef_sandbox_win.h" #include "spdlog/spdlog.h" @@ -45,8 +46,8 @@ DllMain(__in HINSTANCE hinstDll, __in DWORD dwReason, __in LPVOID lpReserved) { switch (dwReason) { case DLL_PROCESS_ATTACH: { SetPaths(); - SetupLogs("ak_cred_provider"); - SentrySetup("ak_cred_provider"); + ak_setup_logs("ak_cred_provider"); + ak_setup_sentry("ak_cred_provider"); SPDLOG_INFO("DllMain::DLL_PROCESS_ATTACH"); DisableThreadLibraryCalls(hinstDll); @@ -60,8 +61,8 @@ DllMain(__in HINSTANCE hinstDll, __in DWORD dwReason, __in LPVOID lpReserved) { break; case DLL_PROCESS_DETACH: SPDLOG_INFO("DllMain::DLL_PROCESS_DETACH"); - SentryShutdown(); - spdlog::shutdown(); + ak_teardown_sentry(); + ak_teardown_logs(); break; } return TRUE; diff --git a/ee/wcp/ak_cred_provider/Helpers.cpp b/ee/wcp/ak_cred_provider/Helpers.cpp index 6e744182..392f352f 100644 --- a/ee/wcp/ak_cred_provider/Helpers.cpp +++ b/ee/wcp/ak_cred_provider/Helpers.cpp @@ -1,8 +1,8 @@ #include "pch.h" #include "Helpers.h" -#include -#include +#include "ak_common/include/strings.h" + // // Copies the field descriptor pointed to by rcpfd into a buffer allocated // using CoTaskMemAlloc. Returns that buffer in ppcpfd. @@ -596,8 +596,3 @@ SplitDomainAndUsername(_In_ PCWSTR pszQualifiedUserName, } return hr; } - -std::wstring utf8_decode(const std::string& str) { - std::wstring_convert> myconv; - return myconv.from_bytes(str); -} diff --git a/ee/wcp/ak_cred_provider/Provider.cpp b/ee/wcp/ak_cred_provider/Provider.cpp index c378ac7b..7c9d757b 100644 --- a/ee/wcp/ak_cred_provider/Provider.cpp +++ b/ee/wcp/ak_cred_provider/Provider.cpp @@ -1,8 +1,8 @@ #include "pch.h" -#include "ak_log.h" +#include "ak_common/include/ak_log.h" +#include "ak_common/include/ak_version.h" #include "Provider.h" -#include "ak_version.h" #include "authentik_sys_bridge/ffi.h" #include "rust/cxx.h" #include @@ -97,7 +97,7 @@ void Provider::SetCefApp(sHookData* pData) { settings.log_severity = LOGSEVERITY_INFO; std::string strUserAgent = std::string("authentik Platform/WCP/CredProvider@") - .append(AK_WCP_VERSION) + .append(AK_VERSION) .append(" (CEF ") .append(CEF_VERSION) .append(")"); diff --git a/ee/wcp/ak_cred_provider/ak_cred_provider.def b/ee/wcp/ak_cred_provider/ak_cred_provider.def index c64ec94e..296c8bbf 100644 --- a/ee/wcp/ak_cred_provider/ak_cred_provider.def +++ b/ee/wcp/ak_cred_provider/ak_cred_provider.def @@ -9,8 +9,4 @@ EXPORTS DllCanUnloadNow PRIVATE DllGetClassObject PRIVATE CefExecuteProcess - SetupLogs - SentrySetup - SentryShutdown - CEFLaunch ; //- todo: remove diff --git a/ee/wcp/ak_cred_provider/include/Common.h b/ee/wcp/ak_cred_provider/include/Common.h index 3e211f94..943f8fe2 100644 --- a/ee/wcp/ak_cred_provider/include/Common.h +++ b/ee/wcp/ak_cred_provider/include/Common.h @@ -1,7 +1,8 @@ #pragma once #include "pch.h" -#include "ak_log.h" +#include "ak_common/include/ak_log.h" + // The indexes of each of the fields in the credential provider's tiles. enum FIELD_ID { FI_TILEIMAGE = 0, diff --git a/ee/wcp/ak_cred_provider/include/ak_log.h b/ee/wcp/ak_cred_provider/include/ak_log.h deleted file mode 100644 index eceb5ac9..00000000 --- a/ee/wcp/ak_cred_provider/include/ak_log.h +++ /dev/null @@ -1,7 +0,0 @@ -// BISMILLAAHIRRAHMAANIRRAHEEM - -#pragma once -#include -#include - -void SetupLogs(const char* logger_name); diff --git a/ee/wcp/ak_lsa/CMakeLists.txt b/ee/wcp/ak_lsa/CMakeLists.txt index ef0b3614..a19f2005 100644 --- a/ee/wcp/ak_lsa/CMakeLists.txt +++ b/ee/wcp/ak_lsa/CMakeLists.txt @@ -26,6 +26,7 @@ target_link_libraries(${PROJECT_NAME} ${SECUR32_LIB_PATH} ${SHLWAPI_LIB_PATH} authentik_sys_bridge + ak_common spdlog ) set_property(TARGET ak_lsa PROPERTY MSVC_RUNTIME_LIBRARY MultiThreaded) diff --git a/ee/wcp/ak_lsa/Main.cpp b/ee/wcp/ak_lsa/Main.cpp index c52a44ae..8e67ac79 100644 --- a/ee/wcp/ak_lsa/Main.cpp +++ b/ee/wcp/ak_lsa/Main.cpp @@ -1,6 +1,10 @@ #include "PrepareToken.hpp" #include "PrepareProfile.hpp" #include "Utils.hpp" +#include "ak_common/include/ak_log.h" +#include "ak_common/include/ak_sentry.h" +#include "ak_common/include/strings.h" +#include "spdlog/spdlog.h" // exported symbols #pragma comment(linker, "/export:SpLsaModeInitialize") @@ -9,39 +13,42 @@ LSA_SECPKG_FUNCTION_TABLE FunctionTable; NTSTATUS NTAPI SpInitialize(_In_ ULONG_PTR PackageId, _In_ SECPKG_PARAMETERS* Parameters, _In_ LSA_SECPKG_FUNCTION_TABLE* functionTable) { - LogMessage("SpInitialize"); + ak_setup_logs("lsa"); + ak_setup_sentry("lsa"); - LogMessage(" PackageId: %u", PackageId); - LogMessage(" Version: %u", Parameters->Version); + spdlog::debug("SpInitialize"); + + spdlog::debug(" PackageId: %u", PackageId); + spdlog::debug(" Version: %u", Parameters->Version); { ULONG state = Parameters->MachineState; - LogMessage(" MachineState:"); + spdlog::debug(" MachineState:"); if (state & SECPKG_STATE_ENCRYPTION_PERMITTED) { state &= ~SECPKG_STATE_ENCRYPTION_PERMITTED; - LogMessage(" - ENCRYPTION_PERMITTED"); + spdlog::debug(" - ENCRYPTION_PERMITTED"); } if (state & SECPKG_STATE_STRONG_ENCRYPTION_PERMITTED) { state &= ~SECPKG_STATE_STRONG_ENCRYPTION_PERMITTED; - LogMessage(" - STRONG_ENCRYPTION_PERMITTED"); + spdlog::debug(" - STRONG_ENCRYPTION_PERMITTED"); } if (state & SECPKG_STATE_DOMAIN_CONTROLLER) { state &= ~SECPKG_STATE_DOMAIN_CONTROLLER; - LogMessage(" - DOMAIN_CONTROLLER"); + spdlog::debug(" - DOMAIN_CONTROLLER"); } if (state & SECPKG_STATE_WORKSTATION) { state &= ~SECPKG_STATE_WORKSTATION; - LogMessage(" - WORKSTATION"); + spdlog::debug(" - WORKSTATION"); } if (state & SECPKG_STATE_STANDALONE) { state &= ~SECPKG_STATE_STANDALONE; - LogMessage(" - STANDALONE"); + spdlog::debug(" - STANDALONE"); } if (state) { // print resudual flags not already covered - LogMessage(" * Unknown flags: 0x%X", state); + spdlog::debug(" * Unknown flags: 0x%X", state); } } - LogMessage(" SetupMode: %u", Parameters->SetupMode); + spdlog::debug(" SetupMode: %u", Parameters->SetupMode); // parameters not logged Parameters->DomainSid; Parameters->DomainName; @@ -50,18 +57,20 @@ NTSTATUS NTAPI SpInitialize(_In_ ULONG_PTR PackageId, _In_ SECPKG_PARAMETERS* Pa FunctionTable = *functionTable; // copy function pointer table - LogMessage(" return STATUS_SUCCESS"); + spdlog::debug(" return STATUS_SUCCESS"); return STATUS_SUCCESS; } NTSTATUS NTAPI SpShutDown() { - LogMessage("SpShutDown"); - LogMessage(" return STATUS_SUCCESS"); + ak_teardown_sentry(); + spdlog::debug("SpShutDown"); + spdlog::debug(" return STATUS_SUCCESS"); + ak_teardown_logs(); return STATUS_SUCCESS; } NTSTATUS NTAPI SpGetInfo(_Out_ SecPkgInfoW* PackageInfo) { - LogMessage("SpGetInfo"); + spdlog::debug("SpGetInfo"); // return security package metadata PackageInfo->fCapabilities = SECPKG_FLAG_LOGON // supports LsaLogonUser @@ -72,7 +81,7 @@ NTSTATUS NTAPI SpGetInfo(_Out_ SecPkgInfoW* PackageInfo) { PackageInfo->Name = (wchar_t*)L"ak_lsa"; PackageInfo->Comment = (wchar_t*)L"authentik Token Authentication"; - LogMessage(" return STATUS_SUCCESS"); + spdlog::debug(" return STATUS_SUCCESS"); return STATUS_SUCCESS; } @@ -87,7 +96,7 @@ NTSTATUS LsaApLogonUser(_In_ PLSA_CLIENT_REQUEST ClientRequest, _In_ SECURITY_LO _Out_ LSA_TOKEN_INFORMATION_TYPE* TokenInformationType, _Outptr_ VOID** TokenInformation, _Out_ LSA_UNICODE_STRING** AccountName, _Out_ LSA_UNICODE_STRING** AuthenticatingAuthority) { - LogMessage("LsaApLogonUser"); + spdlog::debug("LsaApLogonUser"); { // clear output arguments first in case of failure @@ -102,13 +111,13 @@ NTSTATUS LsaApLogonUser(_In_ PLSA_CLIENT_REQUEST ClientRequest, _In_ SECURITY_LO } // input arguments - LogMessage(" LogonType: %i", LogonType); // Interactive=2, RemoteInteractive=10 + spdlog::debug(" LogonType: {}", (int)LogonType); // Interactive=2, RemoteInteractive=10 ClientBufferBase; - LogMessage(" ProtocolSubmitBuffer size: %i", SubmitBufferSize); + spdlog::debug(" ProtocolSubmitBuffer size: {}", (int)SubmitBufferSize); // deliberately restrict supported logontypes if ((LogonType != Interactive) && (LogonType != RemoteInteractive)) { - LogMessage(" return STATUS_NOT_IMPLEMENTED (unsupported LogonType)"); + spdlog::debug(" return STATUS_NOT_IMPLEMENTED (unsupported LogonType)"); return STATUS_NOT_IMPLEMENTED; } @@ -116,7 +125,7 @@ NTSTATUS LsaApLogonUser(_In_ PLSA_CLIENT_REQUEST ClientRequest, _In_ SECURITY_LO auto* logonInfo = (MSV1_0_INTERACTIVE_LOGON*)ProtocolSubmitBuffer; { if (SubmitBufferSize < sizeof(MSV1_0_INTERACTIVE_LOGON)) { - LogMessage(" ERROR: SubmitBufferSize too small"); + spdlog::debug(" ERROR: SubmitBufferSize too small"); return STATUS_INVALID_PARAMETER; } @@ -127,9 +136,8 @@ NTSTATUS LsaApLogonUser(_In_ PLSA_CLIENT_REQUEST ClientRequest, _In_ SECURITY_LO logonInfo->Password.Buffer = (wchar_t*)((BYTE*)logonInfo + (size_t)logonInfo->Password.Buffer); } - LogMessage(" ak_sys_auth_token_validate UserName: '%ls'", ToWstring(logonInfo->UserName).c_str()); if (!ValidateToken(logonInfo)) { - LogMessage(" ValidateToken: failed"); + spdlog::debug(" ValidateToken: failed"); return STATUS_ACCOUNT_RESTRICTION; } // assign output arguments @@ -138,7 +146,7 @@ NTSTATUS LsaApLogonUser(_In_ PLSA_CLIENT_REQUEST ClientRequest, _In_ SECURITY_LO wchar_t computerName[MAX_COMPUTERNAME_LENGTH + 1] = {}; DWORD computerNameSize = ARRAYSIZE(computerName); if (!GetComputerNameW(computerName, &computerNameSize)) { - LogMessage(" return STATUS_INTERNAL_ERROR (GetComputerNameW failed)"); + spdlog::debug(" return STATUS_INTERNAL_ERROR (GetComputerNameW failed)"); return STATUS_INTERNAL_ERROR; } @@ -156,16 +164,16 @@ NTSTATUS LsaApLogonUser(_In_ PLSA_CLIENT_REQUEST ClientRequest, _In_ SECURITY_LO { // assign "LogonId" output argument if (!AllocateLocallyUniqueId(LogonId)) { - LogMessage(" ERROR: AllocateLocallyUniqueId failed"); + spdlog::debug(" ERROR: AllocateLocallyUniqueId failed"); return STATUS_FAIL_FAST_EXCEPTION; } NTSTATUS status = FunctionTable.CreateLogonSession(LogonId); if (status != STATUS_SUCCESS) { - LogMessage(" ERROR: CreateLogonSession failed with err: 0x%x", status); + spdlog::debug(" ERROR: CreateLogonSession failed with err: 0x%x", status); return status; } - LogMessage(" LogonId: High=0x%x , Low=0x%x", LogonId->HighPart, LogonId->LowPart); + spdlog::debug(" LogonId: High=0x%x , Low=0x%x", LogonId->HighPart, LogonId->LowPart); } *SubStatus = STATUS_SUCCESS; // reason for error @@ -176,7 +184,7 @@ NTSTATUS LsaApLogonUser(_In_ PLSA_CLIENT_REQUEST ClientRequest, _In_ SECURITY_LO NTSTATUS subStatus = 0; NTSTATUS status = UserNameToToken(&logonInfo->UserName, &tokenInfo, &subStatus); if (status != STATUS_SUCCESS) { - LogMessage("ERROR: UserNameToToken failed with err: 0x%x", status); + spdlog::debug("ERROR: UserNameToToken failed with err: 0x%x", status); *SubStatus = subStatus; return status; } @@ -187,7 +195,8 @@ NTSTATUS LsaApLogonUser(_In_ PLSA_CLIENT_REQUEST ClientRequest, _In_ SECURITY_LO { // assign "AccountName" output argument - LogMessage(" AccountName: %ls", ToWstring(logonInfo->UserName).c_str()); + std::wstring username = ToWstring(logonInfo->UserName).c_str(); + spdlog::debug(" AccountName: {}", utf8_encode(username)); *AccountName = CreateLsaUnicodeString(logonInfo->UserName.Buffer, logonInfo->UserName.Length); // mandatory } @@ -198,11 +207,12 @@ NTSTATUS LsaApLogonUser(_In_ PLSA_CLIENT_REQUEST ClientRequest, _In_ SECURITY_LO (LSA_UNICODE_STRING*)FunctionTable.AllocateLsaHeap(sizeof(LSA_UNICODE_STRING)); if (logonInfo->LogonDomainName.Length > 0) { - LogMessage(" AuthenticatingAuthority: %ls", ToWstring(logonInfo->LogonDomainName).c_str()); + std::wstring authority = ToWstring(logonInfo->LogonDomainName).c_str(); + spdlog::debug(" AuthenticatingAuthority: {}", utf8_encode(authority)); *AuthenticatingAuthority = CreateLsaUnicodeString(logonInfo->LogonDomainName.Buffer, logonInfo->LogonDomainName.Length); } else { - LogMessage(" AuthenticatingAuthority: "); + spdlog::debug(" AuthenticatingAuthority: "); **AuthenticatingAuthority = { .Length = 0, .MaximumLength = 0, @@ -211,14 +221,14 @@ NTSTATUS LsaApLogonUser(_In_ PLSA_CLIENT_REQUEST ClientRequest, _In_ SECURITY_LO } } - LogMessage(" return STATUS_SUCCESS"); + spdlog::debug(" return STATUS_SUCCESS"); return STATUS_SUCCESS; } void LsaApLogonTerminated(_In_ LUID* LogonId) { - LogMessage("LsaApLogonTerminated"); - LogMessage(" LogonId: High=0x%x , Low=0x%x", LogonId->HighPart, LogonId->LowPart); - LogMessage(" return"); + spdlog::debug("LsaApLogonTerminated"); + spdlog::debug(" LogonId: High=0x%x , Low=0x%x", LogonId->HighPart, LogonId->LowPart); + spdlog::debug(" return"); } SECPKG_FUNCTION_TABLE SecurityPackageFunctionTable = { @@ -271,13 +281,13 @@ SECPKG_FUNCTION_TABLE SecurityPackageFunctionTable = { extern "C" NTSTATUS NTAPI SpLsaModeInitialize(_In_ ULONG LsaVersion, _Out_ ULONG* PackageVersion, _Out_ SECPKG_FUNCTION_TABLE** ppTables, _Out_ ULONG* pcTables) { - LogMessage("SpLsaModeInitialize"); - LogMessage(" LsaVersion %u", LsaVersion); + spdlog::debug("SpLsaModeInitialize"); + spdlog::debug(" LsaVersion %u", LsaVersion); *PackageVersion = SECPKG_INTERFACE_VERSION; *ppTables = &SecurityPackageFunctionTable; *pcTables = 1; - LogMessage(" return STATUS_SUCCESS"); + spdlog::debug(" return STATUS_SUCCESS"); return STATUS_SUCCESS; } diff --git a/ee/wcp/ak_lsa/PrepareToken.cpp b/ee/wcp/ak_lsa/PrepareToken.cpp index 9f81e263..013b0333 100644 --- a/ee/wcp/ak_lsa/PrepareToken.cpp +++ b/ee/wcp/ak_lsa/PrepareToken.cpp @@ -1,6 +1,7 @@ #include "PrepareToken.hpp" #include #include "Utils.hpp" +#include "spdlog/spdlog.h" #pragma comment(lib, "Netapi32.lib") @@ -18,7 +19,7 @@ static bool NameToSid(const wchar_t* username, PSID* userSid) { &referencedDomainNameLen, &Use); if (!res) { DWORD err = GetLastError(); - LogMessage(" LookupAccountNameW failed (err %u)", err); + spdlog::debug(" LookupAccountNameW failed (err %u)", err); return false; } @@ -45,7 +46,7 @@ static bool GetGroups(const wchar_t* UserName, GROUP_USERS_INFO_1** lpGroupInfo, DWORD status = NetUserGetGroups(NULL, UserName, 1, (BYTE**)lpGroupInfo, MAX_PREFERRED_LENGTH, &NumberOfEntries, pTotalEntries); if (status != NERR_Success) { - LogMessage("ERROR: NetUserGetGroups failed with error %u", status); + spdlog::debug("ERROR: NetUserGetGroups failed with error %u", status); return false; } return true; @@ -57,7 +58,7 @@ static bool GetLocalGroups(const wchar_t* UserName, GROUP_USERS_INFO_0** lpGroup DWORD status = NetUserGetLocalGroups(NULL, UserName, 0, 0, (BYTE**)lpGroupInfo, MAX_PREFERRED_LENGTH, &NumberOfEntries, pTotalEntries); if (status != NERR_Success) { - LogMessage("ERROR: NetUserGetLocalGroups failed with error %u", status); + spdlog::debug("ERROR: NetUserGetLocalGroups failed with error %u", status); return false; } return true; @@ -79,11 +80,11 @@ NTSTATUS UserNameToToken(__in LSA_UNICODE_STRING* AccountName, token->ExpirationTime = Forever; PSID userSid = nullptr; + const wchar_t* user = username.c_str(); { // configure "User" if (!NameToSid(username.c_str(), &userSid)) return STATUS_FAIL_FAST_EXCEPTION; - - LogMessage(" User.User: %ls", username.c_str()); + spdlog::debug(" User.User: {}", utf8_encode(username)); token->User.User = { .Sid = userSid, .Attributes = 0, @@ -94,17 +95,17 @@ NTSTATUS UserNameToToken(__in LSA_UNICODE_STRING* AccountName, // configure "Groups" DWORD NumberOfGroups = 0; GROUP_USERS_INFO_1* pGroupInfo = nullptr; - if (!GetGroups(username.c_str(), &pGroupInfo, &NumberOfGroups)) { + if (!GetGroups(user, &pGroupInfo, &NumberOfGroups)) { return STATUS_FAIL_FAST_EXCEPTION; } - LogMessage(" NumberOfGroups: %u", NumberOfGroups); + spdlog::debug(" NumberOfGroups: %u", NumberOfGroups); DWORD NumberOfLocalGroups = 0; GROUP_USERS_INFO_0* pLocalGroupInfo = nullptr; - if (!GetLocalGroups(username.c_str(), &pLocalGroupInfo, &NumberOfLocalGroups)) { + if (!GetLocalGroups(user, &pLocalGroupInfo, &NumberOfLocalGroups)) { return STATUS_FAIL_FAST_EXCEPTION; } - LogMessage(" NumberOfLocalGroups: %u", NumberOfLocalGroups); + spdlog::debug(" NumberOfLocalGroups: %u", NumberOfLocalGroups); TOKEN_GROUPS* tokenGroups = (TOKEN_GROUPS*)FunctionTable.AllocateLsaHeap( FIELD_OFFSET(TOKEN_GROUPS, Groups[NumberOfGroups + NumberOfLocalGroups])); diff --git a/ee/wcp/ak_lsa/Utils.hpp b/ee/wcp/ak_lsa/Utils.hpp index 6046eaf1..0f2fefb9 100644 --- a/ee/wcp/ak_lsa/Utils.hpp +++ b/ee/wcp/ak_lsa/Utils.hpp @@ -4,25 +4,12 @@ #include #include "authentik_sys_bridge/ffi.h" #include "rust/cxx.h" +#include "ak_common/include/ak_log.h" +#include "ak_common/include/strings.h" +#include "spdlog/spdlog.h" extern LSA_SECPKG_FUNCTION_TABLE FunctionTable; -inline void LogMessage(const char* message, ...) { - // append to log file - FILE* file = nullptr; - fopen_s(&file, "C:\\ak_lsa.txt", "a+"); - if (!file) return; - { - // print variadic message - va_list args; - va_start(args, message); - _vfprintf_l(file, message, NULL, args); - va_end(args); - } - fprintf(file, "\n"); - fclose(file); -} - /** Allocate and create a new LSA_STRING object. Assumes that "FunctionTable" is initialized. */ inline LSA_STRING* CreateLsaString(const std::string& msg) { @@ -68,27 +55,17 @@ inline void AssignLsaUnicodeString(const LSA_UNICODE_STRING& source, LSA_UNICODE dest.MaximumLength = source.Length; } -// Convert a wide Unicode string to an UTF8 string -inline std::string utf8_encode(const std::wstring& wstr) { - if (wstr.empty()) return std::string(); - int size_needed = - WideCharToMultiByte(CP_UTF8, 0, &wstr[0], (int)wstr.size(), NULL, 0, NULL, NULL); - std::string strTo(size_needed, 0); - WideCharToMultiByte(CP_UTF8, 0, &wstr[0], (int)wstr.size(), &strTo[0], size_needed, NULL, NULL); - return strTo; -} - inline PWSTR decryptPassword(MSV1_0_INTERACTIVE_LOGON* pkil) { CRED_PROTECTION_TYPE ProtectionType; ULONG Length = pkil->Password.Length; - LogMessage(" decryptPassword: Fixing pointers..."); + spdlog::debug(" decryptPassword: Fixing pointers..."); PWSTR pszCredentials = (PWSTR)FunctionTable.AllocateLsaHeap(Length + sizeof(WCHAR)); memcpy(pszCredentials, pkil->Password.Buffer, pkil->Password.MaximumLength); - LogMessage(" decryptPassword: Checking if password is encrypted..."); + spdlog::debug(" decryptPassword: Checking if password is encrypted..."); if (!CredIsProtectedW(pszCredentials, &ProtectionType)) { - LogMessage(" decryptPassword: Password is not encrypted"); + spdlog::debug(" decryptPassword: Password is not encrypted"); return pszCredentials; } @@ -99,9 +76,9 @@ inline PWSTR decryptPassword(MSV1_0_INTERACTIVE_LOGON* pkil) { HRESULT status; if (ProtectionType != CredUnprotected) { - LogMessage(" decryptPassword: Password is protected"); + spdlog::debug(" decryptPassword: Password is protected"); while (true) { - LogMessage(" decryptPassword: CredUnprotectW call"); + spdlog::debug(" decryptPassword: CredUnprotectW call"); if (CredUnprotectW(FALSE, pszCredentials, cchCredentials, pszPin, &cchPin)) { break; } @@ -110,32 +87,30 @@ inline PWSTR decryptPassword(MSV1_0_INTERACTIVE_LOGON* pkil) { } auto err = GetLastError(); if (err == ERROR_INSUFFICIENT_BUFFER) { - LogMessage(" decryptPassword: ERROR_INSUFFICIENT_BUFFER, %d", cchPin); + spdlog::debug(" decryptPassword: ERROR_INSUFFICIENT_BUFFER, %d", cchPin); // pszPin = (PWSTR)FunctionTable.AllocatePrivateHeap(cchPin * sizeof(WCHAR)); pszPin = (PWSTR)alloca(cchPin * sizeof(WCHAR)); } } } else { - LogMessage(" decryptPassword: PW was not encrypted"); + spdlog::debug(" decryptPassword: PW was not encrypted"); pszPin = pszCredentials; cchPin = cchCredentials; } - LogMessage(" decryptPassword: result: %ls", pszPin); return pszPin; } inline bool ValidateToken(MSV1_0_INTERACTIVE_LOGON* pkil) { try { - LogMessage(" ak_sys_auth_token_validate: Decrypting password"); + spdlog::debug(" ak_sys_auth_token_validate: Decrypting password"); auto pw = decryptPassword(pkil); TokenResponse validatedToken; - LogMessage(" ak_sys_auth_token_validate Token: '%ls'", pw); if (ak_sys_auth_token_validate(utf8_encode(pw), validatedToken)) { - LogMessage(" ak_sys_auth_token_validate Succeeded"); + spdlog::debug(" ak_sys_auth_token_validate Succeeded"); return true; } } catch (const rust::Error& ex) { - LogMessage(" ak_sys_auth_token_validate Error: %s", ex.what()); + spdlog::debug(" ak_sys_auth_token_validate Error: %s", ex.what()); return false; } return false; diff --git a/ee/wcp/cefexe/CMakeLists.txt b/ee/wcp/cefexe/CMakeLists.txt index 86569ce1..a861d17f 100644 --- a/ee/wcp/cefexe/CMakeLists.txt +++ b/ee/wcp/cefexe/CMakeLists.txt @@ -27,6 +27,7 @@ target_include_directories(${PROJECT_NAME} PUBLIC ..) target_link_libraries(${PROJECT_NAME} # ${LIBCEF_LIB_PATH} #//- todo: revise if really needed + ak_common ak_cred_provider # ${LIBCEF_DLL_WRAPPER_LIB_PATH} #- # ${CEF_STANDARD_LIBS} diff --git a/ee/wcp/cefexe/cefexe.cpp b/ee/wcp/cefexe/cefexe.cpp index 2d612c1a..77336474 100644 --- a/ee/wcp/cefexe/cefexe.cpp +++ b/ee/wcp/cefexe/cefexe.cpp @@ -1,8 +1,8 @@ #include "cefsimple/simple_app.h" #include "include/cef_app.h" -#include "ak_cred_provider/include/ak_log.h" -#include "ak_cred_provider/include/ak_sentry.h" +#include "ak_common/include/ak_log.h" +#include "ak_common/include/ak_sentry.h" #include "spdlog/spdlog.h" #include // #pragma comment(lib,"libcef.lib") @@ -19,8 +19,8 @@ int APIENTRY wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmd CefMainArgs main_args(hInst); - SetupLogs("cefexe"); - SentrySetup("cefexe"); + ak_setup_logs("cefexe"); + ak_setup_sentry("cefexe"); SPDLOG_DEBUG("wWinMain"); // printf("> %d\n", argc); @@ -45,7 +45,7 @@ int APIENTRY wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmd } catch (const std::exception&) { SPDLOG_DEBUG("CefExecuteProcess... catch...!"); } - // spdlog::shutdown(); - SentryShutdown(); + ak_teardown_sentry(); + ak_teardown_logs(); return ret; } diff --git a/ee/wcp/cefsimple/cefsimple_win.cc b/ee/wcp/cefsimple/cefsimple_win.cc index f313d777..855fcdf8 100644 --- a/ee/wcp/cefsimple/cefsimple_win.cc +++ b/ee/wcp/cefsimple/cefsimple_win.cc @@ -9,10 +9,10 @@ // #include "cefsimple/simple_app.h" #include "cefsimple/simple_handler.h" #include "cefsimple/cefsimple_win.h" -#include "ak_cred_provider/include/ak_log.h" +#include "ak_common/include/ak_log.h" +#include #include "crypt.h" #include "Credential.h" -#include extern std::string g_strPath; diff --git a/ee/wcp/cefsimple/simple_handler.h b/ee/wcp/cefsimple/simple_handler.h index a8eb7503..dff15ad3 100644 --- a/ee/wcp/cefsimple/simple_handler.h +++ b/ee/wcp/cefsimple/simple_handler.h @@ -98,7 +98,7 @@ class SimpleHandler : public CefClient, if (strURL.substr(0, strKey.length()) == strKey) { spdlog::debug("URL inhibited: ", strURL.c_str()); spdlog::debug("OnBeforeResourceLoad ProcessID: ", std::to_string(GetCurrentProcessId()), - ", ThreadID: ", std::to_string(GetCurrentThreadId())); + ", ThreadID: ", std::to_string(GetCurrentThreadId())); Hide(); m_pData->UpdateStatus(L"Authenticating, please wait..."); std::string validatedToken; diff --git a/ee/wcp/libcef_dll/CMakeLists.txt b/ee/wcp/libcef_dll/CMakeLists.txt index 9575488a..1f3ea207 100644 --- a/ee/wcp/libcef_dll/CMakeLists.txt +++ b/ee/wcp/libcef_dll/CMakeLists.txt @@ -901,8 +901,6 @@ set(WCP_PATH "../${WCP}") set(WCP_SRCS "${WCP_PATH}/ClassFactory.cpp" "${WCP_PATH}/Credential.cpp" - "${WCP_PATH}/ak_sentry.cpp" - "${WCP_PATH}/ak_log.cpp" "${WCP_PATH}/Dll.cpp" "${WCP_PATH}/GUIDs.cpp" "${WCP_PATH}/Helpers.cpp" @@ -984,6 +982,7 @@ target_link_libraries(${CEF_TARGET} ${CRYPT32_LIB_PATH} ${NETAPI32_LIB_PATH} jwt-cpp + ak_common authentik_sys_bridge spdlog sentry From ecc8f41a536efd0a84a8dca0df2966c9bdc4e629 Mon Sep 17 00:00:00 2001 From: Jens Langhammer Date: Sun, 11 Jan 2026 14:40:44 +0000 Subject: [PATCH 5/8] cleanup --- ee/wcp/ak_lsa/CMakeLists.txt | 5 ++++- ee/wcp/ak_lsa/ak.cpp | 0 ee/wcp/ak_lsa/{ => include}/PrepareProfile.hpp | 0 ee/wcp/ak_lsa/{ => include}/PrepareToken.hpp | 0 4 files changed, 4 insertions(+), 1 deletion(-) delete mode 100644 ee/wcp/ak_lsa/ak.cpp rename ee/wcp/ak_lsa/{ => include}/PrepareProfile.hpp (100%) rename ee/wcp/ak_lsa/{ => include}/PrepareToken.hpp (100%) diff --git a/ee/wcp/ak_lsa/CMakeLists.txt b/ee/wcp/ak_lsa/CMakeLists.txt index a19f2005..93a5d5bd 100644 --- a/ee/wcp/ak_lsa/CMakeLists.txt +++ b/ee/wcp/ak_lsa/CMakeLists.txt @@ -7,7 +7,6 @@ set(SRCS PrepareProfile.cpp PrepareToken.cpp Main.cpp - ak.cpp ) find_library(CREDUI_LIB_PATH Credui.lib) @@ -19,6 +18,10 @@ add_library(${PROJECT_NAME} SHARED ) target_compile_definitions(${PROJECT_NAME} PUBLIC UNICODE _UNICODE SECURITY_WIN32) +include_directories( + include +) + include_directories(${PROJECT_NAME} PUBLIC ..) target_link_libraries(${PROJECT_NAME} diff --git a/ee/wcp/ak_lsa/ak.cpp b/ee/wcp/ak_lsa/ak.cpp deleted file mode 100644 index e69de29b..00000000 diff --git a/ee/wcp/ak_lsa/PrepareProfile.hpp b/ee/wcp/ak_lsa/include/PrepareProfile.hpp similarity index 100% rename from ee/wcp/ak_lsa/PrepareProfile.hpp rename to ee/wcp/ak_lsa/include/PrepareProfile.hpp diff --git a/ee/wcp/ak_lsa/PrepareToken.hpp b/ee/wcp/ak_lsa/include/PrepareToken.hpp similarity index 100% rename from ee/wcp/ak_lsa/PrepareToken.hpp rename to ee/wcp/ak_lsa/include/PrepareToken.hpp From 5ddc4e484beff9715a91444b79482e04c060611f Mon Sep 17 00:00:00 2001 From: Jens Langhammer Date: Sun, 11 Jan 2026 16:11:29 +0000 Subject: [PATCH 6/8] fix --- ee/wcp/ak_common/CMakeLists.txt | 1 - ee/wcp/ak_common/include/strings.h | 6 ----- ee/wcp/ak_common/strings.cpp | 13 ---------- ee/wcp/ak_cred_provider/Helpers.cpp | 8 +++++- ee/wcp/ak_lsa/CMakeLists.txt | 4 --- ee/wcp/ak_lsa/Main.cpp | 1 - ee/wcp/ak_lsa/Utils.hpp | 39 ++++++++++++++++++----------- 7 files changed, 32 insertions(+), 40 deletions(-) delete mode 100644 ee/wcp/ak_common/include/strings.h delete mode 100644 ee/wcp/ak_common/strings.cpp diff --git a/ee/wcp/ak_common/CMakeLists.txt b/ee/wcp/ak_common/CMakeLists.txt index 5d4ed4a7..9837df00 100644 --- a/ee/wcp/ak_common/CMakeLists.txt +++ b/ee/wcp/ak_common/CMakeLists.txt @@ -6,7 +6,6 @@ set(CMAKE_CXX_STANDARD 20) set(SRCS ak_log.cpp ak_sentry.cpp - strings.cpp ) add_library(${PROJECT_NAME} STATIC diff --git a/ee/wcp/ak_common/include/strings.h b/ee/wcp/ak_common/include/strings.h deleted file mode 100644 index f6490f81..00000000 --- a/ee/wcp/ak_common/include/strings.h +++ /dev/null @@ -1,6 +0,0 @@ -#include -#include -#include - -std::wstring utf8_decode(const std::string& str); -std::string utf8_encode(const std::wstring& str); diff --git a/ee/wcp/ak_common/strings.cpp b/ee/wcp/ak_common/strings.cpp deleted file mode 100644 index cee7523a..00000000 --- a/ee/wcp/ak_common/strings.cpp +++ /dev/null @@ -1,13 +0,0 @@ -#include -#include -#include - -std::wstring utf8_decode(const std::string& str) { - std::wstring_convert> myconv; - return myconv.from_bytes(str); -} - -std::string utf8_encode(const std::wstring& str) { - std::wstring_convert> myconv; - return myconv.to_bytes(str); -} diff --git a/ee/wcp/ak_cred_provider/Helpers.cpp b/ee/wcp/ak_cred_provider/Helpers.cpp index 392f352f..ce4e1afe 100644 --- a/ee/wcp/ak_cred_provider/Helpers.cpp +++ b/ee/wcp/ak_cred_provider/Helpers.cpp @@ -1,7 +1,8 @@ #include "pch.h" #include "Helpers.h" -#include "ak_common/include/strings.h" +#include +#include // // Copies the field descriptor pointed to by rcpfd into a buffer allocated @@ -596,3 +597,8 @@ SplitDomainAndUsername(_In_ PCWSTR pszQualifiedUserName, } return hr; } + +std::wstring utf8_decode(const std::string& str) { + std::wstring_convert> myconv; + return myconv.from_bytes(str); +} diff --git a/ee/wcp/ak_lsa/CMakeLists.txt b/ee/wcp/ak_lsa/CMakeLists.txt index 93a5d5bd..7a6a9dc6 100644 --- a/ee/wcp/ak_lsa/CMakeLists.txt +++ b/ee/wcp/ak_lsa/CMakeLists.txt @@ -35,7 +35,3 @@ target_link_libraries(${PROJECT_NAME} 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) - -# Copy binary and resource files to the target output directory. - -COPY_FILES("${PROJECT_NAME}" "${PROJECT_NAME}.dll" "${CMAKE_CURRENT_BINARY_DIR}/Release" "${PROJECT_INSALL_DIR}") diff --git a/ee/wcp/ak_lsa/Main.cpp b/ee/wcp/ak_lsa/Main.cpp index 8e67ac79..90d60ba7 100644 --- a/ee/wcp/ak_lsa/Main.cpp +++ b/ee/wcp/ak_lsa/Main.cpp @@ -3,7 +3,6 @@ #include "Utils.hpp" #include "ak_common/include/ak_log.h" #include "ak_common/include/ak_sentry.h" -#include "ak_common/include/strings.h" #include "spdlog/spdlog.h" // exported symbols diff --git a/ee/wcp/ak_lsa/Utils.hpp b/ee/wcp/ak_lsa/Utils.hpp index 0f2fefb9..426719ec 100644 --- a/ee/wcp/ak_lsa/Utils.hpp +++ b/ee/wcp/ak_lsa/Utils.hpp @@ -5,7 +5,6 @@ #include "authentik_sys_bridge/ffi.h" #include "rust/cxx.h" #include "ak_common/include/ak_log.h" -#include "ak_common/include/strings.h" #include "spdlog/spdlog.h" extern LSA_SECPKG_FUNCTION_TABLE FunctionTable; @@ -55,6 +54,16 @@ inline void AssignLsaUnicodeString(const LSA_UNICODE_STRING& source, LSA_UNICODE dest.MaximumLength = source.Length; } +// Convert a wide Unicode string to an UTF8 string +inline std::string utf8_encode(const std::wstring& wstr) { + if (wstr.empty()) return std::string(); + int size_needed = + WideCharToMultiByte(CP_UTF8, 0, &wstr[0], (int)wstr.size(), NULL, 0, NULL, NULL); + std::string strTo(size_needed, 0); + WideCharToMultiByte(CP_UTF8, 0, &wstr[0], (int)wstr.size(), &strTo[0], size_needed, NULL, NULL); + return strTo; +} + inline PWSTR decryptPassword(MSV1_0_INTERACTIVE_LOGON* pkil) { CRED_PROTECTION_TYPE ProtectionType; ULONG Length = pkil->Password.Length; @@ -69,8 +78,8 @@ inline PWSTR decryptPassword(MSV1_0_INTERACTIVE_LOGON* pkil) { return pszCredentials; } - ULONG cchPin = 0; - PWSTR pszPin = 0; + ULONG cchToken = 0; + PWSTR pszToken = 0; ULONG cchCredentials = Length / sizeof(WCHAR); HRESULT status; @@ -79,38 +88,40 @@ inline PWSTR decryptPassword(MSV1_0_INTERACTIVE_LOGON* pkil) { spdlog::debug(" decryptPassword: Password is protected"); while (true) { spdlog::debug(" decryptPassword: CredUnprotectW call"); - if (CredUnprotectW(FALSE, pszCredentials, cchCredentials, pszPin, &cchPin)) { + if (CredUnprotectW(FALSE, pszCredentials, cchCredentials, pszToken, &cchToken)) { break; } - if (pszPin) { + if (pszToken) { break; } auto err = GetLastError(); if (err == ERROR_INSUFFICIENT_BUFFER) { - spdlog::debug(" decryptPassword: ERROR_INSUFFICIENT_BUFFER, %d", cchPin); - // pszPin = (PWSTR)FunctionTable.AllocatePrivateHeap(cchPin * sizeof(WCHAR)); - pszPin = (PWSTR)alloca(cchPin * sizeof(WCHAR)); + spdlog::debug(" decryptPassword: ERROR_INSUFFICIENT_BUFFER, {}", cchToken); + pszToken = (PWSTR)FunctionTable.AllocatePrivateHeap(cchToken * sizeof(WCHAR)); + // pszToken = (PWSTR)alloca(cchToken * sizeof(WCHAR)); } } } else { spdlog::debug(" decryptPassword: PW was not encrypted"); - pszPin = pszCredentials; - cchPin = cchCredentials; + pszToken = pszCredentials; + cchToken = cchCredentials; } - return pszPin; + return pszToken; } inline bool ValidateToken(MSV1_0_INTERACTIVE_LOGON* pkil) { try { spdlog::debug(" ak_sys_auth_token_validate: Decrypting password"); - auto pw = decryptPassword(pkil); + std::wstring pwW = decryptPassword(pkil); + std::string pw = utf8_encode(pwW); TokenResponse validatedToken; - if (ak_sys_auth_token_validate(utf8_encode(pw), validatedToken)) { + spdlog::debug(" ak_sys_auth_token_validate: {:d}, {}", pw.length(), pw); + if (ak_sys_auth_token_validate(pw, validatedToken)) { spdlog::debug(" ak_sys_auth_token_validate Succeeded"); return true; } } catch (const rust::Error& ex) { - spdlog::debug(" ak_sys_auth_token_validate Error: %s", ex.what()); + spdlog::debug(" ak_sys_auth_token_validate Error: {}", ex.what()); return false; } return false; From b47e8015cf56c88b858326d9573130d1281964fc Mon Sep 17 00:00:00 2001 From: Jens Langhammer Date: Sun, 11 Jan 2026 17:34:24 +0100 Subject: [PATCH 7/8] t --- pkg/agent_system/config/domain.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/pkg/agent_system/config/domain.go b/pkg/agent_system/config/domain.go index e38cc41a..33a23829 100644 --- a/pkg/agent_system/config/domain.go +++ b/pkg/agent_system/config/domain.go @@ -49,6 +49,9 @@ func (dc DomainConfig) Config() api.AgentConfig { } func (dc DomainConfig) APIClient() (*api.APIClient, error) { + if dc.c != nil { + return dc.c, nil + } u, err := url.Parse(dc.AuthentikURL) if err != nil { return nil, err @@ -66,8 +69,7 @@ func (dc DomainConfig) APIClient() (*api.APIClient, error) { apiConfig.UserAgent = fmt.Sprintf("goauthentik.io/platform/%s", meta.FullVersion()) c := api.NewAPIClient(apiConfig) - dc.c = c - return dc.c, nil + return c, nil } func (dc DomainConfig) Test() error { From 85027157a07f3bbff327efd95afbf76915c071a5 Mon Sep 17 00:00:00 2001 From: Jens Langhammer Date: Tue, 13 Jan 2026 12:48:52 +0000 Subject: [PATCH 8/8] fix some warnings --- ee/wcp/ak_lsa/include/PrepareToken.hpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ee/wcp/ak_lsa/include/PrepareToken.hpp b/ee/wcp/ak_lsa/include/PrepareToken.hpp index c065d35e..f8104aee 100644 --- a/ee/wcp/ak_lsa/include/PrepareToken.hpp +++ b/ee/wcp/ak_lsa/include/PrepareToken.hpp @@ -1,5 +1,7 @@ #pragma once +#pragma warning(disable : 4005) #include +#pragma warning(default : 4005) #include #include #include