3c09000edc
Update AbstractRecordField interface to match new use-case
321 lines
10 KiB
C++
321 lines
10 KiB
C++
/*
|
|
* auditd-plugin-clickhouse is an auditd plugin for sending auditd data
|
|
* to clickhouse DB.
|
|
* Copyright (C) 2019 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 <clickhouse-cpp/columns/array.h>
|
|
#include <clickhouse-cpp/columns/nullable.h>
|
|
#include <clickhouse-cpp/columns/numeric.h>
|
|
#include <clickhouse-cpp/columns/string.h>
|
|
|
|
#define register_record_type(audit_type, record_type) \
|
|
static const AbstractRecordFieldFactory::AuditRecordFieldRegister<record_type> record_register_##audit_type(audit_type)
|
|
|
|
register_record_type(AUPARSE_TYPE_UNCLASSIFIED, InterpretedStringRecordField);
|
|
register_record_type(AUPARSE_TYPE_UID, IntegerRecordField);
|
|
register_record_type(AUPARSE_TYPE_GID, IntegerRecordField);
|
|
register_record_type(AUPARSE_TYPE_SYSCALL, IntegerRecordField);
|
|
register_record_type(AUPARSE_TYPE_ARCH, InterpretedStringRecordField);
|
|
register_record_type(AUPARSE_TYPE_EXIT, IntegerRecordField);
|
|
register_record_type(AUPARSE_TYPE_ESCAPED, InterpretedStringRecordField);
|
|
register_record_type(AUPARSE_TYPE_PERM, IntegerRecordField);
|
|
register_record_type(AUPARSE_TYPE_MODE, IntegerRecordField);
|
|
register_record_type(AUPARSE_TYPE_SOCKADDR, InterpretedStringRecordField);
|
|
register_record_type(AUPARSE_TYPE_FLAGS, IntegerRecordField);
|
|
register_record_type(AUPARSE_TYPE_PROMISC, IntegerRecordField);
|
|
register_record_type(AUPARSE_TYPE_CAPABILITY, IntegerRecordField);
|
|
register_record_type(AUPARSE_TYPE_SUCCESS, InterpretedStringRecordField);
|
|
register_record_type(AUPARSE_TYPE_A0, InterpretedStringRecordField);
|
|
register_record_type(AUPARSE_TYPE_A1, InterpretedStringRecordField);
|
|
register_record_type(AUPARSE_TYPE_A2, InterpretedStringRecordField);
|
|
register_record_type(AUPARSE_TYPE_A3, InterpretedStringRecordField);
|
|
register_record_type(AUPARSE_TYPE_SIGNAL, IntegerRecordField);
|
|
register_record_type(AUPARSE_TYPE_LIST, IntegerRecordField);
|
|
register_record_type(AUPARSE_TYPE_TTY_DATA, InterpretedStringRecordField);
|
|
register_record_type(AUPARSE_TYPE_SESSION, IntegerRecordField);
|
|
register_record_type(AUPARSE_TYPE_CAP_BITMAP, InterpretedStringRecordField);
|
|
register_record_type(AUPARSE_TYPE_NFPROTO, IntegerRecordField);
|
|
register_record_type(AUPARSE_TYPE_ICMPTYPE, IntegerRecordField);
|
|
register_record_type(AUPARSE_TYPE_PROTOCOL, IntegerRecordField);
|
|
register_record_type(AUPARSE_TYPE_ADDR, InterpretedStringRecordField);
|
|
register_record_type(AUPARSE_TYPE_PERSONALITY, IntegerRecordField);
|
|
register_record_type(AUPARSE_TYPE_SECCOMP, IntegerRecordField);
|
|
register_record_type(AUPARSE_TYPE_OFLAG, IntegerRecordField);
|
|
register_record_type(AUPARSE_TYPE_MMAP, IntegerRecordField);
|
|
register_record_type(AUPARSE_TYPE_MODE_SHORT, IntegerRecordField);
|
|
register_record_type(AUPARSE_TYPE_MAC_LABEL, InterpretedStringRecordField);
|
|
register_record_type(AUPARSE_TYPE_PROCTITLE, InterpretedStringRecordField);
|
|
register_record_type(AUPARSE_TYPE_HOOK, IntegerRecordField);
|
|
register_record_type(AUPARSE_TYPE_NETACTION, IntegerRecordField);
|
|
register_record_type(AUPARSE_TYPE_MACPROTO, IntegerRecordField);
|
|
register_record_type(AUPARSE_TYPE_IOCTL_REQ, IntegerRecordField);
|
|
register_record_type(AUPARSE_TYPE_ESCAPED_KEY, InterpretedStringRecordField);
|
|
register_record_type(AUPARSE_TYPE_ESCAPED_FILE, InterpretedStringRecordField);
|
|
register_record_type(AUPARSE_TYPE_FANOTIFY, InterpretedStringRecordField);
|
|
|
|
#undef register_record_type
|
|
|
|
AbstractRecordFieldFactory& AbstractRecordFieldFactory::instance()
|
|
{
|
|
static AbstractRecordFieldFactory instance;
|
|
return instance;
|
|
}
|
|
|
|
std::shared_ptr<AbstractRecordField> AbstractRecordFieldFactory::createFromAuditRecord(const std::string &field_name, auparse_type_t field_type)
|
|
{
|
|
AbstractRecordFieldFactory &inst = instance();
|
|
|
|
auto record_type_factory = inst.m_factoryMap.find(field_type);
|
|
if ((record_type_factory != inst.m_factoryMap.end()) && (record_type_factory->second))
|
|
{
|
|
return record_type_factory->second(field_name);
|
|
}
|
|
else
|
|
{
|
|
// fallback to string
|
|
fprintf(stderr, "Warning: unknown record type for record name \"%s\"\n", field_name.c_str());
|
|
return InterpretedStringRecordField::createRecord(field_name);
|
|
}
|
|
}
|
|
|
|
AbstractRecordField::AbstractRecordField(const std::string &name)
|
|
: m_name(name)
|
|
{
|
|
}
|
|
|
|
CommonStringRecordField::CommonStringRecordField(const std::string &name)
|
|
: AbstractRecordField(name)
|
|
{
|
|
}
|
|
|
|
void CommonStringRecordField::addToColumn(const std::vector<clickhouse::ColumnRef> &columns) const
|
|
{
|
|
if ((columns.size() != 1) || (!columns[0]))
|
|
{
|
|
throw std::runtime_error("CommonStringRecordField::addToColumn: invalid columns argument");
|
|
}
|
|
|
|
auto nullable = columns[0]->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]->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 = 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 = 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;
|
|
}
|
|
}
|
|
|
|
void IntegerRecordField::addToColumn(const std::vector<clickhouse::ColumnRef> &columns) const
|
|
{
|
|
if ((columns.size() != 2) || (!columns[0]) || (!columns[1]))
|
|
{
|
|
throw std::runtime_error("IntegerRecordField::addToColumn: invalid columns argument");
|
|
}
|
|
|
|
auto nullable = columns[0]->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<clickhouse::ColumnRef> string_columns;
|
|
string_columns.push_back(columns[1]);
|
|
|
|
columns[0]->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_value.push_back(auparse_interpret_field(record));
|
|
}
|
|
}
|
|
|
|
void InterpretedStringArrayRecordField::addToColumn(const std::vector<clickhouse::ColumnRef> &columns) const
|
|
{
|
|
if ((columns.size() != 1) || (!columns[0]))
|
|
{
|
|
throw std::runtime_error("InterpretedStringArrayRecordField::addToColumn: invalid columns argument");
|
|
}
|
|
|
|
auto array = columns[0]->As<clickhouse::ColumnArray>();
|
|
if (!array)
|
|
{
|
|
throw std::runtime_error("Invalid column type: not ColumnArray");
|
|
}
|
|
|
|
auto data_column = std::make_shared<clickhouse::ColumnString>();
|
|
for (auto iter = m_value.begin(); iter != m_value.end(); ++iter)
|
|
{
|
|
data_column->Append(*iter);
|
|
}
|
|
|
|
array->AppendAsColumn(data_column);
|
|
}
|
|
|
|
InterpretedStringArrayRecordField::InterpretedStringArrayRecordField(const std::string &name)
|
|
: AbstractRecordField(name)
|
|
{
|
|
}
|
|
|
|
AbstractRecordField::Type InterpretedStringArrayRecordField::getType() const
|
|
{
|
|
return AbstractRecordField::Type::InterpretedStringArray;
|
|
}
|