FreeRDP
Loading...
Searching...
No Matches
shadow_server.c
1
21#include <freerdp/config.h>
22
23#include <errno.h>
24#include <stdint.h>
25
26#include <winpr/assert.h>
27#include <winpr/crt.h>
28#include <winpr/ssl.h>
29#include <winpr/path.h>
30#include <winpr/cmdline.h>
31#include <winpr/winsock.h>
32
33#include <freerdp/log.h>
34#include <freerdp/version.h>
35
36#include <winpr/tools/makecert.h>
37
38#ifndef _WIN32
39#include <sys/select.h>
40#include <signal.h>
41#endif
42
43#include "shadow.h"
44#include "shadow_cliprdr.h"
45
46#define TAG SERVER_TAG("shadow")
47
48static const char bind_address[] = "bind-address,";
49
50#define fail_at(arg, rc) fail_at_((arg), (rc), __FILE__, __func__, __LINE__)
51static int fail_at_(const COMMAND_LINE_ARGUMENT_A* arg, int rc, const char* file, const char* fkt,
52 size_t line)
53{
54 const DWORD level = WLOG_ERROR;
55 wLog* log = WLog_Get(TAG);
56 if (WLog_IsLevelActive(log, level))
57 WLog_PrintTextMessage(log, level, line, file, fkt,
58 "Command line parsing failed at '%s' value '%s' [%d]", arg->Name,
59 arg->Value, rc);
60 return rc;
61}
62
63WINPR_ATTR_NODISCARD
64static int command_line_compare(const void* pa, const void* pb)
65{
66 const COMMAND_LINE_ARGUMENT_A* a = pa;
67 const COMMAND_LINE_ARGUMENT_A* b = pb;
68
69 if (!a && !b)
70 return 0;
71 if (!a)
72 return -1;
73 if (!b)
74 return 1;
75
76 return strcmp(a->Name, b->Name);
77}
78
79WINPR_ATTR_NODISCARD
80static int shadow_server_print_command_line_help(int argc, char** argv,
81 const COMMAND_LINE_ARGUMENT_A* largs)
82{
83 if ((argc < 1) || !largs || !argv)
84 return -1;
85
86 {
87 char* path = winpr_GetConfigFilePath(TRUE, "SAM");
88 printf("Usage: %s [options]\n", argv[0]);
89 printf("\n");
90 printf("Notes: By default NLA security is active.\n");
91 printf("\tIn this mode a SAM database is required.\n");
92 printf("\tProvide one with /sam-file:<file with path>\n");
93 printf("\telse the default path %s is used.\n", path);
94 printf("\tIf there is no existing SAM file authentication for all users will fail.\n");
95 printf("\n\tIf authentication against PAM is desired, start with -sec-nla (requires "
96 "compiled in "
97 "support for PAM)\n\n");
98 printf("Syntax:\n");
99 printf(" /flag (enables flag)\n");
100 printf(" /option:<value> (specifies option with value)\n");
101 printf(" +toggle -toggle (enables or disables toggle, where '/' is a synonym of '+')\n");
102 printf("\n");
103 free(path);
104 }
105
106 // TODO: Sort arguments
107 size_t nrArgs = 0;
108 {
109 const COMMAND_LINE_ARGUMENT_A* arg = largs;
110 while (arg->Name != nullptr)
111 {
112 nrArgs++;
113 arg++;
114 }
115 nrArgs++;
116 }
117 COMMAND_LINE_ARGUMENT_A* args_copy = calloc(nrArgs, sizeof(COMMAND_LINE_ARGUMENT_A));
118 if (!args_copy)
119 return -1;
120 memcpy(args_copy, largs, nrArgs * sizeof(COMMAND_LINE_ARGUMENT_A));
121 qsort(args_copy, nrArgs - 1, sizeof(COMMAND_LINE_ARGUMENT_A), command_line_compare);
122
123 const COMMAND_LINE_ARGUMENT_A* arg = args_copy;
124
125 int rc = -1;
126 do
127 {
128 if (arg->Flags & COMMAND_LINE_VALUE_FLAG)
129 {
130 printf(" %s", "/");
131 printf("%-20s\n", arg->Name);
132 printf("\t%s\n", arg->Text);
133 }
134 else if ((arg->Flags & COMMAND_LINE_VALUE_REQUIRED) ||
135 (arg->Flags & COMMAND_LINE_VALUE_OPTIONAL))
136 {
137 printf(" %s", "/");
138
139 if (arg->Format)
140 {
141 const size_t length = (strlen(arg->Name) + strlen(arg->Format) + 2);
142 char* str = (char*)calloc(length + 1, sizeof(char));
143
144 if (!str)
145 goto fail;
146
147 (void)sprintf_s(str, length + 1, "%s:%s", arg->Name, arg->Format);
148 (void)printf("%-20s\n", str);
149 free(str);
150 }
151 else
152 {
153 printf("%-20s\n", arg->Name);
154 }
155
156 printf("\t%s\n", arg->Text);
157 }
158 else if (arg->Flags & COMMAND_LINE_VALUE_BOOL)
159 {
160 const size_t length = strlen(arg->Name) + 32;
161 char* str = calloc(length + 1, sizeof(char));
162
163 if (!str)
164 goto fail;
165
166 (void)sprintf_s(str, length + 1, "%s (default:%s)", arg->Name,
167 arg->Default ? "on" : "off");
168 (void)printf(" %s", arg->Default ? "-" : "+");
169 (void)printf("%-20s\n", str);
170 (void)printf("\t%s\n", arg->Text);
171
172 free(str);
173 }
174 } while ((arg = CommandLineFindNextArgumentA(arg)) != nullptr);
175
176 rc = 1;
177fail:
178 free(args_copy);
179 return rc;
180}
181
182int shadow_server_command_line_status_print(rdpShadowServer* server, int argc, char** argv,
183 int status, const COMMAND_LINE_ARGUMENT_A* cargs)
184{
185 WINPR_UNUSED(server);
186
187 if (status == COMMAND_LINE_STATUS_PRINT_VERSION)
188 {
189 printf("FreeRDP version %s (git %s)\n", FREERDP_VERSION_FULL, FREERDP_GIT_REVISION);
190 return COMMAND_LINE_STATUS_PRINT_VERSION;
191 }
192 else if (status == COMMAND_LINE_STATUS_PRINT_BUILDCONFIG)
193 {
194 printf("%s\n", freerdp_get_build_config());
195 return COMMAND_LINE_STATUS_PRINT_BUILDCONFIG;
196 }
197 else if (status == COMMAND_LINE_STATUS_PRINT)
198 {
199 return COMMAND_LINE_STATUS_PRINT;
200 }
201 else if (status < 0)
202 {
203 if (shadow_server_print_command_line_help(argc, argv, cargs) < 0)
204 return -1;
205
206 return COMMAND_LINE_STATUS_PRINT_HELP;
207 }
208
209 return 1;
210}
211
212int shadow_server_parse_command_line(rdpShadowServer* server, int argc, char** argv,
214{
215 int status = 0;
216 DWORD flags = 0;
217 const COMMAND_LINE_ARGUMENT_A* arg = nullptr;
218 rdpSettings* settings = server->settings;
219
220 if ((argc < 2) || !argv || !cargs)
221 return 1;
222
223 CommandLineClearArgumentsA(cargs);
224 flags = COMMAND_LINE_SEPARATOR_COLON;
225 flags |= COMMAND_LINE_SIGIL_SLASH | COMMAND_LINE_SIGIL_PLUS_MINUS;
226 status = CommandLineParseArgumentsA(argc, argv, cargs, flags, server, nullptr, nullptr);
227
228 if (status < 0)
229 return status;
230
231 arg = cargs;
232 errno = 0;
233
234 do
235 {
236 if (!(arg->Flags & COMMAND_LINE_ARGUMENT_PRESENT))
237 continue;
238
239 CommandLineSwitchStart(arg) CommandLineSwitchCase(arg, "port")
240 {
241 long val = strtol(arg->Value, nullptr, 0);
242
243 if ((errno != 0) || (val <= 0) || (val > UINT16_MAX))
244 return fail_at(arg, COMMAND_LINE_ERROR);
245
246 server->port = (DWORD)val;
247 }
248 CommandLineSwitchCase(arg, "ipc-socket")
249 {
250 /* /bind-address is incompatible */
251 if (server->ipcSocket)
252 return fail_at(arg, COMMAND_LINE_ERROR);
253 server->ipcSocket = _strdup(arg->Value);
254
255 if (!server->ipcSocket)
256 return fail_at(arg, COMMAND_LINE_ERROR);
257 }
258 CommandLineSwitchCase(arg, "bind-address")
259 {
260 int rc = 0;
261 size_t len = strlen(arg->Value) + sizeof(bind_address);
262 /* /ipc-socket is incompatible */
263 if (server->ipcSocket)
264 return fail_at(arg, COMMAND_LINE_ERROR);
265 server->ipcSocket = calloc(len, sizeof(CHAR));
266
267 if (!server->ipcSocket)
268 return fail_at(arg, COMMAND_LINE_ERROR);
269
270 rc = _snprintf(server->ipcSocket, len, "%s%s", bind_address, arg->Value);
271 if ((rc < 0) || ((size_t)rc != len - 1))
272 return fail_at(arg, COMMAND_LINE_ERROR);
273 }
274 CommandLineSwitchCase(arg, "may-view")
275 {
276 server->mayView = arg->Value != nullptr;
277 }
278 CommandLineSwitchCase(arg, "bitmap-compat")
279 {
280 server->SupportMultiRectBitmapUpdates = arg->Value == nullptr;
281 }
282 CommandLineSwitchCase(arg, "may-interact")
283 {
284 server->mayInteract = arg->Value != nullptr;
285 }
286 CommandLineSwitchCase(arg, "server-side-cursor")
287 {
288 server->ShowMouseCursor = arg->Value != nullptr;
289 }
290 CommandLineSwitchCase(arg, "mouse-relative")
291 {
292 const BOOL val = arg->Value != nullptr;
293 if (!freerdp_settings_set_bool(settings, FreeRDP_MouseUseRelativeMove, val) ||
294 !freerdp_settings_set_bool(settings, FreeRDP_HasRelativeMouseEvent, val))
295 return fail_at(arg, COMMAND_LINE_ERROR);
296 }
297 CommandLineSwitchCase(arg, "max-connections")
298 {
299 errno = 0;
300 unsigned long val = strtoul(arg->Value, nullptr, 0);
301
302 if ((errno != 0) || (val > UINT32_MAX))
303 return fail_at(arg, COMMAND_LINE_ERROR);
304 server->maxClientsConnected = val;
305 }
306 CommandLineSwitchCase(arg, "rect")
307 {
308 char* p = nullptr;
309 char* tok[4];
310 long x = -1;
311 long y = -1;
312 long w = -1;
313 long h = -1;
314 char* str = _strdup(arg->Value);
315
316 if (!str)
317 return fail_at(arg, COMMAND_LINE_ERROR);
318
319 tok[0] = p = str;
320 p = strchr(p + 1, ',');
321
322 if (!p)
323 {
324 free(str);
325 return fail_at(arg, COMMAND_LINE_ERROR);
326 }
327
328 *p++ = '\0';
329 tok[1] = p;
330 p = strchr(p + 1, ',');
331
332 if (!p)
333 {
334 free(str);
335 return fail_at(arg, COMMAND_LINE_ERROR);
336 }
337
338 *p++ = '\0';
339 tok[2] = p;
340 p = strchr(p + 1, ',');
341
342 if (!p)
343 {
344 free(str);
345 return fail_at(arg, COMMAND_LINE_ERROR);
346 }
347
348 *p++ = '\0';
349 tok[3] = p;
350 x = strtol(tok[0], nullptr, 0);
351
352 if (errno != 0)
353 goto fail;
354
355 y = strtol(tok[1], nullptr, 0);
356
357 if (errno != 0)
358 goto fail;
359
360 w = strtol(tok[2], nullptr, 0);
361
362 if (errno != 0)
363 goto fail;
364
365 h = strtol(tok[3], nullptr, 0);
366
367 if (errno != 0)
368 goto fail;
369
370 fail:
371 free(str);
372
373 if ((x < 0) || (y < 0) || (w < 1) || (h < 1) || (errno != 0))
374 return fail_at(arg, COMMAND_LINE_ERROR);
375
376 if ((x > UINT16_MAX) || (y > UINT16_MAX) || (x + w > UINT16_MAX) ||
377 (y + h > UINT16_MAX))
378 return fail_at(arg, COMMAND_LINE_ERROR);
379 server->subRect.left = (UINT16)x;
380 server->subRect.top = (UINT16)y;
381 server->subRect.right = (UINT16)(x + w);
382 server->subRect.bottom = (UINT16)(y + h);
383 server->shareSubRect = TRUE;
384 }
385 CommandLineSwitchCase(arg, "auth")
386 {
387 server->authentication = arg->Value != nullptr;
388 }
389 CommandLineSwitchCase(arg, "remote-guard")
390 {
391 if (!freerdp_settings_set_bool(settings, FreeRDP_RemoteCredentialGuard,
392 arg->Value != nullptr))
393 return fail_at(arg, COMMAND_LINE_ERROR);
394 }
395 CommandLineSwitchCase(arg, "restricted-admin")
396 {
397 if (!freerdp_settings_set_bool(settings, FreeRDP_RestrictedAdminModeSupported,
398 arg->Value != nullptr))
399 return fail_at(arg, COMMAND_LINE_ERROR);
400 }
401 CommandLineSwitchCase(arg, "vmconnect")
402 {
403 if (!freerdp_settings_set_bool(settings, FreeRDP_VmConnectMode, arg->Value != nullptr))
404 return fail_at(arg, COMMAND_LINE_ERROR);
405 }
406 CommandLineSwitchCase(arg, "sec")
407 {
408 if (strcmp("rdp", arg->Value) == 0) /* Standard RDP */
409 {
410 if (!freerdp_settings_set_bool(settings, FreeRDP_RdpSecurity, TRUE))
411 return fail_at(arg, COMMAND_LINE_ERROR);
412 if (!freerdp_settings_set_bool(settings, FreeRDP_TlsSecurity, FALSE))
413 return fail_at(arg, COMMAND_LINE_ERROR);
414 if (!freerdp_settings_set_bool(settings, FreeRDP_NlaSecurity, FALSE))
415 return fail_at(arg, COMMAND_LINE_ERROR);
416 if (!freerdp_settings_set_bool(settings, FreeRDP_ExtSecurity, FALSE))
417 return fail_at(arg, COMMAND_LINE_ERROR);
418 if (!freerdp_settings_set_bool(settings, FreeRDP_UseRdpSecurityLayer, TRUE))
419 return fail_at(arg, COMMAND_LINE_ERROR);
420 }
421 else if (strcmp("tls", arg->Value) == 0) /* TLS */
422 {
423 if (!freerdp_settings_set_bool(settings, FreeRDP_RdpSecurity, FALSE))
424 return fail_at(arg, COMMAND_LINE_ERROR);
425 if (!freerdp_settings_set_bool(settings, FreeRDP_TlsSecurity, TRUE))
426 return fail_at(arg, COMMAND_LINE_ERROR);
427 if (!freerdp_settings_set_bool(settings, FreeRDP_NlaSecurity, FALSE))
428 return fail_at(arg, COMMAND_LINE_ERROR);
429 if (!freerdp_settings_set_bool(settings, FreeRDP_ExtSecurity, FALSE))
430 return fail_at(arg, COMMAND_LINE_ERROR);
431 }
432 else if (strcmp("nla", arg->Value) == 0) /* NLA */
433 {
434 if (!freerdp_settings_set_bool(settings, FreeRDP_RdpSecurity, FALSE))
435 return fail_at(arg, COMMAND_LINE_ERROR);
436 if (!freerdp_settings_set_bool(settings, FreeRDP_TlsSecurity, FALSE))
437 return fail_at(arg, COMMAND_LINE_ERROR);
438 if (!freerdp_settings_set_bool(settings, FreeRDP_NlaSecurity, TRUE))
439 return fail_at(arg, COMMAND_LINE_ERROR);
440 if (!freerdp_settings_set_bool(settings, FreeRDP_ExtSecurity, FALSE))
441 return fail_at(arg, COMMAND_LINE_ERROR);
442 }
443 else if (strcmp("ext", arg->Value) == 0) /* NLA Extended */
444 {
445 if (!freerdp_settings_set_bool(settings, FreeRDP_RdpSecurity, FALSE))
446 return fail_at(arg, COMMAND_LINE_ERROR);
447 if (!freerdp_settings_set_bool(settings, FreeRDP_TlsSecurity, FALSE))
448 return fail_at(arg, COMMAND_LINE_ERROR);
449 if (!freerdp_settings_set_bool(settings, FreeRDP_NlaSecurity, FALSE))
450 return fail_at(arg, COMMAND_LINE_ERROR);
451 if (!freerdp_settings_set_bool(settings, FreeRDP_ExtSecurity, TRUE))
452 return fail_at(arg, COMMAND_LINE_ERROR);
453 }
454 else
455 {
456 WLog_ERR(TAG, "unknown protocol security: %s", arg->Value);
457 return fail_at(arg, COMMAND_LINE_ERROR_UNEXPECTED_VALUE);
458 }
459 }
460 CommandLineSwitchCase(arg, "sec-rdp")
461 {
462 if (!freerdp_settings_set_bool(settings, FreeRDP_RdpSecurity, arg->Value != nullptr))
463 return fail_at(arg, COMMAND_LINE_ERROR);
464 }
465 CommandLineSwitchCase(arg, "sec-tls")
466 {
467 if (!freerdp_settings_set_bool(settings, FreeRDP_TlsSecurity, arg->Value != nullptr))
468 return fail_at(arg, COMMAND_LINE_ERROR);
469 }
470 CommandLineSwitchCase(arg, "sec-nla")
471 {
472 if (!freerdp_settings_set_bool(settings, FreeRDP_NlaSecurity, arg->Value != nullptr))
473 return fail_at(arg, COMMAND_LINE_ERROR);
474 }
475 CommandLineSwitchCase(arg, "sec-ext")
476 {
477 if (!freerdp_settings_set_bool(settings, FreeRDP_ExtSecurity, arg->Value != nullptr))
478 return fail_at(arg, COMMAND_LINE_ERROR);
479 }
480 CommandLineSwitchCase(arg, "sam-file")
481 {
482 if (!freerdp_settings_set_string(settings, FreeRDP_NtlmSamFile, arg->Value))
483 return fail_at(arg, COMMAND_LINE_ERROR);
484 }
485 CommandLineSwitchCase(arg, "log-level")
486 {
487 wLog* root = WLog_GetRoot();
488
489 if (!WLog_SetStringLogLevel(root, arg->Value))
490 return fail_at(arg, COMMAND_LINE_ERROR);
491 }
492 CommandLineSwitchCase(arg, "log-filters")
493 {
494 if (!WLog_AddStringLogFilters(arg->Value))
495 return fail_at(arg, COMMAND_LINE_ERROR);
496 }
497 CommandLineSwitchCase(arg, "nsc")
498 {
499 if (!freerdp_settings_set_bool(settings, FreeRDP_NSCodec, arg->Value != nullptr))
500 return fail_at(arg, COMMAND_LINE_ERROR);
501 }
502 CommandLineSwitchCase(arg, "rfx")
503 {
504 if (!freerdp_settings_set_bool(settings, FreeRDP_RemoteFxCodec, arg->Value != nullptr))
505 return fail_at(arg, COMMAND_LINE_ERROR);
506 }
507 CommandLineSwitchCase(arg, "gfx")
508 {
509 if (!freerdp_settings_set_bool(settings, FreeRDP_SupportGraphicsPipeline,
510 arg->Value != nullptr))
511 return fail_at(arg, COMMAND_LINE_ERROR);
512 }
513 CommandLineSwitchCase(arg, "gfx-progressive")
514 {
515 if (!freerdp_settings_set_bool(settings, FreeRDP_GfxProgressive, arg->Value != nullptr))
516 return fail_at(arg, COMMAND_LINE_ERROR);
517 }
518#if defined(WITH_GFX_AV1)
519 CommandLineSwitchCase(arg, "gfx-av1")
520 {
521 BOOL parseFailed = FALSE;
522 BOOL enabled = TRUE;
523 UINT32 profile = freerdp_settings_get_uint32(settings, FreeRDP_GfxCodecAV1Profile);
524 if (arg->Value)
525 {
526 size_t count = 0;
527 char** args = CommandLineParseCommaSeparatedValues(arg->Value, &count);
528 WINPR_ASSERT(args || (count == 0));
529 for (size_t x = 0; x < count; x++)
530 {
531 const char* cur = args[x];
532 if (strncmp("profile:", cur, 8) == 0)
533 {
534 const char* val = &cur[8];
535 if (strcmp("high", val) == 0)
536 profile = 1;
537 else if (strcmp("low", val) == 0)
538 profile = 0;
539 else
540 {
541 const unsigned long v = strtoul(val, nullptr, 0);
542 if ((errno != 0) || (v >= UINT32_MAX))
543 {
544 parseFailed = TRUE;
545 break;
546 }
547 else
548 profile = WINPR_ASSERTING_INT_CAST(UINT32, v);
549 }
550 }
551 else if (strcmp("off", cur) == 0)
552 {
553 enabled = FALSE;
554 }
555 else
556 {
557 parseFailed = TRUE;
558 break;
559 }
560 }
561 CommandLineParserFree(args);
562 }
563 if (parseFailed)
564 return fail_at(arg, COMMAND_LINE_ERROR);
565 if (!freerdp_settings_set_bool(settings, FreeRDP_GfxCodecAV1, enabled))
566 return fail_at(arg, COMMAND_LINE_ERROR);
567 if (!freerdp_settings_set_uint32(settings, FreeRDP_GfxCodecAV1Profile, profile))
568 return fail_at(arg, COMMAND_LINE_ERROR);
569 }
570#endif
571#if defined(WITH_GFX_AV2)
572 CommandLineSwitchCase(arg, "gfx-av2")
573 {
574 BOOL parseFailed = FALSE;
575 BOOL enabled = TRUE;
576 UINT32 profile = freerdp_settings_get_uint32(settings, FreeRDP_GfxCodecAV2Profile);
577 if (arg->Value)
578 {
579 size_t count = 0;
580 char** args = CommandLineParseCommaSeparatedValues(arg->Value, &count);
581 WINPR_ASSERT(args || (count == 0));
582 for (size_t x = 0; x < count; x++)
583 {
584 const char* cur = args[x];
585 if (strncmp("profile:", cur, 8) == 0)
586 {
587 const char* val = &cur[8];
588 if (strcmp("high", val) == 0)
589 profile = 1;
590 else if (strcmp("low", val) == 0)
591 profile = 0;
592 else
593 {
594 const unsigned long v = strtoul(val, nullptr, 0);
595 if ((errno != 0) || (v >= UINT32_MAX))
596 {
597 parseFailed = TRUE;
598 break;
599 }
600 else
601 profile = WINPR_ASSERTING_INT_CAST(UINT32, v);
602 }
603 }
604 else if (strcmp("off", cur) == 0)
605 {
606 enabled = FALSE;
607 }
608 else
609 {
610 parseFailed = TRUE;
611 break;
612 }
613 }
614 CommandLineParserFree(args);
615 }
616 if (parseFailed)
617 return fail_at(arg, COMMAND_LINE_ERROR);
618 if (!freerdp_settings_set_bool(settings, FreeRDP_GfxCodecAV2, enabled))
619 return fail_at(arg, COMMAND_LINE_ERROR);
620 if (!freerdp_settings_set_uint32(settings, FreeRDP_GfxCodecAV2Profile, profile))
621 return fail_at(arg, COMMAND_LINE_ERROR);
622 }
623#endif
624 CommandLineSwitchCase(arg, "gfx-rfx")
625 {
626 if (!freerdp_settings_set_bool(settings, FreeRDP_RemoteFxCodec, arg->Value != nullptr))
627 return fail_at(arg, COMMAND_LINE_ERROR);
628 }
629 CommandLineSwitchCase(arg, "gfx-planar")
630 {
631 if (!freerdp_settings_set_bool(settings, FreeRDP_GfxPlanar, arg->Value != nullptr))
632 return fail_at(arg, COMMAND_LINE_ERROR);
633 }
634 CommandLineSwitchCase(arg, "gfx-avc420")
635 {
636 if (!freerdp_settings_set_bool(settings, FreeRDP_GfxH264, arg->Value != nullptr))
637 return fail_at(arg, COMMAND_LINE_ERROR);
638 }
639 CommandLineSwitchCase(arg, "gfx-avc444")
640 {
641 if (!freerdp_settings_set_bool(settings, FreeRDP_GfxAVC444v2, arg->Value != nullptr))
642 return fail_at(arg, COMMAND_LINE_ERROR);
643 if (!freerdp_settings_set_bool(settings, FreeRDP_GfxAVC444, arg->Value != nullptr))
644 return fail_at(arg, COMMAND_LINE_ERROR);
645 }
646 CommandLineSwitchCase(arg, "keytab")
647 {
648 if (!freerdp_settings_set_string(settings, FreeRDP_KerberosKeytab, arg->Value))
649 return fail_at(arg, COMMAND_LINE_ERROR);
650 }
651 CommandLineSwitchCase(arg, "ccache")
652 {
653 if (!freerdp_settings_set_string(settings, FreeRDP_KerberosCache, arg->Value))
654 return fail_at(arg, COMMAND_LINE_ERROR);
655 }
656 CommandLineSwitchCase(arg, "tls-secrets-file")
657 {
658 if (!freerdp_settings_set_string(settings, FreeRDP_TlsSecretsFile, arg->Value))
659 return fail_at(arg, COMMAND_LINE_ERROR);
660 }
661 CommandLineSwitchDefault(arg)
662 {
663 }
664 CommandLineSwitchEnd(arg)
665 } while ((arg = CommandLineFindNextArgumentA(arg)) != nullptr);
666
667 /* Before /monitors: the subsystem decides which monitors there are */
668 arg = CommandLineFindArgumentA(cargs, "subsystem");
669
670 if (arg && (arg->Flags & COMMAND_LINE_ARGUMENT_PRESENT))
671 {
672 if (arg->Flags & COMMAND_LINE_VALUE_PRESENT)
673 {
674 if (!shadow_subsystem_set_order(arg->Value))
675 return fail_at(arg, COMMAND_LINE_ERROR);
676 }
677 else
678 {
679 shadow_subsystem_list();
680 status = COMMAND_LINE_STATUS_PRINT;
681 }
682 }
683
684 arg = CommandLineFindArgumentA(cargs, "monitors");
685
686 if (arg && (arg->Flags & COMMAND_LINE_ARGUMENT_PRESENT))
687 {
688 UINT32 numMonitors = 0;
689 MONITOR_DEF monitors[16] = WINPR_C_ARRAY_INIT;
690 numMonitors = shadow_enum_monitors(monitors, 16);
691
692 if (arg->Flags & COMMAND_LINE_VALUE_PRESENT)
693 {
694 /* Select monitors. errno may hold anything from what ran before
695 * (enumerating the monitors makes system calls of its own) */
696 errno = 0;
697 long val = strtol(arg->Value, nullptr, 0);
698
699 if ((val < 0) || (errno != 0) || ((UINT32)val >= numMonitors))
700 {
701 WLog_ERR(TAG, "no monitor '%s': there are %" PRIu32 " (/monitors lists them)",
702 arg->Value, numMonitors);
703 status = COMMAND_LINE_STATUS_PRINT;
704 }
705
706 server->selectedMonitor = (UINT32)val;
707 }
708 else
709 {
710 /* List monitors */
711
712 for (UINT32 index = 0; index < numMonitors; index++)
713 {
714 const MONITOR_DEF* monitor = &monitors[index];
715 const INT64 width = monitor->right - monitor->left + 1;
716 const INT64 height = monitor->bottom - monitor->top + 1;
717 WLog_INFO(
718 TAG, " %s [%" PRIu32 "] %" PRId64 "x%" PRId64 "\t+%" PRId32 "+%" PRId32 "",
719 (monitor->flags == 1) ? "*" : " ", index, width, height, monitor->left,
720 monitor->top);
721 }
722
723 status = COMMAND_LINE_STATUS_PRINT;
724 }
725 }
726
727 /* If we want to disable authentication we need to ensure that NLA security
728 * is not activated. Only TLS and RDP security allow anonymous login.
729 */
730 if (!server->authentication && !freerdp_settings_get_bool(settings, FreeRDP_VmConnectMode))
731 {
732 if (!freerdp_settings_set_bool(settings, FreeRDP_NlaSecurity, FALSE))
733 return COMMAND_LINE_ERROR;
734 if (!freerdp_settings_set_bool(settings, FreeRDP_ExtSecurity, FALSE))
735 return COMMAND_LINE_ERROR;
736 }
737 return status;
738}
739
740WINPR_ATTR_NODISCARD
741static DWORD WINAPI shadow_server_thread(LPVOID arg)
742{
743 rdpShadowServer* server = (rdpShadowServer*)arg;
744 BOOL running = TRUE;
745 DWORD status = 0;
746 freerdp_listener* listener = server->listener;
747 if (shadow_subsystem_start(server->subsystem) < 0)
748 running = FALSE;
749
750 while (running)
751 {
752 HANDLE events[MAXIMUM_WAIT_OBJECTS] = WINPR_C_ARRAY_INIT;
753 DWORD nCount = 0;
754 events[nCount++] = server->StopEvent;
755 nCount += listener->GetEventHandles(listener, &events[nCount], ARRAYSIZE(events) - nCount);
756
757 if (nCount <= 1)
758 {
759 WLog_ERR(TAG, "Failed to get FreeRDP file descriptor");
760 break;
761 }
762
763 status = WaitForMultipleObjects(nCount, events, FALSE, INFINITE);
764
765 switch (status)
766 {
767 case WAIT_FAILED:
768 case WAIT_OBJECT_0:
769 running = FALSE;
770 break;
771
772 default:
773 {
774 if (!listener->CheckFileDescriptor(listener))
775 {
776 WLog_ERR(TAG, "Failed to check FreeRDP file descriptor");
777 running = FALSE;
778 }
779 else
780 {
781#ifdef _WIN32
782 Sleep(100); /* FIXME: listener event handles */
783#endif
784 }
785 }
786 break;
787 }
788 }
789
790 listener->Close(listener);
791 shadow_subsystem_stop(server->subsystem);
792
793 /* Signal to the clients that server is being stopped and wait for them
794 * to disconnect. */
795 if (shadow_client_boardcast_quit(server, 0))
796 {
797 while (ArrayList_Count(server->clients) > 0)
798 {
799 Sleep(100);
800 }
801 }
802
803 ExitThread(0);
804 return 0;
805}
806
807WINPR_ATTR_NODISCARD
808static BOOL open_port(rdpShadowServer* server, char* address)
809{
810 BOOL status = 0;
811 char* modaddr = address;
812
813 if (modaddr)
814 {
815 if (modaddr[0] == '[')
816 {
817 char* end = strchr(address, ']');
818 if (!end)
819 {
820 WLog_ERR(TAG, "Could not parse bind-address %s", address);
821 return -1;
822 }
823 *end++ = '\0';
824 if (strlen(end) > 0)
825 {
826 WLog_ERR(TAG, "Excess data after IPv6 address: '%s'", end);
827 return -1;
828 }
829 modaddr++;
830 }
831 }
832 status = server->listener->Open(server->listener, modaddr, (UINT16)server->port);
833
834 if (!status)
835 {
836 WLog_ERR(TAG,
837 "Problem creating TCP listener. (Port already used or insufficient permissions?)");
838 }
839
840 return status;
841}
842
843int shadow_server_start(rdpShadowServer* server)
844{
845 BOOL ipc = 0;
846 BOOL status = 0;
847 WSADATA wsaData;
848
849 if (!server)
850 return -1;
851
852 if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
853 return -1;
854
855#ifndef _WIN32
856 (void)signal(SIGPIPE, SIG_IGN);
857#endif
858 server->screen = shadow_screen_new(server);
859
860 if (!server->screen)
861 {
862 WLog_ERR(TAG, "screen_new failed");
863 return -1;
864 }
865
866 server->capture = shadow_capture_new(server);
867
868 if (!server->capture)
869 {
870 WLog_ERR(TAG, "capture_new failed");
871 return -1;
872 }
873
874 /* Bind magic:
875 *
876 * empty ... bind TCP all
877 * <local path> ... bind local (IPC)
878 * bind-socket,<address> ... bind TCP to specified interface
879 */
880 ipc = server->ipcSocket && (strncmp(bind_address, server->ipcSocket,
881 strnlen(bind_address, sizeof(bind_address))) != 0);
882 if (!ipc)
883 {
884 size_t count = 0;
885
886 char** ptr = CommandLineParseCommaSeparatedValuesEx(nullptr, server->ipcSocket, &count);
887 if (!ptr || (count <= 1))
888 {
889 if (server->ipcSocket == nullptr)
890 {
891 if (!open_port(server, nullptr))
892 {
893 CommandLineParserFree(ptr);
894 return -1;
895 }
896 }
897 else
898 {
899 CommandLineParserFree(ptr);
900 return -1;
901 }
902 }
903
904 WINPR_ASSERT(ptr || (count == 0));
905 for (size_t x = 1; x < count; x++)
906 {
907 BOOL success = open_port(server, ptr[x]);
908 if (!success)
909 {
910 CommandLineParserFree(ptr);
911 return -1;
912 }
913 }
914 CommandLineParserFree(ptr);
915 }
916 else
917 {
918 status = server->listener->OpenLocal(server->listener, server->ipcSocket);
919
920 if (!status)
921 {
922 WLog_ERR(TAG, "Problem creating local socket listener. (Port already used or "
923 "insufficient permissions?)");
924 return -1;
925 }
926 }
927
928 if (!(server->thread =
929 CreateThread(nullptr, 0, shadow_server_thread, (void*)server, 0, nullptr)))
930 {
931 return -1;
932 }
933
934 return 0;
935}
936
937int shadow_server_stop(rdpShadowServer* server)
938{
939 if (!server)
940 return -1;
941
942 if (server->thread)
943 {
944 (void)SetEvent(server->StopEvent);
945 (void)WaitForSingleObject(server->thread, INFINITE);
946 (void)CloseHandle(server->thread);
947 server->thread = nullptr;
948 if (server->listener && server->listener->Close)
949 server->listener->Close(server->listener);
950 }
951
952 if (server->screen)
953 {
954 shadow_screen_free(server->screen);
955 server->screen = nullptr;
956 }
957
958 if (server->capture)
959 {
960 shadow_capture_free(server->capture);
961 server->capture = nullptr;
962 }
963
964 return 0;
965}
966
967WINPR_ATTR_NODISCARD
968static int shadow_server_init_config_path(rdpShadowServer* server)
969{
970 if (!server->ConfigPath)
971 {
972 char* configHome = freerdp_settings_get_config_path();
973
974 if (configHome)
975 {
976 if (!winpr_PathFileExists(configHome) && !winpr_PathMakePath(configHome, nullptr))
977 {
978 WLog_ERR(TAG, "Failed to create directory '%s'", configHome);
979 free(configHome);
980 return -1;
981 }
982
983 server->ConfigPath = configHome;
984 }
985 }
986
987 if (!server->ConfigPath)
988 return -1; /* no usable config path */
989
990 return 1;
991}
992
993#if defined(WITH_WINPR_TOOLS)
994WINPR_ATTR_NODISCARD
995static BOOL shadow_server_create_certificate(rdpShadowServer* server, const char* filepath)
996{
997 BOOL rc = FALSE;
998 char* makecert_argv[6] = { "makecert", "-rdp", "-live", "-silent", "-y", "5" };
999
1000 WINPR_STATIC_ASSERT(ARRAYSIZE(makecert_argv) <= INT_MAX);
1001 const size_t makecert_argc = ARRAYSIZE(makecert_argv);
1002
1003 MAKECERT_CONTEXT* makecert = makecert_context_new();
1004
1005 if (!makecert)
1006 goto out_fail;
1007
1008 if (makecert_context_process(makecert, (int)makecert_argc, makecert_argv) < 0)
1009 goto out_fail;
1010
1011 if (makecert_context_set_output_file_name(makecert, "shadow") != 1)
1012 goto out_fail;
1013
1014 WINPR_ASSERT(server);
1015 WINPR_ASSERT(filepath);
1016 if (!winpr_PathFileExists(server->CertificateFile))
1017 {
1018 if (makecert_context_output_certificate_file(makecert, filepath) != 1)
1019 goto out_fail;
1020 }
1021
1022 if (!winpr_PathFileExists(server->PrivateKeyFile))
1023 {
1024 if (makecert_context_output_private_key_file(makecert, filepath) != 1)
1025 goto out_fail;
1026 }
1027 rc = TRUE;
1028out_fail:
1029 makecert_context_free(makecert);
1030 return rc;
1031}
1032#endif
1033
1034WINPR_ATTR_NODISCARD
1035static BOOL shadow_server_read_pem(rdpShadowServer* server)
1036{
1037 BOOL rc = FALSE;
1038 WINPR_ASSERT(server);
1039
1040 rdpSettings* settings = server->settings;
1041 WINPR_ASSERT(settings);
1042
1043 {
1044 rdpPrivateKey* key = freerdp_key_new_from_file_enc(server->PrivateKeyFile, nullptr);
1045 if (!key)
1046 {
1047 WLog_ERR(TAG, "Private Key PEM %s invalid, aborting", server->PrivateKeyFile);
1048 return FALSE;
1049 }
1050
1051 if (!freerdp_settings_set_pointer_len(settings, FreeRDP_RdpServerRsaKey, key, 1))
1052 {
1053 WLog_ERR(TAG, "Private Key PEM %s valid, but some properties failed to be extracted",
1054 server->PrivateKeyFile);
1055 return FALSE;
1056 }
1057 }
1058 {
1059 rdpCertificate* cert = freerdp_certificate_new_from_file(server->CertificateFile);
1060 if (!cert)
1061 {
1062 WLog_ERR(TAG, "Certificate PEM %s invalid, aborting", server->CertificateFile);
1063 return FALSE;
1064 }
1065
1066 if (!freerdp_settings_set_pointer_len(settings, FreeRDP_RdpServerCertificate, cert, 1))
1067 goto out_fail;
1068
1069 if (!freerdp_certificate_is_rdp_security_compatible(cert))
1070 {
1071 if (!freerdp_settings_set_bool(settings, FreeRDP_UseRdpSecurityLayer, FALSE))
1072 goto out_fail;
1073 if (!freerdp_settings_set_bool(settings, FreeRDP_RdpSecurity, FALSE))
1074 goto out_fail;
1075 }
1076 }
1077 rc = TRUE;
1078
1079out_fail:
1080 WLog_ERR(TAG, "Certificate PEM %s valid, but some properties failed to be extracted",
1081 server->CertificateFile);
1082 return rc;
1083}
1084
1085WINPR_ATTR_NODISCARD
1086static BOOL shadow_server_init_certificate(rdpShadowServer* server)
1087{
1088 char* filepath = nullptr;
1089 BOOL ret = FALSE;
1090
1091 WINPR_ASSERT(server);
1092
1093 if (!winpr_PathFileExists(server->ConfigPath) &&
1094 !winpr_PathMakePath(server->ConfigPath, nullptr))
1095 {
1096 WLog_ERR(TAG, "Failed to create directory '%s'", server->ConfigPath);
1097 return FALSE;
1098 }
1099
1100 if (!(filepath = GetCombinedPath(server->ConfigPath, "shadow")))
1101 return FALSE;
1102
1103 if (!winpr_PathFileExists(filepath) && !winpr_PathMakePath(filepath, nullptr))
1104 {
1105 if (!winpr_PathMakePath(filepath, nullptr))
1106 {
1107 WLog_ERR(TAG, "Failed to create directory '%s'", filepath);
1108 goto out_fail;
1109 }
1110 }
1111
1112 server->CertificateFile = GetCombinedPath(filepath, "shadow.crt");
1113 server->PrivateKeyFile = GetCombinedPath(filepath, "shadow.key");
1114
1115 if (!server->CertificateFile || !server->PrivateKeyFile)
1116 goto out_fail;
1117
1118 if ((!winpr_PathFileExists(server->CertificateFile)) ||
1119 (!winpr_PathFileExists(server->PrivateKeyFile)))
1120 {
1121#if defined(WITH_WINPR_TOOLS)
1122 if (!shadow_server_create_certificate(server, filepath))
1123#else
1124 const char* cexist = winpr_PathFileExists(server->CertificateFile) ? "exists" : "not found";
1125 const char* pexist = winpr_PathFileExists(server->PrivateKeyFile) ? "exists" : "not found";
1126 WLog_ERR(TAG, "PEM files do not exist: cert: %s (%s), key: %s (%s)",
1127 server->CertificateFile, cexist, server->PrivateKeyFile, pexist);
1128#endif
1129 goto out_fail;
1130 }
1131
1132 ret = shadow_server_read_pem(server);
1133
1134out_fail:
1135 free(filepath);
1136 return ret;
1137}
1138
1139WINPR_ATTR_NODISCARD
1140static BOOL shadow_server_check_peer_restrictions(freerdp_listener* listener)
1141{
1142 WINPR_ASSERT(listener);
1143
1144 rdpShadowServer* server = (rdpShadowServer*)listener->info;
1145 WINPR_ASSERT(server);
1146
1147 if (server->maxClientsConnected > 0)
1148 {
1149 const size_t count = ArrayList_Count(server->clients);
1150 if (count >= server->maxClientsConnected)
1151 {
1152 WLog_WARN(TAG, "connection limit [%" PRIuz "] reached, discarding client",
1153 server->maxClientsConnected);
1154 return FALSE;
1155 }
1156 }
1157 return TRUE;
1158}
1159
1160int shadow_server_init(rdpShadowServer* server)
1161{
1162 int status = 0;
1163 if (!winpr_InitializeSSL(WINPR_SSL_INIT_DEFAULT))
1164 return -1;
1165 if (!WTSRegisterWtsApiFunctionTable(FreeRDP_InitWtsApi()))
1166 return -1;
1167
1168 if (!(server->clients = ArrayList_New(TRUE)))
1169 goto fail;
1170
1171 if (!(server->StopEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr)))
1172 goto fail;
1173
1174 if (!InitializeCriticalSectionAndSpinCount(&(server->lock), 4000))
1175 goto fail;
1176
1177 status = shadow_server_init_config_path(server);
1178
1179 if (status < 0)
1180 goto fail;
1181
1182 if (!shadow_server_init_certificate(server))
1183 goto fail;
1184
1185 server->listener = freerdp_listener_new();
1186
1187 if (!server->listener)
1188 goto fail;
1189
1190 server->listener->info = (void*)server;
1191 server->listener->CheckPeerAcceptRestrictions = shadow_server_check_peer_restrictions;
1192 server->listener->PeerAccepted = shadow_client_accepted;
1193 server->clipboard = shadow_clipboard_new(server);
1194
1195 server->subsystem = shadow_subsystem_new();
1196
1197 if (!server->subsystem)
1198 goto fail;
1199
1200 status = shadow_subsystem_init(server->subsystem, server);
1201 if (status < 0)
1202 goto fail;
1203
1204 return status;
1205
1206fail:
1207 shadow_server_uninit(server);
1208 WLog_ERR(TAG, "Failed to initialize shadow server");
1209 return -1;
1210}
1211
1212int shadow_server_uninit(rdpShadowServer* server)
1213{
1214 if (!server)
1215 return -1;
1216
1217 shadow_server_stop(server);
1218 shadow_subsystem_uninit(server->subsystem);
1219 shadow_subsystem_free(server->subsystem);
1220 server->subsystem = nullptr;
1221 shadow_clipboard_free(server->clipboard);
1222 server->clipboard = nullptr;
1223 freerdp_listener_free(server->listener);
1224 server->listener = nullptr;
1225 free(server->CertificateFile);
1226 server->CertificateFile = nullptr;
1227 free(server->PrivateKeyFile);
1228 server->PrivateKeyFile = nullptr;
1229 free(server->ConfigPath);
1230 server->ConfigPath = nullptr;
1231 DeleteCriticalSection(&(server->lock));
1232 (void)CloseHandle(server->StopEvent);
1233 server->StopEvent = nullptr;
1234 ArrayList_Free(server->clients);
1235 server->clients = nullptr;
1236 return 1;
1237}
1238
1239rdpShadowServer* shadow_server_new(void)
1240{
1241 rdpShadowServer* server = nullptr;
1242 server = (rdpShadowServer*)calloc(1, sizeof(rdpShadowServer));
1243
1244 if (!server)
1245 return nullptr;
1246
1247 server->SupportMultiRectBitmapUpdates = TRUE;
1248 server->port = 3389;
1249 server->mayView = TRUE;
1250 server->mayInteract = TRUE;
1251 server->h264RateControlMode = H264_RATECONTROL_VBR;
1252 server->h264BitRate = 10000000;
1253 server->h264FrameRate = 30;
1254 server->h264QP = 0;
1255 server->h264HwAccel = FALSE;
1256 server->authentication = TRUE;
1257#if defined(WITH_GFX_AV1)
1258 server->AV1BitRate = 3000; /* kbit/s */
1259 server->AV1RateControlMode = FREERDP_AV1_VBR;
1260#endif
1261#if defined(WITH_GFX_AV2)
1262 server->AV2BitRate = 500;
1263 server->AV2RateControlMode = FREERDP_AV2_VBR;
1264#endif
1266 return server;
1267}
1268
1269void shadow_server_free(rdpShadowServer* server)
1270{
1271 if (!server)
1272 return;
1273
1274 free(server->ipcSocket);
1275 server->ipcSocket = nullptr;
1276 freerdp_settings_free(server->settings);
1277 server->settings = nullptr;
1278 free(server);
1279}
FREERDP_API rdpSettings * freerdp_settings_new(DWORD flags)
creates a new setting struct
WINPR_ATTR_NODISCARD FREERDP_API BOOL freerdp_settings_set_bool(rdpSettings *settings, FreeRDP_Settings_Keys_Bool id, BOOL val)
Sets a BOOL settings value.
WINPR_ATTR_NODISCARD FREERDP_API char * freerdp_settings_get_config_path(void)
return the configuration directory for the library
WINPR_ATTR_NODISCARD FREERDP_API BOOL freerdp_settings_set_uint32(rdpSettings *settings, FreeRDP_Settings_Keys_UInt32 id, UINT32 val)
Sets a UINT32 settings value.
WINPR_ATTR_NODISCARD FREERDP_API BOOL freerdp_settings_set_pointer_len(rdpSettings *settings, FreeRDP_Settings_Keys_Pointer id, const void *data, size_t len)
Set a pointer to value data.
FREERDP_API void freerdp_settings_free(rdpSettings *settings)
Free a settings struct with all data in it.
#define FREERDP_SETTINGS_SERVER_MODE
WINPR_ATTR_NODISCARD FREERDP_API UINT32 freerdp_settings_get_uint32(const rdpSettings *settings, FreeRDP_Settings_Keys_UInt32 id)
Returns a UINT32 settings value.
WINPR_ATTR_NODISCARD FREERDP_API BOOL freerdp_settings_set_string(rdpSettings *settings, FreeRDP_Settings_Keys_String id, const char *val)
Sets a string settings value. The param is copied.
WINPR_ATTR_NODISCARD FREERDP_API BOOL freerdp_settings_get_bool(const rdpSettings *settings, FreeRDP_Settings_Keys_Bool id)
Returns a boolean settings value.