2014-12-18 18:50:47 +00:00
|
|
|
/*
|
|
|
|
* Copyright (c) 2014, Facebook, Inc.
|
|
|
|
* All rights reserved.
|
|
|
|
*
|
|
|
|
* This source code is licensed under the BSD-style license found in the
|
2015-03-03 00:48:01 +00:00
|
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
2014-12-18 18:50:47 +00:00
|
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
|
|
*
|
|
|
|
*/
|
2014-09-19 08:54:33 +00:00
|
|
|
|
2015-01-12 03:43:04 +00:00
|
|
|
#include <exception>
|
2015-01-02 05:55:10 +00:00
|
|
|
|
2014-09-25 17:17:32 +00:00
|
|
|
#include <boost/algorithm/string.hpp>
|
|
|
|
#include <boost/algorithm/string/classification.hpp>
|
2014-09-19 08:54:33 +00:00
|
|
|
#include <boost/lexical_cast.hpp>
|
|
|
|
|
2014-12-03 23:14:02 +00:00
|
|
|
#include <osquery/core.h>
|
|
|
|
#include <osquery/events.h>
|
|
|
|
#include <osquery/flags.h>
|
2014-12-08 10:22:59 +00:00
|
|
|
#include <osquery/logger.h>
|
2014-12-03 23:14:02 +00:00
|
|
|
|
2014-09-19 08:54:33 +00:00
|
|
|
#include "osquery/core/conversions.h"
|
2015-05-24 01:52:42 +00:00
|
|
|
#include "osquery/database/db_handle.h"
|
2014-09-19 08:54:33 +00:00
|
|
|
|
|
|
|
namespace osquery {
|
|
|
|
|
2015-02-10 17:42:13 +00:00
|
|
|
/// Helper cooloff (ms) macro to prevent thread failure thrashing.
|
|
|
|
#define EVENTS_COOLOFF 20
|
|
|
|
|
2015-07-06 07:04:37 +00:00
|
|
|
FLAG(bool, disable_events, false, "Disable osquery publish/subscribe system");
|
2015-02-17 08:36:20 +00:00
|
|
|
|
2015-07-06 07:04:37 +00:00
|
|
|
FLAG(bool,
|
|
|
|
events_optimize,
|
|
|
|
true,
|
|
|
|
"Optimize subscriber select queries (scheduler only)");
|
|
|
|
|
|
|
|
FLAG(int32, events_expiry, 86000, "Timeout to expire event subscriber results");
|
2014-12-04 18:56:31 +00:00
|
|
|
|
2014-11-24 16:47:11 +00:00
|
|
|
const std::vector<size_t> kEventTimeLists = {
|
|
|
|
1 * 60 * 60, // 1 hour
|
2014-12-04 18:56:31 +00:00
|
|
|
1 * 60, // 1 minute
|
|
|
|
10, // 10 seconds
|
2014-09-19 08:54:33 +00:00
|
|
|
};
|
|
|
|
|
2015-03-21 17:14:57 +00:00
|
|
|
void publisherSleep(size_t milli) {
|
|
|
|
boost::this_thread::sleep(boost::posix_time::milliseconds(milli));
|
|
|
|
}
|
|
|
|
|
2015-08-16 03:43:53 +00:00
|
|
|
static inline EventTime timeFromRecord(const std::string& record) {
|
|
|
|
// Convert a stored index "as string bytes" to a time value.
|
|
|
|
char* end = nullptr;
|
|
|
|
long long int afinite = strtoll(record.c_str(), &end, 10);
|
|
|
|
if (end == nullptr || end == record.c_str() || *end != '\0' ||
|
|
|
|
((afinite == LLONG_MIN || afinite == LLONG_MAX) && errno == ERANGE)) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
return afinite;
|
|
|
|
}
|
|
|
|
|
2015-07-06 07:04:37 +00:00
|
|
|
QueryData EventSubscriberPlugin::genTable(QueryContext& context) {
|
|
|
|
EventTime start = 0, stop = -1;
|
|
|
|
if (context.constraints["time"].getAll().size() > 0) {
|
|
|
|
// Use the 'time' constraint to optimize backing-store lookups.
|
|
|
|
for (const auto& constraint : context.constraints["time"].getAll()) {
|
2015-08-16 03:43:53 +00:00
|
|
|
EventTime expr = timeFromRecord(constraint.expr);
|
2015-07-06 07:04:37 +00:00
|
|
|
if (constraint.op == EQUALS) {
|
|
|
|
stop = start = expr;
|
|
|
|
break;
|
|
|
|
} else if (constraint.op == GREATER_THAN) {
|
|
|
|
start = std::max(start, expr + 1);
|
|
|
|
} else if (constraint.op == GREATER_THAN_OR_EQUALS) {
|
|
|
|
start = std::max(start, expr);
|
|
|
|
} else if (constraint.op == LESS_THAN) {
|
|
|
|
stop = std::min(stop, expr - 1);
|
|
|
|
} else if (constraint.op == LESS_THAN_OR_EQUALS) {
|
|
|
|
stop = std::min(stop, expr);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else if (kToolType == OSQUERY_TOOL_DAEMON && FLAGS_events_optimize) {
|
|
|
|
// If the daemon is querying a subscriber without a 'time' constraint and
|
|
|
|
// allows optimization, only emit events since the last query.
|
|
|
|
start = optimize_time_;
|
|
|
|
optimize_time_ = getUnixTime() - 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return get(start, stop);
|
|
|
|
}
|
|
|
|
|
2015-01-30 18:44:25 +00:00
|
|
|
void EventPublisherPlugin::fire(const EventContextRef& ec, EventTime time) {
|
2014-09-23 01:35:12 +00:00
|
|
|
EventContextID ec_id;
|
|
|
|
|
2015-01-12 03:43:04 +00:00
|
|
|
if (isEnding()) {
|
|
|
|
// Cannot emit/fire while ending
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-09-23 01:35:12 +00:00
|
|
|
{
|
|
|
|
boost::lock_guard<boost::mutex> lock(ec_id_lock_);
|
|
|
|
ec_id = next_ec_id_++;
|
|
|
|
}
|
|
|
|
|
2014-09-25 17:17:32 +00:00
|
|
|
// Fill in EventContext ID and time if needed.
|
|
|
|
if (ec != nullptr) {
|
|
|
|
ec->id = ec_id;
|
|
|
|
if (ec->time == 0) {
|
|
|
|
if (time == 0) {
|
|
|
|
time = getUnixTime();
|
|
|
|
}
|
2014-12-08 10:22:59 +00:00
|
|
|
// Todo: add a check to assure normalized (seconds) time.
|
2014-09-25 17:17:32 +00:00
|
|
|
ec->time = time;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-10-03 15:26:41 +00:00
|
|
|
for (const auto& subscription : subscriptions_) {
|
Implement YARA table.
Currently only for OS X, will port to others soon.
Also need to add tests.
Remove old comment and add loading message.
Implement YARA table for Linux.
Use mask properly.
Use the various masks to specify the kinds of events we are interested
in. This removes the need to do the dirty "DELETED" check when the event
fires.
Make getYARAFiles return a const map.
Switch to LOG(WARNING) and emit error number.
Add vim .swp files to .gitignore.
Add yara_utils.(c|h).
Start to condense common code between the Linux and Darwin YARA tables
into a yara_utils.h. Right now it includes a function to compile rules
and store the results back in the map, indexed by category. It also has
the callback used by YARA when a rule is processed. I can not move much
more than that for the row creation code because the structures used in
the event callback are slightly different.
Include a better error message.
The errors are still printed by the compiler callback, but this will
allow my future work to return a Status from the event initialization to
print a useful message in summary.
Make Subscriber init() return Status.
Each EventSubscriber::init() now returns a Status. If the init() fails
for any reason the EventSubscriber is still stored but the failure is
tracked.
EventSubscribers now have a state member, which represents the current
state of the subscriber. The current supported states are:
uninitialized, running, paused, failed. Currently the only meaningful
ones are running and failed, but I put paused in there as a
forward-looking feature.
Subscriptions now have a subscriber_name member. This is used in
EventPublisherPlugin::fire() as a lookup to get the EventSubscriber and
check the state. If the EventSubscriber is not running the event will
not fire.
Only the EventSubscribers on OS X are using this. I'll do the Linux
implementation next.
Chase the init() changes to Linux.
This brings the Linux YARA table in line with the OS X one.
Require a EventSubscriberID when creating a subscription.
Now that Subscriptions are "tied" to EventSubscribers you must create a
Subscription with the name of the Subscriber it is for. This is because
when the event fires the list of Subscriptions is walked and the name is
used to lookup the EventSubscriber and make sure it is in the running
state.
Fix various tests.
Some tests would fire an event with only a Subscription, which is no
longer a valid thing to do. For these tests an EventSubscription is
created and registered in the EventFactory.
When Subscriptions are created pass the name of the EventSubscriber to
them. In some cases where no event is ever fired it is fine to pass a
bogus name.
Fix inotify tests.
Move a test down so the class is defined and make sure to create an
EventSubscriber and use it properly.
Add support for yara to provision.sh.
Right now this grabs yara 3.3.0 and applies the patch to fix min() and max(),
which is commit fc4696c8b725be1ac099d340359c8d550d116041 in the yara repo.
This has been tested under Ubuntu 14.04 only.
Remove NOMINMAX.
This is no longer necessary after the patch was backported to 3.3.0.
Revert "Add support for yara to provision.sh."
This reverts commit a8bd371498c0979f070adeff23d05571882ac3f1.
Use vendored YARA code in third-party.
This switches to using the YARA code contained in third-party, including
the patch to fix min/max macros.
Fix mismerge.
Remove unused function after merge.
Well, soon to be unused as soon as I fix up the Linux YARA table. ;)
Chase config changes.
Make the Linux YARA table use ConfigDataInstance along with files() and
yaraFiles().
2015-03-10 13:22:16 +00:00
|
|
|
auto es = EventFactory::getEventSubscriber(subscription->subscriber_name);
|
2015-09-03 19:22:42 +00:00
|
|
|
if (es != nullptr && es->state() == SUBSCRIBER_RUNNING) {
|
|
|
|
es->event_count_++;
|
Implement YARA table.
Currently only for OS X, will port to others soon.
Also need to add tests.
Remove old comment and add loading message.
Implement YARA table for Linux.
Use mask properly.
Use the various masks to specify the kinds of events we are interested
in. This removes the need to do the dirty "DELETED" check when the event
fires.
Make getYARAFiles return a const map.
Switch to LOG(WARNING) and emit error number.
Add vim .swp files to .gitignore.
Add yara_utils.(c|h).
Start to condense common code between the Linux and Darwin YARA tables
into a yara_utils.h. Right now it includes a function to compile rules
and store the results back in the map, indexed by category. It also has
the callback used by YARA when a rule is processed. I can not move much
more than that for the row creation code because the structures used in
the event callback are slightly different.
Include a better error message.
The errors are still printed by the compiler callback, but this will
allow my future work to return a Status from the event initialization to
print a useful message in summary.
Make Subscriber init() return Status.
Each EventSubscriber::init() now returns a Status. If the init() fails
for any reason the EventSubscriber is still stored but the failure is
tracked.
EventSubscribers now have a state member, which represents the current
state of the subscriber. The current supported states are:
uninitialized, running, paused, failed. Currently the only meaningful
ones are running and failed, but I put paused in there as a
forward-looking feature.
Subscriptions now have a subscriber_name member. This is used in
EventPublisherPlugin::fire() as a lookup to get the EventSubscriber and
check the state. If the EventSubscriber is not running the event will
not fire.
Only the EventSubscribers on OS X are using this. I'll do the Linux
implementation next.
Chase the init() changes to Linux.
This brings the Linux YARA table in line with the OS X one.
Require a EventSubscriberID when creating a subscription.
Now that Subscriptions are "tied" to EventSubscribers you must create a
Subscription with the name of the Subscriber it is for. This is because
when the event fires the list of Subscriptions is walked and the name is
used to lookup the EventSubscriber and make sure it is in the running
state.
Fix various tests.
Some tests would fire an event with only a Subscription, which is no
longer a valid thing to do. For these tests an EventSubscription is
created and registered in the EventFactory.
When Subscriptions are created pass the name of the EventSubscriber to
them. In some cases where no event is ever fired it is fine to pass a
bogus name.
Fix inotify tests.
Move a test down so the class is defined and make sure to create an
EventSubscriber and use it properly.
Add support for yara to provision.sh.
Right now this grabs yara 3.3.0 and applies the patch to fix min() and max(),
which is commit fc4696c8b725be1ac099d340359c8d550d116041 in the yara repo.
This has been tested under Ubuntu 14.04 only.
Remove NOMINMAX.
This is no longer necessary after the patch was backported to 3.3.0.
Revert "Add support for yara to provision.sh."
This reverts commit a8bd371498c0979f070adeff23d05571882ac3f1.
Use vendored YARA code in third-party.
This switches to using the YARA code contained in third-party, including
the patch to fix min/max macros.
Fix mismerge.
Remove unused function after merge.
Well, soon to be unused as soon as I fix up the Linux YARA table. ;)
Chase config changes.
Make the Linux YARA table use ConfigDataInstance along with files() and
yaraFiles().
2015-03-10 13:22:16 +00:00
|
|
|
fireCallback(subscription, ec);
|
|
|
|
}
|
2014-09-19 08:54:33 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-07-06 07:04:37 +00:00
|
|
|
std::set<std::string> EventSubscriberPlugin::getIndexes(EventTime start,
|
|
|
|
EventTime stop,
|
|
|
|
int list_key) {
|
2014-12-04 18:56:31 +00:00
|
|
|
auto db = DBHandle::getInstance();
|
|
|
|
auto index_key = "indexes." + dbNamespace();
|
2015-07-06 07:04:37 +00:00
|
|
|
std::set<std::string> indexes;
|
2014-12-04 18:56:31 +00:00
|
|
|
|
|
|
|
// Keep track of the tail/head of account time while bin searching.
|
|
|
|
EventTime start_max = stop, stop_min = stop, local_start, local_stop;
|
|
|
|
auto types = kEventTimeLists.size();
|
|
|
|
// List types are sized bins of time containing records for this namespace.
|
|
|
|
for (size_t i = 0; i < types; ++i) {
|
|
|
|
auto size = kEventTimeLists[i];
|
|
|
|
if (list_key > 0 && i != list_key) {
|
|
|
|
// A specific list_type was requested, only return bins of this key.
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string time_list;
|
|
|
|
auto list_type = boost::lexical_cast<std::string>(size);
|
|
|
|
auto status = db->Get(kEvents, index_key + "." + list_type, time_list);
|
|
|
|
if (time_list.length() == 0) {
|
|
|
|
// No events in this binning size.
|
|
|
|
return indexes;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (list_key == 0 && i == (types - 1) && types > 1) {
|
|
|
|
// Relax the requested start/stop bounds.
|
|
|
|
if (start != start_max) {
|
|
|
|
start = (start / size) * size;
|
|
|
|
start_max = ((start / size) + 1) * size;
|
|
|
|
if (start_max < stop) {
|
|
|
|
start_max = start + kEventTimeLists[types - 2];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (stop != stop_min) {
|
|
|
|
stop = ((stop / size) + 1) * size;
|
|
|
|
stop_min = (stop / size) * size;
|
|
|
|
if (stop_min > start) {
|
|
|
|
stop_min = stop_min - kEventTimeLists[types - 1];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else if (list_key > 0 || types == 1) {
|
|
|
|
// Relax the requested bounds to fit the requested/only index.
|
|
|
|
start = (start / size) * size;
|
|
|
|
start_max = ((start_max / size) + 1) * size;
|
|
|
|
}
|
|
|
|
|
|
|
|
// (1) The first iteration will have 1 range (start to start_max=stop).
|
2015-06-22 07:54:19 +00:00
|
|
|
// (2) Intermediate iterations will have 2 (start-start_max, stop-stop_min).
|
2014-12-04 18:56:31 +00:00
|
|
|
// For each iteration the range collapses based on the coverage using
|
|
|
|
// the first bin's start time and the last bin's stop time.
|
|
|
|
// (3) The last iteration's range includes relaxed bounds outside the
|
|
|
|
// requested start to stop range.
|
2014-12-06 09:18:29 +00:00
|
|
|
std::vector<std::string> all_bins, bins, expirations;
|
2014-12-04 18:56:31 +00:00
|
|
|
boost::split(all_bins, time_list, boost::is_any_of(","));
|
|
|
|
for (const auto& bin : all_bins) {
|
|
|
|
// Bins are identified by the binning size step.
|
2015-08-16 03:43:53 +00:00
|
|
|
auto step = timeFromRecord(bin);
|
2014-12-04 18:56:31 +00:00
|
|
|
// Check if size * step -> size * (step + 1) is within a range.
|
2015-10-16 16:04:33 +00:00
|
|
|
size_t bin_start = size * step;
|
|
|
|
size_t bin_stop = size * (step + 1);
|
2015-07-02 23:47:16 +00:00
|
|
|
if (expire_events_ && expire_time_ > 0) {
|
|
|
|
if (bin_stop <= expire_time_) {
|
|
|
|
expirations.push_back(bin);
|
|
|
|
} else if (bin_start < expire_time_) {
|
|
|
|
expireRecords(list_type, bin, false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (bin_start >= start && bin_stop <= start_max) {
|
2014-12-04 18:56:31 +00:00
|
|
|
bins.push_back(bin);
|
|
|
|
} else if ((bin_start >= stop_min && bin_stop <= stop) || stop == 0) {
|
|
|
|
bins.push_back(bin);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-06-22 07:54:19 +00:00
|
|
|
// Rewrite the index lists and delete each expired item.
|
|
|
|
if (expirations.size() > 0) {
|
|
|
|
expireIndexes(list_type, all_bins, expirations);
|
|
|
|
}
|
|
|
|
|
2014-12-04 18:56:31 +00:00
|
|
|
if (bins.size() != 0) {
|
2015-06-22 07:54:19 +00:00
|
|
|
// If more precision was achieved though this list's binning.
|
2015-08-16 03:43:53 +00:00
|
|
|
local_start = timeFromRecord(bins.front()) * size;
|
2014-12-04 18:56:31 +00:00
|
|
|
start_max = (local_start < start_max) ? local_start : start_max;
|
2015-08-16 03:43:53 +00:00
|
|
|
local_stop = (timeFromRecord(bins.back()) + 1) * size;
|
2014-12-04 18:56:31 +00:00
|
|
|
stop_min = (local_stop < stop_min) ? local_stop : stop_min;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (const auto& bin : bins) {
|
2015-07-06 07:04:37 +00:00
|
|
|
indexes.insert(list_type + "." + bin);
|
2014-12-04 18:56:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (start == start_max && stop == stop_min) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-12-08 10:22:59 +00:00
|
|
|
// Update the new time that events expire to now - expiry.
|
2014-12-04 18:56:31 +00:00
|
|
|
return indexes;
|
|
|
|
}
|
|
|
|
|
2015-07-02 23:47:16 +00:00
|
|
|
void EventSubscriberPlugin::expireRecords(const std::string& list_type,
|
|
|
|
const std::string& index,
|
|
|
|
bool all) {
|
|
|
|
auto db = DBHandle::getInstance();
|
|
|
|
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});
|
|
|
|
for (const auto& record : expired_records) {
|
|
|
|
if (all) {
|
|
|
|
db->Delete(kEvents, data_key + "." + record.first);
|
|
|
|
} else if (record.second > expire_time_) {
|
|
|
|
persisting_records.push_back(record.first + ":" +
|
|
|
|
std::to_string(record.second));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Either drop or overwrite the record list.
|
|
|
|
if (all) {
|
|
|
|
db->Delete(kEvents, record_key + "." + list_type + "." + index);
|
|
|
|
} else {
|
|
|
|
auto new_records = boost::algorithm::join(persisting_records, ",");
|
|
|
|
db->Put(kEvents, record_key + "." + list_type + "." + index, new_records);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void EventSubscriberPlugin::expireIndexes(
|
2014-12-06 09:18:29 +00:00
|
|
|
const std::string& list_type,
|
|
|
|
const std::vector<std::string>& indexes,
|
|
|
|
const std::vector<std::string>& expirations) {
|
2014-09-25 17:17:32 +00:00
|
|
|
auto db = DBHandle::getInstance();
|
2014-12-06 09:18:29 +00:00
|
|
|
auto index_key = "indexes." + dbNamespace();
|
2014-09-25 17:17:32 +00:00
|
|
|
|
2015-06-22 07:54:19 +00:00
|
|
|
// Construct a mutable list of persisting indexes to rewrite as records.
|
|
|
|
std::vector<std::string> persisting_indexes = indexes;
|
2014-12-06 09:18:29 +00:00
|
|
|
// Remove the records using the list of expired indexes.
|
|
|
|
for (const auto& bin : expirations) {
|
2015-07-02 23:47:16 +00:00
|
|
|
expireRecords(list_type, bin, true);
|
2014-12-06 09:18:29 +00:00
|
|
|
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, ",");
|
|
|
|
db->Put(kEvents, index_key + "." + list_type, new_indexes);
|
|
|
|
}
|
|
|
|
|
2015-01-30 18:44:25 +00:00
|
|
|
std::vector<EventRecord> EventSubscriberPlugin::getRecords(
|
2015-07-06 07:04:37 +00:00
|
|
|
const std::set<std::string>& indexes) {
|
2014-12-06 09:18:29 +00:00
|
|
|
auto db = DBHandle::getInstance();
|
|
|
|
auto record_key = "records." + dbNamespace();
|
2014-09-25 17:17:32 +00:00
|
|
|
|
2015-07-02 23:47:16 +00:00
|
|
|
std::vector<EventRecord> records;
|
2014-12-04 18:56:31 +00:00
|
|
|
for (const auto& index : indexes) {
|
2015-08-16 03:43:53 +00:00
|
|
|
std::vector<std::string> bin_records;
|
|
|
|
{
|
|
|
|
std::string record_value;
|
|
|
|
if (!db->Get(kEvents, record_key + "." + index, record_value).ok()) {
|
|
|
|
return records;
|
|
|
|
}
|
2014-09-25 17:17:32 +00:00
|
|
|
|
2015-08-16 03:43:53 +00:00
|
|
|
if (record_value.length() == 0) {
|
|
|
|
// There are actually no events in this bin, interesting error case.
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Each list is tokenized into a record=event_id:time.
|
|
|
|
boost::split(bin_records, record_value, boost::is_any_of(",:"));
|
2014-09-25 17:17:32 +00:00
|
|
|
}
|
2014-12-04 18:56:31 +00:00
|
|
|
|
2014-09-25 17:17:32 +00:00
|
|
|
auto bin_it = bin_records.begin();
|
2015-08-16 03:43:53 +00:00
|
|
|
// Iterate over every 2 items: EID:TIME.
|
2014-09-25 17:17:32 +00:00
|
|
|
for (; bin_it != bin_records.end(); bin_it++) {
|
2015-08-16 03:43:53 +00:00
|
|
|
const auto& eid = *bin_it;
|
|
|
|
EventTime time = timeFromRecord(*(++bin_it));
|
2014-09-25 17:17:32 +00:00
|
|
|
records.push_back(std::make_pair(eid, time));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-08-16 03:43:53 +00:00
|
|
|
return std::move(records);
|
2014-09-25 17:17:32 +00:00
|
|
|
}
|
|
|
|
|
2015-01-30 18:44:25 +00:00
|
|
|
Status EventSubscriberPlugin::recordEvent(EventID& eid, EventTime time) {
|
2014-09-19 08:54:33 +00:00
|
|
|
Status status;
|
|
|
|
auto db = DBHandle::getInstance();
|
2014-09-25 17:17:32 +00:00
|
|
|
std::string time_value = boost::lexical_cast<std::string>(time);
|
2014-09-19 08:54:33 +00:00
|
|
|
|
|
|
|
// The record is identified by the event type then module name.
|
2014-09-25 17:17:32 +00:00
|
|
|
std::string index_key = "indexes." + dbNamespace();
|
|
|
|
std::string record_key = "records." + dbNamespace();
|
2014-09-19 08:54:33 +00:00
|
|
|
// The list key includes the list type (bin size) and the list ID (bin).
|
|
|
|
std::string list_key;
|
|
|
|
std::string list_id;
|
|
|
|
|
|
|
|
for (auto time_list : kEventTimeLists) {
|
|
|
|
// 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.
|
2014-09-25 17:17:32 +00:00
|
|
|
list_id = boost::lexical_cast<std::string>(time / time_list);
|
2014-09-19 08:54:33 +00:00
|
|
|
// The list name identifies the 'type' of list.
|
|
|
|
list_key = boost::lexical_cast<std::string>(time_list);
|
2014-09-25 17:17:32 +00:00
|
|
|
// list_key = list_key + "." + list_id;
|
2014-09-19 08:54:33 +00:00
|
|
|
|
|
|
|
{
|
|
|
|
boost::lock_guard<boost::mutex> lock(event_record_lock_);
|
|
|
|
// Append the record (eid, unix_time) to the list bin.
|
2014-09-25 17:17:32 +00:00
|
|
|
std::string record_value;
|
|
|
|
status = db->Get(
|
|
|
|
kEvents, record_key + "." + list_key + "." + list_id, record_value);
|
2014-09-19 08:54:33 +00:00
|
|
|
|
|
|
|
if (record_value.length() == 0) {
|
2014-09-25 17:17:32 +00:00
|
|
|
// This is a new list_id for list_key, append the ID to the indirect
|
|
|
|
// lookup for this list_key.
|
|
|
|
std::string index_value;
|
|
|
|
status = db->Get(kEvents, index_key + "." + list_key, index_value);
|
|
|
|
if (index_value.length() == 0) {
|
|
|
|
// A new index.
|
|
|
|
index_value = list_id;
|
|
|
|
} else {
|
|
|
|
index_value += "," + list_id;
|
|
|
|
}
|
|
|
|
status = db->Put(kEvents, index_key + "." + list_key, index_value);
|
2014-09-19 08:54:33 +00:00
|
|
|
record_value = eid + ":" + time_value;
|
|
|
|
} else {
|
|
|
|
// Tokenize a record using ',' and the EID/time using ':'.
|
|
|
|
record_value += "," + eid + ":" + time_value;
|
|
|
|
}
|
2014-09-25 17:17:32 +00:00
|
|
|
status = db->Put(
|
|
|
|
kEvents, record_key + "." + list_key + "." + list_id, record_value);
|
2014-09-19 08:54:33 +00:00
|
|
|
if (!status.ok()) {
|
2014-09-25 17:17:32 +00:00
|
|
|
LOG(ERROR) << "Could not put Event Record key: " << record_key << "."
|
|
|
|
<< list_key << "." << list_id;
|
2014-09-19 08:54:33 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return Status(0, "OK");
|
|
|
|
}
|
|
|
|
|
2015-01-30 18:44:25 +00:00
|
|
|
EventID EventSubscriberPlugin::getEventID() {
|
2014-09-19 08:54:33 +00:00
|
|
|
Status status;
|
|
|
|
auto db = DBHandle::getInstance();
|
|
|
|
// First get an event ID from the meta key.
|
2014-09-25 17:17:32 +00:00
|
|
|
std::string eid_key = "eid." + dbNamespace();
|
2014-09-19 08:54:33 +00:00
|
|
|
std::string last_eid_value;
|
|
|
|
std::string eid_value;
|
|
|
|
|
|
|
|
{
|
|
|
|
boost::lock_guard<boost::mutex> lock(event_id_lock_);
|
|
|
|
status = db->Get(kEvents, eid_key, last_eid_value);
|
2015-09-28 08:50:32 +00:00
|
|
|
if (!status.ok() || last_eid_value.empty()) {
|
2014-09-19 08:54:33 +00:00
|
|
|
last_eid_value = "0";
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t eid = boost::lexical_cast<size_t>(last_eid_value) + 1;
|
|
|
|
eid_value = boost::lexical_cast<std::string>(eid);
|
|
|
|
status = db->Put(kEvents, eid_key, eid_value);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!status.ok()) {
|
|
|
|
return "0";
|
|
|
|
}
|
|
|
|
|
|
|
|
return eid_value;
|
|
|
|
}
|
|
|
|
|
2015-01-30 18:44:25 +00:00
|
|
|
QueryData EventSubscriberPlugin::get(EventTime start, EventTime stop) {
|
2014-09-25 17:17:32 +00:00
|
|
|
QueryData results;
|
|
|
|
Status status;
|
2015-01-02 05:55:10 +00:00
|
|
|
|
2015-08-16 03:43:53 +00:00
|
|
|
std::shared_ptr<DBHandle> db = nullptr;
|
2015-01-02 05:55:10 +00:00
|
|
|
try {
|
|
|
|
db = DBHandle::getInstance();
|
|
|
|
} catch (const std::runtime_error& e) {
|
|
|
|
LOG(ERROR) << "Cannot retrieve subscriber results database is locked";
|
|
|
|
return results;
|
|
|
|
}
|
2014-09-25 17:17:32 +00:00
|
|
|
|
|
|
|
// Get the records for this time range.
|
2014-12-06 09:18:29 +00:00
|
|
|
auto indexes = getIndexes(start, stop);
|
|
|
|
auto records = getRecords(indexes);
|
2015-08-16 03:43:53 +00:00
|
|
|
std::string events_key = "data." + dbNamespace();
|
2014-12-06 09:18:29 +00:00
|
|
|
|
2015-08-16 03:43:53 +00:00
|
|
|
std::vector<std::string> mapped_records;
|
2014-12-06 09:18:29 +00:00
|
|
|
for (const auto& record : records) {
|
|
|
|
if (record.second >= start && (record.second <= stop || stop == 0)) {
|
2015-08-16 03:43:53 +00:00
|
|
|
mapped_records.push_back(events_key + "." + record.first);
|
2014-12-06 09:18:29 +00:00
|
|
|
}
|
|
|
|
}
|
2014-09-25 17:17:32 +00:00
|
|
|
|
2014-12-06 09:18:29 +00:00
|
|
|
// Select mapped_records using event_ids as keys.
|
2014-09-25 17:17:32 +00:00
|
|
|
std::string data_value;
|
2014-12-06 09:18:29 +00:00
|
|
|
for (const auto& record : mapped_records) {
|
2014-09-25 17:17:32 +00:00
|
|
|
Row r;
|
2015-08-16 03:43:53 +00:00
|
|
|
status = db->Get(kEvents, record, data_value);
|
2014-09-25 17:17:32 +00:00
|
|
|
if (data_value.length() == 0) {
|
2015-08-16 03:43:53 +00:00
|
|
|
// There is no record here, interesting error case.
|
2014-09-25 17:17:32 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
status = deserializeRowJSON(data_value, r);
|
2015-08-16 03:43:53 +00:00
|
|
|
data_value.clear();
|
2014-09-25 17:17:32 +00:00
|
|
|
if (status.ok()) {
|
2015-08-16 03:43:53 +00:00
|
|
|
results.push_back(std::move(r));
|
2014-09-25 17:17:32 +00:00
|
|
|
}
|
|
|
|
}
|
2015-08-16 03:43:53 +00:00
|
|
|
|
|
|
|
if (FLAGS_events_expiry > 0) {
|
|
|
|
// Set the expire time to NOW - "configured lifetime".
|
|
|
|
// Index retrieval will apply the constraints checking and auto-expire.
|
|
|
|
expire_time_ = getUnixTime() - FLAGS_events_expiry;
|
|
|
|
}
|
|
|
|
return std::move(results);
|
2014-09-25 17:17:32 +00:00
|
|
|
}
|
|
|
|
|
2015-07-06 07:04:37 +00:00
|
|
|
Status EventSubscriberPlugin::add(Row& r, EventTime event_time) {
|
|
|
|
std::shared_ptr<DBHandle> db = nullptr;
|
2015-01-02 05:55:10 +00:00
|
|
|
try {
|
|
|
|
db = DBHandle::getInstance();
|
|
|
|
} catch (const std::runtime_error& e) {
|
|
|
|
return Status(1, e.what());
|
|
|
|
}
|
2014-09-19 08:54:33 +00:00
|
|
|
|
|
|
|
// Get and increment the EID for this module.
|
|
|
|
EventID eid = getEventID();
|
2015-07-06 07:04:37 +00:00
|
|
|
// Without encouraging a missing event time, do not support a 0-time.
|
2015-07-26 08:48:27 +00:00
|
|
|
r["time"] = std::to_string((event_time == 0) ? getUnixTime() : event_time);
|
2014-09-19 08:54:33 +00:00
|
|
|
|
2015-07-06 07:04:37 +00:00
|
|
|
// Serialize and store the row data, for query-time retrieval.
|
2014-09-19 08:54:33 +00:00
|
|
|
std::string data;
|
2015-07-06 07:04:37 +00:00
|
|
|
auto status = serializeRowJSON(r, data);
|
2014-09-19 08:54:33 +00:00
|
|
|
if (!status.ok()) {
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Store the event data.
|
2015-07-06 07:04:37 +00:00
|
|
|
std::string event_key = "data." + dbNamespace() + "." + eid;
|
2014-09-19 08:54:33 +00:00
|
|
|
status = db->Put(kEvents, event_key, data);
|
2015-07-06 07:04:37 +00:00
|
|
|
// Record the event in the indexing bins, using the index time.
|
|
|
|
recordEvent(eid, event_time);
|
2014-09-19 08:54:33 +00:00
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
2014-09-23 01:35:12 +00:00
|
|
|
void EventFactory::delay() {
|
2015-03-24 22:48:53 +00:00
|
|
|
// Caller may disable event publisher threads.
|
|
|
|
if (FLAGS_disable_events) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create a thread for each event publisher.
|
2014-09-30 02:06:33 +00:00
|
|
|
auto& ef = EventFactory::getInstance();
|
2015-01-06 03:07:08 +00:00
|
|
|
for (const auto& publisher : EventFactory::getInstance().event_pubs_) {
|
2015-09-03 19:22:42 +00:00
|
|
|
// Publishers that did not set up correctly are put into an ending state.
|
|
|
|
if (!publisher.second->isEnding()) {
|
|
|
|
auto thread_ = std::make_shared<boost::thread>(
|
|
|
|
boost::bind(&EventFactory::run, publisher.first));
|
|
|
|
ef.threads_.push_back(thread_);
|
|
|
|
}
|
2014-09-23 01:35:12 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-12-15 18:17:56 +00:00
|
|
|
Status EventFactory::run(EventPublisherID& type_id) {
|
2015-05-07 03:02:23 +00:00
|
|
|
auto& ef = EventFactory::getInstance();
|
2015-07-06 07:04:37 +00:00
|
|
|
if (FLAGS_disable_events) {
|
|
|
|
return Status(0, "Events disabled");
|
|
|
|
}
|
2015-05-07 03:02:23 +00:00
|
|
|
|
2014-09-19 08:54:33 +00:00
|
|
|
// An interesting take on an event dispatched entrypoint.
|
2014-09-22 21:35:07 +00:00
|
|
|
// There is little introspection into the event type.
|
2014-09-19 08:54:33 +00:00
|
|
|
// Assume it can either make use of an entrypoint poller/selector or
|
|
|
|
// take care of async callback registrations in setUp/configure/run
|
2015-07-06 07:04:37 +00:00
|
|
|
// only once and handle event queuing/firing in callbacks.
|
2015-09-03 19:22:42 +00:00
|
|
|
EventPublisherRef publisher = ef.getEventPublisher(type_id);
|
2014-09-19 08:54:33 +00:00
|
|
|
|
2015-05-07 03:02:23 +00:00
|
|
|
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;
|
2015-02-01 08:35:44 +00:00
|
|
|
publisher->hasStarted(true);
|
|
|
|
|
|
|
|
auto status = Status(0, "OK");
|
|
|
|
while (!publisher->isEnding() && status.ok()) {
|
2014-09-23 01:35:12 +00:00
|
|
|
// Can optionally implement a global cooloff latency here.
|
2015-02-01 08:35:44 +00:00
|
|
|
status = publisher->run();
|
2015-09-03 19:22:42 +00:00
|
|
|
publisher->restart_count_++;
|
2015-03-21 17:14:57 +00:00
|
|
|
osquery::publisherSleep(EVENTS_COOLOFF);
|
2014-09-19 08:54:33 +00:00
|
|
|
}
|
|
|
|
// The runloop status is not reflective of the event type's.
|
2015-03-02 23:43:31 +00:00
|
|
|
VLOG(1) << "Event publisher " << publisher->type()
|
2015-05-07 03:02:23 +00:00
|
|
|
<< " run loop terminated for reason: " << status.getMessage();
|
|
|
|
// Publishers auto tear down when their run loop stops.
|
|
|
|
publisher->tearDown();
|
2015-09-03 19:22:42 +00:00
|
|
|
|
|
|
|
// 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.
|
|
|
|
// ef.event_pubs_.erase(type_id);
|
2014-09-19 08:54:33 +00:00
|
|
|
return Status(0, "OK");
|
|
|
|
}
|
|
|
|
|
|
|
|
// There's no reason for the event factory to keep multiple instances.
|
2014-09-30 02:06:33 +00:00
|
|
|
EventFactory& EventFactory::getInstance() {
|
|
|
|
static EventFactory ef;
|
|
|
|
return ef;
|
2014-09-19 08:54:33 +00:00
|
|
|
}
|
|
|
|
|
2015-02-19 07:51:41 +00:00
|
|
|
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(0, "Invalid subscriber");
|
|
|
|
}
|
2014-09-19 08:54:33 +00:00
|
|
|
|
2015-02-19 07:51:41 +00:00
|
|
|
auto& ef = EventFactory::getInstance();
|
|
|
|
auto type_id = specialized_pub->type();
|
2015-01-30 18:44:25 +00:00
|
|
|
if (ef.event_pubs_.count(type_id) != 0) {
|
|
|
|
// This is a duplicate event publisher.
|
2015-02-19 07:51:41 +00:00
|
|
|
return Status(1, "Duplicate publisher type");
|
2014-09-19 08:54:33 +00:00
|
|
|
}
|
|
|
|
|
2015-07-06 07:04:37 +00:00
|
|
|
// Do not set up event publisher if events are disabled.
|
2015-09-03 19:22:42 +00:00
|
|
|
ef.event_pubs_[type_id] = specialized_pub;
|
2015-07-06 07:04:37 +00:00
|
|
|
if (!FLAGS_disable_events) {
|
2015-08-16 03:43:53 +00:00
|
|
|
auto status = specialized_pub->setUp();
|
|
|
|
if (!status.ok()) {
|
2015-07-06 07:04:37 +00:00
|
|
|
// Only start event loop if setUp succeeds.
|
2015-08-16 03:43:53 +00:00
|
|
|
LOG(INFO) << "Event publisher failed setup: " << type_id << ": "
|
|
|
|
<< status.what();
|
2015-09-03 19:22:42 +00:00
|
|
|
specialized_pub->isEnding(true);
|
2015-08-16 03:43:53 +00:00
|
|
|
return status;
|
2015-07-06 07:04:37 +00:00
|
|
|
}
|
2014-12-08 10:22:59 +00:00
|
|
|
}
|
|
|
|
|
2014-09-19 08:54:33 +00:00
|
|
|
return Status(0, "OK");
|
|
|
|
}
|
|
|
|
|
2015-02-19 07:51:41 +00:00
|
|
|
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) {
|
Implement YARA table.
Currently only for OS X, will port to others soon.
Also need to add tests.
Remove old comment and add loading message.
Implement YARA table for Linux.
Use mask properly.
Use the various masks to specify the kinds of events we are interested
in. This removes the need to do the dirty "DELETED" check when the event
fires.
Make getYARAFiles return a const map.
Switch to LOG(WARNING) and emit error number.
Add vim .swp files to .gitignore.
Add yara_utils.(c|h).
Start to condense common code between the Linux and Darwin YARA tables
into a yara_utils.h. Right now it includes a function to compile rules
and store the results back in the map, indexed by category. It also has
the callback used by YARA when a rule is processed. I can not move much
more than that for the row creation code because the structures used in
the event callback are slightly different.
Include a better error message.
The errors are still printed by the compiler callback, but this will
allow my future work to return a Status from the event initialization to
print a useful message in summary.
Make Subscriber init() return Status.
Each EventSubscriber::init() now returns a Status. If the init() fails
for any reason the EventSubscriber is still stored but the failure is
tracked.
EventSubscribers now have a state member, which represents the current
state of the subscriber. The current supported states are:
uninitialized, running, paused, failed. Currently the only meaningful
ones are running and failed, but I put paused in there as a
forward-looking feature.
Subscriptions now have a subscriber_name member. This is used in
EventPublisherPlugin::fire() as a lookup to get the EventSubscriber and
check the state. If the EventSubscriber is not running the event will
not fire.
Only the EventSubscribers on OS X are using this. I'll do the Linux
implementation next.
Chase the init() changes to Linux.
This brings the Linux YARA table in line with the OS X one.
Require a EventSubscriberID when creating a subscription.
Now that Subscriptions are "tied" to EventSubscribers you must create a
Subscription with the name of the Subscriber it is for. This is because
when the event fires the list of Subscriptions is walked and the name is
used to lookup the EventSubscriber and make sure it is in the running
state.
Fix various tests.
Some tests would fire an event with only a Subscription, which is no
longer a valid thing to do. For these tests an EventSubscription is
created and registered in the EventFactory.
When Subscriptions are created pass the name of the EventSubscriber to
them. In some cases where no event is ever fired it is fine to pass a
bogus name.
Fix inotify tests.
Move a test down so the class is defined and make sure to create an
EventSubscriber and use it properly.
Add support for yara to provision.sh.
Right now this grabs yara 3.3.0 and applies the patch to fix min() and max(),
which is commit fc4696c8b725be1ac099d340359c8d550d116041 in the yara repo.
This has been tested under Ubuntu 14.04 only.
Remove NOMINMAX.
This is no longer necessary after the patch was backported to 3.3.0.
Revert "Add support for yara to provision.sh."
This reverts commit a8bd371498c0979f070adeff23d05571882ac3f1.
Use vendored YARA code in third-party.
This switches to using the YARA code contained in third-party, including
the patch to fix min/max macros.
Fix mismerge.
Remove unused function after merge.
Well, soon to be unused as soon as I fix up the Linux YARA table. ;)
Chase config changes.
Make the Linux YARA table use ConfigDataInstance along with files() and
yaraFiles().
2015-03-10 13:22:16 +00:00
|
|
|
return Status(1, "Invalid subscriber");
|
2015-02-19 07:51:41 +00:00
|
|
|
}
|
|
|
|
|
2014-10-03 15:26:41 +00:00
|
|
|
// Let the module initialize any Subscriptions.
|
2015-07-06 07:04:37 +00:00
|
|
|
auto status = Status(0, "OK");
|
|
|
|
if (!FLAGS_disable_events) {
|
|
|
|
status = specialized_sub->init();
|
|
|
|
}
|
2015-02-19 07:51:41 +00:00
|
|
|
|
|
|
|
auto& ef = EventFactory::getInstance();
|
2015-04-01 04:18:56 +00:00
|
|
|
ef.event_subs_[specialized_sub->getName()] = specialized_sub;
|
Implement YARA table.
Currently only for OS X, will port to others soon.
Also need to add tests.
Remove old comment and add loading message.
Implement YARA table for Linux.
Use mask properly.
Use the various masks to specify the kinds of events we are interested
in. This removes the need to do the dirty "DELETED" check when the event
fires.
Make getYARAFiles return a const map.
Switch to LOG(WARNING) and emit error number.
Add vim .swp files to .gitignore.
Add yara_utils.(c|h).
Start to condense common code between the Linux and Darwin YARA tables
into a yara_utils.h. Right now it includes a function to compile rules
and store the results back in the map, indexed by category. It also has
the callback used by YARA when a rule is processed. I can not move much
more than that for the row creation code because the structures used in
the event callback are slightly different.
Include a better error message.
The errors are still printed by the compiler callback, but this will
allow my future work to return a Status from the event initialization to
print a useful message in summary.
Make Subscriber init() return Status.
Each EventSubscriber::init() now returns a Status. If the init() fails
for any reason the EventSubscriber is still stored but the failure is
tracked.
EventSubscribers now have a state member, which represents the current
state of the subscriber. The current supported states are:
uninitialized, running, paused, failed. Currently the only meaningful
ones are running and failed, but I put paused in there as a
forward-looking feature.
Subscriptions now have a subscriber_name member. This is used in
EventPublisherPlugin::fire() as a lookup to get the EventSubscriber and
check the state. If the EventSubscriber is not running the event will
not fire.
Only the EventSubscribers on OS X are using this. I'll do the Linux
implementation next.
Chase the init() changes to Linux.
This brings the Linux YARA table in line with the OS X one.
Require a EventSubscriberID when creating a subscription.
Now that Subscriptions are "tied" to EventSubscribers you must create a
Subscription with the name of the Subscriber it is for. This is because
when the event fires the list of Subscriptions is walked and the name is
used to lookup the EventSubscriber and make sure it is in the running
state.
Fix various tests.
Some tests would fire an event with only a Subscription, which is no
longer a valid thing to do. For these tests an EventSubscription is
created and registered in the EventFactory.
When Subscriptions are created pass the name of the EventSubscriber to
them. In some cases where no event is ever fired it is fine to pass a
bogus name.
Fix inotify tests.
Move a test down so the class is defined and make sure to create an
EventSubscriber and use it properly.
Add support for yara to provision.sh.
Right now this grabs yara 3.3.0 and applies the patch to fix min() and max(),
which is commit fc4696c8b725be1ac099d340359c8d550d116041 in the yara repo.
This has been tested under Ubuntu 14.04 only.
Remove NOMINMAX.
This is no longer necessary after the patch was backported to 3.3.0.
Revert "Add support for yara to provision.sh."
This reverts commit a8bd371498c0979f070adeff23d05571882ac3f1.
Use vendored YARA code in third-party.
This switches to using the YARA code contained in third-party, including
the patch to fix min/max macros.
Fix mismerge.
Remove unused function after merge.
Well, soon to be unused as soon as I fix up the Linux YARA table. ;)
Chase config changes.
Make the Linux YARA table use ConfigDataInstance along with files() and
yaraFiles().
2015-03-10 13:22:16 +00:00
|
|
|
|
|
|
|
// Set state of subscriber.
|
|
|
|
if (!status.ok()) {
|
2015-04-01 04:18:56 +00:00
|
|
|
specialized_sub->state(SUBSCRIBER_FAILED);
|
Implement YARA table.
Currently only for OS X, will port to others soon.
Also need to add tests.
Remove old comment and add loading message.
Implement YARA table for Linux.
Use mask properly.
Use the various masks to specify the kinds of events we are interested
in. This removes the need to do the dirty "DELETED" check when the event
fires.
Make getYARAFiles return a const map.
Switch to LOG(WARNING) and emit error number.
Add vim .swp files to .gitignore.
Add yara_utils.(c|h).
Start to condense common code between the Linux and Darwin YARA tables
into a yara_utils.h. Right now it includes a function to compile rules
and store the results back in the map, indexed by category. It also has
the callback used by YARA when a rule is processed. I can not move much
more than that for the row creation code because the structures used in
the event callback are slightly different.
Include a better error message.
The errors are still printed by the compiler callback, but this will
allow my future work to return a Status from the event initialization to
print a useful message in summary.
Make Subscriber init() return Status.
Each EventSubscriber::init() now returns a Status. If the init() fails
for any reason the EventSubscriber is still stored but the failure is
tracked.
EventSubscribers now have a state member, which represents the current
state of the subscriber. The current supported states are:
uninitialized, running, paused, failed. Currently the only meaningful
ones are running and failed, but I put paused in there as a
forward-looking feature.
Subscriptions now have a subscriber_name member. This is used in
EventPublisherPlugin::fire() as a lookup to get the EventSubscriber and
check the state. If the EventSubscriber is not running the event will
not fire.
Only the EventSubscribers on OS X are using this. I'll do the Linux
implementation next.
Chase the init() changes to Linux.
This brings the Linux YARA table in line with the OS X one.
Require a EventSubscriberID when creating a subscription.
Now that Subscriptions are "tied" to EventSubscribers you must create a
Subscription with the name of the Subscriber it is for. This is because
when the event fires the list of Subscriptions is walked and the name is
used to lookup the EventSubscriber and make sure it is in the running
state.
Fix various tests.
Some tests would fire an event with only a Subscription, which is no
longer a valid thing to do. For these tests an EventSubscription is
created and registered in the EventFactory.
When Subscriptions are created pass the name of the EventSubscriber to
them. In some cases where no event is ever fired it is fine to pass a
bogus name.
Fix inotify tests.
Move a test down so the class is defined and make sure to create an
EventSubscriber and use it properly.
Add support for yara to provision.sh.
Right now this grabs yara 3.3.0 and applies the patch to fix min() and max(),
which is commit fc4696c8b725be1ac099d340359c8d550d116041 in the yara repo.
This has been tested under Ubuntu 14.04 only.
Remove NOMINMAX.
This is no longer necessary after the patch was backported to 3.3.0.
Revert "Add support for yara to provision.sh."
This reverts commit a8bd371498c0979f070adeff23d05571882ac3f1.
Use vendored YARA code in third-party.
This switches to using the YARA code contained in third-party, including
the patch to fix min/max macros.
Fix mismerge.
Remove unused function after merge.
Well, soon to be unused as soon as I fix up the Linux YARA table. ;)
Chase config changes.
Make the Linux YARA table use ConfigDataInstance along with files() and
yaraFiles().
2015-03-10 13:22:16 +00:00
|
|
|
return Status(1, status.getMessage());
|
|
|
|
} else {
|
2015-04-01 04:18:56 +00:00
|
|
|
specialized_sub->state(SUBSCRIBER_RUNNING);
|
Implement YARA table.
Currently only for OS X, will port to others soon.
Also need to add tests.
Remove old comment and add loading message.
Implement YARA table for Linux.
Use mask properly.
Use the various masks to specify the kinds of events we are interested
in. This removes the need to do the dirty "DELETED" check when the event
fires.
Make getYARAFiles return a const map.
Switch to LOG(WARNING) and emit error number.
Add vim .swp files to .gitignore.
Add yara_utils.(c|h).
Start to condense common code between the Linux and Darwin YARA tables
into a yara_utils.h. Right now it includes a function to compile rules
and store the results back in the map, indexed by category. It also has
the callback used by YARA when a rule is processed. I can not move much
more than that for the row creation code because the structures used in
the event callback are slightly different.
Include a better error message.
The errors are still printed by the compiler callback, but this will
allow my future work to return a Status from the event initialization to
print a useful message in summary.
Make Subscriber init() return Status.
Each EventSubscriber::init() now returns a Status. If the init() fails
for any reason the EventSubscriber is still stored but the failure is
tracked.
EventSubscribers now have a state member, which represents the current
state of the subscriber. The current supported states are:
uninitialized, running, paused, failed. Currently the only meaningful
ones are running and failed, but I put paused in there as a
forward-looking feature.
Subscriptions now have a subscriber_name member. This is used in
EventPublisherPlugin::fire() as a lookup to get the EventSubscriber and
check the state. If the EventSubscriber is not running the event will
not fire.
Only the EventSubscribers on OS X are using this. I'll do the Linux
implementation next.
Chase the init() changes to Linux.
This brings the Linux YARA table in line with the OS X one.
Require a EventSubscriberID when creating a subscription.
Now that Subscriptions are "tied" to EventSubscribers you must create a
Subscription with the name of the Subscriber it is for. This is because
when the event fires the list of Subscriptions is walked and the name is
used to lookup the EventSubscriber and make sure it is in the running
state.
Fix various tests.
Some tests would fire an event with only a Subscription, which is no
longer a valid thing to do. For these tests an EventSubscription is
created and registered in the EventFactory.
When Subscriptions are created pass the name of the EventSubscriber to
them. In some cases where no event is ever fired it is fine to pass a
bogus name.
Fix inotify tests.
Move a test down so the class is defined and make sure to create an
EventSubscriber and use it properly.
Add support for yara to provision.sh.
Right now this grabs yara 3.3.0 and applies the patch to fix min() and max(),
which is commit fc4696c8b725be1ac099d340359c8d550d116041 in the yara repo.
This has been tested under Ubuntu 14.04 only.
Remove NOMINMAX.
This is no longer necessary after the patch was backported to 3.3.0.
Revert "Add support for yara to provision.sh."
This reverts commit a8bd371498c0979f070adeff23d05571882ac3f1.
Use vendored YARA code in third-party.
This switches to using the YARA code contained in third-party, including
the patch to fix min/max macros.
Fix mismerge.
Remove unused function after merge.
Well, soon to be unused as soon as I fix up the Linux YARA table. ;)
Chase config changes.
Make the Linux YARA table use ConfigDataInstance along with files() and
yaraFiles().
2015-03-10 13:22:16 +00:00
|
|
|
return Status(0, "OK");
|
|
|
|
}
|
2014-09-24 15:03:16 +00:00
|
|
|
}
|
|
|
|
|
2014-12-15 18:17:56 +00:00
|
|
|
Status EventFactory::addSubscription(EventPublisherID& type_id,
|
2015-04-12 02:50:35 +00:00
|
|
|
EventSubscriberID& name_id,
|
2014-12-15 18:17:56 +00:00
|
|
|
const SubscriptionContextRef& mc,
|
2015-02-18 02:04:15 +00:00
|
|
|
EventCallback cb,
|
|
|
|
void* user_data) {
|
Implement YARA table.
Currently only for OS X, will port to others soon.
Also need to add tests.
Remove old comment and add loading message.
Implement YARA table for Linux.
Use mask properly.
Use the various masks to specify the kinds of events we are interested
in. This removes the need to do the dirty "DELETED" check when the event
fires.
Make getYARAFiles return a const map.
Switch to LOG(WARNING) and emit error number.
Add vim .swp files to .gitignore.
Add yara_utils.(c|h).
Start to condense common code between the Linux and Darwin YARA tables
into a yara_utils.h. Right now it includes a function to compile rules
and store the results back in the map, indexed by category. It also has
the callback used by YARA when a rule is processed. I can not move much
more than that for the row creation code because the structures used in
the event callback are slightly different.
Include a better error message.
The errors are still printed by the compiler callback, but this will
allow my future work to return a Status from the event initialization to
print a useful message in summary.
Make Subscriber init() return Status.
Each EventSubscriber::init() now returns a Status. If the init() fails
for any reason the EventSubscriber is still stored but the failure is
tracked.
EventSubscribers now have a state member, which represents the current
state of the subscriber. The current supported states are:
uninitialized, running, paused, failed. Currently the only meaningful
ones are running and failed, but I put paused in there as a
forward-looking feature.
Subscriptions now have a subscriber_name member. This is used in
EventPublisherPlugin::fire() as a lookup to get the EventSubscriber and
check the state. If the EventSubscriber is not running the event will
not fire.
Only the EventSubscribers on OS X are using this. I'll do the Linux
implementation next.
Chase the init() changes to Linux.
This brings the Linux YARA table in line with the OS X one.
Require a EventSubscriberID when creating a subscription.
Now that Subscriptions are "tied" to EventSubscribers you must create a
Subscription with the name of the Subscriber it is for. This is because
when the event fires the list of Subscriptions is walked and the name is
used to lookup the EventSubscriber and make sure it is in the running
state.
Fix various tests.
Some tests would fire an event with only a Subscription, which is no
longer a valid thing to do. For these tests an EventSubscription is
created and registered in the EventFactory.
When Subscriptions are created pass the name of the EventSubscriber to
them. In some cases where no event is ever fired it is fine to pass a
bogus name.
Fix inotify tests.
Move a test down so the class is defined and make sure to create an
EventSubscriber and use it properly.
Add support for yara to provision.sh.
Right now this grabs yara 3.3.0 and applies the patch to fix min() and max(),
which is commit fc4696c8b725be1ac099d340359c8d550d116041 in the yara repo.
This has been tested under Ubuntu 14.04 only.
Remove NOMINMAX.
This is no longer necessary after the patch was backported to 3.3.0.
Revert "Add support for yara to provision.sh."
This reverts commit a8bd371498c0979f070adeff23d05571882ac3f1.
Use vendored YARA code in third-party.
This switches to using the YARA code contained in third-party, including
the patch to fix min/max macros.
Fix mismerge.
Remove unused function after merge.
Well, soon to be unused as soon as I fix up the Linux YARA table. ;)
Chase config changes.
Make the Linux YARA table use ConfigDataInstance along with files() and
yaraFiles().
2015-03-10 13:22:16 +00:00
|
|
|
auto subscription = Subscription::create(name_id, mc, cb, user_data);
|
2014-12-15 05:20:20 +00:00
|
|
|
return EventFactory::addSubscription(type_id, subscription);
|
|
|
|
}
|
|
|
|
|
2014-12-15 18:17:56 +00:00
|
|
|
Status EventFactory::addSubscription(EventPublisherID& type_id,
|
|
|
|
const SubscriptionRef& subscription) {
|
2015-04-26 10:54:27 +00:00
|
|
|
EventPublisherRef publisher = getInstance().getEventPublisher(type_id);
|
|
|
|
if (publisher == nullptr) {
|
|
|
|
return Status(1, "Unknown event publisher");
|
2014-09-19 08:54:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// The event factory is responsible for configuring the event types.
|
2015-02-01 08:35:44 +00:00
|
|
|
auto status = publisher->addSubscription(subscription);
|
2015-07-06 07:04:37 +00:00
|
|
|
if (!FLAGS_disable_events) {
|
|
|
|
publisher->configure();
|
|
|
|
}
|
2014-09-19 08:54:33 +00:00
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
2014-12-15 18:17:56 +00:00
|
|
|
size_t EventFactory::numSubscriptions(EventPublisherID& type_id) {
|
2015-02-01 08:35:44 +00:00
|
|
|
EventPublisherRef publisher;
|
|
|
|
try {
|
|
|
|
publisher = EventFactory::getInstance().getEventPublisher(type_id);
|
2015-03-03 00:48:01 +00:00
|
|
|
} catch (std::out_of_range& e) {
|
2015-02-01 08:35:44 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
return publisher->numSubscriptions();
|
2014-09-19 08:54:33 +00:00
|
|
|
}
|
|
|
|
|
2014-12-15 18:17:56 +00:00
|
|
|
EventPublisherRef EventFactory::getEventPublisher(EventPublisherID& type_id) {
|
2015-01-30 18:44:25 +00:00
|
|
|
if (getInstance().event_pubs_.count(type_id) == 0) {
|
2015-08-16 03:43:53 +00:00
|
|
|
LOG(ERROR) << "Requested unknown/failed event publisher: " + type_id;
|
2015-04-26 10:54:27 +00:00
|
|
|
return nullptr;
|
2014-09-19 08:54:33 +00:00
|
|
|
}
|
2015-01-30 18:44:25 +00:00
|
|
|
return getInstance().event_pubs_.at(type_id);
|
2014-09-19 08:54:33 +00:00
|
|
|
}
|
|
|
|
|
2014-12-15 18:17:56 +00:00
|
|
|
EventSubscriberRef EventFactory::getEventSubscriber(
|
|
|
|
EventSubscriberID& name_id) {
|
2015-04-26 10:54:27 +00:00
|
|
|
if (!exists(name_id)) {
|
2015-01-30 18:44:25 +00:00
|
|
|
LOG(ERROR) << "Requested unknown event subscriber: " + name_id;
|
2015-04-26 10:54:27 +00:00
|
|
|
return nullptr;
|
2014-12-15 08:25:28 +00:00
|
|
|
}
|
2015-01-30 18:44:25 +00:00
|
|
|
return getInstance().event_subs_.at(name_id);
|
2014-12-15 08:25:28 +00:00
|
|
|
}
|
|
|
|
|
2015-04-26 10:54:27 +00:00
|
|
|
bool EventFactory::exists(EventSubscriberID& name_id) {
|
|
|
|
return (getInstance().event_subs_.count(name_id) > 0);
|
|
|
|
}
|
|
|
|
|
2014-12-15 18:17:56 +00:00
|
|
|
Status EventFactory::deregisterEventPublisher(const EventPublisherRef& pub) {
|
|
|
|
return EventFactory::deregisterEventPublisher(pub->type());
|
2014-09-19 08:54:33 +00:00
|
|
|
}
|
|
|
|
|
2014-12-15 18:17:56 +00:00
|
|
|
Status EventFactory::deregisterEventPublisher(EventPublisherID& type_id) {
|
2015-02-01 08:35:44 +00:00
|
|
|
auto& ef = EventFactory::getInstance();
|
2015-09-03 19:22:42 +00:00
|
|
|
EventPublisherRef publisher = ef.getEventPublisher(type_id);
|
|
|
|
if (publisher == nullptr) {
|
2015-02-19 07:51:41 +00:00
|
|
|
return Status(1, "No event publisher to deregister");
|
2015-01-30 18:44:25 +00:00
|
|
|
}
|
|
|
|
|
2015-07-06 07:04:37 +00:00
|
|
|
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();
|
|
|
|
// If the run loop did run the tear down and erase will happen in the
|
2015-09-03 19:22:42 +00:00
|
|
|
// event thread wrapper when isEnding is next checked.
|
2015-07-06 07:04:37 +00:00
|
|
|
ef.event_pubs_.erase(type_id);
|
|
|
|
} else {
|
|
|
|
publisher->end();
|
|
|
|
}
|
2014-09-19 08:54:33 +00:00
|
|
|
}
|
|
|
|
return Status(0, "OK");
|
|
|
|
}
|
|
|
|
|
2015-02-01 08:35:44 +00:00
|
|
|
std::vector<std::string> EventFactory::publisherTypes() {
|
|
|
|
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() {
|
|
|
|
std::vector<std::string> names;
|
|
|
|
for (const auto& subscriber : getInstance().event_subs_) {
|
|
|
|
names.push_back(subscriber.first);
|
|
|
|
}
|
|
|
|
return names;
|
|
|
|
}
|
|
|
|
|
|
|
|
void EventFactory::end(bool join) {
|
2014-09-30 02:06:33 +00:00
|
|
|
auto& ef = EventFactory::getInstance();
|
2015-02-01 08:35:44 +00:00
|
|
|
|
|
|
|
// Call deregister on each publisher.
|
|
|
|
for (const auto& publisher : ef.publisherTypes()) {
|
|
|
|
deregisterEventPublisher(publisher);
|
2014-09-19 08:54:33 +00:00
|
|
|
}
|
|
|
|
|
2015-02-01 08:35:44 +00:00
|
|
|
// Stop handling exceptions for the publisher threads.
|
|
|
|
for (const auto& thread : ef.threads_) {
|
|
|
|
if (join) {
|
|
|
|
thread->join();
|
|
|
|
} else {
|
|
|
|
thread->detach();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-05-07 03:02:23 +00:00
|
|
|
// A small cool off helps OS API event publisher flushing.
|
2015-07-06 07:04:37 +00:00
|
|
|
if (!FLAGS_disable_events) {
|
|
|
|
::usleep(400);
|
|
|
|
ef.threads_.clear();
|
|
|
|
}
|
2015-09-03 19:22:42 +00:00
|
|
|
|
|
|
|
// Threads may still be executing, when they finish, release publishers.
|
|
|
|
ef.event_pubs_.clear();
|
2014-09-19 08:54:33 +00:00
|
|
|
}
|
2014-09-24 18:25:05 +00:00
|
|
|
|
2015-01-30 18:44:25 +00:00
|
|
|
void attachEvents() {
|
2015-01-31 08:25:51 +00:00
|
|
|
const auto& publishers = Registry::all("event_publisher");
|
2015-01-30 18:44:25 +00:00
|
|
|
for (const auto& publisher : publishers) {
|
2015-02-19 07:51:41 +00:00
|
|
|
EventFactory::registerEventPublisher(publisher.second);
|
2014-09-24 18:25:05 +00:00
|
|
|
}
|
|
|
|
|
2015-01-31 08:25:51 +00:00
|
|
|
const auto& subscribers = Registry::all("event_subscriber");
|
2015-01-30 18:44:25 +00:00
|
|
|
for (const auto& subscriber : subscribers) {
|
Implement YARA table.
Currently only for OS X, will port to others soon.
Also need to add tests.
Remove old comment and add loading message.
Implement YARA table for Linux.
Use mask properly.
Use the various masks to specify the kinds of events we are interested
in. This removes the need to do the dirty "DELETED" check when the event
fires.
Make getYARAFiles return a const map.
Switch to LOG(WARNING) and emit error number.
Add vim .swp files to .gitignore.
Add yara_utils.(c|h).
Start to condense common code between the Linux and Darwin YARA tables
into a yara_utils.h. Right now it includes a function to compile rules
and store the results back in the map, indexed by category. It also has
the callback used by YARA when a rule is processed. I can not move much
more than that for the row creation code because the structures used in
the event callback are slightly different.
Include a better error message.
The errors are still printed by the compiler callback, but this will
allow my future work to return a Status from the event initialization to
print a useful message in summary.
Make Subscriber init() return Status.
Each EventSubscriber::init() now returns a Status. If the init() fails
for any reason the EventSubscriber is still stored but the failure is
tracked.
EventSubscribers now have a state member, which represents the current
state of the subscriber. The current supported states are:
uninitialized, running, paused, failed. Currently the only meaningful
ones are running and failed, but I put paused in there as a
forward-looking feature.
Subscriptions now have a subscriber_name member. This is used in
EventPublisherPlugin::fire() as a lookup to get the EventSubscriber and
check the state. If the EventSubscriber is not running the event will
not fire.
Only the EventSubscribers on OS X are using this. I'll do the Linux
implementation next.
Chase the init() changes to Linux.
This brings the Linux YARA table in line with the OS X one.
Require a EventSubscriberID when creating a subscription.
Now that Subscriptions are "tied" to EventSubscribers you must create a
Subscription with the name of the Subscriber it is for. This is because
when the event fires the list of Subscriptions is walked and the name is
used to lookup the EventSubscriber and make sure it is in the running
state.
Fix various tests.
Some tests would fire an event with only a Subscription, which is no
longer a valid thing to do. For these tests an EventSubscription is
created and registered in the EventFactory.
When Subscriptions are created pass the name of the EventSubscriber to
them. In some cases where no event is ever fired it is fine to pass a
bogus name.
Fix inotify tests.
Move a test down so the class is defined and make sure to create an
EventSubscriber and use it properly.
Add support for yara to provision.sh.
Right now this grabs yara 3.3.0 and applies the patch to fix min() and max(),
which is commit fc4696c8b725be1ac099d340359c8d550d116041 in the yara repo.
This has been tested under Ubuntu 14.04 only.
Remove NOMINMAX.
This is no longer necessary after the patch was backported to 3.3.0.
Revert "Add support for yara to provision.sh."
This reverts commit a8bd371498c0979f070adeff23d05571882ac3f1.
Use vendored YARA code in third-party.
This switches to using the YARA code contained in third-party, including
the patch to fix min/max macros.
Fix mismerge.
Remove unused function after merge.
Well, soon to be unused as soon as I fix up the Linux YARA table. ;)
Chase config changes.
Make the Linux YARA table use ConfigDataInstance along with files() and
yaraFiles().
2015-03-10 13:22:16 +00:00
|
|
|
auto status = EventFactory::registerEventSubscriber(subscriber.second);
|
|
|
|
if (!status.ok()) {
|
|
|
|
LOG(ERROR) << "Error registering subscriber: " << status.getMessage();
|
|
|
|
}
|
2014-09-24 18:25:05 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|