FreeRDP
Loading...
Searching...
No Matches
pf_client.c
1
24#include <winpr/assert.h>
25#include <winpr/cast.h>
26
27#include <freerdp/config.h>
28
29#include <freerdp/freerdp.h>
30#include <freerdp/gdi/gdi.h>
31#include <freerdp/client/cmdline.h>
32
33#include <freerdp/server/proxy/proxy_log.h>
34#include <freerdp/channels/drdynvc.h>
35#include <freerdp/channels/encomsp.h>
36#include <freerdp/channels/rdpdr.h>
37#include <freerdp/channels/rdpsnd.h>
38#include <freerdp/channels/cliprdr.h>
39#include <freerdp/channels/channels.h>
40
41#include "pf_client.h"
42#include "pf_channel.h"
43#include <freerdp/server/proxy/proxy_context.h>
44#include "pf_update.h"
45#include "pf_input.h"
46#include <freerdp/server/proxy/proxy_config.h>
47#include "proxy_modules.h"
48#include "pf_utils.h"
49#include "channels/pf_channel_rdpdr.h"
50#include "channels/pf_channel_smartcard.h"
51
52#define TAG PROXY_TAG("client")
53
54static void channel_data_free(void* obj);
55
56WINPR_ATTR_NODISCARD
57static BOOL proxy_server_reactivate(rdpContext* ps, const rdpContext* pc)
58{
59 WINPR_ASSERT(ps);
60 WINPR_ASSERT(pc);
61
62 if (!pf_context_copy_settings(ps->settings, pc->settings))
63 return FALSE;
64
65 /*
66 * DesktopResize causes internal function rdp_server_reactivate to be called,
67 * which causes the reactivation.
68 */
69 WINPR_ASSERT(ps->update);
70 return (ps->update->DesktopResize(ps));
71}
72
73static void pf_client_on_error_info(void* ctx, const ErrorInfoEventArgs* e)
74{
75 pClientContext* pc = (pClientContext*)ctx;
76 pServerContext* ps = nullptr;
77
78 WINPR_ASSERT(pc);
79 WINPR_ASSERT(pc->pdata);
80 WINPR_ASSERT(e);
81 ps = pc->pdata->ps;
82 WINPR_ASSERT(ps);
83
84 if (e->code == ERRINFO_NONE)
85 return;
86
87 PROXY_LOG_WARN(TAG, pc, "received ErrorInfo PDU. code=0x%08" PRIu32 ", message: %s", e->code,
88 freerdp_get_error_info_string(e->code));
89
90 /* forward error back to client */
91 freerdp_set_error_info(ps->context.rdp, e->code);
92 if (!freerdp_send_error_info(ps->context.rdp))
93 {
94 PROXY_LOG_WARN(TAG, pc, "[fail] reply ErrorInfo PDU. code=0x%08" PRIu32 ", message: %s",
95 e->code, freerdp_get_error_info_string(e->code));
96 }
97}
98
99static void pf_client_on_activated(void* ctx, WINPR_ATTR_UNUSED const ActivatedEventArgs* e)
100{
101 pClientContext* pc = (pClientContext*)ctx;
102 pServerContext* ps = nullptr;
103 freerdp_peer* peer = nullptr;
104
105 WINPR_ASSERT(pc);
106 WINPR_ASSERT(pc->pdata);
107 WINPR_ASSERT(e);
108
109 ps = pc->pdata->ps;
110 WINPR_ASSERT(ps);
111 peer = ps->context.peer;
112 WINPR_ASSERT(peer);
113 WINPR_ASSERT(peer->context);
114
115 PROXY_LOG_INFO(TAG, pc, "client activated, registering server input callbacks");
116
117 /* Register server input/update callbacks only after proxy client is fully activated */
118 pf_server_register_input_callbacks(peer->context->input);
119 pf_server_register_update_callbacks(peer->context->update);
120}
121
122WINPR_ATTR_NODISCARD
123static BOOL pf_client_load_rdpsnd(pClientContext* pc)
124{
125 rdpContext* context = (rdpContext*)pc;
126
127 WINPR_ASSERT(pc);
128 WINPR_ASSERT(pc->pdata);
129 /*
130 * if AudioOutput is enabled in proxy and client connected with rdpsnd, use proxy as rdpsnd
131 * backend. Otherwise, use sys:fake.
132 */
133 if (!freerdp_static_channel_collection_find(context->settings, RDPSND_CHANNEL_NAME))
134 {
135 const char* params[2] = { RDPSND_CHANNEL_NAME, "sys:fake" };
136
137 if (!freerdp_client_add_static_channel(context->settings, ARRAYSIZE(params), params))
138 return FALSE;
139 }
140
141 return TRUE;
142}
143
144WINPR_ATTR_NODISCARD
145static BOOL pf_client_use_peer_load_balance_info(pClientContext* pc)
146{
147 pServerContext* ps = nullptr;
148 rdpSettings* settings = nullptr;
149 DWORD lb_info_len = 0;
150 const char* lb_info = nullptr;
151
152 WINPR_ASSERT(pc);
153 WINPR_ASSERT(pc->pdata);
154 ps = pc->pdata->ps;
155 WINPR_ASSERT(ps);
156 settings = pc->context.settings;
157 WINPR_ASSERT(settings);
158
159 lb_info = freerdp_nego_get_routing_token(&ps->context, &lb_info_len);
160 if (!lb_info)
161 return TRUE;
162
163 return freerdp_settings_set_pointer_len(settings, FreeRDP_LoadBalanceInfo, lb_info,
164 lb_info_len);
165}
166
167WINPR_ATTR_NODISCARD
168static BOOL str_is_empty(const char* str)
169{
170 if (!str)
171 return TRUE;
172 if (strlen(str) == 0)
173 return TRUE;
174 return FALSE;
175}
176
177WINPR_ATTR_NODISCARD
178static BOOL pf_client_use_proxy_smartcard_auth(const rdpSettings* settings)
179{
180 BOOL enable = freerdp_settings_get_bool(settings, FreeRDP_SmartcardLogon);
181 const char* key = freerdp_settings_get_string(settings, FreeRDP_SmartcardPrivateKey);
182 const char* cert = freerdp_settings_get_string(settings, FreeRDP_SmartcardCertificate);
183
184 if (!enable)
185 return FALSE;
186
187 if (str_is_empty(key))
188 return FALSE;
189
190 if (str_is_empty(cert))
191 return FALSE;
192
193 return TRUE;
194}
195
196WINPR_ATTR_NODISCARD
197static BOOL pf_client_pre_connect(freerdp* instance)
198{
199 pClientContext* pc = nullptr;
200 pServerContext* ps = nullptr;
201 const proxyConfig* config = nullptr;
202 rdpSettings* settings = nullptr;
203
204 WINPR_ASSERT(instance);
205 pc = (pClientContext*)instance->context;
206 WINPR_ASSERT(pc);
207 WINPR_ASSERT(pc->pdata);
208 ps = pc->pdata->ps;
209 WINPR_ASSERT(ps);
210 WINPR_ASSERT(ps->pdata);
211 config = ps->pdata->config;
212 WINPR_ASSERT(config);
213 settings = instance->context->settings;
214 WINPR_ASSERT(settings);
215
216 /*
217 * as the client's settings are copied from the server's, GlyphSupportLevel might not be
218 * GLYPH_SUPPORT_NONE. the proxy currently do not support GDI & GLYPH_SUPPORT_CACHE, so
219 * GlyphCacheSupport must be explicitly set to GLYPH_SUPPORT_NONE.
220 *
221 * Also, OrderSupport need to be zeroed, because it is currently not supported.
222 */
223 if (!freerdp_settings_set_uint32(settings, FreeRDP_GlyphSupportLevel, GLYPH_SUPPORT_NONE))
224 return FALSE;
225
226 void* OrderSupport = freerdp_settings_get_pointer_writable(settings, FreeRDP_OrderSupport);
227 ZeroMemory(OrderSupport, 32);
228
229 if (WTSVirtualChannelManagerIsChannelJoined(ps->vcm, DRDYNVC_SVC_CHANNEL_NAME))
230 {
231 if (!freerdp_settings_set_bool(settings, FreeRDP_SupportDynamicChannels, TRUE))
232 return FALSE;
233 }
234
235 /* Multimon */
236 if (!freerdp_settings_set_bool(settings, FreeRDP_UseMultimon, TRUE))
237 return FALSE;
238
239 /* Sound */
240 if (!freerdp_settings_set_bool(settings, FreeRDP_AudioCapture, config->AudioInput) ||
241 !freerdp_settings_set_bool(settings, FreeRDP_AudioPlayback, config->AudioOutput) ||
242 !freerdp_settings_set_bool(settings, FreeRDP_DeviceRedirection,
243 config->DeviceRedirection) ||
244 !freerdp_settings_set_bool(settings, FreeRDP_SupportDisplayControl,
245 config->DisplayControl) ||
246 !freerdp_settings_set_bool(settings, FreeRDP_MultiTouchInput, config->Multitouch))
247 return FALSE;
248
249 if (config->RemoteApp)
250 {
251 if (WTSVirtualChannelManagerIsChannelJoined(ps->vcm, RAIL_SVC_CHANNEL_NAME))
252 {
253 if (!freerdp_settings_set_bool(settings, FreeRDP_RemoteApplicationMode, TRUE))
254 return FALSE;
255 }
256 }
257
258 if (config->DeviceRedirection)
259 {
260 if (WTSVirtualChannelManagerIsChannelJoined(ps->vcm, RDPDR_SVC_CHANNEL_NAME))
261 {
262 if (!freerdp_settings_set_bool(settings, FreeRDP_DeviceRedirection, TRUE))
263 return FALSE;
264 }
265 }
266
267 /* Display control */
268 if (!freerdp_settings_set_bool(settings, FreeRDP_SupportDisplayControl, config->DisplayControl))
269 return FALSE;
270 if (!freerdp_settings_set_bool(settings, FreeRDP_DynamicResolutionUpdate,
271 config->DisplayControl))
272 return FALSE;
273
274 if (WTSVirtualChannelManagerIsChannelJoined(ps->vcm, ENCOMSP_SVC_CHANNEL_NAME))
275 {
276 if (!freerdp_settings_set_bool(settings, FreeRDP_EncomspVirtualChannel, TRUE))
277 return FALSE;
278 }
279
280 if (config->Clipboard)
281 {
282 if (WTSVirtualChannelManagerIsChannelJoined(ps->vcm, CLIPRDR_SVC_CHANNEL_NAME))
283 {
284 if (!freerdp_settings_set_bool(settings, FreeRDP_RedirectClipboard, config->Clipboard))
285 return FALSE;
286 }
287 }
288
289 if (!freerdp_settings_set_bool(settings, FreeRDP_AutoReconnectionEnabled, TRUE))
290 return FALSE;
291
292 if (PubSub_SubscribeErrorInfo(instance->context->pubSub, pf_client_on_error_info) < 0)
293 return FALSE;
294 if (PubSub_SubscribeActivated(instance->context->pubSub, pf_client_on_activated) < 0)
295 return FALSE;
296 if (!pf_client_use_peer_load_balance_info(pc))
297 return FALSE;
298
299 return pf_modules_run_hook(pc->pdata->module, HOOK_TYPE_CLIENT_PRE_CONNECT, pc->pdata, pc);
300}
301
303typedef struct
304{
305 pServerContext* ps;
306 const char* name;
307 UINT32 backId;
308} UpdateBackIdArgs;
309
310WINPR_ATTR_NODISCARD
311static BOOL updateBackIdFn(WINPR_ATTR_UNUSED const void* key, void* value, void* arg)
312{
313 pServerStaticChannelContext* current = (pServerStaticChannelContext*)value;
314 UpdateBackIdArgs* updateArgs = (UpdateBackIdArgs*)arg;
315
316 if (strcmp(updateArgs->name, current->channel_name) != 0)
317 return TRUE;
318
319 current->back_channel_id = updateArgs->backId;
320 if (!HashTable_Insert(updateArgs->ps->channelsByBackId, &current->back_channel_id, current))
321 {
322 WLog_ERR(TAG, "error inserting channel in channelsByBackId table");
323 }
324 return FALSE;
325}
326
327WINPR_ATTR_NODISCARD
328static BOOL pf_client_update_back_id(pServerContext* ps, const char* name, UINT32 backId)
329{
330 UpdateBackIdArgs res = { ps, name, backId };
331
332 return HashTable_Foreach(ps->channelsByFrontId, updateBackIdFn, &res) == FALSE;
333}
334
335WINPR_ATTR_NODISCARD
336static BOOL pf_client_load_channels(freerdp* instance)
337{
338 pClientContext* pc = nullptr;
339 pServerContext* ps = nullptr;
340 const proxyConfig* config = nullptr;
341 rdpSettings* settings = nullptr;
342
343 WINPR_ASSERT(instance);
344 pc = (pClientContext*)instance->context;
345 WINPR_ASSERT(pc);
346 WINPR_ASSERT(pc->pdata);
347 ps = pc->pdata->ps;
348 WINPR_ASSERT(ps);
349 WINPR_ASSERT(ps->pdata);
350 config = ps->pdata->config;
351 WINPR_ASSERT(config);
352 settings = instance->context->settings;
353 WINPR_ASSERT(settings);
358 PROXY_LOG_INFO(TAG, pc, "Loading addins");
359
360 if (!pf_client_load_rdpsnd(pc))
361 {
362 PROXY_LOG_ERR(TAG, pc, "Failed to load rdpsnd client");
363 return FALSE;
364 }
365
366 if (!pf_utils_is_passthrough(config))
367 {
368 if (!freerdp_client_load_addins(instance->context->channels, settings))
369 {
370 PROXY_LOG_ERR(TAG, pc, "Failed to load addins");
371 return FALSE;
372 }
373 }
374 else
375 {
376 if (!pf_channel_rdpdr_client_new(pc))
377 return FALSE;
378#if defined(WITH_PROXY_EMULATE_SMARTCARD)
379 if (!pf_channel_smartcard_client_new(pc))
380 return FALSE;
381#endif
382 /* Copy the current channel settings from the peer connection to the client. */
383 if (!freerdp_channels_from_mcs(settings, &ps->context))
384 return FALSE;
385
386 /* Filter out channels we do not want */
387 {
388 CHANNEL_DEF* channels = (CHANNEL_DEF*)freerdp_settings_get_pointer_array_writable(
389 settings, FreeRDP_ChannelDefArray, 0);
390 UINT32 size = freerdp_settings_get_uint32(settings, FreeRDP_ChannelCount);
391 UINT32 id = MCS_GLOBAL_CHANNEL_ID + 1;
392
393 WINPR_ASSERT(channels || (size == 0));
394
395 UINT32 x = 0;
396 for (; x < size;)
397 {
398 CHANNEL_DEF* cur = &channels[x];
399 proxyChannelDataEventInfo dev = WINPR_C_ARRAY_INIT;
400
401 dev.channel_name = cur->name;
402 dev.flags = cur->options;
403
404 /* Filter out channels blocked by config */
405 if (!pf_modules_run_filter(pc->pdata->module,
406 FILTER_TYPE_CLIENT_PASSTHROUGH_CHANNEL_CREATE, pc->pdata,
407 &dev))
408 {
409 const size_t s = size - MIN(size, x + 1);
410 memmove(cur, &cur[1], sizeof(CHANNEL_DEF) * s);
411 size--;
412 }
413 else
414 {
415 if (!pf_client_update_back_id(ps, cur->name, id++))
416 {
417 WLog_ERR(TAG, "unable to update backid for channel %s", cur->name);
418 return FALSE;
419 }
420 x++;
421 }
422 }
423
424 if (!freerdp_settings_set_uint32(settings, FreeRDP_ChannelCount, x))
425 return FALSE;
426 }
427 }
428 return pf_modules_run_hook(pc->pdata->module, HOOK_TYPE_CLIENT_LOAD_CHANNELS, pc->pdata, pc);
429}
430
431WINPR_ATTR_NODISCARD
432static BOOL pf_client_receive_channel_data_hook(freerdp* instance, UINT16 channelId,
433 const BYTE* xdata, size_t xsize, UINT32 flags,
434 size_t totalSize)
435{
436 pClientContext* pc = nullptr;
437 pServerContext* ps = nullptr;
438 proxyData* pdata = nullptr;
439 pServerStaticChannelContext* channel = nullptr;
440 UINT64 channelId64 = channelId;
441
442 WINPR_ASSERT(instance);
443 WINPR_ASSERT(xdata || (xsize == 0));
444
445 pc = (pClientContext*)instance->context;
446 WINPR_ASSERT(pc);
447 WINPR_ASSERT(pc->pdata);
448
449 ps = pc->pdata->ps;
450 WINPR_ASSERT(ps);
451
452 pdata = ps->pdata;
453 WINPR_ASSERT(pdata);
454
455 channel = HashTable_GetItemValue(ps->channelsByBackId, &channelId64);
456 if (!channel)
457 return TRUE;
458
459 WINPR_ASSERT(channel->onBackData);
460 switch (channel->onBackData(pdata, channel, xdata, xsize, flags, totalSize))
461 {
462 case PF_CHANNEL_RESULT_PASS:
463 /* Ignore messages for channels that can not be mapped.
464 * The client might not have enabled support for this specific channel,
465 * so just drop the message. */
466 if (channel->front_channel_id == 0)
467 return TRUE;
468
469 return ps->context.peer->SendChannelPacket(
470 ps->context.peer, WINPR_ASSERTING_INT_CAST(UINT16, channel->front_channel_id),
471 totalSize, flags, xdata, xsize);
472 case PF_CHANNEL_RESULT_DROP:
473 return TRUE;
474 case PF_CHANNEL_RESULT_ERROR:
475 default:
476 return FALSE;
477 }
478}
479
480WINPR_ATTR_NODISCARD
481static BOOL pf_client_on_server_heartbeat(freerdp* instance, BYTE period, BYTE count1, BYTE count2)
482{
483 pClientContext* pc = nullptr;
484 pServerContext* ps = nullptr;
485
486 WINPR_ASSERT(instance);
487 pc = (pClientContext*)instance->context;
488 WINPR_ASSERT(pc);
489 WINPR_ASSERT(pc->pdata);
490 ps = pc->pdata->ps;
491 WINPR_ASSERT(ps);
492
493 return freerdp_heartbeat_send_heartbeat_pdu(ps->context.peer, period, count1, count2);
494}
495
496WINPR_ATTR_NODISCARD
497static BOOL pf_client_send_channel_data(pClientContext* pc, const proxyChannelDataEventInfo* ev)
498{
499 WINPR_ASSERT(pc);
500 WINPR_ASSERT(ev);
501
502 return Queue_Enqueue(pc->cached_server_channel_data, ev);
503}
504
505WINPR_ATTR_NODISCARD
506static BOOL sendQueuedChannelData(pClientContext* pc)
507{
508 BOOL rc = TRUE;
509
510 WINPR_ASSERT(pc);
511
512 if (pc->connected)
513 {
514 proxyChannelDataEventInfo* ev = nullptr;
515
516 Queue_Lock(pc->cached_server_channel_data);
517 while (rc && (ev = Queue_Dequeue(pc->cached_server_channel_data)))
518 {
519 UINT16 channelId = 0;
520 WINPR_ASSERT(pc->context.instance);
521
522 channelId = freerdp_channels_get_id_by_name(pc->context.instance, ev->channel_name);
523 /* Ignore unmappable channels */
524 if ((channelId == 0) || (channelId == UINT16_MAX))
525 rc = TRUE;
526 else
527 {
528 WINPR_ASSERT(pc->context.instance->SendChannelPacket);
529 rc = pc->context.instance->SendChannelPacket(pc->context.instance, channelId,
530 ev->total_size, ev->flags, ev->data,
531 ev->data_len);
532 }
533 channel_data_free(ev);
534 }
535
536 Queue_Unlock(pc->cached_server_channel_data);
537 }
538
539 return rc;
540}
541
551WINPR_ATTR_NODISCARD
552static BOOL pf_client_post_connect(freerdp* instance)
553{
554 WINPR_ASSERT(instance);
555 rdpContext* context = instance->context;
556 WINPR_ASSERT(context);
557 rdpUpdate* update = context->update;
558 WINPR_ASSERT(update);
559 pClientContext* pc = (pClientContext*)context;
560 WINPR_ASSERT(pc);
561 WINPR_ASSERT(pc->pdata);
562 rdpContext* ps = (rdpContext*)pc->pdata->ps;
563 WINPR_ASSERT(ps);
564
565 if (!pf_modules_run_hook(pc->pdata->module, HOOK_TYPE_CLIENT_POST_CONNECT, pc->pdata, pc))
566 return FALSE;
567
568 if (!gdi_init(instance, PIXEL_FORMAT_BGRA32))
569 return FALSE;
570
571 WINPR_ASSERT(freerdp_settings_get_bool(context->settings, FreeRDP_SoftwareGdi));
572
573 pf_client_register_update_callbacks(update);
574
575 /* virtual channels receive data hook */
576 pc->client_receive_channel_data_original = instance->ReceiveChannelData;
577 instance->ReceiveChannelData = pf_client_receive_channel_data_hook;
578
579 instance->heartbeat->ServerHeartbeat = pf_client_on_server_heartbeat;
580
581 pc->connected = TRUE;
582
583 /* Send cached channel data */
584 if (!sendQueuedChannelData(pc))
585 return FALSE;
586
587 /*
588 * after the connection fully established and settings were negotiated with target server,
589 * send a reactivation sequence to the client with the negotiated settings. This way,
590 * settings are synchorinized between proxy's peer and and remote target.
591 */
592 return proxy_server_reactivate(ps, context);
593}
594
595/* This function is called whether a session ends by failure or success.
596 * Clean up everything allocated by pre_connect and post_connect.
597 */
598static void pf_client_post_disconnect(freerdp* instance)
599{
600 pClientContext* pc = nullptr;
601 proxyData* pdata = nullptr;
602
603 if (!instance)
604 return;
605
606 if (!instance->context)
607 return;
608
609 pc = (pClientContext*)instance->context;
610 WINPR_ASSERT(pc);
611 pdata = pc->pdata;
612 WINPR_ASSERT(pdata);
613
614#if defined(WITH_PROXY_EMULATE_SMARTCARD)
615 pf_channel_smartcard_client_free(pc);
616#endif
617
618 pf_channel_rdpdr_client_free(pc);
619
620 pc->connected = FALSE;
621 (void)pf_modules_run_hook(pc->pdata->module, HOOK_TYPE_CLIENT_POST_DISCONNECT, pc->pdata, pc);
622
623 PubSub_UnsubscribeErrorInfo(instance->context->pubSub, pf_client_on_error_info);
624 gdi_free(instance);
625
626 /* Only close the connection if NLA fallback process is done */
627 if (!pc->allow_next_conn_failure)
628 proxy_data_abort_connect(pdata);
629}
630
631WINPR_ATTR_NODISCARD
632static BOOL pf_client_redirect(freerdp* instance)
633{
634 if (!instance)
635 return FALSE;
636
637 if (!instance->context)
638 return FALSE;
639
640 pClientContext* pc = (pClientContext*)instance->context;
641 WINPR_ASSERT(pc);
642
643#if defined(WITH_PROXY_EMULATE_SMARTCARD)
644 pf_channel_smartcard_client_reset(pc);
645#endif
646 pf_channel_rdpdr_client_reset(pc);
647
648 return pf_modules_run_hook(pc->pdata->module, HOOK_TYPE_CLIENT_REDIRECT, pc->pdata, pc);
649}
650
651/*
652 * pf_client_should_retry_without_nla:
653 *
654 * returns TRUE if in case of connection failure, the client should try again without NLA.
655 * Otherwise, returns FALSE.
656 */
657WINPR_ATTR_NODISCARD
658static BOOL pf_client_should_retry_without_nla(pClientContext* pc)
659{
660 rdpSettings* settings = nullptr;
661 const proxyConfig* config = nullptr;
662
663 WINPR_ASSERT(pc);
664 WINPR_ASSERT(pc->pdata);
665 settings = pc->context.settings;
666 WINPR_ASSERT(settings);
667 config = pc->pdata->config;
668 WINPR_ASSERT(config);
669
670 if (!config->ClientAllowFallbackToTls ||
671 !freerdp_settings_get_bool(settings, FreeRDP_NlaSecurity))
672 return FALSE;
673
674 return config->ClientTlsSecurity || config->ClientRdpSecurity;
675}
676
677WINPR_ATTR_NODISCARD
678static BOOL pf_client_set_security_settings(pClientContext* pc)
679{
680 WINPR_ASSERT(pc);
681 WINPR_ASSERT(pc->pdata);
682 rdpSettings* settings = pc->context.settings;
683 WINPR_ASSERT(settings);
684 const proxyConfig* config = pc->pdata->config;
685 WINPR_ASSERT(config);
686
687 if (!freerdp_settings_set_bool(settings, FreeRDP_RdpSecurity, config->ClientRdpSecurity))
688 return FALSE;
689 if (!freerdp_settings_set_bool(settings, FreeRDP_TlsSecurity, config->ClientTlsSecurity))
690 return FALSE;
691 if (!freerdp_settings_set_bool(settings, FreeRDP_NlaSecurity, config->ClientNlaSecurity))
692 return FALSE;
693
694 if (pf_client_use_proxy_smartcard_auth(settings))
695 {
696 /* Smartcard authentication requires smartcard redirection to be enabled */
697 if (!freerdp_settings_set_bool(settings, FreeRDP_RedirectSmartCards, TRUE))
698 return FALSE;
699
700 /* Reset username/domain, we will get that info later from the sc cert */
701 if (!freerdp_settings_set_string(settings, FreeRDP_Username, nullptr))
702 return FALSE;
703 if (!freerdp_settings_set_string(settings, FreeRDP_Domain, nullptr))
704 return FALSE;
705 }
706
707 return TRUE;
708}
709
710WINPR_ATTR_NODISCARD
711static BOOL pf_client_connect_without_nla(pClientContext* pc)
712{
713 freerdp* instance = nullptr;
714 rdpSettings* settings = nullptr;
715
716 WINPR_ASSERT(pc);
717 instance = pc->context.instance;
718 WINPR_ASSERT(instance);
719
720 if (!freerdp_context_reset(instance))
721 return FALSE;
722
723 settings = pc->context.settings;
724 WINPR_ASSERT(settings);
725
726 /* If already disabled abort early. */
727 if (!freerdp_settings_get_bool(settings, FreeRDP_NlaSecurity))
728 return FALSE;
729
730 /* disable NLA */
731 if (!freerdp_settings_set_bool(settings, FreeRDP_NlaSecurity, FALSE))
732 return FALSE;
733
734 /* do not allow next connection failure */
735 pc->allow_next_conn_failure = FALSE;
736 return freerdp_connect(instance);
737}
738
739WINPR_ATTR_NODISCARD
740static BOOL pf_client_connect(freerdp* instance)
741{
742 pClientContext* pc = nullptr;
743 rdpSettings* settings = nullptr;
744 BOOL rc = FALSE;
745 BOOL retry = FALSE;
746
747 WINPR_ASSERT(instance);
748 pc = (pClientContext*)instance->context;
749 WINPR_ASSERT(pc);
750 settings = instance->context->settings;
751 WINPR_ASSERT(settings);
752
753 PROXY_LOG_INFO(TAG, pc, "connecting using client info: Username: %s, Domain: %s",
754 freerdp_settings_get_string(settings, FreeRDP_Username),
755 freerdp_settings_get_string(settings, FreeRDP_Domain));
756
757 if (!pf_client_set_security_settings(pc))
758 return FALSE;
759
760 if (pf_client_should_retry_without_nla(pc))
761 retry = pc->allow_next_conn_failure = TRUE;
762
763 PROXY_LOG_INFO(TAG, pc, "connecting using security settings: rdp=%d, tls=%d, nla=%d",
764 freerdp_settings_get_bool(settings, FreeRDP_RdpSecurity),
765 freerdp_settings_get_bool(settings, FreeRDP_TlsSecurity),
766 freerdp_settings_get_bool(settings, FreeRDP_NlaSecurity));
767
768 if (!freerdp_connect(instance))
769 {
770 if (!pf_modules_run_hook(pc->pdata->module, HOOK_TYPE_CLIENT_LOGIN_FAILURE, pc->pdata, pc))
771 goto out;
772
773 if (!retry)
774 goto out;
775
776 PROXY_LOG_ERR(TAG, pc, "failed to connect with NLA. retrying to connect without NLA");
777 if (!pf_client_connect_without_nla(pc))
778 {
779 PROXY_LOG_ERR(TAG, pc, "pf_client_connect_without_nla failed!");
780 goto out;
781 }
782 }
783
784 rc = TRUE;
785out:
786 pc->allow_next_conn_failure = FALSE;
787 return rc;
788}
789
795WINPR_ATTR_NODISCARD
796static DWORD WINAPI pf_client_thread_proc(pClientContext* pc)
797{
798 freerdp* instance = nullptr;
799 proxyData* pdata = nullptr;
800 DWORD nCount = 0;
801 DWORD status = 0;
802 HANDLE handles[MAXIMUM_WAIT_OBJECTS] = WINPR_C_ARRAY_INIT;
803
804 WINPR_ASSERT(pc);
805
806 instance = pc->context.instance;
807 WINPR_ASSERT(instance);
808
809 pdata = pc->pdata;
810 WINPR_ASSERT(pdata);
811 /*
812 * during redirection, freerdp's abort event might be overridden (reset) by the library, after
813 * the server set it in order to shutdown the connection. it means that the server might signal
814 * the client to abort, but the library code will override the signal and the client will
815 * continue its work instead of exiting. That's why the client must wait on `pdata->abort_event`
816 * too, which will never be modified by the library.
817 */
818 handles[nCount++] = pdata->abort_event;
819
820 if (!pf_modules_run_hook(pdata->module, HOOK_TYPE_CLIENT_INIT_CONNECT, pdata, pc))
821 {
822 proxy_data_abort_connect(pdata);
823 goto end;
824 }
825
826 if (!pf_client_connect(instance))
827 {
828 proxy_data_abort_connect(pdata);
829 goto end;
830 }
831 handles[nCount++] = Queue_Event(pc->cached_server_channel_data);
832
833 while (!freerdp_shall_disconnect_context(instance->context))
834 {
835 UINT32 tmp = freerdp_get_event_handles(instance->context, &handles[nCount],
836 ARRAYSIZE(handles) - nCount);
837
838 if (tmp == 0)
839 {
840 PROXY_LOG_ERR(TAG, pc, "freerdp_get_event_handles failed!");
841 break;
842 }
843
844 status = WaitForMultipleObjects(nCount + tmp, handles, FALSE, INFINITE);
845
846 if (status == WAIT_FAILED)
847 {
848 WLog_ERR(TAG, "WaitForMultipleObjects failed with %" PRIu32 "", status);
849 break;
850 }
851
852 /* abort_event triggered */
853 if (status == WAIT_OBJECT_0)
854 break;
855
856 if (freerdp_shall_disconnect_context(instance->context))
857 break;
858
859 if (proxy_data_shall_disconnect(pdata))
860 break;
861
862 if (!freerdp_check_event_handles(instance->context))
863 {
864 if (freerdp_get_last_error(instance->context) == FREERDP_ERROR_SUCCESS)
865 WLog_ERR(TAG, "Failed to check FreeRDP event handles");
866
867 break;
868 }
869 if (!sendQueuedChannelData(pc))
870 break;
871 }
872
873 freerdp_disconnect(instance);
874
875end:
876 (void)pf_modules_run_hook(pdata->module, HOOK_TYPE_CLIENT_UNINIT_CONNECT, pdata, pc);
877
878 return 0;
879}
880
881WINPR_ATTR_NODISCARD
882static int pf_logon_error_info(freerdp* instance, UINT32 data, UINT32 type)
883{
884 const char* str_data = freerdp_get_logon_error_info_data(data);
885 const char* str_type = freerdp_get_logon_error_info_type(type);
886
887 if (!instance || !instance->context)
888 return -1;
889
890 WLog_INFO(TAG, "Logon Error Info %s [%s]", str_data, str_type);
891 return 1;
892}
893
894static void pf_client_context_free(freerdp* instance, rdpContext* context)
895{
896 pClientContext* pc = (pClientContext*)context;
897 WINPR_UNUSED(instance);
898
899 if (!pc)
900 return;
901
902 pc->sendChannelData = nullptr;
903 Queue_Free(pc->cached_server_channel_data);
904 Stream_Free(pc->remote_pem, TRUE);
905 free(pc->remote_hostname);
906 free(pc->computerName.v);
907 HashTable_Free(pc->interceptContextMap);
908}
909
910WINPR_ATTR_NODISCARD
911static int pf_client_verify_X509_certificate(freerdp* instance, const BYTE* data, size_t length,
912 const char* hostname, UINT16 port, DWORD flags)
913{
914 pClientContext* pc = nullptr;
915
916 WINPR_ASSERT(instance);
917 WINPR_ASSERT(data);
918 WINPR_ASSERT(length > 0);
919 WINPR_ASSERT(hostname);
920
921 pc = (pClientContext*)instance->context;
922 WINPR_ASSERT(pc);
923
924 if (!Stream_EnsureCapacity(pc->remote_pem, length))
925 return 0;
926 Stream_SetPosition(pc->remote_pem, 0);
927
928 free(pc->remote_hostname);
929 pc->remote_hostname = nullptr;
930
931 if (length > 0)
932 Stream_Write(pc->remote_pem, data, length);
933
934 if (hostname)
935 pc->remote_hostname = _strdup(hostname);
936 pc->remote_port = port;
937 pc->remote_flags = flags;
938
939 Stream_SealLength(pc->remote_pem);
940 if (!pf_modules_run_hook(pc->pdata->module, HOOK_TYPE_CLIENT_VERIFY_X509, pc->pdata, pc))
941 return 0;
942 return 1;
943}
944
945void channel_data_free(void* obj)
946{
947 union
948 {
949 const void* cpv;
950 void* pv;
951 } cnv;
952 proxyChannelDataEventInfo* dst = obj;
953 if (dst)
954 {
955 cnv.cpv = dst->data;
956 free(cnv.pv);
957
958 cnv.cpv = dst->channel_name;
959 free(cnv.pv);
960 free(dst);
961 }
962}
963
964WINPR_ATTR_MALLOC(channel_data_free, 1)
965WINPR_ATTR_NODISCARD
966static void* channel_data_copy(const void* obj)
967{
968 union
969 {
970 const void* cpv;
971 void* pv;
972 } cnv;
973 const proxyChannelDataEventInfo* src = obj;
974 proxyChannelDataEventInfo* dst = nullptr;
975
976 WINPR_ASSERT(src);
977
978 dst = calloc(1, sizeof(proxyChannelDataEventInfo));
979 if (!dst)
980 goto fail;
981
982 *dst = *src;
983 if (src->channel_name)
984 {
985 dst->channel_name = _strdup(src->channel_name);
986 if (!dst->channel_name)
987 goto fail;
988 }
989 dst->data = malloc(src->data_len);
990 if (!dst->data)
991 goto fail;
992
993 cnv.cpv = dst->data;
994 memcpy(cnv.pv, src->data, src->data_len);
995 return dst;
996
997fail:
998 channel_data_free(dst);
999 return nullptr;
1000}
1001
1002WINPR_ATTR_NODISCARD
1003static BOOL pf_client_client_new(freerdp* instance, rdpContext* context)
1004{
1005 wObject* obj = nullptr;
1006 pClientContext* pc = (pClientContext*)context;
1007
1008 if (!instance || !context)
1009 return FALSE;
1010
1011 instance->LoadChannels = pf_client_load_channels;
1012 instance->PreConnect = pf_client_pre_connect;
1013 instance->PostConnect = pf_client_post_connect;
1014 instance->PostDisconnect = pf_client_post_disconnect;
1015 instance->Redirect = pf_client_redirect;
1016 instance->LogonErrorInfo = pf_logon_error_info;
1017 instance->VerifyX509Certificate = pf_client_verify_X509_certificate;
1018
1019 pc->remote_pem = Stream_New(nullptr, 4096);
1020 if (!pc->remote_pem)
1021 return FALSE;
1022
1023 pc->sendChannelData = pf_client_send_channel_data;
1024 pc->cached_server_channel_data = Queue_New(TRUE, -1, -1);
1025 if (!pc->cached_server_channel_data)
1026 return FALSE;
1027 obj = Queue_Object(pc->cached_server_channel_data);
1028 WINPR_ASSERT(obj);
1029 obj->fnObjectNew = channel_data_copy;
1030 obj->fnObjectFree = channel_data_free;
1031
1032 pc->interceptContextMap = HashTable_New(FALSE);
1033 if (!pc->interceptContextMap)
1034 return FALSE;
1035
1036 if (!HashTable_SetupForStringData(pc->interceptContextMap, FALSE))
1037 return FALSE;
1038
1039 obj = HashTable_ValueObject(pc->interceptContextMap);
1040 WINPR_ASSERT(obj);
1041 obj->fnObjectFree = intercept_context_entry_free;
1042
1043 return TRUE;
1044}
1045
1046WINPR_ATTR_NODISCARD
1047static int pf_client_client_stop(rdpContext* context)
1048{
1049 pClientContext* pc = (pClientContext*)context;
1050 proxyData* pdata = nullptr;
1051
1052 WINPR_ASSERT(pc);
1053 pdata = pc->pdata;
1054 WINPR_ASSERT(pdata);
1055
1056 PROXY_LOG_DBG(TAG, pc, "aborting client connection");
1057 proxy_data_abort_connect(pdata);
1058 freerdp_abort_connect_context(context);
1059
1060 return 0;
1061}
1062
1063int RdpClientEntry(RDP_CLIENT_ENTRY_POINTS* pEntryPoints)
1064{
1065 WINPR_ASSERT(pEntryPoints);
1066
1067 ZeroMemory(pEntryPoints, sizeof(RDP_CLIENT_ENTRY_POINTS));
1068 pEntryPoints->Version = RDP_CLIENT_INTERFACE_VERSION;
1069 pEntryPoints->Size = sizeof(RDP_CLIENT_ENTRY_POINTS_V1);
1070 pEntryPoints->ContextSize = sizeof(pClientContext);
1071 /* Client init and finish */
1072 pEntryPoints->ClientNew = pf_client_client_new;
1073 pEntryPoints->ClientFree = pf_client_context_free;
1074 pEntryPoints->ClientStop = pf_client_client_stop;
1075 return 0;
1076}
1077
1081DWORD WINAPI pf_client_start(LPVOID arg)
1082{
1083 DWORD rc = 1;
1084 pClientContext* pc = (pClientContext*)arg;
1085
1086 WINPR_ASSERT(pc);
1087 if (freerdp_client_start(&pc->context) == 0)
1088 rc = pf_client_thread_proc(pc);
1089 freerdp_client_stop(&pc->context);
1090 return rc;
1091}
FREERDP_API BOOL freerdp_settings_set_uint32(rdpSettings *settings, FreeRDP_Settings_Keys_UInt32 id, UINT32 val)
Sets a UINT32 settings value.
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 const char * freerdp_settings_get_string(const rdpSettings *settings, FreeRDP_Settings_Keys_String id)
Returns a immutable string settings value.
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 void * freerdp_settings_get_pointer_writable(rdpSettings *settings, FreeRDP_Settings_Keys_Pointer id)
Returns a mutable pointer settings value.
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_get_bool(const rdpSettings *settings, FreeRDP_Settings_Keys_Bool id)
Returns a boolean settings value.
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.
This struct contains function pointer to initialize/free objects.
Definition collections.h:52
OBJECT_FREE_FN fnObjectFree
Definition collections.h:58
OBJECT_NEW_FN fnObjectNew
Definition collections.h:53