2014-12-18 18:50:47 +00:00
|
|
|
/*
|
|
|
|
* Copyright (c) 2014, Facebook, Inc.
|
|
|
|
* All rights reserved.
|
|
|
|
*
|
|
|
|
* This source code is licensed under the BSD-style license found in the
|
2015-01-22 21:57:14 +00:00
|
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
2014-12-18 18:50:47 +00:00
|
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
|
|
*
|
|
|
|
*/
|
2014-11-29 00:22:02 +00:00
|
|
|
|
2015-01-30 18:44:25 +00:00
|
|
|
#include <boost/property_tree/json_parser.hpp>
|
|
|
|
|
2014-12-03 23:14:02 +00:00
|
|
|
#include <osquery/logger.h>
|
|
|
|
#include <osquery/tables.h>
|
2014-11-29 00:22:02 +00:00
|
|
|
|
|
|
|
namespace osquery {
|
|
|
|
namespace tables {
|
|
|
|
|
2015-01-30 18:44:25 +00:00
|
|
|
|
2014-11-29 00:22:02 +00:00
|
|
|
bool ConstraintList::matches(const std::string& expr) {
|
2015-01-12 18:02:44 +00:00
|
|
|
// Support each SQL affinity type casting.
|
2014-11-29 00:22:02 +00:00
|
|
|
if (affinity == "TEXT") {
|
2014-11-30 05:55:14 +00:00
|
|
|
return literal_matches<TEXT_LITERAL>(expr);
|
2014-11-29 00:22:02 +00:00
|
|
|
} else if (affinity == "INTEGER") {
|
|
|
|
INTEGER_LITERAL lexpr = AS_LITERAL(INTEGER_LITERAL, expr);
|
|
|
|
return literal_matches<INTEGER_LITERAL>(lexpr);
|
|
|
|
} else if (affinity == "BIGINT") {
|
|
|
|
BIGINT_LITERAL lexpr = AS_LITERAL(BIGINT_LITERAL, expr);
|
|
|
|
return literal_matches<BIGINT_LITERAL>(lexpr);
|
|
|
|
} else if (affinity == "UNSIGNED_BIGINT") {
|
|
|
|
UNSIGNED_BIGINT_LITERAL lexpr = AS_LITERAL(UNSIGNED_BIGINT_LITERAL, expr);
|
|
|
|
return literal_matches<UNSIGNED_BIGINT_LITERAL>(lexpr);
|
|
|
|
} else {
|
|
|
|
// Unsupprted affinity type.
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename T>
|
|
|
|
bool ConstraintList::literal_matches(const T& base_expr) {
|
|
|
|
bool aggregate = true;
|
2015-01-12 18:02:44 +00:00
|
|
|
for (size_t i = 0; i < constraints_.size(); ++i) {
|
|
|
|
T constraint_expr = AS_LITERAL(T, constraints_[i].expr);
|
|
|
|
if (constraints_[i].op == EQUALS) {
|
2014-11-29 00:22:02 +00:00
|
|
|
aggregate = aggregate && (base_expr == constraint_expr);
|
2015-01-12 18:02:44 +00:00
|
|
|
} else if (constraints_[i].op == GREATER_THAN) {
|
2014-11-29 00:22:02 +00:00
|
|
|
aggregate = aggregate && (base_expr > constraint_expr);
|
2015-01-12 18:02:44 +00:00
|
|
|
} else if (constraints_[i].op == LESS_THAN) {
|
2014-11-29 00:22:02 +00:00
|
|
|
aggregate = aggregate && (base_expr < constraint_expr);
|
2015-01-12 18:02:44 +00:00
|
|
|
} else if (constraints_[i].op == GREATER_THAN_OR_EQUALS) {
|
2014-11-29 00:22:02 +00:00
|
|
|
aggregate = aggregate && (base_expr >= constraint_expr);
|
2015-01-12 18:02:44 +00:00
|
|
|
} else if (constraints_[i].op == LESS_THAN_OR_EQUALS) {
|
2014-11-29 00:22:02 +00:00
|
|
|
aggregate = aggregate && (base_expr <= constraint_expr);
|
|
|
|
} else {
|
|
|
|
// Unsupported constraint.
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (!aggregate) {
|
|
|
|
// Speed up comparison.
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2015-02-06 17:42:03 +00:00
|
|
|
std::set<std::string> ConstraintList::getAll(ConstraintOperator op) {
|
|
|
|
std::set<std::string> set;
|
2015-01-12 18:02:44 +00:00
|
|
|
for (size_t i = 0; i < constraints_.size(); ++i) {
|
|
|
|
if (constraints_[i].op == op) {
|
2014-11-29 00:22:02 +00:00
|
|
|
// TODO: this does not apply a distinct.
|
2015-02-06 17:42:03 +00:00
|
|
|
set.insert(constraints_[i].expr);
|
2014-11-29 00:22:02 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return set;
|
|
|
|
}
|
2015-01-30 18:44:25 +00:00
|
|
|
|
|
|
|
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<unsigned char>("op"));
|
|
|
|
constraint.expr = list.second.get<std::string>("expr");
|
|
|
|
constraints_.push_back(constraint);
|
|
|
|
}
|
|
|
|
affinity = tree.get<std::string>("affinity");
|
|
|
|
}
|
|
|
|
|
|
|
|
void TablePlugin::setRequestFromContext(const QueryContext& context,
|
|
|
|
PluginRequest& request) {
|
|
|
|
boost::property_tree::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.
|
|
|
|
boost::property_tree::ptree constraints;
|
|
|
|
for (const auto& constraint : context.constraints) {
|
|
|
|
boost::property_tree::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;
|
|
|
|
boost::property_tree::write_json(output, tree, false);
|
|
|
|
request["context"] = output.str();
|
|
|
|
}
|
|
|
|
|
|
|
|
void TablePlugin::setResponseFromQueryData(const QueryData& data,
|
|
|
|
PluginResponse& response) {
|
|
|
|
for (const auto& row : data) {
|
|
|
|
response.push_back(row);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void TablePlugin::setContextFromRequest(const PluginRequest& request,
|
|
|
|
QueryContext& context) {
|
|
|
|
if (request.count("context") == 0) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Read serialized context from PluginRequest.
|
|
|
|
std::stringstream input;
|
|
|
|
input << request.at("context");
|
|
|
|
boost::property_tree::ptree tree;
|
|
|
|
boost::property_tree::read_json(input, tree);
|
|
|
|
|
|
|
|
// Set the context limit and deserialize each column constraint list.
|
|
|
|
context.limit = tree.get<int>("limit");
|
|
|
|
for (const auto& constraint : tree.get_child("constraints")) {
|
|
|
|
auto column_name = constraint.second.get<std::string>("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") == "statement") {
|
|
|
|
// The "statement" action generates an SQL create table statement.
|
|
|
|
response.push_back({{"statement", statement()}});
|
|
|
|
} else 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.
|
|
|
|
auto column_list = columns();
|
|
|
|
for (const auto& column : column_list) {
|
|
|
|
response.push_back({{"name", column.first}, {"type", column.second}});
|
|
|
|
}
|
2015-01-31 01:52:14 +00:00
|
|
|
} else if (request.at("action") == "columns_definition") {
|
|
|
|
response.push_back({{"definition", columnDefinition()}});
|
2015-01-30 18:44:25 +00:00
|
|
|
} else {
|
|
|
|
return Status(1, "Unknown table plugin action: " + request.at("action"));
|
|
|
|
}
|
|
|
|
|
|
|
|
return Status(0, "OK");
|
|
|
|
}
|
|
|
|
|
2015-01-31 01:52:14 +00:00
|
|
|
std::string TablePlugin::columnDefinition() {
|
|
|
|
const auto& column_list = columns();
|
|
|
|
std::string statement = "(";
|
2015-01-30 18:44:25 +00:00
|
|
|
for (size_t i = 0; i < column_list.size(); ++i) {
|
2015-01-31 01:52:14 +00:00
|
|
|
statement += column_list[i].first + " " + column_list.at(i).second;
|
2015-01-30 18:44:25 +00:00
|
|
|
if (i < column_list.size() - 1) {
|
|
|
|
statement += ", ";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
statement += ")";
|
|
|
|
return statement;
|
|
|
|
}
|
2015-01-31 01:52:14 +00:00
|
|
|
|
|
|
|
std::string TablePlugin::statement() {
|
|
|
|
return "CREATE TABLE " + name_ + columnDefinition();
|
|
|
|
}
|
2015-01-22 21:57:14 +00:00
|
|
|
|
2014-11-29 00:22:02 +00:00
|
|
|
}
|
|
|
|
}
|