Milestone 5: deliver embedded RDP sessions and lifecycle hardening

This commit is contained in:
Keith Smith
2026-03-03 18:59:26 -07:00
parent 230a401386
commit 36006bd4aa
2941 changed files with 724359 additions and 77 deletions

View File

@@ -0,0 +1,60 @@
set(MODULE_NAME "TestWtsApi")
set(MODULE_PREFIX "TEST_WTSAPI")
disable_warnings_for_directory(${CMAKE_CURRENT_BINARY_DIR})
set(${MODULE_PREFIX}_DRIVER ${MODULE_NAME}.c)
set(UNIX_ONLY TestWtsApiShutdownSystem.c TestWtsApiWaitSystemEvent.c)
set(${MODULE_PREFIX}_TESTS TestWtsApiEnumerateProcesses.c TestWtsApiEnumerateSessions.c
TestWtsApiQuerySessionInformation.c TestWtsApiSessionNotification.c
)
if(NOT WIN32)
set(${MODULE_PREFIX}_TESTS ${${MODULE_PREFIX}_TESTS} ${UNIX_ONLY})
endif()
create_test_sourcelist(${MODULE_PREFIX}_SRCS ${${MODULE_PREFIX}_DRIVER} ${${MODULE_PREFIX}_TESTS})
add_executable(${MODULE_NAME} ${${MODULE_PREFIX}_SRCS})
target_link_libraries(${MODULE_NAME} winpr)
set_target_properties(${MODULE_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${TESTING_OUTPUT_DIRECTORY}")
foreach(test ${${MODULE_PREFIX}_TESTS})
get_filename_component(TestName ${test} NAME_WE)
add_test(${TestName} ${TESTING_OUTPUT_DIRECTORY}/${MODULE_NAME} ${TestName})
endforeach()
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "WinPR/Test")
if(TESTS_WTSAPI_EXTRA)
set(MODULE_NAME "TestWtsApiExtra")
set(MODULE_PREFIX "TEST_WTSAPI_EXTRA")
set(${MODULE_PREFIX}_DRIVER ${MODULE_NAME}.c)
set(${MODULE_PREFIX}_TESTS
TestWtsApiExtraDisconnectSession.c TestWtsApiExtraDynamicVirtualChannel.c TestWtsApiExtraLogoffSession.c
TestWtsApiExtraSendMessage.c TestWtsApiExtraVirtualChannel.c TestWtsApiExtraStartRemoteSessionEx.c
)
create_test_sourcelist(${MODULE_PREFIX}_SRCS ${${MODULE_PREFIX}_DRIVER} ${${MODULE_PREFIX}_TESTS})
add_executable(${MODULE_NAME} ${${MODULE_PREFIX}_SRCS})
target_link_libraries(${MODULE_NAME} winpr)
set_target_properties(${MODULE_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${TESTING_OUTPUT_DIRECTORY}")
foreach(test ${${MODULE_PREFIX}_TESTS})
get_filename_component(TestName ${test} NAME_WE)
add_test(${TestName} ${TESTING_OUTPUT_DIRECTORY}/${MODULE_NAME} ${TestName})
set_tests_properties(${TestName} PROPERTIES LABELS "WTSAPI_EXTRA")
endforeach()
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "WinPR/Test")
endif()

View File

@@ -0,0 +1,53 @@
#include <winpr/crt.h>
#include <winpr/error.h>
#include <winpr/wtsapi.h>
#include <winpr/environment.h>
int TestWtsApiEnumerateProcesses(int argc, char* argv[])
{
DWORD count = 0;
BOOL bSuccess = 0;
HANDLE hServer = nullptr;
PWTS_PROCESS_INFOA pProcessInfo = nullptr;
WINPR_UNUSED(argc);
WINPR_UNUSED(argv);
#ifndef _WIN32
if (!GetEnvironmentVariableA("WTSAPI_LIBRARY", nullptr, 0))
{
printf("%s: No RDS environment detected, skipping test\n", __func__);
return 0;
}
#endif
hServer = WTS_CURRENT_SERVER_HANDLE;
count = 0;
pProcessInfo = nullptr;
bSuccess = WTSEnumerateProcessesA(hServer, 0, 1, &pProcessInfo, &count);
if (!bSuccess)
{
printf("WTSEnumerateProcesses failed: %" PRIu32 "\n", GetLastError());
return -1;
}
int rc = 0;
{
printf("WTSEnumerateProcesses enumerated %"PRIu32" process:\n", count);
for (DWORD i = 0; i < count; i++)
{
const WTS_PROCESS_INFOA* cur = &pProcessInfo[i];
if (!cur->pProcessName)
rc = -1;
printf("\t[%" PRIu32 "]: %s (%" PRIu32 ")\n", i, cur->pProcessName, cur->ProcessId);
}
}
WTSFreeMemory(pProcessInfo);
return rc;
}

View File

@@ -0,0 +1,50 @@
#include <winpr/crt.h>
#include <winpr/error.h>
#include <winpr/wtsapi.h>
#include <winpr/environment.h>
int TestWtsApiEnumerateSessions(int argc, char* argv[])
{
DWORD count = 0;
BOOL bSuccess = 0;
HANDLE hServer = nullptr;
PWTS_SESSION_INFOA pSessionInfo = nullptr;
WINPR_UNUSED(argc);
WINPR_UNUSED(argv);
#ifndef _WIN32
if (!GetEnvironmentVariableA("WTSAPI_LIBRARY", nullptr, 0))
{
printf("%s: No RDS environment detected, skipping test\n", __func__);
return 0;
}
#endif
hServer = WTS_CURRENT_SERVER_HANDLE;
count = 0;
pSessionInfo = nullptr;
bSuccess = WTSEnumerateSessionsA(hServer, 0, 1, &pSessionInfo, &count);
if (!bSuccess)
{
printf("WTSEnumerateSessions failed: %" PRIu32 "\n", GetLastError());
return 0;
}
printf("WTSEnumerateSessions count: %" PRIu32 "\n", count);
for (DWORD index = 0; index < count; index++)
{
printf("[%" PRIu32 "] SessionId: %" PRIu32 " WinstationName: '%s' State: %s (%u)\n", index,
pSessionInfo[index].SessionId, pSessionInfo[index].pWinStationName,
WTSSessionStateToString(pSessionInfo[index].State), pSessionInfo[index].State);
}
WTSFreeMemory(pSessionInfo);
return 0;
}

View File

@@ -0,0 +1,21 @@
#include <winpr/crt.h>
#include <winpr/error.h>
#include <winpr/wtsapi.h>
int TestWtsApiExtraDisconnectSession(int argc, char* argv[])
{
WINPR_UNUSED(argc);
WINPR_UNUSED(argv);
HANDLE hServer = WTS_CURRENT_SERVER_HANDLE;
BOOL bSuccess = WTSDisconnectSession(hServer, WTS_CURRENT_SESSION, FALSE);
if (!bSuccess)
{
printf("WTSDisconnectSession failed: %" PRIu32 "\n", GetLastError());
return -1;
}
return 0;
}

View File

@@ -0,0 +1,49 @@
#include <winpr/crt.h>
#include <winpr/error.h>
#include <winpr/wtsapi.h>
int TestWtsApiExtraDynamicVirtualChannel(int argc, char* argv[])
{
WINPR_UNUSED(argc);
WINPR_UNUSED(argv);
HANDLE hVirtualChannel =
WTSVirtualChannelOpenEx(WTS_CURRENT_SESSION, "ECHO", WTS_CHANNEL_OPTION_DYNAMIC);
if (hVirtualChannel == INVALID_HANDLE_VALUE)
{
printf("WTSVirtualChannelOpen failed: %" PRIu32 "\n", GetLastError());
return -1;
}
printf("WTSVirtualChannelOpen opend");
ULONG bytesWritten = 0;
char buffer[1024] = WINPR_C_ARRAY_INIT;
size_t length = sizeof(buffer);
BOOL bSuccess = WTSVirtualChannelWrite(hVirtualChannel, buffer, length, &bytesWritten);
if (!bSuccess)
{
printf("WTSVirtualChannelWrite failed: %" PRIu32 "\n", GetLastError());
return -1;
}
printf("WTSVirtualChannelWrite written");
ULONG bytesRead = 0;
bSuccess = WTSVirtualChannelRead(hVirtualChannel, 5000, (PCHAR)buffer, length, &bytesRead);
if (!bSuccess)
{
printf("WTSVirtualChannelRead failed: %" PRIu32 "\n", GetLastError());
return -1;
}
printf("WTSVirtualChannelRead read");
if (!WTSVirtualChannelClose(hVirtualChannel))
{
printf("WTSVirtualChannelClose failed\n");
return -1;
}
return 0;
}

View File

@@ -0,0 +1,22 @@
#include <winpr/crt.h>
#include <winpr/error.h>
#include <winpr/wtsapi.h>
int TestWtsApiExtraLogoffSession(int argc, char* argv[])
{
WINPR_UNUSED(argc);
WINPR_UNUSED(argv);
HANDLE hServer = WTS_CURRENT_SERVER_HANDLE;
BOOL bSuccess = WTSLogoffSession(hServer, WTS_CURRENT_SESSION, FALSE);
if (!bSuccess)
{
printf("WTSLogoffSession failed: %" PRIu32 "\n", GetLastError());
return -1;
}
return 0;
}

View File

@@ -0,0 +1,29 @@
#include <winpr/crt.h>
#include <winpr/error.h>
#include <winpr/wtsapi.h>
#include <winpr/user.h>
#define TITLE "thats the title"
#define MESSAGE "thats the message"
int TestWtsApiExtraSendMessage(int argc, char* argv[])
{
WINPR_UNUSED(argc);
WINPR_UNUSED(argv);
HANDLE hServer = WTS_CURRENT_SERVER_HANDLE;
DWORD result = 0;
BOOL bSuccess = WTSSendMessageA(hServer, WTS_CURRENT_SESSION, TITLE, strlen(TITLE) + 1, MESSAGE,
strlen(MESSAGE) + 1, MB_CANCELTRYCONTINUE, 3, &result, TRUE);
if (!bSuccess)
{
printf("WTSSendMessage failed: %" PRIu32 "\n", GetLastError());
return -1;
}
printf("WTSSendMessage got result: %" PRIu32 "\n", result);
return 0;
}

View File

@@ -0,0 +1,36 @@
#include <winpr/crt.h>
#include <winpr/error.h>
#include <winpr/wtsapi.h>
#include <winpr/input.h>
#include <winpr/environment.h>
int TestWtsApiExtraStartRemoteSessionEx(int argc, char* argv[])
{
WINPR_UNUSED(argc);
WINPR_UNUSED(argv);
ULONG logonId = 0;
char logonIdStr[10] = WINPR_C_ARRAY_INIT;
DWORD bSuccess = GetEnvironmentVariableA("TEST_SESSION_LOGON_ID", logonIdStr, 10);
if (bSuccess > 0)
{
errno = 0;
logonId = strtoul(logonIdStr, nullptr, 0);
if (errno != 0)
return -1;
}
bSuccess = WTSStartRemoteControlSessionEx(
nullptr, logonId, VK_F10, REMOTECONTROL_KBDSHIFT_HOTKEY | REMOTECONTROL_KBDCTRL_HOTKEY,
REMOTECONTROL_FLAG_DISABLE_INPUT);
if (!bSuccess)
{
printf("WTSStartRemoteControlSessionEx failed: %" PRIu32 "\n", GetLastError());
return -1;
}
return 0;
}

View File

@@ -0,0 +1,51 @@
#include <winpr/crt.h>
#include <winpr/error.h>
#include <winpr/wtsapi.h>
int TestWtsApiExtraVirtualChannel(int argc, char* argv[])
{
WINPR_UNUSED(argc);
WINPR_UNUSED(argv);
char buffer[1024] = WINPR_C_ARRAY_INIT;
const size_t length = sizeof(buffer);
HANDLE hVirtualChannel =
WTSVirtualChannelOpen(WTS_CURRENT_SERVER_HANDLE, WTS_CURRENT_SESSION, "sample");
if (hVirtualChannel == INVALID_HANDLE_VALUE)
{
printf("WTSVirtualChannelOpen failed: %" PRIu32 "\n", GetLastError());
return -1;
}
printf("WTSVirtualChannelOpen opend");
ULONG bytesWritten = 0;
BOOL bSuccess = WTSVirtualChannelWrite(hVirtualChannel, buffer, length, &bytesWritten);
if (!bSuccess)
{
printf("WTSVirtualChannelWrite failed: %" PRIu32 "\n", GetLastError());
return -1;
}
printf("WTSVirtualChannelWrite written");
ULONG bytesRead = 0;
bSuccess = WTSVirtualChannelRead(hVirtualChannel, 5000, buffer, length, &bytesRead);
if (!bSuccess)
{
printf("WTSVirtualChannelRead failed: %" PRIu32 "\n", GetLastError());
return -1;
}
printf("WTSVirtualChannelRead read");
if (!WTSVirtualChannelClose(hVirtualChannel))
{
printf("WTSVirtualChannelClose failed\n");
return -1;
}
return 0;
}

View File

@@ -0,0 +1,225 @@
#include <winpr/crt.h>
#include <winpr/error.h>
#include <winpr/wtsapi.h>
#include <winpr/environment.h>
int TestWtsApiQuerySessionInformation(int argc, char* argv[])
{
DWORD count = 0;
BOOL bSuccess = 0;
HANDLE hServer = nullptr;
LPSTR pBuffer = nullptr;
DWORD sessionId = 0;
DWORD bytesReturned = 0;
PWTS_SESSION_INFOA pSessionInfo = nullptr;
WINPR_UNUSED(argc);
WINPR_UNUSED(argv);
#ifndef _WIN32
if (!GetEnvironmentVariableA("WTSAPI_LIBRARY", nullptr, 0))
{
printf("%s: No RDS environment detected, skipping test\n", __func__);
return 0;
}
#endif
hServer = WTS_CURRENT_SERVER_HANDLE;
count = 0;
pSessionInfo = nullptr;
bSuccess = WTSEnumerateSessionsA(hServer, 0, 1, &pSessionInfo, &count);
if (!bSuccess)
{
printf("WTSEnumerateSessions failed: %" PRIu32 "\n", GetLastError());
return 0;
}
printf("WTSEnumerateSessions count: %" PRIu32 "\n", count);
for (DWORD index = 0; index < count; index++)
{
char* Username = nullptr;
char* Domain = nullptr;
char* ClientName = nullptr;
ULONG ClientBuildNumber = 0;
USHORT ClientProductId = 0;
ULONG ClientHardwareId = 0;
USHORT ClientProtocolType = 0;
PWTS_CLIENT_DISPLAY ClientDisplay = nullptr;
PWTS_CLIENT_ADDRESS ClientAddress = nullptr;
WTS_CONNECTSTATE_CLASS ConnectState = WTSInit;
pBuffer = nullptr;
bytesReturned = 0;
sessionId = pSessionInfo[index].SessionId;
printf("[%" PRIu32 "] SessionId: %" PRIu32 " State: %s (%u) WinstationName: '%s'\n", index,
pSessionInfo[index].SessionId, WTSSessionStateToString(pSessionInfo[index].State),
pSessionInfo[index].State, pSessionInfo[index].pWinStationName);
/* WTSUserName */
bSuccess =
WTSQuerySessionInformationA(hServer, sessionId, WTSUserName, &pBuffer, &bytesReturned);
if (!bSuccess)
{
printf("WTSQuerySessionInformation WTSUserName failed: %" PRIu32 "\n", GetLastError());
return -1;
}
Username = (char*)pBuffer;
printf("\tWTSUserName: '%s'\n", Username);
/* WTSDomainName */
bSuccess = WTSQuerySessionInformationA(hServer, sessionId, WTSDomainName, &pBuffer,
&bytesReturned);
if (!bSuccess)
{
printf("WTSQuerySessionInformation WTSDomainName failed: %" PRIu32 "\n",
GetLastError());
return -1;
}
Domain = (char*)pBuffer;
printf("\tWTSDomainName: '%s'\n", Domain);
/* WTSConnectState */
bSuccess = WTSQuerySessionInformationA(hServer, sessionId, WTSConnectState, &pBuffer,
&bytesReturned);
if (!bSuccess)
{
printf("WTSQuerySessionInformation WTSConnectState failed: %" PRIu32 "\n",
GetLastError());
return -1;
}
ConnectState = *((WTS_CONNECTSTATE_CLASS*)pBuffer);
printf("\tWTSConnectState: %u (%s)\n", ConnectState, WTSSessionStateToString(ConnectState));
/* WTSClientBuildNumber */
bSuccess = WTSQuerySessionInformationA(hServer, sessionId, WTSClientBuildNumber, &pBuffer,
&bytesReturned);
if (!bSuccess)
{
printf("WTSQuerySessionInformation WTSClientBuildNumber failed: %" PRIu32 "\n",
GetLastError());
return -1;
}
ClientBuildNumber = *((ULONG*)pBuffer);
printf("\tWTSClientBuildNumber: %" PRIu32 "\n", ClientBuildNumber);
/* WTSClientName */
bSuccess = WTSQuerySessionInformationA(hServer, sessionId, WTSClientName, &pBuffer,
&bytesReturned);
if (!bSuccess)
{
printf("WTSQuerySessionInformation WTSClientName failed: %" PRIu32 "\n",
GetLastError());
return -1;
}
ClientName = (char*)pBuffer;
printf("\tWTSClientName: '%s'\n", ClientName);
/* WTSClientProductId */
bSuccess = WTSQuerySessionInformationA(hServer, sessionId, WTSClientProductId, &pBuffer,
&bytesReturned);
if (!bSuccess)
{
printf("WTSQuerySessionInformation WTSClientProductId failed: %" PRIu32 "\n",
GetLastError());
return -1;
}
ClientProductId = *((USHORT*)pBuffer);
printf("\tWTSClientProductId: %" PRIu16 "\n", ClientProductId);
/* WTSClientHardwareId */
bSuccess = WTSQuerySessionInformationA(hServer, sessionId, WTSClientHardwareId, &pBuffer,
&bytesReturned);
if (!bSuccess)
{
printf("WTSQuerySessionInformation WTSClientHardwareId failed: %" PRIu32 "\n",
GetLastError());
return -1;
}
ClientHardwareId = *((ULONG*)pBuffer);
printf("\tWTSClientHardwareId: %" PRIu32 "\n", ClientHardwareId);
/* WTSClientAddress */
bSuccess = WTSQuerySessionInformationA(hServer, sessionId, WTSClientAddress, &pBuffer,
&bytesReturned);
if (!bSuccess)
{
printf("WTSQuerySessionInformation WTSClientAddress failed: %" PRIu32 "\n",
GetLastError());
return -1;
}
ClientAddress = (PWTS_CLIENT_ADDRESS)pBuffer;
printf("\tWTSClientAddress: AddressFamily: %" PRIu32 " Address: ",
ClientAddress->AddressFamily);
for (DWORD i = 0; i < sizeof(ClientAddress->Address); i++)
printf("%02" PRIX8 "", ClientAddress->Address[i]);
printf("\n");
/* WTSClientDisplay */
bSuccess = WTSQuerySessionInformationA(hServer, sessionId, WTSClientDisplay, &pBuffer,
&bytesReturned);
if (!bSuccess)
{
printf("WTSQuerySessionInformation WTSClientDisplay failed: %" PRIu32 "\n",
GetLastError());
return -1;
}
ClientDisplay = (PWTS_CLIENT_DISPLAY)pBuffer;
printf("\tWTSClientDisplay: HorizontalResolution: %" PRIu32 " VerticalResolution: %" PRIu32
" ColorDepth: %" PRIu32 "\n",
ClientDisplay->HorizontalResolution, ClientDisplay->VerticalResolution,
ClientDisplay->ColorDepth);
/* WTSClientProtocolType */
bSuccess = WTSQuerySessionInformationA(hServer, sessionId, WTSClientProtocolType, &pBuffer,
&bytesReturned);
if (!bSuccess)
{
printf("WTSQuerySessionInformation WTSClientProtocolType failed: %" PRIu32 "\n",
GetLastError());
return -1;
}
ClientProtocolType = *((USHORT*)pBuffer);
printf("\tWTSClientProtocolType: %" PRIu16 "\n", ClientProtocolType);
}
WTSFreeMemory(pSessionInfo);
return 0;
}

View File

@@ -0,0 +1,62 @@
#include <winpr/crt.h>
#include <winpr/error.h>
#include <winpr/wtsapi.h>
#include <winpr/environment.h>
int TestWtsApiSessionNotification(int argc, char* argv[])
{
HWND hWnd = nullptr;
BOOL bSuccess = 0;
DWORD dwFlags = 0;
WINPR_UNUSED(argc);
WINPR_UNUSED(argv);
#ifndef _WIN32
if (!GetEnvironmentVariableA("WTSAPI_LIBRARY", nullptr, 0))
{
printf("%s: No RDS environment detected, skipping test\n", __func__);
return 0;
}
#else
/* We create a message-only window and use the predefined class name "STATIC" for simplicity */
hWnd = CreateWindowA("STATIC", "TestWtsApiSessionNotification", 0, 0, 0, 0, 0, HWND_MESSAGE,
nullptr, nullptr, nullptr);
if (!hWnd)
{
printf("%s: error creating message-only window: %" PRIu32 "\n", __func__, GetLastError());
return -1;
}
#endif
dwFlags = NOTIFY_FOR_ALL_SESSIONS;
bSuccess = WTSRegisterSessionNotification(hWnd, dwFlags);
if (!bSuccess)
{
printf("%s: WTSRegisterSessionNotification failed: %" PRIu32 "\n", __func__,
GetLastError());
return -1;
}
bSuccess = WTSUnRegisterSessionNotification(hWnd);
#ifdef _WIN32
if (hWnd)
{
DestroyWindow(hWnd);
hWnd = nullptr;
}
#endif
if (!bSuccess)
{
printf("%s: WTSUnRegisterSessionNotification failed: %" PRIu32 "\n", __func__,
GetLastError());
return -1;
}
return 0;
}

View File

@@ -0,0 +1,35 @@
#include <winpr/crt.h>
#include <winpr/error.h>
#include <winpr/wtsapi.h>
#include <winpr/environment.h>
int TestWtsApiShutdownSystem(int argc, char* argv[])
{
BOOL bSuccess = 0;
HANDLE hServer = nullptr;
DWORD ShutdownFlag = 0;
WINPR_UNUSED(argc);
WINPR_UNUSED(argv);
#ifndef _WIN32
if (!GetEnvironmentVariableA("WTSAPI_LIBRARY", nullptr, 0))
{
printf("%s: No RDS environment detected, skipping test\n", __func__);
return 0;
}
#endif
hServer = WTS_CURRENT_SERVER_HANDLE;
ShutdownFlag = WTS_WSD_SHUTDOWN;
bSuccess = WTSShutdownSystem(hServer, ShutdownFlag);
if (!bSuccess)
{
printf("WTSShutdownSystem failed: %" PRIu32 "\n", GetLastError());
return -1;
}
return 0;
}

View File

@@ -0,0 +1,39 @@
#include <winpr/crt.h>
#include <winpr/error.h>
#include <winpr/wtsapi.h>
#include <winpr/environment.h>
int TestWtsApiWaitSystemEvent(int argc, char* argv[])
{
BOOL bSuccess = 0;
HANDLE hServer = nullptr;
DWORD eventMask = 0;
DWORD eventFlags = 0;
WINPR_UNUSED(argc);
WINPR_UNUSED(argv);
#ifndef _WIN32
if (!GetEnvironmentVariableA("WTSAPI_LIBRARY", nullptr, 0))
{
printf("%s: No RDS environment detected, skipping test\n", __func__);
return 0;
}
#endif
hServer = WTS_CURRENT_SERVER_HANDLE;
eventMask = WTS_EVENT_ALL;
eventFlags = 0;
bSuccess = WTSWaitSystemEvent(hServer, eventMask, &eventFlags);
if (!bSuccess)
{
printf("WTSWaitSystemEvent failed: %" PRIu32 "\n", GetLastError());
return -1;
}
return 0;
}