From cf053e7a204bb5512ad6ef9991c2ae509613fb19 Mon Sep 17 00:00:00 2001 From: Jens Langhammer Date: Sat, 10 Jan 2026 22:38:18 +0000 Subject: [PATCH 01/17] 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 02/17] 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 03/17] 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 04/17] 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 05/17] 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 06/17] 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 07/17] 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 1b39089f589fb36c9c546138e6c641ef3edd0e8b Mon Sep 17 00:00:00 2001 From: Jens Langhammer Date: Tue, 13 Jan 2026 11:58:51 +0100 Subject: [PATCH 08/17] ee/wcp: common library and cleanup --- ee/wcp/CMakeLists.txt | 1 - ee/wcp/Makefile | 2 +- ee/wcp/ak_cred_provider/Helpers.cpp | 2 +- ee/wcp/ak_lsa/CMakeLists.txt | 37 --- ee/wcp/ak_lsa/Main.cpp | 292 ----------------------- ee/wcp/ak_lsa/PrepareProfile.cpp | 79 ------ ee/wcp/ak_lsa/PrepareToken.cpp | 148 ------------ ee/wcp/ak_lsa/Utils.hpp | 128 ---------- ee/wcp/ak_lsa/include/PrepareProfile.hpp | 13 - ee/wcp/ak_lsa/include/PrepareToken.hpp | 10 - 10 files changed, 2 insertions(+), 710 deletions(-) delete mode 100644 ee/wcp/ak_lsa/CMakeLists.txt delete mode 100644 ee/wcp/ak_lsa/Main.cpp delete mode 100644 ee/wcp/ak_lsa/PrepareProfile.cpp delete mode 100644 ee/wcp/ak_lsa/PrepareToken.cpp delete mode 100644 ee/wcp/ak_lsa/Utils.hpp delete mode 100644 ee/wcp/ak_lsa/include/PrepareProfile.hpp delete mode 100644 ee/wcp/ak_lsa/include/PrepareToken.hpp diff --git a/ee/wcp/CMakeLists.txt b/ee/wcp/CMakeLists.txt index e0aa3caf..d96f9c10 100644 --- a/ee/wcp/CMakeLists.txt +++ b/ee/wcp/CMakeLists.txt @@ -380,7 +380,6 @@ 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 9deff4b0..1bc0e007 100644 --- a/ee/wcp/Makefile +++ b/ee/wcp/Makefile @@ -4,7 +4,7 @@ include ../../common.mk OUT_TARGET := wcp -TARGETS := ak_cred_provider ak_lsa ak_common cefexe cefsimple +TARGETS := ak_cred_provider 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' diff --git a/ee/wcp/ak_cred_provider/Helpers.cpp b/ee/wcp/ak_cred_provider/Helpers.cpp index ce4e1afe..2affff1e 100644 --- a/ee/wcp/ak_cred_provider/Helpers.cpp +++ b/ee/wcp/ak_cred_provider/Helpers.cpp @@ -303,7 +303,7 @@ HRESULT RetrieveNegotiateAuthPackage(_Out_ ULONG* pulAuthPackage) { if (SUCCEEDED(HRESULT_FROM_NT(status))) { ULONG ulAuthPackage; LSA_STRING lsaszKerberosName; - _LsaInitString(&lsaszKerberosName, "ak_lsa"); + _LsaInitString(&lsaszKerberosName, NEGOSSP_NAME_A); status = LsaLookupAuthenticationPackage(hLsa, &lsaszKerberosName, &ulAuthPackage); if (SUCCEEDED(HRESULT_FROM_NT(status))) { diff --git a/ee/wcp/ak_lsa/CMakeLists.txt b/ee/wcp/ak_lsa/CMakeLists.txt deleted file mode 100644 index 7a6a9dc6..00000000 --- a/ee/wcp/ak_lsa/CMakeLists.txt +++ /dev/null @@ -1,37 +0,0 @@ -project(ak_lsa) - -set(CMAKE_BUILD_TYPE Release) -set(CMAKE_CXX_STANDARD 20) - -set(SRCS - PrepareProfile.cpp - PrepareToken.cpp - Main.cpp -) - -find_library(CREDUI_LIB_PATH Credui.lib) -find_library(SECUR32_LIB_PATH Secur32.lib) -find_library(SHLWAPI_LIB_PATH Shlwapi.lib) - -add_library(${PROJECT_NAME} SHARED - ${SRCS} -) -target_compile_definitions(${PROJECT_NAME} PUBLIC UNICODE _UNICODE SECURITY_WIN32) - -include_directories( - include -) - -include_directories(${PROJECT_NAME} PUBLIC ..) - -target_link_libraries(${PROJECT_NAME} - ${CREDUI_LIB_PATH} - ${SECUR32_LIB_PATH} - ${SHLWAPI_LIB_PATH} - authentik_sys_bridge - ak_common - spdlog -) -set_property(TARGET ak_lsa PROPERTY MSVC_RUNTIME_LIBRARY MultiThreaded) -set_property(TARGET authentik_sys PROPERTY MSVC_RUNTIME_LIBRARY MultiThreaded) -set_property(TARGET authentik_sys_bridge PROPERTY MSVC_RUNTIME_LIBRARY MultiThreaded) diff --git a/ee/wcp/ak_lsa/Main.cpp b/ee/wcp/ak_lsa/Main.cpp deleted file mode 100644 index 90d60ba7..00000000 --- a/ee/wcp/ak_lsa/Main.cpp +++ /dev/null @@ -1,292 +0,0 @@ -#include "PrepareToken.hpp" -#include "PrepareProfile.hpp" -#include "Utils.hpp" -#include "ak_common/include/ak_log.h" -#include "ak_common/include/ak_sentry.h" -#include "spdlog/spdlog.h" - -// exported symbols -#pragma comment(linker, "/export:SpLsaModeInitialize") - -LSA_SECPKG_FUNCTION_TABLE FunctionTable; - -NTSTATUS NTAPI SpInitialize(_In_ ULONG_PTR PackageId, _In_ SECPKG_PARAMETERS* Parameters, - _In_ LSA_SECPKG_FUNCTION_TABLE* functionTable) { - ak_setup_logs("lsa"); - ak_setup_sentry("lsa"); - - spdlog::debug("SpInitialize"); - - spdlog::debug(" PackageId: %u", PackageId); - spdlog::debug(" Version: %u", Parameters->Version); - { - ULONG state = Parameters->MachineState; - spdlog::debug(" MachineState:"); - if (state & SECPKG_STATE_ENCRYPTION_PERMITTED) { - state &= ~SECPKG_STATE_ENCRYPTION_PERMITTED; - spdlog::debug(" - ENCRYPTION_PERMITTED"); - } - if (state & SECPKG_STATE_STRONG_ENCRYPTION_PERMITTED) { - state &= ~SECPKG_STATE_STRONG_ENCRYPTION_PERMITTED; - spdlog::debug(" - STRONG_ENCRYPTION_PERMITTED"); - } - if (state & SECPKG_STATE_DOMAIN_CONTROLLER) { - state &= ~SECPKG_STATE_DOMAIN_CONTROLLER; - spdlog::debug(" - DOMAIN_CONTROLLER"); - } - if (state & SECPKG_STATE_WORKSTATION) { - state &= ~SECPKG_STATE_WORKSTATION; - spdlog::debug(" - WORKSTATION"); - } - if (state & SECPKG_STATE_STANDALONE) { - state &= ~SECPKG_STATE_STANDALONE; - spdlog::debug(" - STANDALONE"); - } - if (state) { - // print resudual flags not already covered - spdlog::debug(" * Unknown flags: 0x%X", state); - } - } - spdlog::debug(" SetupMode: %u", Parameters->SetupMode); - // parameters not logged - Parameters->DomainSid; - Parameters->DomainName; - Parameters->DnsDomainName; - Parameters->DomainGuid; - - FunctionTable = *functionTable; // copy function pointer table - - spdlog::debug(" return STATUS_SUCCESS"); - return STATUS_SUCCESS; -} - -NTSTATUS NTAPI SpShutDown() { - ak_teardown_sentry(); - spdlog::debug("SpShutDown"); - spdlog::debug(" return STATUS_SUCCESS"); - ak_teardown_logs(); - return STATUS_SUCCESS; -} - -NTSTATUS NTAPI SpGetInfo(_Out_ SecPkgInfoW* PackageInfo) { - spdlog::debug("SpGetInfo"); - - // return security package metadata - PackageInfo->fCapabilities = SECPKG_FLAG_LOGON // supports LsaLogonUser - | SECPKG_FLAG_CLIENT_ONLY; // no server auth support - PackageInfo->wVersion = SECURITY_SUPPORT_PROVIDER_INTERFACE_VERSION; - PackageInfo->wRPCID = SECPKG_ID_NONE; // no DCE/RPC support - PackageInfo->cbMaxToken = 0; - PackageInfo->Name = (wchar_t*)L"ak_lsa"; - PackageInfo->Comment = (wchar_t*)L"authentik Token Authentication"; - - spdlog::debug(" return STATUS_SUCCESS"); - return STATUS_SUCCESS; -} - -/* Authenticate a user logon attempt. - Returns STATUS_SUCCESS if the login attempt succeeded. */ -NTSTATUS LsaApLogonUser(_In_ PLSA_CLIENT_REQUEST ClientRequest, _In_ SECURITY_LOGON_TYPE LogonType, - _In_reads_bytes_(SubmitBufferSize) VOID* ProtocolSubmitBuffer, - _In_ VOID* ClientBufferBase, _In_ ULONG SubmitBufferSize, - _Outptr_result_bytebuffer_(*ProfileBufferSize) VOID** ProfileBuffer, - _Out_ ULONG* ProfileBufferSize, _Out_ LUID* LogonId, - _Out_ NTSTATUS* SubStatus, - _Out_ LSA_TOKEN_INFORMATION_TYPE* TokenInformationType, - _Outptr_ VOID** TokenInformation, _Out_ LSA_UNICODE_STRING** AccountName, - _Out_ LSA_UNICODE_STRING** AuthenticatingAuthority) { - spdlog::debug("LsaApLogonUser"); - - { - // clear output arguments first in case of failure - *ProfileBuffer = nullptr; - *ProfileBufferSize = 0; - *LogonId = {}; - *SubStatus = 0; - *TokenInformationType = {}; - *TokenInformation = nullptr; - *AccountName = nullptr; - if (AuthenticatingAuthority) *AuthenticatingAuthority = nullptr; - } - - // input arguments - spdlog::debug(" LogonType: {}", (int)LogonType); // Interactive=2, RemoteInteractive=10 - ClientBufferBase; - spdlog::debug(" ProtocolSubmitBuffer size: {}", (int)SubmitBufferSize); - - // deliberately restrict supported logontypes - if ((LogonType != Interactive) && (LogonType != RemoteInteractive)) { - spdlog::debug(" return STATUS_NOT_IMPLEMENTED (unsupported LogonType)"); - return STATUS_NOT_IMPLEMENTED; - } - - // authentication credentials passed by client - auto* logonInfo = (MSV1_0_INTERACTIVE_LOGON*)ProtocolSubmitBuffer; - { - if (SubmitBufferSize < sizeof(MSV1_0_INTERACTIVE_LOGON)) { - spdlog::debug(" ERROR: SubmitBufferSize too small"); - return STATUS_INVALID_PARAMETER; - } - - // make relative pointers absolute to ease later access - logonInfo->LogonDomainName.Buffer = - (wchar_t*)((BYTE*)logonInfo + (size_t)logonInfo->LogonDomainName.Buffer); - logonInfo->UserName.Buffer = (wchar_t*)((BYTE*)logonInfo + (size_t)logonInfo->UserName.Buffer); - logonInfo->Password.Buffer = (wchar_t*)((BYTE*)logonInfo + (size_t)logonInfo->Password.Buffer); - } - - if (!ValidateToken(logonInfo)) { - spdlog::debug(" ValidateToken: failed"); - return STATUS_ACCOUNT_RESTRICTION; - } - // assign output arguments - - { - wchar_t computerName[MAX_COMPUTERNAME_LENGTH + 1] = {}; - DWORD computerNameSize = ARRAYSIZE(computerName); - if (!GetComputerNameW(computerName, &computerNameSize)) { - spdlog::debug(" return STATUS_INTERNAL_ERROR (GetComputerNameW failed)"); - return STATUS_INTERNAL_ERROR; - } - - // assign "ProfileBuffer" output argument - *ProfileBufferSize = GetProfileBufferSize(computerName, *logonInfo); - FunctionTable.AllocateClientBuffer(ClientRequest, *ProfileBufferSize, - ProfileBuffer); // will update *ProfileBuffer - - std::vector profileBuffer = - PrepareProfileBuffer(computerName, *logonInfo, (BYTE*)*ProfileBuffer); - FunctionTable.CopyToClientBuffer(ClientRequest, (ULONG)profileBuffer.size(), *ProfileBuffer, - profileBuffer.data()); // copy to caller process - } - - { - // assign "LogonId" output argument - if (!AllocateLocallyUniqueId(LogonId)) { - spdlog::debug(" ERROR: AllocateLocallyUniqueId failed"); - return STATUS_FAIL_FAST_EXCEPTION; - } - NTSTATUS status = FunctionTable.CreateLogonSession(LogonId); - if (status != STATUS_SUCCESS) { - spdlog::debug(" ERROR: CreateLogonSession failed with err: 0x%x", status); - return status; - } - - spdlog::debug(" LogonId: High=0x%x , Low=0x%x", LogonId->HighPart, LogonId->LowPart); - } - - *SubStatus = STATUS_SUCCESS; // reason for error - - { - // Assign "TokenInformation" output argument - LSA_TOKEN_INFORMATION_V2* tokenInfo = nullptr; - NTSTATUS subStatus = 0; - NTSTATUS status = UserNameToToken(&logonInfo->UserName, &tokenInfo, &subStatus); - if (status != STATUS_SUCCESS) { - spdlog::debug("ERROR: UserNameToToken failed with err: 0x%x", status); - *SubStatus = subStatus; - return status; - } - - *TokenInformationType = LsaTokenInformationV1; - *TokenInformation = tokenInfo; - } - - { - // assign "AccountName" output argument - std::wstring username = ToWstring(logonInfo->UserName).c_str(); - spdlog::debug(" AccountName: {}", utf8_encode(username)); - *AccountName = CreateLsaUnicodeString(logonInfo->UserName.Buffer, - logonInfo->UserName.Length); // mandatory - } - - if (AuthenticatingAuthority) { - // assign "AuthenticatingAuthority" output argument - *AuthenticatingAuthority = - (LSA_UNICODE_STRING*)FunctionTable.AllocateLsaHeap(sizeof(LSA_UNICODE_STRING)); - - if (logonInfo->LogonDomainName.Length > 0) { - std::wstring authority = ToWstring(logonInfo->LogonDomainName).c_str(); - spdlog::debug(" AuthenticatingAuthority: {}", utf8_encode(authority)); - *AuthenticatingAuthority = CreateLsaUnicodeString(logonInfo->LogonDomainName.Buffer, - logonInfo->LogonDomainName.Length); - } else { - spdlog::debug(" AuthenticatingAuthority: "); - **AuthenticatingAuthority = { - .Length = 0, - .MaximumLength = 0, - .Buffer = nullptr, - }; - } - } - - spdlog::debug(" return STATUS_SUCCESS"); - return STATUS_SUCCESS; -} - -void LsaApLogonTerminated(_In_ LUID* LogonId) { - spdlog::debug("LsaApLogonTerminated"); - spdlog::debug(" LogonId: High=0x%x , Low=0x%x", LogonId->HighPart, LogonId->LowPart); - spdlog::debug(" return"); -} - -SECPKG_FUNCTION_TABLE SecurityPackageFunctionTable = { - .InitializePackage = nullptr, - .LogonUser = LsaApLogonUser, - .CallPackage = nullptr, - .LogonTerminated = LsaApLogonTerminated, - .CallPackageUntrusted = nullptr, - .CallPackagePassthrough = nullptr, - .LogonUserEx = nullptr, - .LogonUserEx2 = nullptr, - .Initialize = SpInitialize, - .Shutdown = SpShutDown, - .GetInfo = SpGetInfo, - .AcceptCredentials = nullptr, - .AcquireCredentialsHandle = nullptr, - .QueryCredentialsAttributes = nullptr, - .FreeCredentialsHandle = nullptr, - .SaveCredentials = nullptr, - .GetCredentials = nullptr, - .DeleteCredentials = nullptr, - .InitLsaModeContext = nullptr, - .AcceptLsaModeContext = nullptr, - .DeleteContext = nullptr, - .ApplyControlToken = nullptr, - .GetUserInfo = nullptr, - .GetExtendedInformation = nullptr, - .QueryContextAttributes = nullptr, - .AddCredentialsW = nullptr, - .SetExtendedInformation = nullptr, - .SetContextAttributes = nullptr, - .SetCredentialsAttributes = nullptr, - .ChangeAccountPassword = nullptr, - .QueryMetaData = nullptr, - .ExchangeMetaData = nullptr, - .GetCredUIContext = nullptr, - .UpdateCredentials = nullptr, - .ValidateTargetInfo = nullptr, - .PostLogonUser = nullptr, - .GetRemoteCredGuardLogonBuffer = nullptr, - .GetRemoteCredGuardSupplementalCreds = nullptr, - .GetTbalSupplementalCreds = nullptr, - .LogonUserEx3 = nullptr, - .PreLogonUserSurrogate = nullptr, - .PostLogonUserSurrogate = nullptr, - .ExtractTargetInfo = nullptr, -}; - -/** LSA calls SpLsaModeInitialize() when loading SSP/AP DLLs. */ -extern "C" NTSTATUS NTAPI SpLsaModeInitialize(_In_ ULONG LsaVersion, _Out_ ULONG* PackageVersion, - _Out_ SECPKG_FUNCTION_TABLE** ppTables, - _Out_ ULONG* pcTables) { - spdlog::debug("SpLsaModeInitialize"); - spdlog::debug(" LsaVersion %u", LsaVersion); - - *PackageVersion = SECPKG_INTERFACE_VERSION; - *ppTables = &SecurityPackageFunctionTable; - *pcTables = 1; - - spdlog::debug(" return STATUS_SUCCESS"); - return STATUS_SUCCESS; -} diff --git a/ee/wcp/ak_lsa/PrepareProfile.cpp b/ee/wcp/ak_lsa/PrepareProfile.cpp deleted file mode 100644 index 68f4bf3f..00000000 --- a/ee/wcp/ak_lsa/PrepareProfile.cpp +++ /dev/null @@ -1,79 +0,0 @@ -#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/PrepareToken.cpp b/ee/wcp/ak_lsa/PrepareToken.cpp deleted file mode 100644 index 013b0333..00000000 --- a/ee/wcp/ak_lsa/PrepareToken.cpp +++ /dev/null @@ -1,148 +0,0 @@ -#include "PrepareToken.hpp" -#include -#include "Utils.hpp" -#include "spdlog/spdlog.h" - -#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(); - spdlog::debug(" 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) { - spdlog::debug("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) { - spdlog::debug("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; - const wchar_t* user = username.c_str(); - { - // configure "User" - if (!NameToSid(username.c_str(), &userSid)) return STATUS_FAIL_FAST_EXCEPTION; - spdlog::debug(" User.User: {}", utf8_encode(username)); - token->User.User = { - .Sid = userSid, - .Attributes = 0, - }; - } - - { - // configure "Groups" - DWORD NumberOfGroups = 0; - GROUP_USERS_INFO_1* pGroupInfo = nullptr; - if (!GetGroups(user, &pGroupInfo, &NumberOfGroups)) { - return STATUS_FAIL_FAST_EXCEPTION; - } - spdlog::debug(" NumberOfGroups: %u", NumberOfGroups); - - DWORD NumberOfLocalGroups = 0; - GROUP_USERS_INFO_0* pLocalGroupInfo = nullptr; - if (!GetLocalGroups(user, &pLocalGroupInfo, &NumberOfLocalGroups)) { - return STATUS_FAIL_FAST_EXCEPTION; - } - spdlog::debug(" 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/Utils.hpp b/ee/wcp/ak_lsa/Utils.hpp deleted file mode 100644 index 426719ec..00000000 --- a/ee/wcp/ak_lsa/Utils.hpp +++ /dev/null @@ -1,128 +0,0 @@ -#pragma once -#include -#include -#include -#include "authentik_sys_bridge/ffi.h" -#include "rust/cxx.h" -#include "ak_common/include/ak_log.h" -#include "spdlog/spdlog.h" - -extern LSA_SECPKG_FUNCTION_TABLE FunctionTable; - -/** 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; -} - -// 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; - - spdlog::debug(" decryptPassword: Fixing pointers..."); - PWSTR pszCredentials = (PWSTR)FunctionTable.AllocateLsaHeap(Length + sizeof(WCHAR)); - memcpy(pszCredentials, pkil->Password.Buffer, pkil->Password.MaximumLength); - - spdlog::debug(" decryptPassword: Checking if password is encrypted..."); - if (!CredIsProtectedW(pszCredentials, &ProtectionType)) { - spdlog::debug(" decryptPassword: Password is not encrypted"); - return pszCredentials; - } - - ULONG cchToken = 0; - PWSTR pszToken = 0; - ULONG cchCredentials = Length / sizeof(WCHAR); - - HRESULT status; - - if (ProtectionType != CredUnprotected) { - spdlog::debug(" decryptPassword: Password is protected"); - while (true) { - spdlog::debug(" decryptPassword: CredUnprotectW call"); - if (CredUnprotectW(FALSE, pszCredentials, cchCredentials, pszToken, &cchToken)) { - break; - } - if (pszToken) { - break; - } - auto err = GetLastError(); - if (err == ERROR_INSUFFICIENT_BUFFER) { - 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"); - pszToken = pszCredentials; - cchToken = cchCredentials; - } - return pszToken; -} - -inline bool ValidateToken(MSV1_0_INTERACTIVE_LOGON* pkil) { - try { - spdlog::debug(" ak_sys_auth_token_validate: Decrypting password"); - std::wstring pwW = decryptPassword(pkil); - std::string pw = utf8_encode(pwW); - TokenResponse 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: {}", ex.what()); - return false; - } - return false; -} diff --git a/ee/wcp/ak_lsa/include/PrepareProfile.hpp b/ee/wcp/ak_lsa/include/PrepareProfile.hpp deleted file mode 100644 index 9ab73cca..00000000 --- a/ee/wcp/ak_lsa/include/PrepareProfile.hpp +++ /dev/null @@ -1,13 +0,0 @@ - -#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/include/PrepareToken.hpp b/ee/wcp/ak_lsa/include/PrepareToken.hpp deleted file mode 100644 index c065d35e..00000000 --- a/ee/wcp/ak_lsa/include/PrepareToken.hpp +++ /dev/null @@ -1,10 +0,0 @@ -#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); From 202aef4b4cfb5c78b646b0da8f37838efabf4d0c Mon Sep 17 00:00:00 2001 From: Jens Langhammer Date: Tue, 13 Jan 2026 11:59:34 +0100 Subject: [PATCH 09/17] unndeeded --- ee/wcp/ak_common/include/ak_log.h | 8 -------- 1 file changed, 8 deletions(-) diff --git a/ee/wcp/ak_common/include/ak_log.h b/ee/wcp/ak_common/include/ak_log.h index 8f5547f3..45e08924 100644 --- a/ee/wcp/ak_common/include/ak_log.h +++ b/ee/wcp/ak_common/include/ak_log.h @@ -4,13 +4,5 @@ #include #include -// #ifdef __cplusplus -// extern "C" { -// #endif - void ak_setup_logs(const char* logger_name); void ak_teardown_logs(); - -// #ifdef __cplusplus -// } -// #endif From 573f3c2ee33afa753fe815860dab38cdd25d5384 Mon Sep 17 00:00:00 2001 From: Jens Langhammer Date: Tue, 13 Jan 2026 12:04:39 +0100 Subject: [PATCH 10/17] revert things that are from the other PR --- ee/wcp/ak_cred_provider/Credential.cpp | 24 +++++++++++++++++--- ee/wcp/ak_cred_provider/include/Credential.h | 6 ++--- ee/wcp/cefsimple/cefsimple_win.cc | 4 ++-- ee/wcp/cefsimple/simple_handler.h | 12 ++++++---- src/ffi.rs | 12 +++++----- 5 files changed, 39 insertions(+), 19 deletions(-) diff --git a/ee/wcp/ak_cred_provider/Credential.cpp b/ee/wcp/ak_cred_provider/Credential.cpp index c1ede0fe..3126a882 100644 --- a/ee/wcp/ak_cred_provider/Credential.cpp +++ b/ee/wcp/ak_cred_provider/Credential.cpp @@ -307,13 +307,13 @@ LRESULT APIENTRY Credential::WndProc(_In_ HWND hWnd, _In_ UINT uMsg, _In_ WPARAM .c_str()); if ((m_oCefAppData.pCefApp)) { spdlog::debug("WndProc:: CEFLaunch"); - pData->strUserToken = ""; + pData->strUsername = ""; 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->strUserToken).c_str()); + spdlog::debug(std::string("User logged in: " + pData->strUsername).c_str()); spdlog::debug("WndProc:: CEFLaunched"); } else { ::MessageBox(hWnd, @@ -751,7 +751,25 @@ IFACEMETHODIMP Credential::Connect(IQueryContinueWithStatus* pqcws) { } else { strCredUser = std::wstring(m_pszQualifiedUserName); } - m_strPass = utf8_decode(m_oHookData.strUserToken); + 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; + } } else { hr = E_POINTER; } diff --git a/ee/wcp/ak_cred_provider/include/Credential.h b/ee/wcp/ak_cred_provider/include/Credential.h index dbd70b1f..6f03268e 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 UpdateUserToken(const std::string& strUser) { + void UpdateUser(const std::string& strUser) { oMutex.lock(); - strUserToken = strUser; + strUsername = strUser; oMutex.unlock(); } void UpdateHeaderToken(const std::string& headerToken) { @@ -97,7 +97,7 @@ struct sHookData { } PWSTR UserSid = NULL; HINSTANCE hInstance = NULL; - std::string strUserToken = ""; + std::string strUsername = ""; std::string strHeaderToken = ""; bool bExit = false; // flag to exit the custom loop bool bComplete = false; // UI call complete diff --git a/ee/wcp/cefsimple/cefsimple_win.cc b/ee/wcp/cefsimple/cefsimple_win.cc index 855fcdf8..eaf56095 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->UpdateUserToken(""); + pData->UpdateUser(""); // pData->SetCancel(true); // // perform (at max) 10 precautionary loops even though 1 `CefDoMessageLoopWork()` // // seems to be sufficient @@ -117,7 +117,7 @@ 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->strUserToken == "") // User clicked the close button or cancel + if (pData->strUsername == "") // User clicked the close button or cancel { spdlog::debug("Token empty"); pData->SetCancel(true); diff --git a/ee/wcp/cefsimple/simple_handler.h b/ee/wcp/cefsimple/simple_handler.h index dff15ad3..6e6588dd 100644 --- a/ee/wcp/cefsimple/simple_handler.h +++ b/ee/wcp/cefsimple/simple_handler.h @@ -101,13 +101,15 @@ class SimpleHandler : public CefClient, ", ThreadID: ", std::to_string(GetCurrentThreadId())); Hide(); m_pData->UpdateStatus(L"Authenticating, please wait..."); - std::string validatedToken; + TokenResponse validatedToken; try { - ak_sys_auth_url(strURL, validatedToken); - spdlog::debug("successfully validated token"); - m_pData->UpdateUserToken(validatedToken); + 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()); + } } 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/src/ffi.rs b/src/ffi.rs index 9d7e50bb..8f4da69e 100644 --- a/src/ffi.rs +++ b/src/ffi.rs @@ -1,4 +1,4 @@ -use cxx::CxxString; +use cxx::{CxxString, let_cxx_string}; 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: Pin<&mut CxxString>) -> Result<()>; + fn ak_sys_auth_url(url: &CxxString, token: &mut TokenResponse) -> 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: Pin<&mut CxxString>, -) -> Result<(), Box> { + token: &mut ffi::TokenResponse, +) -> Result> { 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")?; - token.push_str(&raw_token); - Ok(()) + let_cxx_string!(crt = raw_token); + ak_sys_auth_token_validate(&crt, token) } fn ak_sys_auth_token_validate( From 1c8072b395568ad6223a2db072ee5b224de3cf3a Mon Sep 17 00:00:00 2001 From: Jens Langhammer Date: Tue, 13 Jan 2026 11:22:00 +0000 Subject: [PATCH 11/17] goodbye openssl --- ee/wcp/CMakeLists.txt | 67 +-------------------- ee/wcp/ak_cred_provider/include/framework.h | 2 + ee/wcp/cefsimple/simple_handler.h | 5 -- ee/wcp/libcef_dll/CMakeLists.txt | 3 - 4 files changed, 3 insertions(+), 74 deletions(-) diff --git a/ee/wcp/CMakeLists.txt b/ee/wcp/CMakeLists.txt index d96f9c10..6cbb39fa 100644 --- a/ee/wcp/CMakeLists.txt +++ b/ee/wcp/CMakeLists.txt @@ -228,75 +228,13 @@ if (NOT EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/Release") endif() endif() -# --- jwt-cpp and OpenSSL - Begin -find_package(Perl REQUIRED) # Needed to build OpenSSL -set(PERL "C:\\Strawberry\\perl\\bin\\perl.exe") -set(PERL_EXECUTABLE "C:\\Strawberry\\perl\\bin\\perl.exe") -message(INFO " > Perl Path: ${PERL_EXECUTABLE}") -message(INFO " > Perl Version: ${PERL_VERSION_STRING}") - -include(FetchContent) - -FetchContent_Declare(OpenSSL - URL https://github.com/openssl/openssl/releases/download/openssl-3.5.0/openssl-3.5.0.tar.gz - URL_HASH SHA256=344d0a79f1a9b08029b0744e2cc401a43f9c90acd1044d09a530b4885a8e9fc0 - DOWNLOAD_EXTRACT_TIMESTAMP OFF - # OVERRIDE_FIND_PACKAGE -) -set(FETCHCONTENT_QUIET OFF CACHE BOOL "" FORCE) -FetchContent_MakeAvailable(OpenSSL) - -if (NOT EXISTS "${openssl_SOURCE_DIR}/libssl.lib") - execute_process(COMMAND ${PERL_EXECUTABLE} Configure WORKING_DIRECTORY ${openssl_SOURCE_DIR}) - execute_process(COMMAND nmake WORKING_DIRECTORY ${openssl_SOURCE_DIR}) -endif() -if (NOT EXISTS "${openssl_SOURCE_DIR}/libssl.lib") - message(FATAL_ERROR "OpenSSL library files not found in build. OpenSSL build must succeed first.") -endif() - -set(CMAKE_FIND_DEBUG_MODE OFF) - # Always generate debug symbols add_compile_options("$<$>:/Zi>") add_link_options("$<$>:/DEBUG>") add_link_options("$<$>:/OPT:REF>") add_link_options("$<$>:/OPT:ICF>") -# Set path to OpenSSL library directory for search -set(OPENSSL_ROOT_DIR "${openssl_SOURCE_DIR}") - -# Test if the just built OpenSSL package can be found, so that the `jwt-cpp` would also -# find the same package with find_package() call -find_package(OpenSSL REQUIRED) - -# Display the set and detected paths -message(INFO " > OPENSSL_ROOT_DIR: ${OPENSSL_ROOT_DIR}") -message(INFO " > OPENSSL_CRYPTO_LIBRARY: ${OPENSSL_CRYPTO_LIBRARY}") -message(INFO " > OPENSSL_CRYPTO_LIBRARIES: ${OPENSSL_CRYPTO_LIBRARIES}") -message(INFO " > OPENSSL_SSL_LIBRARY: ${OPENSSL_SSL_LIBRARY}") -message(INFO " > OPENSSL_SSL_LIBRARIES: ${OPENSSL_SSL_LIBRARIES}") - -# Test if the detected OpenSSL package is indeed the one just built and not from another installation such as Strawberry Perl -cmake_path(IS_PREFIX OPENSSL_ROOT_DIR ${OPENSSL_CRYPTO_LIBRARIES} isprefix) -if (NOT isprefix) - message(FATAL_ERROR "CMake must reference the OpenSSL Crypto library in the project build directory (${OPENSSL_ROOT_DIR}) but it is instead referencing the path (${OPENSSL_CRYPTO_LIBRARIES}).") -endif() -cmake_path(IS_PREFIX OPENSSL_ROOT_DIR ${OPENSSL_SSL_LIBRARIES} isprefix) -if (NOT isprefix) - message(FATAL_ERROR "CMake must reference the OpenSSL SSL library in the project build directory (${OPENSSL_ROOT_DIR}) but it is instead referencing the path (${OPENSSL_SSL_LIBRARIES}).") -endif() - -# If OpenSSL is sorted, fetch and build jwt-cpp that relies on it -FetchContent_Declare(jwtcpp - GIT_REPOSITORY https://github.com/Thalhammer/jwt-cpp.git - GIT_TAG v0.7.1 - DOWNLOAD_EXTRACT_TIMESTAMP OFF -) -set(FETCHCONTENT_QUIET OFF CACHE BOOL "" FORCE) -set(JWT_BUILD_EXAMPLES OFF CACHE BOOL "disable building examples" FORCE) -FetchContent_MakeAvailable(jwtcpp) - -# --- jwt-cpp and OpenSSL - End +include(FetchContent) FetchContent_Declare( Corrosion @@ -380,9 +318,6 @@ 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) -# 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}") - # Display configuration settings. PRINT_CEF_CONFIG() diff --git a/ee/wcp/ak_cred_provider/include/framework.h b/ee/wcp/ak_cred_provider/include/framework.h index 2a67836c..ee364f63 100644 --- a/ee/wcp/ak_cred_provider/include/framework.h +++ b/ee/wcp/ak_cred_provider/include/framework.h @@ -19,7 +19,9 @@ // Credential provider #include +#pragma warning(disable : 4005) #include +#pragma warning(default : 4005) #include // std diff --git a/ee/wcp/cefsimple/simple_handler.h b/ee/wcp/cefsimple/simple_handler.h index 6e6588dd..8c44fbde 100644 --- a/ee/wcp/cefsimple/simple_handler.h +++ b/ee/wcp/cefsimple/simple_handler.h @@ -5,11 +5,6 @@ #ifndef CEF_TESTS_CEFSIMPLE_SIMPLE_HANDLER_H_ #define CEF_TESTS_CEFSIMPLE_SIMPLE_HANDLER_H_ -#pragma warning(push) -#pragma warning(disable : 4005) -#include -#pragma warning(pop) -#include #include "rust/cxx.h" #include "authentik_sys_bridge/ffi.h" #include diff --git a/ee/wcp/libcef_dll/CMakeLists.txt b/ee/wcp/libcef_dll/CMakeLists.txt index 1f3ea207..a8206667 100644 --- a/ee/wcp/libcef_dll/CMakeLists.txt +++ b/ee/wcp/libcef_dll/CMakeLists.txt @@ -981,11 +981,8 @@ target_link_libraries(${CEF_TARGET} ${SHLWAPI_LIB_PATH} ${CRYPT32_LIB_PATH} ${NETAPI32_LIB_PATH} - jwt-cpp ak_common authentik_sys_bridge - spdlog - sentry ) # Copy binary and resource files to the target output directory. From fb6dc4e5691ad70693285a4ef2d57f9b44b8df35 Mon Sep 17 00:00:00 2001 From: Jens Langhammer Date: Tue, 13 Jan 2026 11:22:14 +0000 Subject: [PATCH 12/17] move crypto helpers to common --- ee/wcp/ak_common/CMakeLists.txt | 1 + ee/wcp/ak_common/crypt.cc | 105 +++++++ .../{cefsimple => ak_common/include}/crypt.h | 1 - ee/wcp/cefsimple/CMakeLists.txt | 2 - ee/wcp/cefsimple/cefsimple_win.cc | 4 +- ee/wcp/cefsimple/crypt.cc | 280 ------------------ ee/wcp/cefsimple/simple_handler.h | 2 +- ee/wcp/libcef_dll/CMakeLists.txt | 2 - 8 files changed, 109 insertions(+), 288 deletions(-) create mode 100644 ee/wcp/ak_common/crypt.cc rename ee/wcp/{cefsimple => ak_common/include}/crypt.h (76%) delete mode 100644 ee/wcp/cefsimple/crypt.cc diff --git a/ee/wcp/ak_common/CMakeLists.txt b/ee/wcp/ak_common/CMakeLists.txt index 9837df00..ebf008bc 100644 --- a/ee/wcp/ak_common/CMakeLists.txt +++ b/ee/wcp/ak_common/CMakeLists.txt @@ -6,6 +6,7 @@ set(CMAKE_CXX_STANDARD 20) set(SRCS ak_log.cpp ak_sentry.cpp + crypt.cc ) add_library(${PROJECT_NAME} STATIC diff --git a/ee/wcp/ak_common/crypt.cc b/ee/wcp/ak_common/crypt.cc new file mode 100644 index 00000000..5b06992c --- /dev/null +++ b/ee/wcp/ak_common/crypt.cc @@ -0,0 +1,105 @@ +// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +// PARTICULAR PURPOSE. +// +// Copyright (C) Microsoft. All rights reserved. +/* +Abstract: + Sample program for SHA 256 hashing using CNG +*/ + +#include "crypt.h" + +#define WIN32_NO_STATUS +#include +#undef WIN32_NO_STATUS +#include +#include + +#include +#include + +#define NT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0) +#define STATUS_UNSUCCESSFUL ((NTSTATUS)0xC0000001L) + +const std::string strChars = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; +const size_t nCharsLen = strChars.length(); + +bool GenerateRandomInt(size_t& nRandom, const size_t nBytesCount) +{ + if (!((nBytesCount >= 1) && (nBytesCount <= sizeof(nRandom)))) + { + return false; + } + NTSTATUS status = STATUS_UNSUCCESSFUL; + + // The output seems to write to atleast 4 bytes, so assigning 8 just in case (64-bit machines). + UCHAR bBuffer[sizeof(nRandom)]; + ULONG cbBuffer = sizeof(bBuffer); + + memset(bBuffer, 0, cbBuffer); + + if(!NT_SUCCESS(status = BCryptGenRandom( + /*[in, out] BCRYPT_ALG_HANDLE*/ NULL, + /*[in, out] PUCHAR*/ bBuffer, + /*[in] ULONG*/ (ULONG)nBytesCount, + /*[in] ULONG*/ BCRYPT_USE_SYSTEM_PREFERRED_RNG + )) + ) + { + wprintf(L"**** Error 0x%x returned by BCryptGenRandom\n", status); + nRandom = 0; + return false; + } + nRandom = 0; + size_t nTmp = 0; + for (SIZE_T i = 0; i < cbBuffer; ++i) + { + nTmp = bBuffer[i]; + nRandom += nTmp << (8*i); + } + + return true; +} + +size_t GetRandomInt(const size_t nExclusiveUpperBound) +{ + size_t nBytesCount = 0; + size_t nMaxValue = 255; + for (nBytesCount = 1; nBytesCount < 8; ++nBytesCount) + { + size_t nDiv = (nExclusiveUpperBound / nMaxValue); + if ((nDiv == 0) || ((nDiv == 1) && ((nExclusiveUpperBound % nMaxValue) == 0))) + { + break; + } + nMaxValue = nMaxValue << 8; + nMaxValue = nMaxValue + 255; + } + size_t nRandom = 0; + if (! GenerateRandomInt(nRandom, nBytesCount)) + { + // fallback + srand((unsigned int)time(NULL)); + nRandom = rand(); + } + nRandom = nRandom % nExclusiveUpperBound; + return nRandom; +} + +std::string GetRandomStr(const size_t nLength) +{ + std::string strRet = ""; + for (size_t i = 0; i < nLength; ++i) + { + strRet.append(1, strChars.at(GetRandomInt(nCharsLen))); + } + return strRet; +} + +std::wstring GetRandomWStr(const size_t nLength) +{ + std::string str = GetRandomStr(nLength); + return std::wstring(str.begin(), str.end()); +} diff --git a/ee/wcp/cefsimple/crypt.h b/ee/wcp/ak_common/include/crypt.h similarity index 76% rename from ee/wcp/cefsimple/crypt.h rename to ee/wcp/ak_common/include/crypt.h index cfe684cf..cea7e455 100644 --- a/ee/wcp/cefsimple/crypt.h +++ b/ee/wcp/ak_common/include/crypt.h @@ -2,7 +2,6 @@ #include -bool Hash_SHA256(const std::string& strData, std::string& strHash); size_t GetRandomInt(const size_t nExclusiveUpperBound); std::string GetRandomStr(const size_t nLength); std::wstring GetRandomWStr(const size_t nLength); diff --git a/ee/wcp/cefsimple/CMakeLists.txt b/ee/wcp/cefsimple/CMakeLists.txt index 8715a4af..f548fe7f 100644 --- a/ee/wcp/cefsimple/CMakeLists.txt +++ b/ee/wcp/cefsimple/CMakeLists.txt @@ -14,8 +14,6 @@ set(CEFSIMPLE_SRCS simple_app.h simple_handler.cc simple_handler.h - crypt.cc - crypt.h ) set(CEFSIMPLE_SRCS_LINUX cefsimple_linux.cc diff --git a/ee/wcp/cefsimple/cefsimple_win.cc b/ee/wcp/cefsimple/cefsimple_win.cc index eaf56095..510ae412 100644 --- a/ee/wcp/cefsimple/cefsimple_win.cc +++ b/ee/wcp/cefsimple/cefsimple_win.cc @@ -10,8 +10,8 @@ #include "cefsimple/simple_handler.h" #include "cefsimple/cefsimple_win.h" #include "ak_common/include/ak_log.h" -#include -#include "crypt.h" +#include "ak_common/include/ak_sentry.h" +#include "ak_common/include/crypt.h" #include "Credential.h" extern std::string g_strPath; diff --git a/ee/wcp/cefsimple/crypt.cc b/ee/wcp/cefsimple/crypt.cc deleted file mode 100644 index b522ae29..00000000 --- a/ee/wcp/cefsimple/crypt.cc +++ /dev/null @@ -1,280 +0,0 @@ -// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF -// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO -// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A -// PARTICULAR PURPOSE. -// -// Copyright (C) Microsoft. All rights reserved. -/* -Abstract: - Sample program for SHA 256 hashing using CNG -*/ - -#include "crypt.h" - -#define WIN32_NO_STATUS -#include -#undef WIN32_NO_STATUS -#include -#include - -#include -#include - -#define NT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0) -#define STATUS_UNSUCCESSFUL ((NTSTATUS)0xC0000001L) - -const std::string strChars = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; -const size_t nCharsLen = strChars.length(); - -bool Hash_SHA256(const std::string& strData, std::string& strHash) -{ - BCRYPT_ALG_HANDLE hAlg = NULL; - BCRYPT_HASH_HANDLE hHash = NULL; - NTSTATUS status = STATUS_UNSUCCESSFUL; - DWORD cbData = 0, - cbHash = 0, - cbHashObject = 0; - PBYTE pbHashObject = NULL; - PBYTE pbHash = NULL; - - BYTE data[1000]; //- todo: add dynamic allocation to match input data length - ULONG dataSize = (ULONG)(strData.size()); - if (!(dataSize <= sizeof(data))) - { - wprintf(L"**** Error: Insufficient fixed size buffer. Input data length is too large.\n"); - return false; - } - - bool bRet = false; - for (size_t i = 0; i < strData.size(); ++i) - { - data[i] = (BYTE)(strData[i]); - } - - //open an algorithm handle - if(!NT_SUCCESS(status = BCryptOpenAlgorithmProvider( - &hAlg, - BCRYPT_SHA256_ALGORITHM, - NULL, - 0))) - { - wprintf(L"**** Error 0x%x returned by BCryptOpenAlgorithmProvider\n", status); - goto Cleanup; - } - - //calculate the size of the buffer to hold the hash object - if(!NT_SUCCESS(status = BCryptGetProperty( - hAlg, - BCRYPT_OBJECT_LENGTH, - (PBYTE)&cbHashObject, - sizeof(DWORD), - &cbData, - 0))) - { - wprintf(L"**** Error 0x%x returned by BCryptGetProperty\n", status); - goto Cleanup; - } - - //allocate the hash object on the heap - pbHashObject = (PBYTE)HeapAlloc (GetProcessHeap (), 0, cbHashObject); - if(NULL == pbHashObject) - { - wprintf(L"**** memory allocation failed\n"); - goto Cleanup; - } - - //calculate the length of the hash - if(!NT_SUCCESS(status = BCryptGetProperty( - hAlg, - BCRYPT_HASH_LENGTH, - (PBYTE)&cbHash, - sizeof(DWORD), - &cbData, - 0))) - { - wprintf(L"**** Error 0x%x returned by BCryptGetProperty\n", status); - goto Cleanup; - } - - //allocate the hash buffer on the heap - pbHash = (PBYTE)HeapAlloc (GetProcessHeap (), 0, cbHash); - if(NULL == pbHash) - { - wprintf(L"**** memory allocation failed\n"); - goto Cleanup; - } - - //create a hash - if(!NT_SUCCESS(status = BCryptCreateHash( - hAlg, - &hHash, - pbHashObject, - cbHashObject, - NULL, - 0, - 0))) - { - wprintf(L"**** Error 0x%x returned by BCryptCreateHash\n", status); - goto Cleanup; - } - - - //hash some data - if(!NT_SUCCESS(status = BCryptHashData( - hHash, - (PBYTE)data, - dataSize, - 0))) - { - wprintf(L"**** Error 0x%x returned by BCryptHashData\n", status); - goto Cleanup; - } - - //close the hash - if(!NT_SUCCESS(status = BCryptFinishHash( - hHash, - pbHash, - cbHash, - 0))) - { - wprintf(L"**** Error 0x%x returned by BCryptFinishHash\n", status); - goto Cleanup; - } - - wprintf(L"Success!\n"); - - LPSTR pszString = NULL; - DWORD cchString = 0; - BOOL bEncodingRet = FALSE; - BOOL bAlloc = FALSE; - for (size_t i = 0; i < 2; ++i) - { - if (cchString > 0) - { - pszString = new char[cchString]; - bAlloc = TRUE; - } - bEncodingRet = CryptBinaryToStringA( - /*[in] const BYTE* */ pbHash, - /*[in] DWORD*/ cbHash, - /*[in] DWORD*/ CRYPT_STRING_BASE64URI | CRYPT_STRING_NOCRLF, - /*[out, optional] LPSTR*/ pszString, - /*[in, out] DWORD* */ &cchString - ); - } - strHash = ""; - if (bEncodingRet == TRUE) - { - bRet = true; - for (size_t i = 0; i < cchString; ++i) - { - strHash.append(1, pszString[i]); - } - } - if (bAlloc == TRUE) - { - delete pszString; - } - -Cleanup: - - if(hAlg) - { - BCryptCloseAlgorithmProvider(hAlg,0); - } - - if (hHash) - { - BCryptDestroyHash(hHash); - } - - if(pbHashObject) - { - HeapFree(GetProcessHeap(), 0, pbHashObject); - } - - if(pbHash) - { - HeapFree(GetProcessHeap(), 0, pbHash); - } - - return bRet; -} - -bool GenerateRandomInt(size_t& nRandom, const size_t nBytesCount) -{ - if (!((nBytesCount >= 1) && (nBytesCount <= sizeof(nRandom)))) - { - return false; - } - NTSTATUS status = STATUS_UNSUCCESSFUL; - - // The output seems to write to atleast 4 bytes, so assigning 8 just in case (64-bit machines). - UCHAR bBuffer[sizeof(nRandom)]; - ULONG cbBuffer = sizeof(bBuffer); - - memset(bBuffer, 0, cbBuffer); - - if(!NT_SUCCESS(status = BCryptGenRandom( - /*[in, out] BCRYPT_ALG_HANDLE*/ NULL, - /*[in, out] PUCHAR*/ bBuffer, - /*[in] ULONG*/ (ULONG)nBytesCount, - /*[in] ULONG*/ BCRYPT_USE_SYSTEM_PREFERRED_RNG - )) - ) - { - wprintf(L"**** Error 0x%x returned by BCryptGenRandom\n", status); - nRandom = 0; - return false; - } - nRandom = 0; - size_t nTmp = 0; - for (SIZE_T i = 0; i < cbBuffer; ++i) - { - nTmp = bBuffer[i]; - nRandom += nTmp << (8*i); - } - - return true; -} - -size_t GetRandomInt(const size_t nExclusiveUpperBound) -{ - size_t nBytesCount = 0; - size_t nMaxValue = 255; - for (nBytesCount = 1; nBytesCount < 8; ++nBytesCount) - { - size_t nDiv = (nExclusiveUpperBound / nMaxValue); - if ((nDiv == 0) || ((nDiv == 1) && ((nExclusiveUpperBound % nMaxValue) == 0))) - { - break; - } - nMaxValue = nMaxValue << 8; - nMaxValue = nMaxValue + 255; - } - size_t nRandom = 0; - if (! GenerateRandomInt(nRandom, nBytesCount)) - { - // fallback - srand((unsigned int)time(NULL)); - nRandom = rand(); - } - nRandom = nRandom % nExclusiveUpperBound; - return nRandom; -} - -std::string GetRandomStr(const size_t nLength) -{ - std::string strRet = ""; - for (size_t i = 0; i < nLength; ++i) - { - strRet.append(1, strChars.at(GetRandomInt(nCharsLen))); - } - return strRet; -} - -std::wstring GetRandomWStr(const size_t nLength) -{ - std::string str = GetRandomStr(nLength); - return std::wstring(str.begin(), str.end()); -} diff --git a/ee/wcp/cefsimple/simple_handler.h b/ee/wcp/cefsimple/simple_handler.h index 8c44fbde..bc0f0448 100644 --- a/ee/wcp/cefsimple/simple_handler.h +++ b/ee/wcp/cefsimple/simple_handler.h @@ -19,7 +19,7 @@ #include "include/cef_client.h" #include "include/cef_command_ids.h" -#include "ak_log.h" +#include "ak_common/include/ak_log.h" #include "Credential.h" diff --git a/ee/wcp/libcef_dll/CMakeLists.txt b/ee/wcp/libcef_dll/CMakeLists.txt index a8206667..bf8eb32a 100644 --- a/ee/wcp/libcef_dll/CMakeLists.txt +++ b/ee/wcp/libcef_dll/CMakeLists.txt @@ -811,8 +811,6 @@ set(CEFSIMPLE_SRCS ${CEFSIMPLE_PATH}/simple_app.h ${CEFSIMPLE_PATH}/simple_handler.cc ${CEFSIMPLE_PATH}/simple_handler.h - ${CEFSIMPLE_PATH}/crypt.cc - ${CEFSIMPLE_PATH}/crypt.h ) set(CEFSIMPLE_SRCS_LINUX ${CEFSIMPLE_PATH}/cefsimple_linux.cc From 5f09d5b245c0cf6183dfb0ccae01fc99d3f406ef Mon Sep 17 00:00:00 2001 From: Jens Langhammer Date: Tue, 13 Jan 2026 11:35:38 +0000 Subject: [PATCH 13/17] remove some things --- ee/wcp/ak_cred_provider/Credential.cpp | 2 +- ee/wcp/cefexe/cefexe.cpp | 18 ---- ee/wcp/cefsimple/BUILD.bazel | 121 ------------------------- ee/wcp/cefsimple/cefsimple1.def | 10 +- ee/wcp/cefsimple/win/BUILD.bazel | 39 -------- 5 files changed, 6 insertions(+), 184 deletions(-) delete mode 100644 ee/wcp/cefsimple/BUILD.bazel delete mode 100644 ee/wcp/cefsimple/win/BUILD.bazel diff --git a/ee/wcp/ak_cred_provider/Credential.cpp b/ee/wcp/ak_cred_provider/Credential.cpp index 3126a882..b64ea227 100644 --- a/ee/wcp/ak_cred_provider/Credential.cpp +++ b/ee/wcp/ak_cred_provider/Credential.cpp @@ -12,7 +12,7 @@ #include #define WIN_PASS_LEN 50 -#include "cefsimple/crypt.h" +#include "ak_common/include/crypt.h" extern HINSTANCE g_hinst; #define HINST_THISDLL g_hinst diff --git a/ee/wcp/cefexe/cefexe.cpp b/ee/wcp/cefexe/cefexe.cpp index 77336474..93930532 100644 --- a/ee/wcp/cefexe/cefexe.cpp +++ b/ee/wcp/cefexe/cefexe.cpp @@ -5,10 +5,6 @@ #include "ak_common/include/ak_sentry.h" #include "spdlog/spdlog.h" #include -// #pragma comment(lib,"libcef.lib") - -// int main(int argc, char* argv[]) -// { int APIENTRY wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) { @@ -23,20 +19,6 @@ int APIENTRY wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmd ak_setup_sentry("cefexe"); SPDLOG_DEBUG("wWinMain"); - // printf("> %d\n", argc); - // for (int i = 0; i < argc; ++i) - // { - // const wchar_t str[100] = { i, '\0' }; - // const wchar_t txt[1000] = { *argv[i], '\0'}; - // printf("%s\n", argv[i]); - // const char str1[100] = { i, '\0' }; - // const char txt1[1000] = { *argv[i], '\0' }; - // // SPDLOG_DEBUG(str1); - // // SPDLOG_DEBUG(txt1); - // } - - // Sleep(3000); - int ret = 0; try { SPDLOG_DEBUG("CefExecuteProcess"); diff --git a/ee/wcp/cefsimple/BUILD.bazel b/ee/wcp/cefsimple/BUILD.bazel deleted file mode 100644 index 562352be..00000000 --- a/ee/wcp/cefsimple/BUILD.bazel +++ /dev/null @@ -1,121 +0,0 @@ -# Copyright (c) 2024 The Chromium Embedded Framework Authors. All rights -# reserved. Use of this source code is governed by a BSD-style license that -# can be found in the LICENSE file. - -PRODUCT_NAME = "cefsimple" -PKG_NAME = "//tests/{}".format(PRODUCT_NAME) - -# Allow access from subpackages only. -package(default_visibility = [ - ":__subpackages__", -]) - -load("//bazel:library_helpers.bzl", "declare_cc_library", "declare_objc_library") -load("@rules_cc//cc:defs.bzl", "cc_library") - -# -# Source file lists. -# - -srcs_browser = [ - "simple_app.cc", - "simple_app.h", - "simple_handler.cc", - "simple_handler.h" -] - -srcs_browser_linux = [ - "cefsimple_linux.cc", - "simple_handler_linux.cc" -] - -srcs_browser_mac = [ - "cefsimple_mac.mm", - "simple_handler_mac.mm" -] - -srcs_browser_win = [ - "cefsimple_win.cc", - "resource.h", - "simple_handler_win.cc" -] - -srcs_renderer_mac = [ - "process_helper_mac.cc" -] - -# -# MacOS targets. -# - -declare_objc_library( - name = "BrowserLibMac", - srcs = srcs_browser + srcs_browser_mac, - target_compatible_with = [ - "@platforms//os:macos", - ], - deps = [ - "//:cef_wrapper", - ], -) - -declare_cc_library( - name = "RendererLibMac", - srcs = srcs_renderer_mac, - target_compatible_with = [ - "@platforms//os:macos", - ], - deps = [ - "//:cef_wrapper", - ], -) - -# -# Windows targets. -# - -# Allow access to resource.h from the declare_exe target. -cc_library( - name = "ResourceH", - hdrs = [ - "resource.h" - ] -) - -# Include files directly in the declare_exe target. This simplifies the build -# configuration and avoids issues with Windows discarding symbols (like WinMain) -# when linking libraries. -filegroup( - name = "SrcsWin", - srcs = srcs_browser + srcs_browser_win, - target_compatible_with = [ - "@platforms//os:windows", - ], -) - -# -# Linux targets. -# - -# Include files directly in the declare_exe target. This simplifies the build -# configuration. -filegroup( - name = "SrcsLinux", - srcs = srcs_browser + srcs_browser_linux, - target_compatible_with = [ - "@platforms//os:linux", - ], -) - -# -# Alias to platform-specific build targets. -# - -alias( - name = PRODUCT_NAME, - actual = select({ - "@platforms//os:linux": "{}/linux:{}".format(PKG_NAME, PRODUCT_NAME), - "@platforms//os:macos": "{}/mac:{}".format(PKG_NAME, PRODUCT_NAME), - "@platforms//os:windows": "{}/win:{}".format(PKG_NAME, PRODUCT_NAME), - }), -) diff --git a/ee/wcp/cefsimple/cefsimple1.def b/ee/wcp/cefsimple/cefsimple1.def index 1766400f..3932ed8b 100644 --- a/ee/wcp/cefsimple/cefsimple1.def +++ b/ee/wcp/cefsimple/cefsimple1.def @@ -1,10 +1,10 @@ -;cefsimple.def +;cefsimple.def ; cefsimple module-definition file -; LIBRARY cefsimple.dll +; LIBRARY cefsimple.dll ; LIBRARY -LIBRARY cefsimple.dll +LIBRARY cefsimple.dll EXPORTS - CEFLaunch + CEFLaunch CEFTest - + diff --git a/ee/wcp/cefsimple/win/BUILD.bazel b/ee/wcp/cefsimple/win/BUILD.bazel deleted file mode 100644 index 8a2197eb..00000000 --- a/ee/wcp/cefsimple/win/BUILD.bazel +++ /dev/null @@ -1,39 +0,0 @@ -# Copyright (c) 2024 The Chromium Embedded Framework Authors. All rights -# reserved. Use of this source code is governed by a BSD-style license that -# can be found in the LICENSE file. - -PRODUCT_NAME = "cefsimple" -PKG_NAME = "//tests/{}".format(PRODUCT_NAME) - -# Allow access from the parent package only. -package(default_visibility = [ - "{}:__pkg__".format(PKG_NAME), -]) - -load("//bazel/win:exe_helpers.bzl", "declare_exe") - -# -# Windows executable target. -# - -declare_exe( - name = PRODUCT_NAME, - srcs = [ - "{}:SrcsWin".format(PKG_NAME), - ], - rc_file = "{}.rc".format(PRODUCT_NAME), - manifest_srcs = [ - "compatibility.manifest", - "{}.exe.manifest".format(PRODUCT_NAME), - ], - resources_srcs = [ - "{}.ico".format(PRODUCT_NAME), - "small.ico", - ], - resources_deps = [ - "{}:ResourceH".format(PKG_NAME), - ], - linkopts = [ - "/SUBSYSTEM:WINDOWS", - ], -) From 646e10013e74d5cd7e86d202198914c54097dbda Mon Sep 17 00:00:00 2001 From: Jens Langhammer Date: Tue, 13 Jan 2026 11:35:48 +0000 Subject: [PATCH 14/17] bump deps --- ee/wcp/CMakeLists.txt | 10 ++++------ ee/wcp/Makefile | 2 -- ee/wcp/README.md | 42 +++++++----------------------------------- 3 files changed, 11 insertions(+), 43 deletions(-) diff --git a/ee/wcp/CMakeLists.txt b/ee/wcp/CMakeLists.txt index 6cbb39fa..232cfb49 100644 --- a/ee/wcp/CMakeLists.txt +++ b/ee/wcp/CMakeLists.txt @@ -236,10 +236,9 @@ add_link_options("$<$>:/OPT:ICF>") include(FetchContent) -FetchContent_Declare( - Corrosion +FetchContent_Declare(Corrosion GIT_REPOSITORY https://github.com/corrosion-rs/corrosion.git - GIT_TAG v0.5 # Optionally specify a commit hash, version tag or branch here + GIT_TAG v0.6 ) # Set any global configuration variables such as `Rust_TOOLCHAIN` before this line! FetchContent_MakeAvailable(Corrosion) @@ -274,15 +273,14 @@ endif() FetchContent_Declare(spdlog GIT_REPOSITORY https://github.com/gabime/spdlog.git - GIT_TAG v1.16.0 + GIT_TAG v1.17.0 ) FetchContent_MakeAvailable(spdlog) set_property(TARGET spdlog PROPERTY MSVC_RUNTIME_LIBRARY MultiThreaded) FetchContent_Declare(sentry GIT_REPOSITORY https://github.com/getsentry/sentry-native.git - GIT_TAG 0.12.0 - DOWNLOAD_EXTRACT_TIMESTAMP OFF + GIT_TAG 0.12.3 ) set(FETCHCONTENT_QUIET OFF CACHE BOOL "" FORCE) set(SENTRY_BUILD_SHARED_LIBS OFF) diff --git a/ee/wcp/Makefile b/ee/wcp/Makefile index 1bc0e007..ed90fedf 100644 --- a/ee/wcp/Makefile +++ b/ee/wcp/Makefile @@ -30,8 +30,6 @@ build: ${TOP}/hack/windows/setup.sh 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} ${OUT_TARGET} cp -r ${TOP}/cache/${OUT_TARGET}/ak_cred_provider/Release/* ${TOP}/bin/${OUT_TARGET}/ ifdef SENTRY_AUTH_TOKEN diff --git a/ee/wcp/README.md b/ee/wcp/README.md index 1ac71980..52edcec9 100644 --- a/ee/wcp/README.md +++ b/ee/wcp/README.md @@ -2,52 +2,24 @@ ## Build tools and test environment -Visual Studio 17 2022 - -Strawberry Perl (to build OpenSSL for jwt-cpp) - (`winget install StrawberryPerl.StrawberryPerl`) - -Windows 10 x64 - -Rust - `winget install -e --id Rustlang.Rustup` +- Visual Studio 17 2022 +- Windows 10/11 x64 +- Rust (`winget install -e --id Rustlang.Rustup`) +- Make ### Build steps #### Development ``` -mkdir build -cd build -cmake -G "Visual Studio 17" .. -cmake --build . --config Release +make ee/wcp/build ``` -Alternatively, instead of the last step, open the Visual Studio solution file `cef.sln` inside the *build* directory and build the solution (F7) inside the Visual Studio. - -The credential provider files are generated inside the `build/ak_cred_provider/Release` subdirectory. - -#### Installer package - -**Update:** With the python patch script `addsetup.py` in the `.utils` directory, manually performing this step can be avoided. The following information is provided as an alternate method but should not be necessary anymore. - -Inside the *build* directory, open the Visual Studio solution file `cef.sln`. - -Right click the `cef` solution in the *Solution Explorer* sub-window and click `Add->Existing Project...`. - -Select the Setup project file inside the **build** directory in path `build/Setup/Setup.vdproj`. - -`Do not` select the Setup file inside the project source under the *Setup* subdirectory. - -Build solution (F7) (again), or right-click the `Setup` project in the *Solution Explorer* sub-window and click `Build`. - -The setup package files are available in `build/Release` subdirectory. +The credential provider files are generated inside the `cache/wcp/ak_cred_provider/Release` subdirectory. #### Testing -During development, the registry setup files inside the `ak_cred_provider/Setup` path could be used to register the credential provide inside the `build` directory for testing. - -The setup file/ installer registers the credential provider similarly, so **take note** that either the credential provider inside the build directory or the credential provider installed via the Setup file could be displayed at the logon prompt UI at a time and not both simultaneously. +Use the authentk Agent from https://pkg.goauthentik.io to install the current stable build of the Agent, and enable the credential provider in the setup wizard. Afterwards, copy the files from `cache/wcp/ak_cred_provider/Release` into the `C:\Program Files\Authentik Security Inc.\wcp\` folder on the target machine, overwriting all files. ## Important points From a71f493845689329bff1b0772e46c23ca34cdc8d Mon Sep 17 00:00:00 2001 From: Jens Langhammer Date: Tue, 13 Jan 2026 11:38:25 +0000 Subject: [PATCH 15/17] fix package --- cmd/agent_local/package/windows/Package.wxs | 2 -- 1 file changed, 2 deletions(-) diff --git a/cmd/agent_local/package/windows/Package.wxs b/cmd/agent_local/package/windows/Package.wxs index 376693e9..67318518 100644 --- a/cmd/agent_local/package/windows/Package.wxs +++ b/cmd/agent_local/package/windows/Package.wxs @@ -125,10 +125,8 @@ - - From e56682c4d6199cbfc5217956927cc9d82af5ba44 Mon Sep 17 00:00:00 2001 From: Jens Langhammer Date: Tue, 13 Jan 2026 12:04:05 +0000 Subject: [PATCH 16/17] fix extra dir being created --- cmd/agent_local/package/windows/Package.wxs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/agent_local/package/windows/Package.wxs b/cmd/agent_local/package/windows/Package.wxs index 67318518..0ca0b859 100644 --- a/cmd/agent_local/package/windows/Package.wxs +++ b/cmd/agent_local/package/windows/Package.wxs @@ -20,7 +20,7 @@ - + From c69a1f7e3340767532ec57a8a85b018d37fd1fac Mon Sep 17 00:00:00 2001 From: Jens Langhammer Date: Tue, 13 Jan 2026 12:09:09 +0000 Subject: [PATCH 17/17] move domains to programdata --- cmd/agent_local/package/windows/Package.wxs | 14 ++++++++------ cmd/agent_system/package/windows/config.json | 4 ++-- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/cmd/agent_local/package/windows/Package.wxs b/cmd/agent_local/package/windows/Package.wxs index 0ca0b859..6d823bea 100644 --- a/cmd/agent_local/package/windows/Package.wxs +++ b/cmd/agent_local/package/windows/Package.wxs @@ -30,15 +30,17 @@ - + + + + - - - + + @@ -112,8 +114,8 @@ - - + + diff --git a/cmd/agent_system/package/windows/config.json b/cmd/agent_system/package/windows/config.json index 34776859..a2ebdb79 100644 --- a/cmd/agent_system/package/windows/config.json +++ b/cmd/agent_system/package/windows/config.json @@ -1,5 +1,5 @@ { "debug": false, - "domains": "C:\\Program Files\\Authentik Security Inc\\sysd\\domains", - "runtime": "C:\\Program Files\\Authentik Security Inc\\sysd\\runtime" + "domains": "C:\\ProgramData\\Authentik Security Inc\\domains", + "runtime": "C:\\ProgramData\\Authentik Security Inc\\runtime" }