Coverage Report

Created: 2026-05-11 07:01

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/FreeRDP/libfreerdp/core/utils.c
Line
Count
Source
1
/**
2
 * FreeRDP: A Remote Desktop Protocol Implementation
3
 * Terminal Server Gateway (utils)
4
 *
5
 * Copyright 2021 Armin Novak <armin.novak@thincast.com>
6
 * Copyright 2021 Thincast Technologies GmbH
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 <freerdp/config.h>
22
23
#include "settings.h"
24
25
#include <winpr/assert.h>
26
27
#include <freerdp/freerdp.h>
28
#include <freerdp/channels/cliprdr.h>
29
#include <freerdp/channels/rdpdr.h>
30
31
#include <freerdp/log.h>
32
#define TAG FREERDP_TAG("core.gateway.utils")
33
34
#include "utils.h"
35
36
#include "../core/rdp.h"
37
38
BOOL utils_str_copy(const char* value, char** dst)
39
0
{
40
0
  WINPR_ASSERT(dst);
41
42
0
  free(*dst);
43
0
  *dst = nullptr;
44
0
  if (!value)
45
0
    return TRUE;
46
47
0
  (*dst) = _strdup(value);
48
0
  return (*dst) != nullptr;
49
0
}
50
51
static BOOL utils_copy_smartcard_settings(const rdpSettings* settings, rdpSettings* origSettings)
52
0
{
53
  /* update original settings with provided smart card settings */
54
0
  origSettings->SmartcardLogon = settings->SmartcardLogon;
55
0
  origSettings->PasswordIsSmartcardPin = settings->PasswordIsSmartcardPin;
56
0
  if (!utils_str_copy(settings->ReaderName, &origSettings->ReaderName))
57
0
    return FALSE;
58
0
  if (!utils_str_copy(settings->CspName, &origSettings->CspName))
59
0
    return FALSE;
60
0
  if (!utils_str_copy(settings->ContainerName, &origSettings->ContainerName))
61
0
    return FALSE;
62
63
0
  return TRUE;
64
0
}
65
66
auth_status utils_authenticate_gateway(freerdp* instance, rdp_auth_reason reason)
67
0
{
68
0
  rdpSettings* settings = nullptr;
69
0
  rdpSettings* origSettings = nullptr;
70
0
  BOOL prompt = FALSE;
71
0
  BOOL proceed = 0;
72
73
0
  WINPR_ASSERT(instance);
74
0
  WINPR_ASSERT(instance->context);
75
0
  WINPR_ASSERT(instance->context->settings);
76
0
  WINPR_ASSERT(instance->context->rdp);
77
0
  WINPR_ASSERT(instance->context->rdp->originalSettings);
78
79
0
  settings = instance->context->settings;
80
0
  origSettings = instance->context->rdp->originalSettings;
81
82
0
  if (freerdp_shall_disconnect_context(instance->context))
83
0
    return AUTH_FAILED;
84
85
0
  if (utils_str_is_empty(freerdp_settings_get_string(settings, FreeRDP_GatewayPassword)))
86
0
    prompt = TRUE;
87
0
  if (utils_str_is_empty(freerdp_settings_get_string(settings, FreeRDP_GatewayUsername)))
88
0
    prompt = TRUE;
89
90
0
  if (!prompt)
91
0
  {
92
0
    if (!utils_sync_credentials(settings, FALSE))
93
0
      return AUTH_FAILED;
94
0
    return AUTH_SKIP;
95
0
  }
96
97
0
#if !defined(WITHOUT_FREERDP_3x_DEPRECATED)
98
0
  WINPR_PRAGMA_DIAG_PUSH
99
0
  WINPR_PRAGMA_DIAG_IGNORED_DEPRECATED_DECL
100
0
#endif
101
102
#if defined(WITHOUT_FREERDP_3x_DEPRECATED)
103
  if (!instance->AuthenticateEx)
104
    return AUTH_NO_CREDENTIALS;
105
#else
106
0
  if (!instance->GatewayAuthenticate && !instance->AuthenticateEx)
107
0
    return AUTH_NO_CREDENTIALS;
108
109
0
  if (!instance->GatewayAuthenticate)
110
0
#endif
111
0
  {
112
0
    proceed =
113
0
        instance->AuthenticateEx(instance, &settings->GatewayUsername,
114
0
                                 &settings->GatewayPassword, &settings->GatewayDomain, reason);
115
0
    if (!proceed)
116
0
      return AUTH_CANCELLED;
117
0
  }
118
0
#if !defined(WITHOUT_FREERDP_3x_DEPRECATED)
119
0
  else
120
0
  {
121
0
    proceed =
122
0
        instance->GatewayAuthenticate(instance, &settings->GatewayUsername,
123
0
                                      &settings->GatewayPassword, &settings->GatewayDomain);
124
0
    if (!proceed)
125
0
      return AUTH_CANCELLED;
126
0
  }
127
0
#endif
128
129
0
#if !defined(WITHOUT_FREERDP_3x_DEPRECATED)
130
0
  WINPR_PRAGMA_DIAG_POP
131
0
#endif
132
133
0
  if (utils_str_is_empty(settings->GatewayUsername) ||
134
0
      utils_str_is_empty(settings->GatewayPassword))
135
0
    return AUTH_NO_CREDENTIALS;
136
137
0
  if (!utils_sync_credentials(settings, FALSE))
138
0
    return AUTH_FAILED;
139
140
  /* update original settings with provided user credentials */
141
0
  if (!utils_str_copy(settings->GatewayUsername, &origSettings->GatewayUsername))
142
0
    return AUTH_FAILED;
143
0
  if (!utils_str_copy(settings->GatewayDomain, &origSettings->GatewayDomain))
144
0
    return AUTH_FAILED;
145
0
  if (!utils_str_copy(settings->GatewayPassword, &origSettings->GatewayPassword))
146
0
    return AUTH_FAILED;
147
0
  if (!utils_sync_credentials(origSettings, FALSE))
148
0
    return AUTH_FAILED;
149
150
0
  if (!utils_copy_smartcard_settings(settings, origSettings))
151
0
    return AUTH_FAILED;
152
153
0
  return AUTH_SUCCESS;
154
0
}
155
156
auth_status utils_authenticate(freerdp* instance, rdp_auth_reason reason, BOOL override)
157
0
{
158
0
  rdpSettings* settings = nullptr;
159
0
  rdpSettings* origSettings = nullptr;
160
0
  BOOL prompt = !override;
161
0
  BOOL proceed = 0;
162
163
0
  WINPR_ASSERT(instance);
164
0
  WINPR_ASSERT(instance->context);
165
0
  WINPR_ASSERT(instance->context->settings);
166
0
  WINPR_ASSERT(instance->context->rdp);
167
0
  WINPR_ASSERT(instance->context->rdp->originalSettings);
168
169
0
  settings = instance->context->settings;
170
0
  origSettings = instance->context->rdp->originalSettings;
171
172
0
  if (freerdp_shall_disconnect_context(instance->context))
173
0
    return AUTH_FAILED;
174
175
0
  if (settings->ConnectChildSession)
176
0
    return AUTH_NO_CREDENTIALS;
177
178
  /* Ask for auth data if no or an empty username was specified or no password was given */
179
0
  if (utils_str_is_empty(freerdp_settings_get_string(settings, FreeRDP_Username)) ||
180
0
      (settings->Password == nullptr && settings->RedirectionPassword == nullptr))
181
0
    prompt = TRUE;
182
183
0
  if (!prompt)
184
0
    return AUTH_SKIP;
185
186
0
  switch (reason)
187
0
  {
188
0
    case AUTH_RDP:
189
0
    case AUTH_TLS:
190
0
      if (settings->SmartcardLogon)
191
0
      {
192
0
        if (!utils_str_is_empty(settings->Password))
193
0
        {
194
0
          WLog_INFO(TAG, "Authentication via smartcard");
195
0
          return AUTH_SUCCESS;
196
0
        }
197
0
        reason = AUTH_SMARTCARD_PIN;
198
0
      }
199
0
      break;
200
0
    case AUTH_NLA:
201
0
      if (settings->SmartcardLogon)
202
0
        reason = AUTH_SMARTCARD_PIN;
203
0
      break;
204
0
    case AUTH_RDSTLS:
205
0
    default:
206
0
      break;
207
0
  }
208
209
0
#if !defined(WITHOUT_FREERDP_3x_DEPRECATED)
210
0
  WINPR_PRAGMA_DIAG_PUSH
211
0
  WINPR_PRAGMA_DIAG_IGNORED_DEPRECATED_DECL
212
0
#endif
213
214
  /* If no callback is specified still continue connection */
215
#if defined(WITHOUT_FREERDP_3x_DEPRECATED)
216
  if (!instance->AuthenticateEx)
217
    return AUTH_NO_CREDENTIALS;
218
#else
219
0
  if (!instance->Authenticate && !instance->AuthenticateEx)
220
0
    return AUTH_NO_CREDENTIALS;
221
0
  if (!instance->Authenticate)
222
0
#endif
223
0
  {
224
0
    proceed = instance->AuthenticateEx(instance, &settings->Username, &settings->Password,
225
0
                                       &settings->Domain, reason);
226
0
    if (!proceed)
227
0
      return AUTH_CANCELLED;
228
0
  }
229
0
#if !defined(WITHOUT_FREERDP_3x_DEPRECATED)
230
0
  else
231
0
  {
232
0
    proceed = instance->Authenticate(instance, &settings->Username, &settings->Password,
233
0
                                     &settings->Domain);
234
0
    if (!proceed)
235
0
      return AUTH_NO_CREDENTIALS;
236
0
  }
237
0
#endif
238
239
0
#if !defined(WITHOUT_FREERDP_3x_DEPRECATED)
240
0
  WINPR_PRAGMA_DIAG_POP
241
0
#endif
242
243
0
  if (utils_str_is_empty(settings->Username) || utils_str_is_empty(settings->Password))
244
0
    return AUTH_NO_CREDENTIALS;
245
246
0
  if (!utils_sync_credentials(settings, TRUE))
247
0
    return AUTH_FAILED;
248
249
  /* update original settings with provided user credentials */
250
0
  if (!utils_str_copy(settings->Username, &origSettings->Username))
251
0
    return AUTH_FAILED;
252
0
  if (!utils_str_copy(settings->Domain, &origSettings->Domain))
253
0
    return AUTH_FAILED;
254
0
  if (!utils_str_copy(settings->Password, &origSettings->Password))
255
0
    return AUTH_FAILED;
256
0
  if (!utils_sync_credentials(origSettings, TRUE))
257
0
    return AUTH_FAILED;
258
259
0
  if (!utils_copy_smartcard_settings(settings, origSettings))
260
0
    return AUTH_FAILED;
261
262
0
  return AUTH_SUCCESS;
263
0
}
264
265
BOOL utils_sync_credentials(rdpSettings* settings, BOOL toGateway)
266
0
{
267
0
  WINPR_ASSERT(settings);
268
0
  if (!settings->GatewayUseSameCredentials)
269
0
    return TRUE;
270
271
0
  if (toGateway)
272
0
  {
273
0
    if (!utils_str_copy(settings->Username, &settings->GatewayUsername))
274
0
      return FALSE;
275
0
    if (!utils_str_copy(settings->Domain, &settings->GatewayDomain))
276
0
      return FALSE;
277
0
    if (!utils_str_copy(settings->Password, &settings->GatewayPassword))
278
0
      return FALSE;
279
0
  }
280
0
  else
281
0
  {
282
0
    if (!utils_str_copy(settings->GatewayUsername, &settings->Username))
283
0
      return FALSE;
284
0
    if (!utils_str_copy(settings->GatewayDomain, &settings->Domain))
285
0
      return FALSE;
286
0
    if (!utils_str_copy(settings->GatewayPassword, &settings->Password))
287
0
      return FALSE;
288
0
  }
289
0
  return TRUE;
290
0
}
291
292
BOOL utils_persist_credentials(rdpSettings* settings, const rdpSettings* current)
293
0
{
294
0
  if (!settings || !current)
295
0
    return FALSE;
296
297
0
  const SSIZE_T keys[] = { FreeRDP_GatewayUsername, FreeRDP_GatewayDomain,
298
0
                         FreeRDP_GatewayPassword, FreeRDP_Username,
299
0
                         FreeRDP_Domain,          FreeRDP_Password };
300
301
0
  for (size_t x = 0; x < ARRAYSIZE(keys); x++)
302
0
  {
303
0
    const SSIZE_T key = keys[x];
304
0
    if (!freerdp_settings_copy_item(settings, current, key))
305
0
    {
306
0
      WLog_ERR(TAG, "Failed to copy %s from current to backup settings",
307
0
               freerdp_settings_get_name_for_key(key));
308
0
      return FALSE;
309
0
    }
310
0
  }
311
312
0
  return TRUE;
313
0
}
314
315
BOOL utils_str_is_empty(const char* str)
316
0
{
317
0
  if (!str)
318
0
    return TRUE;
319
0
  if (*str == '\0')
320
0
    return TRUE;
321
0
  return FALSE;
322
0
}
323
324
BOOL utils_abort_connect(rdpRdp* rdp)
325
1.08k
{
326
1.08k
  if (!rdp)
327
0
    return FALSE;
328
329
1.08k
  return SetEvent(rdp->abortEvent);
330
1.08k
}
331
332
BOOL utils_reset_abort(rdpRdp* rdp)
333
0
{
334
0
  WINPR_ASSERT(rdp);
335
336
0
  return ResetEvent(rdp->abortEvent);
337
0
}
338
339
HANDLE utils_get_abort_event(rdpRdp* rdp)
340
17.4k
{
341
17.4k
  WINPR_ASSERT(rdp);
342
17.4k
  return rdp->abortEvent;
343
17.4k
}
344
345
BOOL utils_abort_event_is_set(const rdpRdp* rdp)
346
66
{
347
66
  DWORD status = 0;
348
66
  WINPR_ASSERT(rdp);
349
66
  status = WaitForSingleObject(rdp->abortEvent, 0);
350
66
  return status == WAIT_OBJECT_0;
351
66
}
352
353
const char* utils_is_vsock(const char* hostname)
354
0
{
355
0
  if (!hostname)
356
0
    return nullptr;
357
358
0
  const char vsock[8] = { 'v', 's', 'o', 'c', 'k', ':', '/', '/' };
359
0
  if (strncmp(hostname, vsock, sizeof(vsock)) == 0)
360
0
    return &hostname[sizeof(vsock)];
361
0
  return nullptr;
362
0
}
363
364
static BOOL remove_rdpdr_type(rdpSettings* settings, UINT32 type)
365
0
{
366
0
  BOOL rc = TRUE;
367
0
  RDPDR_DEVICE* printer = nullptr;
368
0
  do
369
0
  {
370
0
    printer = freerdp_device_collection_find_type(settings, type);
371
0
    if (printer)
372
0
    {
373
0
      if (!freerdp_device_collection_del(settings, printer))
374
0
        rc = FALSE;
375
0
    }
376
0
    freerdp_device_free(printer);
377
0
  } while (printer);
378
0
  return rc;
379
0
}
380
381
static BOOL disable_clipboard(rdpSettings* settings)
382
0
{
383
0
  if (!freerdp_settings_set_bool(settings, FreeRDP_RedirectClipboard, FALSE))
384
0
    return FALSE;
385
0
  freerdp_static_channel_collection_del(settings, CLIPRDR_SVC_CHANNEL_NAME);
386
0
  return TRUE;
387
0
}
388
389
static BOOL disable_drive(rdpSettings* settings)
390
0
{
391
0
  if (!freerdp_settings_set_bool(settings, FreeRDP_RedirectDrives, FALSE))
392
0
    return FALSE;
393
0
  if (!freerdp_settings_set_bool(settings, FreeRDP_RedirectHomeDrive, FALSE))
394
0
    return FALSE;
395
396
0
  return remove_rdpdr_type(settings, RDPDR_DTYP_FILESYSTEM);
397
0
}
398
399
static BOOL disable_printers(rdpSettings* settings)
400
0
{
401
0
  if (!freerdp_settings_set_bool(settings, FreeRDP_RedirectPrinters, FALSE))
402
0
    return FALSE;
403
404
0
  return remove_rdpdr_type(settings, RDPDR_DTYP_PRINT);
405
0
}
406
407
static BOOL disable_port(rdpSettings* settings)
408
0
{
409
0
  if (!freerdp_settings_set_bool(settings, FreeRDP_RedirectParallelPorts, FALSE))
410
0
    return FALSE;
411
0
  if (!freerdp_settings_set_bool(settings, FreeRDP_RedirectSerialPorts, FALSE))
412
0
    return FALSE;
413
0
  if (!remove_rdpdr_type(settings, RDPDR_DTYP_SERIAL))
414
0
    return FALSE;
415
0
  return remove_rdpdr_type(settings, RDPDR_DTYP_PARALLEL);
416
0
}
417
418
static BOOL disable_pnp(WINPR_ATTR_UNUSED rdpSettings* settings)
419
0
{
420
  // TODO(akallabeth): [MS-RDPEPNP] related stuff is disabled.
421
0
  return TRUE;
422
0
}
423
424
static BOOL apply_gw_policy(rdpContext* context)
425
0
{
426
0
  WINPR_ASSERT(context);
427
0
  return utils_reload_channels(context);
428
0
}
429
430
BOOL utils_apply_gateway_policy(wLog* log, rdpContext* context, UINT32 flags, const char* module)
431
0
{
432
0
  WINPR_ASSERT(log);
433
0
  WINPR_ASSERT(context);
434
435
0
  rdpSettings* settings = context->settings;
436
0
  WINPR_ASSERT(settings);
437
438
0
  if (flags & HTTP_TUNNEL_REDIR_ENABLE_ALL)
439
0
  {
440
0
    WLog_Print(log, WLOG_DEBUG, "[%s] policy allows all redirections", module);
441
0
  }
442
0
  else if (freerdp_settings_get_bool(settings, FreeRDP_GatewayIgnoreRedirectionPolicy))
443
0
  {
444
0
    char buffer[128] = WINPR_C_ARRAY_INIT;
445
0
    WLog_Print(log, WLOG_INFO, "[%s] policy ignored on user request %s", module,
446
0
               utils_redir_flags_to_string(flags, buffer, sizeof(buffer)));
447
0
  }
448
0
  else if (flags & HTTP_TUNNEL_REDIR_DISABLE_ALL)
449
0
  {
450
0
    WLog_Print(log, WLOG_INFO, "[%s] policy denies all redirections", module);
451
0
    if (!disable_drive(settings))
452
0
      return FALSE;
453
0
    if (!disable_printers(settings))
454
0
      return FALSE;
455
0
    if (!disable_clipboard(settings))
456
0
      return FALSE;
457
0
    if (!disable_port(settings))
458
0
      return FALSE;
459
0
    if (!disable_pnp(settings))
460
0
      return FALSE;
461
0
    if (!apply_gw_policy(context))
462
0
      return FALSE;
463
0
  }
464
0
  else
465
0
  {
466
0
    if (flags & HTTP_TUNNEL_REDIR_DISABLE_DRIVE)
467
0
    {
468
0
      WLog_Print(log, WLOG_INFO, "[%s] policy denies drive redirections", module);
469
0
      if (!disable_drive(settings))
470
0
        return FALSE;
471
0
    }
472
0
    if (flags & HTTP_TUNNEL_REDIR_DISABLE_PRINTER)
473
0
    {
474
0
      WLog_Print(log, WLOG_INFO, "[%s] policy denies printer redirections", module);
475
0
      if (!disable_printers(settings))
476
0
        return FALSE;
477
0
    }
478
0
    if (flags & HTTP_TUNNEL_REDIR_DISABLE_PORT)
479
0
    {
480
0
      WLog_Print(log, WLOG_INFO, "[%s] policy denies port redirections", module);
481
0
      if (!disable_port(settings))
482
0
        return FALSE;
483
0
    }
484
0
    if (flags & HTTP_TUNNEL_REDIR_DISABLE_CLIPBOARD)
485
0
    {
486
0
      WLog_Print(log, WLOG_INFO, "[%s] policy denies clipboard redirections", module);
487
0
      if (!disable_clipboard(settings))
488
0
        return FALSE;
489
0
    }
490
0
    if (flags & HTTP_TUNNEL_REDIR_DISABLE_PNP)
491
0
    {
492
0
      WLog_Print(log, WLOG_INFO, "[%s] policy denies PNP redirections", module);
493
0
      if (!disable_pnp(settings))
494
0
        return FALSE;
495
0
    }
496
0
    if (flags != 0)
497
0
    {
498
0
      if (!apply_gw_policy(context))
499
0
        return FALSE;
500
0
    }
501
0
  }
502
0
  return TRUE;
503
0
}
504
505
char* utils_redir_flags_to_string(UINT32 flags, char* buffer, size_t size)
506
0
{
507
0
  winpr_str_append("{", buffer, size, "");
508
0
  if (flags & HTTP_TUNNEL_REDIR_ENABLE_ALL)
509
0
    winpr_str_append("ENABLE_ALL", buffer, size, "|");
510
0
  if (flags & HTTP_TUNNEL_REDIR_DISABLE_ALL)
511
0
    winpr_str_append("DISABLE_ALL", buffer, size, "|");
512
0
  if (flags & HTTP_TUNNEL_REDIR_DISABLE_DRIVE)
513
0
    winpr_str_append("DISABLE_DRIVE", buffer, size, "|");
514
0
  if (flags & HTTP_TUNNEL_REDIR_DISABLE_PRINTER)
515
0
    winpr_str_append("DISABLE_PRINTER", buffer, size, "|");
516
0
  if (flags & HTTP_TUNNEL_REDIR_DISABLE_PORT)
517
0
    winpr_str_append("DISABLE_PORT", buffer, size, "|");
518
0
  if (flags & HTTP_TUNNEL_REDIR_DISABLE_CLIPBOARD)
519
0
    winpr_str_append("DISABLE_CLIPBOARD", buffer, size, "|");
520
0
  if (flags & HTTP_TUNNEL_REDIR_DISABLE_PNP)
521
0
    winpr_str_append("DISABLE_PNP", buffer, size, "|");
522
523
0
  char fbuffer[16] = WINPR_C_ARRAY_INIT;
524
0
  (void)_snprintf(fbuffer, sizeof(fbuffer), "[0x%08" PRIx32 "]", flags);
525
526
0
  winpr_str_append(fbuffer, buffer, size, " ");
527
0
  winpr_str_append("{", buffer, size, "}");
528
0
  return buffer;
529
0
}
530
531
BOOL utils_reload_channels(rdpContext* context)
532
0
{
533
0
  WINPR_ASSERT(context);
534
535
0
  if (context->channels)
536
0
  {
537
0
    freerdp_channels_disconnect(context->channels, context->instance);
538
0
    freerdp_channels_close(context->channels, context->instance);
539
0
    freerdp_channels_free(context->channels);
540
0
  }
541
542
0
  context->channels = freerdp_channels_new(context->instance);
543
0
  if (!context->channels)
544
0
    return FALSE;
545
546
0
  freerdp_channels_register_instance(context->channels, context->instance);
547
548
0
  BOOL rc = TRUE;
549
0
  IFCALLRET(context->instance->LoadChannels, rc, context->instance);
550
0
  if (rc)
551
0
    return freerdp_channels_pre_connect(context->channels, context->instance) == CHANNEL_RC_OK;
552
0
  return rc;
553
0
}