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 return FALSE;
644
645 return config->ClientTlsSecurity || config->ClientRdpSecurity;
646}
647
648WINPR_ATTR_NODISCARD
649static BOOL pf_client_set_security_settings(pClientContext* pc)
650{
651 WINPR_ASSERT(pc);
652 WINPR_ASSERT(pc->pdata);
653 rdpSettings* settings = pc->cctx.context.settings;
654 WINPR_ASSERT(settings);
655 const proxyConfig* config = pc->pdata->config;
656 WINPR_ASSERT(config);
657
658 if (!freerdp_settings_set_bool(settings, FreeRDP_RdpSecurity, config->ClientRdpSecurity))
659 return FALSE;
660 if (!freerdp_settings_set_bool(settings, FreeRDP_TlsSecurity, config->ClientTlsSecurity))
661 return FALSE;
662 if (!freerdp_settings_set_bool(settings, FreeRDP_NlaSecurity, config->ClientNlaSecurity))
663 return FALSE;
664
665 if (pf_client_use_proxy_smartcard_auth(settings))
666 {
667 /* Smartcard authentication requires smartcard redirection to be enabled */
668 if (!freerdp_settings_set_bool(settings, FreeRDP_RedirectSmartCards, TRUE))
669 return FALSE;
670
671 /* Reset username/domain, we will get that info later from the sc cert */
672 if (!freerdp_settings_set_string(settings, FreeRDP_Username, nullptr))
673 return FALSE;
674 if (!freerdp_settings_set_string(settings, FreeRDP_Domain, nullptr))
675 return FALSE;
676 }
677
678 return TRUE;
679}
680
681WINPR_ATTR_NODISCARD
682static BOOL pf_client_connect_without_nla(pClientContext* pc)
683{
684 freerdp* instance = nullptr;
685 rdpSettings* settings = nullptr;
686
687 WINPR_ASSERT(pc);
688 instance = pc->cctx.context.instance;
689 WINPR_ASSERT(instance);
690
691 if (!freerdp_context_reset(instance))
692 return FALSE;
693
694 settings = pc->cctx.context.settings;
695 WINPR_ASSERT(settings);
696
697 /* If already disabled abort early. */
698 if (!freerdp_settings_get_bool(settings, FreeRDP_NlaSecurity))
699 return FALSE;
700
701 /* disable NLA */
702 if (!freerdp_settings_set_bool(settings, FreeRDP_NlaSecurity, FALSE))
703 return FALSE;
704
705 /* do not allow next connection failure */
706 pc->allow_next_conn_failure = FALSE;
707 return freerdp_connect(instance);
708}
709
710WINPR_ATTR_NODISCARD
711static BOOL pf_client_connect(freerdp* instance)
712{
713 pClientContext* pc = nullptr;
714 rdpSettings* settings = nullptr;
715 BOOL rc = FALSE;
716 BOOL retry = FALSE;
717
718 WINPR_ASSERT(instance);
719 pc = (pClientContext*)instance->context;
720 WINPR_ASSERT(pc);
721 settings = instance->context->settings;
722 WINPR_ASSERT(settings);
723
724 if (!pf_client_set_security_settings(pc))
725 return FALSE;
726
727 if (pf_client_should_retry_without_nla(pc))
728 retry = pc->allow_next_conn_failure = TRUE;
729
730 PROXY_LOG_INFO(TAG, pc, "connecting using client info: Username: %s, Domain: %s",
731 freerdp_settings_get_string(settings, FreeRDP_Username),
732 freerdp_settings_get_string(settings, FreeRDP_Domain));
733 PROXY_LOG_INFO(TAG, pc, "connecting using security settings: rdp=%d, tls=%d, nla=%d",
734 freerdp_settings_get_bool(settings, FreeRDP_RdpSecurity),
735 freerdp_settings_get_bool(settings, FreeRDP_TlsSecurity),
736 freerdp_settings_get_bool(settings, FreeRDP_NlaSecurity));
737
738 if (!freerdp_connect(instance))
739 {
740 if (!pf_modules_run_hook(pc->pdata->module, HOOK_TYPE_CLIENT_LOGIN_FAILURE, pc->pdata, pc))
741 goto out;
742
743 if (!retry)
744 goto out;
745
746 PROXY_LOG_ERR(TAG, pc, "failed to connect with NLA. retrying to connect without NLA");
747 if (!pf_client_connect_without_nla(pc))
748 {
749 PROXY_LOG_ERR(TAG, pc, "pf_client_connect_without_nla failed!");
750 goto out;
751 }
752 }
753
754 rc = TRUE;
755out:
756 pc->allow_next_conn_failure = FALSE;
757 return rc;
758}
759
765WINPR_ATTR_NODISCARD
766static DWORD WINAPI pf_client_thread_proc(pClientContext* pc)
767{
768 freerdp* instance = nullptr;
769 proxyData* pdata = nullptr;
770 DWORD nCount = 0;
771 DWORD status = 0;
772 HANDLE handles[MAXIMUM_WAIT_OBJECTS] = WINPR_C_ARRAY_INIT;
773
774 WINPR_ASSERT(pc);
775
776 instance = pc->cctx.context.instance;
777 WINPR_ASSERT(instance);
778
779 pdata = pc->pdata;
780 WINPR_ASSERT(pdata);
781 /*
782 * during redirection, freerdp's abort event might be overridden (reset) by the library, after
783 * the server set it in order to shutdown the connection. it means that the server might signal
784 * the client to abort, but the library code will override the signal and the client will
785 * continue its work instead of exiting. That's why the client must wait on `pdata->abort_event`
786 * too, which will never be modified by the library.
787 */
788 handles[nCount++] = pdata->abort_event;
789
790 if (!pf_modules_run_hook(pdata->module, HOOK_TYPE_CLIENT_INIT_CONNECT, pdata, pc))
791 {
792 proxy_data_abort_connect(pdata);
793 goto end;
794 }
795
796 if (!pf_client_connect(instance))
797 {
798 proxy_data_abort_connect(pdata);
799 goto end;
800 }
801 handles[nCount++] = Queue_Event(pc->cached_server_channel_data);
802
803 while (!freerdp_shall_disconnect_context(instance->context))
804 {
805 UINT32 tmp = freerdp_get_event_handles(instance->context, &handles[nCount],
806 ARRAYSIZE(handles) - nCount);
807
808 if (tmp == 0)
809 {
810 PROXY_LOG_ERR(TAG, pc, "freerdp_get_event_handles failed!");
811 break;
812 }
813
814 status = WaitForMultipleObjects(nCount + tmp, handles, FALSE, INFINITE);
815
816 if (status == WAIT_FAILED)
817 {
818 WLog_ERR(TAG, "WaitForMultipleObjects failed with %" PRIu32 "", status);
819 break;
820 }
821
822 /* abort_event triggered */
823 if (status == WAIT_OBJECT_0)
824 break;
825
826 if (freerdp_shall_disconnect_context(instance->context))
827 break;
828
829 if (proxy_data_shall_disconnect(pdata))
830 break;
831
832 if (!freerdp_check_event_handles(instance->context))
833 {
834 if (freerdp_get_last_error(instance->context) == FREERDP_ERROR_SUCCESS)
835 WLog_ERR(TAG, "Failed to check FreeRDP event handles");
836
837 break;
838 }
839 if (!sendQueuedChannelData(pc))
840 break;
841 }
842
843 freerdp_disconnect(instance);
844
845end:
846 (void)pf_modules_run_hook(pdata->module, HOOK_TYPE_CLIENT_UNINIT_CONNECT, pdata, pc);
847
848 return 0;
849}
850
851WINPR_ATTR_NODISCARD
852static int pf_logon_error_info(freerdp* instance, UINT32 data, UINT32 type)
853{
854 const char* str_data = freerdp_get_logon_error_info_data(data);
855 const char* str_type = freerdp_get_logon_error_info_type(type);
856
857 if (!instance || !instance->context)
858 return -1;
859
860 WLog_INFO(TAG, "Logon Error Info %s [%s]", str_data, str_type);
861 return 1;
862}
863
864static void pf_client_context_free(freerdp* instance, rdpContext* context)
865{
866 pClientContext* pc = (pClientContext*)context;
867 WINPR_UNUSED(instance);
868
869 if (!pc)
870 return;
871
872 pc->sendChannelData = nullptr;
873 Queue_Free(pc->cached_server_channel_data);
874 Stream_Free(pc->remote_pem, TRUE);
875 free(pc->remote_hostname);
876 free(pc->computerName.v);
877 HashTable_Free(pc->interceptContextMap);
878}
879
880WINPR_ATTR_NODISCARD
881static int pf_client_verify_X509_certificate(freerdp* instance, const BYTE* data, size_t length,
882 const char* hostname, UINT16 port, DWORD flags)
883{
884 pClientContext* pc = nullptr;
885
886 WINPR_ASSERT(instance);
887 WINPR_ASSERT(data);
888 WINPR_ASSERT(length > 0);
889 WINPR_ASSERT(hostname);
890
891 pc = (pClientContext*)instance->context;
892 WINPR_ASSERT(pc);
893
894 if (!Stream_EnsureCapacity(pc->remote_pem, length))
895 return 0;
896 Stream_ResetPosition(pc->remote_pem);
897
898 free(pc->remote_hostname);
899 pc->remote_hostname = nullptr;
900
901 if (length > 0)
902 Stream_Write(pc->remote_pem, data, length);
903
904 if (hostname)
905 pc->remote_hostname = _strdup(hostname);
906 pc->remote_port = port;
907 pc->remote_flags = flags;
908
909 Stream_SealLength(pc->remote_pem);
910 if (!pf_modules_run_hook(pc->pdata->module, HOOK_TYPE_CLIENT_VERIFY_X509, pc->pdata, pc))
911 return 0;
912 return 1;
913}
914
915WINPR_ATTR_NODISCARD
916static BOOL pf_client_choose_smartcard(WINPR_ATTR_UNUSED freerdp* instance,
917 WINPR_ATTR_UNUSED SmartcardCertInfo** cert_list,
918 WINPR_ATTR_UNUSED DWORD count, DWORD* choice,
919 WINPR_ATTR_UNUSED BOOL gateway)
920{
921 if (count < 1)
922 return FALSE;
923 *choice = 0;
924 return TRUE;
925}
926
927WINPR_ATTR_NODISCARD
928static BOOL pf_client_authenticate_ex(WINPR_ATTR_UNUSED freerdp* instance,
929 WINPR_ATTR_UNUSED char** username,
930 WINPR_ATTR_UNUSED char** password,
931 WINPR_ATTR_UNUSED char** domain, rdp_auth_reason reason)
932{
933 WINPR_ASSERT(instance);
934 WINPR_ASSERT(username);
935 WINPR_ASSERT(password);
936 WINPR_ASSERT(domain);
937
938 /* Here just return success, if the remote does require some non empty credentials
939 * then it will fail, otherwise a login prompt will be shown. */
940 switch (reason)
941 {
942 case AUTH_RDSTLS:
943 case AUTH_NLA:
944 case AUTH_TLS:
945 case AUTH_RDP:
946 case AUTH_SMARTCARD_PIN: /* in this case password is pin code */
947 case GW_AUTH_HTTP:
948 case GW_AUTH_RDG:
949 case GW_AUTH_RPC:
950 return TRUE;
951 default:
952 return FALSE;
953 }
954}
955
956WINPR_ATTR_NODISCARD
957static BOOL pf_client_present_gateway_message(WINPR_ATTR_UNUSED freerdp* instance,
958 WINPR_ATTR_UNUSED UINT32 type,
959 WINPR_ATTR_UNUSED BOOL isDisplayMandatory,
960 WINPR_ATTR_UNUSED BOOL isConsentMandatory,
961 WINPR_ATTR_UNUSED size_t length,
962 WINPR_ATTR_UNUSED const WCHAR* message)
963{
964 WLog_WARN(TAG, "TODO: Implement gateway message forwarding");
965 return TRUE;
966}
967
968void channel_data_free(void* obj)
969{
970 union
971 {
972 const void* cpv;
973 void* pv;
974 } cnv;
975 proxyChannelDataEventInfo* dst = obj;
976 if (dst)
977 {
978 cnv.cpv = dst->data;
979 free(cnv.pv);
980
981 cnv.cpv = dst->channel_name;
982 free(cnv.pv);
983 free(dst);
984 }
985}
986
987WINPR_ATTR_MALLOC(channel_data_free, 1)
988WINPR_ATTR_NODISCARD
989static void* channel_data_copy(const void* obj)
990{
991 union
992 {
993 const void* cpv;
994 void* pv;
995 } cnv;
996 const proxyChannelDataEventInfo* src = obj;
997 proxyChannelDataEventInfo* dst = nullptr;
998
999 WINPR_ASSERT(src);
1000
1001 dst = calloc(1, sizeof(proxyChannelDataEventInfo));
1002 if (!dst)
1003 goto fail;
1004
1005 *dst = *src;
1006 if (src->channel_name)
1007 {
1008 dst->channel_name = _strdup(src->channel_name);
1009 if (!dst->channel_name)
1010 goto fail;
1011 }
1012 dst->data = malloc(src->data_len);
1013 if (!dst->data)
1014 goto fail;
1015
1016 cnv.cpv = dst->data;
1017 memcpy(cnv.pv, src->data, src->data_len);
1018 return dst;
1019
1020fail:
1021 channel_data_free(dst);
1022 return nullptr;
1023}
1024
1025WINPR_ATTR_NODISCARD
1026static BOOL pf_client_client_new(freerdp* instance, rdpContext* context)
1027{
1028 wObject* obj = nullptr;
1029 pClientContext* pc = (pClientContext*)context;
1030
1031 if (!instance || !context)
1032 return FALSE;
1033
1034 instance->LoadChannels = pf_client_load_channels;
1035 instance->PreConnect = pf_client_pre_connect;
1036 instance->PostConnect = pf_client_post_connect;
1037 instance->PostDisconnect = pf_client_post_disconnect;
1038 instance->Redirect = pf_client_redirect;
1039 instance->LogonErrorInfo = pf_logon_error_info;
1040 instance->GetAccessToken = nullptr;
1041 instance->RetryDialog = nullptr;
1042 instance->VerifyCertificateEx = nullptr;
1043 instance->VerifyChangedCertificateEx = nullptr;
1044 instance->VerifyX509Certificate = pf_client_verify_X509_certificate;
1045 instance->AuthenticateEx = pf_client_authenticate_ex;
1046 instance->ChooseSmartcard = pf_client_choose_smartcard;
1047 instance->PresentGatewayMessage = pf_client_present_gateway_message;
1048
1049 pc->remote_pem = Stream_New(nullptr, 4096);
1050 if (!pc->remote_pem)
1051 return FALSE;
1052
1053 pc->sendChannelData = pf_client_send_channel_data;
1054 pc->cached_server_channel_data = Queue_New(TRUE, -1, -1);
1055 if (!pc->cached_server_channel_data)
1056 return FALSE;
1057 obj = Queue_Object(pc->cached_server_channel_data);
1058 WINPR_ASSERT(obj);
1059 obj->fnObjectNew = channel_data_copy;
1060 obj->fnObjectFree = channel_data_free;
1061
1062 pc->interceptContextMap = HashTable_New(FALSE);
1063 if (!pc->interceptContextMap)
1064 return FALSE;
1065
1066 if (!HashTable_SetupForStringData(pc->interceptContextMap, FALSE))
1067 return FALSE;
1068
1069 obj = HashTable_ValueObject(pc->interceptContextMap);
1070 WINPR_ASSERT(obj);
1071 obj->fnObjectFree = intercept_context_entry_free;
1072
1073 return TRUE;
1074}
1075
1076WINPR_ATTR_NODISCARD
1077static int pf_client_client_stop(rdpContext* context)
1078{
1079 pClientContext* pc = (pClientContext*)context;
1080 proxyData* pdata = nullptr;
1081
1082 WINPR_ASSERT(pc);
1083 pdata = pc->pdata;
1084 WINPR_ASSERT(pdata);
1085
1086 PROXY_LOG_DBG(TAG, pc, "aborting client connection");
1087 proxy_data_abort_connect(pdata);
1088 freerdp_abort_connect_context(context);
1089
1090 return 0;
1091}
1092
1093int RdpClientEntry(RDP_CLIENT_ENTRY_POINTS* pEntryPoints)
1094{
1095 WINPR_ASSERT(pEntryPoints);
1096
1097 ZeroMemory(pEntryPoints, sizeof(RDP_CLIENT_ENTRY_POINTS));
1098 pEntryPoints->Version = RDP_CLIENT_INTERFACE_VERSION;
1099 pEntryPoints->Size = sizeof(RDP_CLIENT_ENTRY_POINTS_V1);
1100 pEntryPoints->ContextSize = sizeof(pClientContext);
1101 /* Client init and finish */
1102 pEntryPoints->ClientNew = pf_client_client_new;
1103 pEntryPoints->ClientFree = pf_client_context_free;
1104 pEntryPoints->ClientStop = pf_client_client_stop;
1105 return 0;
1106}
1107
1111DWORD WINAPI pf_client_start(LPVOID arg)
1112{
1113 DWORD rc = 1;
1114 pClientContext* pc = (pClientContext*)arg;
1115
1116 WINPR_ASSERT(pc);
1117 if (freerdp_client_start(&pc->cctx.context) == 0)
1118 rc = pf_client_thread_proc(pc);
1119 freerdp_client_stop(&pc->cctx.context);
1120 return rc;
1121}
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