Added proxy intercept channels

* New configuration to intercept certain channels
* Added rdpdr intercept implementation
This commit is contained in:
Armin Novak 2021-12-14 13:02:29 +01:00 committed by akallabeth
parent 3c72cc3306
commit 4e6e5be654
19 changed files with 2698 additions and 156 deletions

View File

@ -69,6 +69,8 @@ struct proxy_config
BOOL PassthroughIsBlacklist;
char** Passthrough;
size_t PassthroughCount;
char** Intercept;
size_t InterceptCount;
/* clipboard specific settings */
BOOL TextOnly;

View File

@ -33,6 +33,23 @@ typedef struct proxy_data proxyData;
typedef struct proxy_module proxyModule;
typedef struct channel_data_event_info proxyChannelDataEventInfo;
typedef struct _InterceptContextMapEntry
{
void (*free)(struct _InterceptContextMapEntry*);
} InterceptContextMapEntry;
/* All proxy interception channels derive from this base struct
* and set their cleanup function accordingly. */
static INLINE void intercept_context_entry_free(void* obj)
{
InterceptContextMapEntry* entry = obj;
if (!entry)
return;
if (!entry->free)
return;
entry->free(entry);
}
/**
* Wraps rdpContext and holds the state for the proxy's server.
*/
@ -44,6 +61,8 @@ struct p_server_context
HANDLE vcm;
HANDLE dynvcReady;
wHashTable* interceptContextMap;
};
typedef struct p_server_context pServerContext;
@ -83,6 +102,16 @@ struct p_client_context
BOOL input_state_sync_pending;
UINT32 input_state;
wHashTable* interceptContextMap;
UINT32 computerNameLen;
BOOL computerNameUnicode;
union
{
WCHAR* wc;
char* c;
void* v;
} computerName;
};
/**

View File

@ -68,8 +68,8 @@ struct proxy_plugin
proxyHookFn ClientX509Certificate; /* 71 custom=rdpContext* */
proxyHookFn ClientLoginFailure; /* 72 custom=rdpContext* */
proxyHookFn ClientEndPaint; /* 73 custom=rdpContext* */
UINT64 reserved3[96 - 74]; /* 74-95 */
proxyHookFn ClientRedirect; /* 74 custom=rdpContext* */
UINT64 reserved3[96 - 75]; /* 75-95 */
proxyHookFn ServerPostConnect; /* 96 custom=freerdp_peer* */
proxyHookFn ServerPeerActivate; /* 97 custom=freerdp_peer* */

View File

@ -35,7 +35,7 @@ typedef struct _scard_call_context scard_call_context;
FREERDP_API scard_call_context* smartcard_call_context_new(const rdpSettings* settings);
FREERDP_API void smartcard_call_context_free(scard_call_context* ctx);
FREERDP_API BOOL smartcard_call_context_signal_stop(scard_call_context* ctx);
FREERDP_API BOOL smartcard_call_context_signal_stop(scard_call_context* ctx, BOOL reset);
FREERDP_API BOOL smartcard_call_context_add(scard_call_context* ctx, const char* name);
FREERDP_API BOOL smartcard_call_cancel_context(scard_call_context* ctx, SCARDCONTEXT context);
FREERDP_API BOOL smartcard_call_cancel_all_context(scard_call_context* ctx);

View File

@ -1862,7 +1862,7 @@ void smartcard_call_context_free(scard_call_context* ctx)
if (!ctx)
return;
smartcard_call_context_signal_stop(ctx);
smartcard_call_context_signal_stop(ctx, FALSE);
LinkedList_Free(ctx->names);
if (ctx->StartedEvent)
@ -1941,10 +1941,14 @@ BOOL smartcard_call_is_configured(scard_call_context* ctx)
#endif
}
BOOL smartcard_call_context_signal_stop(scard_call_context* ctx)
BOOL smartcard_call_context_signal_stop(scard_call_context* ctx, BOOL reset)
{
WINPR_ASSERT(ctx);
if (!ctx->stopEvent)
return TRUE;
return SetEvent(ctx->stopEvent);
if (reset)
return ResetEvent(ctx->stopEvent);
else
return SetEvent(ctx->stopEvent);
}

View File

@ -41,6 +41,8 @@ set(${MODULE_PREFIX}_SRCS
set(PROXY_APP_SRCS freerdp_proxy.c)
add_subdirectory("channels")
# On windows create dll version information.
# Vendor, product and year are already set in top level CMakeLists.txt
if (WIN32)
@ -58,7 +60,7 @@ if (WIN32)
list(APPEND PROXY_APP_SRCS ${CMAKE_CURRENT_BINARY_DIR}/version.rc)
endif()
add_library(${MODULE_NAME} ${${MODULE_PREFIX}_SRCS})
add_library(${MODULE_NAME} ${${MODULE_PREFIX}_SRCS} $<TARGET_OBJECTS:pf_channels>)
set_target_properties(${MODULE_NAME} PROPERTIES OUTPUT_NAME ${MODULE_NAME}${FREERDP_API_VERSION})
if (WITH_LIBRARY_VERSIONING)
@ -113,3 +115,8 @@ option(WITH_PROXY_MODULES "Compile proxy modules" ON)
if (WITH_PROXY_MODULES)
add_subdirectory("modules")
endif()
option(WITH_PROXY_EMULATE_SMARTCARD "Compile proxy smartcard emulation" OFF)
if (WITH_PROXY_EMULATE_SMARTCARD)
add_definitions("-DWITH_PROXY_EMULATE_SMARTCARD")
endif()

View File

@ -0,0 +1,15 @@
set(MODULE_NAME pf_channels)
set(SOURCES
pf_channel_rdpdr.c
pf_channel_rdpdr.h
)
if (WITH_PROXY_EMULATE_SMARTCARD)
list(APPEND SOURCES
pf_channel_smartcard.c
pf_channel_smartcard.h
)
endif()
add_library(${MODULE_NAME} OBJECT ${SOURCES})

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,43 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* FreeRDP Proxy Server
*
* Copyright 2021 Armin Novak <armin.novak@thincast.com>
* Copyright 2021 Thincast Technologies GmbH
*
* 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.
*/
#ifndef FREERDP_SERVER_PROXY_RDPDR_H
#define FREERDP_SERVER_PROXY_RDPDR_H
#include <freerdp/server/proxy/proxy_context.h>
BOOL pf_channel_rdpdr_client_new(pClientContext* pc);
void pf_channel_rdpdr_client_free(pClientContext* pc);
BOOL pf_channel_rdpdr_client_reset(pClientContext* pc);
BOOL pf_channel_rdpdr_client_handle(pClientContext* pc, UINT16 channelId, const char* channel_name,
const BYTE* xdata, size_t xsize, UINT32 flags,
size_t totalSize);
BOOL pf_channel_rdpdr_server_new(pServerContext* ps);
void pf_channel_rdpdr_server_free(pServerContext* ps);
BOOL pf_channel_rdpdr_server_announce(pServerContext* ps);
BOOL pf_channel_rdpdr_server_handle(pServerContext* ps, UINT16 channelId, const char* channel_name,
const BYTE* xdata, size_t xsize, UINT32 flags,
size_t totalSize);
#endif /* FREERDP_SERVER_PROXY_RDPDR_H */

View File

@ -0,0 +1,396 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* FreeRDP Proxy Server
*
* Copyright 2021 Armin Novak <armin.novak@thincast.com>
* Copyright 2021 Thincast Technologies GmbH
*
* 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 <winpr/assert.h>
#include <winpr/string.h>
#include <winpr/smartcard.h>
#include <winpr/pool.h>
#include <freerdp/server/proxy/proxy_log.h>
#include <freerdp/emulate/scard/smartcard_emulate.h>
#include <freerdp/channels/scard.h>
#include <freerdp/channels/rdpdr.h>
#include <freerdp/utils/rdpdr_utils.h>
#include <freerdp/utils/smartcard_operations.h>
#include <freerdp/utils/smartcard_call.h>
#include "pf_channel_smartcard.h"
#include "pf_channel_rdpdr.h"
#define TAG PROXY_TAG("channel.scard")
#define SCARD_SVC_CHANNEL_NAME "SCARD"
typedef struct
{
InterceptContextMapEntry base;
scard_call_context* callctx;
PTP_POOL ThreadPool;
TP_CALLBACK_ENVIRON ThreadPoolEnv;
wArrayList* workObjects;
} pf_channel_client_context;
typedef struct
{
SMARTCARD_OPERATION op;
wStream* out;
pClientContext* pc;
UINT (*send_fkt)(pClientContext*, wStream*);
} pf_channel_client_queue_element;
static pf_channel_client_context* scard_get_client_context(pClientContext* pc)
{
pf_channel_client_context* scard;
WINPR_ASSERT(pc);
WINPR_ASSERT(pc->interceptContextMap);
scard = HashTable_GetItemValue(pc->interceptContextMap, SCARD_SVC_CHANNEL_NAME);
if (!scard)
WLog_WARN(TAG, "[%s] missing in pc->interceptContextMap", SCARD_SVC_CHANNEL_NAME);
return scard;
}
static BOOL pf_channel_client_write_iostatus(wStream* out, const SMARTCARD_OPERATION* op,
UINT32 ioStatus)
{
UINT16 component, packetid;
UINT32 dID, cID;
size_t pos;
WINPR_ASSERT(op);
WINPR_ASSERT(out);
pos = Stream_GetPosition(out);
if (Stream_Length(out) < 16)
return FALSE;
Stream_SetPosition(out, 0);
Stream_Read_UINT16(out, component);
Stream_Read_UINT16(out, packetid);
Stream_Read_UINT32(out, dID);
Stream_Read_UINT32(out, cID);
WINPR_ASSERT(component == RDPDR_CTYP_CORE);
WINPR_ASSERT(packetid == PAKID_CORE_DEVICE_IOCOMPLETION);
WINPR_ASSERT(dID == op->deviceID);
WINPR_ASSERT(cID == op->completionID);
Stream_Write_UINT32(out, ioStatus);
Stream_SetPosition(out, pos);
return TRUE;
}
struct thread_arg
{
pf_channel_client_context* scard;
pf_channel_client_queue_element* e;
};
static void queue_free(void* obj);
static void* queue_copy(const void* obj);
static VOID irp_thread(PTP_CALLBACK_INSTANCE Instance, PVOID Context, PTP_WORK Work)
{
struct thread_arg* arg = Context;
pf_channel_client_context* scard = arg->scard;
{
UINT32 ioStatus;
LONG rc = smartcard_irp_device_control_call(arg->scard->callctx, arg->e->out, &ioStatus,
&arg->e->op);
if (rc == CHANNEL_RC_OK)
{
if (pf_channel_client_write_iostatus(arg->e->out, &arg->e->op, ioStatus))
arg->e->send_fkt(arg->e->pc, arg->e->out);
}
}
queue_free(arg->e);
free(arg);
ArrayList_Remove(scard->workObjects, Work);
}
static BOOL start_irp_thread(pf_channel_client_context* scard,
const pf_channel_client_queue_element* e)
{
PTP_WORK work;
struct thread_arg* arg = calloc(1, sizeof(struct thread_arg));
if (!arg)
return FALSE;
arg->scard = scard;
arg->e = queue_copy(e);
if (!arg->e)
goto fail;
work = CreateThreadpoolWork(irp_thread, arg, &scard->ThreadPoolEnv);
if (!work)
goto fail;
ArrayList_Append(scard->workObjects, work);
SubmitThreadpoolWork(work);
return TRUE;
fail:
if (arg)
queue_free(arg->e);
free(arg);
return FALSE;
}
BOOL pf_channel_smartcard_client_handle(pClientContext* pc, wStream* s, wStream* out,
UINT (*send_fkt)(pClientContext*, wStream*))
{
BOOL rc = FALSE;
LONG status;
UINT32 FileId;
UINT32 CompletionId;
UINT32 ioStatus;
pf_channel_client_queue_element e = { 0 };
pf_channel_client_context* scard = scard_get_client_context(pc);
WINPR_ASSERT(send_fkt);
WINPR_ASSERT(s);
if (!scard)
return FALSE;
e.pc = pc;
e.out = out;
e.send_fkt = send_fkt;
/* Skip IRP header */
if (Stream_GetRemainingLength(s) < 20)
{
WLog_WARN(TAG, "[%s] Invalid IRP received, expected 20 bytes, have %" PRIuz,
SCARD_SVC_CHANNEL_NAME, Stream_GetRemainingLength(s));
return FALSE;
}
else
{
UINT32 DeviceId;
UINT32 MajorFunction;
UINT32 MinorFunction;
Stream_Read_UINT32(s, DeviceId); /* DeviceId (4 bytes) */
Stream_Read_UINT32(s, FileId); /* FileId (4 bytes) */
Stream_Read_UINT32(s, CompletionId); /* CompletionId (4 bytes) */
Stream_Read_UINT32(s, MajorFunction); /* MajorFunction (4 bytes) */
Stream_Read_UINT32(s, MinorFunction); /* MinorFunction (4 bytes) */
if (MajorFunction != IRP_MJ_DEVICE_CONTROL)
{
WLog_WARN(TAG, "[%s] Invalid IRP received, expected %s, got %2", SCARD_SVC_CHANNEL_NAME,
rdpdr_irp_string(IRP_MJ_DEVICE_CONTROL), rdpdr_irp_string(MajorFunction));
return FALSE;
}
e.op.completionID = CompletionId;
e.op.deviceID = DeviceId;
if (!rdpdr_write_iocompletion_header(out, DeviceId, CompletionId, 0))
return FALSE;
}
status = smartcard_irp_device_control_decode(s, CompletionId, FileId, &e.op);
if (status != 0)
goto fail;
switch (e.op.ioControlCode)
{
case SCARD_IOCTL_LISTREADERGROUPSA:
case SCARD_IOCTL_LISTREADERGROUPSW:
case SCARD_IOCTL_LISTREADERSA:
case SCARD_IOCTL_LISTREADERSW:
case SCARD_IOCTL_LOCATECARDSA:
case SCARD_IOCTL_LOCATECARDSW:
case SCARD_IOCTL_LOCATECARDSBYATRA:
case SCARD_IOCTL_LOCATECARDSBYATRW:
case SCARD_IOCTL_GETSTATUSCHANGEA:
case SCARD_IOCTL_GETSTATUSCHANGEW:
case SCARD_IOCTL_CONNECTA:
case SCARD_IOCTL_CONNECTW:
case SCARD_IOCTL_RECONNECT:
case SCARD_IOCTL_DISCONNECT:
case SCARD_IOCTL_BEGINTRANSACTION:
case SCARD_IOCTL_ENDTRANSACTION:
case SCARD_IOCTL_STATE:
case SCARD_IOCTL_STATUSA:
case SCARD_IOCTL_STATUSW:
case SCARD_IOCTL_TRANSMIT:
case SCARD_IOCTL_CONTROL:
case SCARD_IOCTL_GETATTRIB:
case SCARD_IOCTL_SETATTRIB:
if (!start_irp_thread(scard, &e))
goto fail;
return TRUE;
default:
status = smartcard_irp_device_control_call(scard->callctx, out, &ioStatus, &e.op);
if (status != 0)
goto fail;
if (!pf_channel_client_write_iostatus(out, &e.op, ioStatus))
goto fail;
break;
}
rc = send_fkt(pc, out) == CHANNEL_RC_OK;
fail:
smartcard_operation_free(&e.op, FALSE);
return rc;
}
BOOL pf_channel_smartcard_server_handle(pServerContext* ps, wStream* s)
{
WLog_ERR(TAG, "TODO: %s unimplemented", __FUNCTION__);
return TRUE;
}
static void channel_stop_and_wait(pf_channel_client_context* scard, BOOL reset)
{
WINPR_ASSERT(scard);
smartcard_call_context_signal_stop(scard->callctx, FALSE);
while (ArrayList_Count(scard->workObjects) > 0)
{
PTP_WORK work = ArrayList_GetItem(scard->workObjects, 0);
if (!work)
continue;
WaitForThreadpoolWorkCallbacks(work, TRUE);
}
smartcard_call_context_signal_stop(scard->callctx, reset);
}
static void pf_channel_scard_client_context_free(InterceptContextMapEntry* base)
{
pf_channel_client_context* entry = (pf_channel_client_context*)base;
if (!entry)
return;
/* Set the stop event.
* All threads waiting in blocking operations will abort at the next
* available polling slot */
channel_stop_and_wait(entry, FALSE);
ArrayList_Free(entry->workObjects);
CloseThreadpool(entry->ThreadPool);
DestroyThreadpoolEnvironment(&entry->ThreadPoolEnv);
smartcard_call_context_free(entry->callctx);
free(entry);
}
static void queue_free(void* obj)
{
pf_channel_client_queue_element* element = obj;
if (!element)
return;
smartcard_operation_free(&element->op, FALSE);
Stream_Free(element->out, TRUE);
free(element);
}
static void* queue_copy(const void* obj)
{
const pf_channel_client_queue_element* other = obj;
pf_channel_client_queue_element* copy;
if (!other)
return NULL;
copy = calloc(1, sizeof(pf_channel_client_queue_element));
if (!copy)
return NULL;
*copy = *other;
copy->out = Stream_New(NULL, Stream_Capacity(other->out));
if (!copy->out)
goto fail;
Stream_Write(copy->out, Stream_Buffer(other->out), Stream_GetPosition(other->out));
return copy;
fail:
queue_free(copy);
return NULL;
}
static void work_object_free(void* arg)
{
PTP_WORK work = arg;
CloseThreadpoolWork(work);
}
BOOL pf_channel_smartcard_client_new(pClientContext* pc)
{
pf_channel_client_context* scard;
wObject* obj;
WINPR_ASSERT(pc);
WINPR_ASSERT(pc->interceptContextMap);
scard = calloc(1, sizeof(pf_channel_client_context));
if (!scard)
return FALSE;
scard->base.free = pf_channel_scard_client_context_free;
scard->callctx = smartcard_call_context_new(pc->context.settings);
if (!scard->callctx)
goto fail;
scard->workObjects = ArrayList_New(TRUE);
if (!scard->workObjects)
goto fail;
obj = ArrayList_Object(scard->workObjects);
WINPR_ASSERT(obj);
obj->fnObjectFree = work_object_free;
scard->ThreadPool = CreateThreadpool(NULL);
if (!scard->ThreadPool)
goto fail;
InitializeThreadpoolEnvironment(&scard->ThreadPoolEnv);
SetThreadpoolCallbackPool(&scard->ThreadPoolEnv, scard->ThreadPool);
return HashTable_Insert(pc->interceptContextMap, SCARD_SVC_CHANNEL_NAME, scard);
fail:
pf_channel_scard_client_context_free(&scard->base);
return FALSE;
}
void pf_channel_smartcard_client_free(pClientContext* pc)
{
WINPR_ASSERT(pc);
WINPR_ASSERT(pc->interceptContextMap);
HashTable_Remove(pc->interceptContextMap, SCARD_SVC_CHANNEL_NAME);
}
BOOL pf_channel_smartcard_client_emulate(pClientContext* pc)
{
pf_channel_client_context* scard = scard_get_client_context(pc);
if (!scard)
return FALSE;
return smartcard_call_is_configured(scard->callctx);
}
BOOL pf_channel_smartcard_client_reset(pClientContext* pc)
{
pf_channel_client_context* scard = scard_get_client_context(pc);
if (!scard)
return TRUE;
channel_stop_and_wait(scard, TRUE);
return TRUE;
}

View File

@ -0,0 +1,36 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* FreeRDP Proxy Server
*
* Copyright 2021 Armin Novak <armin.novak@thincast.com>
* Copyright 2021 Thincast Technologies GmbH
*
* 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.
*/
#ifndef FREERDP_SERVER_PROXY_SCARD_H
#define FREERDP_SERVER_PROXY_SCARD_H
#include <freerdp/server/proxy/proxy_context.h>
BOOL pf_channel_smartcard_client_new(pClientContext* pc);
void pf_channel_smartcard_client_free(pClientContext* pc);
BOOL pf_channel_smartcard_client_reset(pClientContext* pc);
BOOL pf_channel_smartcard_client_emulate(pClientContext* pc);
BOOL pf_channel_smartcard_client_handle(pClientContext* pc, wStream* s, wStream* out,
UINT (*send_fkt)(pClientContext*, wStream*));
BOOL pf_channel_smartcard_server_handle(pServerContext* ps, wStream* s);
#endif /* FREERDP_SERVER_PROXY_SCARD_H */

View File

@ -44,6 +44,8 @@
#include <freerdp/server/proxy/proxy_config.h>
#include "proxy_modules.h"
#include "pf_utils.h"
#include "channels/pf_channel_rdpdr.h"
#include "channels/pf_channel_smartcard.h"
#define TAG PROXY_TAG("client")
@ -130,13 +132,7 @@ static BOOL pf_client_load_rdpsnd(pClientContext* pc)
*/
if (!freerdp_static_channel_collection_find(context->settings, RDPSND_CHANNEL_NAME))
{
const char* params[2] = { RDPSND_CHANNEL_NAME };
if (config->AudioOutput &&
WTSVirtualChannelManagerIsChannelJoined(ps->vcm, RDPSND_CHANNEL_NAME))
params[1] = "sys:proxy";
else
params[1] = "sys:fake";
const char* params[2] = { RDPSND_CHANNEL_NAME, "sys:fake" };
if (!freerdp_client_add_static_channel(context->settings, ARRAYSIZE(params), params))
return FALSE;
@ -175,6 +171,64 @@ static BOOL pf_client_use_peer_load_balance_info(pClientContext* pc)
return TRUE;
}
static BOOL str_is_empty(const char* str)
{
if (!str)
return TRUE;
if (strlen(str) == 0)
return TRUE;
return FALSE;
}
static BOOL pf_client_use_proxy_smartcard_auth(const rdpSettings* settings)
{
BOOL enable = freerdp_settings_get_bool(settings, FreeRDP_SmartcardLogon);
const char* key = freerdp_settings_get_string(settings, FreeRDP_SmartcardPrivateKey);
const char* cert = freerdp_settings_get_string(settings, FreeRDP_SmartcardCertificate);
if (!enable)
return FALSE;
if (str_is_empty(key))
return FALSE;
if (str_is_empty(cert))
return FALSE;
return TRUE;
}
static BOOL freerdp_client_load_static_channel_addin(rdpChannels* channels, rdpSettings* settings,
const char* name, void* data)
{
PVIRTUALCHANNELENTRY entry = NULL;
PVIRTUALCHANNELENTRYEX entryEx = NULL;
entryEx = (PVIRTUALCHANNELENTRYEX)(void*)freerdp_load_channel_addin_entry(
name, NULL, NULL, FREERDP_ADDIN_CHANNEL_STATIC | FREERDP_ADDIN_CHANNEL_ENTRYEX);
if (!entryEx)
entry = freerdp_load_channel_addin_entry(name, NULL, NULL, FREERDP_ADDIN_CHANNEL_STATIC);
if (entryEx)
{
if (freerdp_channels_client_load_ex(channels, settings, entryEx, data) == 0)
{
WLog_INFO(TAG, "loading channelEx %s", name);
return TRUE;
}
}
else if (entry)
{
if (freerdp_channels_client_load(channels, settings, entry, data) == 0)
{
WLog_INFO(TAG, "loading channel %s", name);
return TRUE;
}
}
return FALSE;
}
static BOOL pf_client_pre_connect(freerdp* instance)
{
pClientContext* pc;
@ -217,7 +271,7 @@ static BOOL pf_client_pre_connect(freerdp* instance)
config->DeviceRedirection) ||
!freerdp_settings_set_bool(settings, FreeRDP_SupportDisplayControl,
config->DisplayControl) ||
!freerdp_settings_set_bool(settings, FreeRDP_RemoteAssistanceMode, config->RemoteApp) ||
!freerdp_settings_set_bool(settings, FreeRDP_RemoteApplicationMode, config->RemoteApp) ||
!freerdp_settings_set_bool(settings, FreeRDP_MultiTouchInput, config->Multitouch))
return FALSE;
@ -275,6 +329,13 @@ static BOOL pf_client_pre_connect(freerdp* instance)
}
else
{
if (!pf_channel_rdpdr_client_new(pc))
return FALSE;
#if defined(WITH_PROXY_EMULATE_SMARTCARD)
if (!pf_channel_smartcard_client_new(pc))
return FALSE;
#endif
/* Copy the current channel settings from the peer connection to the client. */
if (!freerdp_channels_from_mcs(settings, &ps->context))
return FALSE;
@ -315,6 +376,125 @@ static BOOL pf_client_pre_connect(freerdp* instance)
return pf_modules_run_hook(pc->pdata->module, HOOK_TYPE_CLIENT_PRE_CONNECT, pc->pdata, pc);
}
static BOOL pf_client_receive_channel_passthrough(proxyData* pdata, UINT16 channelId,
const char* channel_name, const BYTE* xdata,
size_t xsize, UINT32 flags, size_t totalSize)
{
proxyChannelDataEventInfo ev;
UINT16 server_channel_id;
pServerContext* ps;
WINPR_ASSERT(pdata);
ps = pdata->ps;
WINPR_ASSERT(ps);
ev.channel_id = channelId;
ev.channel_name = channel_name;
ev.data = xdata;
ev.data_len = xsize;
ev.flags = flags;
ev.total_size = totalSize;
if (!pf_modules_run_filter(pdata->module, FILTER_TYPE_CLIENT_PASSTHROUGH_CHANNEL_DATA, pdata,
&ev))
return TRUE; /* Silently drop */
/* Dynamic channels need special treatment
*
* We need to check every message with CHANNEL_FLAG_FIRST set if it
* is a CREATE_REQUEST_PDU (0x01) and extract channelId and name
* from it.
*
* To avoid issues with (misbehaving) clients assume all packets
* that do not have at least a length of 1 byte and all incomplete
* CREATE_REQUEST_PDU (0x01) packets as invalid.
*/
if ((flags & CHANNEL_FLAG_FIRST) &&
(strncmp(channel_name, DRDYNVC_SVC_CHANNEL_NAME, CHANNEL_NAME_LEN + 1) == 0))
{
BYTE cmd, first;
wStream *s, sbuffer;
s = Stream_StaticConstInit(&sbuffer, xdata, xsize);
if (Stream_Length(s) < 1)
return FALSE;
Stream_Read_UINT8(s, first);
cmd = first >> 4;
if (cmd == CREATE_REQUEST_PDU)
{
proxyChannelDataEventInfo dev;
size_t len, nameLen;
const char* name;
UINT32 dynChannelId;
BYTE cbId = first & 0x03;
switch (cbId)
{
case 0x00:
if (Stream_GetRemainingLength(s) < 1)
return FALSE;
Stream_Read_UINT8(s, dynChannelId);
break;
case 0x01:
if (Stream_GetRemainingLength(s) < 2)
return FALSE;
Stream_Read_UINT16(s, dynChannelId);
break;
case 0x02:
if (Stream_GetRemainingLength(s) < 4)
return FALSE;
Stream_Read_UINT32(s, dynChannelId);
break;
default:
return FALSE;
}
name = (const char*)Stream_Pointer(s);
nameLen = Stream_GetRemainingLength(s);
len = strnlen(name, nameLen);
if ((len == 0) || (len == nameLen))
return FALSE;
dev.channel_id = dynChannelId;
dev.channel_name = name;
dev.data = xdata;
dev.data_len = xsize;
dev.flags = flags;
dev.total_size = totalSize;
if (!pf_modules_run_filter(
pdata->module, FILTER_TYPE_CLIENT_PASSTHROUGH_DYN_CHANNEL_CREATE, pdata, &dev))
return TRUE; /* Silently drop */
}
}
server_channel_id = WTSChannelGetId(ps->context.peer, channel_name);
/* Ignore messages for channels that can not be mapped.
* The client might not have enabled support for this specific channel,
* so just drop the message. */
if (server_channel_id == 0)
return TRUE;
return ps->context.peer->SendChannelPacket(ps->context.peer, server_channel_id, totalSize,
flags, xdata, xsize);
}
static BOOL pf_client_receive_channel_intercept(proxyData* pdata, UINT16 channelId,
const char* channel_name, const BYTE* xdata,
size_t xsize, UINT32 flags, size_t totalSize)
{
WINPR_ASSERT(pdata);
WINPR_ASSERT(channel_name);
if (strcmp(channel_name, RDPDR_SVC_CHANNEL_NAME) == 0)
return pf_channel_rdpdr_client_handle(pdata->pc, channelId, channel_name, xdata, xsize,
flags, totalSize);
WLog_ERR(TAG, "[%s]: Channel %s [0x%04" PRIx16 "] intercept mode not implemented, aborting",
__FUNCTION__, channel_name, channelId);
return FALSE;
}
static BOOL pf_client_receive_channel_data_hook(freerdp* instance, UINT16 channelId,
const BYTE* xdata, size_t xsize, UINT32 flags,
size_t totalSize)
@ -324,7 +504,7 @@ static BOOL pf_client_receive_channel_data_hook(freerdp* instance, UINT16 channe
pServerContext* ps;
proxyData* pdata;
const proxyConfig* config;
int pass;
pf_utils_channel_mode pass;
WINPR_ASSERT(instance);
WINPR_ASSERT(xdata || (xsize == 0));
@ -342,107 +522,19 @@ static BOOL pf_client_receive_channel_data_hook(freerdp* instance, UINT16 channe
config = pdata->config;
WINPR_ASSERT(config);
pass = pf_utils_channel_is_passthrough(config, channel_name);
pass = pf_utils_get_channel_mode(config, channel_name);
switch (pass)
{
case 0:
case PF_UTILS_CHANNEL_BLOCK:
return TRUE; /* Silently drop */
case 1:
{
proxyChannelDataEventInfo ev;
UINT16 server_channel_id;
ev.channel_id = channelId;
ev.channel_name = channel_name;
ev.data = xdata;
ev.data_len = xsize;
ev.flags = flags;
ev.total_size = totalSize;
if (!pf_modules_run_filter(pdata->module, FILTER_TYPE_CLIENT_PASSTHROUGH_CHANNEL_DATA,
pdata, &ev))
return TRUE; /* Silently drop */
/* Dynamic channels need special treatment
*
* We need to check every message with CHANNEL_FLAG_FIRST set if it
* is a CREATE_REQUEST_PDU (0x01) and extract channelId and name
* from it.
*
* To avoid issues with (misbehaving) clients assume all packets
* that do not have at least a length of 1 byte and all incomplete
* CREATE_REQUEST_PDU (0x01) packets as invalid.
*/
if ((flags & CHANNEL_FLAG_FIRST) &&
(strncmp(channel_name, DRDYNVC_SVC_CHANNEL_NAME, CHANNEL_NAME_LEN + 1) == 0))
{
BYTE cmd, first;
wStream *s, sbuffer;
s = Stream_StaticConstInit(&sbuffer, xdata, xsize);
if (Stream_Length(s) < 1)
return FALSE;
Stream_Read_UINT8(s, first);
cmd = first >> 4;
if (cmd == CREATE_REQUEST_PDU)
{
proxyChannelDataEventInfo dev;
size_t len, nameLen;
const char* name;
UINT32 dynChannelId;
BYTE cbId = first & 0x03;
switch (cbId)
{
case 0x00:
if (Stream_GetRemainingLength(s) < 1)
return FALSE;
Stream_Read_UINT8(s, dynChannelId);
break;
case 0x01:
if (Stream_GetRemainingLength(s) < 2)
return FALSE;
Stream_Read_UINT16(s, dynChannelId);
break;
case 0x02:
if (Stream_GetRemainingLength(s) < 4)
return FALSE;
Stream_Read_UINT32(s, dynChannelId);
break;
default:
return FALSE;
}
name = (const char*)Stream_Pointer(s);
nameLen = Stream_GetRemainingLength(s);
len = strnlen(name, nameLen);
if ((len == 0) || (len == nameLen))
return FALSE;
dev.channel_id = dynChannelId;
dev.channel_name = name;
dev.data = xdata;
dev.data_len = xsize;
dev.flags = flags;
dev.total_size = totalSize;
if (!pf_modules_run_filter(pdata->module,
FILTER_TYPE_CLIENT_PASSTHROUGH_DYN_CHANNEL_CREATE,
pdata, &dev))
return TRUE; /* Silently drop */
}
}
server_channel_id = WTSChannelGetId(ps->context.peer, channel_name);
/* Ignore messages for channels that can not be mapped.
* The client might not have enabled support for this specific channel,
* so just drop the message. */
if (server_channel_id == 0)
return TRUE;
return ps->context.peer->SendChannelPacket(ps->context.peer, server_channel_id,
totalSize, flags, xdata, xsize);
}
case PF_UTILS_CHANNEL_PASSTHROUGH:
return pf_client_receive_channel_passthrough(pdata, channelId, channel_name, xdata,
xsize, flags, totalSize);
case PF_UTILS_CHANNEL_INTERCEPT:
return pf_client_receive_channel_intercept(pdata, channelId, channel_name, xdata, xsize,
flags, totalSize);
case PF_UTILS_CHANNEL_NOT_HANDLED:
default:
WINPR_ASSERT(pc->client_receive_channel_data_original);
return pc->client_receive_channel_data_original(instance, channelId, xdata, xsize,
@ -494,10 +586,9 @@ static BOOL sendQueuedChannelData(pClientContext* pc)
rc = TRUE;
else
{
WINPR_ASSERT(pc->context.instance->SendChannelPacket);
rc = pc->context.instance->SendChannelPacket(pc->context.instance, channelId,
ev->total_size, ev->flags, ev->data,
ev->data_len);
WINPR_ASSERT(pc->context.instance->SendChannelData);
rc = pc->context.instance->SendChannelData(pc->context.instance, channelId,
ev->data, ev->data_len);
}
channel_data_free(ev);
}
@ -589,8 +680,14 @@ static void pf_client_post_disconnect(freerdp* instance)
pdata = pc->pdata;
WINPR_ASSERT(pdata);
#if defined(WITH_PROXY_EMULATE_SMARTCARD)
pf_channel_smartcard_client_free(pc);
#endif
pf_channel_rdpdr_client_free(pc);
pc->connected = FALSE;
pf_modules_run_hook(pc->pdata->module, HOOK_TYPE_CLIENT_POST_CONNECT, pc->pdata, pc);
pf_modules_run_hook(pc->pdata->module, HOOK_TYPE_CLIENT_POST_DISCONNECT, pc->pdata, pc);
PubSub_UnsubscribeErrorInfo(instance->context->pubSub, pf_client_on_error_info);
gdi_free(instance);
@ -600,6 +697,30 @@ static void pf_client_post_disconnect(freerdp* instance)
proxy_data_abort_connect(pdata);
}
static BOOL pf_client_redirect(freerdp* instance)
{
pClientContext* pc;
proxyData* pdata;
if (!instance)
return FALSE;
if (!instance->context)
return FALSE;
pc = (pClientContext*)instance->context;
WINPR_ASSERT(pc);
pdata = pc->pdata;
WINPR_ASSERT(pdata);
#if defined(WITH_PROXY_EMULATE_SMARTCARD)
pf_channel_smartcard_client_reset(pc);
#endif
pf_channel_rdpdr_client_reset(pc);
return pf_modules_run_hook(pc->pdata->module, HOOK_TYPE_CLIENT_REDIRECT, pc->pdata, pc);
}
/*
* pf_client_should_retry_without_nla:
*
@ -640,6 +761,14 @@ static void pf_client_set_security_settings(pClientContext* pc)
settings->TlsSecurity = config->ClientTlsSecurity;
settings->NlaSecurity = config->ClientNlaSecurity;
/* Smartcard authentication currently does not work with NLA */
if (pf_client_use_proxy_smartcard_auth(settings))
{
freerdp_settings_set_bool(settings, FreeRDP_NlaSecurity, FALSE);
freerdp_settings_set_bool(settings, FreeRDP_TlsSecurity, TRUE);
freerdp_settings_set_bool(settings, FreeRDP_RedirectSmartCards, TRUE);
}
if (!config->ClientNlaSecurity)
return;
@ -827,6 +956,8 @@ static void pf_client_context_free(freerdp* instance, rdpContext* context)
Queue_Free(pc->cached_server_channel_data);
Stream_Free(pc->remote_pem, TRUE);
free(pc->remote_hostname);
free(pc->computerName.v);
HashTable_Free(pc->interceptContextMap);
}
static int pf_client_verify_X509_certificate(freerdp* instance, const BYTE* data, size_t length,
@ -914,6 +1045,7 @@ static BOOL pf_client_client_new(freerdp* instance, rdpContext* context)
instance->PreConnect = pf_client_pre_connect;
instance->PostConnect = pf_client_post_connect;
instance->PostDisconnect = pf_client_post_disconnect;
instance->Redirect = pf_client_redirect;
instance->LogonErrorInfo = pf_logon_error_info;
instance->VerifyX509Certificate = pf_client_verify_X509_certificate;
@ -929,6 +1061,18 @@ static BOOL pf_client_client_new(freerdp* instance, rdpContext* context)
WINPR_ASSERT(obj);
obj->fnObjectNew = channel_data_copy;
obj->fnObjectFree = channel_data_free;
pc->interceptContextMap = HashTable_New(FALSE);
if (!pc->interceptContextMap)
return FALSE;
if (!HashTable_SetupForStringData(pc->interceptContextMap, FALSE))
return FALSE;
obj = HashTable_ValueObject(pc->interceptContextMap);
WINPR_ASSERT(obj);
obj->fnObjectFree = intercept_context_entry_free;
return TRUE;
}

View File

@ -222,9 +222,10 @@ static BOOL pf_config_load_channels(wIniFile* ini, proxyConfig* config)
config->RemoteApp = pf_config_get_bool(ini, "Channels", "RemoteApp", FALSE);
config->PassthroughIsBlacklist =
pf_config_get_bool(ini, "Channels", "PassthroughIsBlacklist", FALSE);
config->Passthrough = pf_config_parse_comma_separated_list(
pf_config_get_str(ini, "Channels", "Passthrough", FALSE), &config->PassthroughCount);
config->Intercept = pf_config_parse_comma_separated_list(
pf_config_get_str(ini, "Channels", "Intercept", FALSE), &config->InterceptCount);
return TRUE;
}
@ -485,6 +486,8 @@ BOOL pf_server_config_dump(const char* file)
goto fail;
if (IniFile_SetKeyValueString(ini, "Channels", "Passthrough", "") < 0)
goto fail;
if (IniFile_SetKeyValueString(ini, "Channels", "Intercept", "") < 0)
goto fail;
/* Input configuration */
if (IniFile_SetKeyValueString(ini, "Input", "Keyboard", "true") < 0)
@ -668,6 +671,12 @@ void pf_server_config_print(const proxyConfig* config)
pf_server_config_print_list(config->Passthrough, config->PassthroughCount);
}
if (config->InterceptCount)
{
WLog_INFO(TAG, "\tStatic Channels Proxy-Intercept:");
pf_server_config_print_list(config->Intercept, config->InterceptCount);
}
CONFIG_PRINT_SECTION("Clipboard");
CONFIG_PRINT_BOOL(config, TextOnly);
if (config->MaxTextLength > 0)
@ -701,6 +710,7 @@ void pf_server_config_free(proxyConfig* config)
return;
free(config->Passthrough);
free(config->Intercept);
free(config->RequiredPlugins);
free(config->Modules);
free(config->TargetHost);
@ -788,6 +798,9 @@ BOOL pf_config_clone(proxyConfig** dst, const proxyConfig* config)
if (!pf_config_copy_string_list(&tmp->Passthrough, &tmp->PassthroughCount, config->Passthrough,
config->PassthroughCount))
goto fail;
if (!pf_config_copy_string_list(&tmp->Intercept, &tmp->InterceptCount, config->Intercept,
config->InterceptCount))
goto fail;
if (!pf_config_copy_string_list(&tmp->Modules, &tmp->ModulesCount, config->Modules,
config->ModulesCount))
goto fail;
@ -883,7 +896,6 @@ static BOOL config_plugin_mouse_event(proxyPlugin* plugin, proxyData* pdata, voi
WINPR_ASSERT(cfg);
rc = cfg->Mouse;
WLog_DBG(TAG, "%s: %s", __FUNCTION__, rc ? "TRUE" : "FALSE");
return rc;
}
@ -915,7 +927,7 @@ static BOOL config_plugin_server_channel_data(proxyPlugin* plugin, proxyData* pd
static BOOL config_plugin_dynamic_channel_create(proxyPlugin* plugin, proxyData* pdata, void* param)
{
int rc;
pf_utils_channel_mode rc;
BOOL accept;
const struct config_plugin_data* custom;
const proxyConfig* cfg;
@ -931,8 +943,21 @@ static BOOL config_plugin_dynamic_channel_create(proxyPlugin* plugin, proxyData*
cfg = custom->config;
WINPR_ASSERT(cfg);
rc = pf_utils_channel_is_passthrough(cfg, channel->channel_name);
accept = rc > 0;
rc = pf_utils_get_channel_mode(cfg, channel->channel_name);
switch (rc)
{
case PF_UTILS_CHANNEL_INTERCEPT:
case PF_UTILS_CHANNEL_PASSTHROUGH:
accept = TRUE;
break;
case PF_UTILS_CHANNEL_BLOCK:
case PF_UTILS_CHANNEL_NOT_HANDLED:
default:
accept = FALSE;
break;
}
if (accept)
{
if (strncmp(RDPGFX_DVC_CHANNEL_NAME, channel->channel_name,
@ -971,7 +996,7 @@ static BOOL config_plugin_dynamic_channel_create(proxyPlugin* plugin, proxyData*
static BOOL config_plugin_channel_create(proxyPlugin* plugin, proxyData* pdata, void* param)
{
int rc;
pf_utils_channel_mode rc;
BOOL accept;
const struct config_plugin_data* custom;
const proxyConfig* cfg;
@ -987,8 +1012,19 @@ static BOOL config_plugin_channel_create(proxyPlugin* plugin, proxyData* pdata,
cfg = custom->config;
WINPR_ASSERT(cfg);
rc = pf_utils_channel_is_passthrough(cfg, channel->channel_name);
accept = rc > 0;
rc = pf_utils_get_channel_mode(cfg, channel->channel_name);
switch (rc)
{
case PF_UTILS_CHANNEL_INTERCEPT:
case PF_UTILS_CHANNEL_PASSTHROUGH:
accept = TRUE;
break;
case PF_UTILS_CHANNEL_BLOCK:
case PF_UTILS_CHANNEL_NOT_HANDLED:
default:
accept = FALSE;
break;
}
if (accept)
{
if (strncmp(CLIPRDR_SVC_CHANNEL_NAME, channel->channel_name,

View File

@ -29,10 +29,13 @@
#include "pf_client.h"
#include <freerdp/server/proxy/proxy_context.h>
#include "channels/pf_channel_rdpdr.h"
/* Proxy context initialization callback */
static void client_to_proxy_context_free(freerdp_peer* client, rdpContext* ctx);
static BOOL client_to_proxy_context_new(freerdp_peer* client, rdpContext* ctx)
{
wObject* obj;
pServerContext* context = (pServerContext*)ctx;
WINPR_ASSERT(client);
@ -48,6 +51,15 @@ static BOOL client_to_proxy_context_new(freerdp_peer* client, rdpContext* ctx)
if (!(context->dynvcReady = CreateEvent(NULL, TRUE, FALSE, NULL)))
goto error;
context->interceptContextMap = HashTable_New(FALSE);
if (!context->interceptContextMap)
goto error;
if (!HashTable_SetupForStringData(context->interceptContextMap, FALSE))
goto error;
obj = HashTable_ValueObject(context->interceptContextMap);
WINPR_ASSERT(obj);
obj->fnObjectFree = intercept_context_entry_free;
return TRUE;
error:
@ -66,15 +78,17 @@ void client_to_proxy_context_free(freerdp_peer* client, rdpContext* ctx)
if (!context)
return;
if (context->vcm && (context->vcm != INVALID_HANDLE_VALUE))
WTSCloseServer((HANDLE)context->vcm);
context->vcm = NULL;
if (context->dynvcReady)
{
CloseHandle(context->dynvcReady);
context->dynvcReady = NULL;
}
HashTable_Free(context->interceptContextMap);
if (context->vcm && (context->vcm != INVALID_HANDLE_VALUE))
WTSCloseServer((HANDLE)context->vcm);
context->vcm = NULL;
}
BOOL pf_context_init_server_context(freerdp_peer* client)

View File

@ -87,6 +87,8 @@ static const char* pf_modules_get_hook_type_string(PF_HOOK_TYPE result)
return "HOOK_TYPE_CLIENT_POST_CONNECT";
case HOOK_TYPE_CLIENT_POST_DISCONNECT:
return "HOOK_TYPE_CLIENT_POST_DISCONNECT";
case HOOK_TYPE_CLIENT_REDIRECT:
return "HOOK_TYPE_CLIENT_REDIRECT";
case HOOK_TYPE_CLIENT_VERIFY_X509:
return "HOOK_TYPE_CLIENT_VERIFY_X509";
case HOOK_TYPE_CLIENT_LOGIN_FAILURE:
@ -142,6 +144,10 @@ static BOOL pf_modules_proxy_ArrayList_ForEachFkt(void* data, size_t index, va_l
ok = IFCALLRESULT(TRUE, plugin->ClientPostConnect, plugin, pdata, custom);
break;
case HOOK_TYPE_CLIENT_REDIRECT:
ok = IFCALLRESULT(TRUE, plugin->ClientRedirect, plugin, pdata, custom);
break;
case HOOK_TYPE_CLIENT_POST_DISCONNECT:
ok = IFCALLRESULT(TRUE, plugin->ClientPostDisconnect, plugin, pdata, custom);
break;

View File

@ -39,6 +39,8 @@
#include <freerdp/channels/channels.h>
#include <freerdp/build-config.h>
#include <freerdp/channels/rdpdr.h>
#include <freerdp/server/proxy/proxy_server.h>
#include <freerdp/server/proxy/proxy_log.h>
@ -49,9 +51,16 @@
#include "pf_update.h"
#include "proxy_modules.h"
#include "pf_utils.h"
#include "channels/pf_channel_rdpdr.h"
#define TAG PROXY_TAG("server")
typedef struct
{
HANDLE thread;
freerdp_peer* client;
} peer_thread_args;
static BOOL pf_server_parse_target_from_routing_token(rdpContext* context, char** target,
DWORD* port)
{
@ -229,6 +238,18 @@ static BOOL pf_server_post_connect(freerdp_peer* peer)
if (!pf_modules_run_hook(pdata->module, HOOK_TYPE_SERVER_POST_CONNECT, pdata, peer))
return FALSE;
switch (pf_utils_get_channel_mode(ps->pdata->config, RDPDR_SVC_CHANNEL_NAME))
{
case PF_UTILS_CHANNEL_INTERCEPT:
if (!pf_channel_rdpdr_server_new(ps))
return FALSE;
if (!pf_channel_rdpdr_server_announce(ps))
return FALSE;
break;
default:
break;
}
/* Start a proxy's client in it's own thread */
if (!(pdata->client_thread = CreateThread(NULL, 0, pf_client_start, pc, 0, NULL)))
{
@ -290,6 +311,22 @@ static BOOL pf_server_adjust_monitor_layout(freerdp_peer* peer)
return TRUE;
}
static BOOL pf_server_receive_channel_intercept(proxyData* pdata, UINT16 channelId,
const char* channel_name, const BYTE* data,
size_t size, UINT32 flags, size_t totalSize)
{
WINPR_ASSERT(pdata);
WINPR_ASSERT(channel_name);
if (strcmp(channel_name, RDPDR_SVC_CHANNEL_NAME) == 0)
return pf_channel_rdpdr_server_handle(pdata->ps, channelId, channel_name, data, size, flags,
totalSize);
WLog_ERR(TAG, "[%s]: Channel %s [0x%04" PRIx16 "] intercept mode not implemented, aborting",
__FUNCTION__, channel_name, channelId);
return FALSE;
}
static BOOL pf_server_receive_channel_data_hook(freerdp_peer* peer, UINT16 channelId,
const BYTE* data, size_t size, UINT32 flags,
size_t totalSize)
@ -298,7 +335,7 @@ static BOOL pf_server_receive_channel_data_hook(freerdp_peer* peer, UINT16 chann
pClientContext* pc;
proxyData* pdata;
const proxyConfig* config;
int pass;
pf_utils_channel_mode pass;
const char* channel_name = WTSChannelGetName(peer, channelId);
WINPR_ASSERT(peer);
@ -320,12 +357,12 @@ static BOOL pf_server_receive_channel_data_hook(freerdp_peer* peer, UINT16 chann
if (!pc)
goto original_cb;
pass = pf_utils_channel_is_passthrough(config, channel_name);
pass = pf_utils_get_channel_mode(config, channel_name);
switch (pass)
{
case 0:
case PF_UTILS_CHANNEL_BLOCK:
return TRUE;
case 1:
case PF_UTILS_CHANNEL_PASSTHROUGH:
{
proxyChannelDataEventInfo ev;
@ -342,6 +379,9 @@ static BOOL pf_server_receive_channel_data_hook(freerdp_peer* peer, UINT16 chann
return IFCALLRESULT(TRUE, pc->sendChannelData, pc, &ev);
}
case PF_UTILS_CHANNEL_INTERCEPT:
return pf_server_receive_channel_intercept(pdata, channelId, channel_name, data, size,
flags, totalSize);
default:
break;
}
@ -453,10 +493,14 @@ static DWORD WINAPI pf_server_handle_peer(LPVOID arg)
DWORD status;
pServerContext* ps = NULL;
proxyData* pdata = NULL;
freerdp_peer* client = (freerdp_peer*)arg;
freerdp_peer* client;
proxyServer* server;
size_t count;
peer_thread_args* args = arg;
WINPR_ASSERT(args);
client = args->client;
WINPR_ASSERT(client);
server = (proxyServer*)client->ContextExtra;
@ -599,13 +643,9 @@ out_free_peer:
WaitForSingleObject(pdata->client_thread, INFINITE);
}
/* If the server is shutting down the peer_list is already
* locked and we should not try to remove it here but instead
* let the ArrayList_Clear handle that. */
if (WaitForSingleObject(server->stopEvent, 0) != WAIT_OBJECT_0)
{
ArrayList_Lock(server->peer_list);
ArrayList_Remove(server->peer_list, _GetCurrentThread());
ArrayList_Remove(server->peer_list, args->thread);
count = ArrayList_Count(server->peer_list);
ArrayList_Unlock(server->peer_list);
}
@ -617,6 +657,7 @@ out_free_peer:
#if defined(WITH_DEBUG_EVENTS)
DumpEventHandles();
#endif
free(args);
ExitThread(0);
return 0;
}
@ -625,23 +666,28 @@ static BOOL pf_server_start_peer(freerdp_peer* client)
{
HANDLE hThread;
proxyServer* server;
peer_thread_args* args = calloc(1, sizeof(peer_thread_args));
if (!args)
return FALSE;
WINPR_ASSERT(client);
args->client = client;
server = (proxyServer*)client->ContextExtra;
WINPR_ASSERT(server);
hThread = CreateThread(NULL, 0, pf_server_handle_peer, (void*)client, CREATE_SUSPENDED, NULL);
hThread = CreateThread(NULL, 0, pf_server_handle_peer, args, CREATE_SUSPENDED, NULL);
if (!hThread)
return FALSE;
args->thread = hThread;
if (!ArrayList_Append(server->peer_list, hThread))
{
CloseHandle(hThread);
return FALSE;
}
return ResumeThread(hThread) == 0;
return ResumeThread(hThread) != (DWORD)-1;
}
static BOOL pf_server_peer_accepted(freerdp_listener* listener, freerdp_peer* client)
@ -788,11 +834,6 @@ static BOOL are_all_required_modules_loaded(proxyModule* module, const proxyConf
static void peer_free(void* obj)
{
HANDLE hdl = (HANDLE)obj;
/* Threads have been notified about pending termination at this point.
*/
if (hdl != _GetCurrentThread())
WaitForSingleObject(hdl, INFINITE);
CloseHandle(hdl);
}
@ -830,7 +871,7 @@ proxyServer* pf_server_new(const proxyConfig* config)
if (!server->listener)
goto out;
server->peer_list = ArrayList_New(TRUE);
server->peer_list = ArrayList_New(FALSE);
if (!server->peer_list)
goto out;
@ -925,6 +966,17 @@ void pf_server_free(proxyServer* server)
pf_server_stop(server);
while (ArrayList_Count(server->peer_list) > 0)
{
/* pf_server_stop triggers the threads to shut down.
* loop here until all of them stopped.
*
* This must be done before ArrayList_Free otherwise the thread removal
* in pf_server_handle_peer will deadlock due to both threads trying to
* lock the list.
*/
Sleep(100);
}
ArrayList_Free(server->peer_list);
freerdp_listener_free(server->listener);

View File

@ -22,16 +22,30 @@
#include <winpr/string.h>
#include <winpr/wtsapi.h>
#include <freerdp/server/proxy/proxy_log.h>
#include "pf_utils.h"
int pf_utils_channel_is_passthrough(const proxyConfig* config, const char* name)
#define TAG PROXY_TAG("utils")
pf_utils_channel_mode pf_utils_get_channel_mode(const proxyConfig* config, const char* name)
{
pf_utils_channel_mode rc = PF_UTILS_CHANNEL_NOT_HANDLED;
size_t i;
BOOL found = FALSE;
WINPR_ASSERT(config);
WINPR_ASSERT(name);
for (i = 0; i < config->InterceptCount; i++)
{
const char* channel_name = config->Intercept[i];
if (strcmp(name, channel_name) == 0)
{
rc = PF_UTILS_CHANNEL_INTERCEPT;
goto end;
}
}
for (i = 0; i < config->PassthroughCount; i++)
{
const char* channel_name = config->Passthrough[i];
@ -45,13 +59,16 @@ int pf_utils_channel_is_passthrough(const proxyConfig* config, const char* name)
if (found)
{
if (config->PassthroughIsBlacklist)
return 0;
return 1;
rc = PF_UTILS_CHANNEL_BLOCK;
else
rc = PF_UTILS_CHANNEL_PASSTHROUGH;
}
else if (config->PassthroughIsBlacklist)
rc = PF_UTILS_CHANNEL_PASSTHROUGH;
if (config->PassthroughIsBlacklist)
return 1;
return -1;
end:
WLog_DBG(TAG, "%s -> %s", name, pf_utils_channel_mode_string(rc));
return rc;
}
BOOL pf_utils_is_passthrough(const proxyConfig* config)
@ -61,3 +78,19 @@ BOOL pf_utils_is_passthrough(const proxyConfig* config)
/* TODO: For the time being only passthrough mode is supported. */
return TRUE;
}
const char* pf_utils_channel_mode_string(pf_utils_channel_mode mode)
{
switch (mode)
{
case PF_UTILS_CHANNEL_BLOCK:
return "blocked";
case PF_UTILS_CHANNEL_PASSTHROUGH:
return "passthrough";
case PF_UTILS_CHANNEL_INTERCEPT:
return "intercepted";
case PF_UTILS_CHANNEL_NOT_HANDLED:
default:
return "ignored";
}
}

View File

@ -30,9 +30,20 @@
* @param config The proxy configuration to check against. Must NOT be NULL.
* @param name The name of the channel. Must NOT be NULL.
* @return -1 if the channel is not handled, 0 if the channel should be ignored,
* 1 if the channel should be passed.
* 1 if the channel should be passed, 2 the channel will be intercepted
* e.g. proxy client and server are termination points and data passed
* between.
*/
int pf_utils_channel_is_passthrough(const proxyConfig* config, const char* name);
typedef enum
{
PF_UTILS_CHANNEL_NOT_HANDLED,
PF_UTILS_CHANNEL_BLOCK,
PF_UTILS_CHANNEL_PASSTHROUGH,
PF_UTILS_CHANNEL_INTERCEPT,
} pf_utils_channel_mode;
pf_utils_channel_mode pf_utils_get_channel_mode(const proxyConfig* config, const char* name);
const char* pf_utils_channel_mode_string(pf_utils_channel_mode mode);
BOOL pf_utils_is_passthrough(const proxyConfig* config);

View File

@ -48,6 +48,7 @@ enum _PF_HOOK_TYPE
HOOK_TYPE_CLIENT_PRE_CONNECT,
HOOK_TYPE_CLIENT_POST_CONNECT,
HOOK_TYPE_CLIENT_POST_DISCONNECT,
HOOK_TYPE_CLIENT_REDIRECT,
HOOK_TYPE_CLIENT_VERIFY_X509,
HOOK_TYPE_CLIENT_LOGIN_FAILURE,
HOOK_TYPE_CLIENT_END_PAINT,