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