FreeRDP
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 
45 #define TAG SERVER_TAG("shadow")
46 
47 static const char bind_address[] = "bind-address,";
48 
49 #define fail_at(arg, rc) fail_at_((arg), (rc), __FILE__, __func__, __LINE__)
50 static int fail_at_(const COMMAND_LINE_ARGUMENT_A* arg, int rc, const char* file, const char* fkt,
51  size_t line)
52 {
53  const DWORD level = WLOG_ERROR;
54  wLog* log = WLog_Get(TAG);
55  if (WLog_IsLevelActive(log, level))
56  WLog_PrintMessage(log, WLOG_MESSAGE_TEXT, level, line, file, fkt,
57  "Command line parsing failed at '%s' value '%s' [%d]", arg->Name,
58  arg->Value, rc);
59  return rc;
60 }
61 
62 static int shadow_server_print_command_line_help(int argc, char** argv,
64 {
65  char* str = NULL;
66  size_t length = 0;
67  const COMMAND_LINE_ARGUMENT_A* arg = NULL;
68  if ((argc < 1) || !largs || !argv)
69  return -1;
70 
71  char* path = winpr_GetConfigFilePath(TRUE, "SAM");
72  printf("Usage: %s [options]\n", argv[0]);
73  printf("\n");
74  printf("Notes: By default NLA security is active.\n");
75  printf("\tIn this mode a SAM database is required.\n");
76  printf("\tProvide one with /sam-file:<file with path>\n");
77  printf("\telse the default path %s is used.\n", path);
78  printf("\tIf there is no existing SAM file authentication for all users will fail.\n");
79  printf(
80  "\n\tIf authentication against PAM is desired, start with -sec-nla (requires compiled in "
81  "support for PAM)\n\n");
82  printf("Syntax:\n");
83  printf(" /flag (enables flag)\n");
84  printf(" /option:<value> (specifies option with value)\n");
85  printf(" +toggle -toggle (enables or disables toggle, where '/' is a synonym of '+')\n");
86  printf("\n");
87  free(path);
88 
89  arg = largs;
90 
91  do
92  {
93  if (arg->Flags & COMMAND_LINE_VALUE_FLAG)
94  {
95  printf(" %s", "/");
96  printf("%-20s\n", arg->Name);
97  printf("\t%s\n", arg->Text);
98  }
99  else if ((arg->Flags & COMMAND_LINE_VALUE_REQUIRED) ||
100  (arg->Flags & COMMAND_LINE_VALUE_OPTIONAL))
101  {
102  printf(" %s", "/");
103 
104  if (arg->Format)
105  {
106  length = (strlen(arg->Name) + strlen(arg->Format) + 2);
107  str = (char*)malloc(length + 1);
108 
109  if (!str)
110  return -1;
111 
112  (void)sprintf_s(str, length + 1, "%s:%s", arg->Name, arg->Format);
113  (void)printf("%-20s\n", str);
114  free(str);
115  }
116  else
117  {
118  printf("%-20s\n", arg->Name);
119  }
120 
121  printf("\t%s\n", arg->Text);
122  }
123  else if (arg->Flags & COMMAND_LINE_VALUE_BOOL)
124  {
125  length = strlen(arg->Name) + 32;
126  str = (char*)malloc(length + 1);
127 
128  if (!str)
129  return -1;
130 
131  (void)sprintf_s(str, length + 1, "%s (default:%s)", arg->Name,
132  arg->Default ? "on" : "off");
133  (void)printf(" %s", arg->Default ? "-" : "+");
134  (void)printf("%-20s\n", str);
135  free(str);
136  (void)printf("\t%s\n", arg->Text);
137  }
138  } while ((arg = CommandLineFindNextArgumentA(arg)) != NULL);
139 
140  return 1;
141 }
142 
143 int shadow_server_command_line_status_print(rdpShadowServer* server, int argc, char** argv,
144  int status, COMMAND_LINE_ARGUMENT_A* cargs)
145 {
146  WINPR_UNUSED(server);
147 
148  if (status == COMMAND_LINE_STATUS_PRINT_VERSION)
149  {
150  printf("FreeRDP version %s (git %s)\n", FREERDP_VERSION_FULL, FREERDP_GIT_REVISION);
151  return COMMAND_LINE_STATUS_PRINT_VERSION;
152  }
153  else if (status == COMMAND_LINE_STATUS_PRINT_BUILDCONFIG)
154  {
155  printf("%s\n", freerdp_get_build_config());
156  return COMMAND_LINE_STATUS_PRINT_BUILDCONFIG;
157  }
158  else if (status == COMMAND_LINE_STATUS_PRINT)
159  {
160  return COMMAND_LINE_STATUS_PRINT;
161  }
162  else if (status < 0)
163  {
164  if (shadow_server_print_command_line_help(argc, argv, cargs) < 0)
165  return -1;
166 
167  return COMMAND_LINE_STATUS_PRINT_HELP;
168  }
169 
170  return 1;
171 }
172 
173 int shadow_server_parse_command_line(rdpShadowServer* server, int argc, char** argv,
175 {
176  int status = 0;
177  DWORD flags = 0;
178  const COMMAND_LINE_ARGUMENT_A* arg = NULL;
179  rdpSettings* settings = server->settings;
180 
181  if ((argc < 2) || !argv || !cargs)
182  return 1;
183 
184  CommandLineClearArgumentsA(cargs);
185  flags = COMMAND_LINE_SEPARATOR_COLON;
186  flags |= COMMAND_LINE_SIGIL_SLASH | COMMAND_LINE_SIGIL_PLUS_MINUS;
187  status = CommandLineParseArgumentsA(argc, argv, cargs, flags, server, NULL, NULL);
188 
189  if (status < 0)
190  return status;
191 
192  arg = cargs;
193  errno = 0;
194 
195  do
196  {
197  if (!(arg->Flags & COMMAND_LINE_ARGUMENT_PRESENT))
198  continue;
199 
200  CommandLineSwitchStart(arg) CommandLineSwitchCase(arg, "port")
201  {
202  long val = strtol(arg->Value, NULL, 0);
203 
204  if ((errno != 0) || (val <= 0) || (val > UINT16_MAX))
205  return fail_at(arg, COMMAND_LINE_ERROR);
206 
207  server->port = (DWORD)val;
208  }
209  CommandLineSwitchCase(arg, "ipc-socket")
210  {
211  /* /bind-address is incompatible */
212  if (server->ipcSocket)
213  return fail_at(arg, COMMAND_LINE_ERROR);
214  server->ipcSocket = _strdup(arg->Value);
215 
216  if (!server->ipcSocket)
217  return fail_at(arg, COMMAND_LINE_ERROR);
218  }
219  CommandLineSwitchCase(arg, "bind-address")
220  {
221  int rc = 0;
222  size_t len = strlen(arg->Value) + sizeof(bind_address);
223  /* /ipc-socket is incompatible */
224  if (server->ipcSocket)
225  return fail_at(arg, COMMAND_LINE_ERROR);
226  server->ipcSocket = calloc(len, sizeof(CHAR));
227 
228  if (!server->ipcSocket)
229  return fail_at(arg, COMMAND_LINE_ERROR);
230 
231  rc = _snprintf(server->ipcSocket, len, "%s%s", bind_address, arg->Value);
232  if ((rc < 0) || ((size_t)rc != len - 1))
233  return fail_at(arg, COMMAND_LINE_ERROR);
234  }
235  CommandLineSwitchCase(arg, "may-view")
236  {
237  server->mayView = arg->Value ? TRUE : FALSE;
238  }
239  CommandLineSwitchCase(arg, "may-interact")
240  {
241  server->mayInteract = arg->Value ? TRUE : FALSE;
242  }
243  CommandLineSwitchCase(arg, "max-connections")
244  {
245  errno = 0;
246  unsigned long val = strtoul(arg->Value, NULL, 0);
247 
248  if ((errno != 0) || (val > UINT32_MAX))
249  return fail_at(arg, COMMAND_LINE_ERROR);
250  server->maxClientsConnected = val;
251  }
252  CommandLineSwitchCase(arg, "rect")
253  {
254  char* p = NULL;
255  char* tok[4];
256  long x = -1;
257  long y = -1;
258  long w = -1;
259  long h = -1;
260  char* str = _strdup(arg->Value);
261 
262  if (!str)
263  return fail_at(arg, COMMAND_LINE_ERROR);
264 
265  tok[0] = p = str;
266  p = strchr(p + 1, ',');
267 
268  if (!p)
269  {
270  free(str);
271  return fail_at(arg, COMMAND_LINE_ERROR);
272  }
273 
274  *p++ = '\0';
275  tok[1] = p;
276  p = strchr(p + 1, ',');
277 
278  if (!p)
279  {
280  free(str);
281  return fail_at(arg, COMMAND_LINE_ERROR);
282  }
283 
284  *p++ = '\0';
285  tok[2] = p;
286  p = strchr(p + 1, ',');
287 
288  if (!p)
289  {
290  free(str);
291  return fail_at(arg, COMMAND_LINE_ERROR);
292  }
293 
294  *p++ = '\0';
295  tok[3] = p;
296  x = strtol(tok[0], NULL, 0);
297 
298  if (errno != 0)
299  goto fail;
300 
301  y = strtol(tok[1], NULL, 0);
302 
303  if (errno != 0)
304  goto fail;
305 
306  w = strtol(tok[2], NULL, 0);
307 
308  if (errno != 0)
309  goto fail;
310 
311  h = strtol(tok[3], NULL, 0);
312 
313  if (errno != 0)
314  goto fail;
315 
316  fail:
317  free(str);
318 
319  if ((x < 0) || (y < 0) || (w < 1) || (h < 1) || (errno != 0))
320  return fail_at(arg, COMMAND_LINE_ERROR);
321 
322  if ((x > UINT16_MAX) || (y > UINT16_MAX) || (x + w > UINT16_MAX) ||
323  (y + h > UINT16_MAX))
324  return fail_at(arg, COMMAND_LINE_ERROR);
325  server->subRect.left = (UINT16)x;
326  server->subRect.top = (UINT16)y;
327  server->subRect.right = (UINT16)(x + w);
328  server->subRect.bottom = (UINT16)(y + h);
329  server->shareSubRect = TRUE;
330  }
331  CommandLineSwitchCase(arg, "auth")
332  {
333  server->authentication = arg->Value ? TRUE : FALSE;
334  }
335  CommandLineSwitchCase(arg, "remote-guard")
336  {
337  if (!freerdp_settings_set_bool(settings, FreeRDP_RemoteCredentialGuard,
338  arg->Value ? TRUE : FALSE))
339  return fail_at(arg, COMMAND_LINE_ERROR);
340  }
341  CommandLineSwitchCase(arg, "sec")
342  {
343  if (strcmp("rdp", arg->Value) == 0) /* Standard RDP */
344  {
345  if (!freerdp_settings_set_bool(settings, FreeRDP_RdpSecurity, TRUE))
346  return fail_at(arg, COMMAND_LINE_ERROR);
347  if (!freerdp_settings_set_bool(settings, FreeRDP_TlsSecurity, FALSE))
348  return fail_at(arg, COMMAND_LINE_ERROR);
349  if (!freerdp_settings_set_bool(settings, FreeRDP_NlaSecurity, FALSE))
350  return fail_at(arg, COMMAND_LINE_ERROR);
351  if (!freerdp_settings_set_bool(settings, FreeRDP_ExtSecurity, FALSE))
352  return fail_at(arg, COMMAND_LINE_ERROR);
353  if (!freerdp_settings_set_bool(settings, FreeRDP_UseRdpSecurityLayer, TRUE))
354  return fail_at(arg, COMMAND_LINE_ERROR);
355  }
356  else if (strcmp("tls", arg->Value) == 0) /* TLS */
357  {
358  if (!freerdp_settings_set_bool(settings, FreeRDP_RdpSecurity, FALSE))
359  return fail_at(arg, COMMAND_LINE_ERROR);
360  if (!freerdp_settings_set_bool(settings, FreeRDP_TlsSecurity, TRUE))
361  return fail_at(arg, COMMAND_LINE_ERROR);
362  if (!freerdp_settings_set_bool(settings, FreeRDP_NlaSecurity, FALSE))
363  return fail_at(arg, COMMAND_LINE_ERROR);
364  if (!freerdp_settings_set_bool(settings, FreeRDP_ExtSecurity, FALSE))
365  return fail_at(arg, COMMAND_LINE_ERROR);
366  }
367  else if (strcmp("nla", arg->Value) == 0) /* NLA */
368  {
369  if (!freerdp_settings_set_bool(settings, FreeRDP_RdpSecurity, FALSE))
370  return fail_at(arg, COMMAND_LINE_ERROR);
371  if (!freerdp_settings_set_bool(settings, FreeRDP_TlsSecurity, FALSE))
372  return fail_at(arg, COMMAND_LINE_ERROR);
373  if (!freerdp_settings_set_bool(settings, FreeRDP_NlaSecurity, TRUE))
374  return fail_at(arg, COMMAND_LINE_ERROR);
375  if (!freerdp_settings_set_bool(settings, FreeRDP_ExtSecurity, FALSE))
376  return fail_at(arg, COMMAND_LINE_ERROR);
377  }
378  else if (strcmp("ext", arg->Value) == 0) /* NLA Extended */
379  {
380  if (!freerdp_settings_set_bool(settings, FreeRDP_RdpSecurity, FALSE))
381  return fail_at(arg, COMMAND_LINE_ERROR);
382  if (!freerdp_settings_set_bool(settings, FreeRDP_TlsSecurity, FALSE))
383  return fail_at(arg, COMMAND_LINE_ERROR);
384  if (!freerdp_settings_set_bool(settings, FreeRDP_NlaSecurity, FALSE))
385  return fail_at(arg, COMMAND_LINE_ERROR);
386  if (!freerdp_settings_set_bool(settings, FreeRDP_ExtSecurity, TRUE))
387  return fail_at(arg, COMMAND_LINE_ERROR);
388  }
389  else
390  {
391  WLog_ERR(TAG, "unknown protocol security: %s", arg->Value);
392  return fail_at(arg, COMMAND_LINE_ERROR_UNEXPECTED_VALUE);
393  }
394  }
395  CommandLineSwitchCase(arg, "sec-rdp")
396  {
397  if (!freerdp_settings_set_bool(settings, FreeRDP_RdpSecurity,
398  arg->Value ? TRUE : FALSE))
399  return fail_at(arg, COMMAND_LINE_ERROR);
400  }
401  CommandLineSwitchCase(arg, "sec-tls")
402  {
403  if (!freerdp_settings_set_bool(settings, FreeRDP_TlsSecurity,
404  arg->Value ? TRUE : FALSE))
405  return fail_at(arg, COMMAND_LINE_ERROR);
406  }
407  CommandLineSwitchCase(arg, "sec-nla")
408  {
409  if (!freerdp_settings_set_bool(settings, FreeRDP_NlaSecurity,
410  arg->Value ? TRUE : FALSE))
411  return fail_at(arg, COMMAND_LINE_ERROR);
412  }
413  CommandLineSwitchCase(arg, "sec-ext")
414  {
415  if (!freerdp_settings_set_bool(settings, FreeRDP_ExtSecurity,
416  arg->Value ? TRUE : FALSE))
417  return fail_at(arg, COMMAND_LINE_ERROR);
418  }
419  CommandLineSwitchCase(arg, "sam-file")
420  {
421  if (!freerdp_settings_set_string(settings, FreeRDP_NtlmSamFile, arg->Value))
422  return fail_at(arg, COMMAND_LINE_ERROR);
423  }
424  CommandLineSwitchCase(arg, "log-level")
425  {
426  wLog* root = WLog_GetRoot();
427 
428  if (!WLog_SetStringLogLevel(root, arg->Value))
429  return fail_at(arg, COMMAND_LINE_ERROR);
430  }
431  CommandLineSwitchCase(arg, "log-filters")
432  {
433  if (!WLog_AddStringLogFilters(arg->Value))
434  return fail_at(arg, COMMAND_LINE_ERROR);
435  }
436  CommandLineSwitchCase(arg, "nsc")
437  {
438  if (!freerdp_settings_set_bool(settings, FreeRDP_NSCodec, arg->Value ? TRUE : FALSE))
439  return fail_at(arg, COMMAND_LINE_ERROR);
440  }
441  CommandLineSwitchCase(arg, "rfx")
442  {
443  if (!freerdp_settings_set_bool(settings, FreeRDP_RemoteFxCodec,
444  arg->Value ? TRUE : FALSE))
445  return fail_at(arg, COMMAND_LINE_ERROR);
446  }
447  CommandLineSwitchCase(arg, "gfx")
448  {
449  if (!freerdp_settings_set_bool(settings, FreeRDP_SupportGraphicsPipeline,
450  arg->Value ? TRUE : FALSE))
451  return fail_at(arg, COMMAND_LINE_ERROR);
452  }
453  CommandLineSwitchCase(arg, "gfx-progressive")
454  {
455  if (!freerdp_settings_set_bool(settings, FreeRDP_GfxProgressive,
456  arg->Value ? TRUE : FALSE))
457  return fail_at(arg, COMMAND_LINE_ERROR);
458  }
459  CommandLineSwitchCase(arg, "gfx-rfx")
460  {
461  if (!freerdp_settings_set_bool(settings, FreeRDP_RemoteFxCodec,
462  arg->Value ? TRUE : FALSE))
463  return fail_at(arg, COMMAND_LINE_ERROR);
464  }
465  CommandLineSwitchCase(arg, "gfx-planar")
466  {
467  if (!freerdp_settings_set_bool(settings, FreeRDP_GfxPlanar, arg->Value ? TRUE : FALSE))
468  return fail_at(arg, COMMAND_LINE_ERROR);
469  }
470  CommandLineSwitchCase(arg, "gfx-avc420")
471  {
472  if (!freerdp_settings_set_bool(settings, FreeRDP_GfxH264, arg->Value ? TRUE : FALSE))
473  return fail_at(arg, COMMAND_LINE_ERROR);
474  }
475  CommandLineSwitchCase(arg, "gfx-avc444")
476  {
477  if (!freerdp_settings_set_bool(settings, FreeRDP_GfxAVC444v2,
478  arg->Value ? TRUE : FALSE))
479  return fail_at(arg, COMMAND_LINE_ERROR);
480  if (!freerdp_settings_set_bool(settings, FreeRDP_GfxAVC444, arg->Value ? TRUE : FALSE))
481  return fail_at(arg, COMMAND_LINE_ERROR);
482  }
483  CommandLineSwitchCase(arg, "keytab")
484  {
485  if (!freerdp_settings_set_string(settings, FreeRDP_KerberosKeytab, arg->Value))
486  return fail_at(arg, COMMAND_LINE_ERROR);
487  }
488  CommandLineSwitchCase(arg, "ccache")
489  {
490  if (!freerdp_settings_set_string(settings, FreeRDP_KerberosCache, arg->Value))
491  return fail_at(arg, COMMAND_LINE_ERROR);
492  }
493  CommandLineSwitchCase(arg, "tls-secrets-file")
494  {
495  if (!freerdp_settings_set_string(settings, FreeRDP_TlsSecretsFile, arg->Value))
496  return fail_at(arg, COMMAND_LINE_ERROR);
497  }
498  CommandLineSwitchDefault(arg)
499  {
500  }
501  CommandLineSwitchEnd(arg)
502  } while ((arg = CommandLineFindNextArgumentA(arg)) != NULL);
503 
504  arg = CommandLineFindArgumentA(cargs, "monitors");
505 
506  if (arg && (arg->Flags & COMMAND_LINE_ARGUMENT_PRESENT))
507  {
508  UINT32 numMonitors = 0;
509  MONITOR_DEF monitors[16] = { 0 };
510  numMonitors = shadow_enum_monitors(monitors, 16);
511 
512  if (arg->Flags & COMMAND_LINE_VALUE_PRESENT)
513  {
514  /* Select monitors */
515  long val = strtol(arg->Value, NULL, 0);
516 
517  if ((val < 0) || (errno != 0) || ((UINT32)val >= numMonitors))
518  status = COMMAND_LINE_STATUS_PRINT;
519 
520  server->selectedMonitor = (UINT32)val;
521  }
522  else
523  {
524  /* List monitors */
525 
526  for (UINT32 index = 0; index < numMonitors; index++)
527  {
528  const MONITOR_DEF* monitor = &monitors[index];
529  const INT64 width = monitor->right - monitor->left + 1;
530  const INT64 height = monitor->bottom - monitor->top + 1;
531  WLog_INFO(TAG, " %s [%d] %" PRId64 "x%" PRId64 "\t+%" PRId32 "+%" PRId32 "",
532  (monitor->flags == 1) ? "*" : " ", index, width, height, monitor->left,
533  monitor->top);
534  }
535 
536  status = COMMAND_LINE_STATUS_PRINT;
537  }
538  }
539 
540  /* If we want to disable authentication we need to ensure that NLA security
541  * is not activated. Only TLS and RDP security allow anonymous login.
542  */
543  if (!server->authentication)
544  {
545  if (!freerdp_settings_set_bool(settings, FreeRDP_NlaSecurity, FALSE))
546  return COMMAND_LINE_ERROR;
547  }
548  return status;
549 }
550 
551 static DWORD WINAPI shadow_server_thread(LPVOID arg)
552 {
553  rdpShadowServer* server = (rdpShadowServer*)arg;
554  BOOL running = TRUE;
555  DWORD status = 0;
556  freerdp_listener* listener = server->listener;
557  shadow_subsystem_start(server->subsystem);
558 
559  while (running)
560  {
561  HANDLE events[MAXIMUM_WAIT_OBJECTS] = { 0 };
562  DWORD nCount = 0;
563  events[nCount++] = server->StopEvent;
564  nCount += listener->GetEventHandles(listener, &events[nCount], ARRAYSIZE(events) - nCount);
565 
566  if (nCount <= 1)
567  {
568  WLog_ERR(TAG, "Failed to get FreeRDP file descriptor");
569  break;
570  }
571 
572  status = WaitForMultipleObjects(nCount, events, FALSE, INFINITE);
573 
574  switch (status)
575  {
576  case WAIT_FAILED:
577  case WAIT_OBJECT_0:
578  running = FALSE;
579  break;
580 
581  default:
582  {
583  if (!listener->CheckFileDescriptor(listener))
584  {
585  WLog_ERR(TAG, "Failed to check FreeRDP file descriptor");
586  running = FALSE;
587  }
588  else
589  {
590 #ifdef _WIN32
591  Sleep(100); /* FIXME: listener event handles */
592 #endif
593  }
594  }
595  break;
596  }
597  }
598 
599  listener->Close(listener);
600  shadow_subsystem_stop(server->subsystem);
601 
602  /* Signal to the clients that server is being stopped and wait for them
603  * to disconnect. */
604  if (shadow_client_boardcast_quit(server, 0))
605  {
606  while (ArrayList_Count(server->clients) > 0)
607  {
608  Sleep(100);
609  }
610  }
611 
612  ExitThread(0);
613  return 0;
614 }
615 
616 static BOOL open_port(rdpShadowServer* server, char* address)
617 {
618  BOOL status = 0;
619  char* modaddr = address;
620 
621  if (modaddr)
622  {
623  if (modaddr[0] == '[')
624  {
625  char* end = strchr(address, ']');
626  if (!end)
627  {
628  WLog_ERR(TAG, "Could not parse bind-address %s", address);
629  return -1;
630  }
631  *end++ = '\0';
632  if (strlen(end) > 0)
633  {
634  WLog_ERR(TAG, "Excess data after IPv6 address: '%s'", end);
635  return -1;
636  }
637  modaddr++;
638  }
639  }
640  status = server->listener->Open(server->listener, modaddr, (UINT16)server->port);
641 
642  if (!status)
643  {
644  WLog_ERR(TAG,
645  "Problem creating TCP listener. (Port already used or insufficient permissions?)");
646  }
647 
648  return status;
649 }
650 
651 int shadow_server_start(rdpShadowServer* server)
652 {
653  BOOL ipc = 0;
654  BOOL status = 0;
655  WSADATA wsaData;
656 
657  if (!server)
658  return -1;
659 
660  if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
661  return -1;
662 
663 #ifndef _WIN32
664  (void)signal(SIGPIPE, SIG_IGN);
665 #endif
666  server->screen = shadow_screen_new(server);
667 
668  if (!server->screen)
669  {
670  WLog_ERR(TAG, "screen_new failed");
671  return -1;
672  }
673 
674  server->capture = shadow_capture_new(server);
675 
676  if (!server->capture)
677  {
678  WLog_ERR(TAG, "capture_new failed");
679  return -1;
680  }
681 
682  /* Bind magic:
683  *
684  * emtpy ... bind TCP all
685  * <local path> ... bind local (IPC)
686  * bind-socket,<address> ... bind TCP to specified interface
687  */
688  ipc = server->ipcSocket && (strncmp(bind_address, server->ipcSocket,
689  strnlen(bind_address, sizeof(bind_address))) != 0);
690  if (!ipc)
691  {
692  size_t count = 0;
693 
694  char** ptr = CommandLineParseCommaSeparatedValuesEx(NULL, server->ipcSocket, &count);
695  if (!ptr || (count <= 1))
696  {
697  if (server->ipcSocket == NULL)
698  {
699  if (!open_port(server, NULL))
700  {
701  CommandLineParserFree(ptr);
702  return -1;
703  }
704  }
705  else
706  {
707  CommandLineParserFree(ptr);
708  return -1;
709  }
710  }
711 
712  WINPR_ASSERT(ptr || (count == 0));
713  for (size_t x = 1; x < count; x++)
714  {
715  BOOL success = open_port(server, ptr[x]);
716  if (!success)
717  {
718  CommandLineParserFree(ptr);
719  return -1;
720  }
721  }
722  CommandLineParserFree(ptr);
723  }
724  else
725  {
726  status = server->listener->OpenLocal(server->listener, server->ipcSocket);
727 
728  if (!status)
729  {
730  WLog_ERR(TAG, "Problem creating local socket listener. (Port already used or "
731  "insufficient permissions?)");
732  return -1;
733  }
734  }
735 
736  if (!(server->thread = CreateThread(NULL, 0, shadow_server_thread, (void*)server, 0, NULL)))
737  {
738  return -1;
739  }
740 
741  return 0;
742 }
743 
744 int shadow_server_stop(rdpShadowServer* server)
745 {
746  if (!server)
747  return -1;
748 
749  if (server->thread)
750  {
751  (void)SetEvent(server->StopEvent);
752  (void)WaitForSingleObject(server->thread, INFINITE);
753  (void)CloseHandle(server->thread);
754  server->thread = NULL;
755  if (server->listener && server->listener->Close)
756  server->listener->Close(server->listener);
757  }
758 
759  if (server->screen)
760  {
761  shadow_screen_free(server->screen);
762  server->screen = NULL;
763  }
764 
765  if (server->capture)
766  {
767  shadow_capture_free(server->capture);
768  server->capture = NULL;
769  }
770 
771  return 0;
772 }
773 
774 static int shadow_server_init_config_path(rdpShadowServer* server)
775 {
776  if (!server->ConfigPath)
777  {
778  char* configHome = freerdp_settings_get_config_path();
779 
780  if (configHome)
781  {
782  if (!winpr_PathFileExists(configHome) && !winpr_PathMakePath(configHome, 0))
783  {
784  WLog_ERR(TAG, "Failed to create directory '%s'", configHome);
785  free(configHome);
786  return -1;
787  }
788 
789  server->ConfigPath = configHome;
790  }
791  }
792 
793  if (!server->ConfigPath)
794  return -1; /* no usable config path */
795 
796  return 1;
797 }
798 
799 static BOOL shadow_server_create_certificate(rdpShadowServer* server, const char* filepath)
800 {
801  BOOL rc = FALSE;
802  char* makecert_argv[6] = { "makecert", "-rdp", "-live", "-silent", "-y", "5" };
803 
804  WINPR_STATIC_ASSERT(ARRAYSIZE(makecert_argv) <= INT_MAX);
805  const size_t makecert_argc = ARRAYSIZE(makecert_argv);
806 
807  MAKECERT_CONTEXT* makecert = makecert_context_new();
808 
809  if (!makecert)
810  goto out_fail;
811 
812  if (makecert_context_process(makecert, (int)makecert_argc, makecert_argv) < 0)
813  goto out_fail;
814 
815  if (makecert_context_set_output_file_name(makecert, "shadow") != 1)
816  goto out_fail;
817 
818  WINPR_ASSERT(server);
819  WINPR_ASSERT(filepath);
820  if (!winpr_PathFileExists(server->CertificateFile))
821  {
822  if (makecert_context_output_certificate_file(makecert, filepath) != 1)
823  goto out_fail;
824  }
825 
826  if (!winpr_PathFileExists(server->PrivateKeyFile))
827  {
828  if (makecert_context_output_private_key_file(makecert, filepath) != 1)
829  goto out_fail;
830  }
831  rc = TRUE;
832 out_fail:
833  makecert_context_free(makecert);
834  return rc;
835 }
836 static BOOL shadow_server_init_certificate(rdpShadowServer* server)
837 {
838  char* filepath = NULL;
839  BOOL ret = FALSE;
840 
841  WINPR_ASSERT(server);
842 
843  if (!winpr_PathFileExists(server->ConfigPath) && !winpr_PathMakePath(server->ConfigPath, 0))
844  {
845  WLog_ERR(TAG, "Failed to create directory '%s'", server->ConfigPath);
846  return FALSE;
847  }
848 
849  if (!(filepath = GetCombinedPath(server->ConfigPath, "shadow")))
850  return FALSE;
851 
852  if (!winpr_PathFileExists(filepath) && !winpr_PathMakePath(filepath, 0))
853  {
854  if (!CreateDirectoryA(filepath, 0))
855  {
856  WLog_ERR(TAG, "Failed to create directory '%s'", filepath);
857  goto out_fail;
858  }
859  }
860 
861  server->CertificateFile = GetCombinedPath(filepath, "shadow.crt");
862  server->PrivateKeyFile = GetCombinedPath(filepath, "shadow.key");
863 
864  if (!server->CertificateFile || !server->PrivateKeyFile)
865  goto out_fail;
866 
867  if ((!winpr_PathFileExists(server->CertificateFile)) ||
868  (!winpr_PathFileExists(server->PrivateKeyFile)))
869  {
870  if (!shadow_server_create_certificate(server, filepath))
871  goto out_fail;
872  }
873 
874  rdpSettings* settings = server->settings;
875  WINPR_ASSERT(settings);
876 
877  rdpPrivateKey* key = freerdp_key_new_from_file(server->PrivateKeyFile);
878  if (!key)
879  goto out_fail;
880  if (!freerdp_settings_set_pointer_len(settings, FreeRDP_RdpServerRsaKey, key, 1))
881  goto out_fail;
882 
883  rdpCertificate* cert = freerdp_certificate_new_from_file(server->CertificateFile);
884  if (!cert)
885  goto out_fail;
886 
887  if (!freerdp_settings_set_pointer_len(settings, FreeRDP_RdpServerCertificate, cert, 1))
888  goto out_fail;
889 
890  if (!freerdp_certificate_is_rdp_security_compatible(cert))
891  {
892  if (!freerdp_settings_set_bool(settings, FreeRDP_UseRdpSecurityLayer, FALSE))
893  goto out_fail;
894  if (!freerdp_settings_set_bool(settings, FreeRDP_RdpSecurity, FALSE))
895  goto out_fail;
896  }
897  ret = TRUE;
898 out_fail:
899  free(filepath);
900  return ret;
901 }
902 
903 static BOOL shadow_server_check_peer_restrictions(freerdp_listener* listener)
904 {
905  WINPR_ASSERT(listener);
906 
907  rdpShadowServer* server = (rdpShadowServer*)listener->info;
908  WINPR_ASSERT(server);
909 
910  if (server->maxClientsConnected > 0)
911  {
912  const size_t count = ArrayList_Count(server->clients);
913  if (count >= server->maxClientsConnected)
914  {
915  WLog_WARN(TAG, "connection limit [%" PRIuz "] reached, discarding client",
916  server->maxClientsConnected);
917  return FALSE;
918  }
919  }
920  return TRUE;
921 }
922 
923 int shadow_server_init(rdpShadowServer* server)
924 {
925  int status = 0;
926  winpr_InitializeSSL(WINPR_SSL_INIT_DEFAULT);
927  WTSRegisterWtsApiFunctionTable(FreeRDP_InitWtsApi());
928 
929  if (!(server->clients = ArrayList_New(TRUE)))
930  goto fail;
931 
932  if (!(server->StopEvent = CreateEvent(NULL, TRUE, FALSE, NULL)))
933  goto fail;
934 
935  if (!InitializeCriticalSectionAndSpinCount(&(server->lock), 4000))
936  goto fail;
937 
938  status = shadow_server_init_config_path(server);
939 
940  if (status < 0)
941  goto fail;
942 
943  if (!shadow_server_init_certificate(server))
944  goto fail;
945 
946  server->listener = freerdp_listener_new();
947 
948  if (!server->listener)
949  goto fail;
950 
951  server->listener->info = (void*)server;
952  server->listener->CheckPeerAcceptRestrictions = shadow_server_check_peer_restrictions;
953  server->listener->PeerAccepted = shadow_client_accepted;
954  server->subsystem = shadow_subsystem_new();
955 
956  if (!server->subsystem)
957  goto fail;
958 
959  status = shadow_subsystem_init(server->subsystem, server);
960  if (status < 0)
961  goto fail;
962 
963  return status;
964 
965 fail:
966  shadow_server_uninit(server);
967  WLog_ERR(TAG, "Failed to initialize shadow server");
968  return -1;
969 }
970 
971 int shadow_server_uninit(rdpShadowServer* server)
972 {
973  if (!server)
974  return -1;
975 
976  shadow_server_stop(server);
977  shadow_subsystem_uninit(server->subsystem);
978  shadow_subsystem_free(server->subsystem);
979  server->subsystem = NULL;
980  freerdp_listener_free(server->listener);
981  server->listener = NULL;
982  free(server->CertificateFile);
983  server->CertificateFile = NULL;
984  free(server->PrivateKeyFile);
985  server->PrivateKeyFile = NULL;
986  free(server->ConfigPath);
987  server->ConfigPath = NULL;
988  DeleteCriticalSection(&(server->lock));
989  (void)CloseHandle(server->StopEvent);
990  server->StopEvent = NULL;
991  ArrayList_Free(server->clients);
992  server->clients = NULL;
993  return 1;
994 }
995 
996 rdpShadowServer* shadow_server_new(void)
997 {
998  rdpShadowServer* server = NULL;
999  server = (rdpShadowServer*)calloc(1, sizeof(rdpShadowServer));
1000 
1001  if (!server)
1002  return NULL;
1003 
1004  server->port = 3389;
1005  server->mayView = TRUE;
1006  server->mayInteract = TRUE;
1007  server->h264RateControlMode = H264_RATECONTROL_VBR;
1008  server->h264BitRate = 10000000;
1009  server->h264FrameRate = 30;
1010  server->h264QP = 0;
1011  server->authentication = TRUE;
1013  return server;
1014 }
1015 
1016 void shadow_server_free(rdpShadowServer* server)
1017 {
1018  if (!server)
1019  return;
1020 
1021  free(server->ipcSocket);
1022  server->ipcSocket = NULL;
1023  freerdp_settings_free(server->settings);
1024  server->settings = NULL;
1025  free(server);
1026 }
FREERDP_API BOOL freerdp_settings_set_string(rdpSettings *settings, FreeRDP_Settings_Keys_String id, const char *param)
Sets a string settings value. The param is copied.
FREERDP_API rdpSettings * freerdp_settings_new(DWORD flags)
creates a new setting struct
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 char * freerdp_settings_get_config_path(void)
return the configuration directory for the library
FREERDP_API void freerdp_settings_free(rdpSettings *settings)
Free a settings struct with all data in it.
#define FREERDP_SETTINGS_SERVER_MODE
FREERDP_API BOOL freerdp_settings_set_bool(rdpSettings *settings, FreeRDP_Settings_Keys_Bool id, BOOL param)
Sets a BOOL settings value.