Milestone 5: deliver embedded RDP sessions and lifecycle hardening
This commit is contained in:
22
third_party/FreeRDP/channels/rdp2tcp/CMakeLists.txt
vendored
Normal file
22
third_party/FreeRDP/channels/rdp2tcp/CMakeLists.txt
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
# FreeRDP: A Remote Desktop Protocol Implementation
|
||||
# FreeRDP cmake build script
|
||||
#
|
||||
# Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
define_channel("rdp2tcp")
|
||||
|
||||
if(WITH_CLIENT_CHANNELS)
|
||||
add_channel_client(${MODULE_PREFIX} ${CHANNEL_NAME})
|
||||
endif()
|
||||
18
third_party/FreeRDP/channels/rdp2tcp/ChannelOptions.cmake
vendored
Normal file
18
third_party/FreeRDP/channels/rdp2tcp/ChannelOptions.cmake
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
set(OPTION_DEFAULT OFF)
|
||||
set(OPTION_CLIENT_DEFAULT ON)
|
||||
set(OPTION_SERVER_DEFAULT OFF)
|
||||
|
||||
define_channel_options(
|
||||
NAME
|
||||
"rdp2tcp"
|
||||
TYPE
|
||||
"static"
|
||||
DESCRIPTION
|
||||
"Tunneling TCP over RDP"
|
||||
DEFAULT
|
||||
${OPTION_DEFAULT}
|
||||
CLIENT_DEFAULT
|
||||
${OPTION_CLIENT_DEFAULT}
|
||||
SERVER_DEFAULT
|
||||
${OPTION_SERVER_DEFAULT}
|
||||
)
|
||||
23
third_party/FreeRDP/channels/rdp2tcp/client/CMakeLists.txt
vendored
Normal file
23
third_party/FreeRDP/channels/rdp2tcp/client/CMakeLists.txt
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
# FreeRDP: A Remote Desktop Protocol Implementation
|
||||
# FreeRDP cmake build script
|
||||
#
|
||||
# Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
define_channel_client("rdp2tcp")
|
||||
|
||||
set(${MODULE_PREFIX}_SRCS rdp2tcp_main.c)
|
||||
|
||||
set(${MODULE_PREFIX}_LIBS freerdp)
|
||||
add_channel_client_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} TRUE "VirtualChannelEntryEx")
|
||||
330
third_party/FreeRDP/channels/rdp2tcp/client/rdp2tcp_main.c
vendored
Normal file
330
third_party/FreeRDP/channels/rdp2tcp/client/rdp2tcp_main.c
vendored
Normal file
@@ -0,0 +1,330 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* rdp2tcp Virtual Channel Extension
|
||||
*
|
||||
* Copyright 2017 Artur Zaprzala
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <winpr/assert.h>
|
||||
|
||||
#include <winpr/file.h>
|
||||
#include <winpr/pipe.h>
|
||||
#include <winpr/thread.h>
|
||||
|
||||
#include <freerdp/freerdp.h>
|
||||
#include <freerdp/svc.h>
|
||||
#include <freerdp/channels/rdp2tcp.h>
|
||||
|
||||
#include <freerdp/log.h>
|
||||
#define TAG CLIENT_TAG(RDP2TCP_DVC_CHANNEL_NAME)
|
||||
|
||||
typedef struct
|
||||
{
|
||||
HANDLE hStdOutputRead;
|
||||
HANDLE hStdInputWrite;
|
||||
HANDLE hProcess;
|
||||
HANDLE copyThread;
|
||||
HANDLE writeComplete;
|
||||
DWORD openHandle;
|
||||
void* initHandle;
|
||||
CHANNEL_ENTRY_POINTS_FREERDP_EX channelEntryPoints;
|
||||
char buffer[16 * 1024];
|
||||
char* commandline;
|
||||
} Plugin;
|
||||
|
||||
static int init_external_addin(Plugin* plugin)
|
||||
{
|
||||
int rc = -1;
|
||||
SECURITY_ATTRIBUTES saAttr = WINPR_C_ARRAY_INIT;
|
||||
STARTUPINFOA siStartInfo = WINPR_C_ARRAY_INIT; /* Using ANSI type to match CreateProcessA */
|
||||
PROCESS_INFORMATION procInfo = WINPR_C_ARRAY_INIT;
|
||||
|
||||
WINPR_ASSERT(plugin);
|
||||
|
||||
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
|
||||
saAttr.bInheritHandle = TRUE;
|
||||
saAttr.lpSecurityDescriptor = nullptr;
|
||||
siStartInfo.cb = sizeof(STARTUPINFO);
|
||||
siStartInfo.hStdError = GetStdHandle(STD_ERROR_HANDLE);
|
||||
siStartInfo.dwFlags = STARTF_USESTDHANDLES;
|
||||
|
||||
// Create pipes
|
||||
if (!CreatePipe(&plugin->hStdOutputRead, &siStartInfo.hStdOutput, &saAttr, 0))
|
||||
{
|
||||
WLog_ERR(TAG, "stdout CreatePipe");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (!SetHandleInformation(plugin->hStdOutputRead, HANDLE_FLAG_INHERIT, 0))
|
||||
{
|
||||
WLog_ERR(TAG, "stdout SetHandleInformation");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (!CreatePipe(&siStartInfo.hStdInput, &plugin->hStdInputWrite, &saAttr, 0))
|
||||
{
|
||||
WLog_ERR(TAG, "stdin CreatePipe");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (!SetHandleInformation(plugin->hStdInputWrite, HANDLE_FLAG_INHERIT, 0))
|
||||
{
|
||||
WLog_ERR(TAG, "stdin SetHandleInformation");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
// Execute plugin
|
||||
const ADDIN_ARGV* args = (const ADDIN_ARGV*)plugin->channelEntryPoints.pExtendedData;
|
||||
if (!args || (args->argc < 2))
|
||||
{
|
||||
WLog_ERR(TAG, "missing command line options");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
plugin->commandline = _strdup(args->argv[1]);
|
||||
if (!CreateProcessA(nullptr,
|
||||
plugin->commandline, // command line
|
||||
nullptr, // process security attributes
|
||||
nullptr, // primary thread security attributes
|
||||
TRUE, // handles are inherited
|
||||
0, // creation flags
|
||||
nullptr, // use parent's environment
|
||||
nullptr, // use parent's current directory
|
||||
&siStartInfo, // STARTUPINFO pointer
|
||||
&procInfo // receives PROCESS_INFORMATION
|
||||
))
|
||||
{
|
||||
WLog_ERR(TAG, "fork for addin");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
plugin->hProcess = procInfo.hProcess;
|
||||
|
||||
rc = 0;
|
||||
fail:
|
||||
(void)CloseHandle(procInfo.hThread);
|
||||
(void)CloseHandle(siStartInfo.hStdOutput);
|
||||
(void)CloseHandle(siStartInfo.hStdInput);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static DWORD WINAPI copyThread(void* data)
|
||||
{
|
||||
DWORD status = WAIT_OBJECT_0;
|
||||
Plugin* plugin = (Plugin*)data;
|
||||
size_t const bufsize = 16ULL * 1024ULL;
|
||||
|
||||
WINPR_ASSERT(plugin);
|
||||
|
||||
while (status == WAIT_OBJECT_0)
|
||||
{
|
||||
(void)ResetEvent(plugin->writeComplete);
|
||||
|
||||
DWORD dwRead = 0;
|
||||
char* buffer = calloc(bufsize, sizeof(char));
|
||||
|
||||
if (!buffer)
|
||||
{
|
||||
(void)fprintf(stderr, "rdp2tcp copyThread: malloc failed\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
// if (!ReadFile(plugin->hStdOutputRead, plugin->buffer, sizeof plugin->buffer, &dwRead,
|
||||
// nullptr))
|
||||
if (!ReadFile(plugin->hStdOutputRead, buffer, bufsize, &dwRead, nullptr))
|
||||
{
|
||||
free(buffer);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (plugin->channelEntryPoints.pVirtualChannelWriteEx(
|
||||
plugin->initHandle, plugin->openHandle, buffer, dwRead, buffer) != CHANNEL_RC_OK)
|
||||
{
|
||||
free(buffer);
|
||||
(void)fprintf(stderr, "rdp2tcp copyThread failed %i\n", (int)dwRead);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
HANDLE handles[] = { plugin->writeComplete,
|
||||
freerdp_abort_event(plugin->channelEntryPoints.context) };
|
||||
status = WaitForMultipleObjects(ARRAYSIZE(handles), handles, FALSE, INFINITE);
|
||||
}
|
||||
|
||||
fail:
|
||||
ExitThread(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void closeChannel(Plugin* plugin)
|
||||
{
|
||||
WINPR_ASSERT(plugin);
|
||||
WINPR_ASSERT(plugin->channelEntryPoints.pVirtualChannelCloseEx);
|
||||
plugin->channelEntryPoints.pVirtualChannelCloseEx(plugin->initHandle, plugin->openHandle);
|
||||
}
|
||||
|
||||
static void dataReceived(Plugin* plugin, void* pData, UINT32 dataLength, UINT32 totalLength,
|
||||
UINT32 dataFlags)
|
||||
{
|
||||
DWORD dwWritten = 0;
|
||||
|
||||
WINPR_ASSERT(plugin);
|
||||
|
||||
if (dataFlags & CHANNEL_FLAG_SUSPEND)
|
||||
return;
|
||||
|
||||
if (dataFlags & CHANNEL_FLAG_RESUME)
|
||||
return;
|
||||
|
||||
if (dataFlags & CHANNEL_FLAG_FIRST)
|
||||
{
|
||||
if (!WriteFile(plugin->hStdInputWrite, &totalLength, sizeof(totalLength), &dwWritten,
|
||||
nullptr))
|
||||
closeChannel(plugin);
|
||||
}
|
||||
|
||||
if (!WriteFile(plugin->hStdInputWrite, pData, dataLength, &dwWritten, nullptr))
|
||||
closeChannel(plugin);
|
||||
}
|
||||
|
||||
static void VCAPITYPE VirtualChannelOpenEventEx(LPVOID lpUserParam,
|
||||
WINPR_ATTR_UNUSED DWORD openHandle, UINT event,
|
||||
LPVOID pData, UINT32 dataLength, UINT32 totalLength,
|
||||
UINT32 dataFlags)
|
||||
{
|
||||
Plugin* plugin = (Plugin*)lpUserParam;
|
||||
|
||||
WINPR_ASSERT(plugin);
|
||||
switch (event)
|
||||
{
|
||||
case CHANNEL_EVENT_DATA_RECEIVED:
|
||||
dataReceived(plugin, pData, dataLength, totalLength, dataFlags);
|
||||
break;
|
||||
|
||||
case CHANNEL_EVENT_WRITE_CANCELLED:
|
||||
free(pData);
|
||||
break;
|
||||
case CHANNEL_EVENT_WRITE_COMPLETE:
|
||||
(void)SetEvent(plugin->writeComplete);
|
||||
free(pData);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void channel_terminated(Plugin* plugin)
|
||||
{
|
||||
if (!plugin)
|
||||
return;
|
||||
|
||||
if (plugin->copyThread)
|
||||
(void)CloseHandle(plugin->copyThread);
|
||||
if (plugin->writeComplete)
|
||||
(void)CloseHandle(plugin->writeComplete);
|
||||
|
||||
(void)CloseHandle(plugin->hStdInputWrite);
|
||||
(void)CloseHandle(plugin->hStdOutputRead);
|
||||
TerminateProcess(plugin->hProcess, 0);
|
||||
(void)CloseHandle(plugin->hProcess);
|
||||
free(plugin->commandline);
|
||||
free(plugin);
|
||||
}
|
||||
|
||||
static void channel_initialized(Plugin* plugin)
|
||||
{
|
||||
WINPR_ASSERT(plugin);
|
||||
WINPR_ASSERT(!plugin->writeComplete);
|
||||
plugin->writeComplete = CreateEvent(nullptr, TRUE, FALSE, nullptr);
|
||||
|
||||
WINPR_ASSERT(!plugin->copyThread);
|
||||
plugin->copyThread = CreateThread(nullptr, 0, copyThread, plugin, 0, nullptr);
|
||||
}
|
||||
|
||||
static VOID VCAPITYPE VirtualChannelInitEventEx(LPVOID lpUserParam, LPVOID pInitHandle, UINT event,
|
||||
WINPR_ATTR_UNUSED LPVOID pData,
|
||||
WINPR_ATTR_UNUSED UINT dataLength)
|
||||
{
|
||||
Plugin* plugin = (Plugin*)lpUserParam;
|
||||
|
||||
WINPR_ASSERT(plugin);
|
||||
|
||||
switch (event)
|
||||
{
|
||||
case CHANNEL_EVENT_INITIALIZED:
|
||||
channel_initialized(plugin);
|
||||
break;
|
||||
|
||||
case CHANNEL_EVENT_CONNECTED:
|
||||
WINPR_ASSERT(plugin);
|
||||
WINPR_ASSERT(plugin->channelEntryPoints.pVirtualChannelOpenEx);
|
||||
if (plugin->channelEntryPoints.pVirtualChannelOpenEx(
|
||||
pInitHandle, &plugin->openHandle, RDP2TCP_DVC_CHANNEL_NAME,
|
||||
VirtualChannelOpenEventEx) != CHANNEL_RC_OK)
|
||||
return;
|
||||
|
||||
break;
|
||||
|
||||
case CHANNEL_EVENT_DISCONNECTED:
|
||||
closeChannel(plugin);
|
||||
break;
|
||||
|
||||
case CHANNEL_EVENT_TERMINATED:
|
||||
channel_terminated(plugin);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#define VirtualChannelEntryEx rdp2tcp_VirtualChannelEntryEx
|
||||
FREERDP_ENTRY_POINT(BOOL VCAPITYPE VirtualChannelEntryEx(PCHANNEL_ENTRY_POINTS_EX pEntryPoints,
|
||||
PVOID pInitHandle))
|
||||
{
|
||||
CHANNEL_ENTRY_POINTS_FREERDP_EX* pEntryPointsEx =
|
||||
(CHANNEL_ENTRY_POINTS_FREERDP_EX*)pEntryPoints;
|
||||
WINPR_ASSERT(pEntryPointsEx);
|
||||
WINPR_ASSERT(pEntryPointsEx->cbSize >= sizeof(CHANNEL_ENTRY_POINTS_FREERDP_EX) &&
|
||||
pEntryPointsEx->MagicNumber == FREERDP_CHANNEL_MAGIC_NUMBER);
|
||||
|
||||
Plugin* plugin = (Plugin*)calloc(1, sizeof(Plugin));
|
||||
|
||||
if (!plugin)
|
||||
return FALSE;
|
||||
|
||||
plugin->initHandle = pInitHandle;
|
||||
plugin->channelEntryPoints = *pEntryPointsEx;
|
||||
|
||||
if (init_external_addin(plugin) < 0)
|
||||
{
|
||||
channel_terminated(plugin);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
CHANNEL_DEF channelDef = WINPR_C_ARRAY_INIT;
|
||||
strncpy(channelDef.name, RDP2TCP_DVC_CHANNEL_NAME, sizeof(channelDef.name));
|
||||
channelDef.options =
|
||||
CHANNEL_OPTION_INITIALIZED | CHANNEL_OPTION_ENCRYPT_RDP | CHANNEL_OPTION_COMPRESS_RDP;
|
||||
|
||||
if (pEntryPointsEx->pVirtualChannelInitEx(plugin, nullptr, pInitHandle, &channelDef, 1,
|
||||
VIRTUAL_CHANNEL_VERSION_WIN2000,
|
||||
VirtualChannelInitEventEx) != CHANNEL_RC_OK)
|
||||
{
|
||||
channel_terminated(plugin);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
Reference in New Issue
Block a user