osquery-1/osquery/core/virtual_table.h

256 lines
6.7 KiB
C
Raw Normal View History

2014-12-03 04:36:46 +00:00
// Copyright 2004-present Facebook. All Rights Reserved.
#pragma once
#include <map>
#include <sqlite3.h>
#include <stdio.h>
#include "osquery/tables.h"
#include "osquery/registry.h"
namespace osquery {
namespace tables {
2014-12-03 16:29:36 +00:00
/// Helper alias for TablePlugin names.
2014-12-03 04:36:46 +00:00
typedef const std::string TableName;
2014-12-03 16:29:36 +00:00
typedef const std::vector<std::pair<std::string, std::string> > TableColumns;
typedef std::map<std::string, std::vector<std::string> > TableData;
/**
* @brief osquery cursor object.
*
* Only used in the SQLite virtual table module methods.
*/
2014-12-03 04:36:46 +00:00
struct base_cursor {
/// SQLite virtual table cursor.
sqlite3_vtab_cursor base;
/// Current cursor position.
int row;
};
2014-12-03 16:29:36 +00:00
/**
* @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.
*/
template <class TABLE_PLUGIN>
2014-12-03 04:36:46 +00:00
struct x_vtab {
sqlite3_vtab base;
2014-12-03 16:29:36 +00:00
/// To get custom functionality from SQLite virtual tables, add a struct.
TABLE_PLUGIN *pContent;
2014-12-03 04:36:46 +00:00
};
2014-12-03 16:29:36 +00:00
/**
* @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 instanciation.
*/
class TablePlugin {
public:
TableName name;
TableColumns columns;
/// Helper method to generate the virtual table CREATE statement.
std::string statement(TableName name, TableColumns columns);
public:
/// Part of the query state, number of rows generated.
2014-12-03 04:36:46 +00:00
int n;
2014-12-03 16:29:36 +00:00
/// Part of the query state, column data returned from a query.
TableData data;
/// Part of the query state, parsed set of query predicate constraints.
2014-12-03 04:36:46 +00:00
ConstraintSet constraints;
public:
virtual int attachVtable(sqlite3 *db) { return -1; }
virtual ~TablePlugin(){};
protected:
TablePlugin(){};
};
typedef std::shared_ptr<TablePlugin> TablePluginRef;
2014-12-03 05:02:50 +00:00
int xOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor);
2014-12-03 04:36:46 +00:00
2014-12-03 05:02:50 +00:00
int xClose(sqlite3_vtab_cursor *cur);
2014-12-03 04:36:46 +00:00
2014-12-03 05:02:50 +00:00
int xNext(sqlite3_vtab_cursor *cur);
2014-12-03 04:36:46 +00:00
2014-12-03 05:02:50 +00:00
int xRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid);
2014-12-03 04:36:46 +00:00
2014-12-03 16:29:36 +00:00
int xEof(sqlite3_vtab_cursor *cur);
2014-12-03 04:36:46 +00:00
2014-12-03 05:02:50 +00:00
template <typename T>
2014-12-03 04:36:46 +00:00
int xCreate(sqlite3 *db,
void *pAux,
int argc,
const char *const *argv,
sqlite3_vtab **ppVtab,
char **pzErr) {
2014-12-03 05:02:50 +00:00
auto *pVtab = new x_vtab<T>;
2014-12-03 04:36:46 +00:00
2014-12-03 05:02:50 +00:00
if (!pVtab) {
return SQLITE_NOMEM;
2014-12-03 04:36:46 +00:00
}
2014-12-03 05:02:50 +00:00
memset(pVtab, 0, sizeof(x_vtab<T>));
auto *pContent = pVtab->pContent = new T;
2014-12-03 16:29:36 +00:00
auto create = pContent->statement(pContent->name, pContent->columns);
2014-12-03 05:02:50 +00:00
int rc = sqlite3_declare_vtab(db, create.c_str());
2014-12-03 04:36:46 +00:00
*ppVtab = (sqlite3_vtab *)pVtab;
return rc;
}
2014-12-03 16:29:36 +00:00
int xDestroy(sqlite3_vtab *p);
2014-12-03 05:02:50 +00:00
2014-12-03 04:36:46 +00:00
template <typename T>
int xColumn(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int col) {
base_cursor *pCur = (base_cursor *)cur;
2014-12-03 05:02:50 +00:00
auto *pContent = ((x_vtab<T> *)cur->pVtab)->pContent;
2014-12-03 04:36:46 +00:00
2014-12-03 16:29:36 +00:00
if (col >= pContent->columns.size()) {
2014-12-03 04:36:46 +00:00
return SQLITE_ERROR;
}
2014-12-03 16:29:36 +00:00
const auto &column_name = pContent->columns[col].first;
const auto &type = pContent->columns[col].second;
if (pCur->row >= pContent->data[column_name].size()) {
2014-12-03 04:36:46 +00:00
return SQLITE_ERROR;
}
2014-12-03 16:29:36 +00:00
const auto &value = pContent->data[column_name][pCur->row];
2014-12-03 04:36:46 +00:00
if (type == "TEXT") {
sqlite3_result_text(ctx, value.c_str(), -1, nullptr);
} else if (type == "INTEGER") {
int afinite;
try {
afinite = boost::lexical_cast<int>(value);
} catch (const boost::bad_lexical_cast &e) {
afinite = -1;
LOG(WARNING) << "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<long long int>(value);
} catch (const boost::bad_lexical_cast &e) {
afinite = -1;
LOG(WARNING) << "Error casting " << column_name << " (" << value
<< ") to BIGINT";
}
sqlite3_result_int64(ctx, afinite);
}
return SQLITE_OK;
}
template <typename T>
static int xBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo) {
2014-12-03 05:02:50 +00:00
auto *pContent = ((x_vtab<T> *)tab)->pContent;
2014-12-03 04:36:46 +00:00
int expr_index = 0;
for (size_t i = 0; i < pIdxInfo->nConstraint; ++i) {
if (!pIdxInfo->aConstraint[i].usable) {
// TODO: OR is not usable.
continue;
}
2014-12-03 16:29:36 +00:00
const auto &name =
pContent->columns[pIdxInfo->aConstraint[i].iColumn].first;
2014-12-03 04:36:46 +00:00
pContent->constraints.push_back(
std::make_pair(name, Constraint(pIdxInfo->aConstraint[i].op)));
pIdxInfo->aConstraintUsage[i].argvIndex = ++expr_index;
}
return SQLITE_OK;
}
template <typename T>
static int xFilter(sqlite3_vtab_cursor *pVtabCursor,
int idxNum,
const char *idxStr,
int argc,
sqlite3_value **argv) {
base_cursor *pCur = (base_cursor *)pVtabCursor;
2014-12-03 05:02:50 +00:00
auto *pContent = ((x_vtab<T> *)pVtabCursor->pVtab)->pContent;
2014-12-03 04:36:46 +00:00
pCur->row = 0;
pContent->n = 0;
QueryContext request;
2014-12-03 16:29:36 +00:00
for (size_t i = 0; i < pContent->columns.size(); ++i) {
pContent->data[pContent->columns[i].first].clear();
request.constraints[pContent->columns[i].first].affinity =
pContent->columns[i].second;
2014-12-03 04:36:46 +00:00
}
for (size_t i = 0; i < argc; ++i) {
auto expr = (const char *)sqlite3_value_text(argv[i]);
// Set the expression from SQLite's now-populated argv.
pContent->constraints[i].second.expr = std::string(expr);
// Add the constraint to the column-sorted query request map.
request.constraints[pContent->constraints[i].first].add(
pContent->constraints[i].second);
}
for (auto &row : pContent->generate(request)) {
2014-12-03 16:29:36 +00:00
for (const auto &column : pContent->columns) {
pContent->data[column.first].push_back(row[column.first]);
2014-12-03 04:36:46 +00:00
}
pContent->n++;
}
return SQLITE_OK;
}
template <typename T>
int sqlite3_attach_vtable(sqlite3 *db, const std::string &name) {
int rc = SQLITE_OK;
static sqlite3_module module = {
0,
2014-12-03 05:02:50 +00:00
xCreate<T>,
xCreate<T>,
xBestIndex<T>,
2014-12-03 16:29:36 +00:00
xDestroy,
xDestroy,
2014-12-03 05:02:50 +00:00
xOpen,
xClose,
xFilter<T>,
xNext,
2014-12-03 16:29:36 +00:00
xEof,
2014-12-03 05:02:50 +00:00
xColumn<T>,
xRowid,
2014-12-03 04:36:46 +00:00
0,
0,
0,
0,
0,
0,
0,
};
rc = sqlite3_create_module(db, name.c_str(), &module, 0);
if (rc == SQLITE_OK) {
auto format = "CREATE VIRTUAL TABLE temp." + name + " USING " + name;
rc = sqlite3_exec(db, format.c_str(), 0, 0, 0);
}
return rc;
}
void attachVirtualTables(sqlite3 *db);
}
}
DECLARE_REGISTRY(TablePlugins, std::string, osquery::tables::TablePluginRef);
#define REGISTERED_TABLES REGISTRY(TablePlugins)
#define REGISTER_TABLE(name, decorator) REGISTER(TablePlugins, name, decorator);