blob: b0fff85a0f4fba01ae881ceda620c99fc5852eee [file] [log] [blame] [edit]
// Copyright 2022 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <algorithm>
#include <utility>
#include <vector>
#include <absl/strings/match.h>
#include <base/logging.h>
#include <base/strings/string_number_conversions.h>
#include "secanomalyd/audit_log_reader.h"
namespace secanomalyd {
static constexpr LazyRE2 kSuccessFieldPattern = {R"(success=([a-z]+)\s)"};
static constexpr LazyRE2 kSyscallFieldPattern = {R"(SYSCALL=([\w]+)\s)"};
// Extracts the executable path from the cmd field of the log message.
static constexpr LazyRE2 kExePathPattern = {R"(cmd=\"(\S+).*\")"};
bool IsMemfdCreate(const std::string& log_message) {
std::string syscall;
std::string success;
if (!RE2::PartialMatch(log_message, *kSuccessFieldPattern, &success) ||
!RE2::PartialMatch(log_message, *kSyscallFieldPattern, &syscall))
return false;
if (syscall == "memfd_create" && success == "yes")
return true;
return false;
}
bool IsMemfdExecutionAttempt(const std::string& log_message,
std::string& exe_path) {
// Looks for the text snippet appended to log messages coming from the kernel
// LSM code where the execution attempt is blocked.
if (absl::StartsWith(log_message, "ChromeOS LSM: memfd execution attempt")) {
if (!RE2::PartialMatch(log_message, *kExePathPattern, &exe_path)) {
exe_path = secanomalyd::kUnknownExePath;
}
return true;
}
return false;
}
bool Parser::IsValid(const std::string& line, LogRecord& log_record) {
double log_time_in_seconds;
std::string log_message, log_time;
if (!RE2::FullMatch(line, *pattern_, &log_time, &log_message))
return false;
if (!base::StringToDouble(log_time, &log_time_in_seconds)) {
LOG(WARNING) << "Ignoring log entry due to invalid timestamp. time="
<< log_time << " tag=" << tag_ << " message=" << log_message;
return false;
}
log_record.tag = tag_;
log_record.message = log_message;
log_record.timestamp =
base::Time::FromSecondsSinceUnixEpoch(log_time_in_seconds);
return true;
}
bool AuditLogReader::GetNextEntry(LogRecord* log_record) {
std::string line;
while (log_file_.GetLine(&line)) {
// If the log record is matched with any of the Parser objects in the
// |parser_map_| and it is valid, ReadLine returns true and populates the
// LogRecord object.
if (ReadLine(line, *log_record)) {
return true;
}
}
return false;
}
bool AuditLogReader::ReadLine(const std::string& line, LogRecord& log_record) {
// The log line is parsed using the first Parser whose pattern matches the
// line. This is OK because there should only be one Parser per log line type.
for (auto& parser : parser_map_) {
if (parser.second->IsValid(line, log_record))
return true;
}
return false;
}
} // namespace secanomalyd