mirror of
https://github.com/FreeRDP/FreeRDP.git
synced 2025-06-03 00:00:20 +00:00
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;
|
BOOL PassthroughIsBlacklist;
|
||||||
char** Passthrough;
|
char** Passthrough;
|
||||||
size_t PassthroughCount;
|
size_t PassthroughCount;
|
||||||
|
char** Intercept;
|
||||||
|
size_t InterceptCount;
|
||||||
|
|
||||||
/* clipboard specific settings */
|
/* clipboard specific settings */
|
||||||
BOOL TextOnly;
|
BOOL TextOnly;
|
||||||
|
@ -33,6 +33,23 @@ typedef struct proxy_data proxyData;
|
|||||||
typedef struct proxy_module proxyModule;
|
typedef struct proxy_module proxyModule;
|
||||||
typedef struct channel_data_event_info proxyChannelDataEventInfo;
|
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.
|
* Wraps rdpContext and holds the state for the proxy's server.
|
||||||
*/
|
*/
|
||||||
@ -44,6 +61,8 @@ struct p_server_context
|
|||||||
|
|
||||||
HANDLE vcm;
|
HANDLE vcm;
|
||||||
HANDLE dynvcReady;
|
HANDLE dynvcReady;
|
||||||
|
|
||||||
|
wHashTable* interceptContextMap;
|
||||||
};
|
};
|
||||||
typedef struct p_server_context pServerContext;
|
typedef struct p_server_context pServerContext;
|
||||||
|
|
||||||
@ -83,6 +102,16 @@ struct p_client_context
|
|||||||
|
|
||||||
BOOL input_state_sync_pending;
|
BOOL input_state_sync_pending;
|
||||||
UINT32 input_state;
|
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 ClientX509Certificate; /* 71 custom=rdpContext* */
|
||||||
proxyHookFn ClientLoginFailure; /* 72 custom=rdpContext* */
|
proxyHookFn ClientLoginFailure; /* 72 custom=rdpContext* */
|
||||||
proxyHookFn ClientEndPaint; /* 73 custom=rdpContext* */
|
proxyHookFn ClientEndPaint; /* 73 custom=rdpContext* */
|
||||||
|
proxyHookFn ClientRedirect; /* 74 custom=rdpContext* */
|
||||||
UINT64 reserved3[96 - 74]; /* 74-95 */
|
UINT64 reserved3[96 - 75]; /* 75-95 */
|
||||||
|
|
||||||
proxyHookFn ServerPostConnect; /* 96 custom=freerdp_peer* */
|
proxyHookFn ServerPostConnect; /* 96 custom=freerdp_peer* */
|
||||||
proxyHookFn ServerPeerActivate; /* 97 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 scard_call_context* smartcard_call_context_new(const rdpSettings* settings);
|
||||||
FREERDP_API void smartcard_call_context_free(scard_call_context* ctx);
|
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_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_context(scard_call_context* ctx, SCARDCONTEXT context);
|
||||||
FREERDP_API BOOL smartcard_call_cancel_all_context(scard_call_context* ctx);
|
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)
|
if (!ctx)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
smartcard_call_context_signal_stop(ctx);
|
smartcard_call_context_signal_stop(ctx, FALSE);
|
||||||
|
|
||||||
LinkedList_Free(ctx->names);
|
LinkedList_Free(ctx->names);
|
||||||
if (ctx->StartedEvent)
|
if (ctx->StartedEvent)
|
||||||
@ -1941,10 +1941,14 @@ BOOL smartcard_call_is_configured(scard_call_context* ctx)
|
|||||||
#endif
|
#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);
|
WINPR_ASSERT(ctx);
|
||||||
if (!ctx->stopEvent)
|
if (!ctx->stopEvent)
|
||||||
return TRUE;
|
return TRUE;
|
||||||
|
|
||||||
|
if (reset)
|
||||||
|
return ResetEvent(ctx->stopEvent);
|
||||||
|
else
|
||||||
return SetEvent(ctx->stopEvent);
|
return SetEvent(ctx->stopEvent);
|
||||||
}
|
}
|
||||||
|
@ -41,6 +41,8 @@ set(${MODULE_PREFIX}_SRCS
|
|||||||
|
|
||||||
set(PROXY_APP_SRCS freerdp_proxy.c)
|
set(PROXY_APP_SRCS freerdp_proxy.c)
|
||||||
|
|
||||||
|
add_subdirectory("channels")
|
||||||
|
|
||||||
# On windows create dll version information.
|
# On windows create dll version information.
|
||||||
# Vendor, product and year are already set in top level CMakeLists.txt
|
# Vendor, product and year are already set in top level CMakeLists.txt
|
||||||
if (WIN32)
|
if (WIN32)
|
||||||
@ -58,7 +60,7 @@ if (WIN32)
|
|||||||
list(APPEND PROXY_APP_SRCS ${CMAKE_CURRENT_BINARY_DIR}/version.rc)
|
list(APPEND PROXY_APP_SRCS ${CMAKE_CURRENT_BINARY_DIR}/version.rc)
|
||||||
endif()
|
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})
|
set_target_properties(${MODULE_NAME} PROPERTIES OUTPUT_NAME ${MODULE_NAME}${FREERDP_API_VERSION})
|
||||||
|
|
||||||
if (WITH_LIBRARY_VERSIONING)
|
if (WITH_LIBRARY_VERSIONING)
|
||||||
@ -113,3 +115,8 @@ option(WITH_PROXY_MODULES "Compile proxy modules" ON)
|
|||||||
if (WITH_PROXY_MODULES)
|
if (WITH_PROXY_MODULES)
|
||||||
add_subdirectory("modules")
|
add_subdirectory("modules")
|
||||||
endif()
|
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 <freerdp/server/proxy/proxy_config.h>
|
||||||
#include "proxy_modules.h"
|
#include "proxy_modules.h"
|
||||||
#include "pf_utils.h"
|
#include "pf_utils.h"
|
||||||
|
#include "channels/pf_channel_rdpdr.h"
|
||||||
|
#include "channels/pf_channel_smartcard.h"
|
||||||
|
|
||||||
#define TAG PROXY_TAG("client")
|
#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))
|
if (!freerdp_static_channel_collection_find(context->settings, RDPSND_CHANNEL_NAME))
|
||||||
{
|
{
|
||||||
const char* params[2] = { RDPSND_CHANNEL_NAME };
|
const char* params[2] = { RDPSND_CHANNEL_NAME, "sys:fake" };
|
||||||
|
|
||||||
if (config->AudioOutput &&
|
|
||||||
WTSVirtualChannelManagerIsChannelJoined(ps->vcm, RDPSND_CHANNEL_NAME))
|
|
||||||
params[1] = "sys:proxy";
|
|
||||||
else
|
|
||||||
params[1] = "sys:fake";
|
|
||||||
|
|
||||||
if (!freerdp_client_add_static_channel(context->settings, ARRAYSIZE(params), params))
|
if (!freerdp_client_add_static_channel(context->settings, ARRAYSIZE(params), params))
|
||||||
return FALSE;
|
return FALSE;
|
||||||
@ -175,6 +171,64 @@ static BOOL pf_client_use_peer_load_balance_info(pClientContext* pc)
|
|||||||
return TRUE;
|
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)
|
static BOOL pf_client_pre_connect(freerdp* instance)
|
||||||
{
|
{
|
||||||
pClientContext* pc;
|
pClientContext* pc;
|
||||||
@ -217,7 +271,7 @@ static BOOL pf_client_pre_connect(freerdp* instance)
|
|||||||
config->DeviceRedirection) ||
|
config->DeviceRedirection) ||
|
||||||
!freerdp_settings_set_bool(settings, FreeRDP_SupportDisplayControl,
|
!freerdp_settings_set_bool(settings, FreeRDP_SupportDisplayControl,
|
||||||
config->DisplayControl) ||
|
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))
|
!freerdp_settings_set_bool(settings, FreeRDP_MultiTouchInput, config->Multitouch))
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
|
||||||
@ -275,6 +329,13 @@ static BOOL pf_client_pre_connect(freerdp* instance)
|
|||||||
}
|
}
|
||||||
else
|
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. */
|
/* Copy the current channel settings from the peer connection to the client. */
|
||||||
if (!freerdp_channels_from_mcs(settings, &ps->context))
|
if (!freerdp_channels_from_mcs(settings, &ps->context))
|
||||||
return FALSE;
|
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);
|
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,
|
static BOOL pf_client_receive_channel_passthrough(proxyData* pdata, UINT16 channelId,
|
||||||
const BYTE* xdata, size_t xsize, UINT32 flags,
|
const char* channel_name, const BYTE* xdata,
|
||||||
size_t totalSize)
|
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;
|
proxyChannelDataEventInfo ev;
|
||||||
UINT16 server_channel_id;
|
UINT16 server_channel_id;
|
||||||
|
pServerContext* ps;
|
||||||
|
|
||||||
|
WINPR_ASSERT(pdata);
|
||||||
|
|
||||||
|
ps = pdata->ps;
|
||||||
|
WINPR_ASSERT(ps);
|
||||||
|
|
||||||
ev.channel_id = channelId;
|
ev.channel_id = channelId;
|
||||||
ev.channel_name = channel_name;
|
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.flags = flags;
|
||||||
ev.total_size = totalSize;
|
ev.total_size = totalSize;
|
||||||
|
|
||||||
if (!pf_modules_run_filter(pdata->module, FILTER_TYPE_CLIENT_PASSTHROUGH_CHANNEL_DATA,
|
if (!pf_modules_run_filter(pdata->module, FILTER_TYPE_CLIENT_PASSTHROUGH_CHANNEL_DATA, pdata,
|
||||||
pdata, &ev))
|
&ev))
|
||||||
return TRUE; /* Silently drop */
|
return TRUE; /* Silently drop */
|
||||||
|
|
||||||
/* Dynamic channels need special treatment
|
/* 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.flags = flags;
|
||||||
dev.total_size = totalSize;
|
dev.total_size = totalSize;
|
||||||
|
|
||||||
if (!pf_modules_run_filter(pdata->module,
|
if (!pf_modules_run_filter(
|
||||||
FILTER_TYPE_CLIENT_PASSTHROUGH_DYN_CHANNEL_CREATE,
|
pdata->module, FILTER_TYPE_CLIENT_PASSTHROUGH_DYN_CHANNEL_CREATE, pdata, &dev))
|
||||||
pdata, &dev))
|
|
||||||
return TRUE; /* Silently drop */
|
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. */
|
* so just drop the message. */
|
||||||
if (server_channel_id == 0)
|
if (server_channel_id == 0)
|
||||||
return TRUE;
|
return TRUE;
|
||||||
return ps->context.peer->SendChannelPacket(ps->context.peer, server_channel_id,
|
return ps->context.peer->SendChannelPacket(ps->context.peer, server_channel_id, totalSize,
|
||||||
totalSize, flags, xdata, xsize);
|
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:
|
default:
|
||||||
WINPR_ASSERT(pc->client_receive_channel_data_original);
|
WINPR_ASSERT(pc->client_receive_channel_data_original);
|
||||||
return pc->client_receive_channel_data_original(instance, channelId, xdata, xsize,
|
return pc->client_receive_channel_data_original(instance, channelId, xdata, xsize,
|
||||||
@ -494,10 +586,9 @@ static BOOL sendQueuedChannelData(pClientContext* pc)
|
|||||||
rc = TRUE;
|
rc = TRUE;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
WINPR_ASSERT(pc->context.instance->SendChannelPacket);
|
WINPR_ASSERT(pc->context.instance->SendChannelData);
|
||||||
rc = pc->context.instance->SendChannelPacket(pc->context.instance, channelId,
|
rc = pc->context.instance->SendChannelData(pc->context.instance, channelId,
|
||||||
ev->total_size, ev->flags, ev->data,
|
ev->data, ev->data_len);
|
||||||
ev->data_len);
|
|
||||||
}
|
}
|
||||||
channel_data_free(ev);
|
channel_data_free(ev);
|
||||||
}
|
}
|
||||||
@ -589,8 +680,14 @@ static void pf_client_post_disconnect(freerdp* instance)
|
|||||||
pdata = pc->pdata;
|
pdata = pc->pdata;
|
||||||
WINPR_ASSERT(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;
|
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);
|
PubSub_UnsubscribeErrorInfo(instance->context->pubSub, pf_client_on_error_info);
|
||||||
gdi_free(instance);
|
gdi_free(instance);
|
||||||
@ -600,6 +697,30 @@ static void pf_client_post_disconnect(freerdp* instance)
|
|||||||
proxy_data_abort_connect(pdata);
|
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:
|
* pf_client_should_retry_without_nla:
|
||||||
*
|
*
|
||||||
@ -640,6 +761,14 @@ static void pf_client_set_security_settings(pClientContext* pc)
|
|||||||
settings->TlsSecurity = config->ClientTlsSecurity;
|
settings->TlsSecurity = config->ClientTlsSecurity;
|
||||||
settings->NlaSecurity = config->ClientNlaSecurity;
|
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)
|
if (!config->ClientNlaSecurity)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -827,6 +956,8 @@ static void pf_client_context_free(freerdp* instance, rdpContext* context)
|
|||||||
Queue_Free(pc->cached_server_channel_data);
|
Queue_Free(pc->cached_server_channel_data);
|
||||||
Stream_Free(pc->remote_pem, TRUE);
|
Stream_Free(pc->remote_pem, TRUE);
|
||||||
free(pc->remote_hostname);
|
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,
|
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->PreConnect = pf_client_pre_connect;
|
||||||
instance->PostConnect = pf_client_post_connect;
|
instance->PostConnect = pf_client_post_connect;
|
||||||
instance->PostDisconnect = pf_client_post_disconnect;
|
instance->PostDisconnect = pf_client_post_disconnect;
|
||||||
|
instance->Redirect = pf_client_redirect;
|
||||||
instance->LogonErrorInfo = pf_logon_error_info;
|
instance->LogonErrorInfo = pf_logon_error_info;
|
||||||
instance->VerifyX509Certificate = pf_client_verify_X509_certificate;
|
instance->VerifyX509Certificate = pf_client_verify_X509_certificate;
|
||||||
|
|
||||||
@ -929,6 +1061,18 @@ static BOOL pf_client_client_new(freerdp* instance, rdpContext* context)
|
|||||||
WINPR_ASSERT(obj);
|
WINPR_ASSERT(obj);
|
||||||
obj->fnObjectNew = channel_data_copy;
|
obj->fnObjectNew = channel_data_copy;
|
||||||
obj->fnObjectFree = channel_data_free;
|
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;
|
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->RemoteApp = pf_config_get_bool(ini, "Channels", "RemoteApp", FALSE);
|
||||||
config->PassthroughIsBlacklist =
|
config->PassthroughIsBlacklist =
|
||||||
pf_config_get_bool(ini, "Channels", "PassthroughIsBlacklist", FALSE);
|
pf_config_get_bool(ini, "Channels", "PassthroughIsBlacklist", FALSE);
|
||||||
|
|
||||||
config->Passthrough = pf_config_parse_comma_separated_list(
|
config->Passthrough = pf_config_parse_comma_separated_list(
|
||||||
pf_config_get_str(ini, "Channels", "Passthrough", FALSE), &config->PassthroughCount);
|
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;
|
return TRUE;
|
||||||
}
|
}
|
||||||
@ -485,6 +486,8 @@ BOOL pf_server_config_dump(const char* file)
|
|||||||
goto fail;
|
goto fail;
|
||||||
if (IniFile_SetKeyValueString(ini, "Channels", "Passthrough", "") < 0)
|
if (IniFile_SetKeyValueString(ini, "Channels", "Passthrough", "") < 0)
|
||||||
goto fail;
|
goto fail;
|
||||||
|
if (IniFile_SetKeyValueString(ini, "Channels", "Intercept", "") < 0)
|
||||||
|
goto fail;
|
||||||
|
|
||||||
/* Input configuration */
|
/* Input configuration */
|
||||||
if (IniFile_SetKeyValueString(ini, "Input", "Keyboard", "true") < 0)
|
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);
|
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_SECTION("Clipboard");
|
||||||
CONFIG_PRINT_BOOL(config, TextOnly);
|
CONFIG_PRINT_BOOL(config, TextOnly);
|
||||||
if (config->MaxTextLength > 0)
|
if (config->MaxTextLength > 0)
|
||||||
@ -701,6 +710,7 @@ void pf_server_config_free(proxyConfig* config)
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
free(config->Passthrough);
|
free(config->Passthrough);
|
||||||
|
free(config->Intercept);
|
||||||
free(config->RequiredPlugins);
|
free(config->RequiredPlugins);
|
||||||
free(config->Modules);
|
free(config->Modules);
|
||||||
free(config->TargetHost);
|
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,
|
if (!pf_config_copy_string_list(&tmp->Passthrough, &tmp->PassthroughCount, config->Passthrough,
|
||||||
config->PassthroughCount))
|
config->PassthroughCount))
|
||||||
goto fail;
|
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,
|
if (!pf_config_copy_string_list(&tmp->Modules, &tmp->ModulesCount, config->Modules,
|
||||||
config->ModulesCount))
|
config->ModulesCount))
|
||||||
goto fail;
|
goto fail;
|
||||||
@ -883,7 +896,6 @@ static BOOL config_plugin_mouse_event(proxyPlugin* plugin, proxyData* pdata, voi
|
|||||||
WINPR_ASSERT(cfg);
|
WINPR_ASSERT(cfg);
|
||||||
|
|
||||||
rc = cfg->Mouse;
|
rc = cfg->Mouse;
|
||||||
WLog_DBG(TAG, "%s: %s", __FUNCTION__, rc ? "TRUE" : "FALSE");
|
|
||||||
return rc;
|
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)
|
static BOOL config_plugin_dynamic_channel_create(proxyPlugin* plugin, proxyData* pdata, void* param)
|
||||||
{
|
{
|
||||||
int rc;
|
pf_utils_channel_mode rc;
|
||||||
BOOL accept;
|
BOOL accept;
|
||||||
const struct config_plugin_data* custom;
|
const struct config_plugin_data* custom;
|
||||||
const proxyConfig* cfg;
|
const proxyConfig* cfg;
|
||||||
@ -931,8 +943,21 @@ static BOOL config_plugin_dynamic_channel_create(proxyPlugin* plugin, proxyData*
|
|||||||
cfg = custom->config;
|
cfg = custom->config;
|
||||||
WINPR_ASSERT(cfg);
|
WINPR_ASSERT(cfg);
|
||||||
|
|
||||||
rc = pf_utils_channel_is_passthrough(cfg, channel->channel_name);
|
rc = pf_utils_get_channel_mode(cfg, channel->channel_name);
|
||||||
accept = rc > 0;
|
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 (accept)
|
||||||
{
|
{
|
||||||
if (strncmp(RDPGFX_DVC_CHANNEL_NAME, channel->channel_name,
|
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)
|
static BOOL config_plugin_channel_create(proxyPlugin* plugin, proxyData* pdata, void* param)
|
||||||
{
|
{
|
||||||
int rc;
|
pf_utils_channel_mode rc;
|
||||||
BOOL accept;
|
BOOL accept;
|
||||||
const struct config_plugin_data* custom;
|
const struct config_plugin_data* custom;
|
||||||
const proxyConfig* cfg;
|
const proxyConfig* cfg;
|
||||||
@ -987,8 +1012,19 @@ static BOOL config_plugin_channel_create(proxyPlugin* plugin, proxyData* pdata,
|
|||||||
cfg = custom->config;
|
cfg = custom->config;
|
||||||
WINPR_ASSERT(cfg);
|
WINPR_ASSERT(cfg);
|
||||||
|
|
||||||
rc = pf_utils_channel_is_passthrough(cfg, channel->channel_name);
|
rc = pf_utils_get_channel_mode(cfg, channel->channel_name);
|
||||||
accept = rc > 0;
|
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 (accept)
|
||||||
{
|
{
|
||||||
if (strncmp(CLIPRDR_SVC_CHANNEL_NAME, channel->channel_name,
|
if (strncmp(CLIPRDR_SVC_CHANNEL_NAME, channel->channel_name,
|
||||||
|
@ -29,10 +29,13 @@
|
|||||||
#include "pf_client.h"
|
#include "pf_client.h"
|
||||||
#include <freerdp/server/proxy/proxy_context.h>
|
#include <freerdp/server/proxy/proxy_context.h>
|
||||||
|
|
||||||
|
#include "channels/pf_channel_rdpdr.h"
|
||||||
|
|
||||||
/* Proxy context initialization callback */
|
/* Proxy context initialization callback */
|
||||||
static void client_to_proxy_context_free(freerdp_peer* client, rdpContext* ctx);
|
static void client_to_proxy_context_free(freerdp_peer* client, rdpContext* ctx);
|
||||||
static BOOL client_to_proxy_context_new(freerdp_peer* client, rdpContext* ctx)
|
static BOOL client_to_proxy_context_new(freerdp_peer* client, rdpContext* ctx)
|
||||||
{
|
{
|
||||||
|
wObject* obj;
|
||||||
pServerContext* context = (pServerContext*)ctx;
|
pServerContext* context = (pServerContext*)ctx;
|
||||||
|
|
||||||
WINPR_ASSERT(client);
|
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)))
|
if (!(context->dynvcReady = CreateEvent(NULL, TRUE, FALSE, NULL)))
|
||||||
goto error;
|
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;
|
return TRUE;
|
||||||
|
|
||||||
error:
|
error:
|
||||||
@ -66,15 +78,17 @@ void client_to_proxy_context_free(freerdp_peer* client, rdpContext* ctx)
|
|||||||
if (!context)
|
if (!context)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (context->vcm && (context->vcm != INVALID_HANDLE_VALUE))
|
|
||||||
WTSCloseServer((HANDLE)context->vcm);
|
|
||||||
context->vcm = NULL;
|
|
||||||
|
|
||||||
if (context->dynvcReady)
|
if (context->dynvcReady)
|
||||||
{
|
{
|
||||||
CloseHandle(context->dynvcReady);
|
CloseHandle(context->dynvcReady);
|
||||||
context->dynvcReady = NULL;
|
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)
|
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";
|
return "HOOK_TYPE_CLIENT_POST_CONNECT";
|
||||||
case HOOK_TYPE_CLIENT_POST_DISCONNECT:
|
case HOOK_TYPE_CLIENT_POST_DISCONNECT:
|
||||||
return "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:
|
case HOOK_TYPE_CLIENT_VERIFY_X509:
|
||||||
return "HOOK_TYPE_CLIENT_VERIFY_X509";
|
return "HOOK_TYPE_CLIENT_VERIFY_X509";
|
||||||
case HOOK_TYPE_CLIENT_LOGIN_FAILURE:
|
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);
|
ok = IFCALLRESULT(TRUE, plugin->ClientPostConnect, plugin, pdata, custom);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case HOOK_TYPE_CLIENT_REDIRECT:
|
||||||
|
ok = IFCALLRESULT(TRUE, plugin->ClientRedirect, plugin, pdata, custom);
|
||||||
|
break;
|
||||||
|
|
||||||
case HOOK_TYPE_CLIENT_POST_DISCONNECT:
|
case HOOK_TYPE_CLIENT_POST_DISCONNECT:
|
||||||
ok = IFCALLRESULT(TRUE, plugin->ClientPostDisconnect, plugin, pdata, custom);
|
ok = IFCALLRESULT(TRUE, plugin->ClientPostDisconnect, plugin, pdata, custom);
|
||||||
break;
|
break;
|
||||||
|
@ -39,6 +39,8 @@
|
|||||||
#include <freerdp/channels/channels.h>
|
#include <freerdp/channels/channels.h>
|
||||||
#include <freerdp/build-config.h>
|
#include <freerdp/build-config.h>
|
||||||
|
|
||||||
|
#include <freerdp/channels/rdpdr.h>
|
||||||
|
|
||||||
#include <freerdp/server/proxy/proxy_server.h>
|
#include <freerdp/server/proxy/proxy_server.h>
|
||||||
#include <freerdp/server/proxy/proxy_log.h>
|
#include <freerdp/server/proxy/proxy_log.h>
|
||||||
|
|
||||||
@ -49,9 +51,16 @@
|
|||||||
#include "pf_update.h"
|
#include "pf_update.h"
|
||||||
#include "proxy_modules.h"
|
#include "proxy_modules.h"
|
||||||
#include "pf_utils.h"
|
#include "pf_utils.h"
|
||||||
|
#include "channels/pf_channel_rdpdr.h"
|
||||||
|
|
||||||
#define TAG PROXY_TAG("server")
|
#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,
|
static BOOL pf_server_parse_target_from_routing_token(rdpContext* context, char** target,
|
||||||
DWORD* port)
|
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))
|
if (!pf_modules_run_hook(pdata->module, HOOK_TYPE_SERVER_POST_CONNECT, pdata, peer))
|
||||||
return FALSE;
|
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 */
|
/* Start a proxy's client in it's own thread */
|
||||||
if (!(pdata->client_thread = CreateThread(NULL, 0, pf_client_start, pc, 0, NULL)))
|
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;
|
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,
|
static BOOL pf_server_receive_channel_data_hook(freerdp_peer* peer, UINT16 channelId,
|
||||||
const BYTE* data, size_t size, UINT32 flags,
|
const BYTE* data, size_t size, UINT32 flags,
|
||||||
size_t totalSize)
|
size_t totalSize)
|
||||||
@ -298,7 +335,7 @@ static BOOL pf_server_receive_channel_data_hook(freerdp_peer* peer, UINT16 chann
|
|||||||
pClientContext* pc;
|
pClientContext* pc;
|
||||||
proxyData* pdata;
|
proxyData* pdata;
|
||||||
const proxyConfig* config;
|
const proxyConfig* config;
|
||||||
int pass;
|
pf_utils_channel_mode pass;
|
||||||
const char* channel_name = WTSChannelGetName(peer, channelId);
|
const char* channel_name = WTSChannelGetName(peer, channelId);
|
||||||
|
|
||||||
WINPR_ASSERT(peer);
|
WINPR_ASSERT(peer);
|
||||||
@ -320,12 +357,12 @@ static BOOL pf_server_receive_channel_data_hook(freerdp_peer* peer, UINT16 chann
|
|||||||
if (!pc)
|
if (!pc)
|
||||||
goto original_cb;
|
goto original_cb;
|
||||||
|
|
||||||
pass = pf_utils_channel_is_passthrough(config, channel_name);
|
pass = pf_utils_get_channel_mode(config, channel_name);
|
||||||
switch (pass)
|
switch (pass)
|
||||||
{
|
{
|
||||||
case 0:
|
case PF_UTILS_CHANNEL_BLOCK:
|
||||||
return TRUE;
|
return TRUE;
|
||||||
case 1:
|
case PF_UTILS_CHANNEL_PASSTHROUGH:
|
||||||
{
|
{
|
||||||
proxyChannelDataEventInfo ev;
|
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);
|
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:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -453,10 +493,14 @@ static DWORD WINAPI pf_server_handle_peer(LPVOID arg)
|
|||||||
DWORD status;
|
DWORD status;
|
||||||
pServerContext* ps = NULL;
|
pServerContext* ps = NULL;
|
||||||
proxyData* pdata = NULL;
|
proxyData* pdata = NULL;
|
||||||
freerdp_peer* client = (freerdp_peer*)arg;
|
freerdp_peer* client;
|
||||||
proxyServer* server;
|
proxyServer* server;
|
||||||
size_t count;
|
size_t count;
|
||||||
|
peer_thread_args* args = arg;
|
||||||
|
|
||||||
|
WINPR_ASSERT(args);
|
||||||
|
|
||||||
|
client = args->client;
|
||||||
WINPR_ASSERT(client);
|
WINPR_ASSERT(client);
|
||||||
|
|
||||||
server = (proxyServer*)client->ContextExtra;
|
server = (proxyServer*)client->ContextExtra;
|
||||||
@ -599,13 +643,9 @@ out_free_peer:
|
|||||||
WaitForSingleObject(pdata->client_thread, INFINITE);
|
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_Lock(server->peer_list);
|
||||||
ArrayList_Remove(server->peer_list, _GetCurrentThread());
|
ArrayList_Remove(server->peer_list, args->thread);
|
||||||
count = ArrayList_Count(server->peer_list);
|
count = ArrayList_Count(server->peer_list);
|
||||||
ArrayList_Unlock(server->peer_list);
|
ArrayList_Unlock(server->peer_list);
|
||||||
}
|
}
|
||||||
@ -617,6 +657,7 @@ out_free_peer:
|
|||||||
#if defined(WITH_DEBUG_EVENTS)
|
#if defined(WITH_DEBUG_EVENTS)
|
||||||
DumpEventHandles();
|
DumpEventHandles();
|
||||||
#endif
|
#endif
|
||||||
|
free(args);
|
||||||
ExitThread(0);
|
ExitThread(0);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -625,23 +666,28 @@ static BOOL pf_server_start_peer(freerdp_peer* client)
|
|||||||
{
|
{
|
||||||
HANDLE hThread;
|
HANDLE hThread;
|
||||||
proxyServer* server;
|
proxyServer* server;
|
||||||
|
peer_thread_args* args = calloc(1, sizeof(peer_thread_args));
|
||||||
|
if (!args)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
WINPR_ASSERT(client);
|
WINPR_ASSERT(client);
|
||||||
|
args->client = client;
|
||||||
|
|
||||||
server = (proxyServer*)client->ContextExtra;
|
server = (proxyServer*)client->ContextExtra;
|
||||||
WINPR_ASSERT(server);
|
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)
|
if (!hThread)
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
|
||||||
|
args->thread = hThread;
|
||||||
if (!ArrayList_Append(server->peer_list, hThread))
|
if (!ArrayList_Append(server->peer_list, hThread))
|
||||||
{
|
{
|
||||||
CloseHandle(hThread);
|
CloseHandle(hThread);
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
return ResumeThread(hThread) == 0;
|
return ResumeThread(hThread) != (DWORD)-1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static BOOL pf_server_peer_accepted(freerdp_listener* listener, freerdp_peer* client)
|
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)
|
static void peer_free(void* obj)
|
||||||
{
|
{
|
||||||
HANDLE hdl = (HANDLE)obj;
|
HANDLE hdl = (HANDLE)obj;
|
||||||
|
|
||||||
/* Threads have been notified about pending termination at this point.
|
|
||||||
*/
|
|
||||||
if (hdl != _GetCurrentThread())
|
|
||||||
WaitForSingleObject(hdl, INFINITE);
|
|
||||||
CloseHandle(hdl);
|
CloseHandle(hdl);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -830,7 +871,7 @@ proxyServer* pf_server_new(const proxyConfig* config)
|
|||||||
if (!server->listener)
|
if (!server->listener)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
server->peer_list = ArrayList_New(TRUE);
|
server->peer_list = ArrayList_New(FALSE);
|
||||||
if (!server->peer_list)
|
if (!server->peer_list)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
@ -925,6 +966,17 @@ void pf_server_free(proxyServer* server)
|
|||||||
|
|
||||||
pf_server_stop(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);
|
ArrayList_Free(server->peer_list);
|
||||||
freerdp_listener_free(server->listener);
|
freerdp_listener_free(server->listener);
|
||||||
|
|
||||||
|
@ -22,16 +22,30 @@
|
|||||||
#include <winpr/string.h>
|
#include <winpr/string.h>
|
||||||
#include <winpr/wtsapi.h>
|
#include <winpr/wtsapi.h>
|
||||||
|
|
||||||
|
#include <freerdp/server/proxy/proxy_log.h>
|
||||||
#include "pf_utils.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;
|
size_t i;
|
||||||
BOOL found = FALSE;
|
BOOL found = FALSE;
|
||||||
|
|
||||||
WINPR_ASSERT(config);
|
WINPR_ASSERT(config);
|
||||||
WINPR_ASSERT(name);
|
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++)
|
for (i = 0; i < config->PassthroughCount; i++)
|
||||||
{
|
{
|
||||||
const char* channel_name = config->Passthrough[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 (found)
|
||||||
{
|
{
|
||||||
if (config->PassthroughIsBlacklist)
|
if (config->PassthroughIsBlacklist)
|
||||||
return 0;
|
rc = PF_UTILS_CHANNEL_BLOCK;
|
||||||
return 1;
|
else
|
||||||
|
rc = PF_UTILS_CHANNEL_PASSTHROUGH;
|
||||||
}
|
}
|
||||||
|
else if (config->PassthroughIsBlacklist)
|
||||||
|
rc = PF_UTILS_CHANNEL_PASSTHROUGH;
|
||||||
|
|
||||||
if (config->PassthroughIsBlacklist)
|
end:
|
||||||
return 1;
|
WLog_DBG(TAG, "%s -> %s", name, pf_utils_channel_mode_string(rc));
|
||||||
return -1;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOL pf_utils_is_passthrough(const proxyConfig* config)
|
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. */
|
/* TODO: For the time being only passthrough mode is supported. */
|
||||||
return TRUE;
|
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 config The proxy configuration to check against. Must NOT be NULL.
|
||||||
* @param name The name of the channel. 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,
|
* @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);
|
BOOL pf_utils_is_passthrough(const proxyConfig* config);
|
||||||
|
|
||||||
|
@ -48,6 +48,7 @@ enum _PF_HOOK_TYPE
|
|||||||
HOOK_TYPE_CLIENT_PRE_CONNECT,
|
HOOK_TYPE_CLIENT_PRE_CONNECT,
|
||||||
HOOK_TYPE_CLIENT_POST_CONNECT,
|
HOOK_TYPE_CLIENT_POST_CONNECT,
|
||||||
HOOK_TYPE_CLIENT_POST_DISCONNECT,
|
HOOK_TYPE_CLIENT_POST_DISCONNECT,
|
||||||
|
HOOK_TYPE_CLIENT_REDIRECT,
|
||||||
HOOK_TYPE_CLIENT_VERIFY_X509,
|
HOOK_TYPE_CLIENT_VERIFY_X509,
|
||||||
HOOK_TYPE_CLIENT_LOGIN_FAILURE,
|
HOOK_TYPE_CLIENT_LOGIN_FAILURE,
|
||||||
HOOK_TYPE_CLIENT_END_PAINT,
|
HOOK_TYPE_CLIENT_END_PAINT,
|
||||||
|
Loading…
Reference in New Issue
Block a user