Coverage Report

Created: 2025-07-01 06:46

/src/FreeRDP/client/common/client.c
Line
Count
Source (jump to first uncovered line)
1
/**
2
 * FreeRDP: A Remote Desktop Protocol Implementation
3
 * FreeRDP Client Common
4
 *
5
 * Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
6
 * Copyright 2025 Siemens
7
 *
8
 * Licensed under the Apache License, Version 2.0 (the "License");
9
 * you may not use this file except in compliance with the License.
10
 * You may obtain a copy of the License at
11
 *
12
 *     http://www.apache.org/licenses/LICENSE-2.0
13
 *
14
 * Unless required by applicable law or agreed to in writing, software
15
 * distributed under the License is distributed on an "AS IS" BASIS,
16
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17
 * See the License for the specific language governing permissions and
18
 * limitations under the License.
19
 */
20
21
#include <winpr/cast.h>
22
23
#include <freerdp/config.h>
24
25
#include <string.h>
26
#include <errno.h>
27
#include <math.h>
28
#include <limits.h>
29
#include <float.h>
30
31
#include <freerdp/client.h>
32
33
#include <freerdp/freerdp.h>
34
#include <freerdp/addin.h>
35
#include <freerdp/assistance.h>
36
#include <freerdp/client/file.h>
37
#include <freerdp/utils/passphrase.h>
38
#include <freerdp/client/cmdline.h>
39
#include <freerdp/client/channels.h>
40
#include <freerdp/utils/smartcardlogon.h>
41
42
#if defined(CHANNEL_AINPUT_CLIENT)
43
#include <freerdp/client/ainput.h>
44
#include <freerdp/channels/ainput.h>
45
#endif
46
47
#if defined(CHANNEL_VIDEO_CLIENT)
48
#include <freerdp/client/video.h>
49
#include <freerdp/channels/video.h>
50
#endif
51
52
#if defined(CHANNEL_RDPGFX_CLIENT)
53
#include <freerdp/client/rdpgfx.h>
54
#include <freerdp/channels/rdpgfx.h>
55
#include <freerdp/gdi/gfx.h>
56
#endif
57
58
#if defined(CHANNEL_GEOMETRY_CLIENT)
59
#include <freerdp/client/geometry.h>
60
#include <freerdp/channels/geometry.h>
61
#endif
62
63
#if defined(CHANNEL_GEOMETRY_CLIENT) || defined(CHANNEL_VIDEO_CLIENT)
64
#include <freerdp/gdi/video.h>
65
#endif
66
67
#ifdef WITH_AAD
68
#include <freerdp/utils/http.h>
69
#include <freerdp/utils/aad.h>
70
#endif
71
72
#ifdef WITH_SSO_MIB
73
#include "sso_mib_tokens.h"
74
#endif
75
76
#include <freerdp/log.h>
77
#define TAG CLIENT_TAG("common")
78
79
static void set_default_callbacks(freerdp* instance)
80
0
{
81
0
  WINPR_ASSERT(instance);
82
0
  instance->AuthenticateEx = client_cli_authenticate_ex;
83
0
  instance->ChooseSmartcard = client_cli_choose_smartcard;
84
0
  instance->VerifyCertificateEx = client_cli_verify_certificate_ex;
85
0
  instance->VerifyChangedCertificateEx = client_cli_verify_changed_certificate_ex;
86
0
  instance->PresentGatewayMessage = client_cli_present_gateway_message;
87
0
  instance->LogonErrorInfo = client_cli_logon_error_info;
88
0
  instance->GetAccessToken = client_cli_get_access_token;
89
0
  instance->RetryDialog = client_common_retry_dialog;
90
0
}
91
92
static BOOL freerdp_client_common_new(freerdp* instance, rdpContext* context)
93
0
{
94
0
  RDP_CLIENT_ENTRY_POINTS* pEntryPoints = NULL;
95
96
0
  WINPR_ASSERT(instance);
97
0
  WINPR_ASSERT(context);
98
99
0
  instance->LoadChannels = freerdp_client_load_channels;
100
0
  set_default_callbacks(instance);
101
102
0
  pEntryPoints = instance->pClientEntryPoints;
103
0
  WINPR_ASSERT(pEntryPoints);
104
0
  return IFCALLRESULT(TRUE, pEntryPoints->ClientNew, instance, context);
105
0
}
106
107
static void freerdp_client_common_free(freerdp* instance, rdpContext* context)
108
0
{
109
0
  RDP_CLIENT_ENTRY_POINTS* pEntryPoints = NULL;
110
111
0
  WINPR_ASSERT(instance);
112
0
  WINPR_ASSERT(context);
113
114
0
  pEntryPoints = instance->pClientEntryPoints;
115
0
  WINPR_ASSERT(pEntryPoints);
116
0
  IFCALL(pEntryPoints->ClientFree, instance, context);
117
0
}
118
119
/* Common API */
120
121
rdpContext* freerdp_client_context_new(const RDP_CLIENT_ENTRY_POINTS* pEntryPoints)
122
0
{
123
0
  freerdp* instance = NULL;
124
0
  rdpContext* context = NULL;
125
126
0
  if (!pEntryPoints)
127
0
    return NULL;
128
129
0
  IFCALL(pEntryPoints->GlobalInit);
130
0
  instance = freerdp_new();
131
132
0
  if (!instance)
133
0
    return NULL;
134
135
0
  instance->ContextSize = pEntryPoints->ContextSize;
136
0
  instance->ContextNew = freerdp_client_common_new;
137
0
  instance->ContextFree = freerdp_client_common_free;
138
0
  instance->pClientEntryPoints = (RDP_CLIENT_ENTRY_POINTS*)malloc(pEntryPoints->Size);
139
140
0
  if (!instance->pClientEntryPoints)
141
0
    goto out_fail;
142
143
0
  CopyMemory(instance->pClientEntryPoints, pEntryPoints, pEntryPoints->Size);
144
145
0
  if (!freerdp_context_new_ex(instance, pEntryPoints->settings))
146
0
    goto out_fail2;
147
148
0
  context = instance->context;
149
0
  context->instance = instance;
150
151
0
#if defined(WITH_CHANNELS)
152
0
  if (freerdp_register_addin_provider(freerdp_channels_load_static_addin_entry, 0) !=
153
0
      CHANNEL_RC_OK)
154
0
    goto out_fail2;
155
0
#endif
156
157
0
  return context;
158
0
out_fail2:
159
0
  free(instance->pClientEntryPoints);
160
0
out_fail:
161
0
  freerdp_free(instance);
162
0
  return NULL;
163
0
}
164
165
void freerdp_client_context_free(rdpContext* context)
166
0
{
167
0
  freerdp* instance = NULL;
168
169
0
  if (!context)
170
0
    return;
171
172
0
  instance = context->instance;
173
174
0
  if (instance)
175
0
  {
176
0
    RDP_CLIENT_ENTRY_POINTS* pEntryPoints = instance->pClientEntryPoints;
177
0
    freerdp_context_free(instance);
178
179
0
    if (pEntryPoints)
180
0
      IFCALL(pEntryPoints->GlobalUninit);
181
182
0
    free(instance->pClientEntryPoints);
183
0
    freerdp_free(instance);
184
0
  }
185
0
}
186
187
int freerdp_client_start(rdpContext* context)
188
0
{
189
0
  RDP_CLIENT_ENTRY_POINTS* pEntryPoints = NULL;
190
191
0
  if (!context || !context->instance || !context->instance->pClientEntryPoints)
192
0
    return ERROR_BAD_ARGUMENTS;
193
194
0
  if (freerdp_settings_get_bool(context->settings, FreeRDP_UseCommonStdioCallbacks))
195
0
    set_default_callbacks(context->instance);
196
197
#ifdef WITH_SSO_MIB
198
  rdpClientContext* client_context = (rdpClientContext*)context;
199
  client_context->mibClientWrapper = sso_mib_new(context);
200
  if (!client_context->mibClientWrapper)
201
    return ERROR_INTERNAL_ERROR;
202
#endif
203
204
0
  pEntryPoints = context->instance->pClientEntryPoints;
205
0
  return IFCALLRESULT(CHANNEL_RC_OK, pEntryPoints->ClientStart, context);
206
0
}
207
208
int freerdp_client_stop(rdpContext* context)
209
0
{
210
0
  RDP_CLIENT_ENTRY_POINTS* pEntryPoints = NULL;
211
212
0
  if (!context || !context->instance || !context->instance->pClientEntryPoints)
213
0
    return ERROR_BAD_ARGUMENTS;
214
215
0
  pEntryPoints = context->instance->pClientEntryPoints;
216
0
  const int rc = IFCALLRESULT(CHANNEL_RC_OK, pEntryPoints->ClientStop, context);
217
218
#ifdef WITH_SSO_MIB
219
  rdpClientContext* client_context = (rdpClientContext*)context;
220
  sso_mib_free(client_context->mibClientWrapper);
221
  client_context->mibClientWrapper = NULL;
222
#endif // WITH_SSO_MIB
223
0
  return rc;
224
0
}
225
226
freerdp* freerdp_client_get_instance(rdpContext* context)
227
0
{
228
0
  if (!context || !context->instance)
229
0
    return NULL;
230
231
0
  return context->instance;
232
0
}
233
234
HANDLE freerdp_client_get_thread(rdpContext* context)
235
0
{
236
0
  if (!context)
237
0
    return NULL;
238
239
0
  return ((rdpClientContext*)context)->thread;
240
0
}
241
242
static BOOL freerdp_client_settings_post_process(rdpSettings* settings)
243
0
{
244
  /* Moved GatewayUseSameCredentials logic outside of cmdline.c, so
245
   * that the rdp file also triggers this functionality */
246
0
  if (freerdp_settings_get_bool(settings, FreeRDP_GatewayEnabled))
247
0
  {
248
0
    if (freerdp_settings_get_bool(settings, FreeRDP_GatewayUseSameCredentials))
249
0
    {
250
0
      const char* Username = freerdp_settings_get_string(settings, FreeRDP_Username);
251
0
      const char* Domain = freerdp_settings_get_string(settings, FreeRDP_Domain);
252
0
      if (Username)
253
0
      {
254
0
        if (!freerdp_settings_set_string(settings, FreeRDP_GatewayUsername, Username))
255
0
          goto out_error;
256
0
      }
257
258
0
      if (Domain)
259
0
      {
260
0
        if (!freerdp_settings_set_string(settings, FreeRDP_GatewayDomain, Domain))
261
0
          goto out_error;
262
0
      }
263
264
0
      if (freerdp_settings_get_string(settings, FreeRDP_Password))
265
0
      {
266
0
        if (!freerdp_settings_set_string(
267
0
                settings, FreeRDP_GatewayPassword,
268
0
                freerdp_settings_get_string(settings, FreeRDP_Password)))
269
0
          goto out_error;
270
0
      }
271
0
    }
272
0
  }
273
274
  /* Moved logic for Multimon and Span monitors to force fullscreen, so
275
   * that the rdp file also triggers this functionality */
276
0
  if (freerdp_settings_get_bool(settings, FreeRDP_SpanMonitors))
277
0
  {
278
0
    if (!freerdp_settings_set_bool(settings, FreeRDP_UseMultimon, TRUE))
279
0
      goto out_error;
280
0
    if (!freerdp_settings_set_bool(settings, FreeRDP_Fullscreen, TRUE))
281
0
      goto out_error;
282
0
  }
283
0
  else if (freerdp_settings_get_bool(settings, FreeRDP_UseMultimon))
284
0
  {
285
0
    if (!freerdp_settings_set_bool(settings, FreeRDP_Fullscreen, TRUE))
286
0
      goto out_error;
287
0
  }
288
289
  /* deal with the smartcard / smartcard logon stuff */
290
0
  if (freerdp_settings_get_bool(settings, FreeRDP_SmartcardLogon))
291
0
  {
292
0
    if (!freerdp_settings_set_bool(settings, FreeRDP_TlsSecurity, TRUE))
293
0
      goto out_error;
294
0
    if (!freerdp_settings_set_bool(settings, FreeRDP_RedirectSmartCards, TRUE))
295
0
      goto out_error;
296
0
    if (!freerdp_settings_set_bool(settings, FreeRDP_DeviceRedirection, TRUE))
297
0
      goto out_error;
298
0
    if (!freerdp_settings_set_bool(settings, FreeRDP_PasswordIsSmartcardPin, TRUE))
299
0
      goto out_error;
300
0
  }
301
302
0
  return TRUE;
303
0
out_error:
304
0
  return FALSE;
305
0
}
306
307
int freerdp_client_settings_parse_command_line(rdpSettings* settings, int argc, char** argv,
308
                                               BOOL allowUnknown)
309
310
0
{
311
0
  return freerdp_client_settings_parse_command_line_ex(settings, argc, argv, allowUnknown, NULL,
312
0
                                                       0, NULL, NULL);
313
0
}
314
315
int freerdp_client_settings_parse_command_line_ex(
316
    rdpSettings* settings, int argc, char** argv, BOOL allowUnknown, COMMAND_LINE_ARGUMENT_A* args,
317
    size_t count, freerdp_command_line_handle_option_t handle_option, void* handle_userdata)
318
0
{
319
0
  int status = 0;
320
321
0
  if (argc < 1)
322
0
    return 0;
323
324
0
  if (!argv)
325
0
    return -1;
326
327
0
  status = freerdp_client_settings_parse_command_line_arguments_ex(
328
0
      settings, argc, argv, allowUnknown, args, count, handle_option, handle_userdata);
329
330
0
  if (status < 0)
331
0
    return status;
332
333
  /* This function will call logic that is applicable to the settings
334
   * from command line parsing AND the rdp file parsing */
335
0
  if (!freerdp_client_settings_post_process(settings))
336
0
    status = -1;
337
338
0
  const char* name = argv[0];
339
0
  WLog_DBG(TAG, "This is [%s] %s %s", name, freerdp_get_version_string(),
340
0
           freerdp_get_build_config());
341
0
  return status;
342
0
}
343
344
int freerdp_client_settings_parse_connection_file(rdpSettings* settings, const char* filename)
345
0
{
346
0
  rdpFile* file = NULL;
347
0
  int ret = -1;
348
0
  file = freerdp_client_rdp_file_new();
349
350
0
  if (!file)
351
0
    return -1;
352
353
0
  if (!freerdp_client_parse_rdp_file(file, filename))
354
0
    goto out;
355
356
0
  if (!freerdp_client_populate_settings_from_rdp_file(file, settings))
357
0
    goto out;
358
359
0
  ret = 0;
360
0
out:
361
0
  freerdp_client_rdp_file_free(file);
362
0
  return ret;
363
0
}
364
365
int freerdp_client_settings_parse_connection_file_buffer(rdpSettings* settings, const BYTE* buffer,
366
                                                         size_t size)
367
0
{
368
0
  rdpFile* file = NULL;
369
0
  int status = -1;
370
0
  file = freerdp_client_rdp_file_new();
371
372
0
  if (!file)
373
0
    return -1;
374
375
0
  if (freerdp_client_parse_rdp_file_buffer(file, buffer, size) &&
376
0
      freerdp_client_populate_settings_from_rdp_file(file, settings))
377
0
  {
378
0
    status = 0;
379
0
  }
380
381
0
  freerdp_client_rdp_file_free(file);
382
0
  return status;
383
0
}
384
385
int freerdp_client_settings_write_connection_file(const rdpSettings* settings, const char* filename,
386
                                                  BOOL unicode)
387
0
{
388
0
  rdpFile* file = NULL;
389
0
  int ret = -1;
390
0
  file = freerdp_client_rdp_file_new();
391
392
0
  if (!file)
393
0
    return -1;
394
395
0
  if (!freerdp_client_populate_rdp_file_from_settings(file, settings))
396
0
    goto out;
397
398
0
  if (!freerdp_client_write_rdp_file(file, filename, unicode))
399
0
    goto out;
400
401
0
  ret = 0;
402
0
out:
403
0
  freerdp_client_rdp_file_free(file);
404
0
  return ret;
405
0
}
406
407
int freerdp_client_settings_parse_assistance_file(rdpSettings* settings, int argc, char* argv[])
408
0
{
409
0
  int status = 0;
410
0
  int ret = -1;
411
0
  char* filename = NULL;
412
0
  char* password = NULL;
413
0
  rdpAssistanceFile* file = NULL;
414
415
0
  if (!settings || !argv || (argc < 2))
416
0
    return -1;
417
418
0
  filename = argv[1];
419
420
0
  for (int x = 2; x < argc; x++)
421
0
  {
422
0
    const char* key = strstr(argv[x], "assistance:");
423
424
0
    if (key)
425
0
      password = strchr(key, ':') + 1;
426
0
  }
427
428
0
  file = freerdp_assistance_file_new();
429
430
0
  if (!file)
431
0
    return -1;
432
433
0
  status = freerdp_assistance_parse_file(file, filename, password);
434
435
0
  if (status < 0)
436
0
    goto out;
437
438
0
  if (!freerdp_assistance_populate_settings_from_assistance_file(file, settings))
439
0
    goto out;
440
441
0
  ret = 0;
442
0
out:
443
0
  freerdp_assistance_file_free(file);
444
0
  return ret;
445
0
}
446
447
/** Callback set in the rdp_freerdp structure, and used to get the user's password,
448
 *  if required to establish the connection.
449
 *  This function is actually called in credssp_ntlmssp_client_init()
450
 *  @see rdp_server_accept_nego() and rdp_check_fds()
451
 *  @param instance - pointer to the rdp_freerdp structure that contains the connection settings
452
 *  @param username - unused
453
 *  @param password - on return: pointer to a character string that will be filled by the password
454
 * entered by the user. Note that this character string will be allocated inside the function, and
455
 * needs to be deallocated by the caller using free(), even in case this function fails.
456
 *  @param domain - unused
457
 *  @return TRUE if a password was successfully entered. See freerdp_passphrase_read() for more
458
 * details.
459
 */
460
static BOOL client_cli_authenticate_raw(freerdp* instance, rdp_auth_reason reason, char** username,
461
                                        char** password, char** domain)
462
0
{
463
0
  static const size_t password_size = 512;
464
0
  const char* auth[] = { "Username:        ", "Domain:          ", "Password:        " };
465
0
  const char* authPin[] = { "Username:        ", "Domain:          ", "Smartcard-Pin:   " };
466
0
  const char* gw[] = { "GatewayUsername: ", "GatewayDomain:   ", "GatewayPassword: " };
467
0
  const char** prompt = NULL;
468
0
  BOOL pinOnly = FALSE;
469
470
0
  WINPR_ASSERT(instance);
471
0
  WINPR_ASSERT(instance->context);
472
0
  WINPR_ASSERT(instance->context->settings);
473
474
0
  switch (reason)
475
0
  {
476
0
    case AUTH_SMARTCARD_PIN:
477
0
      prompt = authPin;
478
0
      pinOnly = TRUE;
479
0
      break;
480
0
    case AUTH_TLS:
481
0
    case AUTH_RDP:
482
0
    case AUTH_NLA:
483
0
      prompt = auth;
484
0
      break;
485
0
    case GW_AUTH_HTTP:
486
0
    case GW_AUTH_RDG:
487
0
    case GW_AUTH_RPC:
488
0
      prompt = gw;
489
0
      break;
490
0
    default:
491
0
      return FALSE;
492
0
  }
493
494
0
  if (!username || !password || !domain)
495
0
    return FALSE;
496
497
0
  if (!*username && !pinOnly)
498
0
  {
499
0
    size_t username_size = 0;
500
0
    printf("%s", prompt[0]);
501
0
    (void)fflush(stdout);
502
503
0
    if (freerdp_interruptible_get_line(instance->context, username, &username_size, stdin) < 0)
504
0
    {
505
0
      char ebuffer[256] = { 0 };
506
0
      WLog_ERR(TAG, "freerdp_interruptible_get_line returned %s [%d]",
507
0
               winpr_strerror(errno, ebuffer, sizeof(ebuffer)), errno);
508
0
      goto fail;
509
0
    }
510
511
0
    if (*username)
512
0
    {
513
0
      *username = StrSep(username, "\r");
514
0
      *username = StrSep(username, "\n");
515
0
    }
516
0
  }
517
518
0
  if (!*domain && !pinOnly)
519
0
  {
520
0
    size_t domain_size = 0;
521
0
    printf("%s", prompt[1]);
522
0
    (void)fflush(stdout);
523
524
0
    if (freerdp_interruptible_get_line(instance->context, domain, &domain_size, stdin) < 0)
525
0
    {
526
0
      char ebuffer[256] = { 0 };
527
0
      WLog_ERR(TAG, "freerdp_interruptible_get_line returned %s [%d]",
528
0
               winpr_strerror(errno, ebuffer, sizeof(ebuffer)), errno);
529
0
      goto fail;
530
0
    }
531
532
0
    if (*domain)
533
0
    {
534
0
      *domain = StrSep(domain, "\r");
535
0
      *domain = StrSep(domain, "\n");
536
0
    }
537
0
  }
538
539
0
  if (!*password)
540
0
  {
541
0
    *password = calloc(password_size, sizeof(char));
542
543
0
    if (!*password)
544
0
      goto fail;
545
546
0
    const BOOL fromStdin =
547
0
        freerdp_settings_get_bool(instance->context->settings, FreeRDP_CredentialsFromStdin);
548
0
    if (freerdp_passphrase_read(instance->context, prompt[2], *password, password_size,
549
0
                                fromStdin) == NULL)
550
0
      goto fail;
551
0
  }
552
553
0
  return TRUE;
554
0
fail:
555
0
  free(*username);
556
0
  free(*domain);
557
0
  free(*password);
558
0
  *username = NULL;
559
0
  *domain = NULL;
560
0
  *password = NULL;
561
0
  return FALSE;
562
0
}
563
564
BOOL client_cli_authenticate_ex(freerdp* instance, char** username, char** password, char** domain,
565
                                rdp_auth_reason reason)
566
0
{
567
0
  WINPR_ASSERT(instance);
568
0
  WINPR_ASSERT(username);
569
0
  WINPR_ASSERT(password);
570
0
  WINPR_ASSERT(domain);
571
572
0
  switch (reason)
573
0
  {
574
0
    case AUTH_NLA:
575
0
      break;
576
577
0
    case AUTH_TLS:
578
0
    case AUTH_RDP:
579
0
    case AUTH_SMARTCARD_PIN: /* in this case password is pin code */
580
0
      if ((*username) && (*password))
581
0
        return TRUE;
582
0
      break;
583
0
    case GW_AUTH_HTTP:
584
0
    case GW_AUTH_RDG:
585
0
    case GW_AUTH_RPC:
586
0
      break;
587
0
    default:
588
0
      return FALSE;
589
0
  }
590
591
0
  return client_cli_authenticate_raw(instance, reason, username, password, domain);
592
0
}
593
594
BOOL client_cli_choose_smartcard(WINPR_ATTR_UNUSED freerdp* instance, SmartcardCertInfo** cert_list,
595
                                 DWORD count, DWORD* choice, BOOL gateway)
596
0
{
597
0
  unsigned long answer = 0;
598
0
  char* p = NULL;
599
600
0
  printf("Multiple smartcards are available for use:\n");
601
0
  for (DWORD i = 0; i < count; i++)
602
0
  {
603
0
    const SmartcardCertInfo* cert = cert_list[i];
604
0
    char* reader = ConvertWCharToUtf8Alloc(cert->reader, NULL);
605
0
    char* container_name = ConvertWCharToUtf8Alloc(cert->containerName, NULL);
606
607
0
    printf("[%" PRIu32
608
0
           "] %s\n\tReader: %s\n\tUser: %s@%s\n\tSubject: %s\n\tIssuer: %s\n\tUPN: %s\n",
609
0
           i, container_name, reader, cert->userHint, cert->domainHint, cert->subject,
610
0
           cert->issuer, cert->upn);
611
612
0
    free(reader);
613
0
    free(container_name);
614
0
  }
615
616
0
  while (1)
617
0
  {
618
0
    char input[10] = { 0 };
619
620
0
    printf("\nChoose a smartcard to use for %s (0 - %" PRIu32 "): ",
621
0
           gateway ? "gateway authentication" : "logon", count - 1);
622
0
    (void)fflush(stdout);
623
0
    if (!fgets(input, 10, stdin))
624
0
    {
625
0
      WLog_ERR(TAG, "could not read from stdin");
626
0
      return FALSE;
627
0
    }
628
629
0
    answer = strtoul(input, &p, 10);
630
0
    if ((*p == '\n' && p != input) && answer < count)
631
0
    {
632
0
      *choice = (UINT32)answer;
633
0
      return TRUE;
634
0
    }
635
0
  }
636
0
}
637
638
#if defined(WITH_FREERDP_DEPRECATED)
639
BOOL client_cli_authenticate(freerdp* instance, char** username, char** password, char** domain)
640
{
641
  if (freerdp_settings_get_bool(instance->settings, FreeRDP_SmartcardLogon))
642
  {
643
    WLog_INFO(TAG, "Authentication via smartcard");
644
    return TRUE;
645
  }
646
647
  return client_cli_authenticate_raw(instance, FALSE, username, password, domain);
648
}
649
650
BOOL client_cli_gw_authenticate(freerdp* instance, char** username, char** password, char** domain)
651
{
652
  return client_cli_authenticate_raw(instance, TRUE, username, password, domain);
653
}
654
#endif
655
656
static DWORD client_cli_accept_certificate(freerdp* instance)
657
0
{
658
0
  int answer = 0;
659
660
0
  WINPR_ASSERT(instance);
661
0
  WINPR_ASSERT(instance->context);
662
663
0
  const rdpSettings* settings = instance->context->settings;
664
0
  WINPR_ASSERT(settings);
665
666
0
  const BOOL fromStdin = freerdp_settings_get_bool(settings, FreeRDP_CredentialsFromStdin);
667
0
  if (fromStdin)
668
0
    return 0;
669
670
0
  while (1)
671
0
  {
672
0
    printf("Do you trust the above certificate? (Y/T/N) ");
673
0
    (void)fflush(stdout);
674
0
    answer = freerdp_interruptible_getc(instance->context, stdin);
675
676
0
    if ((answer == EOF) || feof(stdin))
677
0
    {
678
0
      printf("\nError: Could not read answer from stdin.\n");
679
0
      return 0;
680
0
    }
681
682
0
    switch (answer)
683
0
    {
684
0
      case 'y':
685
0
      case 'Y':
686
0
        answer = freerdp_interruptible_getc(instance->context, stdin);
687
0
        if (answer == EOF)
688
0
          return 0;
689
0
        return 1;
690
691
0
      case 't':
692
0
      case 'T':
693
0
        answer = freerdp_interruptible_getc(instance->context, stdin);
694
0
        if (answer == EOF)
695
0
          return 0;
696
0
        return 2;
697
698
0
      case 'n':
699
0
      case 'N':
700
0
        answer = freerdp_interruptible_getc(instance->context, stdin);
701
0
        if (answer == EOF)
702
0
          return 0;
703
0
        return 0;
704
705
0
      default:
706
0
        break;
707
0
    }
708
709
0
    printf("\n");
710
0
  }
711
0
}
712
713
/** Callback set in the rdp_freerdp structure, and used to make a certificate validation
714
 *  when the connection requires it.
715
 *  This function will actually be called by tls_verify_certificate().
716
 *  @see rdp_client_connect() and freerdp_tls_connect()
717
 *  @deprecated Use client_cli_verify_certificate_ex
718
 *  @param instance - pointer to the rdp_freerdp structure that contains the connection settings
719
 *  @param common_name
720
 *  @param subject
721
 *  @param issuer
722
 *  @param fingerprint
723
 *  @param host_mismatch Indicates the certificate host does not match.
724
 *  @return 1 if the certificate is trusted, 2 if temporary trusted, 0 otherwise.
725
 */
726
#if defined(WITH_FREERDP_DEPRECATED)
727
DWORD client_cli_verify_certificate(freerdp* instance, const char* common_name, const char* subject,
728
                                    const char* issuer, const char* fingerprint, BOOL host_mismatch)
729
{
730
  WINPR_UNUSED(common_name);
731
  WINPR_UNUSED(host_mismatch);
732
733
  printf("WARNING: This callback is deprecated, migrate to client_cli_verify_certificate_ex\n");
734
  printf("Certificate details:\n");
735
  printf("\tSubject: %s\n", subject);
736
  printf("\tIssuer: %s\n", issuer);
737
  printf("\tThumbprint: %s\n", fingerprint);
738
  printf("The above X.509 certificate could not be verified, possibly because you do not have\n"
739
         "the CA certificate in your certificate store, or the certificate has expired.\n"
740
         "Please look at the OpenSSL documentation on how to add a private CA to the store.\n");
741
  return client_cli_accept_certificate(instance);
742
}
743
#endif
744
745
static char* client_cli_pem_cert(const char* pem)
746
0
{
747
0
  rdpCertificate* cert = freerdp_certificate_new_from_pem(pem);
748
0
  if (!cert)
749
0
    return NULL;
750
751
0
  char* fp = freerdp_certificate_get_fingerprint(cert);
752
0
  char* start = freerdp_certificate_get_validity(cert, TRUE);
753
0
  char* end = freerdp_certificate_get_validity(cert, FALSE);
754
0
  freerdp_certificate_free(cert);
755
756
0
  char* str = NULL;
757
0
  size_t slen = 0;
758
0
  winpr_asprintf(&str, &slen,
759
0
                 "\tValid from:  %s\n"
760
0
                 "\tValid to:    %s\n"
761
0
                 "\tThumbprint:  %s\n",
762
0
                 start, end, fp);
763
0
  free(fp);
764
0
  free(start);
765
0
  free(end);
766
0
  return str;
767
0
}
768
769
/** Callback set in the rdp_freerdp structure, and used to make a certificate validation
770
 *  when the connection requires it.
771
 *  This function will actually be called by tls_verify_certificate().
772
 *  @see rdp_client_connect() and freerdp_tls_connect()
773
 *  @param instance     pointer to the rdp_freerdp structure that contains the connection settings
774
 *  @param host         The host currently connecting to
775
 *  @param port         The port currently connecting to
776
 *  @param common_name  The common name of the certificate, should match host or an alias of it
777
 *  @param subject      The subject of the certificate
778
 *  @param issuer       The certificate issuer name
779
 *  @param fingerprint  The fingerprint of the certificate
780
 *  @param flags        See VERIFY_CERT_FLAG_* for possible values.
781
 *
782
 *  @return 1 if the certificate is trusted, 2 if temporary trusted, 0 otherwise.
783
 */
784
DWORD client_cli_verify_certificate_ex(freerdp* instance, const char* host, UINT16 port,
785
                                       const char* common_name, const char* subject,
786
                                       const char* issuer, const char* fingerprint, DWORD flags)
787
0
{
788
0
  const char* type = "RDP-Server";
789
790
0
  WINPR_ASSERT(instance);
791
0
  WINPR_ASSERT(instance->context);
792
0
  WINPR_ASSERT(instance->context->settings);
793
794
0
  if (flags & VERIFY_CERT_FLAG_GATEWAY)
795
0
    type = "RDP-Gateway";
796
797
0
  if (flags & VERIFY_CERT_FLAG_REDIRECT)
798
0
    type = "RDP-Redirect";
799
800
0
  printf("Certificate details for %s:%" PRIu16 " (%s):\n", host, port, type);
801
0
  printf("\tCommon Name: %s\n", common_name);
802
0
  printf("\tSubject:     %s\n", subject);
803
0
  printf("\tIssuer:      %s\n", issuer);
804
  /* Newer versions of FreeRDP allow exposing the whole PEM by setting
805
   * FreeRDP_CertificateCallbackPreferPEM to TRUE
806
   */
807
0
  if (flags & VERIFY_CERT_FLAG_FP_IS_PEM)
808
0
  {
809
0
    char* str = client_cli_pem_cert(fingerprint);
810
0
    printf("%s", str);
811
0
    free(str);
812
0
  }
813
0
  else
814
0
    printf("\tThumbprint:  %s\n", fingerprint);
815
816
0
  printf("The above X.509 certificate could not be verified, possibly because you do not have\n"
817
0
         "the CA certificate in your certificate store, or the certificate has expired.\n"
818
0
         "Please look at the OpenSSL documentation on how to add a private CA to the store.\n");
819
0
  return client_cli_accept_certificate(instance);
820
0
}
821
822
/** Callback set in the rdp_freerdp structure, and used to make a certificate validation
823
 *  when a stored certificate does not match the remote counterpart.
824
 *  This function will actually be called by tls_verify_certificate().
825
 *  @see rdp_client_connect() and freerdp_tls_connect()
826
 *  @deprecated Use client_cli_verify_changed_certificate_ex
827
 *  @param instance - pointer to the rdp_freerdp structure that contains the connection settings
828
 *  @param common_name
829
 *  @param subject
830
 *  @param issuer
831
 *  @param fingerprint
832
 *  @param old_subject
833
 *  @param old_issuer
834
 *  @param old_fingerprint
835
 *  @return 1 if the certificate is trusted, 2 if temporary trusted, 0 otherwise.
836
 */
837
#if defined(WITH_FREERDP_DEPRECATED)
838
DWORD client_cli_verify_changed_certificate(freerdp* instance, const char* common_name,
839
                                            const char* subject, const char* issuer,
840
                                            const char* fingerprint, const char* old_subject,
841
                                            const char* old_issuer, const char* old_fingerprint)
842
{
843
  WINPR_UNUSED(common_name);
844
845
  printf("WARNING: This callback is deprecated, migrate to "
846
         "client_cli_verify_changed_certificate_ex\n");
847
  printf("!!! Certificate has changed !!!\n");
848
  printf("\n");
849
  printf("New Certificate details:\n");
850
  printf("\tSubject: %s\n", subject);
851
  printf("\tIssuer: %s\n", issuer);
852
  printf("\tThumbprint: %s\n", fingerprint);
853
  printf("\n");
854
  printf("Old Certificate details:\n");
855
  printf("\tSubject: %s\n", old_subject);
856
  printf("\tIssuer: %s\n", old_issuer);
857
  printf("\tThumbprint: %s\n", old_fingerprint);
858
  printf("\n");
859
  printf("The above X.509 certificate does not match the certificate used for previous "
860
         "connections.\n"
861
         "This may indicate that the certificate has been tampered with.\n"
862
         "Please contact the administrator of the RDP server and clarify.\n");
863
  return client_cli_accept_certificate(instance);
864
}
865
#endif
866
867
/** Callback set in the rdp_freerdp structure, and used to make a certificate validation
868
 *  when a stored certificate does not match the remote counterpart.
869
 *  This function will actually be called by tls_verify_certificate().
870
 *  @see rdp_client_connect() and freerdp_tls_connect()
871
 *  @param instance        pointer to the rdp_freerdp structure that contains the connection
872
 * settings
873
 *  @param host            The host currently connecting to
874
 *  @param port            The port currently connecting to
875
 *  @param common_name     The common name of the certificate, should match host or an alias of it
876
 *  @param subject         The subject of the certificate
877
 *  @param issuer          The certificate issuer name
878
 *  @param fingerprint     The fingerprint of the certificate
879
 *  @param old_subject     The subject of the previous certificate
880
 *  @param old_issuer      The previous certificate issuer name
881
 *  @param old_fingerprint The fingerprint of the previous certificate
882
 *  @param flags           See VERIFY_CERT_FLAG_* for possible values.
883
 *
884
 *  @return 1 if the certificate is trusted, 2 if temporary trusted, 0 otherwise.
885
 */
886
DWORD client_cli_verify_changed_certificate_ex(freerdp* instance, const char* host, UINT16 port,
887
                                               const char* common_name, const char* subject,
888
                                               const char* issuer, const char* fingerprint,
889
                                               const char* old_subject, const char* old_issuer,
890
                                               const char* old_fingerprint, DWORD flags)
891
0
{
892
0
  const char* type = "RDP-Server";
893
894
0
  WINPR_ASSERT(instance);
895
0
  WINPR_ASSERT(instance->context);
896
0
  WINPR_ASSERT(instance->context->settings);
897
898
0
  if (flags & VERIFY_CERT_FLAG_GATEWAY)
899
0
    type = "RDP-Gateway";
900
901
0
  if (flags & VERIFY_CERT_FLAG_REDIRECT)
902
0
    type = "RDP-Redirect";
903
904
0
  printf("!!!Certificate for %s:%" PRIu16 " (%s) has changed!!!\n", host, port, type);
905
0
  printf("\n");
906
0
  printf("New Certificate details:\n");
907
0
  printf("\tCommon Name: %s\n", common_name);
908
0
  printf("\tSubject:     %s\n", subject);
909
0
  printf("\tIssuer:      %s\n", issuer);
910
  /* Newer versions of FreeRDP allow exposing the whole PEM by setting
911
   * FreeRDP_CertificateCallbackPreferPEM to TRUE
912
   */
913
0
  if (flags & VERIFY_CERT_FLAG_FP_IS_PEM)
914
0
  {
915
0
    char* str = client_cli_pem_cert(fingerprint);
916
0
    printf("%s", str);
917
0
    free(str);
918
0
  }
919
0
  else
920
0
    printf("\tThumbprint:  %s\n", fingerprint);
921
0
  printf("\n");
922
0
  printf("Old Certificate details:\n");
923
0
  printf("\tSubject:     %s\n", old_subject);
924
0
  printf("\tIssuer:      %s\n", old_issuer);
925
  /* Newer versions of FreeRDP allow exposing the whole PEM by setting
926
   * FreeRDP_CertificateCallbackPreferPEM to TRUE
927
   */
928
0
  if (flags & VERIFY_CERT_FLAG_FP_IS_PEM)
929
0
  {
930
0
    char* str = client_cli_pem_cert(old_fingerprint);
931
0
    printf("%s", str);
932
0
    free(str);
933
0
  }
934
0
  else
935
0
    printf("\tThumbprint:  %s\n", old_fingerprint);
936
0
  printf("\n");
937
0
  if (flags & VERIFY_CERT_FLAG_MATCH_LEGACY_SHA1)
938
0
  {
939
0
    printf("\tA matching entry with legacy SHA1 was found in local known_hosts2 store.\n");
940
0
    printf("\tIf you just upgraded from a FreeRDP version before 2.0 this is expected.\n");
941
0
    printf("\tThe hashing algorithm has been upgraded from SHA1 to SHA256.\n");
942
0
    printf("\tAll manually accepted certificates must be reconfirmed!\n");
943
0
    printf("\n");
944
0
  }
945
0
  printf("The above X.509 certificate does not match the certificate used for previous "
946
0
         "connections.\n"
947
0
         "This may indicate that the certificate has been tampered with.\n"
948
0
         "Please contact the administrator of the RDP server and clarify.\n");
949
0
  return client_cli_accept_certificate(instance);
950
0
}
951
952
BOOL client_cli_present_gateway_message(freerdp* instance, UINT32 type, BOOL isDisplayMandatory,
953
                                        BOOL isConsentMandatory, size_t length,
954
                                        const WCHAR* message)
955
0
{
956
0
  int answer = 0;
957
0
  const char* msgType = (type == GATEWAY_MESSAGE_CONSENT) ? "Consent message" : "Service message";
958
959
0
  WINPR_ASSERT(instance);
960
0
  WINPR_ASSERT(instance->context);
961
0
  WINPR_ASSERT(instance->context->settings);
962
963
0
  if (!isDisplayMandatory && !isConsentMandatory)
964
0
    return TRUE;
965
966
0
  printf("%s:\n", msgType);
967
#if defined(WIN32)
968
  printf("%.*S\n", (int)length, message);
969
#else
970
0
  {
971
0
    LPSTR msg = ConvertWCharNToUtf8Alloc(message, length / sizeof(WCHAR), NULL);
972
0
    if (!msg)
973
0
    {
974
0
      printf("Failed to convert message!\n");
975
0
      return FALSE;
976
0
    }
977
0
    printf("%s\n", msg);
978
0
    free(msg);
979
0
  }
980
0
#endif
981
982
0
  while (isConsentMandatory)
983
0
  {
984
0
    printf("I understand and agree to the terms of this policy (Y/N) \n");
985
0
    (void)fflush(stdout);
986
0
    answer = freerdp_interruptible_getc(instance->context, stdin);
987
988
0
    if ((answer == EOF) || feof(stdin))
989
0
    {
990
0
      printf("\nError: Could not read answer from stdin.\n");
991
0
      return FALSE;
992
0
    }
993
994
0
    switch (answer)
995
0
    {
996
0
      case 'y':
997
0
      case 'Y':
998
0
        answer = freerdp_interruptible_getc(instance->context, stdin);
999
0
        if (answer == EOF)
1000
0
          return FALSE;
1001
0
        return TRUE;
1002
1003
0
      case 'n':
1004
0
      case 'N':
1005
0
        (void)freerdp_interruptible_getc(instance->context, stdin);
1006
0
        return FALSE;
1007
1008
0
      default:
1009
0
        break;
1010
0
    }
1011
1012
0
    printf("\n");
1013
0
  }
1014
1015
0
  return TRUE;
1016
0
}
1017
1018
static const char* extract_authorization_code(char* url)
1019
0
{
1020
0
  WINPR_ASSERT(url);
1021
0
1022
0
  for (char* p = strchr(url, '?'); p++ != NULL; p = strchr(p, '&'))
1023
0
  {
1024
0
    if (strncmp(p, "code=", 5) != 0)
1025
0
      continue;
1026
0
1027
0
    char* end = NULL;
1028
0
    p += 5;
1029
0
1030
0
    end = strchr(p, '&');
1031
0
    if (end)
1032
0
      *end = '\0';
1033
0
1034
0
    return p;
1035
0
  }
1036
0
1037
0
  return NULL;
1038
0
}
1039
1040
#if defined(WITH_AAD)
1041
static BOOL client_cli_get_rdsaad_access_token(freerdp* instance, const char* scope,
1042
                                               const char* req_cnf, char** token)
1043
{
1044
  WINPR_ASSERT(instance);
1045
  WINPR_ASSERT(instance->context);
1046
1047
  size_t size = 0;
1048
  char* url = NULL;
1049
  char* token_request = NULL;
1050
1051
  WINPR_ASSERT(scope);
1052
  WINPR_ASSERT(req_cnf);
1053
  WINPR_ASSERT(token);
1054
1055
  BOOL rc = FALSE;
1056
  *token = NULL;
1057
1058
  char* request = freerdp_client_get_aad_url((rdpClientContext*)instance->context,
1059
                                             FREERDP_CLIENT_AAD_AUTH_REQUEST, scope);
1060
1061
  printf("Browse to: %s\n", request);
1062
  free(request);
1063
  printf("Paste redirect URL here: \n");
1064
1065
  if (freerdp_interruptible_get_line(instance->context, &url, &size, stdin) < 0)
1066
    goto cleanup;
1067
1068
  const char* code = extract_authorization_code(url);
1069
  if (!code)
1070
    goto cleanup;
1071
1072
  token_request =
1073
      freerdp_client_get_aad_url((rdpClientContext*)instance->context,
1074
                                 FREERDP_CLIENT_AAD_TOKEN_REQUEST, scope, code, req_cnf);
1075
  if (!token_request)
1076
    goto cleanup;
1077
1078
  rc = client_common_get_access_token(instance, token_request, token);
1079
1080
cleanup:
1081
  free(token_request);
1082
  free(url);
1083
  return rc && (*token != NULL);
1084
}
1085
1086
static BOOL client_cli_get_avd_access_token(freerdp* instance, char** token)
1087
{
1088
  WINPR_ASSERT(instance);
1089
  WINPR_ASSERT(instance->context);
1090
1091
  size_t size = 0;
1092
  char* url = NULL;
1093
  char* token_request = NULL;
1094
1095
  WINPR_ASSERT(token);
1096
1097
  BOOL rc = FALSE;
1098
1099
  *token = NULL;
1100
1101
  char* request = freerdp_client_get_aad_url((rdpClientContext*)instance->context,
1102
                                             FREERDP_CLIENT_AAD_AVD_AUTH_REQUEST);
1103
  if (!request)
1104
    return FALSE;
1105
  printf("Browse to: %s\n", request);
1106
  free(request);
1107
  printf("Paste redirect URL here: \n");
1108
1109
  if (freerdp_interruptible_get_line(instance->context, &url, &size, stdin) < 0)
1110
    goto cleanup;
1111
1112
  const char* code = extract_authorization_code(url);
1113
  if (!code)
1114
    goto cleanup;
1115
1116
  token_request = freerdp_client_get_aad_url((rdpClientContext*)instance->context,
1117
                                             FREERDP_CLIENT_AAD_AVD_TOKEN_REQUEST, code);
1118
1119
  if (!token_request)
1120
    goto cleanup;
1121
1122
  rc = client_common_get_access_token(instance, token_request, token);
1123
1124
cleanup:
1125
  free(token_request);
1126
  free(url);
1127
  return rc && (*token != NULL);
1128
}
1129
#endif
1130
1131
BOOL client_cli_get_access_token(freerdp* instance, AccessTokenType tokenType, char** token,
1132
                                 size_t count, ...)
1133
0
{
1134
0
  WINPR_ASSERT(instance);
1135
0
  WINPR_ASSERT(token);
1136
1137
0
#if !defined(WITH_AAD)
1138
0
  WLog_ERR(TAG, "Build does not support AAD authentication");
1139
0
  return FALSE;
1140
#else
1141
  BOOL rc = FALSE;
1142
  WINPR_ASSERT(instance->context);
1143
  const BOOL saved =
1144
      freerdp_settings_get_bool(instance->context->settings, FreeRDP_UseCommonStdioCallbacks);
1145
  if (!freerdp_settings_set_bool(instance->context->settings, FreeRDP_UseCommonStdioCallbacks,
1146
                                 TRUE))
1147
    return FALSE;
1148
1149
  switch (tokenType)
1150
  {
1151
    case ACCESS_TOKEN_TYPE_AAD:
1152
    {
1153
      if (count < 2)
1154
      {
1155
        WLog_ERR(TAG,
1156
                 "ACCESS_TOKEN_TYPE_AAD expected 2 additional arguments, but got %" PRIuz
1157
                 ", aborting",
1158
                 count);
1159
        return FALSE;
1160
      }
1161
      else if (count > 2)
1162
        WLog_WARN(TAG,
1163
                  "ACCESS_TOKEN_TYPE_AAD expected 2 additional arguments, but got %" PRIuz
1164
                  ", ignoring",
1165
                  count);
1166
      va_list ap = { 0 };
1167
      va_start(ap, count);
1168
      const char* scope = va_arg(ap, const char*);
1169
      const char* req_cnf = va_arg(ap, const char*);
1170
      rc = client_cli_get_rdsaad_access_token(instance, scope, req_cnf, token);
1171
      va_end(ap);
1172
    }
1173
    break;
1174
    case ACCESS_TOKEN_TYPE_AVD:
1175
      if (count != 0)
1176
        WLog_WARN(TAG,
1177
                  "ACCESS_TOKEN_TYPE_AVD expected 0 additional arguments, but got %" PRIuz
1178
                  ", ignoring",
1179
                  count);
1180
      rc = client_cli_get_avd_access_token(instance, token);
1181
      break;
1182
    default:
1183
      WLog_ERR(TAG, "Unexpected value for AccessTokenType [%" PRIuz "], aborting", tokenType);
1184
      break;
1185
  }
1186
1187
  if (!freerdp_settings_set_bool(instance->context->settings, FreeRDP_UseCommonStdioCallbacks,
1188
                                 saved))
1189
    return FALSE;
1190
  return rc;
1191
#endif
1192
0
}
1193
1194
BOOL client_common_get_access_token(freerdp* instance, const char* request, char** token)
1195
0
{
1196
#ifdef WITH_AAD
1197
  WINPR_ASSERT(request);
1198
  WINPR_ASSERT(token);
1199
1200
  BOOL ret = FALSE;
1201
  long resp_code = 0;
1202
  BYTE* response = NULL;
1203
  size_t response_length = 0;
1204
1205
  wLog* log = WLog_Get(TAG);
1206
1207
  const char* token_ep =
1208
      freerdp_utils_aad_get_wellknown_string(instance->context, AAD_WELLKNOWN_token_endpoint);
1209
  if (!freerdp_http_request(token_ep, request, &resp_code, &response, &response_length))
1210
  {
1211
    WLog_ERR(TAG, "access token request failed");
1212
    return FALSE;
1213
  }
1214
1215
  if (resp_code != HTTP_STATUS_OK)
1216
  {
1217
    char buffer[64] = { 0 };
1218
1219
    WLog_Print(log, WLOG_ERROR,
1220
               "Server unwilling to provide access token; returned status code %s",
1221
               freerdp_http_status_string_format(resp_code, buffer, sizeof(buffer)));
1222
    if (response_length > 0)
1223
      WLog_Print(log, WLOG_ERROR, "[status message] %s", response);
1224
    goto cleanup;
1225
  }
1226
1227
  *token = freerdp_utils_aad_get_access_token(log, (const char*)response, response_length);
1228
  if (*token)
1229
    ret = TRUE;
1230
1231
cleanup:
1232
  free(response);
1233
  return ret;
1234
#else
1235
0
  return FALSE;
1236
0
#endif
1237
0
}
1238
1239
SSIZE_T client_common_retry_dialog(freerdp* instance, const char* what, size_t current,
1240
                                   void* userarg)
1241
0
{
1242
0
  WINPR_UNUSED(instance);
1243
0
  WINPR_ASSERT(instance->context);
1244
0
  WINPR_UNUSED(userarg);
1245
0
  WINPR_ASSERT(instance);
1246
0
  WINPR_ASSERT(what);
1247
1248
0
  if ((strcmp(what, "arm-transport") != 0) && (strcmp(what, "connection") != 0))
1249
0
  {
1250
0
    WLog_ERR(TAG, "Unknown module %s, aborting", what);
1251
0
    return -1;
1252
0
  }
1253
1254
0
  if (current == 0)
1255
0
  {
1256
0
    if (strcmp(what, "arm-transport") == 0)
1257
0
      WLog_INFO(TAG, "[%s] Starting your VM. It may take up to 5 minutes", what);
1258
0
  }
1259
1260
0
  const rdpSettings* settings = instance->context->settings;
1261
0
  const BOOL enabled = freerdp_settings_get_bool(settings, FreeRDP_AutoReconnectionEnabled);
1262
0
  if (!enabled)
1263
0
  {
1264
0
    WLog_WARN(TAG, "Automatic reconnection disabled, terminating. Try to connect again later");
1265
0
    return -1;
1266
0
  }
1267
1268
0
  const size_t max = freerdp_settings_get_uint32(settings, FreeRDP_AutoReconnectMaxRetries);
1269
0
  const size_t delay = freerdp_settings_get_uint32(settings, FreeRDP_TcpConnectTimeout);
1270
0
  if (current >= max)
1271
0
  {
1272
0
    WLog_ERR(TAG,
1273
0
             "[%s] retries exceeded. Your VM failed to start. Try again later or contact your "
1274
0
             "tech support for help if this keeps happening.",
1275
0
             what);
1276
0
    return -1;
1277
0
  }
1278
1279
0
  WLog_INFO(TAG, "[%s] retry %" PRIuz "/%" PRIuz ", delaying %" PRIuz "ms before next attempt",
1280
0
            what, current, max, delay);
1281
0
  return WINPR_ASSERTING_INT_CAST(SSIZE_T, delay);
1282
0
}
1283
1284
BOOL client_auto_reconnect(freerdp* instance)
1285
0
{
1286
0
  return client_auto_reconnect_ex(instance, NULL);
1287
0
}
1288
1289
BOOL client_auto_reconnect_ex(freerdp* instance, BOOL (*window_events)(freerdp* instance))
1290
0
{
1291
0
  BOOL retry = TRUE;
1292
0
  UINT32 error = 0;
1293
0
  UINT32 numRetries = 0;
1294
0
  rdpSettings* settings = NULL;
1295
1296
0
  if (!instance)
1297
0
    return FALSE;
1298
1299
0
  WINPR_ASSERT(instance->context);
1300
1301
0
  settings = instance->context->settings;
1302
0
  WINPR_ASSERT(settings);
1303
1304
0
  const UINT32 maxRetries =
1305
0
      freerdp_settings_get_uint32(settings, FreeRDP_AutoReconnectMaxRetries);
1306
1307
  /* Only auto reconnect on network disconnects. */
1308
0
  error = freerdp_error_info(instance);
1309
0
  switch (error)
1310
0
  {
1311
0
    case ERRINFO_GRAPHICS_SUBSYSTEM_FAILED:
1312
      /* A network disconnect was detected */
1313
0
      WLog_WARN(TAG, "Disconnected by server hitting a bug or resource limit [%s]",
1314
0
                freerdp_get_error_info_string(error));
1315
0
      break;
1316
0
    case ERRINFO_SUCCESS:
1317
      /* A network disconnect was detected */
1318
0
      WLog_INFO(TAG, "Network disconnect!");
1319
0
      break;
1320
0
    default:
1321
0
      return FALSE;
1322
0
  }
1323
1324
0
  if (!freerdp_settings_get_bool(settings, FreeRDP_AutoReconnectionEnabled))
1325
0
  {
1326
    /* No auto-reconnect - just quit */
1327
0
    return FALSE;
1328
0
  }
1329
1330
0
  switch (freerdp_get_last_error(instance->context))
1331
0
  {
1332
0
    case FREERDP_ERROR_CONNECT_CANCELLED:
1333
0
      WLog_WARN(TAG, "Connection aborted by user");
1334
0
      return FALSE;
1335
0
    default:
1336
0
      break;
1337
0
  }
1338
1339
  /* Perform an auto-reconnect. */
1340
0
  while (retry)
1341
0
  {
1342
    /* Quit retrying if max retries has been exceeded */
1343
0
    if ((maxRetries > 0) && (numRetries++ >= maxRetries))
1344
0
    {
1345
0
      return FALSE;
1346
0
    }
1347
1348
    /* Attempt the next reconnect */
1349
0
    WLog_INFO(TAG, "Attempting reconnect (%" PRIu32 " of %" PRIu32 ")", numRetries, maxRetries);
1350
1351
0
    IFCALL(instance->RetryDialog, instance, "connection", numRetries, NULL);
1352
1353
0
    if (freerdp_reconnect(instance))
1354
0
      return TRUE;
1355
1356
0
    switch (freerdp_get_last_error(instance->context))
1357
0
    {
1358
0
      case FREERDP_ERROR_CONNECT_CANCELLED:
1359
0
        WLog_WARN(TAG, "Autoreconnect aborted by user");
1360
0
        return FALSE;
1361
0
      default:
1362
0
        break;
1363
0
    }
1364
0
    for (UINT32 x = 0; x < 50; x++)
1365
0
    {
1366
0
      if (!IFCALLRESULT(TRUE, window_events, instance))
1367
0
        return FALSE;
1368
1369
0
      Sleep(10);
1370
0
    }
1371
0
  }
1372
1373
0
  WLog_ERR(TAG, "Maximum reconnect retries exceeded");
1374
0
  return FALSE;
1375
0
}
1376
1377
int freerdp_client_common_stop(rdpContext* context)
1378
0
{
1379
0
  rdpClientContext* cctx = (rdpClientContext*)context;
1380
0
  WINPR_ASSERT(cctx);
1381
1382
0
  freerdp_abort_connect_context(&cctx->context);
1383
1384
0
  if (cctx->thread)
1385
0
  {
1386
0
    (void)WaitForSingleObject(cctx->thread, INFINITE);
1387
0
    (void)CloseHandle(cctx->thread);
1388
0
    cctx->thread = NULL;
1389
0
  }
1390
1391
0
  return 0;
1392
0
}
1393
1394
#if defined(CHANNEL_ENCOMSP_CLIENT)
1395
BOOL freerdp_client_encomsp_toggle_control(EncomspClientContext* encomsp)
1396
0
{
1397
0
  rdpClientContext* cctx = NULL;
1398
0
  BOOL state = 0;
1399
1400
0
  if (!encomsp)
1401
0
    return FALSE;
1402
1403
0
  cctx = (rdpClientContext*)encomsp->custom;
1404
1405
0
  state = cctx->controlToggle;
1406
0
  cctx->controlToggle = !cctx->controlToggle;
1407
0
  return freerdp_client_encomsp_set_control(encomsp, state);
1408
0
}
1409
1410
BOOL freerdp_client_encomsp_set_control(EncomspClientContext* encomsp, BOOL control)
1411
0
{
1412
0
  ENCOMSP_CHANGE_PARTICIPANT_CONTROL_LEVEL_PDU pdu = { 0 };
1413
1414
0
  if (!encomsp)
1415
0
    return FALSE;
1416
1417
0
  pdu.ParticipantId = encomsp->participantId;
1418
0
  pdu.Flags = ENCOMSP_REQUEST_VIEW;
1419
1420
0
  if (control)
1421
0
    pdu.Flags |= ENCOMSP_REQUEST_INTERACT;
1422
1423
0
  encomsp->ChangeParticipantControlLevel(encomsp, &pdu);
1424
1425
0
  return TRUE;
1426
0
}
1427
1428
static UINT
1429
client_encomsp_participant_created(EncomspClientContext* context,
1430
                                   const ENCOMSP_PARTICIPANT_CREATED_PDU* participantCreated)
1431
0
{
1432
0
  rdpClientContext* cctx = NULL;
1433
0
  rdpSettings* settings = NULL;
1434
0
  BOOL request = 0;
1435
1436
0
  if (!context || !context->custom || !participantCreated)
1437
0
    return ERROR_INVALID_PARAMETER;
1438
1439
0
  cctx = (rdpClientContext*)context->custom;
1440
0
  WINPR_ASSERT(cctx);
1441
1442
0
  settings = cctx->context.settings;
1443
0
  WINPR_ASSERT(settings);
1444
1445
0
  if (participantCreated->Flags & ENCOMSP_IS_PARTICIPANT)
1446
0
    context->participantId = participantCreated->ParticipantId;
1447
1448
0
  request = freerdp_settings_get_bool(settings, FreeRDP_RemoteAssistanceRequestControl);
1449
0
  if (request && (participantCreated->Flags & ENCOMSP_MAY_VIEW) &&
1450
0
      !(participantCreated->Flags & ENCOMSP_MAY_INTERACT))
1451
0
  {
1452
0
    if (!freerdp_client_encomsp_set_control(context, TRUE))
1453
0
      return ERROR_INTERNAL_ERROR;
1454
1455
    /* if auto-request-control setting is enabled then only request control once upon connect,
1456
     * otherwise it will auto request control again every time server turns off control which
1457
     * is a bit annoying */
1458
0
    if (!freerdp_settings_set_bool(settings, FreeRDP_RemoteAssistanceRequestControl, FALSE))
1459
0
      return ERROR_INTERNAL_ERROR;
1460
0
  }
1461
1462
0
  return CHANNEL_RC_OK;
1463
0
}
1464
1465
static void client_encomsp_init(rdpClientContext* cctx, EncomspClientContext* encomsp)
1466
0
{
1467
0
  cctx->encomsp = encomsp;
1468
0
  encomsp->custom = (void*)cctx;
1469
0
  encomsp->ParticipantCreated = client_encomsp_participant_created;
1470
0
}
1471
1472
static void client_encomsp_uninit(rdpClientContext* cctx, EncomspClientContext* encomsp)
1473
0
{
1474
0
  if (encomsp)
1475
0
  {
1476
0
    encomsp->custom = NULL;
1477
0
    encomsp->ParticipantCreated = NULL;
1478
0
  }
1479
1480
0
  if (cctx)
1481
0
    cctx->encomsp = NULL;
1482
0
}
1483
#endif
1484
1485
void freerdp_client_OnChannelConnectedEventHandler(void* context,
1486
                                                   const ChannelConnectedEventArgs* e)
1487
0
{
1488
0
  rdpClientContext* cctx = (rdpClientContext*)context;
1489
1490
0
  WINPR_ASSERT(cctx);
1491
0
  WINPR_ASSERT(e);
1492
1493
0
  if (0)
1494
0
  {
1495
0
  }
1496
0
#if defined(CHANNEL_AINPUT_CLIENT)
1497
0
  else if (strcmp(e->name, AINPUT_DVC_CHANNEL_NAME) == 0)
1498
0
    cctx->ainput = (AInputClientContext*)e->pInterface;
1499
0
#endif
1500
0
#if defined(CHANNEL_RDPEI_CLIENT)
1501
0
  else if (strcmp(e->name, RDPEI_DVC_CHANNEL_NAME) == 0)
1502
0
  {
1503
0
    cctx->rdpei = (RdpeiClientContext*)e->pInterface;
1504
0
  }
1505
0
#endif
1506
0
#if defined(CHANNEL_RDPGFX_CLIENT)
1507
0
  else if (strcmp(e->name, RDPGFX_DVC_CHANNEL_NAME) == 0)
1508
0
  {
1509
0
    gdi_graphics_pipeline_init(cctx->context.gdi, (RdpgfxClientContext*)e->pInterface);
1510
0
  }
1511
0
#endif
1512
0
#if defined(CHANNEL_GEOMETRY_CLIENT)
1513
0
  else if (strcmp(e->name, GEOMETRY_DVC_CHANNEL_NAME) == 0)
1514
0
  {
1515
0
    gdi_video_geometry_init(cctx->context.gdi, (GeometryClientContext*)e->pInterface);
1516
0
  }
1517
0
#endif
1518
0
#if defined(CHANNEL_VIDEO_CLIENT)
1519
0
  else if (strcmp(e->name, VIDEO_CONTROL_DVC_CHANNEL_NAME) == 0)
1520
0
  {
1521
0
    gdi_video_control_init(cctx->context.gdi, (VideoClientContext*)e->pInterface);
1522
0
  }
1523
0
  else if (strcmp(e->name, VIDEO_DATA_DVC_CHANNEL_NAME) == 0)
1524
0
  {
1525
0
    gdi_video_data_init(cctx->context.gdi, (VideoClientContext*)e->pInterface);
1526
0
  }
1527
0
#endif
1528
0
#if defined(CHANNEL_ENCOMSP_CLIENT)
1529
0
  else if (strcmp(e->name, ENCOMSP_SVC_CHANNEL_NAME) == 0)
1530
0
  {
1531
0
    client_encomsp_init(cctx, (EncomspClientContext*)e->pInterface);
1532
0
  }
1533
0
#endif
1534
0
}
1535
1536
void freerdp_client_OnChannelDisconnectedEventHandler(void* context,
1537
                                                      const ChannelDisconnectedEventArgs* e)
1538
0
{
1539
0
  rdpClientContext* cctx = (rdpClientContext*)context;
1540
1541
0
  WINPR_ASSERT(cctx);
1542
0
  WINPR_ASSERT(e);
1543
1544
0
  if (0)
1545
0
  {
1546
0
  }
1547
0
#if defined(CHANNEL_AINPUT_CLIENT)
1548
0
  else if (strcmp(e->name, AINPUT_DVC_CHANNEL_NAME) == 0)
1549
0
    cctx->ainput = NULL;
1550
0
#endif
1551
0
#if defined(CHANNEL_RDPEI_CLIENT)
1552
0
  else if (strcmp(e->name, RDPEI_DVC_CHANNEL_NAME) == 0)
1553
0
  {
1554
0
    cctx->rdpei = NULL;
1555
0
  }
1556
0
#endif
1557
0
#if defined(CHANNEL_RDPGFX_CLIENT)
1558
0
  else if (strcmp(e->name, RDPGFX_DVC_CHANNEL_NAME) == 0)
1559
0
  {
1560
0
    gdi_graphics_pipeline_uninit(cctx->context.gdi, (RdpgfxClientContext*)e->pInterface);
1561
0
  }
1562
0
#endif
1563
0
#if defined(CHANNEL_GEOMETRY_CLIENT)
1564
0
  else if (strcmp(e->name, GEOMETRY_DVC_CHANNEL_NAME) == 0)
1565
0
  {
1566
0
    gdi_video_geometry_uninit(cctx->context.gdi, (GeometryClientContext*)e->pInterface);
1567
0
  }
1568
0
#endif
1569
0
#if defined(CHANNEL_VIDEO_CLIENT)
1570
0
  else if (strcmp(e->name, VIDEO_CONTROL_DVC_CHANNEL_NAME) == 0)
1571
0
  {
1572
0
    gdi_video_control_uninit(cctx->context.gdi, (VideoClientContext*)e->pInterface);
1573
0
  }
1574
0
  else if (strcmp(e->name, VIDEO_DATA_DVC_CHANNEL_NAME) == 0)
1575
0
  {
1576
0
    gdi_video_data_uninit(cctx->context.gdi, (VideoClientContext*)e->pInterface);
1577
0
  }
1578
0
#endif
1579
0
#if defined(CHANNEL_ENCOMSP_CLIENT)
1580
0
  else if (strcmp(e->name, ENCOMSP_SVC_CHANNEL_NAME) == 0)
1581
0
  {
1582
0
    client_encomsp_uninit(cctx, (EncomspClientContext*)e->pInterface);
1583
0
  }
1584
0
#endif
1585
0
}
1586
1587
BOOL freerdp_client_send_wheel_event(rdpClientContext* cctx, UINT16 mflags)
1588
0
{
1589
0
  BOOL handled = FALSE;
1590
1591
0
  WINPR_ASSERT(cctx);
1592
1593
0
#if defined(CHANNEL_AINPUT_CLIENT)
1594
0
  if (cctx->ainput)
1595
0
  {
1596
0
    UINT rc = 0;
1597
0
    UINT64 flags = 0;
1598
0
    INT32 x = 0;
1599
0
    INT32 y = 0;
1600
0
    INT32 value = mflags & 0xFF;
1601
1602
0
    if (mflags & PTR_FLAGS_WHEEL_NEGATIVE)
1603
0
      value = -1 * (0x100 - value);
1604
1605
    /* We have discrete steps, scale this so we can also support high
1606
     * resolution wheels. */
1607
0
    value *= 0x10000;
1608
1609
0
    if (mflags & PTR_FLAGS_WHEEL)
1610
0
    {
1611
0
      flags |= AINPUT_FLAGS_WHEEL;
1612
0
      y = value;
1613
0
    }
1614
1615
0
    if (mflags & PTR_FLAGS_HWHEEL)
1616
0
    {
1617
0
      flags |= AINPUT_FLAGS_WHEEL;
1618
0
      x = value;
1619
0
    }
1620
1621
0
    WINPR_ASSERT(cctx->ainput->AInputSendInputEvent);
1622
0
    rc = cctx->ainput->AInputSendInputEvent(cctx->ainput, flags, x, y);
1623
0
    if (rc == CHANNEL_RC_OK)
1624
0
      handled = TRUE;
1625
0
  }
1626
0
#endif
1627
1628
0
  if (!handled)
1629
0
    freerdp_input_send_mouse_event(cctx->context.input, mflags, 0, 0);
1630
1631
0
  return TRUE;
1632
0
}
1633
1634
#if defined(CHANNEL_AINPUT_CLIENT)
1635
static INLINE BOOL ainput_send_diff_event(rdpClientContext* cctx, UINT64 flags, INT32 x, INT32 y)
1636
0
{
1637
0
  UINT rc = 0;
1638
1639
0
  WINPR_ASSERT(cctx);
1640
0
  WINPR_ASSERT(cctx->ainput);
1641
0
  WINPR_ASSERT(cctx->ainput->AInputSendInputEvent);
1642
1643
0
  rc = cctx->ainput->AInputSendInputEvent(cctx->ainput, flags, x, y);
1644
1645
0
  return rc == CHANNEL_RC_OK;
1646
0
}
1647
#endif
1648
1649
static bool button_pressed(const rdpClientContext* cctx)
1650
0
{
1651
0
  WINPR_ASSERT(cctx);
1652
0
  for (size_t x = 0; x < ARRAYSIZE(cctx->pressed_buttons); x++)
1653
0
  {
1654
0
    const BOOL cur = cctx->pressed_buttons[x];
1655
0
    if (cur)
1656
0
      return true;
1657
0
  }
1658
0
  return false;
1659
0
}
1660
1661
BOOL freerdp_client_send_button_event(rdpClientContext* cctx, BOOL relative, UINT16 mflags, INT32 x,
1662
                                      INT32 y)
1663
0
{
1664
0
  BOOL handled = FALSE;
1665
1666
0
  WINPR_ASSERT(cctx);
1667
1668
0
  if (mflags & PTR_FLAGS_BUTTON1)
1669
0
    cctx->pressed_buttons[0] = mflags & PTR_FLAGS_DOWN;
1670
0
  if (mflags & PTR_FLAGS_BUTTON2)
1671
0
    cctx->pressed_buttons[1] = mflags & PTR_FLAGS_DOWN;
1672
0
  if (mflags & PTR_FLAGS_BUTTON3)
1673
0
    cctx->pressed_buttons[2] = mflags & PTR_FLAGS_DOWN;
1674
1675
0
  if (((mflags & PTR_FLAGS_MOVE) != 0) &&
1676
0
      !freerdp_settings_get_bool(cctx->context.settings, FreeRDP_MouseMotion))
1677
0
  {
1678
0
    if (!button_pressed(cctx))
1679
0
      return TRUE;
1680
0
  }
1681
1682
0
  const BOOL haveRelative =
1683
0
      freerdp_settings_get_bool(cctx->context.settings, FreeRDP_HasRelativeMouseEvent);
1684
0
  if (relative && haveRelative)
1685
0
  {
1686
0
    return freerdp_input_send_rel_mouse_event(cctx->context.input, mflags,
1687
0
                                              WINPR_ASSERTING_INT_CAST(int16_t, x),
1688
0
                                              WINPR_ASSERTING_INT_CAST(int16_t, y));
1689
0
  }
1690
1691
0
#if defined(CHANNEL_AINPUT_CLIENT)
1692
0
  if (cctx->ainput)
1693
0
  {
1694
0
    UINT64 flags = 0;
1695
1696
0
    if (cctx->mouse_grabbed && freerdp_client_use_relative_mouse_events(cctx))
1697
0
      flags |= AINPUT_FLAGS_HAVE_REL;
1698
1699
0
    if (relative)
1700
0
      flags |= AINPUT_FLAGS_REL;
1701
1702
0
    if (mflags & PTR_FLAGS_DOWN)
1703
0
      flags |= AINPUT_FLAGS_DOWN;
1704
0
    if (mflags & PTR_FLAGS_BUTTON1)
1705
0
      flags |= AINPUT_FLAGS_BUTTON1;
1706
0
    if (mflags & PTR_FLAGS_BUTTON2)
1707
0
      flags |= AINPUT_FLAGS_BUTTON2;
1708
0
    if (mflags & PTR_FLAGS_BUTTON3)
1709
0
      flags |= AINPUT_FLAGS_BUTTON3;
1710
0
    if (mflags & PTR_FLAGS_MOVE)
1711
0
      flags |= AINPUT_FLAGS_MOVE;
1712
0
    handled = ainput_send_diff_event(cctx, flags, x, y);
1713
0
  }
1714
0
#endif
1715
1716
0
  if (!handled)
1717
0
  {
1718
0
    if (relative)
1719
0
    {
1720
0
      cctx->lastX += x;
1721
0
      cctx->lastY += y;
1722
0
      WLog_WARN(TAG, "Relative mouse input channel not available, sending absolute!");
1723
0
    }
1724
0
    else
1725
0
    {
1726
0
      cctx->lastX = x;
1727
0
      cctx->lastY = y;
1728
0
    }
1729
0
    freerdp_input_send_mouse_event(cctx->context.input, mflags, (UINT16)cctx->lastX,
1730
0
                                   (UINT16)cctx->lastY);
1731
0
  }
1732
0
  return TRUE;
1733
0
}
1734
1735
BOOL freerdp_client_send_extended_button_event(rdpClientContext* cctx, BOOL relative, UINT16 mflags,
1736
                                               INT32 x, INT32 y)
1737
0
{
1738
0
  BOOL handled = FALSE;
1739
0
  WINPR_ASSERT(cctx);
1740
1741
0
  if (mflags & PTR_XFLAGS_BUTTON1)
1742
0
    cctx->pressed_buttons[3] = mflags & PTR_XFLAGS_DOWN;
1743
0
  if (mflags & PTR_XFLAGS_BUTTON2)
1744
0
    cctx->pressed_buttons[4] = mflags & PTR_XFLAGS_DOWN;
1745
1746
0
  const BOOL haveRelative =
1747
0
      freerdp_settings_get_bool(cctx->context.settings, FreeRDP_HasRelativeMouseEvent);
1748
0
  if (relative && haveRelative)
1749
0
  {
1750
0
    return freerdp_input_send_rel_mouse_event(cctx->context.input, mflags,
1751
0
                                              WINPR_ASSERTING_INT_CAST(int16_t, x),
1752
0
                                              WINPR_ASSERTING_INT_CAST(int16_t, y));
1753
0
  }
1754
1755
0
#if defined(CHANNEL_AINPUT_CLIENT)
1756
0
  if (cctx->ainput)
1757
0
  {
1758
0
    UINT64 flags = 0;
1759
1760
0
    if (relative)
1761
0
      flags |= AINPUT_FLAGS_REL;
1762
0
    if (mflags & PTR_XFLAGS_DOWN)
1763
0
      flags |= AINPUT_FLAGS_DOWN;
1764
0
    if (mflags & PTR_XFLAGS_BUTTON1)
1765
0
      flags |= AINPUT_XFLAGS_BUTTON1;
1766
0
    if (mflags & PTR_XFLAGS_BUTTON2)
1767
0
      flags |= AINPUT_XFLAGS_BUTTON2;
1768
1769
0
    handled = ainput_send_diff_event(cctx, flags, x, y);
1770
0
  }
1771
0
#endif
1772
1773
0
  if (!handled)
1774
0
  {
1775
0
    if (relative)
1776
0
    {
1777
0
      cctx->lastX += x;
1778
0
      cctx->lastY += y;
1779
0
      WLog_WARN(TAG, "Relative mouse input channel not available, sending absolute!");
1780
0
    }
1781
0
    else
1782
0
    {
1783
0
      cctx->lastX = x;
1784
0
      cctx->lastY = y;
1785
0
    }
1786
0
    freerdp_input_send_extended_mouse_event(cctx->context.input, mflags, (UINT16)cctx->lastX,
1787
0
                                            (UINT16)cctx->lastY);
1788
0
  }
1789
1790
0
  return TRUE;
1791
0
}
1792
1793
static BOOL freerdp_handle_touch_up(rdpClientContext* cctx, const FreeRDP_TouchContact* contact)
1794
0
{
1795
0
  WINPR_ASSERT(cctx);
1796
0
  WINPR_ASSERT(contact);
1797
1798
0
#if defined(CHANNEL_RDPEI_CLIENT)
1799
0
  RdpeiClientContext* rdpei = cctx->rdpei;
1800
1801
0
  if (!rdpei)
1802
0
  {
1803
0
    UINT16 flags = 0;
1804
0
    flags |= PTR_FLAGS_BUTTON1;
1805
1806
0
    WINPR_ASSERT(contact->x <= UINT16_MAX);
1807
0
    WINPR_ASSERT(contact->y <= UINT16_MAX);
1808
0
    return freerdp_client_send_button_event(cctx, FALSE, flags, contact->x, contact->y);
1809
0
  }
1810
0
  else
1811
0
  {
1812
0
    int contactId = 0;
1813
1814
0
    if (rdpei->TouchRawEvent)
1815
0
    {
1816
0
      const UINT32 flags = RDPINPUT_CONTACT_FLAG_UP;
1817
0
      const UINT32 contactFlags = ((contact->flags & FREERDP_TOUCH_HAS_PRESSURE) != 0)
1818
0
                                      ? CONTACT_DATA_PRESSURE_PRESENT
1819
0
                                      : 0;
1820
      // Ensure contact position is unchanged from "engaged" to "out of range" state
1821
0
      rdpei->TouchRawEvent(rdpei, contact->id, contact->x, contact->y, &contactId,
1822
0
                           RDPINPUT_CONTACT_FLAG_UPDATE | RDPINPUT_CONTACT_FLAG_INRANGE |
1823
0
                               RDPINPUT_CONTACT_FLAG_INCONTACT,
1824
0
                           contactFlags, contact->pressure);
1825
0
      rdpei->TouchRawEvent(rdpei, contact->id, contact->x, contact->y, &contactId, flags,
1826
0
                           contactFlags, contact->pressure);
1827
0
    }
1828
0
    else
1829
0
    {
1830
0
      WINPR_ASSERT(rdpei->TouchEnd);
1831
0
      rdpei->TouchEnd(rdpei, contact->id, contact->x, contact->y, &contactId);
1832
0
    }
1833
0
  }
1834
#else
1835
  WLog_WARN(TAG, "Touch event detected but RDPEI support not compiled in. Recompile with "
1836
                 "-DWITH_CHANNELS=ON");
1837
#endif
1838
1839
0
  return TRUE;
1840
0
}
1841
1842
static BOOL freerdp_handle_touch_down(rdpClientContext* cctx, const FreeRDP_TouchContact* contact)
1843
0
{
1844
0
  WINPR_ASSERT(cctx);
1845
0
  WINPR_ASSERT(contact);
1846
1847
0
#if defined(CHANNEL_RDPEI_CLIENT)
1848
0
  RdpeiClientContext* rdpei = cctx->rdpei;
1849
1850
  // Emulate mouse click if touch is not possible, like in login screen
1851
0
  if (!rdpei)
1852
0
  {
1853
0
    UINT16 flags = 0;
1854
0
    flags |= PTR_FLAGS_DOWN;
1855
0
    flags |= PTR_FLAGS_MOVE;
1856
0
    flags |= PTR_FLAGS_BUTTON1;
1857
1858
0
    WINPR_ASSERT(contact->x <= UINT16_MAX);
1859
0
    WINPR_ASSERT(contact->y <= UINT16_MAX);
1860
0
    return freerdp_client_send_button_event(cctx, FALSE, flags, contact->x, contact->y);
1861
0
  }
1862
0
  else
1863
0
  {
1864
0
    int contactId = 0;
1865
1866
0
    if (rdpei->TouchRawEvent)
1867
0
    {
1868
0
      const UINT32 flags = RDPINPUT_CONTACT_FLAG_DOWN | RDPINPUT_CONTACT_FLAG_INRANGE |
1869
0
                           RDPINPUT_CONTACT_FLAG_INCONTACT;
1870
0
      const UINT32 contactFlags = ((contact->flags & FREERDP_TOUCH_HAS_PRESSURE) != 0)
1871
0
                                      ? CONTACT_DATA_PRESSURE_PRESENT
1872
0
                                      : 0;
1873
0
      rdpei->TouchRawEvent(rdpei, contact->id, contact->x, contact->y, &contactId, flags,
1874
0
                           contactFlags, contact->pressure);
1875
0
    }
1876
0
    else
1877
0
    {
1878
0
      WINPR_ASSERT(rdpei->TouchBegin);
1879
0
      rdpei->TouchBegin(rdpei, contact->id, contact->x, contact->y, &contactId);
1880
0
    }
1881
0
  }
1882
#else
1883
  WLog_WARN(TAG, "Touch event detected but RDPEI support not compiled in. Recompile with "
1884
                 "-DWITH_CHANNELS=ON");
1885
#endif
1886
1887
0
  return TRUE;
1888
0
}
1889
1890
static BOOL freerdp_handle_touch_motion(rdpClientContext* cctx, const FreeRDP_TouchContact* contact)
1891
0
{
1892
0
  WINPR_ASSERT(cctx);
1893
0
  WINPR_ASSERT(contact);
1894
1895
0
#if defined(CHANNEL_RDPEI_CLIENT)
1896
0
  RdpeiClientContext* rdpei = cctx->rdpei;
1897
1898
0
  if (!rdpei)
1899
0
  {
1900
0
    UINT16 flags = 0;
1901
0
    flags |= PTR_FLAGS_MOVE;
1902
1903
0
    WINPR_ASSERT(contact->x <= UINT16_MAX);
1904
0
    WINPR_ASSERT(contact->y <= UINT16_MAX);
1905
0
    return freerdp_client_send_button_event(cctx, FALSE, flags, contact->x, contact->y);
1906
0
  }
1907
0
  else
1908
0
  {
1909
0
    int contactId = 0;
1910
1911
0
    if (rdpei->TouchRawEvent)
1912
0
    {
1913
0
      const UINT32 flags = RDPINPUT_CONTACT_FLAG_UPDATE | RDPINPUT_CONTACT_FLAG_INRANGE |
1914
0
                           RDPINPUT_CONTACT_FLAG_INCONTACT;
1915
0
      const UINT32 contactFlags = ((contact->flags & FREERDP_TOUCH_HAS_PRESSURE) != 0)
1916
0
                                      ? CONTACT_DATA_PRESSURE_PRESENT
1917
0
                                      : 0;
1918
0
      rdpei->TouchRawEvent(rdpei, contact->id, contact->x, contact->y, &contactId, flags,
1919
0
                           contactFlags, contact->pressure);
1920
0
    }
1921
0
    else
1922
0
    {
1923
0
      WINPR_ASSERT(rdpei->TouchUpdate);
1924
0
      rdpei->TouchUpdate(rdpei, contact->id, contact->x, contact->y, &contactId);
1925
0
    }
1926
0
  }
1927
#else
1928
  WLog_WARN(TAG, "Touch event detected but RDPEI support not compiled in. Recompile with "
1929
                 "-DWITH_CHANNELS=ON");
1930
#endif
1931
1932
0
  return TRUE;
1933
0
}
1934
1935
static BOOL freerdp_client_touch_update(rdpClientContext* cctx, UINT32 flags, INT32 touchId,
1936
                                        UINT32 pressure, INT32 x, INT32 y,
1937
                                        FreeRDP_TouchContact* pcontact)
1938
0
{
1939
0
  WINPR_ASSERT(cctx);
1940
0
  WINPR_ASSERT(pcontact);
1941
1942
0
  for (size_t i = 0; i < ARRAYSIZE(cctx->contacts); i++)
1943
0
  {
1944
0
    FreeRDP_TouchContact* contact = &cctx->contacts[i];
1945
1946
0
    const BOOL newcontact = ((contact->id == 0) && ((flags & FREERDP_TOUCH_DOWN) != 0));
1947
0
    if (newcontact || (contact->id == touchId))
1948
0
    {
1949
0
      contact->id = touchId;
1950
0
      contact->flags = flags;
1951
0
      contact->pressure = pressure;
1952
0
      contact->x = x;
1953
0
      contact->y = y;
1954
1955
0
      *pcontact = *contact;
1956
1957
0
      const BOOL resetcontact = (flags & FREERDP_TOUCH_UP) != 0;
1958
0
      if (resetcontact)
1959
0
      {
1960
0
        FreeRDP_TouchContact empty = { 0 };
1961
0
        *contact = empty;
1962
0
      }
1963
0
      return TRUE;
1964
0
    }
1965
0
  }
1966
1967
0
  return FALSE;
1968
0
}
1969
1970
BOOL freerdp_client_handle_touch(rdpClientContext* cctx, UINT32 flags, INT32 finger,
1971
                                 UINT32 pressure, INT32 x, INT32 y)
1972
0
{
1973
0
  const UINT32 mask = FREERDP_TOUCH_DOWN | FREERDP_TOUCH_UP | FREERDP_TOUCH_MOTION;
1974
0
  WINPR_ASSERT(cctx);
1975
1976
0
  FreeRDP_TouchContact contact = { 0 };
1977
1978
0
  if (!freerdp_client_touch_update(cctx, flags, finger, pressure, x, y, &contact))
1979
0
    return FALSE;
1980
1981
0
  switch (flags & mask)
1982
0
  {
1983
0
    case FREERDP_TOUCH_DOWN:
1984
0
      return freerdp_handle_touch_down(cctx, &contact);
1985
0
    case FREERDP_TOUCH_UP:
1986
0
      return freerdp_handle_touch_up(cctx, &contact);
1987
0
    case FREERDP_TOUCH_MOTION:
1988
0
      return freerdp_handle_touch_motion(cctx, &contact);
1989
0
    default:
1990
0
      WLog_WARN(TAG, "Unhandled FreeRDPTouchEventType %d, ignoring", flags);
1991
0
      return FALSE;
1992
0
  }
1993
0
}
1994
1995
BOOL freerdp_client_load_channels(freerdp* instance)
1996
0
{
1997
0
  WINPR_ASSERT(instance);
1998
0
  WINPR_ASSERT(instance->context);
1999
2000
0
  if (!freerdp_client_load_addins(instance->context->channels, instance->context->settings))
2001
0
  {
2002
0
    WLog_ERR(TAG, "Failed to load addins [%08" PRIx32 "]", GetLastError());
2003
0
    return FALSE;
2004
0
  }
2005
0
  return TRUE;
2006
0
}
2007
2008
int client_cli_logon_error_info(freerdp* instance, UINT32 data, UINT32 type)
2009
0
{
2010
0
  const char* str_data = freerdp_get_logon_error_info_data(data);
2011
0
  const char* str_type = freerdp_get_logon_error_info_type(type);
2012
2013
0
  if (!instance || !instance->context)
2014
0
    return -1;
2015
2016
0
  WLog_INFO(TAG, "Logon Error Info %s [%s]", str_data, str_type);
2017
0
  return 1;
2018
0
}
2019
2020
static FreeRDP_PenDevice* freerdp_client_get_pen(rdpClientContext* cctx, INT32 deviceid,
2021
                                                 size_t* pos)
2022
0
{
2023
0
  WINPR_ASSERT(cctx);
2024
2025
0
  for (size_t i = 0; i < ARRAYSIZE(cctx->pens); i++)
2026
0
  {
2027
0
    FreeRDP_PenDevice* pen = &cctx->pens[i];
2028
0
    if (deviceid == pen->deviceid)
2029
0
    {
2030
0
      if (pos)
2031
0
        *pos = i;
2032
0
      return pen;
2033
0
    }
2034
0
  }
2035
0
  return NULL;
2036
0
}
2037
2038
static BOOL freerdp_client_register_pen(rdpClientContext* cctx, UINT32 flags, INT32 deviceid,
2039
                                        double pressure)
2040
0
{
2041
0
  static const INT32 null_deviceid = 0;
2042
2043
0
  WINPR_ASSERT(cctx);
2044
0
  WINPR_ASSERT((flags & FREERDP_PEN_REGISTER) != 0);
2045
0
  if (freerdp_client_is_pen(cctx, deviceid))
2046
0
  {
2047
0
    WLog_WARN(TAG, "trying to double register pen device %" PRId32, deviceid);
2048
0
    return FALSE;
2049
0
  }
2050
2051
0
  size_t pos = 0;
2052
0
  FreeRDP_PenDevice* pen = freerdp_client_get_pen(cctx, null_deviceid, &pos);
2053
0
  if (pen)
2054
0
  {
2055
0
    const FreeRDP_PenDevice empty = { 0 };
2056
0
    *pen = empty;
2057
2058
0
    pen->deviceid = deviceid;
2059
0
    pen->max_pressure = pressure;
2060
0
    pen->flags = flags;
2061
2062
0
    WLog_DBG(TAG, "registered pen at index %" PRIuz, pos);
2063
0
    return TRUE;
2064
0
  }
2065
2066
0
  WLog_WARN(TAG, "No free slot for an additional pen device, skipping");
2067
0
  return TRUE;
2068
0
}
2069
2070
BOOL freerdp_client_handle_pen(rdpClientContext* cctx, UINT32 flags, INT32 deviceid, ...)
2071
0
{
2072
0
  if ((flags & FREERDP_PEN_REGISTER) != 0)
2073
0
  {
2074
0
    va_list args;
2075
2076
0
    va_start(args, deviceid);
2077
0
    double pressure = va_arg(args, double);
2078
0
    va_end(args);
2079
0
    return freerdp_client_register_pen(cctx, flags, deviceid, pressure);
2080
0
  }
2081
0
  size_t pos = 0;
2082
0
  FreeRDP_PenDevice* pen = freerdp_client_get_pen(cctx, deviceid, &pos);
2083
0
  if (!pen)
2084
0
  {
2085
0
    WLog_WARN(TAG, "unregistered pen device %" PRId32 " event 0x%08" PRIx32, deviceid, flags);
2086
0
    return FALSE;
2087
0
  }
2088
2089
0
  UINT32 fieldFlags = RDPINPUT_PEN_CONTACT_PENFLAGS_PRESENT;
2090
0
  UINT32 penFlags =
2091
0
      ((pen->flags & FREERDP_PEN_IS_INVERTED) != 0) ? RDPINPUT_PEN_FLAG_INVERTED : 0;
2092
2093
0
  RdpeiClientContext* rdpei = cctx->rdpei;
2094
0
  WINPR_ASSERT(rdpei);
2095
2096
0
  UINT32 normalizedpressure = 1024;
2097
0
  INT32 x = 0;
2098
0
  INT32 y = 0;
2099
0
  UINT16 rotation = 0;
2100
0
  INT16 tiltX = 0;
2101
0
  INT16 tiltY = 0;
2102
0
  va_list args;
2103
0
  va_start(args, deviceid);
2104
2105
0
  x = va_arg(args, INT32);
2106
0
  y = va_arg(args, INT32);
2107
0
  if ((flags & FREERDP_PEN_HAS_PRESSURE) != 0)
2108
0
  {
2109
0
    const double pressure = va_arg(args, double);
2110
0
    const double np = (pressure * 1024.0) / pen->max_pressure;
2111
0
    normalizedpressure = (UINT32)lround(np);
2112
0
    WLog_DBG(TAG, "pen pressure %lf -> %" PRIu32, pressure, normalizedpressure);
2113
0
    fieldFlags |= RDPINPUT_PEN_CONTACT_PRESSURE_PRESENT;
2114
0
  }
2115
0
  if ((flags & FREERDP_PEN_HAS_ROTATION) != 0)
2116
0
  {
2117
0
    const unsigned arg = va_arg(args, unsigned);
2118
0
    rotation = WINPR_ASSERTING_INT_CAST(UINT16, arg);
2119
0
    fieldFlags |= RDPINPUT_PEN_CONTACT_ROTATION_PRESENT;
2120
0
  }
2121
0
  if ((flags & FREERDP_PEN_HAS_TILTX) != 0)
2122
0
  {
2123
0
    const int arg = va_arg(args, int);
2124
0
    tiltX = WINPR_ASSERTING_INT_CAST(INT16, arg);
2125
0
    fieldFlags |= RDPINPUT_PEN_CONTACT_TILTX_PRESENT;
2126
0
  }
2127
0
  if ((flags & FREERDP_PEN_HAS_TILTY) != 0)
2128
0
  {
2129
0
    const int arg = va_arg(args, int);
2130
0
    tiltY = WINPR_ASSERTING_INT_CAST(INT16, arg);
2131
0
    fieldFlags |= RDPINPUT_PEN_CONTACT_TILTY_PRESENT;
2132
0
  }
2133
0
  va_end(args);
2134
2135
0
  if ((flags & FREERDP_PEN_PRESS) != 0)
2136
0
  {
2137
    // Ensure that only one button is pressed
2138
0
    if (pen->pressed)
2139
0
      flags = FREERDP_PEN_MOTION |
2140
0
              (flags & (UINT32) ~(FREERDP_PEN_PRESS | FREERDP_PEN_BARREL_PRESSED));
2141
0
    else if ((flags & FREERDP_PEN_BARREL_PRESSED) != 0)
2142
0
      pen->flags |= FREERDP_PEN_BARREL_PRESSED;
2143
0
  }
2144
0
  else if ((flags & FREERDP_PEN_RELEASE) != 0)
2145
0
  {
2146
0
    if (!pen->pressed ||
2147
0
        ((flags & FREERDP_PEN_BARREL_PRESSED) ^ (pen->flags & FREERDP_PEN_BARREL_PRESSED)))
2148
0
      flags = FREERDP_PEN_MOTION |
2149
0
              (flags & (UINT32) ~(FREERDP_PEN_RELEASE | FREERDP_PEN_BARREL_PRESSED));
2150
0
    else
2151
0
      pen->flags &= (UINT32)~FREERDP_PEN_BARREL_PRESSED;
2152
0
  }
2153
2154
0
  flags |= pen->flags;
2155
0
  if ((flags & FREERDP_PEN_ERASER_PRESSED) != 0)
2156
0
    penFlags |= RDPINPUT_PEN_FLAG_ERASER_PRESSED;
2157
0
  if ((flags & FREERDP_PEN_BARREL_PRESSED) != 0)
2158
0
    penFlags |= RDPINPUT_PEN_FLAG_BARREL_PRESSED;
2159
2160
0
  pen->last_x = x;
2161
0
  pen->last_y = y;
2162
0
  if ((flags & FREERDP_PEN_PRESS) != 0)
2163
0
  {
2164
0
    WLog_DBG(TAG, "Pen press %" PRId32, deviceid);
2165
0
    pen->hovering = FALSE;
2166
0
    pen->pressed = TRUE;
2167
2168
0
    WINPR_ASSERT(rdpei->PenBegin);
2169
0
    const UINT rc = rdpei->PenBegin(rdpei, deviceid, fieldFlags, x, y, penFlags,
2170
0
                                    normalizedpressure, rotation, tiltX, tiltY);
2171
0
    return rc == CHANNEL_RC_OK;
2172
0
  }
2173
0
  else if ((flags & FREERDP_PEN_MOTION) != 0)
2174
0
  {
2175
0
    UINT rc = ERROR_INTERNAL_ERROR;
2176
0
    if (pen->pressed)
2177
0
    {
2178
0
      WLog_DBG(TAG, "Pen update %" PRId32, deviceid);
2179
2180
      // TODO: what if no rotation is supported but tilt is?
2181
0
      WINPR_ASSERT(rdpei->PenUpdate);
2182
0
      rc = rdpei->PenUpdate(rdpei, deviceid, fieldFlags, x, y, penFlags, normalizedpressure,
2183
0
                            rotation, tiltX, tiltY);
2184
0
    }
2185
0
    else if (pen->hovering)
2186
0
    {
2187
0
      WLog_DBG(TAG, "Pen hover update %" PRId32, deviceid);
2188
2189
0
      WINPR_ASSERT(rdpei->PenHoverUpdate);
2190
0
      rc = rdpei->PenHoverUpdate(rdpei, deviceid, RDPINPUT_PEN_CONTACT_PENFLAGS_PRESENT, x, y,
2191
0
                                 penFlags, normalizedpressure, rotation, tiltX, tiltY);
2192
0
    }
2193
0
    else
2194
0
    {
2195
0
      WLog_DBG(TAG, "Pen hover begin %" PRId32, deviceid);
2196
0
      pen->hovering = TRUE;
2197
2198
0
      WINPR_ASSERT(rdpei->PenHoverBegin);
2199
0
      rc = rdpei->PenHoverBegin(rdpei, deviceid, RDPINPUT_PEN_CONTACT_PENFLAGS_PRESENT, x, y,
2200
0
                                penFlags, normalizedpressure, rotation, tiltX, tiltY);
2201
0
    }
2202
0
    return rc == CHANNEL_RC_OK;
2203
0
  }
2204
0
  else if ((flags & FREERDP_PEN_RELEASE) != 0)
2205
0
  {
2206
0
    WLog_DBG(TAG, "Pen release %" PRId32, deviceid);
2207
0
    pen->pressed = FALSE;
2208
0
    pen->hovering = TRUE;
2209
2210
0
    WINPR_ASSERT(rdpei->PenUpdate);
2211
0
    const UINT rc = rdpei->PenUpdate(rdpei, deviceid, fieldFlags, x, y, penFlags,
2212
0
                                     normalizedpressure, rotation, tiltX, tiltY);
2213
0
    if (rc != CHANNEL_RC_OK)
2214
0
      return FALSE;
2215
0
    WINPR_ASSERT(rdpei->PenEnd);
2216
0
    const UINT re = rdpei->PenEnd(rdpei, deviceid, RDPINPUT_PEN_CONTACT_PENFLAGS_PRESENT, x, y,
2217
0
                                  penFlags, normalizedpressure, rotation, tiltX, tiltY);
2218
0
    return re == CHANNEL_RC_OK;
2219
0
  }
2220
2221
0
  WLog_WARN(TAG, "Invalid pen %" PRId32 " flags 0x%08" PRIx32, deviceid, flags);
2222
0
  return FALSE;
2223
0
}
2224
2225
BOOL freerdp_client_pen_cancel_all(rdpClientContext* cctx)
2226
0
{
2227
0
  WINPR_ASSERT(cctx);
2228
2229
0
  RdpeiClientContext* rdpei = cctx->rdpei;
2230
2231
0
  if (!rdpei)
2232
0
    return FALSE;
2233
2234
0
  for (size_t i = 0; i < ARRAYSIZE(cctx->pens); i++)
2235
0
  {
2236
0
    FreeRDP_PenDevice* pen = &cctx->pens[i];
2237
0
    if (pen->hovering)
2238
0
    {
2239
0
      WLog_DBG(TAG, "unhover pen %" PRId32, pen->deviceid);
2240
0
      pen->hovering = FALSE;
2241
0
      rdpei->PenHoverCancel(rdpei, pen->deviceid, 0, pen->last_x, pen->last_y);
2242
0
    }
2243
0
  }
2244
0
  return TRUE;
2245
0
}
2246
2247
BOOL freerdp_client_is_pen(rdpClientContext* cctx, INT32 deviceid)
2248
0
{
2249
0
  WINPR_ASSERT(cctx);
2250
2251
0
  if (deviceid == 0)
2252
0
    return FALSE;
2253
2254
0
  for (size_t x = 0; x < ARRAYSIZE(cctx->pens); x++)
2255
0
  {
2256
0
    const FreeRDP_PenDevice* pen = &cctx->pens[x];
2257
0
    if (pen->deviceid == deviceid)
2258
0
      return TRUE;
2259
0
  }
2260
2261
0
  return FALSE;
2262
0
}
2263
2264
BOOL freerdp_client_use_relative_mouse_events(rdpClientContext* ccontext)
2265
0
{
2266
0
  WINPR_ASSERT(ccontext);
2267
2268
0
  const rdpSettings* settings = ccontext->context.settings;
2269
0
  const BOOL useRelative = freerdp_settings_get_bool(settings, FreeRDP_MouseUseRelativeMove);
2270
0
  const BOOL haveRelative = freerdp_settings_get_bool(settings, FreeRDP_HasRelativeMouseEvent);
2271
0
  BOOL ainput = FALSE;
2272
0
#if defined(CHANNEL_AINPUT_CLIENT)
2273
0
  ainput = ccontext->ainput != NULL;
2274
0
#endif
2275
2276
0
  return useRelative && (haveRelative || ainput);
2277
0
}
2278
2279
#if defined(WITH_AAD)
2280
WINPR_ATTR_MALLOC(free, 1)
2281
static char* get_redirect_uri(const rdpSettings* settings)
2282
{
2283
  char* redirect_uri = NULL;
2284
  const bool cli = freerdp_settings_get_bool(settings, FreeRDP_UseCommonStdioCallbacks);
2285
  if (cli)
2286
  {
2287
    const char* redirect_fmt =
2288
        freerdp_settings_get_string(settings, FreeRDP_GatewayAvdAccessAadFormat);
2289
    const BOOL useTenant = freerdp_settings_get_bool(settings, FreeRDP_GatewayAvdUseTenantid);
2290
    const char* tenantid = "common";
2291
    if (useTenant)
2292
      tenantid = freerdp_settings_get_string(settings, FreeRDP_GatewayAvdAadtenantid);
2293
2294
    if (tenantid && redirect_fmt)
2295
    {
2296
      const char* url =
2297
          freerdp_settings_get_string(settings, FreeRDP_GatewayAzureActiveDirectory);
2298
2299
      size_t redirect_len = 0;
2300
      winpr_asprintf(&redirect_uri, &redirect_len, redirect_fmt, url, tenantid);
2301
    }
2302
  }
2303
  else
2304
  {
2305
    const char* client_id = freerdp_settings_get_string(settings, FreeRDP_GatewayAvdClientID);
2306
    const char* redirect_fmt =
2307
        freerdp_settings_get_string(settings, FreeRDP_GatewayAvdAccessTokenFormat);
2308
2309
    size_t redirect_len = 0;
2310
    winpr_asprintf(&redirect_uri, &redirect_len, redirect_fmt, client_id);
2311
  }
2312
  return redirect_uri;
2313
}
2314
2315
static char* avd_auth_request(rdpClientContext* cctx, WINPR_ATTR_UNUSED va_list ap)
2316
{
2317
  const rdpSettings* settings = cctx->context.settings;
2318
  const char* client_id = freerdp_settings_get_string(settings, FreeRDP_GatewayAvdClientID);
2319
  const char* ep = freerdp_utils_aad_get_wellknown_string(&cctx->context,
2320
                                                          AAD_WELLKNOWN_authorization_endpoint);
2321
  const char* scope = freerdp_settings_get_string(settings, FreeRDP_GatewayAvdScope);
2322
2323
  if (!client_id || !ep || !scope)
2324
    return NULL;
2325
2326
  char* redirect_uri = get_redirect_uri(settings);
2327
  if (!redirect_uri)
2328
    return NULL;
2329
2330
  char* url = NULL;
2331
  size_t urllen = 0;
2332
  winpr_asprintf(&url, &urllen, "%s?client_id=%s&response_type=code&scope=%s&redirect_uri=%s", ep,
2333
                 client_id, scope, redirect_uri);
2334
  free(redirect_uri);
2335
  return url;
2336
}
2337
2338
static char* avd_token_request(rdpClientContext* cctx, WINPR_ATTR_UNUSED va_list ap)
2339
{
2340
  const rdpSettings* settings = cctx->context.settings;
2341
  const char* client_id = freerdp_settings_get_string(settings, FreeRDP_GatewayAvdClientID);
2342
  const char* ep = freerdp_utils_aad_get_wellknown_string(&cctx->context,
2343
                                                          AAD_WELLKNOWN_authorization_endpoint);
2344
  const char* scope = freerdp_settings_get_string(settings, FreeRDP_GatewayAvdScope);
2345
2346
  if (!client_id || !ep || !scope)
2347
    return NULL;
2348
2349
  char* redirect_uri = get_redirect_uri(settings);
2350
  if (!redirect_uri)
2351
    return NULL;
2352
2353
  char* url = NULL;
2354
  size_t urllen = 0;
2355
2356
  const char* code = va_arg(ap, const char*);
2357
  winpr_asprintf(&url, &urllen,
2358
                 "grant_type=authorization_code&code=%s&client_id=%s&scope=%s&redirect_uri=%s",
2359
                 code, client_id, scope, redirect_uri);
2360
  free(redirect_uri);
2361
  return url;
2362
}
2363
2364
static char* aad_auth_request(rdpClientContext* cctx, WINPR_ATTR_UNUSED va_list ap)
2365
{
2366
  const rdpSettings* settings = cctx->context.settings;
2367
  char* url = NULL;
2368
  size_t urllen = 0;
2369
  char* redirect_uri = get_redirect_uri(settings);
2370
2371
  const char* client_id = freerdp_settings_get_string(settings, FreeRDP_GatewayAvdClientID);
2372
  if (!client_id || !redirect_uri)
2373
    goto cleanup;
2374
  const char* scope = va_arg(ap, const char*);
2375
  if (!scope)
2376
    goto cleanup;
2377
2378
  const char* ep = freerdp_utils_aad_get_wellknown_string(&cctx->context,
2379
                                                          AAD_WELLKNOWN_authorization_endpoint);
2380
2381
  winpr_asprintf(&url, &urllen, "%s?client_id=%s&response_type=code&scope=%s&redirect_uri=%s", ep,
2382
                 client_id, scope, redirect_uri);
2383
cleanup:
2384
  free(redirect_uri);
2385
  return url;
2386
}
2387
2388
static char* aad_token_request(rdpClientContext* cctx, WINPR_ATTR_UNUSED va_list ap)
2389
{
2390
  const rdpSettings* settings = cctx->context.settings;
2391
  const char* client_id = freerdp_settings_get_string(settings, FreeRDP_GatewayAvdClientID);
2392
  const char* ep = freerdp_utils_aad_get_wellknown_string(&cctx->context,
2393
                                                          AAD_WELLKNOWN_authorization_endpoint);
2394
  const char* scope = va_arg(ap, const char*);
2395
  const char* code = va_arg(ap, const char*);
2396
  const char* req_cnf = va_arg(ap, const char*);
2397
2398
  if (!client_id || !ep || !scope || !code || !req_cnf)
2399
    return NULL;
2400
2401
  char* redirect_uri = get_redirect_uri(settings);
2402
  if (!redirect_uri)
2403
    return NULL;
2404
2405
  char* url = NULL;
2406
  size_t urllen = 0;
2407
2408
  winpr_asprintf(
2409
      &url, &urllen,
2410
      "grant_type=authorization_code&code=%s&client_id=%s&scope=%s&redirect_uri=%s&req_cnf=%s",
2411
      code, client_id, scope, redirect_uri, req_cnf);
2412
  free(redirect_uri);
2413
  return url;
2414
}
2415
#endif
2416
2417
char* freerdp_client_get_aad_url(rdpClientContext* cctx, freerdp_client_aad_type type, ...)
2418
0
{
2419
0
  WINPR_ASSERT(cctx);
2420
0
  char* str = NULL;
2421
2422
0
  va_list ap;
2423
0
  va_start(ap, type);
2424
0
  switch (type)
2425
0
  {
2426
#if defined(WITH_AAD)
2427
    case FREERDP_CLIENT_AAD_AUTH_REQUEST:
2428
      str = aad_auth_request(cctx, ap);
2429
      break;
2430
    case FREERDP_CLIENT_AAD_TOKEN_REQUEST:
2431
      str = aad_token_request(cctx, ap);
2432
      break;
2433
    case FREERDP_CLIENT_AAD_AVD_AUTH_REQUEST:
2434
      str = avd_auth_request(cctx, ap);
2435
      break;
2436
    case FREERDP_CLIENT_AAD_AVD_TOKEN_REQUEST:
2437
      str = avd_token_request(cctx, ap);
2438
      break;
2439
#endif
2440
0
    default:
2441
0
      break;
2442
0
  }
2443
0
  va_end(ap);
2444
0
  return str;
2445
0
}