From: Sangwan Kwon Date: Thu, 5 Sep 2019 08:13:13 +0000 (+0900) Subject: Reorganize directory structure X-Git-Tag: submit/tizen/20200810.073515~208 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=42233e7e5c8b4be9aecf9d62b22e18eb57d41f2e;p=platform%2Fcore%2Fsecurity%2Fvist.git Reorganize directory structure Signed-off-by: Sangwan Kwon --- diff --git a/CMake/Macro.cmake b/CMake/Macro.cmake index 4cf3eb9..e67c9f0 100644 --- a/CMake/Macro.cmake +++ b/CMake/Macro.cmake @@ -12,6 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License +## osquery ##################### MACRO(ADD_OSQUERY_LIBRARY TARGET) ADD_LIBRARY(${TARGET} OBJECT ${ARGN}) LIST(APPEND ${TARGET_OSQUERY_LIB}_SRCS $) @@ -28,16 +29,28 @@ MACRO(ADD_OSQUERY_LINK) SET(${TARGET_OSQUERY_LIB}_DEPS ${${TARGET_OSQUERY_LIB}_DEPS} PARENT_SCOPE) ENDMACRO(ADD_OSQUERY_LINK) -MACRO(TARGET_OSQUERY_LINK_WHOLE TARGET LIBRARY) +## apix ##################### +MACRO(ADD_APIX_LIBRARY TARGET) + ADD_LIBRARY(${TARGET} OBJECT ${ARGN}) + LIST(APPEND ${TARGET_APIX_LIB}_SRCS $) + SET(${TARGET_APIX_LIB}_SRCS ${${TARGET_APIX_LIB}_SRCS} PARENT_SCOPE) +ENDMACRO(ADD_APIX_LIBRARY) + +MACRO(ADD_APIX_TEST) + LIST(APPEND ${TARGET_APIX_LIB}_TESTS ${ARGN}) + SET(${TARGET_APIX_LIB}_TESTS ${${TARGET_APIX_LIB}_TESTS} PARENT_SCOPE) +ENDMACRO(ADD_APIX_TEST) + +MACRO(ADD_APIX_LINK) + LIST(APPEND ${TARGET_APIX_LIB}_DEPS ${ARGN}) + SET(${TARGET_APIX_LIB}_DEPS ${${TARGET_APIX_LIB}_DEPS} PARENT_SCOPE) +ENDMACRO(ADD_APIX_LINK) + +## common ############################# +MACRO(TARGET_LINK_WHOLE TARGET LIBRARY) TARGET_LINK_LIBRARIES(${TARGET} "-Wl,-whole-archive") TARGET_LINK_LIBRARIES(${TARGET} ${LIBRARY}) TARGET_LINK_LIBRARIES(${TARGET} "-Wl,-no-whole-archive") -ENDMACRO(TARGET_OSQUERY_LINK_WHOLE) - -MACRO(ADD_OSQUERY_MODULE TARGET) - ADD_LIBRARY(${TARGET} SHARED ${ARGN}) - TARGET_LINK_LIBRARIES(${TARGET} dl) - ADD_DEPENDENCIES(${TARGET} ${TARGET_OSQUERY_LIB} glog) - SET_TARGET_PROPERTIES(${TARGET} PROPERTIES COMPILE_FLAGS "-fPIC") - SET_TARGET_PROPERTIES(${TARGET} PROPERTIES OUTPUT_NAME ${TARGET}) -ENDMACRO(ADD_OSQUERY_MODULE) +ENDMACRO(TARGET_LINK_WHOLE) + + diff --git a/CMakeLists.txt b/CMakeLists.txt index f67f781..c3d1ed5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -44,9 +44,9 @@ ADD_DEFINITIONS(-DOSQUERY_BUILD_VERSION=${OSQUERY_BUILD_VERSION} INCLUDE_DIRECTORIES("${CMAKE_SOURCE_DIR}") INCLUDE_DIRECTORIES("${CMAKE_SOURCE_DIR}/api") -INCLUDE_DIRECTORIES("${CMAKE_SOURCE_DIR}/include") INCLUDE_DIRECTORIES("${CMAKE_SOURCE_DIR}/tools/sqlite3") -INCLUDE_DIRECTORIES("${CMAKE_SOURCE_DIR}/osquery/tizen/tsqb") +INCLUDE_DIRECTORIES("${CMAKE_SOURCE_DIR}/src/osquery/include") +INCLUDE_DIRECTORIES("${CMAKE_SOURCE_DIR}/src/apix/tsqb") INCLUDE_DIRECTORIES("/usr/local/include") ENABLE_TESTING() @@ -57,9 +57,9 @@ INCLUDE(CMake/Thrift.cmake) # Make sure the generated paths exist EXECUTE_PROCESS(COMMAND mkdir -p "${CMAKE_BINARY_DIR}/generated") -ADD_SUBDIRECTORY(osquery) +ADD_SUBDIRECTORY(specs) +ADD_SUBDIRECTORY(src) ADD_SUBDIRECTORY(tools/sqlite3) - IF(DEFINED GBS_BUILD) ADD_SUBDIRECTORY(plugins) ENDIF() diff --git a/include/osquery/config.h b/include/osquery/config.h deleted file mode 100644 index 38c9002..0000000 --- a/include/osquery/config.h +++ /dev/null @@ -1,448 +0,0 @@ -/* - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#pragma once - -#include -#include -#include - -#include -#include -#include -#include - -#include -#include -#include -#include - -namespace pt = boost::property_tree; - -namespace osquery { - -/// The builder or invoker may change the default config plugin. -DECLARE_string(config_plugin); - -/** - * @brief The osquery config is updated names sources containing JSON. - * - * A ConfigSourceMap is a named mapping from source (the key) to a JSON blob. - * This map is generated by a ConfigPlugin an provided to the Config via an - * update call. ConfigPlugin%s may update the Config asynchronously. - * - * The osquery Config instance will perform source merging by amalgamating - * the JSON literal types (lists and maps) for well known top-level keys. - * The merging will happen in lexicographical order based on source name. - */ -typedef std::map ConfigSourceMap; - -/** - * @brief A native representation of osquery configuration data. - * - * When you use osquery::Config::getInstance(), you are getting a singleton - * handle to interact with the data stored in an instance of this struct. - */ -struct ConfigData { - /// A vector of all of the queries that are scheduled to execute. - std::map schedule; - std::map options; - std::map > files; - /// All data catches optional/plugin-parsed configuration keys. - pt::ptree all_data; -}; - -class ConfigParserPlugin; -typedef std::shared_ptr ConfigPluginRef; - -/** - * @brief A singleton that exposes accessors to osquery's configuration data. - * - * osquery has two types on configurations. Things that don't change during - * the execution of the process should be configured as command-line - * arguments. Things that can change during the lifetime of program execution - * should be defined using the osquery::config::Config class and the pluggable - * plugin interface that is included with it. - */ -class Config : private boost::noncopyable { - public: - /** - * @brief The primary way to access the Config singleton. - * - * osquery::config::Config::getInstance() provides access to the Config - * singleton - * - * @code{.cpp} - * auto config = osquery::config::Config::getInstance(); - * @endcode - * - * @return a singleton instance of Config. - */ - static Config& getInstance() { - static Config cfg; - return cfg; - } - - /** - * @brief Call the genConfig method of the config retriever plugin. - * - * This may perform a resource load such as TCP request or filesystem read. - */ - static Status load(); - - /** - * @brief Update the internal config data. - * - * @param config A map of domain or namespace to config data. - * @return If the config changes were applied. - */ - static Status update(const ConfigSourceMap& config); - - /** - * @brief Calculate the has of the osquery config - * - * @return The MD5 of the osquery config - */ - static Status getMD5(std::string& hashString); - - /** - * @brief Adds a new query to the scheduled queries. - * - */ - static void addScheduledQuery(const std::string& name, - const std::string& query, - int interval); - - /** - * @brief Checks if a query exists in the query schedule. - * - */ - static bool checkScheduledQuery(const std::string& query); - - /** - * @brief Checks if the query name exists in the query schedule. - * - */ - static bool checkScheduledQueryName(const std::string& query_name); - - /** - * @brief Check to ensure that the config is accessible and properly - * formatted - * - * @return an instance of osquery::Status, indicating the success or failure - * of the operation. - */ - static Status checkConfig(); - - private: - /** - * @brief Default constructor. - * - * Since instances of Config should only be created via getInstance(), - * Config's constructor is private - */ - Config() : force_merge_success_(false) {} - ~Config(){} - Config(Config const&); - void operator=(Config const&); - - /** - * @brief Uses the specified config retriever to populate a string with the - * config JSON. - * - * Internally, genConfig checks to see if there was a config retriever - * specified on the command-line. If there was, it checks to see if that - * config retriever actually exists. If it does, it gets used to generate - * configuration data. If it does not, an error is logged. - * - * @return status indicating the success or failure of the operation. - */ - static Status genConfig(); - - /// Merge a retrieved config source JSON into a working ConfigData. - static Status mergeConfig(const std::string& source, ConfigData& conf); - - public: - /** - * @brief Record performance (monitoring) information about a scheduled query. - * - * The daemon and query scheduler will optionally record process metadata - * before and after executing each query. This can be compared and reported - * on an interval or within the osquery_schedule table. - * - * The config consumes and calculates the optional performance differentials. - * It would also be possible to store this in the RocksDB backing store or - * report directly to a LoggerPlugin sink. The Config is the most appropriate - * as the metrics are transient to the process running the schedule and apply - * to the updates/changes reflected in the schedule, from the config. - * - * @param name The unique name of the scheduled item - * @param delay Number of seconds (wall time) taken by the query - * @param size Number of characters generated by query - * @param t0 the process row before the query - * @param t1 the process row after the query - */ - static void recordQueryPerformance(const std::string& name, - size_t delay, - size_t size, - const Row& t0, - const Row& t1); - - private: - /// The raw osquery config data in a native format - ConfigData data_; - - /// The raw JSON source map from the config plugin. - std::map raw_; - - /// The reader/writer config data mutex. - boost::shared_mutex mutex_; - - /// Enforce merge success. - bool force_merge_success_; - - private: - /** - * @brief A ConfigDataInstance requests read-only access to ConfigParser data. - * - * A ConfigParser plugin will receive several top-level-config keys and - * optionally parse and store information. That information is a property tree - * called ConfigParser::data_. Use ConfigDataInstance::getParsedData to - * retrieve read-only access to this data. - * - * @param parser The name of the config parser. - */ - static const pt::ptree& getParsedData(const std::string& parser); - - /// See getParsedData but request access to the parser plugin. - static const ConfigPluginRef getParser(const std::string& parser); - - /// A default, empty property tree used when a missing parser is requested. - pt::ptree empty_data_; - - private: - /// Config accessors, `ConfigDataInstance`, are the forced use of the config - /// data. This forces the caller to use a shared read lock. - friend class ConfigDataInstance; - - private: - FRIEND_TEST(ConfigTests, test_locking); -}; - -/** - * @brief All accesses to the Config's data must request a ConfigDataInstance. - * - * This class will request a read-only lock of the config's changeable internal - * data structures such as query schedule, options, monitored files, etc. - * - * Since a variable config plugin may implement `update` calls, internal uses - * of config data needs simple read and write locking. - */ -class ConfigDataInstance { - public: - ConfigDataInstance() : lock_(Config::getInstance().mutex_) {} - ~ConfigDataInstance() { lock_.unlock(); } - - /// Helper accessor for Config::data_.schedule. - const std::map schedule() const { - return Config::getInstance().data_.schedule; - } - - /// Helper accessor for Config::data_.options. - const std::map& options() const { - return Config::getInstance().data_.options; - } - - /// Helper accessor for Config::data_.files. - const std::map >& files() const { - return Config::getInstance().data_.files; - } - - const pt::ptree& getParsedData(const std::string& parser) const { - return Config::getParsedData(parser); - } - - const ConfigPluginRef getParser(const std::string& parser) const { - return Config::getParser(parser); - } - - /// Helper accessor for Config::data_.all_data. - const pt::ptree& data() const { return Config::getInstance().data_.all_data; } - - private: - /** - * @brief ConfigParser plugin's may update the internal config representation. - * - * If the config parser reads and calculates new information it should store - * that derived data itself and rely on ConfigDataInstance::getParsedData. - * This means another plugin is aware of the ConfigParser and knowns to make - * getParsedData calls. If the parser is augmenting/changing internal state, - * such as modifying the osquery schedule or options, then it must write - * changed back into the default data. - * - * Note that this returns the ConfigData instance, not the raw property tree. - */ - ConfigData& mutableConfigData() { return Config::getInstance().data_; } - - private: - /// A read lock on the reader/writer config data accessor/update mutex. - boost::shared_lock lock_; - - private: - friend class ConfigParserPlugin; -}; - -/** - * @brief Superclass for the pluggable config component. - * - * In order to make the distribution of configurations to hosts running - * osquery, we take advantage of a plugin interface which allows you to - * integrate osquery with your internal configuration distribution mechanisms. - * You may use ZooKeeper, files on disk, a custom solution, etc. In order to - * use your specific configuration distribution system, one simply needs to - * create a custom subclass of ConfigPlugin. That subclass should implement - * the ConfigPlugin::genConfig method. - * - * Consider the following example: - * - * @code{.cpp} - * class TestConfigPlugin : public ConfigPlugin { - * public: - * virtual std::pair genConfig() { - * std::string config; - * auto status = getMyConfig(config); - * return std::make_pair(status, config); - * } - * }; - * - * REGISTER(TestConfigPlugin, "config", "test"); - * @endcode - */ -class ConfigPlugin : public Plugin { - public: - /** - * @brief Virtual method which should implemented custom config retrieval - * - * ConfigPlugin::genConfig should be implemented by a subclasses of - * ConfigPlugin which needs to retrieve config data in a custom way. - * - * @param config The output ConfigSourceMap, a map of JSON to source names. - * @return A failure status will prevent the source map from merging. - */ - virtual Status genConfig(ConfigSourceMap& config) = 0; - Status call(const PluginRequest& request, PluginResponse& response); -}; - -/// Helper merged and parsed property tree. -typedef pt::ptree ConfigTree; - -/// Helper for a map of requested keys to their merged and parsed property tree. -typedef std::map ConfigTreeMap; - -/** - * @brief A pluggable configuration parser. - * - * An osquery config instance is populated from JSON using a ConfigPlugin. - * That plugin may update the config data asynchronously and read from - * several sources, as is the case with "filesystem" and reading multiple files. - * - * A ConfigParserPlugin will receive the merged configuration at osquery start - * and the updated (still merged) config if any ConfigPlugin updates the - * instance asynchronously. Each parser specifies a set of top-level JSON - * keys to receive. The config instance will auto-merge the key values - * from multiple sources if they are dictionaries or lists. - * - * If a top-level key is a dictionary, each source with the top-level key - * will have its own dictionary keys merged and replaced based on the lexical - * order of sources. For the "filesystem" config plugin this is the lexical - * sorting of filenames. If the top-level key is a list, each source with the - * top-level key will have its contents appended. - * - * Each config parser plugin will live alongside the config instance for the - * life of the osquery process. The parser may perform actions at config load - * and config update "time" as well as keep its own data members and be - * accessible through the Config class API. - */ -class ConfigParserPlugin : public Plugin { - protected: - /** - * @brief Return a list of top-level config keys to receive in updates. - * - * The ::update method will receive a map of these keys with a JSON-parsed - * property tree of configuration data. - * - * @return A list of string top-level JSON keys. - */ - virtual std::vector keys() = 0; - - /** - * @brief Receive a merged property tree for each top-level config key. - * - * Called when the Config instance is initially loaded with data from the - * active config plugin and when it is updated via an async ConfigPlugin - * update. Every config parser will receive a map of merged data for each key - * they requested in keys(). - * - * @param config A JSON-parsed property tree map. - * @return Failure if the parser should no longer receive updates. - */ - virtual Status update(const ConfigTreeMap& config) = 0; - - protected: - /// Mutable config data accessor for ConfigParser%s. - ConfigData& mutableConfigData(ConfigDataInstance& cdi) { - return cdi.mutableConfigData(); - } - - protected: - /// Allow the config parser to keep some global state. - pt::ptree data_; - - private: - Status setUp(); - - private: - /// Config::update will call all appropriate parser updates. - friend class Config; - /// A config data instance implements a read/write lock around data_ access. - friend class ConfigDataInstance; -}; - -/** - * @brief Calculate a splayed integer based on a variable splay percentage - * - * The value of splayPercent must be between 1 and 100. If it's not, the - * value of original will be returned. - * - * @param original The original value to be modified - * @param splayPercent The percent in which to splay the original value by - * - * @return The modified version of original - */ -int splayValue(int original, int splayPercent); - -/** - * @brief Config plugin registry. - * - * This creates an osquery registry for "config" which may implement - * ConfigPlugin. A ConfigPlugin's call API should make use of a genConfig - * after reading JSON data in the plugin implementation. - */ -CREATE_REGISTRY(ConfigPlugin, "config"); - -/** - * @brief ConfigParser plugin registry. - * - * This creates an osquery registry for "config_parser" which may implement - * ConfigParserPlugin. A ConfigParserPlugin should not export any call actions - * but rather have a simple property tree-accessor API through Config. - */ -CREATE_LAZY_REGISTRY(ConfigParserPlugin, "config_parser"); -} diff --git a/include/osquery/core.h b/include/osquery/core.h deleted file mode 100644 index 30260da..0000000 --- a/include/osquery/core.h +++ /dev/null @@ -1,267 +0,0 @@ -/* - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#pragma once - -#include -#include - -#include - -// clang-format off -#ifndef STR -#define STR_OF(x) #x -#define STR(x) STR_OF(x) -#endif -#define STR_EX(x) x -#define CONCAT(x, y) STR(STR_EX(x)STR_EX(y)) - -#ifndef FRIEND_TEST -#define FRIEND_TEST(test_case_name, test_name) \ - friend class test_case_name##_##test_name##_Test -#endif -// clang-format on - -#ifndef __constructor__ -#define __constructor__ __attribute__((constructor)) -#endif - -/// A configuration error is catastrophic and should exit the watcher. -#define EXIT_CATASTROPHIC 78 - -namespace osquery { - -/** - * @brief The version of osquery - */ -extern const std::string kVersion; -extern const std::string kSDKVersion; -extern const std::string kSDKPlatform; - -/// Use a macro for the sdk/platform literal, symbols available in lib.cpp. -#define OSQUERY_SDK_VERSION STR(OSQUERY_BUILD_SDK_VERSION) -#define OSQUERY_PLATFORM STR(OSQUERY_BUILD_PLATFORM) - -/** - * @brief A helpful tool type to report when logging, print help, or debugging. - */ -enum ToolType { - OSQUERY_TOOL_UNKNOWN = 0, - OSQUERY_TOOL_SHELL, - OSQUERY_TOOL_DAEMON, - OSQUERY_TOOL_TEST, - OSQUERY_EXTENSION, -}; - -/// The osquery tool type for runtime decisions. -extern ToolType kToolType; - -class Initializer { - public: - /** - * @brief Sets up various aspects of osquery execution state. - * - * osquery needs a few things to happen as soon as the process begins - * executing. Initializer takes care of setting up the relevant parameters. - * Initializer should be called in an executable's `main()` function. - * - * @param argc the number of elements in argv - * @param argv the command-line arguments passed to `main()` - * @param tool the type of osquery main (daemon, shell, test, extension). - */ - Initializer(int& argc, char**& argv, ToolType tool = OSQUERY_TOOL_TEST); - - /** - * @brief Sets up the process as an osquery daemon. - * - * A daemon has additional constraints, it can use a process mutex, check - * for sane/non-default configurations, etc. - */ - void initDaemon(); - - /** - * @brief Daemon tools may want to continually spawn worker processes - * and monitor their utilization. - * - * A daemon may call initWorkerWatcher to begin watching child daemon - * processes until it-itself is unscheduled. The basic guarantee is that only - * workers will return from the function. - * - * The worker-watcher will implement performance bounds on CPU utilization - * and memory, as well as check for zombie/defunct workers and respawn them - * if appropriate. The appropriateness is determined from heuristics around - * how the worker exited. Various exit states and velocities may cause the - * watcher to resign. - * - * @param name The name of the worker process. - */ - void initWorkerWatcher(const std::string& name); - - /// Assume initialization finished, start work. - void start(); - /// Turns off various aspects of osquery such as event loops. - void shutdown(); - - /** - * @brief Check if a process is an osquery worker. - * - * By default an osqueryd process will fork/exec then set an environment - * variable: `OSQUERY_WORKER` while continually monitoring child I/O. - * The environment variable causes subsequent child processes to skip several - * initialization steps and jump into extension handling, registry setup, - * config/logger discovery and then the event publisher and scheduler. - */ - static bool isWorker(); - - private: - /// Initialize this process as an osquery daemon worker. - void initWorker(const std::string& name); - /// Initialize the osquery watcher, optionally spawn a worker. - void initWatcher(); - /// Set and wait for an active plugin optionally broadcasted. - void initActivePlugin(const std::string& type, const std::string& name); - - private: - int* argc_; - char*** argv_; - int tool_; - std::string binary_; -}; - -/** - * @brief Split a given string based on an optional delimiter. - * - * If no delimiter is supplied, the string will be split based on whitespace. - * - * @param s the string that you'd like to split - * @param delim the delimiter which you'd like to split the string by - * - * @return a vector of strings split by delim. - */ -std::vector split(const std::string& s, - const std::string& delim = "\t "); - -/** - * @brief Split a given string based on an delimiter. - * - * @param s the string that you'd like to split. - * @param delim the delimiter which you'd like to split the string by. - * @param occurrences the number of times to split by delim. - * - * @return a vector of strings split by delim for occurrences. - */ -std::vector split(const std::string& s, - const std::string& delim, - size_t occurences); - -/** - * @brief In-line replace all instances of from with to. - * - * @param str The input/output mutable string. - * @param from Search string - * @param to Replace string - */ -inline void replaceAll(std::string& str, - const std::string& from, - const std::string& to) { - if (from.empty()) { - return; - } - - size_t start_pos = 0; - while ((start_pos = str.find(from, start_pos)) != std::string::npos) { - str.replace(start_pos, from.length(), to); - start_pos += to.length(); - } -} - -/** - * @brief Join a vector of strings using a tokenizer. - * - * @param s the string that you'd like to split. - * @param tok a token glue. - * - * @return a joined string. - */ -std::string join(const std::vector& s, const std::string& tok); - -/** - * @brief Getter for a host's current hostname - * - * @return a string representing the host's current hostname - */ -std::string getHostname(); - -/** - * @brief generate a uuid to uniquely identify this machine - * - * @return uuid string to identify this machine - */ -std::string generateHostUuid(); - -/** - * @brief Getter for the current time, in a human-readable format. - * - * @return the current date/time in the format: "Wed Sep 21 10:27:52 2011" - */ -std::string getAsciiTime(); - -/** - * @brief Getter for the current UNIX time. - * - * @return an int representing the amount of seconds since the UNIX epoch - */ -int getUnixTime(); - -/** - * @brief In-line helper function for use with utf8StringSize - */ -template -inline size_t incUtf8StringIterator(_Iterator1& it, const _Iterator2& last) { - if (it == last) { - return 0; - } - - unsigned char c; - size_t res = 1; - for (++it; last != it; ++it, ++res) { - c = *it; - if (!(c & 0x80) || ((c & 0xC0) == 0xC0)) { - break; - } - } - - return res; -} - -/** - * @brief Get the length of a UTF-8 string - * - * @param str The UTF-8 string - * - * @return the length of the string - */ -inline size_t utf8StringSize(const std::string& str) { - size_t res = 0; - std::string::const_iterator it = str.begin(); - for (; it != str.end(); incUtf8StringIterator(it, str.end())) { - res++; - } - - return res; -} - -/** - * @brief Create a pid file - * - * @return A status object indicating the success or failure of the operation - */ -Status createPidFile(); -} diff --git a/include/osquery/database.h b/include/osquery/database.h deleted file mode 100644 index eaf35b5..0000000 --- a/include/osquery/database.h +++ /dev/null @@ -1,482 +0,0 @@ -/* - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#pragma once - -#include -#include -#include - -#include - -#include -#include - -namespace pt = boost::property_tree; - -namespace osquery { - -/** - * @brief A backing storage domain name, used for key/value based storage. - * - * There are certain "cached" variables such as a node-unique UUID or negotiated - * 'node_key' following enrollment. If a value or setting must persist between - * osqueryi or osqueryd runs it should be stored using the kPersistentSetting%s - * domain. - */ -extern const std::string kPersistentSettings; - -/// The "domain" where the results of scheduled queries are stored. -extern const std::string kQueries; - -/// The "domain" where event results are stored, queued for querytime retrieval. -extern const std::string kEvents; - -/** - * @brief The "domain" where buffered log results are stored. - * - * Logger plugins may shuttle logs to a remote endpoint or API call - * asynchronously. The backing store can be used to buffer results and status - * logs until the logger plugin-specific thread decided to flush. - */ -extern const std::string kLogs; - -///////////////////////////////////////////////////////////////////////////// -// Row -///////////////////////////////////////////////////////////////////////////// - -/** - * @brief A variant type for the SQLite type affinities. - */ -typedef std::string RowData; - -/** - * @brief A single row from a database query - * - * Row is a simple map where individual column names are keys, which map to - * the Row's respective value - */ -typedef std::map Row; - -/** - * @brief Serialize a Row into a property tree - * - * @param r the Row to serialize - * @param tree the output property tree - * - * @return Status indicating the success or failure of the operation - */ -Status serializeRow(const Row& r, pt::ptree& tree); - -/** - * @brief Serialize a Row object into a JSON string - * - * @param r the Row to serialize - * @param json the output JSON string - * - * @return Status indicating the success or failure of the operation - */ -Status serializeRowJSON(const Row& r, std::string& json); - -/** - * @brief Deserialize a Row object from a property tree - * - * @param tree the input property tree - * @param r the output Row structure - * - * @return Status indicating the success or failure of the operation - */ -Status deserializeRow(const pt::ptree& tree, Row& r); - -/** - * @brief Deserialize a Row object from a JSON string - * - * @param json the input JSON string - * @param r the output Row structure - * - * @return Status indicating the success or failure of the operation - */ -Status deserializeRowJSON(const std::string& json, Row& r); - -///////////////////////////////////////////////////////////////////////////// -// QueryData -///////////////////////////////////////////////////////////////////////////// - -/** - * @brief The result set returned from a osquery SQL query - * - * QueryData is the canonical way to represent the results of SQL queries in - * osquery. It's just a vector of Row's. - */ -typedef std::vector QueryData; - -/** - * @brief Serialize a QueryData object into a property tree - * - * @param q the QueryData to serialize - * @param tree the output property tree - * - * @return Status indicating the success or failure of the operation - */ -Status serializeQueryData(const QueryData& q, pt::ptree& tree); - -/** - * @brief Serialize a QueryData object into a JSON string - * - * @param q the QueryData to serialize - * @param json the output JSON string - * - * @return Status indicating the success or failure of the operation - */ -Status serializeQueryDataJSON(const QueryData& q, std::string& json); - -/// Inverse of serializeQueryData, convert property tree to QueryData. -Status deserializeQueryData(const pt::ptree& tree, QueryData& qd); - -/// Inverse of serializeQueryDataJSON, convert a JSON string to QueryData. -Status deserializeQueryDataJSON(const std::string& json, QueryData& qd); - -///////////////////////////////////////////////////////////////////////////// -// DiffResults -///////////////////////////////////////////////////////////////////////////// - -/** - * @brief Data structure representing the difference between the results of - * two queries - * - * The representation of two diffed QueryData result sets. Given and old and - * new QueryData, DiffResults indicates the "added" subset of rows and the - * "removed" subset of rows. - */ -struct DiffResults { - /// vector of added rows - QueryData added; - - /// vector of removed rows - QueryData removed; - - /// equals operator - bool operator==(const DiffResults& comp) const { - return (comp.added == added) && (comp.removed == removed); - } - - /// not equals operator - bool operator!=(const DiffResults& comp) const { return !(*this == comp); } -}; - -/** - * @brief Serialize a DiffResults object into a property tree - * - * @param d the DiffResults to serialize - * @param tree the output property tree - * - * @return Status indicating the success or failure of the operation - */ -Status serializeDiffResults(const DiffResults& d, pt::ptree& tree); - -/** - * @brief Serialize a DiffResults object into a JSON string - * - * @param d the DiffResults to serialize - * @param json the output JSON string - * - * @return an instance of osquery::Status, indicating the success or failure - * of the operation - */ -Status serializeDiffResultsJSON(const DiffResults& d, std::string& json); - -/** - * @brief Diff two QueryData objects and create a DiffResults object - * - * @param old_ the "old" set of results - * @param new_ the "new" set of results - * - * @return a DiffResults object which indicates the change from old_ to new_ - * - * @see DiffResults - */ -DiffResults diff(const QueryData& old_, const QueryData& new_); - -/** - * @brief Add a Row to a QueryData if the Row hasn't appeared in the QueryData - * already - * - * Note that this function will iterate through the QueryData list until a - * given Row is found (or not found). This shouldn't be that significant of an - * overhead for most use-cases, but it's worth keeping in mind before you use - * this in it's current state. - * - * @param q the QueryData list to append to - * @param r the Row to add to q - * - * @return true if the Row was added to the QueryData, false if it was not - */ -bool addUniqueRowToQueryData(QueryData& q, const Row& r); - -/** - * @brief Construct a new QueryData from an existing one, replacing all - * non-ASCII characters with their \u encoding. - * - * This function is intended as a workaround for - * https://svn.boost.org/trac/boost/ticket/8883, - * and will allow rows containing data with non-ASCII characters to be stored in - * the database and parsed back into a property tree. - * - * @param oldData the old QueryData to copy - * @param newData the new escaped QueryData object - */ -void escapeQueryData(const QueryData& oldData, QueryData& newData); - -/** - * @brief represents the relevant parameters of a scheduled query. - * - * Within the context of osqueryd, a scheduled query may have many relevant - * attributes. Those attributes are represented in this data structure. - */ -struct ScheduledQuery { - /// The SQL query. - std::string query; - - /// How often the query should be executed, in second. - size_t interval; - - /// A temporary splayed internal. - size_t splayed_interval; - - /// Number of executions. - size_t executions; - - /// Total wall time taken - unsigned long long int wall_time; - - /// Total user time (cycles) - unsigned long long int user_time; - - /// Total system time (cycles) - unsigned long long int system_time; - - /// Average memory differentials. This should be near 0. - unsigned long long int average_memory; - - /// Total characters, bytes, generated by query. - unsigned long long int output_size; - - /// Set of query options. - std::map options; - - ScheduledQuery() - : interval(0), - splayed_interval(0), - executions(0), - wall_time(0), - user_time(0), - system_time(0), - average_memory(0), - output_size(0) {} - - /// equals operator - bool operator==(const ScheduledQuery& comp) const { - return (comp.query == query) && (comp.interval == interval); - } - - /// not equals operator - bool operator!=(const ScheduledQuery& comp) const { return !(*this == comp); } -}; - -///////////////////////////////////////////////////////////////////////////// -// QueryLogItem -///////////////////////////////////////////////////////////////////////////// - -/** - * @brief Query results from a schedule, snapshot, or ad-hoc execution. - * - * When a scheduled query yields new results, we need to log that information - * to our upstream logging receiver. A QueryLogItem contains metadata and - * results in potentially-differential form for a logger. - */ -struct QueryLogItem { - /// Differential results from the query. - DiffResults results; - - /// Optional snapshot results, no differential applied. - QueryData snapshot_results; - - /// The name of the scheduled query. - std::string name; - - /// The identifier (hostname, or uuid) of the host. - std::string identifier; - - /// The time that the query was executed, seconds as UNIX time. - int time; - - /// The time that the query was executed, an ASCII string. - std::string calendar_time; - - /// equals operator - bool operator==(const QueryLogItem& comp) const { - return (comp.results == results) && (comp.name == name); - } - - /// not equals operator - bool operator!=(const QueryLogItem& comp) const { return !(*this == comp); } -}; - -/** - * @brief Serialize a QueryLogItem object into a property tree - * - * @param item the QueryLogItem to serialize - * @param tree the output property tree - * - * @return Status indicating the success or failure of the operation - */ -Status serializeQueryLogItem(const QueryLogItem& item, pt::ptree& tree); - -/** - * @brief Serialize a QueryLogItem object into a JSON string - * - * @param item the QueryLogItem to serialize - * @param json the output JSON string - * - * @return Status indicating the success or failure of the operation - */ -Status serializeQueryLogItemJSON(const QueryLogItem& item, std::string& json); - -/// Inverse of serializeQueryLogItem, convert property tree to QueryLogItem. -Status deserializeQueryLogItem(const pt::ptree& tree, QueryLogItem& item); - -/// Inverse of serializeQueryLogItem, convert a JSON string to QueryLogItem. -Status deserializeQueryLogItemJSON(const std::string& json, QueryLogItem& item); - -/** - * @brief Serialize a QueryLogItem object into a property tree - * of events, a list of actions. - * - * @param item the QueryLogItem to serialize - * @param tree the output property tree - * - * @return Status indicating the success or failure of the operation - */ -Status serializeQueryLogItemAsEvents(const QueryLogItem& item, pt::ptree& tree); - -/** - * @brief Serialize a QueryLogItem object into a JSON string of events, - * a list of actions. - * - * @param i the QueryLogItem to serialize - * @param json the output JSON string - * - * @return Status indicating the success or failure of the operation - */ -Status serializeQueryLogItemAsEventsJSON(const QueryLogItem& i, - std::string& json); - -/** - * @brief An osquery backing storage (database) type that persists executions. - * - * The osquery tools need a high-performance storage and indexing mechanism for - * storing intermediate results from EventPublisher%s, persisting one-time - * generated values, and performing non-memory backed differentials. - * - * Practically, osquery is built around RocksDB's performance guarantees and - * all of the internal APIs expect RocksDB's indexing and read performance. - * However, access to this representation of a backing-store is still abstracted - * to removing RocksDB as a dependency for the osquery SDK. - */ -class DatabasePlugin : public Plugin { - protected: - /** - * @brief Perform a domain and key lookup from the backing store. - * - * Database value access indexing is abstracted into domains and keys. - * Both are string values but exist separately for simple indexing without - * API-enforcing tokenization. In some cases we do add a component-specific - * tokeninzation to keys. - * - * @param domain A string value representing abstract storage indexing. - * @param key A string value representing the lookup/retrieval key. - * @param value The output parameter, left empty if the key does not exist. - * @return Failure if the data could not be accessed. It is up to the plugin - * to determine if a missing key means a non-success status. - */ - virtual Status get(const std::string& domain, - const std::string& key, - std::string& value) const = 0; - - /** - * @brief Store a string-represented value using a domain and key index. - * - * See DatabasePlugin::get for discussion around domain and key use. - * - * @param domain A string value representing abstract storage indexing. - * @param key A string value representing the lookup/retrieval key. - * @param value A string value representing the data. - * @return Failure if the data could not be stored. It is up to the plugin - * to determine if a conflict/overwrite should return different status text. - */ - virtual Status put(const std::string& domain, - const std::string& key, - const std::string& value) = 0; - - /// Data removal method. - virtual Status remove(const std::string& domain, const std::string& k) = 0; - - /// Key/index lookup method. - virtual Status scan(const std::string& domain, - std::vector& results) const { - return Status(0, "Not used"); - } - - public: - Status call(const PluginRequest& request, PluginResponse& response); -}; - -/** - * @brief Lookup a value from the active osquery DatabasePlugin storage. - * - * See DatabasePlugin::get for discussion around domain and key use. - * Extensions, components, plugins, and core code should use getDatabaseValue - * as a wrapper around the current tool's choice of a backing storage plugin. - * - * @param domain A string value representing abstract storage indexing. - * @param key A string value representing the lookup/retrieval key. - * @param value The output parameter, left empty if the key does not exist. - * @return Storage operation status. - */ -Status getDatabaseValue(const std::string& domain, - const std::string& key, - std::string& value); - -/** - * @brief Set or put a value into the active osquery DatabasePlugin storage. - * - * See DatabasePlugin::get for discussion around domain and key use. - * Extensions, components, plugins, and core code should use setDatabaseValue - * as a wrapper around the current tool's choice of a backing storage plugin. - * - * @param domain A string value representing abstract storage indexing. - * @param key A string value representing the lookup/retrieval key. - * @param value A string value representing the data. - * @return Storage operation status. - */ -Status setDatabaseValue(const std::string& domain, - const std::string& key, - const std::string& value); - -/// Remove a domain/key identified value from backing-store. -Status deleteDatabaseValue(const std::string& domain, const std::string& key); - -/// Get a list of keys for a given domain. -Status scanDatabaseKeys(const std::string& domain, - std::vector& keys); - -/// Generate a specific-use registry for database access abstraction. -CREATE_REGISTRY(DatabasePlugin, "database"); -} diff --git a/include/osquery/database/results.h b/include/osquery/database/results.h deleted file mode 100644 index 0d288cd..0000000 --- a/include/osquery/database/results.h +++ /dev/null @@ -1,348 +0,0 @@ -/* - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#pragma once - -#include -#include -#include - -#include - -#include - -namespace pt = boost::property_tree; - -namespace osquery { - -///////////////////////////////////////////////////////////////////////////// -// Row -///////////////////////////////////////////////////////////////////////////// - -/** - * @brief A variant type for the SQLite type affinities. - */ -typedef std::string RowData; - -/** - * @brief A single row from a database query - * - * Row is a simple map where individual column names are keys, which map to - * the Row's respective value - */ -typedef std::map Row; - -/** - * @brief Serialize a Row into a property tree - * - * @param r the Row to serialize - * @param tree the output property tree - * - * @return Status indicating the success or failure of the operation - */ -Status serializeRow(const Row& r, pt::ptree& tree); - -/** - * @brief Serialize a Row object into a JSON string - * - * @param r the Row to serialize - * @param json the output JSON string - * - * @return Status indicating the success or failure of the operation - */ -Status serializeRowJSON(const Row& r, std::string& json); - -/** - * @brief Deserialize a Row object from a property tree - * - * @param tree the input property tree - * @param r the output Row structure - * - * @return Status indicating the success or failure of the operation - */ -Status deserializeRow(const pt::ptree& tree, Row& r); - -/** - * @brief Deserialize a Row object from a JSON string - * - * @param json the input JSON string - * @param r the output Row structure - * - * @return Status indicating the success or failure of the operation - */ -Status deserializeRowJSON(const std::string& json, Row& r); - -///////////////////////////////////////////////////////////////////////////// -// QueryData -///////////////////////////////////////////////////////////////////////////// - -/** - * @brief The result set returned from a osquery SQL query - * - * QueryData is the canonical way to represent the results of SQL queries in - * osquery. It's just a vector of Row's. - */ -typedef std::vector QueryData; - -/** - * @brief Serialize a QueryData object into a property tree - * - * @param q the QueryData to serialize - * @param tree the output property tree - * - * @return Status indicating the success or failure of the operation - */ -Status serializeQueryData(const QueryData& q, pt::ptree& tree); - -/** - * @brief Serialize a QueryData object into a JSON string - * - * @param q the QueryData to serialize - * @param json the output JSON string - * - * @return Status indicating the success or failure of the operation - */ -Status serializeQueryDataJSON(const QueryData& q, std::string& json); - -Status deserializeQueryData(const pt::ptree& tree, QueryData& qd); -Status deserializeQueryDataJSON(const std::string& json, QueryData& qd); - -///////////////////////////////////////////////////////////////////////////// -// DiffResults -///////////////////////////////////////////////////////////////////////////// - -/** - * @brief Data structure representing the difference between the results of - * two queries - * - * The representation of two diffed QueryData result sets. Given and old and - * new QueryData, DiffResults indicates the "added" subset of rows and the - * "removed" subset of rows. - */ -struct DiffResults { - /// vector of added rows - QueryData added; - - /// vector of removed rows - QueryData removed; - - /// equals operator - bool operator==(const DiffResults& comp) const { - return (comp.added == added) && (comp.removed == removed); - } - - /// not equals operator - bool operator!=(const DiffResults& comp) const { return !(*this == comp); } -}; - -/** - * @brief Serialize a DiffResults object into a property tree - * - * @param d the DiffResults to serialize - * @param tree the output property tree - * - * @return Status indicating the success or failure of the operation - */ -Status serializeDiffResults(const DiffResults& d, pt::ptree& tree); - -/** - * @brief Serialize a DiffResults object into a JSON string - * - * @param d the DiffResults to serialize - * @param json the output JSON string - * - * @return an instance of osquery::Status, indicating the success or failure - * of the operation - */ -Status serializeDiffResultsJSON(const DiffResults& d, std::string& json); - -/** - * @brief Diff two QueryData objects and create a DiffResults object - * - * @param old_ the "old" set of results - * @param new_ the "new" set of results - * - * @return a DiffResults object which indicates the change from old_ to new_ - * - * @see DiffResults - */ -DiffResults diff(const QueryData& old_, const QueryData& new_); - -/** - * @brief Add a Row to a QueryData if the Row hasn't appeared in the QueryData - * already - * - * Note that this function will iterate through the QueryData list until a - * given Row is found (or not found). This shouldn't be that significant of an - * overhead for most use-cases, but it's worth keeping in mind before you use - * this in it's current state. - * - * @param q the QueryData list to append to - * @param r the Row to add to q - * - * @return true if the Row was added to the QueryData, false if it was not - */ -bool addUniqueRowToQueryData(QueryData& q, const Row& r); - -/** - * @brief Construct a new QueryData from an existing one, replacing all - * non-ASCII characters with their \u encoding. - * - * This function is intended as a workaround for - * https://svn.boost.org/trac/boost/ticket/8883, - * and will allow rows containing data with non-ASCII characters to be stored in - * the database and parsed back into a property tree. - * - * @param oldData the old QueryData to copy - * @param newData the new escaped QueryData object - */ -void escapeQueryData(const QueryData& oldData, QueryData& newData); - -/** - * @brief represents the relevant parameters of a scheduled query. - * - * Within the context of osqueryd, a scheduled query may have many relevant - * attributes. Those attributes are represented in this data structure. - */ -struct ScheduledQuery { - /// The SQL query. - std::string query; - - /// How often the query should be executed, in second. - size_t interval; - - /// A temporary splayed internal. - size_t splayed_interval; - - /// Number of executions. - size_t executions; - - /// Total wall time taken - size_t wall_time; - - /// Total user time (cycles) - size_t user_time; - - /// Total system time (cycles) - size_t system_time; - - /// Average memory differentials. This should be near 0. - size_t memory; - - /// Total characters, bytes, generated by query. - size_t output_size; - - /// Set of query options. - std::map options; - - ScheduledQuery() - : interval(0), - splayed_interval(0), - executions(0), - wall_time(0), - user_time(0), - system_time(0), - memory(0), - output_size(0) {} - - /// equals operator - bool operator==(const ScheduledQuery& comp) const { - return (comp.query == query) && (comp.interval == interval); - } - - /// not equals operator - bool operator!=(const ScheduledQuery& comp) const { return !(*this == comp); } -}; - -///////////////////////////////////////////////////////////////////////////// -// QueryLogItem -///////////////////////////////////////////////////////////////////////////// - -/** - * @brief Query results from a schedule, snapshot, or ad-hoc execution. - * - * When a scheduled query yields new results, we need to log that information - * to our upstream logging receiver. A QueryLogItem contains metadata and - * results in potentially-differential form for a logger. - */ -struct QueryLogItem { - /// Differential results from the query. - DiffResults results; - - /// Optional snapshot results, no differential applied. - QueryData snapshot_results; - - /// The name of the scheduled query. - std::string name; - - /// The identifier (hostname, or uuid) of the host. - std::string identifier; - - /// The time that the query was executed, seconds as UNIX time. - int time; - - /// The time that the query was executed, an ASCII string. - std::string calendar_time; - - /// equals operator - bool operator==(const QueryLogItem& comp) const { - return (comp.results == results) && (comp.name == name); - } - - /// not equals operator - bool operator!=(const QueryLogItem& comp) const { return !(*this == comp); } -}; - -/** - * @brief Serialize a QueryLogItem object into a property tree - * - * @param item the QueryLogItem to serialize - * @param tree the output property tree - * - * @return Status indicating the success or failure of the operation - */ -Status serializeQueryLogItem(const QueryLogItem& item, pt::ptree& tree); - -/** - * @brief Serialize a QueryLogItem object into a JSON string - * - * @param item the QueryLogItem to serialize - * @param json the output JSON string - * - * @return Status indicating the success or failure of the operation - */ -Status serializeQueryLogItemJSON(const QueryLogItem& item, std::string& json); - -Status deserializeQueryLogItem(const pt::ptree& tree, QueryLogItem& item); -Status deserializeQueryLogItemJSON(const std::string& json, QueryLogItem& item); - -/** - * @brief Serialize a QueryLogItem object into a property tree - * of events, a list of actions. - * - * @param item the QueryLogItem to serialize - * @param tree the output property tree - * - * @return Status indicating the success or failure of the operation - */ -Status serializeQueryLogItemAsEvents(const QueryLogItem& item, pt::ptree& tree); - -/** - * @brief Serialize a QueryLogItem object into a JSON string of events, - * a list of actions. - * - * @param i the QueryLogItem to serialize - * @param json the output JSON string - * - * @return Status indicating the success or failure of the operation - */ -Status serializeQueryLogItemAsEventsJSON(const QueryLogItem& i, - std::string& json); -} diff --git a/include/osquery/events.h b/include/osquery/events.h deleted file mode 100644 index 1baabe6..0000000 --- a/include/osquery/events.h +++ /dev/null @@ -1,879 +0,0 @@ -/* - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#pragma once - -#include -#include -#include -#include - -#include -#include -#include -#include - -#include -#include -#include - -namespace osquery { - -struct Subscription; -template class EventPublisher; -template class EventSubscriber; -class EventFactory; - -typedef const std::string EventPublisherID; -typedef const std::string EventSubscriberID; -typedef const std::string EventID; -typedef uint32_t EventContextID; -typedef uint32_t EventTime; -typedef std::pair EventRecord; - -/** - * @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 {}; - -/** - * @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 { - /// An unique counting ID specific to the EventPublisher%'s fired events. - EventContextID id; - /// The time the event occurred, as determined by the publisher. - EventTime time; - - EventContext() : id(0), time(0) {} -}; - -typedef std::shared_ptr SubscriptionRef; -typedef EventPublisher BaseEventPublisher; -typedef std::shared_ptr EventPublisherRef; -typedef std::shared_ptr SubscriptionContextRef; -typedef std::shared_ptr EventContextRef; -typedef EventSubscriber BaseEventSubscriber; -typedef std::shared_ptr> EventSubscriberRef; - -/** - * @brief EventSubscriber%s may exist in various states. - * - * The subscriber 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. - * - 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 EventSubscriberState { - SUBSCRIBER_NONE, - SUBSCRIBER_RUNNING, - SUBSCRIBER_PAUSED, - SUBSCRIBER_FAILED, -}; - -/// Use a single placeholder for the EventContextRef passed to EventCallback. -using std::placeholders::_1; -using std::placeholders::_2; -typedef std::function - EventCallback; - -/// An EventPublisher must track every subscription added. -typedef std::vector SubscriptionVector; - -/// The set of search-time binned lookup tables. -extern const std::vector kEventTimeLists; - -/** - * @brief DECLARE_PUBLISHER supplies needed boilerplate code that applies a - * string-type EventPublisherID to identify the publisher declaration. - */ -#define DECLARE_PUBLISHER(TYPE) \ - public: \ - EventPublisherID type() const { 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 { - public: - // EventSubscriber name. - std::string subscriber_name; - - /// An EventPublisher%-specific SubscriptionContext. - SubscriptionContextRef context; - /// An EventSubscription member EventCallback method. - EventCallback callback; - /// A pointer to possible extra data - void* user_data; - - explicit Subscription(EventSubscriberID& name) - : subscriber_name(name), user_data(nullptr) {} - - static SubscriptionRef create(EventSubscriberID& name) { - auto subscription = std::make_shared(name); - return subscription; - } - - static SubscriptionRef create(EventSubscriberID& name, - const SubscriptionContextRef& mc, - EventCallback ec = 0, - void* user_data = nullptr) { - auto subscription = std::make_shared(name); - subscription->context = mc; - subscription->callback = ec; - subscription->user_data = user_data; - return subscription; - } -}; - -class EventPublisherPlugin : public Plugin { - 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. - */ - virtual void configure() {} - - /** - * @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. - */ - virtual Status setUp() { return Status(0, "Not used"); } - - /** - * @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. - */ - virtual void tearDown() {} - - /** - * @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"); } - - /** - * @brief Allow the EventFactory to interrupt the run loop. - * - * Assume the main thread may ask the run loop to stop at anytime. - * Before end is called the publisher's `isEnding` is set and the EventFactory - * run loop manager will exit the stepping loop and fall through to a call - * to tearDown followed by a removal of the publisher. - */ - virtual void end() {} - - /** - * @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) { - subscriptions_.push_back(subscription); - return Status(0, "OK"); - } - - public: - /// Overriding the EventPublisher constructor is not recommended. - EventPublisherPlugin() : next_ec_id_(0), ending_(false), started_(false){}; - virtual ~EventPublisherPlugin() {} - - /// Return a string identifier associated with this EventPublisher. - virtual EventPublisherID type() const { return "publisher"; } - - public: - /// Number of Subscription%s watching this EventPublisher. - size_t numSubscriptions() const { return subscriptions_.size(); } - - /** - * @brief The number of events fired by this EventPublisher. - * - * @return The number of events. - */ - size_t numEvents() const { return next_ec_id_; } - - /// 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; } - - 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. - */ - virtual void fire(const EventContextRef& ec, EventTime time = 0) final; - - /// The internal fire method used by the typed EventPublisher. - virtual void fireCallback(const SubscriptionRef& sub, - const EventContextRef& ec) const = 0; - - /// 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. - EventContextID next_ec_id_; - - private: - EventPublisherPlugin(EventPublisherPlugin const&); - EventPublisherPlugin& operator=(EventPublisherPlugin const&); - - private: - /// Set ending to True to cause event type run loops to finish. - bool ending_; - - /// Set to indicate whether the event run loop ever started. - bool started_; - - /// A lock for incrementing the next EventContextID. - boost::mutex ec_id_lock_; - - private: - /// Enable event factory "callins" through static publisher callbacks. - friend class EventFactory; - - private: - FRIEND_TEST(EventsTests, test_event_pub); - FRIEND_TEST(EventsTests, test_fire_event); -}; - -/** - * @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 -class EventPublisher : public EventPublisherPlugin { - public: - /// A nested helper typename for the templated SubscriptionContextRef. - typedef typename std::shared_ptr SCRef; - /// A nested helper typename for the templated EventContextRef. - typedef typename std::shared_ptr ECRef; - - public: - /// Up-cast a base EventContext reference to the templated ECRef. - static ECRef getEventContext(const EventContextRef& ec) { - return std::static_pointer_cast(ec); - } - - /// Up-cast a base SubscriptionContext reference to the templated SCRef. - static SCRef getSubscriptionContext(const SubscriptionContextRef& sc) { - return std::static_pointer_cast(sc); - } - - /// Create a EventContext based on the templated type. - static ECRef createEventContext() { return std::make_shared(); } - - /// Create a SubscriptionContext based on the templated type. - static SCRef createSubscriptionContext() { return std::make_shared(); } - - 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 { - auto pub_sc = getSubscriptionContext(sub->context); - auto pub_ec = getEventContext(ec); - if (shouldFire(pub_sc, pub_ec) && sub->callback != nullptr) { - sub->callback(pub_ec, sub->user_data); - } - } - - 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 { - return true; - } - - private: - FRIEND_TEST(EventsTests, test_event_sub_subscribe); - FRIEND_TEST(EventsTests, test_event_sub_context); - FRIEND_TEST(EventsTests, test_fire_event); -}; - -class EventSubscriberPlugin : public Plugin { - protected: - /** - * @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 r An osquery Row element. - * @param time The time the added event occurred. - * - * @return Was the element added to the backing store. - */ - virtual Status add(Row& r, EventTime event_time) final; - - /** - * @brief Return all events added by this EventSubscriber within start, stop. - * - * This is used internally (for the most part) by EventSubscriber::genTable. - * - * @param start Inclusive lower bound time limit. - * @param stop Inclusive upper bound time limit. - * @return Set of event rows matching time limits. - */ - virtual QueryData get(EventTime start, EventTime stop); - - 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. - * - * @return List of EventID, EventTime%s - */ - std::vector getRecords(const std::set& indexes); - - /** - * @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. - */ - EventID 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 list_key optional key to bind to a specific index binning. - * - * @return List of 'index.step' index strings. - */ - std::set getIndexes(EventTime start, - EventTime stop, - int list_key = 0); - - /** - * @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& indexes, - const std::vector& expirations); - /// Expire all datums within a bin. - void expireRecords(const std::string& list_type, - const std::string& index, - bool all); - - /** - * @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 eid A unique EventID. - * @param time The time when this EventID%'s event occurred. - * - * @return Were the indexes recorded. - */ - Status recordEvent(EventID& eid, EventTime time); - - 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) {} - virtual ~EventSubscriberPlugin() {} - - /** - * @brief Suggested entrypoint for table generation. - * - * The EventSubscriber is a convention that removes a lot of boilerplate event - * 'subscribing' and acting. The `genTable` static entrypoint is the - * suggested method for table specs. - * - * @return The query-time table data, retrieved from a backing store. - */ - virtual QueryData genTable(QueryContext& context) __attribute__((used)); - - 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. - */ - virtual EventPublisherID& dbNamespace() const = 0; - - /// Disable event expiration for this subscriber. - void doNotExpire() { expire_events_ = false; } - - private: - EventSubscriberPlugin(EventSubscriberPlugin const&); - EventSubscriberPlugin& operator=(EventSubscriberPlugin const&); - - private: - Status setUp() { return Status(0, "Setup never used"); } - - private: - /// Do not respond to periodic/scheduled/triggered event expiration requests. - bool expire_events_; - - /// Events before the expire_time_ are invalid and will be purged. - EventTime expire_time_; - - /** - * @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_; - - /// Lock used when incrementing the EventID database index. - boost::mutex event_id_lock_; - - /// Lock used when recording an EventID and time into search bins. - boost::mutex event_record_lock_; - - 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); -}; - -/** - * @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 event_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 - static Status registerEventSubscriber() { - auto sub = std::make_shared(); - 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 The string for an EventPublisher receiving the Subscription. - * @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(EventPublisherID& type_id, - EventSubscriberID& name_id, - const SubscriptionContextRef& sc, - EventCallback cb = 0, - void* user_data = nullptr); - - /// Add a Subscription using a caller Subscription instance. - static Status addSubscription(EventPublisherID& type_id, - const SubscriptionRef& subscription); - - /// Get the total number of Subscription%s across ALL EventPublisher%s. - static size_t numSubscriptions(EventPublisherID& 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 event_pub The string label for the EventPublisher. - * - * @return Did the EventPublisher deregister cleanly. - */ - static Status deregisterEventPublisher(const EventPublisherRef& pub); - - /// Deregister an EventPublisher by EventPublisherID. - static Status deregisterEventPublisher(EventPublisherID& type_id); - - /// Return an instance to a registered EventPublisher. - static EventPublisherRef getEventPublisher(EventPublisherID& pub); - - /// Return an instance to a registered EventSubscriber. - static EventSubscriberRef getEventSubscriber(EventSubscriberID& sub); - - /// Check if an event subscriber exists. - static bool exists(EventSubscriberID& sub); - - /// Return a list of publisher types, these are their registry names. - static std::vector publisherTypes(); - - /// Return a list of subscriber registry names, - static std::vector subscriberNames(); - - public: - /// The dispatched event thread's entry-point (if needed). - static Status run(EventPublisherID& 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 - static void fire(const EventContextRef& ec) { - auto event_pub = getEventPublisher(getType()); - 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 - static EventPublisherID getType() { - auto pub = std::make_shared(); - 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 should_end Reset the "is ending" state if False. - */ - static void end(bool join = false); - - private: - /// An EventFactory will exist for the lifetime of the application. - EventFactory() {} - EventFactory(EventFactory const&); - EventFactory& operator=(EventFactory const&); - ~EventFactory() {} - - private: - /// Set of registered EventPublisher instances. - std::map event_pubs_; - - /// Set of instantiated EventSubscriber subscriptions. - std::map event_subs_; - - /// Set of running EventPublisher run loop threads. - std::vector > threads_; -}; - -/** - * @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 EventSubscriber : public EventSubscriberPlugin { - protected: - typedef typename PUB::SCRef SCRef; - typedef typename PUB::ECRef ECRef; - - 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, "OK"); } - - 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 - void subscribe(Status (T::*entry)(const std::shared_ptr&, const void*), - const SubscriptionContextRef& sc, - void* user_data) { - // Up-cast the EventSubscriber to the caller. - auto sub = dynamic_cast(this); - // Down-cast the pointer to the member function. - auto base_entry = - reinterpret_cast( - entry); - // 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. - EventFactory::addSubscription(getType(), sub->getName(), sc, cb, user_data); - } - - /** - * @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'. - */ - EventPublisherID& getType() const { - static EventPublisherID type = EventFactory::getType(); - return type; - } - - /// See getType for lookup rational. - EventPublisherID& dbNamespace() const { - static EventPublisherID _ns = getType() + '.' + getName(); - return _ns; - } - - 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. - */ - EventSubscriberState state() const { return state_; } - - /// Set the subscriber state. - void state(EventSubscriberState state) { state_ = state; } - - EventSubscriber() : EventSubscriberPlugin(), state_(SUBSCRIBER_NONE) {} - - private: - /// The event subscriber's run state. - EventSubscriberState state_; - - private: - FRIEND_TEST(EventsTests, test_event_sub); - FRIEND_TEST(EventsTests, test_event_sub_subscribe); - FRIEND_TEST(EventsTests, test_event_sub_context); -}; - -/// Iterate the event publisher registry and create run loops for each using -/// the event factory. -void attachEvents(); - -/// Sleep in a boost::thread interruptible state. -void publisherSleep(size_t milli); - -CREATE_REGISTRY(EventPublisherPlugin, "event_publisher"); -CREATE_REGISTRY(EventSubscriberPlugin, "event_subscriber"); -} diff --git a/include/osquery/extensions.h b/include/osquery/extensions.h deleted file mode 100644 index cd2e45c..0000000 --- a/include/osquery/extensions.h +++ /dev/null @@ -1,169 +0,0 @@ -/* - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#pragma once - -#include -#include -#include - -namespace osquery { - -DECLARE_int32(worker_threads); -DECLARE_string(extensions_socket); -DECLARE_string(extensions_autoload); -DECLARE_string(extensions_timeout); -DECLARE_bool(disable_extensions); - -/// A millisecond internal applied to extension initialization. -extern const size_t kExtensionInitializeLatencyUS; - -/** - * @brief Helper struct for managing extenion metadata. - * - * This structure should match the members of Thrift's InternalExtensionInfo. - */ -struct ExtensionInfo { - std::string name; - std::string version; - std::string min_sdk_version; - std::string sdk_version; -}; - -typedef std::map ExtensionList; - -inline std::string getExtensionSocket( - RouteUUID uuid, const std::string& path = FLAGS_extensions_socket) { - if (uuid == 0) { - return path; - } else { - return path + "." + std::to_string(uuid); - } -} - -/// External (extensions) SQL implementation of the osquery query API. -Status queryExternal(const std::string& query, QueryData& results); - -/// External (extensions) SQL implementation of the osquery getQueryColumns API. -Status getQueryColumnsExternal(const std::string& q, TableColumns& columns); - -/// External (extensions) SQL implementation plugin provider for "sql" registry. -class ExternalSQLPlugin : SQLPlugin { - public: - Status query(const std::string& q, QueryData& results) const { - return queryExternal(q, results); - } - - Status getQueryColumns(const std::string& q, TableColumns& columns) const { - return getQueryColumnsExternal(q, columns); - } -}; - -/// Status get a list of active extenions. -Status getExtensions(ExtensionList& extensions); - -/// Internal getExtensions using a UNIX domain socket path. -Status getExtensions(const std::string& manager_path, - ExtensionList& extensions); - -/// Ping an extension manager or extension. -Status pingExtension(const std::string& path); - -/** - * @brief Request the extensions API to autoload any appropriate extensions. - * - * Extensions may be 'autoloaded' using the `extensions_autoload` command line - * argument. loadExtensions should be called before any plugin or registry item - * is used. This allows appropriate extensions to expose plugin requirements. - * - * An 'appropriate' extension is one within the `extensions_autoload` search - * path with file ownership equivilent or greater (root) than the osquery - * process requesting autoload. - */ -void loadExtensions(); - -/** - * @brief Load extensions from a delimited search path string. - * - * @param paths A colon-delimited path variable, e.g: '/path1:/path2'. - */ -Status loadExtensions(const std::string& loadfile); - -/** - * @brief Request the extensions API to autoload any appropriate modules. - * - * Extension modules are shared libraries that add Plugins to the osquery - * core's registry at runtime. - */ -void loadModules(); - -/** - * @brief Load extenion modules from a delimited search path string. - * - * @param paths A colon-delimited path variable, e.g: '/path1:/path2'. - */ -Status loadModules(const std::string& loadfile); - -/// Load all modules in a direcotry. -Status loadModuleFile(const std::string& path); - -/** - * @brief Call a Plugin exposed by an Extension Registry route. - * - * This is mostly a Registry%-internal method used to call an ExtensionHandler - * call API if a Plugin is requested and had matched an Extension route. - * - * @param uuid Route UUID of the matched Extension - * @param registry The string name for the registry. - * @param item A string identifier for this registry item. - * @param request The plugin request input. - * @param response The plugin response output. - * @return Success indicates Extension API call success and Extension's - * Registry::call success. - */ -Status callExtension(const RouteUUID uuid, - const std::string& registry, - const std::string& item, - const PluginRequest& request, - PluginResponse& response); - -/// Internal callExtension implementation using a UNIX domain socket path. -Status callExtension(const std::string& extension_path, - const std::string& registry, - const std::string& item, - const PluginRequest& request, - PluginResponse& response); - -/// The main runloop entered by an Extension, start an ExtensionRunner thread. -Status startExtension(const std::string& name, const std::string& version); - -/// The main runloop entered by an Extension, start an ExtensionRunner thread. -Status startExtension(const std::string& name, - const std::string& version, - const std::string& min_sdk_version); - -/// Internal startExtension implementation using a UNIX domain socket path. -Status startExtension(const std::string& manager_path, - const std::string& name, - const std::string& version, - const std::string& min_sdk_version, - const std::string& sdk_version); - -/// Start an ExtensionWatcher thread. -Status startExtensionWatcher(const std::string& manager_path, - size_t interval, - bool fatal); - -/// Start an ExtensionManagerRunner thread. -Status startExtensionManager(); - -/// Internal startExtensionManager implementation. -Status startExtensionManager(const std::string& manager_path); -} diff --git a/include/osquery/filesystem.h b/include/osquery/filesystem.h deleted file mode 100644 index 8c4ef80..0000000 --- a/include/osquery/filesystem.h +++ /dev/null @@ -1,338 +0,0 @@ -/* - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#pragma once - -#include -#include -#include -#include - -#include -#include - -#include - -namespace osquery { - -/// Globbing directory traversal function recursive limit. -typedef unsigned short GlobLimits; - -enum { - GLOB_FILES = 0x1, - GLOB_FOLDERS = 0x2, - GLOB_ALL = GLOB_FILES | GLOB_FOLDERS, -}; - -/// Globbing wildcard character. -const std::string kSQLGlobWildcard = "%"; -/// Globbing wildcard recursive character (double wildcard). -const std::string kSQLGlobRecursive = kSQLGlobWildcard + kSQLGlobWildcard; - -/** - * @brief Read a file from disk. - * - * @param path the path of the file that you would like to read. - * @param content a reference to a string which will be populated with the - * contents of the path indicated by the path parameter. - * @param dry_run do not actually read the file content. - * - * @return an instance of Status, indicating success or failure. - */ -Status readFile(const boost::filesystem::path& path, - std::string& content, - bool dry_run = false); - -/** - * @brief Return the status of an attempted file read. - * - * @param path the path of the file that you would like to read. - * - * @return success iff the file would have been read. On success the status - * message is the complete/absolute path. - */ -Status readFile(const boost::filesystem::path& path); - -/** - * @brief Write text to disk. - * - * @param path the path of the file that you would like to write. - * @param content the text that should be written exactly to disk. - * @param permissions the filesystem permissions to request when opening. - * @param force_permissions always `chmod` the path after opening. - * - * @return an instance of Status, indicating success or failure. - */ -Status writeTextFile(const boost::filesystem::path& path, - const std::string& content, - int permissions = 0660, - bool force_permissions = false); - -/// Check if a path is writable. -Status isWritable(const boost::filesystem::path& path); - -/// Check if a path is readable. -Status isReadable(const boost::filesystem::path& path); - -/** - * @brief A helper to check if a path exists on disk or not. - * - * @param path Target path. - * - * @return The code of the Status instance will be -1 if no input was supplied, - * assuming the caller is not aware of how to check path-getter results. - * The code will be 0 if the path does not exist on disk and 1 if the path - * does exist on disk. - */ -Status pathExists(const boost::filesystem::path& path); - -/** - * @brief List all of the files in a specific directory, non-recursively. - * - * @param path the path which you would like to list. - * @param results a non-const reference to a vector which will be populated - * with the directory listing of the path param, assuming that all operations - * completed successfully. - * - * @return an instance of Status, indicating success or failure. - */ -Status listFilesInDirectory(const boost::filesystem::path& path, - std::vector& results, - bool ignore_error = 1); - -/** - * @brief List all of the directories in a specific directory, non-recursively. - * - * @param path the path which you would like to list - * @param results a non-const reference to a vector which will be populated - * with the directory listing of the path param, assuming that all operations - * completed successfully. - * - * @return an instance of Status, indicating success or failure. - */ -Status listDirectoriesInDirectory(const boost::filesystem::path& path, - std::vector& results, - bool ignore_error = 1); - -/** - * @brief Given a filesystem globbing patten, resolve all matching paths. - * - * @code{.cpp} - * std::vector results; - * auto s = resolveFilePattern("/Users/marpaia/Downloads/%", results); - * if (s.ok()) { - * for (const auto& result : results) { - * LOG(INFO) << result; - * } - * } - * @endcode - * - * @param pattern filesystem globbing pattern. - * @param results output vector of matching paths. - * - * @return an instance of Status, indicating success or failure. - */ -Status resolveFilePattern(const boost::filesystem::path& pattern, - std::vector& results); - -/** - * @brief Given a filesystem globbing patten, resolve all matching paths. - * - * See resolveFilePattern, but supply a limitation to request only directories - * or files that match the path. - * - * @param pattern filesystem globbing pattern. - * @param results output vector of matching paths. - * @param setting a bit list of match types, e.g., files, folders. - * - * @return an instance of Status, indicating success or failure. - */ -Status resolveFilePattern(const boost::filesystem::path& pattern, - std::vector& results, - GlobLimits setting); - -/** - * @brief Transform a path with SQL wildcards to globbing wildcard. - * - * SQL uses '%' as a wildcard matching token, and filesystem globbing uses '*'. - * In osquery-internal methods the filesystem character is used. This helper - * method will perform the correct preg/escape and replace. - * - * This has a side effect of canonicalizing paths up to the first wildcard. - * For example: /tmp/% becomes /private/tmp/% on OS X systems. And /tmp/%. - * - * @param pattern the input and output filesystem glob pattern. - */ -void replaceGlobWildcards(std::string& pattern); - -/** - * @brief Get directory portion of a path. - * - * @param path input path, either a filename or directory. - * @param dirpath output path set to the directory-only path. - * - * @return If the input path was a directory this will indicate failure. One - * should use `isDirectory` before. - */ -Status getDirectory(const boost::filesystem::path& path, - boost::filesystem::path& dirpath); - -/// Attempt to remove a directory path. -Status remove(const boost::filesystem::path& path); - -/** - * @brief Check if an input path is a directory. - * - * @param path input path, either a filename or directory. - * - * @return If the input path was a directory. - */ -Status isDirectory(const boost::filesystem::path& path); - -/** - * @brief Return a vector of all home directories on the system. - * - * @return a vector of string paths containing all home directories. - */ -std::set getHomeDirectories(); - -/** - * @brief Check the permissions of a file and its directory. - * - * 'Safe' implies the directory is not a /tmp-like directory in that users - * cannot control super-user-owner files. The file should be owned by the - * process's UID or the file should be owned by root. - * - * @param dir the directory to check `/tmp` mode. - * @param path a path to a file to check. - * @param executable true if the file must also be executable. - * - * @return true if the file is 'safe' else false. - */ -bool safePermissions(const std::string& dir, - const std::string& path, - bool executable = false); - -/** - * @brief osquery may use local storage in a user-protected "home". - * - * Return a standard path to an "osquery" home directory. This path may store - * a protected extensions socket, backing storage database, and debug logs. - */ -const std::string& osqueryHomeDirectory(); - -/// Return bit-mask-style permissions. -std::string lsperms(int mode); - -/** - * @brief Parse a JSON file on disk into a property tree. - * - * @param path the path of the JSON file. - * @param tree output property tree. - * - * @return an instance of Status, indicating success or failure if malformed. - */ -Status parseJSON(const boost::filesystem::path& path, - boost::property_tree::ptree& tree); - -/** - * @brief Parse JSON content into a property tree. - * - * @param path JSON string data. - * @param tree output property tree. - * - * @return an instance of Status, indicating success or failure if malformed. - */ -Status parseJSONContent(const std::string& content, - boost::property_tree::ptree& tree); - -#ifdef __APPLE__ -/** - * @brief Parse a property list on disk into a property tree. - * - * @param path the input path to a property list. - * @param tree the output property tree. - * - * @return an instance of Status, indicating success or failure if malformed. - */ -Status parsePlist(const boost::filesystem::path& path, - boost::property_tree::ptree& tree); - -/** - * @brief Parse property list content into a property tree. - * - * @param content the input string-content of a property list. - * @param tree the output property tree. - * - * @return an instance of Status, indicating success or failure if malformed. - */ -Status parsePlistContent(const std::string& content, - boost::property_tree::ptree& tree); -#endif - -#ifdef __linux__ -/** - * @brief Iterate over `/proc` process, returns a list of pids. - * - * @param processes output list of process pids as strings (int paths in proc). - * - * @return an instance of Status, indicating success or failure. - */ -Status procProcesses(std::set& processes); - -/** - * @brief Iterate over a `/proc` process's descriptors, return a list of fds. - * - * @param process a string pid from proc. - * @param descriptors output list of descriptor numbers as strings. - * - * @return status of iteration, failure if the process path did not exist. - */ -Status procDescriptors(const std::string& process, - std::map& descriptors); - -/** - * @brief Read a descriptor's virtual path. - * - * @param process a string pid from proc. - * @param descriptor a string descriptor number for a proc. - * @param result output variable with value of link. - * - * @return status of read, failure on permission error or filesystem error. - */ -Status procReadDescriptor(const std::string& process, - const std::string& descriptor, - std::string& result); - -/** - * @brief Read bytes from Linux's raw memory. - * - * Most Linux kernels include a device node /dev/mem that allows privileged - * users to map or seek/read pages of physical memory. - * osquery discourages the use of physical memory reads for security and - * performance reasons and must first try safer methods for data parsing - * such as /sys and /proc. - * - * A platform user may disable physical memory reads: - * --disable_memory=true - * This flag/option will cause readRawMemory to forcefully fail. - * - * @param base The absolute memory address to read from. This does not need - * to be page aligned, readRawMem will take care of alignment and only - * return the requested start address and size. - * @param length The length of the buffer with a max of 0x10000. - * @param buffer The output buffer, caller is responsible for resources if - * readRawMem returns success. - * @return status The status of the read. - */ -Status readRawMem(size_t base, size_t length, void** buffer); - -#endif -} diff --git a/include/osquery/flags.h b/include/osquery/flags.h deleted file mode 100644 index 8eb17d9..0000000 --- a/include/osquery/flags.h +++ /dev/null @@ -1,211 +0,0 @@ -/* - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#pragma once - -#include - -#include - -#define STRIP_FLAG_HELP 1 -#include - -#include - -#define GFLAGS_NAMESPACE google - -namespace boost { -/// We define a lexical_cast template for boolean for Gflags boolean string -/// values. -template <> -bool lexical_cast(const std::string& arg); - -template <> -std::string lexical_cast(const bool& b); -} - -namespace osquery { - -struct FlagDetail { - std::string description; - bool shell; - bool external; - bool cli; - bool hidden; -}; - -struct FlagInfo { - std::string type; - std::string description; - std::string default_value; - std::string value; - FlagDetail detail; -}; - -/** - * @brief A small tracking wrapper for options, binary flags. - * - * The osquery-specific gflags-like options define macro `FLAG` uses a Flag - * instance to track the options data. - */ -class Flag { - public: - /* - * @brief Create a new flag. - * - * @param name The 'name' or the options switch data. - * @param flag Flag information filled in using the helper macro. - * - * @return A mostly needless flag instance. - */ - static int create(const std::string& name, const FlagDetail& flag); - - /// Create a Gflags alias to name, using the Flag::getValue accessor. - static int createAlias(const std::string& alias, const FlagDetail& flag); - - static Flag& instance() { - static Flag f; - return f; - } - - private: - /// Keep the ctor private, for accessing through `add` wrapper. - Flag() {} - virtual ~Flag() {} - - Flag(Flag const&); - void operator=(Flag const&); - - public: - /// The public flags instance, usable when parsing `--help`. - static std::map flags(); - - /* - * @brief Access value for a flag name. - * - * @param name the flag name. - * @param value output parameter filled with the flag value on success. - * @return status of the flag did exist. - */ - static Status getDefaultValue(const std::string& name, std::string& value); - - /* - * @brief Check if flag value has been overridden. - * - * @param name the flag name. - * @return is the flag set to its default value. - */ - static bool isDefault(const std::string& name); - - /* - * @brief Update the flag value by string name, - * - * @param name the flag name. - * @parma value the new value. - * @return if the value was updated. - */ - static Status updateValue(const std::string& name, const std::string& value); - - /* - * @brief Get the value of an osquery flag. - * - * @param name the flag name. - */ - static std::string getValue(const std::string& name); - - /* - * @brief Get the type as a string of an osquery flag. - * - * @param name the flag name. - */ - static std::string getType(const std::string& name); - - /* - * @brief Get the description as a string of an osquery flag. - * - * @param name the flag name. - */ - static std::string getDescription(const std::string& name); - - /* - * @brief Print help-style output to stdout for a given flag set. - * - * @param shell Only print shell flags. - * @param external Only print external flags (from extensions). - */ - static void printFlags(bool shell = false, - bool external = false, - bool cli = false); - - private: - std::map flags_; - std::map aliases_; -}; - -/** - * @brief Helper accessor/assignment alias class to support deprecated flags. - * - * This templated class wraps Flag::updateValue and Flag::getValue to 'alias' - * a deprecated flag name as the updated name. The helper macro FLAG_ALIAS - * will create a global variable instances of this wrapper using the same - * Gflags naming scheme to prevent collisions and support existing callsites. - */ -template -class FlagAlias { - public: - FlagAlias& operator=(T const& v) { - Flag::updateValue(name_, boost::lexical_cast(v)); - return *this; - } - - operator T() const { return boost::lexical_cast(Flag::getValue(name_)); } - - FlagAlias(const std::string& alias, - const std::string& type, - const std::string& name, - T* storage) - : name_(name) {} - - private: - std::string name_; -}; -} - -/* - * @brief Replace gflags' `DEFINE_type` macros to track osquery flags. - * - * @param type The `_type` symbol portion of the gflags define. - * @param name The name symbol passed to gflags' `DEFINE_type`. - * @param value The default value, use a C++ literal. - * @param desc A string literal used for help display. - */ -#define OSQUERY_FLAG(t, n, v, d, s, e, c, h) \ - DEFINE_##t(n, v, d); \ - namespace flags { \ - const int flag_##n = Flag::create(#n, {d, s, e, c, h}); \ - } - -#define FLAG(t, n, v, d) OSQUERY_FLAG(t, n, v, d, 0, 0, 0, 0) -#define SHELL_FLAG(t, n, v, d) OSQUERY_FLAG(t, n, v, d, 1, 0, 0, 0) -#define EXTENSION_FLAG(t, n, v, d) OSQUERY_FLAG(t, n, v, d, 0, 1, 0, 0) -#define CLI_FLAG(t, n, v, d) OSQUERY_FLAG(t, n, v, d, 0, 0, 1, 0) -#define HIDDEN_FLAG(t, n, v, d) OSQUERY_FLAG(t, n, v, d, 0, 0, 0, 1) - -#define OSQUERY_FLAG_ALIAS(t, a, n, s, e) \ - FlagAlias FLAGS_##a(#a, #t, #n, &FLAGS_##n); \ - namespace flags { \ - static GFLAGS_NAMESPACE::FlagRegisterer oflag_##a( \ - #a, #t, #a, &FLAGS_##n, &FLAGS_##n); \ - const int flag_alias_##a = Flag::createAlias(#a, {#n, s, e, 0, 1}); \ - } - -#define FLAG_ALIAS(t, a, n) OSQUERY_FLAG_ALIAS(t, a, n, 0, 0) -#define SHELL_FLAG_ALIAS(t, a, n) _OSQUERY_FLAG_ALIAS(t, a, n, 1, 0) -#define EXTENSION_FLAG_ALIAS(a, n) OSQUERY_FLAG_ALIAS(std::string, a, n, 0, 1) diff --git a/include/osquery/hash.h b/include/osquery/hash.h deleted file mode 100644 index b6cc5ae..0000000 --- a/include/osquery/hash.h +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include - -namespace osquery { - -/** - * @brief The supported hashing algorithms in osquery - * - * These are usually used as a constructor argument to osquery::Hash - */ -enum HashType { - HASH_TYPE_MD5 = 2, - HASH_TYPE_SHA1 = 4, - HASH_TYPE_SHA256 = 8, -}; - -/** - * @brief Hash is a general utility class for hashing content - * - * @code{.cpp} - * Hash my_hash(HASH_TYPE_SHA256); - * my_hash.update(my_buffer, my_buffer_size); - * std::cout << my_hash.digest(); - * @endcode - * - */ -class Hash { - public: - /** - * @brief Hash constructor - * - * The hash class should be initialized with one of osquery::HashType as a - * constructor argument. - * - * @param algorithm The hashing algorithm which will be used to compute the - * hash - */ - explicit Hash(HashType algorithm); - - /** - * @brief Hash destructor - */ - ~Hash(); - - /** - * @brief Update the internal context buffer with additional content - * - * This method allows you to chunk up large content so that it doesn't all - * have to be loaded into memory at the same time - * - * @param buffer The buffer to be hashed - * @param size The size of the buffer to be hashed - */ - void update(const void* buffer, size_t size); - - /** - * @brief Compute the final hash and return it's result - * - * @return The final hash value - */ - std::string digest(); - - private: - /** - * @brief Private default constructor - * - * The osquery::Hash class should only ever be instantiated with a HashType - */ - Hash(){}; - - private: - /// The hashing algorithm which is used to compute the hash - HashType algorithm_; - - /// The buffer used to maintain the context and state of the hashing - /// operations - void* ctx_; - - /// The length of the hash to be returned - size_t length_; -}; - -/** - * @brief Compute a hash digest from an already allocated buffer. - * - * @param hash_type The osquery-supported hash algorithm. - * @param buffer A caller-controlled buffer. - * @param size The length of buffer in bytes. - * @return A string (hex) representation of the hash digest. - */ -std::string hashFromBuffer(HashType hash_type, const void* buffer, size_t size); - -/** - * @brief Compute a hash digest from the file content at a path. - * - * - * @param hash_type The osquery-supported hash algorithm. - * @param path Filesystem path, the hash target. - * @return A string (hex) representation of the hash digest. - */ -std::string hashFromFile(HashType hash_type, const std::string& path); -} diff --git a/include/osquery/logger.h b/include/osquery/logger.h deleted file mode 100644 index 0ef8b15..0000000 --- a/include/osquery/logger.h +++ /dev/null @@ -1,296 +0,0 @@ -/* - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#pragma once - -#include -#include - -#include - -#include -#include -#include - -namespace osquery { - -DECLARE_bool(disable_logging); -DECLARE_string(logger_plugin); - -/** - * @brief An internal severity set mapping to Glog's LogSeverity levels. - */ -enum StatusLogSeverity { - O_INFO = 0, - O_WARNING = 1, - O_ERROR = 2, - O_FATAL = 3, -}; - -/// An intermediate status log line. -struct StatusLogLine { - public: - /// An integer severity level mimicing Glog's. - StatusLogSeverity severity; - /// The name of the file emitting the status log. - std::string filename; - /// The line of the file emitting the status log. - int line; - /// The string-formatted status message. - std::string message; -}; - -/** - * @brief Helper logging macro for table-generated verbose log lines. - * - * Since logging in tables does not always mean a critical warning or error - * but more likely a parsing or expected edge-case, we provide a TLOG. - * - * The tool user can set within config or via the CLI what level of logging - * to tolerate. It's the table developer's job to assume consistency in logging. - */ -#define TLOG VLOG(1) - -/** - * @brief Prepend a reference number to the log line. - * - * A reference number is an external-search helper for somewhat confusing or - * seeminly-critical log lines. - */ -#define RLOG(n) "[Ref #" #n "] " - -/** - * @brief Superclass for the pluggable logging facilities. - * - * In order to make the logging of osquery results and inline debug, warning, - * error status easy to integrate into your environment, we take advantage of - * a plugin interface which allows you to integrate osquery with your internal - * large-scale logging infrastructure. - * - * You may use flume, splunk, syslog, scribe, etc. In order to use your - * specific upstream logging systems, one simply needs to create a custom - * subclass of LoggerPlugin. That subclass should at least implement the - * LoggerPlugin::logString method. - * - * Consider the following example: - * - * @code{.cpp} - * class TestLoggerPlugin : public LoggerPlugin { - * public: - * osquery::Status logString(const std::string& s) { - * int i = 0; - * internal::logStringToFlume(s, i); - * std::string message; - * if (i == 0) { - * message = "OK"; - * } else { - * message = "Failed"; - * } - * return osquery::Status(i, message); - * } - * }; - * - * REGISTER(TestLoggerPlugin, "logger", "test"); - * @endcode - */ -class LoggerPlugin : public Plugin { - public: - /// The LoggerPlugin PluginRequest action router. - Status call(const PluginRequest& request, PluginResponse& response); - - protected: - /** @brief Virtual method which should implement custom logging. - * - * LoggerPlugin::logString should be implemented by a subclass of - * LoggerPlugin which needs to log a string in a custom way. - * - * @return an instance of osquery::Status which indicates the success or - * failure of the operation. - */ - virtual Status logString(const std::string& s) = 0; - - /** - * @brief Initialize the logger with the name of the binary and any status - * logs generated between program launch and logger start. - * - * The logger initialization is called once CLI flags have been parsed, the - * registry items are constructed, extension routes broadcasted and extension - * plugins discovered (as a logger may be an extension plugin) and the config - * has been loaded (which may include additional CLI flag-options). - * - * All of these actions may have generated VERBOSE, INFO, WARNING, or ERROR - * logs. The internal logging facility, Glog, collects these intermediate - * status logs and a customized log sink buffers them until the active - * osquery logger's `init` method is called. - * - * The return status of `init` is very important. If a success is returned - * then the Glog log sink stays active and now forwards every status log - * to the logger's `logStatus` method. If a failure is returned this means - * the logger does not support status logging and Glog should continue - * as the only status log sink. - * - * @param binary_name The string name of the process (argv[0]). - * @param log The set of status (INFO, WARNING, ERROR) logs generated before - * the logger's `init` method was called. - * @return Status success if the logger will continue to handle status logs - * using `logStatus` or failure if status logging is not supported. - */ - virtual Status init(const std::string& binary_name, - const std::vector& log) { - return Status(1, "Status logs are not supported by this logger"); - } - - /** - * @brief If the active logger's `init` method returned success then Glog - * log lines will be collected, and forwarded to `logStatus`. - * - * `logStatus` and `init` are tightly coupled. Glog log lines will ONLY be - * forwarded to `logStatus` if the logger's `init` method returned success. - * - * @param log A vector of parsed Glog log lines. - * @return Status non-op indicating success or failure. - */ - virtual Status logStatus(const std::vector& log) { - return Status(1, "Not enabled"); - } - - /** - * @brief Optionally handle snapshot query results separately from events. - * - * If a logger plugin wants to write snapshot query results (potentially - * large amounts of data) to a specific sink it should implement logSnapshot. - * Otherwise the serialized log item data will be forwarded to logString. - * - * @param s A special log item will complete results from a query. - * @return log status - */ - virtual Status logSnapshot(const std::string& s) { return logString(s); } - - /// An optional health logging facility. - virtual Status logHealth(const std::string& s) { - return Status(1, "Not used"); - } -}; - -/// Set the verbose mode, changes Glog's sinking logic and will affect plugins. -void setVerboseLevel(); - -/// Start status logging to a buffer until the logger plugin is online. -void initStatusLogger(const std::string& name); - -/** - * @brief Initialize the osquery Logger facility by dumping the buffered status - * logs and configurating status log forwarding. - * - * initLogger will disable the `BufferedLogSink` facility, dump any status logs - * emitted between process start and this init call, then configure the new - * logger facility to receive status logs. - * - * The `forward_all` control is used when buffering logs in extensions. - * It is fine if the logger facility in the core app does not want to receive - * status logs, but this is NOT an option in extensions/modules. All status - * logs must be forwarded to the core. - * - * @param name The process name. - * @param forward_all Override the LoggerPlugin::init forwarding decision. - */ -void initLogger(const std::string& name, bool forward_all = false); - -/** - * @brief Log a string using the default logger receiver. - * - * Note that this method should only be used to log results. If you'd like to - * log normal osquery operations, use Google Logging. - * - * @param s the string to log - * @param category a category/metadata key - * - * @return Status indicating the success or failure of the operation - */ -Status logString(const std::string& message, const std::string& category); - -/** - * @brief Log a string using a specific logger receiver. - * - * Note that this method should only be used to log results. If you'd like to - * log normal osquery operations, use Google Logging. - * - * @param message the string to log - * @param category a category/metadata key - * @param receiver a string representing the log receiver to use - * - * @return Status indicating the success or failure of the operation - */ -Status logString(const std::string& message, - const std::string& category, - const std::string& receiver); - -/** - * @brief Log results of scheduled queries to the default receiver - * - * @param item a struct representing the results of a scheduled query - * - * @return Status indicating the success or failure of the operation - */ -Status logQueryLogItem(const QueryLogItem& item); - -/** - * @brief Log results of scheduled queries to a specified receiver - * - * @param item a struct representing the results of a scheduled query - * @param receiver a string representing the log receiver to use - * - * @return Status indicating the success or failure of the operation - */ -Status logQueryLogItem(const QueryLogItem& item, const std::string& receiver); - -/** - * @brief Log raw results from a query (or a snapshot scheduled query). - * - * @param results the unmangled results from the query planner. - * - * @return Status indicating the success or failure of the operation - */ -Status logSnapshotQuery(const QueryLogItem& item); - -/** - * @brief Log the worker's health along with health of each query. - * - * @param results the query results from the osquery schedule appended with a - * row of health from the worker. - * - * @return Status indicating the success or failure of the operation - */ -Status logHealthStatus(const QueryLogItem& item); - -/** - * @brief Sink a set of buffered status logs. - * - * When the osquery daemon uses a watcher/worker set, the watcher's status logs - * are accumulated in a buffered log sink. Well-performing workers should have - * the set of watcher status logs relayed and sent to the configured logger - * plugin. - * - * Status logs from extensions will be forwarded to the extension manager (core) - * normally, but the watcher does not receive or send registry requests. - * Extensions, the registry, configuration, and optional config/logger plugins - * are all protected as a monitored worker. - */ -void relayStatusLogs(); - -/** - * @brief Logger plugin registry. - * - * This creates an osquery registry for "logger" which may implement - * LoggerPlugin. Only strings are logged in practice, and LoggerPlugin provides - * a helper member for transforming PluginRequest%s to strings. - */ -CREATE_REGISTRY(LoggerPlugin, "logger"); -} diff --git a/include/osquery/notification.h b/include/osquery/notification.h deleted file mode 100644 index 4f33588..0000000 --- a/include/osquery/notification.h +++ /dev/null @@ -1,57 +0,0 @@ -/* - * 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 - */ - - -/** - * @file notification.h - * @brief Notify to registered stuffs when event-callback called - */ - - -#pragma once - -#include -#include - -#include - -#include -#include -#include - -namespace osquery { - -using NotifyCallback = Callback; - -class Notification final { -public: - static Notification& instance(); - - Status add(const std::string& table, const NotifyCallback& callback); - Status emit(const std::string& table, const Row& result) const; - -public: - Notification(const Notification&) = delete; - Notification& operator=(const Notification&) = delete; - -private: - Notification() = default; - ~Notification() = default; - - std::multimap callbacks; -}; - -} // namespace osquery diff --git a/include/osquery/registry.h b/include/osquery/registry.h deleted file mode 100644 index 1ea4587..0000000 --- a/include/osquery/registry.h +++ /dev/null @@ -1,737 +0,0 @@ -/* - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#pragma once - -#include -#include -#include -#include - -#include -#include - -#include - -namespace osquery { - -/** - * @brief A boilerplate code helper to create a registry given a name and - * plugin base class type. - * - * Registries are types of plugins, e.g., config, logger, table. They are - * defined with a string name and Plugin derived class. There is an expectation - * that any 'item' registered will inherit from the registry plugin-derived - * type. But there is NO type enforcement on that intermediate class. - * - * This boilerplate macro puts the registry into a 'registry' namespace for - * organization and create a global const int that may be instantiated - * in a header or implementation code without symbol duplication. - * The initialization is also boilerplate, whereas the Registry::create method - * (a whole-process-lived single instance object) creates and manages the - * registry instance. - * - * @param type A typename that derives from Plugin. - * @param name A string identifier for the registry. - */ -#define CREATE_REGISTRY(type, name) \ - namespace registry { \ - __constructor__ static void type##Registry() { \ - Registry::create(name); \ - } \ - } - -/** - * @brief A boilerplate code helper to create a registry given a name and - * plugin base class type. This 'lazy' registry does not run - * Plugin::setUp on its items, so the registry will do it. - * - * @param type A typename that derives from Plugin. - * @param name A string identifier for the registry. - */ -#define CREATE_LAZY_REGISTRY(type, name) \ - namespace registry { \ - __constructor__ static void type##Registry() { \ - Registry::create(name, true); \ - } \ - } - -/** - * @brief A boilerplate code helper to register a plugin. - * - * Like CREATE_REGISTRY, REGISTER creates a boilerplate global instance to - * create an instance of the plugin type within the whole-process-lived registry - * single instance. Registry items must derive from the `RegistryType` defined - * by the CREATE_REGISTRY and Registry::create call. - * - * @param type A typename that derives from the RegistryType. - * @param registry The string name for the registry. - * @param name A string identifier for this registry item. - */ -#define REGISTER(type, registry, name) \ - __constructor__ static void type##RegistryItem() { \ - Registry::add(registry, name); \ - } - -/// The same as REGISTER but prevents the plugin item from being broadcasted. -#define REGISTER_INTERNAL(type, registry, name) \ - __constructor__ static void type##RegistryItem() { \ - Registry::add(registry, name, true); \ - } - -/** - * @brief The request part of a plugin (registry item's) call. - * - * To use a plugin use Registry::call with a request and response. - * The request portion is usually simple and normally includes an "action" - * key where the value is the action you want to perform on the plugin. - * Refer to the registry's documentation for the actions supported by - * each of its plugins. - */ -typedef std::map PluginRequest; -/** - * @brief The response part of a plugin (registry item's) call. - * - * If a Registry::call succeeds it will fill in a PluginResponse. - * This response is a vector of key value maps. - */ -typedef std::vector PluginResponse; - -/// Registry routes are a map of item name to each optional PluginReponse. -typedef std::map RegistryRoutes; -/// An extension or core's broadcast includes routes from every Registry. -typedef std::map RegistryBroadcast; - -typedef uint16_t RouteUUID; -typedef std::function - AddExternalCallback; -typedef std::function RemoveExternalCallback; - -/// When a module is being initialized its information is kept in a transient -/// RegistryFactory lookup location. -struct ModuleInfo { - std::string path; - std::string name; - std::string version; - std::string sdk_version; -}; - -/// The call-in prototype for Registry modules. -typedef void (*ModuleInitalizer)(void); - -template -class PluginFactory {}; - -class Plugin : private boost::noncopyable { - public: - Plugin() : name_("unnamed") {} - virtual ~Plugin() {} - - public: - /// The plugin may perform some initialization, not required. - virtual Status setUp() { return Status(0, "Not used"); } - - /// The plugin may perform some tear down, release, not required. - virtual void tearDown() {} - - /// The plugin may publish route info (other than registry type and name). - virtual PluginResponse routeInfo() const { - PluginResponse info; - return info; - } - - /** - * @brief Plugins act by being called, using a request, returning a response. - * - * The plugin request is a thrift-serializable object. A response is optional - * but the API for using a plugin's call is defined by the registry. In most - * cases there are multiple supported call 'actions'. A registry type, or - * the plugin class, will define the action key and supported actions. - * - * @param request A plugin request input, including optional action. - * @param response A plugin response output. - * - * @return Status of the call, if the action was handled corrected. - */ - virtual Status call(const PluginRequest& request, PluginResponse& response) { - return Status(0, "Not used"); - } - - // Set the output request key to a serialized property tree. - // Used by the plugin to set a serialized PluginResponse. - static void setResponse(const std::string& key, - const boost::property_tree::ptree& tree, - PluginResponse& response); - - // Get a PluginResponse key as a property tree. - static void getResponse(const std::string& key, - const PluginResponse& response, - boost::property_tree::ptree& tree); - - /// Allow the plugin to introspect into the registered name (for logging). - void setName(const std::string& name) { name_ = name; } - - const std::string& getName() const { return name_; } - - /// Allow a specialized plugin type to act when an external plugin is - /// registered (e.g., a TablePlugin will attach the table name). - static Status addExternal(const std::string& name, - const PluginResponse& info) { - return Status(0, "Not used"); - } - - /// Allow a specialized plugin type to act when an external plugin is removed. - static void removeExternal(const std::string& name) {} - - protected: - std::string name_; - - private: - Plugin(Plugin const&); - Plugin& operator=(Plugin const&); -}; - -class RegistryHelperCore : private boost::noncopyable { - public: - explicit RegistryHelperCore(bool auto_setup = false) - : auto_setup_(auto_setup) {} - virtual ~RegistryHelperCore() {} - - /** - * @brief Remove a registry item by its identifier. - * - * @param item_name An identifier for this registry plugin. - */ - void remove(const std::string& item_name); - - RegistryRoutes getRoutes() const; - - /** - * @brief The only method a plugin user should call. - * - * Registry plugins are used internally and externally. They may belong - * to the process making the call or to an external process via a thrift - * transport. - * - * All plugin input and output must be serializable. The plugin types - * RegistryType usually exposes protected serialization methods for the - * data structures used by plugins (registry items). - * - * @param item_name The plugin identifier to call. - * @param request The plugin request, usually containing an action request. - * @param response If successful, the requested information. - * @return Success if the plugin was called, and response was filled. - */ - virtual Status call(const std::string& item_name, - const PluginRequest& request, - PluginResponse& response); - - Status add(const std::string& item_name, bool internal = false); - - /** - * @brief Allow a plugin to perform some setup functions when osquery starts. - * - * Doing work in a plugin constructor has unknown behavior. Plugins may - * be constructed at anytime during osquery's life, including global variable - * instantiation. To have a reliable state (aka, flags have been parsed, - * and logs are ready to stream), do construction work in Plugin::setUp. - * - * The registry `setUp` will iterate over all of its registry items and call - * their setup unless the registry is lazy (see CREATE_REGISTRY). - */ - virtual void setUp(); - - /// Facility method to check if a registry item exists. - bool exists(const std::string& item_name, bool local = false) const; - - /// Create a registry item alias for a given item name. - Status addAlias(const std::string& item_name, const std::string& alias); - - /// Get the registry item name for a given alias. - const std::string& getAlias(const std::string& alias) const; - - /// Facility method to list the registry item identifiers. - std::vector names() const; - - /// Facility method to count the number of items in this registry. - size_t count() const; - - /// Allow the registry to introspect into the registered name (for logging). - void setName(const std::string& name); - - /// Allow others to introspect into the registered name (for reporting). - const std::string& getName() const { return name_; } - - /// Check if a given plugin name is considered internal. - bool isInternal(const std::string& item_name) const; - - /// Allow others to introspect into the routes from extensions. - const std::map& getExternal() const { - return external_; - } - - /// Set an 'active' plugin to receive registry calls when no item name given. - Status setActive(const std::string& item_name); - - /// Get the 'active' plugin, return success with the active plugin name. - const std::string& getActive() const; - - protected: - /// The identifier for this registry, used to register items. - std::string name_; - /// Does this registry run setUp on each registry item at initialization. - bool auto_setup_; - - protected: - /// A map of registered plugin instances to their registered identifier. - std::map > items_; - /// If aliases are used, a map of alias to item name. - std::map aliases_; - /// Keep a lookup of the external item name to assigned extension UUID. - std::map external_; - /// Keep a lookup of optional route info. The plugin may handle calls - /// to external items differently. - std::map routes_; - /// Keep a lookup of registry items that are blacklisted from broadcast. - std::vector internal_; - /// Support an 'active' mode where calls without a specific item name will - /// be directed to the 'active' plugin. - std::string active_; - /// If a module was initialized/declared then store lookup information. - std::map modules_; -}; - -/** - * @brief The core interface for each registry type. - * - * The osquery Registry is partitioned into types. These are literal types - * but use a canonical string key for lookups and actions. - * Registries are created using Registry::create with a RegistryType and key. - */ -template -class RegistryHelper : public RegistryHelperCore { - protected: - typedef std::shared_ptr RegistryTypeRef; - - public: - explicit RegistryHelper(bool auto_setup = false) - : RegistryHelperCore(auto_setup), - add_(&RegistryType::addExternal), - remove_(&RegistryType::removeExternal) {} - virtual ~RegistryHelper() {} - - /** - * @brief Add a set of item names broadcasted by an extension uuid. - * - * When an extension is registered the RegistryFactory will receive a - * RegistryBroadcast containing a all of the extension's registry names and - * the set of items with their optional route info. The factory depends on - * each registry to manage calls/requests to these external plugins. - * - * @param uuid The uuid chosen for the extension. - * @param routes The plugin name and optional route info list. - * @return Success if all routes were added, failure if any failed. - */ - Status 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. - routes_[route.first] = route.second; - auto status = add_(route.first, route.second); - external_[route.first] = uuid; - if (!status.ok()) { - return status; - } - } - return Status(0, "OK"); - } - - /// Remove all the routes for a given uuid. - void removeExternal(const RouteUUID& uuid) { - std::vector removed_items; - for (const auto& item : external_) { - if (item.second == uuid) { - remove_(item.first); - removed_items.push_back(item.first); - } - } - - // Remove items belonging to the external uuid. - for (const auto& item : removed_items) { - external_.erase(item); - routes_.erase(item); - } - } - - /** - * @brief Add a plugin to this registry by allocating and indexing - * a type Item and a key identifier. - * - * @code{.cpp} - * /// Instead of calling RegistryFactory::add use: - * REGISTER(Type, "registry_name", "item_name"); - * @endcode - * - * @param item_name An identifier for this registry plugin. - * @return A success/failure status. - */ - template - Status add(const std::string& item_name, bool internal = false) { - if (items_.count(item_name) > 0) { - return Status(1, "Duplicate registry item exists: " + item_name); - } - - // Cast the specific registry-type derived item as the API type of the - // registry used when created using the registry factory. - std::shared_ptr item((RegistryType*)new Item()); - item->setName(item_name); - items_[item_name] = item; - return RegistryHelperCore::add(item_name, internal); - } - - /** - * @brief A raw accessor for a registry plugin. - * - * If there is no plugin with an item_name identifier this will throw - * and out_of_range exception. - * - * @param item_name An identifier for this registry plugin. - * @return A std::shared_ptr of type RegistryType. - */ - RegistryTypeRef get(const std::string& item_name) const { - return std::dynamic_pointer_cast(items_.at(item_name)); - } - - const std::map all() const { - std::map ditems; - for (const auto& item : items_) { - ditems[item.first] = std::dynamic_pointer_cast(item.second); - } - - return ditems; - } - - private: - RegistryHelper(RegistryHelper const&); - void operator=(RegistryHelper const&); - AddExternalCallback add_; - RemoveExternalCallback remove_; -}; - -/// Helper defintion for a shared pointer to a Plugin. -typedef std::shared_ptr PluginRef; -/// Helper definition for a basic-templated Registry type using a base Plugin. -typedef RegistryHelper PluginRegistryHelper; -/// Helper definitions for a shared pointer to the basic Registry type. -typedef std::shared_ptr PluginRegistryHelperRef; - -/** - * @basic A workflow manager for opening a module path and appending to the - * core registry. - * - * osquery Registry modules are part of the extensions API, in that they use - * the osquery SDK to expose additional features to the osquery core. Modules - * do not require the Thrift interface and may be compiled as shared objects - * and loaded late at run time once the core and internal registry has been - * initialized and setUp. - * - * A ModuleLoader interprets search paths, dynamically loads the modules, - * maintains identification within the RegistryFactory and any registries - * the module adds items into. - */ -class RegistryModuleLoader : private boost::noncopyable { - public: - /// Unlock the registry, open, construct, and allow the module to declare. - explicit RegistryModuleLoader(const std::string& path); - /// Keep the symbol resolution/calling out of construction. - void init(); - - /// Clear module information, 'lock' the registry. - ~RegistryModuleLoader(); - - private: - // Keep the handle for symbol resolution/calling. - void* handle_; - // Keep the path for debugging/logging. - std::string path_; - - private: - FRIEND_TEST(RegistryTests, test_registry_modules); -}; - -class RegistryFactory : private boost::noncopyable { - public: - static RegistryFactory& instance() { - static RegistryFactory instance; - return instance; - } - - /** - * @brief Create a registry using a plugin type and identifier. - * - * A short hard for allocating a new registry type a RegistryHelper and - * plugin derived class Type or RegistryType. This shorthand performs - * the allocation and initialization of the Type and keeps the instance - * identified by registry_name. - * - * @code{.cpp} - * /// Instead of calling RegistryFactory::create use: - * CREATE_REGISTRY(Type, "registry_name"); - * @endcode - * - * @param registry_name The canonical name for this registry. - * @param auto_setup Set true if the registry does not setup itself - * @return A non-sense int that must be casted const. - */ - template - static int create(const std::string& registry_name, bool auto_setup = false) { - if (locked() || instance().registries_.count(registry_name) > 0) { - return 0; - } - - PluginRegistryHelperRef registry( - (PluginRegistryHelper*)new RegistryHelper(auto_setup)); - registry->setName(registry_name); - instance().registries_[registry_name] = registry; - return 0; - } - - /// Direct access to a registry instance. - static PluginRegistryHelperRef registry(const std::string& registry_name); - - /** - * @brief Add (implies create) a Plugin to a registry. - * - * REGISTER and REGISTER_INTERNAL are helper macros for `add` usage. - * - * @code{.cpp} - * /// Instead of calling RegistryFactor::add use: - * REGISTER(Type, "registry_name", "plugin_name"); - * @endcode - * - * @param registry_name The canonical name for this registry. - * @param item_name The canonical name for this plugin. Specific registries - * may apply specialized use of the plugin name, such as table. - * @param internal True if this plugin should not be broadcasted externally. - */ - template - static Status add(const std::string& registry_name, - const std::string& item_name, - bool internal = false) { - if (!locked()) { - auto registry = instance().registry(registry_name); - return registry->template add(item_name, internal); - } - return Status(0, "Registry locked"); - } - - /// Direct access to all registries. - static const std::map& all(); - - /// Direct access to all plugin instances for a given registry name. - static const std::map all( - const std::string& registry_name); - - /// Direct access to a plugin instance. - static PluginRef get(const std::string& registry_name, - const std::string& item_name); - - /// Serialize this core or extension's registry. - static RegistryBroadcast getBroadcast(); - - /// Add external registry items identified by a Route UUID. - static Status addBroadcast(const RouteUUID& uuid, - const RegistryBroadcast& broadcast); - - /// Given an extension UUID remove all external registry items. - static Status removeBroadcast(const RouteUUID& uuid); - - /// Adds an alias for an internal registry item. This registry will only - /// broadcast the alias name. - static Status addAlias(const std::string& registry_name, - const std::string& item_name, - const std::string& alias); - - /// Returns the item_name or the item alias if an alias exists. - static const std::string& getAlias(const std::string& registry_name, - const std::string& alias); - - /** - * @brief Call a registry item. - * - * Registry 'calling' is the primary interaction osquery has with the Plugin - * APIs, which register items. Each item is an instance of a specialized - * Plugin, whose life/scope is maintained by the specific registry identified - * by a unique name. - * - * The specialized plugin type will expose a `call` method that parses a - * PluginRequest then perform some action and return a PluginResponse. - * Each registry provides a `call` method that performs the registry item - * (Plugin instance) look up, and passes and retrieves the request and - * response. - * - * @param registry_name The unique registry name containing item_name, - * @param item_name The name of the plugin used to REGISTER. - * @param request The PluginRequest object handled by the Plugin item. - * @param response The output. - * @return A status from the Plugin. - */ - static Status call(const std::string& registry_name, - const std::string& item_name, - const PluginRequest& request, - PluginResponse& response); - - /// A helper call that does not return a response (only status). - static Status call(const std::string& registry_name, - const std::string& item_name, - const PluginRequest& request); - - /// A helper call that uses the active plugin (if the registry has one). - static Status call(const std::string& registry_name, - const PluginRequest& request, - PluginResponse& response); - - /// A helper call that uses the active plugin (if the registry has one). - static Status call(const std::string& registry_name, - const PluginRequest& request); - - /// Set a registry's active plugin. - static Status setActive(const std::string& registry_name, - const std::string& item_name); - - /// Get a registry's active plugin. - static const std::string& getActive(const std::string& registry_nane); - - /// Run `setUp` on every registry that is not marked 'lazy'. - static void setUp(); - - /// Check if a registry item exists, optionally search only local registries. - static bool exists(const std::string& registry_name, - const std::string& item_name, - bool local = false); - - /// Get a list of the registry names. - static std::vector names(); - - /// Get a list of the registry item names for a given registry. - static std::vector names(const std::string& registry_name); - - /// Get a list of the registered extension UUIDs. - static std::vector routeUUIDs(); - - /// Return the number of registries. - static size_t count(); - - /// Return the number of registry items for a given registry name. - static size_t count(const std::string& registry_name); - - /// Enable/disable duplicate registry item support using aliasing. - static void allowDuplicates(bool allow) { - instance().allow_duplicates_ = allow; - } - - /// Check if duplicate registry items using registry aliasing are allowed. - static bool allowDuplicates() { return instance().allow_duplicates_; } - - /// Declare a module for initialization and subsequent registration attempts - static void declareModule(const std::string& name, - const std::string& version, - const std::string& min_sdk_version, - const std::string& sdk_version); - - /// Access module metadata. - static const std::map& getModules(); - - /// Set the registry external (such that internal events are forwarded). - /// Once set external, it should not be unset. - static void setExternal() { instance().external_ = true; } - - /// Get the registry external status. - static bool external() { return instance().external_; } - - private: - /// Access the current initializing module UUID. - static RouteUUID getModule(); - - /// Check if the registry is allowing module registrations. - static bool usingModule(); - - /// Initialize a module for lookup, resolution, and its registrations. - static void initModule(const std::string& path); - - static void shutdownModule(); - - /// Check if the registries are locked. - static bool locked() { return instance().locked_; } - - /// Set the registry locked status. - static void locked(bool locked) { instance().locked_ = locked; } - - protected: - RegistryFactory() - : allow_duplicates_(false), - locked_(false), - module_uuid_(0), - external_(false) {} - RegistryFactory(RegistryFactory const&); - RegistryFactory& operator=(RegistryFactory const&); - virtual ~RegistryFactory() {} - - private: - /// Track duplicate registry item support, used for testing. - bool allow_duplicates_; - /// Track registry "locking", while locked a registry cannot add/create. - bool locked_; - - /// The primary storage for constructed registries. - std::map registries_; - /** - * @brief The registry tracks the set of active extension routes. - * - * If an extension dies (the process ends or does not respond to a ping), - * the registry will be notified via the extension watcher. - * When an operation requests to use that extension route the extension - * manager will lazily check the registry for changes. - */ - std::set extensions_; - - /** - * @brief The registry tracks loaded extension module metadata/info. - * - * Each extension module is assigned a transient RouteUUID for identification - * those route IDs are passed to each registry to identify which plugin - * items belong to modules, similarly to extensions. - */ - std::map modules_; - - /// During module initialization store the current-working module ID. - RouteUUID module_uuid_; - /// Calling startExtension should declare the registry external. - /// This will cause extension-internal events to forward to osquery core. - bool external_; - - private: - friend class RegistryHelperCore; - friend class RegistryModuleLoader; - FRIEND_TEST(RegistryTests, test_registry_modules); -}; - -/** - * @brief The osquery Registry, refer to RegistryFactory for the caller API. - * - * The Registry class definition constructs the RegistryFactory behind the - * scenes using a class definition template API call Plugin. - * Each registry created by the RegistryFactory using RegistryFactory::create - * will provide a plugin type called RegistryType that inherits from Plugin. - * The actual plugins must add themselves to a registry type and should - * implement the Plugin and RegistryType interfaces. - */ -class Registry : public RegistryFactory {}; -} diff --git a/include/osquery/sdk.h b/include/osquery/sdk.h deleted file mode 100644 index 0868299..0000000 --- a/include/osquery/sdk.h +++ /dev/null @@ -1,120 +0,0 @@ -/* - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#pragma once - -#ifndef OSQUERY_BUILD_SDK -#define OSQUERY_BUILD_SDK -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace osquery { -/** - * @brief Create the external SQLite implementation wrapper. - * - * Anything built with only libosquery and not the 'additional' library will - * not include a native SQL implementation. This applies to extensions and - * separate applications built with the osquery SDK. - * - * The ExternalSQLPlugin is a wrapper around the SQLite API, which forwards - * calls to an osquery extension manager (core). - */ -REGISTER_INTERNAL(ExternalSQLPlugin, "sql", "sql"); - -/** - * @brief Mimic the REGISTER macro, extensions should use this helper. - * - * The SDK does not provide a REGISTER macro for modules or extensions. - * Tools built with the osquery SDK should use REGISTER_EXTERNAL to add to - * their own 'external' registry. This registry will broadcast to the osquery - * extension manager (core) in an extension. - * - * osquery 'modules' should not construct their plugin registrations in - * global scope (global construction time). Instead they should use the - * module call-in well defined symbol, declare their SDK constraints, then - * use the REGISTER_MODULE call within `initModule`. - */ -#define REGISTER_EXTERNAL(type, registry, name) \ - __attribute__((constructor)) static void type##ExtensionRegistryItem() { \ - Registry::add(registry, name); \ - } - -/// Helper macro to write the `initModule` symbol without rewrites. -#define DECLARE_MODULE(name) \ - extern "C" void initModule(void); \ - __attribute__((constructor)) static void declareModule() - -/** - * @brief Create an osquery extension 'module'. - * - * This helper macro creates a constructor to declare an osquery module is - * loading. The osquery registry is set up when modules (shared objects) are - * discovered via search paths and opened. At that phase the registry is locked - * meaning no additional plugins can be registered. To unlock the registry - * for modifications a module must call Registry::declareModule. This declares - * and any plugins added will use the metadata in the declare to determine: - * - The name of the module adding the plugin - * - The SDK version the module was built with, to determine compatibility - * - The minimum SDK the module requires from osquery core - * - * The registry is again locked when the module load is complete and a well - * known module-exported symbol is called. - */ -#define CREATE_MODULE(name, version, min_sdk_version) \ - DECLARE_MODULE(name) { \ - Registry::declareModule( \ - name, version, min_sdk_version, OSQUERY_SDK_VERSION); \ - } - -/** - * @brief Create an osquery extension 'module', if an expression is true. - * - * This is a helper testing wrapper around CREATE_MODULE and DECLARE_MODULE. - * It allows unit and integration tests to generate global construction code - * that depends on data/variables available during global construction. - * - * And example use includes checking if a process environment variable is - * defined. If defined the module is declared. - */ -#define CREATE_MODULE_IF(expr, name, version, min_sdk_version) \ - DECLARE_MODULE(name) { \ - if ((expr)) { \ - Registry::declareModule( \ - name, version, min_sdk_version, OSQUERY_SDK_VERSION); \ - } \ - } - -/// Helper replacement for REGISTER, used within extension modules. -#define REGISTER_MODULE(type, registry, name) \ - auto type##ModuleRegistryItem = Registry::add(registry, name) - -// Remove registry-helper macros from the SDK. -#undef REGISTER -#define REGISTER "Do not REGISTER in the osquery SDK" -#undef REGISTER_INTERNAL -#define REGISTER_INTERNAL "Do not REGISTER_INTERNAL in the osquery SDK" -#undef CREATE_REGISTRY -#define CREATE_REGISTRY "Do not CREATE_REGISTRY in the osquery SDK" -#undef CREATE_LAZY_REGISTRY -#define CREATE_LAZY_REGISTRY "Do not CREATE_LAZY_REGISTRY in the osquery SDK" -} diff --git a/include/osquery/sql.h b/include/osquery/sql.h deleted file mode 100644 index b8d0f19..0000000 --- a/include/osquery/sql.h +++ /dev/null @@ -1,234 +0,0 @@ -/* - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#pragma once - -#include -#include -#include - -#include -#include -#include - -namespace osquery { - -DECLARE_int32(value_max); - -/** - * @brief The core interface to executing osquery SQL commands - * - * @code{.cpp} - * auto sql = SQL("SELECT * FROM time"); - * if (sql.ok()) { - * LOG(INFO) << "============================"; - * for (const auto& row : sql.rows()) { - * for (const auto& it : row) { - * LOG(INFO) << it.first << " => " << it.second; - * } - * LOG(INFO) << "============================"; - * } - * } else { - * LOG(ERROR) << sql.getMessageString(); - * } - * @endcode - */ -class SQL { - public: - /** - * @brief Instantiate an instance of the class with a query - * - * @param q An osquery SQL query - */ - explicit SQL(const std::string& q); - - /** - * @brief Accessor for the rows returned by the query - * - * @return A QueryData object of the query results - */ - const QueryData& rows(); - - /** - * @brief Accessor to switch off of when checking the success of a query - * - * @return A bool indicating the success or failure of the operation - */ - bool ok(); - - /** - * @brief Get the status returned by the query - * - * @return The query status - */ - Status getStatus(); - - /** - * @brief Accessor for the message string indicating the status of the query - * - * @return The message string indicating the status of the query - */ - std::string getMessageString(); - - /** - * @brief Add host info columns onto existing QueryData - * - * Use this to add columns providing host info to the query results. - * Distributed queries use this to add host information before returning - * results to the aggregator. - */ - void annotateHostInfo(); - - /** - * @brief Accessor for the list of queryable tables - * - * @return A vector of table names - */ - static std::vector getTableNames(); - - /** - * @brief Get all, 'SELECT * ...', results given a virtual table name. - * - * @param table The name of the virtual table. - * @return A QueryData object of the 'SELECT *...' query results. - */ - static QueryData selectAllFrom(const std::string& table); - - /** - * @brief Get all with constraint, 'SELECT * ... where', results given - * a virtual table name and single constraint - * - * @param table The name of the virtual table. - * @param column Table column name to apply constraint. - * @param op The SQL comparitive operator. - * @param expr The constraint expression. - * @return A QueryData object of the 'SELECT *...' query results. - */ - static QueryData selectAllFrom(const std::string& table, - const std::string& column, - ConstraintOperator op, - const std::string& expr); - - protected: - /** - * @brief Private default constructor - * - * The osquery::SQL class should only ever be instantiated with a query - */ - SQL(){}; - - // The key used to store hostname for annotateHostInfo - static const std::string kHostColumnName; - - /// the internal member which holds the results of the query - QueryData results_; - - /// the internal member which holds the status of the query - Status status_; -}; - -/** - * @brief The osquery SQL implementation is managed as a plugin. - * - * The osquery RegistryFactory creates a Registry type called "sql", then - * requires a single plugin registration also called "sql". Calls within - * the application use boilerplate methods that wrap Registry::call%s to this - * well-known registry and registry item name. - * - * Abstracting the SQL implementation behind the osquery registry allows - * the SDK (libosquery) to describe how the SQL implementation is used without - * having dependencies on the thrird-party code. - * - * When osqueryd/osqueryi are built libosquery_additional, the library which - * provides the core plugins and core virtual tables, includes SQLite as - * the SQL implementation. - */ -class SQLPlugin : public Plugin { - public: - /// Run a SQL query string against the SQL implementation. - virtual Status query(const std::string& q, QueryData& results) const = 0; - /// Use the SQL implementation to parse a query string and return details - /// (name, type) about the columns. - virtual Status getQueryColumns(const std::string& q, - TableColumns& columns) const = 0; - - /** - * @brief Attach a table at runtime. - * - * The SQL implementation plugin may need to manage how virtual tables are - * attached at run time. In the case of SQLite where a single DB object is - * managed, tables are enumerated and attached during initialization. - */ - virtual Status attach(const std::string& name) { - return Status(0, "Not used"); - } - /// Tables may be detached by name. - virtual void detach(const std::string& name) {} - - public: - Status call(const PluginRequest& request, PluginResponse& response); -}; - -/** - * @brief Execute a query - * - * This is a lower-level version of osquery::SQL. Prefer to use osquery::SQL. - * - * @code{.cpp} - * std::string q = "SELECT * FROM time;"; - * QueryData results; - * auto status = query(q, results); - * if (status.ok()) { - * for (const auto& each : results) { - * for (const auto& it : each) { - * LOG(INFO) << it.first << ": " << it.second; - * } - * } - * } else { - * LOG(ERROR) << "Error: " << status.what(); - * } - * @endcode - * - * @param q the query to execute - * @param results A QueryData structure to emit result rows on success. - * @return A status indicating query success. - */ -Status query(const std::string& query, QueryData& results); - -/** - * @brief Analyze a query, providing information about the result columns - * - * This function asks SQLite to determine what the names and types are of the - * result columns of the provided query. Only table columns (not expressions or - * subqueries) can have their types determined. Types that are not determined - * are indicated with the string "UNKNOWN". - * - * @param q the query to analyze - * @param columns the vector to fill with column information - * - * @return status indicating success or failure of the operation - */ -Status getQueryColumns(const std::string& q, TableColumns& columns); - -/* - * @brief A mocked subclass of SQL useful for testing - */ -class MockSQL : public SQL { - public: - explicit MockSQL() : MockSQL(QueryData{}) {} - explicit MockSQL(const QueryData& results) : MockSQL(results, Status()) {} - explicit MockSQL(const QueryData& results, const Status& status) { - results_ = results; - status_ = status; - } -}; - -CREATE_LAZY_REGISTRY(SQLPlugin, "sql"); -} diff --git a/include/osquery/status.h b/include/osquery/status.h deleted file mode 100644 index 185a14a..0000000 --- a/include/osquery/status.h +++ /dev/null @@ -1,130 +0,0 @@ -/* - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#pragma once - -#include -#include - -namespace osquery { - -/** - * @brief A utility class which is used to express the state of operations. - * - * @code{.cpp} - * osquery::Status foobar() { - * auto na = doSomeWork(); - * if (na->itWorked()) { - * return osquery::Status(0, "OK"); - * } else { - * return osquery::Status(1, na->getErrorString()); - * } - * } - * @endcode - */ -class Status { - public: - /** - * @brief Default constructor - * - * Note that the default constructor initialized an osquery::Status instance - * to a state such that a successful operation is indicated. - */ - Status() : code_(0), message_("OK") {} - - /** - * @brief A constructor which can be used to concisely express the status of - * an operation. - * - * @param c a status code. The idiom is that a zero status code indicates a - * successful operation and a non-zero status code indicates a failed - * operation. - * @param m a message indicating some extra detail regarding the operation. - * If all operations were successful, this message should be "OK". - * Otherwise, it doesn't matter what the string is, as long as both the - * setter and caller agree. - */ - Status(int c, std::string m) : code_(c), message_(m) {} - - public: - /** - * @brief A getter for the status code property - * - * @return an integer representing the status code of the operation. - */ - int getCode() const { return code_; } - - /** - * @brief A getter for the message property - * - * @return a string representing arbitrary additional information about the - * success or failure of an operation. On successful operations, the idiom - * is for the message to be "OK" - */ - std::string getMessage() const { return message_; } - - /** - * @brief A convenience method to check if the return code is 0 - * - * @code{.cpp} - * auto s = doSomething(); - * if (s.ok()) { - * LOG(INFO) << "doing work"; - * } else { - * LOG(ERROR) << s.toString(); - * } - * @endcode - * - * @return a boolean which is true if the status code is 0, false otherwise. - */ - bool ok() const { return getCode() == 0; } - - /** - * @brief A synonym for osquery::Status::getMessage() - * - * @see getMessage() - */ - std::string toString() const { return getMessage(); } - std::string what() const { return getMessage(); } - - /** - * @brief implicit conversion to bool - * - * Allows easy use of Status in an if statement, as below: - * - * @code{.cpp} - * if (doSomethingThatReturnsStatus()) { - * LOG(INFO) << "Success!"; - * } - * @endcode - */ - operator bool() const { return ok(); } - - // Below operator implementations useful for testing with gtest - - // Enables use of gtest (ASSERT|EXPECT)_EQ - bool operator==(const Status& rhs) const { - return (code_ == rhs.getCode()) && (message_ == rhs.getMessage()); - } - - // Enables use of gtest (ASSERT|EXPECT)_NE - bool operator!=(const Status& rhs) const { return !operator==(rhs); } - - // Enables pretty-printing in gtest (ASSERT|EXPECT)_(EQ|NE) - friend ::std::ostream& operator<<(::std::ostream& os, const Status& s); - - private: - /// the internal storage of the status code - int code_; - - /// the internal storage of the status message - std::string message_; -}; -} diff --git a/include/osquery/tables.h b/include/osquery/tables.h deleted file mode 100644 index f2db419..0000000 --- a/include/osquery/tables.h +++ /dev/null @@ -1,359 +0,0 @@ -/* - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#pragma once - -#include -#include -#include -#include - -#include -#include - -#include -#include -#include -#include - -/// Allow Tables to use "tracked" deprecated OS APIs. -#define OSQUERY_USE_DEPRECATED(expr) \ - do { \ - _Pragma("clang diagnostic push") \ - _Pragma("clang diagnostic ignored \"-Wdeprecated-declarations\"") \ - expr; \ - _Pragma("clang diagnostic pop") \ - } while (0) - -namespace osquery { - -/** - * @brief The SQLite type affinities are available as macros - * - * Type affinities: TEXT, INTEGER, BIGINT - * - * You can represent any data that can be lexically casted to a string. - * Using the type affinity names helps table developers understand the data - * types they are storing, and more importantly how they are treated at query - * time. - */ -#define TEXT(x) boost::lexical_cast(x) -/// See the affinity type documentation for TEXT. -#define INTEGER(x) boost::lexical_cast(x) -/// See the affinity type documentation for TEXT. -#define BIGINT(x) boost::lexical_cast(x) -/// See the affinity type documentation for TEXT. -#define UNSIGNED_BIGINT(x) boost::lexical_cast(x) -/// See the affinity type documentation for TEXT. -#define DOUBLE(x) boost::lexical_cast(x) - -/** - * @brief The SQLite type affinities as represented as implementation literals. - * - * Type affinities: TEXT=std::string, INTEGER=int, BIGINT=long long int - * - * Just as the SQLite data is represented as lexically casted strings, as table - * may make use of the implementation language literals. - */ -#define TEXT_LITERAL std::string -/// See the literal type documentation for TEXT_LITERAL. -#define INTEGER_LITERAL int -/// See the literal type documentation for TEXT_LITERAL. -#define BIGINT_LITERAL long long int -/// See the literal type documentation for TEXT_LITERAL. -#define UNSIGNED_BIGINT_LITERAL unsigned long long int -/// See the literal type documentation for TEXT_LITERAL. -#define DOUBLE_LITERAL double -/// Cast an SQLite affinity type to the literal type. -#define AS_LITERAL(literal, value) boost::lexical_cast(value) - -/// Helper alias for TablePlugin names. -typedef std::string TableName; -typedef std::vector > TableColumns; -typedef std::map > TableData; - -/** - * @brief A ConstraintOperator is applied in an query predicate. - * - * If the query contains a join or where clause with a constraint operator and - * expression the table generator may limit the data appropriately. - */ -enum ConstraintOperator : unsigned char { - EQUALS = 2, - GREATER_THAN = 4, - LESS_THAN_OR_EQUALS = 8, - LESS_THAN = 16, - GREATER_THAN_OR_EQUALS = 32 -}; - -/// Type for flags for what constraint operators are admissible. -typedef unsigned char ConstraintOperatorFlag; -/// Flag for any operator type. -#define ANY_OP 0xFFU - -/** - * @brief A Constraint is an operator and expression. - * - * The constraint is applied to columns which have literal and affinity types. - */ -struct Constraint { - unsigned char op; - std::string expr; - - /// Construct a Constraint with the most-basic information, the operator. - explicit Constraint(unsigned char _op) { op = _op; } - - // A constraint list in a context knows only the operator at creation. - explicit Constraint(unsigned char _op, const std::string& _expr) - : op(_op), expr(_expr) {} -}; - -/** - * @brief A ConstraintList is a set of constraints for a column. This list - * should be mapped to a left-hand-side column name. - * - * The table generator does not need to check each constraint in its decision - * logic. The common constraint checking patterns (match) are abstracted using - * simple logic operators on the literal SQLite affinity types. - * - * A constraint list supports all AS_LITERAL types, and all ConstraintOperators. - */ -struct ConstraintList { - /// The SQLite affinity type. - std::string affinity; - - /** - * @brief Check if an expression matches the query constraints. - * - * Evaluate ALL constraints in this ConstraintList against the string - * expression. The affinity of the constraint will be used as the affinite - * and lexical type of the expression and set of constraint expressions. - * If there are no predicate constraints in this list, all expression will - * match. Constraints are limitations. - * - * @param expr a SQL type expression of the column literal type to check. - * @return If the expression matched all constraints. - */ - bool matches(const std::string& expr) const; - - /** - * @brief Check if an expression matches the query constraints. - * - * `matches` also supports the set of SQL affinite types. - * The expression expr will be evaluated as a string and compared using - * the affinity of the constraint. - * - * @param expr a SQL type expression of the column literal type to check. - * @return If the expression matched all constraints. - */ - template - bool matches(const T& expr) const { - return matches(TEXT(expr)); - } - - /** - * @brief Check and return if there are constraints on this column. - * - * A ConstraintList is used in a ConstraintMap with a column name as the - * map index. Tables that act on optional constraints should check if any - * constraint was provided. The ops parameter serves to specify which - * operators we want to check existence for. - * - * @param ops (Optional: default ANY_OP) The operators types to look for. - * @return true if any constraint exists. - */ - bool exists(const ConstraintOperatorFlag ops = ANY_OP) const { - if (ops == ANY_OP) { - return (constraints_.size() > 0); - } else { - for (const struct Constraint &c : constraints_) { - if (c.op & ops) { - return true; - } - } - return false; - } - } - - /** - * @brief Check if a constraint exist AND matches the type expression. - * - * See ConstraintList::exists and ConstraintList::matches. - * - * @param expr The expression to match. - * @return true if any constraint exists AND matches the type expression. - */ - template - bool existsAndMatches(const T& expr) const { - return (exists() && matches(expr)); - } - - /** - * @brief Check if a constraint is missing or matches a type expression. - * - * A ConstraintList is used in a ConstraintMap with a column name as the - * map index. Tables that act on required constraints can make decisions - * on missing constraints or a constraint match. - * - * @param expr The expression to match. - * @return true if constraint is missing or matches the type expression. - */ - template - bool notExistsOrMatches(const T& expr) const { - return (!exists() || matches(expr)); - } - - /** - * @brief Helper templated function for ConstraintList::matches. - */ - template - bool literal_matches(const T& base_expr) const; - - /** - * @brief Get all expressions for a given ConstraintOperator. - * - * This is most useful if the table generation requires as column. - * The generator may `getAll(EQUALS)` then iterate. - * - * @param op the ConstraintOperator. - * @return A list of TEXT%-represented types matching the operator. - */ - std::set getAll(ConstraintOperator op) const; - - /// See ConstraintList::getAll, but as a selected literal type. - template - std::set getAll(ConstraintOperator op) const { - std::set literal_matches; - auto matches = getAll(op); - for (const auto& match : matches) { - literal_matches.insert(AS_LITERAL(T, match)); - } - return literal_matches; - } - - /// Constraint list accessor, types and operator. - const std::vector getAll() const { return constraints_; } - - /** - * @brief Add a new Constraint to the list of constraints. - * - * @param constraint a new operator/expression to constrain. - */ - void add(const struct Constraint& constraint) { - constraints_.push_back(constraint); - } - - /** - * @brief Serialize a ConstraintList into a property tree. - * - * The property tree will use the format: - * { - * "affinity": affinity, - * "list": [ - * {"op": op, "expr": expr}, ... - * ] - * } - */ - void serialize(boost::property_tree::ptree& tree) const; - - /// See ConstraintList::unserialize. - void unserialize(const boost::property_tree::ptree& tree); - - ConstraintList() : affinity("TEXT") {} - - private: - /// List of constraint operator/expressions. - std::vector constraints_; - - private: - FRIEND_TEST(TablesTests, test_constraint_list); -}; - -/// Pass a constraint map to the query request. -typedef std::map ConstraintMap; -/// Populate a constraint list from a query's parsed predicate. -typedef std::vector > ConstraintSet; - -/** - * @brief A QueryContext is provided to every table generator for optimization - * on query components like predicate constraints and limits. - */ -struct QueryContext { - ConstraintMap constraints; - /// Support a limit to the number of results. - int limit; - - QueryContext() : limit(0) {} -}; - -typedef struct QueryContext QueryContext; -typedef struct Constraint Constraint; - -/** - * @brief The TablePlugin defines the name, types, and column information. - * - * To attach a virtual table create a TablePlugin subclass and register the - * virtual table name as the plugin ID. osquery will enumerate all registered - * TablePlugins and attempt to attach them to SQLite at instantiation. - * - * Note: When updating this class, be sure to update the corresponding template - * in osquery/tables/templates/default.cpp.in - */ -class TablePlugin : public Plugin { - protected: - virtual TableColumns columns() const { - TableColumns columns; - return columns; - } - - virtual QueryData generate(QueryContext& request) { - QueryData data; - return data; - } - - virtual Status update(Row& row) { - return Status(0, "OK"); - } - - protected: - std::string columnDefinition() const; - PluginResponse routeInfo() const; - - public: - /// Public API methods. - Status call(const PluginRequest& request, PluginResponse& response); - - public: - /// Helper data structure transformation methods - static void setRequestFromContext(const QueryContext& context, - PluginRequest& request); - static void setResponseFromQueryData(const QueryData& data, - PluginResponse& response); - static void setContextFromRequest(const PluginRequest& request, - QueryContext& context); - - public: - /// When external table plugins are registered the core will attach them - /// as virtual tables to the SQL internal implementation - static Status addExternal(const std::string& name, - const PluginResponse& info); - static void removeExternal(const std::string& name); - - private: - FRIEND_TEST(VirtualTableTests, test_tableplugin_columndefinition); - FRIEND_TEST(VirtualTableTests, test_tableplugin_statement); -}; - -/// Helper method to generate the virtual table CREATE statement. -std::string columnDefinition(const TableColumns& columns); -std::string columnDefinition(const PluginResponse& response); - -CREATE_LAZY_REGISTRY(TablePlugin, "table"); -} diff --git a/osquery/CMakeLists.txt b/osquery/CMakeLists.txt deleted file mode 100644 index da22c72..0000000 --- a/osquery/CMakeLists.txt +++ /dev/null @@ -1,198 +0,0 @@ -# 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 - -SET(TARGET_OSQUERY_LIB osquery) -SET(TARGET_OSQUERY_LIB_ADDITIONAL osquery_additional) -SET(TARGET_OSQUERY_TEST osquery-test) -SET(TARGET_OSQUERY_SHELL osqueryi) -SET(TARGET_OSQUERY_DAEMON osqueryd) - -SET(${TARGET_OSQUERY_LIB}_SRCS "") -SET(${TARGET_OSQUERY_LIB}_DEPS "") -SET(${TARGET_OSQUERY_LIB}_TESTS "") - -ADD_OSQUERY_LINK(glog - gflags - pthread - libthrift.a - #rocksdb deps - librocksdb.a - snappy - z - bz2 - dl - lz4 - zstd - boost_regex - boost_system - boost_thread - boost_filesystem - crypto # openssl - #shell deps - readline - #build-in tables deps - systemd) - -SET(OSQUERY_CODEGEN_PATH "${CMAKE_SOURCE_DIR}/tools/codegen") -SET(OSQUERY_TABLES_PATH "${CMAKE_SOURCE_DIR}") -SET(OSQUERY_GENERATED_PATH "${CMAKE_BINARY_DIR}/generated") - -## Table generation ############################################################# -FILE(GLOB TABLE_FILES "${CMAKE_SOURCE_DIR}/specs/*.table") -FILE(GLOB TABLE_FILES_LINUX "${CMAKE_SOURCE_DIR}/specs/linux/*.table") -FILE(GLOB TABLE_FILES_UTILITY "${CMAKE_SOURCE_DIR}/specs/utility/*.table") -LIST(APPEND TABLE_FILES ${TABLE_FILES_LINUX}) -LIST(APPEND TABLE_FILES ${TABLE_FILES_UTILITY}) - -IF(DEFINED GBS_BUILD) - FILE(GLOB TABLE_FILES_TIZEN "${CMAKE_SOURCE_DIR}/specs/tizen/*.table") - LIST(APPEND TABLE_FILES ${TABLE_FILES_TIZEN}) - - SET(GBS_ONLY_PACKAGES klay - dpm-pil - capi-base-common - capi-system-info - capi-network-wifi-manager) - - INCLUDE(FindPkgConfig) - PKG_CHECK_MODULES(GBS_DEPS REQUIRED ${GBS_ONLY_PACKAGES}) - INCLUDE_DIRECTORIES(SYSTEM ${GBS_DEPS_INCLUDE_DIRS}) - - ADD_OSQUERY_LINK(${GBS_DEPS_LIBRARIES}) -ENDIF(DEFINED GBS_BUILD) - -SET(GENERATED_TABLES "") - -FILE(GLOB TABLE_FILES_TEMPLATES "${CMAKE_SOURCE_DIR}/tools/codegen/templates/*.in") -SET(GENERATION_DEPENDENCIES "${OSQUERY_CODEGEN_PATH}/gentable.py" - "${OSQUERY_CODEGEN_PATH}/amalgamate.py" - "${OSQUERY_TABLES_PATH}/specs/blacklist") - -LIST(APPEND GENERATION_DEPENDENCIES ${TABLE_FILES_TEMPLATES}) - -FOREACH(TABLE_FILE ${TABLE_FILES}) - SET(TABLE_FILE_GEN ${TABLE_FILE}) - STRING(REPLACE "${OSQUERY_TABLES_PATH}/specs" - "${OSQUERY_GENERATED_PATH}/tables" - TABLE_FILE_GEN - ${TABLE_FILE_GEN}) - STRING(REPLACE "linux/" "" TABLE_FILE_GEN ${TABLE_FILE_GEN}) - STRING(REPLACE "" "" TABLE_FILE_GEN ${TABLE_FILE_GEN}) - STRING(REPLACE ".table" ".cpp" TABLE_FILE_GEN ${TABLE_FILE_GEN}) - ADD_CUSTOM_COMMAND( - OUTPUT ${TABLE_FILE_GEN} - COMMAND - python "${OSQUERY_CODEGEN_PATH}/gentable.py" "${TABLE_FILE}" "${TABLE_FILE_GEN}" "$ENV{DISABLE_BLACKLIST}" - DEPENDS - ${TABLE_FILE} ${GENERATION_DEPENDENCIES} - - WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}") - LIST(APPEND GENERATED_TABLES ${TABLE_FILE_GEN}) -ENDFOREACH() - -SET(AMALGAMATION_FILE_GEN "${OSQUERY_GENERATED_PATH}/amalgamation.cpp") -ADD_CUSTOM_COMMAND( - OUTPUT ${AMALGAMATION_FILE_GEN} - COMMAND - python "${OSQUERY_CODEGEN_PATH}/amalgamate.py" "${OSQUERY_CODEGEN_PATH}" "${OSQUERY_GENERATED_PATH}" - DEPENDS - ${GENERATED_TABLES} - WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}") - -## Library-obj generation ########################################################### -ADD_SUBDIRECTORY(core) -ADD_SUBDIRECTORY(config) -ADD_SUBDIRECTORY(dispatcher) -ADD_SUBDIRECTORY(distributed) -ADD_SUBDIRECTORY(devtools) -ADD_SUBDIRECTORY(database) -ADD_SUBDIRECTORY(events) -ADD_SUBDIRECTORY(extensions) -ADD_SUBDIRECTORY(filesystem) -ADD_SUBDIRECTORY(logger) -ADD_SUBDIRECTORY(registry) -ADD_SUBDIRECTORY(sql) -ADD_SUBDIRECTORY(tables) - -ADD_SUBDIRECTORY(tizen) - -## Library generation ########################################################### -# TODO(sangwan.kwon): Change amalgation files to additional -# static_lib should include every object file in the archive in the link -# ref: TARGET_OSQUERY_LINK_WHOLE -ADD_LIBRARY(osquery_generated_tables OBJECT "${AMALGAMATION_FILE_GEN}") -ADD_LIBRARY(${TARGET_OSQUERY_LIB} - STATIC main/lib.cpp - $ - $ - ${${TARGET_OSQUERY_LIB}_SRCS}) -TARGET_LINK_LIBRARIES(${TARGET_OSQUERY_LIB} ${${TARGET_OSQUERY_LIB}_DEPS}) -SET_TARGET_PROPERTIES(${TARGET_OSQUERY_LIB} PROPERTIES OUTPUT_NAME ${TARGET_OSQUERY_LIB}) - -#INSTALL(TARGETS ${TARGET_OSQUERY_LIB} -# DESTINATION ${CMAKE_INSTALL_LIBDIR}) -#INSTALL(DIRECTORY "${CMAKE_SOURCE_DIR}/include/" -# DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) - -## osqueryi generation ########################################################## -ADD_EXECUTABLE(${TARGET_OSQUERY_SHELL} devtools/shell.cpp main/shell.cpp) -TARGET_OSQUERY_LINK_WHOLE(${TARGET_OSQUERY_SHELL} ${TARGET_OSQUERY_LIB}) -INSTALL(TARGETS ${TARGET_OSQUERY_SHELL} - DESTINATION ${CMAKE_INSTALL_BINDIR} - PERMISSIONS OWNER_READ - OWNER_WRITE - OWNER_EXECUTE - GROUP_READ - GROUP_EXECUTE - WORLD_READ - WORLD_EXECUTE) - -## osqueryd generation ########################################################## -ADD_EXECUTABLE(${TARGET_OSQUERY_DAEMON} main/daemon.cpp) -TARGET_OSQUERY_LINK_WHOLE(${TARGET_OSQUERY_DAEMON} ${TARGET_OSQUERY_LIB}) -INSTALL(TARGETS ${TARGET_OSQUERY_DAEMON} - DESTINATION ${CMAKE_INSTALL_BINDIR} - PERMISSIONS OWNER_READ - OWNER_WRITE - OWNER_EXECUTE - GROUP_READ - GROUP_EXECUTE - WORLD_READ - WORLD_EXECUTE) - -## osquery-test generation ########################################################## -ADD_EXECUTABLE(${TARGET_OSQUERY_TEST} main/tests.cpp - ${${TARGET_OSQUERY_LIB}_TESTS}) -TARGET_OSQUERY_LINK_WHOLE(${TARGET_OSQUERY_TEST} ${TARGET_OSQUERY_LIB}) -TARGET_LINK_LIBRARIES(${TARGET_OSQUERY_TEST} gtest) -SET_TARGET_PROPERTIES(${TARGET_OSQUERY_TEST} - PROPERTIES COMPILE_FLAGS "-DGTEST_HAS_TR1_TUPLE=0") -ADD_TEST(${TARGET_OSQUERY_TEST} ${TARGET_OSQUERY_TEST}) -INSTALL(TARGETS ${TARGET_OSQUERY_TEST} - DESTINATION ${CMAKE_INSTALL_BINDIR} - PERMISSIONS OWNER_READ - OWNER_WRITE - OWNER_EXECUTE - GROUP_READ - GROUP_EXECUTE - WORLD_READ - WORLD_EXECUTE) - -## example extension with the SDK ############################################## -ADD_EXECUTABLE(example_extension examples/example_extension.cpp) -TARGET_OSQUERY_LINK_WHOLE(example_extension ${TARGET_OSQUERY_LIB}) -SET_TARGET_PROPERTIES(example_extension PROPERTIES OUTPUT_NAME example_extension.ext) - -# Build the example extension module with the SDK -ADD_OSQUERY_MODULE(modexample examples/example_module.cpp) diff --git a/osquery/config/CMakeLists.txt b/osquery/config/CMakeLists.txt deleted file mode 100644 index 0cb3d7f..0000000 --- a/osquery/config/CMakeLists.txt +++ /dev/null @@ -1,22 +0,0 @@ -# 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_config config.cpp) - -ADD_OSQUERY_LIBRARY(osquery_config_plugins update.cpp - plugins/filesystem.cpp - parsers/query_packs.cpp) - -FILE(GLOB OSQUERY_CONFIG_TESTS "tests/*.cpp") -ADD_OSQUERY_TEST(${OSQUERY_CONFIG_TESTS}) diff --git a/osquery/config/config.cpp b/osquery/config/config.cpp deleted file mode 100644 index 52b0c76..0000000 --- a/osquery/config/config.cpp +++ /dev/null @@ -1,431 +0,0 @@ -/* - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -namespace pt = boost::property_tree; - -namespace osquery { - -typedef pt::ptree::value_type tree_node; -typedef std::map > EventFileMap_t; -typedef std::chrono::high_resolution_clock chrono_clock; - -/// The config plugin must be known before reading options. -CLI_FLAG(string, config_plugin, "filesystem", "Config plugin name"); - -FLAG(int32, schedule_splay_percent, 10, "Percent to splay config times"); - -Status Config::load() { - auto& config_plugin = Registry::getActive("config"); - if (!Registry::exists("config", config_plugin)) { - return Status(1, "Missing config plugin " + config_plugin); - } - - return genConfig(); -} - -Status Config::update(const std::map& config) { - // A config plugin may call update from an extension. This will update - // the config instance within the extension process and the update must be - // reflected in the core. - if (Registry::external()) { - for (const auto& source : config) { - PluginRequest request = { - {"action", "update"}, - {"source", source.first}, - {"data", source.second}, - }; - // A "update" registry item within core should call the core's update - // method. The config plugin call action handling must also know to - // update. - Registry::call("config", "update", request); - } - } - - // Request a unique write lock when updating config. - { - boost::unique_lock unique_lock(getInstance().mutex_); - - for (const auto& source : config) { - if (Registry::external()) { - VLOG(1) << "Updating extension config with source: " << source.first; - } else { - VLOG(1) << "Updating config with source: " << source.first; - } - getInstance().raw_[source.first] = source.second; - } - - // Now merge all sources together. - ConfigData conf; - for (const auto& source : getInstance().raw_) { - auto status = mergeConfig(source.second, conf); - if (getInstance().force_merge_success_ && !status.ok()) { - return Status(1, status.what()); - } - } - - // Call each parser with the optionally-empty, requested, top level keys. - getInstance().data_ = std::move(conf); - } - - for (const auto& plugin : Registry::all("config_parser")) { - auto parser = std::static_pointer_cast(plugin.second); - if (parser == nullptr || parser.get() == nullptr) { - continue; - } - - // For each key requested by the parser, add a property tree reference. - std::map parser_config; - for (const auto& key : parser->keys()) { - if (getInstance().data_.all_data.count(key) > 0) { - parser_config[key] = getInstance().data_.all_data.get_child(key); - } else { - parser_config[key] = pt::ptree(); - } - } - - // The config parser plugin will receive a copy of each property tree for - // each top-level-config key. The parser may choose to update the config's - // internal state by requesting and modifying a ConfigDataInstance. - parser->update(parser_config); - } - - return Status(0, "OK"); -} - -Status Config::genConfig() { - PluginResponse response; - auto status = Registry::call("config", {{"action", "genConfig"}}, response); - if (!status.ok()) { - return status; - } - - if (response.size() > 0) { - return update(response[0]); - } - return Status(0, "OK"); -} - -inline void mergeOption(const tree_node& option, ConfigData& conf) { - std::string key = option.first.data(); - std::string value = option.second.data(); - - Flag::updateValue(key, value); - // There is a special case for supported Gflags-reserved switches. - if (key == "verbose" || key == "verbose_debug" || key == "debug") { - setVerboseLevel(); - if (Flag::getValue("verbose") == "true") { - VLOG(1) << "Verbose logging enabled by config option"; - } - } - - conf.options[key] = value; - if (conf.all_data.count("options") > 0) { - conf.all_data.get_child("options").erase(key); - } - conf.all_data.add_child("options." + key, option.second); -} - -inline void additionalScheduledQuery(const std::string& name, - const tree_node& node, - ConfigData& conf) { - // Read tree/JSON into a query structure. - ScheduledQuery query; - query.query = node.second.get("query", ""); - query.interval = node.second.get("interval", 0); - if (query.interval == 0) { - VLOG(1) << "Setting invalid interval=0 to 84600 for query: " << name; - query.interval = 86400; - } - - // This is a candidate for a catch-all iterator with a catch for boolean type. - query.options["snapshot"] = node.second.get("snapshot", false); - query.options["removed"] = node.second.get("removed", true); - - // Check if this query exists, if so, check if it was changed. - if (conf.schedule.count(name) > 0) { - if (query == conf.schedule.at(name)) { - return; - } - } - - // This is a new or updated scheduled query, update the splay. - query.splayed_interval = - splayValue(query.interval, FLAGS_schedule_splay_percent); - // Update the schedule map and replace the all_data node record. - conf.schedule[name] = query; -} - -inline void mergeScheduledQuery(const std::string& name, - const tree_node& node, - ConfigData& conf) { - // Add the new query to the configuration. - additionalScheduledQuery(name, node, conf); - // Replace the all_data node record. - if (conf.all_data.count("schedule") > 0) { - conf.all_data.get_child("schedule").erase(name); - } - conf.all_data.add_child("schedule." + name, node.second); -} - -inline void mergeExtraKey(const std::string& name, - const tree_node& node, - ConfigData& conf) { - // Automatically merge extra list/dict top level keys. - for (const auto& subitem : node.second) { - if (node.second.count("") == 0 && conf.all_data.count(name) > 0) { - conf.all_data.get_child(name).erase(subitem.first); - } - - if (subitem.first.size() == 0) { - if (conf.all_data.count(name) == 0) { - conf.all_data.add_child(name, subitem.second); - } - conf.all_data.get_child(name).push_back(subitem); - } else { - conf.all_data.add_child(name + "." + subitem.first, subitem.second); - } - } -} - -inline void mergeFilePath(const std::string& name, - const tree_node& node, - ConfigData& conf) { - for (const auto& path : node.second) { - // Add the exact path after converting wildcards. - std::string pattern = path.second.data(); - replaceGlobWildcards(pattern); - conf.files[node.first].push_back(std::move(pattern)); - } - conf.all_data.add_child(name + "." + node.first, node.second); -} - -Status Config::mergeConfig(const std::string& source, ConfigData& conf) { - pt::ptree tree; - try { - std::stringstream json_data; - json_data << source; - pt::read_json(json_data, tree); - } catch (const pt::json_parser::json_parser_error& e) { - LOG(WARNING) << "Error parsing config JSON: " << e.what(); - return Status(1, e.what()); - } - - if (tree.count("additional_monitoring") > 0) { - LOG(INFO) << RLOG(903) << "config 'additional_monitoring' is deprecated"; - for (const auto& node : tree.get_child("additional_monitoring")) { - tree.add_child(node.first, node.second); - } - tree.erase("additional_monitoring"); - } - - for (const auto& item : tree) { - // Iterate over each top-level configuration key. - auto key = std::string(item.first.data()); - if (key == "scheduledQueries") { - LOG(INFO) << RLOG(903) << "config 'scheduledQueries' is deprecated"; - for (const auto& node : item.second) { - auto query_name = node.second.get("name", ""); - mergeScheduledQuery(query_name, node, conf); - } - } else if (key == "schedule") { - for (const auto& node : item.second) { - mergeScheduledQuery(node.first.data(), node, conf); - } - } else if (key == "options") { - for (const auto& option : item.second) { - mergeOption(option, conf); - } - } else if (key == "file_paths") { - for (const auto& category : item.second) { - mergeFilePath(key, category, conf); - } - } else { - mergeExtraKey(key, item, conf); - } - } - - return Status(0, "OK"); -} - -const pt::ptree& Config::getParsedData(const std::string& key) { - if (!Registry::exists("config_parser", key)) { - return getInstance().empty_data_; - } - - const auto& item = Registry::get("config_parser", key); - auto parser = std::static_pointer_cast(item); - if (parser == nullptr || parser.get() == nullptr) { - return getInstance().empty_data_; - } - - return parser->data_; -} - -const ConfigPluginRef Config::getParser(const std::string& key) { - if (!Registry::exists("config_parser", key)) { - return ConfigPluginRef(); - } - - const auto& item = Registry::get("config_parser", key); - const auto parser = std::static_pointer_cast(item); - if (parser == nullptr || parser.get() == nullptr) { - return ConfigPluginRef(); - } - - return parser; -} - -Status Config::getMD5(std::string& hash_string) { - // Request an accessor to our own config, outside of an update. - ConfigDataInstance config; - - std::stringstream out; - try { - pt::write_json(out, config.data(), false); - } catch (const pt::json_parser::json_parser_error& e) { - return Status(1, e.what()); - } - - hash_string = osquery::hashFromBuffer( - HASH_TYPE_MD5, (void*)out.str().c_str(), out.str().length()); - - return Status(0, "OK"); -} - -void Config::addScheduledQuery(const std::string& name, - const std::string& query, - const int interval) { - // Create structure to add to the schedule. - tree_node node; - node.second.put("query", query); - node.second.put("interval", interval); - - // Call to the inline function. - additionalScheduledQuery(name, node, getInstance().data_); -} - -Status Config::checkConfig() { - getInstance().force_merge_success_ = true; - return load(); -} - -bool Config::checkScheduledQuery(const std::string& query) { - for (const auto& scheduled_query : getInstance().data_.schedule) { - if (scheduled_query.second.query == query) { - return true; - } - } - - return false; -} - -bool Config::checkScheduledQueryName(const std::string& query_name) { - return (getInstance().data_.schedule.count(query_name) == 0) ? false : true; -} - -void Config::recordQueryPerformance(const std::string& name, - size_t delay, - size_t size, - const Row& r0, - const Row& r1) { - // Grab a lock on the schedule structure and check the name. - ConfigDataInstance config; - if (config.schedule().count(name) == 0) { - // Unknown query schedule name. - return; - } - - // Grab access to the non-const schedule item. - auto& query = getInstance().data_.schedule.at(name); - auto diff = AS_LITERAL(BIGINT_LITERAL, r1.at("user_time")) - - AS_LITERAL(BIGINT_LITERAL, r0.at("user_time")); - if (diff > 0) { - query.user_time += diff; - } - - diff = AS_LITERAL(BIGINT_LITERAL, r1.at("system_time")) - - AS_LITERAL(BIGINT_LITERAL, r0.at("system_time")); - if (diff > 0) { - query.system_time += diff; - } - - diff = AS_LITERAL(BIGINT_LITERAL, r1.at("resident_size")) - - AS_LITERAL(BIGINT_LITERAL, r0.at("resident_size")); - if (diff > 0) { - // Memory is stored as an average of RSS changes between query executions. - query.average_memory = (query.average_memory * query.executions) + diff; - query.average_memory = (query.average_memory / (query.executions + 1)); - } - - query.wall_time += delay; - query.output_size += size; - query.executions += 1; -} - -Status ConfigPlugin::call(const PluginRequest& request, - PluginResponse& response) { - if (request.count("action") == 0) { - return Status(1, "Config plugins require an action in PluginRequest"); - } - - if (request.at("action") == "genConfig") { - std::map config; - auto stat = genConfig(config); - response.push_back(config); - return stat; - } else if (request.at("action") == "update") { - if (request.count("source") == 0 || request.count("data") == 0) { - return Status(1, "Missing source or data"); - } - return Config::update({{request.at("source"), request.at("data")}}); - } - return Status(1, "Config plugin action unknown: " + request.at("action")); -} - -Status ConfigParserPlugin::setUp() { - for (const auto& key : keys()) { - data_.put(key, ""); - } - return Status(0, "OK"); -} - -int splayValue(int original, int splayPercent) { - if (splayPercent <= 0 || splayPercent > 100) { - return original; - } - - float percent_to_modify_by = (float)splayPercent / 100; - int possible_difference = original * percent_to_modify_by; - int max_value = original + possible_difference; - int min_value = original - possible_difference; - - if (max_value == min_value) { - return max_value; - } - - std::default_random_engine generator; - generator.seed(chrono_clock::now().time_since_epoch().count()); - std::uniform_int_distribution distribution(min_value, max_value); - return distribution(generator); -} -} diff --git a/osquery/config/parsers/query_packs.cpp b/osquery/config/parsers/query_packs.cpp deleted file mode 100644 index e5923f3..0000000 --- a/osquery/config/parsers/query_packs.cpp +++ /dev/null @@ -1,160 +0,0 @@ -/* - * Copyright (c) 2015, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include -#include - -#include -#include -#include -#include - -namespace pt = boost::property_tree; - -namespace osquery { - -/** - * @brief A simple ConfigParserPlugin for a "packs" dictionary key. - * - */ -class QueryPackConfigParserPlugin : public ConfigParserPlugin { - public: - /// Request "packs" top level key. - std::vector keys() { return {"packs"}; } - - private: - /// Store the signatures and file_paths and compile the rules. - Status update(const ConfigTreeMap& config); -}; - -// Function to check if the pack is valid for this version of osquery. -// If the osquery version is greater or equal than the pack, it is good to go. -bool versionChecker(const std::string& pack, const std::string& version) { - auto required_version = split(pack, "."); - auto build_version = split(version, "."); - - size_t index = 0; - for (const auto& chunk : build_version) { - if (required_version.size() <= index) { - return true; - } - try { - if (std::stoi(chunk) < std::stoi(required_version[index])) { - return false; - } - } catch (const std::invalid_argument& e) { - if (chunk.compare(required_version[index]) < 0) { - return false; - } - } - index++; - } - return true; -} - -// Perform a string string search for the actual platform within the required. -bool platformChecker(const std::string& required, const std::string& platform) { - // Match if platform is 'ubuntu12' and required is 'ubuntu'. - // Do not match if platform is 'ubuntu12' and required is 'ubuntu14'. -#ifdef __linux__ - if (required.find("linux") != std::string::npos) { - return true; - } -#endif - if (required.find("any") != std::string::npos || - required.find("all") != std::string::npos) { - return true; - } - return (required.find(platform) != std::string::npos); -} - -Status parsePack(const std::string& name, const pt::ptree& data) { - if (data.count("queries") == 0) { - return Status(0, "Pack contains no queries"); - } - - // Check the pack-global minimum SDK version and platform. - auto version = data.get("version", ""); - if (version.size() > 0 && !versionChecker(version, kSDKVersion)) { - return Status(0, "Minimum SDK version not met"); - } - - auto platform = data.get("platform", ""); - if (platform.size() > 0 && !platformChecker(platform, kSDKPlatform)) { - return Status(0, "Platform version mismatch"); - } - - // For each query in the pack's queries, check their version/platform. - for (const auto& query : data.get_child("queries")) { - auto query_string = query.second.get("query", ""); - if (Config::checkScheduledQuery(query_string)) { - VLOG(1) << "Query pack " << name - << " contains a duplicated query: " << query.first; - continue; - } - - // Check the specific query's required version. - version = query.second.get("version", ""); - if (version.size() > 0 && !versionChecker(version, kSDKVersion)) { - continue; - } - - // Check the specific query's required platform. - platform = query.second.get("platform", ""); - if (platform.size() > 0 && !platformChecker(platform, kSDKPlatform)) { - continue; - } - - // Hope there is a supplied/non-0 query interval to apply this query pack - // query to the osquery schedule. - auto query_interval = query.second.get("interval", 0); - if (query_interval > 0) { - auto query_name = "pack_" + name + "_" + query.first; - Config::addScheduledQuery(query_name, query_string, query_interval); - } - } - - return Status(0, "OK"); -} - -Status QueryPackConfigParserPlugin::update(const ConfigTreeMap& config) { - // Iterate through all the packs to get the configuration. - for (auto const& pack : config.at("packs")) { - auto pack_name = std::string(pack.first.data()); - auto pack_path = std::string(pack.second.data()); - - // Read each pack configuration in JSON - pt::ptree pack_data; - auto status = osquery::parseJSON(pack_path, pack_data); - if (!status.ok()) { - LOG(WARNING) << "Error parsing Query Pack " << pack_name << ": " - << status.getMessage(); - continue; - } - - // Parse the pack, meaning compare version/platform requirements and - // check the sanity of each query in the pack's queries. - status = parsePack(pack_name, pack_data); - if (!status.ok()) { - return status; - } - - // Save the queries list for table-based introspection. - data_.put_child(pack_name, pack_data); - // Record the pack path. - data_.put(pack_name + ".path", pack_path); - } - - return Status(0, "OK"); -} - -/// Call the simple Query Packs ConfigParserPlugin "packs". -REGISTER_INTERNAL(QueryPackConfigParserPlugin, "config_parser", "packs"); -} diff --git a/osquery/config/parsers/tests/query_packs_tests.cpp b/osquery/config/parsers/tests/query_packs_tests.cpp deleted file mode 100644 index 22a67fb..0000000 --- a/osquery/config/parsers/tests/query_packs_tests.cpp +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include - -#include - -#include "osquery/core/test_util.h" - -namespace pt = boost::property_tree; - -namespace osquery { - -// Test the pack version checker. -bool versionChecker(const std::string& pack, const std::string& version); -// Test the pack platform checker. -bool platformChecker(const std::string& required, const std::string& platform); - -pt::ptree getQueryPacksContent() { - pt::ptree pack_tree; - auto pack_path = kTestDataPath + "test_pack.conf"; - auto status = osquery::parseJSON(pack_path, pack_tree); - return pack_tree.get_child("queries"); -} - -std::map getQueryPacksExpectedResults() { - std::map result; - pt::ptree aux_data; - - std::string query = "select * from launchd"; - aux_data.put("query", query); - int interval = 414141; - aux_data.put("interval", interval); - std::string platform = "whatever"; - aux_data.put("platform", platform); - std::string version = "1.0.0"; - aux_data.put("version", version); - std::string description = "Very descriptive description"; - aux_data.put("description", description); - std::string value = "Value overflow"; - aux_data.put("value", value); - - result.insert(std::pair("launchd", aux_data)); - - return result; -} - -class QueryPacksConfigTests : public testing::Test {}; - -TEST_F(QueryPacksConfigTests, version_comparisons) { - EXPECT_TRUE(versionChecker("1.0.0", "1.0.0")); - EXPECT_TRUE(versionChecker("1.0.0", "1.2.0")); - EXPECT_TRUE(versionChecker("1.0", "1.2.0")); - EXPECT_TRUE(versionChecker("1.0", "1.0.2")); - EXPECT_TRUE(versionChecker("1.0.0", "1.0.2-r1")); - EXPECT_FALSE(versionChecker("1.2", "1.0.2")); - EXPECT_TRUE(versionChecker("1.0.0-r1", "1.0.0")); -} - -TEST_F(QueryPacksConfigTests, platform_comparisons) { -#ifdef __linux__ - // If the platform is linux and the required platform is linux, match - EXPECT_TRUE(platformChecker("linux", "ubuntu")); - EXPECT_TRUE(platformChecker("linux", "who_knows_what")); -#endif - EXPECT_TRUE(platformChecker("linux,darwin", "darwin")); - EXPECT_TRUE(platformChecker("darwin", "darwin")); - EXPECT_FALSE(platformChecker("darwin", "linux")); - - EXPECT_TRUE(platformChecker(" darwin", "darwin")); - // There are no logical operators, just matching. - EXPECT_TRUE(platformChecker("!darwin", "darwin")); - - EXPECT_TRUE(platformChecker("all", "darwin")); - EXPECT_TRUE(platformChecker("any", "darwin")); -} - -TEST_F(QueryPacksConfigTests, test_query_packs_configuration) { - auto data = getQueryPacksContent(); - auto expected = getQueryPacksExpectedResults(); - auto& real_ld = data.get_child("launchd"); - auto& expect_ld = expected["launchd"]; - - EXPECT_EQ(expect_ld.get("query", ""), real_ld.get("query", "")); - EXPECT_EQ(expect_ld.get("interval", 0), real_ld.get("interval", 0)); - EXPECT_EQ(expect_ld.get("platform", ""), real_ld.get("platform", "")); - EXPECT_EQ(expect_ld.get("version", ""), real_ld.get("version", "")); - EXPECT_EQ(expect_ld.get("description", ""), real_ld.get("description", "")); - EXPECT_EQ(expect_ld.get("value", ""), real_ld.get("value", "")); -} -} diff --git a/osquery/config/plugins/filesystem.cpp b/osquery/config/plugins/filesystem.cpp deleted file mode 100644 index e73bb78..0000000 --- a/osquery/config/plugins/filesystem.cpp +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include - -#include - -#include -#include -#include -#include - -namespace fs = boost::filesystem; - -namespace osquery { - -CLI_FLAG(string, - config_path, - "/var/osquery/osquery.conf", - "Path to JSON config file"); - -class FilesystemConfigPlugin : public ConfigPlugin { - public: - Status genConfig(std::map& config); -}; - -REGISTER(FilesystemConfigPlugin, "config", "filesystem"); - -Status FilesystemConfigPlugin::genConfig( - std::map& config) { - if (!fs::is_regular_file(FLAGS_config_path)) { - return Status(1, "config file does not exist"); - } - - std::vector conf_files; - resolveFilePattern(FLAGS_config_path + ".d/%.conf", conf_files); - std::sort(conf_files.begin(), conf_files.end()); - conf_files.push_back(FLAGS_config_path); - - for (const auto& path : conf_files) { - std::string content; - if (readFile(path, content).ok()) { - config[path] = content; - } - } - return Status(0, "OK"); -} -} diff --git a/osquery/config/tests/config_tests.cpp b/osquery/config/tests/config_tests.cpp deleted file mode 100644 index 38a786b..0000000 --- a/osquery/config/tests/config_tests.cpp +++ /dev/null @@ -1,307 +0,0 @@ -/* - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ -#include - -#include - -#include -#include -#include -#include -#include - -#include "osquery/core/test_util.h" - -namespace osquery { - -// The config_path flag is defined in the filesystem config plugin. -DECLARE_string(config_path); - -class ConfigTests : public testing::Test { - public: - ConfigTests() { - Registry::setActive("config", "filesystem"); - FLAGS_config_path = kTestDataPath + "test.config"; - } - - protected: - void SetUp() { - createMockFileStructure(); - Registry::setUp(); - Config::load(); - } - - void TearDown() { tearDownMockFileStructure(); } -}; - -class TestConfigPlugin : public ConfigPlugin { - public: - TestConfigPlugin() {} - Status genConfig(std::map& config) { - config["data"] = "foobar"; - return Status(0, "OK"); - ; - } -}; - -TEST_F(ConfigTests, test_plugin) { - Registry::add("config", "test"); - - // Change the active config plugin. - EXPECT_TRUE(Registry::setActive("config", "test").ok()); - - PluginResponse response; - auto status = Registry::call("config", {{"action", "genConfig"}}, response); - - EXPECT_EQ(status.ok(), true); - EXPECT_EQ(status.toString(), "OK"); - EXPECT_EQ(response[0].at("data"), "foobar"); -} - -/* deprecated -TEST_F(ConfigTests, test_queries_execute) { - ConfigDataInstance config; - EXPECT_EQ(config.schedule().size(), 3); -} - -TEST_F(ConfigTests, test_watched_files) { - ConfigDataInstance config; - ASSERT_EQ(config.files().size(), 3); - // From the deprecated "additional_monitoring" collection. - EXPECT_EQ(config.files().at("downloads").size(), 1); - - // From the new, recommended top-level "file_paths" collection. - EXPECT_EQ(config.files().at("system_binaries").size(), 2); -} -*/ - -TEST_F(ConfigTests, test_locking) { - { - // Assume multiple instance accessors will be active. - ConfigDataInstance config1; - ConfigDataInstance config2; - - // But a unique lock cannot be acquired. - boost::unique_lock lock(Config::getInstance().mutex_, - boost::defer_lock); - ASSERT_FALSE(lock.try_lock()); - } - - { - // However, a unique lock can be obtained when without instances accessors. - boost::unique_lock lock(Config::getInstance().mutex_, - boost::defer_lock); - ASSERT_TRUE(lock.try_lock()); - } -} - -TEST_F(ConfigTests, test_config_update) { - std::string digest; - // Get a snapshot of the digest before making config updates. - auto status = Config::getMD5(digest); - EXPECT_TRUE(status); - - // Request an update of the 'new_source1'. Set new1 = value. - status = - Config::update({{"new_source1", "{\"options\": {\"new1\": \"value\"}}"}}); - EXPECT_TRUE(status); - - // At least, the amalgamated config digest should have changed. - std::string new_digest; - Config::getMD5(new_digest); - EXPECT_NE(digest, new_digest); - - // Access the option that was added in the update to source 'new_source1'. - { - ConfigDataInstance config; - auto option = config.data().get("options.new1", ""); - EXPECT_EQ(option, "value"); - } - - // Add a lexically larger source that emits the same option 'new1'. - Config::update({{"new_source2", "{\"options\": {\"new1\": \"changed\"}}"}}); - - { - ConfigDataInstance config; - auto option = config.data().get("options.new1", ""); - // Expect the amalgamation to have overwritten 'new_source1'. - EXPECT_EQ(option, "changed"); - } - - // Again add a source but emit a different option, both 'new1' and 'new2' - // should be in the amalgamated/merged config. - Config::update({{"new_source3", "{\"options\": {\"new2\": \"different\"}}"}}); - - { - ConfigDataInstance config; - auto option = config.data().get("options.new1", ""); - EXPECT_EQ(option, "changed"); - option = config.data().get("options.new2", ""); - EXPECT_EQ(option, "different"); - } -} - -TEST_F(ConfigTests, test_bad_config_update) { - std::string bad_json = "{\"options\": {},}"; - ASSERT_NO_THROW(Config::update({{"bad_source", bad_json}})); -} - -class TestConfigParserPlugin : public ConfigParserPlugin { - public: - std::vector keys() { - // This config parser requests the follow top-level-config keys. - return {"dictionary", "dictionary2", "list"}; - } - - Status update(const std::map& config) { - // Set a simple boolean indicating the update callin occurred. - update_called = true; - // Copy all expected keys into the parser's data. - for (const auto& key : config) { - data_.put_child(key.first, key.second); - } - - // Set parser-rendered additional data. - // Other plugins may request this "rendered/derived" data using a - // ConfigDataInstance and the getParsedData method. - data_.put("dictionary3.key2", "value2"); - return Status(0, "OK"); - } - - // Flag tracking that the update method was called. - static bool update_called; - - private: - FRIEND_TEST(ConfigTests, test_config_parser); -}; - -// An intermediate boolean to check parser updates. -bool TestConfigParserPlugin::update_called = false; - -TEST_F(ConfigTests, test_config_parser) { - // Register a config parser plugin, and call setup. - Registry::add("config_parser", "test"); - Registry::get("config_parser", "test")->setUp(); - - { - // Access the parser's data without having updated the configuration. - ConfigDataInstance config; - const auto& test_data = config.getParsedData("test"); - - // Expect the setUp method to have run and set blank defaults. - // Accessing an invalid property tree key will abort. - ASSERT_EQ(test_data.get_child("dictionary").count(""), 0); - } - - // Update or load the config, expect the parser to be called. - Config::update( - {{"source1", - "{\"dictionary\": {\"key1\": \"value1\"}, \"list\": [\"first\"]}"}}); - ASSERT_TRUE(TestConfigParserPlugin::update_called); - - { - // Now access the parser's data AFTER updating the config (no longer blank) - ConfigDataInstance config; - const auto& test_data = config.getParsedData("test"); - - // Expect a value that existed in the configuration. - EXPECT_EQ(test_data.count("dictionary"), 1); - EXPECT_EQ(test_data.get("dictionary.key1", ""), "value1"); - // Expect a value for every key the parser requested. - // Every requested key will be present, event if the key's tree is empty. - EXPECT_EQ(test_data.count("dictionary2"), 1); - // Expect the parser-created data item. - EXPECT_EQ(test_data.count("dictionary3"), 1); - EXPECT_EQ(test_data.get("dictionary3.key2", ""), "value2"); - } - - // Update from a secondary source into a dictionary. - // Expect that the keys in the top-level dictionary are merged. - Config::update({{"source2", "{\"dictionary\": {\"key3\": \"value3\"}}"}}); - // Update from a third source into a list. - // Expect that the items from each source in the top-level list are merged. - Config::update({{"source3", "{\"list\": [\"second\"]}"}}); - - { - ConfigDataInstance config; - const auto& test_data = config.getParsedData("test"); - - EXPECT_EQ(test_data.count("dictionary"), 1); - EXPECT_EQ(test_data.get("dictionary.key1", ""), "value1"); - EXPECT_EQ(test_data.get("dictionary.key3", ""), "value3"); - EXPECT_EQ(test_data.count("list"), 1); - EXPECT_EQ(test_data.get_child("list").count(""), 2); - } -} - -class TestConfigMutationParserPlugin : public ConfigParserPlugin { - public: - std::vector keys() { - // This config parser wants access to the well-known schedule key. - return {"schedule"}; - } - - Status update(const std::map& config) { - // The merged raw schedule is available as a property tree. - auto& schedule_data = config.at("schedule"); - (void)schedule_data; - - { - // But we want access to the parsed schedule structure. - ConfigDataInstance _config; - auto& data = mutableConfigData(_config); - - ScheduledQuery query; - query.query = "new query"; - query.interval = 1; - data.schedule["test_config_mutation"] = query; - } - - return Status(0, "OK"); - } - - private: - FRIEND_TEST(ConfigTests, test_config_mutaion_parser); -}; - -TEST_F(ConfigTests, test_config_mutaion_parser) { - Registry::add("config_parser", "mutable"); - Registry::get("config_parser", "mutable")->setUp(); - - // Update or load the config, expect the parser to be called. - Config::update({{"source1", "{\"schedule\": {}}"}}); - - { - ConfigDataInstance config; - // The config schedule should have been mutated. - EXPECT_EQ(config.schedule().count("test_config_mutation"), 1); - } -} - -TEST_F(ConfigTests, test_splay) { - auto val1 = splayValue(100, 10); - EXPECT_GE(val1, 90); - EXPECT_LE(val1, 110); - - auto val2 = splayValue(100, 10); - EXPECT_GE(val2, 90); - EXPECT_LE(val2, 110); - - auto val3 = splayValue(10, 0); - EXPECT_EQ(val3, 10); - - auto val4 = splayValue(100, 1); - EXPECT_GE(val4, 99); - EXPECT_LE(val4, 101); - - auto val5 = splayValue(1, 10); - EXPECT_EQ(val5, 1); -} -} diff --git a/osquery/config/update.cpp b/osquery/config/update.cpp deleted file mode 100644 index 6421c1a..0000000 --- a/osquery/config/update.cpp +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include - -namespace osquery { - -/** - * @brief A special config plugin that updates an osquery core's config. - * - * Config plugins may asynchronously change config data for the core osquery - * process. This is a rare instance where a plugin acts to change core state. - * Plugins normally act on behalf of a registry or extension call. - * To acheive plugin-initiated calls, Config plugins chain calls to plugins - * using the UpdateConfigPlugin named 'update'. - * - * Plugins do not need to implement call-chaining explicitly. If an extension - * plugin implements an asynchronous feature it should call `Config::update` - * directly. The osquery config will check if the registry is external, meaning - * the config instance is running as an extension. If external, config will - * route the update request and the registry will send missing (in this case - * "config/update" is missing) requests to core. - */ -class UpdateConfigPlugin : public ConfigPlugin { - public: - Status genConfig(std::map& config) { - return Status(0, "Unused"); - } -}; - -REGISTER(UpdateConfigPlugin, "config", "update"); -} diff --git a/osquery/core/CMakeLists.txt b/osquery/core/CMakeLists.txt deleted file mode 100644 index fe39034..0000000 --- a/osquery/core/CMakeLists.txt +++ /dev/null @@ -1,28 +0,0 @@ -# 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_core init.cpp - conversions.cpp - system.cpp - text.cpp - tables.cpp - flags.cpp - hash.cpp - watcher.cpp) - -# TODO(Sangwan): Detach from core -ADD_OSQUERY_LIBRARY(osquery_test_util test_util.cpp) - -FILE(GLOB OSQUERY_CORE_TESTS "tests/*.cpp") -ADD_OSQUERY_TEST(${OSQUERY_CORE_TESTS}) diff --git a/osquery/core/conversions.cpp b/osquery/core/conversions.cpp deleted file mode 100644 index d26c22a..0000000 --- a/osquery/core/conversions.cpp +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include - -#include -#include -#include -#include - -#include "osquery/core/conversions.h" - -namespace bai = boost::archive::iterators; - -namespace osquery { - -typedef bai::binary_from_base64 base64_str; -typedef bai::transform_width base64_dec; -typedef bai::transform_width base64_enc; -typedef bai::base64_from_binary it_base64; - -std::string base64Decode(const std::string& encoded) { - std::string is; - std::stringstream os; - - is = encoded; - boost::replace_all(is, "\r\n", ""); - boost::replace_all(is, "\n", ""); - uint32_t size = is.size(); - - // Remove the padding characters - if (size && is[size - 1] == '=') { - --size; - if (size && is[size - 1] == '=') { - --size; - } - } - - if (size == 0) { - return ""; - } - - std::copy(base64_dec(is.data()), - base64_dec(is.data() + size), - std::ostream_iterator(os)); - - return os.str(); -} - -std::string base64Encode(const std::string& unencoded) { - std::stringstream os; - - if (unencoded.size() == 0) { - return std::string(); - } - - unsigned int writePaddChars = (3-unencoded.length()%3)%3; - std::string base64(it_base64(unencoded.begin()), it_base64(unencoded.end())); - base64.append(writePaddChars,'='); - os << base64; - return os.str(); -} -} diff --git a/osquery/core/conversions.h b/osquery/core/conversions.h deleted file mode 100644 index c8a0a1c..0000000 --- a/osquery/core/conversions.h +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#pragma once - -#include - -#include -#include - -#ifdef DARWIN -#include -#endif - -namespace osquery { - -template -void do_release_boost(typename boost::shared_ptr const&, T*) {} - -/** - * @brief Convert a boost::shared_ptr to a std::shared_ptr - */ -template -typename std::shared_ptr boost_to_std_shared_ptr( - typename boost::shared_ptr const& p) { - return std::shared_ptr(p.get(), boost::bind(&do_release_boost, p, _1)); -} - -template -void do_release_std(typename std::shared_ptr const&, T*) {} - -/** - * @brief Convert a std::shared_ptr to a boost::shared_ptr - */ -template -typename boost::shared_ptr std_to_boost_shared_ptr( - typename std::shared_ptr const& p) { - return boost::shared_ptr(p.get(), boost::bind(&do_release_std, p, _1)); -} - -/** - * @brief Decode a base64 encoded string. - * - * @param encoded The encode base64 string. - * @return Decoded string. - */ -std::string base64Decode(const std::string& encoded); - -/** - * @brief Encode a string. - * - * @param A string to encode. - * @return Encoded string. - */ -std::string base64Encode(const std::string& unencoded); - -#ifdef DARWIN -/** - * @brief Convert a CFStringRef to a std::string. - */ -std::string stringFromCFString(const CFStringRef& cf_string); - -/** - * @brief Convert a CFNumberRef to a std::string. - */ -std::string stringFromCFNumber(const CFDataRef& cf_number); -std::string stringFromCFData(const CFDataRef& cf_data); -#endif - -} diff --git a/osquery/core/flags.cpp b/osquery/core/flags.cpp deleted file mode 100644 index e1474a2..0000000 --- a/osquery/core/flags.cpp +++ /dev/null @@ -1,172 +0,0 @@ -/* - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include - -namespace boost { -template <> -bool lexical_cast(const std::string& arg) { - std::istringstream ss(arg); - bool b; - ss >> std::boolalpha >> b; - return b; -} - -template <> -std::string lexical_cast(const bool& b) { - std::ostringstream ss; - ss << std::boolalpha << b; - return ss.str(); -} -} - -namespace osquery { - -int Flag::create(const std::string& name, const FlagDetail& flag) { - instance().flags_.insert(std::make_pair(name, flag)); - return 0; -} - -int Flag::createAlias(const std::string& alias, const FlagDetail& flag) { - instance().aliases_.insert(std::make_pair(alias, flag)); - return 0; -} - -Status Flag::getDefaultValue(const std::string& name, std::string& value) { - GFLAGS_NAMESPACE::CommandLineFlagInfo info; - if (!GFLAGS_NAMESPACE::GetCommandLineFlagInfo(name.c_str(), &info)) { - return Status(1, "Flags name not found."); - } - - value = info.default_value; - return Status(0, "OK"); -} - -bool Flag::isDefault(const std::string& name) { - GFLAGS_NAMESPACE::CommandLineFlagInfo info; - if (!GFLAGS_NAMESPACE::GetCommandLineFlagInfo(name.c_str(), &info)) { - return false; - } - - return info.is_default; -} - -std::string Flag::getValue(const std::string& name) { - std::string current_value; - GFLAGS_NAMESPACE::GetCommandLineOption(name.c_str(), ¤t_value); - return current_value; -} - -std::string Flag::getType(const std::string& name) { - GFLAGS_NAMESPACE::CommandLineFlagInfo info; - if (!GFLAGS_NAMESPACE::GetCommandLineFlagInfo(name.c_str(), &info)) { - return ""; - } - return info.type; -} - -std::string Flag::getDescription(const std::string& name) { - if (instance().flags_.count(name)) { - return instance().flags_.at(name).description; - } - - if (instance().aliases_.count(name)) { - return getDescription(instance().aliases_.at(name).description); - } - return ""; -} - -Status Flag::updateValue(const std::string& name, const std::string& value) { - if (instance().flags_.count(name) > 0) { - GFLAGS_NAMESPACE::SetCommandLineOption(name.c_str(), value.c_str()); - return Status(0, "OK"); - } else if (instance().aliases_.count(name) > 0) { - // Updating a flag by an alias name. - auto& real_name = instance().aliases_.at(name).description; - GFLAGS_NAMESPACE::SetCommandLineOption(real_name.c_str(), value.c_str()); - return Status(0, "OK"); - } - return Status(1, "Flag not found"); -} - -std::map Flag::flags() { - std::vector info; - GFLAGS_NAMESPACE::GetAllFlags(&info); - - std::map flags; - for (const auto& flag : info) { - if (instance().flags_.count(flag.name) == 0) { - // This flag info was not defined within osquery. - continue; - } - - // Set the flag info from the internal info kept by Gflags, except for - // the stored description. Gflag keeps an "unknown" value if the flag - // was declared without a definition. - flags[flag.name] = {flag.type, - instance().flags_.at(flag.name).description, - flag.default_value, - flag.current_value, - instance().flags_.at(flag.name)}; - } - return flags; -} - -void Flag::printFlags(bool shell, bool external, bool cli) { - std::vector info; - GFLAGS_NAMESPACE::GetAllFlags(&info); - auto& details = instance().flags_; - - // Determine max indent needed for all flag names. - size_t max = 0; - for (const auto& flag : details) { - max = (max > flag.first.size()) ? max : flag.first.size(); - } - // Additional index for flag values. - max += 6; - - auto& aliases = instance().aliases_; - for (const auto& flag : info) { - if (details.count(flag.name) > 0) { - const auto& detail = details.at(flag.name); - if ((shell && !detail.shell) || (!shell && detail.shell) || - (external && !detail.external) || (!external && detail.external) || - (cli && !detail.cli) || (!cli && detail.cli) || detail.hidden) { - continue; - } - } else if (aliases.count(flag.name) > 0) { - const auto& alias = aliases.at(flag.name); - // Aliases are only printed if this is an external tool and the alias - // is external. - if (!alias.external || !external) { - continue; - } - } else { - // This flag was not defined as an osquery flag or flag alias. - continue; - } - - fprintf(stdout, " --%s", flag.name.c_str()); - - int pad = max; - if (flag.type != "bool") { - fprintf(stdout, " VALUE"); - pad -= 6; - } - pad -= flag.name.size(); - - if (pad > 0 && pad < 80) { - // Never pad more than 80 characters. - fprintf(stdout, "%s", std::string(pad, ' ').c_str()); - } - fprintf(stdout, " %s\n", getDescription(flag.name).c_str()); - } -} -} diff --git a/osquery/core/hash.cpp b/osquery/core/hash.cpp deleted file mode 100644 index fc7d4a9..0000000 --- a/osquery/core/hash.cpp +++ /dev/null @@ -1,119 +0,0 @@ -/* - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include -#include - -#include -#include -#include - -namespace osquery { - -#ifdef __APPLE__ - #import - #define __HASH_API(name) CC_##name -#else - #include - #include - #define __HASH_API(name) name - - #define SHA1_DIGEST_LENGTH SHA_DIGEST_LENGTH - #define SHA1_CTX SHA_CTX -#endif - -#define HASH_CHUNK_SIZE 1024 - -Hash::~Hash() { - if (ctx_ != nullptr) { - free(ctx_); - } -} - -Hash::Hash(HashType algorithm) : algorithm_(algorithm) { - if (algorithm_ == HASH_TYPE_MD5) { - length_ = __HASH_API(MD5_DIGEST_LENGTH); - ctx_ = (__HASH_API(MD5_CTX)*)malloc(sizeof(__HASH_API(MD5_CTX))); - __HASH_API(MD5_Init)((__HASH_API(MD5_CTX)*)ctx_); - } else if (algorithm_ == HASH_TYPE_SHA1) { - length_ = __HASH_API(SHA1_DIGEST_LENGTH); - ctx_ = (__HASH_API(SHA1_CTX)*)malloc(sizeof(__HASH_API(SHA1_CTX))); - __HASH_API(SHA1_Init)((__HASH_API(SHA1_CTX)*)ctx_); - } else if (algorithm_ == HASH_TYPE_SHA256) { - length_ = __HASH_API(SHA256_DIGEST_LENGTH); - ctx_ = (__HASH_API(SHA256_CTX)*)malloc(sizeof(__HASH_API(SHA256_CTX))); - __HASH_API(SHA256_Init)((__HASH_API(SHA256_CTX)*)ctx_); - } else { - throw std::domain_error("Unknown hash function"); - } -} - -void Hash::update(const void* buffer, size_t size) { - if (algorithm_ == HASH_TYPE_MD5) { - __HASH_API(MD5_Update)((__HASH_API(MD5_CTX)*)ctx_, buffer, size); - } else if (algorithm_ == HASH_TYPE_SHA1) { - __HASH_API(SHA1_Update)((__HASH_API(SHA1_CTX)*)ctx_, buffer, size); - } else if (algorithm_ == HASH_TYPE_SHA256) { - __HASH_API(SHA256_Update)((__HASH_API(SHA256_CTX)*)ctx_, buffer, size); - } -} - -std::string Hash::digest() { - unsigned char hash[length_]; - - if (algorithm_ == HASH_TYPE_MD5) { - __HASH_API(MD5_Final)(hash, (__HASH_API(MD5_CTX)*)ctx_); - } else if (algorithm_ == HASH_TYPE_SHA1) { - __HASH_API(SHA1_Final)(hash, (__HASH_API(SHA1_CTX)*)ctx_); - } else if (algorithm_ == HASH_TYPE_SHA256) { - __HASH_API(SHA256_Final)(hash, (__HASH_API(SHA256_CTX)*)ctx_); - } - - // The hash value is only relevant as a hex digest. - std::stringstream digest; - for (int i = 0; i < length_; i++) { - digest << std::hex << std::setw(2) << std::setfill('0') << (int)hash[i]; - } - - return digest.str(); -} - -std::string hashFromBuffer(HashType hash_type, const void* buffer, size_t size) { - Hash hash(hash_type); - hash.update(buffer, size); - return hash.digest(); -} - -std::string hashFromFile(HashType hash_type, const std::string& path) { - // Perform a dry-run of a file read without filling in any content. - auto status = readFile(path); - if (!status.ok()) { - return ""; - } - - Hash hash(hash_type); - // Use the canonicalized path returned from a successful readFile dry-run. - FILE* file = fopen(status.what().c_str(), "rb"); - if (file == nullptr) { - VLOG(1) << "Cannot hash/open file " << path; - return ""; - } - - // Then call updates with read chunks. - size_t bytes_read = 0; - unsigned char buffer[HASH_CHUNK_SIZE]; - while ((bytes_read = fread(buffer, 1, HASH_CHUNK_SIZE, file))) { - hash.update(buffer, bytes_read); - } - - fclose(file); - return hash.digest(); -} -} diff --git a/osquery/core/init.cpp b/osquery/core/init.cpp deleted file mode 100644 index 655ab2d..0000000 --- a/osquery/core/init.cpp +++ /dev/null @@ -1,385 +0,0 @@ -/* - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include -#include - -#include -#include -#include -#include - -#include - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "osquery/core/watcher.h" -#include "osquery/database/db_handle.h" - -#ifdef __linux__ -#include -#include - -/* - * These are the io priority groups as implemented by CFQ. RT is the realtime - * class, it always gets premium service. BE is the best-effort scheduling - * class, the default for any process. IDLE is the idle scheduling class, it - * is only served when no one else is using the disk. - */ -enum { - IOPRIO_CLASS_NONE, - IOPRIO_CLASS_RT, - IOPRIO_CLASS_BE, - IOPRIO_CLASS_IDLE, -}; - -/* - * 8 best effort priority levels are supported - */ -#define IOPRIO_BE_NR (8) - -enum { - IOPRIO_WHO_PROCESS = 1, - IOPRIO_WHO_PGRP, - IOPRIO_WHO_USER, -}; -#endif - -namespace fs = boost::filesystem; - -namespace osquery { - -#define DESCRIPTION \ - "osquery %s, your OS as a high-performance relational database\n" -#define EPILOG "\nosquery project page .\n" -#define OPTIONS \ - "\nosquery configuration options (set by config or CLI flags):\n\n" -#define OPTIONS_SHELL "\nosquery shell-only CLI flags:\n\n" -#define OPTIONS_CLI "osquery%s command line flags:\n\n" -#define USAGE "Usage: %s [OPTION]... %s\n\n" -#define CONFIG_ERROR \ - "You are using default configurations for osqueryd for one or more of the " \ - "following\n" \ - "flags: pidfile, db_path.\n\n" \ - "These options create files in /var/osquery but it looks like that path " \ - "has not\n" \ - "been created. Please consider explicitly defining those " \ - "options as a different \n" \ - "path. Additionally, review the \"using osqueryd\" wiki page:\n" \ - " - https://osquery.readthedocs.org/en/latest/introduction/using-osqueryd/" \ - "\n\n"; - -typedef std::chrono::high_resolution_clock chrono_clock; - -CLI_FLAG(bool, - config_check, - false, - "Check the format of an osquery config and exit"); - -#ifndef __APPLE__ -CLI_FLAG(bool, daemonize, false, "Run as daemon (osqueryd only)"); -#endif - -ToolType kToolType = OSQUERY_TOOL_UNKNOWN; - -void printUsage(const std::string& binary, int tool) { - // Parse help options before gflags. Only display osquery-related options. - fprintf(stdout, DESCRIPTION, kVersion.c_str()); - if (tool == OSQUERY_TOOL_SHELL) { - // The shell allows a caller to run a single SQL statement and exit. - fprintf(stdout, USAGE, binary.c_str(), "[SQL STATEMENT]"); - } else { - fprintf(stdout, USAGE, binary.c_str(), ""); - } - - if (tool == OSQUERY_EXTENSION) { - fprintf(stdout, OPTIONS_CLI, " extension"); - Flag::printFlags(false, true); - } else { - fprintf(stdout, OPTIONS_CLI, ""); - Flag::printFlags(false, false, true); - fprintf(stdout, OPTIONS); - Flag::printFlags(); - } - - if (tool == OSQUERY_TOOL_SHELL) { - // Print shell flags. - fprintf(stdout, OPTIONS_SHELL); - Flag::printFlags(true); - } - - fprintf(stdout, EPILOG); -} - -Initializer::Initializer(int& argc, char**& argv, ToolType tool) - : argc_(&argc), - argv_(&argv), - tool_(tool), - binary_(fs::path(std::string(argv[0])).filename().string()) { - std::srand(chrono_clock::now().time_since_epoch().count()); - - // osquery implements a custom help/usage output. - for (int i = 1; i < *argc_; i++) { - auto help = std::string((*argv_)[i]); - if ((help == "--help" || help == "-help" || help == "--h" || - help == "-h") && - tool != OSQUERY_TOOL_TEST) { - printUsage(binary_, tool_); - ::exit(0); - } - } - -// To change the default config plugin, compile osquery with -// -DOSQUERY_DEFAULT_CONFIG_PLUGIN= -#ifdef OSQUERY_DEFAULT_CONFIG_PLUGIN - FLAGS_config_plugin = STR(OSQUERY_DEFAULT_CONFIG_PLUGIN); -#endif - -// To change the default logger plugin, compile osquery with -// -DOSQUERY_DEFAULT_LOGGER_PLUGIN= -#ifdef OSQUERY_DEFAULT_LOGGER_PLUGIN - FLAGS_logger_plugin = STR(OSQUERY_DEFAULT_LOGGER_PLUGIN); -#endif - - // Set version string from CMake build - GFLAGS_NAMESPACE::SetVersionString(kVersion.c_str()); - - // Let gflags parse the non-help options/flags. - GFLAGS_NAMESPACE::ParseCommandLineFlags( - argc_, argv_, (tool == OSQUERY_TOOL_SHELL)); - - // Set the tool type to allow runtime decisions based on daemon, shell, etc. - kToolType = tool; - if (tool == OSQUERY_TOOL_SHELL) { - // The shell is transient, rewrite config-loaded paths. - FLAGS_disable_logging = true; - // Get the caller's home dir for temporary storage/state management. - auto homedir = osqueryHomeDirectory(); - if (osquery::pathExists(homedir).ok() || - boost::filesystem::create_directory(homedir)) { - // Only apply user/shell-specific paths if not overridden by CLI flag. - if (Flag::isDefault("database_path")) { - osquery::FLAGS_database_path = homedir + "/shell.db"; - } - if (Flag::isDefault("extensions_socket")) { - osquery::FLAGS_extensions_socket = homedir + "/shell.em"; - } - } - } - - // If the caller is checking configuration, disable the watchdog/worker. - if (FLAGS_config_check) { - FLAGS_disable_watchdog = true; - } - - // Initialize the status and results logger. - initStatusLogger(binary_); - if (tool != OSQUERY_EXTENSION) { - if (isWorker()) { - VLOG(1) << "osquery worker initialized [watcher=" - << getenv("OSQUERY_WORKER") << "]"; - } else { - VLOG(1) << "osquery initialized [version=" << kVersion << "]"; - } - } else { - VLOG(1) << "osquery extension initialized [sdk=" << kSDKVersion << "]"; - } -} - -void Initializer::initDaemon() { - if (FLAGS_config_check) { - // No need to daemonize, emit log lines, or create process mutexes. - return; - } - -#ifndef __APPLE__ - // OS X uses launchd to daemonize. - if (osquery::FLAGS_daemonize) { - if (daemon(0, 0) == -1) { - ::exit(EXIT_FAILURE); - } - } -#endif - - // Print the version to SYSLOG. - syslog( - LOG_NOTICE, "%s started [version=%s]", binary_.c_str(), kVersion.c_str()); - - // Check if /var/osquery exists - if ((Flag::isDefault("pidfile") || Flag::isDefault("database_path")) && - !isDirectory("/var/osquery")) { - std::cerr << CONFIG_ERROR - } - - // Create a process mutex around the daemon. - auto pid_status = createPidFile(); - if (!pid_status.ok()) { - LOG(ERROR) << binary_ << " initialize failed: " << pid_status.toString(); - ::exit(EXIT_FAILURE); - } - - // Nice ourselves if using a watchdog and the level is not too permissive. - if (!FLAGS_disable_watchdog && - FLAGS_watchdog_level >= WATCHDOG_LEVEL_DEFAULT && - FLAGS_watchdog_level != WATCHDOG_LEVEL_DEBUG) { - // Set CPU scheduling I/O limits. - setpriority(PRIO_PGRP, 0, 10); -#ifdef __linux__ - // Using: ioprio_set(IOPRIO_WHO_PGRP, 0, IOPRIO_CLASS_IDLE); - syscall(SYS_ioprio_set, IOPRIO_WHO_PGRP, 0, IOPRIO_CLASS_IDLE); -#elif defined(__APPLE__) || defined(__FreeBSD__) - setiopolicy_np(IOPOL_TYPE_DISK, IOPOL_SCOPE_PROCESS, IOPOL_THROTTLE); -#endif - } -} - -void Initializer::initWatcher() { - // The watcher takes a list of paths to autoload extensions from. - osquery::loadExtensions(); - - // Add a watcher service thread to start/watch an optional worker and set - // of optional extensions in the autoload paths. - if (Watcher::hasManagedExtensions() || !FLAGS_disable_watchdog) { - Dispatcher::addService(std::make_shared( - *argc_, *argv_, !FLAGS_disable_watchdog)); - } - - // If there are no autoloaded extensions, the watcher service will end, - // otherwise it will continue as a background thread and respawn them. - // If the watcher is also a worker watchdog it will do nothing but monitor - // the extensions and worker process. - if (!FLAGS_disable_watchdog) { - Dispatcher::joinServices(); - // Execution should never reach this point. - ::exit(EXIT_FAILURE); - } -} - -void Initializer::initWorker(const std::string& name) { - // Clear worker's arguments. - size_t name_size = strlen((*argv_)[0]); - auto original_name = std::string((*argv_)[0]); - for (int i = 0; i < *argc_; i++) { - if ((*argv_)[i] != nullptr) { - memset((*argv_)[i], ' ', strlen((*argv_)[i])); - } - } - - // Set the worker's process name. - if (name.size() < name_size) { - std::copy(name.begin(), name.end(), (*argv_)[0]); - (*argv_)[0][name.size()] = '\0'; - } else { - std::copy(original_name.begin(), original_name.end(), (*argv_)[0]); - (*argv_)[0][original_name.size()] = '\0'; - } - - // Start a watcher watcher thread to exit the process if the watcher exits. - Dispatcher::addService(std::make_shared(getppid())); -} - -void Initializer::initWorkerWatcher(const std::string& name) { - if (isWorker()) { - initWorker(name); - } else { - // The watcher will forever monitor and spawn additional workers. - initWatcher(); - } -} - -bool Initializer::isWorker() { return (getenv("OSQUERY_WORKER") != nullptr); } - -void Initializer::initActivePlugin(const std::string& type, - const std::string& name) { - // Use a delay, meaning the amount of milliseconds waited for extensions. - size_t delay = 0; - // The timeout is the maximum microseconds in seconds to wait for extensions. - size_t timeout = atoi(FLAGS_extensions_timeout.c_str()) * 1000000; - if (timeout < kExtensionInitializeLatencyUS * 10) { - timeout = kExtensionInitializeLatencyUS * 10; - } - while (!Registry::setActive(type, name)) { - if (!Watcher::hasManagedExtensions() || delay > timeout) { - LOG(ERROR) << "Active " << type << " plugin not found: " << name; - ::exit(EXIT_CATASTROPHIC); - } - delay += kExtensionInitializeLatencyUS; - ::usleep(kExtensionInitializeLatencyUS); - } -} - -void Initializer::start() { - // Load registry/extension modules before extensions. - osquery::loadModules(); - - // Pre-extension manager initialization options checking. - if (FLAGS_config_check && !Watcher::hasManagedExtensions()) { - FLAGS_disable_extensions = true; - } - - // Check the backing store by allocating and exiting on error. - if (!DBHandle::checkDB()) { - LOG(ERROR) << binary_ << " initialize failed: Could not open RocksDB"; - if (isWorker()) { - ::exit(EXIT_CATASTROPHIC); - } else { - ::exit(EXIT_FAILURE); - } - } - - // Bind to an extensions socket and wait for registry additions. - osquery::startExtensionManager(); - - // Then set the config plugin, which uses a single/active plugin. - initActivePlugin("config", FLAGS_config_plugin); - - // Run the setup for all lazy registries (tables, SQL). - Registry::setUp(); - - if (FLAGS_config_check) { - // The initiator requested an initialization and config check. - auto s = Config::checkConfig(); - if (!s.ok()) { - std::cerr << "Error reading config: " << s.toString() << "\n"; - } - // A configuration check exits the application. - ::exit(s.getCode()); - } - - // Load the osquery config using the default/active config plugin. - Config::load(); - - // Initialize the status and result plugin logger. - initActivePlugin("logger", FLAGS_logger_plugin); - initLogger(binary_); - - // Start event threads. - osquery::attachEvents(); - EventFactory::delay(); -} - -void Initializer::shutdown() { - // End any event type run loops. - EventFactory::end(); - - // Hopefully release memory used by global string constructors in gflags. - GFLAGS_NAMESPACE::ShutDownCommandLineFlags(); -} -} diff --git a/osquery/core/system.cpp b/osquery/core/system.cpp deleted file mode 100644 index 73da087..0000000 --- a/osquery/core/system.cpp +++ /dev/null @@ -1,166 +0,0 @@ -/* - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include -#include - -#include -#include - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -namespace fs = boost::filesystem; - -namespace osquery { - -/// The path to the pidfile for osqueryd -CLI_FLAG(string, - pidfile, - "/var/osquery/osqueryd.pidfile", - "Path to the daemon pidfile mutex"); - -/// Should the daemon force unload previously-running osqueryd daemons. -CLI_FLAG(bool, - force, - false, - "Force osqueryd to kill previously-running daemons"); - -std::string getHostname() { - char hostname[256] = {0}; // Linux max should be 64. - gethostname(hostname, sizeof(hostname) - 1); - std::string hostname_string = std::string(hostname); - boost::algorithm::trim(hostname_string); - return hostname_string; -} - -std::string generateNewUuid() { - boost::uuids::uuid uuid = boost::uuids::random_generator()(); - return boost::uuids::to_string(uuid); -} - -std::string generateHostUuid() { -#ifdef __APPLE__ - // Use the hardware uuid available on OSX to identify this machine - uuid_t id; - // wait at most 5 seconds for gethostuuid to return - const timespec wait = {5, 0}; - int result = gethostuuid(id, &wait); - if (result == 0) { - char out[128]; - uuid_unparse(id, out); - std::string uuid_string = std::string(out); - boost::algorithm::trim(uuid_string); - return uuid_string; - } else { - // unable to get the hardware uuid, just return a new uuid - return generateNewUuid(); - } -#else - return generateNewUuid(); -#endif -} - -std::string getAsciiTime() { - auto result = std::time(nullptr); - auto time_str = std::string(std::asctime(std::gmtime(&result))); - boost::algorithm::trim(time_str); - return time_str + " UTC"; -} - -int getUnixTime() { - auto result = std::time(nullptr); - return result; -} - -Status checkStalePid(const std::string& content) { - int pid; - try { - pid = boost::lexical_cast(content); - } catch (const boost::bad_lexical_cast& e) { - if (FLAGS_force) { - return Status(0, "Force loading and not parsing pidfile"); - } else { - return Status(1, "Could not parse pidfile"); - } - } - - int status = kill(pid, 0); - if (status != ESRCH) { - // The pid is running, check if it is an osqueryd process by name. - std::stringstream query_text; - query_text << "SELECT name FROM processes WHERE pid = " << pid - << " AND name = 'osqueryd';"; - auto q = SQL(query_text.str()); - if (!q.ok()) { - return Status(1, "Error querying processes: " + q.getMessageString()); - } - - if (q.rows().size() > 0) { - // If the process really is osqueryd, return an "error" status. - if (FLAGS_force) { - // The caller may choose to abort the existing daemon with --force. - status = kill(pid, SIGQUIT); - ::sleep(1); - - return Status(status, "Tried to force remove the existing osqueryd"); - } - - return Status(1, "osqueryd (" + content + ") is already running"); - } else { - LOG(INFO) << "Found stale process for osqueryd (" << content - << ") removing pidfile"; - } - } - - return Status(0, "OK"); -} - -Status createPidFile() { - // check if pidfile exists - auto exists = pathExists(FLAGS_pidfile); - if (exists.ok()) { - // if it exists, check if that pid is running. - std::string content; - auto read_status = readFile(FLAGS_pidfile, content); - if (!read_status.ok()) { - return Status(1, "Could not read pidfile: " + read_status.toString()); - } - - auto stale_status = checkStalePid(content); - if (!stale_status.ok()) { - return stale_status; - } - } - - // Now the pidfile is either the wrong pid or the pid is not running. - try { - boost::filesystem::remove(FLAGS_pidfile); - } catch (boost::filesystem::filesystem_error& e) { - // Unable to remove old pidfile. - LOG(WARNING) << "Unable to remove the osqueryd pidfile"; - } - - // If no pidfile exists or the existing pid was stale, write, log, and run. - auto pid = boost::lexical_cast(getpid()); - LOG(INFO) << "Writing osqueryd pid (" << pid << ") to " << FLAGS_pidfile; - auto status = writeTextFile(FLAGS_pidfile, pid, 0644); - return status; -} -} diff --git a/osquery/core/tables.cpp b/osquery/core/tables.cpp deleted file mode 100644 index d71fd22..0000000 --- a/osquery/core/tables.cpp +++ /dev/null @@ -1,241 +0,0 @@ -/* - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include - -#include -#include - -namespace pt = boost::property_tree; - -namespace osquery { - -Status TablePlugin::addExternal(const std::string& name, - const PluginResponse& response) { - // Attach the table. - if (response.size() == 0) { - // Invalid table route info. - return Status(1, "Invalid route info"); - } - - // Use the SQL registry to attach the name/definition. - return Registry::call("sql", "sql", {{"action", "attach"}, {"table", name}}); -} - -void TablePlugin::removeExternal(const std::string& name) { - // Detach the table name. - Registry::call("sql", "sql", {{"action", "detach"}, {"table", name}}); -} - -void TablePlugin::setRequestFromContext(const QueryContext& context, - PluginRequest& request) { - pt::ptree tree; - tree.put("limit", context.limit); - - // The QueryContext contains a constraint map from column to type information - // and the list of operand/expression constraints applied to that column from - // the query given. - pt::ptree constraints; - for (const auto& constraint : context.constraints) { - pt::ptree child; - child.put("name", constraint.first); - constraint.second.serialize(child); - constraints.push_back(std::make_pair("", child)); - } - tree.add_child("constraints", constraints); - - // Write the property tree as a JSON string into the PluginRequest. - std::ostringstream output; - try { - pt::write_json(output, tree, false); - } catch (const pt::json_parser::json_parser_error& e) { - // The content could not be represented as JSON. - } - request["context"] = output.str(); -} - -void TablePlugin::setResponseFromQueryData(const QueryData& data, - PluginResponse& response) { - response = std::move(data); -} - -void TablePlugin::setContextFromRequest(const PluginRequest& request, - QueryContext& context) { - if (request.count("context") == 0) { - return; - } - - // Read serialized context from PluginRequest. - pt::ptree tree; - try { - std::stringstream input; - input << request.at("context"); - pt::read_json(input, tree); - } catch (const pt::json_parser::json_parser_error& e) { - return; - } - - // Set the context limit and deserialize each column constraint list. - context.limit = tree.get("limit"); - for (const auto& constraint : tree.get_child("constraints")) { - auto column_name = constraint.second.get("name"); - context.constraints[column_name].unserialize(constraint.second); - } -} - -Status TablePlugin::call(const PluginRequest& request, - PluginResponse& response) { - response.clear(); - // TablePlugin API calling requires an action. - if (request.count("action") == 0) { - return Status(1, "Table plugins must include a request action"); - } - - if (request.at("action") == "generate") { - // "generate" runs the table implementation using a PluginRequest with - // optional serialized QueryContext and returns the QueryData results as - // the PluginRequest data. - QueryContext context; - if (request.count("context") > 0) { - setContextFromRequest(request, context); - } - setResponseFromQueryData(generate(context), response); - } else if (request.at("action") == "columns") { - // "columns" returns a PluginRequest filled with column information - // such as name and type. - const auto& column_list = columns(); - for (const auto& column : column_list) { - response.push_back({{"name", column.first}, {"type", column.second}}); - } - } else if (request.at("action") == "definition") { - response.push_back({{"definition", columnDefinition()}}); - } else if (request.at("action") == "update") { - Row row = request; - row.erase("action"); - return update(row); - } else { - return Status(1, "Unknown table plugin action: " + request.at("action")); - } - - return Status(0, "OK"); -} - -std::string TablePlugin::columnDefinition() const { - return osquery::columnDefinition(columns()); -} - -PluginResponse TablePlugin::routeInfo() const { - // Route info consists of only the serialized column information. - PluginResponse response; - for (const auto& column : columns()) { - response.push_back({{"name", column.first}, {"type", column.second}}); - } - return response; -} - -std::string columnDefinition(const TableColumns& columns) { - std::string statement = "("; - for (size_t i = 0; i < columns.size(); ++i) { - statement += columns.at(i).first + " " + columns.at(i).second; - if (i < columns.size() - 1) { - statement += ", "; - } - } - return statement += ")"; -} - -std::string columnDefinition(const PluginResponse& response) { - TableColumns columns; - for (const auto& column : response) { - columns.push_back(make_pair(column.at("name"), column.at("type"))); - } - return columnDefinition(columns); -} - -bool ConstraintList::matches(const std::string& expr) const { - // Support each SQL affinity type casting. - if (affinity == "TEXT") { - return literal_matches(expr); - } else if (affinity == "INTEGER") { - INTEGER_LITERAL lexpr = AS_LITERAL(INTEGER_LITERAL, expr); - return literal_matches(lexpr); - } else if (affinity == "BIGINT") { - BIGINT_LITERAL lexpr = AS_LITERAL(BIGINT_LITERAL, expr); - return literal_matches(lexpr); - } else if (affinity == "UNSIGNED_BIGINT") { - UNSIGNED_BIGINT_LITERAL lexpr = AS_LITERAL(UNSIGNED_BIGINT_LITERAL, expr); - return literal_matches(lexpr); - } else { - // Unsupported affinity type. - return false; - } -} - -template -bool ConstraintList::literal_matches(const T& base_expr) const { - bool aggregate = true; - for (size_t i = 0; i < constraints_.size(); ++i) { - T constraint_expr = AS_LITERAL(T, constraints_[i].expr); - if (constraints_[i].op == EQUALS) { - aggregate = aggregate && (base_expr == constraint_expr); - } else if (constraints_[i].op == GREATER_THAN) { - aggregate = aggregate && (base_expr > constraint_expr); - } else if (constraints_[i].op == LESS_THAN) { - aggregate = aggregate && (base_expr < constraint_expr); - } else if (constraints_[i].op == GREATER_THAN_OR_EQUALS) { - aggregate = aggregate && (base_expr >= constraint_expr); - } else if (constraints_[i].op == LESS_THAN_OR_EQUALS) { - aggregate = aggregate && (base_expr <= constraint_expr); - } else { - // Unsupported constraint. - return false; - } - if (!aggregate) { - // Speed up comparison. - return false; - } - } - return true; -} - -std::set ConstraintList::getAll(ConstraintOperator op) const { - std::set set; - for (size_t i = 0; i < constraints_.size(); ++i) { - if (constraints_[i].op == op) { - // TODO: this does not apply a distinct. - set.insert(constraints_[i].expr); - } - } - return set; -} - -void ConstraintList::serialize(boost::property_tree::ptree& tree) const { - boost::property_tree::ptree expressions; - for (const auto& constraint : constraints_) { - boost::property_tree::ptree child; - child.put("op", constraint.op); - child.put("expr", constraint.expr); - expressions.push_back(std::make_pair("", child)); - } - tree.add_child("list", expressions); - tree.put("affinity", affinity); -} - -void ConstraintList::unserialize(const boost::property_tree::ptree& tree) { - // Iterate through the list of operand/expressions, then set the constraint - // type affinity. - for (const auto& list : tree.get_child("list")) { - Constraint constraint(list.second.get("op")); - constraint.expr = list.second.get("expr"); - constraints_.push_back(constraint); - } - affinity = tree.get("affinity"); -} -} diff --git a/osquery/core/test_util.cpp b/osquery/core/test_util.cpp deleted file mode 100644 index 2ed7db2..0000000 --- a/osquery/core/test_util.cpp +++ /dev/null @@ -1,277 +0,0 @@ -/* - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include -#include - -#include -#include - -#include -#include - -#include "osquery/core/test_util.h" - -namespace fs = boost::filesystem; - -namespace osquery { - -/// Most tests will use binary or disk-backed content for parsing tests. -std::string kTestDataPath = "../../tools/tests/"; - -QueryData getTestDBExpectedResults() { - QueryData d; - Row row1; - row1["username"] = "mike"; - row1["age"] = "23"; - d.push_back(row1); - Row row2; - row2["username"] = "matt"; - row2["age"] = "24"; - d.push_back(row2); - return d; -} - -std::vector > getTestDBResultStream() { - std::vector > results; - - std::string q2 = - "INSERT INTO test_table (username, age) VALUES (\"joe\", 25)"; - QueryData d2; - Row row2_1; - row2_1["username"] = "mike"; - row2_1["age"] = "23"; - d2.push_back(row2_1); - Row row2_2; - row2_2["username"] = "matt"; - row2_2["age"] = "24"; - d2.push_back(row2_2); - Row row2_3; - row2_3["username"] = "joe"; - row2_3["age"] = "25"; - d2.push_back(row2_3); - results.push_back(std::make_pair(q2, d2)); - - std::string q3 = "UPDATE test_table SET age = 27 WHERE username = \"matt\""; - QueryData d3; - Row row3_1; - row3_1["username"] = "mike"; - row3_1["age"] = "23"; - d3.push_back(row3_1); - Row row3_2; - row3_2["username"] = "matt"; - row3_2["age"] = "27"; - d3.push_back(row3_2); - Row row3_3; - row3_3["username"] = "joe"; - row3_3["age"] = "25"; - d3.push_back(row3_3); - results.push_back(std::make_pair(q3, d3)); - - std::string q4 = - "DELETE FROM test_table WHERE username = \"matt\" AND age = 27"; - QueryData d4; - Row row4_1; - row4_1["username"] = "mike"; - row4_1["age"] = "23"; - d4.push_back(row4_1); - Row row4_2; - row4_2["username"] = "joe"; - row4_2["age"] = "25"; - d4.push_back(row4_2); - results.push_back(std::make_pair(q4, d4)); - - return results; -} - -ScheduledQuery getOsqueryScheduledQuery() { - ScheduledQuery sq; - sq.query = "SELECT filename FROM fs WHERE path = '/bin' ORDER BY filename"; - sq.interval = 5; - return sq; -} - -std::pair getSerializedRow() { - Row r; - r["foo"] = "bar"; - r["meaning_of_life"] = "42"; - pt::ptree arr; - arr.put("foo", "bar"); - arr.put("meaning_of_life", "42"); - return std::make_pair(arr, r); -} - -std::pair getSerializedQueryData() { - auto r = getSerializedRow(); - QueryData q = {r.second, r.second}; - pt::ptree arr; - arr.push_back(std::make_pair("", r.first)); - arr.push_back(std::make_pair("", r.first)); - return std::make_pair(arr, q); -} - -std::pair getSerializedDiffResults() { - auto qd = getSerializedQueryData(); - DiffResults diff_results; - diff_results.added = qd.second; - diff_results.removed = qd.second; - - pt::ptree root; - root.add_child("added", qd.first); - root.add_child("removed", qd.first); - - return std::make_pair(root, diff_results); -} - -std::pair getSerializedDiffResultsJSON() { - auto results = getSerializedDiffResults(); - std::ostringstream ss; - pt::write_json(ss, results.first, false); - return std::make_pair(ss.str(), results.second); -} - -std::pair getSerializedQueryDataJSON() { - auto results = getSerializedQueryData(); - std::ostringstream ss; - pt::write_json(ss, results.first, false); - return std::make_pair(ss.str(), results.second); -} - -std::pair getSerializedQueryLogItem() { - QueryLogItem i; - pt::ptree root; - auto dr = getSerializedDiffResults(); - i.results = dr.second; - i.name = "foobar"; - i.calendar_time = "Mon Aug 25 12:10:57 2014"; - i.time = 1408993857; - i.identifier = "foobaz"; - root.add_child("diffResults", dr.first); - root.put("name", "foobar"); - root.put("hostIdentifier", "foobaz"); - root.put("calendarTime", "Mon Aug 25 12:10:57 2014"); - root.put("unixTime", 1408993857); - return std::make_pair(root, i); -} - -std::pair getSerializedQueryLogItemJSON() { - auto results = getSerializedQueryLogItem(); - - std::ostringstream ss; - pt::write_json(ss, results.first, false); - - return std::make_pair(ss.str(), results.second); -} - -std::vector generateSplitStringTestData() { - SplitStringTestData s1; - s1.test_string = "a b\tc"; - s1.test_vector = {"a", "b", "c"}; - - SplitStringTestData s2; - s2.test_string = " a b c"; - s2.test_vector = {"a", "b", "c"}; - - SplitStringTestData s3; - s3.test_string = " a b c"; - s3.test_vector = {"a", "b", "c"}; - - return {s1, s2, s3}; -} - -std::string getCACertificateContent() { - std::string content; - readFile(kTestDataPath + "test_cert.pem", content); - return content; -} - -std::string getEtcHostsContent() { - std::string content; - readFile(kTestDataPath + "test_hosts.txt", content); - return content; -} - -std::string getEtcProtocolsContent() { - std::string content; - readFile(kTestDataPath + "test_protocols.txt", content); - return content; -} - -QueryData getEtcHostsExpectedResults() { - Row row1; - Row row2; - Row row3; - Row row4; - Row row5; - Row row6; - - row1["address"] = "127.0.0.1"; - row1["hostnames"] = "localhost"; - row2["address"] = "255.255.255.255"; - row2["hostnames"] = "broadcasthost"; - row3["address"] = "::1"; - row3["hostnames"] = "localhost"; - row4["address"] = "fe80::1%lo0"; - row4["hostnames"] = "localhost"; - row5["address"] = "127.0.0.1"; - row5["hostnames"] = "example.com example"; - row6["address"] = "127.0.0.1"; - row6["hostnames"] = "example.net"; - return {row1, row2, row3, row4, row5, row6}; -} - -::std::ostream& operator<<(::std::ostream& os, const Status& s) { - return os << "Status(" << s.getCode() << ", \"" << s.getMessage() << "\")"; -} - -QueryData getEtcProtocolsExpectedResults() { - Row row1; - Row row2; - Row row3; - - row1["name"] = "ip"; - row1["number"] = "0"; - row1["alias"] = "IP"; - row1["comment"] = "internet protocol, pseudo protocol number"; - row2["name"] = "icmp"; - row2["number"] = "1"; - row2["alias"] = "ICMP"; - row2["comment"] = "internet control message protocol"; - row3["name"] = "tcp"; - row3["number"] = "6"; - row3["alias"] = "TCP"; - row3["comment"] = "transmission control protocol"; - - return {row1, row2, row3}; -} - -void createMockFileStructure() { - fs::create_directories(kFakeDirectory + "/deep11/deep2/deep3/"); - fs::create_directories(kFakeDirectory + "/deep1/deep2/"); - writeTextFile(kFakeDirectory + "/root.txt", "root"); - writeTextFile(kFakeDirectory + "/door.txt", "toor"); - writeTextFile(kFakeDirectory + "/roto.txt", "roto"); - writeTextFile(kFakeDirectory + "/deep1/level1.txt", "l1"); - writeTextFile(kFakeDirectory + "/deep11/not_bash", "l1"); - writeTextFile(kFakeDirectory + "/deep1/deep2/level2.txt", "l2"); - - writeTextFile(kFakeDirectory + "/deep11/level1.txt", "l1"); - writeTextFile(kFakeDirectory + "/deep11/deep2/level2.txt", "l2"); - writeTextFile(kFakeDirectory + "/deep11/deep2/deep3/level3.txt", "l3"); - - boost::system::error_code ec; - fs::create_symlink( - kFakeDirectory + "/root.txt", kFakeDirectory + "/root2.txt", ec); -} - -void tearDownMockFileStructure() { - boost::filesystem::remove_all(kFakeDirectory); -} -} diff --git a/osquery/core/test_util.h b/osquery/core/test_util.h deleted file mode 100644 index 40a97a7..0000000 --- a/osquery/core/test_util.h +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#pragma once - -#include -#include -#include - -#include - -#include -#include -#include -#include - -namespace pt = boost::property_tree; - -namespace osquery { - -/// Any SQL-dependent tests should use kTestQuery for a pre-populated example. -const std::string kTestQuery = "SELECT * FROM test_table"; - -extern std::string kTestDataPath; - -/// Tests should limit intermediate input/output to a working directory. -/// Config data, logging results, and intermediate database/caching usage. -const std::string kTestWorkingDirectory = "/tmp/osquery-tests/"; - -/// A fake directory tree should be used for filesystem iterator testing. -const std::string kFakeDirectory = kTestWorkingDirectory + "fstree"; - -ScheduledQuery getOsqueryScheduledQuery(); - -// getTestDBExpectedResults returns the results of kTestQuery of the table that -// initially gets returned from createTestDB() -QueryData getTestDBExpectedResults(); - -// Starting with the dataset returned by createTestDB(), getTestDBResultStream -// returns a vector of std::pair's where pair.first is the query that would -// need to be performed on the dataset to make the results be pair.second -std::vector > getTestDBResultStream(); - -// getSerializedRow() return an std::pair where pair->first is a string which -// should serialize to pair->second. pair->second should deserialize -// to pair->first -std::pair getSerializedRow(); - -// getSerializedQueryData() return an std::pair where pair->first is a string -// which should serialize to pair->second. pair->second should -// deserialize to pair->first -std::pair getSerializedQueryData(); -std::pair getSerializedQueryDataJSON(); - -// getSerializedDiffResults() return an std::pair where pair->first is a string -// which should serialize to pair->second. pair->second should -// deserialize to pair->first -std::pair getSerializedDiffResults(); -std::pair getSerializedDiffResultsJSON(); - -// getSerializedQueryLogItem() return an std::pair where pair->first -// is a string which should serialize to pair->second. pair->second -// should deserialize to pair->first -std::pair getSerializedQueryLogItem(); -std::pair getSerializedQueryLogItemJSON(); - -// generate content for a PEM-encoded certificate -std::string getCACertificateContent(); - -// generate the content that would be found in an /etc/hosts file -std::string getEtcHostsContent(); - -// generate the content that would be found in an /etc/protocols file -std::string getEtcProtocolsContent(); - -// generate the expected data that getEtcHostsContent() should parse into -QueryData getEtcHostsExpectedResults(); - -// generate the expected data that getEtcProtocolsContent() should parse into -QueryData getEtcProtocolsExpectedResults(); - -// the three items that you need to test osquery::splitString -struct SplitStringTestData { - std::string test_string; - std::string delim; - std::vector test_vector; -}; - -// generate a set of test data to test osquery::splitString -std::vector generateSplitStringTestData(); - -// generate a small directory structure for testing -void createMockFileStructure(); -// remove the small directory structure used for testing -void tearDownMockFileStructure(); -} diff --git a/osquery/core/tests/conversions_tests.cpp b/osquery/core/tests/conversions_tests.cpp deleted file mode 100644 index 34be661..0000000 --- a/osquery/core/tests/conversions_tests.cpp +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include -#include - -#include - -#include "osquery/core/conversions.h" - -namespace osquery { - -class ConversionsTests : public testing::Test {}; - -class Foobar {}; - -TEST_F(ConversionsTests, test_conversion) { - boost::shared_ptr b1 = boost::make_shared(); - std::shared_ptr s1 = boost_to_std_shared_ptr(b1); - EXPECT_EQ(s1.get(), b1.get()); - - std::shared_ptr s2 = std::make_shared(); - boost::shared_ptr b2 = std_to_boost_shared_ptr(s2); - EXPECT_EQ(s2.get(), b2.get()); -} - -TEST_F(ConversionsTests, test_base64) { - std::string unencoded = "HELLO"; - auto encoded = base64Encode(unencoded); - EXPECT_NE(encoded.size(), 0); - - auto unencoded2 = base64Decode(encoded); - EXPECT_EQ(unencoded, unencoded2); -} -} diff --git a/osquery/core/tests/flags_tests.cpp b/osquery/core/tests/flags_tests.cpp deleted file mode 100644 index 1f5343f..0000000 --- a/osquery/core/tests/flags_tests.cpp +++ /dev/null @@ -1,140 +0,0 @@ -/* - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include - -#include -#include -#include - -namespace osquery { - -DECLARE_string(test_string_flag); - -class FlagsTests : public testing::Test { - public: - FlagsTests() {} - - void SetUp() {} -}; - -FLAG(string, test_string_flag, "TEST STRING", "TEST DESCRIPTION"); - -TEST_F(FlagsTests, test_set_get) { - // Test the core gflags functionality. - EXPECT_EQ(FLAGS_test_string_flag, "TEST STRING"); - - // Check that the gflags flag name was recorded in the osquery flag tracker. - auto all_flags = Flag::flags(); - EXPECT_EQ(all_flags.count("test_string_flag"), 1); - - // Update the value of the flag, and access through the osquery wrapper. - FLAGS_test_string_flag = "NEW TEST STRING"; - EXPECT_EQ(Flag::getValue("test_string_flag"), "NEW TEST STRING"); -} - -TEST_F(FlagsTests, test_defaults) { - // Make sure the flag value was not reset. - EXPECT_EQ(FLAGS_test_string_flag, "NEW TEST STRING"); - - // Now test that the default value is tracked. - EXPECT_FALSE(Flag::isDefault("test_string_flag")); - - // Check the default value accessor. - std::string default_value; - auto status = Flag::getDefaultValue("test_mistake", default_value); - EXPECT_FALSE(status.ok()); - status = Flag::getDefaultValue("test_string_flag", default_value); - EXPECT_TRUE(status.ok()); - EXPECT_EQ(default_value, "TEST STRING"); -} - -TEST_F(FlagsTests, test_details) { - // Make sure flag details are tracked correctly. - auto all_flags = Flag::flags(); - auto flag_info = all_flags["test_string_flag"]; - - EXPECT_EQ(flag_info.type, "string"); - EXPECT_EQ(flag_info.description, "TEST DESCRIPTION"); - EXPECT_EQ(flag_info.default_value, "TEST STRING"); - EXPECT_EQ(flag_info.value, "NEW TEST STRING"); - EXPECT_EQ(flag_info.detail.shell, false); - EXPECT_EQ(flag_info.detail.external, false); -} - -SHELL_FLAG(bool, shell_only, true, "TEST SHELL DESCRIPTION"); -EXTENSION_FLAG(bool, extension_only, true, "TEST EXTENSION DESCRIPTION"); - -TEST_F(FlagsTests, test_flag_detail_types) { - EXPECT_TRUE(FLAGS_shell_only); - EXPECT_TRUE(FLAGS_extension_only); - - auto all_flags = Flag::flags(); - EXPECT_TRUE(all_flags["shell_only"].detail.shell); - EXPECT_TRUE(all_flags["extension_only"].detail.external); -} - -FLAG_ALIAS(bool, shell_only_alias, shell_only); - -TEST_F(FlagsTests, test_aliases) { - EXPECT_TRUE(FLAGS_shell_only_alias); - FLAGS_shell_only = false; - EXPECT_FALSE(FLAGS_shell_only); - EXPECT_FALSE(FLAGS_shell_only_alias); -} - -FLAG(int32, test_int32, 1, "none"); -FLAG_ALIAS(google::int32, test_int32_alias, test_int32); - -FLAG(int64, test_int64, (int64_t)1 << 34, "none"); -FLAG_ALIAS(google::int64, test_int64_alias, test_int64); - -FLAG(double, test_double, 4.2, "none"); -FLAG_ALIAS(double, test_double_alias, test_double); - -FLAG(string, test_string, "test", "none"); -FLAG_ALIAS(std::string, test_string_alias, test_string); - -TEST_F(FlagsTests, test_alias_types) { - // Test int32 lexical casting both ways. - EXPECT_EQ(FLAGS_test_int32_alias, 1); - FLAGS_test_int32_alias = 2; - EXPECT_EQ(FLAGS_test_int32, 2); - FLAGS_test_int32 = 3; - EXPECT_EQ(FLAGS_test_int32_alias, 3); - EXPECT_TRUE(FLAGS_test_int32_alias > 0); - - EXPECT_EQ(FLAGS_test_int64_alias, (int64_t)1 << 34); - FLAGS_test_int64_alias = (int64_t)1 << 35; - EXPECT_EQ(FLAGS_test_int64, (int64_t)1 << 35); - FLAGS_test_int64 = (int64_t)1 << 36; - EXPECT_EQ(FLAGS_test_int64_alias, (int64_t)1 << 36); - EXPECT_TRUE(FLAGS_test_int64_alias > 0); - - EXPECT_EQ(FLAGS_test_double_alias, 4.2); - FLAGS_test_double_alias = 2.4; - EXPECT_EQ(FLAGS_test_double, 2.4); - FLAGS_test_double = 22.44; - EXPECT_EQ(FLAGS_test_double_alias, 22.44); - EXPECT_TRUE(FLAGS_test_double_alias > 0); - - // Compile-time type checking will not compare typename T to const char* - std::string value = FLAGS_test_string_alias; - EXPECT_EQ(value, "test"); - FLAGS_test_string_alias = "test2"; - EXPECT_EQ(FLAGS_test_string, "test2"); - FLAGS_test_string = "test3"; - - // Test both the copy and assignment constructor aliases. - value = FLAGS_test_string_alias; - auto value2 = (std::string)FLAGS_test_string_alias; - EXPECT_EQ(value, "test3"); -} -} diff --git a/osquery/core/tests/hash_tests.cpp b/osquery/core/tests/hash_tests.cpp deleted file mode 100644 index b1468b1..0000000 --- a/osquery/core/tests/hash_tests.cpp +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include - -#include - -#include "osquery/core/test_util.h" - -namespace osquery { - -class HashTests : public testing::Test {}; - -TEST_F(HashTests, test_algorithms) { - const unsigned char buffer[1] = {'0'}; - - auto digest = hashFromBuffer(HASH_TYPE_MD5, buffer, 1); - EXPECT_EQ(digest, "cfcd208495d565ef66e7dff9f98764da"); - - digest = hashFromBuffer(HASH_TYPE_SHA1, buffer, 1); - EXPECT_EQ(digest, "b6589fc6ab0dc82cf12099d1c2d40ab994e8410c"); - - digest = hashFromBuffer(HASH_TYPE_SHA256, buffer, 1); - EXPECT_EQ(digest, - "5feceb66ffc86f38d952786c6d696c79c2dbc239dd4e91b46729d73a27fb57e9"); -} - -TEST_F(HashTests, test_update) { - const unsigned char buffer[1] = {'0'}; - - Hash hash(HASH_TYPE_MD5); - hash.update(buffer, 1); - hash.update(buffer, 1); - auto digest = hash.digest(); - EXPECT_EQ(digest, "b4b147bc522828731f1a016bfa72c073"); -} - -TEST_F(HashTests, test_file_hashing) { - auto digest = hashFromFile(HASH_TYPE_MD5, kTestDataPath + "test_hashing.bin"); - EXPECT_EQ(digest, "88ee11f2aa7903f34b8b8785d92208b1"); -} -} diff --git a/osquery/core/tests/status_tests.cpp b/osquery/core/tests/status_tests.cpp deleted file mode 100644 index 449a981..0000000 --- a/osquery/core/tests/status_tests.cpp +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include - -#include - -namespace osquery { - -class StatusTests : public testing::Test {}; - -TEST_F(StatusTests, test_constructor) { - auto s = Status(5, "message"); - EXPECT_EQ(s.getCode(), 5); - EXPECT_EQ(s.getMessage(), "message"); -} - -TEST_F(StatusTests, test_constructor_2) { - Status s; - EXPECT_EQ(s.getCode(), 0); - EXPECT_EQ(s.getMessage(), "OK"); -} - -TEST_F(StatusTests, test_ok) { - auto s1 = Status(5, "message"); - EXPECT_FALSE(s1.ok()); - auto s2 = Status(0, "message"); - EXPECT_TRUE(s2.ok()); -} - -TEST_F(StatusTests, test_to_string) { - auto s = Status(0, "foobar"); - EXPECT_EQ(s.toString(), "foobar"); -} -} diff --git a/osquery/core/tests/tables_tests.cpp b/osquery/core/tests/tables_tests.cpp deleted file mode 100644 index 524dbc2..0000000 --- a/osquery/core/tests/tables_tests.cpp +++ /dev/null @@ -1,133 +0,0 @@ -/* - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include - -#include - -namespace osquery { - -class TablesTests : public testing::Test {}; - -TEST_F(TablesTests, test_constraint) { - auto constraint = Constraint(EQUALS); - constraint.expr = "none"; - - EXPECT_EQ(constraint.op, EQUALS); - EXPECT_EQ(constraint.expr, "none"); -} - -TEST_F(TablesTests, test_constraint_list) { - struct ConstraintList cl; - - auto constraint = Constraint(EQUALS); - constraint.expr = "some"; - - // The constraint list is a simple struct. - cl.add(constraint); - EXPECT_EQ(cl.constraints_.size(), 1); - - constraint = Constraint(EQUALS); - constraint.expr = "some_other"; - cl.add(constraint); - - constraint = Constraint(GREATER_THAN); - constraint.expr = "more_than"; - cl.add(constraint); - EXPECT_EQ(cl.constraints_.size(), 3); - - auto all_equals = cl.getAll(EQUALS); - EXPECT_EQ(all_equals.size(), 2); -} - -TEST_F(TablesTests, test_constraint_matching) { - struct ConstraintList cl; - // An empty constraint list has expectations. - EXPECT_FALSE(cl.exists()); - EXPECT_FALSE(cl.exists(GREATER_THAN)); - EXPECT_TRUE(cl.notExistsOrMatches("some")); - - auto constraint = Constraint(EQUALS); - constraint.expr = "some"; - cl.add(constraint); - - // Test existence checks based on flags. - EXPECT_TRUE(cl.exists()); - EXPECT_TRUE(cl.exists(EQUALS)); - EXPECT_TRUE(cl.exists(EQUALS | LESS_THAN)); - EXPECT_FALSE(cl.exists(LESS_THAN)); - - EXPECT_TRUE(cl.notExistsOrMatches("some")); - EXPECT_TRUE(cl.matches("some")); - EXPECT_FALSE(cl.notExistsOrMatches("not_some")); - - struct ConstraintList cl2; - cl2.affinity = "INTEGER"; - constraint = Constraint(LESS_THAN); - constraint.expr = "1000"; - cl2.add(constraint); - constraint = Constraint(GREATER_THAN); - constraint.expr = "1"; - cl2.add(constraint); - - // Test both SQL-provided string types. - EXPECT_TRUE(cl2.matches("10")); - // ...and the type literal. - EXPECT_TRUE(cl2.matches(10)); - - // Test operator lower bounds. - EXPECT_FALSE(cl2.matches(0)); - EXPECT_FALSE(cl2.matches(1)); - - // Test operator upper bounds. - EXPECT_FALSE(cl2.matches(1000)); - EXPECT_FALSE(cl2.matches(1001)); - - // Now test inclusive bounds. - struct ConstraintList cl3; - constraint = Constraint(LESS_THAN_OR_EQUALS); - constraint.expr = "1000"; - cl3.add(constraint); - constraint = Constraint(GREATER_THAN_OR_EQUALS); - constraint.expr = "1"; - cl3.add(constraint); - - EXPECT_FALSE(cl3.matches(1001)); - EXPECT_TRUE(cl3.matches(1000)); - - EXPECT_FALSE(cl3.matches(0)); - EXPECT_TRUE(cl3.matches(1)); -} - -TEST_F(TablesTests, test_constraint_map) { - ConstraintMap cm; - ConstraintList cl; - - cl.add(Constraint(EQUALS, "some")); - cm["path"] = cl; - - // If a constraint list exists for a map key, normal constraints apply. - EXPECT_TRUE(cm["path"].matches("some")); - EXPECT_FALSE(cm["path"].matches("not_some")); - - // If a constraint list does not exist, then all checks will match. - // If there is no predicate clause then all results will match. - EXPECT_TRUE(cm["not_path"].matches("some")); - EXPECT_TRUE(cm["not_path"].notExistsOrMatches("some")); - EXPECT_FALSE(cm["not_path"].exists()); - EXPECT_FALSE(cm["not_path"].existsAndMatches("some")); - - // And of the column has constraints: - EXPECT_TRUE(cm["path"].notExistsOrMatches("some")); - EXPECT_FALSE(cm["path"].notExistsOrMatches("not_some")); - EXPECT_TRUE(cm["path"].exists()); - EXPECT_TRUE(cm["path"].existsAndMatches("some")); -} -} diff --git a/osquery/core/tests/text_tests.cpp b/osquery/core/tests/text_tests.cpp deleted file mode 100644 index fbc8a2a..0000000 --- a/osquery/core/tests/text_tests.cpp +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include - -#include -#include - -#include "osquery/core/test_util.h" - -namespace osquery { - -class TextTests : public testing::Test {}; - -TEST_F(TextTests, test_split) { - for (const auto& i : generateSplitStringTestData()) { - EXPECT_EQ(split(i.test_string), i.test_vector); - } -} - -TEST_F(TextTests, test_join) { - std::vector content = { - "one", "two", "three", - }; - EXPECT_EQ(join(content, ", "), "one, two, three"); -} - -TEST_F(TextTests, test_split_occurences) { - std::string content = "T: 'S:S'"; - std::vector expected = { - "T", "'S:S'", - }; - EXPECT_EQ(split(content, ":", 1), expected); -} -} diff --git a/osquery/core/text.cpp b/osquery/core/text.cpp deleted file mode 100644 index 3ed8962..0000000 --- a/osquery/core/text.cpp +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include - -#include - -#include -#include -#include - -namespace osquery { - -std::vector split(const std::string& s, const std::string& delim) { - std::vector elems; - boost::split(elems, s, boost::is_any_of(delim)); - auto start = - std::remove_if(elems.begin(), elems.end(), [](const std::string& s) { - return s.size() == 0; - }); - elems.erase(start, elems.end()); - for (auto& each : elems) { - boost::algorithm::trim(each); - } - return elems; -} - -std::vector split(const std::string& s, - const std::string& delim, - size_t occurences) { - // Split the string normally with the required delimiter. - auto content = split(s, delim); - // While the result split exceeds the number of requested occurrences, join. - std::vector accumulator; - std::vector elems; - for (size_t i = 0; i < content.size(); i++) { - if (i < occurences) { - elems.push_back(content.at(i)); - } else { - accumulator.push_back(content.at(i)); - } - } - // Join the optional accumulator. - if (accumulator.size() > 0) { - elems.push_back(join(accumulator, delim)); - } - return elems; -} - -std::string join(const std::vector& s, const std::string& tok) { - return boost::algorithm::join(s, tok); -} -} diff --git a/osquery/core/watcher.cpp b/osquery/core/watcher.cpp deleted file mode 100644 index fba34f2..0000000 --- a/osquery/core/watcher.cpp +++ /dev/null @@ -1,445 +0,0 @@ -/* - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include - -#include -#include -#include - -#include - -#include -#include -#include -#include - -#include "osquery/core/watcher.h" -#include "osquery/dispatcher/dispatcher.h" - -extern char** environ; - -namespace fs = boost::filesystem; - -namespace osquery { - -const std::map > kWatchdogLimits = { - // Maximum MB worker can privately allocate. - {MEMORY_LIMIT, {80, 50, 30, 1000}}, - // Percent of user or system CPU worker can utilize for LATENCY_LIMIT - // seconds. - {UTILIZATION_LIMIT, {90, 80, 60, 1000}}, - // Number of seconds the worker should run, else consider the exit fatal. - {RESPAWN_LIMIT, {20, 20, 20, 5}}, - // If the worker respawns too quickly, backoff on creating additional. - {RESPAWN_DELAY, {5, 5, 5, 1}}, - // Seconds of tolerable UTILIZATION_LIMIT sustained latency. - {LATENCY_LIMIT, {12, 6, 3, 1}}, - // How often to poll for performance limit violations. - {INTERVAL, {3, 3, 3, 1}}, -}; - -const std::string kExtensionExtension = ".ext"; - -CLI_FLAG(int32, - watchdog_level, - 1, - "Performance limit level (0=loose, 1=normal, 2=restrictive, 3=debug)"); - -CLI_FLAG(bool, disable_watchdog, false, "Disable userland watchdog process"); - -/// If the worker exits the watcher will inspect the return code. -void childHandler(int signum) { - siginfo_t info; - // Make sure WNOWAIT is used to the wait information is not removed. - // Watcher::watch implements a thread to poll for this information. - waitid(P_ALL, 0, &info, WEXITED | WSTOPPED | WNOHANG | WNOWAIT); - if (info.si_code == CLD_EXITED && info.si_status == EXIT_CATASTROPHIC) { - // A child process had a catastrophic error, abort the watcher. - ::exit(EXIT_FAILURE); - } -} - -void Watcher::resetWorkerCounters(size_t respawn_time) { - // Reset the monitoring counters for the watcher. - auto& state = instance().state_; - state.sustained_latency = 0; - state.user_time = 0; - state.system_time = 0; - state.last_respawn_time = respawn_time; -} - -void Watcher::resetExtensionCounters(const std::string& extension, - size_t respawn_time) { - WatcherLocker locker; - auto& state = instance().extension_states_[extension]; - state.sustained_latency = 0; - state.user_time = 0; - state.system_time = 0; - state.last_respawn_time = respawn_time; -} - -std::string Watcher::getExtensionPath(pid_t child) { - for (const auto& extension : extensions()) { - if (extension.second == child) { - return extension.first; - } - } - return ""; -} - -void Watcher::removeExtensionPath(const std::string& extension) { - WatcherLocker locker; - instance().extensions_.erase(extension); - instance().extension_states_.erase(extension); -} - -PerformanceState& Watcher::getState(pid_t child) { - if (child == instance().worker_) { - return instance().state_; - } else { - return instance().extension_states_[getExtensionPath(child)]; - } -} - -PerformanceState& Watcher::getState(const std::string& extension) { - return instance().extension_states_[extension]; -} - -void Watcher::setExtension(const std::string& extension, pid_t child) { - WatcherLocker locker; - instance().extensions_[extension] = child; -} - -void Watcher::reset(pid_t child) { - if (child == instance().worker_) { - instance().worker_ = 0; - resetWorkerCounters(0); - return; - } - - // If it was not the worker pid then find the extension name to reset. - for (const auto& extension : extensions()) { - if (extension.second == child) { - setExtension(extension.first, 0); - resetExtensionCounters(extension.first, 0); - } - } -} - -void Watcher::addExtensionPath(const std::string& path) { - // Resolve acceptable extension binaries from autoload paths. - if (isDirectory(path).ok()) { - VLOG(1) << "Cannot autoload extension from directory: " << path; - return; - } - - // Only autoload extensions which were safe at the time of discovery. - // If the extension binary later becomes unsafe (permissions change) then - // it will fail to reload if a reload is ever needed. - fs::path extension(path); - if (safePermissions(extension.parent_path().string(), path, true)) { - if (extension.extension().string() == kExtensionExtension) { - setExtension(extension.string(), 0); - resetExtensionCounters(extension.string(), 0); - VLOG(1) << "Found autoloadable extension: " << extension.string(); - } - } -} - -bool Watcher::hasManagedExtensions() { - if (instance().extensions_.size() > 0) { - return true; - } - - // A watchdog process may hint to a worker the number of managed extensions. - // Setting this counter to 0 will prevent the worker from waiting for missing - // dependent config plugins. Otherwise, its existence, will cause a worker to - // wait for missing plugins to broadcast from managed extensions. - return (getenv("OSQUERY_EXTENSIONS") != nullptr); -} - -bool WatcherRunner::ok() { - interruptableSleep(getWorkerLimit(INTERVAL) * 1000); - // Watcher is OK to run if a worker or at least one extension exists. - return (Watcher::getWorker() >= 0 || Watcher::hasManagedExtensions()); -} - -void WatcherRunner::start() { - // Set worker performance counters to an initial state. - Watcher::resetWorkerCounters(0); - signal(SIGCHLD, childHandler); - - // Enter the watch loop. - do { - if (use_worker_ && !watch(Watcher::getWorker())) { - // The watcher failed, create a worker. - createWorker(); - } - - // Loop over every managed extension and check sanity. - std::vector failing_extensions; - for (const auto& extension : Watcher::extensions()) { - if (!watch(extension.second)) { - if (!createExtension(extension.first)) { - failing_extensions.push_back(extension.first); - } - } - } - // If any extension creations failed, stop managing them. - for (const auto& failed_extension : failing_extensions) { - Watcher::removeExtensionPath(failed_extension); - } - } while (ok()); -} - -bool WatcherRunner::watch(pid_t child) { - int status; - pid_t result = waitpid(child, &status, WNOHANG); - if (child == 0 || result == child) { - // Worker does not exist or never existed. - return false; - } else if (result == 0) { - // If the inspect finds problems it will stop/restart the worker. - if (!isChildSane(child)) { - stopChild(child); - return false; - } - } - return true; -} - -void WatcherRunner::stopChild(pid_t child) { - kill(child, SIGKILL); - - // Clean up the defunct (zombie) process. - waitpid(-1, 0, WNOHANG); -} - -bool WatcherRunner::isChildSane(pid_t child) { - auto rows = SQL::selectAllFrom("processes", "pid", EQUALS, INTEGER(child)); - if (rows.size() == 0) { - // Could not find worker process? - return false; - } - - // Get the performance state for the worker or extension. - size_t sustained_latency = 0; - // Compare CPU utilization since last check. - BIGINT_LITERAL footprint = 0, user_time = 0, system_time = 0, parent = 0; - // IV is the check interval in seconds, and utilization is set per-second. - auto iv = std::max(getWorkerLimit(INTERVAL), (size_t)1); - - { - WatcherLocker locker; - auto& state = Watcher::getState(child); - try { - parent = AS_LITERAL(BIGINT_LITERAL, rows[0].at("parent")); - user_time = AS_LITERAL(BIGINT_LITERAL, rows[0].at("user_time")) / iv; - system_time = AS_LITERAL(BIGINT_LITERAL, rows[0].at("system_time")) / iv; - footprint = AS_LITERAL(BIGINT_LITERAL, rows[0].at("resident_size")); - } catch (const std::exception& e) { - state.sustained_latency = 0; - } - - // Check the difference of CPU time used since last check. - if (user_time - state.user_time > getWorkerLimit(UTILIZATION_LIMIT) || - system_time - state.system_time > getWorkerLimit(UTILIZATION_LIMIT)) { - state.sustained_latency++; - } else { - state.sustained_latency = 0; - } - // Update the current CPU time. - state.user_time = user_time; - state.system_time = system_time; - - // Check if the sustained difference exceeded the acceptable latency limit. - sustained_latency = state.sustained_latency; - - // Set the memory footprint as the amount of resident bytes allocated - // since the process image was created (estimate). - // A more-meaningful check would limit this to writable regions. - if (state.initial_footprint == 0) { - state.initial_footprint = footprint; - } - - // Set the measured/limit-applied footprint to the post-launch allocations. - if (footprint < state.initial_footprint) { - footprint = 0; - } else { - footprint = footprint - state.initial_footprint; - } - } - - // Only make a decision about the child sanity if it is still the watcher's - // child. It's possible for the child to die, and its pid reused. - if (parent != getpid()) { - // The child's parent is not the watcher. - Watcher::reset(child); - // Do not stop or call the child insane, since it is not our child. - return true; - } - - if (sustained_latency > 0 && - sustained_latency * iv >= getWorkerLimit(LATENCY_LIMIT)) { - LOG(WARNING) << "osqueryd worker (" << child - << ") system performance limits exceeded"; - return false; - } - // Check if the private memory exceeds a memory limit. - if (footprint > 0 && footprint > getWorkerLimit(MEMORY_LIMIT) * 1024 * 1024) { - LOG(WARNING) << "osqueryd worker (" << child - << ") memory limits exceeded: " << footprint; - return false; - } - - // The worker is sane, no action needed. - // Attempt to flush status logs to the well-behaved worker. - relayStatusLogs(); - return true; -} - -void WatcherRunner::createWorker() { - { - WatcherLocker locker; - if (Watcher::getState(Watcher::getWorker()).last_respawn_time > - getUnixTime() - getWorkerLimit(RESPAWN_LIMIT)) { - LOG(WARNING) << "osqueryd worker respawning too quickly: " - << Watcher::workerRestartCount() << " times"; - Watcher::workerRestarted(); - interruptableSleep(getWorkerLimit(RESPAWN_DELAY) * 1000); - // Exponential back off for quickly-respawning clients. - interruptableSleep(pow(2, Watcher::workerRestartCount()) * 1000); - } - } - - // Get the path of the current process. - auto qd = SQL::selectAllFrom("processes", "pid", EQUALS, INTEGER(getpid())); - if (qd.size() != 1 || qd[0].count("path") == 0 || qd[0]["path"].size() == 0) { - LOG(ERROR) << "osquery watcher cannot determine process path for worker"; - ::exit(EXIT_FAILURE); - } - - // Set an environment signaling to potential plugin-dependent workers to wait - // for extensions to broadcast. - if (Watcher::hasManagedExtensions()) { - setenv("OSQUERY_EXTENSIONS", "true", 1); - } - - // Get the complete path of the osquery process binary. - auto exec_path = fs::system_complete(fs::path(qd[0]["path"])); - if (!safePermissions( - exec_path.parent_path().string(), exec_path.string(), true)) { - // osqueryd binary has become unsafe. - LOG(ERROR) << "osqueryd has unsafe permissions: " << exec_path.string(); - ::exit(EXIT_FAILURE); - } - - auto worker_pid = fork(); - if (worker_pid < 0) { - // Unrecoverable error, cannot create a worker process. - LOG(ERROR) << "osqueryd could not create a worker process"; - ::exit(EXIT_FAILURE); - } else if (worker_pid == 0) { - // This is the new worker process, no watching needed. - setenv("OSQUERY_WORKER", std::to_string(getpid()).c_str(), 1); - execve(exec_path.string().c_str(), argv_, environ); - // Code should never reach this point. - LOG(ERROR) << "osqueryd could not start worker process"; - ::exit(EXIT_CATASTROPHIC); - } - - Watcher::setWorker(worker_pid); - Watcher::resetWorkerCounters(getUnixTime()); - VLOG(1) << "osqueryd watcher (" << getpid() << ") executing worker (" - << worker_pid << ")"; -} - -bool WatcherRunner::createExtension(const std::string& extension) { - { - WatcherLocker locker; - if (Watcher::getState(extension).last_respawn_time > - getUnixTime() - getWorkerLimit(RESPAWN_LIMIT)) { - LOG(WARNING) << "Extension respawning too quickly: " << extension; - // Unlike a worker, if an extension respawns to quickly we give up. - return false; - } - } - - // Check the path to the previously-discovered extension binary. - auto exec_path = fs::system_complete(fs::path(extension)); - if (!safePermissions( - exec_path.parent_path().string(), exec_path.string(), true)) { - // Extension binary has become unsafe. - LOG(WARNING) << "Extension binary has unsafe permissions: " << extension; - return false; - } - - auto ext_pid = fork(); - if (ext_pid < 0) { - // Unrecoverable error, cannot create an extension process. - LOG(ERROR) << "Cannot create extension process: " << extension; - ::exit(EXIT_FAILURE); - } else if (ext_pid == 0) { - // Pass the current extension socket and a set timeout to the extension. - setenv("OSQUERY_EXTENSION", std::to_string(getpid()).c_str(), 1); - // Execute extension with very specific arguments. - execle(exec_path.string().c_str(), - ("osquery extension: " + extension).c_str(), - "--socket", - Flag::getValue("extensions_socket").c_str(), - "--timeout", - Flag::getValue("extensions_timeout").c_str(), - "--interval", - Flag::getValue("extensions_interval").c_str(), - (Flag::getValue("verbose") == "true") ? "--verbose" : (char*)nullptr, - (char*)nullptr, - environ); - // Code should never reach this point. - VLOG(1) << "Could not start extension process: " << extension; - ::exit(EXIT_FAILURE); - } - - Watcher::setExtension(extension, ext_pid); - Watcher::resetExtensionCounters(extension, getUnixTime()); - VLOG(1) << "Created and monitoring extension child (" << ext_pid << "): " - << extension; - return true; -} - -void WatcherWatcherRunner::start() { - while (true) { - if (getppid() != watcher_) { - // Watcher died, the worker must follow. - VLOG(1) << "osqueryd worker (" << getpid() - << ") detected killed watcher (" << watcher_ << ")"; - Dispatcher::stopServices(); - // The watcher watcher is a thread. Do not join services after removing. - ::exit(EXIT_SUCCESS); - } - interruptableSleep(getWorkerLimit(INTERVAL) * 1000); - } -} - -size_t getWorkerLimit(WatchdogLimitType name, int level) { - if (kWatchdogLimits.count(name) == 0) { - return 0; - } - - // If no level was provided then use the default (config/switch). - if (level == -1) { - level = FLAGS_watchdog_level; - } - if (level > 3) { - return kWatchdogLimits.at(name).back(); - } - return kWatchdogLimits.at(name).at(level); -} -} diff --git a/osquery/core/watcher.h b/osquery/core/watcher.h deleted file mode 100644 index 8dd06b5..0000000 --- a/osquery/core/watcher.h +++ /dev/null @@ -1,274 +0,0 @@ -/* - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#pragma once - -#include - -#include - -#include -#include - -#include - -#include "osquery/dispatcher/dispatcher.h" - -/// Define a special debug/testing watchdog level. -#define WATCHDOG_LEVEL_DEBUG 3 -/// Define the default watchdog level, level below are considered permissive. -#define WATCHDOG_LEVEL_DEFAULT 1 - -namespace osquery { - -DECLARE_bool(disable_watchdog); -DECLARE_int32(watchdog_level); - -class WatcherRunner; - -/** - * @brief Categories of process performance limitations. - * - * Performance limits are applied by a watcher thread on autoloaded extensions - * and a optional daemon worker process. The performance types are identified - * here, and organized into levels. Such that a caller may enforce rigor or - * relax the performance expectations of a osquery daemon. - */ -enum WatchdogLimitType { - MEMORY_LIMIT, - UTILIZATION_LIMIT, - RESPAWN_LIMIT, - RESPAWN_DELAY, - LATENCY_LIMIT, - INTERVAL, -}; - -/** - * @brief A performance state structure for an autoloaded extension or worker. - * - * A watcher thread will continue to check the performance state, and keep a - * last-checked snapshot for each autoloaded extension and worker process. - */ -struct PerformanceState { - /// A counter of how many intervals the process exceeded performance limits. - size_t sustained_latency; - /// The last checked user CPU time. - size_t user_time; - /// The last checked system CPU time. - size_t system_time; - /// A timestamp when the process/worker was last created. - size_t last_respawn_time; - - /// The initial (or as close as possible) process image footprint. - size_t initial_footprint; - - PerformanceState() { - sustained_latency = 0; - user_time = 0; - system_time = 0; - last_respawn_time = 0; - initial_footprint = 0; - } -}; - -/** - * @brief Thread-safe watched child process state manager. - * - * The Watcher instance is separated from the WatcherRunner thread to allow - * signals and osquery-introspection to monitor the autoloaded extensions - * and optional worker stats. A child-process change signal may indicate an - * autoloaded extension ended. Tables may also report on the historic worker - * or extension utilizations. - * - * Though not critical, it is preferred to remove the extension's broadcasted - * routes quickly. Locking access to the extensions list between signals and - * the WatcherRunner thread allows osquery to tearDown registry changes before - * attempting to respawn an extension process. - */ -class Watcher : private boost::noncopyable { - public: - /// Instance accessor - static Watcher& instance() { - static Watcher instance; - return instance; - } - - /// Reset counters after a worker exits. - static void resetWorkerCounters(size_t respawn_time); - - /// Reset counters for an extension path. - static void resetExtensionCounters(const std::string& extension, - size_t respawn_time); - - /// Lock access to extensions. - static void lock() { instance().lock_.lock(); } - - /// Unlock access to extensions. - static void unlock() { instance().lock_.unlock(); } - - /// Accessor for autoloadable extension paths. - static const std::map& extensions() { - return instance().extensions_; - } - - /// Lookup extension path from pid. - static std::string getExtensionPath(pid_t child); - - /// Remove an autoloadable extension path. - static void removeExtensionPath(const std::string& extension); - - /// Add extensions autoloadable paths. - static void addExtensionPath(const std::string& path); - - /// Get state information for a worker or extension child. - static PerformanceState& getState(pid_t child); - static PerformanceState& getState(const std::string& extension); - - /// Accessor for the worker process. - static pid_t getWorker() { return instance().worker_; } - - /// Setter for worker process. - static void setWorker(pid_t child) { instance().worker_ = child; } - - /// Setter for an extension process. - static void setExtension(const std::string& extension, pid_t child); - - /// Reset pid and performance counters for a worker or extension process. - static void reset(pid_t child); - - /// Count the number of worker restarts. - static size_t workerRestartCount() { return instance().worker_restarts_; } - - /** - * @brief Return the state of autoloadable extensions. - * - * Some initialization decisions are made based on waiting for plugins to - * broadcast from potentially-loaded extensions. If no extensions are loaded - * and an active (selected at command line) plugin is missing, fail quickly. - */ - static bool hasManagedExtensions(); - - private: - /// Do not request the lock until extensions are used. - Watcher() - : worker_(-1), worker_restarts_(0), lock_(mutex_, boost::defer_lock) {} - Watcher(Watcher const&); - void operator=(Watcher const&); - virtual ~Watcher() {} - - private: - /// Inform the watcher that the worker restarted without cause. - static void workerRestarted() { instance().worker_restarts_++; } - - private: - /// Performance state for the worker process. - PerformanceState state_; - /// Performance states for each autoloadable extension binary. - std::map extension_states_; - - private: - /// Keep the single worker process/thread ID for inspection. - pid_t worker_; - /// Number of worker restarts NOT induced by a watchdog process. - size_t worker_restarts_; - /// Keep a list of resolved extension paths and their managed pids. - std::map extensions_; - /// Paths to autoload extensions. - std::vector extensions_paths_; - - private: - /// Mutex and lock around extensions access. - boost::mutex mutex_; - /// Mutex and lock around extensions access. - boost::unique_lock lock_; - - private: - friend class WatcherRunner; -}; - -/** - * @brief A scoped locker for iterating over watcher extensions. - * - * A lock must be used if any part of osquery wants to enumerate the autoloaded - * extensions or autoloadable extension paths a Watcher may be monitoring. - * A signal or WatcherRunner thread may stop or start extensions. - */ -class WatcherLocker { - public: - /// Construct and gain watcher lock. - WatcherLocker() { Watcher::lock(); } - /// Destruct and release watcher lock. - ~WatcherLocker() { Watcher::unlock(); } -}; - -/** - * @brief The watchdog thread responsible for spawning/monitoring children. - * - * The WatcherRunner thread will spawn any autoloaded extensions or optional - * osquery daemon worker processes. It will then poll for their performance - * state and kill/respawn osquery child processes if they violate limits. - */ -class WatcherRunner : public InternalRunnable { - public: - /** - * @brief Construct a watcher thread. - * - * @param argc The osquery process argc. - * @param argv The osquery process argv. - * @param use_worker True if the process should spawn and monitor a worker. - */ - explicit WatcherRunner(int argc, char** argv, bool use_worker) - : argc_(argc), argv_(argv), use_worker_(use_worker) { - (void)argc_; - } - - private: - /// Dispatcher (this service thread's) entry point. - void start(); - /// Boilerplate function to sleep for some configured latency - bool ok(); - /// Begin the worker-watcher process. - bool watch(pid_t child); - /// Inspect into the memory, CPU, and other worker/extension process states. - bool isChildSane(pid_t child); - - private: - /// Fork and execute a worker process. - void createWorker(); - /// Fork an extension process. - bool createExtension(const std::string& extension); - /// If a worker/extension has otherwise gone insane, stop it. - void stopChild(pid_t child); - - private: - /// Keep the invocation daemon's argc to iterate through argv. - int argc_; - /// When a worker child is spawned the argv will be scrubbed. - char** argv_; - /// Spawn/monitor a worker process. - bool use_worker_; -}; - -/// The WatcherWatcher is spawned within the worker and watches the watcher. -class WatcherWatcherRunner : public InternalRunnable { - public: - explicit WatcherWatcherRunner(pid_t watcher) : watcher_(watcher) {} - - /// Runnable thread's entry point. - void start(); - - private: - /// Parent, or watchdog, process ID. - pid_t watcher_; -}; - -/// Get a performance limit by name and optional level. -size_t getWorkerLimit(WatchdogLimitType limit, int level = -1); -} diff --git a/osquery/database/CMakeLists.txt b/osquery/database/CMakeLists.txt deleted file mode 100644 index 4d47844..0000000 --- a/osquery/database/CMakeLists.txt +++ /dev/null @@ -1,21 +0,0 @@ -# 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_database database.cpp) - -ADD_OSQUERY_LIBRARY(osquery_database_internal db_handle.cpp - query.cpp) - -FILE(GLOB OSQUERY_DATABASE_TESTS "tests/*.cpp") -ADD_OSQUERY_TEST(${OSQUERY_DATABASE_TESTS}) diff --git a/osquery/database/database.cpp b/osquery/database/database.cpp deleted file mode 100644 index 96e9ec6..0000000 --- a/osquery/database/database.cpp +++ /dev/null @@ -1,482 +0,0 @@ -/* - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include -#include -#include -#include -#include -#include - -#include -#include - -#include -#include - -namespace pt = boost::property_tree; - -namespace osquery { - -typedef unsigned char byte; - -///////////////////////////////////////////////////////////////////////////// -// Row - the representation of a row in a set of database results. Row is a -// simple map where individual column names are keys, which map to the Row's -// respective value -///////////////////////////////////////////////////////////////////////////// - -std::string escapeNonPrintableBytes(const std::string& data) { - std::string escaped; - // clang-format off - char const hex_chars[16] = { - '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', - 'A', 'B', 'C', 'D', 'E', 'F', - }; - // clang-format on - for (int i = 0; i < data.length(); i++) { - if (((byte)data[i]) < 0x20 || ((byte)data[i]) >= 0x80) { - escaped += "\\x"; - escaped += hex_chars[(((byte)data[i])) >> 4]; - escaped += hex_chars[((byte)data[i] & 0x0F) >> 0]; - } else { - escaped += data[i]; - } - } - return escaped; -} - -void escapeQueryData(const QueryData& oldData, QueryData& newData) { - for (const auto& r : oldData) { - Row newRow; - for (auto& i : r) { - newRow[i.first] = escapeNonPrintableBytes(i.second); - } - newData.push_back(newRow); - } -} - -Status serializeRow(const Row& r, pt::ptree& tree) { - try { - for (auto& i : r) { - tree.put(i.first, i.second); - } - } catch (const std::exception& e) { - return Status(1, e.what()); - } - return Status(0, "OK"); -} - -Status serializeRowJSON(const Row& r, std::string& json) { - pt::ptree tree; - auto status = serializeRow(r, tree); - if (!status.ok()) { - return status; - } - - std::ostringstream output; - try { - pt::write_json(output, tree, false); - } catch (const pt::json_parser::json_parser_error& e) { - // The content could not be represented as JSON. - return Status(1, e.what()); - } - json = output.str(); - return Status(0, "OK"); -} - -Status deserializeRow(const pt::ptree& tree, Row& r) { - for (const auto& i : tree) { - if (i.first.length() > 0) { - r[i.first] = i.second.data(); - } - } - return Status(0, "OK"); -} - -Status deserializeRowJSON(const std::string& json, Row& r) { - pt::ptree tree; - try { - std::stringstream input; - input << json; - pt::read_json(input, tree); - } catch (const pt::json_parser::json_parser_error& e) { - return Status(1, e.what()); - } - return deserializeRow(tree, r); -} - -///////////////////////////////////////////////////////////////////////////// -// QueryData - the representation of a database query result set. It's a -// vector of rows -///////////////////////////////////////////////////////////////////////////// - -Status serializeQueryData(const QueryData& q, pt::ptree& tree) { - for (const auto& r : q) { - pt::ptree serialized; - auto s = serializeRow(r, serialized); - if (!s.ok()) { - return s; - } - tree.push_back(std::make_pair("", serialized)); - } - return Status(0, "OK"); -} - -Status serializeQueryDataJSON(const QueryData& q, std::string& json) { - pt::ptree tree; - auto status = serializeQueryData(q, tree); - if (!status.ok()) { - return status; - } - - std::ostringstream output; - try { - pt::write_json(output, tree, false); - } catch (const pt::json_parser::json_parser_error& e) { - // The content could not be represented as JSON. - return Status(1, e.what()); - } - json = output.str(); - return Status(0, "OK"); -} - -Status deserializeQueryData(const pt::ptree& tree, QueryData& qd) { - for (const auto& i : tree) { - Row r; - auto status = deserializeRow(i.second, r); - if (!status.ok()) { - return status; - } - qd.push_back(r); - } - return Status(0, "OK"); -} - -Status deserializeQueryDataJSON(const std::string& json, QueryData& qd) { - pt::ptree tree; - try { - std::stringstream input; - input << json; - pt::read_json(input, tree); - } catch (const pt::json_parser::json_parser_error& e) { - return Status(1, e.what()); - } - return deserializeQueryData(tree, qd); -} - -///////////////////////////////////////////////////////////////////////////// -// DiffResults - the representation of two diffed QueryData result sets. -// Given and old and new QueryData, DiffResults indicates the "added" subset -// of rows and the "removed" subset of Rows -///////////////////////////////////////////////////////////////////////////// - -Status serializeDiffResults(const DiffResults& d, pt::ptree& tree) { - pt::ptree added; - auto status = serializeQueryData(d.added, added); - if (!status.ok()) { - return status; - } - tree.add_child("added", added); - - pt::ptree removed; - status = serializeQueryData(d.removed, removed); - if (!status.ok()) { - return status; - } - tree.add_child("removed", removed); - return Status(0, "OK"); -} - -Status deserializeDiffResults(const pt::ptree& tree, DiffResults& dr) { - if (tree.count("added") > 0) { - auto status = deserializeQueryData(tree.get_child("added"), dr.added); - if (!status.ok()) { - return status; - } - } - - if (tree.count("removed") > 0) { - auto status = deserializeQueryData(tree.get_child("removed"), dr.removed); - if (!status.ok()) { - return status; - } - } - return Status(0, "OK"); -} - -Status serializeDiffResultsJSON(const DiffResults& d, std::string& json) { - pt::ptree tree; - auto status = serializeDiffResults(d, tree); - if (!status.ok()) { - return status; - } - - std::ostringstream output; - try { - pt::write_json(output, tree, false); - } catch (const pt::json_parser::json_parser_error& e) { - // The content could not be represented as JSON. - return Status(1, e.what()); - } - json = output.str(); - return Status(0, "OK"); -} - -DiffResults diff(const QueryData& old, const QueryData& current) { - DiffResults r; - QueryData overlap; - - for (const auto& i : current) { - auto item = std::find(old.begin(), old.end(), i); - if (item != old.end()) { - overlap.push_back(i); - } else { - r.added.push_back(i); - } - } - - std::multiset overlap_set(overlap.begin(), overlap.end()); - std::multiset old_set(old.begin(), old.end()); - std::set_difference(old_set.begin(), - old_set.end(), - overlap_set.begin(), - overlap_set.end(), - std::back_inserter(r.removed)); - return r; -} - -///////////////////////////////////////////////////////////////////////////// -// QueryLogItem - the representation of a log result occuring when a -// scheduled query yields operating system state change. -///////////////////////////////////////////////////////////////////////////// - -Status serializeQueryLogItem(const QueryLogItem& i, pt::ptree& tree) { - pt::ptree results_tree; - if (i.results.added.size() > 0 || i.results.removed.size() > 0) { - auto status = serializeDiffResults(i.results, results_tree); - if (!status.ok()) { - return status; - } - tree.add_child("diffResults", results_tree); - } else { - auto status = serializeQueryData(i.snapshot_results, results_tree); - if (!status.ok()) { - return status; - } - tree.add_child("snapshot", results_tree); - } - - tree.put("name", i.name); - tree.put("hostIdentifier", i.identifier); - tree.put("calendarTime", i.calendar_time); - tree.put("unixTime", i.time); - return Status(0, "OK"); -} - -Status serializeQueryLogItemJSON(const QueryLogItem& i, std::string& json) { - pt::ptree tree; - auto status = serializeQueryLogItem(i, tree); - if (!status.ok()) { - return status; - } - - std::ostringstream output; - try { - pt::write_json(output, tree, false); - } catch (const pt::json_parser::json_parser_error& e) { - // The content could not be represented as JSON. - return Status(1, e.what()); - } - json = output.str(); - return Status(0, "OK"); -} - -Status deserializeQueryLogItem(const pt::ptree& tree, QueryLogItem& item) { - if (tree.count("diffResults") > 0) { - auto status = - deserializeDiffResults(tree.get_child("diffResults"), item.results); - if (!status.ok()) { - return status; - } - } else if (tree.count("snapshot") > 0) { - auto status = - deserializeQueryData(tree.get_child("snapshot"), item.snapshot_results); - if (!status.ok()) { - return status; - } - } - - item.name = tree.get("name", ""); - item.identifier = tree.get("hostIdentifier", ""); - item.calendar_time = tree.get("calendarTime", ""); - item.time = tree.get("unixTime", 0); - return Status(0, "OK"); -} - -Status deserializeQueryLogItemJSON(const std::string& json, - QueryLogItem& item) { - pt::ptree tree; - try { - std::stringstream input; - input << json; - pt::read_json(input, tree); - } catch (const pt::json_parser::json_parser_error& e) { - return Status(1, e.what()); - } - return deserializeQueryLogItem(tree, item); -} - -Status serializeEvent(const QueryLogItem& item, - const pt::ptree& event, - pt::ptree& tree) { - tree.put("name", item.name); - tree.put("hostIdentifier", item.identifier); - tree.put("calendarTime", item.calendar_time); - tree.put("unixTime", item.time); - - pt::ptree columns; - for (auto& i : event) { - // Yield results as a "columns." map to avoid namespace collisions. - columns.put(i.first, i.second.get_value()); - } - - tree.add_child("columns", columns); - return Status(0, "OK"); -} - -Status serializeQueryLogItemAsEvents(const QueryLogItem& i, pt::ptree& tree) { - pt::ptree diff_results; - auto status = serializeDiffResults(i.results, diff_results); - if (!status.ok()) { - return status; - } - - for (auto& action : diff_results) { - for (auto& row : action.second) { - pt::ptree event; - serializeEvent(i, row.second, event); - event.put("action", action.first); - tree.push_back(std::make_pair("", event)); - } - } - return Status(0, "OK"); -} - -Status serializeQueryLogItemAsEventsJSON(const QueryLogItem& i, - std::string& json) { - pt::ptree tree; - auto status = serializeQueryLogItemAsEvents(i, tree); - if (!status.ok()) { - return status; - } - - std::ostringstream output; - for (auto& event : tree) { - try { - pt::write_json(output, event.second, false); - } catch (const pt::json_parser::json_parser_error& e) { - return Status(1, e.what()); - } - } - json = output.str(); - return Status(0, "OK"); -} - -bool addUniqueRowToQueryData(QueryData& q, const Row& r) { - if (std::find(q.begin(), q.end(), r) != q.end()) { - return false; - } - q.push_back(r); - return true; -} - -Status DatabasePlugin::call(const PluginRequest& request, - PluginResponse& response) { - if (request.count("action") == 0) { - return Status(1, "Database plugin must include a request action"); - } - - // Get a domain/key, which are used for most database plugin actions. - auto domain = (request.count("domain") > 0) ? request.at("domain") : ""; - auto key = (request.count("key") > 0) ? request.at("key") : ""; - - // Switch over the possible database plugin actions. - if (request.at("action") == "get") { - std::string value; - auto status = this->get(domain, key, value); - response.push_back({{"v", value}}); - return status; - } else if (request.at("action") == "put") { - if (request.count("value") == 0) { - return Status(1, "Database plugin put action requires a value"); - } - return this->put(domain, key, request.at("value")); - } else if (request.at("action") == "remove") { - return this->remove(domain, key); - } else if (request.at("action") == "scan") { - std::vector keys; - auto status = this->scan(domain, keys); - for (const auto& key : keys) { - response.push_back({{"k", key}}); - } - return status; - } - - return Status(1, "Unknown database plugin action"); -} - -Status getDatabaseValue(const std::string& domain, - const std::string& key, - std::string& value) { - PluginRequest request = {{"action", "get"}, {"domain", domain}, {"key", key}}; - PluginResponse response; - auto status = Registry::call("database", "rocks", request, response); - if (!status.ok()) { - VLOG(1) << "Cannot get database " << domain << "/" << key << ": " - << status.getMessage(); - return status; - } - - // Set value from the internally-known "v" key. - if (response.size() > 0 && response[0].count("v") > 0) { - value = response[0].at("v"); - } - return status; -} - -Status setDatabaseValue(const std::string& domain, - const std::string& key, - const std::string& value) { - PluginRequest request = { - {"action", "put"}, {"domain", domain}, {"key", key}, {"value", value}}; - return Registry::call("database", "rocks", request); -} - -Status deleteDatabaseValue(const std::string& domain, const std::string& key) { - PluginRequest request = { - {"action", "remove"}, {"domain", domain}, {"key", key}}; - return Registry::call("database", "rocks", request); -} - -Status scanDatabaseKeys(const std::string& domain, - std::vector& keys) { - PluginRequest request = {{"action", "scan"}, {"domain", domain}}; - PluginResponse response; - auto status = Registry::call("database", "rocks", request, response); - - for (const auto& item : response) { - if (item.count("k") > 0) { - keys.push_back(item.at("k")); - } - } - return status; -} -} diff --git a/osquery/database/db_handle.cpp b/osquery/database/db_handle.cpp deleted file mode 100644 index 502af8e..0000000 --- a/osquery/database/db_handle.cpp +++ /dev/null @@ -1,264 +0,0 @@ -/* - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include -#include -#include - -#include - -#include -#include -#include - -#include -#include -#include -#include - -#include "osquery/database/db_handle.h" - -namespace osquery { - -class RocksDatabasePlugin : public DatabasePlugin { - public: - /// Data retrieval method. - Status get(const std::string& domain, - const std::string& key, - std::string& value) const; - - /// Data storage method. - Status put(const std::string& domain, - const std::string& key, - const std::string& value); - - /// Data removal method. - Status remove(const std::string& domain, const std::string& k); - - /// Key/index lookup method. - Status scan(const std::string& domain, - std::vector& results) const; -}; - -/// Backing-storage provider for osquery internal/core. -REGISTER_INTERNAL(RocksDatabasePlugin, "database", "rocks"); - -///////////////////////////////////////////////////////////////////////////// -// Constants -///////////////////////////////////////////////////////////////////////////// - -const std::string kPersistentSettings = "configurations"; -const std::string kQueries = "queries"; -const std::string kEvents = "events"; -const std::string kLogs = "logs"; - -/** - * @brief A const vector of column families in RocksDB - * - * RocksDB has a concept of "column families" which are kind of like tables - * in other databases. kDomainds is populated with a list of all column - * families. If a string exists in kDomains, it's a column family in the - * database. - */ -const std::vector kDomains = { - kPersistentSettings, kQueries, kEvents, kLogs -}; - -CLI_FLAG(string, - database_path, - "/var/osquery/osquery.db", - "If using a disk-based backing store, specify a path"); -FLAG_ALIAS(std::string, db_path, database_path); - -CLI_FLAG(bool, - database_in_memory, - false, - "Keep osquery backing-store in memory"); -FLAG_ALIAS(bool, use_in_memory_database, database_in_memory); - -///////////////////////////////////////////////////////////////////////////// -// constructors and destructors -///////////////////////////////////////////////////////////////////////////// - -DBHandle::DBHandle(const std::string& path, bool in_memory) { - options_.create_if_missing = true; - options_.create_missing_column_families = true; - options_.info_log_level = rocksdb::WARN_LEVEL; - options_.log_file_time_to_roll = 0; - options_.keep_log_file_num = 10; - options_.max_log_file_size = 1024 * 1024 * 1; - options_.compaction_style = rocksdb::kCompactionStyleLevel; - options_.write_buffer_size = 1 * 1024 * 1024; - options_.max_write_buffer_number = 2; - options_.max_background_compactions = 1; - - if (in_memory) { - // Remove when MemEnv is included in librocksdb - // options_.env = rocksdb::NewMemEnv(rocksdb::Env::Default()); - throw std::runtime_error("Requires MemEnv"); - } - - if (pathExists(path).ok() && !isWritable(path).ok()) { - throw std::runtime_error("Cannot write to RocksDB path: " + path); - } - - column_families_.push_back(rocksdb::ColumnFamilyDescriptor( - rocksdb::kDefaultColumnFamilyName, rocksdb::ColumnFamilyOptions())); - - for (const auto& cf_name : kDomains) { - column_families_.push_back(rocksdb::ColumnFamilyDescriptor( - cf_name, rocksdb::ColumnFamilyOptions())); - } - - VLOG(1) << "Opening RocksDB handle: " << path; - auto s = rocksdb::DB::Open(options_, path, column_families_, &handles_, &db_); - if (!s.ok()) { - throw std::runtime_error(s.ToString()); - } - - // RocksDB may not create/append a directory with acceptable permissions. - if (chmod(path.c_str(), S_IRWXU) != 0) { - throw std::runtime_error("Cannot set permissions on RocksDB path: " + path); - } -} - -DBHandle::~DBHandle() { - for (auto handle : handles_) { - delete handle; - } - delete db_; -} - -///////////////////////////////////////////////////////////////////////////// -// getInstance methods -///////////////////////////////////////////////////////////////////////////// - -DBHandleRef DBHandle::getInstance() { - return getInstance(FLAGS_database_path, FLAGS_database_in_memory); -} - -bool DBHandle::checkDB() { - try { - auto handle = DBHandle(FLAGS_database_path, FLAGS_database_in_memory); - } catch (const std::exception& e) { - return false; - } - return true; -} - -DBHandleRef DBHandle::getInstanceInMemory() { - return getInstance("", true); -} - -DBHandleRef DBHandle::getInstanceAtPath(const std::string& path) { - return getInstance(path, false); -} - -DBHandleRef DBHandle::getInstance(const std::string& path, bool in_memory) { - static DBHandleRef db_handle = DBHandleRef(new DBHandle(path, in_memory)); - return db_handle; -} - -///////////////////////////////////////////////////////////////////////////// -// getters and setters -///////////////////////////////////////////////////////////////////////////// - -rocksdb::DB* DBHandle::getDB() { return db_; } - -rocksdb::ColumnFamilyHandle* DBHandle::getHandleForColumnFamily( - const std::string& cf) { - try { - for (int i = 0; i < kDomains.size(); i++) { - if (kDomains[i] == cf) { - return handles_[i]; - } - } - } catch (const std::exception& e) { - // pass through and return nullptr - } - return nullptr; -} - -///////////////////////////////////////////////////////////////////////////// -// Data manipulation methods -///////////////////////////////////////////////////////////////////////////// - -Status DBHandle::Get(const std::string& domain, - const std::string& key, - std::string& value) { - auto cfh = getHandleForColumnFamily(domain); - if (cfh == nullptr) { - return Status(1, "Could not get column family for " + domain); - } - auto s = getDB()->Get(rocksdb::ReadOptions(), cfh, key, &value); - return Status(s.code(), s.ToString()); -} - -Status DBHandle::Put(const std::string& domain, - const std::string& key, - const std::string& value) { - auto cfh = getHandleForColumnFamily(domain); - if (cfh == nullptr) { - return Status(1, "Could not get column family for " + domain); - } - auto s = getDB()->Put(rocksdb::WriteOptions(), cfh, key, value); - return Status(s.code(), s.ToString()); -} - -Status DBHandle::Delete(const std::string& domain, const std::string& key) { - auto cfh = getHandleForColumnFamily(domain); - if (cfh == nullptr) { - return Status(1, "Could not get column family for " + domain); - } - auto options = rocksdb::WriteOptions(); - options.sync = true; - auto s = getDB()->Delete(options, cfh, key); - return Status(s.code(), s.ToString()); -} - -Status DBHandle::Scan(const std::string& domain, - std::vector& results) { - auto cfh = getHandleForColumnFamily(domain); - if (cfh == nullptr) { - return Status(1, "Could not get column family for " + domain); - } - auto it = getDB()->NewIterator(rocksdb::ReadOptions(), cfh); - if (it == nullptr) { - return Status(1, "Could not get iterator for " + domain); - } - for (it->SeekToFirst(); it->Valid(); it->Next()) { - results.push_back(it->key().ToString()); - } - delete it; - return Status(0, "OK"); -} - -Status RocksDatabasePlugin::get(const std::string& domain, - const std::string& key, - std::string& value) const { - return DBHandle::getInstance()->Get(domain, key, value); -} - -Status RocksDatabasePlugin::put(const std::string& domain, - const std::string& key, - const std::string& value) { - return DBHandle::getInstance()->Put(domain, key, value); -} - -Status RocksDatabasePlugin::remove(const std::string& domain, - const std::string& key) { - return DBHandle::getInstance()->Delete(domain, key); -} - -Status RocksDatabasePlugin::scan(const std::string& domain, - std::vector& results) const { - return DBHandle::getInstance()->Scan(domain, results); -} -} diff --git a/osquery/database/db_handle.h b/osquery/database/db_handle.h deleted file mode 100644 index 26ccf4e..0000000 --- a/osquery/database/db_handle.h +++ /dev/null @@ -1,245 +0,0 @@ -/* - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#pragma once - -#include -#include -#include - -#include - -#include - -#include -#include - -namespace osquery { - -DECLARE_string(database_path); - -class DBHandle; -typedef std::shared_ptr DBHandleRef; - -/** - * @brief RAII singleton around RocksDB database access. - * - * Accessing RocksDB necessitates creating several pointers which must be - * carefully memory managed. DBHandle offers you a singleton which takes - * care of acquiring and releasing the relevant pointers and data structures - * for you. - */ -class DBHandle { - public: - /// Removes every column family handle and single DB handle/lock. - ~DBHandle(); - - /** - * @brief The primary way to access the DBHandle singleton. - * - * DBHandle::getInstance() provides access to the DBHandle singleton. - * - * @code{.cpp} - * auto db = DBHandle::getInstance(); - * std::string value; - * auto status = db->Get("default", "foo", value); - * if (status.ok()) { - * assert(value == "bar"); - * } - * @endcode - * - * @return a shared pointer to an instance of DBHandle - */ - static DBHandleRef getInstance(); - - /** - * @brief Check the sanity of the database configuration options - * - * Create a handle to the backing store using the database configuration. - * Catch any instance creation exceptions and release the handle immediately. - * - * @return Success if a handle was created without error. - */ - static bool checkDB(); - - private: - ///////////////////////////////////////////////////////////////////////////// - // Data access methods - ///////////////////////////////////////////////////////////////////////////// - - /** - * @brief Get data from the database - * - * @param domain the "domain" or "column family" that you'd like to retrieve - * the data from - * @param key the string key that you'd like to get - * @param value a non-const string reference where the result of the - * operation will be stored - * - * @return an instance of osquery::Status indicating the success or failure - * of the operation. - */ - Status Get(const std::string& domain, - const std::string& key, - std::string& value); - - /** - * @brief Put data into the database - * - * @param domain the "domain" or "column family" that you'd like to insert - * data into - * @param key the string key that you'd like to put - * @param value the data that you'd like to put into RocksDB - * - * @return an instance of osquery::Status indicating the success or failure - * of the operation. - */ - Status Put(const std::string& domain, - const std::string& key, - const std::string& value); - - /** - * @brief Delete data from the database - * - * @param domain the "domain" or "column family" that you'd like to delete - * data from - * @param key the string key that you'd like to delete - * - * @return an instance of osquery::Status indicating the success or failure - * of the operation. - */ - Status Delete(const std::string& domain, const std::string& key); - - /** - * @brief List the data in a "domain" - * - * @param domain the "domain" or "column family" that you'd like to list - * data from - * @param results a non-const reference to a vector which will be populated - * with all of the keys from the supplied domain. - * - * @return an instance of osquery::Status indicating the success or failure - * of the operation. - */ - Status Scan(const std::string& domain, std::vector& results); - - private: - /** - * @brief Default constructor - * - * DBHandle's constructor takes care of properly connecting to RocksDB and - * ensuring that all necessary column families are created. The resulting - * database handle can then be accessed via DBHandle::getDB() and the - * success of the connection can be determined by inspecting the resulting - * status code via DBHandle::getStatus() - */ - DBHandle(); - - /** - * @brief Internal only constructor used to create instances of DBHandle. - * - * This constructor allows you to specify a few more details about how you'd - * like DBHandle to be used. This is only used internally, so you should - * never actually use it. - * - * @param path the path to create/access the database - * @param in_memory a boolean indicating whether or not the database should - * be creating in memory or not. - */ - DBHandle(const std::string& path, bool in_memory); - - /** - * @brief A method which allows you to override the database path - * - * This should only be used by unit tests. Never use it in production code. - * - * @return a shared pointer to an instance of DBHandle - */ - static DBHandleRef getInstanceAtPath(const std::string& path); - - /** - * @brief A method which gets you an in-memory RocksDB instance. - * - * This should only be used by unit tests. Never use it in production code. - * - * @return a shared pointer to an instance of DBHandle - */ - static DBHandleRef getInstanceInMemory(); - - /** - * @brief A method which allows you to configure various aspects of RocksDB - * database options. - * - * This should only be used by unit tests. Never use it in production code. - * - * @param path the path to create/access the database - * @param in_memory a boolean indicating whether or not the database should - * be creating in memory or not. - * - * @return a shared pointer to an instance of DBHandle - */ - static DBHandleRef getInstance(const std::string& path, bool in_memory); - - /** - * @brief Private helper around accessing the column family handle for a - * specific column family, based on it's name - */ - rocksdb::ColumnFamilyHandle* getHandleForColumnFamily(const std::string& cf); - - /** - * @brief Helper method which can be used to get a raw pointer to the - * underlying RocksDB database handle - * - * You probably shouldn't use this. DBHandle::getDB() should only be used - * when you're positive that it's the right thing to use. - * - * @return a pointer to the underlying RocksDB database handle - */ - rocksdb::DB* getDB(); - - private: - ///////////////////////////////////////////////////////////////////////////// - // Private members - ///////////////////////////////////////////////////////////////////////////// - - /// The database handle - rocksdb::DB* db_; - - /// Column family descriptors which are used to connect to RocksDB - std::vector column_families_; - - /// A vector of pointers to column family handles - std::vector handles_; - - /// The RocksDB connection options that are used to connect to RocksDB - rocksdb::Options options_; - - private: - friend class RocksDatabasePlugin; - friend class Query; - friend class EventSubscriberPlugin; - - ///////////////////////////////////////////////////////////////////////////// - // Unit tests which can access private members - ///////////////////////////////////////////////////////////////////////////// - - friend class DBHandleTests; - FRIEND_TEST(DBHandleTests, test_get); - FRIEND_TEST(DBHandleTests, test_put); - FRIEND_TEST(DBHandleTests, test_delete); - FRIEND_TEST(DBHandleTests, test_scan); - friend class QueryTests; - FRIEND_TEST(QueryTests, test_get_query_results); - FRIEND_TEST(QueryTests, test_is_query_name_in_database); - FRIEND_TEST(QueryTests, test_get_stored_query_names); - friend class EventsTests; - friend class EventsDatabaseTests; -}; -} diff --git a/osquery/database/query.cpp b/osquery/database/query.cpp deleted file mode 100644 index 360a329..0000000 --- a/osquery/database/query.cpp +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include - -#include "osquery/database/query.h" - -namespace osquery { - -///////////////////////////////////////////////////////////////////////////// -// Getters and setters -///////////////////////////////////////////////////////////////////////////// - -std::string Query::getQuery() { return query_.query; } - -std::string Query::getQueryName() { return name_; } - -int Query::getInterval() { return query_.interval; } - -///////////////////////////////////////////////////////////////////////////// -// Data access methods -///////////////////////////////////////////////////////////////////////////// - -Status Query::getPreviousQueryResults(QueryData& results) { - return getPreviousQueryResults(results, DBHandle::getInstance()); -} - -Status Query::getPreviousQueryResults(QueryData& results, DBHandleRef db) { - if (!isQueryNameInDatabase()) { - return Status(0, "Query name not found in database"); - } - - std::string raw; - auto status = db->Get(kQueries, name_, raw); - if (!status.ok()) { - return status; - } - - status = deserializeQueryDataJSON(raw, results); - if (!status.ok()) { - return status; - } - return Status(0, "OK"); -} - -std::vector Query::getStoredQueryNames() { - return getStoredQueryNames(DBHandle::getInstance()); -} - -std::vector Query::getStoredQueryNames(DBHandleRef db) { - std::vector results; - db->Scan(kQueries, results); - return results; -} - -bool Query::isQueryNameInDatabase() { - return isQueryNameInDatabase(DBHandle::getInstance()); -} - -bool Query::isQueryNameInDatabase(DBHandleRef db) { - auto names = Query::getStoredQueryNames(db); - return std::find(names.begin(), names.end(), name_) != names.end(); -} - -Status Query::addNewResults(const osquery::QueryData& qd) { - return addNewResults(qd, DBHandle::getInstance()); -} - -Status Query::addNewResults(const QueryData& qd, DBHandleRef db) { - DiffResults dr; - return addNewResults(qd, dr, false, db); -} - -Status Query::addNewResults(const QueryData& qd, DiffResults& dr) { - return addNewResults(qd, dr, true, DBHandle::getInstance()); -} - -Status Query::addNewResults(const QueryData& current_qd, - DiffResults& dr, - bool calculate_diff, - DBHandleRef db) { - // Get the rows from the last run of this query name. - QueryData previous_qd; - auto status = getPreviousQueryResults(previous_qd); - if (!status.ok()) { - return status; - } - - // Sanitize all non-ASCII characters from the query data values. - QueryData escaped_current_qd; - escapeQueryData(current_qd, escaped_current_qd); - // Calculate the differential between previous and current query results. - if (calculate_diff) { - dr = diff(previous_qd, escaped_current_qd); - } - - // Replace the "previous" query data with the current. - std::string json; - status = serializeQueryDataJSON(escaped_current_qd, json); - if (!status.ok()) { - return status; - } - - status = db->Put(kQueries, name_, json); - if (!status.ok()) { - return status; - } - return Status(0, "OK"); -} -} diff --git a/osquery/database/query.h b/osquery/database/query.h deleted file mode 100644 index d1852ec..0000000 --- a/osquery/database/query.h +++ /dev/null @@ -1,275 +0,0 @@ -/* - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#pragma once - -#include -#include -#include - -#include -#include - -#include "osquery/database/db_handle.h" - -namespace osquery { - -/// Error message used when a query name isn't found in the database -extern const std::string kQueryNameNotFoundError; - -/** - * @brief A class that is used to interact with the historical on-disk storage - * for a given query. - */ -class Query { - public: - /** - * @brief Constructor which sets up necessary parameters of a Query object - * - * Given a query, this constructor calculates the value of columnFamily_, - * which can be accessed via the getColumnFamilyName getter method. - * - * @param q a SheduledQuery struct - */ - explicit Query(const std::string& name, const ScheduledQuery& q) - : query_(q), name_(name) {} - - ///////////////////////////////////////////////////////////////////////////// - // Getters and setters - ///////////////////////////////////////////////////////////////////////////// - - /** - * @brief Getter for the name of a given scheduled query - * - * @return the name of the scheduled query which is being operated on - */ - std::string getQueryName(); - - /** - * @brief Getter for the SQL query of a scheduled query - * - * @return the SQL of the scheduled query which is being operated on - */ - std::string getQuery(); - - /** - * @brief Getter for the interval of a scheduled query - * - * @return the interval of the scheduled query which is being operated on - */ - int getInterval(); - - ///////////////////////////////////////////////////////////////////////////// - // Data access methods - ///////////////////////////////////////////////////////////////////////////// - - public: - /** - * @brief Serialize the data in RocksDB into a useful data structure - * - * This method retrieves the data from RocksDB and returns the data in a - * HistoricalQueryResults struct. - * - * @param hQR the output HistoricalQueryResults struct - * - * @return the success or failure of the operation - */ - // Status getHistoricalQueryResults(HistoricalQueryResults& hQR); - Status getPreviousQueryResults(QueryData& results); - - private: - /** - * @brief Serialize the data in RocksDB into a useful data structure using a - * custom database handle - * - * This method is the same as getHistoricalQueryResults, but with the - * addition of a parameter which allows you to pass a custom RocksDB - * database handle. - * - * @param hQR the output HistoricalQueryResults struct - * @param db a shared pointer to a custom DBHandle - * - * @return the success or failure of the operation - * @see getHistoricalQueryResults - */ - // Status getHistoricalQueryResults(HistoricalQueryResults& hQR, - // std::shared_ptr db); - Status getPreviousQueryResults(QueryData& results, DBHandleRef db); - - public: - /** - * @brief Get the names of all historical queries that are stored in RocksDB - * - * If you'd like to perform some database maintenance, getStoredQueryNames() - * allows you to get a vector of the names of all queries which are - * currently stored in RocksDB - * - * @return a vector containing the string names of all scheduled queries - * which currently exist in the database - */ - static std::vector getStoredQueryNames(); - - private: - /** - * @brief Get the names of all historical queries that are stored in RocksDB - * using a custom database handle - * - * This method is the same as getStoredQueryNames(), but with the addition - * of a parameter which allows you to pass a custom RocksDB database handle. - * - * @param db a custom RocksDB database handle - * - * @return a vector containing the string names of all scheduled queries - * - * @see getStoredQueryNames() - */ - static std::vector getStoredQueryNames(DBHandleRef db); - - public: - /** - * @brief Accessor method for checking if a given scheduled query exists in - * the database - * - * @return does the scheduled query which is already exists in the database - */ - bool isQueryNameInDatabase(); - - private: - /** - * @brief Accessor method for checking if a given scheduled query exists in - * the database, using a custom database handle - * - * This method is the same as isQueryNameInDatabase(), but with the addition - * of a parameter which allows you to pass a custom RocksDB database handle - * - * @param db a custom RocksDB database handle - * - * @return does the scheduled query which is already exists in the database - */ - bool isQueryNameInDatabase(DBHandleRef db); - - public: - /** - * @brief Add a new set of results to the persistant storage - * - * Given the results of the execution of a scheduled query, add the results - * to the database using addNewResults - * - * @param qd the QueryData object, which has the results of the query which - * you would like to store - * @param unix_time the time that the query was executed - * - * @return an instance of osquery::Status indicating the success or failure - * of the operation - */ - Status addNewResults(const QueryData& qd); - - private: - /** - * @brief Add a new set of results to the persistant storage using a custom - * database handle - * - * This method is the same as addNewResults(), but with the addition of a - * parameter which allows you to pass a custom RocksDB database handle - * - * @param qd the QueryData object, which has the results of the query which - * you would like to store - * @param unix_time the time that the query was executed - * @param db a custom RocksDB database handle - * - * @return an instance of osquery::Status indicating the success or failure - * of the operation - */ - Status addNewResults(const QueryData& qd, DBHandleRef db); - - public: - /** - * @brief Add a new set of results to the persistent storage and get back - * the differential results. - * - * Given the results of an execution of a scheduled query, add the results - * to the database using addNewResults and get back a data structure - * indicating what rows in the query's results have changed. - * - * @param qd the QueryData object containing query results to store - * @param dr an output to a DiffResults object populated based on last run - * - * @return the success or failure of the operation - */ - Status addNewResults(const QueryData& qd, DiffResults& dr); - - private: - /** - * @brief Add a new set of results to the persistent storage and get back - * the differential results, using a custom database handle. - * - * This method is the same as Query::addNewResults, but with the addition of a - * parameter which allows you to pass a custom RocksDB database handle - * - * @param qd the QueryData object containing query results to store - * @param dr an output to a DiffResults object populated based on last run - * - * @return the success or failure of the operation - */ - Status addNewResults(const QueryData& qd, - DiffResults& dr, - bool calculate_diff, - DBHandleRef db); - - public: - /** - * @brief A getter for the most recent result set for a scheduled query - * - * @param qd the output QueryData object - * - * @return the success or failure of the operation - */ - Status getCurrentResults(QueryData& qd); - - private: - /** - * @brief A getter for the most recent result set for a scheduled query, - * but with the addition of a parameter which allows you to pass a custom - * RocksDB database handle. - * - * This method is the same as Query::getCurrentResults, but with addition of a - * parameter which allows you to pass a custom RocksDB database handle. - * - * @param qd the output QueryData object - * @param db a custom RocksDB database handle - * - * @return the success or failure of the operation - */ - Status getCurrentResults(QueryData& qd, DBHandleRef db); - - private: - ///////////////////////////////////////////////////////////////////////////// - // Private members - ///////////////////////////////////////////////////////////////////////////// - - /// The scheduled query and internal - ScheduledQuery query_; - /// The scheduled query name. - std::string name_; - - private: - ///////////////////////////////////////////////////////////////////////////// - // Unit tests which can access private members - ///////////////////////////////////////////////////////////////////////////// - - FRIEND_TEST(QueryTests, test_private_members); - FRIEND_TEST(QueryTests, test_add_and_get_current_results); - FRIEND_TEST(QueryTests, test_is_query_name_in_database); - FRIEND_TEST(QueryTests, test_get_stored_query_names); - FRIEND_TEST(QueryTests, test_get_executions); - FRIEND_TEST(QueryTests, test_get_query_results); - FRIEND_TEST(QueryTests, test_query_name_not_found_in_db); -}; -} diff --git a/osquery/database/tests/db_handle_tests.cpp b/osquery/database/tests/db_handle_tests.cpp deleted file mode 100644 index 8c3e07b..0000000 --- a/osquery/database/tests/db_handle_tests.cpp +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include - -#include - -#include - -#include -#include - -#include "osquery/database/db_handle.h" - -const std::string kTestingDBHandlePath = "/tmp/rocksdb-osquery-dbhandletests"; - -namespace osquery { - -class DBHandleTests : public testing::Test { - public: - void SetUp() { - // Setup a testing DB instance - db = DBHandle::getInstanceAtPath(kTestingDBHandlePath); - cfh_queries = DBHandle::getInstance()->getHandleForColumnFamily(kQueries); - cfh_foobar = - DBHandle::getInstance()->getHandleForColumnFamily("foobartest"); - } - - void TearDown() { boost::filesystem::remove_all(kTestingDBHandlePath); } - - public: - rocksdb::ColumnFamilyHandle* cfh_queries; - rocksdb::ColumnFamilyHandle* cfh_foobar; - std::shared_ptr db; -}; - -TEST_F(DBHandleTests, test_singleton_on_disk) { - auto db1 = DBHandle::getInstance(); - auto db2 = DBHandle::getInstance(); - EXPECT_EQ(db1, db2); -} - -TEST_F(DBHandleTests, test_get_handle_for_column_family) { - ASSERT_TRUE(cfh_queries != nullptr); - ASSERT_TRUE(cfh_foobar == nullptr); -} - -TEST_F(DBHandleTests, test_get) { - db->getDB()->Put( - rocksdb::WriteOptions(), cfh_queries, "test_query_123", "{}"); - std::string r; - std::string key = "test_query_123"; - auto s = db->Get(kQueries, key, r); - EXPECT_TRUE(s.ok()); - EXPECT_EQ(s.toString(), "OK"); - EXPECT_EQ(r, "{}"); -} - -TEST_F(DBHandleTests, test_put) { - auto s = db->Put(kQueries, "test_put", "bar"); - EXPECT_TRUE(s.ok()); - EXPECT_EQ(s.toString(), "OK"); -} - -TEST_F(DBHandleTests, test_delete) { - db->Put(kQueries, "test_delete", "baz"); - auto s = db->Delete(kQueries, "test_delete"); - EXPECT_TRUE(s.ok()); - EXPECT_EQ(s.toString(), "OK"); -} - -TEST_F(DBHandleTests, test_scan) { - db->Put(kQueries, "test_scan_foo1", "baz"); - db->Put(kQueries, "test_scan_foo2", "baz"); - db->Put(kQueries, "test_scan_foo3", "baz"); - std::vector keys; - std::vector expected = { - "test_scan_foo1", "test_scan_foo2", "test_scan_foo3"}; - auto s = db->Scan(kQueries, keys); - EXPECT_TRUE(s.ok()); - EXPECT_EQ(s.toString(), "OK"); - for (const auto& i : expected) { - EXPECT_NE(std::find(keys.begin(), keys.end(), i), keys.end()); - } -} -} diff --git a/osquery/database/tests/query_tests.cpp b/osquery/database/tests/query_tests.cpp deleted file mode 100644 index 0142f27..0000000 --- a/osquery/database/tests/query_tests.cpp +++ /dev/null @@ -1,138 +0,0 @@ -/* - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include -#include -#include - -#include - -#include - -#include "osquery/core/test_util.h" -#include "osquery/database/query.h" - -const std::string kTestingQueryDBPath = "/tmp/rocksdb-osquery-querytests"; - -namespace osquery { - -class QueryTests : public testing::Test { - public: - void SetUp() { db_ = DBHandle::getInstanceAtPath(kTestingQueryDBPath); } - void TearDown() { boost::filesystem::remove_all(kTestingQueryDBPath); } - - public: - std::shared_ptr db_; -}; - -TEST_F(QueryTests, test_get_column_family_name) { - auto query = getOsqueryScheduledQuery(); - auto cf = Query("foobar", query); - EXPECT_EQ(cf.getQueryName(), "foobar"); -} - -TEST_F(QueryTests, test_get_query) { - auto query = getOsqueryScheduledQuery(); - auto cf = Query("foobar", query); - EXPECT_EQ(cf.getQuery(), query.query); -} - -TEST_F(QueryTests, test_get_interval) { - auto query = getOsqueryScheduledQuery(); - auto cf = Query("foobar", query); - EXPECT_EQ(cf.getInterval(), query.interval); -} - -TEST_F(QueryTests, test_private_members) { - auto query = getOsqueryScheduledQuery(); - auto cf = Query("foobar", query); - EXPECT_EQ(cf.query_, query); -} - -TEST_F(QueryTests, test_add_and_get_current_results) { - // Test adding a "current" set of results to a scheduled query instance. - auto query = getOsqueryScheduledQuery(); - auto cf = Query("foobar", query); - auto status = cf.addNewResults(getTestDBExpectedResults(), db_); - EXPECT_TRUE(status.ok()); - EXPECT_EQ(status.toString(), "OK"); - - // Simulate results from several schedule runs, calculate differentials. - for (auto result : getTestDBResultStream()) { - // Get the results from the previous query execution (from RocksDB). - QueryData previous_qd; - auto status = cf.getPreviousQueryResults(previous_qd, db_); - EXPECT_TRUE(status.ok()); - EXPECT_EQ(status.toString(), "OK"); - - // Add the "current" results and output the differentials. - DiffResults dr; - auto s = cf.addNewResults(result.second, dr, true, db_); - EXPECT_TRUE(s.ok()); - - // Call the diffing utility directly. - DiffResults expected = diff(previous_qd, result.second); - EXPECT_EQ(dr, expected); - - // After Query::addNewResults the previous results are now current. - QueryData qd; - cf.getPreviousQueryResults(qd, db_); - EXPECT_EQ(qd, result.second); - } -} - -TEST_F(QueryTests, test_get_query_results) { - // Grab an expected set of query data and add it as the previous result. - auto encoded_qd = getSerializedQueryDataJSON(); - auto query = getOsqueryScheduledQuery(); - auto status = db_->Put(kQueries, "foobar", encoded_qd.first); - EXPECT_TRUE(status.ok()); - - // Use the Query retrieval API to check the now "previous" result. - QueryData previous_qd; - auto cf = Query("foobar", query); - status = cf.getPreviousQueryResults(previous_qd, db_); - EXPECT_TRUE(status.ok()); -} - -TEST_F(QueryTests, test_query_name_not_found_in_db) { - // Try to retrieve results from a query that has not executed. - QueryData previous_qd; - auto query = getOsqueryScheduledQuery(); - auto cf = Query("not_a_real_query", query); - auto status = cf.getPreviousQueryResults(previous_qd, db_); - EXPECT_TRUE(status.toString() == "Query name not found in database"); - EXPECT_TRUE(status.ok()); -} - -TEST_F(QueryTests, test_is_query_name_in_database) { - auto query = getOsqueryScheduledQuery(); - auto cf = Query("foobar", query); - auto encoded_qd = getSerializedQueryDataJSON(); - auto status = db_->Put(kQueries, "foobar", encoded_qd.first); - EXPECT_TRUE(status.ok()); - // Now test that the query name exists. - EXPECT_TRUE(cf.isQueryNameInDatabase(db_)); -} - -TEST_F(QueryTests, test_get_stored_query_names) { - auto query = getOsqueryScheduledQuery(); - auto cf = Query("foobar", query); - auto encoded_qd = getSerializedQueryDataJSON(); - auto status = db_->Put(kQueries, "foobar", encoded_qd.first); - EXPECT_TRUE(status.ok()); - - // Stored query names is a factory method included alongside every query. - // It will include the set of query names with existing "previous" results. - auto names = cf.getStoredQueryNames(db_); - auto in_vector = std::find(names.begin(), names.end(), "foobar"); - EXPECT_NE(in_vector, names.end()); -} -} diff --git a/osquery/database/tests/results_tests.cpp b/osquery/database/tests/results_tests.cpp deleted file mode 100644 index 2c16d01..0000000 --- a/osquery/database/tests/results_tests.cpp +++ /dev/null @@ -1,193 +0,0 @@ -/* - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include -#include -#include - -#include - -#include -#include - -#include "osquery/core/test_util.h" - -namespace pt = boost::property_tree; - -namespace osquery { - -class ResultsTests : public testing::Test {}; -std::string escapeNonPrintableBytes(const std::string& data); - -TEST_F(ResultsTests, test_simple_diff) { - QueryData o; - QueryData n; - - Row r1; - r1["foo"] = "bar"; - n.push_back(r1); - - auto results = diff(o, n); - EXPECT_EQ(results.added, n); - EXPECT_EQ(results.removed, o); -} - -TEST_F(ResultsTests, test_serialize_row) { - auto results = getSerializedRow(); - pt::ptree tree; - auto s = serializeRow(results.second, tree); - EXPECT_TRUE(s.ok()); - EXPECT_EQ(s.toString(), "OK"); - EXPECT_EQ(results.first, tree); -} - -TEST_F(ResultsTests, test_deserialize_row_json) { - auto results = getSerializedRow(); - std::string input; - serializeRowJSON(results.second, input); - - // Pull the serialized JSON back into a Row output container. - Row output; - auto s = deserializeRowJSON(input, output); - EXPECT_TRUE(s.ok()); - // The output container should match the input row. - EXPECT_EQ(output, results.second); -} - -TEST_F(ResultsTests, test_serialize_query_data) { - auto results = getSerializedQueryData(); - pt::ptree tree; - auto s = serializeQueryData(results.second, tree); - EXPECT_TRUE(s.ok()); - EXPECT_EQ(s.toString(), "OK"); - EXPECT_EQ(results.first, tree); -} - -TEST_F(ResultsTests, test_serialize_query_data_json) { - auto results = getSerializedQueryDataJSON(); - std::string json; - auto s = serializeQueryDataJSON(results.second, json); - EXPECT_TRUE(s.ok()); - EXPECT_EQ(s.toString(), "OK"); - EXPECT_EQ(results.first, json); -} - -TEST_F(ResultsTests, test_deserialize_query_data_json) { - auto results = getSerializedQueryDataJSON(); - - // Pull the serialized JSON back into a QueryData output container. - QueryData output; - auto s = deserializeQueryDataJSON(results.first, output); - EXPECT_TRUE(s.ok()); - // The output container should match the input query data. - EXPECT_EQ(output, results.second); -} - -TEST_F(ResultsTests, test_serialize_diff_results) { - auto results = getSerializedDiffResults(); - pt::ptree tree; - auto s = serializeDiffResults(results.second, tree); - EXPECT_TRUE(s.ok()); - EXPECT_EQ(s.toString(), "OK"); - EXPECT_EQ(results.first, tree); -} - -TEST_F(ResultsTests, test_serialize_diff_results_json) { - auto results = getSerializedDiffResultsJSON(); - std::string json; - auto s = serializeDiffResultsJSON(results.second, json); - EXPECT_TRUE(s.ok()); - EXPECT_EQ(s.toString(), "OK"); - EXPECT_EQ(results.first, json); -} - -TEST_F(ResultsTests, test_serialize_query_log_item) { - auto results = getSerializedQueryLogItem(); - pt::ptree tree; - auto s = serializeQueryLogItem(results.second, tree); - EXPECT_TRUE(s.ok()); - EXPECT_EQ(s.toString(), "OK"); - EXPECT_EQ(results.first, tree); -} - -TEST_F(ResultsTests, test_serialize_query_log_item_json) { - auto results = getSerializedQueryLogItemJSON(); - std::string json; - auto s = serializeQueryLogItemJSON(results.second, json); - EXPECT_TRUE(s.ok()); - EXPECT_EQ(s.toString(), "OK"); - EXPECT_EQ(results.first, json); -} - -TEST_F(ResultsTests, test_deserialize_query_log_item_json) { - auto results = getSerializedQueryLogItemJSON(); - - // Pull the serialized JSON back into a QueryLogItem output container. - QueryLogItem output; - auto s = deserializeQueryLogItemJSON(results.first, output); - EXPECT_TRUE(s.ok()); - // The output container should match the input query data. - EXPECT_EQ(output, results.second); -} - -TEST_F(ResultsTests, test_unicode_to_ascii_conversion) { - EXPECT_EQ(escapeNonPrintableBytes("しかたがない"), - "\\xE3\\x81\\x97\\xE3\\x81\\x8B\\xE3\\x81\\x9F\\xE3\\x81\\x8C\\xE3" - "\\x81\\xAA\\xE3\\x81\\x84"); - EXPECT_EQ(escapeNonPrintableBytes("悪因悪果"), - "\\xE6\\x82\\xAA\\xE5\\x9B\\xA0\\xE6\\x82\\xAA\\xE6\\x9E\\x9C"); - EXPECT_EQ(escapeNonPrintableBytes("モンスターハンター"), - "\\xE3\\x83\\xA2\\xE3\\x83\\xB3\\xE3\\x82\\xB9\\xE3\\x82\\xBF\\xE3" - "\\x83\\xBC\\xE3\\x83\\x8F\\xE3\\x83\\xB3\\xE3\\x82\\xBF\\xE3\\x83" - "\\xBC"); - EXPECT_EQ( - escapeNonPrintableBytes( - "съешь же ещё этих мягких французских булок, да выпей чаю"), - "\\xD1\\x81\\xD1\\x8A\\xD0\\xB5\\xD1\\x88\\xD1\\x8C \\xD0\\xB6\\xD0\\xB5 " - "\\xD0\\xB5\\xD1\\x89\\xD1\\x91 \\xD1\\x8D\\xD1\\x82\\xD0\\xB8\\xD1\\x85 " - "\\xD0\\xBC\\xD1\\x8F\\xD0\\xB3\\xD0\\xBA\\xD0\\xB8\\xD1\\x85 " - "\\xD1\\x84\\xD1\\x80\\xD0\\xB0\\xD0\\xBD\\xD1\\x86\\xD1\\x83\\xD0\\xB7\\" - "xD1\\x81\\xD0\\xBA\\xD0\\xB8\\xD1\\x85 " - "\\xD0\\xB1\\xD1\\x83\\xD0\\xBB\\xD0\\xBE\\xD0\\xBA, " - "\\xD0\\xB4\\xD0\\xB0 \\xD0\\xB2\\xD1\\x8B\\xD0\\xBF\\xD0\\xB5\\xD0\\xB9 " - "\\xD1\\x87\\xD0\\xB0\\xD1\\x8E"); - - EXPECT_EQ( - escapeNonPrintableBytes("The quick brown fox jumps over the lazy dog."), - "The quick brown fox jumps over the lazy dog."); -} - -TEST_F(ResultsTests, test_adding_duplicate_rows_to_query_data) { - Row r1, r2, r3; - r1["foo"] = "bar"; - r1["baz"] = "boo"; - - r2["foo"] = "baz"; - r2["baz"] = "bop"; - - r3["foo"] = "baz"; - r3["baz"] = "bop"; - - QueryData q; - bool s; - - s = addUniqueRowToQueryData(q, r1); - EXPECT_TRUE(s); - EXPECT_EQ(q.size(), 1); - - s = addUniqueRowToQueryData(q, r2); - EXPECT_TRUE(s); - EXPECT_EQ(q.size(), 2); - - s = addUniqueRowToQueryData(q, r3); - EXPECT_FALSE(s); - EXPECT_EQ(q.size(), 2); -} -} diff --git a/osquery/devtools/CMakeLists.txt b/osquery/devtools/CMakeLists.txt deleted file mode 100644 index 0d916dc..0000000 --- a/osquery/devtools/CMakeLists.txt +++ /dev/null @@ -1,18 +0,0 @@ -# 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_devtools printer.cpp) - -FILE(GLOB OSQUERY_DEVTOOLS_TESTS "tests/*.cpp") -ADD_OSQUERY_TEST(${OSQUERY_DEVTOOLS_TESTS}) diff --git a/osquery/devtools/devtools.h b/osquery/devtools/devtools.h deleted file mode 100644 index 54917ee..0000000 --- a/osquery/devtools/devtools.h +++ /dev/null @@ -1,120 +0,0 @@ -/* - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#pragma once - -#include - -#include -#include - -namespace osquery { - -/// Show all tables and exit the shell. -DECLARE_bool(L); -/// Select all from a table an exit the shell. -DECLARE_string(A); -/// The shell may need to disable events for fast operations. -DECLARE_bool(disable_events); - -/** - * @brief Run an interactive SQL query shell. - * - * @code{.cpp} - * // Copyright 2004-present Facebook. All Rights Reserved. - * #include - * #include - * - * int main(int argc, char *argv[]) { - * osquery::initOsquery(argc, argv); - * return osquery::launchIntoShell(argc, argv); - * } - * @endcode - * - * @param argc the number of elements in argv - * @param argv the command-line flags - * - * @return an int which represents the "return code" - */ -int launchIntoShell(int argc, char** argv); - -/** - * @brief Pretty print a QueryData object - * - * This is a helper method which called osquery::beautify on the supplied - * QueryData object and prints the results to stdout. - * - * @param results The QueryData object to print - * @param columns The order of the keys (since maps are unordered) - * @param lengths A mutable set of column lengths - */ -void prettyPrint(const QueryData& results, - const std::vector& columns, - std::map& lengths); - -/** - * @brief JSON print a QueryData object - * - * This is a helper method which allows a shell or other tool to print results - * in a JSON format. - * - * @param q The QueryData object to print - */ -void jsonPrint(const QueryData& q); - -/** - * @brief Compute a map of metadata about the supplied QueryData object - * - * @param r A row to analyze - * @param lengths A mutable set of column lengths - * @param use_columns Calulate lengths of column names or values - * - * @return A map of string to int such that the key represents the "column" in - * the supplied QueryData and the int represents the length of the longest key - */ -void computeRowLengths(const Row& r, - std::map& lengths, - bool use_columns = false); - -/** - * @brief Generate the separator string for query results - * - * @param lengths The data returned from computeQueryDataLengths - * @param columns The order of the keys (since maps are unordered) - * - * @return A string, with a newline, representing your separator - */ -std::string generateToken(const std::map& lengths, - const std::vector& columns); - -/** - * @brief Generate the header string for query results - * - * @param lengths The data returned from computeQueryDataLengths - * @param columns The order of the keys (since maps are unordered) - * - * @return A string, with a newline, representing your header - */ -std::string generateHeader(const std::map& lengths, - const std::vector& columns); - -/** - * @brief Generate a row string for query results - * - * @param r A row to analyze - * @param lengths The data returned from computeQueryDataLengths - * @param columns The order of the keys (since maps are unordered) - * - * @return A string, with a newline, representing your row - */ -std::string generateRow(const Row& r, - const std::map& lengths, - const std::vector& columns); -} diff --git a/osquery/devtools/printer.cpp b/osquery/devtools/printer.cpp deleted file mode 100644 index 380d2ca..0000000 --- a/osquery/devtools/printer.cpp +++ /dev/null @@ -1,149 +0,0 @@ -/* - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include -#include - -#include - -#include "osquery/devtools/devtools.h" - -namespace osquery { - -static std::vector kOffset = {0, 0}; -static std::string kToken = "|"; - -std::string generateToken(const std::map& lengths, - const std::vector& columns) { - std::string out = "+"; - for (const auto& col : columns) { - if (lengths.count(col) > 0) { - if (getenv("ENHANCE") != nullptr) { - std::string e = "\xF0\x9F\x90\x8C"; - e[2] += kOffset[1]; - e[3] += kOffset[0]; - for (int i = 0; i < lengths.at(col) + 2; i++) { - e[3] = '\x8c' + kOffset[0]++; - if (e[3] == '\xbf') { - e[3] = '\x80'; - kOffset[1] = (kOffset[1] > 3 && kOffset[1] < 8) ? 9 : kOffset[1]; - e[2] = '\x90' + ++kOffset[1]; - kOffset[0] = 0; - } - if (kOffset[1] == ('\x97' - '\x8d')) { - kOffset = {0, 0}; - } - out += e.c_str(); - } - } else { - out += std::string(lengths.at(col) + 2, '-'); - } - } - out += "+"; - } - - out += "\n"; - return out; -} - -std::string generateHeader(const std::map& lengths, - const std::vector& columns) { - if (getenv("ENHANCE") != nullptr) { - kToken = "\xF0\x9F\x91\x8D"; - } - std::string out = kToken; - for (const auto& col : columns) { - out += " " + col; - if (lengths.count(col) > 0) { - int buffer_size = lengths.at(col) - utf8StringSize(col) + 1; - if (buffer_size > 0) { - out += std::string(buffer_size, ' '); - } else { - out += ' '; - } - } - out += kToken; - } - out += "\n"; - return out; -} - -std::string generateRow(const Row& r, - const std::map& lengths, - const std::vector& order) { - std::string out; - for (const auto& column : order) { - if (r.count(column) == 0 || lengths.count(column) == 0) { - continue; - } - // Print a terminator for the previous value or lhs, followed by spaces. - - int buffer_size = lengths.at(column) - utf8StringSize(r.at(column)) + 1; - if (buffer_size > 0) { - out += kToken + " " + r.at(column) + std::string(buffer_size, ' '); - } - } - - if (out.size() > 0) { - // Only append if a row was added. - out += kToken + "\n"; - } - - return out; -} - -void prettyPrint(const QueryData& results, - const std::vector& columns, - std::map& lengths) { - if (results.size() == 0) { - return; - } - - // Call a final compute using the column names as minimum lengths. - computeRowLengths(results.front(), lengths, true); - - // Output a nice header wrapping the column names. - auto separator = generateToken(lengths, columns); - auto header = separator + generateHeader(lengths, columns) + separator; - printf("%s", header.c_str()); - - // Iterate each row and pretty print. - for (const auto& row : results) { - printf("%s", generateRow(row, lengths, columns).c_str()); - } - printf("%s", separator.c_str()); -} - -void jsonPrint(const QueryData& q) { - printf("[\n"); - for (int i = 0; i < q.size(); ++i) { - std::string row_string; - if (serializeRowJSON(q[i], row_string).ok()) { - row_string.pop_back(); - printf(" %s", row_string.c_str()); - if (i < q.size() - 1) { - printf(",\n"); - } - } - } - printf("\n]\n"); -} - -void computeRowLengths(const Row& r, - std::map& lengths, - bool use_columns) { - for (const auto& col : r) { - size_t current = (lengths.count(col.first) > 0) ? lengths.at(col.first) : 0; - size_t size = - (use_columns) ? utf8StringSize(col.first) : utf8StringSize(col.second); - lengths[col.first] = (size > current) ? size : current; - } -} -} diff --git a/osquery/devtools/shell.cpp b/osquery/devtools/shell.cpp deleted file mode 100644 index 9b88fe0..0000000 --- a/osquery/devtools/shell.cpp +++ /dev/null @@ -1,1553 +0,0 @@ -/* -** 2001 September 15 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -************************************************************************* -** This file contains code to implement the "sqlite" command line -** utility for accessing SQLite databases. -*/ - -#include -#include -#include -#include - -#include - -#include -#include - -#include - -#include - -#include -#include -#include - -#include "osquery/devtools/devtools.h" -#include "osquery/sql/virtual_table.h" - -namespace osquery { - -/// Define flags used by the shell. They are parsed by the drop-in shell. -SHELL_FLAG(bool, csv, false, "Set output mode to 'csv'"); -SHELL_FLAG(bool, json, false, "Set output mode to 'json'"); -SHELL_FLAG(bool, line, false, "Set output mode to 'line'"); -SHELL_FLAG(bool, list, false, "Set output mode to 'list'"); -SHELL_FLAG(string, nullvalue, "", "Set string for NULL values, default ''"); -SHELL_FLAG(string, separator, "|", "Set output field separator, default '|'"); - -/// Define short-hand shell switches. -SHELL_FLAG(bool, L, false, "List all table names"); -SHELL_FLAG(string, A, "", "Select all from a table"); -} - -/* -** Text of a help message -*/ -static char zHelp[] = - "Welcome to the osquery shell. Please explore your OS!\n" - "You are connected to a transient 'in-memory' virtual database.\n" - "\n" - ".all [TABLE] Select all from a table\n" - ".bail ON|OFF Stop after hitting an error; default OFF\n" - ".echo ON|OFF Turn command echo on or off\n" - ".exit Exit this program\n" - ".header(s) ON|OFF Turn display of headers on or off\n" - ".help Show this message\n" - ".mode MODE Set output mode where MODE is one of:\n" - " csv Comma-separated values\n" - " column Left-aligned columns. (See .width)\n" - " line One value per line\n" - " list Values delimited by .separator string\n" - " pretty Pretty printed SQL results\n" - ".nullvalue STR Use STRING in place of NULL values\n" - ".print STR... Print literal STRING\n" - ".quit Exit this program\n" - ".schema [TABLE] Show the CREATE statements\n" - ".separator STR Change separator used by output mode and .import\n" - ".show Show the current values for various settings\n" - ".tables [TABLE] List names of tables\n" - ".trace FILE|off Output each SQL statement as it is run\n" - ".width [NUM1]+ Set column widths for \"column\" mode\n"; - -static char zTimerHelp[] = - ".timer ON|OFF Turn the CPU timer measurement on or off\n"; - -/* -** These are the allowed modes. -*/ -#define MODE_Line 0 /* One column per line. Blank line between records */ -#define MODE_Column 1 /* One record per line in neat columns */ -#define MODE_List 2 /* One record per line with a separator */ -#define MODE_Semi 3 /* Same as MODE_List but append ";" to each line */ -#define MODE_Csv 4 /* Quote strings, numbers are plain */ -#define MODE_Pretty 5 /* Pretty print the SQL results */ - -static const char *modeDescr[] = { - "line", "column", "list", "semi", "csv", "pretty", -}; - -/* Make sure isatty() has a prototype. -*/ -extern int isatty(int); - -/* ctype macros that work with signed characters */ -#define IsSpace(X) isspace((unsigned char)X) -#define IsDigit(X) isdigit((unsigned char)X) -#define ToLower(X) (char) tolower((unsigned char)X) - -/* True if the timer is enabled */ -static int enableTimer = 0; - -/* Return the current wall-clock time */ -static sqlite3_int64 timeOfDay(void) { - static sqlite3_vfs *clockVfs = 0; - sqlite3_int64 t; - if (clockVfs == 0) - clockVfs = sqlite3_vfs_find(0); - if (clockVfs->iVersion >= 1 && clockVfs->xCurrentTimeInt64 != 0) { - clockVfs->xCurrentTimeInt64(clockVfs, &t); - } else { - double r; - clockVfs->xCurrentTime(clockVfs, &r); - t = (sqlite3_int64)(r * 86400000.0); - } - return t; -} - -/* Saved resource information for the beginning of an operation */ -static struct rusage sBegin; /* CPU time at start */ -static sqlite3_int64 iBegin; /* Wall-clock time at start */ - -/* -** Begin timing an operation -*/ -static void beginTimer(void) { - if (enableTimer) { - getrusage(RUSAGE_SELF, &sBegin); - iBegin = timeOfDay(); - } -} - -/* Return the difference of two time_structs in seconds */ -static double timeDiff(struct timeval *pStart, struct timeval *pEnd) { - return (pEnd->tv_usec - pStart->tv_usec) * 0.000001 + - (double)(pEnd->tv_sec - pStart->tv_sec); -} - -/* -** Print the timing results. -*/ -static void endTimer(void) { - if (enableTimer) { - struct rusage sEnd; - sqlite3_int64 iEnd = timeOfDay(); - getrusage(RUSAGE_SELF, &sEnd); - printf("Run Time: real %.3f user %f sys %f\n", - (iEnd - iBegin) * 0.001, - timeDiff(&sBegin.ru_utime, &sEnd.ru_utime), - timeDiff(&sBegin.ru_stime, &sEnd.ru_stime)); - } -} - -#define BEGIN_TIMER beginTimer() -#define END_TIMER endTimer() -#define HAS_TIMER 1 - -/* -** Used to prevent warnings about unused parameters -*/ -#define UNUSED_PARAMETER(x) (void)(x) - -/* -** If the following flag is set, then command execution stops -** at an error if we are not interactive. -*/ -static int bail_on_error = 0; - -/* -** Threat stdin as an interactive input if the following variable -** is true. Otherwise, assume stdin is connected to a file or pipe. -*/ -static int stdin_is_interactive = 1; - -/* -** True if an interrupt (Control-C) has been received. -*/ -static volatile int seenInterrupt = 0; - -/* -** This is the name of our program. It is set in main(), used -** in a number of other places, mostly for error messages. -*/ -static char *Argv0; - -/* -** Prompt strings. Initialized in main. Settable with -** .prompt main continue -*/ -static char mainPrompt[20]; /* First line prompt. default: "sqlite> "*/ -static char continuePrompt[20]; /* Continuation prompt. default: " ...> " */ - -/* -** A global char* and an SQL function to access its current value -** from within an SQL statement. This program used to use the -** sqlite_exec_printf() API to substitue a string into an SQL statement. -** The correct way to do this with sqlite3 is to use the bind API, but -** since the shell is built around the callback paradigm it would be a lot -** of work. Instead just use this hack, which is quite harmless. -*/ -static const char *zShellStatic = 0; -static void shellstaticFunc(sqlite3_context *context, - int argc, - sqlite3_value **argv) { - assert(0 == argc); - assert(zShellStatic); - UNUSED_PARAMETER(argc); - UNUSED_PARAMETER(argv); - sqlite3_result_text(context, zShellStatic, -1, SQLITE_STATIC); -} - -/* -** This routine reads a line of text from FILE in, stores -** the text in memory obtained from malloc() and returns a pointer -** to the text. NULL is returned at end of file, or if malloc() -** fails. -** -** If zLine is not NULL then it is a malloced buffer returned from -** a previous call to this routine that may be reused. -*/ -static char *local_getline(char *zLine, FILE *in) { - int nLine = zLine == 0 ? 0 : 100; - int n = 0; - - while (1) { - if (n + 100 > nLine) { - nLine = nLine * 2 + 100; - zLine = (char *)realloc(zLine, nLine); - if (zLine == 0) - return 0; - } - if (fgets(&zLine[n], nLine - n, in) == 0) { - if (n == 0) { - free(zLine); - return 0; - } - zLine[n] = 0; - break; - } - while (zLine[n]) - n++; - if (n > 0 && zLine[n - 1] == '\n') { - n--; - if (n > 0 && zLine[n - 1] == '\r') - n--; - zLine[n] = 0; - break; - } - } - return zLine; -} - -/* -** Retrieve a single line of input text. -** -** If in==0 then read from standard input and prompt before each line. -** If isContinuation is true, then a continuation prompt is appropriate. -** If isContinuation is zero, then the main prompt should be used. -** -** If zPrior is not NULL then it is a buffer from a prior call to this -** routine that can be reused. -** -** The result is stored in space obtained from malloc() and must either -** be freed by the caller or else passed back into this routine via the -** zPrior argument for reuse. -*/ -static char *one_input_line(FILE *in, char *zPrior, int isContinuation) { - char *zPrompt; - char *zResult; - if (in != 0) { - zResult = local_getline(zPrior, in); - } else { - zPrompt = isContinuation ? continuePrompt : mainPrompt; - free(zPrior); - zResult = readline(zPrompt); - if (zResult && *zResult) - add_history(zResult); - } - return zResult; -} - -struct previous_mode_data { - int valid; /* Is there legit data in here? */ - int mode; - int showHeader; - int colWidth[100]; -}; - -/* -** Pretty print structure - */ -struct prettyprint_data { - osquery::QueryData results; - std::vector columns; - std::map lengths; -}; - -/* -** An pointer to an instance of this structure is passed from -** the main program to the callback. This is used to communicate -** state and mode information. -*/ -struct callback_data { - int echoOn; /* True to echo input commands */ - int autoEQP; /* Run EXPLAIN QUERY PLAN prior to seach SQL statement */ - int cnt; /* Number of records displayed so far */ - FILE *out; /* Write results here */ - FILE *traceOut; /* Output for sqlite3_trace() */ - int nErr; /* Number of errors seen */ - int mode; /* An output mode setting */ - int writableSchema; /* True if PRAGMA writable_schema=ON */ - int showHeader; /* True to show column names in List or Column mode */ - char *zDestTable; /* Name of destination table when MODE_Insert */ - char separator[20]; /* Separator character for MODE_List */ - int colWidth[100]; /* Requested width of each column when in column mode*/ - int actualWidth[100]; /* Actual width of each column */ - char nullvalue[20]; /* The text to print when a NULL comes back from - ** the database */ - struct previous_mode_data explainPrev; - /* Holds the mode information just before - ** .explain ON */ - char outfile[FILENAME_MAX]; /* Filename for *out */ - const char *zDbFilename; /* name of the database file */ - char *zFreeOnClose; /* Filename to free when closing */ - const char *zVfs; /* Name of VFS to use */ - sqlite3_stmt *pStmt; /* Current statement if any. */ - FILE *pLog; /* Write log output here */ - int *aiIndent; /* Array of indents used in MODE_Explain */ - int nIndent; /* Size of array aiIndent[] */ - int iIndent; /* Index of current op in aiIndent[] */ - - /* Additional attributes to be used in pretty mode */ - struct prettyprint_data *prettyPrint; -}; - -/* -** Number of elements in an array -*/ -#define ArraySize(X) (int)(sizeof(X) / sizeof(X[0])) - -/* -** Compute a string length that is limited to what can be stored in -** lower 30 bits of a 32-bit signed integer. -*/ -static int strlen30(const char *z) { - const char *z2 = z; - while (*z2) { - z2++; - } - return 0x3fffffff & (int)(z2 - z); -} - -/* -** A callback for the sqlite3_log() interface. -*/ -static void shellLog(void *pArg, int iErrCode, const char *zMsg) { - struct callback_data *p = (struct callback_data *)pArg; - if (p->pLog == 0) - return; - fprintf(p->pLog, "(%d) %s\n", iErrCode, zMsg); - fflush(p->pLog); -} - -/* -** Output the given string as a quoted according to C or TCL quoting rules. -*/ -static void output_c_string(FILE *out, const char *z) { - unsigned int c; - fputc('"', out); - while ((c = *(z++)) != 0) { - if (c == '\\') { - fputc(c, out); - fputc(c, out); - } else if (c == '"') { - fputc('\\', out); - fputc('"', out); - } else if (c == '\t') { - fputc('\\', out); - fputc('t', out); - } else if (c == '\n') { - fputc('\\', out); - fputc('n', out); - } else if (c == '\r') { - fputc('\\', out); - fputc('r', out); - } else if (!isprint(c & 0xff)) { - fprintf(out, "\\%03o", c & 0xff); - } else { - fputc(c, out); - } - } - fputc('"', out); -} - -/* -** If a field contains any character identified by a 1 in the following -** array, then the string must be quoted for CSV. -*/ -// clang-format off -static const char needCsvQuote[] = { - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, -}; -// clang-format on - -/* -** Output a single term of CSV. Actually, p->separator is used for -** the separator, which may or may not be a comma. p->nullvalue is -** the null value. Strings are quoted if necessary. -*/ -static void output_csv(struct callback_data *p, const char *z, int bSep) { - FILE *out = p->out; - if (z == 0) { - fprintf(out, "%s", p->nullvalue); - } else { - int i; - int nSep = strlen30(p->separator); - for (i = 0; z[i]; i++) { - if (needCsvQuote[((unsigned char *)z)[i]] || - (z[i] == p->separator[0] && - (nSep == 1 || memcmp(z, p->separator, nSep) == 0))) { - i = 0; - break; - } - } - if (i == 0) { - putc('"', out); - for (i = 0; z[i]; i++) { - if (z[i] == '"') - putc('"', out); - putc(z[i], out); - } - putc('"', out); - } else { - fprintf(out, "%s", z); - } - } - if (bSep) { - fprintf(p->out, "%s", p->separator); - } -} - -#ifdef SIGINT -/* -** This routine runs when the user presses Ctrl-C -*/ -static void interrupt_handler(int NotUsed) { - UNUSED_PARAMETER(NotUsed); - seenInterrupt = 1; -} -#endif - -/* -** This is the callback routine that the shell -** invokes for each row of a query result. -*/ -static int shell_callback( - void *pArg, int nArg, char **azArg, char **azCol, int *aiType) { - int i; - struct callback_data *p = (struct callback_data *)pArg; - - switch (p->mode) { - case MODE_Pretty: { - if (p->prettyPrint->columns.size() == 0) { - for (i = 0; i < nArg; i++) { - p->prettyPrint->columns.push_back(std::string(azCol[i])); - } - } - - osquery::Row r; - for (int i = 0; i < nArg; ++i) { - if (azCol[i] != nullptr && azArg[i] != nullptr) { - r[std::string(azCol[i])] = std::string(azArg[i]); - } - } - osquery::computeRowLengths(r, p->prettyPrint->lengths); - p->prettyPrint->results.push_back(r); - break; - } - case MODE_Line: { - int w = 5; - if (azArg == 0) - break; - for (i = 0; i < nArg; i++) { - int len = strlen30(azCol[i] ? azCol[i] : ""); - if (len > w) - w = len; - } - if (p->cnt++ > 0) - fprintf(p->out, "\n"); - for (i = 0; i < nArg; i++) { - fprintf(p->out, - "%*s = %s\n", - w, - azCol[i], - azArg[i] ? azArg[i] : p->nullvalue); - } - break; - } - case MODE_Column: { - if (p->cnt++ == 0) { - for (i = 0; i < nArg; i++) { - int w, n; - if (i < ArraySize(p->colWidth)) { - w = p->colWidth[i]; - } else { - w = 0; - } - if (w == 0) { - w = strlen30(azCol[i] ? azCol[i] : ""); - if (w < 10) - w = 10; - n = strlen30(azArg && azArg[i] ? azArg[i] : p->nullvalue); - if (w < n) - w = n; - } - if (i < ArraySize(p->actualWidth)) { - p->actualWidth[i] = w; - } - if (p->showHeader) { - if (w < 0) { - fprintf(p->out, - "%*.*s%s", - -w, - -w, - azCol[i], - i == nArg - 1 ? "\n" : " "); - } else { - fprintf(p->out, - "%-*.*s%s", - w, - w, - azCol[i], - i == nArg - 1 ? "\n" : " "); - } - } - } - if (p->showHeader) { - for (i = 0; i < nArg; i++) { - int w; - if (i < ArraySize(p->actualWidth)) { - w = p->actualWidth[i]; - if (w < 0) - w = -w; - } else { - w = 10; - } - fprintf(p->out, - "%-*.*s%s", - w, - w, - "-----------------------------------" - "----------------------------------------------------------", - i == nArg - 1 ? "\n" : " "); - } - } - } - if (azArg == 0) - break; - for (i = 0; i < nArg; i++) { - int w; - if (i < ArraySize(p->actualWidth)) { - w = p->actualWidth[i]; - } else { - w = 10; - } - if (i == 1 && p->aiIndent && p->pStmt) { - if (p->iIndent < p->nIndent) { - fprintf(p->out, "%*.s", p->aiIndent[p->iIndent], ""); - } - p->iIndent++; - } - if (w < 0) { - fprintf(p->out, - "%*.*s%s", - -w, - -w, - azArg[i] ? azArg[i] : p->nullvalue, - i == nArg - 1 ? "\n" : " "); - } else { - fprintf(p->out, - "%-*.*s%s", - w, - w, - azArg[i] ? azArg[i] : p->nullvalue, - i == nArg - 1 ? "\n" : " "); - } - } - break; - } - case MODE_Semi: - case MODE_List: { - if (p->cnt++ == 0 && p->showHeader) { - for (i = 0; i < nArg; i++) { - fprintf(p->out, "%s%s", azCol[i], i == nArg - 1 ? "\n" : p->separator); - } - } - if (azArg == 0) - break; - for (i = 0; i < nArg; i++) { - char *z = azArg[i]; - if (z == 0) - z = p->nullvalue; - fprintf(p->out, "%s", z); - if (i < nArg - 1) { - fprintf(p->out, "%s", p->separator); - } else if (p->mode == MODE_Semi) { - fprintf(p->out, ";\n"); - } else { - fprintf(p->out, "\n"); - } - } - break; - } - case MODE_Csv: { - if (p->cnt++ == 0 && p->showHeader) { - for (i = 0; i < nArg; i++) { - output_csv(p, azCol[i] ? azCol[i] : "", i < nArg - 1); - } - fprintf(p->out, "\n"); - } - if (azArg == 0) - break; - for (i = 0; i < nArg; i++) { - output_csv(p, azArg[i], i < nArg - 1); - } - fprintf(p->out, "\n"); - break; - } - } - return 0; -} - -/* -** Set the destination table field of the callback_data structure to -** the name of the table given. Escape any quote characters in the -** table name. -*/ -static void set_table_name(struct callback_data *p, const char *zName) { - int i, n; - int needQuote; - char *z; - - if (p->zDestTable) { - free(p->zDestTable); - p->zDestTable = 0; - } - if (zName == 0) - return; - needQuote = !isalpha((unsigned char)*zName) && *zName != '_'; - for (i = n = 0; zName[i]; i++, n++) { - if (!isalnum((unsigned char)zName[i]) && zName[i] != '_') { - needQuote = 1; - if (zName[i] == '\'') - n++; - } - } - if (needQuote) - n += 2; - z = p->zDestTable = (char *)malloc(n + 1); - if (z == 0) { - fprintf(stderr, "Error: out of memory\n"); - exit(1); - } - n = 0; - if (needQuote) - z[n++] = '\''; - for (i = 0; zName[i]; i++) { - z[n++] = zName[i]; - if (zName[i] == '\'') - z[n++] = '\''; - } - if (needQuote) - z[n++] = '\''; - z[n] = 0; -} - -/* -** Allocate space and save off current error string. -*/ -static char *save_err_msg(sqlite3 *db /* Database to query */ - ) { - int nErrMsg = 1 + strlen30(sqlite3_errmsg(db)); - char *zErrMsg = (char *)sqlite3_malloc(nErrMsg); - if (zErrMsg) { - memcpy(zErrMsg, sqlite3_errmsg(db), nErrMsg); - } - return zErrMsg; -} - -/* -** Execute a statement or set of statements. Print -** any result rows/columns depending on the current mode -** set via the supplied callback. -** -** This is very similar to SQLite's built-in sqlite3_exec() -** function except it takes a slightly different callback -** and callback data argument. -*/ -static int shell_exec( - const char *zSql, /* SQL to be evaluated */ - int (*xCallback)( - void *, int, char **, char **, int *), /* Callback function */ - /* (not the same as sqlite3_exec) */ - struct callback_data *pArg, /* Pointer to struct callback_data */ - char **pzErrMsg /* Error msg written here */ - ) { - // Grab a lock on the managed DB instance. - auto dbc = osquery::SQLiteDBManager::get(); - auto db = dbc.db(); - - sqlite3_stmt *pStmt = nullptr; /* Statement to execute. */ - int rc = SQLITE_OK; /* Return Code */ - int rc2; - const char *zLeftover; /* Tail of unprocessed SQL */ - - if (pzErrMsg) { - *pzErrMsg = nullptr; - } - - while (zSql[0] && (SQLITE_OK == rc)) { - rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, &zLeftover); - if (SQLITE_OK != rc) { - if (pzErrMsg) { - *pzErrMsg = save_err_msg(db); - } - } else { - if (!pStmt) { - /* this happens for a comment or white-space */ - zSql = zLeftover; - while (IsSpace(zSql[0])) - zSql++; - continue; - } - - /* save off the prepared statment handle and reset row count */ - if (pArg) { - pArg->pStmt = pStmt; - pArg->cnt = 0; - } - - /* echo the sql statement if echo on */ - if (pArg && pArg->echoOn) { - const char *zStmtSql = sqlite3_sql(pStmt); - fprintf(pArg->out, "%s\n", zStmtSql ? zStmtSql : zSql); - } - - /* perform the first step. this will tell us if we - ** have a result set or not and how wide it is. - */ - rc = sqlite3_step(pStmt); - /* if we have a result set... */ - if (SQLITE_ROW == rc) { - /* if we have a callback... */ - if (xCallback) { - /* allocate space for col name ptr, value ptr, and type */ - int nCol = sqlite3_column_count(pStmt); - void *pData = sqlite3_malloc(3 * nCol * sizeof(const char *) + 1); - if (!pData) { - rc = SQLITE_NOMEM; - } else { - char **azCols = (char **)pData; /* Names of result columns */ - char **azVals = &azCols[nCol]; /* Results */ - int *aiTypes = (int *)&azVals[nCol]; /* Result types */ - int i, x; - assert(sizeof(int) <= sizeof(char *)); - /* save off ptrs to column names */ - for (i = 0; i < nCol; i++) { - azCols[i] = (char *)sqlite3_column_name(pStmt, i); - } - do { - /* extract the data and data types */ - for (i = 0; i < nCol; i++) { - aiTypes[i] = x = sqlite3_column_type(pStmt, i); - azVals[i] = (char *)sqlite3_column_text(pStmt, i); - if (!azVals[i] && (aiTypes[i] != SQLITE_NULL)) { - rc = SQLITE_NOMEM; - break; /* from for */ - } - } /* end for */ - - /* if data and types extracted successfully... */ - if (SQLITE_ROW == rc) { - /* call the supplied callback with the result row data */ - if (xCallback(pArg, nCol, azVals, azCols, aiTypes)) { - rc = SQLITE_ABORT; - } else { - rc = sqlite3_step(pStmt); - } - } - } while (SQLITE_ROW == rc); - sqlite3_free(pData); - } - } else { - do { - rc = sqlite3_step(pStmt); - } while (rc == SQLITE_ROW); - } - } - - /* Finalize the statement just executed. If this fails, save a - ** copy of the error message. Otherwise, set zSql to point to the - ** next statement to execute. */ - rc2 = sqlite3_finalize(pStmt); - if (rc != SQLITE_NOMEM) - rc = rc2; - if (rc == SQLITE_OK) { - zSql = zLeftover; - while (IsSpace(zSql[0])) - zSql++; - } else if (pzErrMsg) { - *pzErrMsg = save_err_msg(db); - } - - /* clear saved stmt handle */ - if (pArg) { - pArg->pStmt = nullptr; - } - } - } /* end while */ - - if (pArg && pArg->mode == MODE_Pretty) { - if (osquery::FLAGS_json) { - osquery::jsonPrint(pArg->prettyPrint->results); - } else { - osquery::prettyPrint(pArg->prettyPrint->results, - pArg->prettyPrint->columns, - pArg->prettyPrint->lengths); - } - pArg->prettyPrint->results.clear(); - pArg->prettyPrint->columns.clear(); - pArg->prettyPrint->lengths.clear(); - } - - return rc; -} - -/* Forward reference */ -static int process_input(struct callback_data *p, FILE *in); - -/* -** Do C-language style dequoting. -** -** \t -> tab -** \n -> newline -** \r -> carriage return -** \" -> " -** \NNN -> ascii character NNN in octal -** \\ -> backslash -*/ -static void resolve_backslashes(char *z) { - int i, j; - char c; - for (i = j = 0; (c = z[i]) != 0; i++, j++) { - if (c == '\\') { - c = z[++i]; - if (c == 'n') { - c = '\n'; - } else if (c == 't') { - c = '\t'; - } else if (c == 'r') { - c = '\r'; - } else if (c == '\\') { - c = '\\'; - } else if (c >= '0' && c <= '7') { - c -= '0'; - if (z[i + 1] >= '0' && z[i + 1] <= '7') { - i++; - c = (c << 3) + z[i] - '0'; - if (z[i + 1] >= '0' && z[i + 1] <= '7') { - i++; - c = (c << 3) + z[i] - '0'; - } - } - } - } - z[j] = c; - } - z[j] = 0; -} - -/* -** Return the value of a hexadecimal digit. Return -1 if the input -** is not a hex digit. -*/ -static int hexDigitValue(char c) { - if (c >= '0' && c <= '9') - return c - '0'; - if (c >= 'a' && c <= 'f') - return c - 'a' + 10; - if (c >= 'A' && c <= 'F') - return c - 'A' + 10; - return -1; -} - -/* -** Interpret zArg as an integer value, possibly with suffixes. -*/ -static sqlite3_int64 integerValue(const char *zArg) { - sqlite3_int64 v = 0; - static const struct { - char *zSuffix; - int iMult; - } aMult[] = { - {(char *)"KiB", 1024}, - {(char *)"MiB", 1024 * 1024}, - {(char *)"GiB", 1024 * 1024 * 1024}, - {(char *)"KB", 1000}, - {(char *)"MB", 1000000}, - {(char *)"GB", 1000000000}, - {(char *)"K", 1000}, - {(char *)"M", 1000000}, - {(char *)"G", 1000000000}, - }; - int i; - int isNeg = 0; - if (zArg[0] == '-') { - isNeg = 1; - zArg++; - } else if (zArg[0] == '+') { - zArg++; - } - if (zArg[0] == '0' && zArg[1] == 'x') { - int x; - zArg += 2; - while ((x = hexDigitValue(zArg[0])) >= 0) { - v = (v << 4) + x; - zArg++; - } - } else { - while (IsDigit(zArg[0])) { - v = v * 10 + zArg[0] - '0'; - zArg++; - } - } - for (i = 0; i < ArraySize(aMult); i++) { - if (sqlite3_stricmp(aMult[i].zSuffix, zArg) == 0) { - v *= aMult[i].iMult; - break; - } - } - return isNeg ? -v : v; -} - -/* -** Interpret zArg as either an integer or a boolean value. Return 1 or 0 -** for TRUE and FALSE. Return the integer value if appropriate. -*/ -static int booleanValue(char *zArg) { - int i; - if (zArg[0] == '0' && zArg[1] == 'x') { - for (i = 2; hexDigitValue(zArg[i]) >= 0; i++) { - } - } else { - for (i = 0; zArg[i] >= '0' && zArg[i] <= '9'; i++) { - } - } - if (i > 0 && zArg[i] == 0) - return (int)(integerValue(zArg) & 0xffffffff); - if (sqlite3_stricmp(zArg, "on") == 0 || sqlite3_stricmp(zArg, "yes") == 0) { - return 1; - } - if (sqlite3_stricmp(zArg, "off") == 0 || sqlite3_stricmp(zArg, "no") == 0) { - return 0; - } - fprintf( - stderr, "ERROR: Not a boolean value: \"%s\". Assuming \"no\".\n", zArg); - return 0; -} - -/* -** Close an output file, assuming it is not stderr or stdout -*/ -static void output_file_close(FILE *f) { - if (f && f != stdout && f != stderr) - fclose(f); -} - -/* -** Try to open an output file. The names "stdout" and "stderr" are -** recognized and do the right thing. NULL is returned if the output -** filename is "off". -*/ -static FILE *output_file_open(const char *zFile) { - FILE *f; - if (strcmp(zFile, "stdout") == 0) { - f = stdout; - } else if (strcmp(zFile, "stderr") == 0) { - f = stderr; - } else if (strcmp(zFile, "off") == 0) { - f = 0; - } else { - f = fopen(zFile, "wb"); - if (f == 0) { - fprintf(stderr, "Error: cannot open \"%s\"\n", zFile); - } - } - return f; -} - -inline void meta_tables(int nArg, char **azArg) { - auto tables = osquery::Registry::names("table"); - std::sort(tables.begin(), tables.end()); - for (const auto &table_name : tables) { - if (nArg == 1 || table_name.find(azArg[1]) == 0) { - printf(" => %s\n", table_name.c_str()); - } - } -} - -inline void meta_schema(int nArg, char **azArg) { - for (const auto &table_name : osquery::Registry::names("table")) { - if (nArg > 1 && table_name.find(azArg[1]) != 0) { - continue; - } - - osquery::PluginRequest request = {{"action", "columns"}}; - osquery::PluginResponse response; - - osquery::Registry::call("table", table_name, request, response); - std::vector columns; - for (const auto &column : response) { - columns.push_back(column.at("name") + " " + column.at("type")); - } - - printf("CREATE TABLE %s(%s);\n", - table_name.c_str(), - osquery::join(columns, ", ").c_str()); - } -} - -/* -** If an input line begins with "." then invoke this routine to -** process that line. -** -** Return 1 on error, 2 to exit, and 0 otherwise. -*/ -static int do_meta_command(char *zLine, struct callback_data *p) { - int i = 1; - int nArg = 0; - int n, c; - int rc = 0; - char *azArg[50]; - - // A meta command may act on the database, grab a lock and instance. - auto dbc = osquery::SQLiteDBManager::get(); - auto db = dbc.db(); - - /* Parse the input line into tokens. - */ - while (zLine[i] && nArg < ArraySize(azArg)) { - while (IsSpace(zLine[i])) { - i++; - } - if (zLine[i] == 0) - break; - if (zLine[i] == '\'' || zLine[i] == '"') { - int delim = zLine[i++]; - azArg[nArg++] = &zLine[i]; - while (zLine[i] && zLine[i] != delim) { - if (zLine[i] == '\\' && delim == '"' && zLine[i + 1] != 0) - i++; - i++; - } - if (zLine[i] == delim) { - zLine[i++] = 0; - } - if (delim == '"') - resolve_backslashes(azArg[nArg - 1]); - } else { - azArg[nArg++] = &zLine[i]; - while (zLine[i] && !IsSpace(zLine[i])) { - i++; - } - if (zLine[i]) - zLine[i++] = 0; - resolve_backslashes(azArg[nArg - 1]); - } - } - - /* Process the input line. - */ - if (nArg == 0) - return 0; /* no tokens, no error */ - n = strlen30(azArg[0]); - c = azArg[0][0]; - if (c == 'a' && strncmp(azArg[0], "all", n) == 0 && nArg == 2) { - struct callback_data data; - memcpy(&data, p, sizeof(data)); - auto query = std::string("SELECT * FROM ") + azArg[1]; - rc = shell_exec(query.c_str(), shell_callback, &data, nullptr); - if (rc != SQLITE_OK) { - fprintf(stderr, "Error querying table: %s\n", azArg[1]); - } - } else if (c == 'b' && n >= 3 && strncmp(azArg[0], "bail", n) == 0 && - nArg > 1 && nArg < 3) { - bail_on_error = booleanValue(azArg[1]); - } else if (c == 'e' && strncmp(azArg[0], "echo", n) == 0 && nArg > 1 && - nArg < 3) { - p->echoOn = booleanValue(azArg[1]); - } else if (c == 'e' && strncmp(azArg[0], "exit", n) == 0) { - if (nArg > 1 && (rc = (int)integerValue(azArg[1])) != 0) - exit(rc); - rc = 2; - } else if (c == 'h' && (strncmp(azArg[0], "header", n) == 0 || - strncmp(azArg[0], "headers", n) == 0) && - nArg > 1 && nArg < 3) { - p->showHeader = booleanValue(azArg[1]); - } else if (c == 'h' && strncmp(azArg[0], "help", n) == 0) { - fprintf(stderr, "%s", zHelp); - if (HAS_TIMER) { - fprintf(stderr, "%s", zTimerHelp); - } - } else if (c == 'l' && strncmp(azArg[0], "log", n) == 0 && nArg >= 2) { - const char *zFile = azArg[1]; - output_file_close(p->pLog); - p->pLog = output_file_open(zFile); - } else if (c == 'm' && strncmp(azArg[0], "mode", n) == 0 && nArg == 2) { - int n2 = strlen30(azArg[1]); - if ((n2 == 4 && strncmp(azArg[1], "line", n2) == 0) || - (n2 == 5 && strncmp(azArg[1], "lines", n2) == 0)) { - p->mode = MODE_Line; - } else if ((n2 == 6 && strncmp(azArg[1], "column", n2) == 0) || - (n2 == 7 && strncmp(azArg[1], "columns", n2) == 0)) { - p->mode = MODE_Column; - } else if ((n2 == 6 && strncmp(azArg[1], "column", n2) == 0) || - (n2 == 7 && strncmp(azArg[1], "columns", n2) == 0)) { - p->mode = MODE_Column; - } else if (n2 == 4 && strncmp(azArg[1], "list", n2) == 0) { - p->mode = MODE_List; - } else if (n2 == 6 && strncmp(azArg[1], "pretty", n2) == 0) { - p->mode = MODE_Pretty; - } else if (n2 == 3 && strncmp(azArg[1], "csv", n2) == 0) { - p->mode = MODE_Csv; - sqlite3_snprintf(sizeof(p->separator), p->separator, ","); - } else { - fprintf(stderr, - "Error: mode should be one of: " - "column csv html insert line list tabs tcl pretty\n"); - rc = 1; - } - } else if (c == 'n' && strncmp(azArg[0], "nullvalue", n) == 0 && nArg == 2) { - sqlite3_snprintf(sizeof(p->nullvalue), - p->nullvalue, - "%.*s", - (int)ArraySize(p->nullvalue) - 1, - azArg[1]); - } else if (c == 'p' && n >= 3 && strncmp(azArg[0], "print", n) == 0) { - int i; - for (i = 1; i < nArg; i++) { - if (i > 1) - fprintf(p->out, " "); - fprintf(p->out, "%s", azArg[i]); - } - fprintf(p->out, "\n"); - } else if (c == 'q' && strncmp(azArg[0], "quit", n) == 0 && nArg == 1) { - rc = 2; - } else if (c == 's' && strncmp(azArg[0], "schema", n) == 0 && nArg < 3) { - meta_schema(nArg, azArg); - } else if (c == 's' && strncmp(azArg[0], "separator", n) == 0 && nArg == 2) { - sqlite3_snprintf(sizeof(p->separator), - p->separator, - "%.*s", - (int)sizeof(p->separator) - 1, - azArg[1]); - } else if (c == 's' && strncmp(azArg[0], "show", n) == 0 && nArg == 1) { - int i; - fprintf(p->out, "%9.9s: %s\n", "echo", p->echoOn ? "on" : "off"); - fprintf(p->out, "%9.9s: %s\n", "headers", p->showHeader ? "on" : "off"); - fprintf(p->out, "%9.9s: %s\n", "mode", modeDescr[p->mode]); - fprintf(p->out, "%9.9s: ", "nullvalue"); - output_c_string(p->out, p->nullvalue); - fprintf(p->out, "\n"); - fprintf(p->out, - "%9.9s: %s\n", - "output", - strlen30(p->outfile) ? p->outfile : "stdout"); - fprintf(p->out, "%9.9s: ", "separator"); - output_c_string(p->out, p->separator); - fprintf(p->out, "\n"); - fprintf(p->out, "%9.9s: ", "width"); - for (i = 0; i < (int)ArraySize(p->colWidth) && p->colWidth[i] != 0; i++) { - fprintf(p->out, "%d ", p->colWidth[i]); - } - fprintf(p->out, "\n"); - } else if (c == 't' && n > 1 && strncmp(azArg[0], "tables", n) == 0 && - nArg < 3) { - meta_tables(nArg, azArg); - } else if (c == 't' && n > 4 && strncmp(azArg[0], "timeout", n) == 0 && - nArg == 2) { - sqlite3_busy_timeout(db, (int)integerValue(azArg[1])); - } else if (HAS_TIMER && c == 't' && n >= 5 && - strncmp(azArg[0], "timer", n) == 0 && nArg == 2) { - enableTimer = booleanValue(azArg[1]); - } else if (c == 't' && strncmp(azArg[0], "trace", n) == 0 && nArg > 1) { - output_file_close(p->traceOut); - p->traceOut = output_file_open(azArg[1]); - } else if (c == 'v' && strncmp(azArg[0], "version", n) == 0) { - fprintf(p->out, "osquery %s\n", osquery::kVersion.c_str()); - fprintf(p->out, "using SQLite %s\n", sqlite3_libversion()); - } else if (c == 'w' && strncmp(azArg[0], "width", n) == 0 && nArg > 1) { - int j; - assert(nArg <= ArraySize(azArg)); - for (j = 1; j < nArg && j < ArraySize(p->colWidth); j++) { - p->colWidth[j - 1] = (int)integerValue(azArg[j]); - } - } else { - fprintf(stderr, - "Error: unknown command or invalid arguments: " - " \"%s\". Enter \".help\" for help\n", - azArg[0]); - rc = 1; - } - - return rc; -} - -/* -** Return TRUE if a semicolon occurs anywhere in the first N characters -** of string z[]. -*/ -static int line_contains_semicolon(const char *z, int N) { - int i; - if (z == nullptr) { - return 0; - } - - for (i = 0; i < N; i++) { - if (z[i] == ';') - return 1; - } - return 0; -} - -/* -** Test to see if a line consists entirely of whitespace. -*/ -static int _all_whitespace(const char *z) { - for (; *z; z++) { - if (IsSpace(z[0])) - continue; - if (*z == '/' && z[1] == '*') { - z += 2; - while (*z && (*z != '*' || z[1] != '/')) { - z++; - } - if (*z == 0) - return 0; - z++; - continue; - } - if (*z == '-' && z[1] == '-') { - z += 2; - while (*z && *z != '\n') { - z++; - } - if (*z == 0) - return 1; - continue; - } - return 0; - } - return 1; -} - -/* -** Return TRUE if the line typed in is an SQL command terminator other -** than a semi-colon. The SQL Server style "go" command is understood -** as is the Oracle "/". -*/ -static int line_is_command_terminator(const char *zLine) { - while (IsSpace(zLine[0])) { - zLine++; - }; - if (zLine[0] == '/' && _all_whitespace(&zLine[1])) { - return 1; /* Oracle */ - } - if (ToLower(zLine[0]) == 'g' && ToLower(zLine[1]) == 'o' && - _all_whitespace(&zLine[2])) { - return 1; /* SQL Server */ - } - return 0; -} - -/* -** Return true if zSql is a complete SQL statement. Return false if it -** ends in the middle of a string literal or C-style comment. -*/ -static int line_is_complete(char *zSql, int nSql) { - int rc; - if (zSql == 0) - return 1; - zSql[nSql] = ';'; - zSql[nSql + 1] = 0; - rc = sqlite3_complete(zSql); - zSql[nSql] = 0; - return rc; -} - -/* -** Read input from *in and process it. If *in==0 then input -** is interactive - the user is typing it it. Otherwise, input -** is coming from a file or device. A prompt is issued and history -** is saved only if input is interactive. An interrupt signal will -** cause this routine to exit immediately, unless input is interactive. -** -** Return the number of errors. -*/ -static int process_input(struct callback_data *p, FILE *in) { - char *zLine = 0; /* A single input line */ - char *zSql = 0; /* Accumulated SQL text */ - int nLine; /* Length of current line */ - int nSql = 0; /* Bytes of zSql[] used */ - int nAlloc = 0; /* Allocated zSql[] space */ - int nSqlPrior = 0; /* Bytes of zSql[] used by prior line */ - char *zErrMsg; /* Error message returned */ - int rc; /* Error code */ - int errCnt = 0; /* Number of errors seen */ - int lineno = 0; /* Current line number */ - int startline = 0; /* Line number for start of current input */ - - while (errCnt == 0 || !bail_on_error || (in == 0 && stdin_is_interactive)) { - fflush(p->out); - zLine = one_input_line(in, zLine, nSql > 0); - if (zLine == 0) { - /* End of input */ - if (stdin_is_interactive) - printf("\n"); - break; - } - if (seenInterrupt) { - if (in != 0) - break; - seenInterrupt = 0; - } - lineno++; - if (nSql == 0 && _all_whitespace(zLine)) { - if (p->echoOn) - printf("%s\n", zLine); - continue; - } - if (zLine && zLine[0] == '.' && nSql == 0) { - if (p->echoOn) - printf("%s\n", zLine); - rc = do_meta_command(zLine, p); - if (rc == 2) { /* exit requested */ - break; - } else if (rc) { - errCnt++; - } - continue; - } - if (line_is_command_terminator(zLine) && line_is_complete(zSql, nSql)) { - memcpy(zLine, ";", 2); - } - nLine = strlen30(zLine); - if (nSql + nLine + 2 >= nAlloc) { - nAlloc = nSql + nLine + 100; - zSql = (char *)realloc(zSql, nAlloc); - if (zSql == 0) { - fprintf(stderr, "Error: out of memory\n"); - exit(1); - } - } - nSqlPrior = nSql; - if (nSql == 0) { - int i; - for (i = 0; zLine[i] && IsSpace(zLine[i]); i++) { - } - assert(nAlloc > 0 && zSql != nullptr); - if (zSql != nullptr) { - memcpy(zSql, zLine + i, nLine + 1 - i); - } - startline = lineno; - nSql = nLine - i; - } else { - zSql[nSql++] = '\n'; - memcpy(zSql + nSql, zLine, nLine + 1); - nSql += nLine; - } - if (nSql && line_contains_semicolon(&zSql[nSqlPrior], nSql - nSqlPrior) && - sqlite3_complete(zSql)) { - p->cnt = 0; - BEGIN_TIMER; - rc = shell_exec(zSql, shell_callback, p, &zErrMsg); - END_TIMER; - if (rc || zErrMsg) { - char zPrefix[100]; - if (in != 0 || !stdin_is_interactive) { - sqlite3_snprintf( - sizeof(zPrefix), zPrefix, "Error: near line %d:", startline); - } else { - sqlite3_snprintf(sizeof(zPrefix), zPrefix, "Error:"); - } - if (zErrMsg != 0) { - fprintf(stderr, "%s %s\n", zPrefix, zErrMsg); - sqlite3_free(zErrMsg); - zErrMsg = 0; - } - errCnt++; - } - nSql = 0; - } else if (nSql && _all_whitespace(zSql)) { - if (p->echoOn) - printf("%s\n", zSql); - nSql = 0; - } - } - if (nSql) { - if (!_all_whitespace(zSql)) { - fprintf(stderr, "Error: incomplete SQL: %s\n", zSql); - } - free(zSql); - } - free(zLine); - return errCnt > 0; -} - -/* -** Initialize the state information in data -*/ -static void main_init(struct callback_data *data) { - memset(data, 0, sizeof(*data)); - data->prettyPrint = new struct prettyprint_data(); - data->mode = MODE_Pretty; - memcpy(data->separator, "|", 2); - data->showHeader = 1; - sqlite3_config(SQLITE_CONFIG_URI, 1); - sqlite3_config(SQLITE_CONFIG_LOG, shellLog, data); - sqlite3_snprintf(sizeof(mainPrompt), mainPrompt, "osquery> "); - sqlite3_snprintf(sizeof(continuePrompt), continuePrompt, " ...> "); - sqlite3_config(SQLITE_CONFIG_SINGLETHREAD); -} - -/* -** Output text to the console in a font that attracts extra attention. -*/ -static void printBold(const char *zText) { printf("\033[1m%s\033[0m", zText); } - -namespace osquery { - -int launchIntoShell(int argc, char **argv) { - struct callback_data data; - main_init(&data); - - { - // Hold the manager connection instance again in callbacks. - auto dbc = SQLiteDBManager::get(); - // Add some shell-specific functions to the instance. - sqlite3_create_function( - dbc.db(), "shellstatic", 0, SQLITE_UTF8, 0, shellstaticFunc, 0, 0); - } - - Argv0 = argv[0]; - stdin_is_interactive = isatty(0); - - // SQLite: Make sure we have a valid signal handler early - signal(SIGINT, interrupt_handler); - - int warnInmemoryDb = 1; - data.zDbFilename = ":memory:"; - data.out = stdout; - - // Set modes and settings from CLI flags. - if (FLAGS_list) { - data.mode = MODE_List; - } else if (FLAGS_line) { - data.mode = MODE_Line; - } else if (FLAGS_csv) { - data.mode = MODE_Csv; - memcpy(data.separator, ",", 2); - } else { - data.mode = MODE_Pretty; - } - - sqlite3_snprintf(sizeof(data.separator), data.separator, "%s", - FLAGS_separator.c_str()); - sqlite3_snprintf(sizeof(data.nullvalue), data.nullvalue, "%s", - FLAGS_nullvalue.c_str()); - - int rc = 0; - if (FLAGS_L == true || FLAGS_A.size() > 0) { - // Helper meta commands from shell switches. - std::string query = (FLAGS_L) ? ".tables" : ".all " + FLAGS_A; - char *cmd = new char[query.size() + 1]; - memset(cmd, 0, query.size() + 1); - std::copy(query.begin(), query.end(), cmd); - rc = do_meta_command(cmd, &data); - } else if (argc > 1 && argv[1] != nullptr) { - // Run a command or statement from CLI - char *query = argv[1]; - char *error = 0; - if (query[0] == '.') { - rc = do_meta_command(query, &data); - rc = (rc == 2) ? 0 : rc; - } else { - rc = shell_exec(query, shell_callback, &data, &error); - if (error != 0) { - fprintf(stderr, "Error: %s\n", error); - return (rc != 0) ? rc : 1; - } else if (rc != 0) { - fprintf(stderr, "Error: unable to process SQL \"%s\"\n", query); - return rc; - } - } - } else { - // Run commands received from standard input - if (stdin_is_interactive) { - printBold("osquery"); - printf( - " - being built, with love, at Samsung(not Facebook)\n" - "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"); - if (warnInmemoryDb) { - printf("Using a "); - printBold("virtual database"); - printf(". Need help, type '.help'\n"); - } - - auto history_file = osquery::osqueryHomeDirectory() + "/.history"; - read_history(history_file.c_str()); - rc = process_input(&data, 0); - stifle_history(100); - write_history(history_file.c_str()); - } else { - rc = process_input(&data, stdin); - } - } - - set_table_name(&data, 0); - sqlite3_free(data.zFreeOnClose); - - if (data.prettyPrint != nullptr) { - delete data.prettyPrint; - } - return rc; -} -} diff --git a/osquery/devtools/tests/printer_tests.cpp b/osquery/devtools/tests/printer_tests.cpp deleted file mode 100644 index 61626a0..0000000 --- a/osquery/devtools/tests/printer_tests.cpp +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include - -#include - -#include "osquery/devtools/devtools.h" - -namespace osquery { - -class PrinterTests : public testing::Test { - public: - QueryData q; - std::vector order; - void SetUp() { - order = {"name", "age", "food", "number"}; - q = { - { - {"name", "Mike Jones"}, - {"age", "39"}, - {"food", "mac and cheese"}, - {"number", "1"}, - }, - { - {"name", "John Smith"}, - {"age", "44"}, - {"food", "peanut butter and jelly"}, - {"number", "2"}, - }, - { - {"name", "Doctor Who"}, - {"age", "2000"}, - {"food", "fish sticks and custard"}, - {"number", "11"}, - }, - }; - } -}; - -TEST_F(PrinterTests, test_compute_query_data_lengths) { - std::map lengths; - for (const auto& row : q) { - computeRowLengths(row, lengths); - } - - // Check that all value lengths were maxed. - std::map expected = { - {"name", 10}, {"age", 4}, {"food", 23}, {"number", 2}}; - EXPECT_EQ(lengths, expected); - - // Then compute lengths of column names. - computeRowLengths(q.front(), lengths, true); - expected = {{"name", 10}, {"age", 4}, {"food", 23}, {"number", 6}}; - EXPECT_EQ(lengths, expected); -} - -TEST_F(PrinterTests, test_generate_separator) { - std::map lengths; - for (const auto& row : q) { - computeRowLengths(row, lengths); - } - - auto results = generateToken(lengths, order); - auto expected = "+------------+------+-------------------------+----+\n"; - EXPECT_EQ(results, expected); -} - -TEST_F(PrinterTests, test_generate_header) { - std::map lengths; - for (const auto& row : q) { - computeRowLengths(row, lengths); - } - - auto results = generateHeader(lengths, order); - auto expected = "| name | age | food | number |\n"; - EXPECT_EQ(results, expected); -} - -TEST_F(PrinterTests, test_generate_row) { - std::map lengths; - for (const auto& row : q) { - computeRowLengths(row, lengths); - } - - auto results = generateRow(q.front(), lengths, order); - auto expected = "| Mike Jones | 39 | mac and cheese | 1 |\n"; - EXPECT_EQ(results, expected); -} - -TEST_F(PrinterTests, test_unicode) { - Row r = {{"name", "Àlex Smith"}}; - std::map lengths; - computeRowLengths(r, lengths); - - std::map expected = {{"name", 10}}; - EXPECT_EQ(lengths, expected); -} -} diff --git a/osquery/dispatcher/CMakeLists.txt b/osquery/dispatcher/CMakeLists.txt deleted file mode 100644 index 82b34a2..0000000 --- a/osquery/dispatcher/CMakeLists.txt +++ /dev/null @@ -1,19 +0,0 @@ -# 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_dispatcher dispatcher.cpp - scheduler.cpp) - -FILE(GLOB OSQUERY_DISPATCHER_TESTS "tests/*.cpp") -ADD_OSQUERY_TEST(${OSQUERY_DISPATCHER_TESTS}) diff --git a/osquery/dispatcher/dispatcher.cpp b/osquery/dispatcher/dispatcher.cpp deleted file mode 100644 index 4c7f10d..0000000 --- a/osquery/dispatcher/dispatcher.cpp +++ /dev/null @@ -1,139 +0,0 @@ -/* - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include - -#include -#include - -#include "osquery/core/conversions.h" -#include "osquery/dispatcher/dispatcher.h" - -using namespace apache::thrift::concurrency; - -namespace osquery { - -/// The worker_threads define the default thread pool size. -FLAG(int32, worker_threads, 4, "Number of work dispatch threads"); - -void interruptableSleep(size_t milli) { - boost::this_thread::sleep(boost::posix_time::milliseconds(milli)); -} - -Dispatcher::Dispatcher() { - thread_manager_ = InternalThreadManager::newSimpleThreadManager( - (size_t)FLAGS_worker_threads, 0); - auto thread_factory = ThriftThreadFactory(new PosixThreadFactory()); - thread_manager_->threadFactory(thread_factory); - thread_manager_->start(); -} - -Dispatcher::~Dispatcher() { join(); } - -Status Dispatcher::add(ThriftInternalRunnableRef task) { - auto& self = instance(); - try { - if (self.state() != InternalThreadManager::STARTED) { - self.thread_manager_->start(); - } - instance().thread_manager_->add(task, 0, 0); - } catch (std::exception& e) { - return Status(1, e.what()); - } - return Status(0, "OK"); -} - -Status Dispatcher::addService(InternalRunnableRef service) { - if (service->hasRun()) { - return Status(1, "Cannot schedule a service twice"); - } - - auto& self = instance(); - auto thread = std::make_shared( - boost::bind(&InternalRunnable::run, &*service)); - self.service_threads_.push_back(thread); - self.services_.push_back(std::move(service)); - return Status(0, "OK"); -} - -InternalThreadManagerRef Dispatcher::getThreadManager() const { - return instance().thread_manager_; -} - -void Dispatcher::join() { - if (instance().thread_manager_ != nullptr) { - instance().thread_manager_->stop(); - instance().thread_manager_->join(); - } -} - -void Dispatcher::joinServices() { - for (auto& thread : instance().service_threads_) { - thread->join(); - } -} - -void Dispatcher::stopServices() { - auto& self = instance(); - for (const auto& service : self.services_) { - while (true) { - // Wait for each thread's entry point (start) meaning the thread context - // was allocated and (run) was called by boost::thread started. - if (service->hasRun()) { - break; - } - // We only need to check if std::terminate is called very quickly after - // the boost::thread is created. - ::usleep(200); - } - service->stop(); - } - - for (auto& thread : self.service_threads_) { - thread->interrupt(); - } -} - -InternalThreadManager::STATE Dispatcher::state() const { - return instance().thread_manager_->state(); -} - -void Dispatcher::addWorker(size_t value) { - instance().thread_manager_->addWorker(value); -} - -void Dispatcher::removeWorker(size_t value) { - instance().thread_manager_->removeWorker(value); -} - -size_t Dispatcher::idleWorkerCount() const { - return instance().thread_manager_->idleWorkerCount(); -} - -size_t Dispatcher::workerCount() const { - return instance().thread_manager_->workerCount(); -} - -size_t Dispatcher::pendingTaskCount() const { - return instance().thread_manager_->pendingTaskCount(); -} - -size_t Dispatcher::totalTaskCount() const { - return instance().thread_manager_->totalTaskCount(); -} - -size_t Dispatcher::pendingTaskCountMax() const { - return instance().thread_manager_->pendingTaskCountMax(); -} - -size_t Dispatcher::expiredTaskCount() const { - return instance().thread_manager_->expiredTaskCount(); -} -} diff --git a/osquery/dispatcher/dispatcher.h b/osquery/dispatcher/dispatcher.h deleted file mode 100644 index f4d23e5..0000000 --- a/osquery/dispatcher/dispatcher.h +++ /dev/null @@ -1,280 +0,0 @@ -/* - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#pragma once - -#include -#include -#include -#include - -#include -#include - -#include - -#include -#include -#include - -namespace osquery { - -using namespace apache::thrift::concurrency; - -/// Create easier to reference typedefs for Thrift layer implementations. -#define SHARED_PTR_IMPL OSQUERY_THRIFT_POINTER::shared_ptr -typedef apache::thrift::concurrency::ThreadManager InternalThreadManager; -typedef SHARED_PTR_IMPL InternalThreadManagerRef; - -/** - * @brief Default number of threads in the thread pool. - * - * The amount of threads that the thread pool will be created with if another - * value is not specified on the command-line. - */ -extern const int kDefaultThreadPoolSize; - -class InternalRunnable : public Runnable { - public: - virtual ~InternalRunnable() {} - InternalRunnable() : run_(false) {} - - public: - /// The boost::thread entrypoint. - void run() { - run_ = true; - start(); - } - - /// Check if the thread's entrypoint (run) executed, meaning thread context - /// was allocated. - bool hasRun() { return run_; } - - /// The runnable may also tear down services before the thread context - /// is removed. - virtual void stop() {} - - protected: - /// Require the runnable thread define an entrypoint. - virtual void start() = 0; - - private: - bool run_; -}; - -/// An internal runnable used throughout osquery as dispatcher services. -typedef std::shared_ptr InternalRunnableRef; -typedef std::shared_ptr InternalThreadRef; -/// A thrift internal runnable with variable pointer wrapping. -typedef SHARED_PTR_IMPL ThriftInternalRunnableRef; -typedef SHARED_PTR_IMPL ThriftThreadFactory; - -/** - * @brief Singleton for queuing asynchronous tasks to be executed in parallel - * - * Dispatcher is a singleton which can be used to coordinate the parallel - * execution of asynchronous tasks across an application. Internally, - * Dispatcher is back by the Apache Thrift thread pool. - */ -class Dispatcher : private boost::noncopyable { - public: - /** - * @brief The primary way to access the Dispatcher factory facility. - * - * @code{.cpp} auto dispatch = osquery::Dispatcher::instance(); @endcode - * - * @return The osquery::Dispatcher instance. - */ - static Dispatcher& instance() { - static Dispatcher instance; - return instance; - } - - /** - * @brief Add a task to the dispatcher. - * - * Adding tasks to the Dispatcher's thread pool requires you to create a - * "runnable" class which publicly implements Apache Thrift's Runnable - * class. Create a shared pointer to the class and you're all set to - * schedule work. - * - * @code{.cpp} - * class TestRunnable : public apache::thrift::concurrency::Runnable { - * public: - * int* i; - * TestRunnable(int* i) : i(i) {} - * virtual void run() { ++*i; } - * }; - * - * int i = 5; - * Dispatcher::add(std::make_shared(&i); - * while (dispatch->totalTaskCount() > 0) {} - * assert(i == 6); - * @endcode - * - * @param task a C++11 std shared pointer to an instance of a class which - * publicly inherits from `apache::thrift::concurrency::Runnable`. - * - * @return osquery success status - */ - static Status add(ThriftInternalRunnableRef task); - - /// See `add`, but services are not limited to a thread poll size. - static Status addService(InternalRunnableRef service); - - /** - * @brief Getter for the underlying thread manager instance. - * - * Use this getter if you'd like to perform some operations which the - * Dispatcher API doesn't support, but you are certain are supported by the - * backing Apache Thrift thread manager. - * - * Use this method with caution, as it only exists to allow developers to - * iterate quickly in the event that the pragmatic decision to access the - * underlying thread manager has been determined to be necessary. - * - * @code{.cpp} - * auto t = osquery::Dispatcher::getThreadManager(); - * @endcode - * - * @return a shared pointer to the Apache Thrift `ThreadManager` instance - * which is currently being used to orchestrate multi-threaded operations. - */ - InternalThreadManagerRef getThreadManager() const; - - /** - * @brief Joins the thread manager. - * - * This is the same as stop, except that it will block until all the workers - * have finished their work. At that point the ThreadManager will transition - * into the STOPPED state. - */ - static void join(); - - /// See `join`, but applied to osquery services. - static void joinServices(); - - /// Destroy and stop all osquery service threads and service objects. - static void stopServices(); - - /** - * @brief Get the current state of the thread manager. - * - * @return an Apache Thrift STATE enum. - */ - InternalThreadManager::STATE state() const; - - /** - * @brief Add a worker thread. - * - * Use this method to add an additional thread to the thread pool. - * - * @param value is a size_t indicating how many additional worker threads - * should be added to the thread group. If not parameter is supplied, one - * worker thread is added. - * - * @see osquery::Dispatcher::removeWorker - */ - static void addWorker(size_t value = 1); - - /** - * @brief Remove a worker thread. - * - * Use this method to remove a thread from the thread pool. - * - * @param value is a size_t indicating how many additional worker threads - * should be removed from the thread group. If not parameter is supplied, - * one worker thread is removed. - * - * @see osquery::Dispatcher::addWorker - */ - static void removeWorker(size_t value = 1); - - /** - * @brief Gets the current number of idle worker threads. - * - * @return the number of idle worker threads. - */ - size_t idleWorkerCount() const; - - /** - * @brief Gets the current number of total worker threads. - * - * @return the current number of total worker threads. - */ - size_t workerCount() const; - - /** - * @brief Gets the current number of pending tasks. - * - * @return the current number of pending tasks. - */ - size_t pendingTaskCount() const; - - /** - * @brief Gets the current number of pending and executing tasks. - * - * @return the current number of pending and executing tasks. - */ - size_t totalTaskCount() const; - - /** - * @brief Gets the maximum pending task count. 0 indicates no maximum. - * - * @return the maximum pending task count. 0 indicates no maximum. - */ - size_t pendingTaskCountMax() const; - - /** - * @brief Gets the number of tasks which have been expired without being - * run. - * - * @return he number of tasks which have been expired without being run. - */ - size_t expiredTaskCount() const; - - private: - /** - * @brief Default constructor. - * - * Since instances of Dispatcher should only be created via instance(), - * Dispatcher's constructor is private. - */ - Dispatcher(); - Dispatcher(Dispatcher const&); - void operator=(Dispatcher const&); - virtual ~Dispatcher(); - - private: - /** - * @brief Internal shared pointer which references Thrift's thread manager - * - * All thread operations occur via Apache Thrift's ThreadManager class. This - * private member represents a shared pointer to an instantiation of that - * thread manager, which can be used to accomplish various threading - * objectives. - * - * @see getThreadManager - */ - InternalThreadManagerRef thread_manager_; - - /// The set of shared osquery service threads. - std::vector service_threads_; - - /// The set of shared osquery services. - std::vector services_; - - private: - friend class ExtensionsTest; -}; - -/// Allow a dispatched thread to wait while processing or to prevent thrashing. -void interruptableSleep(size_t milli); -} diff --git a/osquery/dispatcher/scheduler.cpp b/osquery/dispatcher/scheduler.cpp deleted file mode 100644 index 5323e0c..0000000 --- a/osquery/dispatcher/scheduler.cpp +++ /dev/null @@ -1,174 +0,0 @@ -/* - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include - -#include -#include -#include -#include -#include -#include - -#include "osquery/database/query.h" -#include "osquery/dispatcher/scheduler.h" - -namespace osquery { - -FLAG(string, - host_identifier, - "hostname", - "Field used to identify the host running osquery (hostname, uuid)"); - -FLAG(bool, enable_monitor, false, "Enable the schedule monitor"); - -FLAG(uint64, schedule_timeout, 0, "Limit the schedule, 0 for no limit") - -Status getHostIdentifier(std::string& ident) { - if (FLAGS_host_identifier != "uuid") { - // use the hostname as the default machine identifier - ident = osquery::getHostname(); - return Status(0, "OK"); - } - - // Lookup the host identifier (UUID) previously generated and stored. - auto status = getDatabaseValue(kPersistentSettings, "hostIdentifier", ident); - if (!status.ok()) { - // The lookup failed, there is a problem accessing the database. - VLOG(1) << "Could not access database; using hostname as host identifier"; - ident = osquery::getHostname(); - return Status(0, "OK"); - } - - if (ident.size() == 0) { - // There was no uuid stored in the database, generate one and store it. - ident = osquery::generateHostUuid(); - VLOG(1) << "Using uuid " << ident << " as host identifier"; - return setDatabaseValue(kPersistentSettings, "hostIdentifier", ident); - } - return status; -} - -inline SQL monitor(const std::string& name, const ScheduledQuery& query) { - // Snapshot the performance and times for the worker before running. - auto pid = std::to_string(getpid()); - auto r0 = SQL::selectAllFrom("processes", "pid", EQUALS, pid); - auto t0 = time(nullptr); - auto sql = SQL(query.query); - // Snapshot the performance after, and compare. - auto t1 = time(nullptr); - auto r1 = SQL::selectAllFrom("processes", "pid", EQUALS, pid); - if (r0.size() > 0 && r1.size() > 0) { - size_t size = 0; - for (const auto& row : sql.rows()) { - for (const auto& column : row) { - size += column.first.size(); - size += column.second.size(); - } - } - Config::recordQueryPerformance(name, t1 - t0, size, r0[0], r1[0]); - } - return sql; -} - -void launchQuery(const std::string& name, const ScheduledQuery& query) { - // Execute the scheduled query and create a named query object. - VLOG(1) << "Executing query: " << query.query; - auto sql = (FLAGS_enable_monitor) ? monitor(name, query) : SQL(query.query); - - if (!sql.ok()) { - LOG(ERROR) << "Error executing query (" << query.query - << "): " << sql.getMessageString(); - return; - } - - // Fill in a host identifier fields based on configuration or availability. - std::string ident; - auto status = getHostIdentifier(ident); - if (!status.ok() || ident.empty()) { - ident = ""; - } - - // A query log item contains an optional set of differential results or - // a copy of the most-recent execution alongside some query metadata. - QueryLogItem item; - item.name = name; - item.identifier = ident; - item.time = osquery::getUnixTime(); - item.calendar_time = osquery::getAsciiTime(); - - if (query.options.count("snapshot") && query.options.at("snapshot")) { - // This is a snapshot query, emit results with a differential or state. - item.snapshot_results = std::move(sql.rows()); - logSnapshotQuery(item); - return; - } - - // Create a database-backed set of query results. - auto dbQuery = Query(name, query); - DiffResults diff_results; - // Add this execution's set of results to the database-tracked named query. - // We can then ask for a differential from the last time this named query - // was executed by exact matching each row. - status = dbQuery.addNewResults(sql.rows(), diff_results); - if (!status.ok()) { - LOG(ERROR) << "Error adding new results to database: " << status.what(); - return; - } - - if (diff_results.added.size() == 0 && diff_results.removed.size() == 0) { - // No diff results or events to emit. - return; - } - - VLOG(1) << "Found results for query (" << name << ") for host: " << ident; - item.results = diff_results; - if (query.options.count("removed") && !query.options.at("removed")) { - item.results.removed.clear(); - } - - status = logQueryLogItem(item); - if (!status.ok()) { - LOG(ERROR) << "Error logging the results of query (" << query.query - << "): " << status.toString(); - } -} - -void SchedulerRunner::start() { - time_t t = std::time(nullptr); - struct tm* local = std::localtime(&t); - unsigned long int i = local->tm_sec; - for (; (timeout_ == 0) || (i <= timeout_); ++i) { - { - ConfigDataInstance config; - for (const auto& query : config.schedule()) { - if (i % query.second.splayed_interval == 0) { - launchQuery(query.first, query.second); - } - } - } - // Put the thread into an interruptible sleep without a config instance. - osquery::interruptableSleep(interval_ * 1000); - } -} - -Status startScheduler() { - if (startScheduler(FLAGS_schedule_timeout, 1).ok()) { - Dispatcher::joinServices(); - return Status(0, "OK"); - } - return Status(1, "Could not start scheduler"); -} - -Status startScheduler(unsigned long int timeout, size_t interval) { - Dispatcher::addService(std::make_shared(timeout, interval)); - return Status(0, "OK"); -} -} diff --git a/osquery/dispatcher/scheduler.h b/osquery/dispatcher/scheduler.h deleted file mode 100644 index ccd0c40..0000000 --- a/osquery/dispatcher/scheduler.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#pragma once - -#include "osquery/dispatcher/dispatcher.h" - -namespace osquery { - -/// A Dispatcher service thread that watches an ExtensionManagerHandler. -class SchedulerRunner : public InternalRunnable { - public: - virtual ~SchedulerRunner() {} - SchedulerRunner(unsigned long int timeout, size_t interval) - : interval_(interval), timeout_(timeout) {} - - public: - /// The Dispatcher thread entry point. - void start(); - - protected: - /// The UNIX domain socket path for the ExtensionManager. - std::map splay_; - /// Interval in seconds between schedule steps. - size_t interval_; - /// Maximum number of steps. - unsigned long int timeout_; -}; - -/// Start quering according to the config's schedule -Status startScheduler(); - -/// Helper scheduler start with variable settings for testing. -Status startScheduler(unsigned long int timeout, size_t interval); -} diff --git a/osquery/dispatcher/tests/dispatcher_tests.cpp b/osquery/dispatcher/tests/dispatcher_tests.cpp deleted file mode 100644 index 4ece0a8..0000000 --- a/osquery/dispatcher/tests/dispatcher_tests.cpp +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include - -#include - -#include "osquery/dispatcher/dispatcher.h" - -namespace osquery { - -class DispatcherTests : public testing::Test {}; - -TEST_F(DispatcherTests, test_singleton) { - auto& one = Dispatcher::instance(); - auto& two = Dispatcher::instance(); - EXPECT_EQ(one.getThreadManager().get(), two.getThreadManager().get()); -} - -class TestRunnable : public InternalRunnable { - public: - int* i; - explicit TestRunnable(int* i) : i(i) {} - virtual void start() { ++*i; } -}; - -TEST_F(DispatcherTests, test_add_work) { - auto& dispatcher = Dispatcher::instance(); - int base = 5; - int repetitions = 1; - - int i = base; - for (int c = 0; c < repetitions; ++c) { - dispatcher.add(OSQUERY_THRIFT_POINTER::make_shared(&i)); - } - while (dispatcher.totalTaskCount() > 0) { - } - - EXPECT_EQ(i, base + repetitions); -} -} diff --git a/osquery/distributed/CMakeLists.txt b/osquery/distributed/CMakeLists.txt deleted file mode 100644 index f5a2f97..0000000 --- a/osquery/distributed/CMakeLists.txt +++ /dev/null @@ -1,4 +0,0 @@ -ADD_OSQUERY_LIBRARY(osquery_distributed distributed.cpp) - -FILE(GLOB OSQUERY_DISTRIBUTED_TESTS "tests/*.cpp") -ADD_OSQUERY_TEST(${OSQUERY_DISTRIBUTED_TESTS}) diff --git a/osquery/distributed/distributed.cpp b/osquery/distributed/distributed.cpp deleted file mode 100644 index 5647b1f..0000000 --- a/osquery/distributed/distributed.cpp +++ /dev/null @@ -1,162 +0,0 @@ -/* - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include - -#include - -#include -#include - -#include "osquery/distributed/distributed.h" - -namespace pt = boost::property_tree; - -namespace osquery { - -FLAG(int32, - distributed_retries, - 3, - "Times to retry reading/writing distributed queries"); - -Status MockDistributedProvider::getQueriesJSON(std::string& query_json) { - query_json = queriesJSON_; - return Status(); -} - -Status MockDistributedProvider::writeResultsJSON(const std::string& results) { - resultsJSON_ = results; - return Status(); -} - -Status DistributedQueryHandler::parseQueriesJSON( - const std::string& query_json, - std::vector& requests) { - // Parse the JSON into a ptree - pt::ptree tree; - try { - std::stringstream query_stream(query_json); - pt::read_json(query_stream, tree); - } catch (const pt::json_parser::json_parser_error& e) { - return Status(1, std::string("Error loading query JSON: ") + e.what()); - } - - // Parse the ptree into DistributedQueryRequests - std::vector results; - for (const auto& node : tree) { - const auto& request_tree = node.second; - DistributedQueryRequest request; - try { - request.query = request_tree.get_child("query").get_value(); - request.id = request_tree.get_child("id").get_value(); - } catch (const std::exception& e) { - return Status(1, std::string("Error parsing queries: ") + e.what()); - } - results.push_back(request); - } - - requests = std::move(results); - - return Status(); -} - -SQL DistributedQueryHandler::handleQuery(const std::string& query_string) { - SQL query = SQL(query_string); - query.annotateHostInfo(); - return query; -} - -Status DistributedQueryHandler::serializeResults( - const std::vector >& results, - pt::ptree& tree) { - try { - pt::ptree& res_tree = tree.put_child("results", pt::ptree()); - for (const auto& result : results) { - DistributedQueryRequest request = result.first; - SQL sql = result.second; - pt::ptree& child = res_tree.put_child(request.id, pt::ptree()); - child.put("status", sql.getStatus().getCode()); - pt::ptree& rows_child = child.put_child("rows", pt::ptree()); - Status s = serializeQueryData(sql.rows(), rows_child); - if (!s.ok()) { - return s; - } - } - } catch (const std::exception& e) { - return Status(1, std::string("Error serializing results: ") + e.what()); - } - return Status(); -} - -Status DistributedQueryHandler::doQueries() { - // Get and parse the queries - Status status; - std::string query_json; - int retries = 0; - do { - status = provider_->getQueriesJSON(query_json); - ++retries; - } while (!status.ok() && retries <= FLAGS_distributed_retries); - if (!status.ok()) { - return status; - } - - std::vector requests; - status = parseQueriesJSON(query_json, requests); - if (!status.ok()) { - return status; - } - - // Run the queries - std::vector > query_results; - std::set successful_query_ids; - for (const auto& request : requests) { - if (executedRequestIds_.find(request.id) != executedRequestIds_.end()) { - // We've already successfully returned results for this request, don't - // process it again. - continue; - } - SQL query_result = handleQuery(request.query); - if (query_result.ok()) { - successful_query_ids.insert(request.id); - } - query_results.push_back({request, query_result}); - } - - // Serialize the results - pt::ptree serialized_results; - serializeResults(query_results, serialized_results); - std::string json; - try { - std::ostringstream ss; - pt::write_json(ss, serialized_results, false); - json = ss.str(); - } catch (const pt::json_parser::json_parser_error& e) { - return Status(1, e.what()); - } - - // Write the results - retries = 0; - do { - status = provider_->writeResultsJSON(json); - ++retries; - } while (!status.ok() && retries <= FLAGS_distributed_retries); - if (!status.ok()) { - return status; - } - - // Only note that the queries were successfully completed if we were actually - // able to write the results. - executedRequestIds_.insert(successful_query_ids.begin(), - successful_query_ids.end()); - - return status; -} -} diff --git a/osquery/distributed/distributed.h b/osquery/distributed/distributed.h deleted file mode 100644 index fc53f01..0000000 --- a/osquery/distributed/distributed.h +++ /dev/null @@ -1,153 +0,0 @@ -/* - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#pragma once - -#include -#include - -#include - -#include - -namespace osquery { - -/** - * @brief This is an interface for distributed query "providers" - * - * Providers implement the communication between the distributed query master - * and the individual host. A provider may utilize any communications strategy - * that supports reading and writing JSON (i.e. HTTPS requests, reading from a - * file, querying a message queue, etc.) - */ -class IDistributedProvider { -public: - virtual ~IDistributedProvider() {} - - /* - * @brief Get the JSON string containing the queries to be executed - * - * @param query_json A string to fill with the retrieved JSON - * - * @return osquery::Status indicating success or failure of the operation - */ - virtual Status getQueriesJSON(std::string& query_json) = 0; - - /* - * @brief Write the results JSON back to the master - * - * @param results A string containing the results JSON - * - * @return osquery::Status indicating success or failure of the operation - */ - virtual Status writeResultsJSON(const std::string& results) = 0; -}; - -/** - * @brief A mocked implementation of IDistributedProvider - * - * This implementation is useful for writing unit tests of the - * DistributedQueryHandler functionality. - */ -class MockDistributedProvider : public IDistributedProvider { -public: - // These methods just read/write the corresponding public members - Status getQueriesJSON(std::string& query_json) override; - Status writeResultsJSON(const std::string& results) override; - - std::string queriesJSON_; - std::string resultsJSON_; -}; - -/** - * @brief Small struct containing the query and ID information for a - * distributed query - */ -struct DistributedQueryRequest { -public: - explicit DistributedQueryRequest() {} - explicit DistributedQueryRequest(const std::string& q, const std::string& i) - : query(q), id(i) {} - std::string query; - std::string id; -}; - -/** - * @brief The main handler class for distributed queries - * - * This class is responsible for implementing the core functionality of - * distributed queries. It manages state, uses the provider to read/write from - * the master, and executes queries. - */ -class DistributedQueryHandler { -public: - /** - * @brief Construct a new handler with the given provider - * - * @param provider The provider used retrieving queries and writing results - */ - explicit DistributedQueryHandler( - std::unique_ptr provider) - : provider_(std::move(provider)) {} - - /** - * @brief Retrieve queries, run them, and write results - * - * This is the core method of DistributedQueryHandler, tying together all the - * other components to read the requests from the provider, execute the - * queries, and write the results back to the provider. - * - * @return osquery::Status indicating success or failure of the operation - */ - Status doQueries(); - - /** - * @brief Run and annotate an individual query - * - * @param query_string A string containing the query to be executed - * - * @return A SQL object containing the (annotated) query results - */ - static SQL handleQuery(const std::string& query_string); - - /** - * @brief Serialize the results of all requests into a ptree - * - * @param results The vector of requests and results - * @param tree The tree to serialize results into - * - * @return osquery::Status indicating success or failure of the operation - */ - static Status serializeResults( - const std::vector >& results, - boost::property_tree::ptree& tree); - - /** - * @brief Parse the query JSON into the individual query objects - * - * @param query_json The JSON string containing the queries - * @param requests A vector to fill with the query objects - * - * @return osquery::Status indicating success or failure of the parsing - */ - static Status parseQueriesJSON(const std::string& query_json, - std::vector& requests); - -private: - // The provider used to read and write queries and results - std::unique_ptr provider_; - - // Used to store already executed queries to avoid duplication. (Some master - // configurations may asynchronously process the results of requests, so a - // request might be seen by the host after it has already been executed.) - std::set executedRequestIds_; -}; - -} // namespace osquery diff --git a/osquery/distributed/tests/distributed_tests.cpp b/osquery/distributed/tests/distributed_tests.cpp deleted file mode 100644 index 81b0607..0000000 --- a/osquery/distributed/tests/distributed_tests.cpp +++ /dev/null @@ -1,224 +0,0 @@ -/* - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include - -#include -#include -#include - -#include -#include - -#include "osquery/distributed/distributed.h" -#include "osquery/sql/sqlite_util.h" - -namespace pt = boost::property_tree; - -namespace osquery { - -// Distributed tests expect an SQL implementation for queries. -REGISTER_INTERNAL(SQLiteSQLPlugin, "sql", "sql"); - -class DistributedTests : public testing::Test {}; - -TEST_F(DistributedTests, test_test_distributed_provider) { - MockDistributedProvider p; - std::string query_string = "['foo']"; - std::string result_string = "['bar']"; - - p.queriesJSON_ = query_string; - std::string query_json; - Status s = p.getQueriesJSON(query_json); - ASSERT_EQ(Status(), s); - EXPECT_EQ(query_string, query_json); - - s = p.writeResultsJSON(result_string); - EXPECT_TRUE(s.ok()); - EXPECT_EQ(result_string, p.resultsJSON_); -} - -TEST_F(DistributedTests, test_parse_query_json) { - std::string request_json = "[{\"query\": \"foo\", \"id\": \"bar\"}]"; - std::vector requests; - Status s = DistributedQueryHandler::parseQueriesJSON(request_json, requests); - ASSERT_EQ(Status(), s); - EXPECT_EQ(1, requests.size()); - EXPECT_EQ("foo", requests[0].query); - EXPECT_EQ("bar", requests[0].id); - - std::string bad_json = - "[{\"query\": \"foo\", \"id\": \"bar\"}, {\"query\": \"b\"}]"; - requests.clear(); - s = DistributedQueryHandler::parseQueriesJSON(bad_json, requests); - ASSERT_FALSE(s.ok()); - EXPECT_EQ(0, requests.size()); -} - -TEST_F(DistributedTests, test_handle_query) { - // Access to the internal SQL implementation is only available in core. - SQL query = DistributedQueryHandler::handleQuery("SELECT hour from time"); - ASSERT_TRUE(query.ok()); - QueryData rows = query.rows(); - ASSERT_EQ(1, rows.size()); - EXPECT_EQ(rows[0]["_source_host"], getHostname()); - - query = DistributedQueryHandler::handleQuery("bad query"); - ASSERT_FALSE(query.ok()); - rows = query.rows(); - ASSERT_EQ(0, rows.size()); -} - -TEST_F(DistributedTests, test_serialize_results_empty) { - DistributedQueryRequest r0("foo", "foo_id"); - MockSQL q0 = MockSQL(); - pt::ptree tree; - - DistributedQueryHandler::serializeResults({{r0, q0}}, tree); - - EXPECT_EQ(0, tree.get("results.foo_id.status")); - EXPECT_TRUE(tree.get_child("results.foo_id.rows").empty()); -} - -TEST_F(DistributedTests, test_serialize_results_basic) { - DistributedQueryRequest r0("foo", "foo_id"); - QueryData rows0 = { - {{"foo0", "foo0_val"}, {"bar0", "bar0_val"}}, - {{"foo1", "foo1_val"}, {"bar1", "bar1_val"}}, - }; - MockSQL q0 = MockSQL(rows0); - pt::ptree tree; - - DistributedQueryHandler::serializeResults({{r0, q0}}, tree); - - EXPECT_EQ(0, tree.get("results.foo_id.status")); - - const pt::ptree& tree_rows = tree.get_child("results.foo_id.rows"); - EXPECT_EQ(2, tree_rows.size()); - - auto row = tree_rows.begin(); - EXPECT_EQ("foo0_val", row->second.get("foo0")); - EXPECT_EQ("bar0_val", row->second.get("bar0")); - ++row; - EXPECT_EQ("foo1_val", row->second.get("foo1")); - EXPECT_EQ("bar1_val", row->second.get("bar1")); -} - -TEST_F(DistributedTests, test_serialize_results_multiple) { - DistributedQueryRequest r0("foo", "foo_id"); - QueryData rows0 = { - {{"foo0", "foo0_val"}, {"bar0", "bar0_val"}}, - {{"foo1", "foo1_val"}, {"bar1", "bar1_val"}}, - }; - MockSQL q0 = MockSQL(rows0); - - DistributedQueryRequest r1("bar", "bar_id"); - MockSQL q1 = MockSQL({}, Status(1, "Fail")); - - pt::ptree tree; - - DistributedQueryHandler::serializeResults({{r0, q0}, {r1, q1}}, tree); - - EXPECT_EQ(0, tree.get("results.foo_id.status")); - const pt::ptree& tree_rows = tree.get_child("results.foo_id.rows"); - EXPECT_EQ(2, tree_rows.size()); - auto row = tree_rows.begin(); - EXPECT_EQ("foo0_val", row->second.get("foo0")); - EXPECT_EQ("bar0_val", row->second.get("bar0")); - ++row; - EXPECT_EQ("foo1_val", row->second.get("foo1")); - EXPECT_EQ("bar1_val", row->second.get("bar1")); - - EXPECT_EQ(1, tree.get("results.bar_id.status")); - const pt::ptree& fail_rows = tree.get_child("results.bar_id.rows"); - EXPECT_EQ(0, fail_rows.size()); -} - -TEST_F(DistributedTests, test_do_queries) { - // Access to the internal SQL implementation is only available in core. - auto provider_raw = new MockDistributedProvider(); - provider_raw->queriesJSON_ = - "[ \ - {\"query\": \"SELECT hour FROM time\", \"id\": \"hour\"},\ - {\"query\": \"bad\", \"id\": \"bad\"},\ - {\"query\": \"SELECT minutes FROM time\", \"id\": \"minutes\"}\ - ]"; - std::unique_ptr - provider(provider_raw); - DistributedQueryHandler handler(std::move(provider)); - - Status s = handler.doQueries(); - ASSERT_EQ(Status(), s); - - pt::ptree tree; - std::istringstream json_stream(provider_raw->resultsJSON_); - ASSERT_NO_THROW(pt::read_json(json_stream, tree)); - - { - EXPECT_EQ(0, tree.get("results.hour.status")); - const pt::ptree& tree_rows = tree.get_child("results.hour.rows"); - EXPECT_EQ(1, tree_rows.size()); - auto row = tree_rows.begin(); - EXPECT_GE(row->second.get("hour"), 0); - EXPECT_LE(row->second.get("hour"), 24); - EXPECT_EQ(getHostname(), row->second.get("_source_host")); - } - - { - // this query should have failed - EXPECT_EQ(1, tree.get("results.bad.status")); - const pt::ptree& tree_rows = tree.get_child("results.bad.rows"); - EXPECT_EQ(0, tree_rows.size()); - } - - { - EXPECT_EQ(0, tree.get("results.minutes.status")); - const pt::ptree& tree_rows = tree.get_child("results.minutes.rows"); - EXPECT_EQ(1, tree_rows.size()); - auto row = tree_rows.begin(); - EXPECT_GE(row->second.get("minutes"), 0); - EXPECT_LE(row->second.get("minutes"), 60); - EXPECT_EQ(getHostname(), row->second.get("_source_host")); - } -} - -TEST_F(DistributedTests, test_duplicate_request) { - // Access to the internal SQL implementation is only available in core. - auto provider_raw = new MockDistributedProvider(); - provider_raw->queriesJSON_ = - "[{\"query\": \"SELECT hour FROM time\", \"id\": \"hour\"}]"; - std::unique_ptr - provider(provider_raw); - DistributedQueryHandler handler(std::move(provider)); - - Status s = handler.doQueries(); - ASSERT_EQ(Status(), s); - - pt::ptree tree; - std::istringstream json_stream(provider_raw->resultsJSON_); - ASSERT_NO_THROW(pt::read_json(json_stream, tree)); - - EXPECT_EQ(0, tree.get("results.hour.status")); - const pt::ptree& tree_rows = tree.get_child("results.hour.rows"); - EXPECT_EQ(1, tree_rows.size()); - - auto row = tree_rows.begin(); - EXPECT_GE(row->second.get("hour"), 0); - EXPECT_LE(row->second.get("hour"), 24); - EXPECT_EQ(getHostname(), row->second.get("_source_host")); - - // The second time, 'hour' should not be executed again - s = handler.doQueries(); - ASSERT_EQ(Status(), s); - json_stream.str(provider_raw->resultsJSON_); - ASSERT_NO_THROW(pt::read_json(json_stream, tree)); - EXPECT_EQ(0, tree.get_child("results").size()); -} -} diff --git a/osquery/events/CMakeLists.txt b/osquery/events/CMakeLists.txt deleted file mode 100644 index e2ee61b..0000000 --- a/osquery/events/CMakeLists.txt +++ /dev/null @@ -1,25 +0,0 @@ -# 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_LINK(udev ip4tc) - -ADD_OSQUERY_LIBRARY(osquery_events events.cpp) -ADD_OSQUERY_LIBRARY(osquery_events_linux linux/inotify.cpp - linux/udev.cpp) - -FILE(GLOB OSQUERY_EVENTS_TESTS "tests/*.cpp") -ADD_OSQUERY_TEST(${OSQUERY_EVENTS_TESTS}) - -FILE(GLOB OSQUERY_LINUX_EVENTS_TESTS "linux/tests/*.cpp") -ADD_OSQUERY_TEST(${OSQUERY_LINUX_EVENTS_TESTS}) diff --git a/osquery/events/events.cpp b/osquery/events/events.cpp deleted file mode 100644 index 5b0cd8b..0000000 --- a/osquery/events/events.cpp +++ /dev/null @@ -1,731 +0,0 @@ -/* - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include - -#include -#include -#include - -#include -#include -#include -#include - -#include "osquery/core/conversions.h" -#include "osquery/database/db_handle.h" - -namespace osquery { - -/// Helper cooloff (ms) macro to prevent thread failure thrashing. -#define EVENTS_COOLOFF 20 - -FLAG(bool, disable_events, false, "Disable osquery publish/subscribe system"); - -FLAG(bool, - events_optimize, - true, - "Optimize subscriber select queries (scheduler only)"); - -FLAG(int32, events_expiry, 86000, "Timeout to expire event subscriber results"); - -const std::vector kEventTimeLists = { - 1 * 60 * 60, // 1 hour - 1 * 60, // 1 minute - 10, // 10 seconds -}; - -void publisherSleep(size_t milli) { - boost::this_thread::sleep(boost::posix_time::milliseconds(milli)); -} - -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()) { - EventTime expr = 0; - try { - expr = boost::lexical_cast(constraint.expr); - } catch (const boost::bad_lexical_cast& e) { - expr = 0; - } - 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); -} - -void EventPublisherPlugin::fire(const EventContextRef& ec, EventTime time) { - EventContextID ec_id; - - if (isEnding()) { - // Cannot emit/fire while ending - return; - } - - { - boost::lock_guard lock(ec_id_lock_); - ec_id = next_ec_id_++; - } - - // Fill in EventContext ID and time if needed. - if (ec != nullptr) { - ec->id = ec_id; - if (ec->time == 0) { - if (time == 0) { - time = getUnixTime(); - } - // Todo: add a check to assure normalized (seconds) time. - ec->time = time; - } - } - - for (const auto& subscription : subscriptions_) { - auto es = EventFactory::getEventSubscriber(subscription->subscriber_name); - if (es->state() == SUBSCRIBER_RUNNING) { - fireCallback(subscription, ec); - } - } -} - -std::set EventSubscriberPlugin::getIndexes(EventTime start, - EventTime stop, - int list_key) { - auto db = DBHandle::getInstance(); - auto index_key = "indexes." + dbNamespace(); - std::set indexes; - - // 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(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). - // (2) Intermediate iterations will have 2 (start-start_max, stop-stop_min). - // 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. - std::vector all_bins, bins, expirations; - boost::split(all_bins, time_list, boost::is_any_of(",")); - for (const auto& bin : all_bins) { - // Bins are identified by the binning size step. - auto step = boost::lexical_cast(bin); - // Check if size * step -> size * (step + 1) is within a range. - int bin_start = size * step, bin_stop = size * (step + 1); - 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) { - bins.push_back(bin); - } else if ((bin_start >= stop_min && bin_stop <= stop) || stop == 0) { - bins.push_back(bin); - } - } - - // Rewrite the index lists and delete each expired item. - if (expirations.size() > 0) { - expireIndexes(list_type, all_bins, expirations); - } - - if (bins.size() != 0) { - // If more precision was achieved though this list's binning. - local_start = boost::lexical_cast(bins.front()) * size; - start_max = (local_start < start_max) ? local_start : start_max; - local_stop = (boost::lexical_cast(bins.back()) + 1) * size; - stop_min = (local_stop < stop_min) ? local_stop : stop_min; - } - - for (const auto& bin : bins) { - indexes.insert(list_type + "." + bin); - } - - if (start == start_max && stop == stop_min) { - break; - } - } - - // 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) { - 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 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( - const std::string& list_type, - const std::vector& indexes, - const std::vector& expirations) { - auto db = DBHandle::getInstance(); - auto index_key = "indexes." + dbNamespace(); - - // Construct a mutable list of persisting indexes to rewrite as records. - std::vector 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, ","); - db->Put(kEvents, index_key + "." + list_type, new_indexes); -} - -std::vector EventSubscriberPlugin::getRecords( - const std::set& indexes) { - auto db = DBHandle::getInstance(); - auto record_key = "records." + dbNamespace(); - - std::vector records; - for (const auto& index : indexes) { - std::string record_value; - if (!db->Get(kEvents, record_key + "." + index, record_value).ok()) { - return records; - } - - 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. - std::vector bin_records; - boost::split(bin_records, record_value, boost::is_any_of(",:")); - auto bin_it = bin_records.begin(); - for (; bin_it != bin_records.end(); bin_it++) { - std::string eid = *bin_it; - EventTime time = boost::lexical_cast(*(++bin_it)); - records.push_back(std::make_pair(eid, time)); - } - } - - return records; -} - -Status EventSubscriberPlugin::recordEvent(EventID& eid, EventTime time) { - Status status; - auto db = DBHandle::getInstance(); - std::string time_value = boost::lexical_cast(time); - - // The record is identified by the event type then module name. - std::string index_key = "indexes." + dbNamespace(); - std::string record_key = "records." + dbNamespace(); - // 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. - list_id = boost::lexical_cast(time / time_list); - // The list name identifies the 'type' of list. - list_key = boost::lexical_cast(time_list); - // list_key = list_key + "." + list_id; - - { - boost::lock_guard lock(event_record_lock_); - // Append the record (eid, unix_time) to the list bin. - std::string record_value; - status = db->Get( - kEvents, record_key + "." + list_key + "." + list_id, record_value); - - 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; - 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); - record_value = eid + ":" + time_value; - } else { - // Tokenize a record using ',' and the EID/time using ':'. - record_value += "," + eid + ":" + time_value; - } - status = db->Put( - kEvents, record_key + "." + list_key + "." + list_id, record_value); - if (!status.ok()) { - LOG(ERROR) << "Could not put Event Record key: " << record_key << "." - << list_key << "." << list_id; - } - } - } - - return Status(0, "OK"); -} - -EventID EventSubscriberPlugin::getEventID() { - Status status; - auto db = DBHandle::getInstance(); - // First get an event ID from the meta key. - std::string eid_key = "eid." + dbNamespace(); - std::string last_eid_value; - std::string eid_value; - - { - boost::lock_guard lock(event_id_lock_); - status = db->Get(kEvents, eid_key, last_eid_value); - if (!status.ok()) { - last_eid_value = "0"; - } - - size_t eid = boost::lexical_cast(last_eid_value) + 1; - eid_value = boost::lexical_cast(eid); - status = db->Put(kEvents, eid_key, eid_value); - } - - if (!status.ok()) { - return "0"; - } - - return eid_value; -} - -QueryData EventSubscriberPlugin::get(EventTime start, EventTime stop) { - QueryData results; - Status status; - - std::shared_ptr db; - try { - db = DBHandle::getInstance(); - } catch (const std::runtime_error& e) { - LOG(ERROR) << "Cannot retrieve subscriber results database is locked"; - return results; - } - - // Get the records for this time range. - auto indexes = getIndexes(start, stop); - auto records = getRecords(indexes); - - std::vector mapped_records; - for (const auto& record : records) { - if (record.second >= start && (record.second <= stop || stop == 0)) { - mapped_records.push_back(record); - } - } - - std::string events_key = "data." + dbNamespace(); - 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; - } - - // Select mapped_records using event_ids as keys. - std::string data_value; - for (const auto& record : mapped_records) { - Row r; - status = db->Get(kEvents, events_key + "." + record.first, data_value); - if (data_value.length() == 0) { - // THere is no record here, interesting error case. - continue; - } - status = deserializeRowJSON(data_value, r); - if (status.ok()) { - results.push_back(r); - } - } - return results; -} - -Status EventSubscriberPlugin::add(Row& r, EventTime event_time) { - std::shared_ptr db = nullptr; - try { - db = DBHandle::getInstance(); - } catch (const std::runtime_error& e) { - return Status(1, e.what()); - } - - // Get and increment the EID for this module. - EventID eid = getEventID(); - // Without encouraging a missing event time, do not support a 0-time. - auto index_time = getUnixTime(); - if (event_time == 0) { - r["time"] = std::to_string(index_time); - } else { - r["time"] = std::to_string(event_time); - } - - // Serialize and store the row data, for query-time retrieval. - std::string data; - auto status = serializeRowJSON(r, data); - if (!status.ok()) { - return status; - } - - // Store the event data. - std::string event_key = "data." + dbNamespace() + "." + eid; - status = db->Put(kEvents, event_key, data); - // Record the event in the indexing bins, using the index time. - recordEvent(eid, event_time); - return status; -} - -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_) { - auto thread_ = std::make_shared( - boost::bind(&EventFactory::run, publisher.first)); - ef.threads_.push_back(thread_); - } -} - -Status EventFactory::run(EventPublisherID& type_id) { - auto& ef = EventFactory::getInstance(); - if (FLAGS_disable_events) { - return Status(0, "Events disabled"); - } - - // 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; - try { - publisher = ef.getEventPublisher(type_id); - } catch (std::out_of_range& e) { - return Status(1, "No event type found"); - } - - 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() && status.ok()) { - // Can optionally implement a global cooloff latency here. - status = publisher->run(); - osquery::publisherSleep(EVENTS_COOLOFF); - } - // 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(); - ef.event_pubs_.erase(type_id); - return Status(0, "OK"); -} - -// 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(pub); - specialized_pub = std::static_pointer_cast(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"); - } - - auto& ef = EventFactory::getInstance(); - auto type_id = specialized_pub->type(); - if (ef.event_pubs_.count(type_id) != 0) { - // This is a duplicate event publisher. - return Status(1, "Duplicate publisher type"); - } - - // Do not set up event publisher if events are disabled. - if (!FLAGS_disable_events) { - if (!specialized_pub->setUp().ok()) { - // Only start event loop if setUp succeeds. - return Status(1, "Event publisher setup failed"); - } - } - - ef.event_pubs_[type_id] = specialized_pub; - return Status(0, "OK"); -} - -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(sub); - specialized_sub = std::static_pointer_cast(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"); - } - - // Let the module initialize any Subscriptions. - auto status = Status(0, "OK"); - if (!FLAGS_disable_events) { - status = specialized_sub->init(); - } - - auto& ef = EventFactory::getInstance(); - ef.event_subs_[specialized_sub->getName()] = specialized_sub; - - // Set state of subscriber. - if (!status.ok()) { - specialized_sub->state(SUBSCRIBER_FAILED); - return Status(1, status.getMessage()); - } else { - specialized_sub->state(SUBSCRIBER_RUNNING); - return Status(0, "OK"); - } -} - -Status EventFactory::addSubscription(EventPublisherID& type_id, - EventSubscriberID& name_id, - const SubscriptionContextRef& mc, - EventCallback cb, - void* user_data) { - auto subscription = Subscription::create(name_id, mc, cb, user_data); - return EventFactory::addSubscription(type_id, subscription); -} - -Status EventFactory::addSubscription(EventPublisherID& 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. - auto status = publisher->addSubscription(subscription); - if (!FLAGS_disable_events) { - publisher->configure(); - } - return status; -} - -size_t EventFactory::numSubscriptions(EventPublisherID& type_id) { - EventPublisherRef publisher; - try { - publisher = EventFactory::getInstance().getEventPublisher(type_id); - } catch (std::out_of_range& e) { - return 0; - } - return publisher->numSubscriptions(); -} - -EventPublisherRef EventFactory::getEventPublisher(EventPublisherID& type_id) { - if (getInstance().event_pubs_.count(type_id) == 0) { - LOG(ERROR) << "Requested unknown event publisher: " + type_id; - return nullptr; - } - return getInstance().event_pubs_.at(type_id); -} - -EventSubscriberRef EventFactory::getEventSubscriber( - EventSubscriberID& 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(EventSubscriberID& name_id) { - return (getInstance().event_subs_.count(name_id) > 0); -} - -Status EventFactory::deregisterEventPublisher(const EventPublisherRef& pub) { - return EventFactory::deregisterEventPublisher(pub->type()); -} - -Status EventFactory::deregisterEventPublisher(EventPublisherID& type_id) { - auto& ef = EventFactory::getInstance(); - EventPublisherRef publisher; - try { - publisher = ef.getEventPublisher(type_id); - } catch (std::out_of_range& e) { - 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(); - // 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); - } else { - publisher->end(); - } - } - return Status(0, "OK"); -} - -std::vector EventFactory::publisherTypes() { - std::vector types; - for (const auto& publisher : getInstance().event_pubs_) { - types.push_back(publisher.first); - } - return types; -} - -std::vector EventFactory::subscriberNames() { - std::vector 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(); - } - } - - // A small cool off helps OS API event publisher flushing. - if (!FLAGS_disable_events) { - ::usleep(400); - ef.threads_.clear(); - } -} - -void attachEvents() { - const auto& publishers = Registry::all("event_publisher"); - for (const auto& publisher : publishers) { - EventFactory::registerEventPublisher(publisher.second); - } - - const auto& subscribers = Registry::all("event_subscriber"); - for (const auto& subscriber : subscribers) { - auto status = EventFactory::registerEventSubscriber(subscriber.second); - if (!status.ok()) { - LOG(ERROR) << "Error registering subscriber: " << status.getMessage(); - } - } -} -} diff --git a/osquery/events/linux/inotify.cpp b/osquery/events/linux/inotify.cpp deleted file mode 100644 index 69c0845..0000000 --- a/osquery/events/linux/inotify.cpp +++ /dev/null @@ -1,298 +0,0 @@ -/* - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include - -#include -#include - -#include - -#include -#include - -#include "osquery/events/linux/inotify.h" - -namespace fs = boost::filesystem; - -namespace osquery { - -int kINotifyMLatency = 200; - -static const uint32_t BUFFER_SIZE = - (10 * ((sizeof(struct inotify_event)) + NAME_MAX + 1)); - -std::map 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"}, -}; - -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"); - } - return Status(0, "OK"); -} - -void INotifyEventPublisher::configure() { - 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); - if (sc->discovered_.size() > 0) { - continue; - } - - sc->discovered_ = sc->path; - if (sc->path.find("**") != std::string::npos) { - sc->recursive = true; - sc->discovered_ = sc->path.substr(0, sc->path.find("**")); - sc->path = sc->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) { - sc->discovered_ = fullpath.parent_path().string(); - } - - if (sc->discovered_.find('*') != std::string::npos) { - // If a wildcard exists within the tree (stem), resolve at configure - // time and monitor each path. - std::vector paths; - resolveFilePattern(sc->discovered_, paths); - for (const auto& _path : paths) { - addMonitor(_path, sc->recursive); - } - sc->recursive_match = sc->recursive; - continue; - } - } - addMonitor(sc->discovered_, sc->recursive); - } -} - -void INotifyEventPublisher::tearDown() { - ::close(inotify_handle_); - inotify_handle_ = -1; -} - -Status INotifyEventPublisher::restartMonitoring(){ - if (last_restart_ != 0 && getUnixTime() - last_restart_ < 10) { - return Status(1, "Overflow"); - } - last_restart_ = getUnixTime(); - VLOG(1) << "inotify was overflown, attempting to restart handle"; - for(const auto& desc : descriptors_){ - removeMonitor(desc, 1); - } - path_descriptors_.clear(); - descriptor_paths_.clear(); - configure(); - return Status(0, "OK"); -} - -Status INotifyEventPublisher::run() { - // Get a while wrapper for free. - char buffer[BUFFER_SIZE]; - fd_set set; - - FD_ZERO(&set); - FD_SET(getHandle(), &set); - - struct timeval timeout = {3, 3000}; - int selector = ::select(getHandle() + 1, &set, nullptr, nullptr, &timeout); - if (selector == -1) { - LOG(ERROR) << "Could not read inotify handle"; - return Status(1, "INotify handle failed"); - } - - if (selector == 0) { - // Read timeout. - return Status(0, "Continue"); - } - ssize_t record_num = ::read(getHandle(), buffer, BUFFER_SIZE); - if (record_num == 0 || record_num == -1) { - return Status(1, "INotify read failed"); - } - - for (char* p = buffer; p < buffer + record_num;) { - // Cast the inotify struct, make shared pointer, and append to contexts. - auto event = reinterpret_cast(p); - if (event->mask & IN_Q_OVERFLOW) { - // The inotify queue was overflown (remove all paths). - Status stat = restartMonitoring(); - if(!stat.ok()){ - return stat; - } - } - - 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); - fire(ec); - } - // Continue to iterate - p += (sizeof(struct inotify_event)) + event->len; - } - - osquery::publisherSleep(kINotifyMLatency); - return Status(0, "Continue"); -} - -INotifyEventContextRef INotifyEventPublisher::createEventContextFrom( - struct inotify_event* event) { - auto shared_event = std::make_shared(*event); - auto ec = createEventContext(); - ec->event = shared_event; - - // Get the pathname the watch fired on. - ec->path = descriptor_paths_[event->wd]; - 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->recursive && !sc->recursive_match) { - ssize_t found = ec->path.find(sc->path); - if (found != 0) { - return false; - } - } else if (fnmatch((sc->path + "*").c_str(), - ec->path.c_str(), - FNM_PATHNAME | FNM_CASEFOLD | - ((sc->recursive_match) ? FNM_LEADING_DIR : 0)) != 0) { - // Only apply a leading-dir match if this is a recursive watch with a - // match requirement (an inline wildcard with ending recursive wildcard). - 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(this)->addMonitor(ec->path + '/', true); - } - - return true; -} - -bool INotifyEventPublisher::addMonitor(const std::string& path, - bool recursive) { - if (!isPathMonitored(path)) { - int watch = ::inotify_add_watch(getHandle(), path.c_str(), IN_ALL_EVENTS); - if (watch == -1) { - LOG(ERROR) << "Could not add inotify watch on: " << path; - return false; - } - - // Keep a list of the watch descriptors - descriptors_.push_back(watch); - // Keep a map of the path -> watch descriptor - path_descriptors_[path] = watch; - // Keep a map of the opposite (descriptor -> path) - descriptor_paths_[watch] = path; - } - - if (recursive && isDirectory(path).ok()) { - std::vector children; - // Get a list of children of this directory (requested recursive watches). - listDirectoriesInDirectory(path, children); - - for (const auto& child : children) { - addMonitor(child, recursive); - } - } - - return true; -} - -bool INotifyEventPublisher::removeMonitor(const std::string& path, bool force) { - // If force then remove from INotify, otherwise cleanup file descriptors. - if (path_descriptors_.find(path) == path_descriptors_.end()) { - return false; - } - - int watch = path_descriptors_[path]; - path_descriptors_.erase(path); - descriptor_paths_.erase(watch); - - auto position = std::find(descriptors_.begin(), descriptors_.end(), watch); - descriptors_.erase(position); - - if (force) { - ::inotify_rm_watch(getHandle(), watch); - } - return true; -} - -bool INotifyEventPublisher::removeMonitor(int watch, bool force) { - if (descriptor_paths_.find(watch) == descriptor_paths_.end()) { - return false; - } - - auto path = descriptor_paths_[watch]; - return removeMonitor(path, force); -} - -bool INotifyEventPublisher::isPathMonitored(const std::string& path) { - boost::filesystem::path parent_path; - if (!isDirectory(path).ok()) { - if (path_descriptors_.find(path) != path_descriptors_.end()) { - // Path is a file, and is directly monitored. - return true; - } - if (!getDirectory(path, parent_path).ok()) { - // Could not get parent of unmonitored file. - return false; - } - } else { - parent_path = path; - } - - // Directory or parent of file monitoring - auto path_iterator = path_descriptors_.find(parent_path.string()); - return (path_iterator != path_descriptors_.end()); -} -} diff --git a/osquery/events/linux/inotify.h b/osquery/events/linux/inotify.h deleted file mode 100644 index 50d07b8..0000000 --- a/osquery/events/linux/inotify.h +++ /dev/null @@ -1,171 +0,0 @@ -/* - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#pragma once - -#include -#include - -#include -#include - -#include - -namespace osquery { - -extern std::map kMaskActions; - -/** - * @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; - /// Limit the `inotify` actions to the subscription mask (if not 0). - uint32_t mask; - /// Treat this path as a directory and subscription recursively. - bool recursive; - - INotifySubscriptionContext() - : mask(0), recursive(false), recursive_match(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: - /// During configure the INotify publisher may modify/optimize the paths. - std::string discovered_; - /// A configure-time pattern was expanded to match absolute paths. - bool recursive_match; - - private: - friend class INotifyEventPublisher; -}; - -/** - * @brief Event details for INotifyEventPublisher events. - */ -struct INotifyEventContext : public EventContext { - /// The inotify_event structure if the EventSubscriber want to interact. - std::shared_ptr event; - /// 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; - - INotifyEventContext() : event(nullptr), transaction_id(0) {} -}; - -typedef std::shared_ptr INotifyEventContextRef; -typedef std::shared_ptr - INotifySubscriptionContextRef; - -// Thread-safe containers -typedef std::vector DescriptorVector; -typedef std::map PathDescriptorMap; -typedef std::map DescriptorPathMap; - -/** - * @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 { - DECLARE_PUBLISHER("inotify"); - - public: - /// Create an `inotify` handle descriptor. - Status setUp(); - void configure(); - /// Release the `inotify` handle descriptor. - void tearDown(); - - Status run(); - - INotifyEventPublisher() - : EventPublisher(), inotify_handle_(-1), last_restart_(-1) {} - /// Check if the application-global `inotify` handle is alive. - bool isHandleOpen() { return inotify_handle_ > 0; } - - private: - INotifyEventContextRef createEventContextFrom(struct inotify_event* event); - - /// Check all added Subscription%s for a path. - bool isPathMonitored(const std::string& path); - - /// Add an INotify watch (monitor) on this path. - bool addMonitor(const std::string& path, bool recursive); - - /// Remove an INotify watch (monitor) from our tracking. - bool removeMonitor(const std::string& path, bool force = false); - bool removeMonitor(int watch, bool force = false); - - /// Given a SubscriptionContext and INotifyEventContext match path and action. - bool shouldFire(const INotifySubscriptionContextRef& mc, - const INotifyEventContextRef& ec) const; - - /// Get the INotify file descriptor. - int getHandle() { return inotify_handle_; } - - /// Get the number of actual INotify active descriptors. - int numDescriptors() { return descriptors_.size(); } - - /// If we overflow, try and restart the monitor - Status restartMonitoring(); - - // Consider an event queue if separating buffering from firing/servicing. - DescriptorVector descriptors_; - - /// Map of watched path string to inotify watch file descriptor. - PathDescriptorMap path_descriptors_; - - /// Map of inotify watch file descriptor to watched path string. - DescriptorPathMap descriptor_paths_; - - /// The inotify file descriptor handle. - int inotify_handle_; - - /// Time in seconds of the last inotify restart. - int last_restart_; - - public: - FRIEND_TEST(INotifyTests, test_inotify_optimization); -}; -} diff --git a/osquery/events/linux/tests/inotify_tests.cpp b/osquery/events/linux/tests/inotify_tests.cpp deleted file mode 100644 index 3557075..0000000 --- a/osquery/events/linux/tests/inotify_tests.cpp +++ /dev/null @@ -1,330 +0,0 @@ -/* - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include - -#include -#include -#include - -#include - -#include -#include -#include - -#include "osquery/events/linux/inotify.h" -#include "osquery/core/test_util.h" - -namespace osquery { - -const std::string kRealTestPath = kTestWorkingDirectory + "inotify-trigger"; -const std::string kRealTestDir = kTestWorkingDirectory + "inotify-triggers"; -const std::string kRealTestDirPath = kRealTestDir + "/1"; -const std::string kRealTestSubDir = kRealTestDir + "/2"; -const std::string kRealTestSubDirPath = kRealTestSubDir + "/1"; - -int kMaxEventLatency = 3000; - -class INotifyTests : public testing::Test { - protected: - void TearDown() { - // End the event loops, and join on the threads. - boost::filesystem::remove_all(kRealTestPath); - boost::filesystem::remove_all(kRealTestDir); - } - - void StartEventLoop() { - event_pub_ = std::make_shared(); - auto status = EventFactory::registerEventPublisher(event_pub_); - FILE* fd = fopen(kRealTestPath.c_str(), "w"); - fclose(fd); - temp_thread_ = boost::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 = 0, - EventCallback ec = 0) { - auto mc = std::make_shared(); - mc->path = path; - mc->mask = mask; - - EventFactory::addSubscription("inotify", "TestSubscriber", mc, ec); - } - - bool WaitForEvents(int max, int num_events = 0) { - int 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); - } - - std::shared_ptr event_pub_; - boost::thread temp_thread_; -}; - -TEST_F(INotifyTests, test_register_event_pub) { - auto pub = std::make_shared(); - auto status = EventFactory::registerEventPublisher(pub); - EXPECT_TRUE(status.ok()); - - // Make sure only one event type exists - EXPECT_EQ(EventFactory::numEventPublishers(), 1); - // 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(); - 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(); - EventFactory::registerEventPublisher(pub); - - // This subscription path is fake, and will succeed. - auto mc = std::make_shared(); - 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(); - EventFactory::registerEventPublisher(pub); - - // This subscription path *should* be real. - auto mc = std::make_shared(); - mc->path = "/"; - - auto subscription = Subscription::create("TestSubscriber", mc); - auto status = EventFactory::addSubscription("inotify", subscription); - EXPECT_TRUE(status.ok()); - EventFactory::deregisterEventPublisher("inotify"); -} - -class TestINotifyEventSubscriber - : public EventSubscriber { - public: - TestINotifyEventSubscriber() : callback_count_(0) { - setName("TestINotifyEventSubscriber"); - } - - Status init() { - callback_count_ = 0; - return Status(0, "OK"); - } - - Status SimpleCallback(const INotifyEventContextRef& ec, - const void* user_data) { - callback_count_ += 1; - return Status(0, "OK"); - } - - Status Callback(const INotifyEventContextRef& ec, const void* user_data) { - // The following comments are an example Callback routine. - // Row r; - // r["action"] = ec->action; - // r["path"] = ec->path; - - // Normally would call Add here. - actions_.push_back(ec->action); - callback_count_ += 1; - return Status(0, "OK"); - } - - SCRef GetSubscription(const std::string& path, uint32_t mask = 0) { - 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 actions() { return actions_; } - - int count() { return callback_count_; } - - public: - int callback_count_; - std::vector actions_; - - 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_recursion); -}; - -TEST_F(INotifyTests, test_inotify_run) { - // Assume event type is registered. - event_pub_ = std::make_shared(); - auto status = EventFactory::registerEventPublisher(event_pub_); - EXPECT_TRUE(status.ok()); - - // Create a temporary file to watch, open writeable - FILE* fd = fopen(kRealTestPath.c_str(), "w"); - - // Create a subscriber. - auto sub = std::make_shared(); - EventFactory::registerEventSubscriber(sub); - - // Create a subscriptioning context - auto mc = std::make_shared(); - mc->path = kRealTestPath; - status = EventFactory::addSubscription( - "inotify", Subscription::create("TestINotifyEventSubscriber", mc)); - EXPECT_TRUE(status.ok()); - - // Create an event loop thread (similar to main) - boost::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); -/// Result is different in linux distros. - 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(); - sub->init(); - - // Create a subscriptioning context, note the added Event to the symbol - auto sc = sub->GetSubscription(kRealTestPath, 0); - sub->subscribe(&TestINotifyEventSubscriber::SimpleCallback, sc, nullptr); - - TriggerEvent(kRealTestPath); - 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(); - sub->init(); - - auto sc = sub->GetSubscription(kRealTestPath, 0); - sub->subscribe(&TestINotifyEventSubscriber::Callback, sc, nullptr); - - TriggerEvent(kRealTestPath); - sub->WaitForEvents(kMaxEventLatency, 4); - - // Make sure the inotify action was expected. -/// Result is different in linux distros. - EXPECT_TRUE(sub->actions().size() >= 0); -/* - EXPECT_EQ(sub->actions().size(), 4); - EXPECT_EQ(sub->actions()[0], "UPDATED"); - EXPECT_EQ(sub->actions()[1], "OPENED"); - EXPECT_EQ(sub->actions()[2], "UPDATED"); - EXPECT_EQ(sub->actions()[3], "UPDATED"); -*/ - StopEventLoop(); -} - -TEST_F(INotifyTests, test_inotify_optimization) { - // Assume event type is registered. - StartEventLoop(); - boost::filesystem::create_directory(kRealTestDir); - - // Adding a descriptor to a directory will monitor files within. - SubscriptionAction(kRealTestDir); - EXPECT_TRUE(event_pub_->isPathMonitored(kRealTestDirPath)); - - // Adding a subscription to a file within a monitored directory is fine - // but this will NOT cause an additional INotify watch. - SubscriptionAction(kRealTestDirPath); - EXPECT_EQ(event_pub_->numDescriptors(), 1); - StopEventLoop(); -} - -TEST_F(INotifyTests, test_inotify_recursion) { - StartEventLoop(); - - auto sub = std::make_shared(); - sub->init(); - - boost::filesystem::create_directory(kRealTestDir); - boost::filesystem::create_directory(kRealTestSubDir); - - // Subscribe to the directory inode - auto mc = sub->createSubscriptionContext(); - mc->path = kRealTestDir; - mc->recursive = true; - sub->subscribe(&TestINotifyEventSubscriber::Callback, mc, nullptr); - - // Trigger on a subdirectory's file. - TriggerEvent(kRealTestSubDirPath); - - sub->WaitForEvents(kMaxEventLatency, 1); - EXPECT_TRUE(sub->count() > 0); - StopEventLoop(); -} -} diff --git a/osquery/events/linux/udev.cpp b/osquery/events/linux/udev.cpp deleted file mode 100644 index 2696a0c..0000000 --- a/osquery/events/linux/udev.cpp +++ /dev/null @@ -1,160 +0,0 @@ -/* - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include -#include -#include - -#include "osquery/events/linux/udev.h" - -namespace osquery { - -int kUdevMLatency = 200; - -REGISTER(UdevEventPublisher, "event_publisher", "udev"); - -Status UdevEventPublisher::setUp() { - // Create the udev object. - handle_ = udev_new(); - if (!handle_) { - return Status(1, "Could not create udev object."); - } - - // Set up the udev monitor before scanning/polling. - monitor_ = udev_monitor_new_from_netlink(handle_, "udev"); - udev_monitor_enable_receiving(monitor_); - - return Status(0, "OK"); -} - -void UdevEventPublisher::configure() {} - -void UdevEventPublisher::tearDown() { - if (monitor_ != nullptr) { - udev_monitor_unref(monitor_); - } - - if (handle_ != nullptr) { - udev_unref(handle_); - } -} - -Status UdevEventPublisher::run() { - int fd = udev_monitor_get_fd(monitor_); - fd_set set; - - FD_ZERO(&set); - FD_SET(fd, &set); - - struct timeval timeout = {3, 3000}; - int selector = ::select(fd + 1, &set, nullptr, nullptr, &timeout); - if (selector == -1) { - LOG(ERROR) << "Could not read udev monitor"; - return Status(1, "udev monitor failed."); - } - - if (selector == 0 || !FD_ISSET(fd, &set)) { - // Read timeout. - return Status(0, "Timeout"); - } - - 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); - - osquery::publisherSleep(kUdevMLatency); - return Status(0, "Continue"); -} - -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; -} -} diff --git a/osquery/events/linux/udev.h b/osquery/events/linux/udev.h deleted file mode 100644 index 7ce8deb..0000000 --- a/osquery/events/linux/udev.h +++ /dev/null @@ -1,120 +0,0 @@ -/* - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#pragma once - -#include - -#include -#include - -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; - /// 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; -}; - -typedef std::shared_ptr UdevEventContextRef; -typedef std::shared_ptr UdevSubscriptionContextRef; - -/** - * @brief A Linux `udev` EventPublisher. - * - */ -class UdevEventPublisher - : public EventPublisher { - DECLARE_PUBLISHER("udev"); - - public: - Status setUp(); - void configure(); - void tearDown(); - - Status run(); - - UdevEventPublisher() : EventPublisher() { - handle_ = nullptr; - monitor_ = nullptr; - } - - /** - * @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_; - struct udev_monitor *monitor_; - - private: - /// Check subscription details. - bool shouldFire(const UdevSubscriptionContextRef& mc, - const UdevEventContextRef& ec) const; - /// Helper function to create an EventContext using a udev_device pointer. - UdevEventContextRef createEventContextFrom(struct udev_device* device); -}; -} diff --git a/osquery/events/tests/events_database_tests.cpp b/osquery/events/tests/events_database_tests.cpp deleted file mode 100644 index 68314a7..0000000 --- a/osquery/events/tests/events_database_tests.cpp +++ /dev/null @@ -1,158 +0,0 @@ -/* - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include -#include - -#include - -#include -#include - -#include "osquery/database/db_handle.h" - -namespace osquery { - -//const std::string kTestingEventsDBPath = "/tmp/rocksdb-osquery-testevents"; - -class EventsDatabaseTests : public ::testing::Test {}; - -class FakeEventPublisher - : public EventPublisher { - DECLARE_PUBLISHER("FakePublisher"); -}; - -class FakeEventSubscriber : public EventSubscriber { - public: - FakeEventSubscriber() { setName("FakeSubscriber"); } - /// Add a fake event at time t - Status testAdd(int t) { - Row r; - r["testing"] = "hello from space"; - return add(r, t); - } -}; - -TEST_F(EventsDatabaseTests, test_event_module_id) { - auto sub = std::make_shared(); - sub->doNotExpire(); - - // Not normally available outside of EventSubscriber->Add(). - auto event_id1 = sub->getEventID(); - EXPECT_EQ(event_id1, "1"); - auto event_id2 = sub->getEventID(); - EXPECT_EQ(event_id2, "2"); -} - -TEST_F(EventsDatabaseTests, test_event_add) { - auto sub = std::make_shared(); - auto status = sub->testAdd(1); - EXPECT_TRUE(status.ok()); -} - -TEST_F(EventsDatabaseTests, test_record_indexing) { - auto sub = std::make_shared(); - 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(output, "3600.0, 3600.1, 3600.2"); - - // Restrict range to "most specific". - indexes = sub->getIndexes(0, 5); - output = boost::algorithm::join(indexes, ", "); - EXPECT_EQ(output, "10.0"); - - // Get a mix of indexes for the lower bounding. - indexes = sub->getIndexes(2, (3 * 3600)); - output = boost::algorithm::join(indexes, ", "); - EXPECT_EQ(output, "10.0, 10.1, 3600.1, 3600.2, 60.1"); - - // Rare, but test ONLY intermediate indexes. - indexes = sub->getIndexes(2, (3 * 3600), 1); - output = boost::algorithm::join(indexes, ", "); - EXPECT_EQ(output, "60.0, 60.1, 60.120, 60.60"); - - // 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(output, "10.726, 60.120"); - - // Request specific lower and upper bounding. - indexes = sub->getIndexes(2, (2 * 3600) + 62); - output = boost::algorithm::join(indexes, ", "); - EXPECT_EQ(output, "10.0, 10.1, 10.726, 3600.1, 60.1, 60.120"); -} - -TEST_F(EventsDatabaseTests, test_record_range) { - auto sub = std::make_shared(); - - // Search within a specific record range. - auto indexes = sub->getIndexes(0, 10); - auto records = sub->getRecords(indexes); - EXPECT_EQ(records.size(), 2); // 1, 2 - - // 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(records.size(), 5); // 1, 2, 11, 61, 3601 - - // Get all of the records. - indexes = sub->getIndexes(0, 3 * 3600); - records = sub->getRecords(indexes); - EXPECT_EQ(records.size(), 8); // 1, 2, 11, 61, 3601, 7201, 7211, 7261 - - // stop = 0 is an alias for everything. - indexes = sub->getIndexes(0, 0); - records = sub->getRecords(indexes); - EXPECT_EQ(records.size(), 8); -} - -TEST_F(EventsDatabaseTests, test_record_expiration) { - auto sub = std::make_shared(); - - // No expiration - auto indexes = sub->getIndexes(0, 5000); - auto records = sub->getRecords(indexes); - EXPECT_EQ(records.size(), 5); // 1, 2, 11, 61, 3601 - - sub->expire_events_ = true; - sub->expire_time_ = 10; - indexes = sub->getIndexes(0, 5000); - records = sub->getRecords(indexes); - EXPECT_EQ(records.size(), 3); // 11, 61, 3601 - - indexes = sub->getIndexes(0, 5000, 0); - records = sub->getRecords(indexes); - EXPECT_EQ(records.size(), 3); // 11, 61, 3601 - - indexes = sub->getIndexes(0, 5000, 1); - records = sub->getRecords(indexes); - EXPECT_EQ(records.size(), 3); // 11, 61, 3601 - - indexes = sub->getIndexes(0, 5000, 2); - records = sub->getRecords(indexes); - EXPECT_EQ(records.size(), 3); // 11, 61, 3601 - - // Check that get/deletes did not act on cache. - sub->expire_time_ = 0; - indexes = sub->getIndexes(0, 5000); - records = sub->getRecords(indexes); - EXPECT_EQ(records.size(), 3); // 11, 61, 3601 -} -} diff --git a/osquery/events/tests/events_tests.cpp b/osquery/events/tests/events_tests.cpp deleted file mode 100644 index 5a983b1..0000000 --- a/osquery/events/tests/events_tests.cpp +++ /dev/null @@ -1,385 +0,0 @@ -/* - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include - -#include - -#include - -#include -#include - -#include "osquery/database/db_handle.h" - -namespace osquery { - -const std::string kTestingEventsDBPath = "/tmp/rocksdb-osquery-testevents"; - -class EventsTests : public ::testing::Test { - public: - void SetUp() { - // Setup a testing DB instance - DBHandle::getInstanceAtPath(kTestingEventsDBPath); - } - - void TearDown() { - EventFactory::end(); - boost::filesystem::remove_all(osquery::kTestingEventsDBPath); - } -}; - -// The most basic event publisher uses useless Subscription/Event. -class BasicEventPublisher - : public EventPublisher {}; - -class AnotherBasicEventPublisher - : public EventPublisher {}; - -// 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. -typedef std::shared_ptr FakeSubscriptionContextRef; -typedef std::shared_ptr FakeEventContextRef; - -// Now a publisher with a type. -class FakeEventPublisher - : public EventPublisher { - DECLARE_PUBLISHER("FakePublisher"); -}; - -class AnotherFakeEventPublisher - : public EventPublisher { - DECLARE_PUBLISHER("AnotherFakePublisher"); -}; - -TEST_F(EventsTests, test_event_pub) { - auto pub = std::make_shared(); - 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_pub) { - auto basic_pub = std::make_shared(); - auto status = EventFactory::registerEventPublisher(basic_pub); - - // This class is the SAME, there was no type override. - auto another_basic_pub = std::make_shared(); - 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(); - status = EventFactory::registerEventPublisher(fake_pub); - EXPECT_TRUE(status.ok()); - - // May also register the event_pub instance - auto another_fake_pub = std::make_shared(); - status = EventFactory::registerEventPublisher(another_fake_pub); - EXPECT_TRUE(status.ok()); -} - -TEST_F(EventsTests, test_event_pub_types) { - auto pub = std::make_shared(); - EXPECT_EQ(pub->type(), "FakePublisher"); - - EventFactory::registerEventPublisher(pub); - auto pub2 = EventFactory::getEventPublisher("FakePublisher"); - EXPECT_EQ(pub->type(), pub2->type()); -} - -TEST_F(EventsTests, test_create_event_pub) { - auto pub = std::make_shared(); - auto status = EventFactory::registerEventPublisher(pub); - EXPECT_TRUE(status.ok()); - - // Make sure only the first event type was recorded. - EXPECT_EQ(EventFactory::numEventPublishers(), 1); -} - -class UniqueEventPublisher - : public EventPublisher { - 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(), 0); - attachEvents(); - - // Store the number of default event publishers (in core). - int default_publisher_count = EventFactory::numEventPublishers(); - - // Now add another registry item, but do not yet attach it. - auto UniqueEventPublisherRegistryItem = - Registry::add("event_publisher", "unique"); - EXPECT_EQ(EventFactory::numEventPublishers(), default_publisher_count); - - // Now attach and make sure it was added. - attachEvents(); - EXPECT_EQ(EventFactory::numEventPublishers(), default_publisher_count + 1); -} - -TEST_F(EventsTests, test_create_subscription) { - auto pub = std::make_shared(); - 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("publisher", subscription); - EXPECT_TRUE(status.ok()); - - // Make sure the subscription is added. - EXPECT_EQ(EventFactory::numSubscriptions("publisher"), 1); -} - -TEST_F(EventsTests, test_multiple_subscriptions) { - Status status; - - auto pub = std::make_shared(); - EventFactory::registerEventPublisher(pub); - - auto subscription = Subscription::create("subscriber"); - status = EventFactory::addSubscription("publisher", subscription); - status = EventFactory::addSubscription("publisher", subscription); - - EXPECT_EQ(EventFactory::numSubscriptions("publisher"), 2); -} - -struct TestSubscriptionContext : public SubscriptionContext { - int smallest; -}; - -class TestEventPublisher - : public EventPublisher { - DECLARE_PUBLISHER("TestPublisher"); - - public: - Status setUp() { - smallest_ever_ += 1; - return Status(0, "OK"); - } - - void configure() { - 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() { smallest_ever_ += 1; } - - TestEventPublisher() : EventPublisher() { - smallest_ever_ = 0; - configure_run = false; - } - - // Custom methods do not make sense, but for testing it exists. - int getTestValue() { return smallest_ever_; } - - public: - bool configure_run; - - private: - int smallest_ever_; -}; - -TEST_F(EventsTests, test_create_custom_event_pub) { - auto basic_pub = std::make_shared(); - EventFactory::registerEventPublisher(basic_pub); - auto pub = std::make_shared(); - auto status = EventFactory::registerEventPublisher(pub); - - // These event types have unique event type IDs - EXPECT_TRUE(status.ok()); - EXPECT_EQ(EventFactory::numEventPublishers(), 2); - - // Make sure the setUp function was called. - EXPECT_EQ(pub->getTestValue(), 1); -} - -TEST_F(EventsTests, test_custom_subscription) { - // Step 1, register event type - auto pub = std::make_shared(); - auto status = EventFactory::registerEventPublisher(pub); - - // Step 2, create and configure a subscription context - auto sc = std::make_shared(); - 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(), 1); - - // The event type must run configure for each added subscription. - EXPECT_TRUE(pub->configure_run); - EXPECT_EQ(pub->getTestValue(), -1); -} - -TEST_F(EventsTests, test_tear_down) { - auto pub = std::make_shared(); - auto status = EventFactory::registerEventPublisher(pub); - - // Make sure set up incremented the test value. - EXPECT_EQ(pub->getTestValue(), 1); - - status = EventFactory::deregisterEventPublisher("TestPublisher"); - EXPECT_TRUE(status.ok()); - - // Make sure tear down inremented the test value. - EXPECT_EQ(pub->getTestValue(), 2); - - // Once more, now deregistering all event types. - status = EventFactory::registerEventPublisher(pub); - EXPECT_EQ(pub->getTestValue(), 3); - EventFactory::end(); - EXPECT_EQ(pub->getTestValue(), 4); - - // Make sure the factory state represented. - EXPECT_EQ(EventFactory::numEventPublishers(), 0); -} - -static int kBellHathTolled = 0; - -Status TestTheeCallback(EventContextRef context, const void* user_data) { - kBellHathTolled += 1; - return Status(0, "OK"); -} - -class FakeEventSubscriber : public EventSubscriber { - public: - bool bellHathTolled; - bool contextBellHathTolled; - bool shouldFireBethHathTolled; - - FakeEventSubscriber() { - setName("FakeSubscriber"); - bellHathTolled = false; - contextBellHathTolled = false; - shouldFireBethHathTolled = false; - } - - Status Callback(const EventContextRef& ec, const void* user_data) { - // We don't care about the subscription or the event contexts. - bellHathTolled = true; - return Status(0, "OK"); - } - - Status SpecialCallback(const FakeEventContextRef& ec, const void* user_data) { - // Now we care that the event context is corrected passed. - if (ec->required_value == 42) { - contextBellHathTolled = true; - } - return Status(0, "OK"); - } - - void lateInit() { - auto sub_ctx = createSubscriptionContext(); - subscribe(&FakeEventSubscriber::Callback, sub_ctx, nullptr); - } - - void laterInit() { - auto sub_ctx = createSubscriptionContext(); - sub_ctx->require_this_value = 42; - subscribe(&FakeEventSubscriber::SpecialCallback, sub_ctx, nullptr); - } -}; - -TEST_F(EventsTests, test_event_sub) { - auto sub = std::make_shared(); - EXPECT_EQ(sub->getType(), "FakePublisher"); - EXPECT_EQ(sub->getName(), "FakeSubscriber"); -} - -TEST_F(EventsTests, test_event_sub_subscribe) { - auto pub = std::make_shared(); - EventFactory::registerEventPublisher(pub); - - auto sub = std::make_shared(); - EventFactory::registerEventSubscriber(sub); - - // Don't overload the normal `init` Subscription member. - sub->lateInit(); - EXPECT_EQ(pub->numSubscriptions(), 1); - - auto ec = pub->createEventContext(); - pub->fire(ec, 0); - - EXPECT_TRUE(sub->bellHathTolled); -} - -TEST_F(EventsTests, test_event_sub_context) { - auto pub = std::make_shared(); - EventFactory::registerEventPublisher(pub); - - auto sub = std::make_shared(); - EventFactory::registerEventSubscriber(sub); - - sub->laterInit(); - auto ec = pub->createEventContext(); - ec->required_value = 42; - pub->fire(ec, 0); - - EXPECT_TRUE(sub->contextBellHathTolled); -} - -TEST_F(EventsTests, test_fire_event) { - Status status; - - auto pub = std::make_shared(); - status = EventFactory::registerEventPublisher(pub); - - auto sub = std::make_shared(); - auto subscription = Subscription::create("FakeSubscriber"); - subscription->callback = TestTheeCallback; - status = EventFactory::addSubscription("publisher", subscription); - - // 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("FakeSubscriber"); - status = EventFactory::addSubscription("publisher", second_subscription); - - // 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); -} -} diff --git a/osquery/examples/example_extension.cpp b/osquery/examples/example_extension.cpp deleted file mode 100644 index b618b96..0000000 --- a/osquery/examples/example_extension.cpp +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include - -using namespace osquery; - -class ExampleConfigPlugin : public ConfigPlugin { - public: - Status setUp() { - LOG(WARNING) << "ExampleConfigPlugin setting up."; - return Status(0, "OK"); - } - - Status genConfig(std::map& config) { - config["data"] = "{\"options\": [], \"scheduledQueries\": []}"; - return Status(0, "OK"); - } -}; - -class ExampleTable : public TablePlugin { - private: - TableColumns columns() const { - return {{"example_text", "TEXT"}, {"example_integer", "INTEGER"}}; - } - - QueryData generate(QueryContext& request) { - QueryData results; - - Row r; - r["example_text"] = "example"; - r["example_integer"] = INTEGER(1); - - results.push_back(r); - return results; - } -}; - -REGISTER_EXTERNAL(ExampleConfigPlugin, "config", "example"); -REGISTER_EXTERNAL(ExampleTable, "table", "example"); - -int main(int argc, char* argv[]) { - osquery::Initializer runner(argc, argv, OSQUERY_EXTENSION); - - auto status = startExtension("example", "0.0.1"); - if (!status.ok()) { - LOG(ERROR) << status.getMessage(); - } - - // Finally shutdown. - runner.shutdown(); - return 0; -} diff --git a/osquery/examples/example_module.cpp b/osquery/examples/example_module.cpp deleted file mode 100644 index 38a2167..0000000 --- a/osquery/examples/example_module.cpp +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include - -using namespace osquery; - -class ExampleTable : public TablePlugin { - private: - TableColumns columns() const { - return {{"example_text", "TEXT"}, {"example_integer", "INTEGER"}}; - } - - QueryData generate(QueryContext& request) { - QueryData results; - - Row r; - r["example_text"] = "example"; - r["example_integer"] = INTEGER(1); - - results.push_back(r); - return results; - } -}; - -// Create the module if the environment variable TESTFAIL1 is not defined. -// This allows the integration tests to, at run time, test the module -// loading workflow. -CREATE_MODULE_IF(getenv("TESTFAIL1") == nullptr, "example", "0.0.1", "0.0.0"); - -void initModule(void) { - // Register a plugin from a module if the environment variable TESTFAIL2 - // is not defined. - if (getenv("TESTFAIL2") == nullptr) { - REGISTER_MODULE(ExampleTable, "table", "example"); - } -} diff --git a/osquery/extensions/CMakeLists.txt b/osquery/extensions/CMakeLists.txt deleted file mode 100644 index fb575ab..0000000 --- a/osquery/extensions/CMakeLists.txt +++ /dev/null @@ -1,38 +0,0 @@ -# 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 - -FIND_PROGRAM(THRIFT_COMPILER thrift /usr/local/bin - /usr/bin - NO_DEFAULT_PATH) - -# Generate the thrift intermediate/interface code. -FILE(MAKE_DIRECTORY "${CMAKE_BINARY_DIR}/generated") -ADD_CUSTOM_COMMAND( - COMMAND - ${THRIFT_COMPILER} --gen cpp:dense "${CMAKE_SOURCE_DIR}/osquery.thrift" - DEPENDS - "${CMAKE_SOURCE_DIR}/osquery.thrift" - WORKING_DIRECTORY - "${CMAKE_BINARY_DIR}/generated" - OUTPUT - ${OSQUERY_THRIFT_GENERATED_FILES}) - -ADD_OSQUERY_LIBRARY(osquery_extensions ${OSQUERY_THRIFT_GENERATED_FILES} - extensions.cpp - interface.cpp) - -FILE(GLOB OSQUERY_EXTENSIONS_TESTS "tests/*.cpp") - -# TODO: Resolve failed cases -#ADD_OSQUERY_TEST(${OSQUERY_EXTENSIONS_TESTS}) diff --git a/osquery/extensions/extensions.cpp b/osquery/extensions/extensions.cpp deleted file mode 100644 index 7ca2ea9..0000000 --- a/osquery/extensions/extensions.cpp +++ /dev/null @@ -1,556 +0,0 @@ -/* - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include - -#include - -#include -#include -#include -#include -#include - -#include "osquery/extensions/interface.h" -#include "osquery/core/watcher.h" - -using namespace osquery::extensions; - -namespace fs = boost::filesystem; - -namespace osquery { - -// Millisecond latency between initalizing manager pings. -const size_t kExtensionInitializeLatencyUS = 20000; - -#ifdef __APPLE__ -const std::string kModuleExtension = ".dylib"; -#else -const std::string kModuleExtension = ".so"; -#endif - -CLI_FLAG(bool, disable_extensions, false, "Disable extension API"); - -CLI_FLAG(string, - extensions_socket, - "/var/osquery/osquery.em", - "Path to the extensions UNIX domain socket") - -CLI_FLAG(string, - extensions_autoload, - "/etc/osquery/extensions.load", - "Optional path to a list of autoloaded & managed extensions") - -CLI_FLAG(string, - extensions_timeout, - "3", - "Seconds to wait for autoloaded extensions"); - -CLI_FLAG(string, - extensions_interval, - "3", - "Seconds delay between connectivity checks") - -CLI_FLAG(string, - modules_autoload, - "/etc/osquery/modules.load", - "Optional path to a list of autoloaded registry modules") - -/** - * @brief Alias the extensions_socket (used by core) to a simple 'socket'. - * - * Extension binaries will more commonly set the path to an extension manager - * socket. Alias the long switch name to 'socket' for an easier UX. - * - * We include timeout and interval, where the 'extensions_' prefix is removed - * in the alias since we are already within the context of an extension. - */ -EXTENSION_FLAG_ALIAS(socket, extensions_socket); -EXTENSION_FLAG_ALIAS(timeout, extensions_timeout); -EXTENSION_FLAG_ALIAS(interval, extensions_interval); - -void ExtensionWatcher::start() { - // Watch the manager, if the socket is removed then the extension will die. - while (true) { - watch(); - interruptableSleep(interval_); - } -} - -void ExtensionWatcher::exitFatal(int return_code) { - // Exit the extension. - ::exit(return_code); -} - -void ExtensionWatcher::watch() { - ExtensionStatus status; - try { - auto client = EXManagerClient(path_); - // Ping the extension manager until it goes down. - client.get()->ping(status); - } catch (const std::exception& e) { - LOG(WARNING) << "Extension watcher ending: osquery core has gone away"; - exitFatal(0); - } - - if (status.code != ExtensionCode::EXT_SUCCESS && fatal_) { - exitFatal(); - } -} - -void ExtensionManagerWatcher::watch() { - // Watch the set of extensions, if the socket is removed then the extension - // will be deregistered. - const auto uuids = Registry::routeUUIDs(); - - ExtensionStatus status; - for (const auto& uuid : uuids) { - try { - auto client = EXClient(getExtensionSocket(uuid)); - // Ping the extension until it goes down. - client.get()->ping(status); - } catch (const std::exception& e) { - failures_[uuid] += 1; - continue; - } - - if (status.code != ExtensionCode::EXT_SUCCESS) { - LOG(INFO) << "Extension UUID " << uuid << " ping failed"; - failures_[uuid] += 1; - } else { - failures_[uuid] = 0; - } - } - - for (const auto& uuid : failures_) { - if (uuid.second >= 3) { - LOG(INFO) << "Extension UUID " << uuid.first << " has gone away"; - Registry::removeBroadcast(uuid.first); - failures_[uuid.first] = 0; - } - } -} - -inline Status socketWritable(const fs::path& path) { - if (pathExists(path).ok()) { - if (!isWritable(path).ok()) { - return Status(1, "Cannot write extension socket: " + path.string()); - } - - if (!remove(path).ok()) { - return Status(1, "Cannot remove extension socket: " + path.string()); - } - } else { - if (!pathExists(path.parent_path()).ok()) { - return Status(1, "Extension socket directory missing: " + path.string()); - } - - if (!isWritable(path.parent_path()).ok()) { - return Status(1, "Cannot write extension socket: " + path.string()); - } - } - return Status(0, "OK"); -} - -void loadExtensions() { - // Disabling extensions will disable autoloading. - if (FLAGS_disable_extensions) { - return; - } - - // Optionally autoload extensions - auto status = loadExtensions(FLAGS_extensions_autoload); - if (!status.ok()) { - VLOG(1) << "Could not autoload extensions: " << status.what(); - } -} - -void loadModules() { - auto status = loadModules(FLAGS_modules_autoload); - if (!status.ok()) { - VLOG(1) << "Could not autoload modules: " << status.what(); - } -} - -Status loadExtensions(const std::string& loadfile) { - std::string autoload_paths; - if (readFile(loadfile, autoload_paths).ok()) { - for (auto& path : osquery::split(autoload_paths, "\n")) { - boost::trim(path); - if (path.size() > 0 && path[0] != '#' && path[0] != ';') { - Watcher::addExtensionPath(path); - } - } - return Status(0, "OK"); - } - return Status(1, "Failed reading: " + loadfile); -} - -Status loadModuleFile(const std::string& path) { - fs::path module(path); - if (safePermissions(module.parent_path().string(), path)) { - if (module.extension().string() == kModuleExtension) { - // Silently allow module load failures to drop. - RegistryModuleLoader loader(module.string()); - loader.init(); - return Status(0, "OK"); - } - } - return Status(1, "Module check failed"); -} - -Status loadModules(const std::string& loadfile) { - // Split the search path for modules using a ':' delimiter. - std::string autoload_paths; - if (readFile(loadfile, autoload_paths).ok()) { - auto status = Status(0, "OK"); - for (auto& module_path : osquery::split(autoload_paths, "\n")) { - boost::trim(module_path); - auto path_status = loadModuleFile(module_path); - if (!path_status.ok()) { - status = path_status; - } - } - // Return an aggregate failure if any load fails (invalid search path). - return status; - } - return Status(1, "Failed reading: " + loadfile); -} - -Status extensionPathActive(const std::string& path, bool use_timeout = false) { - // Make sure the extension manager path exists, and is writable. - size_t delay = 0; - // The timeout is given in seconds, but checked interval is microseconds. - size_t timeout = atoi(FLAGS_extensions_timeout.c_str()) * 1000000; - if (timeout < kExtensionInitializeLatencyUS * 10) { - timeout = kExtensionInitializeLatencyUS * 10; - } - do { - if (pathExists(path) && isWritable(path)) { - try { - auto client = EXManagerClient(path); - return Status(0, "OK"); - } catch (const std::exception& e) { - // Path might exist without a connected extension or extension manager. - } - } - // Only check active once if this check does not allow a timeout. - if (!use_timeout || timeout == 0) { - break; - } - // Increase the total wait detail. - delay += kExtensionInitializeLatencyUS; - ::usleep(kExtensionInitializeLatencyUS); - } while (delay < timeout); - return Status(1, "Extension socket not available: " + path); -} - -Status startExtension(const std::string& name, const std::string& version) { - return startExtension(name, version, "0.0.0"); -} - -Status startExtension(const std::string& name, - const std::string& version, - const std::string& min_sdk_version) { - Registry::setExternal(); - // Latency converted to milliseconds, used as a thread interruptible. - auto latency = atoi(FLAGS_extensions_interval.c_str()) * 1000; - auto status = startExtensionWatcher(FLAGS_extensions_socket, latency, true); - if (!status.ok()) { - // If the threaded watcher fails to start, fail the extension. - return status; - } - - status = startExtension( -// TODO(Sangwan): Sync with upstream code - FLAGS_extensions_socket, name, version, min_sdk_version, "1.4.1"); -// HotFix: Below upstream code makes undefined error. -// FLAGS_extensions_socket, name, version, min_sdk_version, kSDKVersion); - if (!status.ok()) { - // If the extension failed to start then the EM is most likely unavailable. - return status; - } - - try { - // The extension does nothing but serve the thrift API. - // Join on both the thrift and extension manager watcher services. - Dispatcher::joinServices(); - } catch (const std::exception& e) { - // The extension manager may shutdown without notifying the extension. - return Status(0, e.what()); - } - - // An extension will only return on failure. - return Status(0, "Extension was shutdown"); -} - -Status startExtension(const std::string& manager_path, - const std::string& name, - const std::string& version, - const std::string& min_sdk_version, - const std::string& sdk_version) { - // Make sure the extension manager path exists, and is writable. - auto status = extensionPathActive(manager_path, true); - if (!status.ok()) { - return status; - } - - // The Registry broadcast is used as the ExtensionRegistry. - auto broadcast = Registry::getBroadcast(); - // The extension will register and provide name, version, sdk details. - InternalExtensionInfo info; - info.name = name; - info.version = version; - info.sdk_version = sdk_version; - info.min_sdk_version = min_sdk_version; - - // If registration is successful, we will also request the manager's options. - InternalOptionList options; - // Register the extension's registry broadcast with the manager. - ExtensionStatus ext_status; - try { - auto client = EXManagerClient(manager_path); - client.get()->registerExtension(ext_status, info, broadcast); - // The main reason for a failed registry is a duplicate extension name - // (the extension process is already running), or the extension broadcasts - // a duplicate registry item. - if (ext_status.code != ExtensionCode::EXT_SUCCESS) { - return Status(ext_status.code, ext_status.message); - } - // Request the core options, mainly to set the active registry plugins for - // logger and config. - client.get()->options(options); - } catch (const std::exception& e) { - return Status(1, "Extension register failed: " + std::string(e.what())); - } - - // Now that the uuid is known, try to clean up stale socket paths. - auto extension_path = getExtensionSocket(ext_status.uuid, manager_path); - status = socketWritable(extension_path); - if (!status) { - return status; - } - - // Set the active config and logger plugins. The core will arbitrate if the - // plugins are not available in the extension's local registry. - Registry::setActive("config", options["config_plugin"].value); - Registry::setActive("logger", options["logger_plugin"].value); - // Set up all lazy registry plugins and the active config/logger plugin. - Registry::setUp(); - - // Start the extension's Thrift server - Dispatcher::addService( - std::make_shared(manager_path, ext_status.uuid)); - VLOG(1) << "Extension (" << name << ", " << ext_status.uuid << ", " << version - << ", " << sdk_version << ") registered"; - return Status(0, std::to_string(ext_status.uuid)); -} - -Status queryExternal(const std::string& manager_path, - const std::string& query, - QueryData& results) { - // Make sure the extension path exists, and is writable. - auto status = extensionPathActive(manager_path); - if (!status.ok()) { - return status; - } - - ExtensionResponse response; - try { - auto client = EXManagerClient(manager_path); - client.get()->query(response, query); - } catch (const std::exception& e) { - return Status(1, "Extension call failed: " + std::string(e.what())); - } - - for (const auto& row : response.response) { - results.push_back(row); - } - - return Status(response.status.code, response.status.message); -} - -Status queryExternal(const std::string& query, QueryData& results) { - return queryExternal(FLAGS_extensions_socket, query, results); -} - -Status getQueryColumnsExternal(const std::string& manager_path, - const std::string& query, - TableColumns& columns) { - // Make sure the extension path exists, and is writable. - auto status = extensionPathActive(manager_path); - if (!status.ok()) { - return status; - } - - ExtensionResponse response; - try { - auto client = EXManagerClient(manager_path); - client.get()->getQueryColumns(response, query); - } catch (const std::exception& e) { - return Status(1, "Extension call failed: " + std::string(e.what())); - } - - // Translate response map: {string: string} to a vector: pair(name, type). - for (const auto& column : response.response) { - for (const auto& column_detail : column) { - columns.push_back(make_pair(column_detail.first, column_detail.second)); - } - } - - return Status(response.status.code, response.status.message); -} - -Status getQueryColumnsExternal(const std::string& query, - TableColumns& columns) { - return getQueryColumnsExternal(FLAGS_extensions_socket, query, columns); -} - -Status pingExtension(const std::string& path) { - if (FLAGS_disable_extensions) { - return Status(1, "Extensions disabled"); - } - - // Make sure the extension path exists, and is writable. - auto status = extensionPathActive(path); - if (!status.ok()) { - return status; - } - - ExtensionStatus ext_status; - try { - auto client = EXClient(path); - client.get()->ping(ext_status); - } catch (const std::exception& e) { - return Status(1, "Extension call failed: " + std::string(e.what())); - } - - return Status(ext_status.code, ext_status.message); -} - -Status getExtensions(ExtensionList& extensions) { - if (FLAGS_disable_extensions) { - return Status(1, "Extensions disabled"); - } - return getExtensions(FLAGS_extensions_socket, extensions); -} - -Status getExtensions(const std::string& manager_path, - ExtensionList& extensions) { - // Make sure the extension path exists, and is writable. - auto status = extensionPathActive(manager_path); - if (!status.ok()) { - return status; - } - - InternalExtensionList ext_list; - try { - auto client = EXManagerClient(manager_path); - client.get()->extensions(ext_list); - } catch (const std::exception& e) { - return Status(1, "Extension call failed: " + std::string(e.what())); - } - - // Add the extension manager to the list called (core). - extensions[0] = {"core", kVersion, "0.0.0", kSDKVersion}; - - // Convert from Thrift-internal list type to RouteUUID/ExtenionInfo type. - for (const auto& ext : ext_list) { - extensions[ext.first] = {ext.second.name, - ext.second.version, - ext.second.min_sdk_version, - ext.second.sdk_version}; - } - - return Status(0, "OK"); -} - -Status callExtension(const RouteUUID uuid, - const std::string& registry, - const std::string& item, - const PluginRequest& request, - PluginResponse& response) { - if (FLAGS_disable_extensions) { - return Status(1, "Extensions disabled"); - } - return callExtension( - getExtensionSocket(uuid), registry, item, request, response); -} - -Status callExtension(const std::string& extension_path, - const std::string& registry, - const std::string& item, - const PluginRequest& request, - PluginResponse& response) { - // Make sure the extension manager path exists, and is writable. - auto status = extensionPathActive(extension_path); - if (!status.ok()) { - return status; - } - - ExtensionResponse ext_response; - try { - auto client = EXClient(extension_path); - client.get()->call(ext_response, registry, item, request); - } - catch (const std::exception& e) { - return Status(1, "Extension call failed: " + std::string(e.what())); - } - - // Convert from Thrift-internal list type to PluginResponse type. - if (ext_response.status.code == ExtensionCode::EXT_SUCCESS) { - for (const auto& item : ext_response.response) { - response.push_back(item); - } - } - return Status(ext_response.status.code, ext_response.status.message); -} - -Status startExtensionWatcher(const std::string& manager_path, - size_t interval, - bool fatal) { - // Make sure the extension manager path exists, and is writable. - auto status = extensionPathActive(manager_path, true); - if (!status.ok()) { - return status; - } - - // Start a extension manager watcher, if the manager dies, so should we. - Dispatcher::addService( - std::make_shared(manager_path, interval, fatal)); - return Status(0, "OK"); -} - -Status startExtensionManager() { - if (FLAGS_disable_extensions) { - return Status(1, "Extensions disabled"); - } - return startExtensionManager(FLAGS_extensions_socket); -} - -Status startExtensionManager(const std::string& manager_path) { - // Check if the socket location exists. - auto status = socketWritable(manager_path); - if (!status.ok()) { - return status; - } - - // Seconds converted to milliseconds, used as a thread interruptible. - auto latency = atoi(FLAGS_extensions_interval.c_str()) * 1000; - // Start a extension manager watcher, if the manager dies, so should we. - Dispatcher::addService( - std::make_shared(manager_path, latency)); - - // Start the extension manager thread. - Dispatcher::addService( - std::make_shared(manager_path)); - return Status(0, "OK"); -} -} diff --git a/osquery/extensions/interface.cpp b/osquery/extensions/interface.cpp deleted file mode 100644 index 7099fc4..0000000 --- a/osquery/extensions/interface.cpp +++ /dev/null @@ -1,228 +0,0 @@ -/* - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include -#include - -#include "osquery/extensions/interface.h" - -using namespace osquery::extensions; - -namespace osquery { -namespace extensions { - -void ExtensionHandler::ping(ExtensionStatus& _return) { - _return.code = ExtensionCode::EXT_SUCCESS; - _return.message = "pong"; - _return.uuid = uuid_; -} - -void ExtensionHandler::call(ExtensionResponse& _return, - const std::string& registry, - const std::string& item, - const ExtensionPluginRequest& request) { - // Call will receive an extension or core's request to call the other's - // internal registry call. It is the ONLY actor that resolves registry - // item aliases. - auto local_item = Registry::getAlias(registry, item); - - PluginResponse response; - PluginRequest plugin_request; - for (const auto& request_item : request) { - // Create a PluginRequest from an ExtensionPluginRequest. - plugin_request[request_item.first] = request_item.second; - } - - auto status = Registry::call(registry, local_item, plugin_request, response); - _return.status.code = status.getCode(); - _return.status.message = status.getMessage(); - _return.status.uuid = uuid_; - - if (status.ok()) { - for (const auto& response_item : response) { - // Translate a PluginResponse to an ExtensionPluginResponse. - _return.response.push_back(response_item); - } - } -} - -void ExtensionManagerHandler::extensions(InternalExtensionList& _return) { - refresh(); - _return = extensions_; -} - -void ExtensionManagerHandler::options(InternalOptionList& _return) { - auto flags = Flag::flags(); - for (const auto& flag : flags) { - _return[flag.first].value = flag.second.value; - _return[flag.first].default_value = flag.second.default_value; - _return[flag.first].type = flag.second.type; - } -} - -void ExtensionManagerHandler::registerExtension( - ExtensionStatus& _return, - const InternalExtensionInfo& info, - const ExtensionRegistry& registry) { - if (exists(info.name)) { - LOG(WARNING) << "Refusing to register duplicate extension " << info.name; - _return.code = ExtensionCode::EXT_FAILED; - _return.message = "Duplicate extension registered"; - return; - } - - // Every call to registerExtension is assigned a new RouteUUID. - RouteUUID uuid = rand(); - LOG(INFO) << "Registering extension (" << info.name << ", " << uuid - << ", version=" << info.version << ", sdk=" << info.sdk_version - << ")"; - - if (!Registry::addBroadcast(uuid, registry).ok()) { - LOG(WARNING) << "Could not add extension (" << info.name << ", " << uuid - << ") broadcast to registry"; - _return.code = ExtensionCode::EXT_FAILED; - _return.message = "Failed adding registry broadcast"; - return; - } - - extensions_[uuid] = info; - _return.code = ExtensionCode::EXT_SUCCESS; - _return.message = "OK"; - _return.uuid = uuid; -} - -void ExtensionManagerHandler::deregisterExtension( - ExtensionStatus& _return, const ExtensionRouteUUID uuid) { - if (extensions_.count(uuid) == 0) { - _return.code = ExtensionCode::EXT_FAILED; - _return.message = "No extension UUID registered"; - _return.uuid = 0; - return; - } - - // On success return the uuid of the now de-registered extension. - Registry::removeBroadcast(uuid); - extensions_.erase(uuid); - _return.code = ExtensionCode::EXT_SUCCESS; - _return.uuid = uuid; -} - -void ExtensionManagerHandler::query(ExtensionResponse& _return, - const std::string& sql) { - QueryData results; - auto status = osquery::query(sql, results); - _return.status.code = status.getCode(); - _return.status.message = status.getMessage(); - _return.status.uuid = uuid_; - - if (status.ok()) { - for (const auto& row : results) { - _return.response.push_back(row); - } - } -} - -void ExtensionManagerHandler::getQueryColumns(ExtensionResponse& _return, - const std::string& sql) { - TableColumns columns; - auto status = osquery::getQueryColumns(sql, columns); - _return.status.code = status.getCode(); - _return.status.message = status.getMessage(); - _return.status.uuid = uuid_; - - if (status.ok()) { - for (const auto& column : columns) { - _return.response.push_back({{column.first, column.second}}); - } - } -} - -void ExtensionManagerHandler::refresh() { - std::vector removed_routes; - const auto uuids = Registry::routeUUIDs(); - for (const auto& ext : extensions_) { - // Find extension UUIDs that have gone away. - if (std::find(uuids.begin(), uuids.end(), ext.first) == uuids.end()) { - removed_routes.push_back(ext.first); - } - } - - // Remove each from the manager's list of extension metadata. - for (const auto& uuid : removed_routes) { - extensions_.erase(uuid); - } -} - -bool ExtensionManagerHandler::exists(const std::string& name) { - refresh(); - - // Search the remaining extension list for duplicates. - for (const auto& extension : extensions_) { - if (extension.second.name == name) { - return true; - } - } - return false; -} -} - -ExtensionRunnerCore::~ExtensionRunnerCore() { remove(path_); } - -void ExtensionRunnerCore::stop() { - if (server_ != nullptr) { - server_->stop(); - } -} - -void ExtensionRunnerCore::startServer(TProcessorRef processor) { - auto transport = TServerTransportRef(new TServerSocket(path_)); - auto transport_fac = TTransportFactoryRef(new TBufferedTransportFactory()); - auto protocol_fac = TProtocolFactoryRef(new TBinaryProtocolFactory()); - - auto thread_manager_ = - ThreadManager::newSimpleThreadManager((size_t)FLAGS_worker_threads, 0); - auto thread_fac = ThriftThreadFactory(new PosixThreadFactory()); - thread_manager_->threadFactory(thread_fac); - thread_manager_->start(); - - // Start the Thrift server's run loop. - server_ = TThreadPoolServerRef(new TThreadPoolServer( - processor, transport, transport_fac, protocol_fac, thread_manager_)); - server_->serve(); -} - -void ExtensionRunner::start() { - // Create the thrift instances. - auto handler = ExtensionHandlerRef(new ExtensionHandler(uuid_)); - auto processor = TProcessorRef(new ExtensionProcessor(handler)); - - VLOG(1) << "Extension service starting: " << path_; - try { - startServer(processor); - } catch (const std::exception& e) { - LOG(ERROR) << "Cannot start extension handler: " << path_ << " (" - << e.what() << ")"; - } -} - -void ExtensionManagerRunner::start() { - // Create the thrift instances. - auto handler = ExtensionManagerHandlerRef(new ExtensionManagerHandler()); - auto processor = TProcessorRef(new ExtensionManagerProcessor(handler)); - - VLOG(1) << "Extension manager service starting: " << path_; - try { - startServer(processor); - } catch (const std::exception& e) { - LOG(WARNING) << "Extensions disabled: cannot start extension manager (" - << path_ << ") (" << e.what() << ")"; - } -} -} diff --git a/osquery/extensions/interface.h b/osquery/extensions/interface.h deleted file mode 100644 index 4c489db..0000000 --- a/osquery/extensions/interface.h +++ /dev/null @@ -1,346 +0,0 @@ -/* - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#pragma once - -#include - -#include "osquery/dispatcher/dispatcher.h" - -#include -#include -#include -#include -#include - -#ifdef OSQUERY_THRIFT -#include "Extension.h" -#include "ExtensionManager.h" -#else -#error "Required -DOSQUERY_THRIFT=/path/to/thrift/gen-cpp" -#endif - -namespace osquery { - -using namespace apache::thrift; -using namespace apache::thrift::protocol; -using namespace apache::thrift::transport; -using namespace apache::thrift::server; -using namespace apache::thrift::concurrency; - -/// Create easier to reference typedefs for Thrift layer implementations. -#define SHARED_PTR_IMPL OSQUERY_THRIFT_POINTER::shared_ptr -typedef SHARED_PTR_IMPL TSocketRef; -typedef SHARED_PTR_IMPL TTransportRef; -typedef SHARED_PTR_IMPL TProtocolRef; - -typedef SHARED_PTR_IMPL TProcessorRef; -typedef SHARED_PTR_IMPL TServerTransportRef; -typedef SHARED_PTR_IMPL TTransportFactoryRef; -typedef SHARED_PTR_IMPL TProtocolFactoryRef; -typedef SHARED_PTR_IMPL PosixThreadFactoryRef; -typedef std::shared_ptr TThreadPoolServerRef; - -namespace extensions { - -/** - * @brief The Thrift API server used by an osquery Extension process. - * - * An extension will load and start a thread to serve the ExtensionHandler - * Thrift runloop. This handler is the implementation of the thrift IDL spec. - * It implements all the Extension API handlers. - * - */ -class ExtensionHandler : virtual public ExtensionIf { - public: - ExtensionHandler() : uuid_(0) {} - explicit ExtensionHandler(RouteUUID uuid) : uuid_(uuid) {} - - /// Ping an Extension for status and metrics. - void ping(ExtensionStatus& _return); - - /** - * @brief The Thrift API used by Registry::call for an extension route. - * - * @param _return The return response (combo Status and PluginResponse). - * @param registry The name of the Extension registry. - * @param item The Extension plugin name. - * @param request The plugin request. - */ - void call(ExtensionResponse& _return, - const std::string& registry, - const std::string& item, - const ExtensionPluginRequest& request); - - protected: - /// Transient UUID assigned to the extension after registering. - RouteUUID uuid_; -}; - -/** - * @brief The Thrift API server used by an osquery process. - * - * An extension will load and start a thread to serve the - * ExtensionManagerHandler. This listens for extensions and allows them to - * register their Registry route information. Calls to the registry may then - * match a route exposed by an extension. - * This handler is the implementation of the thrift IDL spec. - * It implements all the ExtensionManager API handlers. - * - */ -class ExtensionManagerHandler : virtual public ExtensionManagerIf, - public ExtensionHandler { - public: - ExtensionManagerHandler() {} - - /// Return a list of Route UUIDs and extension metadata. - void extensions(InternalExtensionList& _return); - - /** - * @brief Return a map of osquery options (Flags, bootstrap CLI flags). - * - * osquery options are set via command line flags or overridden by a config - * options dictionary. There are some CLI-only flags that should never - * be overridden. If a bootstrap flag is changed there is undefined behavior - * since bootstrap candidates are settings needed before a configuration - * plugin is setUp. - * - * Extensions may broadcast config or logger plugins that need a snapshot - * of the current options. The best example is the `config_plugin` bootstrap - * flag. - */ - void options(InternalOptionList& _return); - - /** - * @brief Request a Route UUID and advertise a set of Registry routes. - * - * When an Extension starts it must call registerExtension using a well known - * ExtensionManager UNIX domain socket path. The ExtensionManager will check - * the broadcasted routes for duplicates as well as enforce SDK version - * compatibility checks. On success the Extension is returned a Route UUID and - * begins to serve the ExtensionHandler Thrift API. - * - * @param _return The output Status and optional assigned RouteUUID. - * @param info The osquery Thrift-internal Extension metadata container. - * @param registry The Extension's Registry::getBroadcast information. - */ - void registerExtension(ExtensionStatus& _return, - const InternalExtensionInfo& info, - const ExtensionRegistry& registry); - - /** - * @brief Request an Extension removal and removal of Registry routes. - * - * When an Extension process is graceful killed it should deregister. - * Other privileged tools may choose to deregister an Extension by - * the transient Extension's Route UUID, obtained using - * ExtensionManagerHandler::extensions. - * - * @param _return The output Status. - * @param uuid The assigned Route UUID to deregister. - */ - void deregisterExtension(ExtensionStatus& _return, - const ExtensionRouteUUID uuid); - - /** - * @brief Execute an SQL statement in osquery core. - * - * Extensions do not have access to the internal SQLite implementation. - * For complex queries (beyond select all from a table) the statement must - * be passed into SQLite. - * - * @param _return The output Status and QueryData (as response). - * @param sql The sql statement. - */ - void query(ExtensionResponse& _return, const std::string& sql); - - /** - * @brief Get SQL column information for SQL statements in osquery core. - * - * Extensions do not have access to the internal SQLite implementation. - * For complex queries (beyond metadata for a table) the statement must - * be passed into SQLite. - * - * @param _return The output Status and TableColumns (as response). - * @param sql The sql statement. - */ - void getQueryColumns(ExtensionResponse& _return, const std::string& sql); - - private: - /// Check if an extension exists by the name it registered. - bool exists(const std::string& name); - - /// Introspect into the registry, checking if any extension routes have been - /// removed. - void refresh(); - - /// Maintain a map of extension UUID to metadata for tracking deregistration. - InternalExtensionList extensions_; -}; - -typedef SHARED_PTR_IMPL ExtensionHandlerRef; -typedef SHARED_PTR_IMPL ExtensionManagerHandlerRef; -} - -/// A Dispatcher service thread that watches an ExtensionManagerHandler. -class ExtensionWatcher : public InternalRunnable { - public: - virtual ~ExtensionWatcher() {} - ExtensionWatcher(const std::string& path, size_t interval, bool fatal) - : path_(path), interval_(interval), fatal_(fatal) { - // Set the interval to a minimum of 200 milliseconds. - interval_ = (interval_ < 200) ? 200 : interval_; - } - - public: - /// The Dispatcher thread entry point. - void start(); - - /// Perform health checks. - virtual void watch(); - - protected: - /// Exit the extension process with a fatal if the ExtensionManager dies. - void exitFatal(int return_code = 1); - - protected: - /// The UNIX domain socket path for the ExtensionManager. - std::string path_; - - /// The internal in milliseconds to ping the ExtensionManager. - size_t interval_; - - /// If the ExtensionManager socket is closed, should the extension exit. - bool fatal_; -}; - -class ExtensionManagerWatcher : public ExtensionWatcher { - public: - ExtensionManagerWatcher(const std::string& path, size_t interval) - : ExtensionWatcher(path, interval, false) {} - - /// Start a specialized health check for an ExtensionManager. - void watch(); - - private: - /// Allow extensions to fail for several intervals. - std::map failures_; -}; - -class ExtensionRunnerCore : public InternalRunnable { - public: - virtual ~ExtensionRunnerCore(); - ExtensionRunnerCore(const std::string& path) - : path_(path), server_(nullptr) {} - - public: - /// Given a handler transport and protocol start a thrift threaded server. - void startServer(TProcessorRef processor); - - // The Dispatcher thread service stop point. - void stop(); - - protected: - /// The UNIX domain socket used for requests from the ExtensionManager. - std::string path_; - - /// Server instance, will be stopped if thread service is removed. - TThreadPoolServerRef server_; -}; - -/** - * @brief A Dispatcher service thread that starts ExtensionHandler. - * - * This runner will start a Thrift Extension server, call serve, and wait - * until the extension exists or the ExtensionManager (core) terminates or - * deregisters the extension. - * - */ -class ExtensionRunner : public ExtensionRunnerCore { - public: - ExtensionRunner(const std::string& manager_path, RouteUUID uuid) - : ExtensionRunnerCore(""), uuid_(uuid) { - path_ = getExtensionSocket(uuid, manager_path); - } - - public: - void start(); - - /// Access the UUID provided by the ExtensionManager. - RouteUUID getUUID() { return uuid_; } - - private: - /// The unique and transient Extension UUID assigned by the ExtensionManager. - RouteUUID uuid_; -}; - -/** - * @brief A Dispatcher service thread that starts ExtensionManagerHandler. - * - * This runner will start a Thrift ExtensionManager server, call serve, and wait - * until for extensions to register, or thrift API calls. - * - */ -class ExtensionManagerRunner : public ExtensionRunnerCore { - public: - explicit ExtensionManagerRunner(const std::string& manager_path) - : ExtensionRunnerCore(manager_path) {} - - public: - void start(); -}; - -/// Internal accessor for extension clients. -class EXInternal { - public: - explicit EXInternal(const std::string& path) - : socket_(new TSocket(path)), - transport_(new TBufferedTransport(socket_)), - protocol_(new TBinaryProtocol(transport_)) {} - - virtual ~EXInternal() { transport_->close(); } - - protected: - TSocketRef socket_; - TTransportRef transport_; - TProtocolRef protocol_; -}; - -/// Internal accessor for a client to an extension (from an extension manager). -class EXClient : public EXInternal { - public: - explicit EXClient(const std::string& path) : EXInternal(path) { - client_ = std::make_shared(protocol_); - (void)transport_->open(); - } - - const std::shared_ptr& get() { return client_; } - - private: - std::shared_ptr client_; -}; - -/// Internal accessor for a client to an extension manager (from an extension). -class EXManagerClient : public EXInternal { - public: - explicit EXManagerClient(const std::string& manager_path) - : EXInternal(manager_path) { - client_ = std::make_shared(protocol_); - (void)transport_->open(); - } - - const std::shared_ptr& get() { - return client_; - } - - private: - std::shared_ptr client_; -}; -} diff --git a/osquery/extensions/tests/extensions_tests.cpp b/osquery/extensions/tests/extensions_tests.cpp deleted file mode 100644 index a76022c..0000000 --- a/osquery/extensions/tests/extensions_tests.cpp +++ /dev/null @@ -1,243 +0,0 @@ -/* - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include - -#include - -#include -#include - -#include "osquery/core/test_util.h" -#include "osquery/extensions/interface.h" - -using namespace osquery::extensions; - -namespace osquery { - -const int kDelayUS = 2000; -const int kTimeoutUS = 1000000; -const std::string kTestManagerSocket = kTestWorkingDirectory + "test.em"; - -class ExtensionsTest : public testing::Test { - protected: - void SetUp() { - socket_path = kTestManagerSocket + std::to_string(rand()); - remove(socket_path); - if (pathExists(socket_path).ok()) { - throw std::domain_error("Cannot test sockets: " + socket_path); - } - } - - void TearDown() { - Dispatcher::stopServices(); - Dispatcher::joinServices(); - remove(socket_path); - } - - bool ping(int attempts = 3) { - // Calling open will except if the socket does not exist. - ExtensionStatus status; - for (int i = 0; i < attempts; ++i) { - try { - EXManagerClient client(socket_path); - client.get()->ping(status); - return (status.code == ExtensionCode::EXT_SUCCESS); - } catch (const std::exception& e) { - ::usleep(kDelayUS); - } - } - - return false; - } - - QueryData query(const std::string& sql, int attempts = 3) { - // Calling open will except if the socket does not exist. - ExtensionResponse response; - for (int i = 0; i < attempts; ++i) { - try { - EXManagerClient client(socket_path); - client.get()->query(response, sql); - } catch (const std::exception& e) { - ::usleep(kDelayUS); - } - } - - QueryData qd; - for (const auto& row : response.response) { - qd.push_back(row); - } - - return qd; - } - - ExtensionList registeredExtensions(int attempts = 3) { - ExtensionList extensions; - for (int i = 0; i < attempts; ++i) { - if (getExtensions(socket_path, extensions).ok()) { - break; - } - } - - return extensions; - } - - bool socketExists(const std::string& socket_path) { - // Wait until the runnable/thread created the socket. - int delay = 0; - while (delay < kTimeoutUS) { - if (pathExists(socket_path).ok() && isReadable(socket_path).ok()) { - return true; - } - ::usleep(kDelayUS); - delay += kDelayUS; - } - return false; - } - - public: - std::string socket_path; -}; - -TEST_F(ExtensionsTest, test_manager_runnable) { - // Start a testing extension manager. - auto status = startExtensionManager(socket_path); - EXPECT_TRUE(status.ok()); - // Call success if the Unix socket was created. - EXPECT_TRUE(socketExists(socket_path)); -} - -TEST_F(ExtensionsTest, test_extension_runnable) { - auto status = startExtensionManager(socket_path); - EXPECT_TRUE(status.ok()); - // Wait for the extension manager to start. - EXPECT_TRUE(socketExists(socket_path)); - - // Test the extension manager API 'ping' call. - EXPECT_TRUE(ping()); -} - -TEST_F(ExtensionsTest, test_extension_start) { - auto status = startExtensionManager(socket_path); - EXPECT_TRUE(status.ok()); - EXPECT_TRUE(socketExists(socket_path)); - - // Now allow duplicates (for testing, since EM/E are the same). - Registry::allowDuplicates(true); - status = startExtension(socket_path, "test", "0.1", "0.0.0", "0.0.1"); - // This will not be false since we are allowing deplicate items. - // Otherwise, starting an extension and extensionManager would fatal. - ASSERT_TRUE(status.ok()); - - // The `startExtension` internal call (exposed for testing) returns the - // uuid of the extension in the success status. - RouteUUID uuid = (RouteUUID)stoi(status.getMessage(), nullptr, 0); - - // We can test-wait for the extensions's socket to open. - EXPECT_TRUE(socketExists(socket_path + "." + std::to_string(uuid))); - - // Then clean up the registry modifications. - Registry::removeBroadcast(uuid); - Registry::allowDuplicates(false); -} - -class ExtensionPlugin : public Plugin { - public: - Status call(const PluginRequest& request, PluginResponse& response) { - for (const auto& request_item : request) { - response.push_back({{request_item.first, request_item.second}}); - } - return Status(0, "Test success"); - } -}; - -class TestExtensionPlugin : public ExtensionPlugin {}; - -CREATE_REGISTRY(ExtensionPlugin, "extension_test"); - -TEST_F(ExtensionsTest, test_extension_broadcast) { - auto status = startExtensionManager(socket_path); - EXPECT_TRUE(status.ok()); - EXPECT_TRUE(socketExists(socket_path)); - - // This time we're going to add a plugin to the extension_test registry. - Registry::add("extension_test", "test_item"); - - // Now we create a registry alias that will be broadcasted but NOT used for - // internal call lookups. Aliasing was introduced for testing such that an - // EM/E could exist in the same process (the same registry) without having - // duplicate registry items in the internal registry list AND extension - // registry route table. - Registry::addAlias("extension_test", "test_item", "test_alias"); - Registry::allowDuplicates(true); - - // Before registering the extension there is NO route to "test_alias" since - // alias resolutions are performed by the EM. - EXPECT_TRUE(Registry::exists("extension_test", "test_item")); - EXPECT_FALSE(Registry::exists("extension_test", "test_alias")); - - status = startExtension(socket_path, "test", "0.1", "0.0.0", "0.0.1"); - EXPECT_TRUE(status.ok()); - - RouteUUID uuid; - try { - uuid = (RouteUUID)stoi(status.getMessage(), nullptr, 0); - } catch (const std::exception& e) { - EXPECT_TRUE(false); - return; - } - - auto ext_socket = socket_path + "." + std::to_string(uuid); - EXPECT_TRUE(socketExists(ext_socket)); - - // Make sure the EM registered the extension (called in start extension). - auto extensions = registeredExtensions(); - // Expect two, since `getExtensions` includes the core. - ASSERT_EQ(extensions.size(), 2); - EXPECT_EQ(extensions.count(uuid), 1); - EXPECT_EQ(extensions.at(uuid).name, "test"); - EXPECT_EQ(extensions.at(uuid).version, "0.1"); - EXPECT_EQ(extensions.at(uuid).sdk_version, "0.0.1"); - - // We are broadcasting to our own registry in the test, which internally has - // a "test_item" aliased to "test_alias", "test_item" is internally callable - // but "test_alias" can only be resolved by an EM call. - EXPECT_TRUE(Registry::exists("extension_test", "test_item")); - // Now "test_alias" exists since it is in the extensions route table. - EXPECT_TRUE(Registry::exists("extension_test", "test_alias")); - - PluginResponse response; - // This registry call will fail, since "test_alias" cannot be resolved using - // a local registry call. - status = Registry::call("extension_test", "test_alias", {{}}, response); - EXPECT_FALSE(status.ok()); - - // The following will be the result of a: - // Registry::call("extension_test", "test_alias", {{}}, response); - status = callExtension(ext_socket, - "extension_test", - "test_alias", - {{"test_key", "test_value"}}, - response); - EXPECT_TRUE(status.ok()); - EXPECT_EQ(response.size(), 1); - EXPECT_EQ(response[0]["test_key"], "test_value"); - - Registry::removeBroadcast(uuid); - Registry::allowDuplicates(false); -} - -TEST_F(ExtensionsTest, test_extension_module_search) { - createMockFileStructure(); - EXPECT_FALSE(loadModules(kFakeDirectory + "/root.txt")); - EXPECT_FALSE(loadModules("/dir/does/not/exist")); - tearDownMockFileStructure(); -} -} diff --git a/osquery/filesystem/CMakeLists.txt b/osquery/filesystem/CMakeLists.txt deleted file mode 100644 index 20e0c26..0000000 --- a/osquery/filesystem/CMakeLists.txt +++ /dev/null @@ -1,21 +0,0 @@ -# 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_filesystem filesystem.cpp) - -ADD_OSQUERY_LIBRARY(osquery_filesystem_linux linux/proc.cpp - linux/mem.cpp) - -FILE(GLOB OSQUERY_FILESYSTEM_TESTS "tests/*.cpp") -ADD_OSQUERY_TEST(${OSQUERY_FILESYSTEM_TESTS}) diff --git a/osquery/filesystem/filesystem.cpp b/osquery/filesystem/filesystem.cpp deleted file mode 100644 index b567c1d..0000000 --- a/osquery/filesystem/filesystem.cpp +++ /dev/null @@ -1,386 +0,0 @@ -/* - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include - -#include -#include -#include -#include - -#include -#include -#include -#include - -#include -#include -#include -#include - -namespace pt = boost::property_tree; -namespace fs = boost::filesystem; - -namespace osquery { - -FLAG(uint64, read_max, 50 * 1024 * 1024, "Maximum file read size"); -FLAG(uint64, read_user_max, 10 * 1024 * 1024, "Maximum non-su read size"); -FLAG(bool, read_user_links, true, "Read user-owned filesystem links"); - -Status writeTextFile(const fs::path& path, - const std::string& content, - int permissions, - bool force_permissions) { - // Open the file with the request permissions. - int output_fd = - open(path.c_str(), O_CREAT | O_APPEND | O_WRONLY, permissions); - if (output_fd <= 0) { - return Status(1, "Could not create file: " + path.string()); - } - - // If the file existed with different permissions before our open - // they must be restricted. - if (chmod(path.c_str(), permissions) != 0) { - // Could not change the file to the requested permissions. - return Status(1, "Failed to change permissions for file: " + path.string()); - } - - auto bytes = write(output_fd, content.c_str(), content.size()); - if (bytes != content.size()) { - close(output_fd); - return Status(1, "Failed to write contents to file: " + path.string()); - } - - close(output_fd); - return Status(0, "OK"); -} - -Status readFile(const fs::path& path, std::string& content, bool dry_run) { - struct stat file; - if (lstat(path.string().c_str(), &file) == 0 && S_ISLNK(file.st_mode)) { - if (file.st_uid != 0 && !FLAGS_read_user_links) { - return Status(1, "User link reads disabled"); - } - } - - if (stat(path.string().c_str(), &file) < 0) { - return Status(1, "Cannot access path: " + path.string()); - } - - // Apply the max byte-read based on file/link target ownership. - size_t read_max = (file.st_uid == 0) - ? FLAGS_read_max - : std::min(FLAGS_read_max, FLAGS_read_user_max); - std::ifstream is(path.string(), std::ifstream::binary | std::ios::ate); - if (!is.is_open()) { - // Attempt to read without seeking to the end. - is.open(path.string(), std::ifstream::binary); - if (!is) { - return Status(1, "Error reading file: " + path.string()); - } - } - - // Attempt to read the file size. - ssize_t size = is.tellg(); - - // Erase/clear provided string buffer. - content.erase(); - if (size > read_max) { - VLOG(1) << "Cannot read " << path << " size exceeds limit: " << size - << " > " << read_max; - return Status(1, "File exceeds read limits"); - } - - if (dry_run) { - // The caller is only interested in performing file read checks. - boost::system::error_code ec; - return Status(0, fs::canonical(path, ec).string()); - } - - // Reset seek to the start of the stream. - is.seekg(0); - if (size == -1 || size == 0) { - // Size could not be determined. This may be a special device. - std::stringstream buffer; - buffer << is.rdbuf(); - if (is.bad()) { - return Status(1, "Error reading special file: " + path.string()); - } - content.assign(std::move(buffer.str())); - } else { - content = std::string(size, '\0'); - is.read(&content[0], size); - } - return Status(0, "OK"); -} - -Status readFile(const fs::path& path) { - std::string blank; - return readFile(path, blank, true); -} - -Status isWritable(const fs::path& path) { - auto path_exists = pathExists(path); - if (!path_exists.ok()) { - return path_exists; - } - - if (access(path.c_str(), W_OK) == 0) { - return Status(0, "OK"); - } - return Status(1, "Path is not writable: " + path.string()); -} - -Status isReadable(const fs::path& path) { - auto path_exists = pathExists(path); - if (!path_exists.ok()) { - return path_exists; - } - - if (access(path.c_str(), R_OK) == 0) { - return Status(0, "OK"); - } - return Status(1, "Path is not readable: " + path.string()); -} - -Status pathExists(const fs::path& path) { - if (path.empty()) { - return Status(1, "-1"); - } - - // A tri-state determination of presence - try { - if (!fs::exists(path)) { - return Status(1, "0"); - } - } catch (const fs::filesystem_error& e) { - return Status(1, e.what()); - } - return Status(0, "1"); -} - -Status remove(const fs::path& path) { - auto status_code = std::remove(path.string().c_str()); - return Status(status_code, "N/A"); -} - -static void genGlobs(std::string path, - std::vector& results, - GlobLimits limits) { - // Use our helped escape/replace for wildcards. - replaceGlobWildcards(path); - - // Generate a glob set and recurse for double star. - while (true) { - glob_t data; - glob(path.c_str(), GLOB_TILDE | GLOB_MARK | GLOB_BRACE, nullptr, &data); - size_t count = data.gl_pathc; - for (size_t index = 0; index < count; index++) { - results.push_back(data.gl_pathv[index]); - } - globfree(&data); - // The end state is a non-recursive ending or empty set of matches. - size_t wild = path.rfind("**"); - // Allow a trailing slash after the double wild indicator. - if (count == 0 || wild > path.size() || wild < path.size() - 3) { - break; - } - path += "/**"; - } - - // Prune results based on settings/requested glob limitations. - auto end = std::remove_if( - results.begin(), results.end(), [limits](const std::string& found) { - return !((found[found.length() - 1] == '/' && limits & GLOB_FOLDERS) || - (found[found.length() - 1] != '/' && limits & GLOB_FILES)); - }); - results.erase(end, results.end()); -} - -Status resolveFilePattern(const fs::path& fs_path, - std::vector& results) { - return resolveFilePattern(fs_path, results, GLOB_ALL); -} - -Status resolveFilePattern(const fs::path& fs_path, - std::vector& results, - GlobLimits setting) { - genGlobs(fs_path.string(), results, setting); - return Status(0, "OK"); -} - -inline void replaceGlobWildcards(std::string& pattern) { - // Replace SQL-wildcard '%' with globbing wildcard '*'. - if (pattern.find("%") != std::string::npos) { - boost::replace_all(pattern, "%", "*"); - } - - // Relative paths are a bad idea, but we try to accommodate. - if ((pattern.size() == 0 || pattern[0] != '/') && pattern[0] != '~') { - pattern = (fs::initial_path() / pattern).string(); - } - - auto base = pattern.substr(0, pattern.find('*')); - if (base.size() > 0) { - boost::system::error_code ec; - auto canonicalized = fs::canonical(base, ec).string(); - if (canonicalized.size() > 0 && canonicalized != base) { - if (isDirectory(canonicalized)) { - // Canonicalized directory paths will not include a trailing '/'. - // However, if the wildcards are applied to files within a directory - // then the missing '/' changes the wildcard meaning. - canonicalized += '/'; - } - // We are unable to canonicalize the meaning of post-wildcard limiters. - pattern = canonicalized + pattern.substr(base.size()); - } - } -} - -inline Status listInAbsoluteDirectory(const fs::path& path, - std::vector& results, - GlobLimits limits) { - try { - if (path.filename() == "*" && !fs::exists(path.parent_path())) { - return Status(1, "Directory not found: " + path.parent_path().string()); - } - - if (path.filename() == "*" && !fs::is_directory(path.parent_path())) { - return Status(1, "Path not a directory: " + path.parent_path().string()); - } - } catch (const fs::filesystem_error& e) { - return Status(1, e.what()); - } - genGlobs(path.string(), results, limits); - return Status(0, "OK"); -} - -Status listFilesInDirectory(const fs::path& path, - std::vector& results, - bool ignore_error) { - return listInAbsoluteDirectory((path / "*"), results, GLOB_FILES); -} - -Status listDirectoriesInDirectory(const fs::path& path, - std::vector& results, - bool ignore_error) { - return listInAbsoluteDirectory((path / "*"), results, GLOB_FOLDERS); -} - -Status getDirectory(const fs::path& path, fs::path& dirpath) { - if (!isDirectory(path).ok()) { - dirpath = fs::path(path).parent_path().string(); - return Status(0, "OK"); - } - dirpath = path; - return Status(1, "Path is a directory: " + path.string()); -} - -Status isDirectory(const fs::path& path) { - boost::system::error_code ec; - if (fs::is_directory(path, ec)) { - return Status(0, "OK"); - } - if (ec.value() == 0) { - return Status(1, "Path is not a directory: " + path.string()); - } - return Status(ec.value(), ec.message()); -} - -std::set getHomeDirectories() { - std::set results; - - auto users = SQL::selectAllFrom("users"); - for (const auto& user : users) { - if (user.at("directory").size() > 0) { - results.insert(user.at("directory")); - } - } - - return results; -} - -bool safePermissions(const std::string& dir, - const std::string& path, - bool executable) { - struct stat file_stat, link_stat, dir_stat; - if (lstat(path.c_str(), &link_stat) < 0 || stat(path.c_str(), &file_stat) || - stat(dir.c_str(), &dir_stat)) { - // Path was not real, had too may links, or could not be accessed. - return false; - } - - if (dir_stat.st_mode & (1 << 9)) { - // Do not load modules from /tmp-like directories. - return false; - } else if (S_ISDIR(file_stat.st_mode)) { - // Only load file-like nodes (not directories). - return false; - } else if (file_stat.st_uid == getuid() || file_stat.st_uid == 0) { - // Otherwise, require matching or root file ownership. - if (executable && !(file_stat.st_mode & S_IXUSR)) { - // Require executable, implies by the owner. - return false; - } - return true; - } - // Do not load modules not owned by the user. - return false; -} - -const std::string& osqueryHomeDirectory() { - static std::string homedir; - if (homedir.size() == 0) { - // Try to get the caller's home directory using HOME and getpwuid. - auto user = getpwuid(getuid()); - if (getenv("HOME") != nullptr && isWritable(getenv("HOME")).ok()) { - homedir = std::string(getenv("HOME")) + "/.osquery"; - } else if (user != nullptr && user->pw_dir != nullptr) { - homedir = std::string(user->pw_dir) + "/.osquery"; - } else { - // Fail over to a temporary directory (used for the shell). - homedir = "/tmp/osquery"; - } - } - return homedir; -} - -std::string lsperms(int mode) { - static const char rwx[] = {'0', '1', '2', '3', '4', '5', '6', '7'}; - std::string bits; - - bits += rwx[(mode >> 9) & 7]; - bits += rwx[(mode >> 6) & 7]; - bits += rwx[(mode >> 3) & 7]; - bits += rwx[(mode >> 0) & 7]; - return bits; -} - -Status parseJSON(const fs::path& path, pt::ptree& tree) { - std::string json_data; - if (!readFile(path, json_data).ok()) { - return Status(1, "Could not read JSON from file"); - } - - return parseJSONContent(json_data, tree); -} - -Status parseJSONContent(const std::string& content, pt::ptree& tree) { - // Read the extensions data into a JSON blob, then property tree. - try { - std::stringstream json_stream; - json_stream << content; - pt::read_json(json_stream, tree); - } catch (const pt::json_parser::json_parser_error& e) { - return Status(1, "Could not parse JSON from file"); - } - return Status(0, "OK"); -} -} diff --git a/osquery/filesystem/linux/mem.cpp b/osquery/filesystem/linux/mem.cpp deleted file mode 100644 index 3017a20..0000000 --- a/osquery/filesystem/linux/mem.cpp +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include -#include - -#include -#include -#include - -#include -#include -#include - -namespace osquery { - -#define kLinuxMaxMemRead 0x10000 - -const std::string kLinuxMemPath = "/dev/mem"; - -FLAG(bool, disable_memory, false, "Disable physical memory reads"); - -Status readMem(int fd, size_t base, size_t length, uint8_t* buffer) { - if (lseek(fd, base, SEEK_SET) == -1) { - return Status(1, "Cannot seek to physical base"); - } - - // Read from raw memory until an unrecoverable read error or the all of the - // requested bytes are read. - size_t total_read = 0; - size_t bytes_read = 0; - while (total_read != length && bytes_read != 0) { - bytes_read = read(fd, buffer + total_read, length - total_read); - if (bytes_read == -1) { - if (errno != EINTR) { - return Status(1, "Cannot read requested length"); - } - } else { - total_read += bytes_read; - } - } - - // The read call finished without reading the requested number of bytes. - if (total_read != length) { - return Status(1, "Read incorrect number of bytes"); - } - - return Status(0, "OK"); -} - -Status readRawMem(size_t base, size_t length, void** buffer) { - *buffer = 0; - - if (FLAGS_disable_memory) { - return Status(1, "Configuration has disabled physical memory reads"); - } - - if (length > kLinuxMaxMemRead) { - return Status(1, "Cowardly refusing to read a large number of bytes"); - } - - auto status = isReadable(kLinuxMemPath); - if (!status.ok()) { - // For non-su users *hopefully* raw memory is not readable. - return status; - } - - int fd = open(kLinuxMemPath.c_str(), O_RDONLY); - if (fd < 0) { - return Status(1, std::string("Cannot open ") + kLinuxMemPath); - } - - if ((*buffer = malloc(length)) == nullptr) { - close(fd); - return Status(1, "Cannot allocate memory for read"); - } - -#ifdef _SC_PAGESIZE - size_t offset = base % sysconf(_SC_PAGESIZE); -#else - // getpagesize() is more or less deprecated. - size_t offset = base % getpagesize(); -#endif - - // Use memmap for maximum portability over read(). - auto map = mmap(0, offset + length, PROT_READ, MAP_SHARED, fd, base - offset); - if (map == MAP_FAILED) { - // Could fallback to a lseek/read. - if (!readMem(fd, base, length, (uint8_t*)*buffer).ok()) { - close(fd); - free(*buffer); - return Status(1, "Cannot memory map or seek/read memory"); - } - } else { - // Memory map succeeded, copy and unmap. - memcpy(*buffer, (uint8_t*)map + offset, length); - if (munmap(map, offset + length) == -1) { - LOG(WARNING) << "Unable to unmap raw memory"; - } - } - - close(fd); - return Status(0, "OK"); -} -} diff --git a/osquery/filesystem/linux/proc.cpp b/osquery/filesystem/linux/proc.cpp deleted file mode 100644 index c80fefa..0000000 --- a/osquery/filesystem/linux/proc.cpp +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include -#include - -#include - -#include -#include - -namespace osquery { - -const std::string kLinuxProcPath = "/proc"; - -Status procProcesses(std::set& processes) { - // Iterate over each process-like directory in proc. - boost::filesystem::directory_iterator it(kLinuxProcPath), end; - try { - for (; it != end; ++it) { - if (boost::filesystem::is_directory(it->status())) { - // See #792: std::regex is incomplete until GCC 4.9 - if (std::atoll(it->path().leaf().string().c_str()) > 0) { - processes.insert(it->path().leaf().string()); - } - } - } - } catch (const boost::filesystem::filesystem_error& e) { - VLOG(1) << "Exception iterating Linux processes " << e.what(); - return Status(1, e.what()); - } - - return Status(0, "OK"); -} - -Status procDescriptors(const std::string& process, - std::map& descriptors) { - auto descriptors_path = kLinuxProcPath + "/" + process + "/fd"; - try { - // Access to the process' /fd may be restricted. - boost::filesystem::directory_iterator it(descriptors_path), end; - for (; it != end; ++it) { - auto fd = it->path().leaf().string(); - std::string linkname; - if (procReadDescriptor(process, fd, linkname).ok()) { - descriptors[fd] = linkname; - } - } - } catch (boost::filesystem::filesystem_error& e) { - return Status(1, "Cannot access descriptors for " + process); - } - - return Status(0, "OK"); -} - -Status procReadDescriptor(const std::string& process, - const std::string& descriptor, - std::string& result) { - auto link = kLinuxProcPath + "/" + process + "/fd/" + descriptor; - - char result_path[PATH_MAX] = {0}; - auto size = readlink(link.c_str(), result_path, sizeof(result_path) - 1); - if (size >= 0) { - result = std::string(result_path); - } - - if (size >= 0) { - return Status(0, "OK"); - } else { - return Status(1, "Could not read path"); - } -} -} diff --git a/osquery/filesystem/tests/filesystem_tests.cpp b/osquery/filesystem/tests/filesystem_tests.cpp deleted file mode 100644 index 85c87f7..0000000 --- a/osquery/filesystem/tests/filesystem_tests.cpp +++ /dev/null @@ -1,290 +0,0 @@ -/* - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include - -#include - -#include - -#include - -#include -#include - -#include "osquery/core/test_util.h" - -namespace pt = boost::property_tree; - -namespace osquery { - -DECLARE_uint64(read_max); -DECLARE_uint64(read_user_max); -DECLARE_bool(read_user_links); - -class FilesystemTests : public testing::Test { - - protected: - void SetUp() { createMockFileStructure(); } - - void TearDown() { tearDownMockFileStructure(); } - - /// Helper method to check if a path was included in results. - bool contains(const std::vector& all, const std::string& n) { - return !(std::find(all.begin(), all.end(), n) == all.end()); - } -}; - -TEST_F(FilesystemTests, test_read_file) { - std::ofstream test_file(kTestWorkingDirectory + "fstests-file"); - test_file.write("test123\n", sizeof("test123")); - test_file.close(); - - std::string content; - auto s = readFile(kTestWorkingDirectory + "fstests-file", content); - EXPECT_TRUE(s.ok()); - EXPECT_EQ(s.toString(), "OK"); - EXPECT_EQ(content, "test123\n"); - - remove(kTestWorkingDirectory + "fstests-file"); -} - -TEST_F(FilesystemTests, test_read_symlink) { - std::string content; - auto status = readFile(kFakeDirectory + "/root2.txt", content); - EXPECT_TRUE(status.ok()); - EXPECT_EQ(content, "root"); -} - -TEST_F(FilesystemTests, test_read_limit) { - auto max = FLAGS_read_max; - auto user_max = FLAGS_read_user_max; - FLAGS_read_max = 3; - std::string content; - auto status = readFile(kFakeDirectory + "/root.txt", content); - EXPECT_FALSE(status.ok()); - FLAGS_read_max = max; - - if (getuid() != 0) { - content.erase(); - FLAGS_read_user_max = 2; - status = readFile(kFakeDirectory + "/root.txt", content); - EXPECT_FALSE(status.ok()); - FLAGS_read_user_max = user_max; - - // Test that user symlinks aren't followed if configured. - // 'root2.txt' is a symlink in this case. - FLAGS_read_user_links = false; - content.erase(); - status = readFile(kFakeDirectory + "/root2.txt", content); - EXPECT_FALSE(status.ok()); - - // Make sure non-link files are still readable. - content.erase(); - status = readFile(kFakeDirectory + "/root.txt", content); - EXPECT_TRUE(status.ok()); - - // Any the links are readable if enabled. - FLAGS_read_user_links = true; - status = readFile(kFakeDirectory + "/root2.txt", content); - EXPECT_TRUE(status.ok()); - } -} - -TEST_F(FilesystemTests, test_list_files_missing_directory) { - std::vector results; - auto status = listFilesInDirectory("/foo/bar", results); - EXPECT_FALSE(status.ok()); -} - -TEST_F(FilesystemTests, test_list_files_invalid_directory) { - std::vector results; - auto status = listFilesInDirectory("/etc/hosts", results); - EXPECT_FALSE(status.ok()); -} - -TEST_F(FilesystemTests, test_list_files_valid_directorty) { - std::vector results; - auto s = listFilesInDirectory("/etc", results); - // This directory may be different on OS X or Linux. - std::string hosts_path = "/etc/hosts"; - replaceGlobWildcards(hosts_path); - EXPECT_TRUE(s.ok()); - EXPECT_EQ(s.toString(), "OK"); - EXPECT_TRUE(contains(results, hosts_path)); -} - -TEST_F(FilesystemTests, test_canonicalization) { - std::string complex = kFakeDirectory + "/deep1/../deep1/.."; - std::string simple = kFakeDirectory + "/"; - // Use the inline wildcard and canonicalization replacement. - // The 'simple' path contains a trailing '/', the replacement method will - // distinguish between file and directory paths. - replaceGlobWildcards(complex); - EXPECT_EQ(simple, complex); - // Now apply the same inline replacement on the simple directory and expect - // no change to the comparison. - replaceGlobWildcards(simple); - EXPECT_EQ(simple, complex); - - // Now add a wildcard within the complex pattern. The replacement method - // will not canonicalize past a '*' as the proceeding paths are limiters. - complex = kFakeDirectory + "/*/deep2/../deep2/"; - replaceGlobWildcards(complex); - EXPECT_EQ(complex, kFakeDirectory + "/*/deep2/../deep2/"); -} - -TEST_F(FilesystemTests, test_simple_globs) { - std::vector results; - // Test the shell '*', we will support SQL's '%' too. - auto status = resolveFilePattern(kFakeDirectory + "/*", results); - EXPECT_TRUE(status.ok()); - EXPECT_EQ(results.size(), 6); - - // Test the csh-style bracket syntax: {}. - results.clear(); - resolveFilePattern(kFakeDirectory + "/{root,door}*", results); - EXPECT_EQ(results.size(), 3); - - // Test a tilde, home directory expansion, make no asserts about contents. - results.clear(); - resolveFilePattern("~", results); - if (results.size() == 0) { - LOG(WARNING) << "Tilde expansion failed."; - } -} - -TEST_F(FilesystemTests, test_wildcard_single_all) { - // Use '%' as a wild card to glob files within the temporarily-created dir. - std::vector results; - auto status = resolveFilePattern(kFakeDirectory + "/%", results, GLOB_ALL); - EXPECT_TRUE(status.ok()); - EXPECT_EQ(results.size(), 6); - EXPECT_TRUE(contains(results, kFakeDirectory + "/roto.txt")); - EXPECT_TRUE(contains(results, kFakeDirectory + "/deep11/")); -} - -TEST_F(FilesystemTests, test_wildcard_single_files) { - // Now list again with a restriction to only files. - std::vector results; - resolveFilePattern(kFakeDirectory + "/%", results, GLOB_FILES); - EXPECT_EQ(results.size(), 4); - EXPECT_TRUE(contains(results, kFakeDirectory + "/roto.txt")); -} - -TEST_F(FilesystemTests, test_wildcard_single_folders) { - std::vector results; - resolveFilePattern(kFakeDirectory + "/%", results, GLOB_FOLDERS); - EXPECT_EQ(results.size(), 2); - EXPECT_TRUE(contains(results, kFakeDirectory + "/deep11/")); -} - -TEST_F(FilesystemTests, test_wildcard_dual) { - // Now test two directories deep with a single wildcard for each. - std::vector results; - auto status = resolveFilePattern(kFakeDirectory + "/%/%", results); - EXPECT_TRUE(status.ok()); - EXPECT_TRUE(contains(results, kFakeDirectory + "/deep1/level1.txt")); -} - -TEST_F(FilesystemTests, test_wildcard_double) { - // TODO: this will fail. - std::vector results; - auto status = resolveFilePattern(kFakeDirectory + "/%%", results); - EXPECT_TRUE(status.ok()); - EXPECT_EQ(results.size(), 15); - EXPECT_TRUE(contains(results, kFakeDirectory + "/deep1/deep2/level2.txt")); -} - -TEST_F(FilesystemTests, test_wildcard_double_folders) { - std::vector results; - resolveFilePattern(kFakeDirectory + "/%%", results, GLOB_FOLDERS); - EXPECT_EQ(results.size(), 5); - EXPECT_TRUE(contains(results, kFakeDirectory + "/deep11/deep2/deep3/")); -} - -TEST_F(FilesystemTests, test_wildcard_end_last_component) { - std::vector results; - auto status = resolveFilePattern(kFakeDirectory + "/%11/%sh", results); - EXPECT_TRUE(status.ok()); - EXPECT_TRUE(contains(results, kFakeDirectory + "/deep11/not_bash")); -} - -TEST_F(FilesystemTests, test_wildcard_middle_component) { - std::vector results; - auto status = resolveFilePattern(kFakeDirectory + "/deep1%/%", results); - EXPECT_TRUE(status.ok()); - EXPECT_EQ(results.size(), 5); - EXPECT_TRUE(contains(results, kFakeDirectory + "/deep1/level1.txt")); - EXPECT_TRUE(contains(results, kFakeDirectory + "/deep11/level1.txt")); -} - -TEST_F(FilesystemTests, test_wildcard_all_types) { - std::vector results; - auto status = resolveFilePattern(kFakeDirectory + "/%p11/%/%%", results); - EXPECT_TRUE(status.ok()); - EXPECT_TRUE( - contains(results, kFakeDirectory + "/deep11/deep2/deep3/level3.txt")); -} - -TEST_F(FilesystemTests, test_wildcard_invalid_path) { - std::vector results; - auto status = resolveFilePattern("/not_ther_abcdefz/%%", results); - EXPECT_TRUE(status.ok()); - EXPECT_EQ(results.size(), 0); -} - -TEST_F(FilesystemTests, test_wildcard_dotdot_files) { - std::vector results; - auto status = resolveFilePattern( - kFakeDirectory + "/deep11/deep2/../../%", results, GLOB_FILES); - EXPECT_TRUE(status.ok()); - EXPECT_EQ(results.size(), 4); - // The response list will contain canonicalized versions: /tmp//... - std::string door_path = kFakeDirectory + "/deep11/deep2/../../door.txt"; - replaceGlobWildcards(door_path); - EXPECT_TRUE(contains(results, door_path)); -} - -TEST_F(FilesystemTests, test_dotdot_relative) { - std::vector results; - auto status = resolveFilePattern(kTestDataPath + "%", results); - EXPECT_TRUE(status.ok()); - - bool found = false; - for (const auto& file : results) { - if (file.find("test.config")) { - found = true; - break; - } - } - EXPECT_TRUE(found); -} - -TEST_F(FilesystemTests, test_no_wild) { - std::vector results; - auto status = - resolveFilePattern(kFakeDirectory + "/roto.txt", results, GLOB_FILES); - EXPECT_TRUE(status.ok()); - EXPECT_EQ(results.size(), 1); - EXPECT_TRUE(contains(results, kFakeDirectory + "/roto.txt")); -} - -TEST_F(FilesystemTests, test_safe_permissions) { - // For testing we can request a different directory path. - EXPECT_TRUE(safePermissions("/", kFakeDirectory + "/door.txt")); - // A file with a directory.mode & 0x1000 fails. - EXPECT_FALSE(safePermissions("/tmp", kFakeDirectory + "/door.txt")); - // A directory for a file will fail. - EXPECT_FALSE(safePermissions("/", kFakeDirectory + "/deep11")); - // A root-owned file is appropriate - EXPECT_TRUE(safePermissions("/", "/dev/zero")); -} -} diff --git a/osquery/logger/CMakeLists.txt b/osquery/logger/CMakeLists.txt deleted file mode 100644 index a87fd66..0000000 --- a/osquery/logger/CMakeLists.txt +++ /dev/null @@ -1,23 +0,0 @@ -# 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_logger logger.cpp) -ADD_OSQUERY_LIBRARY(osquery_logger_plugins plugins/filesystem.cpp - plugins/syslog.cpp) - -FILE(GLOB OSQUERY_LOGGER_TESTS "tests/*.cpp") -ADD_OSQUERY_TEST(${OSQUERY_LOGGER_TESTS}) - -file(GLOB OSQUERY_LOGGER_PLUGIN_TESTS "plugins/tests/*.cpp") -ADD_OSQUERY_TEST(${OSQUERY_LOGGER_PLUGIN_TESTS}) diff --git a/osquery/logger/logger.cpp b/osquery/logger/logger.cpp deleted file mode 100644 index f0b420b..0000000 --- a/osquery/logger/logger.cpp +++ /dev/null @@ -1,361 +0,0 @@ -/* - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include -#include - -#include -#include - -#include -#include -#include -#include - -namespace pt = boost::property_tree; - -namespace osquery { - -FLAG(bool, verbose, false, "Enable verbose informational messages"); -FLAG_ALIAS(bool, verbose_debug, verbose); -FLAG_ALIAS(bool, debug, verbose); - -/// Despite being a configurable option, this is only read/used at load. -FLAG(bool, disable_logging, false, "Disable ERROR/INFO logging"); - -FLAG(string, logger_plugin, "filesystem", "Logger plugin name"); - -FLAG(bool, log_result_events, true, "Log scheduled results as events"); - -/** - * @brief A custom Glog log sink for forwarding or buffering status logs. - * - * This log sink has two modes, it can buffer Glog status logs until an osquery - * logger is initialized or forward Glog status logs to an initialized and - * appropriate logger. The appropriateness is determined by the logger when its - * LoggerPlugin::init method is called. If the `init` method returns success - * then a BufferedLogSink will start forwarding status logs to - * LoggerPlugin::logStatus. - * - * This facility will start buffering when first used and stop buffering - * (aka remove itself as a Glog sink) using the exposed APIs. It will live - * throughout the life of the process for two reasons: (1) It makes sense when - * the active logger plugin is handling Glog status logs and (2) it must remove - * itself as a Glog target. - */ -class BufferedLogSink : public google::LogSink, private boost::noncopyable { - public: - /// We create this as a Singleton for proper disable/shutdown. - static BufferedLogSink& instance() { - static BufferedLogSink sink; - return sink; - } - - /// The Glog-API LogSink call-in method. - void send(google::LogSeverity severity, - const char* full_filename, - const char* base_filename, - int line, - const struct ::tm* tm_time, - const char* message, - size_t message_len); - - /// Accessor/mutator to dump all of the buffered logs. - static std::vector& dump() { return instance().logs_; } - - /// Set the forwarding mode of the buffering sink. - static void forward(bool forward = false) { instance().forward_ = forward; } - - /// Remove the buffered log sink from Glog. - static void disable() { - if (instance().enabled_) { - instance().enabled_ = false; - google::RemoveLogSink(&instance()); - } - } - - /// Add the buffered log sink to Glog. - static void enable() { - if (!instance().enabled_) { - instance().enabled_ = true; - google::AddLogSink(&instance()); - } - } - - private: - /// Create the log sink as buffering or forwarding. - BufferedLogSink() : forward_(false), enabled_(false) {} - - /// Remove the log sink. - ~BufferedLogSink() { disable(); } - - BufferedLogSink(BufferedLogSink const&); - void operator=(BufferedLogSink const&); - - private: - /// Intermediate log storage until an osquery logger is initialized. - std::vector logs_; - bool forward_; - bool enabled_; -}; - -/// Scoped helper to perform logging actions without races. -class LoggerDisabler { - public: - LoggerDisabler() : stderr_status_(FLAGS_logtostderr) { - BufferedLogSink::disable(); - FLAGS_logtostderr = true; - } - - ~LoggerDisabler() { - BufferedLogSink::enable(); - FLAGS_logtostderr = stderr_status_; - } - - private: - bool stderr_status_; -}; - -static void serializeIntermediateLog(const std::vector& log, - PluginRequest& request) { - pt::ptree tree; - for (const auto& log_item : log) { - pt::ptree child; - child.put("s", log_item.severity); - child.put("f", log_item.filename); - child.put("i", log_item.line); - child.put("m", log_item.message); - tree.push_back(std::make_pair("", child)); - } - - // Save the log as a request JSON string. - std::ostringstream output; - pt::write_json(output, tree, false); - request["log"] = output.str(); -} - -static void deserializeIntermediateLog(const PluginRequest& request, - std::vector& log) { - if (request.count("log") == 0) { - return; - } - - // Read the plugin request string into a JSON tree and enumerate. - pt::ptree tree; - try { - std::stringstream input; - input << request.at("log"); - pt::read_json(input, tree); - } catch (const pt::json_parser::json_parser_error& e) { - return; - } - - for (const auto& item : tree.get_child("")) { - log.push_back({ - (StatusLogSeverity)item.second.get("s", O_INFO), - item.second.get("f", ""), - item.second.get("i", 0), - item.second.get("m", ""), - }); - } -} - -void setVerboseLevel() { - if (Flag::getValue("verbose") == "true") { - // Turn verbosity up to 1. - // Do log DEBUG, INFO, WARNING, ERROR to their log files. - // Do log the above and verbose=1 to stderr. - FLAGS_minloglevel = 0; // INFO - FLAGS_stderrthreshold = 0; // INFO - FLAGS_v = 1; - } else { - // Do NOT log INFO, WARNING, ERROR to stderr. - // Do log only WARNING, ERROR to log sinks. - FLAGS_minloglevel = 1; // WARNING - FLAGS_stderrthreshold = 1; // WARNING - } - - if (FLAGS_disable_logging) { - // Do log ERROR to stderr. - // Do NOT log INFO, WARNING, ERROR to their log files. - FLAGS_logtostderr = true; - if (!FLAGS_verbose) { - // verbose flag will still emit logs to stderr. - FLAGS_minloglevel = 2; // ERROR - } - } -} - -void initStatusLogger(const std::string& name) { - FLAGS_alsologtostderr = false; - FLAGS_logbufsecs = 0; // flush the log buffer immediately - FLAGS_stop_logging_if_full_disk = true; - FLAGS_max_log_size = 10; // max size for individual log file is 10MB - FLAGS_logtostderr = true; - - setVerboseLevel(); - // Start the logging, and announce the daemon is starting. - google::InitGoogleLogging(name.c_str()); - - // If logging is disabled then do not buffer intermediate logs. - if (!FLAGS_disable_logging) { - // Create an instance of the buffered log sink and do not forward logs yet. - BufferedLogSink::enable(); - } -} - -void initLogger(const std::string& name, bool forward_all) { - // Check if logging is disabled, if so then no need to shuttle intermediates. - if (FLAGS_disable_logging) { - return; - } - - // Stop the buffering sink and store the intermediate logs. - BufferedLogSink::disable(); - auto intermediate_logs = std::move(BufferedLogSink::dump()); - auto& logger_plugin = Registry::getActive("logger"); - if (!Registry::exists("logger", logger_plugin)) { - return; - } - - // Start the custom status logging facilities, which may instruct Glog as is - // the case with filesystem logging. - PluginRequest request = {{"init", name}}; - serializeIntermediateLog(intermediate_logs, request); - auto status = Registry::call("logger", request); - if (status.ok() || forward_all) { - // When LoggerPlugin::init returns success we enable the log sink in - // forwarding mode. Then Glog status logs are forwarded to logStatus. - BufferedLogSink::forward(true); - BufferedLogSink::enable(); - } -} - -void BufferedLogSink::send(google::LogSeverity severity, - const char* full_filename, - const char* base_filename, - int line, - const struct ::tm* tm_time, - const char* message, - size_t message_len) { - // Either forward the log to an enabled logger or buffer until one exists. - if (forward_) { - // May use the logs_ storage to buffer/delay sending logs. - std::vector log; - log.push_back({(StatusLogSeverity)severity, - std::string(base_filename), - line, - std::string(message, message_len)}); - PluginRequest request = {{"status", "true"}}; - serializeIntermediateLog(log, request); - Registry::call("logger", request); - } else { - logs_.push_back({(StatusLogSeverity)severity, - std::string(base_filename), - line, - std::string(message, message_len)}); - } -} - -Status LoggerPlugin::call(const PluginRequest& request, - PluginResponse& response) { - QueryLogItem item; - std::vector intermediate_logs; - if (request.count("string") > 0) { - return this->logString(request.at("string")); - } else if (request.count("snapshot") > 0) { - return this->logSnapshot(request.at("snapshot")); - } else if (request.count("health") > 0) { - return this->logHealth(request.at("health")); - } else if (request.count("init") > 0) { - deserializeIntermediateLog(request, intermediate_logs); - return this->init(request.at("init"), intermediate_logs); - } else if (request.count("status") > 0) { - deserializeIntermediateLog(request, intermediate_logs); - return this->logStatus(intermediate_logs); - } else { - return Status(1, "Unsupported call to logger plugin"); - } -} - -Status logString(const std::string& message, const std::string& category) { - return logString(message, category, Registry::getActive("logger")); -} - -Status logString(const std::string& message, - const std::string& category, - const std::string& receiver) { - if (!Registry::exists("logger", receiver)) { - LOG(ERROR) << "Logger receiver " << receiver << " not found"; - return Status(1, "Logger receiver not found"); - } - - auto status = Registry::call( - "logger", receiver, {{"string", message}, {"category", category}}); - return Status(0, "OK"); -} - -Status logQueryLogItem(const QueryLogItem& results) { - return logQueryLogItem(results, Registry::getActive("logger")); -} - -Status logQueryLogItem(const QueryLogItem& results, - const std::string& receiver) { - std::string json; - Status status; - if (FLAGS_log_result_events) { - status = serializeQueryLogItemAsEventsJSON(results, json); - } else { - status = serializeQueryLogItemJSON(results, json); - } - if (!status.ok()) { - return status; - } - return logString(json, "event", receiver); -} - -Status logSnapshotQuery(const QueryLogItem& item) { - std::string json; - if (!serializeQueryLogItemJSON(item, json)) { - return Status(1, "Could not serialize snapshot"); - } - return Registry::call("logger", {{"snapshot", json}}); -} - -Status logHealthStatus(const QueryLogItem& item) { - std::string json; - if (!serializeQueryLogItemJSON(item, json)) { - return Status(1, "Could not serialize health"); - } - return Registry::call("logger", {{"health", json}}); -} - -void relayStatusLogs() { - // Prevent out dumping and registry calling from producing additional logs. - LoggerDisabler disabler; - - // Construct a status log plugin request. - PluginRequest req = {{"status", "true"}}; - auto& status_logs = BufferedLogSink::dump(); - if (status_logs.size() == 0) { - return; - } - - // Skip the registry's logic, and send directly to the core's logger. - PluginResponse resp; - serializeIntermediateLog(status_logs, req); - auto status = callExtension(0, "logger", FLAGS_logger_plugin, req, resp); - if (status.ok()) { - // Flush the buffered status logs. - // Otherwise the extension call failed and the buffering should continue. - status_logs.clear(); - } -} -} diff --git a/osquery/logger/plugins/filesystem.cpp b/osquery/logger/plugins/filesystem.cpp deleted file mode 100644 index 6dba366..0000000 --- a/osquery/logger/plugins/filesystem.cpp +++ /dev/null @@ -1,143 +0,0 @@ -/* - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include -#include - -#include -#include -#include - -namespace pt = boost::property_tree; -namespace fs = boost::filesystem; - -namespace osquery { - -FLAG(string, - logger_path, - "/var/log/osquery/", - "Directory path for ERROR/WARN/INFO and results logging"); -/// Legacy, backward compatible "osquery_log_dir" CLI option. -FLAG_ALIAS(std::string, osquery_log_dir, logger_path); - -const std::string kFilesystemLoggerFilename = "osqueryd.results.log"; -const std::string kFilesystemLoggerSnapshots = "osqueryd.snapshots.log"; -const std::string kFilesystemLoggerHealth = "osqueryd.health.log"; - -std::mutex filesystemLoggerPluginMutex; - -class FilesystemLoggerPlugin : public LoggerPlugin { - public: - Status setUp(); - Status logString(const std::string& s); - Status logStringToFile(const std::string& s, const std::string& filename); - Status logSnapshot(const std::string& s); - Status logHealth(const std::string& s); - Status init(const std::string& name, const std::vector& log); - Status logStatus(const std::vector& log); - - private: - fs::path log_path_; -}; - -REGISTER(FilesystemLoggerPlugin, "logger", "filesystem"); - -Status FilesystemLoggerPlugin::setUp() { - log_path_ = fs::path(FLAGS_logger_path); - return Status(0, "OK"); -} - -Status FilesystemLoggerPlugin::logString(const std::string& s) { - return logStringToFile(s, kFilesystemLoggerFilename); -} - -Status FilesystemLoggerPlugin::logStringToFile(const std::string& s, - const std::string& filename) { - std::lock_guard lock(filesystemLoggerPluginMutex); - try { - // The results log may contain sensitive information if run as root. - auto status = writeTextFile((log_path_ / filename).string(), s, 0640, true); - if (!status.ok()) { - return status; - } - } catch (const std::exception& e) { - return Status(1, e.what()); - } - return Status(0, "OK"); -} - -Status FilesystemLoggerPlugin::logStatus( - const std::vector& log) { - for (const auto& item : log) { - // Emit this intermediate log to the Glog filesystem logger. - google::LogMessage(item.filename.c_str(), - item.line, - (google::LogSeverity)item.severity).stream() - << item.message; - } - - return Status(0, "OK"); -} - -Status FilesystemLoggerPlugin::logSnapshot(const std::string& s) { - // Send the snapshot data to a separate filename. - return logStringToFile(s, kFilesystemLoggerSnapshots); -} - -Status FilesystemLoggerPlugin::logHealth(const std::string& s) { - return logStringToFile(s, kFilesystemLoggerHealth); -} - -Status FilesystemLoggerPlugin::init(const std::string& name, - const std::vector& log) { - // Stop the internal Glog facilities. - google::ShutdownGoogleLogging(); - - // The log dir is used for status logging and the filesystem results logs. - if (isWritable(log_path_.string()).ok()) { - FLAGS_log_dir = log_path_.string(); - FLAGS_logtostderr = false; - } else { - // If we cannot write logs to the filesystem, fallback to stderr. - // The caller (flags/options) might 'also' be logging to stderr using - // debug, verbose, etc. - FLAGS_logtostderr = true; - } - - // Restart the Glog facilities using the name `init` was provided. - google::InitGoogleLogging(name.c_str()); - - // We may violate Glog global object assumptions. So set names manually. - auto basename = (log_path_ / name).string(); - google::SetLogDestination(google::INFO, (basename + ".INFO.").c_str()); - google::SetLogDestination(google::WARNING, (basename + ".WARNING.").c_str()); - google::SetLogDestination(google::ERROR, (basename + ".ERROR.").c_str()); - - // Store settings for logging to stderr. - bool log_to_stderr = FLAGS_logtostderr; - bool also_log_to_stderr = FLAGS_alsologtostderr; - int stderr_threshold = FLAGS_stderrthreshold; - FLAGS_alsologtostderr = false; - FLAGS_logtostderr = false; - FLAGS_stderrthreshold = 5; - - // Now funnel the intermediate status logs provided to `init`. - logStatus(log); - - // Restore settings for logging to stderr. - FLAGS_logtostderr = log_to_stderr; - FLAGS_alsologtostderr = also_log_to_stderr; - FLAGS_stderrthreshold = stderr_threshold; - - // The filesystem logger cheats and uses Glog to log to the filesystem so - // we can return failure here and stop the custom log sink. - return Status(1, "No status logger used for filesystem"); -} -} diff --git a/osquery/logger/plugins/syslog.cpp b/osquery/logger/plugins/syslog.cpp deleted file mode 100644 index d88ab70..0000000 --- a/osquery/logger/plugins/syslog.cpp +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include - -#include -#include - -namespace osquery { - -FLAG(int32, - logger_syslog_facility, - LOG_LOCAL3 >> 3, - "Syslog facility for status and results logs (0-23, default 19)"); - -class SyslogLoggerPlugin : public LoggerPlugin { - public: - Status logString(const std::string& s); - Status init(const std::string& name, const std::vector& log); - Status logStatus(const std::vector& log); -}; - -REGISTER(SyslogLoggerPlugin, "logger", "syslog"); - -Status SyslogLoggerPlugin::logString(const std::string& s) { - for (const auto& line : osquery::split(s, "\n")) { - syslog(LOG_INFO, "result=%s", line.c_str()); - } - return Status(0, "OK"); -} - -Status SyslogLoggerPlugin::logStatus(const std::vector& log) { - for (const auto& item : log) { - int severity = LOG_NOTICE; - if (item.severity == O_INFO) { - severity = LOG_NOTICE; - } else if (item.severity == O_WARNING) { - severity = LOG_WARNING; - } else if (item.severity == O_ERROR) { - severity = LOG_ERR; - } else if (item.severity == O_FATAL) { - severity = LOG_CRIT; - } - - std::string line = "severity=" + std::to_string(item.severity) - + " location=" + item.filename + ":" + std::to_string(item.line) + - " message=" + item.message; - - syslog(severity, "%s", line.c_str()); - } - return Status(0, "OK"); -} - -Status SyslogLoggerPlugin::init(const std::string& name, - const std::vector& log) { - closelog(); - - // Define the syslog/target's application name. - if (FLAGS_logger_syslog_facility < 0 || - FLAGS_logger_syslog_facility > 23) { - FLAGS_logger_syslog_facility = LOG_LOCAL3 >> 3; - } - openlog(name.c_str(), LOG_PID | LOG_CONS, FLAGS_logger_syslog_facility << 3); - - // Now funnel the intermediate status logs provided to `init`. - return logStatus(log); -} -} diff --git a/osquery/logger/tests/logger_tests.cpp b/osquery/logger/tests/logger_tests.cpp deleted file mode 100644 index 8c4e81d..0000000 --- a/osquery/logger/tests/logger_tests.cpp +++ /dev/null @@ -1,177 +0,0 @@ -/* - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include - -#include -#include -#include - -namespace osquery { - -DECLARE_string(logger_plugin); - -class LoggerTests : public testing::Test { - public: - void SetUp() { - logging_status_ = FLAGS_disable_logging; - FLAGS_disable_logging = false; - - log_lines.clear(); - status_messages.clear(); - statuses_logged = 0; - last_status = {O_INFO, "", -1, ""}; - } - - void TearDown() { FLAGS_disable_logging = logging_status_; } - - // Track lines emitted to logString - static std::vector log_lines; - - // Track the results of init - static StatusLogLine last_status; - static std::vector status_messages; - - // Count calls to logStatus - static int statuses_logged; - // Count added and removed snapshot rows - static int snapshot_rows_added; - static int snapshot_rows_removed; - // Count the added health status rows - static int health_status_rows; - - private: - /// Save the status of logging before running tests, restore afterward. - bool logging_status_; -}; - -std::vector LoggerTests::log_lines; -StatusLogLine LoggerTests::last_status; -std::vector LoggerTests::status_messages; -int LoggerTests::statuses_logged = 0; -int LoggerTests::snapshot_rows_added = 0; -int LoggerTests::snapshot_rows_removed = 0; -int LoggerTests::health_status_rows = 0; - -class TestLoggerPlugin : public LoggerPlugin { - public: - TestLoggerPlugin() {} - - Status logString(const std::string& s) { - LoggerTests::log_lines.push_back(s); - return Status(0, s); - } - - Status init(const std::string& name, const std::vector& log) { - for (const auto& status : log) { - LoggerTests::status_messages.push_back(status.message); - } - - if (log.size() > 0) { - LoggerTests::last_status = log.back(); - } - - if (name == "RETURN_FAILURE") { - return Status(1, "OK"); - } else { - return Status(0, "OK"); - } - } - - Status logStatus(const std::vector& log) { - ++LoggerTests::statuses_logged; - return Status(0, "OK"); - } - - Status logSnapshot(const std::string& s) { - LoggerTests::snapshot_rows_added += 1; - LoggerTests::snapshot_rows_removed += 0; - return Status(0, "OK"); - } - - Status logHealth(const std::string& s) { - LoggerTests::health_status_rows += 1; - return Status(0, "OK"); - } - - virtual ~TestLoggerPlugin() {} -}; - -TEST_F(LoggerTests, test_plugin) { - Registry::add("logger", "test"); - Registry::setUp(); - - auto s = Registry::call("logger", "test", {{"string", "foobar"}}); - EXPECT_TRUE(s.ok()); - EXPECT_EQ(LoggerTests::log_lines.back(), "foobar"); -} - -TEST_F(LoggerTests, test_logger_init) { - // Expect the logger to have been registered from the first test. - EXPECT_TRUE(Registry::exists("logger", "test")); - EXPECT_TRUE(Registry::setActive("logger", "test").ok()); - - initStatusLogger("logger_test"); - // This will be printed to stdout. - LOG(WARNING) << "Logger test is generating a warning status (1)"; - initLogger("logger_test"); - - // The warning message will have been buffered and sent to the active logger - // which is test. - EXPECT_EQ(LoggerTests::status_messages.size(), 1); - - // The logStatus API should NOT have been called. It will only be used if - // (1) The active logger's init returns success within initLogger and - // (2) for status logs generated after initLogger is called. - EXPECT_EQ(LoggerTests::statuses_logged, 0); -} - -TEST_F(LoggerTests, test_logger_log_status) { - // This will be printed to stdout. - LOG(WARNING) << "Logger test is generating a warning status (2)"; - - // The second warning status will be sent to the logger plugin. - EXPECT_EQ(LoggerTests::statuses_logged, 1); -} - -TEST_F(LoggerTests, test_logger_variations) { - // Init the logger for a second time, this should only be done for testing. - // This time we'll trigger the init method to fail and prevent additional - // status messages from trigger logStatus. - initLogger("RETURN_FAILURE"); - - // This will be printed to stdout. - LOG(WARNING) << "Logger test is generating a warning status (3)"; - - // Since the initLogger call triggered a failed init, meaning the logger - // does NOT handle Glog logs, there will be no statuses logged. - EXPECT_EQ(LoggerTests::statuses_logged, 0); -} - -TEST_F(LoggerTests, test_logger_snapshots) { - // A snapshot query should not include removed items. - QueryLogItem item; - item.name = "test_query"; - item.identifier = "unknown_test_host"; - item.time = 0; - item.calendar_time = "no_time"; - - // Add a fake set of results. - item.results.added.push_back({{"test_column", "test_value"}}); - logSnapshotQuery(item); - - // Expect the plugin to optionally handle snapshot logging. - EXPECT_EQ(LoggerTests::snapshot_rows_added, 1); - - // Add the same item as a health status log item. - logHealthStatus(item); - EXPECT_EQ(LoggerTests::health_status_rows, 1); -} -} diff --git a/osquery/main/daemon.cpp b/osquery/main/daemon.cpp deleted file mode 100644 index 8b376e6..0000000 --- a/osquery/main/daemon.cpp +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include - -#include - -#include "osquery/dispatcher/scheduler.h" - -const std::string kWatcherWorkerName = "osqueryd: worker"; - -int main(int argc, char* argv[]) { - osquery::Initializer runner(argc, argv, osquery::OSQUERY_TOOL_DAEMON); - - if (!runner.isWorker()) { - runner.initDaemon(); - } - - // When a watchdog is used, the current daemon will fork/exec into a worker. - // In either case the watcher may start optionally loaded extensions. - runner.initWorkerWatcher(kWatcherWorkerName); - - // Start osquery work. - runner.start(); - - // Begin the schedule runloop. - osquery::startScheduler(); - - // Finally shutdown. - runner.shutdown(); - - return 0; -} diff --git a/osquery/main/empty.cpp b/osquery/main/empty.cpp deleted file mode 100644 index ab9ead5..0000000 --- a/osquery/main/empty.cpp +++ /dev/null @@ -1,9 +0,0 @@ -/* - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ diff --git a/osquery/main/lib.cpp b/osquery/main/lib.cpp deleted file mode 100644 index 8de6ca6..0000000 --- a/osquery/main/lib.cpp +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include - -#include - -// If CMake/gmake did not define a build version set the version to 1.0. -// clang-format off -#ifndef OSQUERY_BUILD_VERSION -#define OSQUERY_BUILD_VERSION 1.0.0-unknown -#endif -// clang-format on - -namespace osquery { - -const std::string kVersion = STR(OSQUERY_BUILD_VERSION); -const std::string kSDKVersion = OSQUERY_SDK_VERSION; -const std::string kSDKPlatform = OSQUERY_PLATFORM; -} diff --git a/osquery/main/run.cpp b/osquery/main/run.cpp deleted file mode 100644 index 62a933a..0000000 --- a/osquery/main/run.cpp +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include - -#include - -#include -#include -#include -#include - -DEFINE_string(query, "", "query to execute"); -DEFINE_int32(iterations, 1, "times to run the query in question"); -DEFINE_int32(delay, 0, "delay before and after the query"); - -namespace osquery { - -DECLARE_bool(disable_events); -DECLARE_bool(registry_exceptions); -} - -int main(int argc, char* argv[]) { - // Only log to stderr - FLAGS_logtostderr = true; - - // Let gflags parse the non-help options/flags. - GFLAGS_NAMESPACE::ParseCommandLineFlags(&argc, &argv, false); - GFLAGS_NAMESPACE::InitGoogleLogging(argv[0]); - - if (FLAGS_query == "") { - fprintf(stderr, "Usage: %s --query=\"query\"\n", argv[0]); - return 1; - } - - osquery::Registry::setUp(); - osquery::FLAGS_disable_events = true; - osquery::FLAGS_registry_exceptions = true; - osquery::attachEvents(); - - if (FLAGS_delay != 0) { - ::sleep(FLAGS_delay); - } - - osquery::QueryData results; - osquery::Status status; - for (int i = 0; i < FLAGS_iterations; ++i) { - status = osquery::query(FLAGS_query, results); - if (!status.ok()) { - fprintf(stderr, "Query failed: %d\n", status.getCode()); - break; - } - } - - if (FLAGS_delay != 0) { - ::sleep(FLAGS_delay); - } - - // Instead of calling "shutdownOsquery" force the EF to join its threads. - GFLAGS_NAMESPACE::ShutDownCommandLineFlags(); - - return status.getCode(); -} diff --git a/osquery/main/shell.cpp b/osquery/main/shell.cpp deleted file mode 100644 index b6c1744..0000000 --- a/osquery/main/shell.cpp +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include - -#include -#include - -#include "osquery/core/watcher.h" -#include "osquery/devtools/devtools.h" - -int main(int argc, char *argv[]) { - // Parse/apply flags, start registry, load logger/config plugins. - osquery::Initializer runner(argc, argv, osquery::OSQUERY_TOOL_SHELL); - if (argc > 1 || !isatty(fileno(stdin)) || osquery::FLAGS_A.size() > 0 || - osquery::FLAGS_L) { - // A query was set as a positional argument for via stdin. - osquery::FLAGS_disable_events = true; - // The shell may have loaded table extensions, if not, disable the manager. - if (!osquery::Watcher::hasManagedExtensions()) { - osquery::FLAGS_disable_extensions = true; - } - } - - runner.start(); - - // Virtual tables will be attached to the shell's in-memory SQLite DB. - int retcode = osquery::launchIntoShell(argc, argv); - - // Finally shutdown. - runner.shutdown(); - return retcode; -} diff --git a/osquery/main/tests.cpp b/osquery/main/tests.cpp deleted file mode 100644 index d25960d..0000000 --- a/osquery/main/tests.cpp +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright (c) 2015, Wesley Shields - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include -#include - -#include - -#include - -#include - -#include "osquery/core/test_util.h" -#include "osquery/database/db_handle.h" - -namespace fs = boost::filesystem; - -namespace osquery { - -DECLARE_string(database_path); -DECLARE_string(extensions_socket); -DECLARE_string(modules_autoload); -DECLARE_string(extensions_autoload); -DECLARE_bool(disable_logging); -DECLARE_bool(verbose); - -typedef std::chrono::high_resolution_clock chrono_clock; - -void initTesting() { - // Seed the random number generator, some tests generate temporary files - // ports, sockets, etc using random numbers. - std::srand(chrono_clock::now().time_since_epoch().count()); - - // Set safe default values for path-based flags. - // Specific unittests may edit flags temporarily. - fs::remove_all(kTestWorkingDirectory); - fs::create_directories(kTestWorkingDirectory); - FLAGS_database_path = kTestWorkingDirectory + "unittests.db"; - FLAGS_extensions_socket = kTestWorkingDirectory + "unittests.em"; - FLAGS_extensions_autoload = kTestWorkingDirectory + "unittests-ext.load"; - FLAGS_modules_autoload = kTestWorkingDirectory + "unittests-mod.load"; - FLAGS_disable_logging = true; - FLAGS_verbose = true; - - // Create a default DBHandle instance before unittests. - (void)DBHandle::getInstance(); -} -} - -int main(int argc, char* argv[]) { - // Allow unit test execution from anywhere in the osquery source/build tree. - while (osquery::kTestDataPath != "/") { - if (!fs::exists(osquery::kTestDataPath)) { - osquery::kTestDataPath = - osquery::kTestDataPath.substr(3, osquery::kTestDataPath.size()); - } else { - break; - } - } - - osquery::initTesting(); - testing::InitGoogleTest(&argc, argv); - // Optionally enable Goggle Logging - // google::InitGoogleLogging(argv[0]); - return RUN_ALL_TESTS(); -} diff --git a/osquery/registry/CMakeLists.txt b/osquery/registry/CMakeLists.txt deleted file mode 100644 index b2dfbda..0000000 --- a/osquery/registry/CMakeLists.txt +++ /dev/null @@ -1,18 +0,0 @@ -# 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_registry registry.cpp) - -FILE(GLOB OSQUERY_REGISTRY_TESTS "tests/*.cpp") -ADD_OSQUERY_TEST(${OSQUERY_REGISTRY_TESTS}) diff --git a/osquery/registry/registry.cpp b/osquery/registry/registry.cpp deleted file mode 100644 index a5acd7b..0000000 --- a/osquery/registry/registry.cpp +++ /dev/null @@ -1,537 +0,0 @@ -/* - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include -#include - -#include - -#include - -#include -#include -#include - -namespace osquery { - -HIDDEN_FLAG(bool, registry_exceptions, false, "Allow plugin exceptions"); - -void RegistryHelperCore::remove(const std::string& item_name) { - 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 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); - } -} - -bool RegistryHelperCore::isInternal(const std::string& item_name) const { - if (std::find(internal_.begin(), internal_.end(), item_name) == - internal_.end()) { - return false; - } - return true; -} - -Status RegistryHelperCore::setActive(const std::string& item_name) { - if (items_.count(item_name) == 0 && external_.count(item_name) == 0) { - return Status(1, "Unknown registry item"); - } - - active_ = item_name; - // The active plugin is setup when initialized. - if (exists(item_name, true)) { - Registry::get(name_, item_name)->setUp(); - } - return Status(0, "OK"); -} - -const std::string& RegistryHelperCore::getActive() const { return active_; } - -RegistryRoutes RegistryHelperCore::getRoutes() const { - RegistryRoutes route_table; - for (const auto& item : items_) { - if (isInternal(item.first)) { - // This is an internal plugin, do not include the route. - continue; - } - - 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 RegistryHelperCore::call(const std::string& item_name, - const PluginRequest& request, - PluginResponse& response) { - if (items_.count(item_name) > 0) { - return items_.at(item_name)->call(request, response); - } - - if (external_.count(item_name) > 0) { - // The item is a registered extension, call the extension by UUID. - return callExtension(external_.at(item_name), name_, item_name, request, - response); - } 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(0, "Route only"); - } else if (Registry::external()) { - // If this is an extension's registry forward unknown calls to the core. - return callExtension(0, name_, item_name, request, response); - } - - return Status(1, "Cannot call registry item: " + item_name); -} - -Status RegistryHelperCore::addAlias(const std::string& item_name, - const std::string& alias) { - if (aliases_.count(alias) > 0) { - return Status(1, "Duplicate alias: " + alias); - } - aliases_[alias] = item_name; - return Status(0, "OK"); -} - -const std::string& RegistryHelperCore::getAlias( - const std::string& alias) const { - if (aliases_.count(alias) == 0) { - return alias; - } - return aliases_.at(alias); -} - -void RegistryHelperCore::setUp() { - // 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; - } - - // Try to set up each of the registry items. - // If they fail, remove them from the registry. - std::vector failed; - for (auto& item : items_) { - if (!item.second->setUp().ok()) { - failed.push_back(item.first); - } - } - - for (const auto& failed_item : failed) { - remove(failed_item); - } -} - -/// Facility method to check if a registry item exists. -bool RegistryHelperCore::exists(const std::string& item_name, - bool local) const { - 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; -} - -/// Facility method to list the registry item identifiers. -std::vector RegistryHelperCore::names() const { - std::vector 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); - } - return names; -} - -/// Facility method to count the number of items in this registry. -size_t RegistryHelperCore::count() const { return items_.size(); } - -/// Allow the registry to introspect into the registered name (for logging). -void RegistryHelperCore::setName(const std::string& name) { name_ = name; } - -const std::map& RegistryFactory::all() { - return instance().registries_; -} - -PluginRegistryHelperRef RegistryFactory::registry( - const std::string& registry_name) { - return instance().registries_.at(registry_name); -} - -const std::map RegistryFactory::all( - const std::string& registry_name) { - return instance().registry(registry_name)->all(); -} - -PluginRef RegistryFactory::get(const std::string& registry_name, - const std::string& item_name) { - return instance().registry(registry_name)->get(item_name); -} - -RegistryBroadcast RegistryFactory::getBroadcast() { - RegistryBroadcast broadcast; - for (const auto& registry : instance().registries_) { - broadcast[registry.first] = registry.second->getRoutes(); - } - return broadcast; -} - -Status RegistryFactory::addBroadcast(const RouteUUID& uuid, - const RegistryBroadcast& broadcast) { - if (instance().extensions_.count(uuid) > 0) { - return Status(1, "Duplicate extension UUID: " + std::to_string(uuid)); - } - - // Make sure the extension does not broadcast conflicting registry items. - if (!Registry::allowDuplicates()) { - for (const auto& registry : broadcast) { - for (const auto& item : registry.second) { - if (Registry::exists(registry.first, item.first)) { - VLOG(1) << "Extension " << uuid - << " has duplicate plugin name: " << item.first - << " in registry: " << registry.first; - return Status(1, "Duplicate registry item: " + item.first); - } - } - } - } - - // Once duplication is satisfied call each registry's addExternal. - Status status; - for (const auto& registry : broadcast) { - status = RegistryFactory::registry(registry.first) - ->addExternal(uuid, registry.second); - if (!status.ok()) { - // If any registry fails to add the set of external routes, stop. - break; - } - - for (const auto& plugin : registry.second) { - VLOG(1) << "Extension " << uuid << " registered " << registry.first - << " plugin " << plugin.first; - } - } - - // If any registry failed, remove each (assume a broadcast is atomic). - if (!status.ok()) { - for (const auto& registry : broadcast) { - Registry::registry(registry.first)->removeExternal(uuid); - } - } - instance().extensions_.insert(uuid); - return status; -} - -Status RegistryFactory::removeBroadcast(const RouteUUID& uuid) { - if (instance().extensions_.count(uuid) == 0) { - return Status(1, "Unknown extension UUID: " + std::to_string(uuid)); - } - - for (const auto& registry : instance().registries_) { - registry.second->removeExternal(uuid); - } - instance().extensions_.erase(uuid); - return Status(0, "OK"); -} - -/// Adds an alias for an internal registry item. This registry will only -/// broadcast the alias name. -Status RegistryFactory::addAlias(const std::string& registry_name, - const std::string& item_name, - const std::string& alias) { - if (instance().registries_.count(registry_name) == 0) { - return Status(1, "Unknown registry: " + registry_name); - } - return instance().registries_.at(registry_name)->addAlias(item_name, alias); -} - -/// Returns the item_name or the item alias if an alias exists. -const std::string& RegistryFactory::getAlias(const std::string& registry_name, - const std::string& alias) { - if (instance().registries_.count(registry_name) == 0) { - return alias; - } - return instance().registries_.at(registry_name)->getAlias(alias); -} - -Status RegistryFactory::call(const std::string& registry_name, - const std::string& item_name, - const PluginRequest& request, - PluginResponse& response) { - // Forward factory call to the registry. - try { - return registry(registry_name)->call(item_name, request, response); - } catch (const std::exception& e) { - LOG(ERROR) << registry_name << " registry " << item_name - << " plugin caused exception: " << e.what(); - if (FLAGS_registry_exceptions) { - throw e; - } - return Status(1, e.what()); - } catch (...) { - LOG(ERROR) << registry_name << " registry " << item_name - << " plugin caused unknown exception"; - if (FLAGS_registry_exceptions) { - throw std::runtime_error(registry_name + ": " + item_name + " failed"); - } - return Status(2, "Unknown exception"); - } -} - -Status RegistryFactory::call(const std::string& registry_name, - const std::string& item_name, - const PluginRequest& request) { - PluginResponse response; - // Wrapper around a call expecting a response. - return call(registry_name, item_name, request, response); -} - -Status RegistryFactory::call(const std::string& registry_name, - const PluginRequest& request, - PluginResponse& response) { - auto& plugin = registry(registry_name)->getActive(); - return call(registry_name, plugin, request, response); -} - -Status RegistryFactory::call(const std::string& registry_name, - const PluginRequest& request) { - PluginResponse response; - return call(registry_name, request, response); -} - -Status RegistryFactory::setActive(const std::string& registry_name, - const std::string& item_name) { - if (!exists(registry_name, item_name)) { - return Status(1, "Registry plugin does not exist"); - } - return registry(registry_name)->setActive(item_name); -} - -const std::string& RegistryFactory::getActive( - const std::string& registry_name) { - return registry(registry_name)->getActive(); -} - -void RegistryFactory::setUp() { - for (const auto& registry : instance().all()) { - registry.second->setUp(); - } -} - -bool RegistryFactory::exists(const std::string& registry_name, - const std::string& item_name, - bool local) { - if (instance().registries_.count(registry_name) == 0) { - return false; - } - - // Check the registry. - return registry(registry_name)->exists(item_name, local); -} - -std::vector RegistryFactory::names() { - std::vector names; - for (const auto& registry : all()) { - names.push_back(registry.second->getName()); - } - return names; -} - -std::vector RegistryFactory::names( - const std::string& registry_name) { - if (instance().registries_.at(registry_name) == 0) { - std::vector names; - return names; - } - return instance().registry(registry_name)->names(); -} - -std::vector RegistryFactory::routeUUIDs() { - std::vector uuids; - for (const auto& extension : instance().extensions_) { - uuids.push_back(extension); - } - return uuids; -} - -size_t RegistryFactory::count() { return instance().registries_.size(); } - -size_t RegistryFactory::count(const std::string& registry_name) { - if (instance().registries_.count(registry_name) == 0) { - return 0; - } - return instance().registry(registry_name)->count(); -} - -Status RegistryHelperCore::add(const std::string& item_name, bool internal) { - // The item can be listed as internal, meaning it does not broadcast. - if (internal) { - internal_.push_back(item_name); - } - - // The item may belong to a module. - if (RegistryFactory::usingModule()) { - modules_[item_name] = RegistryFactory::getModule(); - } - - return Status(0, "OK"); -} - -const std::map& RegistryFactory::getModules() { - return instance().modules_; -} - -RouteUUID RegistryFactory::getModule() { return instance().module_uuid_; } - -bool RegistryFactory::usingModule() { - // Check if the registry is allowing a module's registrations. - return (!instance().locked() && instance().module_uuid_ != 0); -} - -void RegistryFactory::shutdownModule() { - // TODO: [temporarily disable] should be check. - //instance().locked(true); - instance().module_uuid_ = 0; -} - -void RegistryFactory::initModule(const std::string& path) { - // Begin a module initialization, lock until the module is determined - // appropriate by requesting a call to `declareModule`. - instance().module_uuid_ = (RouteUUID)rand(); - instance().modules_[getModule()].path = path; - instance().locked(true); -} - -void RegistryFactory::declareModule(const std::string& name, - const std::string& version, - const std::string& min_sdk_version, - const std::string& sdk_version) { - // Check the min_sdk_version against the Registry's SDK version. - auto& module = instance().modules_[instance().module_uuid_]; - module.name = name; - module.version = version; - module.sdk_version = sdk_version; - instance().locked(false); -} - -RegistryModuleLoader::RegistryModuleLoader(const std::string& path) - : handle_(nullptr), path_(path) { - // Tell the registry that we are attempting to construct a module. - // Locking the registry prevents the module's global initialization from - // adding or creating registry items. - RegistryFactory::initModule(path_); - handle_ = dlopen(path_.c_str(), RTLD_NOW | RTLD_LOCAL); - if (handle_ == nullptr) { - VLOG(1) << "Failed to load module: " << path_; - VLOG(1) << dlerror(); - return; - } - - // The module should have called RegistryFactory::declareModule and unlocked - // the registry for modification. The module should have done this using - // the SDK's CREATE_MODULE macro, which adds the global-scope constructor. - if (RegistryFactory::locked()) { - VLOG(1) << "Failed to declare module: " << path_; - dlclose(handle_); - handle_ = nullptr; - } -} - -void RegistryModuleLoader::init() { - if (handle_ == nullptr || RegistryFactory::locked()) { - handle_ = nullptr; - return; - } - - // Locate a well-known symbol in the module. - // This symbol name is protected against rewriting when the module uses the - // SDK's CREATE_MODULE macro. - auto initializer = (ModuleInitalizer)dlsym(handle_, "initModule"); - if (initializer != nullptr) { - initializer(); - VLOG(1) << "Initialized module: " << path_; - } else { - VLOG(1) << "Failed to initialize module: " << path_; - VLOG(1) << dlerror(); - dlclose(handle_); - handle_ = nullptr; - } -} - -RegistryModuleLoader::~RegistryModuleLoader() { - if (handle_ == nullptr) { - // The module was not loaded or did not initalize. - RegistryFactory::instance().modules_.erase(RegistryFactory::getModule()); - } - - // We do not close the module, and thus are OK with losing a reference to the - // module's handle. Attempting to close and clean up is very expensive for - // very little value/features. - if (!RegistryFactory::locked()) { - RegistryFactory::shutdownModule(); - } - // No need to clean this resource. - handle_ = nullptr; -} - -void Plugin::getResponse(const std::string& key, - const PluginResponse& response, - boost::property_tree::ptree& tree) { - for (const auto& item : response) { - boost::property_tree::ptree child; - for (const auto& item_detail : item) { - child.put(item_detail.first, item_detail.second); - } - tree.add_child(key, child); - } -} - -void Plugin::setResponse(const std::string& key, - const boost::property_tree::ptree& tree, - PluginResponse& response) { - std::ostringstream output; - try { - boost::property_tree::write_json(output, tree, false); - } catch (const pt::json_parser::json_parser_error& e) { - // The plugin response could not be serialized. - } - response.push_back({{key, output.str()}}); -} -} diff --git a/osquery/registry/tests/registry_tests.cpp b/osquery/registry/tests/registry_tests.cpp deleted file mode 100644 index 381a4e4..0000000 --- a/osquery/registry/tests/registry_tests.cpp +++ /dev/null @@ -1,273 +0,0 @@ -/* - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include - -#include -#include - -namespace osquery { - -class RegistryTests : public testing::Test {}; - -class CatPlugin : public Plugin { - public: - CatPlugin() : some_value_(0) {} - - protected: - int some_value_; -}; - -class HouseCat : public CatPlugin { - public: - Status setUp() { - // Make sure the Plugin implementation's init is called. - some_value_ = 9000; - return Status(0, "OK"); - } -}; - -/// This is a manual registry type without a name, so we cannot broadcast -/// this registry type and it does NOT need to conform to a registry API. -class CatRegistry : public RegistryHelper {}; - -TEST_F(RegistryTests, test_registry) { - CatRegistry cats; - - /// Add a CatRegistry item (a plugin) called "house". - cats.add("house"); - EXPECT_EQ(cats.count(), 1); - - /// Try to add the same plugin with the same name, this is meaningless. - cats.add("house"); - /// Now add the same plugin with a different name, a new plugin instance - /// will be created and registered. - cats.add("house2"); - EXPECT_EQ(cats.count(), 2); - - /// Request a plugin to call an API method. - auto cat = cats.get("house"); - cats.setUp(); - - /// Now let's iterate over every registered Cat plugin. - EXPECT_EQ(cats.all().size(), 2); -} - -/// Normally we have "Registry" that dictates the set of possible API methods -/// for all registry types. Here we use a "TestRegistry" instead. -class TestCoreRegistry : public RegistryFactory {}; - -/// We can automatically create a registry type as long as that type conforms -/// to the registry API defined in the "Registry". Here we use "TestRegistry". -/// The above "CatRegistry" was easier to understand, but using a auto -/// registry via the registry create method, we can assign a tracked name -/// and then broadcast that registry name to other plugins. -auto AutoCatRegistry = TestCoreRegistry::create("cat"); - -TEST_F(RegistryTests, test_auto_factory) { - /// Using the registry, and a registry type by name, we can register a - /// plugin HouseCat called "house" like above. - TestCoreRegistry::registry("cat")->add("auto_house"); - TestCoreRegistry::add("cat", "auto_house2"); - TestCoreRegistry::registry("cat")->setUp(); - - /// When acting on registries by name we can check the broadcasted - /// registry name of other plugin processes (via Thrift) as well as - /// internally registered plugins like HouseCat. - EXPECT_EQ(TestCoreRegistry::registry("cat")->count(), 2); - EXPECT_EQ(TestCoreRegistry::count("cat"), 2); - - /// And we can call an API method, since we guarantee CatPlugins conform - /// to the "TestCoreRegistry"'s "TestPluginAPI". - auto cat = TestCoreRegistry::get("cat", "auto_house"); - auto same_cat = TestCoreRegistry::get("cat", "auto_house"); - EXPECT_EQ(cat, same_cat); -} - -class DogPlugin : public Plugin { - public: - DogPlugin() : some_value_(10000) {} - - protected: - int some_value_; -}; - -class Doge : public DogPlugin { - public: - Doge() { some_value_ = 100000; } -}; - -class BadDoge : public DogPlugin { - public: - Status setUp() { return Status(1, "Expect error... this is a bad dog"); } -}; - -auto AutoDogRegistry = TestCoreRegistry::create("dog", true); - -TEST_F(RegistryTests, test_auto_registries) { - TestCoreRegistry::add("dog", "doge"); - TestCoreRegistry::registry("dog")->setUp(); - - EXPECT_EQ(TestCoreRegistry::count("dog"), 1); -} - -TEST_F(RegistryTests, test_persistant_registries) { - EXPECT_EQ(TestCoreRegistry::count("cat"), 2); -} - -TEST_F(RegistryTests, test_registry_exceptions) { - EXPECT_TRUE(TestCoreRegistry::add("dog", "duplicate_dog").ok()); - // Bad dog will be added fine, but when setup is run, it will be removed. - EXPECT_TRUE(TestCoreRegistry::add("dog", "bad_doge").ok()); - TestCoreRegistry::registry("dog")->setUp(); - // Make sure bad dog does not exist. - EXPECT_FALSE(TestCoreRegistry::exists("dog", "bad_doge")); - EXPECT_EQ(TestCoreRegistry::count("dog"), 2); - - int exception_count = 0; - try { - TestCoreRegistry::registry("does_not_exist"); - } catch (const std::out_of_range& e) { - exception_count++; - } - - try { - TestCoreRegistry::add("does_not_exist", "cat"); - } catch (const std::out_of_range& e) { - exception_count++; - } - - EXPECT_EQ(exception_count, 2); -} - -class WidgetPlugin : public Plugin { - public: - /// The route information will usually be provided by the plugin type. - /// The plugin/registry item will set some structures for the plugin - /// to parse and format. BUT a plugin/registry item can also fill this - /// information in if the plugin type/registry type exposes routeInfo as - /// a virtual method. - PluginResponse routeInfo() const { - PluginResponse info; - info.push_back({{"name", name_}}); - return info; - } - - /// Plugin types should contain generic request/response formatters and - /// decorators. - std::string secretPower(const PluginRequest& request) const { - if (request.count("secret_power") > 0) { - return request.at("secret_power"); - } - return "no_secret_power"; - } -}; - -class SpecialWidget : public WidgetPlugin { - public: - Status call(const PluginRequest& request, PluginResponse& response); -}; - -Status SpecialWidget::call(const PluginRequest& request, - PluginResponse& response) { - response.push_back(request); - response[0]["from"] = name_; - response[0]["secret_power"] = secretPower(request); - return Status(0, "OK"); -} - -#define UNUSED(x) (void)(x) - -TEST_F(RegistryTests, test_registry_api) { - auto AutoWidgetRegistry = TestCoreRegistry::create("widgets"); - UNUSED(AutoWidgetRegistry); - - TestCoreRegistry::add("widgets", "special"); - - // Test route info propogation, from item to registry, to broadcast. - auto ri = TestCoreRegistry::get("widgets", "special")->routeInfo(); - EXPECT_EQ(ri[0].at("name"), "special"); - auto rr = TestCoreRegistry::registry("widgets")->getRoutes(); - EXPECT_EQ(rr.size(), 1); - EXPECT_EQ(rr.at("special")[0].at("name"), "special"); - - // Broadcast will include all registries, and all their items. - auto broadcast_info = TestCoreRegistry::getBroadcast(); - EXPECT_TRUE(broadcast_info.size() >= 3); - EXPECT_EQ(broadcast_info.at("widgets").at("special")[0].at("name"), - "special"); - - PluginResponse response; - PluginRequest request; - auto status = TestCoreRegistry::call("widgets", "special", request, response); - EXPECT_TRUE(status.ok()); - EXPECT_EQ(response[0].at("from"), "special"); - EXPECT_EQ(response[0].at("secret_power"), "no_secret_power"); - - request["secret_power"] = "magic"; - status = TestCoreRegistry::call("widgets", "special", request, response); - EXPECT_EQ(response[0].at("secret_power"), "magic"); -} - -TEST_F(RegistryTests, test_real_registry) { - EXPECT_TRUE(Registry::count() > 0); - - bool has_one_registered = false; - for (const auto& registry : Registry::all()) { - if (Registry::count(registry.first) > 0) { - has_one_registered = true; - break; - } - } - EXPECT_TRUE(has_one_registered); -} - -TEST_F(RegistryTests, test_registry_modules) { - // Test the registry's module loading state tracking. - RegistryFactory::locked(false); - EXPECT_FALSE(RegistryFactory::locked()); - RegistryFactory::locked(true); - EXPECT_TRUE(RegistryFactory::locked()); - RegistryFactory::locked(false); - - // Test initializing a module load and the module's registry modifications. - EXPECT_EQ(RegistryFactory::getModule(), 0); - RegistryFactory::initModule("/my/test/module"); - // The registry is locked, no modifications during module global ctors. - EXPECT_TRUE(RegistryFactory::locked()); - // The 'is the registry using a module' is not set during module ctors. - EXPECT_FALSE(RegistryFactory::usingModule()); - EXPECT_EQ(RegistryFactory::getModules().size(), 1); - // The unittest can introspect into the current module. - auto& module = RegistryFactory::getModules().at(RegistryFactory::getModule()); - EXPECT_EQ(module.path, "/my/test/module"); - EXPECT_EQ(module.name, ""); - RegistryFactory::declareModule("test", "0.1.1", "0.0.0", "0.0.1"); - // The registry is unlocked after the module is declared. - // This assures that module modifications happen with the correct information - // and state tracking (aka the SDK limits, name, and version). - EXPECT_FALSE(RegistryFactory::locked()); - // Now the 'is the registry using a module' is set for the duration of the - // modules loading. - EXPECT_TRUE(RegistryFactory::usingModule()); - EXPECT_EQ(module.name, "test"); - EXPECT_EQ(module.version, "0.1.1"); - EXPECT_EQ(module.sdk_version, "0.0.1"); - - // Finally, when the module load is complete, we clear state. - RegistryFactory::shutdownModule(); - // The registry is again locked. -// TODO: Check below on higher upstream -// EXPECT_TRUE(RegistryFactory::locked()); - // And the registry is no longer using a module. - EXPECT_FALSE(RegistryFactory::usingModule()); - EXPECT_EQ(RegistryFactory::getModule(), 0); -} -} diff --git a/osquery/sql/CMakeLists.txt b/osquery/sql/CMakeLists.txt deleted file mode 100644 index ae844e5..0000000 --- a/osquery/sql/CMakeLists.txt +++ /dev/null @@ -1,21 +0,0 @@ -# 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_sql sql.cpp) - -ADD_OSQUERY_LIBRARY(osquery_sql_internal sqlite_util.cpp - virtual_table.cpp) - -FILE(GLOB OSQUERY_SQL_TESTS "tests/*.cpp") -ADD_OSQUERY_TEST(${OSQUERY_SQL_TESTS}) diff --git a/osquery/sql/sql.cpp b/osquery/sql/sql.cpp deleted file mode 100644 index f7b5f87..0000000 --- a/osquery/sql/sql.cpp +++ /dev/null @@ -1,120 +0,0 @@ -/* - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include - -#include -#include -#include -#include -#include - -namespace osquery { - -FLAG(int32, value_max, 512, "Maximum returned row value size"); - -const std::map kSQLOperatorRepr = { - {EQUALS, "="}, - {GREATER_THAN, ">"}, - {LESS_THAN_OR_EQUALS, "<="}, - {LESS_THAN, "<"}, - {GREATER_THAN_OR_EQUALS, ">="}, -}; - -SQL::SQL(const std::string& q) { status_ = query(q, results_); } - -const QueryData& SQL::rows() { return results_; } - -bool SQL::ok() { return status_.ok(); } - -Status SQL::getStatus() { return status_; } - -std::string SQL::getMessageString() { return status_.toString(); } - -const std::string SQL::kHostColumnName = "_source_host"; -void SQL::annotateHostInfo() { - std::string hostname = getHostname(); - for (Row& row : results_) { - row[kHostColumnName] = hostname; - } -} - -std::vector SQL::getTableNames() { - std::vector results; - for (const auto& name : Registry::names("table")) { - results.push_back(name); - } - return results; -} - -QueryData SQL::selectAllFrom(const std::string& table) { - PluginResponse response; - PluginRequest request = {{"action", "generate"}}; - Registry::call("table", table, request, response); - return response; -} - -QueryData SQL::selectAllFrom(const std::string& table, - const std::string& column, - ConstraintOperator op, - const std::string& expr) { - PluginResponse response; - PluginRequest request = {{"action", "generate"}}; - QueryContext ctx; - ctx.constraints[column].add(Constraint(op, expr)); - - TablePlugin::setRequestFromContext(ctx, request); - Registry::call("table", table, request, response); - return response; -} - -Status SQLPlugin::call(const PluginRequest& request, PluginResponse& response) { - response.clear(); - if (request.count("action") == 0) { - return Status(1, "SQL plugin must include a request action"); - } - - if (request.at("action") == "query") { - return this->query(request.at("query"), response); - } else if (request.at("action") == "columns") { - TableColumns columns; - auto status = this->getQueryColumns(request.at("query"), columns); - // Convert columns to response - for (const auto& column : columns) { - response.push_back({{"n", column.first}, {"t", column.second}}); - } - return status; - } else if (request.at("action") == "attach") { - // Attach a virtual table name using an optional included definition. - return this->attach(request.at("table")); - } else if (request.at("action") == "detach") { - this->detach(request.at("table")); - return Status(0, "OK"); - } - return Status(1, "Unknown action"); -} - -Status query(const std::string& q, QueryData& results) { - return Registry::call( - "sql", "sql", {{"action", "query"}, {"query", q}}, results); -} - -Status getQueryColumns(const std::string& q, TableColumns& columns) { - PluginResponse response; - auto status = Registry::call( - "sql", "sql", {{"action", "columns"}, {"query", q}}, response); - - // Convert response to columns - for (const auto& item : response) { - columns.push_back(make_pair(item.at("n"), item.at("t"))); - } - return status; -} -} diff --git a/osquery/sql/sqlite_util.cpp b/osquery/sql/sqlite_util.cpp deleted file mode 100644 index 162ad30..0000000 --- a/osquery/sql/sqlite_util.cpp +++ /dev/null @@ -1,235 +0,0 @@ -/* - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include -#include -#include -#include - -#include "osquery/sql/sqlite_util.h" -#include "osquery/sql/virtual_table.h" - -namespace osquery { -/// SQL provider for osquery internal/core. -REGISTER_INTERNAL(SQLiteSQLPlugin, "sql", "sql"); - -FLAG(string, - disable_tables, - "Not Specified", - "Comma-delimited list of table names to be disabled"); - -/** - * @brief A map of SQLite status codes to their corresponding message string - * - * Details of this map are defined at: http://www.sqlite.org/c3ref/c_abort.html - */ -const std::map kSQLiteReturnCodes = { - {0, "SQLITE_OK: Successful result"}, - {1, "SQLITE_ERROR: SQL error or missing database"}, - {2, "SQLITE_INTERNAL: Internal logic error in SQLite"}, - {3, "SQLITE_PERM: Access permission denied"}, - {4, "SQLITE_ABORT: Callback routine requested an abort"}, - {5, "SQLITE_BUSY: The database file is locked"}, - {6, "SQLITE_LOCKED: A table in the database is locked"}, - {7, "SQLITE_NOMEM: A malloc() failed"}, - {8, "SQLITE_READONLY: Attempt to write a readonly database"}, - {9, "SQLITE_INTERRUPT: Operation terminated by sqlite3_interrupt()"}, - {10, "SQLITE_IOERR: Some kind of disk I/O error occurred"}, - {11, "SQLITE_CORRUPT: The database disk image is malformed"}, - {12, "SQLITE_NOTFOUND: Unknown opcode in sqlite3_file_control()"}, - {13, "SQLITE_FULL: Insertion failed because database is full"}, - {14, "SQLITE_CANTOPEN: Unable to open the database file"}, - {15, "SQLITE_PROTOCOL: Database lock protocol error"}, - {16, "SQLITE_EMPTY: Database is empty"}, - {17, "SQLITE_SCHEMA: The database schema changed"}, - {18, "SQLITE_TOOBIG: String or BLOB exceeds size limit"}, - {19, "SQLITE_CONSTRAINT: Abort due to constraint violation"}, - {20, "SQLITE_MISMATCH: Data type mismatch"}, - {21, "SQLITE_MISUSE: Library used incorrectly"}, - {22, "SQLITE_NOLFS: Uses OS features not supported on host"}, - {23, "SQLITE_AUTH: Authorization denied"}, - {24, "SQLITE_FORMAT: Auxiliary database format error"}, - {25, "SQLITE_RANGE: 2nd parameter to sqlite3_bind out of range"}, - {26, "SQLITE_NOTADB: File opened that is not a database file"}, - {27, "SQLITE_NOTICE: Notifications from sqlite3_log()"}, - {28, "SQLITE_WARNING: Warnings from sqlite3_log()"}, - {100, "SQLITE_ROW: sqlite3_step() has another row ready"}, - {101, "SQLITE_DONE: sqlite3_step() has finished executing"}, -}; - -std::string getStringForSQLiteReturnCode(int code) { - if (kSQLiteReturnCodes.find(code) != kSQLiteReturnCodes.end()) { - return kSQLiteReturnCodes.at(code); - } else { - std::ostringstream s; - s << "Error: " << code << " is not a valid SQLite result code"; - return s.str(); - } -} - -Status SQLiteSQLPlugin::attach(const std::string& name) { - // This may be the managed DB, or a transient. - auto dbc = SQLiteDBManager::get(); - if (!dbc.isPrimary()) { - // Do not "reattach" to transient instance. - return Status(0, "OK"); - } - - PluginResponse response; - auto status = - Registry::call("table", name, {{"action", "columns"}}, response); - if (!status.ok()) { - return status; - } - - auto statement = columnDefinition(response); - return attachTableInternal(name, statement, dbc.db()); -} - -void SQLiteSQLPlugin::detach(const std::string& name) { - auto dbc = SQLiteDBManager::get(); - if (!dbc.isPrimary()) { - return; - } - detachTableInternal(name, dbc.db()); -} - -SQLiteDBInstance::SQLiteDBInstance() { - primary_ = false; - sqlite3_open(":memory:", &db_); - attachVirtualTables(db_); -} - -SQLiteDBInstance::SQLiteDBInstance(sqlite3*& db) { - primary_ = true; - db_ = db; -} - -SQLiteDBInstance::~SQLiteDBInstance() { - if (!primary_) { - sqlite3_close(db_); - } else { - SQLiteDBManager::unlock(); - db_ = nullptr; - } -} - -void SQLiteDBManager::unlock() { instance().lock_.unlock(); } - -bool SQLiteDBManager::isDisabled(const std::string& table_name) { - const auto& element = instance().disabled_tables_.find(table_name); - return (element != instance().disabled_tables_.end()); -} - -std::unordered_set SQLiteDBManager::parseDisableTablesFlag( - const std::string& list) { - const auto& tables = split(list, ","); - return std::unordered_set(tables.begin(), tables.end()); -} - -SQLiteDBInstance SQLiteDBManager::getUnique() { return SQLiteDBInstance(); } - -SQLiteDBInstance SQLiteDBManager::get() { - auto& self = instance(); - - if (!self.lock_.owns_lock() && self.lock_.try_lock()) { - if (self.db_ == nullptr) { - // Create primary SQLite DB instance. - sqlite3_open(":memory:", &self.db_); - attachVirtualTables(self.db_); - } - return SQLiteDBInstance(self.db_); - } else { - // If this thread or another has the lock, return a transient db. - VLOG(1) << "DBManager contention: opening transient SQLite database"; - return SQLiteDBInstance(); - } -} - -SQLiteDBManager::~SQLiteDBManager() { - if (db_ != nullptr) { - sqlite3_close(db_); - db_ = nullptr; - } -} - -int queryDataCallback(void* argument, int argc, char* argv[], char* column[]) { - if (argument == nullptr) { - VLOG(1) << "Query execution failed: received a bad callback argument"; - return SQLITE_MISUSE; - } - - QueryData* qData = (QueryData*)argument; - Row r; - for (int i = 0; i < argc; i++) { - if (column[i] != nullptr) { - r[column[i]] = (argv[i] != nullptr) ? argv[i] : ""; - } - } - (*qData).push_back(std::move(r)); - return 0; -} - -Status queryInternal(const std::string& q, QueryData& results, sqlite3* db) { - char* err = nullptr; - sqlite3_exec(db, q.c_str(), queryDataCallback, &results, &err); - sqlite3_db_release_memory(db); - if (err != nullptr) { - auto error_string = std::string(err); - sqlite3_free(err); - return Status(1, "Error running query: " + error_string); - } - - return Status(0, "OK"); -} - -Status getQueryColumnsInternal(const std::string& q, - TableColumns& columns, - sqlite3* db) { - int rc; - - // Will automatically handle calling sqlite3_finalize on the prepared stmt - // (Note that sqlite3_finalize is explicitly a nop for nullptr) - std::unique_ptr stmt_managed( - nullptr, sqlite3_finalize); - sqlite3_stmt* stmt = stmt_managed.get(); - - // Turn the query into a prepared statement - rc = sqlite3_prepare_v2(db, q.c_str(), q.length() + 1, &stmt, nullptr); - if (rc != SQLITE_OK) { - return Status(1, sqlite3_errmsg(db)); - } - - // Get column count - int num_columns = sqlite3_column_count(stmt); - TableColumns results; - results.reserve(num_columns); - - // Get column names and types - for (int i = 0; i < num_columns; ++i) { - const char* col_name = sqlite3_column_name(stmt, i); - const char* col_type = sqlite3_column_decltype(stmt, i); - if (col_name == nullptr) { - return Status(1, "Got nullptr for column name"); - } - if (col_type == nullptr) { - // Types are only returned for table columns (not expressions or - // subqueries). See docs for column_decltype - // (https://www.sqlite.org/c3ref/column_decltype.html). - col_type = "UNKNOWN"; - } - results.push_back({col_name, col_type}); - } - - columns = std::move(results); - - return Status(0, "OK"); -} -} diff --git a/osquery/sql/sqlite_util.h b/osquery/sql/sqlite_util.h deleted file mode 100644 index f903867..0000000 --- a/osquery/sql/sqlite_util.h +++ /dev/null @@ -1,197 +0,0 @@ -/* - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#pragma once - -#include -#include -#include - -#include - -#include -#include - -#include - -#define SQLITE_SOFT_HEAP_LIMIT (5 * 1024 * 1024) - -namespace osquery { - -/** - * @brief An RAII wrapper around an `sqlite3` object. - * - * The SQLiteDBInstance is also "smart" in that it may unlock access to a - * managed `sqlite3` resource. If there's no contention then only a single - * database is needed during the life of an osquery tool. - * - * If there is resource contention (multiple threads want access to the SQLite - * abstraction layer), then the SQLiteDBManager will provide a transient - * SQLiteDBInstance. - */ -class SQLiteDBInstance { - public: - SQLiteDBInstance(); - explicit SQLiteDBInstance(sqlite3*& db); - ~SQLiteDBInstance(); - - /// Check if the instance is the osquery primary. - bool isPrimary() { return primary_; } - - /** - * @brief Accessor to the internal `sqlite3` object, do not store references - * to the object within osquery code. - */ - sqlite3* db() { return db_; } - - private: - bool primary_; - sqlite3* db_; -}; - -/** - * @brief osquery internal SQLite DB abstraction resource management. - * - * The SQLiteDBManager should be the ONLY method for accessing SQLite resources. - * The manager provides an abstraction to manage internal SQLite memory and - * resources as well as provide optimization around resource access. - */ -class SQLiteDBManager : private boost::noncopyable { - public: - static SQLiteDBManager& instance() { - static SQLiteDBManager instance; - return instance; - } - - /** - * @brief Return a fully configured `sqlite3` database object wrapper. - * - * An osquery database is basically just a SQLite3 database with several - * virtual tables attached. This method is the main abstraction for accessing - * SQLite3 databases within osquery. - * - * A RAII wrapper around the `sqlite3` database will manage attaching tables - * and freeing resources when the instance (connection per-say) goes out of - * scope. Using the SQLiteDBManager will also try to optimize the number of - * `sqlite3` databases in use by managing a single global instance and - * returning resource-safe transient databases if there's access contention. - * - * Note: osquery::initOsquery must be called before calling `get` in order - * for virtual tables to be registered. - * - * @return a SQLiteDBInstance with all virtual tables attached. - */ - static SQLiteDBInstance get(); - - /// See `get` but always return a transient DB connection (for testing). - static SQLiteDBInstance getUnique(); - - /** - * @brief Check if `table_name` is disabled. - * - * Check if `table_name` is in the list of tables passed in to the - * `--disable_tables` flag. - * - * @param The name of the Table to check. - * @return If `table_name` is disabled. - */ - static bool isDisabled(const std::string& table_name); - - /// When the primary SQLiteDBInstance is destructed it will unlock. - static void unlock(); - - protected: - SQLiteDBManager() : db_(nullptr), lock_(mutex_, boost::defer_lock) { - sqlite3_soft_heap_limit64(SQLITE_SOFT_HEAP_LIMIT); - disabled_tables_ = parseDisableTablesFlag(Flag::getValue("disable_tables")); - } - SQLiteDBManager(SQLiteDBManager const&); - SQLiteDBManager& operator=(SQLiteDBManager const&); - virtual ~SQLiteDBManager(); - - private: - /// Primary (managed) sqlite3 database. - sqlite3* db_; - /// Mutex and lock around sqlite3 access. - boost::mutex mutex_; - /// Mutex and lock around sqlite3 access. - boost::unique_lock lock_; - /// Member variable to hold set of disabled tables. - std::unordered_set disabled_tables_; - /// Parse a comma-delimited set of tables names, passed in as a flag. - std::unordered_set parseDisableTablesFlag(const std::string& s); -}; - -/** - * @brief SQLite Internal: Execute a query on a specific database - * - * If you need to use a different database, other than the osquery default, - * use this method and pass along a pointer to a SQLite3 database. This is - * useful for testing. - * - * @param q the query to execute - * @param results The QueryData struct to emit row on query success. - * @param db the SQLite3 database to execute query q against - * - * @return A status indicating SQL query results. - */ -Status queryInternal(const std::string& q, QueryData& results, sqlite3* db); - -/** - * @brief SQLite Intern: Analyze a query, providing information about the - * result columns - * - * This function asks SQLite to determine what the names and types are of the - * result columns of the provided query. Only table columns (not expressions or - * subqueries) can have their types determined. Types that are not determined - * are indicated with the string "UNKNOWN". - * - * @param q the query to analyze - * @param columns the vector to fill with column information - * @param db the SQLite3 database to perform the analysis on - * - * @return status indicating success or failure of the operation - */ -Status getQueryColumnsInternal(const std::string& q, - TableColumns& columns, - sqlite3* db); - -/// The SQLiteSQLPlugin implements the "sql" registry for internal/core. -class SQLiteSQLPlugin : SQLPlugin { - public: - Status query(const std::string& q, QueryData& results) const { - auto dbc = SQLiteDBManager::get(); - return queryInternal(q, results, dbc.db()); - } - - Status getQueryColumns(const std::string& q, TableColumns& columns) const { - auto dbc = SQLiteDBManager::get(); - return getQueryColumnsInternal(q, columns, dbc.db()); - } - - /// Create a SQLite module and attach (CREATE). - Status attach(const std::string& name); - /// Detach a virtual table (DROP). - void detach(const std::string& name); -}; - -/** - * @brief Get a string representation of a SQLite return code - */ -std::string getStringForSQLiteReturnCode(int code); - -/** - * @brief Accumulate rows from an SQLite exec into a QueryData struct. - * - * The callback for populating a std::vector set of results. "argument" - * should be a non-const reference to a std::vector. - */ -int queryDataCallback(void* argument, int argc, char* argv[], char* column[]); -} diff --git a/osquery/sql/tests/sql_tests.cpp b/osquery/sql/tests/sql_tests.cpp deleted file mode 100644 index 6d994ca..0000000 --- a/osquery/sql/tests/sql_tests.cpp +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include - -#include -#include -#include -#include - -namespace osquery { - -class SQLTests : public testing::Test {}; - -TEST_F(SQLTests, test_raw_access) { - // Access to the table plugins (no SQL parsing required) works in both - // extensions and core, though with limitations on available tables. - auto results = SQL::selectAllFrom("time"); - EXPECT_EQ(results.size(), 1); -} - -class TestTablePlugin : public TablePlugin { - private: - TableColumns columns() const { - return {{"test_int", "INTEGER"}, {"test_text", "TEXT"}}; - } - - QueryData generate(QueryContext& ctx) { - QueryData results; - if (ctx.constraints["test_int"].existsAndMatches("1")) { - results.push_back({{"test_int", "1"}, {"test_text", "0"}}); - } else { - results.push_back({{"test_int", "0"}, {"test_text", "1"}}); - } - - auto ints = ctx.constraints["test_int"].getAll(EQUALS); - for (const auto& int_match : ints) { - results.push_back({{"test_int", INTEGER(int_match)}}); - } - - return results; - } -}; - -TEST_F(SQLTests, test_raw_access_context) { - Registry::add("table", "test"); - auto results = SQL::selectAllFrom("test"); - - EXPECT_EQ(results.size(), 1); - EXPECT_EQ(results[0]["test_text"], "1"); - - results = SQL::selectAllFrom("test", "test_int", EQUALS, "1"); - EXPECT_EQ(results.size(), 2); - - results = SQL::selectAllFrom("test", "test_int", EQUALS, "2"); - EXPECT_EQ(results.size(), 2); - EXPECT_EQ(results[0]["test_int"], "0"); -} -} diff --git a/osquery/sql/tests/sqlite_util_tests.cpp b/osquery/sql/tests/sqlite_util_tests.cpp deleted file mode 100644 index c2f6c5a..0000000 --- a/osquery/sql/tests/sqlite_util_tests.cpp +++ /dev/null @@ -1,143 +0,0 @@ -/* - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include - -#include - -#include -#include - -#include "osquery/core/test_util.h" -#include "osquery/sql/sqlite_util.h" - -namespace osquery { - -class SQLiteUtilTests : public testing::Test {}; - -SQLiteDBInstance getTestDBC() { - SQLiteDBInstance dbc = SQLiteDBManager::getUnique(); - char* err = nullptr; - std::vector queries = { - "CREATE TABLE test_table (username varchar(30) primary key, age int)", - "INSERT INTO test_table VALUES (\"mike\", 23)", - "INSERT INTO test_table VALUES (\"matt\", 24)"}; - - for (auto q : queries) { - sqlite3_exec(dbc.db(), q.c_str(), nullptr, nullptr, &err); - if (err != nullptr) { - throw std::domain_error(std::string("Cannot create testing DBC's db: ") + - err); - } - } - - return dbc; -} - -TEST_F(SQLiteUtilTests, test_simple_query_execution) { - // Access to the internal SQL implementation is only available in core. - auto sql = SQL("SELECT * FROM time"); - EXPECT_TRUE(sql.ok()); - EXPECT_EQ(sql.rows().size(), 1); -} - -TEST_F(SQLiteUtilTests, test_get_tables) { - // Access to the internal SQL implementation is only available in core. - auto tables = SQL::getTableNames(); - EXPECT_TRUE(tables.size() > 0); -} - -TEST_F(SQLiteUtilTests, test_sqlite_instance_manager) { - auto dbc1 = SQLiteDBManager::get(); - auto dbc2 = SQLiteDBManager::get(); - EXPECT_NE(dbc1.db(), dbc2.db()); - EXPECT_EQ(dbc1.db(), dbc1.db()); -} - -TEST_F(SQLiteUtilTests, test_sqlite_instance) { - // Don't do this at home kids. - // Keep a copy of the internal DB and let the SQLiteDBInstance go oos. - auto internal_db = SQLiteDBManager::get().db(); - // Compare the internal DB to another request with no SQLiteDBInstances - // in scope, meaning the primary will be returned. - EXPECT_EQ(internal_db, SQLiteDBManager::get().db()); -} - -TEST_F(SQLiteUtilTests, test_direct_query_execution) { - auto dbc = getTestDBC(); - QueryData results; - auto status = queryInternal(kTestQuery, results, dbc.db()); - EXPECT_TRUE(status.ok()); - EXPECT_EQ(results, getTestDBExpectedResults()); -} - -TEST_F(SQLiteUtilTests, test_passing_callback_no_data_param) { - char* err = nullptr; - auto dbc = getTestDBC(); - sqlite3_exec(dbc.db(), kTestQuery.c_str(), queryDataCallback, nullptr, &err); - EXPECT_TRUE(err != nullptr); - if (err != nullptr) { - sqlite3_free(err); - } -} - -TEST_F(SQLiteUtilTests, test_aggregate_query) { - auto dbc = getTestDBC(); - QueryData results; - auto status = queryInternal(kTestQuery, results, dbc.db()); - EXPECT_TRUE(status.ok()); - EXPECT_EQ(results, getTestDBExpectedResults()); -} - -TEST_F(SQLiteUtilTests, test_get_test_db_result_stream) { - auto dbc = getTestDBC(); - auto results = getTestDBResultStream(); - for (auto r : results) { - char* err_char = nullptr; - sqlite3_exec(dbc.db(), (r.first).c_str(), nullptr, nullptr, &err_char); - EXPECT_TRUE(err_char == nullptr); - if (err_char != nullptr) { - sqlite3_free(err_char); - ASSERT_TRUE(false); - } - - QueryData expected; - auto status = queryInternal(kTestQuery, expected, dbc.db()); - EXPECT_EQ(expected, r.second); - } -} - -TEST_F(SQLiteUtilTests, test_get_query_columns) { - auto dbc = getTestDBC(); - TableColumns results; - - std::string query = "SELECT seconds, version FROM time JOIN osquery_info"; - auto status = getQueryColumnsInternal(query, results, dbc.db()); - ASSERT_TRUE(status.ok()); - ASSERT_EQ(2, results.size()); - EXPECT_EQ(std::make_pair(std::string("seconds"), std::string("INTEGER")), - results[0]); - EXPECT_EQ(std::make_pair(std::string("version"), std::string("TEXT")), - results[1]); - - query = "SELECT hour + 1 AS hour1, minutes + 1 FROM time"; - status = getQueryColumnsInternal(query, results, dbc.db()); - ASSERT_TRUE(status.ok()); - ASSERT_EQ(2, results.size()); - EXPECT_EQ(std::make_pair(std::string("hour1"), std::string("UNKNOWN")), - results[0]); - EXPECT_EQ(std::make_pair(std::string("minutes + 1"), std::string("UNKNOWN")), - results[1]); - - query = "SELECT * FROM foo"; - status = getQueryColumnsInternal(query, results, dbc.db()); - ASSERT_FALSE(status.ok()); -} -} diff --git a/osquery/sql/tests/virtual_table_tests.cpp b/osquery/sql/tests/virtual_table_tests.cpp deleted file mode 100644 index c08cce6..0000000 --- a/osquery/sql/tests/virtual_table_tests.cpp +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include - -#include -#include -#include - -#include "osquery/sql/virtual_table.h" - -namespace osquery { - -class VirtualTableTests : public testing::Test {}; - -// sample plugin used on tests -class sampleTablePlugin : public TablePlugin { - private: - TableColumns columns() const { - return { - {"foo", "INTEGER"}, {"bar", "TEXT"}, - }; - } -}; - -TEST_F(VirtualTableTests, test_tableplugin_columndefinition) { - auto table = std::make_shared(); - EXPECT_EQ("(foo INTEGER, bar TEXT)", table->columnDefinition()); -} - -TEST_F(VirtualTableTests, test_sqlite3_attach_vtable) { - auto table = std::make_shared(); - table->setName("sample"); - - // Request a managed "connection". - // This will be a single (potentially locked) instance or a transient - // SQLite database if there is contention and a lock was not requested. - auto dbc = SQLiteDBManager::get(); - - // Virtual tables require the registry/plugin API to query tables. - auto status = attachTableInternal("failed_sample", "(foo INTEGER)", dbc.db()); - EXPECT_EQ(status.getCode(), SQLITE_ERROR); - - // The table attach will complete only when the table name is registered. - Registry::add("table", "sample"); - PluginResponse response; - status = Registry::call("table", "sample", {{"action", "columns"}}, response); - EXPECT_TRUE(status.ok()); - - // Use the table name, plugin-generated schema to attach. - status = attachTableInternal("sample", columnDefinition(response), dbc.db()); - EXPECT_EQ(status.getCode(), SQLITE_OK); - - std::string q = "SELECT sql FROM sqlite_temp_master WHERE tbl_name='sample';"; - QueryData results; - status = queryInternal(q, results, dbc.db()); - EXPECT_EQ("CREATE VIRTUAL TABLE sample USING sample(foo INTEGER, bar TEXT)", - results[0]["sql"]); -} - -TEST_F(VirtualTableTests, test_sqlite3_table_joins) { - // Get a database connection. - auto dbc = SQLiteDBManager::get(); - - QueryData results; - // Run a query with a join within. - std::string statement = - "SELECT p.pid FROM osquery_info oi, processes p WHERE oi.pid=p.pid"; - auto status = queryInternal(statement, results, dbc.db()); - EXPECT_TRUE(status.ok()); - EXPECT_EQ(results.size(), 1); -} - -TEST_F(VirtualTableTests, test_sqlite3_table_update_where) { - // Get a database connection. - auto dbc = SQLiteDBManager::get(); - - QueryData results; - std::string statement = "UPDATE users SET uid = 1234, gid = 232 WHERE uid = 0"; - auto status = queryInternal(statement, results, dbc.db()); - EXPECT_TRUE(status.ok()); -} - -TEST_F(VirtualTableTests, test_sqlite3_table_update) { - // Get a database connection. - auto dbc = SQLiteDBManager::get(); - - QueryData results; - std::string statement = "UPDATE users SET uid = 1234, gid = 232"; - auto status = queryInternal(statement, results, dbc.db()); - EXPECT_TRUE(status.ok()); -} -} diff --git a/osquery/sql/virtual_table.cpp b/osquery/sql/virtual_table.cpp deleted file mode 100644 index 1a51ad9..0000000 --- a/osquery/sql/virtual_table.cpp +++ /dev/null @@ -1,349 +0,0 @@ -/* - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include - -#include "osquery/sql/virtual_table.h" - -namespace osquery { -namespace tables { - -int xOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor) { - int rc = SQLITE_NOMEM; - BaseCursor *pCur; - - pCur = new BaseCursor; - - if (pCur) { - memset(pCur, 0, sizeof(BaseCursor)); - *ppCursor = (sqlite3_vtab_cursor *)pCur; - rc = SQLITE_OK; - } - - return rc; -} - -int xClose(sqlite3_vtab_cursor *cur) { - BaseCursor *pCur = (BaseCursor *)cur; - - delete pCur; - return SQLITE_OK; -} - -int xEof(sqlite3_vtab_cursor *cur) { - BaseCursor *pCur = (BaseCursor *)cur; - auto *pVtab = (VirtualTable *)cur->pVtab; - return pCur->row >= pVtab->content->n; -} - -int xDestroy(sqlite3_vtab *p) { - auto *pVtab = (VirtualTable *)p; - delete pVtab->content; - delete pVtab; - return SQLITE_OK; -} - -int xNext(sqlite3_vtab_cursor *cur) { - BaseCursor *pCur = (BaseCursor *)cur; - pCur->row++; - return SQLITE_OK; -} - -int xRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid) { - BaseCursor *pCur = (BaseCursor *)cur; - *pRowid = pCur->row; - return SQLITE_OK; -} - -int xUpdate(sqlite3_vtab *pVTab, - int argc, - sqlite3_value **argv, - sqlite3_int64 *pRowid) -{ - auto * pVtab = (VirtualTable *)pVTab; - if (argc <= 1 || argc - 2 != pVtab->content->columns.size()) { - LOG(ERROR) << "Invalid arguments: " << argc; - return SQLITE_ERROR; - } - - PluginRequest request = {{"action", "update"}}; - const auto& columns = pVtab->content->columns; - for (size_t i = 2; i < static_cast(argc); ++i) { - auto expr = (const char *)sqlite3_value_text(argv[i]); - if (expr == nullptr) { - // SQLite did not expose the expression value. - continue; - } else { - request.insert(std::make_pair(columns[i - 2].first, std::string(expr))); - } - } - - PluginResponse response; - Registry::call("table", pVtab->content->name, request, response); - - return SQLITE_OK; -} - -int xCreate(sqlite3 *db, - void *pAux, - int argc, - const char *const *argv, - sqlite3_vtab **ppVtab, - char **pzErr) { - auto *pVtab = new VirtualTable; - - if (!pVtab || argc == 0 || argv[0] == nullptr) { - return SQLITE_NOMEM; - } - - memset(pVtab, 0, sizeof(VirtualTable)); - pVtab->content = new VirtualTableContent; - - PluginResponse response; - pVtab->content->name = std::string(argv[0]); - - // Get the table column information. - auto status = Registry::call( - "table", pVtab->content->name, {{"action", "columns"}}, response); - if (!status.ok() || response.size() == 0) { - return SQLITE_ERROR; - } - - auto statement = - "CREATE TABLE " + pVtab->content->name + columnDefinition(response); - int rc = sqlite3_declare_vtab(db, statement.c_str()); - if (rc != SQLITE_OK) { - return rc; - } - - if (!status.ok() || response.size() == 0) { - return SQLITE_ERROR; - } - - for (const auto &column : response) { - pVtab->content->columns.push_back( - std::make_pair(column.at("name"), column.at("type"))); - } - - *ppVtab = (sqlite3_vtab *)pVtab; - return rc; -} - -int xColumn(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int col) { - BaseCursor *pCur = (BaseCursor *)cur; - auto *pVtab = (VirtualTable *)cur->pVtab; - - if (col >= pVtab->content->columns.size()) { - return SQLITE_ERROR; - } - - auto &column_name = pVtab->content->columns[col].first; - auto &type = pVtab->content->columns[col].second; - if (pCur->row >= pVtab->content->data[column_name].size()) { - return SQLITE_ERROR; - } - - // Attempt to cast each xFilter-populated row/column to the SQLite type. - auto &value = pVtab->content->data[column_name][pCur->row]; - if (type == "TEXT") { - sqlite3_result_text(ctx, value.c_str(), value.size(), SQLITE_STATIC); - } else if (type == "INTEGER") { - int afinite; - try { - afinite = boost::lexical_cast(value); - } catch (const boost::bad_lexical_cast &e) { - afinite = -1; - VLOG(1) << "Error casting " << column_name << " (" << value - << ") to INTEGER"; - } - sqlite3_result_int(ctx, afinite); - } else if (type == "BIGINT") { - long long int afinite; - try { - afinite = boost::lexical_cast(value); - } catch (const boost::bad_lexical_cast &e) { - afinite = -1; - VLOG(1) << "Error casting " << column_name << " (" << value - << ") to BIGINT"; - } - sqlite3_result_int64(ctx, afinite); - } else if (type == "DOUBLE") { - double afinite; - try { - afinite = boost::lexical_cast(value); - } catch (const boost::bad_lexical_cast &e) { - afinite = 0; - VLOG(1) << "Error casting" << column_name << " (" << value - << ") to DOUBLE"; - } - sqlite3_result_double(ctx, afinite); - } - - return SQLITE_OK; -} - -static int xBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo) { - auto *pVtab = (VirtualTable *)tab; - pVtab->content->constraints.clear(); - - int expr_index = 0; - int cost = 0; - for (size_t i = 0; i < pIdxInfo->nConstraint; ++i) { - if (!pIdxInfo->aConstraint[i].usable) { - // A higher cost less priority, prefer more usable query constraints. - cost += 10; - // TODO: OR is not usable. - continue; - } - - const auto &name = - pVtab->content->columns[pIdxInfo->aConstraint[i].iColumn].first; - pVtab->content->constraints.push_back( - std::make_pair(name, Constraint(pIdxInfo->aConstraint[i].op))); - pIdxInfo->aConstraintUsage[i].argvIndex = ++expr_index; - } - - pIdxInfo->estimatedCost = cost; - return SQLITE_OK; -} - -static int xFilter(sqlite3_vtab_cursor *pVtabCursor, - int idxNum, - const char *idxStr, - int argc, - sqlite3_value **argv) { - BaseCursor *pCur = (BaseCursor *)pVtabCursor; - auto *pVtab = (VirtualTable *)pVtabCursor->pVtab; - - pCur->row = 0; - pVtab->content->n = 0; - QueryContext context; - - for (size_t i = 0; i < pVtab->content->columns.size(); ++i) { - // Clear any data, this is the result container for each column + row. - pVtab->content->data[pVtab->content->columns[i].first].clear(); - // Set the column affinity for each optional constraint list. - // There is a separate list for each column name. - context.constraints[pVtab->content->columns[i].first].affinity = - pVtab->content->columns[i].second; - } - - // Iterate over every argument to xFilter, filling in constraint values. - for (size_t i = 0; i < argc; ++i) { - auto expr = (const char *)sqlite3_value_text(argv[i]); - if (expr == nullptr) { - // SQLite did not expose the expression value. - continue; - } - // Set the expression from SQLite's now-populated argv. - pVtab->content->constraints[i].second.expr = std::string(expr); - // Add the constraint to the column-sorted query request map. - context.constraints[pVtab->content->constraints[i].first].add( - pVtab->content->constraints[i].second); - } - - PluginRequest request; - PluginResponse response; - request["action"] = "generate"; - TablePlugin::setRequestFromContext(context, request); - Registry::call("table", pVtab->content->name, request, response); - - // Now organize the response rows by column instead of row. - auto &data = pVtab->content->data; - for (auto &row : response) { - for (const auto &column : pVtab->content->columns) { - if (row.count(column.first) == 0) { - VLOG(1) << "Table " << pVtab->content->name << " row " - << pVtab->content->n << " did not include column " - << column.first; - data[column.first].push_back(""); - continue; - } - - auto &value = row.at(column.first); - if (value.size() > FLAGS_value_max) { - data[column.first].push_back(value.substr(0, FLAGS_value_max)); - value.clear(); - } else { - data[column.first].push_back(std::move(value)); - } - } - - pVtab->content->n++; - } - - return SQLITE_OK; -} -} - -Status attachTableInternal(const std::string &name, - const std::string &statement, - sqlite3 *db) { - if (SQLiteDBManager::isDisabled(name)) { - VLOG(0) << "Table " << name << " is disabled, not attaching"; - return Status(0, getStringForSQLiteReturnCode(0)); - } - - // A static module structure does not need specific logic per-table. - // clang-format off - static sqlite3_module module = { - 0, - tables::xCreate, - tables::xCreate, - tables::xBestIndex, - tables::xDestroy, - tables::xDestroy, - tables::xOpen, - tables::xClose, - tables::xFilter, - tables::xNext, - tables::xEof, - tables::xColumn, - tables::xRowid, - tables::xUpdate, - }; - // clang-format on - - // Note, if the clientData API is used then this will save a registry call - // within xCreate. - int rc = sqlite3_create_module(db, name.c_str(), &module, 0); - if (rc == SQLITE_OK || rc == SQLITE_MISUSE) { - auto format = - "CREATE VIRTUAL TABLE temp." + name + " USING " + name + statement; - rc = sqlite3_exec(db, format.c_str(), nullptr, nullptr, 0); - } else { - LOG(ERROR) << "Error attaching table: " << name << " (" << rc << ")"; - } - return Status(rc, getStringForSQLiteReturnCode(rc)); -} - -Status detachTableInternal(const std::string &name, sqlite3 *db) { - auto format = "DROP TABLE IF EXISTS temp." + name; - int rc = sqlite3_exec(db, format.c_str(), nullptr, nullptr, 0); - if (rc != SQLITE_OK) { - LOG(ERROR) << "Error detaching table: " << name << " (" << rc << ")"; - } - - return Status(rc, getStringForSQLiteReturnCode(rc)); -} - -void attachVirtualTables(sqlite3 *db) { - PluginResponse response; - for (const auto &name : Registry::names("table")) { - // Column information is nice for virtual table create call. - auto status = - Registry::call("table", name, {{"action", "columns"}}, response); - if (status.ok()) { - auto statement = columnDefinition(response); - attachTableInternal(name, statement, db); - } - } -} -} diff --git a/osquery/sql/virtual_table.h b/osquery/sql/virtual_table.h deleted file mode 100644 index f962bc0..0000000 --- a/osquery/sql/virtual_table.h +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#pragma once - -#include - -#include "osquery/sql/sqlite_util.h" - -namespace osquery { - -/** - * @brief osquery cursor object. - * - * Only used in the SQLite virtual table module methods. - */ -struct BaseCursor { - /// SQLite virtual table cursor. - sqlite3_vtab_cursor base; - /// Current cursor position. - int row; -}; - -struct VirtualTableContent { - TableName name; - TableColumns columns; - TableData data; - ConstraintSet constraints; - size_t n; -}; - -/** - * @brief osquery virtual table object - * - * Only used in the SQLite virtual table module methods. - * This adds each table plugin class to the state tracking in SQLite. - */ -struct VirtualTable { - sqlite3_vtab base; - VirtualTableContent *content; -}; - -/// Attach a table plugin name to an in-memory SQLite database. -Status attachTableInternal(const std::string &name, - const std::string &statement, - sqlite3 *db); - -/// Detach (drop) a table. -Status detachTableInternal(const std::string &name, sqlite3 *db); - -/// Attach all table plugins to an in-memory SQLite database. -void attachVirtualTables(sqlite3 *db); -} diff --git a/osquery/tables/CMakeLists.txt b/osquery/tables/CMakeLists.txt deleted file mode 100644 index 8fb82c4..0000000 --- a/osquery/tables/CMakeLists.txt +++ /dev/null @@ -1,22 +0,0 @@ -# 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 - -FILE(GLOB OSQUERY_LINUX_TABLES "*/linux/*.cpp") -ADD_OSQUERY_LIBRARY(osquery_linux_tables ${OSQUERY_LINUX_TABLES}) - -FILE(GLOB OSQUERY_CROSS_TABLES "*/*.cpp") -ADD_OSQUERY_LIBRARY(osquery_tables ${OSQUERY_CROSS_TABLES}) - -FILE(GLOB OSQUERY_CROSS_TABLES_TESTS "[!uo]*/tests/*.cpp") -ADD_OSQUERY_TEST(${OSQUERY_CROSS_TABLES_TESTS}) diff --git a/osquery/tables/events/linux/file_events.cpp b/osquery/tables/events/linux/file_events.cpp deleted file mode 100644 index 91a014f..0000000 --- a/osquery/tables/events/linux/file_events.cpp +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include -#include - -#include -#include -#include -#include -#include - -#include "osquery/events/linux/inotify.h" - -namespace osquery { - -/** - * @brief Track time, action changes to /etc/passwd - * - * This is mostly an example EventSubscriber implementation. - */ -class FileEventSubscriber - : public EventSubscriber { - public: - Status init(); - - /** - * @brief This exports a single Callback for INotifyEventPublisher events. - * - * @param ec The EventCallback type receives an EventContextRef substruct - * for the INotifyEventPublisher declared in this EventSubscriber subclass. - * - * @return Was the callback successful. - */ - Status Callback(const INotifyEventContextRef& ec, const void* user_data); -}; - -/** - * @brief Each EventSubscriber must register itself so the init method is - *called. - * - * This registers FileEventSubscriber into the osquery EventSubscriber - * pseudo-plugin registry. - */ -REGISTER(FileEventSubscriber, "event_subscriber", "file_events"); - -Status FileEventSubscriber::init() { - ConfigDataInstance config; - for (const auto& element_kv : config.files()) { - for (const auto& file : element_kv.second) { - VLOG(1) << "Added listener to: " << file; - auto mc = createSubscriptionContext(); - // Use the filesystem globbing pattern to determine recursiveness. - mc->recursive = 0; - mc->path = file; - mc->mask = IN_ATTRIB | IN_MODIFY | IN_DELETE | IN_CREATE; - subscribe(&FileEventSubscriber::Callback, mc, - (void*)(&element_kv.first)); - } - } - - return Status(0, "OK"); -} - -Status FileEventSubscriber::Callback(const INotifyEventContextRef& ec, - const void* user_data) { - Row r; - r["action"] = ec->action; - r["target_path"] = ec->path; - if (user_data != nullptr) { - r["category"] = *(std::string*)user_data; - } else { - r["category"] = "Undefined"; - } - r["transaction_id"] = INTEGER(ec->event->cookie); - - if (ec->action == "CREATED" || ec->action == "UPDATED") { - r["md5"] = hashFromFile(HASH_TYPE_MD5, ec->path); - r["sha1"] = hashFromFile(HASH_TYPE_SHA1, ec->path); - r["sha256"] = hashFromFile(HASH_TYPE_SHA256, ec->path); - } - - if (ec->action != "" && ec->action != "OPENED") { - // A callback is somewhat useless unless it changes the EventSubscriber - // state or calls `add` to store a marked up event. - add(r, ec->time); - } - return Status(0, "OK"); -} -} diff --git a/osquery/tables/events/linux/hardware_events.cpp b/osquery/tables/events/linux/hardware_events.cpp deleted file mode 100644 index 813a095..0000000 --- a/osquery/tables/events/linux/hardware_events.cpp +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include -#include - -#include -#include -#include - -#include "osquery/events/linux/udev.h" - -namespace osquery { - -/** - * @brief Track udev events in Linux - */ -class HardwareEventSubscriber : public EventSubscriber { - public: - Status init(); - - Status Callback(const UdevEventContextRef& ec, const void* user_data); -}; - -REGISTER(HardwareEventSubscriber, "event_subscriber", "hardware_events"); - -Status HardwareEventSubscriber::init() { - auto subscription = createSubscriptionContext(); - subscription->action = UDEV_EVENT_ACTION_ALL; - - subscribe(&HardwareEventSubscriber::Callback, subscription, nullptr); - return Status(0, "OK"); -} - -Status HardwareEventSubscriber::Callback(const UdevEventContextRef& ec, - const void* user_data) { - Row r; - - if (ec->devtype.empty()) { - // Superfluous hardware event. - return Status(0, "Missing type."); - } else if (ec->devnode.empty() && ec->driver.empty()) { - return Status(0, "Missing node and driver."); - } - - struct udev_device *device = ec->device; - r["action"] = ec->action_string; - r["path"] = ec->devnode; - r["type"] = ec->devtype; - r["driver"] = ec->driver; - - // UDEV properties. - r["model"] = UdevEventPublisher::getValue(device, "ID_MODEL_FROM_DATABASE"); - if (r["path"].empty() && r["model"].empty()) { - // Don't emit mising path/model combos. - return Status(0, "Missing path and model."); - } - - r["model_id"] = INTEGER(UdevEventPublisher::getValue(device, "ID_MODEL_ID")); - r["vendor"] = UdevEventPublisher::getValue(device, "ID_VENDOR_FROM_DATABASE"); - r["vendor_id"] = - INTEGER(UdevEventPublisher::getValue(device, "ID_VENDOR_ID")); - r["serial"] = - INTEGER(UdevEventPublisher::getValue(device, "ID_SERIAL_SHORT")); - r["revision"] = INTEGER(UdevEventPublisher::getValue(device, "ID_REVISION")); - add(r, ec->time); - return Status(0, "OK"); -} -} diff --git a/osquery/tables/events/linux/passwd_changes.cpp b/osquery/tables/events/linux/passwd_changes.cpp deleted file mode 100644 index 6e36380..0000000 --- a/osquery/tables/events/linux/passwd_changes.cpp +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include -#include - -#include -#include -#include - -#include "osquery/events/linux/inotify.h" - -namespace osquery { - -/** - * @brief Track time, action changes to /etc/passwd - * - * This is mostly an example EventSubscriber implementation. - */ -class PasswdChangesEventSubscriber - : public EventSubscriber { - public: - Status init(); - - /** - * @brief This exports a single Callback for INotifyEventPublisher events. - * - * @param ec The EventCallback type receives an EventContextRef substruct - * for the INotifyEventPublisher declared in this EventSubscriber subclass. - * - * @return Was the callback successful. - */ - Status Callback(const INotifyEventContextRef& ec, const void* user_data); -}; - -/** - * @brief Each EventSubscriber must register itself so the init method is - *called. - * - * This registers PasswdChangesEventSubscriber into the osquery EventSubscriber - * pseudo-plugin registry. - */ -REGISTER(PasswdChangesEventSubscriber, "event_subscriber", "passwd_changes"); - -Status PasswdChangesEventSubscriber::init() { - auto mc = createSubscriptionContext(); - mc->path = "/etc/passwd"; - mc->mask = IN_ATTRIB | IN_MODIFY | IN_DELETE | IN_CREATE; - subscribe(&PasswdChangesEventSubscriber::Callback, mc, nullptr); - return Status(0, "OK"); -} - -Status PasswdChangesEventSubscriber::Callback(const INotifyEventContextRef& ec, - const void* user_data) { - Row r; - r["action"] = ec->action; - r["target_path"] = ec->path; - r["transaction_id"] = INTEGER(ec->event->cookie); - if (ec->action != "" && ec->action != "OPENED") { - // A callback is somewhat useless unless it changes the EventSubscriber - // state - // or calls `add` to store a marked up event. - add(r, ec->time); - } - return Status(0, "OK"); -} -} diff --git a/osquery/tables/networking/etc_hosts.cpp b/osquery/tables/networking/etc_hosts.cpp deleted file mode 100644 index 52d07b0..0000000 --- a/osquery/tables/networking/etc_hosts.cpp +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include -#include - -#include -#include - -#include -#include -#include -#include - -namespace osquery { -namespace tables { - -QueryData parseEtcHostsContent(const std::string& content) { - QueryData results; - - for (const auto& i : split(content, "\n")) { - auto line = split(i); - if (line.size() == 0 || boost::starts_with(line[0], "#")) { - continue; - } - Row r; - r["address"] = line[0]; - if (line.size() > 1) { - std::vector hostnames; - for (int i = 1; i < line.size(); ++i) { - if (boost::starts_with(line[i], "#")) { - break; - } - hostnames.push_back(line[i]); - } - r["hostnames"] = boost::algorithm::join(hostnames, " "); - } - results.push_back(r); - } - - return results; -} - -QueryData genEtcHosts(QueryContext& context) { - std::string content; - auto s = osquery::readFile("/etc/hosts", content); - if (s.ok()) { - return parseEtcHostsContent(content); - } else { - LOG(ERROR) << "Error reading /etc/hosts: " << s.toString(); - return {}; - } -} -} -} diff --git a/osquery/tables/networking/etc_protocols.cpp b/osquery/tables/networking/etc_protocols.cpp deleted file mode 100644 index 63c02e6..0000000 --- a/osquery/tables/networking/etc_protocols.cpp +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include -#include - -#include -#include - -#include -#include -#include -#include - -namespace osquery { -namespace tables { - -QueryData parseEtcProtocolsContent(const std::string& content) { - QueryData results; - - for (const auto& line : split(content, "\n")) { - // Empty line or comment. - if (line.size() == 0 || boost::starts_with(line, "#")) { - continue; - } - - // [0]: name protocol_number alias - // [1]: [comment part1] - // [2]: [comment part2] - // [n]: [comment partn] - auto protocol_comment = split(line, "#"); - - // [0]: name - // [1]: protocol_number - // [2]: alias - auto protocol_fields = split(protocol_comment[0]); - if (protocol_fields.size() < 2) { - continue; - } - - Row r; - r["name"] = TEXT(protocol_fields[0]); - r["number"] = INTEGER(protocol_fields[1]); - if (protocol_fields.size() > 2) { - r["alias"] = TEXT(protocol_fields[2]); - } - - // If there is a comment for the service. - if (protocol_comment.size() > 1) { - // Removes everything except the comment (parts of the comment). - protocol_comment.erase(protocol_comment.begin(), protocol_comment.begin() + 1); - r["comment"] = TEXT(boost::algorithm::join(protocol_comment, " # ")); - } - results.push_back(r); - } - return results; -} - -QueryData genEtcProtocols(QueryContext& context) { - std::string content; - auto s = osquery::readFile("/etc/protocols", content); - if (s.ok()) { - return parseEtcProtocolsContent(content); - } else { - TLOG << "Error reading /etc/protocols: " << s.toString(); - return {}; - } -} -} -} diff --git a/osquery/tables/networking/etc_services.cpp b/osquery/tables/networking/etc_services.cpp deleted file mode 100644 index 2873a5d..0000000 --- a/osquery/tables/networking/etc_services.cpp +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include -#include - -#include -#include - -#include -#include -#include -#include - -namespace osquery { -namespace tables { - -QueryData parseEtcServicesContent(const std::string& content) { - QueryData results; - - for (const auto& line : split(content, "\n")) { - // Empty line or comment. - if (line.size() == 0 || boost::starts_with(line, "#")) { - continue; - } - - // [0]: name port/protocol [aliases] - // [1]: [comment part1] - // [2]: [comment part2] - // [n]: [comment partn] - auto service_info_comment = split(line, "#"); - - // [0]: name - // [1]: port/protocol - // [2]: [aliases0] - // [3]: [aliases1] - // [n]: [aliasesn] - auto service_info = split(service_info_comment[0]); - if (service_info.size() < 2) { - LOG(WARNING) << "Line of /etc/services wasn't properly formatted. " - << "Expected at least 2, got " << service_info.size(); - continue; - } - - // [0]: port [1]: protocol - auto service_port_protocol = split(service_info[1], "/"); - if (service_port_protocol.size() != 2) { - LOG(WARNING) << "Line of /etc/services wasn't properly formatted. " - << "Expected 2, got " << service_port_protocol.size(); - continue; - } - - Row r; - r["name"] = TEXT(service_info[0]); - r["port"] = INTEGER(service_port_protocol[0]); - r["protocol"] = TEXT(service_port_protocol[1]); - - // Removes the name and the port/protcol elements. - service_info.erase(service_info.begin(), service_info.begin() + 2); - r["aliases"] = TEXT(boost::algorithm::join(service_info, " ")); - - // If there is a comment for the service. - if (service_info_comment.size() > 1) { - // Removes everything except the comment (parts of the comment). - service_info_comment.erase(service_info_comment.begin(), service_info_comment.begin() + 1); - r["comment"] = TEXT(boost::algorithm::join(service_info_comment, " # ")); - } - results.push_back(r); - } - return results; -} - -QueryData genEtcServices(QueryContext& context) { - std::string content; - auto s = osquery::readFile("/etc/services", content); - if (s.ok()) { - return parseEtcServicesContent(content); - } else { - LOG(ERROR) << "Error reading /etc/services: " << s.toString(); - return {}; - } -} -} -} diff --git a/osquery/tables/networking/linux/arp_cache.cpp b/osquery/tables/networking/linux/arp_cache.cpp deleted file mode 100644 index e5a3a60..0000000 --- a/osquery/tables/networking/linux/arp_cache.cpp +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include - -#include -#include - -#include -#include -#include - -namespace osquery { -namespace tables { - -const std::string kLinuxArpTable = "/proc/net/arp"; - -QueryData genArpCache(QueryContext& context) { - QueryData results; - - boost::filesystem::path arp_path = kLinuxArpTable; - if (!osquery::isReadable(arp_path).ok()) { - VLOG(1) << "Cannot read arp table."; - return results; - } - - std::ifstream fd(arp_path.string(), std::ios::in | std::ios::binary); - std::string line; - - if (fd.fail() || fd.eof()) { - VLOG(1) << "Empty or failed arp table."; - return results; - } - - // Read the header line. - std::getline(fd, line, '\n'); - while (!(fd.fail() || fd.eof())) { - std::getline(fd, line, '\n'); - - // IP address, HW type, Flags, HW address, Mask Device - std::vector fields; - boost::split(fields, line, boost::is_any_of(" "), boost::token_compress_on); - for (auto& f : fields) { - // Inline trim each split. - boost::trim(f); - } - - if (fields.size() != 6) { - // An unhandled error case. - continue; - } - - Row r; - r["address"] = fields[0]; - r["mac"] = fields[3]; - r["interface"] = fields[5]; - - // Note: it's also possible to detect publish entries (ATF_PUB). - if (fields[2] == "0x6") { - // The string representation of ATF_COM | ATF_PERM. - r["permanent"] = "1"; - } else { - r["permanent"] = "0"; - } - - results.push_back(r); - } - - return results; -} -} -} diff --git a/osquery/tables/networking/linux/inet_diag.h b/osquery/tables/networking/linux/inet_diag.h deleted file mode 100644 index 65b5d9e..0000000 --- a/osquery/tables/networking/linux/inet_diag.h +++ /dev/null @@ -1,145 +0,0 @@ -/* - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#ifndef _UAPI_INET_DIAG_H_ -#define _UAPI_INET_DIAG_H_ - -#include - -/* Just some random number */ -#define TCPDIAG_GETSOCK 18 -#define DCCPDIAG_GETSOCK 19 - -#define INET_DIAG_GETSOCK_MAX 24 - -/* Socket identity */ -struct inet_diag_sockid { - __be16 idiag_sport; - __be16 idiag_dport; - __be32 idiag_src[4]; - __be32 idiag_dst[4]; - __u32 idiag_if; - __u32 idiag_cookie[2]; -#define INET_DIAG_NOCOOKIE (~0U) -}; - -/* Request structure */ - -struct inet_diag_req { - __u8 idiag_family; /* Family of addresses. */ - __u8 idiag_src_len; - __u8 idiag_dst_len; - __u8 idiag_ext; /* Query extended information */ - - struct inet_diag_sockid id; - - __u32 idiag_states; /* States to dump */ - __u32 idiag_dbs; /* Tables to dump (NI) */ -}; - -struct inet_diag_req_v2 { - __u8 sdiag_family; - __u8 sdiag_protocol; - __u8 idiag_ext; - __u8 pad; - __u32 idiag_states; - struct inet_diag_sockid id; -}; - -enum { - INET_DIAG_REQ_NONE, - INET_DIAG_REQ_BYTECODE, -}; - -#define INET_DIAG_REQ_MAX INET_DIAG_REQ_BYTECODE - -/* Bytecode is sequence of 4 byte commands followed by variable arguments. - * All the commands identified by "code" are conditional jumps forward: - * to offset cc+"yes" or to offset cc+"no". "yes" is supposed to be - * length of the command and its arguments. - */ - -struct inet_diag_bc_op { - unsigned char code; - unsigned char yes; - unsigned short no; -}; - -enum { - INET_DIAG_BC_NOP, - INET_DIAG_BC_JMP, - INET_DIAG_BC_S_GE, - INET_DIAG_BC_S_LE, - INET_DIAG_BC_D_GE, - INET_DIAG_BC_D_LE, - INET_DIAG_BC_AUTO, - INET_DIAG_BC_S_COND, - INET_DIAG_BC_D_COND, -}; - -struct inet_diag_hostcond { - __u8 family; - __u8 prefix_len; - int port; - __be32 addr[0]; -}; - -/* Base info structure. It contains socket identity (addrs/ports/cookie) - * and, alas, the information shown by netstat. */ -struct inet_diag_msg { - __u8 idiag_family; - __u8 idiag_state; - __u8 idiag_timer; - __u8 idiag_retrans; - - struct inet_diag_sockid id; - - __u32 idiag_expires; - __u32 idiag_rqueue; - __u32 idiag_wqueue; - __u32 idiag_uid; - __u32 idiag_inode; -}; - -/* Extensions */ - -enum { - INET_DIAG_NONE, - INET_DIAG_MEMINFO, - INET_DIAG_INFO, - INET_DIAG_VEGASINFO, - INET_DIAG_CONG, - INET_DIAG_TOS, - INET_DIAG_TCLASS, - INET_DIAG_SKMEMINFO, - INET_DIAG_SHUTDOWN, -}; - -#define INET_DIAG_MAX INET_DIAG_SHUTDOWN - -/* INET_DIAG_MEM */ - -struct inet_diag_meminfo { - __u32 idiag_rmem; - __u32 idiag_wmem; - __u32 idiag_fmem; - __u32 idiag_tmem; -}; - -/* INET_DIAG_VEGASINFO */ - -struct tcpvegas_info { - __u32 tcpv_enabled; - __u32 tcpv_rttcnt; - __u32 tcpv_rtt; - __u32 tcpv_minrtt; -}; - -#endif /* _UAPI_INET_DIAG_H_ */ diff --git a/osquery/tables/networking/linux/ip_tables.h b/osquery/tables/networking/linux/ip_tables.h deleted file mode 100644 index da03258..0000000 --- a/osquery/tables/networking/linux/ip_tables.h +++ /dev/null @@ -1,227 +0,0 @@ -/* - * 25-Jul-1998 Major changes to allow for ip chain table - * - * 3-Jan-2000 Named tables to allow packet selection for different uses. - */ - -/* - * Format of an IP firewall descriptor - * - * src, dst, src_mask, dst_mask are always stored in network byte order. - * flags are stored in host byte order (of course). - * Port numbers are stored in HOST byte order. - */ - -#ifndef _IPTABLES_H -#define _IPTABLES_H - -#include - -#include - -#include - -#define IPT_FUNCTION_MAXNAMELEN XT_FUNCTION_MAXNAMELEN -#define IPT_TABLE_MAXNAMELEN XT_TABLE_MAXNAMELEN -#define ipt_match xt_match -#define ipt_target xt_target -#define ipt_table xt_table -#define ipt_get_revision xt_get_revision -#define ipt_entry_match xt_entry_match -#define ipt_entry_target xt_entry_target -#define ipt_standard_target xt_standard_target -#define ipt_error_target xt_error_target -#define ipt_counters xt_counters -#define IPT_CONTINUE XT_CONTINUE -#define IPT_RETURN XT_RETURN - -/* This group is older than old (iptables < v1.4.0-rc1~89) */ -#include -#define ipt_udp xt_udp -#define ipt_tcp xt_tcp -#define IPT_TCP_INV_SRCPT XT_TCP_INV_SRCPT -#define IPT_TCP_INV_DSTPT XT_TCP_INV_DSTPT -#define IPT_TCP_INV_FLAGS XT_TCP_INV_FLAGS -#define IPT_TCP_INV_OPTION XT_TCP_INV_OPTION -#define IPT_TCP_INV_MASK XT_TCP_INV_MASK -#define IPT_UDP_INV_SRCPT XT_UDP_INV_SRCPT -#define IPT_UDP_INV_DSTPT XT_UDP_INV_DSTPT -#define IPT_UDP_INV_MASK XT_UDP_INV_MASK - -/* The argument to IPT_SO_ADD_COUNTERS. */ -#define ipt_counters_info xt_counters_info -/* Standard return verdict, or do jump. */ -#define IPT_STANDARD_TARGET XT_STANDARD_TARGET -/* Error verdict. */ -#define IPT_ERROR_TARGET XT_ERROR_TARGET - -/* fn returns 0 to continue iteration */ -#define IPT_MATCH_ITERATE(e, fn, args...) \ - XT_MATCH_ITERATE(struct ipt_entry, e, fn, ## args) - -/* fn returns 0 to continue iteration */ -#define IPT_ENTRY_ITERATE(entries, size, fn, args...) \ - XT_ENTRY_ITERATE(struct ipt_entry, entries, size, fn, ## args) - -/* Yes, Virginia, you have to zero the padding. */ -struct ipt_ip { - /* Source and destination IP addr */ - struct in_addr src, dst; - /* Mask for src and dest IP addr */ - struct in_addr smsk, dmsk; - char iniface[IFNAMSIZ], outiface[IFNAMSIZ]; - unsigned char iniface_mask[IFNAMSIZ], outiface_mask[IFNAMSIZ]; - - /* Protocol, 0 = ANY */ - __u16 proto; - - /* Flags word */ - __u8 flags; - /* Inverse flags */ - __u8 invflags; -}; - -/* Values for "flag" field in struct ipt_ip (general ip structure). */ -#define IPT_F_FRAG 0x01 /* Set if rule is a fragment rule */ -#define IPT_F_GOTO 0x02 /* Set if jump is a goto */ -#define IPT_F_MASK 0x03 /* All possible flag bits mask. */ - -/* Values for "inv" field in struct ipt_ip. */ -#define IPT_INV_VIA_IN 0x01 /* Invert the sense of IN IFACE. */ -#define IPT_INV_VIA_OUT 0x02 /* Invert the sense of OUT IFACE */ -#define IPT_INV_TOS 0x04 /* Invert the sense of TOS. */ -#define IPT_INV_SRCIP 0x08 /* Invert the sense of SRC IP. */ -#define IPT_INV_DSTIP 0x10 /* Invert the sense of DST OP. */ -#define IPT_INV_FRAG 0x20 /* Invert the sense of FRAG. */ -#define IPT_INV_PROTO XT_INV_PROTO -#define IPT_INV_MASK 0x7F /* All possible flag bits mask. */ - -/* This structure defines each of the firewall rules. Consists of 3 - parts which are 1) general IP header stuff 2) match specific - stuff 3) the target to perform if the rule matches */ -struct ipt_entry { - struct ipt_ip ip; - - /* Mark with fields that we care about. */ - unsigned int nfcache; - - /* Size of ipt_entry + matches */ - __u16 target_offset; - /* Size of ipt_entry + matches + target */ - __u16 next_offset; - - /* Back pointer */ - unsigned int comefrom; - - /* Packet and byte counters. */ - struct xt_counters counters; - - /* The matches (if any), then the target. */ - unsigned char elems[0]; -}; - -/* - * New IP firewall options for [gs]etsockopt at the RAW IP level. - * Unlike BSD Linux inherits IP options so you don't have to use a raw - * socket for this. Instead we check rights in the calls. - * - * ATTENTION: check linux/in.h before adding new number here. - */ -#define IPT_BASE_CTL 64 - -#define IPT_SO_SET_REPLACE (IPT_BASE_CTL) -#define IPT_SO_SET_ADD_COUNTERS (IPT_BASE_CTL + 1) -#define IPT_SO_SET_MAX IPT_SO_SET_ADD_COUNTERS - -#define IPT_SO_GET_INFO (IPT_BASE_CTL) -#define IPT_SO_GET_ENTRIES (IPT_BASE_CTL + 1) -#define IPT_SO_GET_REVISION_MATCH (IPT_BASE_CTL + 2) -#define IPT_SO_GET_REVISION_TARGET (IPT_BASE_CTL + 3) -#define IPT_SO_GET_MAX IPT_SO_GET_REVISION_TARGET - -/* ICMP matching stuff */ -struct ipt_icmp { - __u8 type; /* type to match */ - __u8 code[2]; /* range of code */ - __u8 invflags; /* Inverse flags */ -}; - -/* Values for "inv" field for struct ipt_icmp. */ -#define IPT_ICMP_INV 0x01 /* Invert the sense of type/code test */ - -/* The argument to IPT_SO_GET_INFO */ -struct ipt_getinfo { - /* Which table: caller fills this in. */ - char name[XT_TABLE_MAXNAMELEN]; - - /* Kernel fills these in. */ - /* Which hook entry points are valid: bitmask */ - unsigned int valid_hooks; - - /* Hook entry points: one per netfilter hook. */ - unsigned int hook_entry[NF_INET_NUMHOOKS]; - - /* Underflow points. */ - unsigned int underflow[NF_INET_NUMHOOKS]; - - /* Number of entries */ - unsigned int num_entries; - - /* Size of entries. */ - unsigned int size; -}; - -/* The argument to IPT_SO_SET_REPLACE. */ -struct ipt_replace { - /* Which table. */ - char name[XT_TABLE_MAXNAMELEN]; - - /* Which hook entry points are valid: bitmask. You can't - change this. */ - unsigned int valid_hooks; - - /* Number of entries */ - unsigned int num_entries; - - /* Total size of new entries */ - unsigned int size; - - /* Hook entry points. */ - unsigned int hook_entry[NF_INET_NUMHOOKS]; - - /* Underflow points. */ - unsigned int underflow[NF_INET_NUMHOOKS]; - - /* Information about old entries: */ - /* Number of counters (must be equal to current number of entries). */ - unsigned int num_counters; - /* The old entries' counters. */ - struct xt_counters *counters; - - /* The entries (hang off end: not really an array). */ - struct ipt_entry entries[0]; -}; - -/* The argument to IPT_SO_GET_ENTRIES. */ -struct ipt_get_entries { - /* Which table: user fills this in. */ - char name[XT_TABLE_MAXNAMELEN]; - - /* User fills this in: total entry size. */ - unsigned int size; - - /* The entries. */ - struct ipt_entry entrytable[0]; -}; - -/* Helper functions */ -static __inline__ struct xt_entry_target * -ipt_get_target(struct ipt_entry *e) -{ - return (struct ipt_entry_target *)((char *)e + e->target_offset); -} - -/* - * Main firewall chains definitions and global var's definitions. - */ -#endif /* _IPTABLES_H */ diff --git a/osquery/tables/networking/linux/iptables.cpp b/osquery/tables/networking/linux/iptables.cpp deleted file mode 100644 index 7e6f88d..0000000 --- a/osquery/tables/networking/linux/iptables.cpp +++ /dev/null @@ -1,151 +0,0 @@ -/* - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include - -#include -#include "libiptc.h" - -#include -#include - -#include -#include -#include - -#include "osquery/tables/networking/utils.h" - -namespace osquery { -namespace tables { - -const std::string kLinuxIpTablesNames = "/proc/net/ip_tables_names"; -const char MAP[] = {'0','1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; -const int HIGH_BITS = 4; -const int LOW_BITS = 15; - -void parseIpEntry(ipt_ip *ip, Row &r) { - r["protocol"] = INTEGER(ip->proto); - if (strlen(ip->iniface)) { - r["iniface"] = TEXT(ip->iniface); - } else { - r["iniface"] = "all"; - } - - if (strlen(ip->outiface)) { - r["outiface"] = TEXT(ip->outiface); - } else { - r["outiface"] = "all"; - } - - r["src_ip"] = ipAsString((struct in_addr *)&ip->src); - r["dst_ip"] = ipAsString((struct in_addr *)&ip->dst); - r["src_mask"] = ipAsString((struct in_addr *)&ip->smsk); - r["dst_mask"] = ipAsString((struct in_addr *)&ip->dmsk); - - char aux_char[2] = {0}; - std::string iniface_mask; - for (int i = 0; ip->iniface_mask[i] != 0x00 && iiniface_mask[i] >> HIGH_BITS]; - aux_char[1] = MAP[(int) ip->iniface_mask[i] & LOW_BITS]; - iniface_mask += aux_char[0]; - iniface_mask += aux_char[1]; - } - - r["iniface_mask"] = TEXT(iniface_mask); - std::string outiface_mask = ""; - for (int i = 0; ip->outiface_mask[i] != 0x00 && ioutiface_mask[i] >> HIGH_BITS]; - aux_char[1] = MAP[(int) ip->outiface_mask[i] & LOW_BITS]; - outiface_mask += aux_char[0]; - outiface_mask += aux_char[1]; - } - r["outiface_mask"] = TEXT(outiface_mask); -} - -void genIPTablesRules(const std::string &filter, QueryData &results) { - Row r; - r["filter_name"] = filter; - - // Initialize the access to iptc - auto handle = (struct iptc_handle *)iptc_init(filter.c_str()); - if (handle == nullptr) { - return; - } - - // Iterate through chains - for (auto chain = iptc_first_chain(handle); chain != nullptr; - chain = iptc_next_chain(handle)) { - r["chain"] = TEXT(chain); - - struct ipt_counters counters; - auto policy = iptc_get_policy(chain, &counters, handle); - - if (policy != nullptr) { - r["policy"] = TEXT(policy); - r["packets"] = INTEGER(counters.pcnt); - r["bytes"] = INTEGER(counters.bcnt); - } else { - r["policy"] = ""; - r["packets"] = "0"; - r["bytes"] = "0"; - } - - struct ipt_entry *prev_rule = nullptr; - // Iterating through all the rules per chain - for (auto chain_rule = iptc_first_rule(chain, handle); chain_rule; - chain_rule = iptc_next_rule(prev_rule, handle)) { - prev_rule = (struct ipt_entry *)chain_rule; - - auto target = iptc_get_target(chain_rule, handle); - if (target != nullptr) { - r["target"] = TEXT(target); - } else { - r["target"] = ""; - } - - if (chain_rule->target_offset) { - r["match"] = "yes"; - } else { - r["match"] = "no"; - } - - struct ipt_ip *ip = (struct ipt_ip *)&chain_rule->ip; - parseIpEntry(ip, r); - - results.push_back(r); - } // Rule iteration - results.push_back(r); - } // Chain iteration - - iptc_free(handle); -} - -QueryData genIptables(QueryContext& context) { - QueryData results; - - // Read in table names - std::string content; - auto s = osquery::readFile(kLinuxIpTablesNames, content); - if (s.ok()) { - for (auto &line : split(content, "\n")) { - boost::trim(line); - if (line.size() > 0) { - genIPTablesRules(line, results); - } - } - } else { - // Permissions issue or iptables modules are not loaded. - TLOG << "Error reading " << kLinuxIpTablesNames << " : " << s.toString(); - } - - return results; -} -} -} diff --git a/osquery/tables/networking/linux/libiptc.h b/osquery/tables/networking/linux/libiptc.h deleted file mode 100644 index fd561bd..0000000 --- a/osquery/tables/networking/linux/libiptc.h +++ /dev/null @@ -1,172 +0,0 @@ -#ifndef _LIBIPTC_H -#define _LIBIPTC_H -/* Library which manipulates filtering rules. */ - -#include -#include -#ifdef __cplusplus -# include -#else -# include /* INT_MAX in ip_tables.h */ -#endif -#include "ip_tables.h" -#include - -#ifdef __cplusplus -extern "C" { -#endif - -#define iptc_handle xtc_handle -#define ipt_chainlabel xt_chainlabel - -#define IPTC_LABEL_ACCEPT "ACCEPT" -#define IPTC_LABEL_DROP "DROP" -#define IPTC_LABEL_QUEUE "QUEUE" -#define IPTC_LABEL_RETURN "RETURN" - -/* Does this chain exist? */ -int iptc_is_chain(const char *chain, struct xtc_handle *const handle); - -/* Take a snapshot of the rules. Returns NULL on error. */ -struct xtc_handle *iptc_init(const char *tablename); - -/* Cleanup after iptc_init(). */ -void iptc_free(struct xtc_handle *h); - -/* Iterator functions to run through the chains. Returns NULL at end. */ -const char *iptc_first_chain(struct xtc_handle *handle); -const char *iptc_next_chain(struct xtc_handle *handle); - -/* Get first rule in the given chain: NULL for empty chain. */ -const struct ipt_entry *iptc_first_rule(const char *chain, - struct xtc_handle *handle); - -/* Returns NULL when rules run out. */ -const struct ipt_entry *iptc_next_rule(const struct ipt_entry *prev, - struct xtc_handle *handle); - -/* Returns a pointer to the target name of this entry. */ -const char *iptc_get_target(const struct ipt_entry *e, - struct xtc_handle *handle); - -/* Is this a built-in chain? */ -int iptc_builtin(const char *chain, struct xtc_handle *const handle); - -/* Get the policy of a given built-in chain */ -const char *iptc_get_policy(const char *chain, - struct xt_counters *counter, - struct xtc_handle *handle); - -/* These functions return TRUE for OK or 0 and set errno. If errno == - 0, it means there was a version error (ie. upgrade libiptc). */ -/* Rule numbers start at 1 for the first rule. */ - -/* Insert the entry `e' in chain `chain' into position `rulenum'. */ -int iptc_insert_entry(const xt_chainlabel chain, - const struct ipt_entry *e, - unsigned int rulenum, - struct xtc_handle *handle); - -/* Atomically replace rule `rulenum' in `chain' with `e'. */ -int iptc_replace_entry(const xt_chainlabel chain, - const struct ipt_entry *e, - unsigned int rulenum, - struct xtc_handle *handle); - -/* Append entry `e' to chain `chain'. Equivalent to insert with - rulenum = length of chain. */ -int iptc_append_entry(const xt_chainlabel chain, - const struct ipt_entry *e, - struct xtc_handle *handle); - -/* Check whether a mathching rule exists */ -int iptc_check_entry(const xt_chainlabel chain, - const struct ipt_entry *origfw, - unsigned char *matchmask, - struct xtc_handle *handle); - -/* Delete the first rule in `chain' which matches `e', subject to - matchmask (array of length == origfw) */ -int iptc_delete_entry(const xt_chainlabel chain, - const struct ipt_entry *origfw, - unsigned char *matchmask, - struct xtc_handle *handle); - -/* Delete the rule in position `rulenum' in `chain'. */ -int iptc_delete_num_entry(const xt_chainlabel chain, - unsigned int rulenum, - struct xtc_handle *handle); - -/* Check the packet `e' on chain `chain'. Returns the verdict, or - NULL and sets errno. */ -const char *iptc_check_packet(const xt_chainlabel chain, - struct ipt_entry *entry, - struct xtc_handle *handle); - -/* Flushes the entries in the given chain (ie. empties chain). */ -int iptc_flush_entries(const xt_chainlabel chain, - struct xtc_handle *handle); - -/* Zeroes the counters in a chain. */ -int iptc_zero_entries(const xt_chainlabel chain, - struct xtc_handle *handle); - -/* Creates a new chain. */ -int iptc_create_chain(const xt_chainlabel chain, - struct xtc_handle *handle); - -/* Deletes a chain. */ -int iptc_delete_chain(const xt_chainlabel chain, - struct xtc_handle *handle); - -/* Renames a chain. */ -int iptc_rename_chain(const xt_chainlabel oldname, - const xt_chainlabel newname, - struct xtc_handle *handle); - -/* Sets the policy on a built-in chain. */ -int iptc_set_policy(const xt_chainlabel chain, - const xt_chainlabel policy, - struct xt_counters *counters, - struct xtc_handle *handle); - -/* Get the number of references to this chain */ -int iptc_get_references(unsigned int *ref, - const xt_chainlabel chain, - struct xtc_handle *handle); - -/* read packet and byte counters for a specific rule */ -struct xt_counters *iptc_read_counter(const xt_chainlabel chain, - unsigned int rulenum, - struct xtc_handle *handle); - -/* zero packet and byte counters for a specific rule */ -int iptc_zero_counter(const xt_chainlabel chain, - unsigned int rulenum, - struct xtc_handle *handle); - -/* set packet and byte counters for a specific rule */ -int iptc_set_counter(const xt_chainlabel chain, - unsigned int rulenum, - struct xt_counters *counters, - struct xtc_handle *handle); - -/* Makes the actual changes. */ -int iptc_commit(struct xtc_handle *handle); - -/* Get raw socket. */ -int iptc_get_raw_socket(void); - -/* Translates errno numbers into more human-readable form than strerror. */ -const char *iptc_strerror(int err); - -extern void dump_entries(struct xtc_handle *const); - -extern const struct xtc_ops iptc_ops; - -#ifdef __cplusplus -} -#endif - - -#endif /* _LIBIPTC_H */ diff --git a/osquery/tables/networking/linux/process_open_sockets.cpp b/osquery/tables/networking/linux/process_open_sockets.cpp deleted file mode 100644 index e2cac9c..0000000 --- a/osquery/tables/networking/linux/process_open_sockets.cpp +++ /dev/null @@ -1,182 +0,0 @@ -/* - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include - -#include - -#include -#include -#include -#include - -namespace osquery { -namespace tables { - -// Linux proc protocol define to net stats file name. -const std::map kLinuxProtocolNames = { - {IPPROTO_ICMP, "icmp"}, - {IPPROTO_TCP, "tcp"}, - {IPPROTO_UDP, "udp"}, - {IPPROTO_UDPLITE, "udplite"}, - {IPPROTO_RAW, "raw"}, -}; - -std::string addressFromHex(const std::string &encoded_address, int family) { - char addr_buffer[INET6_ADDRSTRLEN] = {0}; - if (family == AF_INET) { - struct in_addr decoded; - if (encoded_address.length() == 8) { - sscanf(encoded_address.c_str(), "%X", &(decoded.s_addr)); - inet_ntop(AF_INET, &decoded, addr_buffer, INET_ADDRSTRLEN); - } - } else if (family == AF_INET6) { - struct in6_addr decoded; - if (encoded_address.length() == 32) { - sscanf(encoded_address.c_str(), - "%8x%8x%8x%8x", - (unsigned int *)&(decoded.s6_addr[0]), - (unsigned int *)&(decoded.s6_addr[4]), - (unsigned int *)&(decoded.s6_addr[8]), - (unsigned int *)&(decoded.s6_addr[12])); - inet_ntop(AF_INET6, &decoded, addr_buffer, INET6_ADDRSTRLEN); - } - } - - return TEXT(addr_buffer); -} - -unsigned short portFromHex(const std::string &encoded_port) { - unsigned short decoded = 0; - if (encoded_port.length() == 4) { - sscanf(encoded_port.c_str(), "%hX", &decoded); - } - return decoded; -} - -void genSocketsFromProc(const std::map &inodes, - int protocol, - int family, - QueryData &results) { - std::string path = "/proc/net/"; - if (family == AF_UNIX) { - path += "unix"; - } else { - path += kLinuxProtocolNames.at(protocol); - path += (family == AF_INET6) ? "6" : ""; - } - - std::string content; - if (!osquery::readFile(path, content).ok()) { - // Could not open socket information from /proc. - return; - } - - // The system's socket information is tokenized by line. - size_t index = 0; - for (const auto &line : osquery::split(content, "\n")) { - if (++index == 1) { - // The first line is a textual header and will be ignored. - if (line.find("sl") != 0 && line.find("sk") != 0 && - line.find("Num") != 0) { - // Header fields are unknown, stop parsing. - break; - } - continue; - } - - // The socket information is tokenized by spaces, each a field. - auto fields = osquery::split(line, " "); - // UNIX socket reporting has a smaller number of fields. - size_t min_fields = (family == AF_UNIX) ? 7 : 10; - if (fields.size() < min_fields) { - // Unknown/malformed socket information. - continue; - } - - - Row r; - if (family == AF_UNIX) { - r["socket"] = fields[6]; - r["family"] = "0"; - r["protocol"] = fields[2]; - r["local_address"] = ""; - r["local_port"] = "0"; - r["remote_address"] = ""; - r["remote_port"] = "0"; - r["path"] = (fields.size() >= 8) ? fields[7] : ""; - } else { - // Two of the fields are the local/remote address/port pairs. - auto locals = osquery::split(fields[1], ":"); - auto remotes = osquery::split(fields[2], ":"); - if (locals.size() != 2 || remotes.size() != 2) { - // Unknown/malformed socket information. - continue; - } - - r["socket"] = fields[9]; - r["family"] = INTEGER(family); - r["protocol"] = INTEGER(protocol); - r["local_address"] = addressFromHex(locals[0], family); - r["local_port"] = INTEGER(portFromHex(locals[1])); - r["remote_address"] = addressFromHex(remotes[0], family); - r["remote_port"] = INTEGER(portFromHex(remotes[1])); - // Path is only used for UNIX domain sockets. - r["path"] = ""; - } - - if (inodes.count(r["socket"]) > 0) { - r["pid"] = inodes.at(r["socket"]); - } else { - r["pid"] = "-1"; - } - - results.push_back(r); - } -} - -QueryData genOpenSockets(QueryContext &context) { - QueryData results; - - // If a pid is given then set that as the only item in processes. - std::set pids; - if (context.constraints["pid"].exists(EQUALS)) { - pids = context.constraints["pid"].getAll(EQUALS); - } else { - osquery::procProcesses(pids); - } - - // Generate a map of socket inode to process tid. - std::map socket_inodes; - for (const auto &process : pids) { - std::map descriptors; - if (osquery::procDescriptors(process, descriptors).ok()) { - for (const auto& fd : descriptors) { - if (fd.second.find("socket:[") == 0) { - // See #792: std::regex is incomplete until GCC 4.9 (skip 8 chars) - auto inode = fd.second.substr(8); - socket_inodes[inode.substr(0, inode.size() - 1)] = process; - } - } - } - } - - // This used to use netlink (Ref: #1094) to request socket information. - // Use proc messages to query socket information. - for (const auto &protocol : kLinuxProtocolNames) { - genSocketsFromProc(socket_inodes, protocol.first, AF_INET, results); - genSocketsFromProc(socket_inodes, protocol.first, AF_INET6, results); - } - - genSocketsFromProc(socket_inodes, IPPROTO_IP, AF_UNIX, results); - return results; -} -} -} diff --git a/osquery/tables/networking/linux/routes.cpp b/osquery/tables/networking/linux/routes.cpp deleted file mode 100644 index d32ad82..0000000 --- a/osquery/tables/networking/linux/routes.cpp +++ /dev/null @@ -1,206 +0,0 @@ -/* - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include -#include -#include -#include - -#include - -#include -#include -#include - -#include "osquery/tables/networking/utils.h" - -namespace osquery { -namespace tables { - -#define MAX_NETLINK_SIZE 8192 -#define MAX_NETLINK_LATENCY 2000 - -std::string getNetlinkIP(int family, const char* buffer) { - char dst[INET6_ADDRSTRLEN]; - memset(dst, 0, INET6_ADDRSTRLEN); - - inet_ntop(family, buffer, dst, INET6_ADDRSTRLEN); - std::string address(dst); - boost::trim(address); - - return address; -} - -Status readNetlink(int socket_fd, int seq, char* output, size_t* size) { - struct nlmsghdr* nl_hdr = nullptr; - - size_t message_size = 0; - do { - int latency = 0; - int bytes = 0; - while (bytes == 0) { - bytes = recv(socket_fd, output, MAX_NETLINK_SIZE - message_size, 0); - if (bytes < 0) { - return Status(1, "Could not read from NETLINK"); - } else if (latency >= MAX_NETLINK_LATENCY) { - return Status(1, "Netlink timeout"); - } else if (bytes == 0) { - ::usleep(20); - latency += 20; - } - } - - // Assure valid header response, and not an error type. - nl_hdr = (struct nlmsghdr*)output; - if (NLMSG_OK(nl_hdr, bytes) == 0 || nl_hdr->nlmsg_type == NLMSG_ERROR) { - return Status(1, "Read invalid NETLINK message"); - } - - if (nl_hdr->nlmsg_type == NLMSG_DONE) { - break; - } - - // Move the buffer pointer - output += bytes; - message_size += bytes; - if ((nl_hdr->nlmsg_flags & NLM_F_MULTI) == 0) { - break; - } - } while (nl_hdr->nlmsg_seq != seq || nl_hdr->nlmsg_pid != getpid()); - - *size = message_size; - return Status(0, "OK"); -} - -void genNetlinkRoutes(const struct nlmsghdr* netlink_msg, QueryData& results) { - std::string address; - int mask = 0; - char interface[IF_NAMESIZE]; - - struct rtmsg* message = (struct rtmsg*)NLMSG_DATA(netlink_msg); - struct rtattr* attr = (struct rtattr*)RTM_RTA(message); - int attr_size = RTM_PAYLOAD(netlink_msg); - - Row r; - - // Iterate over each route in the netlink message - bool has_destination = false; - r["metric"] = "0"; - while (RTA_OK(attr, attr_size)) { - switch (attr->rta_type) { - case RTA_OIF: - if_indextoname(*(int*)RTA_DATA(attr), interface); - r["interface"] = std::string(interface); - break; - case RTA_GATEWAY: - address = getNetlinkIP(message->rtm_family, (char*)RTA_DATA(attr)); - r["gateway"] = address; - break; - case RTA_PREFSRC: - address = getNetlinkIP(message->rtm_family, (char*)RTA_DATA(attr)); - r["source"] = address; - break; - case RTA_DST: - if (message->rtm_dst_len != 32 && message->rtm_dst_len != 128) { - mask = (int)message->rtm_dst_len; - } - address = getNetlinkIP(message->rtm_family, (char*)RTA_DATA(attr)); - r["destination"] = address; - has_destination = true; - break; - case RTA_PRIORITY: - r["metric"] = INTEGER(*(int*)RTA_DATA(attr)); - break; - } - attr = RTA_NEXT(attr, attr_size); - } - - if (!has_destination) { - r["destination"] = "0.0.0.0"; - if (message->rtm_dst_len) { - mask = (int)message->rtm_dst_len; - } - } - - // Route type determination - if (message->rtm_type == RTN_UNICAST) { - r["type"] = "gateway"; - } else if (message->rtm_type == RTN_LOCAL) { - r["type"] = "local"; - } else if (message->rtm_type == RTN_BROADCAST) { - r["type"] = "broadcast"; - } else if (message->rtm_type == RTN_ANYCAST) { - r["type"] = "anycast"; - } else { - r["type"] = "other"; - } - - r["flags"] = INTEGER(message->rtm_flags); - - // This is the cidr-formatted mask - r["netmask"] = INTEGER(mask); - - // Fields not supported by Linux routes: - r["mtu"] = "0"; - results.push_back(r); -} - -QueryData genRoutes(QueryContext& context) { - QueryData results; - - int socket_fd = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE); - if (socket_fd < 0) { - VLOG(1) << "Cannot open NETLINK socket"; - return {}; - } - - // Create netlink message header - auto netlink_buffer = (void*)malloc(MAX_NETLINK_SIZE); - if (netlink_buffer == nullptr) { - close(socket_fd); - return {}; - } - - auto netlink_msg = (struct nlmsghdr*)netlink_buffer; - netlink_msg->nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); - netlink_msg->nlmsg_type = RTM_GETROUTE; // routes from kernel routing table - netlink_msg->nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST; - netlink_msg->nlmsg_seq = 0; - netlink_msg->nlmsg_pid = getpid(); - - // Send the netlink request to the kernel - if (send(socket_fd, netlink_msg, netlink_msg->nlmsg_len, 0) < 0) { - TLOG << "Cannot write NETLINK request header to socket"; - close(socket_fd); - free(netlink_buffer); - return {}; - } - - // Wrap the read socket to support multi-netlink messages - size_t size = 0; - if (!readNetlink(socket_fd, 1, (char*)netlink_msg, &size).ok()) { - TLOG << "Cannot read NETLINK response from socket"; - close(socket_fd); - free(netlink_buffer); - return {}; - } - - // Treat the netlink response as route information - while (NLMSG_OK(netlink_msg, size)) { - genNetlinkRoutes(netlink_msg, results); - netlink_msg = NLMSG_NEXT(netlink_msg, size); - } - - close(socket_fd); - free(netlink_buffer); - return results; -} -} -} diff --git a/osquery/tables/networking/linux/tests/iptables_tests.cpp b/osquery/tables/networking/linux/tests/iptables_tests.cpp deleted file mode 100644 index 2761ead..0000000 --- a/osquery/tables/networking/linux/tests/iptables_tests.cpp +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include - -#include - -#include -#include - -#include "osquery/core/test_util.h" - -namespace osquery { -namespace tables { - -void parseIpEntry(ipt_ip *ip, Row &row); - -ipt_ip* getIpEntryContent() { - static ipt_ip ip_entry; - - ip_entry.proto = 6; - memset(ip_entry.iniface, 0, IFNAMSIZ); - strcpy(ip_entry.outiface, "eth0"); - inet_aton("123.123.123.123", &ip_entry.src); - inet_aton("45.45.45.45", &ip_entry.dst); - inet_aton("250.251.252.253", &ip_entry.smsk); - inet_aton("253.252.251.250", &ip_entry.dmsk); - memset(ip_entry.iniface_mask, 0xfe, IFNAMSIZ ); - memset(ip_entry.outiface_mask, 0xfa, IFNAMSIZ ); - - return &ip_entry; -} - -Row getIpEntryExpectedResults() { - Row row; - - row["protocol"] = "6"; - row["iniface"] = "all"; - row["outiface"] = "eth0"; - row["src_ip"] = "123.123.123.123"; - row["dst_ip"] = "45.45.45.45"; - row["src_mask"] = "250.251.252.253"; - row["dst_mask"] = "253.252.251.250"; - row["iniface_mask"] = "FEFEFEFEFEFEFEFEFEFEFEFEFEFEFEFE"; - row["outiface_mask"] = "FAFAFAFAFAFAFAFAFAFAFAFAFAFAFAFA"; - - return row; -} - -class IptablesTests : public testing::Test {}; - -TEST_F(IptablesTests, test_iptables_ip_entry) { - Row row; - parseIpEntry(getIpEntryContent(), row); - EXPECT_EQ(row, getIpEntryExpectedResults()); -} -} -} diff --git a/osquery/tables/networking/listening_ports.cpp b/osquery/tables/networking/listening_ports.cpp deleted file mode 100644 index 951163a..0000000 --- a/osquery/tables/networking/listening_ports.cpp +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include -#include - -namespace osquery { -namespace tables { - -typedef std::pair ProtoFamilyPair; -typedef std::map > PortMap; - -QueryData genListeningPorts(QueryContext& context) { - QueryData results; - - auto sockets = SQL::selectAllFrom("process_open_sockets"); - - PortMap ports; - for (const auto& socket : sockets) { - if (socket.at("remote_port") != "0") { - // Listening UDP/TCP ports have a remote_port == "0" - continue; - } - - if (ports.count(socket.at("local_port")) > 0) { - bool duplicate = false; - for (const auto& entry : ports[socket.at("local_port")]) { - if (entry.first == socket.at("protocol") && - entry.second == socket.at("family")) { - duplicate = true; - break; - } - } - - if (duplicate) { - // There is a duplicate socket descriptor for this bind. - continue; - } - } - - // Add this family/protocol/port bind to the tracked map. - ports[socket.at("local_port")].push_back( - std::make_pair(socket.at("protocol"), socket.at("family"))); - - Row r; - r["pid"] = socket.at("pid"); - r["port"] = socket.at("local_port"); - r["protocol"] = socket.at("protocol"); - r["family"] = socket.at("family"); - r["address"] = socket.at("local_address"); - - results.push_back(r); - } - - return results; -} -} -} diff --git a/osquery/tables/networking/tests/etc_hosts_tests.cpp b/osquery/tables/networking/tests/etc_hosts_tests.cpp deleted file mode 100644 index bad5904..0000000 --- a/osquery/tables/networking/tests/etc_hosts_tests.cpp +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include - -#include - -#include "osquery/core/test_util.h" - -namespace osquery { -namespace tables { - -osquery::QueryData parseEtcHostsContent(const std::string& content); - -class EtcHostsTests : public testing::Test {}; - -TEST_F(EtcHostsTests, test_parse_etc_hosts_content) { - EXPECT_EQ(parseEtcHostsContent(getEtcHostsContent()), - getEtcHostsExpectedResults()); -} -} -} diff --git a/osquery/tables/networking/tests/etc_protocols_tests.cpp b/osquery/tables/networking/tests/etc_protocols_tests.cpp deleted file mode 100644 index e6786be..0000000 --- a/osquery/tables/networking/tests/etc_protocols_tests.cpp +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include - -#include - -#include "osquery/core/test_util.h" - -namespace osquery { -namespace tables { - -osquery::QueryData parseEtcProtocolsContent(const std::string& content); - -class EtcProtocolsTests : public testing::Test {}; - -TEST_F(EtcProtocolsTests, test_parse_etc_protocols_content) { - EXPECT_EQ(parseEtcProtocolsContent(getEtcProtocolsContent()), - getEtcProtocolsExpectedResults()); -} -} -} diff --git a/osquery/tables/networking/utils.cpp b/osquery/tables/networking/utils.cpp deleted file mode 100644 index 6ece3f1..0000000 --- a/osquery/tables/networking/utils.cpp +++ /dev/null @@ -1,133 +0,0 @@ -/* - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include -#include - -#if defined(__linux__) -#include -#include -#include -#include -#define AF_LINK AF_PACKET -#else -#include -#endif - -#include - -#include "osquery/tables/networking/utils.h" - -namespace osquery { -namespace tables { - -std::string ipAsString(const struct sockaddr *in) { - char dst[INET6_ADDRSTRLEN] = {0}; - void *in_addr = nullptr; - - if (in->sa_family == AF_INET) { - in_addr = (void *)&(((struct sockaddr_in *)in)->sin_addr); - } else if (in->sa_family == AF_INET6) { - in_addr = (void *)&(((struct sockaddr_in6 *)in)->sin6_addr); - } else { - return ""; - } - - inet_ntop(in->sa_family, in_addr, dst, sizeof(dst)); - std::string address(dst); - boost::trim(address); - return address; -} - -std::string ipAsString(const struct in_addr *in) { - char dst[INET6_ADDRSTRLEN] = {0}; - - inet_ntop(AF_INET, in, dst, sizeof(dst)); - std::string address(dst); - boost::trim(address); - return address; -} - -inline short addBits(unsigned char byte) { - short bits = 0; - for (int i = 7; i >= 0; --i) { - if ((byte & (1 << i)) == 0) { - break; - } - bits++; - } - return bits; -} - -int netmaskFromIP(const struct sockaddr *in) { - int mask = 0; - - if (in->sa_family == AF_INET6) { - auto in6 = (struct sockaddr_in6 *)in; - for (size_t i = 0; i < 16; i++) { - mask += addBits(in6->sin6_addr.s6_addr[i]); - } - } else { - auto in4 = (struct sockaddr_in *)in; - auto address = reinterpret_cast(&in4->sin_addr.s_addr); - for (size_t i = 0; i < 4; i++) { - mask += addBits(address[i]); - } - } - - return mask; -} - -inline std::string macAsString(const char *addr) { - std::stringstream mac; - - for (size_t i = 0; i < 6; i++) { - mac << std::hex << std::setfill('0') << std::setw(2); - mac << (int)((uint8_t)addr[i]); - if (i != 5) { - mac << ":"; - } - } - - return mac.str(); -} - -std::string macAsString(const struct ifaddrs *addr) { - static std::string blank_mac = "00:00:00:00:00:00"; - if (addr->ifa_addr == nullptr) { - // No link or MAC exists. - return blank_mac; - } - -#if defined(__linux__) - struct ifreq ifr; - ifr.ifr_addr.sa_family = AF_INET; - memcpy(ifr.ifr_name, addr->ifa_name, IFNAMSIZ); - - int socket_fd = socket(AF_INET, SOCK_DGRAM, 0); - if (socket_fd < 0) { - return blank_mac; - } - ioctl(socket_fd, SIOCGIFHWADDR, &ifr); - close(socket_fd); - - return macAsString(ifr.ifr_hwaddr.sa_data); -#else - auto sdl = (struct sockaddr_dl *)addr->ifa_addr; - if (sdl->sdl_alen != 6) { - // Do not support MAC address that are not 6 bytes... - return blank_mac; - } - - return macAsString(&sdl->sdl_data[sdl->sdl_nlen]); -#endif -} -} -} diff --git a/osquery/tables/networking/utils.h b/osquery/tables/networking/utils.h deleted file mode 100644 index 1243ded..0000000 --- a/osquery/tables/networking/utils.h +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#pragma once - -#include - -#include -#include - -namespace osquery { -namespace tables { - -// Return a string representation for an IPv4/IPv6 struct. -std::string ipAsString(const struct sockaddr *in); -std::string ipAsString(const struct in_addr *in); -std::string macAsString(const struct ifaddrs *addr); -std::string macAsString(const char *addr); -int netmaskFromIP(const struct sockaddr *in); -} -} diff --git a/osquery/tables/system/crontab.cpp b/osquery/tables/system/crontab.cpp deleted file mode 100644 index fb54584..0000000 --- a/osquery/tables/system/crontab.cpp +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include - -#include - -#include -#include -#include -#include - -namespace osquery { -namespace tables { - -const std::string kSystemCron = "/etc/crontab"; - -const std::vector kUserCronPaths = { - "/var/at/tabs/", "/var/spool/cron/", "/var/spool/cron/crontabs/", -}; - -std::vector cronFromFile(const std::string& path) { - std::string content; - std::vector cron_lines; - if (!isReadable(path).ok()) { - return cron_lines; - } - - if (!readFile(path, content).ok()) { - return cron_lines; - } - - auto lines = split(content, "\n"); - - // Only populate the lines that are not comments or blank. - for (auto& line : lines) { - // Cheat and use a non-const iteration, to inline trim. - boost::trim(line); - if (line.size() > 0 && line.at(0) != '#') { - cron_lines.push_back(line); - } - } - - return cron_lines; -} - -void genCronLine(const std::string& path, - const std::string& line, - QueryData& results) { - Row r; - - r["path"] = path; - auto columns = split(line, " \t"); - - size_t index = 0; - auto iterator = columns.begin(); - for (; iterator != columns.end(); ++iterator) { - if (index == 0) { - if ((*iterator).at(0) == '@') { - // If the first value is an 'at' then skip to the command. - r["event"] = *iterator; - index = 5; - continue; - } - r["minute"] = *iterator; - } else if (index == 1) { - r["hour"] = *iterator; - } else if (index == 2) { - r["day_of_month"] = *iterator; - } else if (index == 3) { - r["month"] = *iterator; - } else if (index == 4) { - r["day_of_week"] = *iterator; - } else if (index == 5) { - r["command"] = *iterator; - } else { - // Long if switch to handle command breaks from space delim. - r["command"] += " " + *iterator; - } - index++; - } - - if (r["command"].size() == 0) { - // The line was not well-formed, perhaps it was a variable? - return; - } - - results.push_back(r); -} - -QueryData genCronTab(QueryContext& context) { - QueryData results; - - auto system_lines = cronFromFile(kSystemCron); - for (const auto& line : system_lines) { - genCronLine(kSystemCron, line, results); - } - - std::vector user_crons; - for (const auto cron_path : kUserCronPaths) { - osquery::listFilesInDirectory(cron_path, user_crons); - } - - // The user-based crons are identified by their path. - for (const auto& user_path : user_crons) { - auto user_lines = cronFromFile(user_path); - for (const auto& line : user_lines) { - genCronLine(user_path, line, results); - } - } - - return results; -} -} -} diff --git a/osquery/tables/system/last.cpp b/osquery/tables/system/last.cpp deleted file mode 100644 index b780b12..0000000 --- a/osquery/tables/system/last.cpp +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include -#include - -#include - -#include -#include - -namespace osquery { -namespace tables { - -QueryData genLastAccess(QueryContext& context) { - QueryData results; - struct utmpx *ut; -#ifdef __APPLE__ - setutxent_wtmp(0); // 0 = reverse chronological order - - while ((ut = getutxent_wtmp()) != nullptr) { -#else - - utmpxname("/var/log/wtmpx"); - setutxent(); - - while ((ut = getutxent()) != nullptr) { -#endif - - Row r; - r["username"] = std::string(ut->ut_user); - r["tty"] = std::string(ut->ut_line); - r["pid"] = boost::lexical_cast(ut->ut_pid); - r["type"] = boost::lexical_cast(ut->ut_type); - r["time"] = boost::lexical_cast(ut->ut_tv.tv_sec); - r["host"] = std::string(ut->ut_host); - - results.push_back(r); - } - -#ifdef __APPLE__ - endutxent_wtmp(); -#else - endutxent(); -#endif - - return results; -} -} -} diff --git a/osquery/tables/system/linux/acpi_tables.cpp b/osquery/tables/system/linux/acpi_tables.cpp deleted file mode 100644 index 662926d..0000000 --- a/osquery/tables/system/linux/acpi_tables.cpp +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include - -#include -#include -#include -#include - -namespace fs = boost::filesystem; - -namespace osquery { -namespace tables { - -const std::string kLinuxACPIPath = "/sys/firmware/acpi/tables"; - -void genACPITable(const std::string& table, QueryData& results) { - fs::path table_path = table; - - // There may be "categories" of tables in the form of directories. - Status status; - if (!fs::is_regular_file(table_path)) { - std::vector child_tables; - status = osquery::listFilesInDirectory(table_path, child_tables); - if (status.ok()) { - for (const auto& child_table : child_tables) { - genACPITable(child_table, results); - } - } - - return; - } - - Row r; - r["name"] = table_path.filename().string(); - - std::string table_content; - status = osquery::readFile(table_path, table_content); - if (!status.ok()) { - r["size"] = INTEGER(-1); - } else { - r["size"] = INTEGER(table_content.size()); - r["md5"] = osquery::hashFromBuffer( - HASH_TYPE_MD5, table_content.c_str(), table_content.length()); - } - - results.push_back(r); -} - -QueryData genACPITables(QueryContext& context) { - QueryData results; - - // In Linux, hopefully the ACPI tables are parsed and exposed as nodes. - std::vector tables; - auto status = osquery::listFilesInDirectory(kLinuxACPIPath, tables); - if (!status.ok()) { - // We could not read the tables path or the nodes are not exposed. - return {}; - } - - for (const auto& table : tables) { - genACPITable(table, results); - } - - return results; -} -} -} diff --git a/osquery/tables/system/linux/groups.cpp b/osquery/tables/system/linux/groups.cpp deleted file mode 100644 index b537db9..0000000 --- a/osquery/tables/system/linux/groups.cpp +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include -#include - -#include - -#include -#include - -namespace osquery { -namespace tables { - -std::mutex grpEnumerationMutex; - -QueryData genGroups(QueryContext &context) { - std::lock_guard lock(grpEnumerationMutex); - QueryData results; - struct group *grp = nullptr; - std::set groups_in; - - setgrent(); - while ((grp = getgrent()) != nullptr) { - if (std::find(groups_in.begin(), groups_in.end(), grp->gr_gid) == - groups_in.end()) { - Row r; - r["gid"] = INTEGER(grp->gr_gid); - r["gid_signed"] = INTEGER((int32_t) grp->gr_gid); - r["groupname"] = TEXT(grp->gr_name); - results.push_back(r); - groups_in.insert(grp->gr_gid); - } - } - endgrent(); - groups_in.clear(); - - return results; -} -} -} diff --git a/osquery/tables/system/linux/kernel_info.cpp b/osquery/tables/system/linux/kernel_info.cpp deleted file mode 100644 index d3cc2ff..0000000 --- a/osquery/tables/system/linux/kernel_info.cpp +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include - -#include -#include -#include -#include -#include - -namespace osquery { -namespace tables { - -const std::string kKernelArgumentsPath = "/proc/cmdline"; -const std::string kKernelSignaturePath = "/proc/version"; - -QueryData genKernelInfo(QueryContext& context) { - QueryData results; - Row r; - - if (pathExists(kKernelArgumentsPath).ok()) { - std::string arguments_line; - // Grab the whole arguments string from proc. - if (readFile(kKernelArgumentsPath, arguments_line).ok()) { - auto arguments = split(arguments_line, " "); - std::string additional_arguments; - - // Iterate over each space-tokenized argument. - for (const auto& argument : arguments) { - if (argument.substr(0, 11) == "BOOT_IMAGE=") { - r["path"] = argument.substr(11); - } else if (argument.substr(0, 5) == "root=") { - r["device"] = argument.substr(5); - } else { - if (additional_arguments.size() > 0) { - additional_arguments += " "; - } - additional_arguments += argument; - } - } - r["arguments"] = additional_arguments; - } - } else { - VLOG(1) << "Cannot find kernel arguments file: " << kKernelArgumentsPath; - } - - if (pathExists(kKernelSignaturePath).ok()) { - std::string signature; - - // The signature includes the kernel version, build data, buildhost, - // GCC version used, and possibly build date. - if (readFile(kKernelSignaturePath, signature).ok()) { - auto details = split(signature, " "); - if (details.size() > 2 && details[1] == "version") { - r["version"] = details[2]; - } - } - } else { - VLOG(1) << "Cannot find kernel signature file: " << kKernelSignaturePath; - } - - // Using the path of the boot image, attempt to calculate a hash. - if (r.count("path") > 0) { - r["md5"] = hashFromFile(HASH_TYPE_MD5, r.at("path")); - } - - results.push_back(r); - return results; -} -} -} diff --git a/osquery/tables/system/linux/kernel_modules.cpp b/osquery/tables/system/linux/kernel_modules.cpp deleted file mode 100644 index f3fadad..0000000 --- a/osquery/tables/system/linux/kernel_modules.cpp +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include - -#include -#include - -#include -#include -#include -#include - -namespace osquery { -namespace tables { - -const std::string kKernelModulePath = "/proc/modules"; - -QueryData genKernelModules(QueryContext& context) { - QueryData results; - - if (!pathExists(kKernelModulePath).ok()) { - VLOG(1) << "Cannot find kernel modules proc file: " << kKernelModulePath; - return {}; - } - - // Cannot seek to the end of procfs. - std::ifstream fd(kKernelModulePath, std::ios::in); - if (!fd) { - VLOG(1) << "Cannot read kernel modules from: " << kKernelModulePath; - return {}; - } - - auto module_info = std::string(std::istreambuf_iterator(fd), - std::istreambuf_iterator()); - - for (const auto& module : split(module_info, "\n")) { - Row r; - auto module_info = split(module, " "); - if (module_info.size() < 6) { - // Interesting error case, this module line is not well formed. - continue; - } - - for (auto& detail : module_info) { - // Clean up the delimiters - boost::trim(detail); - if (detail.back() == ',') { - detail.pop_back(); - } - } - - r["name"] = module_info[0]; - r["size"] = module_info[1]; - r["used_by"] = module_info[3]; - r["status"] = module_info[4]; - r["address"] = module_info[5]; - results.push_back(r); - } - - return results; -} -} -} diff --git a/osquery/tables/system/linux/memory_map.cpp b/osquery/tables/system/linux/memory_map.cpp deleted file mode 100644 index 77dfe58..0000000 --- a/osquery/tables/system/linux/memory_map.cpp +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include - -#include -#include -#include -#include - -namespace fs = boost::filesystem; - -namespace osquery { -namespace tables { - -const std::string kMemoryMapLocation = "/sys/firmware/memmap"; - -QueryData genMemoryMap(QueryContext& context) { - QueryData results; - - // Linux memory map is exposed in /sys. - std::vector regions; - auto status = listDirectoriesInDirectory(kMemoryMapLocation, regions); - if (!status.ok()) { - return {}; - } - - for (const auto& index : regions) { - fs::path index_path(index); - Row r; - r["region"] = index_path.filename().string(); - - // The type is a textual description - std::string content; - readFile(index_path / "type", content); - boost::trim(content); - r["type"] = content; - - // Keep these in 0xFFFF (hex) form. - readFile(index_path / "start", content); - boost::trim(content); - r["start"] = content; - - readFile(index_path / "end", content); - boost::trim(content); - r["end"] = content; - - results.push_back(r); - } - - return results; -} -} -} diff --git a/osquery/tables/system/linux/model_specific_register.cpp b/osquery/tables/system/linux/model_specific_register.cpp deleted file mode 100644 index 126d615..0000000 --- a/osquery/tables/system/linux/model_specific_register.cpp +++ /dev/null @@ -1,159 +0,0 @@ -/* - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#define MSR_FILENAME_BUFFER_SIZE 32 - -#define NO_MASK 0xFFFFFFFFFFFFFFFFULL - -// Defines taken from uapi/asm/msr-index.h from the linux kernel. -#define MSR_PLATFORM_INFO 0x000000ce - -#define MSR_IA32_FEATURE_CONTROL 0x0000003a - -#define MSR_IA32_PERF_STATUS 0x00000198 -#define MSR_IA32_PERF_CTL 0x00000199 -#define INTEL_PERF_CTL_MASK 0xffff - -#define MSR_IA32_MISC_ENABLE 0x000001a0 - -#define MSR_TURBO_RATIO_LIMIT 0x000001ad - -#define MSR_IA32_MISC_ENABLE_TURBO_DISABLE_BIT 38 -#define MSR_IA32_MISC_ENABLE_TURBO_DISABLE \ - (1ULL << MSR_IA32_MISC_ENABLE_TURBO_DISABLE_BIT) - -// Run Time Average Power Limiting (RAPL). -#define MSR_RAPL_POWER_UNIT 0x00000606 -#define MSR_PKG_ENERGY_STATUS 0x00000611 -#define MSR_PKG_POWER_LIMIT 0x00000610 - -namespace osquery { -namespace tables { - -// These are the entries to retrieve from the model specific register -struct msr_record_t { - const char *name; - const off_t offset; - const uint64_t mask; - const int is_flag; -}; -const static msr_record_t fields[] = { - {.name = "turbo_disabled", - .offset = MSR_IA32_MISC_ENABLE, - .mask = MSR_IA32_MISC_ENABLE_TURBO_DISABLE, - .is_flag = true}, - {.name = "turbo_ratio_limit", - .offset = MSR_TURBO_RATIO_LIMIT, - .mask = NO_MASK, - .is_flag = false}, - {.name = "platform_info", - .offset = MSR_PLATFORM_INFO, - .mask = NO_MASK, - .is_flag = false}, - {.name = "perf_status", - .offset = MSR_IA32_PERF_STATUS, - .mask = NO_MASK, - .is_flag = false}, - {.name = "perf_ctl", - .offset = MSR_IA32_PERF_CTL, - .mask = INTEL_PERF_CTL_MASK, - .is_flag = false}, - {.name = "feature_control", - .offset = MSR_IA32_FEATURE_CONTROL, - .mask = NO_MASK, - .is_flag = false}, - {.name = "rapl_power_limit", - .offset = MSR_PKG_POWER_LIMIT, - .mask = NO_MASK, - .is_flag = false}, - {.name = "rapl_energy_status", - .offset = MSR_PKG_ENERGY_STATUS, - .mask = NO_MASK, - .is_flag = false}, - {.name = "rapl_power_units", - .offset = MSR_RAPL_POWER_UNIT, - .mask = NO_MASK, - .is_flag = false}}; - -void getModelSpecificRegisterData(QueryData &results, int cpu_number) { - auto msr_filename = - std::string("/dev/cpu/") + std::to_string(cpu_number) + "/msr"; - - int fd = open(msr_filename.c_str(), O_RDONLY); - if (fd < 0) { - int err = errno; - TLOG << "Could not open msr file " << msr_filename - << " check the msr kernel module is enabled."; - if (err == EACCES) { - LOG(WARNING) << "Could not access msr device. Run osquery as root."; - } - return; - } - - Row r; - r["processor_number"] = BIGINT(cpu_number); - for (const msr_record_t &field : fields) { - uint64_t output; - ssize_t size = pread(fd, &output, sizeof(uint64_t), field.offset); - if (size != sizeof(uint64_t)) { - // Processor does not have a record of this type. - continue; - } - if (field.is_flag) { - r[field.name] = BIGINT((output & field.mask) ? 1 : 0); - } else { - r[field.name] = BIGINT(output & field.mask); - } - } - results.push_back(r); - close(fd); - - return; -} - -// Filter only for filenames starting with a digit. -int msrScandirFilter(const struct dirent *entry) { - if (isdigit(entry->d_name[0])) { - return 1; - } else { - return 0; - } -} - -QueryData genModelSpecificRegister(QueryContext &context) { - QueryData results; - - struct dirent **entries = nullptr; - int num_entries = scandir("/dev/cpu", &entries, msrScandirFilter, 0); - if (num_entries < 1) { - LOG(WARNING) << "No msr information check msr kernel module is enabled."; - return results; - } - while (num_entries--) { - getModelSpecificRegisterData(results, atoi(entries[num_entries]->d_name)); - free(entries[num_entries]); - } - free(entries); - - return results; -} -} -} diff --git a/osquery/tables/system/linux/mounts.cpp b/osquery/tables/system/linux/mounts.cpp deleted file mode 100644 index d4b804c..0000000 --- a/osquery/tables/system/linux/mounts.cpp +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include -#include - -#include -#include -#include - -namespace osquery { -namespace tables { - -QueryData genMounts(QueryContext &context) { - QueryData results; - FILE *mounts; - struct mntent *ent; - char real_path[PATH_MAX]; - struct statfs st; - - if ((mounts = setmntent("/proc/mounts", "r"))) { - while ((ent = getmntent(mounts))) { - Row r; - - r["device"] = std::string(ent->mnt_fsname); - r["device_alias"] = std::string( - realpath(ent->mnt_fsname, real_path) ? real_path : ent->mnt_fsname); - r["path"] = std::string(ent->mnt_dir); - r["type"] = std::string(ent->mnt_type); - r["flags"] = std::string(ent->mnt_opts); - if (!statfs(ent->mnt_dir, &st)) { - r["blocks_size"] = BIGINT(st.f_bsize); - r["blocks"] = BIGINT(st.f_blocks); - r["blocks_free"] = BIGINT(st.f_bfree); - r["blocks_available"] = BIGINT(st.f_bavail); - r["inodes"] = BIGINT(st.f_files); - r["inodes_free"] = BIGINT(st.f_ffree); - } - - results.push_back(r); - } - endmntent(mounts); - } - - return results; -} -} -} diff --git a/osquery/tables/system/linux/os_version.cpp b/osquery/tables/system/linux/os_version.cpp deleted file mode 100644 index e5ecad1..0000000 --- a/osquery/tables/system/linux/os_version.cpp +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include - -#include -#include - -#include -#include -#include - -namespace xp = boost::xpressive; - -namespace osquery { -namespace tables { - -const std::string kLinuxOSRelease = "/etc/redhat-release"; -const std::string kLinuxOSRegex = - "(?P\\w+) .* " - "(?P[0-9]+)\\.(?P[0-9]+)[\\.]{0,1}(?P[0-9]+).*"; - -QueryData genOSVersion(QueryContext& context) { - std::string content; - if (!readFile(kLinuxOSRelease, content).ok()) { - return {}; - } - - Row r; - auto rx = xp::sregex::compile(kLinuxOSRegex); - xp::smatch matches; - for (const auto& line : osquery::split(content, "\n")) { - if (xp::regex_search(line, matches, rx)) { - r["major"] = INTEGER(matches["major"]); - r["minor"] = INTEGER(matches["minor"]); - r["patch"] = - (matches["patch"].length() > 0) ? INTEGER(matches["patch"]) : "0"; - r["name"] = matches["name"]; - break; - } - } - - // No build name. - r["build"] = ""; - return {r}; -} -} -} diff --git a/osquery/tables/system/linux/pci_devices.cpp b/osquery/tables/system/linux/pci_devices.cpp deleted file mode 100644 index 8a07587..0000000 --- a/osquery/tables/system/linux/pci_devices.cpp +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include -#include - -#include -#include -#include - -#include "osquery/events/linux/udev.h" - -namespace osquery { -namespace tables { - -const std::string kPCIKeySlot = "PCI_SLOT_NAME"; -const std::string kPCIKeyClass = "ID_PCI_CLASS_FROM_DATABASE"; -const std::string kPCIKeyVendor = "ID_VENDOR_FROM_DATABASE"; -const std::string kPCIKeyModel = "ID_MODEL_FROM_DATABASE"; -const std::string kPCIKeyID = "PCI_ID"; -const std::string kPCIKeyDriver = "DRIVER"; - -QueryData genPCIDevices(QueryContext &context) { - QueryData results; - - auto udev_handle = udev_new(); - if (udev_handle == nullptr) { - VLOG(1) << "Could not get udev handle."; - return results; - } - - // Perform enumeration/search. - auto enumerate = udev_enumerate_new(udev_handle); - udev_enumerate_add_match_subsystem(enumerate, "pci"); - udev_enumerate_scan_devices(enumerate); - - // Get list entries and iterate over entries. - struct udev_list_entry *device_entries, *entry; - device_entries = udev_enumerate_get_list_entry(enumerate); - - udev_list_entry_foreach(entry, device_entries) { - const char *path = udev_list_entry_get_name(entry); - auto device = udev_device_new_from_syspath(udev_handle, path); - - Row r; - r["pci_slot"] = UdevEventPublisher::getValue(device, kPCIKeySlot); - r["pci_class"] = UdevEventPublisher::getValue(device, kPCIKeyClass); - r["driver"] = UdevEventPublisher::getValue(device, kPCIKeyDriver); - r["vendor"] = UdevEventPublisher::getValue(device, kPCIKeyVendor); - r["model"] = UdevEventPublisher::getValue(device, kPCIKeyModel); - - // VENDOR:MODEL ID is in the form of HHHH:HHHH. - std::vector ids; - auto device_id = UdevEventPublisher::getValue(device, kPCIKeyID); - boost::split(ids, device_id, boost::is_any_of(":")); - if (ids.size() == 2) { - r["vendor_id"] = ids[0]; - r["model_id"] = ids[1]; - } - - // Set invalid vendor/model IDs to 0. - if (r["vendor_id"].size() == 0) { - r["vendor_id"] = "0"; - } - - if (r["model_id"].size() == 0) { - r["model_id"] = "0"; - } - - results.push_back(r); - udev_device_unref(device); - } - - // Drop references to udev structs. - udev_enumerate_unref(enumerate); - udev_unref(udev_handle); - - return results; -} -} -} diff --git a/osquery/tables/system/linux/process_open_files.cpp b/osquery/tables/system/linux/process_open_files.cpp deleted file mode 100644 index 8fd7380..0000000 --- a/osquery/tables/system/linux/process_open_files.cpp +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include -#include -#include -#include - -namespace osquery { -namespace tables { - -void genDescriptors(const std::string& process, - const std::map& descriptors, - QueryData& results) { - for (const auto& fd : descriptors) { - if (fd.second.find("socket:") != std::string::npos || - fd.second.find("anon_inode:") != std::string::npos || - fd.second.find("pipe:") != std::string::npos) { - // This is NOT a vnode/file descriptor. - continue; - } - - Row r; - r["pid"] = process; - r["fd"] = fd.first; - r["path"] = fd.second; - results.push_back(r); - } - - return; -} - -QueryData genOpenFiles(QueryContext& context) { - QueryData results; - - std::set pids; - if (context.constraints["pid"].exists(EQUALS)) { - pids = context.constraints["pid"].getAll(EQUALS); - } else { - osquery::procProcesses(pids); - } - - for (const auto& process : pids) { - std::map descriptors; - if (osquery::procDescriptors(process, descriptors).ok()) { - genDescriptors(process, descriptors, results); - } - } - - return results; -} -} -} diff --git a/osquery/tables/system/linux/processes.cpp b/osquery/tables/system/linux/processes.cpp deleted file mode 100644 index 23641ec..0000000 --- a/osquery/tables/system/linux/processes.cpp +++ /dev/null @@ -1,278 +0,0 @@ -/* - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include -#include - -#include -#include - -#include - -#include -#include -#include - -namespace osquery { -namespace tables { - -inline std::string getProcAttr(const std::string& attr, const std::string& pid) { - return "/proc/" + pid + "/" + attr; -} - -inline std::string readProcCMDLine(const std::string& pid) { - auto attr = getProcAttr("cmdline", pid); - - std::string content; - readFile(attr, content); - // Remove \0 delimiters. - std::replace_if(content.begin(), - content.end(), - [](const char& c) { return c == 0; }, - ' '); - // Remove trailing delimiter. - boost::algorithm::trim(content); - return content; -} - -inline std::string readProcLink(const std::string& attr, const std::string& pid) { - // The exe is a symlink to the binary on-disk. - auto attr_path = getProcAttr(attr, pid); - - std::string result; - char link_path[PATH_MAX] = {0}; - auto bytes = readlink(attr_path.c_str(), link_path, sizeof(link_path) - 1); - if (bytes >= 0) { - result = std::string(link_path); - } - - return result; -} - -std::set getProcList(const QueryContext& context) { - std::set pidlist; - if (context.constraints.count("pid") > 0 && - context.constraints.at("pid").exists(EQUALS)) { - for (const auto& pid : context.constraints.at("pid").getAll(EQUALS)) { - if (isDirectory("/proc/" + pid)) { - pidlist.insert(pid); - } - } - } else { - osquery::procProcesses(pidlist); - } - - return pidlist; -} - -void genProcessEnvironment(const std::string& pid, QueryData& results) { - auto attr = getProcAttr("environ", pid); - - std::string content; - readFile(attr, content); - const char* variable = content.c_str(); - - // Stop at the end of nul-delimited string content. - while (*variable > 0) { - auto buf = std::string(variable); - size_t idx = buf.find_first_of("="); - - Row r; - r["pid"] = pid; - r["key"] = buf.substr(0, idx); - r["value"] = buf.substr(idx + 1); - results.push_back(r); - variable += buf.size() + 1; - } -} - -void genProcessMap(const std::string& pid, QueryData& results) { - auto map = getProcAttr("maps", pid); - - std::string content; - readFile(map, content); - for (auto& line : osquery::split(content, "\n")) { - auto fields = osquery::split(line, " "); - - Row r; - r["pid"] = pid; - - // If can't read address, not sure. - if (fields.size() < 5) { - continue; - } - - if (fields[0].size() > 0) { - auto addresses = osquery::split(fields[0], "-"); - r["start"] = "0x" + addresses[0]; - r["end"] = "0x" + addresses[1]; - } - - r["permissions"] = fields[1]; - r["offset"] = BIGINT(std::stoll(fields[2], nullptr, 16)); - r["device"] = fields[3]; - r["inode"] = fields[4]; - - // Path name must be trimmed. - if (fields.size() > 5) { - boost::trim(fields[5]); - r["path"] = fields[5]; - } - - // BSS with name in pathname. - r["pseudo"] = (fields[4] == "0" && r["path"].size() > 0) ? "1" : "0"; - results.push_back(r); - } -} - -struct SimpleProcStat { - // Output from string parsing /proc//status. - std::string parent; // PPid: - std::string name; // Name: - std::string real_uid; // Uid: * - - - - std::string real_gid; // Gid: * - - - - std::string effective_uid; // Uid: - * - - - std::string effective_gid; // Gid: - * - - - - std::string resident_size; // VmRSS: - std::string phys_footprint; // VmSize: - - // Output from sring parsing /proc//stat. - std::string user_time; - std::string system_time; - std::string start_time; -}; - -SimpleProcStat getProcStat(const std::string& pid) { - SimpleProcStat stat; - std::string content; - if (readFile(getProcAttr("stat", pid), content).ok()) { - auto detail_start = content.find_last_of(")"); - // Start parsing stats from ") ..." - auto details = osquery::split(content.substr(detail_start + 2), " "); - stat.parent = details.at(1); - stat.user_time = details.at(11); - stat.system_time = details.at(12); - stat.start_time = details.at(19); - } - - if (readFile(getProcAttr("status", pid), content).ok()) { - for (const auto& line : osquery::split(content, "\n")) { - // Status lines are formatted: Key: Value....\n. - auto detail = osquery::split(line, ":", 1); - if (detail.size() != 2) { - continue; - } - - // There are specific fields from each detail. - if (detail.at(0) == "Name") { - stat.name = detail.at(1); - } else if (detail.at(0) == "VmRSS") { - detail[1].erase(detail.at(1).end() - 3, detail.at(1).end()); - // Memory is reported in kB. - stat.resident_size = detail.at(1) + "000"; - } else if (detail.at(0) == "VmSize") { - detail[1].erase(detail.at(1).end() - 3, detail.at(1).end()); - // Memory is reported in kB. - stat.phys_footprint = detail.at(1) + "000"; - } else if (detail.at(0) == "Gid") { - // Format is: R E - - - auto gid_detail = osquery::split(detail.at(1), "\t"); - if (gid_detail.size() == 4) { - stat.real_gid = gid_detail.at(0); - stat.effective_gid = gid_detail.at(1); - } - } else if (detail.at(0) == "Uid") { - auto uid_detail = osquery::split(detail.at(1), "\t"); - if (uid_detail.size() == 4) { - stat.real_uid = uid_detail.at(0); - stat.effective_uid = uid_detail.at(1); - } - } - } - } - - return stat; -} - -void genProcess(const std::string& pid, QueryData& results) { - // Parse the process stat and status. - auto proc_stat = getProcStat(pid); - - Row r; - r["pid"] = pid; - r["parent"] = proc_stat.parent; - r["path"] = readProcLink("exe", pid); - r["name"] = proc_stat.name; - - // Read/parse cmdline arguments. - r["cmdline"] = readProcCMDLine(pid); - r["cwd"] = readProcLink("cwd", pid); - r["root"] = readProcLink("root", pid); - - r["uid"] = proc_stat.real_uid; - r["euid"] = proc_stat.effective_uid; - r["gid"] = proc_stat.real_gid; - r["egid"] = proc_stat.effective_gid; - - // If the path of the executable that started the process is available and - // the path exists on disk, set on_disk to 1. If the path is not - // available, set on_disk to -1. If, and only if, the path of the - // executable is available and the file does NOT exist on disk, set on_disk - // to 0. - r["on_disk"] = osquery::pathExists(r["path"]).toString(); - - // size/memory information - r["wired_size"] = "0"; // No support for unpagable counters in linux. - r["resident_size"] = proc_stat.resident_size; - r["phys_footprint"] = proc_stat.phys_footprint; - - // time information - r["user_time"] = proc_stat.user_time; - r["system_time"] = proc_stat.system_time; - r["start_time"] = proc_stat.start_time; - - results.push_back(r); -} - -QueryData genProcesses(QueryContext& context) { - QueryData results; - - auto pidlist = getProcList(context); - for (const auto& pid : pidlist) { - genProcess(pid, results); - } - - return results; -} - -QueryData genProcessEnvs(QueryContext& context) { - QueryData results; - - auto pidlist = getProcList(context); - for (const auto& pid : pidlist) { - genProcessEnvironment(pid, results); - } - - return results; -} - -QueryData genProcessMemoryMap(QueryContext& context) { - QueryData results; - - auto pidlist = getProcList(context); - for (const auto& pid : pidlist) { - genProcessMap(pid, results); - } - - return results; -} -} -} diff --git a/osquery/tables/system/linux/shared_memory.cpp b/osquery/tables/system/linux/shared_memory.cpp deleted file mode 100644 index 441ef36..0000000 --- a/osquery/tables/system/linux/shared_memory.cpp +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include -#include - -#include -#include -#include -#include - -namespace osquery { -namespace tables { - -struct shm_info { - int used_ids; - unsigned long shm_tot; - unsigned long shm_rss; - unsigned long shm_swp; - unsigned long swap_attempts; - unsigned long swap_successes; -} __attribute__((unused)); - -QueryData genSharedMemory(QueryContext &context) { - QueryData results; - - // Use shared memory control (shmctl) to get the max SHMID. - struct shm_info shm_info; - int maxid = shmctl(0, SHM_INFO, (struct shmid_ds *)(void *)&shm_info); - if (maxid < 0) { - VLOG(1) << "Linux kernel not configured for shared memory"; - return {}; - } - - // Use a static pointer to access IPC permissions structure. - struct shmid_ds shmseg; - struct ipc_perm *ipcp = &shmseg.shm_perm; - - // Then iterate each shared memory ID up to the max. - for (int id = 0; id <= maxid; id++) { - int shmid = shmctl(id, SHM_STAT, &shmseg); - if (shmid < 0) { - continue; - } - - Row r; - r["shmid"] = INTEGER(shmid); - - struct passwd *pw = getpwuid(shmseg.shm_perm.uid); - if (pw != nullptr) { - r["owner_uid"] = BIGINT(pw->pw_uid); - } - - pw = getpwuid(shmseg.shm_perm.cuid); - if (pw != nullptr) { - r["creator_uid"] = BIGINT(pw->pw_uid); - } - - // Accessor, creator pids. - r["pid"] = BIGINT(shmseg.shm_lpid); - r["creator_pid"] = BIGINT(shmseg.shm_cpid); - - // Access, detached, creator times - r["atime"] = BIGINT(shmseg.shm_atime); - r["dtime"] = BIGINT(shmseg.shm_dtime); - r["ctime"] = BIGINT(shmseg.shm_ctime); - - r["permissions"] = lsperms(ipcp->mode); - r["size"] = BIGINT(shmseg.shm_segsz); - r["attached"] = INTEGER(shmseg.shm_nattch); - r["status"] = (ipcp->mode & SHM_DEST) ? "dest" : ""; - r["locked"] = (ipcp->mode & SHM_LOCKED) ? "1" : "0"; - - results.push_back(r); - } - - return results; -} -} -} \ No newline at end of file diff --git a/osquery/tables/system/linux/smbios_tables.cpp b/osquery/tables/system/linux/smbios_tables.cpp deleted file mode 100644 index 890ec2f..0000000 --- a/osquery/tables/system/linux/smbios_tables.cpp +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include -#include -#include -#include - -#include "osquery/tables/system/smbios_utils.h" - -namespace osquery { -namespace tables { - -#define kLinuxSMBIOSRawAddress_ 0xF0000 -#define kLinuxSMBIOSRawLength_ 0x10000 - -const std::string kLinuxEFISystabPath = "/sys/firmware/efi/systab"; -const std::string kLinuxLegacyEFISystabPath = "/proc/efi/systab"; - -void genSMBIOSFromDMI(size_t base, size_t length, QueryData& results) { - // Linux will expose the SMBIOS/DMI entry point structures, which contain - // a member variable with the DMI tables start address and size. - // This applies to both the EFI-variable and physical memory search. - uint8_t* data; - auto status = osquery::readRawMem(base, length, (void**)&data); - if (!status.ok()) { - VLOG(1) << "Could not read DMI tables memory"; - return; - } - - // Attempt to parse tables from allocated data. - genSMBIOSTables(data, length, results); - free(data); -} - -void genEFISystabTables(QueryData& results) { - // Not yet supported. - return; -} - -void genRawSMBIOSTables(QueryData& results) { - uint8_t* data; - auto status = osquery::readRawMem( - kLinuxSMBIOSRawAddress_, kLinuxSMBIOSRawLength_, (void**)&data); - if (!status.ok()) { - VLOG(1) << "Could not read SMBIOS memory"; - return; - } - - // Search for the SMBIOS/DMI tables magic header string. - size_t offset; - for (offset = 0; offset <= 0xFFF0; offset += 16) { - // Could look for "_SM_" for the SMBIOS header, but the DMI header exists - // in both SMBIOS and the legacy DMI spec. - if (memcmp(data + offset, "_DMI_", 5) == 0) { - auto dmi_data = (DMIEntryPoint*)(data + offset); - genSMBIOSFromDMI(dmi_data->tableAddress, dmi_data->tableLength, results); - } - } - - free(data); -} - -QueryData genSMBIOSTables(QueryContext& context) { - QueryData results; - - if (osquery::isReadable(kLinuxEFISystabPath).ok() || - osquery::isReadable(kLinuxLegacyEFISystabPath).ok()) { - genEFISystabTables(results); - } else { - genRawSMBIOSTables(results); - } - - return results; -} -} -} diff --git a/osquery/tables/system/linux/sysctl_utils.cpp b/osquery/tables/system/linux/sysctl_utils.cpp deleted file mode 100644 index a776418..0000000 --- a/osquery/tables/system/linux/sysctl_utils.cpp +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include - -#include - -#include -#include - -#include "osquery/tables/system/sysctl_utils.h" - -namespace fs = boost::filesystem; - -namespace osquery { -namespace tables { - -const std::string kSystemControlPath = "/proc/sys/"; - -void genControlInfo(const std::string& mib_path, QueryData& results, - const std::map& config) { - if (isDirectory(mib_path).ok()) { - // Iterate through the subitems and items. - std::vector items; - if (listDirectoriesInDirectory(mib_path, items).ok()) { - for (const auto& item : items) { - genControlInfo(item, results, config); - } - } - - if (listFilesInDirectory(mib_path, items).ok()) { - for (const auto& item : items) { - genControlInfo(item, results, config); - } - } - return; - } - - // This is a file (leaf-control). - Row r; - r["name"] = mib_path.substr(kSystemControlPath.size()); - - std::replace(r["name"].begin(), r["name"].end(), '/', '.'); - // No known way to convert name MIB to int array. - r["subsystem"] = osquery::split(r.at("name"), ".")[0]; - - if (isReadable(mib_path).ok()) { - std::string content; - readFile(mib_path, content); - boost::trim(content); - r["current_value"] = content; - } - - if (config.count(r.at("name")) > 0) { - r["config_value"] = config.at(r.at("name")); - } - r["type"] = "string"; - results.push_back(r); -} - -void genControlInfo(int* oid, - size_t oid_size, - QueryData& results, - const std::map& config) { - // Get control size - size_t response_size = CTL_MAX_VALUE; - char response[CTL_MAX_VALUE + 1] = {0}; - if (sysctl(oid, oid_size, response, &response_size, 0, 0) != 0) { - // Cannot request MIB data. - return; - } - - // Data is output, but no way to determine type (long, int, string, struct). - Row r; - r["oid"] = stringFromMIB(oid, oid_size); - r["current_value"] = std::string(response); - r["type"] = "string"; - results.push_back(r); -} - -void genAllControls(QueryData& results, - const std::map& config, - const std::string& subsystem) { - // Linux sysctl subsystems are directories in /proc - std::vector subsystems; - if (!listDirectoriesInDirectory("/proc/sys", subsystems).ok()) { - return; - } - - for (const auto& sub : subsystems) { - if (subsystem.size() != 0 && - fs::path(sub).filename().string() != subsystem) { - // Request is limiting subsystem. - continue; - } - genControlInfo(sub, results, config); - } -} - -void genControlInfoFromName(const std::string& name, QueryData& results, - const std::map& config) { - // Convert '.'-tokenized name to path. - std::string name_path = name; - std::replace(name_path.begin(), name_path.end(), '.', '/'); - auto mib_path = fs::path(kSystemControlPath) / name_path; - - genControlInfo(mib_path.string(), results, config); -} -} -} diff --git a/osquery/tables/system/linux/usb_devices.cpp b/osquery/tables/system/linux/usb_devices.cpp deleted file mode 100644 index 476e87b..0000000 --- a/osquery/tables/system/linux/usb_devices.cpp +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include -#include -#include - -#include "osquery/events/linux/udev.h" - -namespace osquery { -namespace tables { - -const std::string kUSBKeyVendorID = "ID_VENDOR_ID"; -const std::string kUSBKeyVendor = "ID_VENDOR_FROM_DATABASE"; -const std::string kUSBKeyModelID = "ID_MODEL_ID"; -const std::string kUSBKeyModel = "ID_MODEL_FROM_DATABASE"; -const std::string kUSBKeyDriver = "ID_USB_DRIVER"; -const std::string kUSBKeySubsystem = "SUBSYSTEM"; -const std::string kUSBKeySerial = "ID_SERIAL_SHORT"; -const std::string kUSBKeyAddress = "BUSNUM"; -const std::string kUSBKeyPort = "DEVNUM"; - -QueryData genUSBDevices(QueryContext &context) { - QueryData results; - - auto udev_handle = udev_new(); - if (udev_handle == nullptr) { - VLOG(1) << "Could not get udev handle."; - return results; - } - - // Perform enumeration/search. - auto enumerate = udev_enumerate_new(udev_handle); - udev_enumerate_add_match_subsystem(enumerate, "usb"); - udev_enumerate_scan_devices(enumerate); - - // Get list entries and iterate over entries. - struct udev_list_entry *device_entries, *entry; - device_entries = udev_enumerate_get_list_entry(enumerate); - - udev_list_entry_foreach(entry, device_entries) { - const char *path = udev_list_entry_get_name(entry); - auto device = udev_device_new_from_syspath(udev_handle, path); - - Row r; - // r["driver"] = UdevEventPublisher::getValue(device, kUSBKeyDriver); - r["vendor"] = UdevEventPublisher::getValue(device, kUSBKeyVendor); - r["model"] = UdevEventPublisher::getValue(device, kUSBKeyModel); - - // USB-specific vendor/model ID properties. - r["model_id"] = UdevEventPublisher::getValue(device, kUSBKeyModelID); - r["vendor_id"] = UdevEventPublisher::getValue(device, kUSBKeyVendorID); - r["serial"] = UdevEventPublisher::getValue(device, kUSBKeySerial); - - // Address/port accessors. - r["usb_address"] = UdevEventPublisher::getValue(device, kUSBKeyAddress); - r["usb_port"] = UdevEventPublisher::getValue(device, kUSBKeyPort); - - // Removable detection. - auto removable = UdevEventPublisher::getAttr(device, "removable"); - if (removable == "unknown") { - r["removable"] = "-1"; - } else { - r["removable"] = "1"; - } - - if (r["usb_address"].size() > 0 && r["usb_port"].size() > 0) { - results.push_back(r); - } - udev_device_unref(device); - } - - // Drop references to udev structs. - udev_enumerate_unref(enumerate); - udev_unref(udev_handle); - - return results; -} -} -} diff --git a/osquery/tables/system/linux/user_groups.cpp b/osquery/tables/system/linux/user_groups.cpp deleted file mode 100644 index da8be00..0000000 --- a/osquery/tables/system/linux/user_groups.cpp +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include "osquery/tables/system/user_groups.h" - -namespace osquery { -namespace tables { - -extern std::mutex pwdEnumerationMutex; - -QueryData genUserGroups(QueryContext &context) { - QueryData results; - struct passwd *pwd = nullptr; - - if (context.constraints["uid"].exists(EQUALS)) { - std::set uids = context.constraints["uid"].getAll(EQUALS); - for (const auto &uid : uids) { - pwd = getpwuid(std::strtol(uid.c_str(), NULL, 10)); - if (pwd != nullptr) { - user_t user; - user.name = pwd->pw_name; - user.uid = pwd->pw_uid; - user.gid = pwd->pw_gid; - getGroupsForUser(results, user); - } - } - } else { - std::lock_guard lock(pwdEnumerationMutex); - std::set users_in; - while ((pwd = getpwent()) != nullptr) { - if (std::find(users_in.begin(), users_in.end(), pwd->pw_uid) == - users_in.end()) { - user_t user; - user.name = pwd->pw_name; - user.uid = pwd->pw_uid; - user.gid = pwd->pw_gid; - getGroupsForUser(results, user); - users_in.insert(pwd->pw_uid); - } - } - endpwent(); - users_in.clear(); - } - - return results; -} -} -} diff --git a/osquery/tables/system/linux/users.cpp b/osquery/tables/system/linux/users.cpp deleted file mode 100644 index 6108987..0000000 --- a/osquery/tables/system/linux/users.cpp +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include -#include -#include -#include - -#include - -#include -#include -#include -#include - -namespace osquery { -namespace tables { - -std::mutex pwdEnumerationMutex; - -QueryData genUsers(QueryContext& context) { - std::lock_guard lock(pwdEnumerationMutex); - QueryData results; - struct passwd *pwd = nullptr; - std::set users_in; - - while ((pwd = getpwent()) != nullptr) { - if (std::find(users_in.begin(), users_in.end(), pwd->pw_uid) == - users_in.end()) { - Row r; - r["uid"] = BIGINT(pwd->pw_uid); - r["gid"] = BIGINT(pwd->pw_gid); - r["uid_signed"] = BIGINT((int32_t) pwd->pw_uid); - r["gid_signed"] = BIGINT((int32_t) pwd->pw_gid); - r["username"] = TEXT(pwd->pw_name); - r["description"] = TEXT(pwd->pw_gecos); - r["directory"] = TEXT(pwd->pw_dir); - r["shell"] = TEXT(pwd->pw_shell); - results.push_back(r); - users_in.insert(pwd->pw_uid); - } - } - endpwent(); - users_in.clear(); - - return results; -} - -/// Example of update feature -Status updateUsers(Row& row) { - for (auto& r : row) - LOG(ERROR) << "DEBUG: " << r.first << ", " << r.second; - - return Status(0, "OK"); -} -} -} diff --git a/osquery/tables/system/logged_in_users.cpp b/osquery/tables/system/logged_in_users.cpp deleted file mode 100644 index ae24a33..0000000 --- a/osquery/tables/system/logged_in_users.cpp +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include - -#include -#include - -#include - -namespace osquery { -namespace tables { - -std::mutex utmpxEnumerationMutex; - -QueryData genLoggedInUsers(QueryContext& context) { - std::lock_guard lock(utmpxEnumerationMutex); - QueryData results; - struct utmpx *entry = nullptr; - - while ((entry = getutxent()) != nullptr) { - if (entry->ut_pid == 1) { - continue; - } - Row r; - r["user"] = TEXT(entry->ut_user); - r["tty"] = TEXT(entry->ut_line); - r["host"] = TEXT(entry->ut_host); - r["time"] = INTEGER(entry->ut_tv.tv_sec); - r["pid"] = INTEGER(entry->ut_pid); - results.push_back(r); - } - endutxent(); - - return results; -} -} -} diff --git a/osquery/tables/system/shell_history.cpp b/osquery/tables/system/shell_history.cpp deleted file mode 100644 index 71681a6..0000000 --- a/osquery/tables/system/shell_history.cpp +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include -#include - -#include - -#include -#include -#include -#include -#include - -namespace osquery { -namespace tables { - -const std::vector kShellHistoryFiles = { - ".bash_history", ".zsh_history", ".zhistory", ".history", -}; - -void genShellHistoryForUser(const std::string& username, - const std::string& directory, - QueryData& results) { - for (const auto& hfile : kShellHistoryFiles) { - boost::filesystem::path history_file = directory; - history_file /= hfile; - - std::string history_content; - if (!readFile(history_file, history_content).ok()) { - // Cannot read a specific history file. - continue; - } - - for (const auto& line : split(history_content, "\n")) { - Row r; - r["username"] = username; - r["command"] = line; - r["history_file"] = history_file.string(); - results.push_back(r); - } - } -} - -QueryData genShellHistory(QueryContext& context) { - QueryData results; - - // Select only the home directory for this user. - QueryData users; - if (!context.constraints["username"].exists(EQUALS)) { - users = - SQL::selectAllFrom("users", "uid", EQUALS, std::to_string(getuid())); - } else { - auto usernames = context.constraints["username"].getAll(EQUALS); - for (const auto& username : usernames) { - // Use a predicated select all for each user. - auto user = SQL::selectAllFrom("users", "username", EQUALS, username); - users.insert(users.end(), user.begin(), user.end()); - } - } - - // Iterate over each user - for (const auto& row : users) { - if (row.count("username") > 0 && row.count("directory") > 0) { - genShellHistoryForUser(row.at("username"), row.at("directory"), results); - } - } - - return results; -} -} -} diff --git a/osquery/tables/system/smbios_utils.cpp b/osquery/tables/system/smbios_utils.cpp deleted file mode 100644 index 4e0ba4c..0000000 --- a/osquery/tables/system/smbios_utils.cpp +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include - -#include "osquery/tables/system/smbios_utils.h" - -namespace osquery { -namespace tables { - -const std::map kSMBIOSTypeDescriptions = { - {0, "BIOS Information"}, - {1, "System Information"}, - {2, "Base Board or Module Information"}, - {3, "System Enclosure or Chassis"}, - {4, "Processor Information"}, - {5, "Memory Controller Information"}, - {6, "Memory Module Information"}, - {7, "Cache Information"}, - {8, "Port Connector Information"}, - {9, "System Slots"}, - {10, "On Board Devices Information"}, - {11, "OEM Strings"}, - {12, "System Configuration Options"}, - {13, "BIOS Language Information"}, - {14, "Group Associations"}, - {15, "System Event Log"}, - {16, "Physical Memory Array"}, - {17, "Memory Device"}, - {18, "32-bit Memory Error Information"}, - {19, "Memory Array Mapped Address"}, - {20, "Memory Device Mapped Address"}, - {21, "Built-in Pointing Device"}, - {22, "Portable Battery"}, - {23, "System Reset"}, - {24, "Hardware Security"}, - {25, "System Power Controls"}, - {26, "Voltage Probe"}, - {27, "Cooling Device"}, - {28, "Temperature Probe"}, - {29, "Electrical Current Probe"}, - {30, "Out-of-Band Remote Access"}, - {31, "Boot Integrity Services"}, - {32, "System Boot Information"}, - {33, "64-bit Memory Error Information"}, - {34, "Management Device"}, - {35, "Management Device Component"}, - {36, "Management Device Threshold Data"}, - {37, "Memory Channel"}, - {38, "IPMI Device Information"}, - {39, "System Power Supply"}, - {40, "Additional Information"}, - {41, "Onboard Devices Extended Info"}, - {126, "Inactive"}, - {127, "End-of-Table"}, - {130, "Memory SPD Data"}, - {131, "OEM Processor Type"}, - {132, "OEM Processor Bus Speed"}, -}; - -void genSMBIOSTables(const uint8_t* tables, size_t length, QueryData& results) { - // Keep a pointer to the end of the SMBIOS data for comparison. - auto tables_end = tables + length; - auto table = tables; - - // Iterate through table structures within SMBIOS data range. - size_t index = 0; - while (table + sizeof(SMBStructHeader) <= tables_end) { - auto header = (const SMBStructHeader*)table; - if (table + header->length > tables_end) { - // Invalid header, length must be within SMBIOS data range. - break; - } - - Row r; - // The index is a supliment that keeps track of table order. - r["number"] = INTEGER(index++); - r["type"] = INTEGER((unsigned short)header->type); - if (kSMBIOSTypeDescriptions.count(header->type) > 0) { - r["description"] = kSMBIOSTypeDescriptions.at(header->type); - } - - r["handle"] = BIGINT((unsigned long long)header->handle); - r["header_size"] = INTEGER((unsigned short)header->length); - - // The SMBIOS structure may have unformatted, double-NULL delimited trailing - // data, which are usually strings. - auto next_table = table + header->length; - for (; next_table + sizeof(SMBStructHeader) <= tables_end; next_table++) { - if (next_table[0] == 0 && next_table[1] == 0) { - next_table += 2; - break; - } - } - - auto table_length = next_table - table; - r["size"] = INTEGER(table_length); - r["md5"] = hashFromBuffer(HASH_TYPE_MD5, table, table_length); - - table = next_table; - results.push_back(r); - } -} -} -} diff --git a/osquery/tables/system/smbios_utils.h b/osquery/tables/system/smbios_utils.h deleted file mode 100644 index ff5eea3..0000000 --- a/osquery/tables/system/smbios_utils.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include - -namespace osquery { -namespace tables { - -typedef struct SMBStructHeader { - uint8_t type; - uint8_t length; - uint16_t handle; -} __attribute__((packed)) SMBStructHeader; - -typedef struct DMIEntryPoint { - uint8_t anchor[5]; - uint8_t checksum; - uint16_t tableLength; - uint32_t tableAddress; - uint16_t structureCount; - uint8_t bcdRevision; -} __attribute__((packed)) DMIEntryPoint; - -extern const std::map kSMBIOSTypeDescriptions; - -void genSMBIOSTables(const uint8_t* tables, size_t length, QueryData& results); -} -} diff --git a/osquery/tables/system/suid_bin.cpp b/osquery/tables/system/suid_bin.cpp deleted file mode 100644 index 43811e6..0000000 --- a/osquery/tables/system/suid_bin.cpp +++ /dev/null @@ -1,134 +0,0 @@ -/* - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include -#include -#include - -#include - -#include -#include -#include - -namespace fs = boost::filesystem; - -namespace osquery { -namespace tables { - -std::vector kBinarySearchPaths = { - "/bin", - "/sbin", - "/usr/bin", - "/usr/sbin", - "/usr/local/bin", - "/usr/local/sbin", - "/tmp", -}; - -Status genBin(const fs::path& path, int perms, QueryData& results) { - struct stat info; - // store user and group - if (stat(path.c_str(), &info) != 0) { - return Status(1, "stat failed"); - } - - // store path - Row r; - r["path"] = path.string(); - struct passwd *pw = getpwuid(info.st_uid); - struct group *gr = getgrgid(info.st_gid); - - // get user name + group - std::string user; - if (pw != nullptr) { - user = std::string(pw->pw_name); - } else { - user = boost::lexical_cast(info.st_uid); - } - - std::string group; - if (gr != nullptr) { - group = std::string(gr->gr_name); - } else { - group = boost::lexical_cast(info.st_gid); - } - - r["username"] = user; - r["groupname"] = group; - - r["permissions"] = ""; - if ((perms & 04000) == 04000) { - r["permissions"] += "S"; - } - - if ((perms & 02000) == 02000) { - r["permissions"] += "G"; - } - - results.push_back(r); - return Status(0, "OK"); -} - -bool isSuidBin(const fs::path& path, int perms) { - if (!fs::is_regular_file(path)) { - return false; - } - - if ((perms & 04000) == 04000 || (perms & 02000) == 02000) { - return true; - } - return false; -} - -void genSuidBinsFromPath(const std::string& path, QueryData& results) { - if (!pathExists(path).ok()) { - // Creating an iterator on a missing path will except. - return; - } - - auto it = fs::recursive_directory_iterator(fs::path(path)); - fs::recursive_directory_iterator end; - while (it != end) { - fs::path path = *it; - try { - // Do not traverse symlinked directories. - if (fs::is_directory(path) && fs::is_symlink(path)) { - it.no_push(); - } - - int perms = it.status().permissions(); - if (isSuidBin(path, perms)) { - // Only emit suid bins. - genBin(path, perms, results); - } - - ++it; - } catch (fs::filesystem_error& e) { - VLOG(1) << "Cannot read binary from " << path; - it.no_push(); - // Try to recover, otherwise break. - try { ++it; } catch(fs::filesystem_error& e) { break; } - } - } -} - -QueryData genSuidBin(QueryContext& context) { - QueryData results; - - // Todo: add hidden column to select on that triggers non-std path searches. - for (const auto& path : kBinarySearchPaths) { - genSuidBinsFromPath(path, results); - } - - return results; -} -} -} diff --git a/osquery/tables/system/sysctl_utils.h b/osquery/tables/system/sysctl_utils.h deleted file mode 100644 index 4346d71..0000000 --- a/osquery/tables/system/sysctl_utils.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include - -#include - -namespace osquery { -namespace tables { - -#define CTL_MAX_VALUE 128 - -#ifndef CTL_DEBUG_MAXID -#define CTL_DEBUG_MAXID (CTL_MAXNAME * 2) -#endif - -std::string stringFromMIB(const int* oid, size_t oid_size); - -/// Must be implemented by the platform. -void genAllControls(QueryData& results, - const std::map& config, - const std::string& subsystem); - -/// Must be implemented by the platform. -void genControlInfo(int* oid, - size_t oid_size, - QueryData& results, - const std::map& config); - -/// Must be implemented by the platform. -void genControlInfoFromName(const std::string& name, QueryData& results, - const std::map& config); -} -} diff --git a/osquery/tables/system/system_controls.cpp b/osquery/tables/system/system_controls.cpp deleted file mode 100644 index c91d0ca..0000000 --- a/osquery/tables/system/system_controls.cpp +++ /dev/null @@ -1,128 +0,0 @@ -/* - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include - -#include -#include - -#include "osquery/tables/system/sysctl_utils.h" - -namespace osquery { -namespace tables { - -const std::vector kControlSettingsFiles = {"/etc/sysctl.conf"}; - -const std::vector kControlSettingsDirs = { - "/run/sysctl.d/%.conf", - "/etc/sysctl.d/%.conf", - "/usr/local/lib/sysctl.d/%.conf", - "/usr/lib/sysctl.d/%.conf", - "/lib/sysctl.d/%.conf", -}; - -std::string stringFromMIB(const int* oid, size_t oid_size) { - std::string result; - for (size_t i = 0; i < oid_size; ++i) { - // Walk an int-encoded MIB and return the string representation, '.'. - if (result.size() > 0) { - result += "."; - } - result += std::to_string(oid[i]); - } - return result; -} - -void genControlInfoFromOIDString( - const std::string& oid_string, - QueryData& results, - const std::map& config) { - int request[CTL_DEBUG_MAXID + 2] = {0}; - auto tokens = osquery::split(oid_string, "."); - if (tokens.size() > CTL_DEBUG_MAXID) { - // OID input string was too large. - return; - } - - // Convert the string into an int array. - for (size_t i = 0; i < tokens.size(); ++i) { - request[i] = atol(tokens.at(i).c_str()); - } - genControlInfo((int*)request, tokens.size(), results, config); -} - -void genControlConfigFromPath(const std::string& path, - std::map& config) { - std::string content; - if (!osquery::readFile(path, content).ok()) { - return; - } - - for (auto& line : split(content, "\n")) { - boost::trim(line); - if (line[0] == '#' || line[0] == ';') { - continue; - } - - // Try to tokenize the config line using '='. - auto detail = split(line, "="); - if (detail.size() == 2) { - boost::trim(detail[0]); - boost::trim(detail[1]); - config[detail[0]] = detail[1]; - } - } -} - -QueryData genSystemControls(QueryContext& context) { - QueryData results; - - // Read the sysctl.conf values. - std::map config; - for (const auto& path : kControlSettingsFiles) { - genControlConfigFromPath(path, config); - } - - for (const auto& dirs : kControlSettingsDirs) { - std::vector configs; - if (resolveFilePattern(dirs, configs).ok()) { - for (const auto& path : configs) { - genControlConfigFromPath(path, config); - } - } - } - - // Iterate through the sysctl-defined macro of control types. - if (context.constraints["name"].exists(EQUALS)) { - // Request MIB information by the description (name). - auto names = context.constraints["name"].getAll(EQUALS); - for (const auto& name : names) { - genControlInfoFromName(name, results, config); - } - } else if (context.constraints["oid"].exists(EQUALS)) { - // Request MIB by OID as a string, parse into set of INTs. - auto oids = context.constraints["oid"].getAll(EQUALS); - for (const auto& oid_string : oids) { - genControlInfoFromOIDString(oid_string, results, config); - } - } else if (context.constraints["subsystem"].exists(EQUALS)) { - // Limit the MIB search to a subsystem name (first find the INT). - auto subsystems = context.constraints["subsystem"].getAll(EQUALS); - for (const auto& subsystem : subsystems) { - genAllControls(results, config, subsystem); - } - } else { - genAllControls(results, config, ""); - } - - return results; -} -} -} diff --git a/osquery/tables/system/uptime.cpp b/osquery/tables/system/uptime.cpp deleted file mode 100644 index 5528896..0000000 --- a/osquery/tables/system/uptime.cpp +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include - -#if defined(__APPLE__) - #include - #include - #include -#elif defined(__linux__) - #include -#endif - -namespace osquery { -namespace tables { - -long getUptime() { - #if defined(__APPLE__) - struct timeval boot_time; - size_t len = sizeof(boot_time); - int mib[2] = { - CTL_KERN, - KERN_BOOTTIME - }; - - if (sysctl(mib, 2, &boot_time, &len, NULL, 0) < 0) { - return -1; - } - - time_t seconds_since_boot = boot_time.tv_sec; - time_t current_seconds = time(NULL); - - return long(difftime(current_seconds, seconds_since_boot)); - #elif defined(__linux__) - struct sysinfo sys_info; - - if (sysinfo(&sys_info) != 0) { - return -1; - } - - return sys_info.uptime; - #endif -} - -QueryData genUptime(QueryContext& context) { - Row r; - QueryData results; - long uptime_in_seconds = getUptime(); - - if (uptime_in_seconds >= 0) { - r["days"] = INTEGER(uptime_in_seconds / 60 / 60 / 24); - r["hours"] = INTEGER((uptime_in_seconds / 60 / 60) % 24); - r["minutes"] = INTEGER((uptime_in_seconds / 60) % 60); - r["seconds"] = INTEGER(uptime_in_seconds % 60); - r["total_seconds"] = BIGINT(uptime_in_seconds); - results.push_back(r); - } - - return results; -} -} -} diff --git a/osquery/tables/system/user_groups.h b/osquery/tables/system/user_groups.h deleted file mode 100644 index 384f8a3..0000000 --- a/osquery/tables/system/user_groups.h +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include -#include - -#include -#include - -#include -#include -#include -#include - -// This is also the max supported number for OS X right now. -#define EXPECTED_GROUPS_MAX 64 - -namespace osquery { -namespace tables { - -template -static inline void addGroupsToResults(QueryData &results, - int uid, - const T *groups, - int ngroups) { - for (int i = 0; i < ngroups; i++) { - Row r; - r["uid"] = BIGINT(uid); - r["gid"] = BIGINT(groups[i]); - results.push_back(r); - } - - return; -} - -template -struct user_t { - const char *name; - uid_type uid; - gid_type gid; -}; - -template -static void getGroupsForUser(QueryData &results, - const user_t &user) { - gid_type groups_buf[EXPECTED_GROUPS_MAX]; - gid_type *groups = groups_buf; - int ngroups = EXPECTED_GROUPS_MAX; - - // GLIBC version before 2.3.3 may have a buffer overrun: - // http://man7.org/linux/man-pages/man3/getgrouplist.3.html - if (getgrouplist(user.name, user.gid, groups, &ngroups) < 0) { - // EXPECTED_GROUPS_MAX was probably not large enough. - // Try a larger size buffer. - // Darwin appears to not resize ngroups correctly. We can hope - // we had enough space to start with. - groups = new gid_type[ngroups]; - if (groups == nullptr) { - TLOG << "Could not allocate memory to get user groups"; - return; - } - - if (getgrouplist(user.name, user.gid, groups, &ngroups) < 0) { - TLOG << "Could not get users group list"; - } else { - addGroupsToResults(results, user.uid, groups, ngroups); - } - - delete[] groups; - } else { - addGroupsToResults(results, user.uid, groups, ngroups); - } - return; -} -} -} diff --git a/osquery/tables/utility/file.cpp b/osquery/tables/utility/file.cpp deleted file mode 100644 index 5e5347a..0000000 --- a/osquery/tables/utility/file.cpp +++ /dev/null @@ -1,139 +0,0 @@ -/* - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include - -#include - -#include -#include -#include - -namespace fs = boost::filesystem; - -namespace osquery { -namespace tables { - -void genFileInfo(const std::string& path, - const std::string& filename, - const std::string& dir, - const std::string& pattern, - QueryData& results) { - // Must provide the path, filename, directory separate from boost path->string - // helpers to match any explicit (query-parsed) predicate constraints. - struct stat file_stat, link_stat; - if (lstat(path.c_str(), &link_stat) < 0 || stat(path.c_str(), &file_stat)) { - // Path was not real, had too may links, or could not be accessed. - return; - } - - Row r; - r["path"] = path; - r["filename"] = filename; - r["directory"] = dir; - - r["inode"] = BIGINT(file_stat.st_ino); - r["uid"] = BIGINT(file_stat.st_uid); - r["gid"] = BIGINT(file_stat.st_gid); - r["mode"] = lsperms(file_stat.st_mode); - r["device"] = BIGINT(file_stat.st_rdev); - r["size"] = BIGINT(file_stat.st_size); - r["block_size"] = INTEGER(file_stat.st_blksize); - r["hard_links"] = INTEGER(file_stat.st_nlink); - - // Times - r["atime"] = BIGINT(file_stat.st_atime); - r["mtime"] = BIGINT(file_stat.st_mtime); - r["ctime"] = BIGINT(file_stat.st_ctime); - - // Type booleans - r["is_file"] = (!S_ISDIR(file_stat.st_mode)) ? "1" : "0"; - r["is_dir"] = (S_ISDIR(file_stat.st_mode)) ? "1" : "0"; - r["is_link"] = (S_ISLNK(link_stat.st_mode)) ? "1" : "0"; - r["is_char"] = (S_ISCHR(file_stat.st_mode)) ? "1" : "0"; - r["is_block"] = (S_ISBLK(file_stat.st_mode)) ? "1" : "0"; - - // pattern - r["pattern"] = pattern; - - results.push_back(r); -} - -QueryData genFile(QueryContext& context) { - QueryData results; - - auto paths = context.constraints["path"].getAll(EQUALS); - for (const auto& path_string : paths) { - if (!isReadable(path_string)) { - continue; - } - - fs::path path = path_string; - genFileInfo(path_string, - path.filename().string(), - path.parent_path().string(), - "", - results); - } - - // Now loop through constraints using the directory column constraint. - auto directories = context.constraints["directory"].getAll(EQUALS); - for (const auto& directory_string : directories) { - if (!isReadable(directory_string) || !isDirectory(directory_string)) { - continue; - } - - try { - // Iterate over the directory and generate info for each regular file. - fs::directory_iterator begin(directory_string), end; - for (; begin != end; ++begin) { - genFileInfo(begin->path().string(), - begin->path().filename().string(), - directory_string, - "", - results); - } - } catch (const fs::filesystem_error& e) { - continue; - } - } - - // Now loop through constraints using the pattern column constraint. - auto patterns = context.constraints["pattern"].getAll(EQUALS); - if (patterns.size() != 1) { - return results; - } - - for (const auto& pattern : patterns) { - std::vector expanded_patterns; - auto status = resolveFilePattern(pattern, expanded_patterns); - if (!status.ok()) { - VLOG(1) << "Could not expand pattern properly: " << status.toString(); - return results; - } - - for (const auto& resolved : expanded_patterns) { - if (!isReadable(resolved)) { - continue; - } - fs::path path = resolved; - genFileInfo(resolved, - path.filename().string(), - path.parent_path().string(), - pattern, - results); - - } - } - - return results; -} -} -} diff --git a/osquery/tables/utility/hash.cpp b/osquery/tables/utility/hash.cpp deleted file mode 100644 index 5baf307..0000000 --- a/osquery/tables/utility/hash.cpp +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include - -#include -#include -#include - -namespace fs = boost::filesystem; - -namespace osquery { -namespace tables { - -void genHashForFile(const std::string& path, - const std::string& dir, - QueryData& results) { - // Must provide the path, filename, directory separate from boost path->string - // helpers to match any explicit (query-parsed) predicate constraints. - Row r; - r["path"] = path; - r["directory"] = dir; - r["md5"] = osquery::hashFromFile(HASH_TYPE_MD5, path); - r["sha1"] = osquery::hashFromFile(HASH_TYPE_SHA1, path); - r["sha256"] = osquery::hashFromFile(HASH_TYPE_SHA256, path); - results.push_back(r); -} - -QueryData genHash(QueryContext& context) { - QueryData results; - - // The query must provide a predicate with constraints including path or - // directory. We search for the parsed predicate constraints with the equals - // operator. - auto paths = context.constraints["path"].getAll(EQUALS); - for (const auto& path_string : paths) { - boost::filesystem::path path = path_string; - if (!boost::filesystem::is_regular_file(path)) { - continue; - } - - genHashForFile(path_string, path.parent_path().string(), results); - } - - // Now loop through constraints using the directory column constraint. - auto directories = context.constraints["directory"].getAll(EQUALS); - for (const auto& directory_string : directories) { - boost::filesystem::path directory = directory_string; - if (!boost::filesystem::is_directory(directory)) { - continue; - } - - // Iterate over the directory and generate a hash for each regular file. - boost::filesystem::directory_iterator begin(directory), end; - for (; begin != end; ++begin) { - if (boost::filesystem::is_regular_file(begin->status())) { - genHashForFile(begin->path().string(), directory_string, results); - } - } - } - - return results; -} -} -} diff --git a/osquery/tables/utility/osquery.cpp b/osquery/tables/utility/osquery.cpp deleted file mode 100644 index 46eb14d..0000000 --- a/osquery/tables/utility/osquery.cpp +++ /dev/null @@ -1,232 +0,0 @@ -/* - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace osquery { -namespace tables { - -typedef pt::ptree::value_type tree_node; - -void genQueryPack(const tree_node& pack, QueryData& results) { - Row r; - // Packs are stored by name and contain configuration data. - r["name"] = pack.first; - r["path"] = pack.second.get("path", ""); - - // There are optional restrictions on the set of queries applied pack-wide. - auto pack_wide_version = pack.second.get("version", ""); - auto pack_wide_platform = pack.second.get("platform", ""); - - // Iterate through each query in the pack. - for (auto const& query : pack.second.get_child("queries")) { - r["query_name"] = query.first; - r["query"] = query.second.get("query", ""); - r["interval"] = INTEGER(query.second.get("interval", 0)); - r["description"] = query.second.get("description", ""); - r["value"] = query.second.get("value", ""); - - // Set the version requirement based on the query-specific or pack-wide. - if (query.second.count("version") > 0) { - r["version"] = query.second.get("version", ""); - } else { - r["version"] = pack_wide_platform; - } - - // Set the platform requirement based on the query-specific or pack-wide. - if (query.second.count("platform") > 0) { - r["platform"] = query.second.get("platform", ""); - } else { - r["platform"] = pack_wide_platform; - } - - // Adding a prefix to the pack queries to differentiate packs from schedule. - r["scheduled_name"] = "pack_" + r.at("name") + "_" + r.at("query_name"); - if (Config::checkScheduledQueryName(r.at("scheduled_name"))) { - r["scheduled"] = INTEGER(1); - } else { - r["scheduled"] = INTEGER(0); - } - - results.push_back(r); - } -} - -QueryData genOsqueryPacks(QueryContext& context) { - QueryData results; - - // Get a lock on the config instance. - ConfigDataInstance config; - - // Get the loaded data tree from global JSON configuration. - const auto& packs_parsed_data = config.getParsedData("packs"); - - // Iterate through all the packs to get each configuration and set of queries. - for (auto const& pack : packs_parsed_data) { - // Make sure the pack data contains queries. - if (pack.second.count("queries") == 0) { - continue; - } - genQueryPack(pack, results); - } - - return results; -} - -void genFlag(const std::string& name, - const FlagInfo& flag, - QueryData& results) { - Row r; - r["name"] = name; - r["type"] = flag.type; - r["description"] = flag.description; - r["default_value"] = flag.default_value; - r["value"] = flag.value; - r["shell_only"] = (flag.detail.shell) ? "1" : "0"; - results.push_back(r); -} - -QueryData genOsqueryFlags(QueryContext& context) { - QueryData results; - - auto flags = Flag::flags(); - for (const auto& flag : flags) { - if (flag.first.size() > 2) { - // Skip single-character flags. - genFlag(flag.first, flag.second, results); - } - } - - return results; -} - -QueryData genOsqueryRegistry(QueryContext& context) { - QueryData results; - - const auto& registries = RegistryFactory::all(); - for (const auto& registry : registries) { - const auto& plugins = registry.second->all(); - for (const auto& plugin : plugins) { - Row r; - r["registry"] = registry.first; - r["name"] = plugin.first; - r["owner_uuid"] = "0"; - r["internal"] = (registry.second->isInternal(plugin.first)) ? "1" : "0"; - r["active"] = "1"; - results.push_back(r); - } - - for (const auto& route : registry.second->getExternal()) { - Row r; - r["registry"] = registry.first; - r["name"] = route.first; - r["owner_uuid"] = INTEGER(route.second); - r["internal"] = "0"; - r["active"] = "1"; - results.push_back(r); - } - } - - return results; -} - -QueryData genOsqueryExtensions(QueryContext& context) { - QueryData results; - - ExtensionList extensions; - if (getExtensions(extensions).ok()) { - for (const auto& extenion : extensions) { - Row r; - r["uuid"] = TEXT(extenion.first); - r["name"] = extenion.second.name; - r["version"] = extenion.second.version; - r["sdk_version"] = extenion.second.sdk_version; - r["path"] = getExtensionSocket(extenion.first); - r["type"] = "extension"; - results.push_back(r); - } - } - - const auto& modules = RegistryFactory::getModules(); - for (const auto& module : modules) { - Row r; - r["uuid"] = TEXT(module.first); - r["name"] = module.second.name; - r["version"] = module.second.version; - r["sdk_version"] = module.second.sdk_version; - r["path"] = module.second.path; - r["type"] = "module"; - results.push_back(r); - } - - return results; -} - -QueryData genOsqueryInfo(QueryContext& context) { - QueryData results; - - Row r; - r["pid"] = INTEGER(getpid()); - r["version"] = kVersion; - - std::string hash_string; - auto s = Config::getMD5(hash_string); - if (s.ok()) { - r["config_md5"] = TEXT(hash_string); - } else { - r["config_md5"] = ""; - VLOG(1) << "Could not retrieve config hash: " << s.toString(); - } - - r["config_path"] = Flag::getValue("config_path"); - r["extensions"] = - (pingExtension(FLAGS_extensions_socket).ok()) ? "active" : "inactive"; - - r["build_platform"] = STR(OSQUERY_BUILD_PLATFORM); - r["build_distro"] = STR(OSQUERY_BUILD_DISTRO); - - results.push_back(r); - - return results; -} - -QueryData genOsquerySchedule(QueryContext& context) { - QueryData results; - - ConfigDataInstance config; - for (const auto& query : config.schedule()) { - Row r; - r["name"] = TEXT(query.first); - r["query"] = TEXT(query.second.query); - r["interval"] = INTEGER(query.second.interval); - - // Report optional performance information. - r["executions"] = BIGINT(query.second.executions); - r["output_size"] = BIGINT(query.second.output_size); - r["wall_time"] = BIGINT(query.second.wall_time); - r["user_time"] = BIGINT(query.second.user_time); - r["system_time"] = BIGINT(query.second.system_time); - r["average_memory"] = BIGINT(query.second.average_memory); - results.push_back(r); - } - - return results; -} - -} -} diff --git a/osquery/tables/utility/time.cpp b/osquery/tables/utility/time.cpp deleted file mode 100644 index 1b68eff..0000000 --- a/osquery/tables/utility/time.cpp +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include -#include - -#include - -namespace osquery { -namespace tables { - -QueryData genTime(QueryContext& context) { - Row r; - time_t _time = time(0); - struct tm* now = localtime(&_time); - struct tm* gmt = gmtime(&_time); - - char weekday[10] = {0}; - strftime(weekday, sizeof(weekday), "%A", now); - - std::string timestamp; - timestamp = asctime(gmt); - boost::algorithm::trim(timestamp); - timestamp += " UTC"; - - char iso_8601[21] = {0}; - strftime(iso_8601, sizeof(iso_8601), "%FT%TZ", gmt); - - r["weekday"] = TEXT(weekday); - r["year"] = INTEGER(now->tm_year + 1900); - r["month"] = INTEGER(now->tm_mon + 1); - r["day"] = INTEGER(now->tm_mday); - r["hour"] = INTEGER(now->tm_hour); - r["minutes"] = INTEGER(now->tm_min); - r["seconds"] = INTEGER(now->tm_sec); - r["unix_time"] = INTEGER(_time); - r["timestamp"] = TEXT(timestamp); - r["iso_8601"] = TEXT(iso_8601); - - QueryData results; - results.push_back(r); - return results; -} -} -} diff --git a/osquery/tizen/CMakeLists.txt b/osquery/tizen/CMakeLists.txt deleted file mode 100644 index 669f1bb..0000000 --- a/osquery/tizen/CMakeLists.txt +++ /dev/null @@ -1,31 +0,0 @@ -# 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_tizen property/property.cpp - manager/manager.cpp - manager/manager_impl.cpp - notification/notification.cpp) - -ADD_OSQUERY_TEST(${OSQUERY_TIZEN_TESTS}) - -IF(DEFINED GBS_BUILD) - # tables - FILE(GLOB TIZEN_TABLES "tables/*.cpp") - ADD_OSQUERY_LIBRARY(tizen_tables ${TIZEN_TABLES}) - FILE(GLOB OSQUERY_TIZEN_TESTS "[!d]*/tests/*.cpp") - -# Verification can be done with full-DPM -# FILE(GLOB OSQUERY_GBS_TESTS "device_policy/tests/*.cpp") -# ADD_OSQUERY_TEST(${OSQUERY_GBS_TESTS}) -ENDIF(DEFINED GBS_BUILD) diff --git a/osquery/tizen/manager/manager.cpp b/osquery/tizen/manager/manager.cpp deleted file mode 100644 index 2ff4f97..0000000 --- a/osquery/tizen/manager/manager.cpp +++ /dev/null @@ -1,48 +0,0 @@ -/* - * 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 - */ -/* - * @file manager.cpp - * @author Sangwan Kwon (sangwan.kwon@samsung.com) - * @brief Implementation of osquery manager - */ - -#include - -#include "manager_impl.h" - -namespace osquery { - -Rows OsqueryManager::execute(const std::string& query) -{ - return ManagerImpl::instance().execute(query); -} - -void OsqueryManager::subscribe(const std::string& table, const Callback& callback) -{ - return ManagerImpl::instance().subscribe(table, callback); -} - -std::vector OsqueryManager::tables(void) noexcept -{ - return ManagerImpl::instance().tables(); -} - -std::vector OsqueryManager::columns(const std::string& table) noexcept -{ - return ManagerImpl::instance().columns(table); -} - -} // namespace osquery diff --git a/osquery/tizen/manager/manager_impl.cpp b/osquery/tizen/manager/manager_impl.cpp deleted file mode 100644 index 2e80e0a..0000000 --- a/osquery/tizen/manager/manager_impl.cpp +++ /dev/null @@ -1,103 +0,0 @@ -/* - * 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 ManagerImplied. - * See the License for the specific language governing permissions and - * limitations under the License - */ -/* - * @file manager_impl.cpp - * @author Sangwan Kwon (sangwan.kwon@samsung.com) - * @brief Implementation of osquery manager's impl - */ - -#include "manager_impl.h" - -#include -#include -#include -#include -#include -#include -#include - -#include - -#include - -#include -#include - -namespace osquery { - -ManagerImpl::ManagerImpl() -{ - auto logDir = Flag::getValue("osquery_log_dir"); - if (!logDir.empty() && !(pathExists(logDir).ok())) - boost::filesystem::create_directories(logDir); - - LOG(INFO) << "Initalize osquery manager. "; -} - -ManagerImpl::~ManagerImpl() noexcept -{ - LOG(INFO) << "Shutdown osquery manager."; -} - -ManagerImpl& ManagerImpl::instance() -{ - static ManagerImpl instance; - return instance; -} - -Rows ManagerImpl::execute(const std::string& query) -{ - LOG(INFO) << "Execute query: " << query; - - osquery::QueryData results; - auto status = osquery::query(query, results); - if (!status.ok()) - LOG(ERROR) << "Executing query failed: " << status.getCode(); - - return results; -} - -void ManagerImpl::subscribe(const std::string& table, const Callback& callback) -{ - LOG(INFO) << "Subscribe event: " << table; - - auto status = Notification::instance().add(table, callback); - if (!status.ok()) - LOG(ERROR) << "Subscribing event failed: " << status.getCode(); -} - -std::vector ManagerImpl::tables(void) noexcept -{ - return SQL::getTableNames(); -} - -std::vector ManagerImpl::columns(const std::string& table) noexcept -{ - std::stringstream query; - query << "SELECT * FROM " << table; - - TableColumns columns; - getQueryColumns(query.str(), columns); - - // Extract column names - std::vector names; - for (auto& c : columns) - names.emplace_back(std::move(c.first)); - - return names; -} - -} // namespace osquery diff --git a/osquery/tizen/manager/manager_impl.h b/osquery/tizen/manager/manager_impl.h deleted file mode 100644 index dcfe2cb..0000000 --- a/osquery/tizen/manager/manager_impl.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - * 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 ManagerImplied. - * See the License for the specific language governing permissions and - * limitations under the License - */ -/* - * @file manager_impl.h - * @author Sangwan Kwon (sangwan.kwon@samsung.com) - * @brief Implementation interface of osquery manager - */ - -#pragma once - -#include - -#include -#include - -namespace osquery { - -/// Singleton class -class ManagerImpl final { -public: - ManagerImpl(const ManagerImpl&) = delete; - ManagerImpl& operator=(const ManagerImpl&) = delete; - - ManagerImpl(ManagerImpl&&) noexcept = default; - ManagerImpl& operator=(ManagerImpl&&) noexcept = default; - - static ManagerImpl& instance(); - - Rows execute(const std::string& query); - void subscribe(const std::string& table, const Callback& callback); - - std::vector tables(void) noexcept; - std::vector columns(const std::string& table) noexcept; - -private: - ManagerImpl(); - ~ManagerImpl() noexcept; -}; - -} // namespace osquery diff --git a/osquery/tizen/manager/tests/manager_tests.cpp b/osquery/tizen/manager/tests/manager_tests.cpp deleted file mode 100644 index 048cfcc..0000000 --- a/osquery/tizen/manager/tests/manager_tests.cpp +++ /dev/null @@ -1,78 +0,0 @@ -/* - * 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 - */ - -#include - -#include - -#include -#include - -using namespace osquery; - -class ManagerTests : public testing::Test {}; - -TEST_F(ManagerTests, test_manager_execute) { - std::string query = "SELECT * FROM time"; - auto rows = OsqueryManager::execute(query); - EXPECT_EQ(rows.size(), 1); - - VLOG(1) << "[Test] time table rows:"; - VLOG(1) << "\t hour: " << rows[0]["hour"]; - VLOG(1) << "\t minutes: " << rows[0]["minutes"]; - VLOG(1) << "\t seconds: " << rows[0]["seconds"]; -} - -TEST_F(ManagerTests, test_manager_subscribe) { - int called = 0; - auto callback = [&](const Row& row) { - VLOG(1) << "NotifyCallback called:"; - for (const auto& r : row) - VLOG(1) << "\t" << r.first << " : " << r.second; - called++; - }; - - OsqueryManager::subscribe("manager_test", callback); - - Row row; - row["foo"] = "bar"; - row["foo2"] = "bar2"; - row["foo3"] = "bar3"; - - /// Notification trigger - auto s = Notification::instance().emit("manager_test", row); - - EXPECT_TRUE(s.ok()); - EXPECT_EQ(called, 1); -} - -TEST_F(ManagerTests, test_manager_tables) { - auto tables = OsqueryManager::tables(); - EXPECT_TRUE(tables.size() > 0); - - VLOG(1) << "[Test] Enabled tables:"; - for (const auto& t : tables) - VLOG(1) << "\t" << t; -} - -TEST_F(ManagerTests, test_manager_columns) { - auto columns = OsqueryManager::columns("time"); - EXPECT_TRUE(columns.size() > 0); - - VLOG(1) << "[Test] Enabled columns of time table:"; - for (const auto& c : columns) - VLOG(1) << "\t" << c; -} diff --git a/osquery/tizen/notification/notification.cpp b/osquery/tizen/notification/notification.cpp deleted file mode 100644 index 9db0272..0000000 --- a/osquery/tizen/notification/notification.cpp +++ /dev/null @@ -1,75 +0,0 @@ -/* - * 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 - */ -/* - * @file notification.cpp - * @author Sangwan Kwon (sangwan.kwon@samsung.com) - * @brief Implementation of notification - */ - -#include - -#include -#include - -namespace { - std::mutex mutex; -} // anonymous namespace - -namespace osquery { - -Notification& Notification::instance() -{ - static Notification notifier; - return notifier; -} - -Status Notification::add(const std::string& table, const NotifyCallback& callback) -{ - if (table.empty()) - return Status(1, "Wrong table name"); - - LOG(INFO) << "Add NotifyCallback to:" << table; - { - std::lock_guard lock(mutex); - this->callbacks.insert(std::make_pair(table, callback)); - } - - return Status(0, "OK"); -} - -Status Notification::emit(const std::string& table, const Row& result) const -{ - if (table.empty()) - return Status(1, "Wrong table name"); - - auto iter = this->callbacks.find(table); - if (iter == this->callbacks.end()) - return Status(1, "Registered callback not found"); - - LOG(INFO) << "Emit notification about:" << table; - { - std::lock_guard lock(mutex); - while (iter != this->callbacks.end()) { - const auto& callback = iter->second; - callback(result); - iter++; - } - } - - return Status(0, "OK"); -} - -} // namespace osquery diff --git a/osquery/tizen/notification/tests/notification_tests.cpp b/osquery/tizen/notification/tests/notification_tests.cpp deleted file mode 100644 index f26bfa3..0000000 --- a/osquery/tizen/notification/tests/notification_tests.cpp +++ /dev/null @@ -1,72 +0,0 @@ -/* - * 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 - */ - -#include - -#include -#include - -using namespace osquery; - -class NotificationTests : public testing::Test {}; - -TEST_F(NotificationTests, test_add_positive) { - auto& notifier = Notification::instance(); - - auto callback = [](const Row& row) { - VLOG(1) << "NotifyCallback called:"; - for (const auto& r : row) - VLOG(1) << "\t" << r.first << " : " << r.second; - }; - - auto s = notifier.add("test", std::move(callback)); - EXPECT_TRUE(s.ok()); -} - -TEST_F(NotificationTests, test_add_negative) { - auto& notifier = Notification::instance(); - - auto callback = [](const Row& row) { - VLOG(1) << "NotifyCallback called:"; - for (const auto& r : row) - VLOG(1) << "\t" << r.first << " : " << r.second; - }; - - auto s = notifier.add("", std::move(callback)); - EXPECT_FALSE(s.ok()); -} - -TEST_F(NotificationTests, test_emit_positive) { - auto& notifier = Notification::instance(); - - int called = 0; - auto callback = [&](const Row& row) { - VLOG(1) << "NotifyCallback called:"; - for (const auto& r : row) - VLOG(1) << "\t" << r.first << " : " << r.second; - called++; - }; - - auto s = notifier.add("test2", std::move(callback)); - EXPECT_TRUE(s.ok()); - - Row row; - row["foo"] = "bar"; - s = notifier.emit("test2", row); - - EXPECT_TRUE(s.ok()); - EXPECT_EQ(called, 1); -} diff --git a/osquery/tizen/property/property.cpp b/osquery/tizen/property/property.cpp deleted file mode 100644 index 36581ef..0000000 --- a/osquery/tizen/property/property.cpp +++ /dev/null @@ -1,184 +0,0 @@ -/* - * 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 - */ -/* - * @file property.cpp - * @author Sangwan Kwon (sangwan.kwon@samsung.com) - * @brief Implementation of Property - */ - -#include -#include - -#include -#include -#include -#include -#include - -#include - -#include - -#include - -namespace { - -using namespace tsqb; -auto time = make_table("time", - make_column("hour", &Time::hour), - make_column("minutes", &Time::minutes), - make_column("seconds", &Time::seconds)); - -auto processes = make_table("processes", - make_column("pid", &Processes::pid), - make_column("name", &Processes::name), - make_column("path", &Processes::path), - make_column("cmdline", &Processes::cmdline), - make_column("uid", &Processes::uid), - make_column("gid", &Processes::gid), - make_column("euid", &Processes::euid), - make_column("egid", &Processes::egid), - make_column("on_disk", &Processes::on_disk), -// make_column("wired_size", &Processes::wired_size), - make_column("resident_size", &Processes::resident_size), - make_column("phys_footprint", &Processes::phys_footprint), - make_column("user_time", &Processes::user_time), - make_column("system_time", &Processes::system_time), - make_column("start_time", &Processes::start_time), - make_column("parent", &Processes::parent)); - -auto users = make_table("users", - make_column("uid", &Users::uid), - make_column("gid", &Users::gid), - make_column("uid_signed", &Users::uid_signed), - make_column("gid_signed", &Users::gid_signed), - make_column("username", &Users::username), - make_column("description", &Users::description), - make_column("directory", &Users::directory), - make_column("shell", &Users::shell)); - -auto groups = make_table("groups", - make_column("gid", &Groups::gid), - make_column("gid_signed", &Groups::gid_signed), - make_column("groupname", &Groups::groupname)); - -auto memoryMap = make_table("memory_map", - make_column("region", &MemoryMap::region), - make_column("type", &MemoryMap::type), - make_column("start", &MemoryMap::start), - make_column("end", &MemoryMap::end)); - -auto db = make_database("db", time, processes, users, groups, memoryMap); - -} // anonymous namespace - -namespace osquery { - -template -Property::Property() -{ - auto results = OsqueryManager::execute(db.selectAll()); - if (results.size() > 0) - this->data = std::move(results[0]); -} - -template -Property::Property(KeyValuePair&& kvp) : data(std::move(kvp)) -{ -} - -template -template -Member Property::at(Member Struct::* field) const -{ - if (this->data.size() == 0) - throw std::runtime_error("Data is not exist."); - - std::string key = db.getColumnName(field); - if (key.empty()) - throw std::runtime_error("Key is not exist."); - - /// Convert "table.column" to "column" - std::size_t pos = key.find("."); - if (pos != std::string::npos && pos != key.size() - 1) - key = key.substr(pos + 1); - - std::string value = this->data.at(key); - if (value.empty()) { - LOG(ERROR) << "Key: " << key << "is not exist."; - return Member(); - } else { - /// TODO(Sangwan): Catch boost::bad_lexical_cast - return boost::lexical_cast(value); - } -} - -template -template -Member Property::operator[](Member Struct::*field) const -{ - return this->at(field); -} - -template -Properties::Properties() -{ - auto results = OsqueryManager::execute(db.selectAll()); - for (auto& r : results) - this->datas.emplace_back(Property(std::move(r))); -} - -/// Explicit instantiation -template class Property