mirror of
https://github.com/falcosecurity/falco.git
synced 2025-09-28 13:47:50 +00:00
Signed-off-by: Federico Di Pierro <nierro92@gmail.com> Co-authored-by: Leonardo Grasso <me@leonardograsso.com>
496 lines
12 KiB
C++
496 lines
12 KiB
C++
/*
|
|
Copyright (C) 2019 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 <sstream>
|
|
|
|
#include "rules.h"
|
|
|
|
extern "C" {
|
|
#include "lua.h"
|
|
#include "lualib.h"
|
|
#include "lauxlib.h"
|
|
}
|
|
|
|
#include "falco_engine.h"
|
|
#include "banned.h" // This raises a compilation error when certain functions are used
|
|
|
|
const static struct luaL_Reg ll_falco_rules[] =
|
|
{
|
|
{"clear_filters", &falco_rules::clear_filters},
|
|
{"create_lua_parser", &falco_rules::create_lua_parser},
|
|
{"add_filter", &falco_rules::add_filter},
|
|
{"enable_rule", &falco_rules::enable_rule},
|
|
{"engine_version", &falco_rules::engine_version},
|
|
{"is_source_valid", &falco_rules::is_source_valid},
|
|
{"is_format_valid", &falco_rules::is_format_valid},
|
|
{"is_defined_field", &falco_rules::is_defined_field},
|
|
{NULL, NULL}};
|
|
|
|
falco_rules::falco_rules(falco_engine *engine,
|
|
lua_State *ls)
|
|
: m_engine(engine),
|
|
m_ls(ls)
|
|
{
|
|
}
|
|
|
|
void falco_rules::add_filter_factory(const std::string &source,
|
|
std::shared_ptr<gen_event_filter_factory> factory)
|
|
{
|
|
m_filter_factories[source] = factory;
|
|
}
|
|
|
|
void falco_rules::init(lua_State *ls)
|
|
{
|
|
luaL_openlib(ls, "falco_rules", ll_falco_rules, 0);
|
|
lua_parser::register_callbacks(ls, "filter");
|
|
}
|
|
|
|
int falco_rules::clear_filters(lua_State *ls)
|
|
{
|
|
if (! lua_islightuserdata(ls, -1))
|
|
{
|
|
lua_pushstring(ls, "Invalid arguments passed to clear_filters()");
|
|
lua_error(ls);
|
|
}
|
|
|
|
falco_rules *rules = (falco_rules *) lua_topointer(ls, -1);
|
|
rules->clear_filters();
|
|
|
|
return 0;
|
|
}
|
|
|
|
void falco_rules::clear_filters()
|
|
{
|
|
m_engine->clear_filters();
|
|
}
|
|
|
|
int falco_rules::create_lua_parser(lua_State *ls)
|
|
{
|
|
if (! lua_islightuserdata(ls, -2) ||
|
|
! lua_isstring(ls, -1))
|
|
{
|
|
lua_pushstring(ls, "Invalid argument passed to create_lua_parser()");
|
|
lua_error(ls);
|
|
}
|
|
|
|
falco_rules *rules = (falco_rules *) lua_topointer(ls, -2);
|
|
std::string source = lua_tostring(ls, -1);
|
|
|
|
std::string errstr;
|
|
lua_parser *lp = rules->create_lua_parser(source, errstr);
|
|
|
|
if(lp == NULL) {
|
|
lua_pushstring(ls, errstr.c_str());
|
|
lua_error(ls);
|
|
}
|
|
|
|
lua_pushlightuserdata(ls, lp);
|
|
return 1;
|
|
}
|
|
|
|
lua_parser *falco_rules::create_lua_parser(std::string &source, std::string &errstr)
|
|
{
|
|
auto it = m_filter_factories.find(source);
|
|
|
|
if(it == m_filter_factories.end())
|
|
{
|
|
errstr = string("Unknown event source ") + source;
|
|
return NULL;
|
|
}
|
|
|
|
lua_parser *lp = new lua_parser(it->second);
|
|
|
|
return lp;
|
|
}
|
|
|
|
int falco_rules::add_filter(lua_State *ls)
|
|
{
|
|
if (! lua_islightuserdata(ls, -5) ||
|
|
! lua_islightuserdata(ls, -4) ||
|
|
! lua_isstring(ls, -3) ||
|
|
! lua_isstring(ls, -2) ||
|
|
! lua_istable(ls, -1))
|
|
{
|
|
lua_pushstring(ls, "Invalid arguments passed to add_filter()");
|
|
lua_error(ls);
|
|
}
|
|
|
|
falco_rules *rules = (falco_rules *) lua_topointer(ls, -5);
|
|
lua_parser *lp = (lua_parser *) lua_topointer(ls, -4);
|
|
std::string rule = lua_tostring(ls, -3);
|
|
std::string source = lua_tostring(ls, -2);
|
|
|
|
set<string> tags;
|
|
|
|
lua_pushnil(ls); /* first key */
|
|
while (lua_next(ls, -2) != 0) {
|
|
// key is at index -2, value is at index
|
|
// -1. We want the values.
|
|
tags.insert(lua_tostring(ls, -1));
|
|
|
|
// Remove value, keep key for next iteration
|
|
lua_pop(ls, 1);
|
|
}
|
|
|
|
size_t num_evttypes = lp->filter()->evttypes().size();
|
|
|
|
try {
|
|
rules->add_filter(lp->filter(), rule, source, tags);
|
|
} catch (exception &e) {
|
|
std::string errstr = string("Could not add rule to falco engine: ") + e.what();
|
|
lua_pushstring(ls, errstr.c_str());
|
|
lua_error(ls);
|
|
}
|
|
|
|
delete lp;
|
|
|
|
lua_pushnumber(ls, num_evttypes);
|
|
return 1;
|
|
}
|
|
|
|
void falco_rules::add_filter(std::shared_ptr<gen_event_filter> filter, string &rule, string &source, set<string> &tags)
|
|
{
|
|
m_engine->add_filter(filter, rule, source, tags);
|
|
}
|
|
|
|
int falco_rules::enable_rule(lua_State *ls)
|
|
{
|
|
if (! lua_islightuserdata(ls, -3) ||
|
|
! lua_isstring(ls, -2) ||
|
|
! lua_isnumber(ls, -1))
|
|
{
|
|
lua_pushstring(ls, "Invalid arguments passed to enable_rule()");
|
|
lua_error(ls);
|
|
}
|
|
|
|
falco_rules *rules = (falco_rules *) lua_topointer(ls, -3);
|
|
const char *rulec = lua_tostring(ls, -2);
|
|
std::string rule = rulec;
|
|
bool enabled = (lua_tonumber(ls, -1) ? true : false);
|
|
|
|
rules->enable_rule(rule, enabled);
|
|
|
|
return 0;
|
|
}
|
|
|
|
void falco_rules::enable_rule(string &rule, bool enabled)
|
|
{
|
|
m_engine->enable_rule(rule, enabled);
|
|
}
|
|
|
|
int falco_rules::engine_version(lua_State *ls)
|
|
{
|
|
if (! lua_islightuserdata(ls, -1))
|
|
{
|
|
lua_pushstring(ls, "Invalid arguments passed to engine_version()");
|
|
lua_error(ls);
|
|
}
|
|
|
|
falco_rules *rules = (falco_rules *) lua_topointer(ls, -1);
|
|
|
|
lua_pushnumber(ls, rules->m_engine->engine_version());
|
|
|
|
return 1;
|
|
}
|
|
|
|
bool falco_rules::is_source_valid(const std::string &source)
|
|
{
|
|
return m_engine->is_source_valid(source);
|
|
}
|
|
|
|
int falco_rules::is_source_valid(lua_State *ls)
|
|
{
|
|
if (! lua_islightuserdata(ls, -2) ||
|
|
! lua_isstring(ls, -1))
|
|
{
|
|
lua_pushstring(ls, "Invalid arguments passed to is_source_valid");
|
|
lua_error(ls);
|
|
}
|
|
|
|
falco_rules *rules = (falco_rules *) lua_topointer(ls, -2);
|
|
string source = luaL_checkstring(ls, -1);
|
|
|
|
bool ret = rules->is_source_valid(source);
|
|
|
|
lua_pushboolean(ls, (ret ? 1 : 0));
|
|
|
|
return 1;
|
|
}
|
|
|
|
int falco_rules::is_format_valid(lua_State *ls)
|
|
{
|
|
if (! lua_islightuserdata(ls, -3) ||
|
|
! lua_isstring(ls, -2) ||
|
|
! lua_isstring(ls, -1))
|
|
{
|
|
lua_pushstring(ls, "Invalid arguments passed to is_format_valid");
|
|
lua_error(ls);
|
|
}
|
|
|
|
falco_rules *rules = (falco_rules *) lua_topointer(ls, -3);
|
|
string source = luaL_checkstring(ls, -2);
|
|
string format = luaL_checkstring(ls, -1);
|
|
string errstr;
|
|
|
|
bool ret = rules->is_format_valid(source, format, errstr);
|
|
|
|
if (!ret)
|
|
{
|
|
lua_pushstring(ls, errstr.c_str());
|
|
}
|
|
else
|
|
{
|
|
lua_pushnil(ls);
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
bool falco_rules::is_format_valid(const std::string &source, const std::string &format, std::string &errstr)
|
|
{
|
|
bool ret = true;
|
|
|
|
try
|
|
{
|
|
std::shared_ptr<gen_event_formatter> formatter;
|
|
|
|
formatter = m_engine->create_formatter(source, format);
|
|
}
|
|
catch(exception &e)
|
|
{
|
|
std::ostringstream os;
|
|
|
|
os << "Invalid output format '"
|
|
<< format
|
|
<< "': '"
|
|
<< e.what()
|
|
<< "'";
|
|
|
|
errstr = os.str();
|
|
ret = false;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
int falco_rules::is_defined_field(lua_State *ls)
|
|
{
|
|
if (! lua_islightuserdata(ls, -3) ||
|
|
! lua_isstring(ls, -2) ||
|
|
! lua_isstring(ls, -1))
|
|
{
|
|
lua_pushstring(ls, "Invalid arguments passed to is_defined_field");
|
|
lua_error(ls);
|
|
}
|
|
|
|
falco_rules *rules = (falco_rules *) lua_topointer(ls, -3);
|
|
string source = luaL_checkstring(ls, -2);
|
|
string fldname = luaL_checkstring(ls, -1);
|
|
|
|
bool ret = rules->is_defined_field(source, fldname);
|
|
|
|
lua_pushboolean(ls, (ret ? 1 : 0));
|
|
|
|
return 1;
|
|
}
|
|
|
|
bool falco_rules::is_defined_field(const std::string &source, const std::string &fldname)
|
|
{
|
|
auto it = m_filter_factories.find(source);
|
|
|
|
if(it == m_filter_factories.end())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
auto *chk = it->second->new_filtercheck(fldname.c_str());
|
|
|
|
if (chk == NULL)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
delete(chk);
|
|
|
|
return true;
|
|
}
|
|
|
|
static std::list<std::string> get_lua_table_values(lua_State *ls, int idx)
|
|
{
|
|
std::list<std::string> ret;
|
|
|
|
if (lua_isnil(ls, idx)) {
|
|
return ret;
|
|
}
|
|
|
|
lua_pushnil(ls); /* first key */
|
|
while (lua_next(ls, idx-1) != 0) {
|
|
// key is at index -2, value is at index
|
|
// -1. We want the values.
|
|
if (! lua_isstring(ls, -1)) {
|
|
std::string err = "Non-string value in table of strings";
|
|
throw falco_exception(err);
|
|
}
|
|
ret.push_back(string(lua_tostring(ls, -1)));
|
|
|
|
// Remove value, keep key for next iteration
|
|
lua_pop(ls, 1);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void get_lua_table_list_values(lua_State *ls,
|
|
int idx,
|
|
std::map<std::string, std::list<std::string>> &required_plugin_versions)
|
|
{
|
|
if (lua_isnil(ls, idx)) {
|
|
return;
|
|
}
|
|
|
|
lua_pushnil(ls); /* first key */
|
|
while (lua_next(ls, idx-1) != 0) {
|
|
// key is at index -2, table of values is at index -1.
|
|
if (! lua_isstring(ls, -2)) {
|
|
std::string err = "Non-string key in table of strings";
|
|
throw falco_exception(err);
|
|
}
|
|
|
|
std::string key = string(lua_tostring(ls, -2));
|
|
std::list<std::string> vals = get_lua_table_values(ls, -1);
|
|
|
|
if (required_plugin_versions.find(key) == required_plugin_versions.end())
|
|
{
|
|
required_plugin_versions[key] = vals;
|
|
}
|
|
else
|
|
{
|
|
required_plugin_versions[key].insert(required_plugin_versions[key].end(),
|
|
vals.begin(),
|
|
vals.end());
|
|
}
|
|
|
|
// Remove value, keep key for next iteration
|
|
lua_pop(ls, 1);
|
|
}
|
|
}
|
|
|
|
|
|
void falco_rules::load_rules(const string &rules_content,
|
|
bool verbose, bool all_events,
|
|
string &extra, bool replace_container_info,
|
|
falco_common::priority_type min_priority,
|
|
uint64_t &required_engine_version,
|
|
std::map<std::string, std::list<std::string>> &required_plugin_versions)
|
|
{
|
|
lua_getglobal(m_ls, m_lua_load_rules.c_str());
|
|
if(lua_isfunction(m_ls, -1))
|
|
{
|
|
lua_pushstring(m_ls, rules_content.c_str());
|
|
lua_pushlightuserdata(m_ls, this);
|
|
lua_pushboolean(m_ls, (verbose ? 1 : 0));
|
|
lua_pushboolean(m_ls, (all_events ? 1 : 0));
|
|
lua_pushstring(m_ls, extra.c_str());
|
|
lua_pushboolean(m_ls, (replace_container_info ? 1 : 0));
|
|
lua_pushnumber(m_ls, min_priority);
|
|
if(lua_pcall(m_ls, 7, 5, 0) != 0)
|
|
{
|
|
const char* lerr = lua_tostring(m_ls, -1);
|
|
|
|
string err = "Error loading rules: " + string(lerr);
|
|
|
|
throw falco_exception(err);
|
|
}
|
|
|
|
// Returns:
|
|
// Load result: bool
|
|
// required engine version: will be nil when load result is false
|
|
// required_plugin_versions: will be nil when load result is false
|
|
// array of errors
|
|
// array of warnings
|
|
bool successful = lua_toboolean(m_ls, -5);
|
|
required_engine_version = lua_tonumber(m_ls, -4);
|
|
get_lua_table_list_values(m_ls, -3, required_plugin_versions);
|
|
std::list<std::string> errors = get_lua_table_values(m_ls, -2);
|
|
std::list<std::string> warnings = get_lua_table_values(m_ls, -1);
|
|
|
|
// Concatenate errors/warnings
|
|
std::ostringstream os;
|
|
if (errors.size() > 0)
|
|
{
|
|
os << errors.size() << " errors:" << std::endl;
|
|
for(auto err : errors)
|
|
{
|
|
os << err << std::endl;
|
|
}
|
|
}
|
|
|
|
if (warnings.size() > 0)
|
|
{
|
|
os << warnings.size() << " warnings:" << std::endl;
|
|
for(auto warn : warnings)
|
|
{
|
|
os << warn << std::endl;
|
|
}
|
|
}
|
|
|
|
if(!successful)
|
|
{
|
|
throw falco_exception(os.str());
|
|
}
|
|
|
|
if (verbose && os.str() != "") {
|
|
// We don't really have a logging callback
|
|
// from the falco engine, but this would be a
|
|
// good place to use it.
|
|
fprintf(stderr, "When reading rules content: %s", os.str().c_str());
|
|
}
|
|
|
|
lua_pop(m_ls, 4);
|
|
|
|
} else {
|
|
throw falco_exception("No function " + m_lua_load_rules + " found in lua rule module");
|
|
}
|
|
}
|
|
|
|
void falco_rules::describe_rule(std::string *rule)
|
|
{
|
|
lua_getglobal(m_ls, m_lua_describe_rule.c_str());
|
|
if(lua_isfunction(m_ls, -1))
|
|
{
|
|
if (rule == NULL)
|
|
{
|
|
lua_pushnil(m_ls);
|
|
} else {
|
|
lua_pushstring(m_ls, rule->c_str());
|
|
}
|
|
|
|
if(lua_pcall(m_ls, 1, 0, 0) != 0)
|
|
{
|
|
const char* lerr = lua_tostring(m_ls, -1);
|
|
string err = "Could not describe " + (rule == NULL ? "all rules" : "rule " + *rule) + ": " + string(lerr);
|
|
throw falco_exception(err);
|
|
}
|
|
} else {
|
|
throw falco_exception("No function " + m_lua_describe_rule + " found in lua rule module");
|
|
}
|
|
}
|
|
|
|
|
|
falco_rules::~falco_rules()
|
|
{
|
|
}
|