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