1 //--------------------------------------------------------------------------
2 // Copyright (C) 2014-2021 Cisco and/or its affiliates. All rights reserved.
3 //
4 // This program is free software; you can redistribute it and/or modify it
5 // under the terms of the GNU General Public License Version 2 as published
6 // by the Free Software Foundation. You may not use, modify or distribute
7 // this program under any other version of the GNU General Public License.
8 //
9 // This program is distributed in the hope that it will be useful, but
10 // WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 // General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License along
15 // with this program; if not, write to the Free Software Foundation, Inc.,
16 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 //--------------------------------------------------------------------------
18 // plugin_manager.cc author Russ Combs <rucombs@cisco.com>
19
20 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif
23
24 #include "plugin_manager.h"
25
26 #include <dlfcn.h>
27 #include <iostream>
28 #include <sstream>
29 #include <sys/stat.h>
30
31 #include "framework/codec.h"
32 #include "framework/connector.h"
33 #include "framework/logger.h"
34 #include "framework/mpse.h"
35 #include "framework/policy_selector.h"
36 #include "helpers/directory.h"
37 #include "helpers/markup.h"
38 #include "log/messages.h"
39 #include "main/snort_config.h"
40
41 #ifdef PIGLET
42 #include "piglet/piglet_api.h"
43 #include "piglet/piglet_manager.h"
44 #endif
45
46 #include "action_manager.h"
47 #include "codec_manager.h"
48 #include "connector_manager.h"
49 #include "event_manager.h"
50 #include "inspector_manager.h"
51 #include "ips_manager.h"
52 #include "module_manager.h"
53 #include "mpse_manager.h"
54 #include "policy_selector_manager.h"
55 #include "script_manager.h"
56 #include "so_manager.h"
57
58 using namespace snort;
59 using namespace std;
60
61 #define lib_pattern "*.so"
62
63 struct Symbol
64 {
65 const char* name;
66 unsigned version;
67 unsigned size;
68 };
69
70 #if 1
71 // sequence must match PlugType definition
72 // compiler catches too many but not too few
73 static Symbol symbols[PT_MAX] =
74 {
75 { "codec", CDAPI_VERSION, sizeof(CodecApi) },
76 { "inspector", INSAPI_VERSION, sizeof(InspectApi) },
77 { "ips_action", ACTAPI_VERSION, sizeof(ActionApi) },
78 { "ips_option", IPSAPI_VERSION, sizeof(IpsApi) },
79 { "search_engine", SEAPI_VERSION, sizeof(MpseApi) },
80 { "so_rule", SOAPI_VERSION, sizeof(SoApi) },
81 { "logger", LOGAPI_VERSION, sizeof(LogApi) },
82 { "connector", CONNECTOR_API_VERSION, sizeof(ConnectorApi) },
83 { "policy_selector", POLICY_SELECTOR_API_VERSION, sizeof(PolicySelectorApi) },
84 #ifdef PIGLET
85 { "piglet", PIGLET_API_VERSION, sizeof(Piglet::Api) }
86 #endif
87 };
88 #else
89 // this gets around the sequence issue with some compilers
90 // but does not fail if we are missing an entry :(
91 #define stringify(name) # name
92 static Symbol symbols[PT_MAX] =
93 {
94 [PT_CODEC] = { stringify(PT_CODEC), CDAPI_VERSION, sizeof(CodecApi) },
95 [PT_INSPECTOR] = { stringify(PT_INSPECTOR), INSAPI_VERSION, sizeof(InspectApi) },
96 [PT_IPS_ACTION] = { stringify(PT_IPS_ACTION), ACTAPI_VERSION, sizeof(ActionApi) },
97 [PT_IPS_OPTION] = { stringify(PT_IPS_OPTION), IPSAPI_VERSION, sizeof(IpsApi) },
98 [PT_SEARCH_ENGINE] = { stringify(PT_SEARCH_ENGINE), SEAPI_VERSION, sizeof(MpseApi) },
99 [PT_SO_RULE] = { stringify(PT_SO_RULE), SOAPI_VERSION, sizeof(SoApi) },
100 [PT_LOGGER] = { stringify(PT_LOGGER), LOGAPI_VERSION, sizeof(LogApi) },
101 [PT_CONNECTOR] = { stringify(PT_CONNECTOR), CONNECTOR_API_VERSION, sizeof(ConnectorApi) },
102 [PT_POLICY_SELECTOR] = { stringify(PT_POLICY_SELECTOR), POLICY_SELECTOR_API_VERSION,
103 sizeof(PolicySelectorApi) }
104 };
105 #endif
106
get_type(const char * s)107 PlugType PluginManager::get_type(const char* s)
108 {
109 for ( int i = 0; i < PT_MAX; i++ )
110 if ( !strcmp(s, symbols[i].name) )
111 return (PlugType)i;
112
113 return PT_MAX;
114 }
115
get_type_name(PlugType pt)116 const char* PluginManager::get_type_name(PlugType pt)
117 {
118 if ( pt >= PT_MAX )
119 return "error";
120
121 return symbols[pt].name;
122 }
123
124 static const char* current_plugin = nullptr;
125
get_current_plugin()126 const char* PluginManager::get_current_plugin()
127 { return current_plugin; }
128
129 struct Plugin
130 {
131 string source;
132 string key;
133 const BaseApi* api = nullptr;
134 SoHandlePtr handle;
135 };
136
~Plugins()137 Plugins::~Plugins()
138 {
139 plug_map.clear();
140 }
141
~SoHandle()142 SoHandle::~SoHandle()
143 {
144 #ifndef REG_TEST
145 if ( handle )
146 dlclose(handle);
147 #endif
148 }
149
150 static Plugins s_plugins;
151
set_key(string & key,Symbol * sym,const char * name)152 static void set_key(string& key, Symbol* sym, const char* name)
153 {
154 key = sym->name;
155 key += "::";
156 key += name;
157 }
158
159 // plugins are linked when loaded (RTLD_NOW) so missing symbols
160 // don't make it this far. we therefore only need to check
161 // that shared structs are defined identically, so opts strings
162 // must be identical.
compatible_builds(const char * plug_opts)163 static bool compatible_builds(const char* plug_opts)
164 {
165 const char* snort_opts = API_OPTIONS;
166
167 if ( !snort_opts and !plug_opts )
168 return true;
169
170 if ( !snort_opts or !plug_opts )
171 return false;
172
173 if ( strcmp(snort_opts, plug_opts) )
174 return false;
175
176 return true;
177 }
178
plugin_is_reloadable(const BaseApi * api)179 static bool plugin_is_reloadable(const BaseApi* api)
180 {
181 if ( api->type == PT_SO_RULE )
182 return true;
183 else
184 return false;
185 }
186
register_plugin(const BaseApi * api,SoHandlePtr handle,const char * file,SnortConfig * sc)187 static bool register_plugin(
188 const BaseApi* api, SoHandlePtr handle, const char* file, SnortConfig* sc)
189 {
190 if ( api->type >= PT_MAX )
191 return false;
192
193 Symbol* sym = symbols + api->type;
194
195 if ( api->size != sym->size )
196 {
197 ParseWarning(WARN_PLUGINS, "%s: size mismatch; expected %u, got %u",
198 api->name, sym->size, api->size);
199 return false;
200 }
201
202 if ( api->api_version != sym->version )
203 {
204 ParseWarning(WARN_PLUGINS, "%s: version mismatch; expected 0x%x, got 0x%x",
205 api->name, sym->version, api->api_version);
206 return false;
207 }
208
209 if ( !compatible_builds(api->options) )
210 {
211 ParseWarning(WARN_PLUGINS, "%s: incompatible builds", api->name);
212 return false;
213 }
214
215 if ( sc and !plugin_is_reloadable(api) )
216 return false;
217
218 string key;
219 set_key(key, sym, api->name);
220
221 Plugin& p = ( sc ? sc->plugins->plug_map[key] : s_plugins.plug_map[key] );
222 if ( p.api )
223 {
224 if ( p.api->version > api->version )
225 return false; // keep the old one
226 }
227
228 p.key = key;
229 p.api = api;
230 p.handle = handle;
231 p.source = file;
232
233 return true;
234 }
235
load_list(const BaseApi ** api,void * handle=nullptr,const char * file="static",SnortConfig * sc=nullptr)236 static void load_list(
237 const BaseApi** api, void* handle = nullptr, const char* file = "static", SnortConfig* sc = nullptr)
238 {
239 SoHandlePtr so_file;
240 if ( handle and sc )
241 { // for reload, if the so lib file was previously opened, reuse the shared_ptr
242 for( auto const& i : s_plugins.plug_map )
243 {
244 if ( i.second.api == (*api) and i.second.handle.get()->handle == handle )
245 {
246 so_file = i.second.handle;
247 break;
248 }
249 }
250 }
251 if ( !so_file.get() )
252 so_file = std::make_shared<SoHandle>(handle);
253
254 while ( *api )
255 {
256 register_plugin(*api, so_file, file, sc);
257 ++api;
258 }
259 }
260
load_lib(const char * file,SnortConfig * sc)261 static bool load_lib(const char* file, SnortConfig* sc)
262 {
263 struct stat fs;
264 void* handle;
265
266 if ( stat(file, &fs) || !(fs.st_mode & S_IFREG) )
267 return false;
268
269 if ( !(handle = dlopen(file, RTLD_NOW|RTLD_LOCAL)) )
270 {
271 if ( const char* err = dlerror() )
272 ParseWarning(WARN_PLUGINS, "%s (%s)", err, file);
273 return false;
274 }
275 const BaseApi** api = (const BaseApi**)dlsym(handle, "snort_plugins");
276
277 if ( !api )
278 {
279 if ( const char* err = dlerror() )
280 ParseWarning(WARN_PLUGINS, "%s (%s)", err, file);
281
282 dlclose(handle);
283 return false;
284 }
285 load_list(api, handle, file, sc);
286 return true;
287 }
288
add_plugin(Plugin & p)289 static void add_plugin(Plugin& p)
290 {
291 Module* m = nullptr;
292 if ( p.api->mod_ctor )
293 {
294 current_plugin = p.api->name;
295 m = p.api->mod_ctor();
296 ModuleManager::add_module(m, p.api);
297 }
298
299 switch ( p.api->type )
300 {
301 case PT_CODEC:
302 CodecManager::add_plugin((const CodecApi*)p.api);
303 break;
304
305 case PT_INSPECTOR:
306 // probes must always be global. they run regardless of selected policy.
307 assert( (m && ((const InspectApi*)p.api)->type == IT_PROBE) ?
308 m->get_usage() == Module::GLOBAL :
309 true );
310
311 InspectorManager::add_plugin((const InspectApi*)p.api);
312 break;
313
314 case PT_IPS_ACTION:
315 ActionManager::add_plugin((const ActionApi*)p.api);
316 break;
317
318 case PT_IPS_OPTION:
319 IpsManager::add_plugin((const IpsApi*)p.api);
320 break;
321
322 case PT_SEARCH_ENGINE:
323 MpseManager::add_plugin((const MpseApi*)p.api);
324 break;
325
326 case PT_SO_RULE:
327 // SO rules are added later
328 break;
329
330 case PT_LOGGER:
331 EventManager::add_plugin((const LogApi*)p.api);
332 break;
333
334 case PT_CONNECTOR:
335 ConnectorManager::add_plugin((const ConnectorApi*)p.api);
336 break;
337
338 case PT_POLICY_SELECTOR:
339 PolicySelectorManager::add_plugin((const PolicySelectorApi*)p.api);
340 break;
341
342 #ifdef PIGLET
343 case PT_PIGLET:
344 Piglet::Manager::add_plugin((const Piglet::Api*)p.api);
345 break;
346 #endif
347
348 default:
349 assert(false);
350 break;
351 }
352 }
353
load_plugins(const std::string & paths,SnortConfig * sc=nullptr)354 static void load_plugins(const std::string& paths, SnortConfig* sc = nullptr)
355 {
356 struct stat sb;
357 stringstream paths_stream(paths);
358 string segment;
359 vector<string> path_list;
360
361 while ( getline(paths_stream, segment, ':') )
362 if ( segment.length() > 0 )
363 path_list.push_back(segment);
364
365 for ( auto& path : path_list )
366 {
367 if ( stat(path.c_str(), &sb) )
368 continue;
369
370 if ( sb.st_mode & S_IFDIR )
371 {
372 Directory d(path.c_str(), lib_pattern);
373
374 while ( const char* f = d.next() )
375 load_lib(f, sc);
376 }
377 else
378 {
379 if ( path.find("/") == string::npos )
380 path = "./" + path;
381
382 load_lib(path.c_str(), sc);
383 }
384 }
385 }
386
add_plugins()387 static void add_plugins()
388 {
389 for ( auto it = s_plugins.plug_map.begin(); it != s_plugins.plug_map.end(); ++it )
390 add_plugin(it->second);
391 }
392
unload_plugins()393 static void unload_plugins()
394 {
395 s_plugins.plug_map.clear();
396 }
397
398 //-------------------------------------------------------------------------
399 // framework methods
400 //-------------------------------------------------------------------------
401
load_plugins(const BaseApi ** lp)402 void PluginManager::load_plugins(const BaseApi** lp)
403 {
404 // builtins
405 load_list(lp);
406 }
407
load_plugins(const std::string & paths)408 void PluginManager::load_plugins(const std::string& paths)
409 {
410 SoManager::load_so_proxy();
411
412 // dynamic plugins
413 if ( !paths.empty() )
414 ::load_plugins(paths);
415
416 // script plugins
417 // FIXIT-L need path to script for --list-plugins
418 load_list(ScriptManager::get_plugins());
419
420 add_plugins();
421 }
422
reload_so_plugins(const char * paths,SnortConfig * sc)423 void PluginManager::reload_so_plugins(const char* paths, SnortConfig* sc)
424 {
425 sc->plugins = new Plugins;
426 sc->plugins->plug_map = s_plugins.plug_map;
427 if ( paths )
428 {
429 // once plugin_path is provided for reload, old so_rules will be dropped
430 for( auto i = sc->plugins->plug_map.begin(); i != sc->plugins->plug_map.end(); )
431 {
432 if ( plugin_is_reloadable(i->second.api) )
433 i = sc->plugins->plug_map.erase(i);
434 else
435 ++i;
436 }
437 ::load_plugins(paths, sc);
438 }
439 load_so_plugins(sc, true);
440 }
441
reload_so_plugins_cleanup(SnortConfig * sc)442 void PluginManager::reload_so_plugins_cleanup(SnortConfig* sc)
443 {
444 if ( !sc->plugins )
445 return;
446
447 // set the new plugins to current
448 s_plugins.plug_map.clear();
449 s_plugins.plug_map = sc->plugins->plug_map;
450 sc->plugins->plug_map.clear();
451 }
452
load_so_plugins(SnortConfig * sc,bool is_reload)453 void PluginManager::load_so_plugins(SnortConfig* sc, bool is_reload)
454 {
455 auto p = is_reload ? sc->plugins->plug_map : s_plugins.plug_map;
456 for ( auto it = p.begin(); it != p.end(); ++it )
457 if ( it->second.api->type == PT_SO_RULE )
458 SoManager::add_plugin((const SoApi*)it->second.api, sc, it->second.handle);
459 }
460
list_plugins()461 void PluginManager::list_plugins()
462 {
463 for ( auto it = s_plugins.plug_map.begin(); it != s_plugins.plug_map.end(); ++it )
464 {
465 Plugin& p = it->second;
466 cout << Markup::item();
467 cout << p.key;
468 cout << " v" << p.api->version;
469 cout << " " << p.source;
470 cout << endl;
471 }
472 }
473
show_plugins()474 void PluginManager::show_plugins()
475 {
476 for ( auto it = s_plugins.plug_map.begin(); it != s_plugins.plug_map.end(); ++it )
477 {
478 Plugin& p = it->second;
479
480 cout << Markup::item();
481 cout << Markup::emphasis(p.key);
482 cout << ": " << p.api->help << endl;
483 }
484 }
485
dump_plugins()486 void PluginManager::dump_plugins()
487 {
488 CodecManager::dump_plugins();
489 InspectorManager::dump_plugins();
490 MpseManager::dump_plugins();
491 IpsManager::dump_plugins();
492 SoManager::dump_plugins();
493 ActionManager::dump_plugins();
494 EventManager::dump_plugins();
495 ConnectorManager::dump_plugins();
496 PolicySelectorManager::dump_plugins();
497 }
498
release_plugins()499 void PluginManager::release_plugins()
500 {
501 EventManager::release_plugins();
502 ActionManager::release_plugins();
503 InspectorManager::release_plugins();
504 IpsManager::release_plugins();
505 MpseManager::release_plugins();
506 CodecManager::release_plugins();
507 ConnectorManager::release_plugins();
508 PolicySelectorManager::release_plugins();
509
510 unload_plugins();
511 }
512
get_api(PlugType type,const char * name)513 const BaseApi* PluginManager::get_api(PlugType type, const char* name)
514 {
515 if ( type >= PT_MAX )
516 return nullptr;
517
518 string key;
519 set_key(key, symbols+type, name);
520
521 auto it = s_plugins.plug_map.find(key);
522
523 if ( it != s_plugins.plug_map.end() )
524 return it->second.api;
525
526 return nullptr;
527 }
528
529 #ifdef PIGLET
get_type_from_name(const std::string & name)530 PlugType PluginManager::get_type_from_name(const std::string& name)
531 {
532 for ( auto it = s_plugins.plug_map.begin(); it != s_plugins.plug_map.end(); ++it )
533 {
534 const auto* api = it->second.api;
535 if ( name == api->name )
536 return api->type;
537 }
538
539 return PT_MAX;
540 }
541
542 #endif
543
instantiate(const BaseApi * api,Module * mod,SnortConfig * sc)544 void PluginManager::instantiate(
545 const BaseApi* api, Module* mod, SnortConfig* sc)
546 {
547 switch ( api->type )
548 {
549 case PT_CODEC:
550 CodecManager::instantiate((const CodecApi*)api, mod, sc);
551 break;
552
553 case PT_INSPECTOR:
554 InspectorManager::instantiate((const InspectApi*)api, mod, sc);
555 break;
556
557 case PT_IPS_ACTION:
558 ActionManager::instantiate((const ActionApi*)api, mod, sc);
559 break;
560
561 case PT_IPS_OPTION:
562 // do not instantiate here; done later
563 //IpsManager::instantiate((IpsApi*)api, mod, sc);
564 break;
565
566 case PT_SEARCH_ENGINE:
567 MpseManager::instantiate((const MpseApi*)api, mod, sc);
568 break;
569
570 case PT_CONNECTOR:
571 ConnectorManager::instantiate((const ConnectorApi*)api, mod, sc);
572 break;
573
574 case PT_POLICY_SELECTOR:
575 PolicySelectorManager::instantiate((const PolicySelectorApi*)api, mod, sc);
576 break;
577
578 case PT_SO_RULE:
579 // do not instantiate here; done later
580 //IpsManager::instantiate((SoApi*)api, mod, sc);
581 break;
582
583 case PT_LOGGER:
584 EventManager::instantiate((const LogApi*)api, mod, sc);
585 break;
586
587 default:
588 assert(false);
589 break;
590 }
591 }
592
instantiate(const BaseApi * api,Module * mod,SnortConfig * sc,const char * name)593 void PluginManager::instantiate(
594 const BaseApi* api, Module* mod, SnortConfig* sc, const char* name)
595 {
596 if ( api->type == PT_INSPECTOR )
597 InspectorManager::instantiate((const InspectApi*)api, mod, sc, name);
598
599 else
600 assert(false);
601 }
602
get_available_plugins(PlugType t)603 const char* PluginManager::get_available_plugins(PlugType t)
604 {
605 static std::string s;
606 s.clear();
607 for ( auto it = s_plugins.plug_map.begin(); it != s_plugins.plug_map.end(); ++it )
608 {
609 const auto* api = it->second.api;
610
611 if ( t != api->type )
612 continue;
613
614 if ( !s.empty() )
615 s += " | ";
616
617 s += api->name;
618 }
619 return s.c_str();
620 }
621