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 // module_manager.cc author Russ Combs <rucombs@cisco.com>
19 
20 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif
23 
24 #include "module_manager.h"
25 
26 #include <libgen.h>
27 #include <lua.hpp>
28 
29 #include <algorithm>
30 #include <cassert>
31 #include <iostream>
32 #include <string>
33 #include <unordered_map>
34 #include <vector>
35 
36 #include "framework/base_api.h"
37 #include "framework/module.h"
38 #include "helpers/json_stream.h"
39 #include "helpers/markup.h"
40 #include "log/messages.h"
41 #include "main/modules.h"
42 #include "main/shell.h"
43 #include "main/snort.h"
44 #include "main/snort_config.h"
45 #include "managers/inspector_manager.h"
46 #include "parser/parse_conf.h"
47 #include "parser/parser.h"
48 #include "profiler/profiler.h"
49 #include "protocols/packet_manager.h"
50 #include "utils/util.h"
51 
52 #include "plugin_manager.h"
53 
54 // "Lua" includes
55 #include "lua_coreinit.h"
56 
57 using namespace snort;
58 using namespace std;
59 
60 struct ModHook
61 {
62     Module* mod;
63     const BaseApi* api;
64     luaL_Reg* reg;
65 
66     ModHook(Module*, const BaseApi*);
67     ~ModHook();
68 
69     void init();
70 };
71 
72 static std::unordered_map<std::string, ModHook*> s_modules;
73 static std::unordered_map<std::string, const Parameter*> s_pmap;
74 
75 static unsigned s_errors = 0;
76 
77 set<uint32_t> ModuleManager::gids;
78 mutex ModuleManager::stats_mutex;
79 
80 static string s_current;
81 static string s_aliased_name;
82 static string s_aliased_type;
83 
84 // for callbacks from Lua
85 static SnortConfig* s_config = nullptr;
86 
87 // forward decls
88 extern "C"
89 {
90     bool open_table(const char*, int);
91     void close_table(const char*, int);
92 
93     bool set_bool(const char* fqn, bool val);
94     bool set_number(const char* fqn, double val);
95     bool set_string(const char* fqn, const char* val);
96 
97     bool set_alias(const char* from, const char* to);
98     void clear_alias();
99 
100     const char* push_include_path(const char* file);
101     void pop_include_path();
102     void snort_whitelist_append(const char*);
103     void snort_whitelist_add_prefix(const char*);
104 }
105 
106 //-------------------------------------------------------------------------
107 // boot foo
108 //-------------------------------------------------------------------------
109 
get_lua_coreinit()110 const char* ModuleManager::get_lua_coreinit()
111 { return lua_coreinit; }
112 
113 //-------------------------------------------------------------------------
114 // ModHook foo
115 //-------------------------------------------------------------------------
116 
ModHook(Module * m,const BaseApi * b)117 ModHook::ModHook(Module* m, const BaseApi* b)
118 {
119     mod = m;
120     api = b;
121     reg = nullptr;
122     init();
123 }
124 
~ModHook()125 ModHook::~ModHook()
126 {
127     if ( reg )
128         delete[] reg;
129 
130     if ( api && api->mod_dtor )
131         api->mod_dtor(mod);
132     else
133         delete mod;
134 }
135 
init()136 void ModHook::init()
137 {
138     const Command* c = mod->get_commands();
139 
140     if ( !c )
141         return;
142 
143     unsigned n = 0;
144     while ( c[n].name )
145         n++;
146 
147     // constructing reg here may seem like overkill
148     // ... why not just typedef Command to luaL_Reg?
149     // because the help would not be supplied or it
150     // would be out of date, out of sync, etc. QED
151     reg = new luaL_Reg[++n];
152     unsigned k = 0;
153 
154     while ( k < n )
155     {
156         reg[k].name = c[k].name;
157         reg[k].func = c[k].func;
158         k++;
159     }
160 }
161 
162 //-------------------------------------------------------------------------
163 // helper functions
164 //-------------------------------------------------------------------------
get_sub_table(const std::string & fqn)165 static std::string get_sub_table(const std::string& fqn)
166 {
167     auto pos = fqn.find_last_of(".");
168     if ( pos != std::string::npos )
169         return fqn.substr(pos + 1);
170     else
171         return fqn;
172 }
173 
set_type(string & fqn)174 static void set_type(string& fqn)
175 {
176     if ( s_aliased_type.empty() )
177         return;
178 
179     size_t pos = fqn.find_first_of('.');
180 
181     if ( pos == fqn.npos )
182         pos = fqn.size();
183 
184     fqn.replace(0, pos, s_aliased_type);
185 }
186 
set_top(string & fqn)187 static void set_top(string& fqn)
188 {
189     size_t pos = fqn.find_first_of('.');
190 
191     if ( pos != fqn.npos )
192         fqn.erase(pos);
193 }
194 
get_hook(const char * s)195 static ModHook* get_hook(const char* s)
196 {
197     auto mh = s_modules.find(s);
198     if ( mh != s_modules.end() )
199         return mh->second;
200 
201     return nullptr;
202 }
203 
204 //-------------------------------------------------------------------------
205 // dump methods:
206 // recurse over parameters and output like this:
207 // int snort_target_based.max_attribute_hosts = 0: maximum number of hosts
208 // in attribute table { 32:207551 }
209 // (type, fqn, default, brief help, range)
210 //-------------------------------------------------------------------------
211 
212 enum DumpFormat { DF_STD, DF_TAB, DF_LUA };
213 static DumpFormat dump_fmt = DF_STD;
214 
dump_field_std(const string & key,const Parameter * p)215 static void dump_field_std(const string& key, const Parameter* p)
216 {
217     cout << Markup::item();
218     cout << p->get_type();
219     cout << " " << Markup::emphasis(Markup::escape(key));
220 
221     if ( p->deflt )
222         cout << " = " << p->deflt;
223 
224     cout << ": " << p->help;
225 
226     if ( const char* r = p->get_range() )
227         cout << " { " << r << " }";
228 
229     cout << endl;
230 }
231 
dump_field_tab(const string & key,const Parameter * p)232 static void dump_field_tab(const string& key, const Parameter* p)
233 {
234     cout << Markup::item();
235     cout << p->get_type();
236     cout << "\t" << Markup::emphasis(Markup::escape(key));
237 
238     if ( p->deflt )
239         cout << "\t" << p->deflt;
240     else
241         cout << "\t";
242 
243     cout << "\t" << p->help;
244 
245     if ( const char* r = p->get_range() )
246         cout << "\t" << r;
247     else
248         cout << "\t";
249 
250     cout << endl;
251 }
252 
dump_field_lua(const string & key,const Parameter * p,bool table=false)253 static void dump_field_lua(const string& key, const Parameter* p, bool table = false)
254 {
255     // implied values (rule keywords) and command line args
256     // don't really have defaults, so skip them
257     if ( key.find('~') != string::npos ||
258         key.find('-') != string::npos ||
259         key.find('*') != string::npos )
260         return;
261 
262     if ( table || p->is_table() )
263         cout << key << " = { }";
264 
265     // if there is no default, emit nothing
266     else if ( !p->deflt )
267         return;
268 
269     else if ( p->is_quoted() )
270     {
271         const char* s = p->deflt ? p->deflt : " ";
272         cout << key << " = '" << s << "'";
273     }
274     else
275     {
276         const char* s = p->deflt ? p->deflt : "0";
277         cout << key << " = " << s;
278     }
279 
280     cout << endl;
281 }
282 
283 static void dump_table(string&, const char* pfx, const Parameter*, bool list = false);
284 
dump_field(string & key,const char * pfx,const Parameter * p,bool list=false)285 static void dump_field(string& key, const char* pfx, const Parameter* p, bool list = false)
286 {
287     unsigned n = key.size();
288 
289     if ( list || !p->name )
290         key += (dump_fmt == DF_LUA) ? "[1]" : "[]";
291 
292     if ( p->name )
293     {
294         if ( n )
295             key += ".";
296         key += p->name;
297     }
298 
299     if ( pfx && strncmp(key.c_str(), pfx, strlen(pfx)) )
300     {
301         key.erase();
302         return;
303     }
304     // we dump just one list entry
305     if ( p->type == Parameter::PT_TABLE )
306         dump_table(key, pfx, (const Parameter*)p->range);
307 
308     else if ( p->type == Parameter::PT_LIST )
309         dump_table(key, pfx, (const Parameter*)p->range, true);
310 
311     else
312     {
313         if ( dump_fmt == DF_LUA )
314             dump_field_lua(key, p);
315 
316         else if ( dump_fmt == DF_TAB )
317             dump_field_tab(key, p);
318 
319         else
320             dump_field_std(key, p);
321     }
322     key.erase(n);
323 }
324 
dump_table(string & key,const char * pfx,const Parameter * p,bool list)325 static void dump_table(string& key, const char* pfx, const Parameter* p, bool list)
326 {
327     if ( dump_fmt == DF_LUA )
328     {
329         dump_field_lua(key, p, true);
330 
331         if ( list )
332         {
333             string fqn = key + "[1]";
334             dump_field_lua(fqn, p, true);
335         }
336     }
337     while ( p && p->name )
338         dump_field(key, pfx, p++, list);
339 }
340 
341 //-------------------------------------------------------------------------
342 // set methods
343 //-------------------------------------------------------------------------
344 
get_params(const string & sfx,Module * m,const Parameter * p,int idx=1)345 static const Parameter* get_params(
346     const string& sfx, Module* m, const Parameter* p, int idx = 1)
347 {
348     size_t pos = sfx.find_first_of('.');
349     std::string new_fqn;
350 
351     if ( pos == string::npos )
352     {
353         if ( p[0].name && !p[1].name )
354             return p;
355         else
356             new_fqn = sfx;
357     }
358     else
359     {
360         new_fqn = sfx.substr(pos + 1);
361     }
362 
363     string name = new_fqn.substr(0, new_fqn.find_first_of('.'));
364 
365     while ( p->name )
366     {
367         if ( *p->name == '$' and m->matches(p->name, name) )
368             break;
369 
370         else if ( name == p->name )
371             break;
372 
373         ++p;
374     }
375 
376     if ( !p->name )
377         return nullptr;
378 
379     if ( p->type != Parameter::PT_TABLE &&
380         p->type != Parameter::PT_LIST )
381         return p;
382 
383     if ( new_fqn.find_first_of('.') == std::string::npos )
384     {
385         if ( idx && p->type == Parameter::PT_LIST )
386         {
387             const Parameter* tmp_p =
388                 reinterpret_cast<const Parameter*>(p->range);
389 
390             // FIXIT-L this will fail if we are opening a a list with only one Parameter
391             if ( tmp_p[0].name && !tmp_p[1].name )
392                 return tmp_p;
393         }
394         return p;
395     }
396 
397     p = (const Parameter*)p->range;
398     return get_params(new_fqn, m, p, idx);
399 }
400 
set_param(Module * mod,const char * fqn,Value & val)401 static bool set_param(Module* mod, const char* fqn, Value& val)
402 {
403     Shell::set_config_value(fqn, val);
404 
405     if ( !mod->verified_set(fqn, val, s_config) )
406     {
407         ParseError("%s is invalid", fqn);
408         ++s_errors;
409     }
410 
411     return true;
412 }
413 
set_value(const char * fqn,Value & v)414 static bool set_value(const char* fqn, Value& v)
415 {
416     string t = fqn;
417     set_type(t);
418     fqn = t.c_str();
419 
420     string key = t;
421     set_top(key);
422 
423     Module* mod = ModuleManager::get_module(key.c_str());
424 
425     if ( !mod )
426     {
427         ParseError("can't find %s", key.c_str());
428         ++s_errors;
429         return false;
430     }
431 
432     const Parameter* p;
433     auto a = s_pmap.find(t);
434 
435     if ( a != s_pmap.end() )
436         p = a->second;
437 
438     else
439     {
440         // now we must traverse the mod params to get the leaf
441         string s = fqn;
442         p = get_params(s, mod, mod->get_parameters());
443     }
444 
445     if ( !p )
446     {
447         // FIXIT-L handle things like x = { 1 } where x is a table not a
448         // list and 1 should be considered a key not a value; ideally say
449         // can't find x.1 instead of just can't find x
450         ParseError("can't find %s", fqn);
451         ++s_errors;
452         return false;
453     }
454 
455     if ( p->validate(v) )
456     {
457         v.set(p);
458         set_param(mod, fqn, v);
459         return true;
460     }
461 
462     if ( v.get_type() == Value::VT_STR )
463         ParseError("invalid %s = '%s'", fqn, v.get_string());
464     else if ( v.get_real() == v.get_int64() )
465         ParseError("invalid %s = " STDi64, fqn, v.get_int64());
466     else
467         ParseError("invalid %s = %g", fqn, v.get_real());
468 
469     ++s_errors;
470     return false;
471 }
472 
473 //-------------------------------------------------------------------------
474 // defaults - set all parameter table defaults for each configured module
475 // but there are no internal default list or list items.  since Lua calls
476 // open table for each explicitly configured table only, here is what we
477 // do:
478 //
479 // -- on open_table(), call Module::begin() for each module, list, and list
480 //    item
481 // -- recursively set all defaults after calling Module::begin(), skipping
482 //    lists and list items
483 // -- on close_table(), call Module::end() for each module, list, and list
484 //    item
485 //-------------------------------------------------------------------------
486 
top_level(const char * s)487 static bool top_level(const char* s)
488 { return !strchr(s, '.'); }
489 
begin(Module * m,const Parameter * p,const char * s,int idx,int depth)490 static bool begin(Module* m, const Parameter* p, const char* s, int idx, int depth)
491 {
492     // Module::(verified_)begin() will be called for top-level tables, lists, and list items only
493     if ( top_level(s) )
494     {
495         if ( !m->verified_begin(s, idx, s_config) )
496             return false;
497         // don't set list defaults
498         if ( m->is_list() and !idx )
499             return true;
500         if ( !p )
501         {
502             p = m->get_parameters();
503             assert(p);
504         }
505     }
506     else
507     {
508         assert(p);
509         if ( (!idx and p->type == Parameter::PT_LIST) or
510              (idx and p->type != Parameter::PT_LIST) )
511         {
512             if ( !m->verified_begin(s, idx, s_config) )
513                 return false;
514         }
515         if ( p->type == Parameter::PT_LIST )
516         {
517             // don't set list defaults (list items have idx > 0)
518             if ( !idx )
519                 return true;
520 
521             // set list item defaults only if explicitly configured
522             // (this is why it is done here and not in the loop below)
523             const Parameter* list_item_params = reinterpret_cast<const Parameter*>(p->range);
524 
525             return begin(m, list_item_params, s, idx, depth+1);
526         }
527     }
528 
529     // don't begin subtables again
530     if ( !top_level(s) && !depth )
531         return true;
532 
533     while ( p->name )
534     {
535         string fqn = s;
536         fqn += '.';
537         fqn += p->name;
538 
539         switch ( p->type )
540         {
541         // traverse subtables only to set defaults
542         case Parameter::PT_TABLE:
543             {
544                 const Parameter* table_item_params = reinterpret_cast<const Parameter*>(p->range);
545 
546                 Shell::add_config_child_node(get_sub_table(fqn), p->type, false);
547 
548                 if ( !begin(m, table_item_params, fqn.c_str(), idx, depth+1) )
549                     return false;
550             }
551             break;
552 
553         // skip lists, they must be configured explicitly
554         case Parameter::PT_LIST:
555         case Parameter::PT_MAX:
556             break;
557 
558         case Parameter::PT_BOOL:
559             if ( p->deflt )
560             {
561                 bool b = p->get_bool();
562                 set_bool(fqn.c_str(), b);
563             }
564             break;
565 
566         case Parameter::PT_INT:
567         case Parameter::PT_PORT:
568         case Parameter::PT_REAL:
569             if ( p->deflt )
570             {
571                 double d = p->get_number();
572                 set_number(fqn.c_str(), d);
573             }
574             break;
575 
576         // everything else is a string of some sort
577         default:
578             if ( p->deflt )
579                 set_string(fqn.c_str(), p->deflt);
580             break;
581         }
582         ++p;
583     }
584 
585     Shell::update_current_config_node();
586 
587     return true;
588 }
589 
590 // no need to recurse here; we only call Module::end() for
591 // top-level, lists, and list items
end(Module * m,const Parameter * p,const char * s,int idx)592 static bool end(Module* m, const Parameter* p, const char* s, int idx)
593 {
594     bool top_param = !p;
595 
596     if ( !p )
597     {
598         p = m->get_parameters();
599         assert(p);
600     }
601     // same as begin() but we must include top_param to catch
602     // top-level lists
603     if ( top_level(s) or
604          (top_param and p->type != Parameter::PT_TABLE) or
605          (!idx and p->type == Parameter::PT_LIST) or
606          (idx and p->type != Parameter::PT_LIST) )
607     {
608         return m->verified_end(s, idx, s_config);
609     }
610     return true;
611 }
612 
interested(Module * m)613 static bool interested(Module* m)
614 {
615     NetworkPolicy* np = get_network_policy();
616     if ( m->get_usage() == Module::GLOBAL && (!np || np->policy_id) )
617         return false;
618 
619     if ( m->get_usage() != Module::INSPECT && only_inspection_policy() )
620         return false;
621 
622     if ( m->get_usage() != Module::DETECT && only_ips_policy() )
623         return false;
624 
625     if ( m->get_usage() == Module::CONTEXT && !np )
626         return false;
627 
628     return true;
629 }
630 
631 
632 //-------------------------------------------------------------------------
633 // ffi methods - only called from Lua so cppcheck suppressions required
634 //-------------------------------------------------------------------------
635 
636 // cppcheck-suppress unusedFunction
clear_alias()637 SO_PUBLIC void clear_alias()
638 {
639     s_aliased_name.clear();
640     s_aliased_type.clear();
641 }
642 
643 // cppcheck-suppress unusedFunction
set_alias(const char * from,const char * to)644 SO_PUBLIC bool set_alias(const char* from, const char* to)
645 {
646     if ( !from or !to )
647         return false;
648 
649     const Module* m = ModuleManager::get_module(to);
650 
651     if ( !m or !m->is_bindable() )
652         return false;
653 
654     if ( (m->get_usage() == Module::GLOBAL) and from )
655     {
656         ParseError("global module type '%s' can't be aliased", to);
657         return false;
658     }
659 
660     if (  ModuleManager::get_module(from) )
661     {
662         ParseError("alias name can't be an existing module '%s'", from);
663         return false;
664     }
665 
666     s_aliased_name = from;
667     s_aliased_type = to;
668 
669     return true;
670 }
671 
672 // cppcheck-suppress unusedFunction
snort_whitelist_append(const char * s)673 SO_PUBLIC void snort_whitelist_append(const char* s)
674 {
675     Shell::allowlist_append(s, false);
676 }
677 
678 // cppcheck-suppress unusedFunction
snort_whitelist_add_prefix(const char * s)679 SO_PUBLIC void snort_whitelist_add_prefix(const char* s)
680 {
681     Shell::allowlist_append(s, true);
682 }
683 
684 // cppcheck-suppress unusedFunction
push_include_path(const char * file)685 SO_PUBLIC const char* push_include_path(const char* file)
686 {
687     static std::string path;
688     path = "";
689     const char* code = get_config_file(file, path);
690     push_parse_location(code, path.c_str(), file);
691     return path.c_str();
692 }
693 
694 // cppcheck-suppress unusedFunction
pop_include_path()695 SO_PUBLIC void pop_include_path()
696 {
697     pop_parse_location();
698 }
699 
700 //-------------------------------------------------------------------------
701 // ffi methods - also called internally so no cppcheck suppressions
702 //-------------------------------------------------------------------------
703 
open_table(const char * s,int idx)704 SO_PUBLIC bool open_table(const char* s, int idx)
705 {
706     const char* orig = s;
707     string fqn = s;
708     set_type(fqn);
709     s = fqn.c_str();
710 
711     string key = fqn;
712     set_top(key);
713 
714     // ips option parameters only used in rules which
715     // are non-lua; may be possible to allow a subtable
716     // for lua config of ips option globals
717     ModHook* h = get_hook(key.c_str());
718 
719     if ( !h || (h->api && h->api->type == PT_IPS_OPTION) )
720     {
721         if ( !Shell::is_trusted(key) )
722             ParseWarning(WARN_CONF_STRICT, "unknown table %s", key.c_str());
723         return false;
724     }
725 
726     // FIXIT-M only basic modules, inspectors and ips actions can be reloaded at present
727     if ( ( Snort::is_reloading() ) and h->api
728             and h->api->type != PT_INSPECTOR and h->api->type != PT_IPS_ACTION )
729     {
730         return false;
731     }
732 
733     Module* m = h->mod;
734     const Parameter* p = nullptr;
735 
736     if ( !interested(m) )
737         return false;
738 
739     if ( strcmp(m->get_name(), s) )
740     {
741         std::string sfqn = s;
742         p = get_params(sfqn, m, m->get_parameters(), idx);
743 
744         if ( !p )
745         {
746             ParseError("can't find %s", s);
747             return false;
748         }
749         else if ( (idx > 0) && (p->type == Parameter::PT_TABLE) )
750         {
751             ParseError("%s is a table; all elements must be named", s);
752             return false;
753         }
754     }
755 
756     string unique_key = key;
757     if ( !s_aliased_name.empty() )
758         unique_key = s_aliased_name;
759 
760     if ( s_current != unique_key )
761     {
762         if ( fqn != orig )
763             LogMessage("\t%s (%s)\n", key.c_str(), orig);
764         else
765             LogMessage("\t%s\n", key.c_str());
766         s_current = unique_key;
767     }
768 
769     if ( s_config->dump_config_mode() )
770     {
771         std::string table_name = get_sub_table(s);
772         bool is_top_level = false;
773         if ( top_level(s) && !idx )
774         {
775             table_name = s_current;
776             is_top_level = true;
777         }
778 
779         Shell::config_open_table(is_top_level, m->is_list(), idx, table_name, p);
780     }
781 
782     if ( !begin(m, p, s, idx, 0) )
783     {
784         ParseError("can't open %s", m->get_name());
785         return false;
786     }
787 
788     return true;
789 }
790 
close_table(const char * s,int idx)791 SO_PUBLIC void close_table(const char* s, int idx)
792 {
793     string fqn = s;
794     set_type(fqn);
795     s = fqn.c_str();
796 
797     string key = fqn;
798     set_top(key);
799 
800     const bool top = !idx && key == s;
801 
802     if ( ModHook* h = get_hook(key.c_str()) )
803     {
804         if ( !end(h->mod, nullptr, s, idx) )
805             ParseError("can't close %s", h->mod->get_name());
806 
807         else if (h->api && top)
808         {
809             if ( !s_aliased_name.empty() )
810                 PluginManager::instantiate(h->api, h->mod, s_config, s_aliased_name.c_str());
811             else
812                 PluginManager::instantiate(h->api, h->mod, s_config);
813         }
814     }
815 
816     Shell::config_close_table();
817 }
818 
set_bool(const char * fqn,bool b)819 SO_PUBLIC bool set_bool(const char* fqn, bool b)
820 {
821     Value v(b);
822     return set_value(fqn, v);
823 }
824 
set_number(const char * fqn,double d)825 SO_PUBLIC bool set_number(const char* fqn, double d)
826 {
827     Value v(d);
828     return set_value(fqn, v);
829 }
830 
set_string(const char * fqn,const char * s)831 SO_PUBLIC bool set_string(const char* fqn, const char* s)
832 {
833     Value v(s);
834     return set_value(fqn, v);
835 }
836 
837 //-------------------------------------------------------------------------
838 // private methods
839 //-------------------------------------------------------------------------
840 
comp_mods(const ModHook * l,const ModHook * r)841 static bool comp_mods(const ModHook* l, const ModHook* r)
842 {
843     const Module* lm = l->mod;
844     const Module* rm = r->mod;
845     return strcmp(lm->get_name(), rm->get_name()) < 0;
846 }
847 
comp_gids(const ModHook * l,const ModHook * r)848 static bool comp_gids(const ModHook* l, const ModHook* r)
849 {
850     const Module* lm = l->mod;
851     const Module* rm = r->mod;
852 
853     if ( lm->get_gid() == rm->get_gid() )
854         return comp_mods(l, r);
855 
856     return ( lm->get_gid() < rm->get_gid() );
857 }
858 
859 //-------------------------------------------------------------------------
860 // public methods
861 //-------------------------------------------------------------------------
862 
init()863 void ModuleManager::init()
864 {
865     module_init();
866 }
867 
term()868 void ModuleManager::term()
869 {
870     for ( auto& mh : s_modules )
871         delete mh.second;
872 
873     s_modules.clear();
874 }
875 
add_module(Module * m,const BaseApi * b)876 void ModuleManager::add_module(Module* m, const BaseApi* b)
877 {
878     ModHook* mh = new ModHook(m, b);
879 
880     assert(s_modules.find(mh->mod->get_name()) == s_modules.end());
881 
882     s_modules[mh->mod->get_name()] = mh;
883 
884     Profiler::register_module(m);
885 
886     if ( m->get_gid() )
887         gids.emplace(m->get_gid());
888 }
889 
get_module(const char * s)890 Module* ModuleManager::get_module(const char* s)
891 {
892     auto mh = s_modules.find(s);
893     if ( mh != s_modules.end() )
894         return mh->second->mod;
895 
896     return nullptr;
897 }
898 
get_default_module(const char * s,SnortConfig * sc)899 Module* ModuleManager::get_default_module(const char* s, SnortConfig* sc)
900 {
901     Module* mod = get_module(s);
902 
903     if ( mod )
904     {
905         mod->verified_begin(s, 0, sc);
906         mod->verified_end(s, 0, sc);
907     }
908     return mod;
909 }
910 
get_all_modules()911 list<Module*> ModuleManager::get_all_modules()
912 {
913     list<Module*> ret;
914 
915     for ( auto& mh : s_modules )
916        ret.emplace_back(mh.second->mod);
917 
918     return ret;
919 }
920 
get_all_modhooks()921 static list<ModHook*> get_all_modhooks()
922 {
923     list<ModHook*> ret;
924 
925     for ( auto& mh : s_modules )
926        ret.emplace_back(mh.second);
927 
928     return ret;
929 }
930 
set_config(SnortConfig * sc)931 void ModuleManager::set_config(SnortConfig* sc)
932 { s_config = sc; }
933 
reset_errors()934 void ModuleManager::reset_errors()
935 { s_errors = 0; }
936 
get_errors()937 unsigned ModuleManager::get_errors()
938 { return s_errors; }
939 
list_modules(const char * s)940 void ModuleManager::list_modules(const char* s)
941 {
942     PlugType pt = s ? PluginManager::get_type(s) : PT_MAX;
943     auto mod_hooks = get_all_modhooks();
944     mod_hooks.sort(comp_mods);
945     unsigned c = 0;
946 
947     for ( auto* mh : mod_hooks )
948     {
949         if (
950             !s || !*s ||
951             (mh->api && mh->api->type == pt) ||
952             (!mh->api && !strcmp(s, "basic"))
953             )
954         {
955             LogMessage("%s\n", mh->mod->get_name());
956             c++;
957         }
958     }
959     if ( !c )
960         cout << "no match" << endl;
961 }
962 
show_modules()963 void ModuleManager::show_modules()
964 {
965     auto mod_hooks = get_all_modhooks();
966     mod_hooks.sort(comp_mods);
967 
968     for ( auto* mh : mod_hooks )
969     {
970         const char* t = mh->api ? PluginManager::get_type_name(mh->api->type) : "basic";
971 
972         cout << Markup::item();
973         cout << Markup::emphasis(mh->mod->get_name());
974         cout << " (" << t;
975         cout << "): " << mh->mod->get_help();
976         cout << endl;
977     }
978 }
979 
dump_modules()980 void ModuleManager::dump_modules()
981 {
982     auto mod_hooks = get_all_modhooks();
983     mod_hooks.sort(comp_mods);
984     Dumper d("Modules");
985 
986     for ( auto* mh : mod_hooks )
987         if ( !mh->api )
988             d.dump(mh->mod->get_name());
989 }
990 
mod_type(const BaseApi * api)991 static std::string mod_type(const BaseApi* api)
992 {
993     if ( !api )
994         return "basic";
995 
996     std::string type(PluginManager::get_type_name(api->type));
997 
998     if ( api->type == PT_INSPECTOR )
999     {
1000         std::string itype = InspectorManager::get_inspector_type(api->name);
1001         if ( !itype.empty() )
1002             type += " (" + itype + ")";
1003     }
1004 
1005     return type;
1006 }
1007 
mod_use(Module::Usage use)1008 static const char* mod_use(Module::Usage use)
1009 {
1010     switch ( use )
1011     {
1012     case Module::GLOBAL : return "global";
1013     case Module::CONTEXT: return "context";
1014     case Module::INSPECT: return "inspect";
1015     case Module::DETECT : return "detect";
1016     }
1017     assert(false);
1018     return "error";
1019 }
1020 
mod_bind(const Module * m)1021 static const char* mod_bind(const Module* m)
1022 {
1023     if ( m->is_bindable() )
1024         return "multiton";
1025     else if (m->get_usage() == Module::GLOBAL)
1026         return "global";
1027     else if (m->get_usage() == Module::CONTEXT)
1028         return "network";
1029 
1030     return "singleton";
1031 }
1032 
show_module(const char * name)1033 void ModuleManager::show_module(const char* name)
1034 {
1035     if ( !name || !*name )
1036     {
1037         cerr << "module name required" << endl;
1038         return;
1039     }
1040     auto mod_hooks = get_all_modhooks();
1041     mod_hooks.sort(comp_gids);
1042     unsigned c = 0;
1043 
1044     for ( auto* mh : mod_hooks )
1045     {
1046         const Module* m = mh->mod;
1047         assert(m);
1048 
1049         if ( strcmp(m->get_name(), name) )
1050             continue;
1051 
1052         cout << endl << Markup::head(3) << name << endl << endl;
1053 
1054         if ( const char* h = m->get_help() )
1055             cout << endl << "Help: " << h << endl;
1056 
1057         cout << endl << "Type: "  << mod_type(mh->api) << endl;
1058         cout << endl << "Usage: "  << mod_use(m->get_usage()) << endl;
1059 
1060         if ( mh->api and mh->api->type == PT_INSPECTOR )
1061             cout << endl << "Instance Type: " << mod_bind(m) << endl;
1062 
1063         const Parameter* params = m->get_parameters();
1064         if ( params and params->type < Parameter::PT_MAX )
1065         {
1066             cout << endl << "Configuration: " << endl << endl;
1067             show_configs(name, true);
1068         }
1069 
1070         if ( m->get_commands() )
1071         {
1072             cout << endl << "Commands: " << endl << endl;
1073             show_commands(name, true);
1074         }
1075 
1076         if ( m->get_rules() )
1077         {
1078             cout << endl << "Rules: " << endl << endl;
1079             show_rules(name, true);
1080         }
1081 
1082         if ( m->get_pegs() )
1083         {
1084             cout << endl << "Peg counts: " << endl << endl;
1085             show_pegs(name, true);
1086         }
1087         c++;
1088     }
1089     if ( !c )
1090         cout << "no match" << endl;
1091 }
1092 
reload_module(const char * name,SnortConfig * sc)1093 void ModuleManager::reload_module(const char* name, SnortConfig* sc)
1094 {
1095     ModHook* h = get_hook(name);
1096 
1097     // Most of the modules don't support yet reload_module.
1098     // This list contains the ones that do, and should be updated as
1099     // more modules support reload_module.
1100     const vector<string> supported_modules =
1101     {
1102         "dns_si", "firewall", "identity", "qos", "reputation", "url_si", "rt_network"
1103     };
1104     auto it = find(supported_modules.begin(), supported_modules.end(), name);
1105 
1106     // FIXIT-L: we can check that h->api is not null here or inside instantiate.
1107     // Both alternatives prevent crashing in instantiate(). However,
1108     // checking it here might be too aggressive, because we are also saying it
1109     // is an error. That makes the caller of this function
1110     // (get_updated_module()) discard other legitimate reload operations, e.g.
1111     // the newly read configuration. We should decide on this when proper
1112     // reload functionality gets implemented.
1113     if ( it != supported_modules.end() and h and h->api and h->mod and sc )
1114     {
1115         PluginManager::instantiate(h->api, h->mod, sc);
1116         s_errors += get_parse_errors();
1117     }
1118     else
1119     {
1120         cout << "Module " << name <<" doesn't exist or reload not implemented.";
1121         cout << endl;
1122         ++s_errors;
1123     }
1124 }
1125 
selected(const Module * m,const char * pfx,bool exact)1126 static bool selected(const Module* m, const char* pfx, bool exact)
1127 {
1128     if ( !pfx )
1129         return true;
1130 
1131     if ( exact && strcmp(m->get_name(), pfx) )
1132         return false;
1133 
1134     else if ( !exact && strncmp(m->get_name(), pfx, strlen(pfx)) )
1135         return false;
1136 
1137     return true;
1138 }
1139 
show_configs(const char * pfx,bool exact)1140 void ModuleManager::show_configs(const char* pfx, bool exact)
1141 {
1142     auto mod_hooks = get_all_modhooks();
1143     mod_hooks.sort(comp_mods);
1144     unsigned c = 0;
1145 
1146     for ( auto* mh : mod_hooks )
1147     {
1148         Module* m = mh->mod;
1149         string s;
1150 
1151         if ( !selected(m, pfx, exact) )
1152             continue;
1153 
1154         if ( m->is_list() )
1155         {
1156             s = m->name;
1157 
1158             if ( m->params->name )
1159                 dump_table(s, pfx, m->params, true);
1160             else
1161                 dump_field(s, pfx, m->params);
1162         }
1163         else if ( m->is_table() )
1164         {
1165             s = m->name;
1166             dump_table(s, pfx, m->params);
1167         }
1168         else
1169         {
1170             dump_field(s, pfx, m->params);
1171         }
1172 
1173         if ( !pfx )
1174             cout << endl;
1175 
1176         c++;
1177     }
1178     if ( !c )
1179         cout << "no match" << endl;
1180 }
1181 
dump_defaults(const char * pfx)1182 void ModuleManager::dump_defaults(const char* pfx)
1183 {
1184     dump_fmt = DF_LUA;
1185     show_configs(pfx);
1186 }
1187 
show_commands(const char * pfx,bool exact)1188 void ModuleManager::show_commands(const char* pfx, bool exact)
1189 {
1190     auto mod_hooks = get_all_modhooks();
1191     mod_hooks.sort(comp_mods);
1192     unsigned n = 0;
1193 
1194     for ( auto* mh : mod_hooks )
1195     {
1196         const Module* m = mh->mod;
1197 
1198         if ( !selected(m, pfx, exact) )
1199             continue;
1200 
1201         const Command* c = m->get_commands();
1202 
1203         if ( !c )
1204             continue;
1205 
1206         while ( c->name )
1207         {
1208             cout << Markup::item();
1209             cout << Markup::emphasis_on();
1210             cout << mh->mod->get_name();
1211             cout << "." << c->name;
1212             cout << Markup::emphasis_off();
1213             cout << c->get_arg_list();
1214             cout << ": " << c->help;
1215             cout << endl;
1216             c++;
1217         }
1218         n++;
1219     }
1220     if ( !n )
1221         cout << "no match" << endl;
1222 }
1223 
gid_in_use(uint32_t gid)1224 bool ModuleManager::gid_in_use(uint32_t gid)
1225 {
1226     return gids.find(gid) != gids.end();
1227 }
1228 
show_gids(const char * pfx,bool exact)1229 void ModuleManager::show_gids(const char* pfx, bool exact)
1230 {
1231     auto mod_hooks = get_all_modhooks();
1232     mod_hooks.sort(comp_gids);
1233     unsigned c = 0;
1234 
1235     for ( auto* mh : mod_hooks )
1236     {
1237         const Module* m = mh->mod;
1238         assert(m);
1239 
1240         if ( !selected(m, pfx, exact) )
1241             continue;
1242 
1243         unsigned gid = m->get_gid();
1244 
1245         if ( gid )
1246         {
1247             cout << Markup::item();
1248             cout << Markup::emphasis_on();
1249             cout << gid;
1250             cout << Markup::emphasis_off();
1251             cout << ": " << m->get_name();
1252             cout << endl;
1253         }
1254         c++;
1255     }
1256     if ( !c )
1257         cout << "no match" << endl;
1258 }
1259 
peg_op(CountType ct)1260 static const char* peg_op(CountType ct)
1261 {
1262     switch ( ct )
1263     {
1264     case CountType::SUM: return "sum";
1265     case CountType::NOW: return "now";
1266     case CountType::MAX: return "max";
1267     default: break;
1268     }
1269     assert(false);
1270     return "error";
1271 }
1272 
show_pegs(const char * pfx,bool exact)1273 void ModuleManager::show_pegs(const char* pfx, bool exact)
1274 {
1275     auto mod_hooks = get_all_modhooks();
1276     mod_hooks.sort(comp_gids);
1277     unsigned c = 0;
1278 
1279     for ( auto* mh : mod_hooks )
1280     {
1281         const Module* m = mh->mod;
1282         assert(m);
1283 
1284         if ( !selected(m, pfx, exact) )
1285             continue;
1286 
1287         const PegInfo* pegs = m->get_pegs();
1288 
1289         if ( !pegs )
1290             continue;
1291 
1292         while ( pegs->name )
1293         {
1294             cout << Markup::item();
1295             cout << Markup::emphasis_on();
1296             cout << mh->mod->get_name();
1297             cout << "." << pegs->name;
1298             cout << Markup::emphasis_off();
1299             cout << ": " << pegs->help;
1300             cout << " (" << peg_op(pegs->type) << ")";
1301             cout << endl;
1302             ++pegs;
1303         }
1304         c++;
1305     }
1306     if ( !c )
1307         cout << "no match" << endl;
1308 }
1309 
load_commands(Shell * sh)1310 void ModuleManager::load_commands(Shell* sh)
1311 {
1312     // FIXIT-L ideally only install commands from configured modules
1313     // FIXIT-L install commands into working shell
1314     auto mod_hooks = get_all_modhooks();
1315     mod_hooks.sort(comp_mods);
1316 
1317     for ( auto* mh : mod_hooks )
1318     {
1319         if ( mh->reg )
1320             sh->install(mh->mod->get_name(), mh->reg);
1321     }
1322 }
1323 
1324 // move builtin generation to a better home?
1325 // FIXIT-L builtins should allow configurable nets and ports
1326 // FIXIT-L builtins should have accurate proto
1327 //       (but ip winds up in all others)
1328 // FIXIT-L if msg has C escaped embedded quotes, we break
1329 //ss << "alert tcp any any -> any any ( ";
make_rule(ostream & os,const Module * m,const RuleMap * r,const char * opts=nullptr)1330 static void make_rule(ostream& os, const Module* m, const RuleMap* r, const char* opts = nullptr)
1331 {
1332     os << "alert ( ";
1333     os << "gid:" << m->get_gid() << "; ";
1334     os << "sid:" << r->sid << "; ";
1335     os << "msg:\"" << "(" << m->get_name() << ") ";
1336     os << r->msg << "\";";
1337     if ( opts and *opts )
1338         os << " " << opts;
1339     os << " )";
1340     os << endl;
1341 }
1342 
1343 // FIXIT-L currently no way to know whether a module was activated or not
1344 // so modules with common rules will cause duplicate sid warnings
1345 // eg http_server (old) and http_inspect (new) both have 119:1-34
1346 // only way to avoid that now is to not load plugins with common rules
1347 // (we don't want to suppress it because it could mean something is broken)
load_rules(SnortConfig * sc)1348 void ModuleManager::load_rules(SnortConfig* sc)
1349 {
1350     auto mod_hooks = get_all_modhooks();
1351     mod_hooks.sort(comp_gids);
1352 
1353     for ( auto* mh : mod_hooks )
1354     {
1355         const Module* m = mh->mod;
1356         const RuleMap* r = m->get_rules();
1357 
1358         if ( !r )
1359             continue;
1360 
1361         stringstream ss;
1362 
1363         while ( r->msg )
1364         {
1365             ss.str("");
1366             const char* historical_opts = "rev:1; priority:3;";
1367             make_rule(ss, m, r, historical_opts);
1368 
1369             // note:  you can NOT do ss.str().c_str() here
1370             const string& rule = ss.str();
1371             parse_rules_string(sc, rule.c_str());
1372 
1373             r++;
1374         }
1375     }
1376 }
1377 
dump_stats(const char * skip,bool dynamic)1378 void ModuleManager::dump_stats(const char* skip, bool dynamic)
1379 {
1380     auto mod_hooks = get_all_modhooks();
1381     mod_hooks.sort(comp_mods);
1382 
1383     for ( auto* mh : mod_hooks )
1384     {
1385         if ( !skip || !strstr(skip, mh->mod->get_name()) )
1386         {
1387             lock_guard<mutex> lock(stats_mutex);
1388             if ( dynamic )
1389                 mh->mod->show_dynamic_stats();
1390             else
1391                 mh->mod->show_stats();
1392         }
1393     }
1394 }
1395 
accumulate()1396 void ModuleManager::accumulate()
1397 {
1398     auto mod_hooks = get_all_modhooks();
1399 
1400     for ( auto* mh : mod_hooks )
1401     {
1402         if ( !strcmp(mh->mod->name, "memory") )
1403             continue;
1404 
1405         lock_guard<mutex> lock(stats_mutex);
1406         mh->mod->prep_counts();
1407         mh->mod->sum_stats(true);
1408     }
1409 }
1410 
accumulate_module(const char * name)1411 void ModuleManager::accumulate_module(const char* name)
1412 {
1413     ModHook* mh = get_hook(name);
1414     if ( mh )
1415     {
1416         lock_guard<mutex> lock(stats_mutex);
1417         mh->mod->prep_counts();
1418         mh->mod->sum_stats(true);
1419     }
1420 }
1421 
reset_stats(SnortConfig *)1422 void ModuleManager::reset_stats(SnortConfig*)
1423 {
1424     auto mod_hooks = get_all_modhooks();
1425 
1426     for ( auto* mh : mod_hooks )
1427     {
1428         lock_guard<mutex> lock(stats_mutex);
1429         mh->mod->reset_stats();
1430     }
1431 }
1432 
clear_global_active_counters()1433 void ModuleManager::clear_global_active_counters()
1434 {
1435     auto mod_hooks = get_all_modhooks();
1436 
1437     for ( auto* mh : mod_hooks )
1438     {
1439         lock_guard<mutex> lock(stats_mutex);
1440         mh->mod->clear_global_active_counters();
1441     }
1442 }
1443 
reset_stats(clear_counter_type_t type)1444 void ModuleManager::reset_stats(clear_counter_type_t type)
1445 {
1446     if ( type != TYPE_MODULE and type != TYPE_UNKNOWN )
1447     {
1448         ModHook* mh = get_hook(clear_counter_type_string_map[type]);
1449         if ( mh and mh->mod )
1450         {
1451             lock_guard<mutex> lock(stats_mutex);
1452             mh->mod->reset_stats();
1453         }
1454     }
1455     else
1456     {
1457         auto mod_hooks = get_all_modhooks();
1458         for ( auto* mh : mod_hooks )
1459         {
1460             bool ignore = false;
1461 
1462             // FIXIT-M Will remove this for loop when will come up with more
1463             //  granular form of clearing module stats.
1464             for ( int i = 0; i < static_cast<int>(clear_counter_type_string_map.size()); i++ )
1465             {
1466                 if ( !strcmp(mh->mod->get_name(), clear_counter_type_string_map[i]) )
1467                 {
1468                     ignore = true;
1469                     break;
1470                 }
1471             }
1472 
1473             if ( type == TYPE_UNKNOWN or !ignore )
1474             {
1475                 lock_guard<mutex> lock(stats_mutex);
1476                 mh->mod->reset_stats();
1477             }
1478         }
1479     }
1480     if ( type == TYPE_DAQ or type == TYPE_UNKNOWN )
1481     {
1482         lock_guard<mutex> lock(stats_mutex);
1483         PacketManager::reset_stats();
1484     }
1485 }
1486 
1487 
1488 
1489 //-------------------------------------------------------------------------
1490 // parameter loading
1491 //-------------------------------------------------------------------------
1492 
1493 static void load_table(string&, const Parameter*);
1494 
load_field(string & key,const Parameter * p)1495 static void load_field(string& key, const Parameter* p)
1496 {
1497     unsigned n = key.size();
1498 
1499     if ( p->name )
1500     {
1501         if ( n )
1502             key += ".";
1503         key += p->name;
1504     }
1505 
1506     if ( p->type == Parameter::PT_TABLE or p->type == Parameter::PT_LIST )
1507         load_table(key, (const Parameter*)p->range);
1508 
1509     else
1510         s_pmap[key] = p;
1511 
1512     key.erase(n);
1513 }
1514 
load_table(string & key,const Parameter * p)1515 static void load_table(string& key, const Parameter* p)
1516 {
1517     while ( p && p->name )
1518         load_field(key, p++);
1519 }
1520 
load_params()1521 void ModuleManager::load_params()
1522 {
1523     auto mod_hooks = get_all_modhooks();
1524     mod_hooks.sort(comp_mods);
1525 
1526     for ( auto* mh : mod_hooks )
1527     {
1528         Module* m = mh->mod;
1529         string s;
1530 
1531         if ( m->is_list() )
1532         {
1533             s = m->name;
1534 
1535             if ( m->params->name )
1536                 load_table(s, m->params);
1537             else
1538                 load_field(s, m->params);
1539         }
1540         else if ( m->is_table() )
1541         {
1542             s = m->name;
1543             load_table(s, m->params);
1544         }
1545         else
1546         {
1547             load_field(s, m->params);
1548         }
1549     }
1550 }
1551 
get_parameter(const char * table,const char * option)1552 const Parameter* ModuleManager::get_parameter(const char* table, const char* option)
1553 {
1554     string key = table;
1555     key += '.';
1556     key += option;
1557 
1558     auto a = s_pmap.find(key);
1559 
1560     if (a != s_pmap.end() )
1561         return a->second;
1562 
1563     return nullptr;
1564 }
1565 
1566 //--------------------------------------------------------------------------
1567 // builtin rule outputs
1568 //--------------------------------------------------------------------------
1569 
1570 struct RulePtr
1571 {
1572     const Module* mod;
1573     const RuleMap* rule;
1574 
RulePtrRulePtr1575     RulePtr(const Module* m, const RuleMap* r) : mod(m), rule(r) { }
1576 
operator <RulePtr1577     bool operator< (const RulePtr& rhs) const
1578     {
1579         if ( mod->get_gid() != rhs.mod->get_gid() )
1580             return mod->get_gid() < rhs.mod->get_gid();
1581 
1582          return rule->sid < rhs.rule->sid;
1583     }
1584 };
1585 
get_rules(const char * pfx,bool exact=false)1586 static std::vector<RulePtr> get_rules(const char* pfx, bool exact = false)
1587 {
1588     auto mod_hooks = get_all_modhooks();
1589     std::vector<RulePtr> rule_set;
1590 
1591     for ( auto* mh : mod_hooks )
1592     {
1593         const Module* m = mh->mod;
1594 
1595         if ( !selected(m, pfx, exact) )
1596             continue;
1597 
1598         const RuleMap* r = m->get_rules();
1599 
1600         if ( !r )
1601             continue;
1602 
1603         while ( r->msg )
1604             rule_set.push_back(RulePtr(m, r++));
1605     }
1606     std::sort(rule_set.begin(), rule_set.end());
1607     return rule_set;
1608 }
1609 
dump_rules(const char * pfx,const char * opts)1610 void ModuleManager::dump_rules(const char* pfx, const char* opts)
1611 {
1612     std::vector<RulePtr> rule_set = get_rules(pfx);
1613 
1614     for ( auto rp : rule_set )
1615         make_rule(cout, rp.mod, rp.rule, opts);
1616 
1617     if ( !rule_set.size() )
1618         cout << "no match" << endl;
1619 }
1620 
show_rules(const char * pfx,bool exact)1621 void ModuleManager::show_rules(const char* pfx, bool exact)
1622 {
1623     std::vector<RulePtr> rule_set = get_rules(pfx, exact);
1624 
1625     for ( auto rp : rule_set )
1626     {
1627         cout << Markup::item();
1628         cout << Markup::emphasis_on();
1629         cout << rp.mod->get_gid() << ":" << rp.rule->sid;
1630         cout << Markup::emphasis_off();
1631         cout << " (" << rp.mod->get_name() << ")";
1632         cout << " " << rp.rule->msg;
1633         cout << endl;
1634     }
1635     if ( !rule_set.size() )
1636         cout << "no match" << endl;
1637 }
1638 
1639 //--------------------------------------------------------------------------
1640 // JSON dumpers
1641 //--------------------------------------------------------------------------
1642 
dump_param_range_json(JsonStream & json,const Parameter * p)1643 static void dump_param_range_json(JsonStream& json, const Parameter* p)
1644 {
1645     const char* range = p->get_range();
1646 
1647     if ( !range )
1648         json.put("range");
1649     else
1650     {
1651         switch ( p->type )
1652         {
1653         case Parameter::PT_INT:
1654         case Parameter::PT_PORT:
1655         {
1656             std::string tr = range;
1657             const char* d = strchr(range, ':');
1658             if ( *range == 'm' )
1659             {
1660                 if ( d )
1661                 {
1662                     tr = std::to_string(Parameter::get_int(range)) +
1663                         tr.substr(tr.find(":"));
1664                 }
1665                 else
1666                     tr = std::to_string(Parameter::get_int(range));
1667             }
1668             if ( d and *++d == 'm' )
1669             {
1670                 tr = tr.substr(0, tr.find(":") + 1) +
1671                     std::to_string(Parameter::get_int(d));
1672             }
1673             json.put("range", tr);
1674             break;
1675         }
1676 
1677         default:
1678             json.put("range", p->get_range());
1679         }
1680     }
1681 }
1682 
dump_param_default_json(JsonStream & json,const Parameter * p)1683 static void dump_param_default_json(JsonStream& json, const Parameter* p)
1684 {
1685     const char* def = p->deflt;
1686 
1687     if ( !def )
1688         json.put("default");
1689     else
1690     {
1691         switch ( p->type )
1692         {
1693         case Parameter::PT_INT:
1694         case Parameter::PT_PORT:
1695             json.put("default", std::stol(def));
1696             break;
1697 
1698         case Parameter::PT_REAL:
1699         {
1700             const char* dot = strchr(def, '.');
1701             if ( dot )
1702                 json.put("default", std::stod(def), strlen(dot) - 1);
1703             else
1704                 json.put("default", std::stod(def));
1705 
1706             break;
1707         }
1708 
1709         case Parameter::PT_BOOL:
1710             !strcmp(def, "true") ? json.put_true("default") : json.put_false("default");
1711             break;
1712 
1713         default:
1714             json.put("default", def);
1715         }
1716     }
1717 }
1718 
dump_params_tree_json(JsonStream & json,const Parameter * p)1719 static void dump_params_tree_json(JsonStream& json, const Parameter* p)
1720 {
1721     while ( p and p->type != Parameter::PT_MAX )
1722     {
1723         assert(p->name);
1724 
1725         json.open();
1726         json.put("option", p->name);
1727         json.put("type", p->get_type());
1728         if ( p->is_table() and p->range )
1729         {
1730             json.open_array("sub_options");
1731             dump_params_tree_json(json, (const Parameter*)p->range);
1732             json.close_array();
1733         }
1734         else
1735             dump_param_range_json(json, p);
1736 
1737         dump_param_default_json(json, p);
1738         if ( p->help )
1739             json.put("help", p->help);
1740         else
1741             json.put("help");
1742 
1743         json.close();
1744 
1745         ++p;
1746     }
1747 }
1748 
dump_configs_json(JsonStream & json,const Module * mod)1749 static void dump_configs_json(JsonStream& json, const Module* mod)
1750 {
1751     const Parameter* params = mod->get_parameters();
1752 
1753     json.open_array("configuration");
1754     dump_params_tree_json(json, params);
1755     json.close_array();
1756 }
1757 
dump_commands_json(JsonStream & json,const Module * mod)1758 static void dump_commands_json(JsonStream& json, const Module* mod)
1759 {
1760     const Command* cmds = mod->get_commands();
1761 
1762     json.open_array("commands");
1763 
1764     while ( cmds and cmds->name )
1765     {
1766         json.open();
1767 
1768         json.put("name", cmds->name);
1769 
1770         json.open_array("params");
1771         if ( cmds->params )
1772             dump_params_tree_json(json, cmds->params);
1773 
1774         json.close_array();
1775 
1776         if ( cmds->help )
1777             json.put("help", cmds->help);
1778         else
1779             json.put("help");
1780 
1781         json.close();
1782 
1783         ++cmds;
1784     }
1785 
1786     json.close_array();
1787 }
1788 
dump_rules_json(JsonStream & json,const Module * mod)1789 static void dump_rules_json(JsonStream& json, const Module* mod)
1790 {
1791     auto rules = get_rules(mod->get_name(), true);
1792 
1793     json.open_array("rules");
1794     for ( const auto& rp : rules )
1795     {
1796         json.open();
1797 
1798         json.put("gid", rp.mod->get_gid());
1799         json.put("sid", rp.rule->sid);
1800         json.put("msg", rp.rule->msg);
1801 
1802         json.close();
1803     }
1804     json.close_array();
1805 }
1806 
dump_pegs_json(JsonStream & json,const Module * mod)1807 static void dump_pegs_json(JsonStream& json, const Module* mod)
1808 {
1809     const PegInfo* pegs = mod->get_pegs();
1810 
1811     json.open_array("peg_counts");
1812     while ( pegs and pegs->type != CountType::END )
1813     {
1814         json.open();
1815         json.put("type", peg_op(pegs->type));
1816 
1817         assert(pegs->name);
1818         json.put("name", pegs->name);
1819 
1820         if ( pegs->help )
1821             json.put("help", pegs->help);
1822         else
1823             json.put("help");
1824 
1825         json.close();
1826 
1827         ++pegs;
1828     }
1829     json.close_array();
1830 }
1831 
show_modules_json()1832 void ModuleManager::show_modules_json()
1833 {
1834     auto mod_hooks = get_all_modhooks();
1835     mod_hooks.sort(comp_mods);
1836     JsonStream json(std::cout);
1837 
1838     json.open_array();
1839     for ( const auto* mh : mod_hooks )
1840     {
1841         const Module* mod = mh->mod;
1842         assert(mod);
1843 
1844         std::string name = "";
1845         if ( const char* n = mod->get_name() )
1846             name = n;
1847 
1848         assert(!name.empty());
1849 
1850         std::string help = "";
1851         if ( const char* h = mod->get_help() )
1852             help = h;
1853 
1854         std::string type = mod_type(mh->api);
1855         const char* usage = mod_use(mod->get_usage());
1856 
1857         json.open();
1858         json.put("module", name);
1859         json.put("help", help);
1860         json.put("type", type);
1861         json.put("usage", usage);
1862         if ( mh->api and mh->api->type == PT_INSPECTOR )
1863             json.put("instance_type", mod_bind(mod));
1864 
1865         dump_configs_json(json, mod);
1866         dump_commands_json(json, mod);
1867         dump_rules_json(json, mod);
1868         dump_pegs_json(json, mod);
1869         json.close();
1870     }
1871     json.close_array();
1872 }
1873 
1874 #ifdef UNIT_TEST
1875 
1876 #include <catch/snort_catch.h>
1877 
1878 TEST_CASE("param range JSON dumper", "[ModuleManager]")
1879 {
1880     std::stringstream ss;
1881     JsonStream json(ss);
1882 
1883     SECTION("null")
1884     {
1885         const Parameter p("string", Parameter::PT_STRING, nullptr, nullptr, "help");
1886         dump_param_range_json(json, &p);
1887         std::string x = R"-("range": null)-";
1888         CHECK(ss.str() == x);
1889     }
1890 
1891     SECTION("common string")
1892     {
1893         const Parameter p("enum", Parameter::PT_ENUM, "one | two | three", nullptr, "help");
1894         dump_param_range_json(json, &p);
1895         std::string x = R"-("range": "one | two | three")-";
1896         CHECK(ss.str() == x);
1897     }
1898 
1899     SECTION("number string")
1900     {
1901         const Parameter i_max("int_max", Parameter::PT_INT, "255", nullptr, "help");
1902         dump_param_range_json(json, &i_max);
1903         std::string x = R"-("range": "255")-";
1904         CHECK(ss.str() == x);
1905         ss.str("");
1906 
1907         const Parameter i_min("int_min", Parameter::PT_INT, "255:", nullptr, "help");
1908         dump_param_range_json(json, &i_min);
1909         x = R"-(, "range": "255:")-";
1910         CHECK(ss.str() == x);
1911         ss.str("");
1912 
1913         const Parameter i_exp_max("int_exp_max", Parameter::PT_INT, ":255", nullptr, "help");
1914         dump_param_range_json(json, &i_exp_max);
1915         x = R"-(, "range": ":255")-";
1916         CHECK(ss.str() == x);
1917         ss.str("");
1918 
1919         const Parameter p_min_max("int_min_max", Parameter::PT_PORT, "0:65535", nullptr, "help");
1920         dump_param_range_json(json, &p_min_max);
1921         x = R"-(, "range": "0:65535")-";
1922         CHECK(ss.str() == x);
1923         ss.str("");
1924 
1925         const Parameter i_hex("int_in_hex", Parameter::PT_INT, "0x5:0xFF", nullptr, "help");
1926         dump_param_range_json(json, &i_hex);
1927         x = R"-(, "range": "0x5:0xFF")-";
1928         CHECK(ss.str() == x);
1929     }
1930 
1931     SECTION("number string with maxN")
1932     {
1933         const Parameter i_max("int_max", Parameter::PT_INT, "max32", nullptr, "help");
1934         dump_param_range_json(json, &i_max);
1935         std::string x = R"-("range": "4294967295")-";
1936         CHECK(ss.str() == x);
1937         ss.str("");
1938 
1939         const Parameter i_min("int_min", Parameter::PT_INT, "max32:", nullptr, "help");
1940         dump_param_range_json(json, &i_min);
1941         x = R"-(, "range": "4294967295:")-";
1942         CHECK(ss.str() == x);
1943         ss.str("");
1944 
1945         const Parameter i_exp_max("int_exp_max", Parameter::PT_INT, ":max32", nullptr, "help");
1946         dump_param_range_json(json, &i_exp_max);
1947         x = R"-(, "range": ":4294967295")-";
1948         CHECK(ss.str() == x);
1949         ss.str("");
1950 
1951         const Parameter p_min_max("int_min_max", Parameter::PT_INT, "max31:max32", nullptr, "help");
1952         dump_param_range_json(json, &p_min_max);
1953         x = R"-(, "range": "2147483647:4294967295")-";
1954         CHECK(ss.str() == x);
1955     }
1956 }
1957 
1958 TEST_CASE("param default JSON dumper", "[ModuleManager]")
1959 {
1960     std::stringstream ss;
1961     JsonStream json(ss);
1962 
1963     SECTION("null")
1964     {
1965         const Parameter p("int", Parameter::PT_INT, nullptr, nullptr, "help");
1966         dump_param_default_json(json, &p);
1967         std::string x = R"-("default": null)-";
1968         CHECK(ss.str() == x);
1969     }
1970 
1971     SECTION("string")
1972     {
1973         const Parameter p("multi", Parameter::PT_MULTI, "one | two | three", "one two", "help");
1974         dump_param_default_json(json, &p);
1975         std::string x = R"-("default": "one two")-";
1976         CHECK(ss.str() == x);
1977     }
1978 
1979     SECTION("integer")
1980     {
1981         const Parameter p("int", Parameter::PT_INT, nullptr, "5", "help");
1982         dump_param_default_json(json, &p);
1983         std::string x = R"-("default": 5)-";
1984         CHECK(ss.str() == x);
1985     }
1986 
1987     SECTION("real")
1988     {
1989         const Parameter p("real", Parameter::PT_REAL, nullptr, "12.345", "help");
1990         dump_param_default_json(json, &p);
1991         std::string x = R"-("default": 12.345)-";
1992         CHECK(ss.str() == x);
1993     }
1994 
1995     SECTION("boolean")
1996     {
1997         const Parameter t("bool_true", Parameter::PT_BOOL, nullptr, "true", "help");
1998         dump_param_default_json(json, &t);
1999         std::string x = R"-("default": true)-";
2000         CHECK(ss.str() == x);
2001         ss.str("");
2002 
2003         const Parameter f("bool_false", Parameter::PT_BOOL, nullptr, "false", "help");
2004         dump_param_default_json(json, &f);
2005         x = R"-(, "default": false)-";
2006         CHECK(ss.str() == x);
2007     }
2008 }
2009 
2010 #endif // UNIT_TEST
2011 
2012