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