Improve rule parsing, fix bugs (#28)
This commit is contained in:
Родитель
7b8b09d5d4
Коммит
dbe2bd2c78
277
AuditRules.cpp
277
AuditRules.cpp
|
@ -20,6 +20,7 @@
|
|||
#include "StringUtils.h"
|
||||
#include "KernelInfo.h"
|
||||
#include "FileUtils.h"
|
||||
#include "UserDB.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <sstream>
|
||||
|
@ -28,6 +29,8 @@
|
|||
|
||||
#include <climits>
|
||||
#include <system_error>
|
||||
#include <pwd.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
// Character that seperates key in AUDIT_FILTERKEY field in rules
|
||||
#define KEY_SEP 0x01
|
||||
|
@ -216,11 +219,13 @@ bool AuditRule::Parse(const std::string& text, std::string& error) {
|
|||
if (!parse_add_p_arg(args[idx+1], error)) {
|
||||
return false;
|
||||
}
|
||||
idx += 2;
|
||||
break;
|
||||
case 'k':
|
||||
if (!parse_add_k_arg(args[idx+1], error)) {
|
||||
return false;
|
||||
}
|
||||
idx += 2;
|
||||
break;
|
||||
case 'S':
|
||||
if (watch) {
|
||||
|
@ -236,6 +241,7 @@ bool AuditRule::Parse(const std::string& text, std::string& error) {
|
|||
if (!parse_add_S_arg(args[idx+1], error)) {
|
||||
return false;
|
||||
}
|
||||
idx += 2;
|
||||
break;
|
||||
case 'F':
|
||||
if (watch) {
|
||||
|
@ -244,8 +250,16 @@ bool AuditRule::Parse(const std::string& text, std::string& error) {
|
|||
error.append("' option");
|
||||
return false;
|
||||
}
|
||||
if (!parse_add_F_arg(args[idx+1], error)) {
|
||||
return false;
|
||||
if (args[idx].size() > 2) {
|
||||
if (!parse_add_F_arg(args[idx].substr(2), error)) {
|
||||
return false;
|
||||
}
|
||||
idx += 1;
|
||||
} else {
|
||||
if (!parse_add_F_arg(args[idx+1], error)) {
|
||||
return false;
|
||||
}
|
||||
idx += 2;
|
||||
}
|
||||
break;
|
||||
case 'C':
|
||||
|
@ -258,6 +272,7 @@ bool AuditRule::Parse(const std::string& text, std::string& error) {
|
|||
if (!parse_add_C_arg(args[idx+1], error)) {
|
||||
return false;
|
||||
}
|
||||
idx += 2;
|
||||
break;
|
||||
default:
|
||||
error.append("Unsupported option '");
|
||||
|
@ -265,7 +280,6 @@ bool AuditRule::Parse(const std::string& text, std::string& error) {
|
|||
error.append("'");
|
||||
return false;
|
||||
}
|
||||
idx += 2;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -302,28 +316,32 @@ bool AuditRule::parse_add_a_arg(const std::string& val, std::string& error) {
|
|||
return false;
|
||||
}
|
||||
|
||||
bool action_found = false;
|
||||
for (auto& part: parts) {
|
||||
auto itr = s_a_actions.find(part);
|
||||
if (itr != s_a_actions.end()) {
|
||||
ruleptr()->action = itr->second;
|
||||
action_found = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (ruleptr()->action == 0) {
|
||||
if (!action_found) {
|
||||
error.append("Invalid or missing action value for option '-a': '");
|
||||
error.append(val);
|
||||
error.append("'");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool flags_found = false;
|
||||
for (auto& part: parts) {
|
||||
auto itr = s_a_flags.find(part);
|
||||
if (itr != s_a_flags.end()) {
|
||||
ruleptr()->flags |= itr->second;
|
||||
flags_found = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (ruleptr()->flags == 0) {
|
||||
if (!flags_found) {
|
||||
error.append("Invalid or missing flags value for option '-a': '");
|
||||
error.append(val);
|
||||
error.append("'");
|
||||
|
@ -422,6 +440,18 @@ std::unordered_map<std::string, uint32_t> s_F_ops(
|
|||
}
|
||||
);
|
||||
|
||||
std::unordered_map<std::string, uint32_t> s_F_ftypes(
|
||||
{
|
||||
{"socket", S_IFSOCK},
|
||||
{"link", S_IFLNK},
|
||||
{"file", S_IFREG},
|
||||
{"block", S_IFBLK},
|
||||
{"dir", S_IFDIR},
|
||||
{"character", S_IFCHR},
|
||||
{"fifo", S_IFIFO},
|
||||
}
|
||||
);
|
||||
|
||||
bool AuditRule::parse_add_F_arg(const std::string& val, std::string& error) {
|
||||
auto idx = val.find_first_of("=!<>&");
|
||||
if (idx == 0) {
|
||||
|
@ -474,6 +504,128 @@ bool AuditRule::parse_add_F_arg(const std::string& val, std::string& error) {
|
|||
}
|
||||
|
||||
switch (field) {
|
||||
case AUDIT_UID:
|
||||
case AUDIT_EUID:
|
||||
case AUDIT_SUID:
|
||||
case AUDIT_FSUID:
|
||||
case AUDIT_LOGINUID:
|
||||
case AUDIT_OBJ_UID:
|
||||
try {
|
||||
if (std::isdigit(value[0])) {
|
||||
uint32_t v = static_cast<uint32_t>(stoul(value, 0, 0));
|
||||
add_field(field, op, v);
|
||||
} else if (value.size() > 1 && value[0] == '-' && std::isdigit(value[1])) {
|
||||
uint32_t v = static_cast<uint32_t>(stol(value, 0, 0));
|
||||
add_field(field, op, v);
|
||||
} else {
|
||||
if (value == "unset") {
|
||||
add_field(field, op, 4294967295);
|
||||
} else {
|
||||
auto uid = UserDB::UserNameToUid(value);
|
||||
if (uid < 0) {
|
||||
error.append("Invalid value for option '-F': Unknown username: '");
|
||||
error.append(val);
|
||||
error.append("'");
|
||||
return false;
|
||||
}
|
||||
add_field(field, op, uid);
|
||||
}
|
||||
}
|
||||
} catch (std::exception &) {
|
||||
error.append("Invalid value for option '-F': Invalid numeric value: '");
|
||||
error.append(val);
|
||||
error.append("'");
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case AUDIT_GID:
|
||||
case AUDIT_EGID:
|
||||
case AUDIT_SGID:
|
||||
case AUDIT_FSGID:
|
||||
case AUDIT_OBJ_GID:
|
||||
try {
|
||||
if (std::isdigit(value[0])) {
|
||||
uint32_t v = static_cast<uint32_t>(stoul(value, 0, 0));
|
||||
add_field(field, op, v);
|
||||
} else if (value.size() > 1 && value[0] == '-' && std::isdigit(value[1])) {
|
||||
uint32_t v = static_cast<uint32_t>(stol(value, 0, 0));
|
||||
add_field(field, op, v);
|
||||
} else {
|
||||
if (value == "unset") {
|
||||
add_field(field, op, 4294967295);
|
||||
} else {
|
||||
auto gid = UserDB::GroupNameToGid(value);
|
||||
if (gid < 0) {
|
||||
error.append("Invalid value for option '-F': Unknown group name: '");
|
||||
error.append(val);
|
||||
error.append("'");
|
||||
return false;
|
||||
}
|
||||
add_field(field, op, gid);
|
||||
}
|
||||
}
|
||||
} catch (std::exception &) {
|
||||
error.append("Invalid value for option '-F': Invalid numeric value: '");
|
||||
error.append(val);
|
||||
error.append("'");
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case AUDIT_EXIT:
|
||||
if ((ruleptr()->flags & FILTER_MASK) != AUDIT_FILTER_EXIT) {
|
||||
error.append("Invalid value for option '-F': Cannot filter on exit field unless flags (-a option) == 'exit'");
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
if (std::isdigit(value[0])) {
|
||||
uint32_t v = static_cast<uint32_t>(stoul(value, 0, 0));
|
||||
add_field(field, op, v);
|
||||
} else if (value.size() > 1 && value[0] == '-' && std::isdigit(value[1])) {
|
||||
uint32_t v = static_cast<uint32_t>(stol(value, 0, 0));
|
||||
add_field(field, op, v);
|
||||
} else {
|
||||
auto v = NameToErrno(value);
|
||||
if (v == 0) {
|
||||
error.append("Invalid value for option '-F': Invalid errno name: '");
|
||||
error.append(val);
|
||||
error.append("'");
|
||||
return false;
|
||||
}
|
||||
add_field(field, op, v);
|
||||
}
|
||||
} catch (std::exception &) {
|
||||
error.append("Invalid value for option '-F': Invalid numeric value: '");
|
||||
error.append(val);
|
||||
error.append("'");
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case AUDIT_MSGTYPE:
|
||||
if ((ruleptr()->flags & FILTER_MASK) != AUDIT_FILTER_TYPE) {
|
||||
error.append("Invalid value for option '-F': Cannot filter on msg type unless flags (-a option) == 'exclude'");
|
||||
return false;
|
||||
}
|
||||
if (std::isdigit(value[0])) {
|
||||
try {
|
||||
uint32_t v = static_cast<uint32_t>(stoul(value, 0, 0));
|
||||
add_field(field, op, v);
|
||||
} catch (std::exception &) {
|
||||
error.append("Invalid value for option '-F': Invalid numeric value: '");
|
||||
error.append(val);
|
||||
error.append("'");
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
auto rt = RecordNameToType(value);
|
||||
if (rt == RecordType::UNKNOWN) {
|
||||
error.append("Invalid value for option '-F': Invalid record type name: '");
|
||||
error.append(val);
|
||||
error.append("'");
|
||||
return false;
|
||||
}
|
||||
add_field(field, op, static_cast<uint32_t>(rt));
|
||||
}
|
||||
break;
|
||||
case AUDIT_ARCH: {
|
||||
auto arch = ArchNameToArch(value);
|
||||
if (arch == 0) {
|
||||
|
@ -489,12 +641,6 @@ bool AuditRule::parse_add_F_arg(const std::string& val, std::string& error) {
|
|||
add_field(field, op, arch);
|
||||
break;
|
||||
}
|
||||
case AUDIT_MSGTYPE:
|
||||
if ((ruleptr()->flags & FILTER_MASK) != AUDIT_FILTER_TYPE) {
|
||||
error.append("Invalid value for option '-F': Cannot filter on msg type unless flags (-a option) == 'exclude'");
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case AUDIT_PERM: {
|
||||
uint32_t perms = 0;
|
||||
for (auto c: value) {
|
||||
|
@ -521,11 +667,29 @@ bool AuditRule::parse_add_F_arg(const std::string& val, std::string& error) {
|
|||
add_field(AUDIT_PERM, op, perms);
|
||||
break;
|
||||
}
|
||||
case AUDIT_OBJ_USER:
|
||||
/* fallthrough */
|
||||
case AUDIT_OBJ_ROLE:
|
||||
/* fallthrough */
|
||||
case AUDIT_OBJ_TYPE:
|
||||
/* fallthrough */
|
||||
case AUDIT_OBJ_LEV_LOW:
|
||||
/* fallthrough */
|
||||
case AUDIT_OBJ_LEV_HIGH:
|
||||
if (ruleptr()->flags != AUDIT_FILTER_EXIT) {
|
||||
error = "Field '" + field_name + "' can only be used with 'exit' filter";
|
||||
return false;
|
||||
}
|
||||
add_str_field(field, op, value);
|
||||
break;
|
||||
case AUDIT_WATCH:
|
||||
/* fallthrough */
|
||||
case AUDIT_DIR: {
|
||||
/* fallthrough */
|
||||
auto path = clean_path(val);
|
||||
if (ruleptr()->flags != AUDIT_FILTER_EXIT) {
|
||||
error = "Field '" + field_name + "' can only be used with 'exit' filter";
|
||||
return false;
|
||||
}
|
||||
auto path = clean_path(value);
|
||||
if (!check_path(path, error)) {
|
||||
error = "Invalid path option: " + error;
|
||||
return false;
|
||||
|
@ -533,12 +697,38 @@ bool AuditRule::parse_add_F_arg(const std::string& val, std::string& error) {
|
|||
add_str_field(field, op, path);
|
||||
break;
|
||||
}
|
||||
case AUDIT_SUBJ_USER:
|
||||
/* fallthrough */
|
||||
case AUDIT_SUBJ_ROLE:
|
||||
/* fallthrough */
|
||||
case AUDIT_SUBJ_TYPE:
|
||||
/* fallthrough */
|
||||
case AUDIT_SUBJ_SEN:
|
||||
/* fallthrough */
|
||||
case AUDIT_SUBJ_CLR:
|
||||
/* fallthrough */
|
||||
case AUDIT_EXE:
|
||||
add_str_field(field, op, value);
|
||||
break;
|
||||
case AUDIT_FILTERKEY:
|
||||
AddKey(value);
|
||||
break;
|
||||
case AUDIT_FILETYPE: {
|
||||
if (ruleptr()->flags != AUDIT_FILTER_EXIT) {
|
||||
error = "Field '" + field_name + "' can only be used with 'exit' filter";
|
||||
return false;
|
||||
}
|
||||
auto ft = s_F_ftypes.find(value);
|
||||
if (ft != s_F_ftypes.end()) {
|
||||
add_field(field, op, ft->second);
|
||||
} else {
|
||||
error.append("Invalid value for option '-F': Invalid filetype name: '");
|
||||
error.append(val);
|
||||
error.append("'");
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
try {
|
||||
uint32_t v = static_cast<uint32_t>(stol(value, 0, 0));
|
||||
|
@ -1689,7 +1879,7 @@ void RemoveSection(std::vector<std::string>& lines, const std::string& start_mar
|
|||
lines.erase(start, end);
|
||||
}
|
||||
|
||||
std::vector<AuditRule> ParseRules(const std::vector<std::string>& lines) {
|
||||
std::vector<AuditRule> ParseRules(const std::vector<std::string>& lines, std::vector<std::string>* errors) {
|
||||
std::vector<AuditRule> rules;
|
||||
for (int i = 0; i < lines.size(); ++i) {
|
||||
AuditRule rule;
|
||||
|
@ -1697,7 +1887,11 @@ std::vector<AuditRule> ParseRules(const std::vector<std::string>& lines) {
|
|||
if (rule.Parse(lines[i], error)) {
|
||||
rules.emplace_back(rule);
|
||||
} else if (!error.empty()) {
|
||||
throw std::runtime_error("Failed to parse line " + std::to_string(i+1) + ": " + error);
|
||||
if (errors != nullptr) {
|
||||
errors->emplace_back("Failed to parse line " + std::to_string(i + 1) + ": " + error);
|
||||
} else {
|
||||
throw std::runtime_error("Failed to parse line " + std::to_string(i + 1) + ": " + error);
|
||||
}
|
||||
}
|
||||
}
|
||||
return rules;
|
||||
|
@ -1827,7 +2021,7 @@ std::vector<AuditRule> DiffRules(const std::vector<AuditRule>& actual, const std
|
|||
return rules;
|
||||
}
|
||||
|
||||
std::vector<AuditRule> ReadAuditRulesFromDir(const std::string& dir) {
|
||||
std::vector<AuditRule> ReadAuditRulesFromDir(const std::string& dir, std::vector<std::string>* errors) {
|
||||
std::vector<std::string> files;
|
||||
std::vector<AuditRule> rules;
|
||||
|
||||
|
@ -1836,7 +2030,7 @@ std::vector<AuditRule> ReadAuditRulesFromDir(const std::string& dir) {
|
|||
for(auto& file: files) {
|
||||
if (ends_with(file, ".rules")) {
|
||||
auto lines = ReadFile(dir + "/" + file);
|
||||
rules = MergeRules(rules, ParseRules(lines));
|
||||
rules = MergeRules(rules, ParseRules(lines, errors));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1844,7 +2038,7 @@ std::vector<AuditRule> ReadAuditRulesFromDir(const std::string& dir) {
|
|||
}
|
||||
|
||||
// Read rules from auditd rules.d dir
|
||||
std::vector<AuditRule> ReadAuditdRulesDir(bool exclude_auoms) {
|
||||
std::vector<AuditRule> ReadAuditdRulesDir(bool exclude_auoms, std::vector<std::string>* errors) {
|
||||
std::vector<std::string> files;
|
||||
std::vector<AuditRule> rules;
|
||||
|
||||
|
@ -1853,8 +2047,9 @@ std::vector<AuditRule> ReadAuditdRulesDir(bool exclude_auoms) {
|
|||
for(auto& file: files) {
|
||||
if (ends_with(file, ".rules")) {
|
||||
auto lines = ReadFile(std::string(AUDITD_RULES_DIR) + "/" + file);
|
||||
std::vector<std::string> file_errors;
|
||||
if (exclude_auoms) {
|
||||
auto in_rules = ParseRules(lines);
|
||||
auto in_rules = ParseRules(lines, &file_errors);
|
||||
std::vector<AuditRule> out_rules;
|
||||
out_rules.reserve(in_rules.size());
|
||||
// Only include non-auoms rules
|
||||
|
@ -1866,7 +2061,19 @@ std::vector<AuditRule> ReadAuditdRulesDir(bool exclude_auoms) {
|
|||
}
|
||||
rules = MergeRules(rules, out_rules);
|
||||
} else {
|
||||
rules = MergeRules(rules, ParseRules(lines));
|
||||
rules = MergeRules(rules, ParseRules(lines, &file_errors));
|
||||
}
|
||||
if (errors != nullptr) {
|
||||
for (auto &err : file_errors) {
|
||||
errors->emplace_back("Encountered parse error in '" + std::string(AUDITD_RULES_DIR) + "/" + file + "': " + err);
|
||||
}
|
||||
} else {
|
||||
std::stringstream ss;
|
||||
ss << "Encountered parse errors in '" << std::string(AUDITD_RULES_DIR) << "/" << file << "': " << std::endl;
|
||||
for (auto &err : file_errors) {
|
||||
ss << " " << err << std::endl;
|
||||
}
|
||||
throw std::runtime_error(ss.str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1908,7 +2115,7 @@ bool HasAuditdRulesFiles() {
|
|||
return PathExists(AUDITD_RULES_FILE);
|
||||
}
|
||||
|
||||
std::vector<AuditRule> ReadActualAuditdRules(bool exclude_auoms) {
|
||||
std::vector<AuditRule> ReadActualAuditdRules(bool exclude_auoms, std::vector<std::string>* errors) {
|
||||
if (!PathExists(AUDITD_RULES_FILE)) {
|
||||
return std::vector<AuditRule>();
|
||||
}
|
||||
|
@ -1918,13 +2125,15 @@ std::vector<AuditRule> ReadActualAuditdRules(bool exclude_auoms) {
|
|||
// If the audit.rules file was at any time in the past generated with augenrules
|
||||
// assume it is still in use
|
||||
if (PathExists(AUGENRULES_BIN) && lines.size() > 0 && starts_with(lines[0], AUGENRULES_HEADER)) {
|
||||
return ReadAuditdRulesDir(exclude_auoms);
|
||||
return ReadAuditdRulesDir(exclude_auoms, errors);
|
||||
} else {
|
||||
std::vector<AuditRule> rules;
|
||||
std::vector<std::string> file_errors;
|
||||
if (exclude_auoms) {
|
||||
// Remove the auoms rules section if present.
|
||||
RemoveSection(lines, AUOMS_AUDITD_RULES_FILE_START_MARKER, AUOMS_AUDITD_RULES_FILE_END_MARKER);
|
||||
|
||||
auto in_rules = ParseRules(lines);
|
||||
auto in_rules = ParseRules(lines, &file_errors);
|
||||
std::vector<AuditRule> out_rules;
|
||||
out_rules.reserve(in_rules.size());
|
||||
// Only include non-auoms rules
|
||||
|
@ -1934,17 +2143,30 @@ std::vector<AuditRule> ReadActualAuditdRules(bool exclude_auoms) {
|
|||
out_rules.emplace_back(rule);
|
||||
}
|
||||
}
|
||||
return MergeRules({}, out_rules);
|
||||
rules = MergeRules({}, out_rules);
|
||||
} else {
|
||||
return MergeRules({}, ParseRules(lines));
|
||||
rules = MergeRules({}, ParseRules(lines, &file_errors));
|
||||
}
|
||||
if (errors != nullptr) {
|
||||
for (auto &err : file_errors) {
|
||||
errors->emplace_back("Encountered parse error in '" + std::string(AUDITD_RULES_FILE) + "': " + err);
|
||||
}
|
||||
} else {
|
||||
std::stringstream ss;
|
||||
ss << "Encountered parse errors in '" << std::string(AUDITD_RULES_FILE) << "': " << std::endl;
|
||||
for (auto &err : file_errors) {
|
||||
ss << " " << err << std::endl;
|
||||
}
|
||||
throw std::runtime_error(ss.str());
|
||||
}
|
||||
return rules;
|
||||
}
|
||||
}
|
||||
|
||||
bool WriteAuditdRules(const std::vector<AuditRule>& rules) {
|
||||
auto lines = ReadFile(AUDITD_RULES_FILE);
|
||||
|
||||
if (PathExists(AUGENRULES_BIN) && lines.size() > 0 && starts_with(lines[0], AUGENRULES_HEADER)) {
|
||||
if (PathExists(AUGENRULES_BIN) && !lines.empty() && starts_with(lines[0], AUGENRULES_HEADER)) {
|
||||
WriteAuomsRuleToAuditDir(rules);
|
||||
return true;
|
||||
} else {
|
||||
|
@ -1960,8 +2182,9 @@ bool WriteAuditdRules(const std::vector<AuditRule>& rules) {
|
|||
// Remove auoms's desired rules to auditd config
|
||||
// Returns true if augenrules needs to be run
|
||||
bool RemoveAuomsRulesAuditdFiles() {
|
||||
std::vector<std::string> errors;
|
||||
bool has_auoms_rules = false;
|
||||
auto rules = ReadActualAuditdRules(false);
|
||||
auto rules = ReadActualAuditdRules(false, &errors);
|
||||
for (auto &rule: rules) {
|
||||
auto keys = rule.GetKeys();
|
||||
if (keys.count(AUOMS_RULE_KEY) > 0) {
|
||||
|
|
12
AuditRules.h
12
AuditRules.h
|
@ -141,6 +141,9 @@ public:
|
|||
|
||||
AuditRule(const void* data, size_t len): _data(), _value_offsets(), is_delete_rule(false) {
|
||||
_data.fill(0);
|
||||
if (len > _data.size()) {
|
||||
throw std::out_of_range("len too large");
|
||||
}
|
||||
::memcpy(_data.data(), data, len);
|
||||
fill_value_offsets();
|
||||
}
|
||||
|
@ -240,7 +243,10 @@ private:
|
|||
|
||||
void ReplaceSection(std::vector<std::string>& lines, const std::vector<std::string>& replacement, const std::string& start_marker, const std::string& end_marker);
|
||||
void RemoveSection(std::vector<std::string>& lines, const std::string& start_marker, const std::string& end_marker);
|
||||
std::vector<AuditRule> ParseRules(const std::vector<std::string>& lines);
|
||||
|
||||
// If errors is null, then ParseRules will throiw an exception if there is a parse error
|
||||
// If errors is not null, then ParseRules willa append each parse error to errors and return only the parsed rules.
|
||||
std::vector<AuditRule> ParseRules(const std::vector<std::string>& lines, std::vector<std::string>* errors);
|
||||
|
||||
std::vector<AuditRule> MergeRules(const std::vector<AuditRule>& rules1);
|
||||
std::vector<AuditRule> MergeRules(const std::vector<AuditRule>& rules1, const std::vector<AuditRule>& rules2);
|
||||
|
@ -253,10 +259,10 @@ std::vector<AuditRule> DiffRules(const std::vector<AuditRule>& actual, const std
|
|||
bool HasAuditdRulesFiles();
|
||||
|
||||
// Read all *.rules files from dir, parse, merge then return them.
|
||||
std::vector<AuditRule> ReadAuditRulesFromDir(const std::string& dir);
|
||||
std::vector<AuditRule> ReadAuditRulesFromDir(const std::string& dir, std::vector<std::string>* errors);
|
||||
|
||||
// Read rules from auditd rules (excluding auoms rules)
|
||||
std::vector<AuditRule> ReadActualAuditdRules(bool exclude_auoms);
|
||||
std::vector<AuditRule> ReadActualAuditdRules(bool exclude_auoms, std::vector<std::string>* errors);
|
||||
|
||||
// Adds auoms's desired rules to auditd config
|
||||
// Returns true if augenrules needs to be run
|
||||
|
|
|
@ -2,13 +2,20 @@
|
|||
// Created by tad on 3/18/19.
|
||||
//
|
||||
|
||||
#include <sstream>
|
||||
#include "AuditRulesMonitor.h"
|
||||
|
||||
#include "Logger.h"
|
||||
#include "ExecUtil.h"
|
||||
|
||||
#define PREFERRED_BACKLOG_LIMIT 8192
|
||||
|
||||
|
||||
void AuditRulesMonitor::run() {
|
||||
Logger::Info("AuditRulesMonitor: Starting");
|
||||
|
||||
check_audit_status();
|
||||
|
||||
while(!_sleep(1000)) {
|
||||
auto now = std::chrono::steady_clock::now();
|
||||
|
||||
|
@ -35,7 +42,8 @@ void AuditRulesMonitor::on_stop() {
|
|||
|
||||
void AuditRulesMonitor::get_desired_rules() {
|
||||
try {
|
||||
auto rules = ReadAuditRulesFromDir(_audit_rules_dir);
|
||||
std::vector<std::string> errors;
|
||||
auto rules = ReadAuditRulesFromDir(_audit_rules_dir, &errors);
|
||||
_desired_rules.resize(0);
|
||||
for (auto& rule: rules) {
|
||||
// Only include the rule in the desired rules if it is supported on the host system
|
||||
|
@ -44,7 +52,16 @@ void AuditRulesMonitor::get_desired_rules() {
|
|||
_desired_rules.emplace_back(rule);
|
||||
}
|
||||
}
|
||||
_op_status->ClearErrorCondition(ErrorCategory::DESIRED_RULES);
|
||||
if (errors.empty()) {
|
||||
_op_status->ClearErrorCondition(ErrorCategory::DESIRED_RULES);
|
||||
} else {
|
||||
std::stringstream ss;
|
||||
ss << " Encountered parse errors: " << std::endl;
|
||||
for (auto& err : errors) {
|
||||
ss << " " << err << std::endl;
|
||||
}
|
||||
_op_status->SetErrorCondition(ErrorCategory::DESIRED_RULES, ss.str());
|
||||
}
|
||||
} catch(std::exception& ex) {
|
||||
Logger::Error("AuditRulesMonitor: Failed to read desired rules from %s: %s", _audit_rules_dir.c_str(), ex.what());
|
||||
_op_status->SetErrorCondition(ErrorCategory::DESIRED_RULES, "Failed to read desired rules from " + _audit_rules_dir + ": " + ex.what());
|
||||
|
@ -57,16 +74,27 @@ void AuditRulesMonitor::check_file_rules() {
|
|||
return;
|
||||
}
|
||||
try {
|
||||
auto rules = ReadActualAuditdRules(false);
|
||||
std::vector<std::string> errors;
|
||||
auto rules = ReadActualAuditdRules(false, &errors);
|
||||
auto merged_rules = MergeRules(rules);
|
||||
auto diff = DiffRules(merged_rules, _desired_rules, "");
|
||||
if (diff.empty()) {
|
||||
_op_status->ClearErrorCondition(ErrorCategory::AUDIT_RULES_FILE);
|
||||
if (errors.empty()) {
|
||||
_op_status->ClearErrorCondition(ErrorCategory::AUDIT_RULES_FILE);
|
||||
} else {
|
||||
std::stringstream ss;
|
||||
ss << " Encountered parse errors: " << std::endl;
|
||||
for (auto& err : errors) {
|
||||
ss << " " << err << std::endl;
|
||||
}
|
||||
_op_status->SetErrorCondition(ErrorCategory::AUDIT_RULES_FILE, ss.str());
|
||||
}
|
||||
return;
|
||||
}
|
||||
Logger::Info("AuditRulesMonitor: Found desired audit rules not currently present in auditd rules files(s), adding new rules");
|
||||
// Re-read rules but exclude auoms rules
|
||||
rules = ReadActualAuditdRules(true);
|
||||
errors.clear();
|
||||
rules = ReadActualAuditdRules(true, &errors);
|
||||
merged_rules = MergeRules(rules);
|
||||
// Re-calculate diff
|
||||
diff = DiffRules(merged_rules, _desired_rules, "");
|
||||
|
@ -78,12 +106,32 @@ void AuditRulesMonitor::check_file_rules() {
|
|||
if (ret != 0) {
|
||||
Logger::Warn("AuditRulesMonitor: augenrules failed: %s", cmd.FailMsg().c_str());
|
||||
Logger::Warn("AuditRulesMonitor: augenrules output: %s", output.c_str());
|
||||
_op_status->SetErrorCondition(ErrorCategory::AUDIT_RULES_FILE, std::string("augenrules failed: ") + cmd.FailMsg());
|
||||
if (errors.empty()) {
|
||||
_op_status->SetErrorCondition(ErrorCategory::AUDIT_RULES_FILE, std::string("augenrules failed: ") + cmd.FailMsg());
|
||||
} else {
|
||||
std::stringstream ss;
|
||||
ss << " Encountered parse errors and augenrules failed: " << std::endl;
|
||||
ss << " augenrules error:" << cmd.FailMsg() << std::endl;
|
||||
for (auto& err : errors) {
|
||||
ss << " " << err << std::endl;
|
||||
}
|
||||
_op_status->SetErrorCondition(ErrorCategory::AUDIT_RULES_FILE, ss.str());
|
||||
}
|
||||
return;
|
||||
} else {
|
||||
Logger::Warn("AuditRulesMonitor: augenrules succeeded");
|
||||
}
|
||||
}
|
||||
_op_status->ClearErrorCondition(ErrorCategory::AUDIT_RULES_FILE);
|
||||
if (errors.empty()) {
|
||||
_op_status->ClearErrorCondition(ErrorCategory::AUDIT_RULES_FILE);
|
||||
} else {
|
||||
std::stringstream ss;
|
||||
ss << " Encountered parse errors: " << std::endl;
|
||||
for (auto& err : errors) {
|
||||
ss << " " << err << std::endl;
|
||||
}
|
||||
_op_status->SetErrorCondition(ErrorCategory::AUDIT_RULES_FILE, ss.str());
|
||||
}
|
||||
} catch (std::exception& ex) {
|
||||
Logger::Error("AuditRulesMonitor: Failed to check/update auditd rules: %s", ex.what());
|
||||
_op_status->SetErrorCondition(ErrorCategory::AUDIT_RULES_FILE, std::string("Failed to check/update auditd rules: ") + ex.what());
|
||||
|
@ -226,3 +274,26 @@ bool AuditRulesMonitor::check_kernel_rules() {
|
|||
|
||||
return true;
|
||||
}
|
||||
|
||||
void AuditRulesMonitor::check_audit_status() {
|
||||
audit_status status;
|
||||
auto ret = NetlinkRetry([this,&status]() { return _netlink.AuditGet(status); } );
|
||||
if (ret != 0) {
|
||||
Logger::Error("Failed to get audit status: %s", std::strerror(-ret));
|
||||
return;
|
||||
}
|
||||
if (status.backlog_limit < PREFERRED_BACKLOG_LIMIT) {
|
||||
Logger::Error("Increasing audit backlog limit from %u to %u", status.backlog_limit, PREFERRED_BACKLOG_LIMIT);
|
||||
ret = NetlinkRetry([this]() {
|
||||
audit_status status;
|
||||
::memset(&status, 0, sizeof(status));
|
||||
status.mask = AUDIT_STATUS_BACKLOG_LIMIT;
|
||||
status.backlog_limit = PREFERRED_BACKLOG_LIMIT;
|
||||
return _netlink.AuditSet(status);
|
||||
});
|
||||
if (ret != 0) {
|
||||
Logger::Error("Failed to set audit backlog limit to %d: %s", PREFERRED_BACKLOG_LIMIT, std::strerror(-ret));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -36,6 +36,7 @@ private:
|
|||
void get_desired_rules();
|
||||
void check_file_rules();
|
||||
bool check_kernel_rules();
|
||||
void check_audit_status();
|
||||
|
||||
Netlink& _netlink;
|
||||
std::string _audit_rules_dir;
|
||||
|
|
|
@ -113,6 +113,7 @@ add_executable(auoms
|
|||
TranslateRecordType.cpp
|
||||
TranslateFieldType.cpp
|
||||
TranslateField.cpp
|
||||
TranslateErrno.cpp
|
||||
AuditRules.cpp
|
||||
AuditRulesMonitor.cpp
|
||||
KernelInfo.cpp
|
||||
|
@ -155,6 +156,7 @@ add_executable(auomsctl
|
|||
TranslateRecordType.cpp
|
||||
TranslateFieldType.cpp
|
||||
TranslateField.cpp
|
||||
TranslateErrno.cpp
|
||||
AuditRules.cpp
|
||||
StringUtils.cpp
|
||||
KernelInfo.cpp
|
||||
|
@ -165,6 +167,7 @@ add_executable(auomsctl
|
|||
FileUtils.cpp
|
||||
UnixDomainListener.cpp
|
||||
Event.cpp
|
||||
UserDB.cpp
|
||||
)
|
||||
|
||||
# See https://gcc.gnu.org/onlinedocs/libstdc++/manual/license.html
|
||||
|
|
|
@ -180,8 +180,6 @@ bool CollectionMonitor::is_collector_alive() {
|
|||
}
|
||||
|
||||
void CollectionMonitor::send_audit_pid_report(int pid) {
|
||||
Logger::Info("CollectionMonitor: Audit Pid: %d", pid);
|
||||
|
||||
auto pinfo = ProcessInfo::Open(pid);
|
||||
std::string exe;
|
||||
int ppid = -1;
|
||||
|
|
|
@ -107,7 +107,7 @@ bool CursorWriter::Read() {
|
|||
Logger::Error("Output(%s): Failed to open cursor file (%s): %s", _name.c_str(), _path.c_str(), std::strerror(errno));
|
||||
return false;
|
||||
} else {
|
||||
_cursor = QueueCursor::TAIL;
|
||||
_cursor = QueueCursor::HEAD;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -114,7 +114,7 @@ bool ProcessInfo::parse_stat() {
|
|||
if (*ptr != '(') {
|
||||
return false;
|
||||
}
|
||||
f_end = strchr(ptr, ')');
|
||||
f_end = strstr(ptr, ") ");
|
||||
if (f_end != nullptr && f_end+1 < end && f_end[1] == ')') {
|
||||
f_end += 1;
|
||||
}
|
||||
|
|
15
Queue.cpp
15
Queue.cpp
|
@ -294,8 +294,13 @@ void Queue::Close(bool save)
|
|||
save_locked(lock);
|
||||
}
|
||||
|
||||
// Wait for any active save to complete
|
||||
_cond.wait(lock, [this]() { return !_save_active; });
|
||||
|
||||
close(_fd);
|
||||
_fd = -1;
|
||||
|
||||
_cond.notify_all();
|
||||
}
|
||||
|
||||
void Queue::Save() {
|
||||
|
@ -329,8 +334,6 @@ void Queue::save_locked(std::unique_lock<std::mutex>& lock)
|
|||
return;
|
||||
}
|
||||
|
||||
auto fd = _fd;
|
||||
|
||||
_save_active = true;
|
||||
|
||||
FileHeader before;
|
||||
|
@ -393,20 +396,22 @@ void Queue::save_locked(std::unique_lock<std::mutex>& lock)
|
|||
int64_t save_size = 0;
|
||||
|
||||
if (nregions > 0) {
|
||||
_pwrite(fd, &before, sizeof(FileHeader), 0);
|
||||
_pwrite(_fd, &before, sizeof(FileHeader), 0);
|
||||
|
||||
for (int i = 0; i < nregions; i++) {
|
||||
_pwrite(fd, regions[i].data, regions[i].size, regions[i].index);
|
||||
_pwrite(_fd, regions[i].data, regions[i].size, regions[i].index);
|
||||
save_size += regions[i].size;
|
||||
}
|
||||
}
|
||||
|
||||
_pwrite(fd, &after, sizeof(FileHeader), 0);
|
||||
_pwrite(_fd, &after, sizeof(FileHeader), 0);
|
||||
|
||||
lock.lock();
|
||||
|
||||
_saved_size += save_size;
|
||||
_save_active = false;
|
||||
|
||||
_cond.notify_all();
|
||||
}
|
||||
|
||||
// Assumes queue is locked
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include <signal.h>
|
||||
|
||||
std::atomic<bool> Signals::_exit(false);
|
||||
std::mutex Signals::_mutex;
|
||||
std::function<void()> Signals::_hup_fn;
|
||||
std::function<void()> Signals::_exit_fn;
|
||||
pthread_t Signals::_main_id;
|
||||
|
@ -106,13 +107,17 @@ void Signals::run() {
|
|||
return;
|
||||
}
|
||||
if (sig == SIGHUP) {
|
||||
std::lock_guard<std::mutex> _lock(_mutex);
|
||||
if (_hup_fn) {
|
||||
_hup_fn();
|
||||
}
|
||||
} else {
|
||||
_exit.store(true);
|
||||
if (_exit_fn) {
|
||||
_exit_fn();
|
||||
{
|
||||
std::lock_guard<std::mutex> _lock(_mutex);
|
||||
if (_exit_fn) {
|
||||
_exit_fn();
|
||||
}
|
||||
}
|
||||
// Break main thread out of blocking syscall
|
||||
pthread_kill(_main_id, SIGQUIT);
|
||||
|
|
12
Signals.h
12
Signals.h
|
@ -18,6 +18,7 @@
|
|||
|
||||
#include <atomic>
|
||||
#include <functional>
|
||||
#include <mutex>
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
|
@ -29,13 +30,20 @@ public:
|
|||
static bool IsExit();
|
||||
static void Terminate();
|
||||
|
||||
static void SetHupHandler(std::function<void()> fn) { _hup_fn = fn; }
|
||||
static void SetExitHandler(std::function<void()> fn) { _exit_fn = fn; }
|
||||
static void SetHupHandler(std::function<void()> fn) {
|
||||
std::lock_guard<std::mutex> _lock(_mutex);
|
||||
_hup_fn = fn;
|
||||
}
|
||||
static void SetExitHandler(std::function<void()> fn) {
|
||||
std::lock_guard<std::mutex> _lock(_mutex);
|
||||
_exit_fn = fn;
|
||||
}
|
||||
|
||||
private:
|
||||
static void run();
|
||||
|
||||
static std::atomic<bool> _exit;
|
||||
static std::mutex _mutex;
|
||||
static std::function<void()> _hup_fn;
|
||||
static std::function<void()> _exit_fn;
|
||||
static pthread_t _main_id;
|
||||
|
|
|
@ -45,4 +45,7 @@ field_type_t FieldNameToType(RecordType rtype, const std::string_view& name, con
|
|||
std::string FieldIdToName(int field);
|
||||
int FieldNameToId(const std::string_view& name);
|
||||
|
||||
std::string ErrnoToName(int field);
|
||||
int NameToErrno(const std::string_view& name);
|
||||
|
||||
#endif //AUOMS_TRANSLATE_H
|
||||
|
|
|
@ -0,0 +1,182 @@
|
|||
/*
|
||||
microsoft-oms-auditd-plugin
|
||||
|
||||
Copyright (c) Microsoft Corporation
|
||||
|
||||
All rights reserved.
|
||||
|
||||
MIT License
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the ""Software""), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <linux/audit.h>
|
||||
|
||||
#include "Translate.h"
|
||||
#include "StringTable.h"
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
static StringTable<int> s_errno_table(0, {
|
||||
{"EPERM", EPERM},
|
||||
{"ENOENT", ENOENT},
|
||||
{"ESRCH", ESRCH},
|
||||
{"EINTR", EINTR},
|
||||
{"EIO", EIO},
|
||||
{"ENXIO", ENXIO},
|
||||
{"E2BIG", E2BIG},
|
||||
{"ENOEXEC", ENOEXEC},
|
||||
{"EBADF", EBADF},
|
||||
{"ECHILD", ECHILD},
|
||||
{"EAGAIN", EAGAIN},
|
||||
{"ENOMEM", ENOMEM},
|
||||
{"EACCES", EACCES},
|
||||
{"EFAULT", EFAULT},
|
||||
{"ENOTBLK", ENOTBLK},
|
||||
{"EBUSY", EBUSY},
|
||||
{"EEXIST", EEXIST},
|
||||
{"EXDEV", EXDEV},
|
||||
{"ENODEV", ENODEV},
|
||||
{"ENOTDIR", ENOTDIR},
|
||||
{"EISDIR", EISDIR},
|
||||
{"EINVAL", EINVAL},
|
||||
{"ENFILE", ENFILE},
|
||||
{"EMFILE", EMFILE},
|
||||
{"ENOTTY", ENOTTY},
|
||||
{"ETXTBSY", ETXTBSY},
|
||||
{"EFBIG", EFBIG},
|
||||
{"ENOSPC", ENOSPC},
|
||||
{"ESPIPE", ESPIPE},
|
||||
{"EROFS", EROFS},
|
||||
{"EMLINK", EMLINK},
|
||||
{"EPIPE", EPIPE},
|
||||
{"EDOM", EDOM},
|
||||
{"ERANGE", ERANGE},
|
||||
{"EDEADLK", EDEADLK},
|
||||
{"ENAMETOOLONG", ENAMETOOLONG},
|
||||
{"ENOLCK", ENOLCK},
|
||||
{"ENOSYS", ENOSYS},
|
||||
{"ENOTEMPTY", ENOTEMPTY},
|
||||
{"ELOOP", ELOOP},
|
||||
{"EWOULDBLOCK", EWOULDBLOCK},
|
||||
{"ENOMSG", ENOMSG},
|
||||
{"EIDRM", EIDRM},
|
||||
{"ECHRNG", ECHRNG},
|
||||
{"EL2NSYNC", EL2NSYNC},
|
||||
{"EL3HLT", EL3HLT},
|
||||
{"EL3RST", EL3RST},
|
||||
{"ELNRNG", ELNRNG},
|
||||
{"EUNATCH", EUNATCH},
|
||||
{"ENOCSI", ENOCSI},
|
||||
{"EL2HLT", EL2HLT},
|
||||
{"EBADE", EBADE},
|
||||
{"EBADR", EBADR},
|
||||
{"EXFULL", EXFULL},
|
||||
{"ENOANO", ENOANO},
|
||||
{"EBADRQC", EBADRQC},
|
||||
{"EBADSLT", EBADSLT},
|
||||
{"EDEADLOCK", EDEADLOCK},
|
||||
{"EBFONT", EBFONT},
|
||||
{"ENOSTR", ENOSTR},
|
||||
{"ENODATA", ENODATA},
|
||||
{"ETIME", ETIME},
|
||||
{"ENOSR", ENOSR},
|
||||
{"ENONET", ENONET},
|
||||
{"ENOPKG", ENOPKG},
|
||||
{"EREMOTE", EREMOTE},
|
||||
{"ENOLINK", ENOLINK},
|
||||
{"EADV", EADV},
|
||||
{"ESRMNT", ESRMNT},
|
||||
{"ECOMM", ECOMM},
|
||||
{"EPROTO", EPROTO},
|
||||
{"EMULTIHOP", EMULTIHOP},
|
||||
{"EDOTDOT", EDOTDOT},
|
||||
{"EBADMSG", EBADMSG},
|
||||
{"EOVERFLOW", EOVERFLOW},
|
||||
{"ENOTUNIQ", ENOTUNIQ},
|
||||
{"EBADFD", EBADFD},
|
||||
{"EREMCHG", EREMCHG},
|
||||
{"ELIBACC", ELIBACC},
|
||||
{"ELIBBAD", ELIBBAD},
|
||||
{"ELIBSCN", ELIBSCN},
|
||||
{"ELIBMAX", ELIBMAX},
|
||||
{"ELIBEXEC", ELIBEXEC},
|
||||
{"EILSEQ", EILSEQ},
|
||||
{"ERESTART", ERESTART},
|
||||
{"ESTRPIPE", ESTRPIPE},
|
||||
{"EUSERS", EUSERS},
|
||||
{"ENOTSOCK", ENOTSOCK},
|
||||
{"EDESTADDRREQ", EDESTADDRREQ},
|
||||
{"EMSGSIZE", EMSGSIZE},
|
||||
{"EPROTOTYPE", EPROTOTYPE},
|
||||
{"ENOPROTOOPT", ENOPROTOOPT},
|
||||
{"EPROTONOSUPPORT", EPROTONOSUPPORT},
|
||||
{"ESOCKTNOSUPPORT", ESOCKTNOSUPPORT},
|
||||
{"EOPNOTSUPP", EOPNOTSUPP},
|
||||
{"EPFNOSUPPORT", EPFNOSUPPORT},
|
||||
{"EAFNOSUPPORT", EAFNOSUPPORT},
|
||||
{"EADDRINUSE", EADDRINUSE},
|
||||
{"EADDRNOTAVAIL", EADDRNOTAVAIL},
|
||||
{"ENETDOWN", ENETDOWN},
|
||||
{"ENETUNREACH", ENETUNREACH},
|
||||
{"ENETRESET", ENETRESET},
|
||||
{"ECONNABORTED", ECONNABORTED},
|
||||
{"ECONNRESET", ECONNRESET},
|
||||
{"ENOBUFS", ENOBUFS},
|
||||
{"EISCONN", EISCONN},
|
||||
{"ENOTCONN", ENOTCONN},
|
||||
{"ESHUTDOWN", ESHUTDOWN},
|
||||
{"ETOOMANYREFS", ETOOMANYREFS},
|
||||
{"ETIMEDOUT", ETIMEDOUT},
|
||||
{"ECONNREFUSED", ECONNREFUSED},
|
||||
{"EHOSTDOWN", EHOSTDOWN},
|
||||
{"EHOSTUNREACH", EHOSTUNREACH},
|
||||
{"EALREADY", EALREADY},
|
||||
{"EINPROGRESS", EINPROGRESS},
|
||||
{"ESTALE", ESTALE},
|
||||
{"EUCLEAN", EUCLEAN},
|
||||
{"ENOTNAM", ENOTNAM},
|
||||
{"ENAVAIL", ENAVAIL},
|
||||
{"EISNAM", EISNAM},
|
||||
{"EREMOTEIO", EREMOTEIO},
|
||||
{"EDQUOT", EDQUOT},
|
||||
{"ENOMEDIUM", ENOMEDIUM},
|
||||
{"EMEDIUMTYPE", EMEDIUMTYPE},
|
||||
{"ECANCELED", ECANCELED},
|
||||
{"ENOKEY", ENOKEY},
|
||||
{"EKEYEXPIRED", EKEYEXPIRED},
|
||||
{"EKEYREVOKED", EKEYREVOKED},
|
||||
{"EKEYREJECTED", EKEYREJECTED},
|
||||
{"EOWNERDEAD", EOWNERDEAD},
|
||||
{"ENOTRECOVERABLE", ENOTRECOVERABLE},
|
||||
{"ERFKILL", ERFKILL},
|
||||
{"EHWPOISON", EHWPOISON},
|
||||
});
|
||||
|
||||
std::string ErrnoToName(int n) {
|
||||
int err = n;
|
||||
if (err < 0) {
|
||||
err = -err;
|
||||
}
|
||||
auto str = std::string(s_errno_table.ToString(n));
|
||||
if (str.empty()) {
|
||||
str = std::to_string(n);
|
||||
}
|
||||
if (n < 0) {
|
||||
return "-" + str;
|
||||
} else {
|
||||
return str;
|
||||
}
|
||||
}
|
||||
|
||||
int NameToErrno(const std::string_view& name) {
|
||||
if (!name.empty() && name[0] == '-') {
|
||||
return -(s_errno_table.ToInt(name.substr(1)));
|
||||
} else {
|
||||
return s_errno_table.ToInt(name);
|
||||
}
|
||||
}
|
49
UserDB.cpp
49
UserDB.cpp
|
@ -204,8 +204,9 @@ void UserDB::update_task()
|
|||
}
|
||||
}
|
||||
|
||||
void parse_file(const std::string& path, std::unordered_map<int, std::string>& db)
|
||||
std::vector<std::pair<int, std::string>> parse_file(const std::string& path)
|
||||
{
|
||||
std::vector<std::pair<int, std::string>> entries;
|
||||
std::ifstream fs(path);
|
||||
|
||||
int line_num = 1;
|
||||
|
@ -232,11 +233,9 @@ void parse_file(const std::string& path, std::unordered_map<int, std::string>& d
|
|||
} catch (...) {
|
||||
continue;
|
||||
}
|
||||
// Just in case there are multiple entries, only the first id->name is used
|
||||
if (db.count(id) == 0) {
|
||||
db.emplace(std::make_pair(id, name));
|
||||
}
|
||||
entries.emplace_back(id, name);
|
||||
}
|
||||
return entries;
|
||||
}
|
||||
|
||||
void UserDB::update()
|
||||
|
@ -245,8 +244,18 @@ void UserDB::update()
|
|||
std::unordered_map<int, std::string> groups;
|
||||
|
||||
try {
|
||||
parse_file(_dir + "/passwd", users);
|
||||
parse_file(_dir + "/group", groups);
|
||||
for (auto& e : parse_file(_dir + "/passwd")) {
|
||||
// Just in case there are multiple entries, only the first id->name is used
|
||||
if (users.count(e.first) == 0) {
|
||||
users.emplace(e);
|
||||
}
|
||||
}
|
||||
for (auto& e : parse_file(_dir + "/group")) {
|
||||
// Just in case there are multiple entries, only the first id->name is used
|
||||
if (groups.count(e.first) == 0) {
|
||||
groups.emplace(e);
|
||||
}
|
||||
}
|
||||
} catch (const std::exception& ex) {
|
||||
Logger::Warn("UserDB: Update failed: %s", ex.what());
|
||||
return;
|
||||
|
@ -256,3 +265,29 @@ void UserDB::update()
|
|||
_users = users;
|
||||
_groups = groups;
|
||||
}
|
||||
|
||||
int UserDB::UserNameToUid(const std::string& name) {
|
||||
try {
|
||||
for (auto& e : parse_file("/etc/passwd")) {
|
||||
if (e.second == name) {
|
||||
return e.first;
|
||||
}
|
||||
}
|
||||
} catch (const std::exception& ex) {
|
||||
return -1;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int UserDB::GroupNameToGid(const std::string& name) {
|
||||
try {
|
||||
for (auto& e : parse_file("/etc/group")) {
|
||||
if (e.second == name) {
|
||||
return e.first;
|
||||
}
|
||||
}
|
||||
} catch (const std::exception& ex) {
|
||||
return -1;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
|
3
UserDB.h
3
UserDB.h
|
@ -37,6 +37,9 @@ public:
|
|||
|
||||
void update(); // Exposed only to simplify tests
|
||||
|
||||
static int UserNameToUid(const std::string& name);
|
||||
static int GroupNameToGid(const std::string& name);
|
||||
|
||||
private:
|
||||
void inotify_task();
|
||||
|
||||
|
|
|
@ -8,6 +8,6 @@
|
|||
AUOMS_BUILDVERSION_MAJOR=2
|
||||
AUOMS_BUILDVERSION_MINOR=0
|
||||
AUOMS_BUILDVERSION_PATCH=0
|
||||
AUOMS_BUILDVERSION_BUILDNR=10
|
||||
AUOMS_BUILDVERSION_DATE=20190421
|
||||
AUOMS_BUILDVERSION_BUILDNR=12
|
||||
AUOMS_BUILDVERSION_DATE=20190501
|
||||
AUOMS_BUILDVERSION_STATUS=Developer_Build
|
||||
|
|
71
auomsctl.cpp
71
auomsctl.cpp
|
@ -77,9 +77,6 @@ int show_audit_status() {
|
|||
return 1;
|
||||
}
|
||||
|
||||
Signals::Init();
|
||||
Signals::Start();
|
||||
|
||||
Netlink netlink;
|
||||
netlink.SetQuite();
|
||||
|
||||
|
@ -116,9 +113,6 @@ int list_rules(bool raw_fmt, const std::string& key) {
|
|||
return 1;
|
||||
}
|
||||
|
||||
Signals::Init();
|
||||
Signals::Start();
|
||||
|
||||
Netlink netlink;
|
||||
netlink.SetQuite();
|
||||
|
||||
|
@ -163,9 +157,6 @@ int delete_rules(const std::string& key) {
|
|||
return 1;
|
||||
}
|
||||
|
||||
Signals::Init();
|
||||
Signals::Start();
|
||||
|
||||
Netlink netlink;
|
||||
netlink.SetQuite();
|
||||
|
||||
|
@ -222,10 +213,7 @@ int load_rules(const std::string& path) {
|
|||
int exit_code = 0;
|
||||
try {
|
||||
auto lines = ReadFile(path);
|
||||
auto rules = ParseRules(lines);
|
||||
|
||||
Signals::Init();
|
||||
Signals::Start();
|
||||
auto rules = ParseRules(lines, nullptr);
|
||||
|
||||
Netlink netlink;
|
||||
netlink.SetQuite();
|
||||
|
@ -268,9 +256,16 @@ int load_rules(const std::string& path) {
|
|||
int print_rules(const std::string& path) {
|
||||
try {
|
||||
auto lines = ReadFile(path);
|
||||
auto rules = ParseRules(lines);
|
||||
for (auto& rule: rules) {
|
||||
std::cout << rule.CanonicalText() << std::endl;
|
||||
std::vector<AuditRule> rules;
|
||||
for (int i = 0; i < lines.size(); ++i) {
|
||||
AuditRule rule;
|
||||
std::string error;
|
||||
if (rule.Parse(lines[i], error)) {
|
||||
std::cout << rule.CanonicalText() << std::endl;
|
||||
} else if (!error.empty()) {
|
||||
std::cout << "Failed to parse line " << i+1 << ": " << error << std::endl;
|
||||
std::cout << " " << lines[i] << std::endl;
|
||||
}
|
||||
}
|
||||
} catch (std::exception& ex) {
|
||||
std::cerr << ex.what() << std::endl;
|
||||
|
@ -282,8 +277,8 @@ int print_rules(const std::string& path) {
|
|||
|
||||
int merge_rules(const std::string& file1, const std::string& file2) {
|
||||
try {
|
||||
auto rules1 = ParseRules(ReadFile(file1));
|
||||
auto rules2 = ParseRules(ReadFile(file2));
|
||||
auto rules1 = ParseRules(ReadFile(file1), nullptr);
|
||||
auto rules2 = ParseRules(ReadFile(file2), nullptr);
|
||||
auto merged_rules = MergeRules(rules1, rules2);
|
||||
for (auto& rule: merged_rules) {
|
||||
std::cout << rule.CanonicalText() << std::endl;
|
||||
|
@ -298,8 +293,8 @@ int merge_rules(const std::string& file1, const std::string& file2) {
|
|||
|
||||
int diff_rules(const std::string& file1, const std::string& file2) {
|
||||
try {
|
||||
auto rules1 = MergeRules(ParseRules(ReadFile(file1)));
|
||||
auto rules2 = MergeRules(ParseRules(ReadFile(file2)));
|
||||
auto rules1 = MergeRules(ParseRules(ReadFile(file1), nullptr));
|
||||
auto rules2 = MergeRules(ParseRules(ReadFile(file2), nullptr));
|
||||
auto diffed_rules = DiffRules(rules1, rules2, "");
|
||||
for (auto& rule: diffed_rules) {
|
||||
std::cout << rule.CanonicalText() << std::endl;
|
||||
|
@ -318,9 +313,6 @@ int show_auoms_status() {
|
|||
return 1;
|
||||
}
|
||||
|
||||
Signals::Init();
|
||||
Signals::Start();
|
||||
|
||||
UnixDomainWriter io("/var/run/auoms/status.socket");
|
||||
if (!io.Open()) {
|
||||
std::cout << "auoms is not running" << std::endl;
|
||||
|
@ -676,10 +668,6 @@ int enable_auoms() {
|
|||
return 1;
|
||||
}
|
||||
|
||||
Signals::Init();
|
||||
Signals::Start();
|
||||
Signals::SetExitHandler([](){ exit(1); });
|
||||
|
||||
// Return
|
||||
// 0 on success
|
||||
// 1 if service could not be enabled
|
||||
|
@ -739,10 +727,6 @@ int disable_auoms() {
|
|||
return 1;
|
||||
}
|
||||
|
||||
Signals::Init();
|
||||
Signals::Start();
|
||||
Signals::SetExitHandler([](){ exit(1); });
|
||||
|
||||
// Return
|
||||
// 0 on success
|
||||
// 1 if service could not be disabled
|
||||
|
@ -884,9 +868,6 @@ int tap_audit() {
|
|||
return false;
|
||||
};
|
||||
|
||||
Signals::Init();
|
||||
Signals::Start();
|
||||
|
||||
Logger::Info("Connecting to AUDIT NETLINK socket");
|
||||
auto ret = netlink.Open(handler);
|
||||
if (ret != 0) {
|
||||
|
@ -1068,9 +1049,6 @@ int monitor_auoms_events() {
|
|||
std::string sock_path = "/var/run/auoms/auomsctl.socket";
|
||||
std::string config_path = "/etc/opt/microsoft/auoms/outconf.d/auomsctl.conf";
|
||||
|
||||
Signals::Init();
|
||||
Signals::Start();
|
||||
|
||||
UnixDomainListener listener(sock_path, 0666);
|
||||
if (!listener.Open()) {
|
||||
return -1;
|
||||
|
@ -1106,7 +1084,7 @@ int monitor_auoms_events() {
|
|||
}
|
||||
|
||||
int set_rules() {
|
||||
auto rules = ReadAuditRulesFromDir("/etc/opt/microsoft/auoms/rules.d");
|
||||
auto rules = ReadAuditRulesFromDir("/etc/opt/microsoft/auoms/rules.d", nullptr);
|
||||
std::vector<AuditRule> desired_rules;
|
||||
for (auto& rule: rules) {
|
||||
// Only include the rule in the desired rules if it is supported on the host system
|
||||
|
@ -1117,14 +1095,14 @@ int set_rules() {
|
|||
}
|
||||
|
||||
try {
|
||||
auto rules = ReadActualAuditdRules(false);
|
||||
auto rules = ReadActualAuditdRules(false, nullptr);
|
||||
auto diff = DiffRules(rules, desired_rules, "");
|
||||
if (diff.empty()) {
|
||||
return 0;
|
||||
}
|
||||
Logger::Info("AuditRulesMonitor: Found desired audit rules not currently present in auditd rules files(s), adding new rules");
|
||||
// Re-read rules but exclude auoms rules
|
||||
rules = ReadActualAuditdRules(true);
|
||||
rules = ReadActualAuditdRules(true, nullptr);
|
||||
// Re-calculate diff
|
||||
diff = DiffRules(rules, desired_rules, "");
|
||||
if (WriteAuditdRules(diff)) {
|
||||
|
@ -1156,7 +1134,7 @@ bool is_set_intersect(T a, T b) {
|
|||
}
|
||||
|
||||
int load_rules() {
|
||||
auto rules = ReadAuditRulesFromDir("/etc/opt/microsoft/auoms/rules.d");
|
||||
auto rules = ReadAuditRulesFromDir("/etc/opt/microsoft/auoms/rules.d", nullptr);
|
||||
std::vector<AuditRule> desired_rules;
|
||||
for (auto& rule: rules) {
|
||||
// Only include the rule in the desired rules if it is supported on the host system
|
||||
|
@ -1168,9 +1146,6 @@ int load_rules() {
|
|||
|
||||
Netlink netlink;
|
||||
|
||||
Signals::Init();
|
||||
Signals::Start();
|
||||
|
||||
Logger::Info("Connecting to AUDIT NETLINK socket");
|
||||
auto ret = netlink.Open(nullptr);
|
||||
if (ret != 0) {
|
||||
|
@ -1285,10 +1260,6 @@ int upgrade() {
|
|||
return 1;
|
||||
}
|
||||
|
||||
Signals::Init();
|
||||
Signals::Start();
|
||||
Signals::SetExitHandler([](){ exit(1); });
|
||||
|
||||
try {
|
||||
// Use auditd plugin file to determine if auoms should be enabled
|
||||
if (is_auditd_plugin_enabled()) {
|
||||
|
@ -1354,6 +1325,10 @@ int main(int argc, char**argv) {
|
|||
exit(1);
|
||||
}
|
||||
|
||||
Signals::Init();
|
||||
Signals::Start();
|
||||
Signals::SetExitHandler([](){ exit(1); });
|
||||
|
||||
if (strcmp(argv[1], "-v") == 0) {
|
||||
std::cout << std::string(AUOMS_VERSION) << std::endl;
|
||||
return 0;
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
# Default is ${run_dir}/input.socket
|
||||
#socket_path = /var/run/auoms/input.socket
|
||||
|
||||
# The path to the file where auomscollect will keep trask of which events have been sent to auoms
|
||||
# The path to the file where auomscollect will keep track of which events have been sent to auoms
|
||||
#
|
||||
# Default is ${data_dir}/collect.cursor
|
||||
#cursor_path = /var/opt/microsoft/auoms/data/collect.cursor
|
||||
|
|
|
@ -28,7 +28,6 @@ if [ "$1" = "upgrade" ]; then
|
|||
fi
|
||||
|
||||
%Postinstall_100
|
||||
echo "Postinstall_100 $1"
|
||||
if [ "$1" = "configure" ]; then
|
||||
if [ -e ${{AUOMS_AUDISP_CONF}}.auomssave ]; then
|
||||
if [ -e ${{AUOMS_AUDISP_CONF}} ]; then
|
||||
|
|
Загрузка…
Ссылка в новой задаче