## osquery v4.0.0
ADD_SUBDIRECTORY(core)
ADD_SUBDIRECTORY(database)
-ADD_SUBDIRECTORY(events)
ADD_SUBDIRECTORY(filesystem)
ADD_SUBDIRECTORY(logger)
ADD_SUBDIRECTORY(plugins)
#include "osquery/utils/info/platform_type.h"
#include <osquery/core.h>
#include <osquery/data_logger.h>
-#include <osquery/events.h>
#include <osquery/filesystem/filesystem.h>
#include <osquery/flags.h>
#include <osquery/registry.h>
DECLARE_bool(database_dump);
DECLARE_string(database_path);
DECLARE_bool(disable_database);
-DECLARE_bool(disable_events);
DECLARE_bool(disable_logging);
CLI_FLAG(bool, S, false, "Run as a shell process");
// The shell is transient, rewrite config-loaded paths.
FLAGS_disable_logging = true;
// The shell never will not fork a worker.
- FLAGS_disable_events = true;
}
if (default_flags && isReadable(kBackupDefaultFlagfile)) {
initActivePlugin("logger", FLAGS_logger_plugin);
initLogger(binary_);
}
-
- // Start event threads.
- osquery::attachEvents();
- EventFactory::delay();
}
void Initializer::waitForShutdown() {
}
}
- // End any event type run loops.
- EventFactory::end(true);
-
// Hopefully release memory used by global string constructors in gflags.
GFLAGS_NAMESPACE::ShutDownCommandLineFlags();
DatabasePlugin::shutdown();
/// The shell may request execution of all queries in a pack immediately.
DECLARE_string(pack);
-/// The shell may need to disable events for fast operations.
-DECLARE_bool(disable_events);
-
/**
* @brief Run an interactive SQL query shell.
*
+++ /dev/null
-# Copyright (c) 2019 Samsung Electronics Co., Ltd All Rights Reserved
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License
-
-ADD_OSQUERY_LIBRARY(osquery_event events.cpp)
-
-FILE(GLOB OSQUERY_EVENT_TESTS "tests/*.cpp")
-ADD_OSQUERY_TEST(${OSQUERY_EVENT_TESTS})
+++ /dev/null
-/**
- * Copyright (c) 2014-present, Facebook, Inc.
- * All rights reserved.
- *
- * This source code is licensed in accordance with the terms specified in
- * the LICENSE file found in the root directory of this source tree.
- */
-
-#include <chrono>
-#include <exception>
-#include <thread>
-
-#include <boost/algorithm/string.hpp>
-#include <boost/algorithm/string/classification.hpp>
-#include <boost/lexical_cast.hpp>
-
-#include <osquery/database.h>
-#include <osquery/events.h>
-#include <osquery/flags.h>
-#include <osquery/logger.h>
-#include <osquery/registry_factory.h>
-#include <osquery/sql.h>
-#include <osquery/sql/dynamic_table_row.h>
-#include <osquery/system.h>
-#include <osquery/utils/conversions/split.h>
-#include <osquery/utils/conversions/tryto.h>
-#include <osquery/utils/system/time.h>
-
-namespace osquery {
-
-CREATE_REGISTRY(EventPublisherPlugin, "event_publisher");
-CREATE_REGISTRY(EventSubscriberPlugin, "event_subscriber");
-
-/// Checkpoint interval to inspect max event buffering.
-#define EVENTS_CHECKPOINT 256
-
-FLAG(bool, disable_events, false, "Disable osquery publish/subscribe system");
-
-FLAG(bool,
- events_optimize,
- true,
- "Optimize subscriber select queries (scheduler only)");
-
-// Access this flag through EventSubscriberPlugin::getEventsExpiry to allow for
-// overriding in subclasses
-FLAG(uint64, events_expiry, 3600, "Timeout to expire event subscriber results");
-
-// Access this flag through EventSubscriberPlugin::getEventsMax to allow for
-// overriding in subclasses
-FLAG(uint64, events_max, 50000, "Maximum number of events per type to buffer");
-
-static inline EventTime timeFromRecord(const std::string& record) {
- // Convert a stored index "as string bytes" to a time value.
- return static_cast<EventTime>(tryTo<long long>(record).takeOr(0ll));
-}
-
-static inline std::string toIndex(size_t i) {
- auto str_index = std::to_string(i);
- if (str_index.size() < 10) {
- str_index.insert(str_index.begin(), 10 - str_index.size(), '0');
- }
- return str_index;
-}
-
-static inline void getOptimizeData(EventTime& o_time,
- size_t& o_eid,
- std::string& query_name,
- const std::string& publisher) {
- // Read the optimization time for the current executing query.
- getDatabaseValue(kPersistentSettings, "", query_name);
- if (query_name.empty()) {
- o_time = 0;
- o_eid = 0;
- return;
- }
-
- {
- std::string content;
- getDatabaseValue(kEvents, "optimize." + query_name, content);
- o_time = timeFromRecord(content);
- }
-
- {
- std::string content;
- getDatabaseValue(kEvents, "optimize_eid." + query_name, content);
- o_eid = tryTo<std::size_t>(content).takeOr(std::size_t{0});
- }
-}
-
-static inline void setOptimizeData(EventTime time,
- size_t eid,
- const std::string& publisher) {
- // Store the optimization time and eid.
- std::string query_name;
- getDatabaseValue(kPersistentSettings, "", query_name);
- if (query_name.empty()) {
- return;
- }
-
- setDatabaseValue(kEvents, "optimize." + query_name, std::to_string(time));
- setDatabaseValue(kEvents, "optimize_eid." + query_name, toIndex(eid));
-}
-
-EventContextID EventPublisherPlugin::numEvents() const {
- return next_ec_id_.load();
-}
-
-size_t EventPublisherPlugin::numSubscriptions() {
- ReadLock lock(subscription_lock_);
- return subscriptions_.size();
-}
-
-void EventPublisherPlugin::fire(const EventContextRef& ec, EventTime time) {
- if (isEnding()) {
- // Cannot emit/fire while ending
- return;
- }
-
- EventContextID ec_id = 0;
- ec_id = next_ec_id_.fetch_add(1);
-
- // Fill in EventContext ID and time if needed.
- if (ec != nullptr) {
- ec->id = ec_id;
- if (ec->time == 0) {
- if (time == 0) {
- time = getUnixTime();
- }
- ec->time = time;
- }
- }
-
- ReadLock lock(subscription_lock_);
- for (const auto& subscription : subscriptions_) {
- auto es = EventFactory::getEventSubscriber(subscription->subscriber_name);
- if (es != nullptr && es->state() == EventState::EVENT_RUNNING) {
- fireCallback(subscription, ec);
- }
- }
-}
-
-std::vector<std::string> EventSubscriberPlugin::getIndexes(EventTime start,
- EventTime stop,
- bool sort) {
- auto index_key = "indexes." + dbNamespace();
- std::vector<std::string> indexes;
-
- EventTime l_start = (start > 0) ? start / 60 : 0;
- EventTime r_stop = (stop > 0) ? stop / 60 + 1 : 0;
-
- std::string content;
- getDatabaseValue(kEvents, index_key + ".60", content);
- if (content.empty()) {
- return indexes;
- }
-
- std::vector<std::string> bins, expirations;
- boost::split(bins, content, boost::is_any_of(","));
- for (const auto& bin : bins) {
- auto step = timeFromRecord(bin);
- auto step_start = step * 60;
- auto step_stop = (step + 1) * 60;
- if (step_stop <= expire_time_) {
- expirations.push_back(bin);
- } else if (step_start < expire_time_ && sort) {
- expireRecords("60", bin, false);
- }
-
- if (step >= l_start && (r_stop == 0 || step < r_stop) && sort) {
- indexes.push_back("60." + bin);
- }
- }
-
- // Rewrite the index lists and delete each expired item.
- if (!expirations.empty()) {
- expireIndexes("60", bins, expirations);
- }
-
- // Return indexes in binning order.
- if (sort) {
- std::sort(indexes.begin(),
- indexes.end(),
- [](const std::string& left, const std::string& right) {
- auto n1 = timeFromRecord(left.substr(left.find('.') + 1));
- auto n2 = timeFromRecord(right.substr(right.find('.') + 1));
- return n1 < n2;
- });
- }
-
- // Update the new time that events expire to now - expiry.
- return indexes;
-}
-
-void EventSubscriberPlugin::expireRecords(const std::string& list_type,
- const std::string& index,
- bool all) {
- if (!executedAllQueries()) {
- return;
- }
-
- auto record_key = "records." + dbNamespace();
- auto data_key = "data." + dbNamespace();
-
- // If the expirations is not removing all records, rewrite the persisting.
- std::vector<std::string> persisting_records;
- // Request all records within this list-size + bin offset.
- auto expired_records = getRecords({list_type + '.' + index}, false);
- if (all && expired_records.size() > 1) {
- deleteDatabaseRange(kEvents,
- data_key + '.' + expired_records.begin()->first,
- data_key + '.' + expired_records.rbegin()->first);
- } else {
- for (const auto& record : expired_records) {
- if (record.second <= expire_time_) {
- deleteDatabaseValue(kEvents, data_key + '.' + record.first);
- } else {
- persisting_records.push_back(record.first + ':' +
- std::to_string(record.second));
- }
- }
- }
-
- // Either drop or overwrite the record list.
- if (all) {
- deleteDatabaseValue(kEvents, record_key + "." + list_type + "." + index);
- } else if (persisting_records.size() < expired_records.size()) {
- auto new_records = boost::algorithm::join(persisting_records, ",");
- setDatabaseValue(
- kEvents, record_key + "." + list_type + "." + index, new_records);
- }
-}
-
-void EventSubscriberPlugin::expireIndexes(
- const std::string& list_type,
- const std::vector<std::string>& indexes,
- const std::vector<std::string>& expirations) {
- if (!executedAllQueries()) {
- return;
- }
-
- auto index_key = "indexes." + dbNamespace();
-
- // Construct a mutable list of persisting indexes to rewrite as records.
- std::vector<std::string> persisting_indexes = indexes;
- // Remove the records using the list of expired indexes.
- for (const auto& bin : expirations) {
- expireRecords(list_type, bin, true);
- persisting_indexes.erase(
- std::remove(persisting_indexes.begin(), persisting_indexes.end(), bin),
- persisting_indexes.end());
- }
-
- // Update the list of indexes with the non-expired indexes.
- auto new_indexes = boost::algorithm::join(persisting_indexes, ",");
- setDatabaseValue(kEvents, index_key + "." + list_type, new_indexes);
-}
-
-void EventSubscriberPlugin::expireCheck() {
- auto data_key = "data." + dbNamespace();
- auto eid_key = "eid." + dbNamespace();
- // Min key will be the last surviving key.
- size_t threshold_key = 0;
-
- {
- auto limit = getEventsMax();
- std::vector<std::string> keys;
- scanDatabaseKeys(kEvents, keys, data_key);
- if (keys.size() <= limit) {
- return;
- }
-
- // There is an overflow of events buffered for this subscriber.
- LOG(WARNING) << "Expiring events for subscriber: " << getName()
- << " (overflowed limit " << limit << ")";
- VLOG(1) << "Subscriber events " << getName() << " exceeded limit " << limit
- << " by: " << keys.size() - limit;
- // Inspect the N-FLAGS_events_max -th event's value and expire before the
- // time within the content.
- std::string last_key;
- getDatabaseValue(kEvents, eid_key, last_key);
- // The EID is the next-index.
- // EID - events_max is the most last-recent event to keep.
- threshold_key = boost::lexical_cast<size_t>(last_key) - getEventsMax();
-
- // Scan each of the keys in keys, if their ID portion is < threshold.
- // Nix them, this requires lots of conversions, use with care.
- std::string max_key;
- std::string min_key;
- unsigned long min_key_value = 0;
- unsigned long max_key_value = 0;
- for (const auto& key : keys) {
- auto const key_value =
- tryTo<unsigned long int>(key.substr(key.rfind('.') + 1), 10)
- .takeOr(0ul);
-
- if (key_value < static_cast<unsigned long>(threshold_key)) {
- min_key_value = (min_key_value == 0 || key_value < min_key_value)
- ? key_value
- : min_key_value;
- if (min_key_value == key_value) {
- min_key = key;
- }
-
- max_key_value = (key_value > max_key_value) ? key_value : max_key_value;
- if (max_key_value == key_value) {
- max_key = key;
- }
- }
- }
-
- if (!min_key.empty()) {
- if (max_key_value == min_key_value) {
- deleteDatabaseValue(kEvents, min_key);
- } else {
- deleteDatabaseRange(kEvents, min_key, max_key);
- }
- }
- }
-
- // Convert the key index into a time using the content.
- // The last-recent event is fetched and the corresponding time is used as
- // the expiration time for the subscriber.
- std::string content;
- getDatabaseValue(kEvents, data_key + "." + toIndex(threshold_key), content);
-
- // Decode the value into a row structure to extract the time.
- Row r;
- if (!deserializeRowJSON(content, r) || r.count("time") == 0) {
- return;
- }
-
- // The last time will become the implicit expiration time.
- size_t last_time = boost::lexical_cast<size_t>(r.at("time"));
- if (last_time > 0) {
- expire_time_ = last_time - (last_time % 60);
- }
-
- // Finally, attempt an index query to trigger expirations.
- // In this case the result set is not used.
- getIndexes(expire_time_, 0, false);
-}
-
-bool EventSubscriberPlugin::executedAllQueries() const {
- ReadLock lock(event_query_record_);
- return queries_.size() >= query_count_;
-}
-
-std::vector<EventRecord> EventSubscriberPlugin::getRecords(
- const std::vector<std::string>& indexes, bool optimize) {
- auto record_key = "records." + dbNamespace();
-
- std::vector<EventRecord> records;
- for (const auto& index : indexes) {
- std::vector<std::string> bin_records;
- {
- std::string record_value;
- getDatabaseValue(kEvents, record_key + "." + index, record_value);
- if (record_value.empty()) {
- // There are actually no events in this bin, interesting error case.
- continue;
- }
-
- // Each list is tokenized into a record=event_id:time.
- bin_records = split(record_value, ",");
- }
-
- // Iterate over every 2 items: EID:TIME.
- for (const auto& record : bin_records) {
- const auto vals = split(record, ":");
- if (vals.size() != 2) {
- LOG(WARNING) << "Event records mismatch: " << record
- << " does not have a matching eid/event_time";
- continue;
- }
- const auto eid = vals[0];
- const EventTime et = timeFromRecord(vals[1]);
- if (FLAGS_events_optimize && optimize && et <= optimize_time_ + 1) {
- auto eidr = timeFromRecord(eid);
- if (eidr <= optimize_eid_) {
- continue;
- }
- }
- records.push_back(std::make_pair(eid, et));
- }
- }
-
- return records;
-}
-
-Status EventSubscriberPlugin::recordEvents(
- const std::vector<std::string>& event_id_list, EventTime event_time) {
- WriteLock lock(event_record_lock_);
-
- DatabaseStringValueList database_data;
- database_data.reserve(2U);
-
- // The list key includes the list type (bin size) and the list ID (bin).
- // The list_id is the MOST-Specific key ID, the bin for this list.
- // If the event time was 13 and the time_list is 5 seconds, lid = 2.
- auto list_id = boost::lexical_cast<std::string>(event_time / 60);
- std::string time_value = boost::lexical_cast<std::string>(event_time);
-
- // The record is identified by the event type then module name.
- // Append the record (eid, unix_time) to the list bin.
- auto database_key = "records." + dbNamespace() + ".60." + list_id;
- auto index_key = "indexes." + dbNamespace() + ".60";
-
- std::string record_value;
- getDatabaseValue(kEvents, database_key, record_value);
-
- for (const auto& eid : event_id_list) {
- if (record_value.length() == 0) {
- // This is a new list_id for list_key, append the ID to the indirect
- // lookup for this list_key.
- std::string index_value;
- getDatabaseValue(kEvents, index_key, index_value);
- if (index_value.length() == 0) {
- // A new index.
- index_value = list_id;
- } else {
- index_value += "," + list_id;
- }
- database_data.push_back(std::make_pair(index_key, index_value));
- record_value = eid + ":" + time_value;
- } else {
- // Tokenize a record using ',' and the EID/time using ':'.
- record_value += "," + eid + ":" + time_value;
- }
- }
-
- database_data.push_back(std::make_pair(database_key, record_value));
- auto status = setDatabaseBatch(kEvents, database_data);
- if (!status.ok()) {
- LOG(ERROR) << "Could not put Event Records";
- }
-
- return status;
-}
-
-size_t EventSubscriberPlugin::getEventsExpiry() {
- return FLAGS_events_expiry;
-}
-
-size_t EventSubscriberPlugin::getEventsMax() {
- return FLAGS_events_max;
-}
-
-const std::string EventSubscriberPlugin::getEventID() {
- Status status;
- // First get an event ID from the meta key.
- std::string eid_key = "eid." + dbNamespace();
-
- {
- WriteLock lock(event_id_lock_);
- if (last_eid_ == 0) {
- std::string last_eid_value;
- status = getDatabaseValue(kEvents, eid_key, last_eid_value);
- if (!status.ok() || last_eid_value.empty()) {
- last_eid_value = "0";
- }
- last_eid_ = boost::lexical_cast<size_t>(last_eid_value);
- }
-
- if (last_eid_ % 10 == 0) {
- status = setDatabaseValue(kEvents, eid_key, toIndex(last_eid_ + 10));
- }
- last_eid_++;
- }
-
- return toIndex(last_eid_);
-}
-
-Status EventSubscriberPlugin::add(const Row& r) {
- std::vector<Row> batch = {r};
- return addBatch(batch, getUnixTime());
-}
-
-Status EventSubscriberPlugin::addBatch(std::vector<Row>& row_list) {
- return addBatch(row_list, getUnixTime());
-}
-
-Status EventSubscriberPlugin::addBatch(std::vector<Row>& row_list,
- EventTime custom_event_time) {
- DatabaseStringValueList database_data;
- database_data.reserve(row_list.size());
-
- std::vector<std::string> event_id_list;
- event_id_list.reserve(row_list.size());
-
- auto event_time = custom_event_time != 0 ? custom_event_time : getUnixTime();
- auto event_time_str = std::to_string(event_time);
-
- for (auto& row : row_list) {
- row["time"] = event_time_str;
- row["eid"] = getEventID();
-
- // Serialize and store the row data, for query-time retrieval.
- std::string serialized_row;
- auto status = serializeRowJSON(row, serialized_row);
- if (!status.ok()) {
- VLOG(1) << status.getMessage();
- continue;
- }
-
- // Then remove the newline.
- if (serialized_row.size() > 0 && serialized_row.back() == '\n') {
- serialized_row.pop_back();
- }
-
- // Logger plugins may request events to be forwarded directly.
- // If no active logger is marked 'usesLogEvent' then this is a no-op.
- EventFactory::forwardEvent(serialized_row);
-
- // Store the event data in the batch
- database_data.push_back(std::make_pair(
- "data." + dbNamespace() + "." + row["eid"], serialized_row));
-
- event_id_list.push_back(std::move(row["eid"]));
- event_count_++;
-
- // Use the last EventID and a checkpoint bucket size to periodically apply
- // buffer eviction. Eviction occurs if the total count exceeds events_max.
- if (last_eid_ % EVENTS_CHECKPOINT == 0) {
- expireCheck();
- }
- }
-
- if (database_data.empty()) {
- return Status(1, "Failed to process the rows");
- }
-
- // Save the batched data inside the database
- auto status = setDatabaseBatch(kEvents, database_data);
- if (!status.ok()) {
- return status;
- }
-
- return recordEvents(event_id_list, event_time);
-}
-
-EventPublisherRef EventSubscriberPlugin::getPublisher() const {
- return EventFactory::getEventPublisher(getType());
-}
-
-void EventSubscriberPlugin::removeSubscriptions() {
- subscription_count_ = 0;
- auto publisher = getPublisher();
- if (publisher == nullptr) {
- LOG(WARNING) << "Cannot remove subscriptions from: " << getName();
- return;
- }
-
- getPublisher()->removeSubscriptions(getName());
-}
-
-void EventFactory::delay() {
- // Caller may disable event publisher threads.
- if (FLAGS_disable_events) {
- return;
- }
-
- // Create a thread for each event publisher.
- auto& ef = EventFactory::getInstance();
- for (const auto& publisher : EventFactory::getInstance().event_pubs_) {
- // Publishers that did not set up correctly are put into an ending state.
- if (!publisher.second->isEnding()) {
- auto thread_ = std::make_shared<std::thread>(
- std::bind(&EventFactory::run, publisher.first));
- ef.threads_.push_back(thread_);
- }
- }
-}
-
-Status EventPublisherPlugin::addSubscription(
- const SubscriptionRef& subscription) {
- // The publisher threads may be running and if they fire events the list of
- // subscriptions will be walked.
- WriteLock lock(subscription_lock_);
- subscriptions_.push_back(subscription);
- return Status(0);
-}
-
-void EventPublisherPlugin::removeSubscriptions(const std::string& subscriber) {
- // See addSubscription for details on the critical section.
- WriteLock lock(subscription_lock_);
- auto end =
- std::remove_if(subscriptions_.begin(),
- subscriptions_.end(),
- [&subscriber](const SubscriptionRef& subscription) {
- return (subscription->subscriber_name == subscriber);
- });
- subscriptions_.erase(end, subscriptions_.end());
-}
-
-void EventFactory::addForwarder(const std::string& logger) {
- getInstance().loggers_.push_back(logger);
-}
-
-void EventFactory::forwardEvent(const std::string& event) {
- for (const auto& logger : getInstance().loggers_) {
- Registry::call("logger", logger, {{"event", event}});
- }
-}
-
-void EventFactory::configUpdate() {
- // Scan the schedule for queries that touch "_events" tables.
- // We will count the queries
- std::map<std::string, SubscriberExpirationDetails> subscriber_details;
- auto& ef = EventFactory::getInstance();
- for (const auto& details : subscriber_details) {
- if (!ef.exists(details.first)) {
- continue;
- }
-
- WriteLock lock(ef.factory_lock_);
- auto subscriber = ef.getEventSubscriber(details.first);
- subscriber->min_expiration_ = details.second.max_interval * 3;
- subscriber->min_expiration_ += (60 - (subscriber->min_expiration_ % 60));
-
- // Emit a warning for each subscriber affected by the small expiration.
- auto expiry = subscriber->getEventsExpiry();
- if (expiry > 0 && subscriber->min_expiration_ > expiry) {
- LOG(INFO) << "Subscriber expiration is too low: "
- << subscriber->getName();
- }
- subscriber->query_count_ = details.second.query_count;
-
- WriteLock subscriber_lock(subscriber->event_query_record_);
- subscriber->queries_.clear();
- }
-
- // If events are enabled configure the subscribers before publishers.
- if (!FLAGS_disable_events) {
- RegistryFactory::get().registry("event_subscriber")->configure();
- RegistryFactory::get().registry("event_publisher")->configure();
- }
-}
-
-Status EventFactory::run(const std::string& type_id) {
- if (FLAGS_disable_events) {
- return Status::success();
- }
-
- // An interesting take on an event dispatched entrypoint.
- // There is little introspection into the event type.
- // Assume it can either make use of an entrypoint poller/selector or
- // take care of async callback registrations in setUp/configure/run
- // only once and handle event queuing/firing in callbacks.
- EventPublisherRef publisher = nullptr;
- {
- auto& ef = EventFactory::getInstance();
- WriteLock lock(ef.factory_lock_);
- publisher = ef.getEventPublisher(type_id);
- }
-
- if (publisher == nullptr) {
- return Status(1, "Event publisher is missing");
- } else if (publisher->hasStarted()) {
- return Status(1, "Cannot restart an event publisher");
- }
- VLOG(1) << "Starting event publisher run loop: " + type_id;
- publisher->hasStarted(true);
-
- auto status = Status(0, "OK");
- while (!publisher->isEnding()) {
- // Can optionally implement a global cooloff latency here.
- status = publisher->run();
- if (!status.ok()) {
- break;
- }
- publisher->restart_count_++;
- }
- if (!status.ok()) {
- // The runloop status is not reflective of the event type's.
- VLOG(1) << "Event publisher " << publisher->type()
- << " run loop terminated for reason: " << status.getMessage();
- // Publishers auto tear down when their run loop stops.
- }
- publisher->tearDown();
- publisher->state(EventState::EVENT_NONE);
-
- // Do not remove the publisher from the event factory.
- // If the event factory's `end` method was called these publishers will be
- // cleaned up after their thread context is removed; otherwise, a removed
- // thread context and failed publisher will remain available for stats.
- return Status::success();
-}
-
-// There's no reason for the event factory to keep multiple instances.
-EventFactory& EventFactory::getInstance() {
- static EventFactory ef;
- return ef;
-}
-
-Status EventFactory::registerEventPublisher(const PluginRef& pub) {
- // Try to downcast the plugin to an event publisher.
- EventPublisherRef specialized_pub;
- try {
- auto base_pub = std::dynamic_pointer_cast<EventPublisherPlugin>(pub);
- specialized_pub = std::static_pointer_cast<BaseEventPublisher>(base_pub);
- } catch (const std::bad_cast& /* e */) {
- return Status(1, "Incorrect plugin");
- }
-
- if (specialized_pub == nullptr || specialized_pub.get() == nullptr) {
- return Status::success();
- }
-
- auto type_id = specialized_pub->type();
- if (type_id.empty()) {
- // This subscriber did not override its name.
- return Status(1, "Publishers must have a type");
- }
-
- auto& ef = EventFactory::getInstance();
- {
- WriteLock lock(getInstance().factory_lock_);
- if (ef.event_pubs_.count(type_id) != 0) {
- // This is a duplicate event publisher.
- return Status(1, "Duplicate publisher type");
- }
-
- ef.event_pubs_[type_id] = specialized_pub;
- }
-
- // Do not set up event publisher if events are disabled.
- if (!FLAGS_disable_events) {
- if (specialized_pub->state() != EventState::EVENT_NONE) {
- specialized_pub->tearDown();
- }
-
- auto status = specialized_pub->setUp();
- specialized_pub->state(EventState::EVENT_SETUP);
- if (!status.ok()) {
- // Only start event loop if setUp succeeds.
- LOG(INFO) << "Event publisher not enabled: " << type_id << ": "
- << status.what();
- specialized_pub->isEnding(true);
- return status;
- }
- }
-
- return Status::success();
-}
-
-Status EventFactory::registerEventSubscriber(const PluginRef& sub) {
- // Try to downcast the plugin to an event subscriber.
- EventSubscriberRef specialized_sub;
- try {
- auto base_sub = std::dynamic_pointer_cast<EventSubscriberPlugin>(sub);
- specialized_sub = std::static_pointer_cast<BaseEventSubscriber>(base_sub);
- } catch (const std::bad_cast& /* e */) {
- return Status(1, "Incorrect plugin");
- }
-
- if (specialized_sub == nullptr || specialized_sub.get() == nullptr) {
- return Status(1, "Invalid subscriber");
- }
-
- // The config may use an "events" key to explicitly enabled or disable
- // event subscribers. See EventSubscriber::disable.
- auto name = specialized_sub->getName();
- if (name.empty()) {
- // This subscriber did not override its name.
- return Status(1, "Subscribers must have set a name");
- }
-
- if (specialized_sub->state() != EventState::EVENT_NONE) {
- specialized_sub->tearDown();
- }
-
- // Allow subscribers a configure-time setup to determine if they should run.
- auto status = specialized_sub->setUp();
- if (!status) {
- specialized_sub->disabled = true;
- }
- specialized_sub->state(EventState::EVENT_SETUP);
-
- // Let the subscriber initialize any Subscriptions.
- if (!FLAGS_disable_events && !specialized_sub->disabled) {
- specialized_sub->expireCheck();
- status = specialized_sub->init();
- specialized_sub->state(EventState::EVENT_RUNNING);
- } else {
- specialized_sub->state(EventState::EVENT_PAUSED);
- }
-
- auto& ef = EventFactory::getInstance();
- {
- WriteLock lock(getInstance().factory_lock_);
- ef.event_subs_[name] = specialized_sub;
- }
-
- // Set state of subscriber.
- if (!status.ok()) {
- specialized_sub->state(EventState::EVENT_FAILED);
- return Status(1, status.getMessage());
- } else {
- return Status::success();
- }
-}
-
-Status EventFactory::addSubscription(const std::string& type_id,
- const std::string& name_id,
- const SubscriptionContextRef& mc,
- EventCallback cb) {
- auto subscription = Subscription::create(name_id, mc, cb);
- return EventFactory::addSubscription(type_id, subscription);
-}
-
-Status EventFactory::addSubscription(const std::string& type_id,
- const SubscriptionRef& subscription) {
- EventPublisherRef publisher = getInstance().getEventPublisher(type_id);
- if (publisher == nullptr) {
- return Status(1, "Unknown event publisher");
- }
-
- // The event factory is responsible for configuring the event types.
- return publisher->addSubscription(subscription);
-}
-
-size_t EventFactory::numSubscriptions(const std::string& type_id) {
- EventPublisherRef publisher;
- try {
- publisher = EventFactory::getInstance().getEventPublisher(type_id);
- } catch (std::out_of_range& /* e */) {
- return 0;
- }
- if (publisher == nullptr) {
- return 0;
- }
- return publisher->numSubscriptions();
-}
-
-EventPublisherRef EventFactory::getEventPublisher(const std::string& type_id) {
- if (getInstance().event_pubs_.count(type_id) == 0) {
- LOG(ERROR) << "Requested unknown/failed event publisher: " + type_id;
- return nullptr;
- }
- return getInstance().event_pubs_.at(type_id);
-}
-
-EventSubscriberRef EventFactory::getEventSubscriber(
- const std::string& name_id) {
- if (!exists(name_id)) {
- LOG(ERROR) << "Requested unknown event subscriber: " + name_id;
- return nullptr;
- }
- return getInstance().event_subs_.at(name_id);
-}
-
-bool EventFactory::exists(const std::string& name_id) {
- return (getInstance().event_subs_.count(name_id) > 0);
-}
-
-Status EventFactory::deregisterEventPublisher(const EventPublisherRef& pub) {
- return EventFactory::deregisterEventPublisher(pub->type());
-}
-
-Status EventFactory::deregisterEventPublisher(const std::string& type_id) {
- auto& ef = EventFactory::getInstance();
-
- WriteLock lock(ef.factory_lock_);
- EventPublisherRef publisher = ef.getEventPublisher(type_id);
- if (publisher == nullptr) {
- return Status(1, "No event publisher to deregister");
- }
-
- if (!FLAGS_disable_events) {
- publisher->isEnding(true);
- if (!publisher->hasStarted()) {
- // If a publisher's run loop was not started, call tearDown since
- // the setUp happened at publisher registration time.
- publisher->tearDown();
- publisher->state(EventState::EVENT_NONE);
- // If the run loop did run the tear down and erase will happen in the
- // event thread wrapper when isEnding is next checked.
- ef.event_pubs_.erase(type_id);
- }
- }
- return Status::success();
-}
-
-Status EventFactory::deregisterEventSubscriber(const std::string& sub) {
- auto& ef = EventFactory::getInstance();
-
- WriteLock lock(ef.factory_lock_);
- if (ef.event_subs_.count(sub) == 0) {
- return Status(1, "Event subscriber is missing");
- }
-
- auto& subscriber = ef.event_subs_.at(sub);
- subscriber->state(EventState::EVENT_NONE);
- subscriber->tearDown();
- ef.event_subs_.erase(sub);
- return Status(0);
-}
-
-std::vector<std::string> EventFactory::publisherTypes() {
- WriteLock lock(getInstance().factory_lock_);
- std::vector<std::string> types;
- for (const auto& publisher : getInstance().event_pubs_) {
- types.push_back(publisher.first);
- }
- return types;
-}
-
-std::vector<std::string> EventFactory::subscriberNames() {
- WriteLock lock(getInstance().factory_lock_);
- std::vector<std::string> names;
- for (const auto& subscriber : getInstance().event_subs_) {
- names.push_back(subscriber.first);
- }
- return names;
-}
-
-void EventFactory::end(bool join) {
- auto& ef = EventFactory::getInstance();
-
- // Call deregister on each publisher.
- for (const auto& publisher : ef.publisherTypes()) {
- deregisterEventPublisher(publisher);
- }
-
- // Stop handling exceptions for the publisher threads.
- for (const auto& thread : ef.threads_) {
- if (join) {
- thread->join();
- } else {
- thread->detach();
- }
- }
-
- {
- WriteLock lock(getInstance().factory_lock_);
- // A small cool off helps OS API event publisher flushing.
- if (!FLAGS_disable_events) {
- ef.threads_.clear();
- }
-
- // Threads may still be executing, when they finish, release publishers.
- ef.event_pubs_.clear();
- ef.event_subs_.clear();
- }
-}
-
-void attachEvents() {
- const auto& publishers = RegistryFactory::get().plugins("event_publisher");
- for (const auto& publisher : publishers) {
- EventFactory::registerEventPublisher(publisher.second);
- }
-
- const auto& subscribers = RegistryFactory::get().plugins("event_subscriber");
- for (const auto& subscriber : subscribers) {
- if (!boost::ends_with(subscriber.first, "_events")) {
- LOG(ERROR) << "Error registering subscriber: " << subscriber.first
- << ": Must use a '_events' suffix";
- continue;
- }
-
- auto status = EventFactory::registerEventSubscriber(subscriber.second);
- if (!status.ok()) {
- VLOG(1) << "Error registering subscriber: " << subscriber.first << ": "
- << status.getMessage();
- }
- }
-
- // Configure the event publishers and subscribers.
- EventFactory::configUpdate();
-}
-} // namespace osquery
+++ /dev/null
-/**
- * Copyright (c) 2014-present, Facebook, Inc.
- * All rights reserved.
- *
- * This source code is licensed in accordance with the terms specified in
- * the LICENSE file found in the root directory of this source tree.
- */
-
-#include <linux/audit.h>
-#include <poll.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include <chrono>
-#include <iostream>
-
-#include <boost/utility/string_ref.hpp>
-
-#include <osquery/events/linux/auditdnetlink.h>
-#include <osquery/events/linux/process_events.h>
-#include <osquery/events/linux/process_file_events.h>
-#include <osquery/events/linux/selinux_events.h>
-#include <osquery/events/linux/socket_events.h>
-#include <osquery/flags.h>
-#include <osquery/logger.h>
-#include <osquery/utils/conversions/tryto.h>
-#include <osquery/utils/expected/expected.h>
-#include <osquery/utils/system/time.h>
-
-namespace osquery {
-/// Control the audit subsystem by electing to be the single process sink.
-FLAG(bool, audit_persist, true, "Attempt to retain control of audit");
-
-/// Audit debugger helper
-HIDDEN_FLAG(bool, audit_debug, false, "Debug Linux audit messages");
-
-/// Control the audit subsystem by allowing subscriptions to apply rules.
-FLAG(bool,
- audit_allow_config,
- false,
- "Allow the audit publisher to change auditing configuration");
-
-/// Always uninstall all the audit rules that osquery uses when exiting
-FLAG(bool,
- audit_force_unconfigure,
- false,
- "Always uninstall all rules, regardless of whether they were already "
- "installed or not");
-
-/// Forces osquery to remove all rules upon startup
-FLAG(bool,
- audit_force_reconfigure,
- false,
- "Configure the audit subsystem from scratch");
-
-// External flags; they are used to determine which rules need to be installed
-DECLARE_bool(audit_allow_fim_events);
-DECLARE_bool(audit_allow_process_events);
-DECLARE_bool(audit_allow_sockets);
-DECLARE_bool(audit_allow_user_events);
-DECLARE_bool(audit_allow_selinux_events);
-
-// user messages should be filtered
-// also, we should handle the 2nd user message type
-namespace {
-bool IsSELinuxRecord(const audit_reply& reply) noexcept {
- static const auto& selinux_event_set = kSELinuxEventList;
- return (selinux_event_set.find(reply.type) != selinux_event_set.end());
-}
-
-bool ShouldHandle(const audit_reply& reply) noexcept {
- if (IsSELinuxRecord(reply)) {
- return FLAGS_audit_allow_selinux_events;
- }
-
- switch (reply.type) {
- case NLMSG_NOOP:
- case NLMSG_DONE:
- case NLMSG_ERROR:
- case AUDIT_LIST_RULES:
- case AUDIT_SECCOMP:
- case AUDIT_GET:
- case (AUDIT_GET + 1)...(AUDIT_LIST_RULES - 1):
- case (AUDIT_LIST_RULES + 1)...(AUDIT_FIRST_USER_MSG - 1):
- case AUDIT_DAEMON_START ... AUDIT_DAEMON_CONFIG: // 1200 - 1203
- case AUDIT_CONFIG_CHANGE:
- return false;
-
- default:
- return true;
- }
-}
-} // namespace
-
-enum AuditStatus {
- AUDIT_DISABLED = 0,
- AUDIT_ENABLED = 1,
- AUDIT_IMMUTABLE = 2,
-};
-
-AuditdNetlink::AuditdNetlink() {
- try {
- auditd_context_ = std::make_shared<AuditdContext>();
-
- Dispatcher::addService(
- std::make_shared<AuditdNetlinkReader>(auditd_context_));
-
- Dispatcher::addService(
- std::make_shared<AuditdNetlinkParser>(auditd_context_));
-
- } catch (const std::bad_alloc&) {
- VLOG(1) << "Failed to initialize the AuditdNetlink services due to a "
- "memory allocation error";
- throw;
- }
-}
-
-std::vector<AuditEventRecord> AuditdNetlink::getEvents() noexcept {
- std::vector<AuditEventRecord> record_list;
-
- {
- std::unique_lock<std::mutex> queue_lock(
- auditd_context_->processed_events_mutex);
-
- if (auditd_context_->processed_records_cv.wait_for(
- queue_lock, std::chrono::seconds(1)) ==
- std::cv_status::no_timeout) {
- record_list = std::move(auditd_context_->processed_events);
- auditd_context_->processed_events.clear();
- }
- }
-
- return record_list;
-}
-
-AuditdNetlinkReader::AuditdNetlinkReader(AuditdContextRef context)
- : InternalRunnable("AuditdNetlinkReader"),
- auditd_context_(std::move(context)),
- read_buffer_(4096U) {}
-
-void AuditdNetlinkReader::start() {
- int counter_to_next_status_request = 0;
- const int status_request_countdown = 1000;
-
- while (!interrupted()) {
- if (auditd_context_->acquire_handle) {
- if (FLAGS_audit_debug) {
- std::cout << "(re)acquiring the audit handle.." << std::endl;
- }
-
- NetlinkStatus netlink_status = acquireHandle();
-
- if (netlink_status == NetlinkStatus::Disabled ||
- netlink_status == NetlinkStatus::Error) {
- std::this_thread::sleep_for(std::chrono::seconds(5));
- continue;
- }
-
- auditd_context_->acquire_handle = false;
- counter_to_next_status_request = status_request_countdown;
- }
-
- if (counter_to_next_status_request == 0) {
- errno = 0;
-
- if (audit_request_status(audit_netlink_handle_) <= 0) {
- if (errno == ENOBUFS) {
- VLOG(1) << "Warning: Failed to request audit status (ENOBUFS). "
- "Retrying again later";
-
- } else {
- VLOG(1) << "Error: Failed to request audit status. Requesting a "
- "handle reset";
-
- auditd_context_->acquire_handle = true;
- }
- }
-
- counter_to_next_status_request = status_request_countdown;
- } else {
- --counter_to_next_status_request;
- }
-
- if (!acquireMessages()) {
- auditd_context_->acquire_handle = true;
- }
- }
-}
-
-void AuditdNetlinkReader::stop() {
- if (audit_netlink_handle_ == -1) {
- return;
- }
-
- VLOG(1) << "Releasing the audit handle...";
-
- auditd_context_->unprocessed_records_cv.notify_all();
- restoreAuditServiceConfiguration();
-
- audit_close(audit_netlink_handle_);
- audit_netlink_handle_ = -1;
-}
-
-bool AuditdNetlinkReader::acquireMessages() noexcept {
- pollfd fds[] = {{audit_netlink_handle_, POLLIN, 0}};
-
- struct sockaddr_nl nladdr = {};
- socklen_t nladdrlen = sizeof(nladdr);
-
- bool reset_handle = false;
- size_t events_received = 0;
-
- // Attempt to read as many messages as possible before we exit, and terminate
- // early if we have been asked to terminate
- for (events_received = 0;
- !interrupted() && events_received < read_buffer_.size();
- events_received++) {
- errno = 0;
- int poll_status = ::poll(fds, 1, 2000);
- if (poll_status == 0) {
- break;
- }
-
- if (poll_status < 0) {
- if (errno != EINTR) {
- reset_handle = true;
- VLOG(1) << "poll() failed with error " << errno;
- }
-
- break;
- }
-
- if ((fds[0].revents & POLLIN) == 0) {
- break;
- }
-
- audit_reply reply = {};
- ssize_t len = recvfrom(audit_netlink_handle_,
- &reply.msg,
- sizeof(reply.msg),
- 0,
- reinterpret_cast<struct sockaddr*>(&nladdr),
- &nladdrlen);
-
- if (len < 0) {
- VLOG(1) << "Failed to receive data from the audit netlink";
- reset_handle = true;
- break;
- }
-
- if (nladdrlen != sizeof(nladdr)) {
- VLOG(1) << "Protocol error";
- reset_handle = true;
- break;
- }
-
- if (nladdr.nl_pid) {
- VLOG(1) << "Invalid netlink endpoint";
- reset_handle = true;
- break;
- }
-
- if (!NLMSG_OK(&reply.msg.nlh, static_cast<unsigned int>(len))) {
- if (len == sizeof(reply.msg)) {
- VLOG(1) << "Netlink event too big (EFBIG)";
- } else {
- VLOG(1) << "Broken netlink event (EBADE)";
- }
-
- reset_handle = true;
- break;
- }
-
- read_buffer_[events_received] = reply;
- }
-
- if (events_received != 0) {
- std::unique_lock<std::mutex> lock(
- auditd_context_->unprocessed_records_mutex);
-
- auditd_context_->unprocessed_records.reserve(
- auditd_context_->unprocessed_records.size() + events_received);
-
- auditd_context_->unprocessed_records.insert(
- auditd_context_->unprocessed_records.end(),
- read_buffer_.begin(),
- std::next(read_buffer_.begin(), events_received));
-
- auditd_context_->unprocessed_records_cv.notify_all();
- }
-
- if (reset_handle) {
- VLOG(1) << "Requesting audit handle reset";
- return false;
- }
-
- return true;
-}
-
-bool AuditdNetlinkReader::configureAuditService() noexcept {
- VLOG(1) << "Attempting to configure the audit service";
-
- // Want to set a min sane buffer and maximum number of events/second min.
- // This is normally controlled through the audit config, but we must
- // enforce sane minimums: -b 8192 -e 100
- audit_set_backlog_wait_time(audit_netlink_handle_, 1);
- audit_set_backlog_limit(audit_netlink_handle_, 4096);
- audit_set_failure(audit_netlink_handle_, AUDIT_FAIL_SILENT);
-
- // Request only the highest priority of audit status messages.
- set_aumessage_mode(MSG_QUIET, DBG_NO);
-
- //
- // Audit rules
- //
-
- // Rules required by the socket_events table
- if (FLAGS_audit_allow_sockets) {
- VLOG(1) << "Enabling audit rules for the socket_events table";
-
- for (int syscall : kSocketEventsSyscalls) {
- monitored_syscall_list_.insert(syscall);
- }
- }
-
- // Rules required by the process_events table
- if (FLAGS_audit_allow_process_events) {
- VLOG(1) << "Enabling audit rules for the process_events table";
-
- for (int syscall : kProcessEventsSyscalls) {
- monitored_syscall_list_.insert(syscall);
- }
- }
-
- // Rules required by the process_file_events table
- if (FLAGS_audit_allow_fim_events) {
- VLOG(1) << "Enabling audit rules for the process_file_events table";
-
- for (int syscall : kProcessFileEventsSyscalls) {
- monitored_syscall_list_.insert(syscall);
- }
- }
-
- // Attempt to add each one of the rules we collected
- for (int syscall_number : monitored_syscall_list_) {
- audit_rule_data rule = {};
- audit_rule_syscall_data(&rule, syscall_number);
-
- // clang-format off
- int rule_add_error = audit_add_rule_data(audit_netlink_handle_, &rule,
- // We want to be notified when we exit from the syscall
- AUDIT_FILTER_EXIT,
-
- // Always audit this syscall event
- AUDIT_ALWAYS
- );
- // clang-format on
-
- // When exiting, don't remove the rules that were already installed, unless
- // we have been asked to
- if (rule_add_error >= 0) {
- if (FLAGS_audit_debug) {
- std::cout << "Audit rule installed for syscall " << syscall_number
- << std::endl;
- }
-
- installed_rule_list_.push_back(rule);
- continue;
- }
-
- if (FLAGS_audit_debug) {
- std::cout << "Audit rule for syscall " << syscall_number
- << " could not be installed. Errno: " << (-errno) << std::endl;
- }
-
- if (FLAGS_audit_force_unconfigure) {
- installed_rule_list_.push_back(rule);
- }
-
- rule_add_error = -rule_add_error;
-
- if (rule_add_error != EEXIST) {
- VLOG(1) << "The following syscall number could not be added to the audit "
- "service rules: "
- << syscall_number << ". Some of the auditd "
- << "table may not work properly (process_events, "
- << "socket_events, process_file_events, user_events)";
- }
- }
-
- return true;
-}
-
-bool AuditdNetlinkReader::clearAuditConfiguration() noexcept {
- int seq = audit_request_rules_list_data(audit_netlink_handle_);
- if (seq <= 0) {
- VLOG(1) << "Failed to list the audit rules";
- return false;
- }
-
- // Attempt to list all rules
- std::vector<AuditRuleDataObject> rule_object_list;
- auto timeout = getUnixTime() + 5;
-
- for (size_t read_retry = 0U; read_retry < 3U; ++read_retry) {
- if (timeout < getUnixTime()) {
- VLOG(1) << "Failed to unconfigure the audit service (timeout)";
- return false;
- }
-
- bool netlink_ready = false;
-
- for (size_t poll_retry = 0U; poll_retry < 3U; ++poll_retry) {
- pollfd fds[] = {{audit_netlink_handle_, POLLIN, 0}};
-
- errno = 0;
- int poll_status = ::poll(fds, 1, 4);
- if (poll_status == 0) {
- continue;
- }
-
- if (poll_status < 0) {
- VLOG(1) << "poll() failed with errno " << errno;
- return false;
- }
-
- if ((fds[0].revents & POLLIN) != 0) {
- netlink_ready = true;
- break;
- }
- }
-
- if (!netlink_ready) {
- VLOG(1) << "Could not read from the audit netlink";
- return false;
- }
-
- // Get the reply from the audit link
- struct audit_reply reply = {};
- if (audit_get_reply(
- audit_netlink_handle_, &reply, GET_REPLY_NONBLOCKING, 0) <= 0) {
- continue;
- }
-
- read_retry = 0;
- if (reply.nlh->nlmsg_seq != static_cast<unsigned int>(seq)) {
- continue;
- }
-
- if (reply.type == NLMSG_DONE) {
- // We have finished listing the rules
- break;
- }
-
- if (reply.type == NLMSG_ERROR && reply.error->error != 0) {
- return false;
- }
-
- if (reply.type != AUDIT_LIST_RULES) {
- // Skip this reply if it is not part of the rule list output
- continue;
- }
-
- // Save the rule
- const auto reply_size = sizeof(reply) + reply.ruledata->buflen;
-
- AuditRuleDataObject reply_object(reply_size);
-
- std::memcpy(reply_object.data(), reply.ruledata, reply_size);
- rule_object_list.push_back(reply_object);
- }
-
- // Delete each rule
- size_t error_count = 0U;
- for (auto& rule_object : rule_object_list) {
- if (!deleteAuditRule(rule_object)) {
- error_count++;
- }
- }
-
- if (error_count != 0U) {
- VLOG(1) << error_count << " audit rules could not be correctly removed";
- return false;
- }
-
- VLOG(1) << "The audit configuration has been cleared";
- return true;
-}
-
-bool AuditdNetlinkReader::deleteAuditRule(
- const AuditRuleDataObject& rule_object) {
- if (NLMSG_SPACE(rule_object.size()) > MAX_AUDIT_MESSAGE_LENGTH) {
- return false;
- }
-
- auto rule_data =
- reinterpret_cast<const struct audit_rule_data*>(rule_object.data());
-
- struct audit_message request = {};
- request.nlh.nlmsg_len = static_cast<__u32>(NLMSG_SPACE(rule_object.size()));
- request.nlh.nlmsg_type = AUDIT_DEL_RULE;
- request.nlh.nlmsg_flags = NLM_F_REQUEST;
- std::memcpy(NLMSG_DATA(&request.nlh), rule_data, rule_object.size());
-
- struct sockaddr_nl address = {};
- address.nl_family = AF_NETLINK;
-
- bool success = false;
-
- for (size_t retry = 0U; retry < 3U; retry++) {
- ssize_t bytes_sent;
-
- while (true) {
- errno = 0;
- bytes_sent = sendto(audit_netlink_handle_,
- &request,
- request.nlh.nlmsg_len,
- 0,
- reinterpret_cast<struct sockaddr*>(&address),
- sizeof(address));
- if (bytes_sent >= 0) {
- break;
- }
-
- if (errno != EINTR) {
- return false;
- }
- }
-
- if (bytes_sent == static_cast<ssize_t>(request.nlh.nlmsg_len)) {
- success = true;
- break;
- }
- }
-
- return success;
-}
-
-void AuditdNetlinkReader::restoreAuditServiceConfiguration() noexcept {
- if (FLAGS_audit_debug) {
- std::cout << "Uninstalling audit rules" << std::endl;
- }
-
- // Remove the rules we have added
- VLOG(1) << "Uninstalling the audit rules we have installed";
-
- for (auto& rule : installed_rule_list_) {
- audit_delete_rule_data(
- audit_netlink_handle_, &rule, AUDIT_FILTER_EXIT, AUDIT_ALWAYS);
- }
-
- installed_rule_list_.clear();
-
- // Restore audit configuration defaults.
- if (FLAGS_audit_debug) {
- std::cout << "Restoring default settings and disabling the service"
- << std::endl;
- }
-
- VLOG(1) << "Restoring the default configuration for the audit service";
-
- audit_set_backlog_limit(audit_netlink_handle_, 0);
- audit_set_backlog_wait_time(audit_netlink_handle_, 60000);
- audit_set_failure(audit_netlink_handle_, AUDIT_FAIL_PRINTK);
- audit_set_enabled(audit_netlink_handle_, AUDIT_DISABLED);
-}
-
-NetlinkStatus AuditdNetlinkReader::acquireHandle() noexcept {
- // Returns the audit netlink status
- auto L_GetNetlinkStatus = [](int netlink_handle) -> NetlinkStatus {
- if (netlink_handle <= 0) {
- return NetlinkStatus::Error;
- }
-
- errno = 0;
- if (audit_request_status(netlink_handle) < 0 && errno != ENOBUFS) {
- VLOG(1) << "Failed to query the audit netlink status";
- return NetlinkStatus::Error;
- }
-
- auto enabled = audit_is_enabled(netlink_handle);
-
- if (enabled == AUDIT_IMMUTABLE || getuid() != 0 ||
- !FLAGS_audit_allow_config) {
- return NetlinkStatus::ActiveImmutable;
-
- } else if (enabled == AUDIT_ENABLED) {
- return NetlinkStatus::ActiveMutable;
-
- } else if (enabled == AUDIT_DISABLED) {
- return NetlinkStatus::Disabled;
-
- } else {
- return NetlinkStatus::Error;
- }
- };
-
- if (audit_netlink_handle_ != -1) {
- audit_close(audit_netlink_handle_);
- audit_netlink_handle_ = -1;
- }
-
- audit_netlink_handle_ = audit_open();
- if (audit_netlink_handle_ <= 0) {
- VLOG(1) << "Failed to acquire the netlink handle";
-
- audit_netlink_handle_ = -1;
- return NetlinkStatus::Error;
- }
-
- if (audit_set_pid(audit_netlink_handle_, getpid(), WAIT_NO) < 0) {
- VLOG(1) << "Failed to set the netlink owner";
-
- audit_close(audit_netlink_handle_);
- audit_netlink_handle_ = -1;
-
- return NetlinkStatus::Error;
- }
-
- NetlinkStatus netlink_status = L_GetNetlinkStatus(audit_netlink_handle_);
- if (FLAGS_audit_allow_config &&
- (netlink_status != NetlinkStatus::ActiveMutable &&
- netlink_status != NetlinkStatus::ActiveImmutable)) {
- if (audit_set_enabled(audit_netlink_handle_, AUDIT_ENABLED) < 0) {
- VLOG(1) << "Failed to enable the audit service";
-
- audit_close(audit_netlink_handle_);
- audit_netlink_handle_ = -1;
-
- return NetlinkStatus::Error;
- }
-
- if (FLAGS_audit_debug) {
- std::cout << "Audit service enabled" << std::endl;
- }
- }
-
- if (FLAGS_audit_allow_config) {
- if (FLAGS_audit_force_reconfigure) {
- if (!clearAuditConfiguration()) {
- audit_netlink_handle_ = -1;
- return NetlinkStatus::Error;
- }
- }
-
- if (!configureAuditService()) {
- return NetlinkStatus::ActiveImmutable;
- }
-
- if (FLAGS_audit_debug) {
- std::cout << "Audit service configured" << std::endl;
- }
- }
-
- return NetlinkStatus::ActiveMutable;
-}
-
-AuditdNetlinkParser::AuditdNetlinkParser(AuditdContextRef context)
- : InternalRunnable("AuditdNetlinkParser"),
- auditd_context_(std::move(context)) {}
-
-void AuditdNetlinkParser::start() {
- while (!interrupted()) {
- std::vector<audit_reply> queue;
-
- {
- std::unique_lock<std::mutex> lock(
- auditd_context_->unprocessed_records_mutex);
-
- while (auditd_context_->unprocessed_records.empty() && !interrupted()) {
- auditd_context_->unprocessed_records_cv.wait_for(
- lock, std::chrono::seconds(1));
- }
-
- queue = std::move(auditd_context_->unprocessed_records);
- auditd_context_->unprocessed_records.clear();
- }
-
- std::vector<AuditEventRecord> audit_event_record_queue;
- audit_event_record_queue.reserve(queue.size());
-
- for (auto& reply : queue) {
- if (interrupted()) {
- break;
- }
-
- AdjustAuditReply(reply);
-
- // This record carries the process id of the controlling daemon; in case
- // we lost control of the audit service, we are going to request a reset
- // as soon as we finish processing the pending queue
- if (reply.type == AUDIT_GET) {
- reply.status = static_cast<struct audit_status*>(NLMSG_DATA(reply.nlh));
- auto new_pid = static_cast<pid_t>(reply.status->pid);
-
- if (new_pid != getpid()) {
- VLOG(1) << "Audit control lost to pid: " << new_pid;
-
- if (FLAGS_audit_persist) {
- VLOG(1) << "Attempting to reacquire control of the audit service";
- auditd_context_->acquire_handle = true;
- }
- }
-
- continue;
- }
-
- // We are not interested in all messages; only get the ones related to
- // user events, syscalls and SELinux events
- if (!ShouldHandle(reply)) {
- continue;
- }
-
- AuditEventRecord audit_event_record = {};
- if (!ParseAuditReply(reply, audit_event_record)) {
- VLOG(1) << "Malformed audit record received";
- continue;
- }
-
- audit_event_record_queue.push_back(audit_event_record);
- }
-
- // Save the new records and notify the reader
- if (!audit_event_record_queue.empty()) {
- std::lock_guard<std::mutex> queue_lock(
- auditd_context_->processed_events_mutex);
-
- auditd_context_->processed_events.reserve(
- auditd_context_->processed_events.size() +
- audit_event_record_queue.size());
-
- auditd_context_->processed_events.insert(
- auditd_context_->processed_events.end(),
- audit_event_record_queue.begin(),
- audit_event_record_queue.end());
-
- auditd_context_->processed_records_cv.notify_all();
- }
-
- queue.clear();
- audit_event_record_queue.clear();
- }
-}
-
-bool AuditdNetlinkParser::ParseAuditReply(
- const audit_reply& reply, AuditEventRecord& event_record) noexcept {
- event_record = {};
-
- if (FLAGS_audit_debug) {
- std::cout << reply.type << ", " << std::string(reply.message, reply.len)
- << std::endl;
- }
-
- // Parse the record header
- event_record.type = reply.type;
- boost::string_ref message_view(reply.message,
- static_cast<unsigned int>(reply.len));
-
- auto preamble_end = message_view.find("): ");
- if (preamble_end == std::string::npos) {
- return false;
- }
-
- event_record.time =
- tryTo<unsigned long int>(message_view.substr(6, 10).to_string(), 10)
- .takeOr(event_record.time);
- event_record.audit_id = message_view.substr(6, preamble_end - 6).to_string();
-
- // SELinux doesn't output valid audit records; just save them as they are
- if (IsSELinuxRecord(reply)) {
- event_record.raw_data = reply.message;
- return true;
- }
-
- // Tokenize the message
- boost::string_ref field_view(message_view.substr(preamble_end + 3));
-
- // The linear search will construct series of key value pairs.
- std::string key, value;
- key.reserve(20);
- value.reserve(256);
-
- // There are several ways of representing value data (enclosed strings,
- // etc).
- bool found_assignment{false};
- bool found_enclose{false};
-
- for (const auto& c : field_view) {
- // Iterate over each character in the audit message.
- if ((found_enclose && c == '"') || (!found_enclose && c == ' ')) {
- if (c == '"') {
- value += c;
- }
-
- // This is a terminating sequence, the end of an enclosure or space
- // tok.
- if (!key.empty()) {
- // Multiple space tokens are supported.
- event_record.fields.emplace(
- std::make_pair(std::move(key), std::move(value)));
- }
-
- found_enclose = false;
- found_assignment = false;
-
- key.clear();
- value.clear();
-
- } else if (!found_assignment && c == ' ') {
- // A field tokenizer.
-
- } else if (found_assignment) {
- // Enclosure sequences appear immediately following assignment.
- if (c == '"') {
- found_enclose = true;
- }
-
- value += c;
-
- } else if (c == '=') {
- found_assignment = true;
-
- } else {
- key += c;
- }
- }
-
- // Last step, if there was no trailing tokenizer.
- if (!key.empty()) {
- event_record.fields.emplace(
- std::make_pair(std::move(key), std::move(value)));
- }
-
- return true;
-}
-
-void AuditdNetlinkParser::AdjustAuditReply(audit_reply& reply) noexcept {
- reply.type = reply.msg.nlh.nlmsg_type;
- reply.len = reply.msg.nlh.nlmsg_len;
- reply.nlh = &reply.msg.nlh;
-
- reply.status = nullptr;
- reply.ruledata = nullptr;
- reply.login = nullptr;
- reply.message = nullptr;
- reply.error = nullptr;
- reply.signal_info = nullptr;
- reply.conf = nullptr;
-
- switch (reply.type) {
- case NLMSG_ERROR: {
- reply.error = static_cast<struct nlmsgerr*>(NLMSG_DATA(reply.nlh));
- break;
- }
-
- case AUDIT_LIST_RULES: {
- reply.ruledata =
- static_cast<struct audit_rule_data*>(NLMSG_DATA(reply.nlh));
- break;
- }
-
- case AUDIT_USER:
- case AUDIT_LOGIN:
- case AUDIT_KERNEL:
- case AUDIT_FIRST_USER_MSG ... AUDIT_LAST_USER_MSG:
- case AUDIT_FIRST_USER_MSG2 ... AUDIT_LAST_USER_MSG2:
- case AUDIT_FIRST_EVENT ... AUDIT_INTEGRITY_LAST_MSG: {
- reply.message = static_cast<char*>(NLMSG_DATA(reply.nlh));
- break;
- }
-
- case AUDIT_SIGNAL_INFO: {
- reply.signal_info = static_cast<audit_sig_info*>(NLMSG_DATA(reply.nlh));
- break;
- }
-
- default:
- break;
- }
-}
-} // namespace osquery
+++ /dev/null
-/**
- * Copyright (c) 2014-present, Facebook, Inc.
- * All rights reserved.
- *
- * This source code is licensed in accordance with the terms specified in
- * the LICENSE file found in the root directory of this source tree.
- */
-
-#pragma once
-
-#include <libaudit.h>
-
-#include <atomic>
-#include <condition_variable>
-#include <future>
-#include <map>
-#include <memory>
-#include <set>
-#include <unordered_map>
-#include <vector>
-
-#include <boost/algorithm/hex.hpp>
-
-#include <osquery/dispatcher.h>
-
-namespace osquery {
-
-/// Netlink status, used by AuditNetlink::acquireHandle()
-enum class NetlinkStatus { ActiveMutable, ActiveImmutable, Disabled, Error };
-
-/// Contains an audit_rule_data structure
-using AuditRuleDataObject = std::vector<std::uint8_t>;
-
-/// A single, prepared audit event record.
-struct AuditEventRecord final {
- /// Record type (i.e.: AUDIT_SYSCALL, AUDIT_PATH, ...)
- int type;
-
- /// Event time.
- unsigned long int time;
-
- /// Audit event id that owns this record. Remember: PRIMARY KEY(id, timestamp)
- std::string audit_id;
-
- /// The field list for this record. Valid for everything except SELinux
- /// records
- std::map<std::string, std::string> fields;
-
- /// The raw message, only valid for SELinux records (because they have broken
- /// syntax)
- std::string raw_data;
-};
-
-static_assert(std::is_move_constructible<AuditEventRecord>::value,
- "not move constructible");
-
-// This structure is used to share data between the reading and processing
-// services
-struct AuditdContext final {
- /// Unprocessed audit records
- std::vector<audit_reply> unprocessed_records;
- static_assert(
- std::is_move_constructible<decltype(unprocessed_records)>::value,
- "not move constructible");
-
- /// Mutex for the list of unprocessed records
- std::mutex unprocessed_records_mutex;
-
- /// Processed events condition variable
- std::condition_variable unprocessed_records_cv;
-
- /// This queue contains processed events
- std::vector<AuditEventRecord> processed_events;
-
- /// Processed events queue mutex.
- std::mutex processed_events_mutex;
-
- /// Used to wake up the thread that processes the raw audit records
- std::condition_variable processed_records_cv;
-
- /// When set to true, the audit handle is (re)acquired
- std::atomic_bool acquire_handle{true};
-};
-
-using AuditdContextRef = std::shared_ptr<AuditdContext>;
-
-/// This is the service responsible for reading data from the audit netlink
-class AuditdNetlinkReader final : public InternalRunnable {
- public:
- explicit AuditdNetlinkReader(AuditdContextRef context);
-
- protected:
- virtual void start() override;
- virtual void stop() override;
-
- private:
- /// Reads as many audit event records as possible before returning.
- bool acquireMessages() noexcept;
-
- /// Configures the audit service and applies required rules
- bool configureAuditService() noexcept;
-
- /// Clears out the audit configuration
- bool clearAuditConfiguration() noexcept;
-
- /// Deletes the given audit rule
- bool deleteAuditRule(const AuditRuleDataObject& rule_object);
-
- /// Removes the rules that we have applied
- void restoreAuditServiceConfiguration() noexcept;
-
- /// (Re)acquire the netlink handle.
- NetlinkStatus acquireHandle() noexcept;
-
- private:
- /// Shared data
- AuditdContextRef auditd_context_;
-
- /// Read buffer used when receiving events from the netlink
- std::vector<audit_reply> read_buffer_;
-
- /// The set of rules we applied (and that we'll uninstall when exiting)
- std::vector<audit_rule_data> installed_rule_list_;
-
- /// The syscalls we are listening for
- std::set<int> monitored_syscall_list_;
-
- /// Netlink handle.
- int audit_netlink_handle_{-1};
-};
-
-/// This service parses the raw audit records
-class AuditdNetlinkParser final : public InternalRunnable {
- public:
- explicit AuditdNetlinkParser(AuditdContextRef context);
- virtual void start() override;
-
- /// Parses an audit_reply structure into an AuditEventRecord object
- static bool ParseAuditReply(const audit_reply& reply,
- AuditEventRecord& event_record) noexcept;
-
- /// Adjusts the internal pointers of the audit_reply object
- static void AdjustAuditReply(audit_reply& reply) noexcept;
-
- private:
- /// Shared data
- AuditdContextRef auditd_context_;
-};
-
-/// This class provides access to the audit netlink data
-class AuditdNetlink final : private boost::noncopyable {
- public:
- AuditdNetlink();
- virtual ~AuditdNetlink() = default;
-
- /// Prepares the raw audit event records stored in the given context.
- std::vector<AuditEventRecord> getEvents() noexcept;
-
- private:
- /// Shared data
- AuditdContextRef auditd_context_;
-};
-
-/// Handle quote and hex-encoded audit field content.
-inline std::string DecodeAuditPathValues(const std::string& s) {
- if (s.size() > 1 && s[0] == '"') {
- return s.substr(1, s.size() - 2);
- }
-
- try {
- return boost::algorithm::unhex(s);
- } catch (const boost::algorithm::hex_decode_error& e) {
- return s;
- }
-}
-} // namespace osquery
+++ /dev/null
-/**
- * Copyright (c) 2014-present, Facebook, Inc.
- * All rights reserved.
- *
- * This source code is licensed in accordance with the terms specified in
- * the LICENSE file found in the root directory of this source tree.
- */
-
-#include <array>
-
-#include <osquery/events/linux/auditeventpublisher.h>
-#include <osquery/events/linux/selinux_events.h>
-#include <osquery/flags.h>
-#include <osquery/logger.h>
-#include <osquery/registry_factory.h>
-#include <osquery/utils/conversions/tryto.h>
-
-namespace osquery {
-/// The audit subsystem may have a performance impact on the system.
-FLAG(bool,
- disable_audit,
- true,
- "Disable receiving events from the audit subsystem");
-
-// External flags; they are used to determine whether we should run or not
-DECLARE_bool(audit_allow_fim_events);
-DECLARE_bool(audit_allow_process_events);
-DECLARE_bool(audit_allow_sockets);
-DECLARE_bool(audit_allow_user_events);
-DECLARE_bool(audit_allow_selinux_events);
-
-REGISTER(AuditEventPublisher, "event_publisher", "auditeventpublisher");
-
-namespace {
-bool IsPublisherEnabled() noexcept {
- if (FLAGS_disable_audit) {
- return false;
- }
-
- return (FLAGS_audit_allow_fim_events || FLAGS_audit_allow_process_events ||
- FLAGS_audit_allow_sockets || FLAGS_audit_allow_user_events ||
- FLAGS_audit_allow_selinux_events);
-}
-} // namespace
-
-std::string AuditEventPublisher::executable_path_;
-
-Status AuditEventPublisher::setUp() {
- if (!IsPublisherEnabled()) {
- return Status(1, "Publisher disabled via configuration");
- }
-
- if (executable_path_.empty()) {
- char buffer[PATH_MAX] = {};
- assert(readlink("/proc/self/exe", buffer, sizeof(buffer)) != -1);
- executable_path_ = buffer;
- }
-
- return Status::success();
-}
-
-void AuditEventPublisher::configure() {
- if (!IsPublisherEnabled()) {
- return;
- }
-
- if (audit_netlink_ == nullptr) {
- audit_netlink_ = std::make_unique<AuditdNetlink>();
- }
-}
-
-void AuditEventPublisher::tearDown() {
- if (!IsPublisherEnabled()) {
- return;
- }
-
- audit_netlink_.reset();
-}
-
-Status AuditEventPublisher::run() {
- if (!IsPublisherEnabled()) {
- return Status(1, "Publisher disabled via configuration");
- }
-
- auto audit_event_record_queue = audit_netlink_->getEvents();
-
- auto event_context = createEventContext();
-
- // This is a simple estimate based on the process_file_events_tests.cpp
- // records
- auto event_count_estimate = audit_event_record_queue.size() / 4U;
- event_context->audit_events.reserve(event_count_estimate);
-
- ProcessEvents(event_context, audit_event_record_queue, audit_trace_context_);
- if (!event_context->audit_events.empty()) {
- fire(event_context);
- }
-
- return Status::success();
-}
-
-void AuditEventPublisher::ProcessEvents(
- AuditEventContextRef event_context,
- const std::vector<AuditEventRecord>& record_list,
- AuditTraceContext& trace_context) noexcept {
- static const auto& selinux_event_set = kSELinuxEventList;
-
- // Assemble each record into a AuditEvent object; multi-record events
- // are complete when we receive the terminator (AUDIT_EOE)
- for (const auto& audit_event_record : record_list) {
- auto audit_event_it = trace_context.find(audit_event_record.audit_id);
-
- // We have two entry points here; the first one is for user messages, while
- // the second one is for syscalls
- if (audit_event_record.type >= AUDIT_FIRST_USER_MSG &&
- audit_event_record.type <= AUDIT_LAST_USER_MSG) {
- UserAuditEventData data = {};
- data.user_event_id = static_cast<std::uint64_t>(audit_event_record.type);
-
- AuditEvent audit_event;
- audit_event.type = AuditEvent::Type::UserEvent;
- audit_event.record_list.push_back(std::move(audit_event_record));
- audit_event.data = data;
-
- event_context->audit_events.push_back(audit_event);
-
- // SELinux events
- } else if (selinux_event_set.find(audit_event_record.type) !=
- selinux_event_set.end()) {
- AuditEvent audit_event;
- audit_event.type = AuditEvent::Type::SELinux;
- audit_event.record_list.push_back(audit_event_record);
-
- event_context->audit_events.push_back(audit_event);
-
- } else if (audit_event_record.type == AUDIT_SYSCALL) {
- if (audit_event_it != trace_context.end()) {
- VLOG(1) << "Received a duplicated event.";
- trace_context.erase(audit_event_it);
- }
-
- AuditEvent audit_event;
- audit_event.type = AuditEvent::Type::Syscall;
-
- // Estimate based on the process_file_events_tests.cpp records
- audit_event.record_list.reserve(4U);
-
- SyscallAuditEventData data;
-
- std::string raw_executable_path;
- if (!GetStringFieldFromMap(
- raw_executable_path, audit_event_record.fields, "exe")) {
- VLOG(1) << "Malformed AUDIT_SYSCALL record received. The "
- "executable path field is either missing or not valid.";
-
- continue;
- }
-
- data.executable_path = DecodeAuditPathValues(raw_executable_path);
-
- // Do not process events originated by the osquery watchdog or daemon
- if (executable_path_ == data.executable_path) {
- continue;
- }
-
- if (!GetIntegerFieldFromMap(
- data.syscall_number, audit_event_record.fields, "syscall")) {
- VLOG(1) << "Malformed AUDIT_SYSCALL record received. The "
- "syscall field "
- "is either missing or not valid.";
-
- continue;
- }
-
- std::string syscall_status;
- GetStringFieldFromMap(
- syscall_status, audit_event_record.fields, "success", "yes");
-
- // By discarding this event, we will also automatically discard any other
- // attached record
- if (syscall_status != "yes") {
- continue;
- }
-
- std::uint64_t process_id;
- if (!GetIntegerFieldFromMap(
- process_id, audit_event_record.fields, "pid")) {
- VLOG(1) << "Malformed AUDIT_SYSCALL record received. The process id "
- "field is either missing or not valid.";
-
- continue;
- }
-
- std::uint64_t parent_process_id;
- if (!GetIntegerFieldFromMap(
- parent_process_id, audit_event_record.fields, "ppid")) {
- VLOG(1) << "Malformed AUDIT_SYSCALL record received. The parent "
- "process id field is either missing or not valid.";
-
- continue;
- }
-
- data.process_id = static_cast<pid_t>(process_id);
- data.parent_process_id = static_cast<pid_t>(parent_process_id);
- audit_event.data = data;
-
- std::uint64_t process_uid;
- if (!GetIntegerFieldFromMap(
- process_uid, audit_event_record.fields, "uid")) {
- VLOG(1) << "Missing or invalid uid field in AUDIT_SYSCALL";
-
- continue;
- }
-
- std::uint64_t process_euid;
- if (!GetIntegerFieldFromMap(
- process_euid, audit_event_record.fields, "euid")) {
- VLOG(1) << "Missing or invalid euid field in AUDIT_SYSCALL";
-
- continue;
- }
-
- std::uint64_t process_gid;
- if (!GetIntegerFieldFromMap(
- process_gid, audit_event_record.fields, "gid")) {
- VLOG(1) << "Missing or invalid gid field in AUDIT_SYSCALL";
-
- continue;
- }
-
- std::uint64_t process_egid;
- if (!GetIntegerFieldFromMap(
- process_egid, audit_event_record.fields, "egid")) {
- VLOG(1) << "Missing or invalid egid field in AUDIT_SYSCALL";
-
- continue;
- }
-
- data.process_uid = static_cast<uid_t>(process_uid);
- data.process_euid = static_cast<uid_t>(process_euid);
- data.process_gid = static_cast<gid_t>(process_gid);
- data.process_egid = static_cast<gid_t>(process_egid);
-
- audit_event.record_list.push_back(audit_event_record);
- trace_context[audit_event_record.audit_id] = std::move(audit_event);
-
- // This is the terminator for multi-record audit events
- } else if (audit_event_record.type == AUDIT_EOE) {
- if (audit_event_it == trace_context.end()) {
- continue;
- }
-
- auto completed_audit_event = audit_event_it->second;
- trace_context.erase(audit_event_it);
-
- event_context->audit_events.push_back(std::move(completed_audit_event));
-
- } else {
- if (audit_event_it == trace_context.end()) {
- continue;
- }
-
- audit_event_it->second.record_list.push_back(audit_event_record);
- }
- }
-
- // Drop events that are older than 5 minutes; it means that we have failed to
- // receive the end of record and will never complete them correctly
-
- std::time_t current_time;
- std::time(¤t_time);
-
- std::unordered_map<std::string, std::time_t> timestamp_cache;
-
- // The first part of the audit id is a timestamp: 1501323932.710:7670542
- for (auto event_it = trace_context.begin();
- event_it != trace_context.end();) {
- const auto& audit_event_id = event_it->first;
- std::time_t event_timestamp;
-
- auto timestamp_it = timestamp_cache.find(audit_event_id);
- if (timestamp_it == timestamp_cache.end()) {
- std::string string_timestamp = audit_event_id.substr(0, 10);
-
- event_timestamp = tryTo<long long>(string_timestamp).takeOr(0ll);
-
- timestamp_cache[audit_event_id] = event_timestamp;
-
- } else {
- event_timestamp = timestamp_it->second;
- }
-
- if (current_time - event_timestamp >= 300) {
- event_it = trace_context.erase(event_it);
- } else {
- event_it++;
- }
- }
-}
-
-const AuditEventRecord* GetEventRecord(const AuditEvent& event,
- int record_type) noexcept {
- auto it = std::find_if(event.record_list.begin(),
- event.record_list.end(),
- [record_type](const AuditEventRecord& record) -> bool {
- return (record.type == record_type);
- });
-
- if (it == event.record_list.end()) {
- return nullptr;
- }
-
- return &(*it);
-};
-
-bool GetStringFieldFromMap(std::string& value,
- const std::map<std::string, std::string>& fields,
- const std::string& name,
- const std::string& default_value) noexcept {
- auto it = fields.find(name);
- if (it == fields.end()) {
- value = default_value;
- return false;
- }
-
- value = it->second;
- return true;
-}
-
-bool GetIntegerFieldFromMap(std::uint64_t& value,
- const std::map<std::string, std::string>& field_map,
- const std::string& field_name,
- std::size_t base,
- std::uint64_t default_value) noexcept {
- std::string string_value;
- if (!GetStringFieldFromMap(string_value, field_map, field_name, "")) {
- value = default_value;
- return false;
- }
- auto exp = tryTo<std::uint64_t>(string_value, base);
- value = exp.takeOr(std::move(default_value));
- return exp.isValue();
-}
-
-void CopyFieldFromMap(Row& row,
- const std::map<std::string, std::string>& fields,
- const std::string& name,
- const std::string& default_value) noexcept {
- GetStringFieldFromMap(row[name], fields, name, default_value);
-}
-} // namespace osquery
+++ /dev/null
-/**
- * Copyright (c) 2014-present, Facebook, Inc.
- * All rights reserved.
- *
- * This source code is licensed in accordance with the terms specified in
- * the LICENSE file found in the root directory of this source tree.
- */
-
-#pragma once
-
-#include <cstdint>
-#include <limits>
-#include <memory>
-
-#include <boost/variant.hpp>
-
-#include <osquery/events.h>
-#include <osquery/events/linux/auditdnetlink.h>
-
-namespace osquery {
-
-struct UserAuditEventData final {
- std::uint64_t user_event_id;
-};
-
-struct SyscallAuditEventData final {
- std::uint64_t syscall_number;
-
- pid_t process_id;
- pid_t parent_process_id;
-
- uid_t process_uid;
- uid_t process_euid;
-
- gid_t process_gid;
- gid_t process_egid;
-
- std::string executable_path;
-};
-
-/// Audit event descriptor
-struct AuditEvent final {
- enum class Type { UserEvent, Syscall, SELinux };
-
- Type type;
- boost::variant<UserAuditEventData, SyscallAuditEventData> data;
-
- std::vector<AuditEventRecord> record_list;
-};
-
-/// Audit event pretty printer, used for the --audit_fim_debug flag
-std::ostream& operator<<(std::ostream& stream, const AuditEvent& audit_event);
-
-struct AuditSubscriptionContext final : public SubscriptionContext {
- private:
- friend class AuditEventPublisher;
-};
-
-struct AuditEventContext final : public EventContext {
- std::vector<AuditEvent> audit_events;
-};
-
-using AuditEventContextRef = std::shared_ptr<AuditEventContext>;
-using AuditSubscriptionContextRef = std::shared_ptr<AuditSubscriptionContext>;
-
-/// This type maps audit event id with the corresponding audit event object
-using AuditTraceContext = std::map<std::string, AuditEvent>;
-
-class AuditEventPublisher final
- : public EventPublisher<AuditSubscriptionContext, AuditEventContext> {
- DECLARE_PUBLISHER("auditeventpublisher");
-
- public:
- Status setUp() override;
- void configure() override;
- void tearDown() override;
- Status run() override;
-
- virtual ~AuditEventPublisher() {
- tearDown();
- }
-
- /// Executable path
- static std::string executable_path_;
-
- /// Aggregates raw event records into audit events
- static void ProcessEvents(AuditEventContextRef event_context,
- const std::vector<AuditEventRecord>& record_list,
- AuditTraceContext& trace_context) noexcept;
-
- private:
- /// Netlink reader
- std::unique_ptr<AuditdNetlink> audit_netlink_;
-
- /// This is where audit records are assembled
- AuditTraceContext audit_trace_context_;
-};
-
-/// Extracts the specified audit event record from the given audit event
-const AuditEventRecord* GetEventRecord(const AuditEvent& event,
- int record_type) noexcept;
-
-/// Extracts the specified string key from the given string map
-bool GetStringFieldFromMap(
- std::string& value,
- const std::map<std::string, std::string>& fields,
- const std::string& name,
- const std::string& default_value = std::string()) noexcept;
-
-/// Extracts the specified integer key from the given string map
-bool GetIntegerFieldFromMap(
- std::uint64_t& value,
- const std::map<std::string, std::string>& field_map,
- const std::string& field_name,
- std::size_t base = 10,
- std::uint64_t default_value =
- std::numeric_limits<std::uint64_t>::max()) noexcept;
-
-/// Copies a named field from the 'fields' map to the specified row
-void CopyFieldFromMap(
- Row& row,
- const std::map<std::string, std::string>& fields,
- const std::string& name,
- const std::string& default_value = std::string()) noexcept;
-} // namespace osquery
+++ /dev/null
-/**
- * Copyright (c) 2014-present, Facebook, Inc.
- * All rights reserved.
- *
- * This source code is licensed in accordance with the terms specified in
- * the LICENSE file found in the root directory of this source tree.
- */
-
-#include <sstream>
-
-#include <fnmatch.h>
-#include <linux/limits.h>
-#include <poll.h>
-
-#include <boost/filesystem.hpp>
-
-#include <osquery/config/config.h>
-#include <osquery/filesystem/filesystem.h>
-#include <osquery/logger.h>
-#include <osquery/registry_factory.h>
-#include <osquery/utils/system/time.h>
-
-#include "osquery/events/linux/inotify.h"
-
-namespace fs = boost::filesystem;
-
-namespace osquery {
-
-static const size_t kINotifyMaxEvents = 512;
-static const size_t kINotifyEventSize =
- sizeof(struct inotify_event) + (NAME_MAX + 1);
-static const size_t kINotifyBufferSize =
- (kINotifyMaxEvents * kINotifyEventSize);
-
-std::map<int, std::string> kMaskActions = {
- {IN_ACCESS, "ACCESSED"},
- {IN_ATTRIB, "ATTRIBUTES_MODIFIED"},
- {IN_CLOSE_WRITE, "UPDATED"},
- {IN_CREATE, "CREATED"},
- {IN_DELETE, "DELETED"},
- {IN_MODIFY, "UPDATED"},
- {IN_MOVED_FROM, "MOVED_FROM"},
- {IN_MOVED_TO, "MOVED_TO"},
- {IN_OPEN, "OPENED"},
-};
-
-const uint32_t kFileDefaultMasks = IN_MOVED_TO | IN_MOVED_FROM | IN_MODIFY |
- IN_DELETE | IN_CREATE | IN_CLOSE_WRITE |
- IN_ATTRIB;
-const uint32_t kFileAccessMasks = IN_OPEN | IN_ACCESS;
-
-REGISTER(INotifyEventPublisher, "event_publisher", "inotify");
-
-Status INotifyEventPublisher::setUp() {
- inotify_handle_ = ::inotify_init();
- // If this does not work throw an exception.
- if (inotify_handle_ == -1) {
- return Status(1, "Could not start inotify: inotify_init failed");
- }
-
- WriteLock lock(scratch_mutex_);
- scratch_ = (char*)malloc(kINotifyBufferSize);
- if (scratch_ == nullptr) {
- return Status(1, "Could not allocate scratch space");
- }
- return Status::success();
-}
-
-bool INotifyEventPublisher::needMonitoring(const std::string& path,
- INotifySubscriptionContextRef& isc,
- uint32_t mask,
- bool recursive,
- bool add_watch) {
- bool rc = true;
- struct stat file_dir_stat;
- time_t sc_time = isc->path_sc_time_[path];
-
- if (stat(path.c_str(), &file_dir_stat) == -1) {
- LOG(WARNING) << "Failed to do stat on: " << path;
- return false;
- }
-
- if (sc_time != file_dir_stat.st_ctime) {
- if ((rc = addMonitor(path, isc, isc->mask, isc->recursive, add_watch))) {
- isc->path_sc_time_[path] = file_dir_stat.st_ctime;
- }
- }
- return rc;
-}
-
-bool INotifyEventPublisher::monitorSubscription(
- INotifySubscriptionContextRef& sc, bool add_watch) {
- std::string discovered = sc->path;
- if (sc->path.find("**") != std::string::npos) {
- sc->recursive = true;
- discovered = sc->path.substr(0, sc->path.find("**"));
- sc->path = discovered;
- }
-
- if (sc->path.find('*') != std::string::npos) {
- // If the wildcard exists within the file (leaf), remove and monitor the
- // directory instead. Apply a fnmatch on fired events to filter leafs.
- auto fullpath = fs::path(sc->path);
- if (fullpath.filename().string().find('*') != std::string::npos) {
- discovered = fullpath.parent_path().string() + '/';
- }
-
- if (discovered.find('*') != std::string::npos) {
- // If a wildcard exists within the tree (stem), resolve at configure
- // time and monitor each path.
- std::vector<std::string> paths;
- resolveFilePattern(discovered, paths);
- sc->recursive_match = sc->recursive;
- for (const auto& _path : paths) {
- needMonitoring(_path, sc, sc->mask, sc->recursive, add_watch);
- }
- return true;
- }
- }
-
- if (isDirectory(discovered) && discovered.back() != '/') {
- sc->path += '/';
- discovered += '/';
- }
-
- return needMonitoring(discovered, sc, sc->mask, sc->recursive, add_watch);
-}
-
-void INotifyEventPublisher::buildExcludePathsSet() {
- auto parser = Config::getParser("file_paths");
-
- WriteLock lock(subscription_lock_);
- exclude_paths_.clear();
-
- const auto& doc = parser->getData();
- if (!doc.doc().HasMember("exclude_paths")) {
- return;
- }
-
- for (const auto& category : doc.doc()["exclude_paths"].GetObject()) {
- for (const auto& excl_path : category.value.GetArray()) {
- std::string pattern = excl_path.GetString();
- if (pattern.empty()) {
- continue;
- }
- exclude_paths_.insert(pattern);
- }
- }
-}
-
-void INotifyEventPublisher::configure() {
- if (inotify_handle_ == -1) {
- // This publisher has not been setup correctly.
- return;
- }
-
- SubscriptionVector delete_subscriptions;
- {
- WriteLock lock(subscription_lock_);
- auto end = std::remove_if(
- subscriptions_.begin(),
- subscriptions_.end(),
- [&delete_subscriptions](const SubscriptionRef& subscription) {
- auto inotify_sc = getSubscriptionContext(subscription->context);
- if (inotify_sc->mark_for_deletion == true) {
- delete_subscriptions.push_back(subscription);
- return true;
- }
- return false;
- });
- subscriptions_.erase(end, subscriptions_.end());
- }
-
- for (auto& sub : delete_subscriptions) {
- auto ino_sc = getSubscriptionContext(sub->context);
- for (const auto& path : ino_sc->descriptor_paths_) {
- removeMonitor(path.first, true, true);
- }
- ino_sc->descriptor_paths_.clear();
- }
- delete_subscriptions.clear();
-
- buildExcludePathsSet();
-
- for (auto& sub : subscriptions_) {
- // Anytime a configure is called, try to monitor all subscriptions.
- // Configure is called as a response to removing/adding subscriptions.
- // This means recalculating all monitored paths.
- auto sc = getSubscriptionContext(sub->context);
- monitorSubscription(sc);
- }
-}
-
-void INotifyEventPublisher::tearDown() {
- if (inotify_handle_ > -1) {
- ::close(inotify_handle_);
- }
- inotify_handle_ = -1;
-
- WriteLock lock(scratch_mutex_);
- if (scratch_ != nullptr) {
- free(scratch_);
- scratch_ = nullptr;
- }
-}
-
-void INotifyEventPublisher::handleOverflow() {
- if (inotify_events_ < kINotifyMaxEvents) {
- VLOG(1) << "inotify was overflown: increasing scratch buffer";
- // Exponential increment.
- inotify_events_ = inotify_events_ * 2;
- } else if (last_overflow_ != -1 && getUnixTime() - last_overflow_ < 60) {
- return;
- } else {
- VLOG(1) << "inotify was overflown";
- last_overflow_ = getUnixTime();
- }
-}
-
-Status INotifyEventPublisher::run() {
- struct pollfd fds[1];
- fds[0].fd = getHandle();
- fds[0].events = POLLIN;
- int selector = ::poll(fds, 1, 1000);
- if (selector == -1) {
- if (errno == EINTR) {
- return Status::success();
- }
- LOG(WARNING) << "Could not read inotify handle";
- return Status(1, "inotify poll failed");
- }
-
- if (selector == 0) {
- // Read timeout.
- return Status::success();
- }
-
- if (!(fds[0].revents & POLLIN)) {
- return Status::success();
- }
-
- WriteLock lock(scratch_mutex_);
- ssize_t record_num =
- ::read(getHandle(), scratch_, inotify_events_ * kINotifyEventSize);
- if (record_num == 0 || record_num == -1) {
- return Status(1, "INotify read failed");
- }
-
- for (char* p = scratch_; p < scratch_ + record_num;) {
- // Cast the inotify struct, make shared pointer, and append to contexts.
- auto event = reinterpret_cast<struct inotify_event*>(p);
- if (event->mask & IN_Q_OVERFLOW) {
- // The inotify queue was overflown (try to receive more events from OS).
- handleOverflow();
- } else if (event->mask & IN_IGNORED) {
- // This inotify watch was removed.
- removeMonitor(event->wd, false);
- } else if (event->mask & IN_MOVE_SELF) {
- // This inotify path was moved, but is still watched.
- removeMonitor(event->wd, true);
- } else if (event->mask & IN_DELETE_SELF) {
- // A file was moved to replace the watched path.
- removeMonitor(event->wd, false);
- } else {
- auto ec = createEventContextFrom(event);
- if (!ec->action.empty()) {
- fire(ec);
- }
- }
- // Continue to iterate
- p += (sizeof(struct inotify_event)) + event->len;
- }
-
- return Status::success();
-}
-
-INotifyEventContextRef INotifyEventPublisher::createEventContextFrom(
- struct inotify_event* event) const {
- auto ec = createEventContext();
- ec->event = std::make_unique<struct inotify_event>(*event);
-
- // Get the pathname the watch fired on.
- {
- WriteLock lock(path_mutex_);
- if (descriptor_inosubctx_.find(event->wd) == descriptor_inosubctx_.end()) {
- // return a blank event context if we can't find the paths for the event
- return ec;
- } else {
- auto isc = descriptor_inosubctx_.at(event->wd);
- ec->path = isc->descriptor_paths_.at(event->wd);
- ec->isub_ctx = isc;
- }
- }
-
- if (event->len > 1) {
- ec->path += event->name;
- }
-
- for (const auto& action : kMaskActions) {
- if (event->mask & action.first) {
- ec->action = action.second;
- break;
- }
- }
- return ec;
-}
-
-bool INotifyEventPublisher::shouldFire(const INotifySubscriptionContextRef& sc,
- const INotifyEventContextRef& ec) const {
- if (sc.get() != ec->isub_ctx.get()) {
- /// Not my event.
- return false;
- }
-
- // The subscription may supply a required event mask.
- if (sc->mask != 0 && !(ec->event->mask & sc->mask)) {
- return false;
- }
-
- // inotify will not monitor recursively, new directories need watches.
- if (sc->recursive && ec->action == "CREATED" && isDirectory(ec->path)) {
- const_cast<INotifyEventPublisher*>(this)->addMonitor(
- ec->path + '/',
- const_cast<INotifySubscriptionContextRef&>(sc),
- sc->mask,
- true);
- }
-
- // exclude paths should be applied at last
- auto path = ec->path.substr(0, ec->path.rfind('/'));
- // Need to have two finds,
- // what if somebody excluded an individual file inside a directory
- if (!exclude_paths_.empty() &&
- (exclude_paths_.find(path) || exclude_paths_.find(ec->path))) {
- return false;
- }
-
- return true;
-}
-
-bool INotifyEventPublisher::addMonitor(const std::string& path,
- INotifySubscriptionContextRef& isc,
- uint32_t mask,
- bool recursive,
- bool add_watch) {
- {
- WriteLock lock(path_mutex_);
- int watch = ::inotify_add_watch(
- getHandle(), path.c_str(), ((mask == 0) ? kFileDefaultMasks : mask));
- if (add_watch && watch == -1) {
- LOG(WARNING) << "Could not add inotify watch on: " << path;
- return false;
- }
-
- if (descriptor_inosubctx_.find(watch) != descriptor_inosubctx_.end()) {
- auto ino_sc = descriptor_inosubctx_.at(watch);
- if (inotify_sanity_check) {
- std::string watched_path = ino_sc->descriptor_paths_[watch];
- path_descriptors_.erase(watched_path);
- }
- ino_sc->descriptor_paths_.erase(watch);
- descriptor_inosubctx_.erase(watch);
- }
-
- // Keep a map of (descriptor -> path)
- isc->descriptor_paths_[watch] = path;
- descriptor_inosubctx_[watch] = isc;
- if (inotify_sanity_check) {
- // Keep a map of the path -> watch descriptor
- path_descriptors_[path] = watch;
- }
- }
-
- if (recursive && isDirectory(path).ok()) {
- std::vector<std::string> children;
- // Get a list of children of this directory (requested recursive watches).
- listDirectoriesInDirectory(path, children, true);
-
- boost::system::error_code ec;
- for (const auto& child : children) {
- auto canonicalized = fs::canonical(child, ec).string() + '/';
- addMonitor(canonicalized, isc, mask, false);
- }
- }
-
- return true;
-}
-
-bool INotifyEventPublisher::removeMonitor(int watch,
- bool force,
- bool batch_del) {
- {
- WriteLock lock(path_mutex_);
- if (descriptor_inosubctx_.find(watch) == descriptor_inosubctx_.end()) {
- return false;
- }
-
- auto isc = descriptor_inosubctx_.at(watch);
- descriptor_inosubctx_.erase(watch);
-
- if (inotify_sanity_check) {
- std::string watched_path = isc->descriptor_paths_[watch];
- path_descriptors_.erase(watched_path);
- }
-
- if (!batch_del) {
- isc->descriptor_paths_.erase(watch);
- }
- }
-
- if (force) {
- ::inotify_rm_watch(getHandle(), watch);
- }
-
- return true;
-}
-
-void INotifyEventPublisher::removeSubscriptions(const std::string& subscriber) {
- WriteLock lock(subscription_lock_);
- std::for_each(subscriptions_.begin(),
- subscriptions_.end(),
- [&subscriber](const SubscriptionRef& sub) {
- if (sub->subscriber_name == subscriber) {
- getSubscriptionContext(sub->context)->mark_for_deletion =
- true;
- }
- });
-}
-
-Status INotifyEventPublisher::addSubscription(
- const SubscriptionRef& subscription) {
- WriteLock lock(subscription_lock_);
- auto received_inotify_sc = getSubscriptionContext(subscription->context);
- for (auto& sub : subscriptions_) {
- auto inotify_sc = getSubscriptionContext(sub->context);
- if (*received_inotify_sc == *inotify_sc) {
- if (inotify_sc->mark_for_deletion) {
- inotify_sc->mark_for_deletion = false;
- return Status(0);
- }
- // Returning non zero signals EventSubscriber::subscribe
- // do not bump up subscription_count_.
- return Status(1);
- }
- }
-
- subscriptions_.push_back(subscription);
- return Status(0);
-}
-
-bool INotifyEventPublisher::isPathMonitored(const std::string& path) const {
- WriteLock lock(path_mutex_);
- std::string parent_path;
- if (!isDirectory(path).ok()) {
- if (path_descriptors_.find(path) != path_descriptors_.end()) {
- // Path is a file, and is directly monitored.
- return true;
- }
- // Important to add a trailing "/" for inotify.
- parent_path = fs::path(path).parent_path().string() + '/';
- } else {
- parent_path = path;
- }
- // Directory or parent of file monitoring
- auto path_iterator = path_descriptors_.find(parent_path);
- return (path_iterator != path_descriptors_.end());
-}
-}
+++ /dev/null
-/**
- * Copyright (c) 2014-present, Facebook, Inc.
- * All rights reserved.
- *
- * This source code is licensed in accordance with the terms specified in
- * the LICENSE file found in the root directory of this source tree.
- */
-
-#pragma once
-
-#include <map>
-#include <vector>
-
-#include <sys/inotify.h>
-#include <sys/stat.h>
-
-#include <osquery/events.h>
-
-#include "osquery/events/pathset.h"
-
-namespace osquery {
-
-extern std::map<int, std::string> kMaskActions;
-
-extern const uint32_t kFileDefaultMasks;
-extern const uint32_t kFileAccessMasks;
-
-// INotifySubscriptionContext containers
-using PathDescriptorMap = std::map<std::string, int>;
-using DescriptorPathMap = std::map<int, std::string>;
-using PathStatusChangeTimeMap = std::map<std::string, time_t>;
-
-/**
- * @brief Subscription details for INotifyEventPublisher events.
- *
- * This context is specific to INotifyEventPublisher. It allows the
- * subscribing EventSubscriber to set a path (file or directory) and a
- * limited action mask.
- * Events are passed to the EventSubscriber if they match the context
- * path (or anything within a directory if the path is a directory) and if the
- * event action is part of the mask. If the mask is 0 then all actions are
- * passed to the EventSubscriber.
- */
-struct INotifySubscriptionContext : public SubscriptionContext {
- /// Subscription the following filesystem path.
- std::string path;
-
- /// original path, read from config
- std::string opath;
-
- /// Limit the `inotify` actions to the subscription mask (if not 0).
- uint32_t mask{0};
-
- /// Treat this path as a directory and subscription recursively.
- bool recursive{false};
-
- /// Save the category this path originated form within the config.
- std::string category;
-
- /// Lazy deletion of a subscription.
- bool mark_for_deletion{false};
-
- /**
- * @brief Helper method to map a string action to `inotify` action mask bit.
- *
- * This helper method will set the `mask` value for this SubscriptionContext.
- *
- * @param action The string action, a value in kMaskAction%s.
- */
- void requireAction(const std::string& action) {
- for (const auto& bit : kMaskActions) {
- if (action == bit.second) {
- mask = mask | bit.first;
- }
- }
- }
-
- private:
- /// A configure-time pattern was expanded to match absolute paths.
- bool recursive_match{false};
-
- /// Map of inotify watch file descriptor to watched path string.
- DescriptorPathMap descriptor_paths_;
-
- /// Map of path and status change time of file/directory.
- PathStatusChangeTimeMap path_sc_time_;
-
- private:
- friend class INotifyEventPublisher;
-};
-
-/// Overloaded '==' operator, to check if two inotify subscriptions are same.
-inline bool operator==(const INotifySubscriptionContext& lsc,
- const INotifySubscriptionContext& rsc) {
- return ((lsc.category == rsc.category) && (lsc.opath == rsc.opath));
-}
-
-using INotifySubscriptionContextRef =
- std::shared_ptr<INotifySubscriptionContext>;
-
-/**
- * @brief Event details for INotifyEventPublisher events.
- */
-struct INotifyEventContext : public EventContext {
- /// The inotify_event structure if the EventSubscriber want to interact.
- std::unique_ptr<struct inotify_event> event{nullptr};
-
- /// A string path parsed from the inotify_event.
- std::string path;
-
- /// A string action representing the event action `inotify` bit.
- std::string action;
-
- /// A no-op event transaction id.
- uint32_t transaction_id{0};
-
- /// This event ctx belongs to isub_ctx
- INotifySubscriptionContextRef isub_ctx;
-};
-
-using INotifyEventContextRef = std::shared_ptr<INotifyEventContext>;
-
-// Publisher container
-using DescriptorINotifySubCtxMap = std::map<int, INotifySubscriptionContextRef>;
-
-using ExcludePathSet = PathSet<patternedPath>;
-
-/**
- * @brief A Linux `inotify` EventPublisher.
- *
- * This EventPublisher allows EventSubscriber%s to subscription for Linux
- *`inotify` events.
- * Since these events are limited this EventPublisher will optimize the watch
- * descriptors, keep track of the usage, implement optimizations/priority
- * where possible, and abstract file system events to a path/action context.
- *
- * Uses INotifySubscriptionContext and INotifyEventContext for subscriptioning,
- *eventing.
- */
-class INotifyEventPublisher
- : public EventPublisher<INotifySubscriptionContext, INotifyEventContext> {
- DECLARE_PUBLISHER("inotify");
-
- public:
- //@param unit_test publisher is instantiated for unit test.
- INotifyEventPublisher(bool unit_test = false)
- : inotify_sanity_check(unit_test) {}
-
- virtual ~INotifyEventPublisher() {
- tearDown();
- }
-
- /// Create an `inotify` handle descriptor.
- Status setUp() override;
-
- /// The configuration finished loading or was updated.
- void configure() override;
-
- /// Release the `inotify` handle descriptor.
- void tearDown() override;
-
- /// The calling for beginning the thread's run loop.
- Status run() override;
-
- /// Mark for delete, subscriptions.
- void removeSubscriptions(const std::string& subscriber) override;
-
- /// Only add the subscription, if it not already part of subscription list.
- Status addSubscription(const SubscriptionRef& subscription) override;
-
- private:
- /// Helper/specialized event context creation.
- INotifyEventContextRef createEventContextFrom(
- struct inotify_event* event) const;
-
- /// Check if the application-global `inotify` handle is alive.
- bool isHandleOpen() const {
- return inotify_handle_ > 0;
- }
-
- /// Check all added Subscription%s for a path.
- /// Used for sanity check from unit test(s).
- bool isPathMonitored(const std::string& path) const;
-
- /**
- * @brief Add an INotify watch (monitor) on this path.
- *
- * Check if a given path is already monitored (perhaps the parent path) has
- * and existing monitor and this is a non-directory leaf? On success the
- * file descriptor is stored for lookup when events fire.
- *
- * A recursive flag will tell addMonitor to enumerate all subdirectories
- * recursively and add monitors to them.
- *
- * @param path complete (non-glob) canonical path to monitor.
- * @param subscription context tracking the path.
- * @param recursive perform a single recursive search of subdirectories.
- * @param add_watch (testing only) should an inotify watch be created.
- * @return success if the inotify watch was created.
- */
- bool addMonitor(const std::string& path,
- INotifySubscriptionContextRef& isc,
- uint32_t mask,
- bool recursive,
- bool add_watch = true);
-
- /**
- * Some decision making code refactored in needMonitoring before calling
- * addMonitor in the context of monitorSubscription.
- * Decision to call addMonitor from the context of monitorSubscription
- * is done based on the status change time of file/directory, since
- * creation time is not available on linux.
- */
- bool needMonitoring(const std::string& path,
- INotifySubscriptionContextRef& isc,
- uint32_t mask,
- bool recursive,
- bool add_watch);
-
- /// Helper method to parse a subscription and add an equivalent monitor.
- bool monitorSubscription(INotifySubscriptionContextRef& sc,
- bool add_watch = true);
-
- /// Build the set of excluded paths for which events are not to be propogated.
- void buildExcludePathsSet();
-
- /// Remove an INotify watch (monitor) from our tracking.
- bool removeMonitor(int watch, bool force = false, bool batch_del = false);
-
- /// Given a SubscriptionContext and INotifyEventContext match path and action.
- bool shouldFire(const INotifySubscriptionContextRef& mc,
- const INotifyEventContextRef& ec) const override;
-
- /// Get the INotify file descriptor.
- int getHandle() const {
- return inotify_handle_;
- }
-
- /// Get the number of actual INotify active descriptors.
- size_t numDescriptors() const {
- return descriptor_inosubctx_.size();
- }
-
- /// If we overflow, try to read more events from OS at time.
- void handleOverflow();
-
- /// Map of watched path string to inotify watch file descriptor.
- /// Used for sanity check from unit test(s).
- PathDescriptorMap path_descriptors_;
-
- /// Map of inotify watch file descriptor to subscription context.
- DescriptorINotifySubCtxMap descriptor_inosubctx_;
-
- /// Events pertaining to these paths not to be propagated.
- ExcludePathSet exclude_paths_;
-
- /// The inotify file descriptor handle.
- std::atomic<int> inotify_handle_{-1};
-
- /// Time in seconds of the last inotify overflow.
- std::atomic<int> last_overflow_{-1};
-
- /// Tracks how many events to be received from OS.
- size_t inotify_events_{16};
-
- /// Enable for sanity check from unit test(s).
- bool inotify_sanity_check{false};
-
- /**
- * @brief Scratch space for reading INotify responses.
- *
- * We place this here, and include a mutex to do heap/lazy allocation of the
- * near-3k buffer when the publisher loads. This reduces the need to stack
- * allocate a local buffer every 200mils and also improves the eventless-case.
- *
- * Allocated during setUp, removed in tearDown, protected by scratch_mutex_.
- */
- char* scratch_{nullptr};
-
- /// Access to path and descriptor mappings.
- mutable Mutex path_mutex_;
-
- /// Access the Inofity response scratch space.
- mutable Mutex scratch_mutex_;
-
- public:
- friend class INotifyTests;
- FRIEND_TEST(INotifyTests, test_inotify_init);
- FRIEND_TEST(INotifyTests, test_inotify_optimization);
- FRIEND_TEST(INotifyTests, DISABLED_test_inotify_recursion);
- FRIEND_TEST(INotifyTests, test_inotify_match_subscription);
- FRIEND_TEST(INotifyTests, test_inotify_embedded_wildcards);
-};
-}
+++ /dev/null
-/**
- * Copyright (c) 2014-present, Facebook, Inc.
- * All rights reserved.
- *
- * This source code is licensed in accordance with the terms specified in
- * the LICENSE file found in the root directory of this source tree.
- */
-
-#pragma once
-
-#include <asm/unistd_64.h>
-
-#include <set>
-
-const std::set<int> kProcessEventsSyscalls = {
- __NR_execve};
+++ /dev/null
-/**
- * Copyright (c) 2014-present, Facebook, Inc.
- * All rights reserved.
- *
- * This source code is licensed in accordance with the terms specified in
- * the LICENSE file found in the root directory of this source tree.
- */
-
-#pragma once
-
-#include <asm/unistd_64.h>
-
-#include <set>
-
-const std::set<int> kProcessFileEventsSyscalls = {
- __NR_linkat,
- __NR_symlink,
- __NR_symlinkat,
- __NR_unlink,
- __NR_unlinkat,
- __NR_rename,
- __NR_renameat,
- __NR_renameat2,
- __NR_creat,
- __NR_mknod,
- __NR_mknodat,
- __NR_open,
- __NR_openat,
- __NR_open_by_handle_at,
- __NR_name_to_handle_at,
- __NR_close,
- __NR_dup,
- __NR_dup2,
- __NR_dup3,
- __NR_pread64,
- __NR_preadv,
- __NR_read,
- __NR_readv,
- __NR_mmap,
- __NR_write,
- __NR_writev,
- __NR_pwrite64,
- __NR_pwritev,
- __NR_truncate,
- __NR_ftruncate,
- __NR_clone,
- __NR_fork,
- __NR_vfork};
+++ /dev/null
-/**
- * Copyright (c) 2014-present, Facebook, Inc.
- * All rights reserved.
- *
- * This source code is licensed in accordance with the terms specified in
- * the LICENSE file found in the root directory of this source tree.
- */
-
-#pragma once
-
-#include <linux/audit.h>
-
-#include <set>
-#include <map>
-
-// This map must contain exactly the same elements that
-// SELinuxEventSubscriber::GetEventSet() returns!
-// clang-format off
-const std::map<int, std::string> kSELinuxRecordLabels = {
- {AUDIT_USER_AVC, "USER_AVC"},
- {AUDIT_AVC, "AVC"},
- {AUDIT_SELINUX_ERR, "SELINUX_ERR"},
- {AUDIT_AVC_PATH, "AVC_PATH"},
- {AUDIT_MAC_POLICY_LOAD, "MAC_POLICY_LOAD"},
- {AUDIT_MAC_STATUS, "MAC_STATUS"},
- {AUDIT_MAC_CONFIG_CHANGE, "MAC_CONFIG_CHANGE"},
- {AUDIT_MAC_UNLBL_ALLOW, "MAC_UNLBL_ALLOW"},
- {AUDIT_MAC_CIPSOV4_ADD, "MAC_CIPSOV4_ADD"},
- {AUDIT_MAC_CIPSOV4_DEL, "MAC_CIPSOV4_DEL"},
- {AUDIT_MAC_MAP_ADD, "MAC_MAP_ADD"},
- {AUDIT_MAC_MAP_DEL, "MAC_MAP_DEL"},
- {AUDIT_MAC_IPSEC_ADDSA, "MAC_IPSEC_ADDSA"},
- {AUDIT_MAC_IPSEC_DELSA, "MAC_IPSEC_DELSA"},
- {AUDIT_MAC_IPSEC_ADDSPD, "MAC_IPSEC_ADDSPD"},
- {AUDIT_MAC_IPSEC_DELSPD, "MAC_IPSEC_DELSPD"},
- {AUDIT_MAC_IPSEC_EVENT, "MAC_IPSEC_EVENT"},
- {AUDIT_MAC_UNLBL_STCADD, "MAC_UNLBL_STCADD"},
- {AUDIT_MAC_UNLBL_STCDEL, "MAC_UNLBL_STCDEL"}
-};
-// clang-format on
-
-// Documented events that could not be found in the headers:
-// - USER_SELINUX_ERR
-// - USER_MAC_POLICY_LOAD
-// - USER_ROLE_CHANGE
-// - USER_LABEL_EXPORT
-const std::set<int> kSELinuxEventList = {
- // This is outside the documented numeric range (1400-1499)
- AUDIT_USER_AVC,
-
- AUDIT_AVC,
- AUDIT_SELINUX_ERR,
- AUDIT_AVC_PATH,
- AUDIT_MAC_POLICY_LOAD,
- AUDIT_MAC_STATUS,
- AUDIT_MAC_CONFIG_CHANGE,
- AUDIT_MAC_UNLBL_ALLOW,
- AUDIT_MAC_CIPSOV4_ADD,
- AUDIT_MAC_CIPSOV4_DEL,
- AUDIT_MAC_MAP_ADD,
- AUDIT_MAC_MAP_DEL,
- AUDIT_MAC_IPSEC_ADDSA,
- AUDIT_MAC_IPSEC_DELSA,
- AUDIT_MAC_IPSEC_ADDSPD,
- AUDIT_MAC_IPSEC_DELSPD,
- AUDIT_MAC_IPSEC_EVENT,
- AUDIT_MAC_UNLBL_STCADD,
- AUDIT_MAC_UNLBL_STCDEL};
+++ /dev/null
-/**
- * Copyright (c) 2014-present, Facebook, Inc.
- * All rights reserved.
- *
- * This source code is licensed in accordance with the terms specified in
- * the LICENSE file found in the root directory of this source tree.
- */
-
-#pragma once
-
-#include <asm/unistd_64.h>
-
-#include <set>
-
-const std::set<int> kSocketEventsSyscalls = {
- __NR_bind,
- __NR_connect};
+++ /dev/null
-/**
- * Copyright (c) 2014-present, Facebook, Inc.
- * All rights reserved.
- *
- * This source code is licensed in accordance with the terms specified in
- * the LICENSE file found in the root directory of this source tree.
- */
-
-#include <fcntl.h>
-#include <grp.h>
-#include <sys/file.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <time.h>
-#include <unistd.h>
-
-#include <istream>
-#include <string>
-
-#include <boost/algorithm/string/trim.hpp>
-#include <boost/filesystem.hpp>
-#include <boost/lexical_cast.hpp>
-#include <boost/property_tree/json_parser.hpp>
-#include <boost/tokenizer.hpp>
-#include <osquery/registry_factory.h>
-
-#include <osquery/filesystem/filesystem.h>
-#include <osquery/flags.h>
-#include <osquery/logger.h>
-
-#include "osquery/events/linux/syslog.h"
-
-namespace fs = boost::filesystem;
-
-namespace osquery {
-
-FLAG(bool, enable_syslog, false, "Enable the syslog ingestion event publisher");
-
-FLAG(string,
- syslog_pipe_path,
- "/var/osquery/syslog_pipe",
- "Path to the named pipe used for forwarding rsyslog events");
-
-FLAG(uint64,
- syslog_rate_limit,
- 100,
- "Maximum number of logs to ingest per run (~200ms between runs)");
-
-REGISTER(SyslogEventPublisher, "event_publisher", "syslog");
-
-// rsyslog needs read/write access, osquery process needs read access
-const mode_t kPipeMode = 0460;
-const std::string kPipeGroupName = "syslog";
-const char* kTimeFormat = "%Y-%m-%dT%H:%M:%S";
-const std::vector<std::string> kCsvFields = {
- "time", "host", "severity", "facility", "tag", "message"};
-const size_t kErrorThreshold = 10;
-
-Status SyslogEventPublisher::setUp() {
- if (!FLAGS_enable_syslog) {
- return Status(1, "Publisher disabled via configuration");
- }
-
- Status s;
- if (!pathExists(FLAGS_syslog_pipe_path)) {
- VLOG(1) << "Pipe does not exist: creating pipe " << FLAGS_syslog_pipe_path;
- s = createPipe(FLAGS_syslog_pipe_path);
- if (!s.ok()) {
- LOG(WARNING) << RLOG(1964)
- << "Problems encountered creating pipe: " << s.getMessage();
- }
- }
-
- fs::file_status file_status = fs::status(FLAGS_syslog_pipe_path);
- if (file_status.type() != fs::fifo_file) {
- return Status(1, "Not a FIFO file: " + FLAGS_syslog_pipe_path);
- }
-
- // Try to acquire a lock on the pipe, to make sure we're the only osquery
- // related process reading from it.
- s = lockPipe(FLAGS_syslog_pipe_path);
- if (!s.ok()) {
- return s;
- }
-
- // Opening with both flags appears to be the only way to open the pipe
- // without blocking for a writer. We won't ever write to the pipe, but we
- // don't want to block here and will instead block waiting for a read in the
- // run() method
- readStream_.open(FLAGS_syslog_pipe_path,
- std::ifstream::in | std::ifstream::out);
- if (!readStream_.good()) {
- return Status(1,
- "Error opening pipe for reading: " + FLAGS_syslog_pipe_path);
- }
- VLOG(1) << "Successfully opened pipe for syslog ingestion: "
- << FLAGS_syslog_pipe_path;
-
- return Status::success();
-}
-
-Status SyslogEventPublisher::createPipe(const std::string& path) {
- if (mkfifo(FLAGS_syslog_pipe_path.c_str(), kPipeMode) != 0) {
- return Status(1, "Error in mkfifo: " + std::string(strerror(errno)));
- }
-
- // Explicitly set the permissions since the umask will effect the
- // permissions created by mkfifo
- if (chmod(FLAGS_syslog_pipe_path.c_str(), kPipeMode) != 0) {
- return Status(1, "Error in chmod: " + std::string(strerror(errno)));
- }
-
- // Try to set the group so that rsyslog will be able to write to the pipe
- struct group* group = getgrnam(kPipeGroupName.c_str());
- if (group == nullptr) {
- VLOG(1) << "No group " << kPipeGroupName
- << " found. Not changing group for the pipe.";
- return Status::success();
- }
- if (chown(FLAGS_syslog_pipe_path.c_str(), -1, group->gr_gid) == -1) {
- return Status(1,
- "Error in chown to group " + kPipeGroupName + ": " +
- std::string(strerror(errno)));
- }
- return Status::success();
-}
-
-Status SyslogEventPublisher::lockPipe(const std::string& path) {
- lockFd_ = open(path.c_str(), O_NONBLOCK);
- if (lockFd_ == -1) {
- return Status(
- 1, "Error in open for locking pipe: " + std::string(strerror(errno)));
- }
- if (flock(lockFd_, LOCK_EX | LOCK_NB) != 0) {
- close(lockFd_);
- lockFd_ = -1;
- return Status(
- 1, "Unable to acquire pipe lock: " + std::string(strerror(errno)));
- }
- return Status::success();
-}
-
-void SyslogEventPublisher::unlockPipe() {
- if (lockFd_ != -1) {
- if (flock(lockFd_, LOCK_UN) != 0) {
- LOG(WARNING) << "Error unlocking pipe: " << std::string(strerror(errno));
- }
- close(lockFd_);
- lockFd_ = -1;
- }
-}
-
-Status SyslogEventPublisher::run() {
- // This run function will be called by the event factory with ~100ms pause
- // (see InterruptableRunnable::pause()) between runs. In case something goes
- // weird and there is a huge amount of input, we limit how many logs we
- // take in per run to avoid pegging the CPU.
- for (size_t i = 0; i < FLAGS_syslog_rate_limit; ++i) {
- if (readStream_.rdbuf()->in_avail() == 0) {
- // If there is no pending data, we have flushed everything and can wait
- // until the next time EventFactory calls run(). This also allows the
- // thread to join when it is stopped by EventFactory.
- return Status::success();
- }
- std::string line;
- std::getline(readStream_, line);
- auto ec = createEventContext();
- Status status = populateEventContext(line, ec);
- if (status.ok()) {
- fire(ec);
- if (errorCount_ > 0) {
- --errorCount_;
- }
- } else {
- LOG(ERROR) << status.getMessage() << " in line: " << line;
- ++errorCount_;
- if (errorCount_ >= kErrorThreshold) {
- return Status(1, "Too many errors in syslog parsing.");
- }
- }
- }
- return Status::success();
-}
-
-void SyslogEventPublisher::tearDown() {
- unlockPipe();
-}
-
-Status SyslogEventPublisher::populateEventContext(const std::string& line,
- SyslogEventContextRef& ec) {
- boost::tokenizer<RsyslogCsvSeparator> tokenizer(line);
- auto key = kCsvFields.begin();
- for (std::string value : tokenizer) {
- if (key == kCsvFields.end()) {
- return Status(1, "Received more fields than expected");
- }
-
- boost::trim(value);
- if (*key == "time") {
- ec->fields["datetime"] = value;
- } else if (*key == "tag" && !value.empty() && value.back() == ':') {
- // rsyslog sends "tag" with a trailing colon that we don't need
- ec->fields.emplace(*key, value.substr(0, value.size() - 1));
- } else {
- ec->fields.emplace(*key, value);
- }
- ++key;
- }
-
- if (key == kCsvFields.end()) {
- return Status::success();
- } else {
- return Status(1, "Received fewer fields than expected");
- }
-}
-
-bool SyslogEventPublisher::shouldFire(const SyslogSubscriptionContextRef& sc,
- const SyslogEventContextRef& ec) const {
- return true;
-}
-}
+++ /dev/null
-/**
- * Copyright (c) 2014-present, Facebook, Inc.
- * All rights reserved.
- *
- * This source code is licensed in accordance with the terms specified in
- * the LICENSE file found in the root directory of this source tree.
- */
-
-#pragma once
-
-#include <stdio.h>
-
-#include <fstream>
-#include <map>
-
-#include <osquery/events.h>
-
-namespace osquery {
-
-/**
- * @brief Subscription context for syslog events
- *
- * Currently there is no use for the subscription context, so this class
- * remains empty.
- */
-
-struct SyslogSubscriptionContext : public SubscriptionContext {
- private:
- friend class SyslogEventPublisher;
-};
-
-/**
- * @brief Event details for SyslogEventPublisher events
- */
-struct SyslogEventContext : public EventContext {
- /**
- * @brief The syslog message tokenized into fields.
- *
- * Fields will be stripped of extra space
- */
- std::map<std::string, std::string> fields;
-};
-
-using SyslogEventContextRef = std::shared_ptr<SyslogEventContext>;
-using SyslogSubscriptionContextRef = std::shared_ptr<SyslogSubscriptionContext>;
-
-/**
- * @brief Event publisher for syslog lines forwarded through rsyslog
- *
- * This event publisher ingests JSON representations of syslog entries, and
- * publishes them to it's subscribers. In order for it to function properly,
- * rsyslog must be configured to forward JSON to a named pipe that this
- * publisher will read from.
- */
-class SyslogEventPublisher
- : public EventPublisher<SyslogSubscriptionContext, SyslogEventContext> {
- DECLARE_PUBLISHER("syslog");
-
- public:
- Status setUp() override;
-
- void configure() override {}
-
- void tearDown() override;
-
- Status run() override;
-
- public:
- SyslogEventPublisher() : EventPublisher(), errorCount_(0), lockFd_(-1) {}
-
- private:
- /// Apply normal subscription to event matching logic.
- bool shouldFire(const SyslogSubscriptionContextRef& mc,
- const SyslogEventContextRef& ec) const override;
-
- /**
- * @brief Create the named pipe for log forwarding.
- *
- * Attempts to properly set the permissions so that rsyslog will be able to
- * write logs to the pipe. If osquery is not running with the appropriate
- * permissions, the named pipe permissions may have to be manually edited by
- * the user in order for rsyslog to be able to write to it.
- */
- Status createPipe(const std::string& path);
-
- /**
- * @brief Attempt to lock the pipe for reading.
- *
- * We lock the pipe to ensure that (for example) a user opening osqueryi
- * while osqueryd is running will not try to simultaneously read from the
- * pipe and invalidate the reads from osqueryd. Only the first osquery
- * process to successfully lock the pipe will be allowed to read.
- *
- * @param path Path to the file to lock.
- * @return 0 if successful, nonzero if unable to lock the file.
- */
- Status lockPipe(const std::string& path);
-
- /**
- * @brief Attempt to unlock the pipe.
- */
- void unlockPipe();
-
- /**
- * @brief Populate the SyslogEventContext with the syslog JSON.
- *
- * Performs basic cleanup on the JSON data as it is populated into the
- * context.
- */
- static Status populateEventContext(const std::string& line,
- SyslogEventContextRef& ec);
-
- /**
- * @brief Input stream for reading from the pipe.
- */
- std::fstream readStream_;
-
- /**
- * @brief Counter used to shut down thread when too many errors occur.
- *
- * This counter is incremented when an error occurs, and decremented when a
- * log line is processed successfully. If it goes over kErrorThreshold, the
- * thread will return a nonzero status and stop, preventing us from flooding
- * the logs when things are in a bad state.
- */
- size_t errorCount_;
-
- /**
- * @brief File descriptor used to lock the pipe for reading.
- *
- * This fd should not be used for reading from the pipe, instead use
- * readStream_.
- */
- int lockFd_;
-
- private:
- FRIEND_TEST(SyslogTests, test_populate_event_context);
-};
-
-/**
- * Boost TokenizerFunction functor for tokenizing rsyslog CSV data
- *
- * This functor is intended to be used with boost::tokenizer in order to
- * properly parse CSV data generated by rsyslog. The default
- * boost::escaped_list_separator provided with boost::tokenizer chokes on
- * rsyslog CSV output, because rsyslog escapes " with "", and also does not
- * escape backslashes. Our implementation closely follows the one provided with
- * Boost, but allows for the idiosyncrasies of rsyslog output and simplifies
- * the implementation for our limited use case.
- */
-class RsyslogCsvSeparator {
- public:
- RsyslogCsvSeparator() : last_(false) {}
-
- void reset() {
- last_ = false;
- }
-
- template <typename InputIterator, typename Token>
- bool operator()(InputIterator& next, InputIterator end, Token& tok) {
- bool in_quote = false;
- tok = Token();
-
- if (next == end) {
- if (last_) {
- // The last character was a comma, so we got an empty field at the end
- last_ = false;
- return true;
- } else {
- return false;
- }
- }
- last_ = false;
- for (; next != end; ++next) {
- if (*next == ',') {
- if (!in_quote) {
- ++next;
- last_ = true;
- return true;
- } else {
- tok += *next;
- }
- } else if (*next == '"') {
- auto after = next + 1;
- if (!in_quote) {
- in_quote = true;
- } else if (after != end && *after == '"') {
- // rsyslog escapes " with "", so reverse this by inserting "
- tok += "\"";
- ++next;
- } else {
- in_quote = false;
- }
- } else {
- tok += *next;
- }
- }
- return true;
- }
-
- private:
- bool last_;
-};
-}
+++ /dev/null
-/**
- * Copyright (c) 2014-present, Facebook, Inc.
- * All rights reserved.
- *
- * This source code is licensed in accordance with the terms specified in
- * the LICENSE file found in the root directory of this source tree.
- */
-
-#include <poll.h>
-
-#include <osquery/events.h>
-#include <osquery/filesystem/filesystem.h>
-#include <osquery/logger.h>
-#include <osquery/registry_factory.h>
-
-#include "osquery/events/linux/udev.h"
-
-namespace osquery {
-
-static const int kUdevMLatency = 200;
-
-REGISTER(UdevEventPublisher, "event_publisher", "udev");
-
-Status UdevEventPublisher::setUp() {
- // The Setup and Teardown workflows should be protected against races.
- // Just in case let's protect the publisher's resources.
- WriteLock lock(mutex_);
-
- // Create the udev object.
- handle_ = udev_new();
- if (handle_ == nullptr) {
- return Status(1, "Could not create udev object.");
- }
-
- // Set up the udev monitor before scanning/polling.
- monitor_ = udev_monitor_new_from_netlink(handle_, "udev");
- if (monitor_ == nullptr) {
- udev_unref(handle_);
- handle_ = nullptr;
- return Status(1, "Could not create udev monitor.");
- }
-
- udev_monitor_enable_receiving(monitor_);
- return Status::success();
-}
-
-void UdevEventPublisher::tearDown() {
- WriteLock lock(mutex_);
- if (monitor_ != nullptr) {
- udev_monitor_unref(monitor_);
- monitor_ = nullptr;
- }
-
- if (handle_ != nullptr) {
- udev_unref(handle_);
- handle_ = nullptr;
- }
-}
-
-Status UdevEventPublisher::run() {
- {
- WriteLock lock(mutex_);
- if (monitor_ == nullptr) {
- return Status(1);
- }
- int fd = udev_monitor_get_fd(monitor_);
- if (fd < 0) {
- LOG(ERROR) << "Could not get udev monitor fd";
- return Status::failure("udev monitor failed");
- }
-
- struct pollfd fds[1];
- fds[0].fd = fd;
- fds[0].events = POLLIN;
-
- int selector = ::poll(fds, 1, 1000);
- if (selector == -1 && errno != EINTR && errno != EAGAIN) {
- LOG(ERROR) << "Could not read udev monitor";
- return Status(1, "udev monitor failed.");
- }
-
- if (selector == 0 || !(fds[0].revents & POLLIN)) {
- // Read timeout.
- return Status::success();
- }
-
- struct udev_device* device = udev_monitor_receive_device(monitor_);
- if (device == nullptr) {
- LOG(ERROR) << "udev monitor returned invalid device";
- return Status(1, "udev monitor failed.");
- }
-
- auto ec = createEventContextFrom(device);
- fire(ec);
-
- udev_device_unref(device);
- }
-
- pause(std::chrono::milliseconds(kUdevMLatency));
- return Status::success();
-}
-
-std::string UdevEventPublisher::getValue(struct udev_device* device,
- const std::string& property) {
- auto value = udev_device_get_property_value(device, property.c_str());
- if (value != nullptr) {
- return std::string(value);
- }
- return "";
-}
-
-std::string UdevEventPublisher::getAttr(struct udev_device* device,
- const std::string& attr) {
- auto value = udev_device_get_sysattr_value(device, attr.c_str());
- if (value != nullptr) {
- return std::string(value);
- }
- return "";
-}
-
-UdevEventContextRef UdevEventPublisher::createEventContextFrom(
- struct udev_device* device) {
- auto ec = createEventContext();
- ec->device = device;
- // Map the action string to the eventing enum.
- ec->action = UDEV_EVENT_ACTION_UNKNOWN;
- ec->action_string = std::string(udev_device_get_action(device));
- if (ec->action_string == "add") {
- ec->action = UDEV_EVENT_ACTION_ADD;
- } else if (ec->action_string == "remove") {
- ec->action = UDEV_EVENT_ACTION_REMOVE;
- } else if (ec->action_string == "change") {
- ec->action = UDEV_EVENT_ACTION_CHANGE;
- }
-
- // Set the subscription-aware variables for the event.
- auto value = udev_device_get_subsystem(device);
- if (value != nullptr) {
- ec->subsystem = std::string(value);
- }
-
- value = udev_device_get_devnode(device);
- if (value != nullptr) {
- ec->devnode = std::string(value);
- }
-
- value = udev_device_get_devtype(device);
- if (value != nullptr) {
- ec->devtype = std::string(value);
- }
-
- value = udev_device_get_driver(device);
- if (value != nullptr) {
- ec->driver = std::string(value);
- }
-
- return ec;
-}
-
-bool UdevEventPublisher::shouldFire(const UdevSubscriptionContextRef& sc,
- const UdevEventContextRef& ec) const {
- if (sc->action != UDEV_EVENT_ACTION_ALL) {
- if (sc->action != ec->action) {
- return false;
- }
- }
-
- if (sc->subsystem.length() != 0 && sc->subsystem != ec->subsystem) {
- return false;
- } else if (sc->devnode.length() != 0 && sc->devnode != ec->devnode) {
- return false;
- } else if (sc->devtype.length() != 0 && sc->devtype != ec->devtype) {
- return false;
- } else if (sc->driver.length() != 0 && sc->driver != ec->driver) {
- return false;
- }
-
- return true;
-}
-} // namespace osquery
+++ /dev/null
-/**
- * Copyright (c) 2014-present, Facebook, Inc.
- * All rights reserved.
- *
- * This source code is licensed in accordance with the terms specified in
- * the LICENSE file found in the root directory of this source tree.
- */
-
-#pragma once
-
-#include <libudev.h>
-
-#include <osquery/events.h>
-#include <osquery/utils/status/status.h>
-
-namespace osquery {
-
-enum udev_event_action {
- UDEV_EVENT_ACTION_ADD = 1,
- UDEV_EVENT_ACTION_REMOVE = 2,
- UDEV_EVENT_ACTION_CHANGE = 3,
- UDEV_EVENT_ACTION_UNKNOWN = 4,
-
- // Custom subscriber-only catch-all for actions.
- UDEV_EVENT_ACTION_ALL = 10,
-};
-
-/**
- * @brief Subscriptioning details for UdevEventPublisher events.
- *
- */
-struct UdevSubscriptionContext : public SubscriptionContext {
- /// The hardware event action, add/remove/change.
- udev_event_action action;
-
- /// Restrict to a specific subsystem.
- std::string subsystem;
-
- /// Restrict to a specific devnode.
- std::string devnode;
-
- /// Restrict to a specific devtype.
- std::string devtype;
-
- /// Limit to a specific driver name.
- std::string driver;
-};
-
-/**
- * @brief Event details for UdevEventPublisher events.
- */
-struct UdevEventContext : public EventContext {
- /// A pointer to the device object, most subscribers will only use device.
- struct udev_device* device{nullptr};
-
- /// The udev_event_action identifier.
- udev_event_action action;
-
- /// Action as a string (as given by udev).
- std::string action_string;
-
- std::string subsystem;
- std::string devnode;
- std::string devtype;
- std::string driver;
-};
-
-using UdevEventContextRef = std::shared_ptr<UdevEventContext>;
-using UdevSubscriptionContextRef = std::shared_ptr<UdevSubscriptionContext>;
-
-/**
- * @brief A Linux `udev` EventPublisher.
- *
- */
-class UdevEventPublisher
- : public EventPublisher<UdevSubscriptionContext, UdevEventContext> {
- DECLARE_PUBLISHER("udev");
-
- public:
- virtual ~UdevEventPublisher() {
- tearDown();
- }
-
- Status setUp() override;
-
- void tearDown() override;
-
- Status run() override;
-
- /**
- * @brief Return a string representation of a udev property.
- *
- * @param device the udev device pointer.
- * @param property the udev property identifier string.
- * @return string representation of the property or empty if null.
- */
- static std::string getValue(struct udev_device* device,
- const std::string& property);
-
- /**
- * @brief Return a string representation of a udev system attribute.
- *
- * @param device the udev device pointer.
- * @param property the udev system attribute identifier string.
- * @return string representation of the attribute or empty if null.
- */
- static std::string getAttr(struct udev_device* device,
- const std::string& attr);
-
- private:
- /// udev handle (socket descriptor contained within).
- struct udev* handle_{nullptr};
-
- /// udev monitor.
- struct udev_monitor* monitor_{nullptr};
-
- /// Protection around udev resources.
- Mutex mutex_;
-
- private:
- /// Check subscription details.
- bool shouldFire(const UdevSubscriptionContextRef& mc,
- const UdevEventContextRef& ec) const override;
-
- /// Helper function to create an EventContext using a udev_device pointer.
- UdevEventContextRef createEventContextFrom(struct udev_device* device);
-};
-}
+++ /dev/null
-/**
- * Copyright (c) 2014-present, Facebook, Inc.
- * All rights reserved.
- *
- * This source code is licensed in accordance with the terms specified in
- * the LICENSE file found in the root directory of this source tree.
- */
-
-#pragma once
-
-#include <mutex>
-#include <set>
-#include <string>
-#include <vector>
-
-#include <boost/noncopyable.hpp>
-#include <boost/tokenizer.hpp>
-
-#include <osquery/core.h>
-#include <osquery/filesystem/filesystem.h>
-
-namespace osquery {
-
-/**
- * @brief multiset based implementation for path search.
- *
- * 'multiset' is used because with patterns we can serach for equivalent keys.
- * Since '/This/Path/is' ~= '/This/Path/%' ~= '/This/Path/%%' (equivalent).
- *
- * multiset is protected by lock. It is threadsafe.
- *
- * PathSet can take any of the two policies -
- * 1. patternedPath - Path can contain pattern '%' and '%%'.
- * Path components containing only '%' and '%%' are supported
- * e.g. '/This/Path/%'.
- * Path components containing partial patterns are not
- * supported e.g. '/This/Path/xyz%' ('xyz%' will not be
- * treated as pattern).
- */
-template <typename PathType>
-class PathSet : private boost::noncopyable {
- public:
- void insert(const std::string& str) {
- auto pattern = str;
- replaceGlobWildcards(pattern);
- auto vpath = PathType::createVPath(pattern);
-
- WriteLock lock(mset_lock_);
- for (auto& path : vpath) {
- paths_.insert(std::move(path));
- }
- }
-
- bool find(const std::string& str) const {
- auto path = PathType::createPath(str);
-
- ReadLock lock(mset_lock_);
- if (paths_.find(path) != paths_.end()) {
- return true;
- }
- return false;
- }
-
- void clear() {
- WriteLock lock(mset_lock_);
- paths_.clear();
- }
-
- bool empty() const {
- ReadLock lock(mset_lock_);
- return paths_.empty();
- }
-
- private:
- typedef typename PathType::Path Path;
- typedef typename PathType::Compare Compare;
- std::multiset<Path, Compare> paths_;
- mutable Mutex mset_lock_;
-};
-
-class patternedPath {
- public:
- typedef boost::tokenizer<boost::char_separator<char>> tokenizer;
- typedef std::vector<std::string> Path;
- typedef std::vector<Path> VPath;
- struct Compare {
- bool operator()(const Path& lhs, const Path& rhs) const {
- size_t psize = (lhs.size() < rhs.size()) ? lhs.size() : rhs.size();
- unsigned ndx;
- for (ndx = 0; ndx < psize; ++ndx) {
- if (lhs[ndx] == "**" || rhs[ndx] == "**") {
- return false;
- }
-
- if (lhs[ndx] == "*" || rhs[ndx] == "*") {
- continue;
- }
-
- int rc = lhs[ndx].compare(rhs[ndx]);
-
- if (rc > 0) {
- return false;
- }
-
- if (rc < 0) {
- return true;
- }
- }
-
- if ((ndx == rhs.size() && rhs[ndx - 1] == "*") ||
- (ndx == lhs.size() && lhs[ndx - 1] == "*")) {
- return false;
- }
-
- return (lhs.size() < rhs.size());
- }
- };
-
- static Path createPath(const std::string& str) {
- boost::char_separator<char> sep{"/"};
- tokenizer tokens(str, sep);
- Path path;
-
- if (str == "/") {
- path.push_back("");
- }
-
- for (std::string component : tokens) {
- path.push_back(std::move(component));
- }
- return path;
- }
-
- static VPath createVPath(const std::string& str) {
- boost::char_separator<char> sep{"/"};
- tokenizer tokens(str, sep);
- VPath vpath;
- Path path;
-
- if (str == "/") {
- path.push_back("");
- }
-
- for (std::string component : tokens) {
- if (component == "**") {
- vpath.push_back(path);
- path.push_back(std::move(component));
- break;
- }
- path.push_back(std::move(component));
- }
- vpath.push_back(std::move(path));
- return vpath;
- }
-};
-
-} // namespace osquery
+++ /dev/null
-/**
- * Copyright (c) 2014-present, Facebook, Inc.
- * All rights reserved.
- *
- * This source code is licensed in accordance with the terms specified in
- * the LICENSE file found in the root directory of this source tree.
- */
-
-#include <memory>
-
-#include <boost/algorithm/string.hpp>
-#include <boost/filesystem/operations.hpp>
-
-#include <gtest/gtest.h>
-
-#include <osquery/core.h>
-#include <osquery/core/sql/row.h>
-#include <osquery/database.h>
-#include <osquery/events.h>
-#include <osquery/flags.h>
-#include <osquery/logger.h>
-#include <osquery/registry_factory.h>
-#include <osquery/system.h>
-#include <osquery/tables.h>
-#include <osquery/utils/system/time.h>
-
-namespace osquery {
-DECLARE_bool(disable_database);
-
-static TableRows genRows(EventSubscriberPlugin* sub) {
- auto vtc = std::make_shared<VirtualTableContent>();
- QueryContext context(vtc);
- TableRows results;
- return results;
-}
-
-DECLARE_uint64(events_expiry);
-DECLARE_uint64(events_max);
-DECLARE_bool(events_optimize);
-
-class EventsDatabaseTests : public ::testing::Test {
- void SetUp() override {
- registryAndPluginInit();
-
- FLAGS_disable_database = true;
- DatabasePlugin::setAllowOpen(true);
- DatabasePlugin::initPlugin();
-
- std::vector<std::string> event_keys;
- scanDatabaseKeys(kEvents, event_keys);
- for (const auto& key : event_keys) {
- deleteDatabaseValue(kEvents, key);
- }
- }
-
- void TearDown() override {
- FLAGS_events_optimize = optimize_;
- }
-
- private:
- bool optimize_;
-};
-
-class DBFakeEventPublisher
- : public EventPublisher<SubscriptionContext, EventContext> {
- DECLARE_PUBLISHER("DBFakePublisher");
-};
-
-class DBFakeEventSubscriber : public EventSubscriber<DBFakeEventPublisher> {
- public:
- DBFakeEventSubscriber() {
- setName("DBFakeSubscriber");
- setEventsMax(FLAGS_events_max);
- setEventsExpiry(FLAGS_events_expiry);
- }
-
- /// Add num_of_events fake events at time t
- Status testAdd(time_t t, size_t num_of_events = 1) {
- auto indexes = getIndexes(0, t);
- auto records = getRecords(indexes);
- const size_t old_records_size = records.size();
-
- Row r;
- r["testing"] = "hello from space";
- r["time"] = INTEGER(t);
- r["uptime"] = INTEGER(10);
-
- std::vector<Row> row_list;
- for (size_t i = 0U; i < num_of_events; i++) {
- row_list.push_back(r);
- }
-
- auto status = addBatch(row_list, t);
- if (!status.ok()) {
- return Status::failure(
- "Failed to save the batch to the database, with error: " +
- status.getMessage());
- }
-
- indexes = getIndexes(0, t);
- records = getRecords(indexes);
-
- if (records.size() != row_list.size() + old_records_size) {
- return Status::failure("We expected " + std::to_string(row_list.size()) +
- " records but only " +
- std::to_string(records.size()) + " were found!");
- }
-
- return Status::success();
- }
-
- size_t getEventsMax() override {
- return max_;
- }
-
- void setEventsMax(size_t max) {
- max_ = max;
- }
-
- size_t getEventsExpiry() override {
- return expiry_;
- }
-
- void setEventsExpiry(size_t expiry) {
- expiry_ = expiry;
- }
-
- private:
- size_t max_;
-
- size_t expiry_;
-};
-
-TEST_F(EventsDatabaseTests, test_event_module_id) {
- auto sub = std::make_shared<DBFakeEventSubscriber>();
- sub->doNotExpire();
-
- // Not normally available outside of EventSubscriber->Add().
- auto event_id1 = sub->getEventID();
- EXPECT_EQ(event_id1, "0000000001");
- auto event_id2 = sub->getEventID();
- EXPECT_EQ(event_id2, "0000000002");
-}
-
-TEST_F(EventsDatabaseTests, test_event_add) {
- auto sub = std::make_shared<DBFakeEventSubscriber>();
- auto status = sub->testAdd(1);
- EXPECT_TRUE(status.ok());
-}
-
-TEST_F(EventsDatabaseTests, test_event_add_batch) {
- auto sub = std::make_shared<DBFakeEventSubscriber>();
- auto status = sub->testAdd(1, 10);
- EXPECT_TRUE(status.ok()) << status.getMessage();
-}
-
-TEST_F(EventsDatabaseTests, test_record_indexing) {
- auto sub = std::make_shared<DBFakeEventSubscriber>();
- auto status = sub->testAdd(2);
- status = sub->testAdd(11);
- status = sub->testAdd(61);
- status = sub->testAdd((1 * 3600) + 1);
- status = sub->testAdd((2 * 3600) + 1);
-
- // An "all" range, will pick up everything in the largest index.
- auto indexes = sub->getIndexes(0, 3 * 3600);
- auto output = boost::algorithm::join(indexes, ", ");
- EXPECT_EQ("60.0, 60.1, 60.60, 60.120", output);
-
- // Restrict range to "most specific", which is an index by 10.
- indexes = sub->getIndexes(0, 5);
- output = boost::algorithm::join(indexes, ", ");
- // The order 10, 0th index include results with t = [0, 10).
- EXPECT_EQ("60.0", output);
-
- // Add specific indexes to the upper bound.
- status = sub->testAdd((2 * 3600) + 11);
- status = sub->testAdd((2 * 3600) + 61);
- indexes = sub->getIndexes(2 * 3600, (2 * 3600) + 62);
- output = boost::algorithm::join(indexes, ", ");
- EXPECT_EQ("60.120, 60.121", output);
-
- // Request specific lower and upper bounding.
- indexes = sub->getIndexes(2, (2 * 3600) + 62);
- output = boost::algorithm::join(indexes, ", ");
- EXPECT_EQ("60.0, 60.1, 60.60, 60.120, 60.121", output);
-}
-
-TEST_F(EventsDatabaseTests, test_record_range) {
- auto sub = std::make_shared<DBFakeEventSubscriber>();
- auto status = sub->testAdd(1);
- status = sub->testAdd(2);
- status = sub->testAdd(11);
- status = sub->testAdd(61);
- status = sub->testAdd((1 * 3600) + 1);
- status = sub->testAdd((2 * 3600) + 1);
-
- // Search within a specific record range.
- auto indexes = sub->getIndexes(0, 10);
- EXPECT_EQ(1U, indexes.size());
- auto records = sub->getRecords(indexes);
- // This will return 3 results, ::get filters records by an absolute range.
- EXPECT_EQ(3U, records.size()); // 1, 2, 11
-
- // Search within a large bound.
- indexes = sub->getIndexes(3, 3601);
- // This will include the 0-10 bucket meaning 1, 2 will show up.
- records = sub->getRecords(indexes);
- EXPECT_EQ(5U, records.size()); // 1, 2, 11, 61, 3601
-
- // Get all of the records.
- indexes = sub->getIndexes(0, 3 * 3600);
- records = sub->getRecords(indexes);
- EXPECT_EQ(6U, records.size()); // 1, 2, 11, 61, 3601, 7201
-
- // stop = 0 is an alias for everything.
- indexes = sub->getIndexes(0, 0);
- records = sub->getRecords(indexes);
- EXPECT_EQ(6U, records.size());
-
- for (size_t j = 0; j < 30; j++) {
- // 110 is 10 below an index (60.2).
- sub->testAdd(110 + static_cast<int>(j));
- }
-
- indexes = sub->getIndexes(110, 0);
- auto output = boost::algorithm::join(indexes, ", ");
- EXPECT_EQ("60.1, 60.2, 60.60, 60.120", output);
- records = sub->getRecords(indexes);
- EXPECT_EQ(33U, records.size()); // (61) 110 - 139 + 3601, 7201
-}
-
-TEST_F(EventsDatabaseTests, test_record_corruption) {
- auto sub = std::make_shared<DBFakeEventSubscriber>();
-
- std::string corrupted_index = "60.25440186";
- std::string key =
- "records.DBFakePublisher.DBFakeSubscriber." + corrupted_index;
- std::string value =
- "0002985852:1526411162,0002985853:1526411162,00?\16?\bE/"
- "?:1526411170,0002985912:1526411170,0002?\16?\bE/"
- "?526411178,0002985921:1526411178,0002985922:1526411178";
-
- // Set some corrupted values in the DB
- auto s = setDatabaseValue(kEvents, key, value);
- auto records = sub->getRecords({corrupted_index});
-
- // We should gracefully skip over corrupted record entries
- EXPECT_EQ(6U, records.size());
-}
-
-TEST_F(EventsDatabaseTests, test_record_expiration) {
- auto sub = std::make_shared<DBFakeEventSubscriber>();
- auto status = sub->testAdd(1);
- status = sub->testAdd(2);
- status = sub->testAdd(11);
- status = sub->testAdd(61);
- status = sub->testAdd((1 * 3600) + 1);
- status = sub->testAdd((2 * 3600) + 1);
-
- // No expiration
- auto indexes = sub->getIndexes(0, 5000);
- auto records = sub->getRecords(indexes);
- EXPECT_EQ(5U, records.size()); // 1, 2, 11, 61, 3601
-
- sub->expire_events_ = true;
- sub->expire_time_ = 10;
- indexes = sub->getIndexes(0, 5000);
- records = sub->getRecords(indexes);
- EXPECT_EQ(3U, records.size()); // 11, 61, 3601
-
- indexes = sub->getIndexes(0, 5000);
- records = sub->getRecords(indexes);
- EXPECT_EQ(3U, records.size()); // 11, 61, 3601
-
- indexes = sub->getIndexes(0, 5000);
- records = sub->getRecords(indexes);
- EXPECT_EQ(3U, records.size()); // 11, 61, 3601
-
- indexes = sub->getIndexes(0, 5000);
- records = sub->getRecords(indexes);
- EXPECT_EQ(3U, records.size()); // 11, 61, 3601
-
- // Check that get/deletes did not act on cache.
- // This implies that RocksDB is flushing the requested delete records.
- sub->expire_time_ = 0;
- indexes = sub->getIndexes(0, 5000);
- records = sub->getRecords(indexes);
- EXPECT_EQ(3U, records.size()); // 11, 61, 3601
-}
-
-TEST_F(EventsDatabaseTests, test_gentable) {
- auto sub = std::make_shared<DBFakeEventSubscriber>();
- auto status = sub->testAdd(1);
- status = sub->testAdd(2);
- status = sub->testAdd(11);
- status = sub->testAdd(61);
- status = sub->testAdd((1 * 3600) + 1);
- status = sub->testAdd((2 * 3600) + 1);
-
- ASSERT_EQ(0U, sub->optimize_time_);
- ASSERT_EQ(0U, sub->expire_time_);
- ASSERT_EQ(0U, sub->min_expiration_);
-
- auto t = getUnixTime();
- sub->testAdd(t - 1);
- sub->testAdd(t);
- sub->testAdd(t + 1);
-
- // Test the expire workflow by creating a short expiration time.
- sub->setEventsExpiry(10);
-
- std::vector<std::string> keys;
- scanDatabaseKeys("events", keys);
- // 9 data records, 1 eid counter, 3 indexes, 15 index records.
- // Depending on the moment, an additional 3 indexes may be introduced.
- EXPECT_LE(16U, keys.size());
-
- // Perform a "select" equivalent.
- auto results = genRows(sub.get());
-
- // Expect all non-expired results: 11, +
- EXPECT_EQ(9U, results.size());
- // The expiration time is now - events_expiry +/ 60.
- EXPECT_LT(t - (sub->getEventsExpiry() * 2), sub->expire_time_ + 60);
- EXPECT_GT(t, sub->expire_time_);
- // The optimize time will not be changed.
- ASSERT_EQ(0U, sub->optimize_time_);
-
- results = genRows(sub.get());
- EXPECT_EQ(3U, results.size());
-
- results = genRows(sub.get());
- EXPECT_EQ(3U, results.size());
-
- keys.clear();
- scanDatabaseKeys("events", keys);
- EXPECT_LE(6U, keys.size());
-}
-
-TEST_F(EventsDatabaseTests, test_optimize) {
- auto sub = std::make_shared<DBFakeEventSubscriber>();
- for (size_t i = 800; i < 800 + 10; ++i) {
- sub->testAdd(i);
- }
-
- // Lie about the tool type to enable optimizations.
- auto default_type = kToolType;
- kToolType = ToolType::DAEMON;
- FLAGS_events_optimize = true;
-
- // Must also define an executing query.
- setDatabaseValue(kPersistentSettings, "", "events_db_test");
-
- auto t = getUnixTime();
- auto results = genRows(sub.get());
- EXPECT_EQ(10U, results.size());
- // Optimization will set the time NOW as the minimum event time.
- // Thus it is not possible to set event in past.
- EXPECT_GE(sub->optimize_time_ + 100, t);
- EXPECT_LE(sub->optimize_time_ - 100, t);
- // The last EID returned will also be stored for duplication checks.
- EXPECT_EQ(10U, sub->optimize_eid_);
-
- for (size_t i = t + 800; i < t + 800 + 10; ++i) {
- sub->testAdd(i);
- }
-
- results = genRows(sub.get());
- EXPECT_EQ(10U, results.size());
-
- // The optimize time should have been written to the database.
- // It should be the same as the current (relative) optimize time.
- std::string content;
- getDatabaseValue("events", "optimize.events_db_test", content);
- EXPECT_EQ(std::to_string(sub->optimize_time_), content);
-
- // Restore the tool type.
- kToolType = default_type;
-}
-
-TEST_F(EventsDatabaseTests, test_expire_check) {
- auto sub = std::make_shared<DBFakeEventSubscriber>();
- // Set the max number of buffered events to something reasonably small.
- sub->setEventsMax(50);
- size_t t = 10000;
-
- // We are still at the mercy of the opaque EVENTS_CHECKPOINT define.
- for (size_t x = 0; x < 3; x++) {
- size_t num_events = 256 * x;
- for (size_t i = 0; i < num_events; i++) {
- sub->testAdd(t++);
- }
-
- // Since events tests are dependent, expect 257 + 3 events.
- auto results = genRows(sub.get());
- if (x == 0) {
- // The first iteration is dependent on previous test state.
- continue;
- }
-
- // The number of events should remain constant.
- // In practice there may be an event still in the write queue.
- EXPECT_LT(results.size(), 60U);
- }
-
- // Try again, this time with a scan
- for (size_t k = 0; k < 3; k++) {
- for (size_t x = 0; x < 3; x++) {
- size_t num_events = 256 * x;
- for (size_t i = 0; i < num_events; i++) {
- sub->testAdd(t++);
- }
-
- // Records hold the event_id + time indexes.
- // Data hosts the event_id + JSON content.
- auto record_key = "records." + sub->dbNamespace();
- auto data_key = "data." + sub->dbNamespace();
-
- std::vector<std::string> records, datas;
- scanDatabaseKeys(kEvents, records, record_key);
- scanDatabaseKeys(kEvents, datas, data_key);
-
- EXPECT_LT(records.size(), 20U);
- EXPECT_LT(datas.size(), 60U);
- }
- }
-}
-} // namespace osquery
+++ /dev/null
-/**
- * Copyright (c) 2014-present, Facebook, Inc.
- * All rights reserved.
- *
- * This source code is licensed in accordance with the terms specified in
- * the LICENSE file found in the root directory of this source tree.
- */
-
-#include <boost/filesystem/operations.hpp>
-
-#include <gflags/gflags.h>
-#include <gtest/gtest.h>
-
-#include <osquery/database.h>
-#include <osquery/events.h>
-#include <osquery/registry_factory.h>
-#include <osquery/tables.h>
-#include <osquery/utils/info/tool_type.h>
-
-namespace osquery {
-DECLARE_bool(disable_database);
-
-class EventsTests : public ::testing::Test {
- protected:
- void SetUp() override {
- kToolType = ToolType::TEST;
- registryAndPluginInit();
-
- // Force registry to use ephemeral database plugin
- FLAGS_disable_database = true;
- DatabasePlugin::setAllowOpen(true);
- DatabasePlugin::initPlugin();
-
- RegistryFactory::get().registry("config_parser")->setUp();
- }
- void TearDown() override {
- EventFactory::end(true);
- }
-};
-
-// The most basic event publisher uses useless Subscription/Event.
-class BasicEventPublisher
- : public EventPublisher<SubscriptionContext, EventContext> {};
-
-class AnotherBasicEventPublisher
- : public EventPublisher<SubscriptionContext, EventContext> {};
-
-// Create some semi-useless subscription and event structures.
-struct FakeSubscriptionContext : SubscriptionContext {
- int require_this_value;
-};
-
-struct FakeEventContext : EventContext {
- int required_value;
-};
-
-// Typedef the shared_ptr accessors.
-using FakeSubscriptionContextRef = std::shared_ptr<FakeSubscriptionContext>;
-using FakeEventContextRef = std::shared_ptr<FakeEventContext>;
-
-// Now a publisher with a type.
-class FakeEventPublisher
- : public EventPublisher<FakeSubscriptionContext, FakeEventContext> {
- DECLARE_PUBLISHER("FakePublisher");
-};
-
-class AnotherFakeEventPublisher
- : public EventPublisher<FakeSubscriptionContext, FakeEventContext> {
- DECLARE_PUBLISHER("AnotherFakePublisher");
-};
-
-TEST_F(EventsTests, test_event_publisher) {
- auto pub = std::make_shared<FakeEventPublisher>();
- EXPECT_EQ(pub->type(), "FakePublisher");
-
- // Test type names.
- auto pub_sub = pub->createSubscriptionContext();
- EXPECT_EQ(typeid(FakeSubscriptionContext), typeid(*pub_sub));
-}
-
-TEST_F(EventsTests, test_register_event_publisher) {
- auto basic_pub = std::make_shared<BasicEventPublisher>();
- auto status = EventFactory::registerEventPublisher(basic_pub);
- EXPECT_FALSE(status.ok());
-
- // Set a name for the publisher, which becomes the type by default.
- basic_pub->setName("BasicPublisher");
- status = EventFactory::registerEventPublisher(basic_pub);
- EXPECT_TRUE(status.ok());
-
- // This class is the SAME, there was no type override.
- auto another_basic_pub = std::make_shared<AnotherBasicEventPublisher>();
- another_basic_pub->setName(basic_pub->getName());
- status = EventFactory::registerEventPublisher(another_basic_pub);
- EXPECT_FALSE(status.ok());
-
- // This class is different but also uses different types!
- auto fake_pub = std::make_shared<FakeEventPublisher>();
- status = EventFactory::registerEventPublisher(fake_pub);
- EXPECT_TRUE(status.ok());
-
- // May also register a similar (same EC, SC), but different publisher.
- auto another_fake_pub = std::make_shared<AnotherFakeEventPublisher>();
- status = EventFactory::registerEventPublisher(another_fake_pub);
- EXPECT_TRUE(status.ok());
-
- status = EventFactory::deregisterEventPublisher(basic_pub->type());
- EXPECT_TRUE(status.ok());
- status = EventFactory::deregisterEventPublisher(fake_pub->type());
- EXPECT_TRUE(status.ok());
- status = EventFactory::deregisterEventPublisher(another_fake_pub->type());
- EXPECT_TRUE(status.ok());
-
- // Attempting to deregister a publisher a second time.
- status = EventFactory::deregisterEventPublisher(another_fake_pub->type());
- EXPECT_FALSE(status.ok());
-
- // Attempting to deregister a publish that failed registering.
- status = EventFactory::deregisterEventPublisher(another_basic_pub->type());
- EXPECT_FALSE(status.ok());
-}
-
-TEST_F(EventsTests, test_event_publisher_types) {
- auto pub = std::make_shared<FakeEventPublisher>();
- EXPECT_EQ(pub->type(), "FakePublisher");
-
- EventFactory::registerEventPublisher(pub);
- auto pub2 = EventFactory::getEventPublisher("FakePublisher");
- EXPECT_EQ(pub->type(), pub2->type());
-
- // It is possible to deregister by base event publisher type.
- auto status = EventFactory::deregisterEventPublisher(pub2);
- EXPECT_TRUE(status.ok());
- // And attempting to deregister by type afterward will fail.
- status = EventFactory::deregisterEventPublisher(pub->type());
- EXPECT_FALSE(status.ok());
-}
-
-TEST_F(EventsTests, test_duplicate_event_publisher) {
- auto pub = std::make_shared<BasicEventPublisher>();
- pub->setName("BasicPublisher");
- auto status = EventFactory::registerEventPublisher(pub);
- EXPECT_TRUE(status.ok());
-
- // Make sure only the first event type was recorded.
- EXPECT_EQ(EventFactory::numEventPublishers(), 1U);
-}
-
-class UniqueEventPublisher
- : public EventPublisher<FakeSubscriptionContext, FakeEventContext> {
- DECLARE_PUBLISHER("unique");
-};
-
-TEST_F(EventsTests, test_create_using_registry) {
- // The events API uses attachEvents to move registry event publishers and
- // subscribers into the events factory.
- EXPECT_EQ(EventFactory::numEventPublishers(), 0U);
- attachEvents();
-
- // Store the number of default event publishers (in core).
- size_t default_publisher_count = EventFactory::numEventPublishers();
-
- auto& rf = RegistryFactory::get();
- // Now add another registry item, but do not yet attach it.
- rf.registry("event_publisher")
- ->add("unique", std::make_shared<UniqueEventPublisher>());
- EXPECT_EQ(EventFactory::numEventPublishers(), default_publisher_count);
-
- // Now attach and make sure it was added.
- attachEvents();
- EXPECT_EQ(EventFactory::numEventPublishers(), default_publisher_count + 1U);
-
- auto status = EventFactory::deregisterEventPublisher("unique");
- EXPECT_TRUE(status.ok());
-}
-
-TEST_F(EventsTests, test_create_subscription) {
- std::string basic_publisher_type = "BasicPublisher";
-
- auto pub = std::make_shared<BasicEventPublisher>();
- pub->setName(basic_publisher_type);
- EventFactory::registerEventPublisher(pub);
-
- // Make sure a subscription cannot be added for a non-existent event type.
- // Note: It normally would not make sense to create a blank subscription.
- auto subscription = Subscription::create("FakeSubscriber");
- auto status = EventFactory::addSubscription("FakePublisher", subscription);
- EXPECT_FALSE(status.ok());
-
- // In this case we can still add a blank subscription to an existing event
- // type.
- status = EventFactory::addSubscription(basic_publisher_type, subscription);
- EXPECT_TRUE(status.ok());
-
- // Make sure the subscription is added.
- EXPECT_EQ(EventFactory::numSubscriptions(basic_publisher_type), 1U);
-
- status = EventFactory::deregisterEventPublisher(basic_publisher_type);
- EXPECT_TRUE(status.ok());
-}
-
-TEST_F(EventsTests, test_multiple_subscriptions) {
- std::string basic_publisher_type = "BasicPublisher";
-
- auto pub = std::make_shared<BasicEventPublisher>();
- pub->setName(basic_publisher_type);
- EventFactory::registerEventPublisher(pub);
-
- auto subscription = Subscription::create("subscriber");
- auto status =
- EventFactory::addSubscription(basic_publisher_type, subscription);
- status = EventFactory::addSubscription(basic_publisher_type, subscription);
- EXPECT_TRUE(status.ok());
-
- EXPECT_EQ(EventFactory::numSubscriptions(basic_publisher_type), 2U);
-
- status = EventFactory::deregisterEventPublisher(basic_publisher_type);
- EXPECT_TRUE(status.ok());
-}
-
-struct TestSubscriptionContext : public SubscriptionContext {
- int smallest;
-};
-
-class TestEventPublisher
- : public EventPublisher<TestSubscriptionContext, EventContext> {
- DECLARE_PUBLISHER("TestPublisher");
-
- public:
- Status setUp() override {
- smallest_ever_ += 1;
- return Status::success();
- }
-
- void configure() override {
- int smallest_subscription = smallest_ever_;
-
- configure_run = true;
- for (const auto& subscription : subscriptions_) {
- auto subscription_context = getSubscriptionContext(subscription->context);
- if (smallest_subscription > subscription_context->smallest) {
- smallest_subscription = subscription_context->smallest;
- }
- }
-
- smallest_ever_ = smallest_subscription;
- }
-
- void tearDown() override {
- smallest_ever_ += 1;
- }
-
- // Custom methods do not make sense, but for testing it exists.
- int getTestValue() {
- return smallest_ever_;
- }
-
- public:
- bool configure_run{false};
-
- private:
- int smallest_ever_{0};
-};
-
-TEST_F(EventsTests, test_create_custom_event_publisher) {
- auto basic_pub = std::make_shared<BasicEventPublisher>();
- basic_pub->setName("BasicPublisher");
- EventFactory::registerEventPublisher(basic_pub);
-
- auto pub = std::make_shared<TestEventPublisher>();
- auto status = EventFactory::registerEventPublisher(pub);
- ASSERT_TRUE(status.ok());
-
- // These event types have unique event type IDs
- EXPECT_EQ(EventFactory::numEventPublishers(), 2U);
-
- // Make sure the setUp function was called.
- EXPECT_EQ(pub->getTestValue(), 1);
-
- status = EventFactory::deregisterEventPublisher(pub->type());
- EXPECT_TRUE(status.ok());
- status = EventFactory::deregisterEventPublisher(basic_pub->type());
- EXPECT_TRUE(status.ok());
-}
-
-TEST_F(EventsTests, test_custom_subscription) {
- // Step 1, register event type
- auto pub = std::make_shared<TestEventPublisher>();
- auto status = EventFactory::registerEventPublisher(pub);
- ASSERT_TRUE(status.ok());
-
- // Step 2, create and configure a subscription context
- auto sc = std::make_shared<TestSubscriptionContext>();
- sc->smallest = -1;
-
- // Step 3, add the subscription to the event type
- status = EventFactory::addSubscription("TestPublisher", "TestSubscriber", sc);
- EXPECT_TRUE(status.ok());
- EXPECT_EQ(pub->numSubscriptions(), 1U);
- // Run configure on this publisher.
- pub->configure();
-
- // The event type must run configure for each added subscription.
- EXPECT_TRUE(pub->configure_run);
- EXPECT_EQ(pub->getTestValue(), -1);
-
- status = EventFactory::deregisterEventPublisher(pub->type());
- EXPECT_TRUE(status.ok());
-}
-
-TEST_F(EventsTests, test_tear_down) {
- auto pub = std::make_shared<TestEventPublisher>();
- auto status = EventFactory::registerEventPublisher(pub);
- ASSERT_TRUE(status.ok());
-
- // Make sure set up incremented the test value.
- EXPECT_EQ(pub->getTestValue(), 1);
-
- status = EventFactory::deregisterEventPublisher(pub->type());
- EXPECT_TRUE(status.ok());
-
- // Make sure tear down incremented the test value.
- EXPECT_EQ(pub->getTestValue(), 2);
-
- // Once more, now deregistering all event types.
- status = EventFactory::registerEventPublisher(pub);
- EXPECT_TRUE(status.ok());
-
- EXPECT_EQ(pub->getTestValue(), 3);
- EventFactory::end();
- EXPECT_EQ(pub->getTestValue(), 4);
-
- // Make sure the factory state represented.
- EXPECT_EQ(EventFactory::numEventPublishers(), 0U);
-
- // Implicit deregister due to end of event factory.
- status = EventFactory::deregisterEventPublisher(pub->type());
- EXPECT_FALSE(status.ok());
-}
-
-static int kBellHathTolled = 0;
-
-Status TestTheeCallback(const EventContextRef& ec,
- const SubscriptionContextRef& sc) {
- kBellHathTolled += 1;
- return Status::success();
-}
-
-class FakeEventSubscriber : public EventSubscriber<FakeEventPublisher> {
- public:
- bool bellHathTolled{false};
- bool contextBellHathTolled{false};
- bool shouldFireBethHathTolled{false};
- size_t timesConfigured{0};
-
- FakeEventSubscriber() {
- setName("fake_events");
- }
-
- explicit FakeEventSubscriber(bool skip_name) {
- if (!skip_name) {
- FakeEventSubscriber();
- }
- }
-
- void configure() override {
- timesConfigured++;
- }
-
- Status Callback(const ECRef& ec, const SCRef& sc) {
- // We don't care about the subscription or the event contexts.
- bellHathTolled = true;
- return Status::success();
- }
-
- Status SpecialCallback(const ECRef& ec, const SCRef& sc) {
- // Now we care that the event context is corrected passed.
- if (ec->required_value == 42) {
- contextBellHathTolled = true;
- }
- return Status::success();
- }
-
- void lateInit() {
- auto sub_ctx = createSubscriptionContext();
- subscribe(&FakeEventSubscriber::Callback, sub_ctx);
- }
-
- void laterInit() {
- auto sub_ctx = createSubscriptionContext();
- sub_ctx->require_this_value = 42;
- subscribe(&FakeEventSubscriber::SpecialCallback, sub_ctx);
- }
-
- private:
- FRIEND_TEST(EventsTests, test_subscriber_names);
- FRIEND_TEST(EventsTests, test_event_subscriber_configure);
-};
-
-TEST_F(EventsTests, test_event_subscriber) {
- auto sub = std::make_shared<FakeEventSubscriber>();
- EXPECT_EQ(sub->getType(), "FakePublisher");
- EXPECT_EQ(sub->getName(), "fake_events");
-}
-
-TEST_F(EventsTests, test_event_subscriber_subscribe) {
- auto pub = std::make_shared<FakeEventPublisher>();
- EventFactory::registerEventPublisher(pub);
-
- auto sub = std::make_shared<FakeEventSubscriber>();
- EventFactory::registerEventSubscriber(sub);
-
- // Don't overload the normal `init` Subscription member.
- sub->lateInit();
- pub->configure();
- EXPECT_EQ(pub->numSubscriptions(), 1U);
-
- auto ec = pub->createEventContext();
- pub->fire(ec, 0);
-
- EXPECT_TRUE(sub->bellHathTolled);
-
- auto status = EventFactory::deregisterEventSubscriber(sub->getName());
- EXPECT_TRUE(status.ok());
- status = EventFactory::deregisterEventPublisher(pub->type());
- EXPECT_TRUE(status.ok());
-}
-
-TEST_F(EventsTests, test_event_subscriber_context) {
- auto pub = std::make_shared<FakeEventPublisher>();
- EventFactory::registerEventPublisher(pub);
-
- auto sub = std::make_shared<FakeEventSubscriber>();
- EventFactory::registerEventSubscriber(sub);
-
- sub->laterInit();
- pub->configure();
- auto ec = pub->createEventContext();
- ec->required_value = 42;
- pub->fire(ec, 0);
-
- EXPECT_TRUE(sub->contextBellHathTolled);
-
- auto status = EventFactory::deregisterEventSubscriber(sub->getName());
- EXPECT_TRUE(status.ok());
- status = EventFactory::deregisterEventPublisher(pub->type());
- EXPECT_TRUE(status.ok());
-}
-
-TEST_F(EventsTests, test_fire_event) {
- auto pub = std::make_shared<BasicEventPublisher>();
- pub->setName("BasicPublisher");
- auto status = EventFactory::registerEventPublisher(pub);
- ASSERT_TRUE(status.ok());
-
- auto sub = std::make_shared<FakeEventSubscriber>();
- status = EventFactory::registerEventSubscriber(sub);
- ASSERT_TRUE(status.ok());
-
- auto subscription = Subscription::create("fake_events");
- subscription->callback = TestTheeCallback;
- status = EventFactory::addSubscription("BasicPublisher", subscription);
- ASSERT_TRUE(status.ok());
-
- pub->configure();
-
- // The event context creation would normally happen in the event type.
- auto ec = pub->createEventContext();
- pub->fire(ec, 0);
- EXPECT_EQ(kBellHathTolled, 1);
-
- auto second_subscription = Subscription::create("fake_events");
- status = EventFactory::addSubscription("BasicPublisher", second_subscription);
- ASSERT_TRUE(status.ok());
-
- pub->configure();
-
- // Now there are two subscriptions (one sans callback).
- pub->fire(ec, 0);
- EXPECT_EQ(kBellHathTolled, 2);
-
- // Now both subscriptions have callbacks.
- second_subscription->callback = TestTheeCallback;
- pub->fire(ec, 0);
- EXPECT_EQ(kBellHathTolled, 4);
-
- status = EventFactory::deregisterEventSubscriber(sub->getName());
- EXPECT_TRUE(status.ok());
-
- status = EventFactory::deregisterEventPublisher(pub->type());
- EXPECT_TRUE(status.ok());
-}
-
-class SubFakeEventSubscriber : public FakeEventSubscriber {
- public:
- SubFakeEventSubscriber() : FakeEventSubscriber(true) {
- setName("sub_fake_events");
- }
-
- private:
- FRIEND_TEST(EventsTests, test_subscriber_names);
-};
-
-TEST_F(EventsTests, test_subscriber_names) {
- auto subsub = std::make_shared<SubFakeEventSubscriber>();
- EXPECT_EQ(subsub->getType(), "FakePublisher");
- EXPECT_EQ(subsub->getName(), "sub_fake_events");
- EXPECT_EQ(subsub->dbNamespace(), "FakePublisher.sub_fake_events");
-
- auto sub = std::make_shared<FakeEventSubscriber>();
- EXPECT_EQ(sub->getName(), "fake_events");
- EXPECT_EQ(sub->dbNamespace(), "FakePublisher.fake_events");
-}
-
-class DisabledEventSubscriber : public EventSubscriber<FakeEventPublisher> {
- public:
- DisabledEventSubscriber() : EventSubscriber(false) {}
-};
-
-TEST_F(EventsTests, test_event_toggle_subscribers) {
- // Make sure subscribers can disable themselves using the event subscriber
- // constructor parameter.
- auto sub = std::make_shared<DisabledEventSubscriber>();
- sub->setName("disabled_events");
- EXPECT_TRUE(sub->disabled);
-
- // Normal subscribers will be enabled.
- auto sub2 = std::make_shared<SubFakeEventSubscriber>();
- EXPECT_FALSE(sub2->disabled);
-
- // Registering a disabled subscriber will put it into a paused state.
- EventFactory::registerEventSubscriber(sub);
- EXPECT_EQ(sub->state(), EventState::EVENT_PAUSED);
-
- auto status = EventFactory::deregisterEventSubscriber(sub->getName());
- EXPECT_TRUE(status.ok());
-}
-}
+++ /dev/null
-/**
- * Copyright (c) 2014-present, Facebook, Inc.
- * All rights reserved.
- *
- * This source code is licensed in accordance with the terms specified in
- * the LICENSE file found in the root directory of this source tree.
- */
-
-#include <stdio.h>
-
-#include <gtest/gtest.h>
-
-#include <cstdint>
-#include <ctime>
-
-#include <sstream>
-
-#include <osquery/events.h>
-#include <osquery/flags.h>
-#include <osquery/tables.h>
-
-#include "osquery/events/linux/auditdnetlink.h"
-#include "osquery/tests/test_util.h"
-
-namespace osquery {
-
-DECLARE_bool(audit_allow_unix);
-
-/// Internal audit subscriber (socket events) testable methods.
-extern void parseSockAddr(const std::string& saddr, Row& r, bool& unix_socket);
-
-using StringMap = std::map<std::string, std::string>;
-
-/// Generates a fake audit id
-std::string generateAuditId(std::uint32_t event_id) noexcept {
- std::stringstream str_helper;
- str_helper << std::time(nullptr) << ".000:" << event_id;
-
- return str_helper.str();
-}
-
-class AuditTests : public testing::Test {
- protected:
- void SetUp() override {
- Row().swap(row_);
- }
-
- protected:
- Row row_;
-};
-
-TEST_F(AuditTests, test_handle_reply) {
- // A 'fake' audit message.
- std::string message =
- "audit(1440542781.644:403030): argc=3 a0=\"H=1 \" a1=\"/bin/sh\" a2=c";
-
- struct audit_reply reply;
- reply.type = 1;
- reply.len = message.size();
- reply.message = (char*)malloc(sizeof(char) * (message.size() + 1));
- memset((void*)reply.message, 0, message.size() + 1);
- memcpy((void*)reply.message, message.c_str(), message.size());
-
- // Perform the parsing.
- AuditEventRecord audit_event_record = {};
- bool parser_status =
- AuditdNetlinkParser::ParseAuditReply(reply, audit_event_record);
- EXPECT_EQ(parser_status, true);
-
- free((char*)reply.message);
-
- EXPECT_EQ(reply.type, audit_event_record.type);
- EXPECT_EQ("1440542781.644:403030", audit_event_record.audit_id);
- EXPECT_EQ(audit_event_record.fields.size(), 4U);
- EXPECT_EQ(audit_event_record.fields.count("argc"), 1U);
- EXPECT_EQ(audit_event_record.fields["argc"], "3");
- EXPECT_EQ(audit_event_record.fields["a0"], "\"H=1 \"");
- EXPECT_EQ(audit_event_record.fields["a1"], "\"/bin/sh\"");
- EXPECT_EQ(audit_event_record.fields["a2"], "c");
-}
-
-TEST_F(AuditTests, test_audit_value_decode) {
- // In the normal case the decoding only removes '"' characters from the ends.
- auto decoded_normal = DecodeAuditPathValues("\"/bin/ls\"");
- EXPECT_EQ(decoded_normal, "/bin/ls");
-
- // If the first char is not '"', the value is expected to be hex-encoded.
- auto decoded_hex = DecodeAuditPathValues("736C6565702031");
- EXPECT_EQ(decoded_hex, "sleep 1");
-
- // When the hex fails to decode the input value is returned as the result.
- auto decoded_fail = DecodeAuditPathValues("7");
- EXPECT_EQ(decoded_fail, "7");
-}
-
-size_t kAuditCounter{0};
-
-bool SimpleUpdate(size_t t, const StringMap& f, StringMap& m) {
- kAuditCounter++;
- for (const auto& i : f) {
- m[i.first] = i.second;
- }
- return true;
-}
-
-TEST_F(AuditTests, test_parse_sock_addr) {
- Row r;
- std::string msg = "02001F907F0000010000000000000000";
- bool unix_socket;
- parseSockAddr(msg, r, unix_socket);
- ASSERT_FALSE(r["remote_address"].empty());
- EXPECT_EQ(r["remote_address"], "127.0.0.1");
- EXPECT_EQ(r["family"], "2");
- EXPECT_EQ(r["remote_port"], "8080");
-
- Row r3;
- std::string msg2 = "0A001F9100000000FE80000000000000022522FFFEB03684000000";
- parseSockAddr(msg2, r3, unix_socket);
- ASSERT_FALSE(r3["remote_address"].empty());
- EXPECT_EQ(r3["remote_address"], "fe80:0000:0000:0000:0225:22ff:feb0:3684");
- EXPECT_EQ(r3["remote_port"], "8081");
-
- auto socket_flag = FLAGS_audit_allow_unix;
- FLAGS_audit_allow_unix = true;
- Row r4;
- std::string msg3 = "01002F746D702F6F7371756572792E656D0000";
- parseSockAddr(msg3, r4, unix_socket);
- ASSERT_FALSE(r4["socket"].empty());
- EXPECT_EQ(r4["socket"], "/tmp/osquery.em");
-
- msg3 = "0100002F746D702F6F7371756572792E656D";
- parseSockAddr(msg3, r4, unix_socket);
- EXPECT_EQ(r4["socket"], "/tmp/osquery.em");
- FLAGS_audit_allow_unix = socket_flag;
-}
-}
+++ /dev/null
-/**
- * Copyright (c) 2014-present, Facebook, Inc.
- * All rights reserved.
- *
- * This source code is licensed in accordance with the terms specified in
- * the LICENSE file found in the root directory of this source tree.
- */
-
-#include <stdio.h>
-
-#include <boost/filesystem/operations.hpp>
-#include <boost/filesystem/path.hpp>
-
-#include <gflags/gflags.h>
-#include <gtest/gtest.h>
-
-#include <osquery/database.h>
-#include <osquery/events.h>
-#include <osquery/events/linux/inotify.h>
-#include <osquery/filesystem/filesystem.h>
-#include <osquery/registry_factory.h>
-#include <osquery/tables.h>
-#include <osquery/utils/info/tool_type.h>
-
-namespace fs = boost::filesystem;
-
-namespace osquery {
-DECLARE_bool(disable_database);
-
-const int kMaxEventLatency = 3000;
-
-class INotifyTests : public testing::Test {
- protected:
- void SetUp() override {
- kToolType = ToolType::TEST;
- registryAndPluginInit();
-
- FLAGS_disable_database = true;
- DatabasePlugin::setAllowOpen(true);
- DatabasePlugin::initPlugin();
-
- // INotify will use data from the config and config parsers.
- Registry::get().registry("config_parser")->setUp();
-
- // Create a basic path trigger, this is a file path.
- real_test_path =
- fs::weakly_canonical(fs::temp_directory_path() /
- fs::unique_path("inotify-trigger.%%%%.%%%%"))
- .string();
- // Create a similar directory for embedded paths and directories.
- real_test_dir =
- fs::weakly_canonical(fs::temp_directory_path() /
- fs::unique_path("inotify-trigger.%%%%.%%%%"))
- .string();
-
- // Create the embedded paths.
- real_test_dir_path = real_test_dir + "/1";
- real_test_sub_dir = real_test_dir + "/2";
- real_test_sub_dir_path = real_test_sub_dir + "/1";
- }
-
- void TearDown() override {
- // End the event loops, and join on the threads.
- removePath(real_test_dir);
- removePath(real_test_path);
- }
-
- void StartEventLoop() {
- event_pub_ = std::make_shared<INotifyEventPublisher>(true);
- auto status = EventFactory::registerEventPublisher(event_pub_);
- FILE* fd = fopen(real_test_path.c_str(), "w");
- fclose(fd);
- temp_thread_ = std::thread(EventFactory::run, "inotify");
- }
-
- void StopEventLoop() {
- while (!event_pub_->hasStarted()) {
- ::usleep(20);
- }
-
- EventFactory::end(true);
- temp_thread_.join();
- }
-
- void SubscriptionAction(const std::string& path,
- uint32_t mask = IN_ALL_EVENTS,
- EventCallback ec = nullptr) {
- auto sc = std::make_shared<INotifySubscriptionContext>();
- sc->path = path;
- sc->mask = mask;
-
- EventFactory::addSubscription("inotify", "TestSubscriber", sc, ec);
- event_pub_->configure();
- }
-
- bool WaitForEvents(size_t max, size_t num_events = 0) {
- size_t delay = 0;
- while (delay <= max * 1000) {
- if (num_events > 0 && event_pub_->numEvents() >= num_events) {
- return true;
- } else if (num_events == 0 && event_pub_->numEvents() > 0) {
- return true;
- }
- delay += 50;
- ::usleep(50);
- }
- return false;
- }
-
- void TriggerEvent(const std::string& path) {
- FILE* fd = fopen(path.c_str(), "w");
- fputs("inotify", fd);
- fclose(fd);
- }
-
- void addMonitor(const std::string& path,
- uint32_t mask,
- bool recursive,
- bool add_watch) {
- auto sc = event_pub_->createSubscriptionContext();
- event_pub_->addMonitor(path, sc, mask, recursive, add_watch);
- }
-
- void RemoveAll(std::shared_ptr<INotifyEventPublisher>& pub) {
- pub->subscriptions_.clear();
- // Reset monitors.
- std::vector<int> wds;
- for (const auto& path : pub->descriptor_inosubctx_) {
- wds.push_back(path.first);
- }
- for (const auto& wd : wds) {
- pub->removeMonitor(wd, true);
- }
- }
-
- protected:
- /// Internal state managers: publisher reference.
- std::shared_ptr<INotifyEventPublisher> event_pub_{nullptr};
-
- /// Internal state managers: event publisher thread.
- std::thread temp_thread_;
-
- /// Transient paths ./inotify-trigger.
- std::string real_test_path;
-
- /// Transient paths ./inotify-triggers/.
- std::string real_test_dir;
-
- /// Transient paths ./inotify-triggers/1.
- std::string real_test_dir_path;
-
- /// Transient paths ./inotify-triggers/2/.
- std::string real_test_sub_dir;
-
- /// Transient paths ./inotify-triggers/2/1.
- std::string real_test_sub_dir_path;
-};
-
-TEST_F(INotifyTests, test_register_event_pub) {
- auto pub = std::make_shared<INotifyEventPublisher>(true);
- auto status = EventFactory::registerEventPublisher(pub);
- EXPECT_TRUE(status.ok());
-
- // Make sure only one event type exists
- EXPECT_EQ(EventFactory::numEventPublishers(), 1U);
- // And deregister
- status = EventFactory::deregisterEventPublisher("inotify");
- EXPECT_TRUE(status.ok());
-}
-
-TEST_F(INotifyTests, test_inotify_init) {
- // Handle should not be initialized during ctor.
- auto event_pub = std::make_shared<INotifyEventPublisher>(true);
- EXPECT_FALSE(event_pub->isHandleOpen());
-
- // Registering the event type initializes inotify.
- auto status = EventFactory::registerEventPublisher(event_pub);
- EXPECT_TRUE(status.ok());
- EXPECT_TRUE(event_pub->isHandleOpen());
-
- // Similarly deregistering closes the handle.
- EventFactory::deregisterEventPublisher("inotify");
- EXPECT_FALSE(event_pub->isHandleOpen());
-}
-
-TEST_F(INotifyTests, test_inotify_add_subscription_missing_path) {
- auto pub = std::make_shared<INotifyEventPublisher>(true);
- EventFactory::registerEventPublisher(pub);
-
- // This subscription path is fake, and will succeed.
- auto mc = std::make_shared<INotifySubscriptionContext>();
- mc->path = "/this/path/is/fake";
-
- auto subscription = Subscription::create("TestSubscriber", mc);
- auto status = EventFactory::addSubscription("inotify", subscription);
- EXPECT_TRUE(status.ok());
- EventFactory::deregisterEventPublisher("inotify");
-}
-
-TEST_F(INotifyTests, test_inotify_add_subscription_success) {
- auto pub = std::make_shared<INotifyEventPublisher>(true);
- EventFactory::registerEventPublisher(pub);
-
- // This subscription path *should* be real.
- auto mc = std::make_shared<INotifySubscriptionContext>();
- mc->path = "/";
- mc->mask = IN_ALL_EVENTS;
-
- auto subscription = Subscription::create("TestSubscriber", mc);
- auto status = EventFactory::addSubscription("inotify", subscription);
- EXPECT_TRUE(status.ok());
- EventFactory::deregisterEventPublisher("inotify");
-}
-
-TEST_F(INotifyTests, test_inotify_match_subscription) {
- event_pub_ = std::make_shared<INotifyEventPublisher>(true);
- addMonitor("/etc", IN_ALL_EVENTS, false, false);
- EXPECT_EQ(event_pub_->path_descriptors_.count("/etc"), 1U);
- // This will fail because there is no trailing "/" at the end.
- // The configure component should take care of these paths.
- EXPECT_FALSE(event_pub_->isPathMonitored("/etc/passwd"));
- event_pub_->path_descriptors_.clear();
-
- // Calling addMonitor the correct way.
- addMonitor("/etc/", IN_ALL_EVENTS, false, false);
- EXPECT_TRUE(event_pub_->isPathMonitored("/etc/passwd"));
- event_pub_->path_descriptors_.clear();
-
- // Test the matching capability.
- {
- auto sc = event_pub_->createSubscriptionContext();
- sc->path = "/etc";
- event_pub_->monitorSubscription(sc, false);
- EXPECT_EQ(sc->path, "/etc/");
- EXPECT_TRUE(event_pub_->isPathMonitored("/etc/"));
- EXPECT_TRUE(event_pub_->isPathMonitored("/etc/passwd"));
- }
-
- std::vector<std::string> valid_dirs = {"/etc", "/etc/", "/etc/*"};
- for (const auto& dir : valid_dirs) {
- event_pub_->path_descriptors_.clear();
- auto sc = event_pub_->createSubscriptionContext();
- sc->path = dir;
- event_pub_->monitorSubscription(sc, false);
- auto ec = event_pub_->createEventContext();
- ec->isub_ctx = sc;
- ec->path = "/etc/";
- EXPECT_TRUE(event_pub_->shouldFire(sc, ec));
- ec->path = "/etc/passwd";
- EXPECT_TRUE(event_pub_->shouldFire(sc, ec));
- }
-
- std::vector<std::string> exclude_paths = {
- "/etc/ssh/%%", "/etc/", "/etc/ssl/openssl.cnf", "/"};
- for (const auto& path : exclude_paths) {
- event_pub_->exclude_paths_.insert(path);
- }
-
- {
- event_pub_->path_descriptors_.clear();
- auto sc = event_pub_->createSubscriptionContext();
- sc->path = "/etc/%%";
- auto ec = event_pub_->createEventContext();
- ec->isub_ctx = sc;
- ec->path = "/etc/ssh/ssh_config";
- EXPECT_FALSE(event_pub_->shouldFire(sc, ec));
- ec->path = "/etc/passwd";
- EXPECT_FALSE(event_pub_->shouldFire(sc, ec));
- ec->path = "/etc/group";
- EXPECT_FALSE(event_pub_->shouldFire(sc, ec));
- ec->path = "/etc/ssl/openssl.cnf";
- EXPECT_FALSE(event_pub_->shouldFire(sc, ec));
- ec->path = "/etc/ssl/certs/";
- EXPECT_TRUE(event_pub_->shouldFire(sc, ec));
- }
-}
-
-class TestINotifyEventSubscriber
- : public EventSubscriber<INotifyEventPublisher> {
- public:
- TestINotifyEventSubscriber() {
- setName("TestINotifyEventSubscriber");
- }
-
- Status init() override {
- callback_count_ = 0;
- return Status::success();
- }
-
- Status SimpleCallback(const ECRef& ec, const SCRef& sc) {
- callback_count_ += 1;
- return Status::success();
- }
-
- Status Callback(const ECRef& ec, const SCRef& sc) {
- // The following comments are an example Callback routine.
- // Row r;
- // r["action"] = ec->action;
- // r["path"] = ec->path;
-
- // Normally would call Add here.
- callback_count_++;
-
- WriteLock lock(actions_lock_);
- actions_.push_back(ec->action);
- return Status::success();
- }
-
- SCRef GetSubscription(const std::string& path,
- uint32_t mask = IN_ALL_EVENTS) {
- auto mc = createSubscriptionContext();
- mc->path = path;
- mc->mask = mask;
- return mc;
- }
-
- void WaitForEvents(int max, int num_events = 1) {
- int delay = 0;
- while (delay < max * 1000) {
- if (callback_count_ >= num_events) {
- return;
- }
- ::usleep(50);
- delay += 50;
- }
- }
-
- std::vector<std::string> actions() {
- WriteLock lock(actions_lock_);
- return actions_;
- }
-
- int count() {
- return callback_count_;
- }
-
- public:
- std::atomic<int> callback_count_{0};
- std::vector<std::string> actions_;
-
- private:
- Mutex actions_lock_;
-
- private:
- FRIEND_TEST(INotifyTests, test_inotify_fire_event);
- FRIEND_TEST(INotifyTests, test_inotify_event_action);
- FRIEND_TEST(INotifyTests, test_inotify_optimization);
- FRIEND_TEST(INotifyTests, test_inotify_directory_watch);
- FRIEND_TEST(INotifyTests, DISABLED_test_inotify_recursion);
- FRIEND_TEST(INotifyTests, test_inotify_embedded_wildcards);
-};
-
-TEST_F(INotifyTests, test_inotify_run) {
- // Assume event type is registered.
- event_pub_ = std::make_shared<INotifyEventPublisher>(true);
- auto status = EventFactory::registerEventPublisher(event_pub_);
- EXPECT_TRUE(status.ok());
-
- // Create a temporary file to watch, open writeable
- FILE* fd = fopen(real_test_path.c_str(), "w");
-
- // Create a subscriber.
- auto sub = std::make_shared<TestINotifyEventSubscriber>();
- EventFactory::registerEventSubscriber(sub);
-
- // Create a subscription context
- auto mc = std::make_shared<INotifySubscriptionContext>();
- mc->path = real_test_path;
- mc->mask = IN_ALL_EVENTS;
- status = EventFactory::addSubscription(
- "inotify", Subscription::create("TestINotifyEventSubscriber", mc));
- EXPECT_TRUE(status.ok());
- event_pub_->configure();
-
- // Create an event loop thread (similar to main)
- std::thread temp_thread(EventFactory::run, "inotify");
- EXPECT_TRUE(event_pub_->numEvents() == 0);
-
- // Cause an inotify event by writing to the watched path.
- fputs("inotify", fd);
- fclose(fd);
-
- // Wait for the thread's run loop to select.
- WaitForEvents(kMaxEventLatency);
- EXPECT_TRUE(event_pub_->numEvents() > 0);
- EventFactory::end();
- temp_thread.join();
-}
-
-TEST_F(INotifyTests, test_inotify_fire_event) {
- // Assume event type is registered.
- StartEventLoop();
- auto sub = std::make_shared<TestINotifyEventSubscriber>();
- EventFactory::registerEventSubscriber(sub);
-
- // Create a subscriptioning context, note the added Event to the symbol
- auto sc = sub->GetSubscription(real_test_path, 0);
- sub->subscribe(&TestINotifyEventSubscriber::SimpleCallback, sc);
- event_pub_->configure();
-
- TriggerEvent(real_test_path);
- sub->WaitForEvents(kMaxEventLatency);
-
- // Make sure our expected event fired (aka subscription callback was called).
- EXPECT_TRUE(sub->count() > 0);
- StopEventLoop();
-}
-
-TEST_F(INotifyTests, test_inotify_event_action) {
- // Assume event type is registered.
- StartEventLoop();
- auto sub = std::make_shared<TestINotifyEventSubscriber>();
- EventFactory::registerEventSubscriber(sub);
-
- auto sc = sub->GetSubscription(real_test_path, IN_ALL_EVENTS);
- sub->subscribe(&TestINotifyEventSubscriber::Callback, sc);
- event_pub_->configure();
-
- TriggerEvent(real_test_path);
- sub->WaitForEvents(kMaxEventLatency, 2);
-
- // Make sure the inotify action was expected.
- EXPECT_GT(sub->actions().size(), 0U);
- if (sub->actions().size() >= 2) {
- EXPECT_EQ(sub->actions()[0], "UPDATED");
- }
-
- StopEventLoop();
-}
-
-TEST_F(INotifyTests, test_inotify_optimization) {
- // Assume event type is registered.
- StartEventLoop();
- fs::create_directory(real_test_dir);
-
- // Adding a descriptor to a directory will monitor files within.
- SubscriptionAction(real_test_dir);
- EXPECT_TRUE(event_pub_->isPathMonitored(real_test_dir_path));
-
- // Adding a subscription to a file within a monitored directory is fine
- // but this will NOT cause an additional INotify watch.
- SubscriptionAction(real_test_dir_path);
- EXPECT_EQ(event_pub_->numDescriptors(), 1U);
- StopEventLoop();
-}
-
-TEST_F(INotifyTests, test_inotify_directory_watch) {
- StartEventLoop();
-
- auto sub = std::make_shared<TestINotifyEventSubscriber>();
- EventFactory::registerEventSubscriber(sub);
-
- fs::create_directory(real_test_dir);
- fs::create_directory(real_test_sub_dir);
-
- // Subscribe to the directory inode
- auto mc = sub->createSubscriptionContext();
- mc->path = real_test_dir;
- mc->recursive = true;
- sub->subscribe(&TestINotifyEventSubscriber::Callback, mc);
- event_pub_->configure();
-
- // Trigger on a subdirectory's file.
- TriggerEvent(real_test_sub_dir_path);
-
- sub->WaitForEvents(kMaxEventLatency, 1);
- EXPECT_TRUE(sub->count() > 0);
- StopEventLoop();
-}
-
-TEST_F(INotifyTests, DISABLED_test_inotify_recursion) {
- // Create a non-registered publisher and subscriber.
- auto pub = std::make_shared<INotifyEventPublisher>(true);
- EventFactory::registerEventPublisher(pub);
- auto sub = std::make_shared<TestINotifyEventSubscriber>();
-
- // Create a mock directory structure.
- fs::create_directory(real_test_dir);
-
- // Create and test several subscriptions.
- auto sc = sub->createSubscriptionContext();
-
- sc->path = real_test_dir + "/*";
- sub->subscribe(&TestINotifyEventSubscriber::Callback, sc);
- // Trigger a configure step manually.
- pub->configure();
-
- // Expect a single monitor on the root of the fake tree.
- EXPECT_EQ(pub->path_descriptors_.size(), 1U);
- EXPECT_EQ(pub->path_descriptors_.count(real_test_dir + "/"), 1U);
- RemoveAll(pub);
-
- // Make sure monitors are empty.
- EXPECT_EQ(pub->numDescriptors(), 0U);
-
- auto sc2 = sub->createSubscriptionContext();
- sc2->path = real_test_dir + "/**";
- sub->subscribe(&TestINotifyEventSubscriber::Callback, sc2);
- pub->configure();
-
- // Expect only the directories to be monitored.
- // TODO test fails in the following assert.
- EXPECT_EQ(pub->path_descriptors_.size(), 11U);
- RemoveAll(pub);
-
- // Use a directory structure that includes a loop.
- boost::system::error_code ec;
- fs::create_symlink(real_test_dir, real_test_dir + "/link", ec);
-
- auto sc3 = sub->createSubscriptionContext();
- sc3->path = real_test_dir + "/**";
- sub->subscribe(&TestINotifyEventSubscriber::Callback, sc3);
- pub->configure();
-
- // Also expect canonicalized resolution (to prevent loops).
- EXPECT_EQ(pub->path_descriptors_.size(), 9U);
- RemoveAll(pub);
-
- EventFactory::deregisterEventPublisher("inotify");
-}
-
-TEST_F(INotifyTests, test_inotify_embedded_wildcards) {
- // Assume event type is not registered.
- event_pub_ = std::make_shared<INotifyEventPublisher>(true);
- EventFactory::registerEventPublisher(event_pub_);
-
- auto sub = std::make_shared<TestINotifyEventSubscriber>();
- EventFactory::registerEventSubscriber(sub);
-
- // Create ./inotify-triggers/2/1/.
- fs::create_directories(real_test_dir + "/2/1");
-
- // Create a subscription to match an embedded wildcard: "*"
- // The assumption is a watch will be created on the 'most-specific' directory
- // before the wildcard request.
- auto mc = sub->createSubscriptionContext();
- mc->path = real_test_dir + "/*/1";
- mc->recursive = true;
- sub->subscribe(&TestINotifyEventSubscriber::Callback, mc);
-
- // Now the publisher must be configured.
- event_pub_->configure();
-
- // Assume there is one watched path: real_test_dir.
- ASSERT_EQ(event_pub_->numDescriptors(), 1U);
- EXPECT_EQ(event_pub_->path_descriptors_.count(real_test_dir + "/2/1/"), 1U);
-}
-}
+++ /dev/null
-/**
- * Copyright (c) 2014-present, Facebook, Inc.
- * All rights reserved.
- *
- * This source code is licensed in accordance with the terms specified in
- * the LICENSE file found in the root directory of this source tree.
- */
-
-#include <gtest/gtest.h>
-
-#include <linux/audit.h>
-#include <cstdint>
-#include <cstdio>
-#include <ctime>
-#include <string>
-#include <vector>
-#include <sstream>
-
-#include <osquery/events.h>
-#include <osquery/events/linux/auditeventpublisher.h>
-#include <osquery/flags.h>
-#include <osquery/tables.h>
-#include <osquery/tables/events/linux/process_file_events.h>
-
-namespace osquery {
-extern std::vector<std::pair<int, std::string>> complete_event_list;
-extern StringList included_file_paths;
-extern std::string generateAuditId(std::uint32_t event_id) noexcept;
-
-class AuditdFimTests : public testing::Test {
- protected:
- void SetUp() override {
- Row().swap(row_);
- }
-
- protected:
- Row row_;
-};
-
-void DumpRow(Row r) {
- std::cout << " " << r["operation"] << " " << r["path1"];
- if (r.find("path2") != r.end()) {
- std::cout << " " << r["path2"];
- }
-
- std::cout << "\n";
-}
-
-void DumpRowList(const std::vector<Row>& row_list) {
- for (const auto& r : row_list) {
- DumpRow(r);
- }
-}
-
-TEST_F(AuditdFimTests, row_emission) {
- std::vector<AuditEventRecord> event_record_list;
-
- // Parse the raw messages and make sure we get the right amount
- // of records
- for (const auto& record_descriptor : complete_event_list) {
- std::string audit_message_copy = record_descriptor.second;
-
- audit_reply reply = {};
- reply.type = record_descriptor.first;
- reply.len = audit_message_copy.size();
- reply.message = &audit_message_copy[0];
-
- AuditEventRecord audit_event_record = {};
-
- bool parser_status =
- AuditdNetlinkParser::ParseAuditReply(reply, audit_event_record);
- EXPECT_EQ(parser_status, true);
-
- event_record_list.push_back(audit_event_record);
- }
-
- EXPECT_EQ(event_record_list.size(), 243U);
-
- // Assemble the audit records into audit events, and make sure
- // we get the correct amount of objects
- auto event_context = std::make_shared<AuditEventContext>();
- AuditTraceContext audit_trace_context;
-
- AuditEventPublisher::ProcessEvents(
- event_context, event_record_list, audit_trace_context);
-
- EXPECT_EQ(audit_trace_context.size(), 0U);
- EXPECT_EQ(event_context->audit_events.size(), 71U);
-
- // Configure what we want to log and what we want to ignore
- AuditdFimContext fim_context;
- fim_context.included_path_list = included_file_paths;
-
- // Emit the rows, showing only writes
- std::vector<Row> emitted_row_list;
- Status status = ProcessFileEventSubscriber::ProcessEvents(
- emitted_row_list, fim_context, event_context->audit_events);
-
- EXPECT_EQ(status.ok(), true);
- // @TODO fix failing test
- // EXPECT_EQ(emitted_row_list.size(), 15U);
-}
-
-// clang-format off
-StringList included_file_paths = {
- "/etc/ld.so.cache",
- "/home/alessandro/test_file",
- "/lib64/libc.so.6",
- "/lib64/libgcc_s.so.1",
- "/lib64/libm.so.6",
- "/lib64/libstdc++.so.6",
- "/home/alessandro/test_file",
- "/home/alessandro/test_file1",
- "/home/alessandro/test_file2",
- "/home/alessandro/test_file3",
- "/home/alessandro/test_file4",
- "/home/alessandro/test_file5",
- "/home/alessandro/test_file7",
- "/home/alessandro/test_file_rename",
- "/home/alessandro/test_file_renameat"
-};
-
-std::vector<std::pair<int, std::string>> complete_event_list = {
- {1300, "audit(1502573850.697:38395): arch=c000003e syscall=9 success=yes exit=140095431475200 a0=0 a1=1000 a2=3 a3=22 items=0 ppid=4316 pid=5581 auid=1000 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=pts1 ses=1 comm=\"mytest\" exe=\"/home/alessandro/mytest\" subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 key=(null)"},
- {1320, "audit(1502573850.697:38395): "},
- {1300, "audit(1502573850.697:38396): arch=c000003e syscall=2 success=yes exit=3 a0=7f6a824d4df5 a1=80000 a2=1 a3=7f6a826db4f8 items=1 ppid=4316 pid=5581 auid=1000 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=pts1 ses=1 comm=\"mytest\" exe=\"/home/alessandro/mytest\" subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 key=(null)"},
- {1307, "audit(1502573850.697:38396): cwd=\"/home/alessandro\""},
- {1302, "audit(1502573850.697:38396): item=0 name=\"/etc/ld.so.cache\" inode=67842177 dev=fd:00 mode=0100644 ouid=0 ogid=0 rdev=00:00 obj=unconfined_u:object_r:ld_so_cache_t:s0 objtype=NORMAL"},
- {1320, "audit(1502573850.697:38396): "},
- {1300, "audit(1502573850.697:38397): arch=c000003e syscall=9 success=yes exit=140095431385088 a0=0 a1=15e5b a2=1 a3=2 items=0 ppid=4316 pid=5581 auid=1000 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=pts1 ses=1 comm=\"mytest\" exe=\"/home/alessandro/mytest\" subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 key=(null)"},
- {1323, "audit(1502573850.697:38397): fd=3 flags=0x2"},
- {1320, "audit(1502573850.697:38397): "},
- {1300, "audit(1502573850.697:38398): arch=c000003e syscall=3 success=yes exit=0 a0=3 a1=15e5b a2=1 a3=2 items=0 ppid=4316 pid=5581 auid=1000 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=pts1 ses=1 comm=\"mytest\" exe=\"/home/alessandro/mytest\" subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 key=(null)"},
- {1320, "audit(1502573850.697:38398): "},
- {1300, "audit(1502573850.697:38399): arch=c000003e syscall=2 success=yes exit=3 a0=7f6a826d8640 a1=80000 a2=7f6a826db150 a3=7f6a826d8640 items=1 ppid=4316 pid=5581 auid=1000 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=pts1 ses=1 comm=\"mytest\" exe=\"/home/alessandro/mytest\" subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 key=(null)"},
- {1307, "audit(1502573850.697:38399): cwd=\"/home/alessandro\""},
- {1302, "audit(1502573850.697:38399): item=0 name=\"/lib64/libstdc++.so.6\" inode=33604382 dev=fd:00 mode=0100755 ouid=0 ogid=0 rdev=00:00 obj=system_u:object_r:lib_t:s0 objtype=NORMAL"},
- {1320, "audit(1502573850.697:38399): "},
- {1300, "audit(1502573850.697:38400): arch=c000003e syscall=0 success=yes exit=832 a0=3 a1=7fff15c09350 a2=340 a3=7f6a826d8640 items=0 ppid=4316 pid=5581 auid=1000 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=pts1 ses=1 comm=\"mytest\" exe=\"/home/alessandro/mytest\" subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 key=(null)"},
- {1320, "audit(1502573850.697:38400): "},
- {1300, "audit(1502573850.697:38401): arch=c000003e syscall=9 success=yes exit=140095426068480 a0=0 a1=308420 a2=5 a3=802 items=0 ppid=4316 pid=5581 auid=1000 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=pts1 ses=1 comm=\"mytest\" exe=\"/home/alessandro/mytest\" subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 key=(null)"},
- {1323, "audit(1502573850.697:38401): fd=3 flags=0x802"},
- {1320, "audit(1502573850.697:38401): "},
- {1300, "audit(1502573850.697:38402): arch=c000003e syscall=9 success=yes exit=140095429120000 a0=7f6a82499000 a1=b000 a2=3 a3=812 items=0 ppid=4316 pid=5581 auid=1000 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=pts1 ses=1 comm=\"mytest\" exe=\"/home/alessandro/mytest\" subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 key=(null)"},
- {1323, "audit(1502573850.697:38402): fd=3 flags=0x812"},
- {1320, "audit(1502573850.697:38402): "},
- {1300, "audit(1502573850.697:38403): arch=c000003e syscall=9 success=yes exit=140095429165056 a0=7f6a824a4000 a1=14420 a2=3 a3=32 items=0 ppid=4316 pid=5581 auid=1000 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=pts1 ses=1 comm=\"mytest\" exe=\"/home/alessandro/mytest\" subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 key=(null)"},
- {1320, "audit(1502573850.697:38403): "},
- {1300, "audit(1502573850.697:38404): arch=c000003e syscall=3 success=yes exit=0 a0=3 a1=7f6a826d8698 a2=0 a3=31 items=0 ppid=4316 pid=5581 auid=1000 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=pts1 ses=1 comm=\"mytest\" exe=\"/home/alessandro/mytest\" subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 key=(null)"},
- {1320, "audit(1502573850.697:38404): "},
- {1300, "audit(1502573850.697:38405): arch=c000003e syscall=2 success=yes exit=3 a0=7f6a826d8b08 a1=80000 a2=7f6a826db150 a3=7f6a826d8b08 items=1 ppid=4316 pid=5581 auid=1000 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=pts1 ses=1 comm=\"mytest\" exe=\"/home/alessandro/mytest\" subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 key=(null)"},
- {1307, "audit(1502573850.697:38405): cwd=\"/home/alessandro\""},
- {1302, "audit(1502573850.697:38405): item=0 name=\"/lib64/libm.so.6\" inode=33604048 dev=fd:00 mode=0100755 ouid=0 ogid=0 rdev=00:00 obj=system_u:object_r:lib_t:s0 objtype=NORMAL"},
- {1320, "audit(1502573850.697:38405): "},
- {1300, "audit(1502573850.697:38406): arch=c000003e syscall=0 success=yes exit=832 a0=3 a1=7fff15c09320 a2=340 a3=7f6a826d8b08 items=0 ppid=4316 pid=5581 auid=1000 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=pts1 ses=1 comm=\"mytest\" exe=\"/home/alessandro/mytest\" subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 key=(null)"},
- {1320, "audit(1502573850.697:38406): "},
- {1300, "audit(1502573850.697:38407): arch=c000003e syscall=9 success=yes exit=140095422914560 a0=0 a1=301148 a2=5 a3=802 items=0 ppid=4316 pid=5581 auid=1000 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=pts1 ses=1 comm=\"mytest\" exe=\"/home/alessandro/mytest\" subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 key=(null)"},
- {1323, "audit(1502573850.697:38407): fd=3 flags=0x802"},
- {1320, "audit(1502573850.697:38407): "},
- {1300, "audit(1502573850.697:38408): arch=c000003e syscall=9 success=yes exit=140095426060288 a0=7f6a821ae000 a1=2000 a2=3 a3=812 items=0 ppid=4316 pid=5581 auid=1000 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=pts1 ses=1 comm=\"mytest\" exe=\"/home/alessandro/mytest\" subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 key=(null)"},
- {1323, "audit(1502573850.697:38408): fd=3 flags=0x812"},
- {1320, "audit(1502573850.697:38408): "},
- {1300, "audit(1502573850.697:38409): arch=c000003e syscall=3 success=yes exit=0 a0=3 a1=7f6a826d8b60 a2=0 a3=31 items=0 ppid=4316 pid=5581 auid=1000 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=pts1 ses=1 comm=\"mytest\" exe=\"/home/alessandro/mytest\" subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 key=(null)"},
- {1320, "audit(1502573850.697:38409): "},
- {1300, "audit(1502573850.697:38410): arch=c000003e syscall=2 success=yes exit=3 a0=7f6a826d8fd0 a1=80000 a2=7f6a826db150 a3=7f6a826d8fd0 items=1 ppid=4316 pid=5581 auid=1000 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=pts1 ses=1 comm=\"mytest\" exe=\"/home/alessandro/mytest\" subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 key=(null)"},
- {1307, "audit(1502573850.697:38410): cwd=\"/home/alessandro\""},
- {1302, "audit(1502573850.697:38410): item=0 name=\"/lib64/libgcc_s.so.1\" inode=33554508 dev=fd:00 mode=0100755 ouid=0 ogid=0 rdev=00:00 obj=system_u:object_r:lib_t:s0 objtype=NORMAL"},
- {1320, "audit(1502573850.697:38410): "},
- {1300, "audit(1502573850.697:38411): arch=c000003e syscall=0 success=yes exit=832 a0=3 a1=7fff15c092f0 a2=340 a3=7f6a826d8fd0 items=0 ppid=4316 pid=5581 auid=1000 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=pts1 ses=1 comm=\"mytest\" exe=\"/home/alessandro/mytest\" subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 key=(null)"},
- {1320, "audit(1502573850.697:38411): "},
- {1300, "audit(1502573850.697:38412): arch=c000003e syscall=9 success=yes exit=140095431380992 a0=0 a1=1000 a2=3 a3=22 items=0 ppid=4316 pid=5581 auid=1000 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=pts1 ses=1 comm=\"mytest\" exe=\"/home/alessandro/mytest\" subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 key=(null)"},
- {1320, "audit(1502573850.697:38412): "},
- {1300, "audit(1502573850.697:38413): arch=c000003e syscall=9 success=yes exit=140095420727296 a0=0 a1=215400 a2=5 a3=802 items=0 ppid=4316 pid=5581 auid=1000 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=pts1 ses=1 comm=\"mytest\" exe=\"/home/alessandro/mytest\" subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 key=(null)"},
- {1323, "audit(1502573850.697:38413): fd=3 flags=0x802"},
- {1320, "audit(1502573850.697:38413): "},
- {1300, "audit(1502573850.697:38414): arch=c000003e syscall=9 success=yes exit=140095422906368 a0=7f6a81eac000 a1=2000 a2=3 a3=812 items=0 ppid=4316 pid=5581 auid=1000 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=pts1 ses=1 comm=\"mytest\" exe=\"/home/alessandro/mytest\" subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 key=(null)"},
- {1323, "audit(1502573850.697:38414): fd=3 flags=0x812"},
- {1320, "audit(1502573850.697:38414): "},
- {1300, "audit(1502573850.697:38415): arch=c000003e syscall=3 success=yes exit=0 a0=3 a1=7f6a826c1040 a2=0 a3=31 items=0 ppid=4316 pid=5581 auid=1000 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=pts1 ses=1 comm=\"mytest\" exe=\"/home/alessandro/mytest\" subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 key=(null)"},
- {1320, "audit(1502573850.697:38415): "},
- {1300, "audit(1502573850.698:38416): arch=c000003e syscall=2 success=yes exit=3 a0=7f6a826c14b0 a1=80000 a2=7f6a826db150 a3=7f6a826c14b0 items=1 ppid=4316 pid=5581 auid=1000 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=pts1 ses=1 comm=\"mytest\" exe=\"/home/alessandro/mytest\" subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 key=(null)"},
- {1307, "audit(1502573850.698:38416): cwd=\"/home/alessandro\""},
- {1302, "audit(1502573850.698:38416): item=0 name=\"/lib64/libc.so.6\" inode=33604039 dev=fd:00 mode=0100755 ouid=0 ogid=0 rdev=00:00 obj=system_u:object_r:lib_t:s0 objtype=NORMAL"},
- {1320, "audit(1502573850.698:38416): "},
- {1300, "audit(1502573850.698:38417): arch=c000003e syscall=0 success=yes exit=832 a0=3 a1=7fff15c092c0 a2=340 a3=7f6a826c14b0 items=0 ppid=4316 pid=5581 auid=1000 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=pts1 ses=1 comm=\"mytest\" exe=\"/home/alessandro/mytest\" subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 key=(null)"},
- {1320, "audit(1502573850.698:38417): "},
- {1300, "audit(1502573850.698:38418): arch=c000003e syscall=9 success=yes exit=140095416791040 a0=0 a1=3c0200 a2=5 a3=802 items=0 ppid=4316 pid=5581 auid=1000 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=pts1 ses=1 comm=\"mytest\" exe=\"/home/alessandro/mytest\" subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 key=(null)"},
- {1323, "audit(1502573850.698:38418): fd=3 flags=0x802"},
- {1320, "audit(1502573850.698:38418): "},
- {1300, "audit(1502573850.698:38419): arch=c000003e syscall=9 success=yes exit=140095420682240 a0=7f6a81c8d000 a1=6000 a2=3 a3=812 items=0 ppid=4316 pid=5581 auid=1000 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=pts1 ses=1 comm=\"mytest\" exe=\"/home/alessandro/mytest\" subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 key=(null)"},
- {1323, "audit(1502573850.698:38419): fd=3 flags=0x812"},
- {1320, "audit(1502573850.698:38419): "},
- {1300, "audit(1502573850.698:38420): arch=c000003e syscall=9 success=yes exit=140095420706816 a0=7f6a81c93000 a1=4200 a2=3 a3=32 items=0 ppid=4316 pid=5581 auid=1000 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=pts1 ses=1 comm=\"mytest\" exe=\"/home/alessandro/mytest\" subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 key=(null)"},
- {1320, "audit(1502573850.698:38420): "},
- {1300, "audit(1502573850.698:38421): arch=c000003e syscall=3 success=yes exit=0 a0=3 a1=7f6a826c1508 a2=0 a3=31 items=0 ppid=4316 pid=5581 auid=1000 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=pts1 ses=1 comm=\"mytest\" exe=\"/home/alessandro/mytest\" subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 key=(null)"},
- {1320, "audit(1502573850.698:38421): "},
- {1300, "audit(1502573850.698:38422): arch=c000003e syscall=9 success=yes exit=140095431376896 a0=0 a1=1000 a2=3 a3=22 items=0 ppid=4316 pid=5581 auid=1000 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=pts1 ses=1 comm=\"mytest\" exe=\"/home/alessandro/mytest\" subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 key=(null)"},
- {1320, "audit(1502573850.698:38422): "},
- {1300, "audit(1502573850.698:38423): arch=c000003e syscall=9 success=yes exit=140095431368704 a0=0 a1=2000 a2=3 a3=22 items=0 ppid=4316 pid=5581 auid=1000 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=pts1 ses=1 comm=\"mytest\" exe=\"/home/alessandro/mytest\" subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 key=(null)"},
- {1320, "audit(1502573850.698:38423): "},
- {1300, "audit(1502573850.698:38424): arch=c000003e syscall=9 success=yes exit=140095431364608 a0=0 a1=1000 a2=3 a3=22 items=0 ppid=4316 pid=5581 auid=1000 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=pts1 ses=1 comm=\"mytest\" exe=\"/home/alessandro/mytest\" subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 key=(null)"},
- {1320, "audit(1502573850.698:38424): "},
- {1300, "audit(1502573850.698:38425): arch=c000003e syscall=87 success=yes exit=0 a0=40219c a1=7fff15c0ab48 a2=7fff15c0ab58 a3=7fff15c0a7d0 items=2 ppid=4316 pid=5581 auid=1000 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=pts1 ses=1 comm=\"mytest\" exe=\"/home/alessandro/mytest\" subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 key=(null)"},
- {1307, "audit(1502573850.698:38425): cwd=\"/home/alessandro\""},
- {1302, "audit(1502573850.698:38425): item=0 name=\"/home/alessandro\" inode=67 dev=fd:02 mode=040700 ouid=1000 ogid=1000 rdev=00:00 obj=unconfined_u:object_r:user_home_dir_t:s0 objtype=PARENT"},
- {1302, "audit(1502573850.698:38425): item=1 name=\"test_file\" inode=724389 dev=fd:02 mode=0100644 ouid=0 ogid=0 rdev=00:00 obj=unconfined_u:object_r:user_home_t:s0 objtype=DELETE"},
- {1320, "audit(1502573850.698:38425): "},
- {1300, "audit(1502573850.699:38428): arch=c000003e syscall=87 success=yes exit=0 a0=4021ff a1=7fff15c0ab48 a2=7fff15c0ab58 a3=7fff15c0a7d0 items=2 ppid=4316 pid=5581 auid=1000 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=pts1 ses=1 comm=\"mytest\" exe=\"/home/alessandro/mytest\" subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 key=(null)"},
- {1307, "audit(1502573850.699:38428): cwd=\"/home/alessandro\""},
- {1302, "audit(1502573850.699:38428): item=0 name=\"/home/alessandro\" inode=67 dev=fd:02 mode=040700 ouid=1000 ogid=1000 rdev=00:00 obj=unconfined_u:object_r:user_home_dir_t:s0 objtype=PARENT"},
- {1302, "audit(1502573850.699:38428): item=1 name=\"test_file3\" inode=724389 dev=fd:02 mode=0100644 ouid=0 ogid=0 rdev=00:00 obj=unconfined_u:object_r:user_home_t:s0 objtype=DELETE"},
- {1320, "audit(1502573850.699:38428): "},
- {1300, "audit(1502573850.699:38429): arch=c000003e syscall=87 success=yes exit=0 a0=40220a a1=7fff15c0ab48 a2=7fff15c0ab58 a3=7fff15c0a7d0 items=2 ppid=4316 pid=5581 auid=1000 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=pts1 ses=1 comm=\"mytest\" exe=\"/home/alessandro/mytest\" subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 key=(null)"},
- {1307, "audit(1502573850.699:38429): cwd=\"/home/alessandro\""},
- {1302, "audit(1502573850.699:38429): item=0 name=\"/home/alessandro\" inode=67 dev=fd:02 mode=040700 ouid=1000 ogid=1000 rdev=00:00 obj=unconfined_u:object_r:user_home_dir_t:s0 objtype=PARENT"},
- {1302, "audit(1502573850.699:38429): item=1 name=\"test_file4\" inode=724416 dev=fd:02 mode=0120777 ouid=0 ogid=0 rdev=00:00 obj=unconfined_u:object_r:user_home_t:s0 objtype=DELETE"},
- {1320, "audit(1502573850.699:38429): "},
- {1300, "audit(1502573850.699:38430): arch=c000003e syscall=87 success=yes exit=0 a0=40242d a1=7fff15c0ab48 a2=7fff15c0ab58 a3=7fff15c0a7d0 items=2 ppid=4316 pid=5581 auid=1000 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=pts1 ses=1 comm=\"mytest\" exe=\"/home/alessandro/mytest\" subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 key=(null)"},
- {1307, "audit(1502573850.699:38430): cwd=\"/home/alessandro\""},
- {1302, "audit(1502573850.699:38430): item=0 name=\"/home/alessandro\" inode=67 dev=fd:02 mode=040700 ouid=1000 ogid=1000 rdev=00:00 obj=unconfined_u:object_r:user_home_dir_t:s0 objtype=PARENT"},
- {1302, "audit(1502573850.699:38430): item=1 name=\"test_file5\" inode=724406 dev=fd:02 mode=0100644 ouid=0 ogid=0 rdev=00:00 obj=unconfined_u:object_r:user_home_t:s0 objtype=DELETE"},
- {1320, "audit(1502573850.699:38430): "},
- {1300, "audit(1502573850.699:38431): arch=c000003e syscall=87 success=yes exit=0 a0=402451 a1=7fff15c0ab48 a2=7fff15c0ab58 a3=7fff15c0a7d0 items=2 ppid=4316 pid=5581 auid=1000 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=pts1 ses=1 comm=\"mytest\" exe=\"/home/alessandro/mytest\" subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 key=(null)"},
- {1307, "audit(1502573850.699:38431): cwd=\"/home/alessandro\""},
- {1302, "audit(1502573850.699:38431): item=0 name=\"/home/alessandro\" inode=67 dev=fd:02 mode=040700 ouid=1000 ogid=1000 rdev=00:00 obj=unconfined_u:object_r:user_home_dir_t:s0 objtype=PARENT"},
- {1302, "audit(1502573850.699:38431): item=1 name=\"test_file6\" inode=724418 dev=fd:02 mode=0100644 ouid=0 ogid=0 rdev=00:00 obj=unconfined_u:object_r:user_home_t:s0 objtype=DELETE"},
- {1320, "audit(1502573850.699:38431): "},
- {1300, "audit(1502573850.699:38432): arch=c000003e syscall=87 success=yes exit=0 a0=402477 a1=7fff15c0ab48 a2=7fff15c0ab58 a3=7fff15c0a7d0 items=2 ppid=4316 pid=5581 auid=1000 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=pts1 ses=1 comm=\"mytest\" exe=\"/home/alessandro/mytest\" subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 key=(null)"},
- {1307, "audit(1502573850.699:38432): cwd=\"/home/alessandro\""},
- {1302, "audit(1502573850.699:38432): item=0 name=\"/home/alessandro\" inode=67 dev=fd:02 mode=040700 ouid=1000 ogid=1000 rdev=00:00 obj=unconfined_u:object_r:user_home_dir_t:s0 objtype=PARENT"},
- {1302, "audit(1502573850.699:38432): item=1 name=\"test_file7\" inode=724419 dev=fd:02 mode=0100644 ouid=0 ogid=0 rdev=00:00 obj=unconfined_u:object_r:user_home_t:s0 objtype=DELETE"},
- {1320, "audit(1502573850.699:38432): "},
- {1300, "audit(1502573850.725:38494): arch=c000003e syscall=257 success=yes exit=3 a0=ffffffffffffff9c a1=40259e a2=90800 a3=0 items=1 ppid=4316 pid=5581 auid=1000 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=pts1 ses=1 comm=\"mytest\" exe=\"/home/alessandro/mytest\" subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 key=(null)"},
- {1307, "audit(1502573850.725:38494): cwd=\"/home/alessandro\""},
- {1302, "audit(1502573850.725:38494): item=0 name=\".\" inode=67 dev=fd:02 mode=040700 ouid=1000 ogid=1000 rdev=00:00 obj=unconfined_u:object_r:user_home_dir_t:s0 objtype=NORMAL"},
- {1320, "audit(1502573850.725:38494): "},
- {1300, "audit(1502573850.725:38495): arch=c000003e syscall=9 success=yes exit=140095431471104 a0=0 a1=1000 a2=3 a3=22 items=0 ppid=4316 pid=5581 auid=1000 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=pts1 ses=1 comm=\"mytest\" exe=\"/home/alessandro/mytest\" subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 key=(null)"},
- {1320, "audit(1502573850.725:38495): "},
- {1300, "audit(1502573850.725:38496): arch=c000003e syscall=1 success=yes exit=18 a0=1 a1=7f6a826d7000 a2=12 a3=0 items=0 ppid=4316 pid=5581 auid=1000 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=pts1 ses=1 comm=\"mytest\" exe=\"/home/alessandro/mytest\" subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 key=(null)"},
- {1320, "audit(1502573850.725:38496): "},
- {1300, "audit(1502573850.725:38497): arch=c000003e syscall=9 success=yes exit=140095431467008 a0=0 a1=1000 a2=3 a3=22 items=0 ppid=4316 pid=5581 auid=1000 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=pts1 ses=1 comm=\"mytest\" exe=\"/home/alessandro/mytest\" subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 key=(null)"},
- {1320, "audit(1502573850.725:38497): "},
- {1300, "audit(1502573850.726:38498): arch=c000003e syscall=1 success=yes exit=31 a0=1 a1=7f6a826d7000 a2=1f a3=22 items=0 ppid=4316 pid=5581 auid=1000 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=pts1 ses=1 comm=\"mytest\" exe=\"/home/alessandro/mytest\" subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 key=(null)"},
- {1320, "audit(1502573850.726:38498): "},
- {1300, "audit(1502573850.726:46799): arch=c000003e syscall=0 success=yes exit=1 a0=0 a1=7f6a826d6000 a2=400 a3=22 items=0 ppid=4316 pid=5581 auid=1000 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=pts1 ses=1 comm=\"mytest\" exe=\"/home/alessandro/mytest\" subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 key=(null)"},
- {1320, "audit(1502573850.726:46799): "},
- {1300, "audit(1502573858.178:46800): arch=c000003e syscall=76 success=yes exit=0 a0=40219c a1=c a2=a a3=7fff15c0a7c0 items=1 ppid=4316 pid=5581 auid=1000 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=pts1 ses=1 comm=\"mytest\" exe=\"/home/alessandro/mytest\" subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 key=(null)"},
- {1307, "audit(1502573858.178:46800): cwd=\"/home/alessandro\""},
- {1302, "audit(1502573858.178:46800): item=0 name=\"test_file\" inode=98362 dev=fd:02 mode=0100644 ouid=0 ogid=0 rdev=00:00 obj=unconfined_u:object_r:user_home_t:s0 objtype=NORMAL"},
- {1320, "audit(1502573858.178:46800): "},
- {1300, "audit(1502573858.178:46801): arch=c000003e syscall=86 success=yes exit=0 a0=40219c a1=402191 a2=a a3=7fff15c0a7c0 items=3 ppid=4316 pid=5581 auid=1000 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=pts1 ses=1 comm=\"mytest\" exe=\"/home/alessandro/mytest\" subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 key=(null)"},
- {1307, "audit(1502573858.178:46801): cwd=\"/home/alessandro\""},
- {1302, "audit(1502573858.178:46801): item=0 name=\"test_file\" inode=98362 dev=fd:02 mode=0100644 ouid=0 ogid=0 rdev=00:00 obj=unconfined_u:object_r:user_home_t:s0 objtype=NORMAL"},
- {1302, "audit(1502573858.178:46801): item=1 name=\"/home/alessandro\" inode=67 dev=fd:02 mode=040700 ouid=1000 ogid=1000 rdev=00:00 obj=unconfined_u:object_r:user_home_dir_t:s0 objtype=PARENT"},
- {1302, "audit(1502573858.178:46801): item=2 name=\"test_file1\" inode=98362 dev=fd:02 mode=0100644 ouid=0 ogid=0 rdev=00:00 obj=unconfined_u:object_r:user_home_t:s0 objtype=CREATE"},
- {1320, "audit(1502573858.178:46801): "},
- {1300, "audit(1502573858.178:46802): arch=c000003e syscall=88 success=yes exit=0 a0=40219c a1=4021cc a2=a a3=7fff15c0a7c0 items=3 ppid=4316 pid=5581 auid=1000 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=pts1 ses=1 comm=\"mytest\" exe=\"/home/alessandro/mytest\" subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 key=(null)"},
- {1307, "audit(1502573858.178:46802): cwd=\"/home/alessandro\""},
- {1302, "audit(1502573858.178:46802): item=0 name=\"test_file\" objtype=UNKNOWN"},
- {1302, "audit(1502573858.178:46802): item=1 name=\"/home/alessandro\" inode=67 dev=fd:02 mode=040700 ouid=1000 ogid=1000 rdev=00:00 obj=unconfined_u:object_r:user_home_dir_t:s0 objtype=PARENT"},
- {1302, "audit(1502573858.178:46802): item=2 name=\"test_file2\" inode=724389 dev=fd:02 mode=0120777 ouid=0 ogid=0 rdev=00:00 obj=unconfined_u:object_r:user_home_t:s0 objtype=CREATE"},
- {1320, "audit(1502573858.178:46802): "},
- {1300, "audit(1502573858.178:46803): arch=c000003e syscall=265 success=yes exit=0 a0=3 a1=40219c a2=3 a3=4021ff items=3 ppid=4316 pid=5581 auid=1000 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=pts1 ses=1 comm=\"mytest\" exe=\"/home/alessandro/mytest\" subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 key=(null)"},
- {1307, "audit(1502573858.178:46803): cwd=\"/home/alessandro\""},
- {1302, "audit(1502573858.178:46803): item=0 name=\"test_file\" inode=98362 dev=fd:02 mode=0100644 ouid=0 ogid=0 rdev=00:00 obj=unconfined_u:object_r:user_home_t:s0 objtype=NORMAL"},
- {1302, "audit(1502573858.178:46803): item=1 name=\"/home/alessandro\" inode=67 dev=fd:02 mode=040700 ouid=1000 ogid=1000 rdev=00:00 obj=unconfined_u:object_r:user_home_dir_t:s0 objtype=PARENT"},
- {1302, "audit(1502573858.178:46803): item=2 name=\"test_file3\" inode=98362 dev=fd:02 mode=0100644 ouid=0 ogid=0 rdev=00:00 obj=unconfined_u:object_r:user_home_t:s0 objtype=CREATE"},
- {1320, "audit(1502573858.178:46803): "},
- {1300, "audit(1502573858.178:46804): arch=c000003e syscall=266 success=yes exit=0 a0=40219c a1=3 a2=40220a a3=7fff15c0a7c0 items=3 ppid=4316 pid=5581 auid=1000 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=pts1 ses=1 comm=\"mytest\" exe=\"/home/alessandro/mytest\" subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 key=(null)"},
- {1307, "audit(1502573858.178:46804): cwd=\"/home/alessandro\""},
- {1302, "audit(1502573858.178:46804): item=0 name=\"test_file\" objtype=UNKNOWN"},
- {1302, "audit(1502573858.178:46804): item=1 name=\"/home/alessandro\" inode=67 dev=fd:02 mode=040700 ouid=1000 ogid=1000 rdev=00:00 obj=unconfined_u:object_r:user_home_dir_t:s0 objtype=PARENT"},
- {1302, "audit(1502573858.178:46804): item=2 name=\"test_file4\" inode=724406 dev=fd:02 mode=0120777 ouid=0 ogid=0 rdev=00:00 obj=unconfined_u:object_r:user_home_t:s0 objtype=CREATE"},
- {1320, "audit(1502573858.178:46804): "},
- {1300, "audit(1502573858.178:46805): arch=c000003e syscall=82 success=yes exit=0 a0=40219c a1=402215 a2=40220a a3=7fff15c0a7c0 items=4 ppid=4316 pid=5581 auid=1000 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=pts1 ses=1 comm=\"mytest\" exe=\"/home/alessandro/mytest\" subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 key=(null)"},
- {1307, "audit(1502573858.178:46805): cwd=\"/home/alessandro\""},
- {1302, "audit(1502573858.178:46805): item=0 name=\"/home/alessandro\" inode=67 dev=fd:02 mode=040700 ouid=1000 ogid=1000 rdev=00:00 obj=unconfined_u:object_r:user_home_dir_t:s0 objtype=PARENT"},
- {1302, "audit(1502573858.178:46805): item=1 name=\"/home/alessandro\" inode=67 dev=fd:02 mode=040700 ouid=1000 ogid=1000 rdev=00:00 obj=unconfined_u:object_r:user_home_dir_t:s0 objtype=PARENT"},
- {1302, "audit(1502573858.178:46805): item=2 name=\"test_file\" inode=98362 dev=fd:02 mode=0100644 ouid=0 ogid=0 rdev=00:00 obj=unconfined_u:object_r:user_home_t:s0 objtype=DELETE"},
- {1302, "audit(1502573858.178:46805): item=3 name=\"test_file_rename\" inode=98362 dev=fd:02 mode=0100644 ouid=0 ogid=0 rdev=00:00 obj=unconfined_u:object_r:user_home_t:s0 objtype=CREATE"},
- {1320, "audit(1502573858.178:46805): "},
- {1300, "audit(1502573858.178:46806): arch=c000003e syscall=264 success=yes exit=0 a0=3 a1=402215 a2=3 a3=40224e items=4 ppid=4316 pid=5581 auid=1000 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=pts1 ses=1 comm=\"mytest\" exe=\"/home/alessandro/mytest\" subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 key=(null)"},
- {1307, "audit(1502573858.178:46806): cwd=\"/home/alessandro\""},
- {1302, "audit(1502573858.178:46806): item=0 name=\"/home/alessandro\" inode=67 dev=fd:02 mode=040700 ouid=1000 ogid=1000 rdev=00:00 obj=unconfined_u:object_r:user_home_dir_t:s0 objtype=PARENT"},
- {1302, "audit(1502573858.178:46806): item=1 name=\"/home/alessandro\" inode=67 dev=fd:02 mode=040700 ouid=1000 ogid=1000 rdev=00:00 obj=unconfined_u:object_r:user_home_dir_t:s0 objtype=PARENT"},
- {1302, "audit(1502573858.178:46806): item=2 name=\"test_file_rename\" inode=98362 dev=fd:02 mode=0100644 ouid=0 ogid=0 rdev=00:00 obj=unconfined_u:object_r:user_home_t:s0 objtype=DELETE"},
- {1302, "audit(1502573858.178:46806): item=3 name=\"test_file_renameat\" inode=98362 dev=fd:02 mode=0100644 ouid=0 ogid=0 rdev=00:00 obj=unconfined_u:object_r:user_home_t:s0 objtype=CREATE"},
- {1320, "audit(1502573858.178:46806): "},
- {1300, "audit(1502573858.178:46807): arch=c000003e syscall=316 success=yes exit=0 a0=3 a1=40224e a2=3 a3=40219c items=4 ppid=4316 pid=5581 auid=1000 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=pts1 ses=1 comm=\"mytest\" exe=\"/home/alessandro/mytest\" subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 key=(null)"},
- {1307, "audit(1502573858.178:46807): cwd=\"/home/alessandro\""},
- {1302, "audit(1502573858.178:46807): item=0 name=\"/home/alessandro\" inode=67 dev=fd:02 mode=040700 ouid=1000 ogid=1000 rdev=00:00 obj=unconfined_u:object_r:user_home_dir_t:s0 objtype=PARENT"},
- {1302, "audit(1502573858.178:46807): item=1 name=\"/home/alessandro\" inode=67 dev=fd:02 mode=040700 ouid=1000 ogid=1000 rdev=00:00 obj=unconfined_u:object_r:user_home_dir_t:s0 objtype=PARENT"},
- {1302, "audit(1502573858.178:46807): item=2 name=\"test_file_renameat\" inode=98362 dev=fd:02 mode=0100644 ouid=0 ogid=0 rdev=00:00 obj=unconfined_u:object_r:user_home_t:s0 objtype=DELETE"},
- {1302, "audit(1502573858.178:46807): item=3 name=\"test_file\" inode=98362 dev=fd:02 mode=0100644 ouid=0 ogid=0 rdev=00:00 obj=unconfined_u:object_r:user_home_t:s0 objtype=CREATE"},
- {1320, "audit(1502573858.178:46807): "},
- {1300, "audit(1502573858.178:46808): arch=c000003e syscall=87 success=yes exit=0 a0=402191 a1=40224e a2=3 a3=40219c items=2 ppid=4316 pid=5581 auid=1000 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=pts1 ses=1 comm=\"mytest\" exe=\"/home/alessandro/mytest\" subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 key=(null)"},
- {1307, "audit(1502573858.178:46808): cwd=\"/home/alessandro\""},
- {1302, "audit(1502573858.178:46808): item=0 name=\"/home/alessandro\" inode=67 dev=fd:02 mode=040700 ouid=1000 ogid=1000 rdev=00:00 obj=unconfined_u:object_r:user_home_dir_t:s0 objtype=PARENT"},
- {1302, "audit(1502573858.178:46808): item=1 name=\"test_file1\" inode=98362 dev=fd:02 mode=0100644 ouid=0 ogid=0 rdev=00:00 obj=unconfined_u:object_r:user_home_t:s0 objtype=DELETE"},
- {1320, "audit(1502573858.178:46808): "},
- {1300, "audit(1502573858.178:46809): arch=c000003e syscall=263 success=yes exit=0 a0=3 a1=4021cc a2=0 a3=7fff15c0a7b0 items=2 ppid=4316 pid=5581 auid=1000 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=pts1 ses=1 comm=\"mytest\" exe=\"/home/alessandro/mytest\" subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 key=(null)"},
- {1307, "audit(1502573858.178:46809): cwd=\"/home/alessandro\""},
- {1302, "audit(1502573858.178:46809): item=0 name=\"/home/alessandro\" inode=67 dev=fd:02 mode=040700 ouid=1000 ogid=1000 rdev=00:00 obj=unconfined_u:object_r:user_home_dir_t:s0 objtype=PARENT"},
- {1302, "audit(1502573858.178:46809): item=1 name=\"test_file2\" inode=724389 dev=fd:02 mode=0120777 ouid=0 ogid=0 rdev=00:00 obj=unconfined_u:object_r:user_home_t:s0 objtype=DELETE"},
- {1320, "audit(1502573858.178:46809): "},
- {1300, "audit(1502573858.178:46810): arch=c000003e syscall=2 success=yes exit=4 a0=4022c6 a1=42 a2=0 a3=7fff15c0a3a0 items=2 ppid=4316 pid=5581 auid=1000 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=pts1 ses=1 comm=\"mytest\" exe=\"/home/alessandro/mytest\" subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 key=(null)"},
- {1307, "audit(1502573858.178:46810): cwd=\"/home/alessandro\""},
- {1302, "audit(1502573858.178:46810): item=0 name=\"/home/alessandro/\" inode=67 dev=fd:02 mode=040700 ouid=1000 ogid=1000 rdev=00:00 obj=unconfined_u:object_r:user_home_dir_t:s0 objtype=PARENT"},
- {1302, "audit(1502573858.178:46810): item=1 name=\"/home/alessandro/test_file\" inode=98362 dev=fd:02 mode=0100644 ouid=0 ogid=0 rdev=00:00 obj=unconfined_u:object_r:user_home_t:s0 objtype=NORMAL"},
- {1320, "audit(1502573858.178:46810): "},
- {1300, "audit(1502573858.178:46811): arch=c000003e syscall=32 success=yes exit=5 a0=4 a1=42 a2=0 a3=7fff15c0a3a0 items=0 ppid=4316 pid=5581 auid=1000 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=pts1 ses=1 comm=\"mytest\" exe=\"/home/alessandro/mytest\" subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 key=(null)"},
- {1320, "audit(1502573858.178:46811): "},
- {1300, "audit(1502573858.178:46812): arch=c000003e syscall=33 success=yes exit=10 a0=4 a1=a a2=0 a3=7fff15c0a3a0 items=0 ppid=4316 pid=5581 auid=1000 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=pts1 ses=1 comm=\"mytest\" exe=\"/home/alessandro/mytest\" subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 key=(null)"},
- {1320, "audit(1502573858.178:46812): "},
- {1300, "audit(1502573858.178:46813): arch=c000003e syscall=292 success=yes exit=11 a0=4 a1=b a2=0 a3=7fff15c0a3a0 items=0 ppid=4316 pid=5581 auid=1000 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=pts1 ses=1 comm=\"mytest\" exe=\"/home/alessandro/mytest\" subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 key=(null)"},
- {1320, "audit(1502573858.178:46813): "},
- {1300, "audit(1502573858.178:46814): arch=c000003e syscall=3 success=yes exit=0 a0=b a1=b a2=0 a3=7fff15c0a3a0 items=0 ppid=4316 pid=5581 auid=1000 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=pts1 ses=1 comm=\"mytest\" exe=\"/home/alessandro/mytest\" subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 key=(null)"},
- {1320, "audit(1502573858.178:46814): "},
- {1300, "audit(1502573858.178:46815): arch=c000003e syscall=257 success=yes exit=6 a0=3 a1=40219c a2=0 a3=0 items=1 ppid=4316 pid=5581 auid=1000 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=pts1 ses=1 comm=\"mytest\" exe=\"/home/alessandro/mytest\" subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 key=(null)"},
- {1307, "audit(1502573858.178:46815): cwd=\"/home/alessandro\""},
- {1302, "audit(1502573858.178:46815): item=0 name=\"test_file\" inode=98362 dev=fd:02 mode=0100644 ouid=0 ogid=0 rdev=00:00 obj=unconfined_u:object_r:user_home_t:s0 objtype=NORMAL"},
- {1320, "audit(1502573858.178:46815): "},
- {1300, "audit(1502573858.178:46816): arch=c000003e syscall=303 success=yes exit=0 a0=ffffff9c a1=40219c a2=7fff15c0a620 a3=7fff15c0aa2c items=1 ppid=4316 pid=5581 auid=1000 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=pts1 ses=1 comm=\"mytest\" exe=\"/home/alessandro/mytest\" subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 key=(null)"},
- {1307, "audit(1502573858.178:46816): cwd=\"/home/alessandro\""},
- {1302, "audit(1502573858.178:46816): item=0 name=\"test_file\" inode=98362 dev=fd:02 mode=0100644 ouid=0 ogid=0 rdev=00:00 obj=unconfined_u:object_r:user_home_t:s0 objtype=NORMAL"},
- {1320, "audit(1502573858.178:46816): "},
- {1300, "audit(1502573858.178:46817): arch=c000003e syscall=304 success=yes exit=7 a0=ffffff9c a1=7fff15c0a620 a2=2 a3=7fff15c0a3a0 items=1 ppid=4316 pid=5581 auid=1000 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=pts1 ses=1 comm=\"mytest\" exe=\"/home/alessandro/mytest\" subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 key=(null)"},
- {1307, "audit(1502573858.178:46817): cwd=\"/home/alessandro\""},
- {1302, "audit(1502573858.178:46817): item=0 name="" inode=98362 dev=fd:02 mode=0100644 ouid=0 ogid=0 rdev=00:00 obj=unconfined_u:object_r:user_home_t:s0 objtype=NORMAL"},
- {1320, "audit(1502573858.178:46817): "},
- {1300, "audit(1502573858.178:46818): arch=c000003e syscall=303 success=yes exit=0 a0=3 a1=40219c a2=7fff15c0a620 a3=7fff15c0aa2c items=1 ppid=4316 pid=5581 auid=1000 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=pts1 ses=1 comm=\"mytest\" exe=\"/home/alessandro/mytest\" subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 key=(null)"},
- {1307, "audit(1502573858.178:46818): cwd=\"/home/alessandro\""},
- {1302, "audit(1502573858.178:46818): item=0 name=\"test_file\" inode=98362 dev=fd:02 mode=0100644 ouid=0 ogid=0 rdev=00:00 obj=unconfined_u:object_r:user_home_t:s0 objtype=NORMAL"},
- {1320, "audit(1502573858.178:46818): "},
- {1300, "audit(1502573858.178:46819): arch=c000003e syscall=304 success=yes exit=8 a0=3 a1=7fff15c0a620 a2=2 a3=7fff15c0aa2c items=1 ppid=4316 pid=5581 auid=1000 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=pts1 ses=1 comm=\"mytest\" exe=\"/home/alessandro/mytest\" subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 key=(null)"},
- {1307, "audit(1502573858.178:46819): cwd=\"/home/alessandro\""},
- {1302, "audit(1502573858.178:46819): item=0 name="" inode=98362 dev=fd:02 mode=0100644 ouid=0 ogid=0 rdev=00:00 obj=unconfined_u:object_r:user_home_t:s0 objtype=NORMAL"},
- {1320, "audit(1502573858.178:46819): "},
- {1300, "audit(1502573858.178:46820): arch=c000003e syscall=133 success=yes exit=0 a0=40242d a1=81a4 a2=0 a3=7fff15c0a380 items=2 ppid=4316 pid=5581 auid=1000 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=pts1 ses=1 comm=\"mytest\" exe=\"/home/alessandro/mytest\" subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 key=(null)"},
- {1307, "audit(1502573858.178:46820): cwd=\"/home/alessandro\""},
- {1302, "audit(1502573858.178:46820): item=0 name=\"/home/alessandro\" inode=67 dev=fd:02 mode=040700 ouid=1000 ogid=1000 rdev=00:00 obj=unconfined_u:object_r:user_home_dir_t:s0 objtype=PARENT"},
- {1302, "audit(1502573858.178:46820): item=1 name=\"test_file5\" inode=560977 dev=fd:02 mode=0100644 ouid=0 ogid=0 rdev=00:00 obj=unconfined_u:object_r:user_home_t:s0 objtype=CREATE"},
- {1320, "audit(1502573858.178:46820): "},
- {1300, "audit(1502573858.178:46821): arch=c000003e syscall=259 success=yes exit=0 a0=3 a1=402451 a2=81a4 a3=0 items=2 ppid=4316 pid=5581 auid=1000 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=pts1 ses=1 comm=\"mytest\" exe=\"/home/alessandro/mytest\" subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 key=(null)"},
- {1307, "audit(1502573858.178:46821): cwd=\"/home/alessandro\""},
- {1302, "audit(1502573858.178:46821): item=0 name=\"/home/alessandro\" inode=67 dev=fd:02 mode=040700 ouid=1000 ogid=1000 rdev=00:00 obj=unconfined_u:object_r:user_home_dir_t:s0 objtype=PARENT"},
- {1302, "audit(1502573858.178:46821): item=1 name=\"test_file6\" inode=560986 dev=fd:02 mode=0100644 ouid=0 ogid=0 rdev=00:00 obj=unconfined_u:object_r:user_home_t:s0 objtype=CREATE"},
- {1320, "audit(1502573858.178:46821): "},
- {1300, "audit(1502573858.178:46822): arch=c000003e syscall=85 success=yes exit=9 a0=402477 a1=81a4 a2=81a4 a3=7fff15c0a3a0 items=2 ppid=4316 pid=5581 auid=1000 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=pts1 ses=1 comm=\"mytest\" exe=\"/home/alessandro/mytest\" subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 key=(null)"},
- {1307, "audit(1502573858.178:46822): cwd=\"/home/alessandro\""},
- {1302, "audit(1502573858.178:46822): item=0 name=\"/home/alessandro\" inode=67 dev=fd:02 mode=040700 ouid=1000 ogid=1000 rdev=00:00 obj=unconfined_u:object_r:user_home_dir_t:s0 objtype=PARENT"},
- {1302, "audit(1502573858.178:46822): item=1 name=\"test_file7\" inode=560990 dev=fd:02 mode=0100644 ouid=0 ogid=0 rdev=00:00 obj=unconfined_u:object_r:user_home_t:s0 objtype=CREATE"},
- {1320, "audit(1502573858.178:46822): "},
- {1300, "audit(1502573858.179:46823): arch=c000003e syscall=0 success=yes exit=10 a0=4 a1=7fff15c0a640 a2=a a3=7fff15c0a3c0 items=0 ppid=4316 pid=5581 auid=1000 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=pts1 ses=1 comm=\"mytest\" exe=\"/home/alessandro/mytest\" subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 key=(null)"},
- {1320, "audit(1502573858.179:46823): "},
- {1300, "audit(1502573858.179:46824): arch=c000003e syscall=1 success=yes exit=1024 a0=4 a1=7fff15c0a640 a2=400 a3=7fff15c0a3c0 items=0 ppid=4316 pid=5581 auid=1000 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=pts1 ses=1 comm=\"mytest\" exe=\"/home/alessandro/mytest\" subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 key=(null)"},
- {1320, "audit(1502573858.179:46824): "},
- {1300, "audit(1502573858.179:46825): arch=c000003e syscall=17 success=yes exit=10 a0=4 a1=7fff15c0a640 a2=a a3=1 items=0 ppid=4316 pid=5581 auid=1000 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=pts1 ses=1 comm=\"mytest\" exe=\"/home/alessandro/mytest\" subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 key=(null)"},
- {1320, "audit(1502573858.179:46825): "},
- {1300, "audit(1502573858.179:46826): arch=c000003e syscall=18 success=yes exit=1024 a0=4 a1=7fff15c0a640 a2=400 a3=1 items=0 ppid=4316 pid=5581 auid=1000 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=pts1 ses=1 comm=\"mytest\" exe=\"/home/alessandro/mytest\" subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 key=(null)"},
- {1320, "audit(1502573858.179:46826): "},
- {1300, "audit(1502573858.179:46827): arch=c000003e syscall=77 success=yes exit=0 a0=4 a1=b a2=400 a3=7fff15c0a7c0 items=0 ppid=4316 pid=5581 auid=1000 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=pts1 ses=1 comm=\"mytest\" exe=\"/home/alessandro/mytest\" subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 key=(null)"},
- {1320, "audit(1502573858.179:46827): "},
- {1300, "audit(1502573858.179:46828): arch=c000003e syscall=9 success=yes exit=140095431462912 a0=0 a1=a a2=7 a3=1 items=0 ppid=4316 pid=5581 auid=1000 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=pts1 ses=1 comm=\"mytest\" exe=\"/home/alessandro/mytest\" subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 key=(null)"},
- {1323, "audit(1502573858.179:46828): fd=4 flags=0x1"},
- {1320, "audit(1502573858.179:46828): "}
-};
-// clang-format on
-} // namespace osquery
+++ /dev/null
-/**
- * Copyright (c) 2014-present, Facebook, Inc.
- * All rights reserved.
- *
- * This source code is licensed in accordance with the terms specified in
- * the LICENSE file found in the root directory of this source tree.
- */
-
-#include <vector>
-
-#include <gtest/gtest.h>
-
-#include <boost/tokenizer.hpp>
-
-#include "osquery/events/linux/syslog.h"
-#include "osquery/tests/test_util.h"
-
-namespace osquery {
-
-class SyslogTests : public testing::Test {
- public:
- std::vector<std::string> splitCsv(std::string line) {
- boost::tokenizer<RsyslogCsvSeparator> tokenizer(line);
- std::vector<std::string> result(tokenizer.begin(), tokenizer.end());
- return result;
- }
-};
-
-TEST_F(SyslogTests, test_populate_event_context) {
- std::string line =
- R"|("2016-03-22T21:17:01.701882+00:00","vagrant-ubuntu-trusty-64","6","cron","CRON[16538]:"," (root) CMD ( cd / && run-parts --report /etc/cron.hourly)")|";
- SyslogEventPublisher pub;
- auto ec = pub.createEventContext();
- Status status = pub.populateEventContext(line, ec);
-
- ASSERT_TRUE(status.ok());
- // Note: the time-parsing was removed to allow events to auto-assign.
- ASSERT_EQ(0U, ec->time);
- ASSERT_EQ("2016-03-22T21:17:01.701882+00:00", ec->fields.at("datetime"));
- ASSERT_EQ("vagrant-ubuntu-trusty-64", ec->fields.at("host"));
- ASSERT_EQ("6", ec->fields.at("severity"));
- ASSERT_EQ("cron", ec->fields.at("facility"));
- ASSERT_EQ("CRON[16538]", ec->fields.at("tag"));
- ASSERT_EQ("(root) CMD ( cd / && run-parts --report /etc/cron.hourly)",
- ec->fields.at("message"));
-
- // Too few fields
-
- std::string bad_line =
- R"("2016-03-22T21:17:01.701882+00:00","vagrant-ubuntu-trusty-64","6","cron",)";
- ec = pub.createEventContext();
- status = pub.populateEventContext(bad_line, ec);
- ASSERT_FALSE(status.ok());
- ASSERT_NE(std::string::npos, status.getMessage().find("fewer"));
-
- // Too many fields
- bad_line = R"("2016-03-22T21:17:01.701882+00:00","","6","","","","")";
- ec = pub.createEventContext();
- status = pub.populateEventContext(bad_line, ec);
- ASSERT_FALSE(status.ok());
- ASSERT_NE(std::string::npos, status.getMessage().find("more"));
-}
-
-TEST_F(SyslogTests, test_csv_separator) {
- ASSERT_EQ(std::vector<std::string>({"", "", "", "", ""}), splitCsv(",,,,"));
- ASSERT_EQ(std::vector<std::string>({" ", " ", " ", " ", " "}),
- splitCsv(" , , , , "));
- ASSERT_EQ(std::vector<std::string>({"foo", "bar", "baz"}),
- splitCsv("foo,bar,baz"));
- ASSERT_EQ(std::vector<std::string>({"foo", "bar", "baz"}),
- splitCsv("\"foo\",\"bar\",\"baz\""));
- ASSERT_EQ(std::vector<std::string>({",foo,", ",bar", "baz,"}),
- splitCsv("\",foo,\",\",bar\",\"baz,\""));
- ASSERT_EQ(std::vector<std::string>({",f\\oo,", ",ba\\'r", "baz\\,"}),
- splitCsv("\",f\\oo,\",\",ba\\'r\",\"baz\\,\""));
- ASSERT_EQ(std::vector<std::string>({"\",f\\o\"o,", "\",ba\\'r", "baz\\,\""}),
- splitCsv("\"\"\",f\\o\"\"o,\",\"\"\",ba\\'r\",\"baz\\,\"\"\""));
- ASSERT_EQ(std::vector<std::string>({"\",f\\ø\"o,", "\",bá\\'r", "baz\\,\""}),
- splitCsv("\"\"\",f\\ø\"\"o,\",\"\"\",bá\\'r\",\"baz\\,\"\"\""));
-}
-}
+++ /dev/null
-/**
- * Copyright (c) 2014-present, Facebook, Inc.
- * All rights reserved.
- *
- * This source code is licensed in accordance with the terms specified in
- * the LICENSE file found in the root directory of this source tree.
- */
-
-#pragma once
-
-#include <atomic>
-#include <functional>
-#include <map>
-#include <memory>
-#include <mutex>
-#include <thread>
-#include <utility>
-#include <vector>
-
-#include <gtest/gtest_prod.h>
-
-#include <osquery/core.h>
-#include <osquery/tables.h>
-#include <osquery/utils/status/status.h>
-#include <osquery/utils/mutex.h>
-
-namespace osquery {
-
-struct Subscription;
-template <class SC, class EC>
-class EventPublisher;
-template <class PUB>
-class EventSubscriber;
-class EventFactory;
-
-using EventID = const std::string;
-using EventContextID = uint64_t;
-using EventTime = uint64_t;
-using EventRecord = std::pair<std::string, EventTime>;
-
-/**
- * @brief An EventPublisher will define a SubscriptionContext for
- * EventSubscriber%s to use.
- *
- * Most EventPublisher%s will require specific information for interacting with
- * an OS to receive events. The SubscriptionContext contains information the
- * EventPublisher will use to register OS API callbacks, create
- * subscriptioning/listening handles, etc.
- *
- * Linux `inotify` should implement a SubscriptionContext that subscribes
- * filesystem events based on a filesystem path. `libpcap` will subscribe on
- * networking protocols at various stacks. Process creation may subscribe on
- * process name, parent pid, etc.
- */
-struct SubscriptionContext : private boost::noncopyable {};
-
-/**
- * @brief An EventSubscriber EventCallback method will receive an EventContext.
- *
- * The EventContext contains the event-related data supplied by an
- * EventPublisher when the event occurs. If a subscribing EventSubscriber
- * would be called for the event, the EventSubscriber%'s EventCallback is
- * passed an EventContext.
- */
-struct EventContext : private boost::noncopyable {
- /// An unique counting ID specific to the EventPublisher%'s fired events.
- EventContextID id{0};
-
- /// The time the event occurred, as determined by the publisher.
- EventTime time{0};
-};
-
-using SubscriptionRef = std::shared_ptr<Subscription>;
-using BaseEventPublisher = EventPublisher<SubscriptionContext, EventContext>;
-using EventPublisherRef = std::shared_ptr<BaseEventPublisher>;
-using SubscriptionContextRef = std::shared_ptr<SubscriptionContext>;
-using EventContextRef = std::shared_ptr<EventContext>;
-using BaseEventSubscriber = EventSubscriber<BaseEventPublisher>;
-using EventSubscriberRef = std::shared_ptr<EventSubscriber<BaseEventPublisher>>;
-
-/**
- * @brief EventSubscriber%s and Publishers may exist in various states.
- *
- * The class will move through states when osquery is initializing the
- * registry, starting event publisher loops, and requesting initialization of
- * each subscriber and the optional set of subscriptions it creates. If this
- * initialization fails the publishers or EventFactory may eject, warn, or
- * otherwise not use the subscriber's subscriptions.
- *
- * The supported states are:
- * - None: The default state, uninitialized.
- * - Setup: The Subscriber is attached and has run setup.
- * - Running: Subscriber is ready for events.
- * - Paused: Subscriber was initialized but is not currently accepting events.
- * - Failed: Subscriber failed to initialize or is otherwise offline.
- */
-enum class EventState {
- EVENT_NONE = 0,
- EVENT_SETUP,
- EVENT_RUNNING,
- EVENT_PAUSED,
- EVENT_FAILED,
-};
-
-/// Use a single placeholder for the EventContextRef passed to EventCallback.
-using EventCallback = std::function<Status(const EventContextRef&,
- const SubscriptionContextRef&)>;
-
-/// An EventPublisher must track every subscription added.
-using SubscriptionVector = std::vector<SubscriptionRef>;
-
-/// The set of search-time binned lookup tables.
-extern const std::vector<size_t> kEventTimeLists;
-
-/**
- * @brief Details for each subscriber as it relates to the schedule.
- *
- * This is populated for each configuration update by scanning the schedule.
- */
-struct SubscriberExpirationDetails {
- public:
- /// The max internal is the minimum wait time for expiring subscriber data.
- size_t max_interval{0};
-
- /// The number of queries that should run between intervals.
- size_t query_count{0};
-};
-
-/**
- * @brief DECLARE_PUBLISHER supplies needed boilerplate code that applies a
- * string-type EventPublisherID to identify the publisher declaration.
- */
-#define DECLARE_PUBLISHER(TYPE) \
- public: \
- const std::string type() const override final { \
- return TYPE; \
- }
-
-/**
- * @brief A Subscription is used to configure an EventPublisher and bind a
- * callback to a SubscriptionContext.
- *
- * A Subscription is the input to an EventPublisher when the EventPublisher
- * decides on the scope and details of the events it watches/generates.
- * An example includes a filesystem change event. A subscription would include
- * a path with optional recursion and attribute selectors as well as a callback
- * function to fire when an event for that path and selector occurs.
- *
- * A Subscription also functions to greatly scope an EventPublisher%'s work.
- * Using the same filesystem example and the Linux inotify subsystem a
- * Subscription limits the number of inode watches to only those requested by
- * appropriate EventSubscriber%s.
- * Note: EventSubscriber%s and Subscriptions can be configured by the osquery
- * user.
- *
- * Subscriptions are usually created with EventFactory members:
- *
- * @code{.cpp}
- * EventFactory::addSubscription("MyEventPublisher", my_subscription_context);
- * @endcode
- */
-struct Subscription : private boost::noncopyable {
- public:
- // EventSubscriber name.
- std::string subscriber_name;
-
- /// An EventPublisher%-specific SubscriptionContext.
- SubscriptionContextRef context;
-
- /// An EventSubscription member EventCallback method.
- EventCallback callback;
-
- explicit Subscription(std::string name) : subscriber_name(std::move(name)){};
-
- static SubscriptionRef create(const std::string& name) {
- return std::make_shared<Subscription>(name);
- }
-
- static SubscriptionRef create(const std::string& name,
- const SubscriptionContextRef& mc,
- EventCallback ec = nullptr) {
- auto subscription = std::make_shared<Subscription>(name);
- subscription->context = mc;
- subscription->callback = std::move(ec);
- return subscription;
- }
-
- public:
- Subscription() = delete;
-};
-
-class Eventer {
- public:
- /**
- * @brief Request the subscriber's initialization state.
- *
- * When event subscribers are created (initialized) they are expected to emit
- * a set of subscriptions to their publisher "type". If the subscriber fails
- * to initialize then the publisher may remove any intermediate subscriptions.
- */
- EventState state() const {
- return state_;
- }
-
- protected:
- /// Set the subscriber state.
- void state(EventState state) {
- state_ = state;
- }
-
- private:
- /// The event subscriber's run state.
- EventState state_{EventState::EVENT_NONE};
-
- friend class EventFactory;
-};
-
-class EventPublisherPlugin : public Plugin,
- public Eventer {
- public:
- /**
- * @brief A new Subscription was added, potentially change state based on all
- * subscriptions for this EventPublisher.
- *
- * `configure` allows the EventPublisher to optimize on the state of all
- * subscriptions. An example is Linux `inotify` where multiple
- * EventSubscription%s will subscription identical paths, e.g., /etc for
- * config changes. Since Linux `inotify` has a subscription limit, `configure`
- * can dedup paths.
- */
- void configure() override{};
-
- /**
- * @brief Perform handle opening, OS API callback registration.
- *
- * `setUp` is the event framework's EventPublisher constructor equivalent.
- * This is called in the main thread before the publisher's run loop has
- * started, immediately following registration.
- */
- Status setUp() override {
- return Status::success();
- }
-
- /**
- * @brief Perform handle closing, resource cleanup.
- *
- * osquery is about to end, the EventPublisher should close handle descriptors
- * unblock resources, and prepare to exit. This will be called from the main
- * thread after the run loop thread has exited.
- */
- void tearDown() override {}
-
- /**
- * @brief Implement a "step" of an optional run loop.
- *
- * @return A SUCCESS status will immediately call `run` again. A FAILED status
- * will exit the run loop and the thread.
- */
- virtual Status run() {
- return Status(1, "No run loop required");
- }
-
- /// This is a plugin type and must implement a call method.
- Status call(const PluginRequest& /*request*/,
- PluginResponse& /*response*/) override {
- return Status(0);
- }
-
- /**
- * @brief A new EventSubscriber is subscribing events of this publisher type.
- *
- * @param subscription The Subscription context information and optional
- * EventCallback.
- *
- * @return If the Subscription is not appropriate (mismatched type) fail.
- */
- virtual Status addSubscription(const SubscriptionRef& subscription);
-
- /// Remove all subscriptions from a named subscriber.
- virtual void removeSubscriptions(const std::string& subscriber);
-
- public:
- /// Overriding the EventPublisher constructor is not recommended.
- EventPublisherPlugin() = default;
- ~EventPublisherPlugin() override = default;
-
- /// Return a string identifier associated with this EventPublisher.
- virtual const std::string type() const {
- return getName();
- }
-
- public:
- /// Number of Subscription%s watching this EventPublisher.
- size_t numSubscriptions();
-
- /**
- * @brief The number of events fired by this EventPublisher.
- *
- * @return The number of events.
- */
- EventContextID numEvents() const;
-
- /// Check if the EventFactory is ending all publisher threads.
- bool isEnding() const {
- return ending_;
- }
-
- /// Set the ending status for this publisher.
- void isEnding(bool ending) {
- ending_ = ending;
- }
-
- /// Check if the publisher's run loop has started.
- bool hasStarted() const {
- return started_;
- }
-
- /// Set the run or started status for this publisher.
- void hasStarted(bool started) {
- started_ = started;
- }
-
- /// Get the number of publisher restarts.
- size_t restartCount() const {
- return restart_count_;
- }
-
- public:
- explicit EventPublisherPlugin(EventPublisherPlugin const&) = delete;
- EventPublisherPlugin& operator=(EventPublisherPlugin const&) = delete;
-
- protected:
- /**
- * @brief The generic check loop to call SubscriptionContext callback methods.
- *
- * It is NOT recommended to override `fire`. The simple logic of enumerating
- * the Subscription%s and using `shouldFire` is more appropriate.
- *
- * @param ec The EventContext created and fired by the EventPublisher.
- * @param time The most accurate time associated with the event.
- */
- void fire(const EventContextRef& ec, EventTime time = 0);
-
- /// The internal fire method used by the typed EventPublisher.
- virtual void fireCallback(const SubscriptionRef& sub,
- const EventContextRef& ec) const = 0;
-
- /// A lock for subscription manipulation.
- mutable Mutex subscription_lock_;
-
- /// The EventPublisher will keep track of Subscription%s that contain callins.
- SubscriptionVector subscriptions_;
-
- /// An Event ID is assigned by the EventPublisher within the EventContext.
- /// This is not used to store event date in the backing store.
- std::atomic<EventContextID> next_ec_id_{0};
-
- private:
- /// Set ending to True to cause event type run loops to finish.
- std::atomic<bool> ending_{false};
-
- /// Set to indicate whether the event run loop ever started.
- std::atomic<bool> started_{false};
-
- /// A helper count of event publisher runloop iterations.
- std::atomic<size_t> restart_count_{0};
-
- private:
- /// Enable event factory "callins" through static publisher callbacks.
- friend class EventFactory;
-
- private:
- FRIEND_TEST(EventsTests, test_event_publisher);
- FRIEND_TEST(EventsTests, test_fire_event);
-};
-
-class EventSubscriberPlugin : public Plugin, public Eventer {
- public:
- /**
- * @brief Add Subscription%s to the EventPublisher this module will act on.
- *
- * When the EventSubscriber%'s `init` method is called you are assured the
- * EventPublisher has `setUp` and is ready to subscription for events.
- */
- virtual Status init() {
- return Status(0);
- }
-
- /// This is a plugin type and must implement a call method.
- Status call(const PluginRequest& /*request*/,
- PluginResponse& /*response*/) override {
- return Status(0);
- }
-
- protected:
- /**
- * @brief Store parsed event data from an EventCallback in a backing store.
- *
- * This method stores a single event
- *
- * @param r The row to add
- *
- * @return Was the element added to the backing store.
- */
- Status add(const Row& r);
-
- /**
- * @brief Store parsed event data from an EventCallback in a backing store.
- *
- * Within a EventCallback the EventSubscriber has an opportunity to create
- * an osquery Row element, add the relevant table data for the EventSubscriber
- * and store that element in the osquery backing store. At query-time
- * the added data will apply selection criteria and return these elements.
- * The backing store data retrieval is optimized by time-based indexes. It
- * is important to added EventTime as it relates to "when the event occurred".
- *
- * @param row_list A (writable) vector of osquery Row elements.
- *
- * @return Was the element added to the backing store.
- */
- Status addBatch(std::vector<Row>& row_list);
-
- private:
- /// Overload add for tests and allow them to override the event time.
- virtual Status addBatch(std::vector<Row>& row_list,
- EventTime custom_event_time) final;
-
- private:
- /*
- * @brief When `get`ing event results, return EventID%s from time indexes.
- *
- * Used by EventSubscriber::get to retrieve EventID, EventTime indexes. This
- * applies the lookup-efficiency checks for time list appropriate bins.
- * If the time range in 24 hours and there is a 24-hour list bin it will
- * be queried using a single backing store `Get` followed by two `Get`s of
- * the most-specific boundary lists.
- *
- * @param index the set of index to scan.
- * @param optimize if true apply optimization checks.
- *
- * @return List of EventID, EventTime%s
- */
- std::vector<EventRecord> getRecords(const std::vector<std::string>& indexes,
- bool optimize = true);
-
- /**
- * @brief Get a unique storage-related EventID.
- *
- * An EventID is an index/element-identifier for the backing store.
- * Each EventPublisher maintains a fired EventContextID to identify the many
- * events that may or may not be fired based on subscription criteria for this
- * EventSubscriber. This EventContextID is NOT the same as an EventID.
- * EventSubscriber development should not require use of EventID%s. If this
- * indexing is required within-EventCallback consider an
- * EventSubscriber%-unique indexing, counting mechanic.
- *
- * @return A unique ID for backing storage.
- */
- const std::string getEventID();
-
- /**
- * @brief Plan the best set of indexes for event record access.
- *
- * @param start an inclusive time to begin searching.
- * @param stop an inclusive time to end searching.
- * @param sort if true the indexes will be sorted.
- *
- * @return List of 'index.step' index strings.
- */
- std::vector<std::string> getIndexes(EventTime start,
- EventTime stop,
- bool sort = true);
-
- /**
- * @brief Expire indexes and eventually records.
- *
- * @param list_type the string representation of list binning type.
- * @param indexes complete set of 'index.step' indexes for the list_type.
- * @param expirations of the indexes, the set to expire.
- */
- void expireIndexes(const std::string& list_type,
- const std::vector<std::string>& indexes,
- const std::vector<std::string>& expirations);
-
- /// Expire all datums within a bin.
- void expireRecords(const std::string& list_type,
- const std::string& index,
- bool all);
-
- /**
- * @brief Inspect the number of events, expire those overflowing events_max.
- *
- * When the event manager starts, or after a checkpoint number of events,
- * the EventFactory will call expireCheck for each subscriber.
- *
- * The subscriber must count the number of buffered records and check if
- * that count exceeds the configured `events_max` limit. If an overflow
- * occurs the subscriber will expire N-events_max from the end of the queue.
- */
- void expireCheck();
-
- /**
- * @brief Add an EventID, EventTime pair to all matching list types.
- *
- * The list types are defined by time size. Based on the EventTime this pair
- * is added to the list bin for each list type. If there are two list types:
- * 60 seconds and 3600 seconds and `time` is 92, this pair will be added to
- * list type 1 bin 4 and list type 2 bin 1.
- *
- * @param event_id_list A vector of (unique) EventIDs
- * @param event_time The event time for this batch
- *
- * @return Were the indexes recorded.
- */
-
- Status recordEvents(const std::vector<std::string>& event_id_list,
- EventTime event_time);
-
- /**
- * @brief Get the expiration timeout for this event type
- *
- * The default implementation retrieves this value from FLAGS_events_expiry.
- * This method can be overridden to allow custom event expiration timeouts in
- * subclasses of EventSubscriberPlugin.
- *
- * @return The events expiration timeout for this event type
- */
- virtual size_t getEventsExpiry();
-
- /**
- * @brief Get the max number of events for this event type
- *
- * The default implementation retrieves this value from FLAGS_events_max.
- * This method can be overridden to allow custom max event numbers in
- * subclasses of EventSubscriberPlugin.
- *
- * @return The max number of events for this event type
- */
- virtual size_t getEventsMax();
-
- public:
- /**
- * @brief A single instance requirement for static callback facilities.
- *
- * The EventSubscriber constructor is NOT responsible for adding
- * Subscription%s. Please use `init` for adding Subscription%s as all
- * EventPublisher instances will have run `setUp` and initialized their run
- * loops.
- */
- EventSubscriberPlugin()
- : expire_events_(true), expire_time_(0), optimize_time_(0) {}
- ~EventSubscriberPlugin() override = default;
-
- /// Number of Subscription%s this EventSubscriber has used.
- size_t numSubscriptions() const {
- return subscription_count_;
- }
-
- /// The number of events this EventSubscriber has received.
- EventContextID numEvents() const {
- return event_count_;
- }
-
- /// Compare the number of queries run against the queries configured.
- bool executedAllQueries() const;
-
- public:
- explicit EventSubscriberPlugin(EventSubscriberPlugin const&) = delete;
- EventSubscriberPlugin& operator=(EventSubscriberPlugin const&) = delete;
-
- protected:
- /**
- * @brief Backing storage indexing namespace.
- *
- * The backing storage will accumulate events for this subscriber. A namespace
- * is provided to prevent event indexing collisions between subscribers and
- * publishers. The namespace is a combination of the publisher and subscriber
- * registry plugin names.
- */
- /// See getType for lookup rational.
- virtual const std::string dbNamespace() const {
- return getType() + '.' + getName();
- }
-
- /// Disable event expiration for this subscriber.
- void doNotExpire() {
- expire_events_ = false;
- }
-
- /// Trampoline into the EventFactory and lookup the name of the publisher.
- virtual const std::string& getType() const = 0;
-
- /// Get a handle to the EventPublisher.
- EventPublisherRef getPublisher() const;
-
- /// Remove all subscriptions from this subscriber.
- void removeSubscriptions();
-
- protected:
- /// A helper value counting the number of fired events tracked by publishers.
- EventContextID event_count_{0};
-
- /// A helper value counting the number of subscriptions created.
- size_t subscription_count_{0};
-
- private:
- Status setUp() override {
- return Status::success();
- }
-
- private:
- /// Do not respond to periodic/scheduled/triggered event expiration requests.
- bool expire_events_{false};
-
- /// Events before the expire_time_ are invalid and will be purged.
- EventTime expire_time_{0};
-
- /// Cached value of last generated EventID.
- size_t last_eid_{0};
-
- /**
- * @brief Optimize subscriber selects by tracking the last select time.
- *
- * Event subscribers may optimize selects when used in a daemon schedule by
- * requiring an event 'time' constraint and otherwise applying a minimum time
- * as the last time the scheduled query ran.
- */
- EventTime optimize_time_{0};
-
- /**
- * @brief Last event ID returned while using events-optimization.
- *
- * A time with second precision is not sufficient, but it works for index
- * retrieval. While sorting using the time optimization, discard events
- * before or equal to the optimization ID.
- */
- size_t optimize_eid_{0};
-
- /// The minimum acceptable expiration, based on the query schedule.
- std::atomic<size_t> min_expiration_{0};
-
- /// The number of scheduled queries using this subscriber.
- std::atomic<size_t> query_count_{0};
-
- /// Set of queries that have used this subscriber table.
- std::set<std::string> queries_;
-
- /// Lock used when incrementing the EventID database index.
- Mutex event_id_lock_;
-
- /// Lock used when recording an EventID and time into search bins.
- Mutex event_record_lock_;
-
- /// Lock used when recording queries executing against this subscriber.
- mutable Mutex event_query_record_;
-
- private:
- friend class EventFactory;
- friend class EventPublisherPlugin;
-
- private:
- FRIEND_TEST(EventsDatabaseTests, test_event_module_id);
- FRIEND_TEST(EventsDatabaseTests, test_record_indexing);
- FRIEND_TEST(EventsDatabaseTests, test_record_range);
- FRIEND_TEST(EventsDatabaseTests, test_record_expiration);
- FRIEND_TEST(EventsDatabaseTests, test_gentable);
- FRIEND_TEST(EventsDatabaseTests, test_expire_check);
- FRIEND_TEST(EventsDatabaseTests, test_optimize);
- FRIEND_TEST(EventsDatabaseTests, test_record_corruption);
- FRIEND_TEST(EventsTests, test_event_subscriber_configure);
- friend class DBFakeEventSubscriber;
- friend class BenchmarkEventSubscriber;
-};
-
-/**
- * @brief A factory for associating event generators to EventPublisherID%s.
- *
- * This factory both registers new event types and the subscriptions that use
- * them. An EventPublisher is also a factory, the single event factory
- * arbitrates Subscription creation and management for each associated
- * EventPublisher.
- *
- * Since event types may be plugins, they are created using the factory.
- * Since subscriptions may be configured/disabled they are also factory-managed.
- */
-class EventFactory : private boost::noncopyable {
- public:
- /// Access to the EventFactory instance.
- static EventFactory& getInstance();
-
- /**
- * @brief Add an EventPublisher to the factory.
- *
- * The registration is mostly abstracted using osquery's registry.
- *
- * @param pub If for some reason the caller needs access to the
- * EventPublisher instance they can register-by-instance.
- *
- * Access to the EventPublisher instance is not discouraged, but using the
- * EventFactory `getEventPublisher` accessor is encouraged.
- */
- static Status registerEventPublisher(const PluginRef& pub);
-
- /**
- * @brief Add an EventSubscriber to the factory.
- *
- * The registration is mostly abstracted using osquery's registry.
- */
- template <class T>
- static Status registerEventSubscriber() {
- auto sub = std::make_shared<T>();
- return registerEventSubscriber(sub);
- };
-
- /**
- * @brief Add an EventSubscriber to the factory.
- *
- * The registration is mostly abstracted using osquery's registry.
- *
- * @param sub If the caller must access the EventSubscriber instance
- * control may be passed to the registry.
- *
- * Access to the EventSubscriber instance outside of the within-instance
- * table generation method and set of EventCallback%s is discouraged.
- */
- static Status registerEventSubscriber(const PluginRef& sub);
-
- /**
- * @brief Add a SubscriptionContext and EventCallback Subscription to an
- * EventPublisher.
- *
- * Create a Subscription from a given SubscriptionContext and EventCallback
- * and add that Subscription to the EventPublisher associated identifier.
- *
- * @param type_id ID string for an EventPublisher receiving the Subscription.
- * @param name_id ID string for the EventSubscriber.
- * @param sc A SubscriptionContext related to the EventPublisher.
- * @param cb When the EventPublisher fires an event the SubscriptionContext
- * will be evaluated, if the event matches optional specifics in the context
- * this callback function will be called. It should belong to an
- * EventSubscription.
- *
- * @return Was the SubscriptionContext appropriate for the EventPublisher.
- */
- static Status addSubscription(const std::string& type_id,
- const std::string& name_id,
- const SubscriptionContextRef& sc,
- EventCallback cb = nullptr);
-
- /// Add a Subscription using a caller Subscription instance.
- static Status addSubscription(const std::string& type_id,
- const SubscriptionRef& subscription);
-
- /// Get the total number of Subscription%s across ALL EventPublisher%s.
- static size_t numSubscriptions(const std::string& type_id);
-
- /// Get the number of EventPublishers.
- static size_t numEventPublishers() {
- return EventFactory::getInstance().event_pubs_.size();
- }
-
- /**
- * @brief Halt the EventPublisher run loop.
- *
- * Any EventSubscriber%s with Subscription%s for this EventPublisher will
- * become useless. osquery callers MUST deregister events.
- * EventPublisher%s assume they can hook/trampoline, which requires cleanup.
- * This will tear down and remove the publisher if the run loop did not start.
- * Otherwise it will call end on the publisher and assume the run loop will
- * tear down and remove.
- *
- * @param pub The string label for the EventPublisher.
- *
- * @return Did the EventPublisher deregister cleanly.
- */
- static Status deregisterEventPublisher(const EventPublisherRef& pub);
-
- /// Deregister an EventPublisher by publisher name.
- static Status deregisterEventPublisher(const std::string& type_id);
-
- /// Deregister an EventSubscriber by the subscriber name.
- static Status deregisterEventSubscriber(const std::string& sub);
-
- /// Return an instance to a registered EventPublisher.
- static EventPublisherRef getEventPublisher(const std::string& pub);
-
- /// Return an instance to a registered EventSubscriber.
- static EventSubscriberRef getEventSubscriber(const std::string& sub);
-
- /// Check if an event subscriber exists.
- static bool exists(const std::string& sub);
-
- /// Return a list of publisher types, these are their registry names.
- static std::vector<std::string> publisherTypes();
-
- /// Return a list of subscriber registry names,
- static std::vector<std::string> subscriberNames();
-
- /// Set log forwarding by adding a logger receiver.
- static void addForwarder(const std::string& logger);
-
- /// Optionally forward events to loggers.
- static void forwardEvent(const std::string& event);
-
- /**
- * @brief The event factory, subscribers, and publishers respond to updates.
- *
- * This should be called by the Config instance when configuration data is
- * updated. It is separate from the config parser that takes configuration
- * information specific to events and acts. This allows the event factory
- * to make changes relative to the schedule or packs.
- */
- static void configUpdate();
-
- public:
- /// The dispatched event thread's entry-point (if needed).
- static Status run(const std::string& type_id);
-
- /// An initializer's entry-point for spawning all event type run loops.
- static void delay();
-
- /// If a static EventPublisher callback wants to fire
- template <typename PUB>
- static void fire(const EventContextRef& ec) {
- auto event_pub = getEventPublisher(getType<PUB>());
- event_pub->fire(ec);
- }
-
- /**
- * @brief Return the publisher registry name given a type.
- *
- * Subscriber initialization and runtime static callbacks can lookup the
- * publisher type name, which is the registry plugin name. This allows static
- * callbacks to fire into subscribers.
- */
- template <class PUB>
- static const std::string getType() {
- auto pub = std::make_shared<PUB>();
- return pub->type();
- }
-
- /**
- * @brief End all EventPublisher run loops and deregister.
- *
- * End is NOT the same as deregistration. End will call deregister on all
- * publishers then either join or detach their run loop threads.
- * See EventFactory::deregisterEventPublisher for actions taken during
- * deregistration.
- *
- * @param join if true, threads will be joined
- */
- static void end(bool join = false);
-
- public:
- EventFactory(EventFactory const&) = delete;
- EventFactory& operator=(EventFactory const&) = delete;
-
- private:
- /// An EventFactory will exist for the lifetime of the application.
- EventFactory() = default;
- ~EventFactory() = default;
-
- private:
- /// Set of registered EventPublisher instances.
- std::map<std::string, EventPublisherRef> event_pubs_;
-
- /// Set of instantiated EventSubscriber subscriptions.
- std::map<std::string, EventSubscriberRef> event_subs_;
-
- /// Set of running EventPublisher run loop threads.
- std::vector<std::shared_ptr<std::thread>> threads_;
-
- /// Set of logger plugins to forward events.
- std::vector<std::string> loggers_;
-
- /// Factory publisher state manipulation.
- Mutex factory_lock_;
-};
-
-/**
- * @brief Generate OS events of a type (FS, Network, Syscall, ioctl).
- *
- * A 'class' of OS events is abstracted into an EventPublisher responsible for
- * remaining as agile as possible given a known-set of subscriptions.
- *
- * The life cycle of an EventPublisher may include, `setUp`, `configure`, `run`,
- * `tearDown`, and `fire`. `setUp` and `tearDown` happen when osquery starts and
- * stops either as a daemon or interactive shell. `configure` is a pseudo-start
- * called every time a Subscription is added. EventPublisher%s can adjust their
- * scope/agility specific to each added subscription by overriding
- *`addSubscription`, and/or globally in `configure`.
- *
- * Not all EventPublisher%s leverage pure async OS APIs, and most will require a
- * run loop either polling with a timeout on a descriptor or for a change. When
- * osquery initializes the EventFactory will optionally create a thread for each
- * EventPublisher using `run` as the thread's entrypoint. `run` is called in a
- * within-thread loop where returning a FAILED status ends the run loop and
- * shuts down the thread.
- *
- * To opt-out of polling in a thread, consider the following run implementation:
- *
- * @code{.cpp}
- * Status run() { return Status(1, "Not Implemented"); }
- * @endcode
- *
- * The final life cycle component, `fire` will iterate over the EventPublisher
- * Subscription%s and call `shouldFire` for each, using the EventContext fired.
- * The `shouldFire` method should check the subscription-specific selectors and
- * only call the Subscription%'s callback function if the EventContext
- * (thus event) matches.
- */
-template <typename SC, typename EC>
-class EventPublisher : public EventPublisherPlugin {
- public:
- /// A nested helper typename for the templated SubscriptionContextRef.
- using SCRef = typename std::shared_ptr<SC>;
- /// A nested helper typename for the templated EventContextRef.
- using ECRef = typename std::shared_ptr<EC>;
-
- public:
- EventPublisher() = default;
- ~EventPublisher() override = default;
-
- /// Up-cast a base EventContext reference to the templated ECRef.
- static ECRef getEventContext(const EventContextRef& ec) {
- return std::static_pointer_cast<EC>(ec);
- };
-
- /// Up-cast a base SubscriptionContext reference to the templated SCRef.
- static SCRef getSubscriptionContext(const SubscriptionContextRef& sc) {
- return std::static_pointer_cast<SC>(sc);
- }
-
- /// Create a EventContext based on the templated type.
- static ECRef createEventContext() {
- return std::make_shared<EC>();
- }
-
- /// Create a SubscriptionContext based on the templated type.
- static SCRef createSubscriptionContext() {
- return std::make_shared<SC>();
- }
-
- protected:
- /**
- * @brief The internal `fire` phase of publishing.
- *
- * This is a template-generated method that up-casts the generic fired
- * event/subscription contexts, and calls the callback if the event should
- * fire given a subscription.
- *
- * @param sub The SubscriptionContext and optional EventCallback.
- * @param ec The event that was fired.
- */
- void fireCallback(const SubscriptionRef& sub,
- const EventContextRef& ec) const override {
- auto pub_sc = getSubscriptionContext(sub->context);
- auto pub_ec = getEventContext(ec);
- if (shouldFire(pub_sc, pub_ec) && sub->callback != nullptr) {
- sub->callback(pub_ec, pub_sc);
- }
- }
-
- protected:
- /**
- * @brief The generic `fire` will call `shouldFire` for each Subscription.
- *
- * @param sc A SubscriptionContext with optional specifications for events
- * details.
- * @param ec The event fired with event details.
- *
- * @return should the Subscription%'s EventCallback be called for this event.
- */
- virtual bool shouldFire(const SCRef& sc, const ECRef& ec) const {
- (void)sc;
- (void)ec;
- return true;
- }
-
- private:
- FRIEND_TEST(EventsTests, test_event_subscriber_subscribe);
- FRIEND_TEST(EventsTests, test_event_subscriber_context);
- FRIEND_TEST(EventsTests, test_fire_event);
-};
-
-/**
- * @brief An interface binding Subscriptions, event response, and table
- *generation.
- *
- * Use the EventSubscriber interface when adding event subscriptions and
- * defining callin functions. The EventCallback is usually a member function
- * for an EventSubscriber. The EventSubscriber interface includes a very
- * important `add` method that abstracts the needed event to backing store
- * interaction.
- *
- * Storing event data in the backing store must match a table spec for queries.
- * Small overheads exist that help query-time indexing and lookups.
- */
-template <class PUB>
-class EventSubscriber : public EventSubscriberPlugin {
- protected:
- using SCRef = typename PUB::SCRef;
- using ECRef = typename PUB::ECRef;
-
- public:
- /**
- * @brief The registry plugin name for the subscriber's publisher.
- *
- * During event factory initialization the subscribers 'peek' at the registry
- * plugin name assigned to publishers. The corresponding publisher name is
- * interpreted as the subscriber's event 'type'.
- */
- const std::string& getType() const override {
- static const std::string type = EventFactory::getType<PUB>();
- return type;
- };
-
- protected:
- /// Helper function to call the publisher's templated subscription generator.
- SCRef createSubscriptionContext() const {
- return PUB::createSubscriptionContext();
- }
-
- /**
- * @brief Bind a registered EventSubscriber member function to a Subscription.
- *
- * @param entry A templated EventSubscriber member function.
- * @param sc The subscription context.
- */
- template <class T, typename E>
- void subscribe(Status (T::*entry)(const std::shared_ptr<E>&, const SCRef&),
- const SCRef& sc) {
- using std::placeholders::_1;
- using std::placeholders::_2;
- using CallbackFunc =
- Status (T::*)(const EventContextRef&, const SubscriptionContextRef&);
-
- // Down-cast the pointer to the member function.
- auto base_entry = reinterpret_cast<CallbackFunc>(entry);
- // Up-cast the EventSubscriber to the caller.
- auto sub = dynamic_cast<T*>(this);
- if (base_entry != nullptr && sub != nullptr) {
- // Create a callable through the member function using the instance of the
- // EventSubscriber and a single parameter placeholder (the EventContext).
- auto cb = std::bind(base_entry, sub, _1, _2);
- // Add a subscription using the callable and SubscriptionContext.
- Status stat =
- EventFactory::addSubscription(sub->getType(), sub->getName(), sc, cb);
- if (stat.ok()) {
- subscription_count_++;
- }
- }
- }
-
- public:
- explicit EventSubscriber(bool enabled = true)
- : EventSubscriberPlugin(), disabled(!enabled) {}
- ~EventSubscriber() override = default;
-
- protected:
- /**
- * @brief Allow subscriber implementations to default disable themselves.
- *
- * A subscriber may induce latency on a system within the callback routines.
- * Before the initialization and set up is performed the EventFactory can
- * choose to exclude a subscriber if it is not explicitly enabled within
- * the config.
- *
- * EventSubscriber%s that should be default-disabled should set this flag
- * in their constructor or worst case before EventSubsciber::init.
- */
- bool disabled{false};
-
- private:
- friend class EventFactory;
-
- private:
- FRIEND_TEST(EventsTests, test_event_sub);
- FRIEND_TEST(EventsTests, test_event_sub_subscribe);
- FRIEND_TEST(EventsTests, test_event_sub_context);
- FRIEND_TEST(EventsTests, test_event_toggle_subscribers);
-};
-
-/// Iterate the event publisher registry and create run loops for each using
-/// the event factory.
-void attachEvents();
-} // namespace osquery
#include <osquery/data_logger.h>
#include <osquery/database.h>
-#include <osquery/events.h>
#include <osquery/filesystem/filesystem.h>
#include <osquery/flags.h>
#include <osquery/plugins/logger.h>
FLAG(string, logger_plugin, "filesystem", "Logger plugin name");
-/// Log each added or removed line individually, as an "event".
-FLAG(bool, logger_event_type, true, "Log scheduled results as events");
-
-/// Log each row from a snapshot query individually, as an "event".
-FLAG(bool,
- logger_snapshot_event_type,
- false,
- "Log scheduled snapshot results as events");
-
/// Alias for the minloglevel used internally by GLOG.
FLAG(int32, logger_min_status, 0, "Minimum level for status log recording");
// return a success status after initialization.
BufferedLogSink::get().addPlugin(logger);
}
-
- if ((status.getCode() & LOGGER_FEATURE_LOGEVENT) > 0) {
- EventFactory::addForwarder(logger);
- }
}
if (forward) {
std::vector<std::string> json_items;
Status status;
- if (FLAGS_logger_event_type) {
- status = serializeQueryLogItemAsEventsJSON(results, json_items);
- } else {
- std::string json;
- status = serializeQueryLogItemJSON(results, json);
- json_items.emplace_back(json);
- }
+ std::string json;
+ status = serializeQueryLogItemJSON(results, json);
+ json_items.emplace_back(json);
+
if (!status.ok()) {
return status;
}
std::vector<std::string> json_items;
Status status;
- if (FLAGS_logger_snapshot_event_type) {
- status = serializeQueryLogItemAsEventsJSON(item, json_items);
- } else {
- std::string json;
- status = serializeQueryLogItemJSON(item, json);
- json_items.emplace_back(json);
- }
+ std::string json;
+ status = serializeQueryLogItemJSON(item, json);
+ json_items.emplace_back(json);
+
if (!status.ok()) {
return status;
}
DECLARE_int32(logger_min_stderr);
DECLARE_bool(logger_secondary_status_only);
DECLARE_bool(logger_status_sync);
-DECLARE_bool(logger_event_type);
-DECLARE_bool(logger_snapshot_event_type);
DECLARE_bool(disable_logging);
DECLARE_bool(log_numerics_as_numbers);
logSnapshotQuery(item);
EXPECT_EQ(2U, LoggerTests::snapshot_rows_added);
- FLAGS_logger_snapshot_event_type = true;
logSnapshotQuery(item);
EXPECT_EQ(4U, LoggerTests::snapshot_rows_added);
- FLAGS_logger_snapshot_event_type = false;
}
class SecondTestLoggerPlugin : public LoggerPlugin {
EXPECT_EQ(1U, LoggerTests::log_lines.size());
// The entire removed/added is one event when result events is false.
- FLAGS_logger_event_type = false;
item.results.removed.push_back({{"test_column", "test_new_value\n"}});
logQueryLogItem(item);
EXPECT_EQ(2U, LoggerTests::log_lines.size());
- FLAGS_logger_event_type = true;
// Now the two removed will be individual events.
logQueryLogItem(item);
!osquery::FLAGS_A.empty() || !osquery::FLAGS_pack.empty() ||
osquery::FLAGS_L || osquery::FLAGS_profile > 0) {
// A query was set as a positional argument, via stdin, or profiling is on.
- osquery::FLAGS_disable_events = true;
osquery::FLAGS_disable_caching = true;
}
SHELL_FLAG(bool, planner, false, "Enable osquery runtime planner output");
-DECLARE_bool(disable_events);
-
RecursiveMutex kAttachMutex;
namespace tables {
// For event-based tables, help the caller if events are disabled.
bool events_satisfied =
- ((content->attributes & TableAttributes::EVENT_BASED) == 0 ||
- !FLAGS_disable_events);
+ ((content->attributes & TableAttributes::EVENT_BASED) == 0);
std::map<std::string, ColumnOptions> options;
for (size_t i = 0; i < content->columns.size(); ++i) {
return sq;
}
-TableRows genRows(EventSubscriberPlugin* sub) {
- auto vtc = std::make_shared<VirtualTableContent>();
- QueryContext context(vtc);
-
- TableRows results;
- return results;
-}
-
} // namespace osquery
#include <osquery/core.h>
#include <osquery/database.h>
-#include <osquery/events.h>
#include <osquery/filesystem/filesystem.h>
namespace osquery {
ScheduledQuery getOsqueryScheduledQuery();
-// Helper function to generate all rows from a generator-based table.
-TableRows genRows(EventSubscriberPlugin* sub);
-
} // namespace osquery
** This file is generated. Do not modify it manually!
*/
-#include <osquery/events.h>
#include <osquery/logger.h>
#include <osquery/sql/dynamic_table_row.h>
#include <osquery/tables.h>
** This file is generated. Do not modify it manually!
*/
-#include <osquery/events.h>
#include <osquery/logger.h>
#include <osquery/tables.h>
#include <osquery/registry_factory.h>
bool usesGenerator() const override { return true; }
void generator(RowYield& yield, QueryContext& context) override {
-{% if class_name != "" %}\
- if (EventFactory::exists(getName())) {
- auto subscriber = EventFactory::getEventSubscriber(getName());
- return subscriber->{{function}}(yield, context);
- } else {
- LOG(ERROR) << "Subscriber table missing: " << getName();
- }
-{% else %}\
tables::{{function}}(yield, context);
-{% endif %}\
}
{% else %}\
TableRows generate(QueryContext& context) override {
** This file is generated. Do not modify it manually!
*/
-#include <osquery/events.h>
#include <osquery/logger.h>
#include <osquery/tables.h>
#include <osquery/registry_factory.h>