Added proxy intercept channels
* New configuration to intercept certain channels * Added rdpdr intercept implementation
This commit is contained in:
parent
3c72cc3306
commit
4e6e5be654
@ -69,6 +69,8 @@ struct proxy_config
|
||||
BOOL PassthroughIsBlacklist;
|
||||
char** Passthrough;
|
||||
size_t PassthroughCount;
|
||||
char** Intercept;
|
||||
size_t InterceptCount;
|
||||
|
||||
/* clipboard specific settings */
|
||||
BOOL TextOnly;
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -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* */
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
||||
if (reset)
|
||||
return ResetEvent(ctx->stopEvent);
|
||||
else
|
||||
return SetEvent(ctx->stopEvent);
|
||||
}
|
||||
|
@ -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()
|
||||
|
15
server/proxy/channels/CMakeLists.txt
Normal file
15
server/proxy/channels/CMakeLists.txt
Normal 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})
|
1713
server/proxy/channels/pf_channel_rdpdr.c
Normal file
1713
server/proxy/channels/pf_channel_rdpdr.c
Normal file
File diff suppressed because it is too large
Load Diff
43
server/proxy/channels/pf_channel_rdpdr.h
Normal file
43
server/proxy/channels/pf_channel_rdpdr.h
Normal 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 */
|
396
server/proxy/channels/pf_channel_smartcard.c
Normal file
396
server/proxy/channels/pf_channel_smartcard.c
Normal 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;
|
||||
}
|
36
server/proxy/channels/pf_channel_smartcard.h
Normal file
36
server/proxy/channels/pf_channel_smartcard.h
Normal 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 */
|
@ -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,43 +376,18 @@ 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_data_hook(freerdp* instance, UINT16 channelId,
|
||||
const BYTE* xdata, size_t xsize, UINT32 flags,
|
||||
size_t totalSize)
|
||||
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)
|
||||
{
|
||||
const char* channel_name = freerdp_channels_get_name_by_id(instance, channelId);
|
||||
pClientContext* pc;
|
||||
pServerContext* ps;
|
||||
proxyData* pdata;
|
||||
const proxyConfig* config;
|
||||
int pass;
|
||||
|
||||
WINPR_ASSERT(instance);
|
||||
WINPR_ASSERT(xdata || (xsize == 0));
|
||||
|
||||
pc = (pClientContext*)instance->context;
|
||||
WINPR_ASSERT(pc);
|
||||
WINPR_ASSERT(pc->pdata);
|
||||
|
||||
ps = pc->pdata->ps;
|
||||
WINPR_ASSERT(ps);
|
||||
|
||||
pdata = ps->pdata;
|
||||
WINPR_ASSERT(pdata);
|
||||
|
||||
config = pdata->config;
|
||||
WINPR_ASSERT(config);
|
||||
|
||||
pass = pf_utils_channel_is_passthrough(config, channel_name);
|
||||
|
||||
switch (pass)
|
||||
{
|
||||
case 0:
|
||||
return TRUE; /* Silently drop */
|
||||
case 1:
|
||||
{
|
||||
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;
|
||||
@ -360,8 +396,8 @@ static BOOL pf_client_receive_channel_data_hook(freerdp* instance, UINT16 channe
|
||||
ev.flags = flags;
|
||||
ev.total_size = totalSize;
|
||||
|
||||
if (!pf_modules_run_filter(pdata->module, FILTER_TYPE_CLIENT_PASSTHROUGH_CHANNEL_DATA,
|
||||
pdata, &ev))
|
||||
if (!pf_modules_run_filter(pdata->module, FILTER_TYPE_CLIENT_PASSTHROUGH_CHANNEL_DATA, pdata,
|
||||
&ev))
|
||||
return TRUE; /* Silently drop */
|
||||
|
||||
/* Dynamic channels need special treatment
|
||||
@ -427,9 +463,8 @@ static BOOL pf_client_receive_channel_data_hook(freerdp* instance, UINT16 channe
|
||||
dev.flags = flags;
|
||||
dev.total_size = totalSize;
|
||||
|
||||
if (!pf_modules_run_filter(pdata->module,
|
||||
FILTER_TYPE_CLIENT_PASSTHROUGH_DYN_CHANNEL_CREATE,
|
||||
pdata, &dev))
|
||||
if (!pf_modules_run_filter(
|
||||
pdata->module, FILTER_TYPE_CLIENT_PASSTHROUGH_DYN_CHANNEL_CREATE, pdata, &dev))
|
||||
return TRUE; /* Silently drop */
|
||||
}
|
||||
}
|
||||
@ -440,9 +475,66 @@ static BOOL pf_client_receive_channel_data_hook(freerdp* instance, UINT16 channe
|
||||
* 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);
|
||||
}
|
||||
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)
|
||||
{
|
||||
const char* channel_name = freerdp_channels_get_name_by_id(instance, channelId);
|
||||
pClientContext* pc;
|
||||
pServerContext* ps;
|
||||
proxyData* pdata;
|
||||
const proxyConfig* config;
|
||||
pf_utils_channel_mode pass;
|
||||
|
||||
WINPR_ASSERT(instance);
|
||||
WINPR_ASSERT(xdata || (xsize == 0));
|
||||
|
||||
pc = (pClientContext*)instance->context;
|
||||
WINPR_ASSERT(pc);
|
||||
WINPR_ASSERT(pc->pdata);
|
||||
|
||||
ps = pc->pdata->ps;
|
||||
WINPR_ASSERT(ps);
|
||||
|
||||
pdata = ps->pdata;
|
||||
WINPR_ASSERT(pdata);
|
||||
|
||||
config = pdata->config;
|
||||
WINPR_ASSERT(config);
|
||||
|
||||
pass = pf_utils_get_channel_mode(config, channel_name);
|
||||
|
||||
switch (pass)
|
||||
{
|
||||
case PF_UTILS_CHANNEL_BLOCK:
|
||||
return TRUE; /* Silently drop */
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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";
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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,
|
||||
|
Loading…
Reference in New Issue
Block a user