osquery-1/osquery/sql/sqlite_filesystem.cpp
Ted Reed b938ff11e5 Fix sqlite API usage errors (#5551)
Summary:
Pull Request resolved: https://github.com/facebook/osquery/pull/5551

This handles exceptional error cases when using SQLite APIs in MacOS's Gatekeeper inspection and in osquery's added SQLite functions.

Reviewed By: marekcirkos

Differential Revision: D14641507

fbshipit-source-id: ac5ba661966ab374ef6bb33676b73c365278864a
2019-03-31 11:56:03 -07:00

212 lines
6.3 KiB
C++

/**
* Copyright (c) 2017-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed in accordance with the terms specified in
* the LICENSE file found in the root directory of this source tree.
*/
#include <string>
#include <boost/filesystem.hpp>
#include <boost/optional.hpp>
#include <sqlite3.h>
namespace osquery {
static boost::optional<std::string> 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<std::string> 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<const char*>(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<const char*>(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<int>(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);
}
static void getParentDirectory(sqlite3_context* context,
int argc,
sqlite3_value** argv) {
if (sqlite3_value_type(argv[0]) != SQLITE_TEXT) {
sqlite3_result_error(
context, "Invalid inputs to parent_directory, TEXT was expected", -1);
return;
}
const char* path = reinterpret_cast<const char*>(sqlite3_value_text(argv[0]));
if (path == nullptr) {
sqlite3_result_null(context);
return;
}
int pos = 0;
int last_slash_pos = -1;
#if defined(OSQUERY_WINDOWS)
char directory_symbol = '\\';
#elif defined(OSQUERY_POSIX)
char directory_symbol = '/';
#else
#error Unsupported platform
#endif
while (path[pos] != '\0') {
if (path[pos] == directory_symbol) {
last_slash_pos = pos;
}
pos++;
}
if (last_slash_pos < 0) {
// No parent directory
sqlite3_result_null(context);
return;
}
char* result = reinterpret_cast<char*>(malloc(last_slash_pos));
memcpy(result, path, last_slash_pos);
sqlite3_result_text(context, result, last_slash_pos, free);
}
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);
sqlite3_create_function(db,
"parent_directory",
1,
SQLITE_UTF8 | SQLITE_DETERMINISTIC,
nullptr,
getParentDirectory,
nullptr,
nullptr);
}
} // namespace osquery