FreeRDP
Loading...
Searching...
No Matches
pf_modules.c
1
23#include <winpr/assert.h>
24
25#include <winpr/file.h>
26#include <winpr/wlog.h>
27#include <winpr/path.h>
28#include <winpr/library.h>
29#include <freerdp/api.h>
30#include <freerdp/build-config.h>
31
32#include <freerdp/server/proxy/proxy_log.h>
33#include <freerdp/server/proxy/proxy_modules_api.h>
34
35#include <freerdp/server/proxy/proxy_context.h>
36#include "proxy_modules.h"
37
38#define TAG PROXY_TAG("modules")
39
40#define MODULE_ENTRY_POINT "proxy_module_entry_point"
41
42struct proxy_module
43{
44 proxyPluginsManager mgr;
45 wArrayList* plugins;
46 wArrayList* handles;
47};
48
49static const char* pf_modules_get_filter_type_string(PF_FILTER_TYPE result)
50{
51 switch (result)
52 {
53 case FILTER_TYPE_KEYBOARD:
54 return "FILTER_TYPE_KEYBOARD";
55 case FILTER_TYPE_UNICODE:
56 return "FILTER_TYPE_UNICODE";
57 case FILTER_TYPE_MOUSE:
58 return "FILTER_TYPE_MOUSE";
59 case FILTER_TYPE_MOUSE_EX:
60 return "FILTER_TYPE_MOUSE_EX";
61 case FILTER_TYPE_CLIENT_PASSTHROUGH_CHANNEL_DATA:
62 return "FILTER_TYPE_CLIENT_PASSTHROUGH_CHANNEL_DATA";
63 case FILTER_TYPE_SERVER_PASSTHROUGH_CHANNEL_DATA:
64 return "FILTER_TYPE_SERVER_PASSTHROUGH_CHANNEL_DATA";
65 case FILTER_TYPE_CLIENT_PASSTHROUGH_DYN_CHANNEL_CREATE:
66 return "FILTER_TYPE_CLIENT_PASSTHROUGH_DYN_CHANNEL_CREATE";
67 case FILTER_TYPE_SERVER_FETCH_TARGET_ADDR:
68 return "FILTER_TYPE_SERVER_FETCH_TARGET_ADDR";
69 case FILTER_TYPE_SERVER_PEER_LOGON:
70 return "FILTER_TYPE_SERVER_PEER_LOGON";
71 case FILTER_TYPE_CLIENT_PASSTHROUGH_CHANNEL_CREATE:
72 return "FILTER_TYPE_CLIENT_PASSTHROUGH_CHANNEL_CREATE";
73 case FILTER_LAST:
74 return "FILTER_LAST";
75 default:
76 return "FILTER_UNKNOWN";
77 }
78}
79
80static const char* pf_modules_get_hook_type_string(PF_HOOK_TYPE result)
81{
82 switch (result)
83 {
84 case HOOK_TYPE_CLIENT_INIT_CONNECT:
85 return "HOOK_TYPE_CLIENT_INIT_CONNECT";
86 case HOOK_TYPE_CLIENT_UNINIT_CONNECT:
87 return "HOOK_TYPE_CLIENT_UNINIT_CONNECT";
88 case HOOK_TYPE_CLIENT_PRE_CONNECT:
89 return "HOOK_TYPE_CLIENT_PRE_CONNECT";
90 case HOOK_TYPE_CLIENT_POST_CONNECT:
91 return "HOOK_TYPE_CLIENT_POST_CONNECT";
92 case HOOK_TYPE_CLIENT_POST_DISCONNECT:
93 return "HOOK_TYPE_CLIENT_POST_DISCONNECT";
94 case HOOK_TYPE_CLIENT_REDIRECT:
95 return "HOOK_TYPE_CLIENT_REDIRECT";
96 case HOOK_TYPE_CLIENT_VERIFY_X509:
97 return "HOOK_TYPE_CLIENT_VERIFY_X509";
98 case HOOK_TYPE_CLIENT_LOGIN_FAILURE:
99 return "HOOK_TYPE_CLIENT_LOGIN_FAILURE";
100 case HOOK_TYPE_CLIENT_END_PAINT:
101 return "HOOK_TYPE_CLIENT_END_PAINT";
102 case HOOK_TYPE_SERVER_POST_CONNECT:
103 return "HOOK_TYPE_SERVER_POST_CONNECT";
104 case HOOK_TYPE_SERVER_ACTIVATE:
105 return "HOOK_TYPE_SERVER_ACTIVATE";
106 case HOOK_TYPE_SERVER_CHANNELS_INIT:
107 return "HOOK_TYPE_SERVER_CHANNELS_INIT";
108 case HOOK_TYPE_SERVER_CHANNELS_FREE:
109 return "HOOK_TYPE_SERVER_CHANNELS_FREE";
110 case HOOK_TYPE_SERVER_SESSION_END:
111 return "HOOK_TYPE_SERVER_SESSION_END";
112 case HOOK_TYPE_CLIENT_LOAD_CHANNELS:
113 return "HOOK_TYPE_CLIENT_LOAD_CHANNELS";
114 case HOOK_TYPE_SERVER_SESSION_INITIALIZE:
115 return "HOOK_TYPE_SERVER_SESSION_INITIALIZE";
116 case HOOK_TYPE_SERVER_SESSION_STARTED:
117 return "HOOK_TYPE_SERVER_SESSION_STARTED";
118 case HOOK_LAST:
119 return "HOOK_LAST";
120 default:
121 return "HOOK_TYPE_UNKNOWN";
122 }
123}
124
125static BOOL pf_modules_proxy_ArrayList_ForEachFkt(void* data, size_t index, va_list ap)
126{
127 proxyPlugin* plugin = (proxyPlugin*)data;
128 BOOL ok = FALSE;
129
130 WINPR_UNUSED(index);
131
132 PF_HOOK_TYPE type = va_arg(ap, PF_HOOK_TYPE);
133 proxyData* pdata = va_arg(ap, proxyData*);
134 void* custom = va_arg(ap, void*);
135
136 WLog_VRB(TAG, "running hook %s.%s", plugin->name, pf_modules_get_hook_type_string(type));
137
138 switch (type)
139 {
140 case HOOK_TYPE_CLIENT_INIT_CONNECT:
141 ok = IFCALLRESULT(TRUE, plugin->ClientInitConnect, plugin, pdata, custom);
142 break;
143 case HOOK_TYPE_CLIENT_UNINIT_CONNECT:
144 ok = IFCALLRESULT(TRUE, plugin->ClientUninitConnect, plugin, pdata, custom);
145 break;
146 case HOOK_TYPE_CLIENT_PRE_CONNECT:
147 ok = IFCALLRESULT(TRUE, plugin->ClientPreConnect, plugin, pdata, custom);
148 break;
149
150 case HOOK_TYPE_CLIENT_POST_CONNECT:
151 ok = IFCALLRESULT(TRUE, plugin->ClientPostConnect, plugin, pdata, custom);
152 break;
153
154 case HOOK_TYPE_CLIENT_REDIRECT:
155 ok = IFCALLRESULT(TRUE, plugin->ClientRedirect, plugin, pdata, custom);
156 break;
157
158 case HOOK_TYPE_CLIENT_POST_DISCONNECT:
159 ok = IFCALLRESULT(TRUE, plugin->ClientPostDisconnect, plugin, pdata, custom);
160 break;
161
162 case HOOK_TYPE_CLIENT_VERIFY_X509:
163 ok = IFCALLRESULT(TRUE, plugin->ClientX509Certificate, plugin, pdata, custom);
164 break;
165
166 case HOOK_TYPE_CLIENT_LOGIN_FAILURE:
167 ok = IFCALLRESULT(TRUE, plugin->ClientLoginFailure, plugin, pdata, custom);
168 break;
169
170 case HOOK_TYPE_CLIENT_END_PAINT:
171 ok = IFCALLRESULT(TRUE, plugin->ClientEndPaint, plugin, pdata, custom);
172 break;
173
174 case HOOK_TYPE_CLIENT_LOAD_CHANNELS:
175 ok = IFCALLRESULT(TRUE, plugin->ClientLoadChannels, plugin, pdata, custom);
176 break;
177
178 case HOOK_TYPE_SERVER_POST_CONNECT:
179 ok = IFCALLRESULT(TRUE, plugin->ServerPostConnect, plugin, pdata, custom);
180 break;
181
182 case HOOK_TYPE_SERVER_ACTIVATE:
183 ok = IFCALLRESULT(TRUE, plugin->ServerPeerActivate, plugin, pdata, custom);
184 break;
185
186 case HOOK_TYPE_SERVER_CHANNELS_INIT:
187 ok = IFCALLRESULT(TRUE, plugin->ServerChannelsInit, plugin, pdata, custom);
188 break;
189
190 case HOOK_TYPE_SERVER_CHANNELS_FREE:
191 ok = IFCALLRESULT(TRUE, plugin->ServerChannelsFree, plugin, pdata, custom);
192 break;
193
194 case HOOK_TYPE_SERVER_SESSION_END:
195 ok = IFCALLRESULT(TRUE, plugin->ServerSessionEnd, plugin, pdata, custom);
196 break;
197
198 case HOOK_TYPE_SERVER_SESSION_INITIALIZE:
199 ok = IFCALLRESULT(TRUE, plugin->ServerSessionInitialize, plugin, pdata, custom);
200 break;
201
202 case HOOK_TYPE_SERVER_SESSION_STARTED:
203 ok = IFCALLRESULT(TRUE, plugin->ServerSessionStarted, plugin, pdata, custom);
204 break;
205
206 case HOOK_LAST:
207 default:
208 WLog_ERR(TAG, "invalid hook called");
209 }
210
211 if (!ok)
212 {
213 WLog_INFO(TAG, "plugin %s, hook %s failed!", plugin->name,
214 pf_modules_get_hook_type_string(type));
215 return FALSE;
216 }
217 return TRUE;
218}
219
220/*
221 * runs all hooks of type `type`.
222 *
223 * @type: hook type to run.
224 * @server: pointer of server's rdpContext struct of the current session.
225 */
226BOOL pf_modules_run_hook(proxyModule* module, PF_HOOK_TYPE type, proxyData* pdata, void* custom)
227{
228 WINPR_ASSERT(module);
229 WINPR_ASSERT(module->plugins);
230 return ArrayList_ForEach(module->plugins, pf_modules_proxy_ArrayList_ForEachFkt, type, pdata,
231 custom);
232}
233
234static BOOL pf_modules_ArrayList_ForEachFkt(void* data, size_t index, va_list ap)
235{
236 proxyPlugin* plugin = (proxyPlugin*)data;
237 BOOL result = FALSE;
238
239 WINPR_UNUSED(index);
240
241 PF_FILTER_TYPE type = va_arg(ap, PF_FILTER_TYPE);
242 proxyData* pdata = va_arg(ap, proxyData*);
243 void* param = va_arg(ap, void*);
244
245 WLog_VRB(TAG, "running filter: %s", plugin->name);
246
247 switch (type)
248 {
249 case FILTER_TYPE_KEYBOARD:
250 result = IFCALLRESULT(TRUE, plugin->KeyboardEvent, plugin, pdata, param);
251 break;
252
253 case FILTER_TYPE_UNICODE:
254 result = IFCALLRESULT(TRUE, plugin->UnicodeEvent, plugin, pdata, param);
255 break;
256
257 case FILTER_TYPE_MOUSE:
258 result = IFCALLRESULT(TRUE, plugin->MouseEvent, plugin, pdata, param);
259 break;
260
261 case FILTER_TYPE_MOUSE_EX:
262 result = IFCALLRESULT(TRUE, plugin->MouseExEvent, plugin, pdata, param);
263 break;
264
265 case FILTER_TYPE_CLIENT_PASSTHROUGH_CHANNEL_DATA:
266 result = IFCALLRESULT(TRUE, plugin->ClientChannelData, plugin, pdata, param);
267 break;
268
269 case FILTER_TYPE_SERVER_PASSTHROUGH_CHANNEL_DATA:
270 result = IFCALLRESULT(TRUE, plugin->ServerChannelData, plugin, pdata, param);
271 break;
272
273 case FILTER_TYPE_CLIENT_PASSTHROUGH_CHANNEL_CREATE:
274 result = IFCALLRESULT(TRUE, plugin->ChannelCreate, plugin, pdata, param);
275 break;
276
277 case FILTER_TYPE_CLIENT_PASSTHROUGH_DYN_CHANNEL_CREATE:
278 result = IFCALLRESULT(TRUE, plugin->DynamicChannelCreate, plugin, pdata, param);
279 break;
280
281 case FILTER_TYPE_SERVER_FETCH_TARGET_ADDR:
282 result = IFCALLRESULT(TRUE, plugin->ServerFetchTargetAddr, plugin, pdata, param);
283 break;
284
285 case FILTER_TYPE_SERVER_PEER_LOGON:
286 result = IFCALLRESULT(TRUE, plugin->ServerPeerLogon, plugin, pdata, param);
287 break;
288
289 case FILTER_TYPE_INTERCEPT_CHANNEL:
290 result = IFCALLRESULT(TRUE, plugin->DynChannelIntercept, plugin, pdata, param);
291 break;
292
293 case FILTER_TYPE_DYN_INTERCEPT_LIST:
294 result = IFCALLRESULT(TRUE, plugin->DynChannelToIntercept, plugin, pdata, param);
295 break;
296
297 case FILTER_TYPE_STATIC_INTERCEPT_LIST:
298 result = IFCALLRESULT(TRUE, plugin->StaticChannelToIntercept, plugin, pdata, param);
299 break;
300
301 case FILTER_LAST:
302 default:
303 WLog_ERR(TAG, "invalid filter called");
304 }
305
306 if (!result)
307 {
308 /* current filter return FALSE, no need to run other filters. */
309 WLog_DBG(TAG, "plugin %s, filter type [%s] returned FALSE", plugin->name,
310 pf_modules_get_filter_type_string(type));
311 }
312 return result;
313}
314
315/*
316 * runs all filters of type `type`.
317 *
318 * @type: filter type to run.
319 * @server: pointer of server's rdpContext struct of the current session.
320 */
321BOOL pf_modules_run_filter(proxyModule* module, PF_FILTER_TYPE type, proxyData* pdata, void* param)
322{
323 WINPR_ASSERT(module);
324 WINPR_ASSERT(module->plugins);
325
326 return ArrayList_ForEach(module->plugins, pf_modules_ArrayList_ForEachFkt, type, pdata, param);
327}
328
329/*
330 * stores per-session data needed by a plugin.
331 *
332 * @context: current session server's rdpContext instance.
333 * @info: pointer to per-session data.
334 */
335static BOOL pf_modules_set_plugin_data(WINPR_ATTR_UNUSED proxyPluginsManager* mgr,
336 const char* plugin_name, proxyData* pdata, void* data)
337{
338 union
339 {
340 const char* ccp;
341 char* cp;
342 } ccharconv;
343
344 WINPR_ASSERT(plugin_name);
345
346 ccharconv.ccp = plugin_name;
347 if (data == NULL) /* no need to store anything */
348 return FALSE;
349
350 if (!HashTable_Insert(pdata->modules_info, ccharconv.cp, data))
351 {
352 WLog_ERR(TAG, "[%s]: HashTable_Insert failed!");
353 return FALSE;
354 }
355
356 return TRUE;
357}
358
359/*
360 * returns per-session data needed a plugin.
361 *
362 * @context: current session server's rdpContext instance.
363 * if there's no data related to `plugin_name` in `context` (current session), a NULL will be
364 * returned.
365 */
366static void* pf_modules_get_plugin_data(WINPR_ATTR_UNUSED proxyPluginsManager* mgr,
367 const char* plugin_name, proxyData* pdata)
368{
369 union
370 {
371 const char* ccp;
372 char* cp;
373 } ccharconv;
374 WINPR_ASSERT(plugin_name);
375 WINPR_ASSERT(pdata);
376 ccharconv.ccp = plugin_name;
377
378 return HashTable_GetItemValue(pdata->modules_info, ccharconv.cp);
379}
380
381static void pf_modules_abort_connect(WINPR_ATTR_UNUSED proxyPluginsManager* mgr, proxyData* pdata)
382{
383 WINPR_ASSERT(pdata);
384 WLog_DBG(TAG, "is called!");
385 proxy_data_abort_connect(pdata);
386}
387
388static BOOL pf_modules_register_ArrayList_ForEachFkt(void* data, size_t index, va_list ap)
389{
390 proxyPlugin* plugin = (proxyPlugin*)data;
391 proxyPlugin* plugin_to_register = va_arg(ap, proxyPlugin*);
392
393 WINPR_UNUSED(index);
394
395 if (strcmp(plugin->name, plugin_to_register->name) == 0)
396 {
397 WLog_ERR(TAG, "can not register plugin '%s', it is already registered!", plugin->name);
398 return FALSE;
399 }
400 return TRUE;
401}
402
403static BOOL pf_modules_register_plugin(proxyPluginsManager* mgr,
404 const proxyPlugin* plugin_to_register)
405{
406 proxyPlugin internal = { 0 };
407 proxyModule* module = (proxyModule*)mgr;
408 WINPR_ASSERT(module);
409
410 if (!plugin_to_register)
411 return FALSE;
412
413 internal = *plugin_to_register;
414 internal.mgr = mgr;
415
416 /* make sure there's no other loaded plugin with the same name of `plugin_to_register`. */
417 if (!ArrayList_ForEach(module->plugins, pf_modules_register_ArrayList_ForEachFkt, &internal))
418 return FALSE;
419
420 if (!ArrayList_Append(module->plugins, &internal))
421 {
422 WLog_ERR(TAG, "failed adding plugin to list: %s", plugin_to_register->name);
423 return FALSE;
424 }
425
426 return TRUE;
427}
428
429static BOOL pf_modules_load_ArrayList_ForEachFkt(void* data, size_t index, va_list ap)
430{
431 proxyPlugin* plugin = (proxyPlugin*)data;
432 const char* plugin_name = va_arg(ap, const char*);
433 BOOL* res = va_arg(ap, BOOL*);
434
435 WINPR_UNUSED(index);
436 WINPR_UNUSED(ap);
437 WINPR_ASSERT(res);
438
439 if (strcmp(plugin->name, plugin_name) == 0)
440 *res = TRUE;
441 return TRUE;
442}
443
444BOOL pf_modules_is_plugin_loaded(proxyModule* module, const char* plugin_name)
445{
446 BOOL rc = FALSE;
447 WINPR_ASSERT(module);
448 if (ArrayList_Count(module->plugins) < 1)
449 return FALSE;
450 if (!ArrayList_ForEach(module->plugins, pf_modules_load_ArrayList_ForEachFkt, plugin_name, &rc))
451 return FALSE;
452 return rc;
453}
454
455static BOOL pf_modules_print_ArrayList_ForEachFkt(void* data, size_t index, va_list ap)
456{
457 proxyPlugin* plugin = (proxyPlugin*)data;
458
459 WINPR_UNUSED(index);
460 WINPR_UNUSED(ap);
461
462 WLog_INFO(TAG, "\tName: %s", plugin->name);
463 WLog_INFO(TAG, "\tDescription: %s", plugin->description);
464 return TRUE;
465}
466
467void pf_modules_list_loaded_plugins(proxyModule* module)
468{
469 size_t count = 0;
470
471 WINPR_ASSERT(module);
472 WINPR_ASSERT(module->plugins);
473
474 count = ArrayList_Count(module->plugins);
475
476 if (count > 0)
477 WLog_INFO(TAG, "Loaded plugins:");
478
479 ArrayList_ForEach(module->plugins, pf_modules_print_ArrayList_ForEachFkt);
480}
481
482static BOOL pf_modules_load_module(const char* module_path, proxyModule* module, void* userdata)
483{
484 WINPR_ASSERT(module);
485
486 HANDLE handle = LoadLibraryX(module_path);
487
488 if (handle == NULL)
489 {
490 WLog_ERR(TAG, "failed loading external library: %s", module_path);
491 return FALSE;
492 }
493
494 proxyModuleEntryPoint pEntryPoint =
495 GetProcAddressAs(handle, MODULE_ENTRY_POINT, proxyModuleEntryPoint);
496 if (!pEntryPoint)
497 {
498 WLog_ERR(TAG, "GetProcAddress failed while loading %s", module_path);
499 goto error;
500 }
501 if (!ArrayList_Append(module->handles, handle))
502 {
503 WLog_ERR(TAG, "ArrayList_Append failed!");
504 goto error;
505 }
506 return pf_modules_add(module, pEntryPoint, userdata);
507
508error:
509 FreeLibrary(handle);
510 return FALSE;
511}
512
513static void free_handle(void* obj)
514{
515 HANDLE handle = (HANDLE)obj;
516 if (handle)
517 FreeLibrary(handle);
518}
519
520static void free_plugin(void* obj)
521{
522 proxyPlugin* plugin = (proxyPlugin*)obj;
523 WINPR_ASSERT(plugin);
524
525 if (!IFCALLRESULT(TRUE, plugin->PluginUnload, plugin))
526 WLog_WARN(TAG, "PluginUnload failed for plugin '%s'", plugin->name);
527
528 free(plugin);
529}
530
531static void* new_plugin(const void* obj)
532{
533 const proxyPlugin* src = obj;
534 proxyPlugin* proxy = calloc(1, sizeof(proxyPlugin));
535 if (!proxy)
536 return NULL;
537 *proxy = *src;
538 return proxy;
539}
540
541proxyModule* pf_modules_new(const char* root_dir, const char** modules, size_t count)
542{
543 wObject* obj = NULL;
544 char* path = NULL;
545 proxyModule* module = calloc(1, sizeof(proxyModule));
546 if (!module)
547 return NULL;
548
549 module->mgr.RegisterPlugin = pf_modules_register_plugin;
550 module->mgr.SetPluginData = pf_modules_set_plugin_data;
551 module->mgr.GetPluginData = pf_modules_get_plugin_data;
552 module->mgr.AbortConnect = pf_modules_abort_connect;
553 module->plugins = ArrayList_New(FALSE);
554
555 if (module->plugins == NULL)
556 {
557 WLog_ERR(TAG, "ArrayList_New failed!");
558 goto error;
559 }
560 obj = ArrayList_Object(module->plugins);
561 WINPR_ASSERT(obj);
562
563 obj->fnObjectFree = free_plugin;
564 obj->fnObjectNew = new_plugin;
565
566 module->handles = ArrayList_New(FALSE);
567 if (module->handles == NULL)
568 {
569
570 WLog_ERR(TAG, "ArrayList_New failed!");
571 goto error;
572 }
573 ArrayList_Object(module->handles)->fnObjectFree = free_handle;
574
575 if (count > 0)
576 {
577 WINPR_ASSERT(root_dir);
578 if (!winpr_PathFileExists(root_dir))
579 path = GetCombinedPath(FREERDP_INSTALL_PREFIX, root_dir);
580 else
581 path = _strdup(root_dir);
582
583 if (!winpr_PathFileExists(path))
584 {
585 if (!winpr_PathMakePath(path, NULL))
586 {
587 WLog_ERR(TAG, "error occurred while creating modules directory: %s", root_dir);
588 goto error;
589 }
590 }
591
592 if (winpr_PathFileExists(path))
593 WLog_DBG(TAG, "modules root directory: %s", path);
594
595 for (size_t i = 0; i < count; i++)
596 {
597 char name[8192] = { 0 };
598 char* fullpath = NULL;
599 (void)_snprintf(name, sizeof(name), "proxy-%s-plugin%s", modules[i],
600 FREERDP_SHARED_LIBRARY_SUFFIX);
601 fullpath = GetCombinedPath(path, name);
602 pf_modules_load_module(fullpath, module, NULL);
603 free(fullpath);
604 }
605 }
606
607 free(path);
608 return module;
609
610error:
611 free(path);
612 pf_modules_free(module);
613 return NULL;
614}
615
616void pf_modules_free(proxyModule* module)
617{
618 if (!module)
619 return;
620
621 ArrayList_Free(module->plugins);
622 ArrayList_Free(module->handles);
623 free(module);
624}
625
626BOOL pf_modules_add(proxyModule* module, proxyModuleEntryPoint ep, void* userdata)
627{
628 WINPR_ASSERT(module);
629 WINPR_ASSERT(ep);
630
631 return ep(&module->mgr, userdata);
632}
This struct contains function pointer to initialize/free objects.
Definition collections.h:57