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