auditd-plugin-clickhouse/auditd-record.cpp
2020-01-27 13:07:59 +03:00

467 lines
16 KiB
C++

/*
* auditd-plugin-clickhouse is an auditd plugin for sending auditd data
* to clickhouse DB.
* Copyright (C) 2019-2020 Aleksei Nikiforov <darktemplar@basealt.ru>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
#include "auditd-record.hpp"
#include "logging.hpp"
#include "utils.hpp"
#include <functional>
#include <set>
#include <clickhouse-cpp/columns/array.h>
#include <clickhouse-cpp/columns/nullable.h>
#include <clickhouse-cpp/columns/numeric.h>
#include <clickhouse-cpp/columns/string.h>
namespace {
std::map<auparse_type_t, std::function<bool(const std::string &)> >& get_audit_type_check_instance()
{
static std::map<auparse_type_t, std::function<bool(const std::string &)> > instance;
return instance;
}
class AuditTypeCheckRegister
{
public:
AuditTypeCheckRegister(auparse_type_t type, const std::function<bool(const std::string &)> &func)
{
get_audit_type_check_instance()[type] = func;
}
};
std::function<bool(const std::string &)> integer_record_type_check_function()
{
return [](const std::string &value) -> bool {
std::set<std::string> allowed_values = {
"integer"
};
return (allowed_values.find(value) != allowed_values.end());
};
}
std::function<bool(const std::string &)> interpreted_string_record_type_check_function()
{
return [](const std::string &value) -> bool {
std::set<std::string> allowed_values = {
"string",
"string_array"
};
return (allowed_values.find(value) != allowed_values.end());
};
}
std::function<bool(const std::string &)> any_record_type_check_function()
{
return [](const std::string &value) -> bool {
return true;
};
}
} // unnamed namespace
#define register_record_type(audit_type, check_function) \
static const AuditTypeCheckRegister audit_type_check_register_##audit_type(audit_type, check_function)
register_record_type(AUPARSE_TYPE_UNCLASSIFIED, any_record_type_check_function());
register_record_type(AUPARSE_TYPE_UID, integer_record_type_check_function());
register_record_type(AUPARSE_TYPE_GID, integer_record_type_check_function());
register_record_type(AUPARSE_TYPE_SYSCALL, integer_record_type_check_function());
register_record_type(AUPARSE_TYPE_ARCH, integer_record_type_check_function());
register_record_type(AUPARSE_TYPE_EXIT, integer_record_type_check_function());
register_record_type(AUPARSE_TYPE_ESCAPED, interpreted_string_record_type_check_function());
register_record_type(AUPARSE_TYPE_PERM, integer_record_type_check_function());
register_record_type(AUPARSE_TYPE_MODE, integer_record_type_check_function());
register_record_type(AUPARSE_TYPE_SOCKADDR, interpreted_string_record_type_check_function());
register_record_type(AUPARSE_TYPE_FLAGS, integer_record_type_check_function());
register_record_type(AUPARSE_TYPE_PROMISC, integer_record_type_check_function());
register_record_type(AUPARSE_TYPE_CAPABILITY, integer_record_type_check_function());
register_record_type(AUPARSE_TYPE_SUCCESS, interpreted_string_record_type_check_function());
register_record_type(AUPARSE_TYPE_A0, interpreted_string_record_type_check_function());
register_record_type(AUPARSE_TYPE_A1, interpreted_string_record_type_check_function());
register_record_type(AUPARSE_TYPE_A2, interpreted_string_record_type_check_function());
register_record_type(AUPARSE_TYPE_A3, interpreted_string_record_type_check_function());
register_record_type(AUPARSE_TYPE_SIGNAL, integer_record_type_check_function());
register_record_type(AUPARSE_TYPE_LIST, integer_record_type_check_function());
register_record_type(AUPARSE_TYPE_TTY_DATA, interpreted_string_record_type_check_function());
register_record_type(AUPARSE_TYPE_SESSION, integer_record_type_check_function());
register_record_type(AUPARSE_TYPE_CAP_BITMAP, integer_record_type_check_function());
register_record_type(AUPARSE_TYPE_NFPROTO, integer_record_type_check_function());
register_record_type(AUPARSE_TYPE_ICMPTYPE, integer_record_type_check_function());
register_record_type(AUPARSE_TYPE_PROTOCOL, integer_record_type_check_function());
register_record_type(AUPARSE_TYPE_ADDR, interpreted_string_record_type_check_function());
register_record_type(AUPARSE_TYPE_PERSONALITY, integer_record_type_check_function());
register_record_type(AUPARSE_TYPE_SECCOMP, integer_record_type_check_function());
register_record_type(AUPARSE_TYPE_OFLAG, integer_record_type_check_function());
register_record_type(AUPARSE_TYPE_MMAP, integer_record_type_check_function());
register_record_type(AUPARSE_TYPE_MODE_SHORT, integer_record_type_check_function());
register_record_type(AUPARSE_TYPE_MAC_LABEL, interpreted_string_record_type_check_function());
register_record_type(AUPARSE_TYPE_PROCTITLE, interpreted_string_record_type_check_function());
register_record_type(AUPARSE_TYPE_HOOK, integer_record_type_check_function());
register_record_type(AUPARSE_TYPE_NETACTION, integer_record_type_check_function());
register_record_type(AUPARSE_TYPE_MACPROTO, integer_record_type_check_function());
register_record_type(AUPARSE_TYPE_IOCTL_REQ, integer_record_type_check_function());
register_record_type(AUPARSE_TYPE_ESCAPED_KEY, interpreted_string_record_type_check_function());
register_record_type(AUPARSE_TYPE_ESCAPED_FILE, interpreted_string_record_type_check_function());
register_record_type(AUPARSE_TYPE_FANOTIFY, interpreted_string_record_type_check_function());
#undef register_record_type
bool check_field_type(auparse_type_t field_type, const std::string &database_type, const std::string &database_field_name)
{
const auto &check_instance = get_audit_type_check_instance();
auto check_type_function = check_instance.find(field_type);
if (check_type_function != check_instance.end())
{
return check_type_function->second(database_type);
}
else
{
Logger::write("Warning: unknown record type for record name \"%s\"\n", database_field_name.c_str());
return interpreted_string_record_type_check_function()(database_type);
}
}
std::string field_type_to_string(auparse_type_t field_type)
{
#define field_type_macro(T) { T, #T }
static const std::map<auparse_type_t, std::string> s_field_type_to_string_map = {
field_type_macro(AUPARSE_TYPE_UNCLASSIFIED),
field_type_macro(AUPARSE_TYPE_UID),
field_type_macro(AUPARSE_TYPE_GID),
field_type_macro(AUPARSE_TYPE_SYSCALL),
field_type_macro(AUPARSE_TYPE_ARCH),
field_type_macro(AUPARSE_TYPE_EXIT),
field_type_macro(AUPARSE_TYPE_ESCAPED),
field_type_macro(AUPARSE_TYPE_PERM),
field_type_macro(AUPARSE_TYPE_MODE),
field_type_macro(AUPARSE_TYPE_SOCKADDR),
field_type_macro(AUPARSE_TYPE_FLAGS),
field_type_macro(AUPARSE_TYPE_PROMISC),
field_type_macro(AUPARSE_TYPE_CAPABILITY),
field_type_macro(AUPARSE_TYPE_SUCCESS),
field_type_macro(AUPARSE_TYPE_A0),
field_type_macro(AUPARSE_TYPE_A1),
field_type_macro(AUPARSE_TYPE_A2),
field_type_macro(AUPARSE_TYPE_A3),
field_type_macro(AUPARSE_TYPE_SIGNAL),
field_type_macro(AUPARSE_TYPE_LIST),
field_type_macro(AUPARSE_TYPE_TTY_DATA),
field_type_macro(AUPARSE_TYPE_SESSION),
field_type_macro(AUPARSE_TYPE_CAP_BITMAP),
field_type_macro(AUPARSE_TYPE_NFPROTO),
field_type_macro(AUPARSE_TYPE_ICMPTYPE),
field_type_macro(AUPARSE_TYPE_PROTOCOL),
field_type_macro(AUPARSE_TYPE_ADDR),
field_type_macro(AUPARSE_TYPE_PERSONALITY),
field_type_macro(AUPARSE_TYPE_SECCOMP),
field_type_macro(AUPARSE_TYPE_OFLAG),
field_type_macro(AUPARSE_TYPE_MMAP),
field_type_macro(AUPARSE_TYPE_MODE_SHORT),
field_type_macro(AUPARSE_TYPE_MAC_LABEL),
field_type_macro(AUPARSE_TYPE_PROCTITLE),
field_type_macro(AUPARSE_TYPE_HOOK),
field_type_macro(AUPARSE_TYPE_NETACTION),
field_type_macro(AUPARSE_TYPE_MACPROTO),
field_type_macro(AUPARSE_TYPE_IOCTL_REQ),
field_type_macro(AUPARSE_TYPE_ESCAPED_KEY),
field_type_macro(AUPARSE_TYPE_ESCAPED_FILE),
field_type_macro(AUPARSE_TYPE_FANOTIFY)
};
#undef field_type_macro
auto iter = s_field_type_to_string_map.find(field_type);
if (iter != s_field_type_to_string_map.end())
{
return iter->second;
}
else
{
Logger::write("Warning: unknown field type for field type id \"%d\"\n", field_type);
return "unknown";
}
}
AbstractRecordField::AbstractRecordField(const std::string &name)
: m_name(name)
{
}
CommonStringRecordField::CommonStringRecordField(const std::string &name)
: AbstractRecordField(name)
{
}
std::vector<AbstractRecordField::Column> CommonStringRecordField::generateColumnsAndNames() const
{
return std::vector<AbstractRecordField::Column> {
Column { m_name, std::make_shared<clickhouse::ColumnNullable>(std::make_shared<clickhouse::ColumnString>(), std::make_shared<clickhouse::ColumnUInt8>()) }
};
}
void CommonStringRecordField::addToColumn(const std::vector<Column> &columns) const
{
if ((columns.size() != 1) || (!columns[0].value))
{
throw std::runtime_error("CommonStringRecordField::addToColumn: invalid columns argument");
}
auto nullable = columns[0].value->As<clickhouse::ColumnNullable>();
if (!nullable)
{
throw std::runtime_error("Invalid column type: not ColumnNullable");
}
auto nested = nullable->Nested();
if ((!nested) || (nested->Type()->GetCode() != clickhouse::Type::String))
{
throw std::runtime_error("Invalid nested column type: not String");
}
auto data_column = std::make_shared<clickhouse::ColumnString>();
auto null_column = std::make_shared<clickhouse::ColumnUInt8>();
if (m_value)
{
data_column->Append(*m_value);
null_column->Append(0);
}
else
{
data_column->Append(std::string());
null_column->Append(1);
}
columns[0].value->Append(std::make_shared<clickhouse::ColumnNullable>(data_column, null_column));
}
std::shared_ptr<AbstractRecordField> StringRecordField::createRecord(const std::string &name)
{
std::shared_ptr<AbstractRecordField> result;
result.reset(new StringRecordField(name));
return result;
}
void StringRecordField::addOrUpdateValue(auparse_state_t *record)
{
if (record)
{
m_value = string_or_null(auparse_get_field_str(record));
}
else
{
m_value = boost::none;
}
}
StringRecordField::StringRecordField(const std::string &name)
: CommonStringRecordField(name)
{
}
AbstractRecordField::Type StringRecordField::getType() const
{
return AbstractRecordField::Type::String;
}
std::shared_ptr<AbstractRecordField> InterpretedStringRecordField::createRecord(const std::string &name)
{
std::shared_ptr<AbstractRecordField> result;
result.reset(new InterpretedStringRecordField(name));
return result;
}
InterpretedStringRecordField::InterpretedStringRecordField(const std::string &name)
: CommonStringRecordField(name)
{
}
void InterpretedStringRecordField::addOrUpdateValue(auparse_state_t *record)
{
if (record)
{
m_value = string_or_null(auparse_interpret_field(record));
}
else
{
m_value = boost::none;
}
}
AbstractRecordField::Type InterpretedStringRecordField::getType() const
{
return AbstractRecordField::Type::InterpretedString;
}
std::shared_ptr<AbstractRecordField> IntegerRecordField::createRecord(const std::string &name)
{
std::shared_ptr<AbstractRecordField> result;
result.reset(new IntegerRecordField(name));
return result;
}
IntegerRecordField::IntegerRecordField(const std::string &name)
: InterpretedStringRecordField(name)
{
}
void IntegerRecordField::addOrUpdateValue(auparse_state_t *record)
{
InterpretedStringRecordField::addOrUpdateValue(record);
if (record)
{
m_int_value = auparse_get_field_int(record);
}
else
{
m_int_value = boost::none;
}
}
std::vector<AbstractRecordField::Column> IntegerRecordField::generateColumnsAndNames() const
{
return std::vector<AbstractRecordField::Column> {
Column { m_name + "_IntValue", std::make_shared<clickhouse::ColumnNullable>(std::make_shared<clickhouse::ColumnInt64>(), std::make_shared<clickhouse::ColumnUInt8>()) },
Column { m_name + "_InterpretedValue", std::make_shared<clickhouse::ColumnNullable>(std::make_shared<clickhouse::ColumnString>(), std::make_shared<clickhouse::ColumnUInt8>()) }
};
}
void IntegerRecordField::addToColumn(const std::vector<Column> &columns) const
{
if ((columns.size() != 2) || (!columns[0].value) || (!columns[1].value))
{
throw std::runtime_error("IntegerRecordField::addToColumn: invalid columns argument");
}
auto nullable = columns[0].value->As<clickhouse::ColumnNullable>();
if (!nullable)
{
throw std::runtime_error("Invalid column type: not ColumnNullable");
}
auto nested = nullable->Nested();
if ((!nested) || (nested->Type()->GetCode() != clickhouse::Type::Int64))
{
throw std::runtime_error("Invalid nested column type: not Int64");
}
auto data_column = std::make_shared<clickhouse::ColumnInt64>();
auto null_column = std::make_shared<clickhouse::ColumnUInt8>();
if (m_int_value)
{
data_column->Append(*m_int_value);
null_column->Append(0);
}
else
{
data_column->Append(0);
null_column->Append(1);
}
std::vector<Column> string_columns;
string_columns.push_back(columns[1]);
columns[0].value->Append(std::make_shared<clickhouse::ColumnNullable>(data_column, null_column));
// now also add string value
InterpretedStringRecordField::addToColumn(string_columns);
}
AbstractRecordField::Type IntegerRecordField::getType() const
{
return AbstractRecordField::Type::Int;
}
std::shared_ptr<AbstractRecordField> InterpretedStringArrayRecordField::createRecord(const std::string &name)
{
std::shared_ptr<AbstractRecordField> result;
result.reset(new InterpretedStringArrayRecordField(name));
return result;
}
void InterpretedStringArrayRecordField::addOrUpdateValue(auparse_state_t *record)
{
if (record)
{
m_names_array.push_back(string_or_null(auparse_get_field_name(record)));
m_values_array.push_back(string_or_null(auparse_interpret_field(record)));
}
}
std::vector<AbstractRecordField::Column> InterpretedStringArrayRecordField::generateColumnsAndNames() const
{
return std::vector<AbstractRecordField::Column> {
Column { m_name + "_Name", std::make_shared<clickhouse::ColumnArray>(std::make_shared<clickhouse::ColumnString>()) },
Column { m_name + "_Value", std::make_shared<clickhouse::ColumnArray>(std::make_shared<clickhouse::ColumnString>()) }
};
}
void InterpretedStringArrayRecordField::addToColumn(const std::vector<Column> &columns) const
{
if ((columns.size() != 2) || (!columns[0].value) || (!columns[1].value))
{
throw std::runtime_error("InterpretedStringArrayRecordField::addToColumn: invalid columns argument");
}
auto array_names = columns[0].value->As<clickhouse::ColumnArray>();
if (!array_names)
{
throw std::runtime_error("Invalid column 0 type: not ColumnArray");
}
auto array_values = columns[1].value->As<clickhouse::ColumnArray>();
if (!array_values)
{
throw std::runtime_error("Invalid column 1 type: not ColumnArray");
}
auto name_column = std::make_shared<clickhouse::ColumnString>();
for (auto iter = m_names_array.begin(); iter != m_names_array.end(); ++iter)
{
name_column->Append(*iter);
}
auto value_column = std::make_shared<clickhouse::ColumnString>();
for (auto iter = m_values_array.begin(); iter != m_values_array.end(); ++iter)
{
value_column->Append(*iter);
}
array_names->AppendAsColumn(name_column);
array_values->AppendAsColumn(value_column);
}
InterpretedStringArrayRecordField::InterpretedStringArrayRecordField(const std::string &name)
: AbstractRecordField(name)
{
}
AbstractRecordField::Type InterpretedStringArrayRecordField::getType() const
{
return AbstractRecordField::Type::InterpretedStringArray;
}