[proxy] implement dynamic channel filter
* Allow modules to configure intercept channels * Allow modules to rewrite packets
This commit is contained in:
parent
2067a480e9
commit
f26079edf2
@ -26,6 +26,7 @@
|
||||
#include <freerdp/channels/wtsvc.h>
|
||||
|
||||
#include <freerdp/server/proxy/proxy_config.h>
|
||||
#include <freerdp/server/proxy/proxy_types.h>
|
||||
|
||||
#define PROXY_SESSION_ID_LENGTH 32
|
||||
|
||||
@ -46,24 +47,6 @@ extern "C"
|
||||
/* All proxy interception channels derive from this base struct
|
||||
* and set their cleanup function accordingly. */
|
||||
FREERDP_API void intercept_context_entry_free(void* obj);
|
||||
|
||||
/** @brief how is handled a channel */
|
||||
typedef enum
|
||||
{
|
||||
PF_UTILS_CHANNEL_NOT_HANDLED, /*!< channel not handled */
|
||||
PF_UTILS_CHANNEL_BLOCK, /*!< block and drop traffic on this channel */
|
||||
PF_UTILS_CHANNEL_PASSTHROUGH, /*!< pass traffic from this channel */
|
||||
PF_UTILS_CHANNEL_INTERCEPT, /*!< inspect traffic from this channel */
|
||||
} pf_utils_channel_mode;
|
||||
|
||||
/** @brief result of a channel treatment */
|
||||
typedef enum
|
||||
{
|
||||
PF_CHANNEL_RESULT_PASS, /*!< pass the packet as is */
|
||||
PF_CHANNEL_RESULT_DROP, /*!< drop the packet */
|
||||
PF_CHANNEL_RESULT_ERROR /*!< error during packet treatment */
|
||||
} PfChannelResult;
|
||||
|
||||
typedef PfChannelResult (*proxyChannelDataFn)(proxyData* pdata,
|
||||
const pServerStaticChannelContext* channel,
|
||||
const BYTE* xdata, size_t xsize, UINT32 flags,
|
||||
|
@ -6,6 +6,8 @@
|
||||
* Copyright 2019 Idan Freiberg <speidy@gmail.com>
|
||||
* Copyright 2021 Armin Novak <anovak@thincast.com>
|
||||
* Copyright 2021 Thincast Technologies GmbH
|
||||
* Copyright 2023 Armin Novak <anovak@thincast.com>
|
||||
* Copyright 2023 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.
|
||||
@ -23,34 +25,41 @@
|
||||
#ifndef FREERDP_SERVER_PROXY_MODULES_API_H
|
||||
#define FREERDP_SERVER_PROXY_MODULES_API_H
|
||||
|
||||
#include <freerdp/freerdp.h>
|
||||
#include <winpr/winpr.h>
|
||||
#include <winpr/stream.h>
|
||||
#include <winpr/sspi.h>
|
||||
|
||||
#include <freerdp/server/proxy/proxy_types.h>
|
||||
|
||||
#define MODULE_TAG(module) "proxy.modules." module
|
||||
|
||||
typedef struct proxy_data proxyData;
|
||||
typedef struct proxy_module proxyModule;
|
||||
typedef struct proxy_plugin proxyPlugin;
|
||||
typedef struct proxy_plugins_manager proxyPluginsManager;
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
/* hook callback. should return TRUE on success or FALSE on error. */
|
||||
typedef BOOL (*proxyHookFn)(proxyPlugin*, proxyData*, void*);
|
||||
typedef struct proxy_data proxyData;
|
||||
typedef struct proxy_module proxyModule;
|
||||
typedef struct proxy_plugin proxyPlugin;
|
||||
typedef struct proxy_plugins_manager proxyPluginsManager;
|
||||
|
||||
/*
|
||||
/* hook callback. should return TRUE on success or FALSE on error. */
|
||||
typedef BOOL (*proxyHookFn)(proxyPlugin*, proxyData*, void*);
|
||||
|
||||
/*
|
||||
* Filter callback:
|
||||
* It MUST return TRUE if the related event should be proxied,
|
||||
* or FALSE if it should be ignored.
|
||||
*/
|
||||
typedef BOOL (*proxyFilterFn)(proxyPlugin*, proxyData*, void*);
|
||||
typedef BOOL (*proxyFilterFn)(proxyPlugin*, proxyData*, void*);
|
||||
|
||||
/* describes a plugin: name, description and callbacks to execute.
|
||||
/* describes a plugin: name, description and callbacks to execute.
|
||||
*
|
||||
* This is public API, so always add new fields at the end of the struct to keep
|
||||
* some backward compatibility.
|
||||
*/
|
||||
struct proxy_plugin
|
||||
{
|
||||
struct proxy_plugin
|
||||
{
|
||||
const char* name; /* 0: unique module name */
|
||||
const char* description; /* 1: module description */
|
||||
|
||||
@ -93,28 +102,37 @@ struct proxy_plugin
|
||||
proxyFilterFn ChannelCreate; /* 135 passthrough drdynvc channel create data */
|
||||
proxyFilterFn UnicodeEvent; /* 136 */
|
||||
proxyFilterFn MouseExEvent; /* 137 */
|
||||
UINT64 reserved5[160 - 138]; /* 138-159 */
|
||||
|
||||
/* proxy dynamic channel filters:
|
||||
*
|
||||
* - a function that returns the list of channels to intercept
|
||||
* - a function to call with the data received
|
||||
*/
|
||||
proxyFilterFn DynChannelToIntercept; /* 138 */
|
||||
proxyFilterFn DynChannelIntercept; /* 139 */
|
||||
proxyFilterFn StaticChannelToIntercept; /* 140 */
|
||||
UINT64 reserved5[160 - 141]; /* 141-159 */
|
||||
|
||||
/* Runtime data fields */
|
||||
proxyPluginsManager* mgr; /* 160 */ /** Set during plugin registration */
|
||||
void* userdata; /* 161 */ /** Custom data provided with RegisterPlugin, memory managed
|
||||
outside of plugin. */
|
||||
void* custom; /* 162 */ /** Custom configuration data, must be allocated in RegisterPlugin and
|
||||
freed in PluginUnload */
|
||||
void* custom; /* 162 */ /** Custom configuration data, must be allocated in RegisterPlugin
|
||||
and freed in PluginUnload */
|
||||
|
||||
UINT64 reserved6[192 - 163]; /* 163-191 Add some filler data to allow for new callbacks or
|
||||
* fields without breaking API */
|
||||
};
|
||||
};
|
||||
|
||||
/*
|
||||
/*
|
||||
* Main API for use by external modules.
|
||||
* Supports:
|
||||
* - Registering a plugin.
|
||||
* - Setting/getting plugin's per-session specific data.
|
||||
* - Aborting a session.
|
||||
*/
|
||||
struct proxy_plugins_manager
|
||||
{
|
||||
struct proxy_plugins_manager
|
||||
{
|
||||
/* 0 used for registering a fresh new proxy plugin. */
|
||||
BOOL (*RegisterPlugin)(struct proxy_plugins_manager* mgr, const proxyPlugin* plugin);
|
||||
|
||||
@ -128,9 +146,9 @@ struct proxy_plugins_manager
|
||||
void (*AbortConnect)(struct proxy_plugins_manager* mgr, proxyData*);
|
||||
|
||||
UINT64 reserved[128 - 4]; /* 4-127 reserved fields */
|
||||
};
|
||||
};
|
||||
|
||||
typedef BOOL (*proxyModuleEntryPoint)(proxyPluginsManager* plugins_manager, void* userdata);
|
||||
typedef BOOL (*proxyModuleEntryPoint)(proxyPluginsManager* plugins_manager, void* userdata);
|
||||
|
||||
/* filter events parameters */
|
||||
#define WINPR_PACK_PUSH
|
||||
@ -174,14 +192,6 @@ typedef struct
|
||||
UINT32 flags;
|
||||
} proxyChannelDataEventInfo;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
PROXY_FETCH_TARGET_METHOD_DEFAULT,
|
||||
PROXY_FETCH_TARGET_METHOD_CONFIG,
|
||||
PROXY_FETCH_TARGET_METHOD_LOAD_BALANCE_INFO,
|
||||
PROXY_FETCH_TARGET_USE_CUSTOM_ADDR
|
||||
} ProxyFetchTargetMethod;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
/* out values */
|
||||
@ -200,7 +210,32 @@ typedef struct server_peer_logon
|
||||
const SEC_WINNT_AUTH_IDENTITY* identity;
|
||||
BOOL automatic;
|
||||
} proxyServerPeerLogon;
|
||||
|
||||
typedef struct dyn_channel_intercept_data
|
||||
{
|
||||
const char* name;
|
||||
UINT32 channelId;
|
||||
wStream* data;
|
||||
BOOL isBackData;
|
||||
BOOL first;
|
||||
BOOL last;
|
||||
BOOL rewritten;
|
||||
size_t packetSize;
|
||||
PfChannelResult result;
|
||||
} proxyDynChannelInterceptData;
|
||||
|
||||
typedef struct dyn_channel_to_intercept_data
|
||||
{
|
||||
const char* name;
|
||||
UINT32 channelId;
|
||||
BOOL intercept;
|
||||
} proxyChannelToInterceptData;
|
||||
|
||||
#define WINPR_PACK_POP
|
||||
#include <winpr/pack.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* FREERDP_SERVER_PROXY_MODULES_API_H */
|
||||
|
48
include/freerdp/server/proxy/proxy_types.h
Normal file
48
include/freerdp/server/proxy/proxy_types.h
Normal file
@ -0,0 +1,48 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* FreeRDP Proxy enum types
|
||||
*
|
||||
* Copyright 2023 Armin Novak <armin.novak@thincast.com>
|
||||
* Copyright 2023 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_TYPES_H
|
||||
#define FREERDP_SERVER_PROXY_TYPES_H
|
||||
|
||||
/** @brief how is handled a channel */
|
||||
typedef enum
|
||||
{
|
||||
PF_UTILS_CHANNEL_NOT_HANDLED, /*!< channel not handled */
|
||||
PF_UTILS_CHANNEL_BLOCK, /*!< block and drop traffic on this channel */
|
||||
PF_UTILS_CHANNEL_PASSTHROUGH, /*!< pass traffic from this channel */
|
||||
PF_UTILS_CHANNEL_INTERCEPT, /*!< inspect traffic from this channel */
|
||||
} pf_utils_channel_mode;
|
||||
|
||||
/** @brief result of a channel treatment */
|
||||
typedef enum
|
||||
{
|
||||
PF_CHANNEL_RESULT_PASS, /*!< pass the packet as is */
|
||||
PF_CHANNEL_RESULT_DROP, /*!< drop the packet */
|
||||
PF_CHANNEL_RESULT_ERROR /*!< error during packet treatment */
|
||||
} PfChannelResult;
|
||||
typedef enum
|
||||
{
|
||||
PROXY_FETCH_TARGET_METHOD_DEFAULT,
|
||||
PROXY_FETCH_TARGET_METHOD_CONFIG,
|
||||
PROXY_FETCH_TARGET_METHOD_LOAD_BALANCE_INFO,
|
||||
PROXY_FETCH_TARGET_USE_CUSTOM_ADDR
|
||||
} ProxyFetchTargetMethod;
|
||||
|
||||
#endif /* FREERDP_SERVER_PROXY_TYPES_H */
|
@ -87,6 +87,35 @@ typedef enum
|
||||
DYNCVC_READ_INCOMPLETE /*!< missing bytes to read the complete packet */
|
||||
} DynvcReadResult;
|
||||
|
||||
static PfChannelResult data_cb(pServerContext* ps, pServerDynamicChannelContext* channel,
|
||||
BOOL isBackData, ChannelStateTracker* tracker, BOOL firstPacket,
|
||||
BOOL lastPacket)
|
||||
{
|
||||
WINPR_ASSERT(ps);
|
||||
WINPR_ASSERT(channel);
|
||||
WINPR_ASSERT(tracker);
|
||||
WINPR_ASSERT(ps->pdata);
|
||||
|
||||
wStream* currentPacket = channelTracker_getCurrentPacket(tracker);
|
||||
proxyDynChannelInterceptData dyn = { .name = channel->channelName,
|
||||
.channelId = channel->channelId,
|
||||
.data = currentPacket,
|
||||
.isBackData = isBackData,
|
||||
.first = firstPacket,
|
||||
.last = lastPacket,
|
||||
.rewritten = FALSE,
|
||||
.packetSize = channelTracker_getCurrentPacketSize(tracker),
|
||||
.result = PF_CHANNEL_RESULT_ERROR };
|
||||
Stream_SealLength(dyn.data);
|
||||
if (!pf_modules_run_filter(ps->pdata->module, FILTER_TYPE_INTERCEPT_CHANNEL, ps->pdata, &dyn))
|
||||
return PF_CHANNEL_RESULT_ERROR;
|
||||
|
||||
channelTracker_setCurrentPacketSize(tracker, dyn.packetSize);
|
||||
if (dyn.rewritten)
|
||||
return channelTracker_flushCurrent(tracker, firstPacket, lastPacket, !isBackData);
|
||||
return dyn.result;
|
||||
}
|
||||
|
||||
static pServerDynamicChannelContext* DynamicChannelContext_new(pServerContext* ps, const char* name,
|
||||
UINT32 id)
|
||||
{
|
||||
@ -106,6 +135,14 @@ static pServerDynamicChannelContext* DynamicChannelContext_new(pServerContext* p
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ret->frontTracker.dataCallback = data_cb;
|
||||
ret->backTracker.dataCallback = data_cb;
|
||||
|
||||
proxyChannelToInterceptData dyn = { .name = name, .channelId = id, .intercept = FALSE };
|
||||
if (pf_modules_run_filter(ps->pdata->module, FILTER_TYPE_DYN_INTERCEPT_LIST, ps->pdata, &dyn) &&
|
||||
dyn.intercept)
|
||||
ret->channelMode = PF_UTILS_CHANNEL_INTERCEPT;
|
||||
else
|
||||
ret->channelMode = pf_utils_get_channel_mode(ps->pdata->config, name);
|
||||
ret->openStatus = CHANNEL_OPENSTATE_OPENED;
|
||||
ret->packetReassembly = (ret->channelMode == PF_UTILS_CHANNEL_INTERCEPT);
|
||||
@ -113,8 +150,9 @@ static pServerDynamicChannelContext* DynamicChannelContext_new(pServerContext* p
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void DynamicChannelContext_free(pServerDynamicChannelContext* c)
|
||||
static void DynamicChannelContext_free(void* ptr)
|
||||
{
|
||||
pServerDynamicChannelContext* c = (pServerDynamicChannelContext*)ptr;
|
||||
if (!c)
|
||||
return;
|
||||
|
||||
@ -137,8 +175,10 @@ static UINT32 ChannelId_Hash(const void* key)
|
||||
return *v;
|
||||
}
|
||||
|
||||
static BOOL ChannelId_Compare(const UINT32* v1, const UINT32* v2)
|
||||
static BOOL ChannelId_Compare(const void* objA, const void* objB)
|
||||
{
|
||||
const UINT32* v1 = objA;
|
||||
const UINT32* v2 = objB;
|
||||
return (*v1 == *v2);
|
||||
}
|
||||
|
||||
@ -557,10 +597,10 @@ static DynChannelContext* DynChannelContext_new(proxyData* pdata,
|
||||
goto fail;
|
||||
|
||||
obj = HashTable_KeyObject(dyn->channels);
|
||||
obj->fnObjectEquals = (OBJECT_EQUALS_FN)ChannelId_Compare;
|
||||
obj->fnObjectEquals = ChannelId_Compare;
|
||||
|
||||
obj = HashTable_ValueObject(dyn->channels);
|
||||
obj->fnObjectFree = (OBJECT_FREE_FN)DynamicChannelContext_free;
|
||||
obj->fnObjectFree = DynamicChannelContext_free;
|
||||
|
||||
return dyn;
|
||||
|
||||
@ -575,9 +615,11 @@ static PfChannelResult pf_dynvc_back_data(proxyData* pdata,
|
||||
size_t totalSize)
|
||||
{
|
||||
WINPR_ASSERT(channel);
|
||||
|
||||
DynChannelContext* dyn = (DynChannelContext*)channel->context;
|
||||
WINPR_UNUSED(pdata);
|
||||
WINPR_ASSERT(dyn);
|
||||
|
||||
return channelTracker_update(dyn->backTracker, xdata, xsize, flags, totalSize);
|
||||
}
|
||||
|
||||
@ -587,9 +629,11 @@ static PfChannelResult pf_dynvc_front_data(proxyData* pdata,
|
||||
size_t totalSize)
|
||||
{
|
||||
WINPR_ASSERT(channel);
|
||||
|
||||
DynChannelContext* dyn = (DynChannelContext*)channel->context;
|
||||
WINPR_UNUSED(pdata);
|
||||
WINPR_ASSERT(dyn);
|
||||
|
||||
return channelTracker_update(dyn->frontTracker, xdata, xsize, flags, totalSize);
|
||||
}
|
||||
|
||||
|
@ -29,6 +29,8 @@
|
||||
|
||||
#include "pf_client.h"
|
||||
#include "pf_utils.h"
|
||||
#include "proxy_modules.h"
|
||||
|
||||
#include <freerdp/server/proxy/proxy_context.h>
|
||||
|
||||
#include "channels/pf_channel_rdpdr.h"
|
||||
@ -65,6 +67,13 @@ pServerStaticChannelContext* StaticChannelContext_new(pServerContext* ps, const
|
||||
return NULL;
|
||||
}
|
||||
|
||||
proxyChannelToInterceptData channel = { .name = name, .channelId = id, .intercept = FALSE };
|
||||
|
||||
if (pf_modules_run_filter(ps->pdata->module, FILTER_TYPE_STATIC_INTERCEPT_LIST, ps->pdata,
|
||||
&channel) &&
|
||||
channel.intercept)
|
||||
ret->channelMode = PF_UTILS_CHANNEL_INTERCEPT;
|
||||
else
|
||||
ret->channelMode = pf_utils_get_channel_mode(ps->pdata->config, name);
|
||||
return ret;
|
||||
}
|
||||
|
@ -292,6 +292,18 @@ static BOOL pf_modules_ArrayList_ForEachFkt(void* data, size_t index, va_list ap
|
||||
result = IFCALLRESULT(TRUE, plugin->ServerPeerLogon, plugin, pdata, param);
|
||||
break;
|
||||
|
||||
case FILTER_TYPE_INTERCEPT_CHANNEL:
|
||||
result = IFCALLRESULT(TRUE, plugin->DynChannelIntercept, plugin, pdata, param);
|
||||
break;
|
||||
|
||||
case FILTER_TYPE_DYN_INTERCEPT_LIST:
|
||||
result = IFCALLRESULT(TRUE, plugin->DynChannelToIntercept, plugin, pdata, param);
|
||||
break;
|
||||
|
||||
case FILTER_TYPE_STATIC_INTERCEPT_LIST:
|
||||
result = IFCALLRESULT(TRUE, plugin->StaticChannelToIntercept, plugin, pdata, param);
|
||||
break;
|
||||
|
||||
case FILTER_LAST:
|
||||
default:
|
||||
WLog_ERR(TAG, "invalid filter called");
|
||||
|
@ -39,6 +39,9 @@ typedef enum
|
||||
FILTER_TYPE_SERVER_PEER_LOGON, /* proxyServerPeerLogon */
|
||||
FILTER_TYPE_CLIENT_PASSTHROUGH_CHANNEL_CREATE, /* proxyChannelDataEventInfo */
|
||||
|
||||
FILTER_TYPE_STATIC_INTERCEPT_LIST, /* proxyChannelToInterceptData */
|
||||
FILTER_TYPE_DYN_INTERCEPT_LIST, /* proxyChannelToInterceptData */
|
||||
FILTER_TYPE_INTERCEPT_CHANNEL, /* proxyDynChannelInterceptData */
|
||||
FILTER_LAST
|
||||
} PF_FILTER_TYPE;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user