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