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