// SPDX-License-Identifier: Apache-2.0 /* Copyright (C) 2023 The Falco Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #include #ifndef _WIN32 #include #else #include #include #define srandom srand #define random rand #endif #include #include #include #include #include #include #include #include #include #include #include "falco_engine.h" #include "falco_utils.h" #include "falco_engine_version.h" #include "formats.h" #include "evttype_index_ruleset.h" #include "rule_json_schema.h" const std::string falco_engine::s_default_ruleset = "falco-default-ruleset"; using namespace falco; falco_engine::falco_engine(bool seed_rng): m_syscall_source(NULL), m_syscall_source_idx(SIZE_MAX), m_rule_reader(std::make_shared()), m_rule_collector(std::make_shared()), m_rule_compiler(std::make_shared()), m_next_ruleset_id(0), m_min_priority(falco_common::PRIORITY_DEBUG), m_sampling_ratio(1), m_sampling_multiplier(0) { if(seed_rng) { srandom((unsigned)getpid()); } m_default_ruleset_id = find_ruleset_id(s_default_ruleset); fill_engine_state_funcs(m_engine_state); m_rule_schema = nlohmann::json::parse(rule_schema_string); } falco_engine::~falco_engine() { m_rules.clear(); m_rule_collector->clear(); m_rule_stats_manager.clear(); m_sources.clear(); m_extra_output_format.clear(); } sinsp_version falco_engine::engine_version() { return sinsp_version(FALCO_ENGINE_VERSION); } void falco_engine::set_rule_reader(std::shared_ptr reader) { m_rule_reader = reader; } std::shared_ptr falco_engine::get_rule_reader() { return m_rule_reader; } void falco_engine::set_rule_collector(std::shared_ptr collector) { m_rule_collector = collector; } std::shared_ptr falco_engine::get_rule_collector() { return m_rule_collector; } void falco_engine::set_rule_compiler(std::shared_ptr compiler) { m_rule_compiler = compiler; } std::shared_ptr falco_engine::get_rule_compiler() { return m_rule_compiler; } // Return a key that uniquely represents a field class. // For now, we assume name + shortdesc is unique. static std::string fieldclass_key(const sinsp_filter_factory::filter_fieldclass_info &fld_info) { return fld_info.name + fld_info.shortdesc; } void falco_engine::list_fields(const std::string &source, bool verbose, bool names_only, bool markdown) const { // Maps from field class name + short desc to list of event // sources for which this field class can be used. std::map> fieldclass_event_sources; // Do a first pass to group together classes that are // applicable to multiple event sources. for(const auto &it : m_sources) { if(source != "" && source != it.name) { continue; } for(const auto &fld_class : it.filter_factory->get_fields()) { fieldclass_event_sources[fieldclass_key(fld_class)].insert(it.name); } } // The set of field classes already printed. Used to avoid // printing field classes multiple times for different sources std::set seen_fieldclasses; // In the second pass, actually print info, skipping duplicate // field classes and also printing info on supported sources. for(const auto &it : m_sources) { if(source != "" && source != it.name) { continue; } for(auto &fld_class : it.filter_factory->get_fields()) { std::string key = fieldclass_key(fld_class); if(seen_fieldclasses.find(key) != seen_fieldclasses.end()) { continue; } seen_fieldclasses.insert(key); if(names_only) { for(auto &field : fld_class.fields) { if(field.is_skippable() || field.is_deprecated()) { continue; } printf("%s\n", field.name.c_str()); } } else if(markdown) { printf("%s\n", fld_class.as_markdown(fieldclass_event_sources[fieldclass_key(fld_class)]) .c_str()); } else { printf("%s\n", fld_class .as_string(verbose, fieldclass_event_sources[fieldclass_key(fld_class)]) .c_str()); } } } } std::unique_ptr falco_engine::load_rules(const std::string &rules_content, const std::string &name) { rule_loader::configuration cfg(rules_content, m_sources, name); cfg.extra_output_format = m_extra_output_format; cfg.extra_output_fields = m_extra_output_fields; // read rules YAML file and collect its definitions if(m_rule_reader->read(cfg, *m_rule_collector, m_rule_schema)) { // compile the definitions (resolve macro/list refs, exceptions, ...) m_last_compile_output = m_rule_compiler->new_compile_output(); m_rule_compiler->compile(cfg, *m_rule_collector, *m_last_compile_output); if(!cfg.res->successful()) { return std::move(cfg.res); } // clear the rules known by the engine and each ruleset m_rules.clear(); for(auto &src : m_sources) // add rules to each ruleset { src.ruleset = create_ruleset(src.ruleset_factory); src.ruleset->add_compile_output(*m_last_compile_output, m_min_priority, src.name); } // add rules to the engine and the rulesets for(const auto &rule : m_last_compile_output->rules) { auto info = m_rule_collector->rules().at(rule.name); if(!info) { // this is just defensive, it should never happen throw falco_exception("can't find internal rule info at name: " + name); } auto source = find_source(rule.source); auto rule_id = m_rules.insert(rule, rule.name); if(rule_id != rule.id) { throw falco_exception("Incompatible ID for rule: " + rule.name + " | compiled ID: " + std::to_string(rule.id) + " | stats_mgr ID: " + std::to_string(rule_id)); } // By default rules are enabled/disabled for the default ruleset // skip the rule if below the minimum priority if(rule.priority > m_min_priority) { continue; } if(info->enabled) { source->ruleset->enable(rule.name, filter_ruleset::match_type::exact, m_default_ruleset_id); } else { source->ruleset->disable(rule.name, filter_ruleset::match_type::exact, m_default_ruleset_id); } } } if(cfg.res->successful()) { m_rule_stats_manager.clear(); for(const auto &r : m_rules) { m_rule_stats_manager.on_rule_loaded(r); } } return std::move(cfg.res); } void falco_engine::enable_rule(const std::string &substring, bool enabled, const std::string &ruleset) { uint16_t ruleset_id = find_ruleset_id(ruleset); enable_rule(substring, enabled, ruleset_id); } void falco_engine::enable_rule(const std::string &substring, bool enabled, const uint16_t ruleset_id) { for(const auto &it : m_sources) { if(enabled) { it.ruleset->enable(substring, filter_ruleset::match_type::substring, ruleset_id); } else { it.ruleset->disable(substring, filter_ruleset::match_type::substring, ruleset_id); } } } void falco_engine::enable_rule_exact(const std::string &rule_name, bool enabled, const std::string &ruleset) { uint16_t ruleset_id = find_ruleset_id(ruleset); enable_rule_exact(rule_name, enabled, ruleset_id); } void falco_engine::enable_rule_exact(const std::string &rule_name, bool enabled, const uint16_t ruleset_id) { for(const auto &it : m_sources) { if(enabled) { it.ruleset->enable(rule_name, filter_ruleset::match_type::exact, ruleset_id); } else { it.ruleset->disable(rule_name, filter_ruleset::match_type::exact, ruleset_id); } } } void falco_engine::enable_rule_wildcard(const std::string &rule_name, bool enabled, const std::string &ruleset) { uint16_t ruleset_id = find_ruleset_id(ruleset); enable_rule_wildcard(rule_name, enabled, ruleset_id); } void falco_engine::enable_rule_wildcard(const std::string &rule_name, bool enabled, const uint16_t ruleset_id) { for(const auto &it : m_sources) { if(enabled) { it.ruleset->enable(rule_name, filter_ruleset::match_type::wildcard, ruleset_id); } else { it.ruleset->disable(rule_name, filter_ruleset::match_type::wildcard, ruleset_id); } } } void falco_engine::enable_rule_by_tag(const std::set &tags, bool enabled, const std::string &ruleset) { uint16_t ruleset_id = find_ruleset_id(ruleset); enable_rule_by_tag(tags, enabled, ruleset_id); } void falco_engine::enable_rule_by_tag(const std::set &tags, bool enabled, const uint16_t ruleset_id) { for(const auto &it : m_sources) { if(enabled) { it.ruleset->enable_tags(tags, ruleset_id); } else { it.ruleset->disable_tags(tags, ruleset_id); } } } void falco_engine::set_min_priority(falco_common::priority_type priority) { m_min_priority = priority; } uint16_t falco_engine::find_ruleset_id(const std::string &ruleset) { auto it = m_known_rulesets.lower_bound(ruleset); if(it == m_known_rulesets.end() || it->first != ruleset) { it = m_known_rulesets.emplace_hint(it, std::make_pair(ruleset, m_next_ruleset_id++)); } return it->second; } uint64_t falco_engine::num_rules_for_ruleset(const std::string &ruleset) { uint16_t ruleset_id = find_ruleset_id(ruleset); uint64_t ret = 0; for(const auto &src : m_sources) { ret += src.ruleset->enabled_count(ruleset_id); } return ret; } void falco_engine::evttypes_for_ruleset(const std::string &source, std::set &evttypes, const std::string &ruleset) { find_source(source)->ruleset->enabled_evttypes(evttypes, find_ruleset_id(ruleset)); } libsinsp::events::set falco_engine::sc_codes_for_ruleset(const std::string &source, const std::string &ruleset) { return find_source(source)->ruleset->enabled_sc_codes(find_ruleset_id(ruleset)); } libsinsp::events::set falco_engine::event_codes_for_ruleset( const std::string &source, const std::string &ruleset) { return find_source(source)->ruleset->enabled_event_codes(find_ruleset_id(ruleset)); } std::shared_ptr falco_engine::create_formatter( const std::string &source, const std::string &output) const { return find_source(source)->formatter_factory->create_formatter(output); } std::unique_ptr> falco_engine::process_event( std::size_t source_idx, sinsp_evt *ev, uint16_t ruleset_id, falco_common::rule_matching strategy) { // note: there are no thread-safety guarantees on the filter_ruleset::run() // method, but the thread-safety assumptions of falco_engine::process_event() // imply that concurrent invokers use different and non-switchable values of // source_idx, which means that at any time each filter_ruleset will only // be accessed by a single thread. const falco_source *source = find_source(source_idx); if(should_drop_evt() || !source) { return nullptr; } switch(strategy) { case falco_common::rule_matching::ALL: if(source->m_rules.size() > 0) { source->m_rules.clear(); } if(!source->ruleset->run(ev, source->m_rules, ruleset_id)) { return nullptr; } break; case falco_common::rule_matching::FIRST: if(source->m_rules.size() != 1) { source->m_rules.resize(1); } if(!source->ruleset->run(ev, source->m_rules[0], ruleset_id)) { return nullptr; } break; } auto res = std::make_unique>(); for(const auto &rule : source->m_rules) { rule_result rule_result; rule_result.evt = ev; rule_result.rule = rule.name; rule_result.source = rule.source; rule_result.format = rule.output; rule_result.priority_num = rule.priority; rule_result.tags = rule.tags; rule_result.exception_fields = rule.exception_fields; rule_result.extra_output_fields = rule.extra_output_fields; m_rule_stats_manager.on_event(rule); res->push_back(rule_result); } return res; } std::unique_ptr> falco_engine::process_event( std::size_t source_idx, sinsp_evt *ev, falco_common::rule_matching strategy) { return process_event(source_idx, ev, m_default_ruleset_id, strategy); } std::size_t falco_engine::add_source( const std::string &source, std::shared_ptr filter_factory, std::shared_ptr formatter_factory) { // evttype_index_ruleset is the default ruleset implementation size_t idx = add_source(source, filter_factory, formatter_factory, std::make_shared(filter_factory)); if(source == falco_common::syscall_source) { m_syscall_source_idx = idx; } return idx; } std::size_t falco_engine::add_source(const std::string &source, std::shared_ptr filter_factory, std::shared_ptr formatter_factory, std::shared_ptr ruleset_factory) { falco_source src; src.name = source; src.filter_factory = filter_factory; src.formatter_factory = formatter_factory; src.ruleset_factory = ruleset_factory; src.ruleset = create_ruleset(src.ruleset_factory); return m_sources.insert(src, source); } template inline nlohmann::json sequence_to_json_array(const T &seq) { nlohmann::json ret = nlohmann::json::array(); for(const auto &v : seq) { ret.push_back(v); } return ret; } nlohmann::json falco_engine::describe_rule( std::string *rule_name, const std::vector> &plugins) const { // use previously-loaded collector definitions and the compiled // output of rules, macros, and lists. if(m_last_compile_output == nullptr) { throw falco_exception("rules must be loaded before describing them"); } // use collected and compiled info to print a json output nlohmann::json output; if(!rule_name) { // Store required engine version auto required_engine_version = m_rule_collector->required_engine_version(); output["required_engine_version"] = required_engine_version.version.as_string(); // Store required plugin versions nlohmann::json plugin_versions = nlohmann::json::array(); auto required_plugin_versions = m_rule_collector->required_plugin_versions(); for(const auto &req : required_plugin_versions) { nlohmann::json r; r["name"] = req.at(0).name; r["version"] = req.at(0).version; nlohmann::json alternatives = nlohmann::json::array(); for(size_t i = 1; i < req.size(); i++) { nlohmann::json alternative; alternative["name"] = req[i].name; alternative["version"] = req[i].version; alternatives.push_back(std::move(alternative)); } r["alternatives"] = std::move(alternatives); plugin_versions.push_back(std::move(r)); } output["required_plugin_versions"] = std::move(plugin_versions); // Store information about rules nlohmann::json rules_array = nlohmann::json::array(); for(const auto &rule : m_last_compile_output->rules) { auto info = m_rule_collector->rules().at(rule.name); nlohmann::json details; get_json_details(details, rule, *info, plugins); rules_array.push_back(std::move(details)); } output["rules"] = std::move(rules_array); // Store information about macros nlohmann::json macros_array = nlohmann::json::array(); for(const auto ¯o : m_last_compile_output->macros) { auto info = m_rule_collector->macros().at(macro.name); nlohmann::json details; get_json_details(details, macro, *info, plugins); macros_array.push_back(std::move(details)); } output["macros"] = std::move(macros_array); // Store information about lists nlohmann::json lists_array = nlohmann::json::array(); for(const auto &list : m_last_compile_output->lists) { auto info = m_rule_collector->lists().at(list.name); nlohmann::json details; get_json_details(details, list, *info, plugins); lists_array.push_back(std::move(details)); } output["lists"] = std::move(lists_array); } else { // build json information for just the specified rule auto ri = m_rule_collector->rules().at(*rule_name); if(ri == nullptr || ri->unknown_source) { throw falco_exception("Rule \"" + *rule_name + "\" is not loaded"); } auto rule = m_rules.at(ri->name); nlohmann::json details; get_json_details(details, *rule, *ri, plugins); nlohmann::json rules_array = nlohmann::json::array(); rules_array.push_back(std::move(details)); output["rules"] = std::move(rules_array); } return output; } void falco_engine::get_json_details( nlohmann::json &out, const falco_rule &r, const rule_loader::rule_info &info, const std::vector> &plugins) const { nlohmann::json rule_info; // Fill general rule information rule_info["name"] = r.name; rule_info["condition"] = info.cond; rule_info["priority"] = format_priority(r.priority, false); rule_info["output"] = info.output; rule_info["description"] = r.description; rule_info["enabled"] = info.enabled; rule_info["source"] = r.source; rule_info["tags"] = sequence_to_json_array(info.tags); out["info"] = std::move(rule_info); // Parse rule condition and build the non-compiled AST // Assumption: no error because rules have already been loaded. auto ast = libsinsp::filter::parser(info.cond).parse(); // get details related to the condition's filter filter_details details; filter_details compiled_details; nlohmann::json json_details; for(const auto &m : m_rule_collector->macros()) { details.known_macros.insert(m.name); compiled_details.known_macros.insert(m.name); } for(const auto &l : m_rule_collector->lists()) { details.known_lists.insert(l.name); compiled_details.known_lists.insert(l.name); } filter_details_resolver().run(ast.get(), details); filter_details_resolver().run(r.condition.get(), compiled_details); out["details"]["macros"] = sequence_to_json_array(details.macros); out["details"]["lists"] = sequence_to_json_array(details.lists); out["details"]["condition_operators"] = sequence_to_json_array(compiled_details.operators); out["details"]["condition_fields"] = sequence_to_json_array(compiled_details.fields); // Get extra requested fields std::vector out_fields; for(auto const &f : r.extra_output_fields) { // add all the field keys out_fields.emplace_back(f.second.first); if(!f.second.second) // formatted field { out["details"]["extra_output_formatted_fields"][f.first] = f.second.first; } } // Get fields from output string auto fmt = create_formatter(r.source, r.output); fmt->get_field_names(out_fields); out["details"]["output_fields"] = sequence_to_json_array(out_fields); // Get fields from exceptions out["details"]["exception_fields"] = sequence_to_json_array(r.exception_fields); // Get names and operators from exceptions std::unordered_set exception_names; std::unordered_set exception_operators; for(const auto &e : info.exceptions) { exception_names.insert(e.name); if(e.comps.is_list) { for(const auto &c : e.comps.items) { if(c.is_list) { // considering max two levels of lists for(const auto &i : c.items) { exception_operators.insert(i.item); } } else { exception_operators.insert(c.item); } } } else { exception_operators.insert(e.comps.item); } } out["details"]["exception_names"] = sequence_to_json_array(exception_names); out["details"]["exception_operators"] = sequence_to_json_array(exception_operators); // Store event types nlohmann::json events; get_json_evt_types(events, info.source, r.condition.get()); out["details"]["events"] = std::move(events); // Store compiled condition and output out["details"]["condition_compiled"] = libsinsp::filter::ast::as_string(r.condition.get()); out["details"]["output_compiled"] = r.output; // Compute the plugins that are actually used by this rule. This is involves: // - The rule's event source, that can be implemented by a plugin // - The fields used in the rule's condition, output, and exceptions // - The evt types used in the rule's condition checks, that can potentially // match plugin-provided async events nlohmann::json used_plugins; // note: making a union of conditions's and output's fields // note: the condition's AST accounts for all the resolved refs and exceptions compiled_details.fields.insert(out_fields.begin(), out_fields.end()); get_json_used_plugins(used_plugins, info.source, compiled_details.evtnames, compiled_details.fields, plugins); out["details"]["plugins"] = std::move(used_plugins); } void falco_engine::get_json_details( nlohmann::json &out, const falco_macro ¯o, const rule_loader::macro_info &info, const std::vector> &plugins) const { nlohmann::json macro_info; macro_info["name"] = macro.name; macro_info["condition"] = info.cond; out["info"] = std::move(macro_info); // Parse the macro condition and build the non-compiled AST // Assumption: no exception because rules have already been loaded. auto ast = libsinsp::filter::parser(info.cond).parse(); // get details related to the condition's filter filter_details details; filter_details compiled_details; nlohmann::json json_details; for(const auto &m : m_rule_collector->macros()) { details.known_macros.insert(m.name); compiled_details.known_macros.insert(m.name); } for(const auto &l : m_rule_collector->lists()) { details.known_lists.insert(l.name); compiled_details.known_lists.insert(l.name); } filter_details_resolver().run(ast.get(), details); filter_details_resolver().run(macro.condition.get(), compiled_details); out["details"]["used"] = macro.used; out["details"]["macros"] = sequence_to_json_array(details.macros); out["details"]["lists"] = sequence_to_json_array(details.lists); out["details"]["condition_operators"] = sequence_to_json_array(compiled_details.operators); out["details"]["condition_fields"] = sequence_to_json_array(compiled_details.fields); // Store event types nlohmann::json events; get_json_evt_types(events, "", macro.condition.get()); out["details"]["events"] = std::move(events); // Store compiled condition out["details"]["condition_compiled"] = libsinsp::filter::ast::as_string(macro.condition.get()); // Compute the plugins that are actually used by this macro. // Note: macros have no specific source, we need to set an empty list of used // plugins because we can't be certain about their actual usage. For example, // if a macro uses a plugin's field, we can't be sure which plugin actually // is used until we resolve the macro ref in a rule providing a source for // disambiguation. out["details"]["plugins"] = nlohmann::json::array(); } void falco_engine::get_json_details( nlohmann::json &out, const falco_list &l, const rule_loader::list_info &info, const std::vector> &plugins) const { nlohmann::json list_info; list_info["name"] = l.name; // note: the syntactic definitions still has the list refs unresolved nlohmann::json items = nlohmann::json::array(); std::unordered_set lists; for(const auto &i : info.items) { // if an item is present in the syntactic def of a list, but not // on the compiled_items of the same list, then we can assume it // being a resolved list ref if(std::find(l.items.begin(), l.items.end(), i) == l.items.end()) { lists.insert(i); continue; } items.push_back(std::move(i)); } list_info["items"] = std::move(items); out["info"] = std::move(list_info); out["details"]["used"] = l.used; out["details"]["lists"] = sequence_to_json_array(lists); out["details"]["items_compiled"] = sequence_to_json_array(l.items); out["details"]["plugins"] = nlohmann::json::array(); // always empty } void falco_engine::get_json_evt_types(nlohmann::json &out, const std::string &source, libsinsp::filter::ast::expr *ast) const { // note: this duplicates part of the logic of evttype_index_ruleset, // not good but it's our best option for now if(source.empty() || source == falco_common::syscall_source) { auto evtcodes = libsinsp::filter::ast::ppm_event_codes(ast); evtcodes.insert(ppm_event_code::PPME_ASYNCEVENT_E); auto syscodes = libsinsp::filter::ast::ppm_sc_codes(ast); auto syscodes_to_evt_names = libsinsp::events::sc_set_to_event_names(syscodes); auto evtcodes_to_evt_names = libsinsp::events::event_set_to_names(evtcodes, false); out = sequence_to_json_array( unordered_set_union(syscodes_to_evt_names, evtcodes_to_evt_names)); } else { out = sequence_to_json_array(libsinsp::events::event_set_to_names( {ppm_event_code::PPME_PLUGINEVENT_E, ppm_event_code::PPME_ASYNCEVENT_E})); } } void falco_engine::get_json_used_plugins( nlohmann::json &out, const std::string &source, const std::unordered_set &evtnames, const std::unordered_set &fields, const std::vector> &plugins) const { // note: condition and output fields may have an argument, so // we need to isolate the field names std::unordered_set fieldnames; for(const auto &f : fields) { auto argpos = f.find('['); if(argpos != std::string::npos) { fieldnames.insert(f.substr(0, argpos)); } else { fieldnames.insert(f); } } std::unordered_set used_plugins; for(const auto &p : plugins) { bool used = false; if(p->caps() & CAP_SOURCING) { // The rule's source is implemented by a plugin with event // sourcing capability. // Note: if Falco loads two plugins implementing the same source, // they will both be included in the list. if(!used && p->event_source() == source) { used_plugins.insert(p->name()); used = true; } } if(!used && p->caps() & CAP_EXTRACTION) { // The rule uses a field implemented by a plugin with field // extraction capability that is compatible with the rule's source. // Note: here we're assuming that Falco will prevent loading // plugins implementing fields with the same name for the same // event source (implemented in init_inspectors app action). if(sinsp_plugin::is_source_compatible(p->extract_event_sources(), source)) { for(const auto &f : p->fields()) { if(!used && fieldnames.find(f.m_name) != fieldnames.end()) { used_plugins.insert(p->name()); used = true; break; } } } } if(!used && p->caps() & CAP_ASYNC) { // The rule matches an event type implemented by a plugin with // async events capability that is compatible with the rule's source. // Note: if Falco loads two plugins implementing async events with // the same name, they will both be included in the list. if(sinsp_plugin::is_source_compatible(p->async_event_sources(), source)) { for(const auto &n : p->async_event_names()) { if(!used && evtnames.find(n) != evtnames.end()) { used_plugins.insert(p->name()); used = true; break; } } } } } out = sequence_to_json_array(used_plugins); } void falco_engine::print_stats() const { std::string out; m_rule_stats_manager.format(m_rules, out); // todo(jasondellaluce): introduce a logging callback in Falco fprintf(stdout, "%s", out.c_str()); } const stats_manager &falco_engine::get_rule_stats_manager() const { return m_rule_stats_manager; } bool falco_engine::is_source_valid(const std::string &source) const { return m_sources.at(source) != nullptr; } std::shared_ptr falco_engine::filter_factory_for_source( const std::string &source) { return find_source(source)->filter_factory; } std::shared_ptr falco_engine::filter_factory_for_source( std::size_t source_idx) { return find_source(source_idx)->filter_factory; } std::shared_ptr falco_engine::formatter_factory_for_source( const std::string &source) { return find_source(source)->formatter_factory; } std::shared_ptr falco_engine::formatter_factory_for_source( std::size_t source_idx) { return find_source(source_idx)->formatter_factory; } std::shared_ptr falco_engine::ruleset_factory_for_source( const std::string &source) { return find_source(source)->ruleset_factory; } std::shared_ptr falco_engine::ruleset_factory_for_source( std::size_t source_idx) { return find_source(source_idx)->ruleset_factory; } std::shared_ptr falco_engine::ruleset_for_source(const std::string &source_name) { const falco_source *source = find_source(source_name); return source->ruleset; } std::shared_ptr falco_engine::ruleset_for_source(std::size_t source_idx) { const falco_source *source = find_source(source_idx); return source->ruleset; } static bool check_plugin_requirement_alternatives( const std::vector &plugins, const rule_loader::plugin_version_info::requirement_alternatives &alternatives, std::string &err) { for(const auto &req : alternatives) { for(const auto &plugin : plugins) { if(req.name == plugin.name) { sinsp_version req_version(req.version); sinsp_version plugin_version(plugin.version); if(!plugin_version.is_valid()) { err = "Plugin '" + plugin.name + "' has invalid version string '" + plugin.version + "'"; return false; } if(!plugin_version.compatible_with(req_version)) { err = "Plugin '" + plugin.name + "' version '" + plugin.version + "' is not compatible with required plugin version '" + req.version + "'"; return false; } return true; } } } return false; } bool falco_engine::check_plugin_requirements(const std::vector &plugins, std::string &err) const { err = ""; for(const auto &alternatives : m_rule_collector->required_plugin_versions()) { if(!check_plugin_requirement_alternatives(plugins, alternatives, err)) { if(err.empty()) { for(const auto &req : alternatives) { err += err.empty() ? "" : ", "; err += req.name + " (>= " + req.version + ")"; } err = "Plugin requirement not satisfied, must load one of: " + err; } return false; } } return true; } std::shared_ptr falco_engine::create_ruleset( std::shared_ptr &ruleset_factory) { auto ret = ruleset_factory->new_ruleset(); ret->set_engine_state(m_engine_state); return ret; } void falco_engine::fill_engine_state_funcs(filter_ruleset::engine_state_funcs &engine_state) { engine_state.get_ruleset = [this](const std::string &source_name, std::shared_ptr &ruleset) -> bool { const falco_source *src = m_sources.at(source_name); if(src == nullptr) { return false; } ruleset = src->ruleset; return true; }; }; void falco_engine::complete_rule_loading() const { for(const auto &src : m_sources) { src.ruleset->on_loading_complete(); } } void falco_engine::set_sampling_ratio(uint32_t sampling_ratio) { m_sampling_ratio = sampling_ratio; } void falco_engine::set_sampling_multiplier(double sampling_multiplier) { m_sampling_multiplier = sampling_multiplier; } void falco_engine::add_extra_output_format(const std::string &format, const std::string &source, const std::set &tags, const std::string &rule) { m_extra_output_format.push_back({format, source, tags, rule}); } void falco_engine::add_extra_output_formatted_field(const std::string &key, const std::string &format, const std::string &source, const std::set &tags, const std::string &rule) { m_extra_output_fields.push_back({key, format, source, tags, rule, false}); } void falco_engine::add_extra_output_raw_field(const std::string &key, const std::string &source, const std::set &tags, const std::string &rule) { std::string format = "%" + key; m_extra_output_fields.push_back({key, format, source, tags, rule, true}); } inline bool falco_engine::should_drop_evt() const { if(m_sampling_multiplier == 0) { return false; } if(m_sampling_ratio == 1) { return false; } double coin = (random() * (1.0 / RAND_MAX)); return (coin >= (1.0 / (m_sampling_multiplier * m_sampling_ratio))); }