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