1 /**
2  * FreeRDP: A Remote Desktop Protocol Implementation
3  *
4  * Copyright 2014 Marc-Andre Moreau <marcandre.moreau@gmail.com>
5  * Copyright 2017 Armin Novak <armin.novak@thincast.com>
6  * Copyright 2017 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 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24 
25 #include <errno.h>
26 
27 #include <winpr/crt.h>
28 #include <winpr/ssl.h>
29 #include <winpr/wnd.h>
30 #include <winpr/path.h>
31 #include <winpr/cmdline.h>
32 #include <winpr/winsock.h>
33 
34 #include <freerdp/log.h>
35 #include <freerdp/version.h>
36 
37 #include <winpr/tools/makecert.h>
38 
39 #ifndef _WIN32
40 #include <sys/select.h>
41 #include <signal.h>
42 #endif
43 
44 #include "shadow.h"
45 
46 #define TAG SERVER_TAG("shadow")
47 
48 static const char bind_address[] = "bind-address,";
49 
50 static const COMMAND_LINE_ARGUMENT_A shadow_args[] = {
51 	{ "port", COMMAND_LINE_VALUE_REQUIRED, "<number>", NULL, NULL, -1, NULL, "Server port" },
52 	{ "ipc-socket", COMMAND_LINE_VALUE_REQUIRED, "<ipc-socket>", NULL, NULL, -1, NULL,
53 	  "Server IPC socket" },
54 	{ "bind-address", COMMAND_LINE_VALUE_REQUIRED, "<bind-address>[,<another address>, ...]", NULL,
55 	  NULL, -1, NULL,
56 	  "An address to bind to. Use '[<ipv6>]' for IPv6 addresses, e.g. '[::1]' for "
57 	  "localhost" },
58 	{ "monitors", COMMAND_LINE_VALUE_OPTIONAL, "<0,1,2...>", NULL, NULL, -1, NULL,
59 	  "Select or list monitors" },
60 	{ "rect", COMMAND_LINE_VALUE_REQUIRED, "<x,y,w,h>", NULL, NULL, -1, NULL,
61 	  "Select rectangle within monitor to share" },
62 	{ "auth", COMMAND_LINE_VALUE_BOOL, NULL, BoolValueFalse, NULL, -1, NULL,
63 	  "Clients must authenticate" },
64 	{ "may-view", COMMAND_LINE_VALUE_BOOL, NULL, BoolValueTrue, NULL, -1, NULL,
65 	  "Clients may view without prompt" },
66 	{ "may-interact", COMMAND_LINE_VALUE_BOOL, NULL, BoolValueTrue, NULL, -1, NULL,
67 	  "Clients may interact without prompt" },
68 	{ "sec", COMMAND_LINE_VALUE_REQUIRED, "<rdp|tls|nla|ext>", NULL, NULL, -1, NULL,
69 	  "force specific protocol security" },
70 	{ "sec-rdp", COMMAND_LINE_VALUE_BOOL, NULL, BoolValueTrue, NULL, -1, NULL,
71 	  "rdp protocol security" },
72 	{ "sec-tls", COMMAND_LINE_VALUE_BOOL, NULL, BoolValueTrue, NULL, -1, NULL,
73 	  "tls protocol security" },
74 	{ "sec-nla", COMMAND_LINE_VALUE_BOOL, NULL, BoolValueTrue, NULL, -1, NULL,
75 	  "nla protocol security" },
76 	{ "sec-ext", COMMAND_LINE_VALUE_BOOL, NULL, BoolValueFalse, NULL, -1, NULL,
77 	  "nla extended protocol security" },
78 	{ "sam-file", COMMAND_LINE_VALUE_REQUIRED, "<file>", NULL, NULL, -1, NULL,
79 	  "NTLM SAM file for NLA authentication" },
80 	{ "version", COMMAND_LINE_VALUE_FLAG | COMMAND_LINE_PRINT_VERSION, NULL, NULL, NULL, -1, NULL,
81 	  "Print version" },
82 	{ "buildconfig", COMMAND_LINE_VALUE_FLAG | COMMAND_LINE_PRINT_BUILDCONFIG, NULL, NULL, NULL, -1,
83 	  NULL, "Print the build configuration" },
84 	{ "help", COMMAND_LINE_VALUE_FLAG | COMMAND_LINE_PRINT_HELP, NULL, NULL, NULL, -1, "?",
85 	  "Print help" },
86 	{ NULL, 0, NULL, NULL, NULL, -1, NULL, NULL }
87 };
88 
shadow_server_print_command_line_help(int argc,char ** argv)89 static int shadow_server_print_command_line_help(int argc, char** argv)
90 {
91 	char* str;
92 	size_t length;
93 	COMMAND_LINE_ARGUMENT_A* arg;
94 	COMMAND_LINE_ARGUMENT_A largs[ARRAYSIZE(shadow_args)];
95 	memcpy(largs, shadow_args, sizeof(shadow_args));
96 	if (argc < 1)
97 		return -1;
98 
99 	WLog_INFO(TAG, "Usage: %s [options]", argv[0]);
100 	WLog_INFO(TAG, "");
101 	WLog_INFO(TAG, "Syntax:");
102 	WLog_INFO(TAG, "    /flag (enables flag)");
103 	WLog_INFO(TAG, "    /option:<value> (specifies option with value)");
104 	WLog_INFO(TAG,
105 	          "    +toggle -toggle (enables or disables toggle, where '/' is a synonym of '+')");
106 	WLog_INFO(TAG, "");
107 	arg = largs;
108 
109 	do
110 	{
111 		if (arg->Flags & COMMAND_LINE_VALUE_FLAG)
112 		{
113 			WLog_INFO(TAG, "    %s", "/");
114 			WLog_INFO(TAG, "%-20s", arg->Name);
115 			WLog_INFO(TAG, "\t%s", arg->Text);
116 		}
117 		else if ((arg->Flags & COMMAND_LINE_VALUE_REQUIRED) ||
118 		         (arg->Flags & COMMAND_LINE_VALUE_OPTIONAL))
119 		{
120 			WLog_INFO(TAG, "    %s", "/");
121 
122 			if (arg->Format)
123 			{
124 				length = (strlen(arg->Name) + strlen(arg->Format) + 2);
125 				str = (char*)malloc(length + 1);
126 
127 				if (!str)
128 					return -1;
129 
130 				sprintf_s(str, length + 1, "%s:%s", arg->Name, arg->Format);
131 				WLog_INFO(TAG, "%-20s", str);
132 				free(str);
133 			}
134 			else
135 			{
136 				WLog_INFO(TAG, "%-20s", arg->Name);
137 			}
138 
139 			WLog_INFO(TAG, "\t%s", arg->Text);
140 		}
141 		else if (arg->Flags & COMMAND_LINE_VALUE_BOOL)
142 		{
143 			length = strlen(arg->Name) + 32;
144 			str = (char*)malloc(length + 1);
145 
146 			if (!str)
147 				return -1;
148 
149 			sprintf_s(str, length + 1, "%s (default:%s)", arg->Name, arg->Default ? "on" : "off");
150 			WLog_INFO(TAG, "    %s", arg->Default ? "-" : "+");
151 			WLog_INFO(TAG, "%-20s", str);
152 			free(str);
153 			WLog_INFO(TAG, "\t%s", arg->Text);
154 		}
155 	} while ((arg = CommandLineFindNextArgumentA(arg)) != NULL);
156 
157 	return 1;
158 }
159 
shadow_server_command_line_status_print(rdpShadowServer * server,int argc,char ** argv,int status)160 int shadow_server_command_line_status_print(rdpShadowServer* server, int argc, char** argv,
161                                             int status)
162 {
163 	WINPR_UNUSED(server);
164 
165 	if (status == COMMAND_LINE_STATUS_PRINT_VERSION)
166 	{
167 		WLog_INFO(TAG, "FreeRDP version %s (git %s)", FREERDP_VERSION_FULL, GIT_REVISION);
168 		return COMMAND_LINE_STATUS_PRINT_VERSION;
169 	}
170 	else if (status == COMMAND_LINE_STATUS_PRINT_BUILDCONFIG)
171 	{
172 		WLog_INFO(TAG, "%s", freerdp_get_build_config());
173 		return COMMAND_LINE_STATUS_PRINT_BUILDCONFIG;
174 	}
175 	else if (status == COMMAND_LINE_STATUS_PRINT)
176 	{
177 		return COMMAND_LINE_STATUS_PRINT;
178 	}
179 	else if (status < 0)
180 	{
181 		if (shadow_server_print_command_line_help(argc, argv) < 0)
182 			return -1;
183 
184 		return COMMAND_LINE_STATUS_PRINT_HELP;
185 	}
186 
187 	return 1;
188 }
189 
shadow_server_parse_command_line(rdpShadowServer * server,int argc,char ** argv)190 int shadow_server_parse_command_line(rdpShadowServer* server, int argc, char** argv)
191 {
192 	int status;
193 	DWORD flags;
194 	COMMAND_LINE_ARGUMENT_A* arg;
195 	rdpSettings* settings = server->settings;
196 	COMMAND_LINE_ARGUMENT_A largs[ARRAYSIZE(shadow_args)];
197 	memcpy(largs, shadow_args, sizeof(shadow_args));
198 
199 	if (argc < 2)
200 		return 1;
201 
202 	CommandLineClearArgumentsA(largs);
203 	flags = COMMAND_LINE_SEPARATOR_COLON;
204 	flags |= COMMAND_LINE_SIGIL_SLASH | COMMAND_LINE_SIGIL_PLUS_MINUS;
205 	status = CommandLineParseArgumentsA(argc, argv, largs, flags, server, NULL, NULL);
206 
207 	if (status < 0)
208 		return status;
209 
210 	arg = largs;
211 	errno = 0;
212 
213 	do
214 	{
215 		if (!(arg->Flags & COMMAND_LINE_ARGUMENT_PRESENT))
216 			continue;
217 
218 		CommandLineSwitchStart(arg) CommandLineSwitchCase(arg, "port")
219 		{
220 			long val = strtol(arg->Value, NULL, 0);
221 
222 			if ((errno != 0) || (val <= 0) || (val > UINT16_MAX))
223 				return -1;
224 
225 			server->port = (DWORD)val;
226 		}
227 		CommandLineSwitchCase(arg, "ipc-socket")
228 		{
229 			/* /bind-address is incompatible */
230 			if (server->ipcSocket)
231 				return -1;
232 
233 			server->ipcSocket = _strdup(arg->Value);
234 
235 			if (!server->ipcSocket)
236 				return -1;
237 		}
238 		CommandLineSwitchCase(arg, "bind-address")
239 		{
240 			int rc;
241 			size_t len = strlen(arg->Value) + sizeof(bind_address);
242 			/* /ipc-socket is incompatible */
243 			if (server->ipcSocket)
244 				return -1;
245 			server->ipcSocket = calloc(len, sizeof(CHAR));
246 
247 			if (!server->ipcSocket)
248 				return -1;
249 
250 			rc = _snprintf(server->ipcSocket, len, "%s%s", bind_address, arg->Value);
251 			if ((rc < 0) || ((size_t)rc != len - 1))
252 				return -1;
253 		}
254 		CommandLineSwitchCase(arg, "may-view")
255 		{
256 			server->mayView = arg->Value ? TRUE : FALSE;
257 		}
258 		CommandLineSwitchCase(arg, "may-interact")
259 		{
260 			server->mayInteract = arg->Value ? TRUE : FALSE;
261 		}
262 		CommandLineSwitchCase(arg, "rect")
263 		{
264 			char* p;
265 			char* tok[4];
266 			long x = -1, y = -1, w = -1, h = -1;
267 			char* str = _strdup(arg->Value);
268 
269 			if (!str)
270 				return -1;
271 
272 			tok[0] = p = str;
273 			p = strchr(p + 1, ',');
274 
275 			if (!p)
276 			{
277 				free(str);
278 				return -1;
279 			}
280 
281 			*p++ = '\0';
282 			tok[1] = p;
283 			p = strchr(p + 1, ',');
284 
285 			if (!p)
286 			{
287 				free(str);
288 				return -1;
289 			}
290 
291 			*p++ = '\0';
292 			tok[2] = p;
293 			p = strchr(p + 1, ',');
294 
295 			if (!p)
296 			{
297 				free(str);
298 				return -1;
299 			}
300 
301 			*p++ = '\0';
302 			tok[3] = p;
303 			x = strtol(tok[0], NULL, 0);
304 
305 			if (errno != 0)
306 				goto fail;
307 
308 			y = strtol(tok[1], NULL, 0);
309 
310 			if (errno != 0)
311 				goto fail;
312 
313 			w = strtol(tok[2], NULL, 0);
314 
315 			if (errno != 0)
316 				goto fail;
317 
318 			h = strtol(tok[3], NULL, 0);
319 
320 			if (errno != 0)
321 				goto fail;
322 
323 		fail:
324 			free(str);
325 
326 			if ((x < 0) || (y < 0) || (w < 1) || (h < 1) || (errno != 0))
327 				return -1;
328 
329 			server->subRect.left = x;
330 			server->subRect.top = y;
331 			server->subRect.right = x + w;
332 			server->subRect.bottom = y + h;
333 			server->shareSubRect = TRUE;
334 		}
335 		CommandLineSwitchCase(arg, "auth")
336 		{
337 			server->authentication = arg->Value ? TRUE : FALSE;
338 		}
339 		CommandLineSwitchCase(arg, "sec")
340 		{
341 			if (strcmp("rdp", arg->Value) == 0) /* Standard RDP */
342 			{
343 				settings->RdpSecurity = TRUE;
344 				settings->TlsSecurity = FALSE;
345 				settings->NlaSecurity = FALSE;
346 				settings->ExtSecurity = FALSE;
347 				settings->UseRdpSecurityLayer = TRUE;
348 			}
349 			else if (strcmp("tls", arg->Value) == 0) /* TLS */
350 			{
351 				settings->RdpSecurity = FALSE;
352 				settings->TlsSecurity = TRUE;
353 				settings->NlaSecurity = FALSE;
354 				settings->ExtSecurity = FALSE;
355 			}
356 			else if (strcmp("nla", arg->Value) == 0) /* NLA */
357 			{
358 				settings->RdpSecurity = FALSE;
359 				settings->TlsSecurity = FALSE;
360 				settings->NlaSecurity = TRUE;
361 				settings->ExtSecurity = FALSE;
362 			}
363 			else if (strcmp("ext", arg->Value) == 0) /* NLA Extended */
364 			{
365 				settings->RdpSecurity = FALSE;
366 				settings->TlsSecurity = FALSE;
367 				settings->NlaSecurity = FALSE;
368 				settings->ExtSecurity = TRUE;
369 			}
370 			else
371 			{
372 				WLog_ERR(TAG, "unknown protocol security: %s", arg->Value);
373 			}
374 		}
375 		CommandLineSwitchCase(arg, "sec-rdp")
376 		{
377 			settings->RdpSecurity = arg->Value ? TRUE : FALSE;
378 		}
379 		CommandLineSwitchCase(arg, "sec-tls")
380 		{
381 			settings->TlsSecurity = arg->Value ? TRUE : FALSE;
382 		}
383 		CommandLineSwitchCase(arg, "sec-nla")
384 		{
385 			settings->NlaSecurity = arg->Value ? TRUE : FALSE;
386 		}
387 		CommandLineSwitchCase(arg, "sec-ext")
388 		{
389 			settings->ExtSecurity = arg->Value ? TRUE : FALSE;
390 		}
391 		CommandLineSwitchCase(arg, "sam-file")
392 		{
393 			freerdp_settings_set_string(settings, FreeRDP_NtlmSamFile, arg->Value);
394 		}
395 		CommandLineSwitchDefault(arg)
396 		{
397 		}
398 		CommandLineSwitchEnd(arg)
399 	} while ((arg = CommandLineFindNextArgumentA(arg)) != NULL);
400 
401 	arg = CommandLineFindArgumentA(largs, "monitors");
402 
403 	if (arg && (arg->Flags & COMMAND_LINE_ARGUMENT_PRESENT))
404 	{
405 		int index;
406 		int numMonitors;
407 		MONITOR_DEF monitors[16];
408 		numMonitors = shadow_enum_monitors(monitors, 16);
409 
410 		if (arg->Flags & COMMAND_LINE_VALUE_PRESENT)
411 		{
412 			/* Select monitors */
413 			long val = strtol(arg->Value, NULL, 0);
414 
415 			if ((val < 0) || (errno != 0) || (val >= numMonitors))
416 				status = COMMAND_LINE_STATUS_PRINT;
417 
418 			server->selectedMonitor = val;
419 		}
420 		else
421 		{
422 			int width, height;
423 			MONITOR_DEF* monitor;
424 
425 			/* List monitors */
426 
427 			for (index = 0; index < numMonitors; index++)
428 			{
429 				monitor = &monitors[index];
430 				width = monitor->right - monitor->left;
431 				height = monitor->bottom - monitor->top;
432 				WLog_INFO(TAG, "      %s [%d] %dx%d\t+%" PRId32 "+%" PRId32 "",
433 				          (monitor->flags == 1) ? "*" : " ", index, width, height, monitor->left,
434 				          monitor->top);
435 			}
436 
437 			status = COMMAND_LINE_STATUS_PRINT;
438 		}
439 	}
440 
441 	return status;
442 }
443 
shadow_server_thread(LPVOID arg)444 static DWORD WINAPI shadow_server_thread(LPVOID arg)
445 {
446 	rdpShadowServer* server = (rdpShadowServer*)arg;
447 	BOOL running = TRUE;
448 	DWORD status;
449 	freerdp_listener* listener = server->listener;
450 	shadow_subsystem_start(server->subsystem);
451 
452 	while (running)
453 	{
454 		HANDLE events[32];
455 		DWORD nCount = 0;
456 		events[nCount++] = server->StopEvent;
457 		nCount += listener->GetEventHandles(listener, &events[nCount], 32 - nCount);
458 
459 		if (nCount <= 1)
460 		{
461 			WLog_ERR(TAG, "Failed to get FreeRDP file descriptor");
462 			break;
463 		}
464 
465 		status = WaitForMultipleObjects(nCount, events, FALSE, INFINITE);
466 
467 		switch (status)
468 		{
469 			case WAIT_FAILED:
470 			case WAIT_OBJECT_0:
471 				running = FALSE;
472 				break;
473 
474 			default:
475 			{
476 				if (!listener->CheckFileDescriptor(listener))
477 				{
478 					WLog_ERR(TAG, "Failed to check FreeRDP file descriptor");
479 					running = FALSE;
480 				}
481 				else
482 				{
483 #ifdef _WIN32
484 					Sleep(100); /* FIXME: listener event handles */
485 #endif
486 				}
487 			}
488 			break;
489 		}
490 	}
491 
492 	listener->Close(listener);
493 	shadow_subsystem_stop(server->subsystem);
494 
495 	/* Signal to the clients that server is being stopped and wait for them
496 	 * to disconnect. */
497 	if (shadow_client_boardcast_quit(server, 0))
498 	{
499 		while (ArrayList_Count(server->clients) > 0)
500 		{
501 			Sleep(100);
502 		}
503 	}
504 
505 	ExitThread(0);
506 	return 0;
507 }
508 
open_port(rdpShadowServer * server,char * address)509 static BOOL open_port(rdpShadowServer* server, char* address)
510 {
511 	BOOL status;
512 	char* modaddr = address;
513 
514 	if (modaddr)
515 	{
516 		if (modaddr[0] == '[')
517 		{
518 			char* end = strchr(address, ']');
519 			if (!end)
520 			{
521 				WLog_ERR(TAG, "Could not parse bind-address %s", address);
522 				return -1;
523 			}
524 			*end++ = '\0';
525 			if (strlen(end) > 0)
526 			{
527 				WLog_ERR(TAG, "Excess data after IPv6 address: '%s'", end);
528 				return -1;
529 			}
530 			modaddr++;
531 		}
532 	}
533 	status = server->listener->Open(server->listener, modaddr, (UINT16)server->port);
534 
535 	if (!status)
536 	{
537 		WLog_ERR(TAG,
538 		         "Problem creating TCP listener. (Port already used or insufficient permissions?)");
539 	}
540 
541 	return status;
542 }
543 
shadow_server_start(rdpShadowServer * server)544 int shadow_server_start(rdpShadowServer* server)
545 {
546 	BOOL ipc;
547 	BOOL status;
548 	WSADATA wsaData;
549 
550 	if (!server)
551 		return -1;
552 
553 	if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
554 		return -1;
555 
556 #ifndef _WIN32
557 	signal(SIGPIPE, SIG_IGN);
558 #endif
559 	server->screen = shadow_screen_new(server);
560 
561 	if (!server->screen)
562 	{
563 		WLog_ERR(TAG, "screen_new failed");
564 		return -1;
565 	}
566 
567 	server->capture = shadow_capture_new(server);
568 
569 	if (!server->capture)
570 	{
571 		WLog_ERR(TAG, "capture_new failed");
572 		return -1;
573 	}
574 
575 	/* Bind magic:
576 	 *
577 	 * emtpy                 ... bind TCP all
578 	 * <local path>          ... bind local (IPC)
579 	 * bind-socket,<address> ... bind TCP to specified interface
580 	 */
581 	ipc = server->ipcSocket && (strncmp(bind_address, server->ipcSocket,
582 	                                    strnlen(bind_address, sizeof(bind_address))) != 0);
583 	if (!ipc)
584 	{
585 		size_t x, count;
586 		char** list = CommandLineParseCommaSeparatedValuesEx(NULL, server->ipcSocket, &count);
587 		if (!list || (count <= 1))
588 		{
589 			if (server->ipcSocket == NULL)
590 			{
591 				if (!open_port(server, NULL))
592 				{
593 					free(list);
594 					return -1;
595 				}
596 			}
597 			else
598 			{
599 				free(list);
600 				return -1;
601 			}
602 		}
603 
604 		for (x = 1; x < count; x++)
605 		{
606 			BOOL success = open_port(server, list[x]);
607 			if (!success)
608 			{
609 				free(list);
610 				return -1;
611 			}
612 		}
613 		free(list);
614 	}
615 	else
616 	{
617 		status = server->listener->OpenLocal(server->listener, server->ipcSocket);
618 		if (!status)
619 		{
620 			WLog_ERR(TAG, "Problem creating local socket listener. (Port already used or "
621 			              "insufficient permissions?)");
622 			return -1;
623 		}
624 	}
625 
626 	if (!(server->thread = CreateThread(NULL, 0, shadow_server_thread, (void*)server, 0, NULL)))
627 	{
628 		return -1;
629 	}
630 
631 	return 0;
632 }
633 
shadow_server_stop(rdpShadowServer * server)634 int shadow_server_stop(rdpShadowServer* server)
635 {
636 	if (!server)
637 		return -1;
638 
639 	if (server->thread)
640 	{
641 		SetEvent(server->StopEvent);
642 		WaitForSingleObject(server->thread, INFINITE);
643 		CloseHandle(server->thread);
644 		server->thread = NULL;
645 		server->listener->Close(server->listener);
646 	}
647 
648 	if (server->screen)
649 	{
650 		shadow_screen_free(server->screen);
651 		server->screen = NULL;
652 	}
653 
654 	if (server->capture)
655 	{
656 		shadow_capture_free(server->capture);
657 		server->capture = NULL;
658 	}
659 
660 	return 0;
661 }
662 
shadow_server_init_config_path(rdpShadowServer * server)663 static int shadow_server_init_config_path(rdpShadowServer* server)
664 {
665 #ifdef _WIN32
666 
667 	if (!server->ConfigPath)
668 	{
669 		server->ConfigPath = GetEnvironmentSubPath("LOCALAPPDATA", "freerdp");
670 	}
671 
672 #endif
673 #ifdef __APPLE__
674 
675 	if (!server->ConfigPath)
676 	{
677 		char* userLibraryPath;
678 		char* userApplicationSupportPath;
679 		userLibraryPath = GetKnownSubPath(KNOWN_PATH_HOME, "Library");
680 
681 		if (userLibraryPath)
682 		{
683 			if (!winpr_PathFileExists(userLibraryPath) && !winpr_PathMakePath(userLibraryPath, 0))
684 			{
685 				WLog_ERR(TAG, "Failed to create directory '%s'", userLibraryPath);
686 				free(userLibraryPath);
687 				return -1;
688 			}
689 
690 			userApplicationSupportPath = GetCombinedPath(userLibraryPath, "Application Support");
691 
692 			if (userApplicationSupportPath)
693 			{
694 				if (!winpr_PathFileExists(userApplicationSupportPath) &&
695 				    !winpr_PathMakePath(userApplicationSupportPath, 0))
696 				{
697 					WLog_ERR(TAG, "Failed to create directory '%s'", userApplicationSupportPath);
698 					free(userLibraryPath);
699 					free(userApplicationSupportPath);
700 					return -1;
701 				}
702 
703 				server->ConfigPath = GetCombinedPath(userApplicationSupportPath, "freerdp");
704 			}
705 
706 			free(userLibraryPath);
707 			free(userApplicationSupportPath);
708 		}
709 	}
710 
711 #endif
712 
713 	if (!server->ConfigPath)
714 	{
715 		char* configHome;
716 		configHome = GetKnownPath(KNOWN_PATH_XDG_CONFIG_HOME);
717 
718 		if (configHome)
719 		{
720 			if (!winpr_PathFileExists(configHome) && !winpr_PathMakePath(configHome, 0))
721 			{
722 				WLog_ERR(TAG, "Failed to create directory '%s'", configHome);
723 				free(configHome);
724 				return -1;
725 			}
726 
727 			server->ConfigPath = GetKnownSubPath(KNOWN_PATH_XDG_CONFIG_HOME, "freerdp");
728 			free(configHome);
729 		}
730 	}
731 
732 	if (!server->ConfigPath)
733 		return -1; /* no usable config path */
734 
735 	return 1;
736 }
737 
shadow_server_init_certificate(rdpShadowServer * server)738 static BOOL shadow_server_init_certificate(rdpShadowServer* server)
739 {
740 	char* filepath;
741 	MAKECERT_CONTEXT* makecert = NULL;
742 	BOOL ret = FALSE;
743 	const char* makecert_argv[6] = { "makecert", "-rdp", "-live", "-silent", "-y", "5" };
744 	int makecert_argc = (sizeof(makecert_argv) / sizeof(char*));
745 
746 	if (!winpr_PathFileExists(server->ConfigPath) && !winpr_PathMakePath(server->ConfigPath, 0))
747 	{
748 		WLog_ERR(TAG, "Failed to create directory '%s'", server->ConfigPath);
749 		return FALSE;
750 	}
751 
752 	if (!(filepath = GetCombinedPath(server->ConfigPath, "shadow")))
753 		return FALSE;
754 
755 	if (!winpr_PathFileExists(filepath) && !winpr_PathMakePath(filepath, 0))
756 	{
757 		if (!CreateDirectoryA(filepath, 0))
758 		{
759 			WLog_ERR(TAG, "Failed to create directory '%s'", filepath);
760 			goto out_fail;
761 		}
762 	}
763 
764 	server->CertificateFile = GetCombinedPath(filepath, "shadow.crt");
765 	server->PrivateKeyFile = GetCombinedPath(filepath, "shadow.key");
766 
767 	if (!server->CertificateFile || !server->PrivateKeyFile)
768 		goto out_fail;
769 
770 	if ((!winpr_PathFileExists(server->CertificateFile)) ||
771 	    (!winpr_PathFileExists(server->PrivateKeyFile)))
772 	{
773 		makecert = makecert_context_new();
774 
775 		if (!makecert)
776 			goto out_fail;
777 
778 		if (makecert_context_process(makecert, makecert_argc, (char**)makecert_argv) < 0)
779 			goto out_fail;
780 
781 		if (makecert_context_set_output_file_name(makecert, "shadow") != 1)
782 			goto out_fail;
783 
784 		if (!winpr_PathFileExists(server->CertificateFile))
785 		{
786 			if (makecert_context_output_certificate_file(makecert, filepath) != 1)
787 				goto out_fail;
788 		}
789 
790 		if (!winpr_PathFileExists(server->PrivateKeyFile))
791 		{
792 			if (makecert_context_output_private_key_file(makecert, filepath) != 1)
793 				goto out_fail;
794 		}
795 	}
796 
797 	ret = TRUE;
798 out_fail:
799 	makecert_context_free(makecert);
800 	free(filepath);
801 	return ret;
802 }
803 
shadow_server_init(rdpShadowServer * server)804 int shadow_server_init(rdpShadowServer* server)
805 {
806 	int status;
807 	winpr_InitializeSSL(WINPR_SSL_INIT_DEFAULT);
808 	WTSRegisterWtsApiFunctionTable(FreeRDP_InitWtsApi());
809 
810 	if (!(server->clients = ArrayList_New(TRUE)))
811 		goto fail_client_array;
812 
813 	if (!(server->StopEvent = CreateEvent(NULL, TRUE, FALSE, NULL)))
814 		goto fail_stop_event;
815 
816 	if (!InitializeCriticalSectionAndSpinCount(&(server->lock), 4000))
817 		goto fail_server_lock;
818 
819 	status = shadow_server_init_config_path(server);
820 
821 	if (status < 0)
822 		goto fail_config_path;
823 
824 	status = shadow_server_init_certificate(server);
825 
826 	if (status < 0)
827 		goto fail_certificate;
828 
829 	server->listener = freerdp_listener_new();
830 
831 	if (!server->listener)
832 		goto fail_listener;
833 
834 	server->listener->info = (void*)server;
835 	server->listener->PeerAccepted = shadow_client_accepted;
836 	server->subsystem = shadow_subsystem_new();
837 
838 	if (!server->subsystem)
839 		goto fail_subsystem_new;
840 
841 	status = shadow_subsystem_init(server->subsystem, server);
842 
843 	if (status >= 0)
844 		return status;
845 
846 	shadow_subsystem_free(server->subsystem);
847 fail_subsystem_new:
848 	freerdp_listener_free(server->listener);
849 	server->listener = NULL;
850 fail_listener:
851 	free(server->CertificateFile);
852 	server->CertificateFile = NULL;
853 	free(server->PrivateKeyFile);
854 	server->PrivateKeyFile = NULL;
855 fail_certificate:
856 	free(server->ConfigPath);
857 	server->ConfigPath = NULL;
858 fail_config_path:
859 	DeleteCriticalSection(&(server->lock));
860 fail_server_lock:
861 	CloseHandle(server->StopEvent);
862 	server->StopEvent = NULL;
863 fail_stop_event:
864 	ArrayList_Free(server->clients);
865 	server->clients = NULL;
866 fail_client_array:
867 	WLog_ERR(TAG, "Failed to initialize shadow server");
868 	return -1;
869 }
870 
shadow_server_uninit(rdpShadowServer * server)871 int shadow_server_uninit(rdpShadowServer* server)
872 {
873 	if (!server)
874 		return -1;
875 
876 	shadow_server_stop(server);
877 	shadow_subsystem_uninit(server->subsystem);
878 	shadow_subsystem_free(server->subsystem);
879 	freerdp_listener_free(server->listener);
880 	server->listener = NULL;
881 	free(server->CertificateFile);
882 	server->CertificateFile = NULL;
883 	free(server->PrivateKeyFile);
884 	server->PrivateKeyFile = NULL;
885 	free(server->ConfigPath);
886 	server->ConfigPath = NULL;
887 	DeleteCriticalSection(&(server->lock));
888 	CloseHandle(server->StopEvent);
889 	server->StopEvent = NULL;
890 	ArrayList_Free(server->clients);
891 	server->clients = NULL;
892 	return 1;
893 }
894 
shadow_server_new(void)895 rdpShadowServer* shadow_server_new(void)
896 {
897 	rdpShadowServer* server;
898 	server = (rdpShadowServer*)calloc(1, sizeof(rdpShadowServer));
899 
900 	if (!server)
901 		return NULL;
902 
903 	server->port = 3389;
904 	server->mayView = TRUE;
905 	server->mayInteract = TRUE;
906 	server->rfxMode = RLGR3;
907 	server->h264RateControlMode = H264_RATECONTROL_VBR;
908 	server->h264BitRate = 10000000;
909 	server->h264FrameRate = 30;
910 	server->h264QP = 0;
911 	server->authentication = FALSE;
912 	server->settings = freerdp_settings_new(FREERDP_SETTINGS_SERVER_MODE);
913 	return server;
914 }
915 
shadow_server_free(rdpShadowServer * server)916 void shadow_server_free(rdpShadowServer* server)
917 {
918 	if (!server)
919 		return;
920 
921 	free(server->ipcSocket);
922 	server->ipcSocket = NULL;
923 	freerdp_settings_free(server->settings);
924 	server->settings = NULL;
925 	free(server);
926 }
927