osquery-1/osquery/registry/registry_interface.cpp

394 lines
10 KiB
C++
Raw Normal View History

/**
* Copyright (c) 2018-present, Facebook, Inc.
2015-01-30 18:44:25 +00:00
* 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.
2015-01-30 18:44:25 +00:00
*/
#include <osquery/extensions.h>
#include <osquery/registry_factory.h>
#include <osquery/registry_interface.h>
#include <osquery/utils/conversions/split.h>
[fix #1390] query pack re-org This commit contains the features specified in #1390 as well as a refactoring of the general osquery configuration code. The API for the config plugins hasn't changed, although now there's a `genPack` method that config plugins can implement. If a plugin doesn't implement `genPack`, then the map<string, string> format cannot be used. The default config plugin, the filesystem plugin, now implements `genPack`, so existing query packs code will continue to work as it always has. Now many other config plugins can implement custom pack handling for what makes sense in their context. `genPacks` is not a pure virtual, so it doesn't have to be implemented in your plugin if you don't want to use it. Also, more importantly, all config plugins can use the standard inline pack format if they want to use query packs. Which is awesome. For more information, refer to #1390, the documentation and the doxygen comments included with this pull requests, as well as the following example config which is now supported, regardless of what config plugin you're using: ```json { "options": { "enable_monitor": "true" }, "packs": { "core_os_monitoring": { "version": "1.4.5", "discovery": [ "select pid from processes where name like '%osqueryd%';" ], "queries": { "kernel_modules": { "query": "SELECT name, size FROM kernel_modules;", "interval": 600 }, "system_controls": { "query": "SELECT * FROM system_controls;", "interval": 600, "snapshot": true, }, "usb_devices": { "query": "SELECT * FROM usb_devices;", "interval": 600 } } }, "osquery_internal_info": { "version": "1.4.5", "discovery": [ "select pid from processes where name like '%osqueryd%';" ], "queries": { "info": { "query": "select i.*, p.resident_size, p.user_time, p.system_time, time.minutes as counter from osquery_info i, processes p, time where p.pid = i.pid;", "interval": 60, "snapshot": true }, "registry": { "query": "SELECT * FROM osquery_registry;", "interval": 600, "snapshot": true }, "schedule": { "query": "select name, interval, executions, output_size, wall_time, (user_time/executions) as avg_user_time, (system_time/executions) as avg_system_time, average_memory from osquery_schedule;", "interval": 60, "snapshot": true } } } } } ``` The `osquery_packs` table was modified to remove the superfluous columns which could already have been found in `osquery_schedule`. Two more columns were added in their place, representing stats about pack's discovery query execution history. Notably, the internal API for the `osquery::Config` class has changed rather dramatically as apart of the refactoring. We think this is an improvement. While strictly adhering to the osquery config plugin interface will have avoided any compatibility errors, advanced users may notice compilation errors if they access config data directly. All internal users of the config have obviously been updated. Yet another reason to merge your code into mainline; we update it for you when we refactor!
2015-08-19 20:27:49 +00:00
2015-01-30 18:44:25 +00:00
namespace osquery {
void RegistryInterface::removeUnsafe(const std::string& item_name) {
2015-02-04 03:55:16 +00:00
if (items_.count(item_name) > 0) {
items_[item_name]->tearDown();
items_.erase(item_name);
}
// Populate list of aliases to remove (those that mask item_name).
std::vector<std::string> removed_aliases;
for (const auto& alias : aliases_) {
if (alias.second == item_name) {
removed_aliases.push_back(alias.first);
}
}
for (const auto& alias : removed_aliases) {
aliases_.erase(alias);
}
}
void RegistryInterface::remove(const std::string& item_name) {
WriteLock lock(mutex_);
removeUnsafe(item_name);
}
bool RegistryInterface::isInternal(const std::string& item_name) const {
ReadLock lock(mutex_);
return isInternal_(item_name);
}
std::map<std::string, RouteUUID> RegistryInterface::getExternal() const {
ReadLock lock(mutex_);
return external_;
}
std::string RegistryInterface::getActive() const {
ReadLock lock(mutex_);
return active_;
}
std::string RegistryInterface::getName() const {
ReadLock lock(mutex_);
return name_;
}
size_t RegistryInterface::count() const {
ReadLock lock(mutex_);
return items_.size();
}
Status RegistryInterface::setActive(const std::string& item_name) {
UpgradeLock lock(mutex_);
2015-11-24 05:52:00 +00:00
// Default support multiple active plugins.
for (const auto& item : osquery::split(item_name, ",")) {
if (items_.count(item) == 0 && external_.count(item) == 0) {
return Status::failure("Unknown registry plugin: " + item);
2015-11-24 05:52:00 +00:00
}
}
Status status;
{
WriteUpgradeLock wlock(lock);
active_ = item_name;
}
// The active plugin is setup when initialized.
2015-11-24 05:52:00 +00:00
for (const auto& item : osquery::split(item_name, ",")) {
if (exists_(item, true)) {
status = RegistryFactory::get().plugin(name_, item)->setUp();
} else if (exists_(item, false) && !RegistryFactory::get().external()) {
// If the active plugin is within an extension we must wait.
// An extension will first broadcast the registry, then receive the list
// of active plugins, active them if they are extension-local, and finally
// start their extension socket.
status = pingExtension(getExtensionSocket(external_.at(item)));
2015-11-24 05:52:00 +00:00
}
if (!status.ok()) {
break;
}
}
return status;
}
RegistryRoutes RegistryInterface::getRoutes() const {
ReadLock lock(mutex_);
2015-02-04 03:55:16 +00:00
RegistryRoutes route_table;
for (const auto& item : items_) {
if (isInternal_(item.first)) {
2015-02-19 01:19:45 +00:00
// This is an internal plugin, do not include the route.
continue;
}
2015-02-04 03:55:16 +00:00
bool has_alias = false;
for (const auto& alias : aliases_) {
if (alias.second == item.first) {
// If the item name is masked by at least one alias, it will not
// broadcast under the internal item name.
route_table[alias.first] = item.second->routeInfo();
has_alias = true;
}
}
if (!has_alias) {
route_table[item.first] = item.second->routeInfo();
}
}
return route_table;
}
Status RegistryInterface::call(const std::string& item_name,
const PluginRequest& request,
PluginResponse& response) {
PluginRef plugin;
{
ReadLock lock(mutex_);
// Search local plugins (items) for the plugin.
if (items_.count(item_name) > 0) {
plugin = items_.at(item_name);
}
}
if (plugin) {
return plugin->call(request, response);
2015-02-04 03:55:16 +00:00
}
RouteUUID uuid;
{
ReadLock lock(mutex_);
// Check if the item was broadcasted as a plugin within an extension.
if (external_.count(item_name) > 0) {
// The item is a registered extension, call the extension by UUID.
uuid = external_.at(item_name);
} else if (routes_.count(item_name) > 0) {
// The item has a route, but no extension, pass in the route info.
response = routes_.at(item_name);
return Status::success();
} else if (RegistryFactory::get().external()) {
// If this is an extension's registry forward unknown calls to the core.
uuid = 0;
} else {
return Status::failure("Cannot call registry item: " + item_name);
}
}
return callExtension(uuid, name_, item_name, request, response);
2015-02-04 03:55:16 +00:00
}
Status RegistryInterface::addAlias(const std::string& item_name,
const std::string& alias) {
WriteLock lock(mutex_);
2015-02-04 03:55:16 +00:00
if (aliases_.count(alias) > 0) {
return Status::failure("Duplicate alias: " + alias);
2015-02-04 03:55:16 +00:00
}
aliases_[alias] = item_name;
return Status::success();
2015-02-04 03:55:16 +00:00
}
std::string RegistryInterface::getAlias(const std::string& alias) const {
ReadLock lock(mutex_);
2015-02-04 03:55:16 +00:00
if (aliases_.count(alias) == 0) {
return alias;
}
return aliases_.at(alias);
}
Status RegistryInterface::addPlugin(const std::string& plugin_name,
const PluginRef& plugin_item,
bool internal) {
WriteLock lock(mutex_);
if (items_.count(plugin_name) > 0) {
return Status::failure("Duplicate registry item exists: " + plugin_name);
}
plugin_item->setName(plugin_name);
items_.emplace(std::make_pair(plugin_name, plugin_item));
// The item can be listed as internal, meaning it does not broadcast.
if (internal) {
internal_.push_back(plugin_name);
}
return Status::success();
}
void RegistryInterface::setUp() {
UpgradeLock lock(mutex_);
// If this registry does not auto-setup do NOT setup the registry items.
if (!auto_setup_) {
return;
}
// If the registry is using a single 'active' plugin, setUp that plugin.
// For config and logger, only setUp the selected plugin.
if (active_.size() != 0 && exists_(active_, true)) {
items_.at(active_)->setUp();
return;
}
2015-02-04 03:55:16 +00:00
// Try to set up each of the registry items.
// If they fail, remove them from the registry.
std::vector<std::string> failed;
for (auto& item : items_) {
if (!item.second->setUp().ok()) {
failed.push_back(item.first);
}
}
{
WriteUpgradeLock wlock(lock);
for (const auto& failed_item : failed) {
removeUnsafe(failed_item);
}
2015-02-04 03:55:16 +00:00
}
}
void RegistryInterface::configure() {
ReadLock lock(mutex_);
if (!active_.empty() && exists_(active_, true)) {
items_.at(active_)->configure();
} else {
for (auto& item : items_) {
item.second->configure();
}
}
}
Status RegistryInterface::addExternal(const RouteUUID& uuid,
const RegistryRoutes& routes) {
// Add each route name (item name) to the tracking.
for (const auto& route : routes) {
// Keep the routes info assigned to the registry.
{
WriteLock wlock(mutex_);
routes_[route.first] = route.second;
}
auto status = addExternalPlugin(route.first, route.second);
if (status.ok()) {
WriteLock wlock(mutex_);
external_[route.first] = uuid;
} else {
return status;
}
}
return Status::success();
}
/// Remove all the routes for a given uuid.
void RegistryInterface::removeExternal(const RouteUUID& uuid) {
WriteLock lock(mutex_);
std::vector<std::string> removed_items;
// Create list of items to remove by filtering uuid
for (const auto& item : external_) {
if (item.second == uuid) {
removed_items.push_back(item.first);
}
}
for (const auto& item : removed_items) {
removeExternalPlugin(item);
}
// Remove items belonging to the external uuid.
for (const auto& item : removed_items) {
external_.erase(item);
routes_.erase(item);
}
}
2015-02-04 03:55:16 +00:00
/// Facility method to check if a registry item exists.
bool RegistryInterface::exists(const std::string& item_name, bool local) const {
ReadLock lock(mutex_);
return exists_(item_name, local);
2015-02-04 03:55:16 +00:00
}
/// Facility method to list the registry item identifiers.
std::vector<std::string> RegistryInterface::names() const {
ReadLock lock(mutex_);
2015-02-04 03:55:16 +00:00
std::vector<std::string> names;
for (const auto& item : items_) {
names.push_back(item.first);
}
// Also add names of external plugins.
for (const auto& item : external_) {
names.push_back(item.first);
}
2015-02-04 03:55:16 +00:00
return names;
}
std::map<std::string, PluginRef> RegistryInterface::plugins() {
ReadLock lock(mutex_);
return items_;
}
void RegistryInterface::setname(const std::string& name) {
WriteLock lock(mutex_);
name_ = name;
}
bool RegistryInterface::isInternal_(const std::string& item_name) const {
ReadLock lock(mutex_);
if (std::find(internal_.begin(), internal_.end(), item_name) ==
internal_.end()) {
return false;
}
return true;
}
bool RegistryInterface::exists_(const std::string& item_name,
bool local) const {
ReadLock lock(mutex_);
bool has_local = (items_.count(item_name) > 0);
bool has_external = (external_.count(item_name) > 0);
bool has_route = (routes_.count(item_name) > 0);
return (local) ? has_local : has_local || has_external || has_route;
}
AutoRegisterInterface::AutoRegisterInterface(const char* _type,
const char* _name,
bool optional)
: type_(_type), name_(_name), optional_(optional) {}
AutoRegisterSet& AutoRegisterInterface::registries() {
static AutoRegisterSet registries_;
return registries_;
}
AutoRegisterSet& AutoRegisterInterface::plugins() {
static AutoRegisterSet plugins_;
return plugins_;
}
void AutoRegisterInterface::autoloadRegistry(
std::unique_ptr<AutoRegisterInterface> ar_) {
registries().push_back(std::move(ar_));
}
void AutoRegisterInterface::autoloadPlugin(
std::unique_ptr<AutoRegisterInterface> ar_) {
plugins().push_back(std::move(ar_));
}
void registryAndPluginInit() {
for (const auto& it : AutoRegisterInterface::registries()) {
it->run();
}
for (const auto& it : AutoRegisterInterface::plugins()) {
it->run();
}
AutoRegisterSet().swap(AutoRegisterInterface::registries());
AutoRegisterSet().swap(AutoRegisterInterface::plugins());
2015-01-30 18:44:25 +00:00
}
} // namespace osquery