auditd-plugin-clickhouse/auditd-record.cpp

249 lines
8.8 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/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 &name, auparse_state_t *record)
{
AbstractRecordFieldFactory &inst = instance();
auparse_type_t record_type = static_cast<auparse_type_t>(auparse_get_field_type(record));
auto record_type_factory = inst.m_factoryMap.find(record_type);
if ((record_type_factory != inst.m_factoryMap.end()) && (record_type_factory->second))
{
return record_type_factory->second(name, record);
}
else
{
// fallback to string
fprintf(stderr, "Warning: unknown record type for record name \"%s\"\n", name.c_str());
return InterpretedStringRecordField::createRecord(name, record);
}
}
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, auparse_state_t *record)
{
std::shared_ptr<AbstractRecordField> result;
result.reset(new StringRecordField(name, record));
return result;
}
StringRecordField::StringRecordField(const std::string &name, auparse_state_t *record)
: CommonStringRecordField(name)
{
if (record)
{
m_value = auparse_get_field_str(record);
}
}
AbstractRecordField::Type StringRecordField::getType() const
{
return AbstractRecordField::Type::String;
}
std::shared_ptr<AbstractRecordField> InterpretedStringRecordField::createRecord(const std::string &name, auparse_state_t *record)
{
std::shared_ptr<AbstractRecordField> result;
result.reset(new InterpretedStringRecordField(name, record));
return result;
}
InterpretedStringRecordField::InterpretedStringRecordField(const std::string &name, auparse_state_t *record)
: CommonStringRecordField(name)
{
if (record)
{
m_value = auparse_interpret_field(record);
}
}
AbstractRecordField::Type InterpretedStringRecordField::getType() const
{
return AbstractRecordField::Type::InterpretedString;
}
std::shared_ptr<AbstractRecordField> IntegerRecordField::createRecord(const std::string &name, auparse_state_t *record)
{
std::shared_ptr<AbstractRecordField> result;
result.reset(new IntegerRecordField(name, record));
return result;
}
IntegerRecordField::IntegerRecordField(const std::string &name, auparse_state_t *record)
: InterpretedStringRecordField(name, record)
{
if (record)
{
m_int_value = auparse_get_field_int(record);
}
}
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;
}