diff options
Diffstat (limited to 'server/proxy/pf_context.c')
-rw-r--r-- | server/proxy/pf_context.c | 394 |
1 files changed, 394 insertions, 0 deletions
diff --git a/server/proxy/pf_context.c b/server/proxy/pf_context.c new file mode 100644 index 0000000..04433d9 --- /dev/null +++ b/server/proxy/pf_context.c @@ -0,0 +1,394 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * FreeRDP Proxy Server + * + * Copyright 2019 Mati Shabtay <matishabtay@gmail.com> + * Copyright 2019 Kobi Mizrachi <kmizrachi18@gmail.com> + * Copyright 2019 Idan Freiberg <speidy@gmail.com> + * Copyright 2021 Armin Novak <anovak@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/crypto.h> +#include <winpr/print.h> + +#include <freerdp/server/proxy/proxy_log.h> +#include <freerdp/server/proxy/proxy_server.h> + +#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" + +#define TAG PROXY_TAG("server") + +static UINT32 ChannelId_Hash(const void* key) +{ + const UINT32* v = (const UINT32*)key; + return *v; +} + +static BOOL ChannelId_Compare(const void* pv1, const void* pv2) +{ + const UINT32* v1 = pv1; + const UINT32* v2 = pv2; + WINPR_ASSERT(v1); + WINPR_ASSERT(v2); + return (*v1 == *v2); +} + +pServerStaticChannelContext* StaticChannelContext_new(pServerContext* ps, const char* name, + UINT32 id) +{ + pServerStaticChannelContext* ret = calloc(1, sizeof(*ret)); + if (!ret) + { + PROXY_LOG_ERR(TAG, ps, "error allocating channel context for '%s'", name); + return NULL; + } + + ret->front_channel_id = id; + ret->channel_name = _strdup(name); + if (!ret->channel_name) + { + PROXY_LOG_ERR(TAG, ps, "error allocating name in channel context for '%s'", name); + free(ret); + 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; +} + +void StaticChannelContext_free(pServerStaticChannelContext* ctx) +{ + if (!ctx) + return; + + IFCALL(ctx->contextDtor, ctx->context); + + free(ctx->channel_name); + free(ctx); +} + +static void HashStaticChannelContext_free(void* ptr) +{ + pServerStaticChannelContext* ctx = (pServerStaticChannelContext*)ptr; + StaticChannelContext_free(ctx); +} + +/* 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 = NULL; + pServerContext* context = (pServerContext*)ctx; + + WINPR_ASSERT(client); + WINPR_ASSERT(context); + + context->dynvcReady = NULL; + + context->vcm = WTSOpenServerA((LPSTR)client->context); + + if (!context->vcm || context->vcm == INVALID_HANDLE_VALUE) + goto error; + + 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; + + /* channels by ids */ + context->channelsByFrontId = HashTable_New(FALSE); + if (!context->channelsByFrontId) + goto error; + if (!HashTable_SetHashFunction(context->channelsByFrontId, ChannelId_Hash)) + goto error; + + obj = HashTable_KeyObject(context->channelsByFrontId); + obj->fnObjectEquals = ChannelId_Compare; + + obj = HashTable_ValueObject(context->channelsByFrontId); + obj->fnObjectFree = HashStaticChannelContext_free; + + context->channelsByBackId = HashTable_New(FALSE); + if (!context->channelsByBackId) + goto error; + if (!HashTable_SetHashFunction(context->channelsByBackId, ChannelId_Hash)) + goto error; + + obj = HashTable_KeyObject(context->channelsByBackId); + obj->fnObjectEquals = ChannelId_Compare; + + return TRUE; + +error: + client_to_proxy_context_free(client, ctx); + + return FALSE; +} + +/* Proxy context free callback */ +void client_to_proxy_context_free(freerdp_peer* client, rdpContext* ctx) +{ + pServerContext* context = (pServerContext*)ctx; + + WINPR_UNUSED(client); + + if (!context) + return; + + if (context->dynvcReady) + { + CloseHandle(context->dynvcReady); + context->dynvcReady = NULL; + } + + HashTable_Free(context->interceptContextMap); + HashTable_Free(context->channelsByFrontId); + HashTable_Free(context->channelsByBackId); + + if (context->vcm && (context->vcm != INVALID_HANDLE_VALUE)) + WTSCloseServer((HANDLE)context->vcm); + context->vcm = NULL; +} + +BOOL pf_context_init_server_context(freerdp_peer* client) +{ + WINPR_ASSERT(client); + + client->ContextSize = sizeof(pServerContext); + client->ContextNew = client_to_proxy_context_new; + client->ContextFree = client_to_proxy_context_free; + + return freerdp_peer_context_new(client); +} + +static BOOL pf_context_revert_str_settings(rdpSettings* dst, const rdpSettings* before, size_t nr, + const FreeRDP_Settings_Keys_String* ids) +{ + WINPR_ASSERT(dst); + WINPR_ASSERT(before); + WINPR_ASSERT(ids || (nr == 0)); + + for (size_t x = 0; x < nr; x++) + { + FreeRDP_Settings_Keys_String id = ids[x]; + const char* what = freerdp_settings_get_string(before, id); + if (!freerdp_settings_set_string(dst, id, what)) + return FALSE; + } + + return TRUE; +} + +void intercept_context_entry_free(void* obj) +{ + InterceptContextMapEntry* entry = obj; + if (!entry) + return; + if (!entry->free) + return; + entry->free(entry); +} + +BOOL pf_context_copy_settings(rdpSettings* dst, const rdpSettings* src) +{ + BOOL rc = FALSE; + rdpSettings* before_copy = NULL; + const FreeRDP_Settings_Keys_String to_revert[] = { FreeRDP_ConfigPath, + FreeRDP_CertificateName }; + + if (!dst || !src) + return FALSE; + + before_copy = freerdp_settings_clone(dst); + if (!before_copy) + return FALSE; + + if (!freerdp_settings_copy(dst, src)) + goto out_fail; + + /* keep original ServerMode value */ + if (!freerdp_settings_copy_item(dst, before_copy, FreeRDP_ServerMode)) + goto out_fail; + + /* revert some values that must not be changed */ + if (!pf_context_revert_str_settings(dst, before_copy, ARRAYSIZE(to_revert), to_revert)) + goto out_fail; + + if (!freerdp_settings_get_bool(dst, FreeRDP_ServerMode)) + { + /* adjust instance pointer */ + if (!freerdp_settings_copy_item(dst, before_copy, FreeRDP_instance)) + goto out_fail; + + /* + * RdpServerRsaKey must be set to NULL if `dst` is client's context + * it must be freed before setting it to NULL to avoid a memory leak! + */ + + if (!freerdp_settings_set_pointer_len(dst, FreeRDP_RdpServerRsaKey, NULL, 1)) + goto out_fail; + } + + /* We handle certificate management for this client ourselfes. */ + rc = freerdp_settings_set_bool(dst, FreeRDP_ExternalCertificateManagement, TRUE); + +out_fail: + freerdp_settings_free(before_copy); + return rc; +} + +pClientContext* pf_context_create_client_context(const rdpSettings* clientSettings) +{ + RDP_CLIENT_ENTRY_POINTS clientEntryPoints; + pClientContext* pc = NULL; + rdpContext* context = NULL; + + WINPR_ASSERT(clientSettings); + + RdpClientEntry(&clientEntryPoints); + context = freerdp_client_context_new(&clientEntryPoints); + + if (!context) + return NULL; + + pc = (pClientContext*)context; + + if (!pf_context_copy_settings(context->settings, clientSettings)) + goto error; + + return pc; +error: + freerdp_client_context_free(context); + return NULL; +} + +proxyData* proxy_data_new(void) +{ + BYTE temp[16]; + char* hex = NULL; + proxyData* pdata = NULL; + + pdata = calloc(1, sizeof(proxyData)); + if (!pdata) + return NULL; + + if (!(pdata->abort_event = CreateEvent(NULL, TRUE, FALSE, NULL))) + goto error; + + if (!(pdata->gfx_server_ready = CreateEvent(NULL, TRUE, FALSE, NULL))) + goto error; + + winpr_RAND(&temp, 16); + hex = winpr_BinToHexString(temp, 16, FALSE); + if (!hex) + goto error; + + CopyMemory(pdata->session_id, hex, PROXY_SESSION_ID_LENGTH); + pdata->session_id[PROXY_SESSION_ID_LENGTH] = '\0'; + free(hex); + + if (!(pdata->modules_info = HashTable_New(FALSE))) + goto error; + + /* modules_info maps between plugin name to custom data */ + if (!HashTable_SetupForStringData(pdata->modules_info, FALSE)) + goto error; + + return pdata; +error: + WINPR_PRAGMA_DIAG_PUSH + WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC + proxy_data_free(pdata); + WINPR_PRAGMA_DIAG_POP + return NULL; +} + +/* updates circular pointers between proxyData and pClientContext instances */ +void proxy_data_set_client_context(proxyData* pdata, pClientContext* context) +{ + WINPR_ASSERT(pdata); + WINPR_ASSERT(context); + pdata->pc = context; + context->pdata = pdata; +} + +/* updates circular pointers between proxyData and pServerContext instances */ +void proxy_data_set_server_context(proxyData* pdata, pServerContext* context) +{ + WINPR_ASSERT(pdata); + WINPR_ASSERT(context); + pdata->ps = context; + context->pdata = pdata; +} + +void proxy_data_free(proxyData* pdata) +{ + if (!pdata) + return; + + if (pdata->abort_event) + CloseHandle(pdata->abort_event); + + if (pdata->client_thread) + CloseHandle(pdata->client_thread); + + if (pdata->gfx_server_ready) + CloseHandle(pdata->gfx_server_ready); + + if (pdata->modules_info) + HashTable_Free(pdata->modules_info); + + if (pdata->pc) + freerdp_client_context_free(&pdata->pc->context); + + free(pdata); +} + +void proxy_data_abort_connect(proxyData* pdata) +{ + WINPR_ASSERT(pdata); + WINPR_ASSERT(pdata->abort_event); + SetEvent(pdata->abort_event); + if (pdata->pc) + freerdp_abort_connect_context(&pdata->pc->context); +} + +BOOL proxy_data_shall_disconnect(proxyData* pdata) +{ + WINPR_ASSERT(pdata); + WINPR_ASSERT(pdata->abort_event); + return WaitForSingleObject(pdata->abort_event, 0) == WAIT_OBJECT_0; +} |