249 lines
8.8 KiB
C++
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;
|
|
}
|