From 6047e7d6f86e4b0916c18216b0d974c5b63f9b13 Mon Sep 17 00:00:00 2001 From: Max Kareta Date: Thu, 24 May 2018 12:26:43 +0100 Subject: [PATCH] Added custom path functions (#4265) --- osquery/sql/CMakeLists.txt | 1 + osquery/sql/sqlite_encoding.cpp | 8 +- osquery/sql/sqlite_filesystem.cpp | 164 ++++++++++++++++++++++++++++++ osquery/sql/sqlite_hashing.cpp | 24 +++-- osquery/sql/sqlite_math.cpp | 62 +++++------ osquery/sql/sqlite_operations.cpp | 2 +- osquery/sql/sqlite_string.cpp | 8 +- osquery/sql/sqlite_util.cpp | 3 +- osquery/sql/sqlite_util.h | 7 +- 9 files changed, 231 insertions(+), 48 deletions(-) create mode 100644 osquery/sql/sqlite_filesystem.cpp diff --git a/osquery/sql/CMakeLists.txt b/osquery/sql/CMakeLists.txt index d0621522..b68c998c 100644 --- a/osquery/sql/CMakeLists.txt +++ b/osquery/sql/CMakeLists.txt @@ -8,6 +8,7 @@ set(OSQUERY_SQL_INTERNAL "sqlite_hashing.cpp" "sqlite_encoding.cpp" "virtual_table.cpp" + "sqlite_filesystem.cpp" "virtual_sqlite_table.cpp" ) diff --git a/osquery/sql/sqlite_encoding.cpp b/osquery/sql/sqlite_encoding.cpp index a690b6b7..e3b6ed33 100644 --- a/osquery/sql/sqlite_encoding.cpp +++ b/osquery/sql/sqlite_encoding.cpp @@ -75,7 +75,7 @@ void registerEncodingExtensions(sqlite3* db) { sqlite3_create_function(db, "conditional_to_base64", 1, - SQLITE_UTF8, + SQLITE_UTF8 | SQLITE_DETERMINISTIC, nullptr, sqliteB64ConditionalEncFunc, nullptr, @@ -83,7 +83,7 @@ void registerEncodingExtensions(sqlite3* db) { sqlite3_create_function(db, "to_base64", 1, - SQLITE_UTF8, + SQLITE_UTF8 | SQLITE_DETERMINISTIC, nullptr, sqliteB64EncFunc, nullptr, @@ -91,10 +91,10 @@ void registerEncodingExtensions(sqlite3* db) { sqlite3_create_function(db, "from_base64", 1, - SQLITE_UTF8, + SQLITE_UTF8 | SQLITE_DETERMINISTIC, nullptr, sqliteB64DecFunc, nullptr, nullptr); } -} +} // namespace osquery diff --git a/osquery/sql/sqlite_filesystem.cpp b/osquery/sql/sqlite_filesystem.cpp new file mode 100644 index 00000000..92a3f26e --- /dev/null +++ b/osquery/sql/sqlite_filesystem.cpp @@ -0,0 +1,164 @@ +/** + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under both the Apache 2.0 license (found in the + * LICENSE file in the root directory of this source tree) and the GPLv2 (found + * in the COPYING file in the root directory of this source tree). + * You may select, at your option, one of the above-listed licenses. + */ + +#include +#include + +#include + +namespace osquery { + +static boost::optional findExistingProgramPathFromCommand( + const char* path, char escape_symbol, bool allow_quoting, bool shortest) { + size_t length = strlen(path); + std::string result; + size_t pos = 0; + // Skip spaces + for (; pos < length; ++pos) { + if (!isspace(path[pos])) { + break; + } + } + std::string temp_string; + bool is_quoted = false; + bool is_escaped = false; + for (; pos < length; ++pos) { + if (is_escaped) { + temp_string += path[pos]; + is_escaped = false; + continue; + } + if (allow_quoting && path[pos] == '"') { + is_quoted = !is_quoted; + continue; + } + if (path[pos] == escape_symbol) { + is_escaped = true; + continue; + } + if (!is_quoted && isspace(path[pos])) { + // validate temp string + boost::filesystem::path test_path = temp_string; + auto status = boost::filesystem::status(test_path); + if (boost::filesystem::exists(status) && + !boost::filesystem::is_directory(status)) { + result = temp_string; + if (shortest) { + break; + } + } + } + temp_string += path[pos]; + } + if (result.length() == 0 || !shortest) { + boost::filesystem::path test_path = temp_string; + auto status = boost::filesystem::status(test_path); + if (boost::filesystem::exists(status) && + !boost::filesystem::is_directory(status)) { + result = temp_string; + } + } + return result; +} + +static boost::optional findExistingProgramPathFromCommandSqlArgs( + int argc, sqlite3_value** argv, bool shortest) { + if (argc == 0) { + return boost::none; + } + // NULLs are not allowed + for (int i = 0; i < argc; i++) { + if (SQLITE_NULL == sqlite3_value_type(argv[i])) { + return boost::none; + } + } + const char* path = reinterpret_cast(sqlite3_value_text(argv[0])); + bool allow_quoting = false; + if (argc > 1) { + allow_quoting = sqlite3_value_int(argv[1]) != 0 ? true : false; + } +#ifdef WIN32 + char escape_symbol = '^'; +#else + char escape_symbol = '\\'; +#endif + if (argc > 2) { + const char* escape_symbol_string = + reinterpret_cast(sqlite3_value_text(argv[2])); + if (escape_symbol_string == NULL || + std::strlen(escape_symbol_string) != 1) { + return boost::none; + } + escape_symbol = escape_symbol_string[0]; + } + return findExistingProgramPathFromCommand( + path, escape_symbol, allow_quoting, shortest); +} + +static void findFilePathInLaunchCommand(sqlite3_context* context, + int argc, + sqlite3_value** argv) { + auto result = findExistingProgramPathFromCommandSqlArgs(argc, argv, true); + if (result) { + sqlite3_result_text(context, + result->c_str(), + static_cast(result->size()), + SQLITE_TRANSIENT); + } else { + sqlite3_result_error( + context, "Invalid inputs to find_binary_path_from_cmd", -1); + } +} + +static void isPathDeterministic(sqlite3_context* context, + int argc, + sqlite3_value** argv) { + auto shortest = findExistingProgramPathFromCommandSqlArgs(argc, argv, true); + if (shortest) { + const char* path = (const char*)sqlite3_value_text(argv[0]); + if (shortest->length() == 0 || shortest->length() == strlen(path)) { + // There are 2 cases: + // 1 - empty string, all parts of path are invalid, + // so path is deterministic + // 2 - short == full, then there is only 1 valid path + sqlite3_result_int(context, 1); + return; + } else { + auto longest = + findExistingProgramPathFromCommandSqlArgs(argc, argv, false); + if (longest) { + sqlite3_result_int(context, + shortest->length() == longest->length() ? 1 : 0); + return; + } + } + } + sqlite3_result_error(context, "Invalid inputs to is_path_deterministic", -1); +} + +void registerFilesystemExtensions(sqlite3* db) { + sqlite3_create_function(db, + "is_path_deterministic", + -1, + SQLITE_UTF8 | SQLITE_DETERMINISTIC, + nullptr, + isPathDeterministic, + nullptr, + nullptr); + sqlite3_create_function(db, + "find_file_path_in_cmd", + -1, + SQLITE_UTF8 | SQLITE_DETERMINISTIC, + nullptr, + findFilePathInLaunchCommand, + nullptr, + nullptr); +} +} // namespace osquery diff --git a/osquery/sql/sqlite_hashing.cpp b/osquery/sql/sqlite_hashing.cpp index 14f74d24..47e2b113 100644 --- a/osquery/sql/sqlite_hashing.cpp +++ b/osquery/sql/sqlite_hashing.cpp @@ -58,17 +58,29 @@ static void sqliteSHA256Func(sqlite3_context* context, } void registerHashingExtensions(sqlite3* db) { - sqlite3_create_function( - db, "md5", 1, SQLITE_UTF8, nullptr, sqliteMD5Func, nullptr, nullptr); - sqlite3_create_function( - db, "sha1", 1, SQLITE_UTF8, nullptr, sqliteSHA1Func, nullptr, nullptr); + sqlite3_create_function(db, + "md5", + 1, + SQLITE_UTF8 | SQLITE_DETERMINISTIC, + nullptr, + sqliteMD5Func, + nullptr, + nullptr); + sqlite3_create_function(db, + "sha1", + 1, + SQLITE_UTF8 | SQLITE_DETERMINISTIC, + nullptr, + sqliteSHA1Func, + nullptr, + nullptr); sqlite3_create_function(db, "sha256", 1, - SQLITE_UTF8, + SQLITE_UTF8 | SQLITE_DETERMINISTIC, nullptr, sqliteSHA256Func, nullptr, nullptr); } -} +} // namespace osquery diff --git a/osquery/sql/sqlite_math.cpp b/osquery/sql/sqlite_math.cpp index f398673b..dcff4610 100644 --- a/osquery/sql/sqlite_math.cpp +++ b/osquery/sql/sqlite_math.cpp @@ -52,9 +52,9 @@ using DoubleDoubleFunction = std::function; /** * @brief Call a math function that takes a double and returns a double. */ -static void callDoubleFunc(sqlite3_context *context, +static void callDoubleFunc(sqlite3_context* context, int argc, - sqlite3_value **argv, + sqlite3_value** argv, DoubleDoubleFunction f) { double rVal = 0.0, val; assert(argc == 1); @@ -75,27 +75,27 @@ static void callDoubleFunc(sqlite3_context *context, } } -static void sinFunc(sqlite3_context *context, int argc, sqlite3_value **argv) { +static void sinFunc(sqlite3_context* context, int argc, sqlite3_value** argv) { callDoubleFunc(context, argc, argv, SIN_FUNC); } -static void cosFunc(sqlite3_context *context, int argc, sqlite3_value **argv) { +static void cosFunc(sqlite3_context* context, int argc, sqlite3_value** argv) { callDoubleFunc(context, argc, argv, COS_FUNC); } -static void tanFunc(sqlite3_context *context, int argc, sqlite3_value **argv) { +static void tanFunc(sqlite3_context* context, int argc, sqlite3_value** argv) { callDoubleFunc(context, argc, argv, TAN_FUNC); } -static void asinFunc(sqlite3_context *context, int argc, sqlite3_value **argv) { +static void asinFunc(sqlite3_context* context, int argc, sqlite3_value** argv) { callDoubleFunc(context, argc, argv, ASIN_FUNC); } -static void acosFunc(sqlite3_context *context, int argc, sqlite3_value **argv) { +static void acosFunc(sqlite3_context* context, int argc, sqlite3_value** argv) { callDoubleFunc(context, argc, argv, ACOS_FUNC); } -static void atanFunc(sqlite3_context *context, int argc, sqlite3_value **argv) { +static void atanFunc(sqlite3_context* context, int argc, sqlite3_value** argv) { callDoubleFunc(context, argc, argv, ATAN_FUNC); } @@ -103,31 +103,31 @@ static double cot(double x) { return 1.0 / tan(x); } -static void cotFunc(sqlite3_context *context, int argc, sqlite3_value **argv) { +static void cotFunc(sqlite3_context* context, int argc, sqlite3_value** argv) { callDoubleFunc(context, argc, argv, cot); } -static void logFunc(sqlite3_context *context, int argc, sqlite3_value **argv) { +static void logFunc(sqlite3_context* context, int argc, sqlite3_value** argv) { callDoubleFunc(context, argc, argv, LOG_FUNC); } -static void log10Func(sqlite3_context *context, +static void log10Func(sqlite3_context* context, int argc, - sqlite3_value **argv) { + sqlite3_value** argv) { callDoubleFunc(context, argc, argv, LOG10_FUNC); } -static void sqrtFunc(sqlite3_context *context, int argc, sqlite3_value **argv) { +static void sqrtFunc(sqlite3_context* context, int argc, sqlite3_value** argv) { callDoubleFunc(context, argc, argv, SQRT_FUNC); } -static void expFunc(sqlite3_context *context, int argc, sqlite3_value **argv) { +static void expFunc(sqlite3_context* context, int argc, sqlite3_value** argv) { callDoubleFunc(context, argc, argv, EXP_FUNC); } -static void powerFunc(sqlite3_context *context, +static void powerFunc(sqlite3_context* context, int argc, - sqlite3_value **argv) { + sqlite3_value** argv) { assert(argc == 2); if (sqlite3_value_type(argv[0]) == SQLITE_NULL || @@ -146,9 +146,9 @@ static void powerFunc(sqlite3_context *context, } } -static void callCastedDoubleFunc(sqlite3_context *context, +static void callCastedDoubleFunc(sqlite3_context* context, int argc, - sqlite3_value **argv, + sqlite3_value** argv, DoubleDoubleFunction f) { double rVal = 0.0; assert(argc == 1); @@ -168,13 +168,13 @@ static void callCastedDoubleFunc(sqlite3_context *context, } } -static void ceilFunc(sqlite3_context *context, int argc, sqlite3_value **argv) { +static void ceilFunc(sqlite3_context* context, int argc, sqlite3_value** argv) { callCastedDoubleFunc(context, argc, argv, CEIL_FUNC); } -static void floorFunc(sqlite3_context *context, +static void floorFunc(sqlite3_context* context, int argc, - sqlite3_value **argv) { + sqlite3_value** argv) { callCastedDoubleFunc(context, argc, argv, FLOOR_FUNC); } @@ -188,29 +188,29 @@ static double rad2deg(double x) { return 180.0 * x / M_PI; } -static void rad2degFunc(sqlite3_context *context, +static void rad2degFunc(sqlite3_context* context, int argc, - sqlite3_value **argv) { + sqlite3_value** argv) { callDoubleFunc(context, argc, argv, rad2deg); } -static void deg2radFunc(sqlite3_context *context, +static void deg2radFunc(sqlite3_context* context, int argc, - sqlite3_value **argv) { + sqlite3_value** argv) { callDoubleFunc(context, argc, argv, deg2rad); } -static void piFunc(sqlite3_context *context, int argc, sqlite3_value **argv) { +static void piFunc(sqlite3_context* context, int argc, sqlite3_value** argv) { sqlite3_result_double(context, M_PI); } struct FuncDef { - const char *zFunctionName; + const char* zFunctionName; int nArg; - void (*xFunc)(sqlite3_context *, int, sqlite3_value **); + void (*xFunc)(sqlite3_context*, int, sqlite3_value**); }; -void registerMathExtensions(sqlite3 *db) { +void registerMathExtensions(sqlite3* db) { // This approach to adding non-standard Math functions was inspired by the // somewhat deprecated/legacy work by Liam Healy from 2010 in the extension // functions contribution. @@ -238,11 +238,11 @@ void registerMathExtensions(sqlite3 *db) { sqlite3_create_function(db, aFuncs[i].zFunctionName, aFuncs[i].nArg, - SQLITE_UTF8, + SQLITE_UTF8 | SQLITE_DETERMINISTIC, nullptr, aFuncs[i].xFunc, nullptr, nullptr); } } -} +} // namespace osquery diff --git a/osquery/sql/sqlite_operations.cpp b/osquery/sql/sqlite_operations.cpp index 06fec8d8..6165c3ff 100644 --- a/osquery/sql/sqlite_operations.cpp +++ b/osquery/sql/sqlite_operations.cpp @@ -68,4 +68,4 @@ void registerOperationExtensions(sqlite3* db) { addCarveFile, executeCarve); } -} +} // namespace osquery diff --git a/osquery/sql/sqlite_string.cpp b/osquery/sql/sqlite_string.cpp index af973096..eeb92d0d 100644 --- a/osquery/sql/sqlite_string.cpp +++ b/osquery/sql/sqlite_string.cpp @@ -156,7 +156,7 @@ void registerStringExtensions(sqlite3* db) { sqlite3_create_function(db, "split", 3, - SQLITE_UTF8, + SQLITE_UTF8 | SQLITE_DETERMINISTIC, nullptr, tokenStringSplitFunc, nullptr, @@ -164,7 +164,7 @@ void registerStringExtensions(sqlite3* db) { sqlite3_create_function(db, "regex_split", 3, - SQLITE_UTF8, + SQLITE_UTF8 | SQLITE_DETERMINISTIC, nullptr, regexStringSplitFunc, nullptr, @@ -172,10 +172,10 @@ void registerStringExtensions(sqlite3* db) { sqlite3_create_function(db, "inet_aton", 1, - SQLITE_UTF8, + SQLITE_UTF8 | SQLITE_DETERMINISTIC, nullptr, ip4StringToDecimalFunc, nullptr, nullptr); } -} +} // namespace osquery diff --git a/osquery/sql/sqlite_util.cpp b/osquery/sql/sqlite_util.cpp index f2ec92ab..4bc97134 100644 --- a/osquery/sql/sqlite_util.cpp +++ b/osquery/sql/sqlite_util.cpp @@ -236,6 +236,7 @@ static inline void openOptimized(sqlite3*& db) { #if !defined(SKIP_CARVER) registerOperationExtensions(db); #endif + registerFilesystemExtensions(db); registerHashingExtensions(db); registerEncodingExtensions(db); } @@ -549,4 +550,4 @@ Status getQueryColumnsInternal(const std::string& q, return status; } -} +} // namespace osquery diff --git a/osquery/sql/sqlite_util.h b/osquery/sql/sqlite_util.h index 1170d131..2c6822dd 100644 --- a/osquery/sql/sqlite_util.h +++ b/osquery/sql/sqlite_util.h @@ -405,6 +405,11 @@ void registerOperationExtensions(sqlite3* db); */ void registerEncodingExtensions(sqlite3* db); +/** + * @brief Register filesystem-related 'custom' functions. + */ +void registerFilesystemExtensions(sqlite3* db); + /** * @brief Generate the data for auto-constructed sqlite tables * @@ -419,4 +424,4 @@ void registerEncodingExtensions(sqlite3* db); Status genQueryDataForSqliteTable(const boost::filesystem::path& sqlite_db, const std::string& sqlite_query, QueryData& results); -} +} // namespace osquery