FreeRDP
Loading...
Searching...
No Matches
pf_client.c
1
24#include <ctype.h>
25#include <winpr/assert.h>
26#include <winpr/cast.h>
27
28#include <freerdp/config.h>
29
30#include <freerdp/freerdp.h>
31#include <freerdp/message.h>
32#include <freerdp/gdi/gdi.h>
33#include <freerdp/client/cmdline.h>
34
35#include <freerdp/server/proxy/proxy_log.h>
36#include <freerdp/channels/drdynvc.h>
37#include <freerdp/channels/encomsp.h>
38#include <freerdp/channels/rdpdr.h>
39#include <freerdp/channels/rdpsnd.h>
40#include <freerdp/channels/cliprdr.h>
41#include <freerdp/channels/channels.h>
42
43#include "pf_client.h"
44#include "pf_server.h"
45#include "pf_channel.h"
46#include <freerdp/server/proxy/proxy_context.h>
47#include "pf_update.h"
48#include "pf_input.h"
49#include <freerdp/server/proxy/proxy_config.h>
50#include "proxy_modules.h"
51#include "pf_utils.h"
52#include "channels/pf_channel_rdpdr.h"
53#include "channels/pf_channel_smartcard.h"
54
55#define TAG PROXY_TAG("client")
56
57/* Upper bound for pClientContext::server_messages, the proxy server thread queues whatever the
58 * client sends while the proxy client thread might be busy (re)connecting */
59#define PF_CLIENT_MAX_SERVER_MESSAGES 4096
60
61static void channel_data_free(void* obj);
62
63WINPR_ATTR_NODISCARD
64static BOOL proxy_server_reactivate(rdpContext* ps, const rdpContext* pc)
65{
66 WINPR_ASSERT(ps);
67 WINPR_ASSERT(pc);
68
69 if (!pf_context_copy_settings(ps->settings, pc->settings))
70 return FALSE;
71
72 /*
73 * DesktopResize causes internal function rdp_server_reactivate to be called,
74 * which causes the reactivation.
75 */
76 WINPR_ASSERT(ps->update);
77 return (ps->update->DesktopResize(ps));
78}
79
80static void pf_client_on_error_info(void* ctx, const ErrorInfoEventArgs* e)
81{
82 pClientContext* pc = (pClientContext*)ctx;
83 WINPR_ASSERT(pc);
84 WINPR_ASSERT(pc->pdata);
85 WINPR_ASSERT(e);
86
87 pServerContext* ps = proxy_data_get_server_context(pc->pdata);
88 WINPR_ASSERT(ps);
89
90 if (e->code == ERRINFO_NONE)
91 return;
92
93 PROXY_LOG_WARN(TAG, pc, "received ErrorInfo PDU. code=0x%08" PRIu32 ", message: %s", e->code,
94 freerdp_get_error_info_string(e->code));
95
96 /* forward error back to client */
97 freerdp_set_error_info(ps->context.rdp, e->code);
98 if (!freerdp_send_error_info(ps->context.rdp))
99 {
100 PROXY_LOG_WARN(TAG, pc, "[fail] reply ErrorInfo PDU. code=0x%08" PRIu32 ", message: %s",
101 e->code, freerdp_get_error_info_string(e->code));
102 }
103}
104
105static void pf_client_on_activated(WINPR_ATTR_UNUSED void* ctx,
106 WINPR_ATTR_UNUSED const ActivatedEventArgs* e)
107{
108}
109
110WINPR_ATTR_NODISCARD
111static BOOL pf_client_load_rdpsnd(pClientContext* pc)
112{
113 rdpContext* context = (rdpContext*)pc;
114
115 WINPR_ASSERT(pc);
116 WINPR_ASSERT(pc->pdata);
117 /*
118 * if AudioOutput is enabled in proxy and client connected with rdpsnd, use proxy as rdpsnd
119 * backend. Otherwise, use sys:fake.
120 */
121 if (!freerdp_static_channel_collection_find(context->settings, RDPSND_CHANNEL_NAME))
122 {
123 const char* params[2] = { RDPSND_CHANNEL_NAME, "sys:fake" };
124
125 if (!freerdp_client_add_static_channel(context->settings, ARRAYSIZE(params), params))
126 return FALSE;
127 }
128
129 return TRUE;
130}
131
132WINPR_ATTR_NODISCARD
133static BOOL pf_client_use_peer_load_balance_info(pClientContext* pc)
134{
135 WINPR_ASSERT(pc);
136 WINPR_ASSERT(pc->pdata);
137
138 pServerContext* ps = proxy_data_get_server_context(pc->pdata);
139 WINPR_ASSERT(ps);
140 rdpSettings* settings = pc->cctx.context.settings;
141 WINPR_ASSERT(settings);
142
143 DWORD lb_info_len = 0;
144 const char* lb_info = freerdp_nego_get_routing_token(&ps->context, &lb_info_len);
145 if (!lb_info)
146 return TRUE;
147
148 return freerdp_settings_set_pointer_len(settings, FreeRDP_LoadBalanceInfo, lb_info,
149 lb_info_len);
150}
151
152WINPR_ATTR_NODISCARD
153static BOOL str_is_empty(const char* str)
154{
155 if (!str)
156 return TRUE;
157 if (strlen(str) == 0)
158 return TRUE;
159 return FALSE;
160}
161
162WINPR_ATTR_NODISCARD
163static BOOL pf_client_use_proxy_smartcard_auth(const rdpSettings* settings)
164{
165 BOOL enable = freerdp_settings_get_bool(settings, FreeRDP_SmartcardLogon);
166 const char* key = freerdp_settings_get_string(settings, FreeRDP_SmartcardPrivateKey);
167 const char* cert = freerdp_settings_get_string(settings, FreeRDP_SmartcardCertificate);
168
169 if (!enable)
170 return FALSE;
171
172 if (str_is_empty(key))
173 return FALSE;
174
175 if (str_is_empty(cert))
176 return FALSE;
177
178 return TRUE;
179}
180
181WINPR_ATTR_NODISCARD
182static BOOL pf_client_pre_connect(freerdp* instance)
183{
184 WINPR_ASSERT(instance);
185 pClientContext* pc = (pClientContext*)instance->context;
186 WINPR_ASSERT(pc);
187 WINPR_ASSERT(pc->pdata);
188
189 pServerContext* ps = proxy_data_get_server_context(pc->pdata);
190 WINPR_ASSERT(ps);
191 WINPR_ASSERT(ps->pdata);
192
193 const proxyConfig* config = ps->pdata->config;
194 WINPR_ASSERT(config);
195
196 rdpSettings* settings = instance->context->settings;
197 WINPR_ASSERT(settings);
198
199 /*
200 * as the client's settings are copied from the server's, GlyphSupportLevel might not be
201 * GLYPH_SUPPORT_NONE. the proxy currently do not support GDI & GLYPH_SUPPORT_CACHE, so
202 * GlyphCacheSupport must be explicitly set to GLYPH_SUPPORT_NONE.
203 *
204 * Also, OrderSupport need to be zeroed, because it is currently not supported.
205 */
206 if (!freerdp_settings_set_uint32(settings, FreeRDP_GlyphSupportLevel, GLYPH_SUPPORT_NONE))
207 return FALSE;
208
209 void* OrderSupport = freerdp_settings_get_pointer_writable(settings, FreeRDP_OrderSupport);
210 ZeroMemory(OrderSupport, 32);
211
212 if (WTSVirtualChannelManagerIsChannelJoined(ps->vcm, DRDYNVC_SVC_CHANNEL_NAME))
213 {
214 if (!freerdp_settings_set_bool(settings, FreeRDP_SupportDynamicChannels, TRUE))
215 return FALSE;
216 }
217
218 /* Multimon */
219 if (!freerdp_settings_set_bool(settings, FreeRDP_UseMultimon, TRUE))
220 return FALSE;
221
222 /* Sound */
223 if (!freerdp_settings_set_bool(settings, FreeRDP_AudioCapture, config->AudioInput) ||
224 !freerdp_settings_set_bool(settings, FreeRDP_AudioPlayback, config->AudioOutput) ||
225 !freerdp_settings_set_bool(settings, FreeRDP_DeviceRedirection,
226 config->DeviceRedirection) ||
227 !freerdp_settings_set_bool(settings, FreeRDP_MultiTouchInput, config->Multitouch))
228 return FALSE;
229
230 if (config->RemoteApp)
231 {
232 if (WTSVirtualChannelManagerIsChannelJoined(ps->vcm, RAIL_SVC_CHANNEL_NAME))
233 {
234 if (!freerdp_settings_set_bool(settings, FreeRDP_RemoteApplicationMode, TRUE))
235 return FALSE;
236 }
237 }
238
239 if (config->DeviceRedirection)
240 {
241 if (WTSVirtualChannelManagerIsChannelJoined(ps->vcm, RDPDR_SVC_CHANNEL_NAME))
242 {
243 if (!freerdp_settings_set_bool(settings, FreeRDP_DeviceRedirection, TRUE))
244 return FALSE;
245 }
246 }
247
248 /* Display control */
249 if (!freerdp_settings_set_bool(settings, FreeRDP_SupportDisplayControl, config->DisplayControl))
250 return FALSE;
251 if (!freerdp_settings_set_bool(settings, FreeRDP_DynamicResolutionUpdate,
252 config->DisplayControl))
253 return FALSE;
254
255 if (WTSVirtualChannelManagerIsChannelJoined(ps->vcm, ENCOMSP_SVC_CHANNEL_NAME))
256 {
257 if (!freerdp_settings_set_bool(settings, FreeRDP_EncomspVirtualChannel, TRUE))
258 return FALSE;
259 }
260
261 if (config->Clipboard)
262 {
263 if (WTSVirtualChannelManagerIsChannelJoined(ps->vcm, CLIPRDR_SVC_CHANNEL_NAME))
264 {
265 if (!freerdp_settings_set_bool(settings, FreeRDP_RedirectClipboard, config->Clipboard))
266 return FALSE;
267 }
268 }
269
270 if (!freerdp_settings_set_bool(settings, FreeRDP_AutoReconnectionEnabled, TRUE))
271 return FALSE;
272
273 if (PubSub_SubscribeErrorInfo(instance->context->pubSub, pf_client_on_error_info) < 0)
274 return FALSE;
275 if (PubSub_SubscribeActivated(instance->context->pubSub, pf_client_on_activated) < 0)
276 return FALSE;
277 if (!pf_client_use_peer_load_balance_info(pc))
278 return FALSE;
279
280 return pf_modules_run_hook(pc->pdata->module, HOOK_TYPE_CLIENT_PRE_CONNECT, pc->pdata, pc);
281}
282
284typedef struct
285{
286 pServerContext* ps;
287 const char* name;
288 UINT32 backId;
289} UpdateBackIdArgs;
290
291WINPR_ATTR_NODISCARD
292static BOOL updateBackIdFn(WINPR_ATTR_UNUSED const void* key, void* value, void* arg)
293{
294 pServerStaticChannelContext* current = (pServerStaticChannelContext*)value;
295 UpdateBackIdArgs* updateArgs = (UpdateBackIdArgs*)arg;
296
297 if (strcmp(updateArgs->name, current->channel_name) != 0)
298 return TRUE;
299
300 current->back_channel_id = updateArgs->backId;
301 if (!HashTable_Insert(updateArgs->ps->channelsByBackId, &current->back_channel_id, current))
302 {
303 WLog_ERR(TAG, "error inserting channel in channelsByBackId table");
304 }
305 return FALSE;
306}
307
308WINPR_ATTR_NODISCARD
309static BOOL pf_client_update_back_id(pServerContext* ps, const char* name, UINT32 backId)
310{
311 UpdateBackIdArgs res = { ps, name, backId };
312
313 return HashTable_Foreach(ps->channelsByFrontId, updateBackIdFn, &res) == FALSE;
314}
315
316WINPR_ATTR_NODISCARD
317static BOOL pf_client_load_channels(freerdp* instance)
318{
319 WINPR_ASSERT(instance);
320
321 pClientContext* pc = (pClientContext*)instance->context;
322 WINPR_ASSERT(pc);
323 WINPR_ASSERT(pc->pdata);
324
325 pServerContext* ps = proxy_data_get_server_context(pc->pdata);
326 WINPR_ASSERT(ps);
327 WINPR_ASSERT(ps->pdata);
328
329 const proxyConfig* config = ps->pdata->config;
330 WINPR_ASSERT(config);
331
332 rdpSettings* settings = instance->context->settings;
333 WINPR_ASSERT(settings);
338 PROXY_LOG_INFO(TAG, pc, "Loading addins");
339
340 if (!pf_client_load_rdpsnd(pc))
341 {
342 PROXY_LOG_ERR(TAG, pc, "Failed to load rdpsnd client");
343 return FALSE;
344 }
345
346 if (!pf_utils_is_passthrough(config))
347 {
348 if (!freerdp_client_load_addins(instance->context->channels, settings))
349 {
350 PROXY_LOG_ERR(TAG, pc, "Failed to load addins");
351 return FALSE;
352 }
353 }
354 else
355 {
356 if (!pf_channel_rdpdr_client_new(pc))
357 return FALSE;
358#if defined(WITH_PROXY_EMULATE_SMARTCARD)
359 if (!pf_channel_smartcard_client_new(pc))
360 return FALSE;
361#endif
362 /* Copy the current channel settings from the peer connection to the client. */
363 if (!freerdp_channels_from_mcs(settings, &ps->context))
364 return FALSE;
365
366 /* Filter out channels we do not want */
367 {
368 CHANNEL_DEF* channels = (CHANNEL_DEF*)freerdp_settings_get_pointer_array_writable(
369 settings, FreeRDP_ChannelDefArray, 0);
370 UINT32 size = freerdp_settings_get_uint32(settings, FreeRDP_ChannelCount);
371 UINT32 id = MCS_GLOBAL_CHANNEL_ID + 1;
372
373 WINPR_ASSERT(channels || (size == 0));
374
375 UINT32 x = 0;
376 for (; x < size;)
377 {
378 CHANNEL_DEF* cur = &channels[x];
379 proxyChannelDataEventInfo dev = WINPR_C_ARRAY_INIT;
380
381 dev.channel_name = cur->name;
382 dev.flags = cur->options;
383
384 /* Filter out channels blocked by config */
385 if (!pf_modules_run_filter(pc->pdata->module,
386 FILTER_TYPE_CLIENT_PASSTHROUGH_CHANNEL_CREATE, pc->pdata,
387 &dev))
388 {
389 const size_t s = size - MIN(size, x + 1);
390 memmove(cur, &cur[1], sizeof(CHANNEL_DEF) * s);
391 size--;
392 }
393 else
394 {
395 if (!pf_client_update_back_id(ps, cur->name, id++))
396 {
397 WLog_ERR(TAG, "unable to update backid for channel %s", cur->name);
398 return FALSE;
399 }
400 x++;
401 }
402 }
403
404 if (!freerdp_settings_set_uint32(settings, FreeRDP_ChannelCount, x))
405 return FALSE;
406 }
407 }
408 return pf_modules_run_hook(pc->pdata->module, HOOK_TYPE_CLIENT_LOAD_CHANNELS, pc->pdata, pc);
409}
410
411WINPR_ATTR_NODISCARD
412static BOOL pf_client_receive_channel_data_hook(freerdp* instance, UINT16 channelId,
413 const BYTE* xdata, size_t xsize, UINT32 flags,
414 size_t totalSize)
415{
416 UINT64 channelId64 = channelId;
417
418 WINPR_ASSERT(instance);
419 WINPR_ASSERT(xdata || (xsize == 0));
420
421 pClientContext* pc = (pClientContext*)instance->context;
422 WINPR_ASSERT(pc);
423 WINPR_ASSERT(pc->pdata);
424
425 pServerContext* ps = proxy_data_get_server_context(pc->pdata);
426 WINPR_ASSERT(ps);
427
428 proxyData* pdata = ps->pdata;
429 WINPR_ASSERT(pdata);
430
431 pServerStaticChannelContext* channel =
432 HashTable_GetItemValue(ps->channelsByBackId, &channelId64);
433 if (!channel)
434 return TRUE;
435
436 WINPR_ASSERT(channel->onBackData);
437 switch (channel->onBackData(pdata, channel, xdata, xsize, flags, totalSize))
438 {
439 case PF_CHANNEL_RESULT_PASS:
440 /* Ignore messages for channels that can not be mapped.
441 * The client might not have enabled support for this specific channel,
442 * so just drop the message. */
443 if (channel->front_channel_id == 0)
444 return TRUE;
445
446 return ps->context.peer->SendChannelPacket(
447 ps->context.peer, WINPR_ASSERTING_INT_CAST(UINT16, channel->front_channel_id),
448 totalSize, flags, xdata, xsize);
449 case PF_CHANNEL_RESULT_DROP:
450 return TRUE;
451 case PF_CHANNEL_RESULT_ERROR:
452 default:
453 return FALSE;
454 }
455}
456
457WINPR_ATTR_NODISCARD
458static BOOL pf_client_on_server_heartbeat(freerdp* instance, BYTE period, BYTE count1, BYTE count2)
459{
460 WINPR_ASSERT(instance);
461 pClientContext* pc = (pClientContext*)instance->context;
462 WINPR_ASSERT(pc);
463 WINPR_ASSERT(pc->pdata);
464
465 pServerContext* ps = proxy_data_get_server_context(pc->pdata);
466 WINPR_ASSERT(ps);
467
468 return freerdp_heartbeat_send_heartbeat_pdu(ps->context.peer, period, count1, count2);
469}
470
471WINPR_ATTR_NODISCARD
472static BOOL pf_client_send_channel_data(pClientContext* pc, const proxyChannelDataEventInfo* ev)
473{
474 WINPR_ASSERT(pc);
475 WINPR_ASSERT(ev);
476
477 return Queue_Enqueue(pc->cached_server_channel_data, ev);
478}
479
480WINPR_ATTR_NODISCARD
481static BOOL sendQueuedChannelData(pClientContext* pc)
482{
483 BOOL rc = TRUE;
484
485 WINPR_ASSERT(pc);
486
487 if (pc->connected)
488 {
489 proxyChannelDataEventInfo* ev = nullptr;
490
491 Queue_Lock(pc->cached_server_channel_data);
492 while (rc && (ev = Queue_Dequeue(pc->cached_server_channel_data)))
493 {
494 UINT16 channelId = 0;
495 WINPR_ASSERT(pc->cctx.context.instance);
496
497 channelId =
498 freerdp_channels_get_id_by_name(pc->cctx.context.instance, ev->channel_name);
499 /* Ignore unmappable channels */
500 if ((channelId == 0) || (channelId == UINT16_MAX))
501 rc = TRUE;
502 else
503 {
504 WINPR_ASSERT(pc->cctx.context.instance->SendChannelPacket);
505 rc = pc->cctx.context.instance->SendChannelPacket(
506 pc->cctx.context.instance, channelId, ev->total_size, ev->flags, ev->data,
507 ev->data_len);
508 }
509 channel_data_free(ev);
510 }
511
512 Queue_Unlock(pc->cached_server_channel_data);
513 }
514
515 return rc;
516}
517
518static void server_message_free(void* obj)
519{
520 wMessage* msg = obj;
521
522 if (msg && (GetMessageClass(msg->id) == Update_Class))
523 free(msg->lParam);
524}
525
526BOOL pf_client_post_message(pClientContext* pc, UINT32 id, size_t wParam, void* lParam)
527{
528 wMessage msg = { .id = id, .context = pc, .wParam = (void*)wParam, .lParam = lParam };
529
530 WINPR_ASSERT(pc);
531 WINPR_ASSERT(pc->server_messages);
532
533 /* The proxy client thread does not keep up, drop the message like for a connection that is
534 * not active */
535 if (MessageQueue_Size(pc->server_messages) >= PF_CLIENT_MAX_SERVER_MESSAGES)
536 {
537 server_message_free(&msg);
538 return TRUE;
539 }
540
541 if (!MessageQueue_Dispatch(pc->server_messages, &msg))
542 {
543 server_message_free(&msg);
544 return FALSE;
545 }
546
547 return TRUE;
548}
549
550WINPR_ATTR_NODISCARD
551static BOOL sendQueuedServerMessages(pClientContext* pc)
552{
553 BOOL rc = TRUE;
554 wMessage msg = WINPR_C_ARRAY_INIT;
555
556 WINPR_ASSERT(pc);
557
558 /* Only what is queued right now, new messages must not keep this thread from receiving */
559 size_t count = MessageQueue_Size(pc->server_messages);
560 while (rc && (count-- > 0) && (MessageQueue_Peek(pc->server_messages, &msg, TRUE) > 0))
561 {
562 switch (GetMessageClass(msg.id))
563 {
564 case Update_Class:
565 rc = pf_client_process_update_message(pc, &msg);
566 break;
567 case Input_Class:
568 rc = pf_client_process_input_message(pc, &msg);
569 break;
570 default:
571 rc = FALSE;
572 break;
573 }
574 server_message_free(&msg);
575 }
576
577 return rc;
578}
579
589WINPR_ATTR_NODISCARD
590static BOOL pf_client_post_connect(freerdp* instance)
591{
592 WINPR_ASSERT(instance);
593 rdpContext* context = instance->context;
594 WINPR_ASSERT(context);
595 rdpUpdate* update = context->update;
596 WINPR_ASSERT(update);
597 pClientContext* pc = (pClientContext*)context;
598 WINPR_ASSERT(pc);
599 WINPR_ASSERT(pc->pdata);
600
601 pServerContext* ps = proxy_data_get_server_context(pc->pdata);
602 WINPR_ASSERT(ps);
603
604 if (!pf_modules_run_hook(pc->pdata->module, HOOK_TYPE_CLIENT_POST_CONNECT, pc->pdata, pc))
605 return FALSE;
606
607 if (!gdi_init(instance, PIXEL_FORMAT_BGRA32))
608 return FALSE;
609
610 WINPR_ASSERT(freerdp_settings_get_bool(context->settings, FreeRDP_SoftwareGdi));
611
612 pf_client_register_update_callbacks(update);
613
614 /* virtual channels receive data hook */
615 pc->client_receive_channel_data_original = instance->ReceiveChannelData;
616 instance->ReceiveChannelData = pf_client_receive_channel_data_hook;
617
618 instance->heartbeat->ServerHeartbeat = pf_client_on_server_heartbeat;
619
620 pc->connected = TRUE;
621
622 /* Send cached channel data */
623 if (!sendQueuedChannelData(pc))
624 return FALSE;
625
626 /*
627 * after the connection fully established and settings were negotiated with target server,
628 * send a reactivation sequence to the client with the negotiated settings. This way,
629 * settings are synchorinized between proxy's peer and and remote target.
630 */
631 return proxy_server_reactivate(&ps->context, context);
632}
633
634/* This function is called whether a session ends by failure or success.
635 * Clean up everything allocated by pre_connect and post_connect.
636 */
637static void pf_client_post_disconnect(freerdp* instance)
638{
639 pClientContext* pc = nullptr;
640 proxyData* pdata = nullptr;
641
642 if (!instance)
643 return;
644
645 if (!instance->context)
646 return;
647
648 pc = (pClientContext*)instance->context;
649 WINPR_ASSERT(pc);
650 pdata = pc->pdata;
651 WINPR_ASSERT(pdata);
652
653#if defined(WITH_PROXY_EMULATE_SMARTCARD)
654 pf_channel_smartcard_client_free(pc);
655#endif
656
657 pf_channel_rdpdr_client_free(pc);
658
659 pc->connected = FALSE;
660 (void)pf_modules_run_hook(pc->pdata->module, HOOK_TYPE_CLIENT_POST_DISCONNECT, pc->pdata, pc);
661
662 PubSub_UnsubscribeErrorInfo(instance->context->pubSub, pf_client_on_error_info);
663 gdi_free(instance);
664
665 /* Only close the connection if NLA fallback process is done */
666 if (!pc->allow_next_conn_failure)
667 proxy_data_abort_connect(pdata);
668}
669
670WINPR_ATTR_NODISCARD
671static BOOL pf_client_redirect(freerdp* instance)
672{
673 if (!instance)
674 return FALSE;
675
676 if (!instance->context)
677 return FALSE;
678
679 pClientContext* pc = (pClientContext*)instance->context;
680 WINPR_ASSERT(pc);
681
682#if defined(WITH_PROXY_EMULATE_SMARTCARD)
683 pf_channel_smartcard_client_reset(pc);
684#endif
685 pf_channel_rdpdr_client_reset(pc);
686
687 return pf_modules_run_hook(pc->pdata->module, HOOK_TYPE_CLIENT_REDIRECT, pc->pdata, pc);
688}
689
690/*
691 * pf_client_should_retry_without_nla:
692 *
693 * returns TRUE if in case of connection failure, the client should try again without NLA.
694 * Otherwise, returns FALSE.
695 */
696WINPR_ATTR_NODISCARD
697static BOOL pf_client_should_retry_without_nla(pClientContext* pc)
698{
699 rdpSettings* settings = nullptr;
700 const proxyConfig* config = nullptr;
701
702 WINPR_ASSERT(pc);
703 WINPR_ASSERT(pc->pdata);
704 settings = pc->cctx.context.settings;
705 WINPR_ASSERT(settings);
706 config = pc->pdata->config;
707 WINPR_ASSERT(config);
708
709 if (!config->ClientAllowFallbackToTls ||
710 (!freerdp_settings_get_bool(settings, FreeRDP_NlaSecurity) &&
711 !freerdp_settings_get_bool(settings, FreeRDP_ExtSecurity)))
712 return FALSE;
713
714 return config->ClientTlsSecurity || config->ClientRdpSecurity;
715}
716
717WINPR_ATTR_NODISCARD
718static BOOL pf_client_set_security_settings(pClientContext* pc)
719{
720 WINPR_ASSERT(pc);
721 WINPR_ASSERT(pc->pdata);
722 rdpSettings* settings = pc->cctx.context.settings;
723 WINPR_ASSERT(settings);
724 const proxyConfig* config = pc->pdata->config;
725 WINPR_ASSERT(config);
726
727 if (!freerdp_settings_set_bool(settings, FreeRDP_RdpSecurity, config->ClientRdpSecurity))
728 return FALSE;
729 if (!freerdp_settings_set_bool(settings, FreeRDP_TlsSecurity, config->ClientTlsSecurity))
730 return FALSE;
731 if (!freerdp_settings_set_bool(settings, FreeRDP_NlaSecurity, config->ClientNlaSecurity))
732 return FALSE;
733 if (!freerdp_settings_set_bool(settings, FreeRDP_ExtSecurity, config->ClientExtSecurity))
734 return FALSE;
735
736 if (pf_client_use_proxy_smartcard_auth(settings))
737 {
738 /* Smartcard authentication requires smartcard redirection to be enabled */
739 if (!freerdp_settings_set_bool(settings, FreeRDP_RedirectSmartCards, TRUE))
740 return FALSE;
741
742 /* Reset username/domain, we will get that info later from the sc cert */
743 if (!freerdp_settings_set_string(settings, FreeRDP_Username, nullptr))
744 return FALSE;
745 if (!freerdp_settings_set_string(settings, FreeRDP_Domain, nullptr))
746 return FALSE;
747 }
748
749 return TRUE;
750}
751
752WINPR_ATTR_NODISCARD
753static BOOL pf_client_connect_without_nla(pClientContext* pc)
754{
755 freerdp* instance = nullptr;
756 rdpSettings* settings = nullptr;
757
758 WINPR_ASSERT(pc);
759 instance = pc->cctx.context.instance;
760 WINPR_ASSERT(instance);
761
762 if (!freerdp_context_reset(instance))
763 return FALSE;
764
765 settings = pc->cctx.context.settings;
766 WINPR_ASSERT(settings);
767
768 /* If already disabled abort early. */
769 if (!freerdp_settings_get_bool(settings, FreeRDP_NlaSecurity) &&
770 !freerdp_settings_get_bool(settings, FreeRDP_ExtSecurity))
771 return FALSE;
772
773 /* disable NLA */
774 if (!freerdp_settings_set_bool(settings, FreeRDP_NlaSecurity, FALSE))
775 return FALSE;
776 if (!freerdp_settings_set_bool(settings, FreeRDP_ExtSecurity, FALSE))
777 return FALSE;
778
779 /* do not allow next connection failure */
780 pc->allow_next_conn_failure = FALSE;
781 return freerdp_connect(instance);
782}
783
784WINPR_ATTR_NODISCARD
785static BOOL pf_client_connect(freerdp* instance)
786{
787 pClientContext* pc = nullptr;
788 rdpSettings* settings = nullptr;
789 BOOL rc = FALSE;
790 BOOL retry = FALSE;
791
792 WINPR_ASSERT(instance);
793 pc = (pClientContext*)instance->context;
794 WINPR_ASSERT(pc);
795 settings = instance->context->settings;
796 WINPR_ASSERT(settings);
797
798 if (!pf_client_set_security_settings(pc))
799 return FALSE;
800
801 if (pf_client_should_retry_without_nla(pc))
802 retry = pc->allow_next_conn_failure = TRUE;
803
804 PROXY_LOG_INFO(TAG, pc, "connecting using client info: Username: %s, Domain: %s",
805 freerdp_settings_get_string(settings, FreeRDP_Username),
806 freerdp_settings_get_string(settings, FreeRDP_Domain));
807 PROXY_LOG_INFO(TAG, pc, "connecting using security settings: rdp=%d, tls=%d, nla=%d, ext=%d",
808 freerdp_settings_get_bool(settings, FreeRDP_RdpSecurity),
809 freerdp_settings_get_bool(settings, FreeRDP_TlsSecurity),
810 freerdp_settings_get_bool(settings, FreeRDP_NlaSecurity),
811 freerdp_settings_get_bool(settings, FreeRDP_ExtSecurity));
812
813 if (!freerdp_connect(instance))
814 {
815 if (!pf_modules_run_hook(pc->pdata->module, HOOK_TYPE_CLIENT_LOGIN_FAILURE, pc->pdata, pc))
816 goto out;
817
818 if (!retry)
819 goto out;
820
821 PROXY_LOG_ERR(TAG, pc, "failed to connect with NLA. retrying to connect without NLA");
822 if (!pf_client_connect_without_nla(pc))
823 {
824 PROXY_LOG_ERR(TAG, pc, "pf_client_connect_without_nla failed!");
825 goto out;
826 }
827 }
828
829 rc = TRUE;
830out:
831 pc->allow_next_conn_failure = FALSE;
832 return rc;
833}
834
840WINPR_ATTR_NODISCARD
841static DWORD WINAPI pf_client_thread_proc(pClientContext* pc)
842{
843 freerdp* instance = nullptr;
844 proxyData* pdata = nullptr;
845 DWORD nCount = 0;
846 DWORD status = 0;
847 HANDLE handles[MAXIMUM_WAIT_OBJECTS] = WINPR_C_ARRAY_INIT;
848
849 WINPR_ASSERT(pc);
850
851 instance = pc->cctx.context.instance;
852 WINPR_ASSERT(instance);
853
854 pdata = pc->pdata;
855 WINPR_ASSERT(pdata);
856 /*
857 * during redirection, freerdp's abort event might be overridden (reset) by the library, after
858 * the server set it in order to shutdown the connection. it means that the server might signal
859 * the client to abort, but the library code will override the signal and the client will
860 * continue its work instead of exiting. That's why the client must wait on `pdata->abort_event`
861 * too, which will never be modified by the library.
862 */
863 handles[nCount++] = pdata->abort_event;
864
865 if (!pf_modules_run_hook(pdata->module, HOOK_TYPE_CLIENT_INIT_CONNECT, pdata, pc))
866 {
867 proxy_data_abort_connect(pdata);
868 goto end;
869 }
870
871 if (!pf_client_connect(instance))
872 {
873 proxy_data_abort_connect(pdata);
874 goto end;
875 }
876 handles[nCount++] = Queue_Event(pc->cached_server_channel_data);
877 handles[nCount++] = MessageQueue_Event(pc->server_messages);
878
879 while (!freerdp_shall_disconnect_context(instance->context))
880 {
881 UINT32 tmp = freerdp_get_event_handles(instance->context, &handles[nCount],
882 ARRAYSIZE(handles) - nCount);
883
884 if (tmp == 0)
885 {
886 PROXY_LOG_ERR(TAG, pc, "freerdp_get_event_handles failed!");
887 break;
888 }
889
890 status = WaitForMultipleObjects(nCount + tmp, handles, FALSE, INFINITE);
891
892 if (status == WAIT_FAILED)
893 {
894 WLog_ERR(TAG, "WaitForMultipleObjects failed with %" PRIu32 "", status);
895 break;
896 }
897
898 /* abort_event triggered */
899 if (status == WAIT_OBJECT_0)
900 break;
901
902 if (freerdp_shall_disconnect_context(instance->context))
903 break;
904
905 if (proxy_data_shall_disconnect(pdata))
906 break;
907
908 if (!freerdp_check_event_handles(instance->context))
909 {
910 if (freerdp_get_last_error(instance->context) == FREERDP_ERROR_SUCCESS)
911 WLog_ERR(TAG, "Failed to check FreeRDP event handles");
912
913 break;
914 }
915 if (!sendQueuedChannelData(pc))
916 break;
917 if (!sendQueuedServerMessages(pc))
918 break;
919 }
920
921 freerdp_disconnect(instance);
922
923end:
924 (void)pf_modules_run_hook(pdata->module, HOOK_TYPE_CLIENT_UNINIT_CONNECT, pdata, pc);
925
926 return 0;
927}
928
929WINPR_ATTR_NODISCARD
930static int pf_logon_error_info(freerdp* instance, UINT32 data, UINT32 type)
931{
932 const char* str_data = freerdp_get_logon_error_info_data(data);
933 const char* str_type = freerdp_get_logon_error_info_type(type);
934
935 if (!instance || !instance->context)
936 return -1;
937
938 WLog_INFO(TAG, "Logon Error Info %s [%s]", str_data, str_type);
939 return 1;
940}
941
942static void pf_client_context_free(freerdp* instance, rdpContext* context)
943{
944 pClientContext* pc = (pClientContext*)context;
945 WINPR_UNUSED(instance);
946
947 if (!pc)
948 return;
949
950 pc->sendChannelData = nullptr;
951 Queue_Free(pc->cached_server_channel_data);
952 MessageQueue_Free(pc->server_messages);
953 Stream_Free(pc->remote_pem, TRUE);
954 free(pc->remote_hostname);
955 free(pc->computerName.v);
956 HashTable_Free(pc->interceptContextMap);
957}
958
959WINPR_ATTR_NODISCARD
960static BOOL pf_client_try_compare_hash(rdpCertificate* cert, const char* hash,
961 const char* TargetCertHash, BOOL useSeparator)
962{
963 WINPR_ASSERT(cert);
964 WINPR_ASSERT(TargetCertHash);
965
966 BOOL rc = FALSE;
967 char* got = freerdp_certificate_get_fingerprint_by_hash_ex(cert, hash, useSeparator);
968 if (got)
969 rc = _stricmp(TargetCertHash, got) == 0;
970
971 winpr_zfree(got);
972 return rc;
973}
974
975WINPR_ATTR_MALLOC(freerdp_certificate_free, 1)
976static rdpCertificate* fromPem(const char* policy, pClientContext* pc, const char* hostname,
977 UINT16 port, const BYTE* data, size_t length)
978{
979 if (!data || (length == 0))
980 {
981 PROXY_LOG_WARN(TAG, pc, "certificate policy %s: empty PEM, deny access to %s:%" PRIu16,
982 policy, hostname, port);
983 return nullptr;
984 }
985 char* copy = strndup((const char*)data, length);
986 if (!copy)
987 {
988 PROXY_LOG_WARN(TAG, pc,
989 "certificate policy %s: remote PEM clone failed, deny access to %s:%" PRIu16,
990 policy, hostname, port);
991 return nullptr;
992 }
993 rdpCertificate* cert = freerdp_certificate_new_from_pem(copy);
994 winpr_znfree(copy, length);
995 if (!cert)
996 {
997 PROXY_LOG_WARN(
998 TAG, pc, "certificate policy %s: remote PEM parsing failed, deny access to %s:%" PRIu16,
999 policy, hostname, port);
1000 }
1001 return cert;
1002}
1003
1004WINPR_ATTR_NODISCARD
1005static int pf_client_verify_strict(pClientContext* pc, const proxyConfig* config,
1006 const char* hostname, UINT16 port, const BYTE* data,
1007 size_t length, BOOL hostMustMatch)
1008{
1009 WINPR_ASSERT(pc);
1010 WINPR_ASSERT(config);
1011
1012 char* policy = _strdup(pf_config_policy_to_str(config->TargetCertPolicy));
1013 if (!policy)
1014 return 0;
1015 const size_t len = strlen(policy);
1016 for (size_t x = 0; x < len; x++)
1017 policy[x] = WINPR_ASSERTING_INT_CAST(char, toupper(policy[x]));
1018
1019 rdpCertificate* cert = fromPem(policy, pc, hostname, port, data, length);
1020 if (!cert)
1021 {
1022 free(policy);
1023 return 0;
1024 }
1025 BOOL rc = freerdp_certificate_verify(cert, config->CertStorePath);
1026 if (rc && hostMustMatch)
1027 {
1028 size_t hostlen = 0;
1029 if (hostname)
1030 hostlen = strlen(hostname);
1031 rc = freerdp_certificate_matches_hostname(cert, hostname, hostlen);
1032 if (!rc)
1033 PROXY_LOG_WARN(TAG, pc,
1034 "certificate policy %s: hostname mismatch, deny access to %s:%" PRIu16,
1035 policy, hostname, port);
1036 }
1037 else
1038 PROXY_LOG_WARN(TAG, pc,
1039 "certificate policy %s: certificate not valid, deny access to %s:%" PRIu16,
1040 policy, hostname, port);
1041 freerdp_certificate_free(cert);
1042 free(policy);
1043 return rc ? 1 : 0;
1044}
1045
1046WINPR_ATTR_NODISCARD
1047static int pf_client_verify_pinned(pClientContext* pc, const char* hostname, UINT16 port,
1048 const BYTE* data, size_t length)
1049{
1050 int rc = 0;
1051 WINPR_ASSERT(pc);
1052 WINPR_ASSERT(pc->pdata);
1053
1054 pServerContext* ps = proxy_data_get_server_context(pc->pdata);
1055 WINPR_ASSERT(ps);
1056 WINPR_ASSERT(ps->pdata);
1057 const proxyConfig* config = ps->pdata->config;
1058 WINPR_ASSERT(config);
1059
1060 rdpCertificate* cert = fromPem("PINNED", pc, hostname, port, data, length);
1061 if (!cert)
1062 return 0;
1063
1064 if (config->TargetCertPEM && (config->TargetCertPEMLength > 0))
1065 {
1066 rdpCertificate* tcert = freerdp_certificate_new_from_pem(config->TargetCertPEM);
1067 if (tcert)
1068 {
1069 char* remote = freerdp_certificate_get_fingerprint_by_hash(cert, "sha512");
1070 char* target = freerdp_certificate_get_fingerprint_by_hash(tcert, "sha512");
1071 if (remote && target)
1072 {
1073 const BOOL match = strcmp(remote, target) == 0;
1074 if (!match)
1075 PROXY_LOG_WARN(
1076 TAG, pc,
1077 "certificate policy PINNED: PEM mismatch, deny access to %s:%" PRIu16,
1078 hostname, port);
1079 else
1080 PROXY_LOG_INFO(
1081 TAG, pc,
1082 "certificate policy PINNED: PEM matches, allow access to %s:%" PRIu16,
1083 hostname, port);
1084 rc = match ? 1 : 0;
1085 }
1086 else
1087 {
1088 PROXY_LOG_WARN(TAG, pc,
1089 "certificate policy PINNED: PEM hashing failed (remote=%p, "
1090 "target=%p), deny accessto %s:%" PRIu16,
1091 (void*)remote, (void*)target, hostname, port);
1092 }
1093 winpr_zfree(remote);
1094 winpr_zfree(target);
1095
1096 freerdp_certificate_free(tcert);
1097 }
1098 else
1099 {
1100 PROXY_LOG_WARN(
1101 TAG, pc,
1102 "certificate policy PINNED: config PEM parsing failed, deny access to %s:%" PRIu16,
1103 hostname, port);
1104 }
1105 }
1106
1107 else if (config->TargetCertHash)
1108 {
1109 char* str = _strdup(config->TargetCertHash);
1110 if (str)
1111 {
1112 char* hash = strchr(str, ':');
1113 if (hash)
1114 *hash++ = '\0';
1115
1116 BOOL success = pf_client_try_compare_hash(cert, str, hash, FALSE);
1117 if (!success)
1118 success = pf_client_try_compare_hash(cert, str, hash, TRUE);
1119 if (success)
1120 rc = 1;
1121 }
1122
1123 winpr_zfree(str);
1124 if (rc == 1)
1125 PROXY_LOG_INFO(TAG, pc,
1126 "certificate policy PINNED: hash matches, allow access to %s:%" PRIu16,
1127 hostname, port);
1128 else
1129 PROXY_LOG_WARN(
1130 TAG, pc,
1131 "certificate policy PINNED: certificate hash mismatch, deny access to %s:%" PRIu16,
1132 hostname, port);
1133 }
1134 else
1135 {
1136 PROXY_LOG_WARN(TAG, pc,
1137 "certificate policy PINNED: neigter PEM nor HASH set for "
1138 "comparison, deny access to %s:%" PRIu16,
1139 hostname, port);
1140 }
1141
1142 freerdp_certificate_free(cert);
1143 return rc;
1144}
1145
1146WINPR_ATTR_NODISCARD
1147static int pf_client_verify_by_policy(pClientContext* pc, const char* hostname, UINT16 port,
1148 const BYTE* data, size_t length)
1149{
1150 WINPR_ASSERT(pc);
1151 WINPR_ASSERT(pc->pdata);
1152
1153 pServerContext* ps = proxy_data_get_server_context(pc->pdata);
1154 WINPR_ASSERT(ps);
1155 WINPR_ASSERT(ps->pdata);
1156 const proxyConfig* config = ps->pdata->config;
1157 WINPR_ASSERT(config);
1158
1159 switch (config->TargetCertPolicy)
1160 {
1161 case FREERDP_PROXY_CERT_POLICY_VERIFIED_HOST_MATCH:
1162 return pf_client_verify_strict(pc, config, hostname, port, data, length, TRUE);
1163 case FREERDP_PROXY_CERT_POLICY_VERIFIED:
1164 return pf_client_verify_strict(pc, config, hostname, port, data, length, FALSE);
1165 case FREERDP_PROXY_CERT_POLICY_PINNED:
1166 return pf_client_verify_pinned(pc, hostname, port, data, length);
1167 case FREERDP_PROXY_CERT_POLICY_ALLOW:
1168 PROXY_LOG_INFO(TAG, pc, "certificate policy ALLOW: allow access to %s:%" PRIu16,
1169 hostname, port);
1170 return 1;
1171 case FREERDP_PROXY_CERT_POLICY_DENY:
1172 default:
1173 PROXY_LOG_WARN(TAG, pc, "certificate policy DENY: deny access to %s:%" PRIu16, hostname,
1174 port);
1175 return 0;
1176 }
1177}
1178
1179WINPR_ATTR_NODISCARD
1180static int pf_client_verify_X509_certificate(freerdp* instance, const BYTE* data, size_t length,
1181 const char* hostname, UINT16 port, DWORD flags)
1182{
1183 WINPR_ASSERT(instance);
1184 WINPR_ASSERT(data);
1185 WINPR_ASSERT(length > 0);
1186 WINPR_ASSERT(hostname);
1187
1188 pClientContext* pc = (pClientContext*)instance->context;
1189
1190 Stream_ResetPosition(pc->remote_pem);
1191 if (!Stream_EnsureRemainingCapacity(pc->remote_pem, length))
1192 return 0;
1193
1194 free(pc->remote_hostname);
1195 pc->remote_hostname = nullptr;
1196
1197 if (length > 0)
1198 Stream_Write(pc->remote_pem, data, length);
1199 if (!Stream_EnsureRemainingCapacity(pc->remote_pem, 1))
1200 return 0;
1201 Stream_Zero(pc->remote_pem, 1);
1202
1203 if (hostname)
1204 pc->remote_hostname = _strdup(hostname);
1205 pc->remote_port = port;
1206 pc->remote_flags = flags;
1207
1208 Stream_SealLength(pc->remote_pem);
1209
1210 /* Allow modules to override the default target cert policy */
1211 if (pf_modules_run_hook(pc->pdata->module, HOOK_TYPE_CLIENT_VERIFY_X509, pc->pdata, pc))
1212 {
1213 PROXY_LOG_INFO(TAG, pc, "certificate policy: certificate accepted by plugin hook");
1214 return 1;
1215 }
1216
1217 return pf_client_verify_by_policy(pc, hostname, port, data, length);
1218}
1219
1220WINPR_ATTR_NODISCARD
1221static BOOL pf_client_choose_smartcard(WINPR_ATTR_UNUSED freerdp* instance,
1222 WINPR_ATTR_UNUSED SmartcardCertInfo** cert_list,
1223 WINPR_ATTR_UNUSED DWORD count, DWORD* choice,
1224 WINPR_ATTR_UNUSED BOOL gateway)
1225{
1226 if (count < 1)
1227 return FALSE;
1228 *choice = 0;
1229 return TRUE;
1230}
1231
1232WINPR_ATTR_NODISCARD
1233static BOOL pf_client_authenticate_ex(WINPR_ATTR_UNUSED freerdp* instance,
1234 WINPR_ATTR_UNUSED char** username,
1235 WINPR_ATTR_UNUSED char** password,
1236 WINPR_ATTR_UNUSED char** domain, rdp_auth_reason reason)
1237{
1238 WINPR_ASSERT(instance);
1239 WINPR_ASSERT(username);
1240 WINPR_ASSERT(password);
1241 WINPR_ASSERT(domain);
1242
1243 /* Here just return success, if the remote does require some non empty credentials
1244 * then it will fail, otherwise a login prompt will be shown. */
1245 switch (reason)
1246 {
1247 case AUTH_RDSTLS:
1248 case AUTH_NLA:
1249 case AUTH_TLS:
1250 case AUTH_RDP:
1251 case AUTH_SMARTCARD_PIN: /* in this case password is pin code */
1252 case GW_AUTH_HTTP:
1253 case GW_AUTH_RDG:
1254 case GW_AUTH_RPC:
1255 return TRUE;
1256 default:
1257 return FALSE;
1258 }
1259}
1260
1261WINPR_ATTR_NODISCARD
1262static BOOL pf_client_present_gateway_message(WINPR_ATTR_UNUSED freerdp* instance,
1263 WINPR_ATTR_UNUSED UINT32 type,
1264 WINPR_ATTR_UNUSED BOOL isDisplayMandatory,
1265 WINPR_ATTR_UNUSED BOOL isConsentMandatory,
1266 WINPR_ATTR_UNUSED size_t length,
1267 WINPR_ATTR_UNUSED const WCHAR* message)
1268{
1269 WLog_WARN(TAG, "TODO: Implement gateway message forwarding");
1270 return TRUE;
1271}
1272
1273void channel_data_free(void* obj)
1274{
1275 union
1276 {
1277 const void* cpv;
1278 void* pv;
1279 } cnv;
1280 proxyChannelDataEventInfo* dst = obj;
1281 if (dst)
1282 {
1283 cnv.cpv = dst->data;
1284 free(cnv.pv);
1285
1286 cnv.cpv = dst->channel_name;
1287 free(cnv.pv);
1288 free(dst);
1289 }
1290}
1291
1292WINPR_ATTR_MALLOC(channel_data_free, 1)
1293WINPR_ATTR_NODISCARD
1294static void* channel_data_copy(const void* obj)
1295{
1296 union
1297 {
1298 const void* cpv;
1299 void* pv;
1300 } cnv;
1301 const proxyChannelDataEventInfo* src = obj;
1302 proxyChannelDataEventInfo* dst = nullptr;
1303
1304 WINPR_ASSERT(src);
1305
1306 dst = calloc(1, sizeof(proxyChannelDataEventInfo));
1307 if (!dst)
1308 goto fail;
1309
1310 *dst = *src;
1311 if (src->channel_name)
1312 {
1313 dst->channel_name = _strdup(src->channel_name);
1314 if (!dst->channel_name)
1315 goto fail;
1316 }
1317 dst->data = malloc(src->data_len);
1318 if (!dst->data)
1319 goto fail;
1320
1321 cnv.cpv = dst->data;
1322 memcpy(cnv.pv, src->data, src->data_len);
1323 return dst;
1324
1325fail:
1326 channel_data_free(dst);
1327 return nullptr;
1328}
1329
1330WINPR_ATTR_NODISCARD
1331static BOOL pf_client_client_new(freerdp* instance, rdpContext* context)
1332{
1333 wObject* obj = nullptr;
1334 pClientContext* pc = (pClientContext*)context;
1335
1336 if (!instance || !context)
1337 return FALSE;
1338
1339 instance->LoadChannels = pf_client_load_channels;
1340 instance->PreConnect = pf_client_pre_connect;
1341 instance->PostConnect = pf_client_post_connect;
1342 instance->PostDisconnect = pf_client_post_disconnect;
1343 instance->Redirect = pf_client_redirect;
1344 instance->LogonErrorInfo = pf_logon_error_info;
1345 instance->GetAccessToken = nullptr;
1346 instance->RetryDialog = nullptr;
1347 instance->VerifyCertificateEx = nullptr;
1348 instance->VerifyChangedCertificateEx = nullptr;
1349 instance->VerifyX509Certificate = pf_client_verify_X509_certificate;
1350 instance->AuthenticateEx = pf_client_authenticate_ex;
1351 instance->ChooseSmartcard = pf_client_choose_smartcard;
1352 instance->PresentGatewayMessage = pf_client_present_gateway_message;
1353
1354 pc->remote_pem = Stream_New(nullptr, 4096);
1355 if (!pc->remote_pem)
1356 return FALSE;
1357
1358 pc->sendChannelData = pf_client_send_channel_data;
1359 pc->cached_server_channel_data = Queue_New(TRUE, -1, -1);
1360 if (!pc->cached_server_channel_data)
1361 return FALSE;
1362 obj = Queue_Object(pc->cached_server_channel_data);
1363 WINPR_ASSERT(obj);
1364 obj->fnObjectNew = channel_data_copy;
1365 obj->fnObjectFree = channel_data_free;
1366
1367 pc->server_messages = MessageQueue_New(nullptr);
1368 if (!pc->server_messages)
1369 return FALSE;
1370 obj = MessageQueue_Object(pc->server_messages);
1371 WINPR_ASSERT(obj);
1372 obj->fnObjectFree = server_message_free;
1373
1374 pc->interceptContextMap = HashTable_New(FALSE);
1375 if (!pc->interceptContextMap)
1376 return FALSE;
1377
1378 if (!HashTable_SetupForStringData(pc->interceptContextMap, FALSE))
1379 return FALSE;
1380
1381 obj = HashTable_ValueObject(pc->interceptContextMap);
1382 WINPR_ASSERT(obj);
1383 obj->fnObjectFree = intercept_context_entry_free;
1384
1385 return TRUE;
1386}
1387
1388WINPR_ATTR_NODISCARD
1389static int pf_client_client_stop(rdpContext* context)
1390{
1391 pClientContext* pc = (pClientContext*)context;
1392 proxyData* pdata = nullptr;
1393
1394 WINPR_ASSERT(pc);
1395 pdata = pc->pdata;
1396 WINPR_ASSERT(pdata);
1397
1398 PROXY_LOG_DBG(TAG, pc, "aborting client connection");
1399 proxy_data_abort_connect(pdata);
1400 freerdp_abort_connect_context(context);
1401
1402 return 0;
1403}
1404
1405int RdpClientEntry(RDP_CLIENT_ENTRY_POINTS* pEntryPoints)
1406{
1407 WINPR_ASSERT(pEntryPoints);
1408
1409 ZeroMemory(pEntryPoints, sizeof(RDP_CLIENT_ENTRY_POINTS));
1410 pEntryPoints->Version = RDP_CLIENT_INTERFACE_VERSION;
1411 pEntryPoints->Size = sizeof(RDP_CLIENT_ENTRY_POINTS_V1);
1412 pEntryPoints->ContextSize = sizeof(pClientContext);
1413 /* Client init and finish */
1414 pEntryPoints->ClientNew = pf_client_client_new;
1415 pEntryPoints->ClientFree = pf_client_context_free;
1416 pEntryPoints->ClientStop = pf_client_client_stop;
1417 return 0;
1418}
1419
1423DWORD WINAPI pf_client_start(LPVOID arg)
1424{
1425 DWORD rc = 1;
1426 pClientContext* pc = (pClientContext*)arg;
1427
1428 WINPR_ASSERT(pc);
1429 if (freerdp_client_start(&pc->cctx.context) == 0)
1430 rc = pf_client_thread_proc(pc);
1431 freerdp_client_stop(&pc->cctx.context);
1432 return rc;
1433}
WINPR_ATTR_NODISCARD FREERDP_API const char * pf_config_policy_to_str(FreeRDP_ProxyCertPolicy policy)
Convert a FreeRDP_ProxyCertPolicy value to a string.
Definition pf_config.c:1641
WINPR_ATTR_NODISCARD FREERDP_API const char * freerdp_settings_get_string(const rdpSettings *settings, FreeRDP_Settings_Keys_String id)
Returns a immutable string settings value.
WINPR_ATTR_NODISCARD FREERDP_API BOOL freerdp_settings_set_bool(rdpSettings *settings, FreeRDP_Settings_Keys_Bool id, BOOL val)
Sets a BOOL settings value.
WINPR_ATTR_NODISCARD FREERDP_API void * freerdp_settings_get_pointer_writable(rdpSettings *settings, FreeRDP_Settings_Keys_Pointer id)
Returns a mutable pointer settings value.
WINPR_ATTR_NODISCARD FREERDP_API BOOL freerdp_settings_set_uint32(rdpSettings *settings, FreeRDP_Settings_Keys_UInt32 id, UINT32 val)
Sets a UINT32 settings value.
WINPR_ATTR_NODISCARD FREERDP_API BOOL freerdp_settings_set_pointer_len(rdpSettings *settings, FreeRDP_Settings_Keys_Pointer id, const void *data, size_t len)
Set a pointer to value data.
WINPR_ATTR_NODISCARD FREERDP_API UINT32 freerdp_settings_get_uint32(const rdpSettings *settings, FreeRDP_Settings_Keys_UInt32 id)
Returns a UINT32 settings value.
WINPR_ATTR_NODISCARD FREERDP_API BOOL freerdp_settings_set_string(rdpSettings *settings, FreeRDP_Settings_Keys_String id, const char *val)
Sets a string settings value. The param is copied.
WINPR_ATTR_NODISCARD FREERDP_API BOOL freerdp_settings_get_bool(const rdpSettings *settings, FreeRDP_Settings_Keys_Bool id)
Returns a boolean settings value.
This struct contains function pointer to initialize/free objects.
Definition collections.h:52
OBJECT_FREE_FN fnObjectFree
Definition collections.h:59
WINPR_ATTR_NODISCARD OBJECT_NEW_FN fnObjectNew
Definition collections.h:54