1 /**
2 * FreeRDP: A Remote Desktop Protocol Implementation
3 * FreeRDP Proxy Server
4 *
5 * Copyright 2019 Kobi Mizrachi <kmizrachi18@gmail.com>
6 * Copyright 2019 Idan Freiberg <speidy@gmail.com>
7 *
8 * Licensed under the Apache License, Version 2.0 (the "License");
9 * you may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
11 *
12 * http://www.apache.org/licenses/LICENSE-2.0
13 *
14 * Unless required by applicable law or agreed to in writing, software
15 * distributed under the License is distributed on an "AS IS" BASIS,
16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 * See the License for the specific language governing permissions and
18 * limitations under the License.
19 */
20
21 #include <stdio.h>
22 #include <string.h>
23 #include <winpr/crt.h>
24 #include <winpr/collections.h>
25 #include <winpr/cmdline.h>
26
27 #include "pf_log.h"
28 #include "pf_server.h"
29 #include "pf_config.h"
30 #include "pf_modules.h"
31
32 #define TAG PROXY_TAG("config")
33
34 #define CONFIG_PRINT_SECTION(section) WLog_INFO(TAG, "\t%s:", section)
35 #define CONFIG_PRINT_STR(config, key) WLog_INFO(TAG, "\t\t%s: %s", #key, config->key)
36 #define CONFIG_PRINT_BOOL(config, key) \
37 WLog_INFO(TAG, "\t\t%s: %s", #key, config->key ? "TRUE" : "FALSE")
38 #define CONFIG_PRINT_UINT16(config, key) WLog_INFO(TAG, "\t\t%s: %" PRIu16 "", #key, config->key)
39 #define CONFIG_PRINT_UINT32(config, key) WLog_INFO(TAG, "\t\t%s: %" PRIu32 "", #key, config->key)
40
pf_config_get_uint16(wIniFile * ini,const char * section,const char * key,UINT16 * result)41 BOOL pf_config_get_uint16(wIniFile* ini, const char* section, const char* key, UINT16* result)
42 {
43 int val;
44
45 val = IniFile_GetKeyValueInt(ini, section, key);
46 if ((val < 0) || (val > UINT16_MAX))
47 {
48 WLog_ERR(TAG, "[%s]: invalid value %d for key '%s.%s'.", __FUNCTION__, val, section, key);
49 return FALSE;
50 }
51
52 *result = (UINT16)val;
53 return TRUE;
54 }
55
pf_config_get_uint32(wIniFile * ini,const char * section,const char * key,UINT32 * result)56 BOOL pf_config_get_uint32(wIniFile* ini, const char* section, const char* key, UINT32* result)
57 {
58 int val;
59
60 val = IniFile_GetKeyValueInt(ini, section, key);
61 if ((val < 0) || (val > INT32_MAX))
62 {
63 WLog_ERR(TAG, "[%s]: invalid value %d for key '%s.%s'.", __FUNCTION__, val, section, key);
64 return FALSE;
65 }
66
67 *result = (UINT32)val;
68 return TRUE;
69 }
70
pf_config_get_bool(wIniFile * ini,const char * section,const char * key)71 BOOL pf_config_get_bool(wIniFile* ini, const char* section, const char* key)
72 {
73 int num_value;
74 const char* str_value;
75
76 str_value = IniFile_GetKeyValueString(ini, section, key);
77 if (!str_value)
78 {
79 WLog_WARN(TAG, "[%s]: key '%s.%s' not found, value defaults to false.", __FUNCTION__,
80 section, key);
81 return FALSE;
82 }
83
84 if (strcmp(str_value, "TRUE") == 0 || strcmp(str_value, "true") == 0)
85 return TRUE;
86
87 num_value = IniFile_GetKeyValueInt(ini, section, key);
88
89 if (num_value == 1)
90 return TRUE;
91
92 return FALSE;
93 }
94
pf_config_get_str(wIniFile * ini,const char * section,const char * key)95 const char* pf_config_get_str(wIniFile* ini, const char* section, const char* key)
96 {
97 const char* value;
98
99 value = IniFile_GetKeyValueString(ini, section, key);
100
101 if (!value)
102 {
103 WLog_ERR(TAG, "[%s]: key '%s.%s' not found.", __FUNCTION__, section, key);
104 return NULL;
105 }
106
107 return value;
108 }
109
pf_config_load_server(wIniFile * ini,proxyConfig * config)110 static BOOL pf_config_load_server(wIniFile* ini, proxyConfig* config)
111 {
112 const char* host;
113
114 if (!pf_config_get_uint16(ini, "Server", "Port", &config->Port))
115 return FALSE;
116
117 host = pf_config_get_str(ini, "Server", "Host");
118
119 if (!host)
120 return FALSE;
121
122 config->Host = _strdup(host);
123
124 if (!config->Host)
125 return FALSE;
126
127 return TRUE;
128 }
129
pf_config_load_target(wIniFile * ini,proxyConfig * config)130 static BOOL pf_config_load_target(wIniFile* ini, proxyConfig* config)
131 {
132 const char* target_host;
133
134 if (!pf_config_get_uint16(ini, "Target", "Port", &config->TargetPort))
135 return FALSE;
136
137 target_host = pf_config_get_str(ini, "Target", "Host");
138
139 if (!target_host)
140 return FALSE;
141
142 config->TargetHost = _strdup(target_host);
143 if (!config->TargetHost)
144 return FALSE;
145
146 config->UseLoadBalanceInfo = pf_config_get_bool(ini, "Target", "UseLoadBalanceInfo");
147 return TRUE;
148 }
149
pf_config_load_channels(wIniFile * ini,proxyConfig * config)150 static BOOL pf_config_load_channels(wIniFile* ini, proxyConfig* config)
151 {
152 config->GFX = pf_config_get_bool(ini, "Channels", "GFX");
153 config->DisplayControl = pf_config_get_bool(ini, "Channels", "DisplayControl");
154 config->Clipboard = pf_config_get_bool(ini, "Channels", "Clipboard");
155 config->AudioOutput = pf_config_get_bool(ini, "Channels", "AudioOutput");
156 config->RemoteApp = pf_config_get_bool(ini, "Channels", "RemoteApp");
157 config->Passthrough = CommandLineParseCommaSeparatedValues(
158 pf_config_get_str(ini, "Channels", "Passthrough"), &config->PassthroughCount);
159
160 {
161 /* validate channel name length */
162 size_t i;
163
164 for (i = 0; i < config->PassthroughCount; i++)
165 {
166 if (strlen(config->Passthrough[i]) > CHANNEL_NAME_LEN)
167 {
168 WLog_ERR(TAG, "passthrough channel: %s: name too long!", config->Passthrough[i]);
169 return FALSE;
170 }
171 }
172 }
173
174 return TRUE;
175 }
176
pf_config_load_input(wIniFile * ini,proxyConfig * config)177 static BOOL pf_config_load_input(wIniFile* ini, proxyConfig* config)
178 {
179 config->Keyboard = pf_config_get_bool(ini, "Input", "Keyboard");
180 config->Mouse = pf_config_get_bool(ini, "Input", "Mouse");
181 return TRUE;
182 }
183
pf_config_load_security(wIniFile * ini,proxyConfig * config)184 static BOOL pf_config_load_security(wIniFile* ini, proxyConfig* config)
185 {
186 config->ServerTlsSecurity = pf_config_get_bool(ini, "Security", "ServerTlsSecurity");
187 config->ServerRdpSecurity = pf_config_get_bool(ini, "Security", "ServerRdpSecurity");
188
189 config->ClientTlsSecurity = pf_config_get_bool(ini, "Security", "ClientTlsSecurity");
190 config->ClientNlaSecurity = pf_config_get_bool(ini, "Security", "ClientNlaSecurity");
191 config->ClientRdpSecurity = pf_config_get_bool(ini, "Security", "ClientRdpSecurity");
192 config->ClientAllowFallbackToTls =
193 pf_config_get_bool(ini, "Security", "ClientAllowFallbackToTls");
194 return TRUE;
195 }
196
pf_config_load_clipboard(wIniFile * ini,proxyConfig * config)197 static BOOL pf_config_load_clipboard(wIniFile* ini, proxyConfig* config)
198 {
199 config->TextOnly = pf_config_get_bool(ini, "Clipboard", "TextOnly");
200
201 if (!pf_config_get_uint32(ini, "Clipboard", "MaxTextLength", &config->MaxTextLength))
202 return FALSE;
203
204 return TRUE;
205 }
206
pf_config_load_modules(wIniFile * ini,proxyConfig * config)207 static BOOL pf_config_load_modules(wIniFile* ini, proxyConfig* config)
208 {
209 const char* modules_to_load;
210 const char* required_modules;
211
212 modules_to_load = IniFile_GetKeyValueString(ini, "Plugins", "Modules");
213 required_modules = IniFile_GetKeyValueString(ini, "Plugins", "Required");
214
215 config->Modules = CommandLineParseCommaSeparatedValues(modules_to_load, &config->ModulesCount);
216
217 config->RequiredPlugins =
218 CommandLineParseCommaSeparatedValues(required_modules, &config->RequiredPluginsCount);
219 return TRUE;
220 }
221
pf_config_load_captures(wIniFile * ini,proxyConfig * config)222 static BOOL pf_config_load_captures(wIniFile* ini, proxyConfig* config)
223 {
224 const char* captures_dir;
225
226 config->SessionCapture = pf_config_get_bool(ini, "SessionCapture", "Enabled");
227 if (!config->SessionCapture)
228 return TRUE;
229
230 captures_dir = pf_config_get_str(ini, "SessionCapture", "CapturesDirectory");
231
232 if (!captures_dir)
233 return FALSE;
234
235 config->CapturesDirectory = strdup(captures_dir);
236 if (!config->CapturesDirectory)
237 return FALSE;
238
239 if (!PathFileExistsA(config->CapturesDirectory))
240 {
241 if (!CreateDirectoryA(config->CapturesDirectory, NULL))
242 {
243 free(config->CapturesDirectory);
244 config->CapturesDirectory = NULL;
245 return FALSE;
246 }
247 }
248
249 return TRUE;
250 }
251
pf_server_config_load(const char * path)252 proxyConfig* pf_server_config_load(const char* path)
253 {
254 proxyConfig* config = NULL;
255 wIniFile* ini = IniFile_New();
256
257 if (!ini)
258 {
259 WLog_ERR(TAG, "[%s]: IniFile_New() failed!", __FUNCTION__);
260 return FALSE;
261 }
262
263 if (IniFile_ReadFile(ini, path) < 0)
264 {
265 WLog_ERR(TAG, "[%s] failed to parse ini file: '%s'", __FUNCTION__, path);
266 goto out;
267 }
268
269 config = calloc(1, sizeof(proxyConfig));
270
271 if (!pf_config_load_server(ini, config))
272 goto out;
273
274 if (!pf_config_load_target(ini, config))
275 goto out;
276
277 if (!pf_config_load_channels(ini, config))
278 goto out;
279
280 if (!pf_config_load_input(ini, config))
281 goto out;
282
283 if (!pf_config_load_security(ini, config))
284 goto out;
285
286 if (!pf_config_load_modules(ini, config))
287 goto out;
288
289 if (!pf_config_load_clipboard(ini, config))
290 goto out;
291
292 if (!pf_config_load_captures(ini, config))
293 goto out;
294
295 IniFile_Free(ini);
296 return config;
297
298 out:
299 IniFile_Free(ini);
300 pf_server_config_free(config);
301 return NULL;
302 }
303
pf_server_config_print_list(char ** list,size_t count)304 static void pf_server_config_print_list(char** list, size_t count)
305 {
306 size_t i;
307
308 for (i = 0; i < count; i++)
309 WLog_INFO(TAG, "\t\t- %s", list[i]);
310 }
311
pf_server_config_print(proxyConfig * config)312 void pf_server_config_print(proxyConfig* config)
313 {
314 WLog_INFO(TAG, "Proxy configuration:");
315
316 CONFIG_PRINT_SECTION("Server");
317 CONFIG_PRINT_STR(config, Host);
318 CONFIG_PRINT_UINT16(config, Port);
319 CONFIG_PRINT_BOOL(config, SessionCapture);
320
321 if (!config->UseLoadBalanceInfo)
322 {
323 CONFIG_PRINT_SECTION("Target");
324 CONFIG_PRINT_STR(config, TargetHost);
325 CONFIG_PRINT_UINT16(config, TargetPort);
326 }
327
328 CONFIG_PRINT_SECTION("Input");
329 CONFIG_PRINT_BOOL(config, Keyboard);
330 CONFIG_PRINT_BOOL(config, Mouse);
331
332 CONFIG_PRINT_SECTION("Server Security");
333 CONFIG_PRINT_BOOL(config, ServerTlsSecurity);
334 CONFIG_PRINT_BOOL(config, ServerRdpSecurity);
335
336 CONFIG_PRINT_SECTION("Client Security");
337 CONFIG_PRINT_BOOL(config, ClientNlaSecurity);
338 CONFIG_PRINT_BOOL(config, ClientTlsSecurity);
339 CONFIG_PRINT_BOOL(config, ClientRdpSecurity);
340 CONFIG_PRINT_BOOL(config, ClientAllowFallbackToTls);
341
342 CONFIG_PRINT_SECTION("Channels");
343 CONFIG_PRINT_BOOL(config, GFX);
344 CONFIG_PRINT_BOOL(config, DisplayControl);
345 CONFIG_PRINT_BOOL(config, Clipboard);
346 CONFIG_PRINT_BOOL(config, AudioOutput);
347 CONFIG_PRINT_BOOL(config, RemoteApp);
348
349 if (config->PassthroughCount)
350 {
351 WLog_INFO(TAG, "\tStatic Channels Proxy:");
352 pf_server_config_print_list(config->Passthrough, config->PassthroughCount);
353 }
354
355 CONFIG_PRINT_SECTION("Clipboard");
356 CONFIG_PRINT_BOOL(config, TextOnly);
357 if (config->MaxTextLength > 0)
358 CONFIG_PRINT_UINT32(config, MaxTextLength);
359
360 CONFIG_PRINT_SECTION("SessionCapture");
361 CONFIG_PRINT_BOOL(config, SessionCapture);
362 CONFIG_PRINT_STR(config, CapturesDirectory);
363 }
364
pf_server_config_free(proxyConfig * config)365 void pf_server_config_free(proxyConfig* config)
366 {
367 if (config == NULL)
368 return;
369
370 free(config->Passthrough);
371 free(config->CapturesDirectory);
372 free(config->RequiredPlugins);
373 free(config->Modules);
374 free(config->TargetHost);
375 free(config->Host);
376 free(config);
377 }
378