osquery-1/osquery/tables/manual/hash.cpp
2014-08-05 10:54:14 -07:00

441 lines
14 KiB
C++

// Copyright 2004-present Facebook. All Rights Reserved.
#include "osquery/tables/manual/hash.h"
#include "osquery/core/md5.h"
#include <string>
#include <vector>
#include <iostream>
#include <cstring>
#include <sstream>
#include <boost/filesystem/operations.hpp>
#include <boost/filesystem/path.hpp>
namespace fs = boost::filesystem;
/*
** Definition of the sqlite3_hash object.
**
** The internal representation of an hash object is subject
** to change, is not externally visible, and should be used by
** the implementation of hash only. This object is opaque
** to users.
*/
struct sqlite3_hash {
int n; /* number of elements */
std::vector<std::string> filename; /* the full path of a hash object */
std::vector<std::string> md5; /* the hash of the file at the path */
};
/*
* Objects used internally by the virtual table implementation
*
* we write "typedef struct x x" here so that we can write "x" later instead of
* "stuct x"
**/
typedef struct hash_vtab hash_vtab;
typedef struct hash_cursor hash_cursor;
/*
* Our virtual table object
**/
struct hash_vtab {
// virtual table implementations will normally subclass this structure to add
// additional private and implementation-specific fields
sqlite3_vtab base;
// to get custom functionality, add our own struct as well
sqlite3_hash *pContent;
};
/*
* Our cursor object
**/
struct hash_cursor {
// similarly to sqlite3_vtab, practical implementations will likely subclass
// this structure to add additional private fields.
sqlite3_vtab_cursor base;
// field that will be used to represent the current cursor position
int row;
// the path that is being queried
std::string path;
};
/*
** Free an sqlite3_hash object.
*/
static void hashFree(sqlite3_hash *p) {
sqlite3_free(p);
}
/*
* This method releases a connection to a virtual table, just like the
* xDisconnect method, and it also destroys the underlying table
* implementation. This method undoes the work of xCreate.
*/
static int hashDestroy(sqlite3_vtab *p) {
hash_vtab *pVtab = (hash_vtab*)p;
sqlite3_free(pVtab);
return SQLITE_OK;
}
/*
** Table constructor for the hash module.
*/
static int hashCreate(
sqlite3 *db, /* Database where module is created */
void *pAux, /* clientdata for the module */
int argc, /* Number of arguments */
const char *const *argv, /* Value for all arguments */
sqlite3_vtab **ppVtab, /* Write the new virtual table object here */
char **pzErr /* Put error message text here */
) {
// this will get overwritten if pVtab was successfully allocated. if pVtab
// wasn't allocated, it means we have no memory
int rc = SQLITE_NOMEM;
// allocate the correct amount of memory for your virtual table structure
// hash_vtab *pVtab = sqlite3_malloc(sizeof(hash_vtab));
hash_vtab *pVtab = new hash_vtab;
// if the virtual table structure was successfully allocated
if(pVtab) {
// overwrite the entire memory that was allocated with zeros
memset(pVtab, 0, sizeof(hash_vtab));
// the pAux argument is the copy of the client data pointer that was the
// fourth argument to the sqlite3_create_module() or
// sqlite3_create_module_v2() call that registered the virtual table
// module. This sets the pContent value of the virtual table struct to
// whatever that value was
pVtab->pContent = (sqlite3_hash*)pAux;
// this interface is called to declare the format (the names and datatypes
// of the columns) of the virtual tables they implement
const char *create_table_statement =
"CREATE TABLE hash("
"path VARCHAR, "
"filename VARCHAR, "
"md5 VARCHAR"
")";
rc = sqlite3_declare_vtab(db, create_table_statement);
}
// cast your virtual table objet back to type sqlite3_vtab and assign it to
// the address supplied by the function call
*ppVtab = (sqlite3_vtab *)pVtab;
// if all went well, sqlite3_declare_vtab will have returned SQLITE_OK and
// that is what will be returned
return rc;
}
/*
** Open a new cursor on the hash table.
*/
static int hashOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor) {
// this will get overwritten if pVtab was successfully allocated. if pVtab
// wasn't allocated, it means we have no memory
int rc = SQLITE_NOMEM;
// declare a value to be used as the virtual table's cursor
hash_cursor *pCur;
// allocate the correct amount of memory for your virtual table cursor
//pCur = sqlite3_malloc(sizeof(hash_cursor));
pCur = new hash_cursor;
// if the cursor was successfully allocated
if(pCur) {
// overwrite the entire memory that was allocated with zeros
memset(pCur, 0, sizeof(hash_cursor));
// cast the cursor object back to type sqlite3_vtab_cursor and assign it to
// the address that was supplied by the function call
*ppCursor = (sqlite3_vtab_cursor *)pCur;
// if you've gotten this far, everything succeeded so we can set rc, which
// will be used as our return value, to SQLITE_OK
rc = SQLITE_OK;
}
// return the value we set to rc, which can either be SQLITE_OK or
// SQLITE_NOMEM
return rc;
}
/*
** Close a hash table cursor.
*/
static int hashClose(sqlite3_vtab_cursor *cur) {
// the xClose interface accepts a sqlite3_vtab_cursor. if we need to do
// something specific to our virtual table to free it, cast it back to
// your own cursor type
hash_cursor *pCur = (hash_cursor *)cur;
// finally, free the structure using sqlite's built-in memory allocation
// function
// in C, we would use sqlite3_free(pCur);
delete pCur;
// return SQLITE_OK because everything succeeded
return SQLITE_OK;
}
/*
** Retrieve a column of data.
*/
static int hashColumn(
sqlite3_vtab_cursor *cur,
sqlite3_context *ctx,
int col
) {
hash_cursor *pCur = (hash_cursor*)cur;
hash_vtab *pVtab = (hash_vtab*)cur->pVtab;
// return a specific column from a specific row, depending on the state of
// the cursor
if(pCur->row >= 0 && pCur->row < pVtab->pContent->n) {
switch (col) {
// path
case 0:
sqlite3_result_text(ctx, (pCur->path).c_str(), -1, nullptr);
break;
// filename
case 1:
sqlite3_result_text(
ctx,
(pVtab->pContent->filename[pCur->row]).c_str(),
-1,
nullptr
);
break;
// md5
case 2:
sqlite3_result_text(
ctx,
(pVtab->pContent->md5[pCur->row]).c_str(),
-1,
nullptr
);
break;
}
}
return SQLITE_OK;
}
/*
** Retrieve the current rowid.
*/
static int hashRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid) {
hash_cursor *pCur = (hash_cursor *)cur;
// return the value of i, which is set to 0 in xFilter and incremented in
// xNext
*pRowid = pCur->row;
return SQLITE_OK;
}
static int hashEof(sqlite3_vtab_cursor *cur) {
hash_cursor *pCur = (hash_cursor *)cur;
hash_vtab *pVtab = (hash_vtab *)cur->pVtab;
return pCur->row >= pVtab->pContent->n;
}
/*
** Advance the cursor to the next row.
*/
static int hashNext(sqlite3_vtab_cursor *cur) {
hash_cursor *pCur = (hash_cursor *)cur;
// increment the value of i, so that xColumn knowns what value to return
pCur->row++;
return SQLITE_OK;
}
/*
* This function resets the cursor for a new query. From the documentation:
*
* This method begins a search of a virtual table. The first argument is a
* cursor opened by xOpen. The next two arguments define a particular search
* index previously chosen by xBestIndex. The specific meanings of idxNum and
* idxStr are unimportant as long as xFilter and xBestIndex agree on what that
* meaning is.
*
* This method must return SQLITE_OK if successful, or an sqlite error code if
* an error occurs.
**/
static int hashFilter(
sqlite3_vtab_cursor *pVtabCursor,
int idxNum,
const char *idxStr,
int argc,
sqlite3_value **argv
) {
// you need to operate on the sqlite3_vtab_cursor object as your own
// virtual table's cursor type, so cast it back to x_cursor
hash_cursor *pCur = (hash_cursor *)pVtabCursor;
hash_vtab *pVtab = (hash_vtab*)pVtabCursor->pVtab;
// reset the count value of your cursor's structure
pCur->row = 0;
// the hash table requires you to have a where clause to specify the
// path. if argc is 0, then no values were specified as valid constraints in
// xBestIndex. there's currently logic in xBestIndex to prevent execution
// from getting this far if that were to happen, but we check argc here as
// well to illustrate the requirement
if (argc <= 0) {
return SQLITE_MISUSE;
}
// extract the RHS value for the path constraint into the cursor's path field
pCur->path = std::string((const char*)sqlite3_value_text(argv[0]));
// if the path doesn't exist, return early
if (!fs::exists(pCur->path)) {
std::cerr << pCur->path << " doesn't exist" << std::endl;
return SQLITE_OK;
}
// iterate through the directory that is being queried upon and gether the
// information needed to complete a table scan
osquery::md5::MD5 md5;
if (fs::is_regular_file(pCur->path)) {
pVtab->pContent->filename.push_back(pCur->path);
const char* filename = pCur->path.c_str();
const char* md5_value = md5.digestFile(filename);
pVtab->pContent->md5.push_back(std::string(md5_value));
} else if (fs::is_directory(pCur->path)) {
fs::directory_iterator end_iter;
for (fs::directory_iterator dir_itr(pCur->path);
dir_itr != end_iter;
++dir_itr) {
pVtab->pContent->filename.push_back(dir_itr->path().string());
if (fs::is_regular_file(dir_itr->status())) {
const char* filename = dir_itr->path().string().c_str();
const char* md5_value = md5.digestFile(filename);
pVtab->pContent->md5.push_back(std::string(md5_value));
} else {
pVtab->pContent->md5.push_back("");
}
}
}
// set the size of the table based on the amount of results that were queried
pVtab->pContent->n = pVtab->pContent->filename.size();
// return SQLITE_OK because everything went as planned
return SQLITE_OK;
}
/*
* This is executed when you query a virtual table with a WHERE claue. From
* the documentation:
*
* SQLite calls this method when it is running sqlite3_prepare() or the
* equivalent. By calling this method, the SQLite core is saying to the virtual
* table that it needs to access some subset of the rows in the virtual table
* and it wants to know the most efficient way to do that access. The xBestIndex
* method replies with information that the SQLite core can then use to conduct
* an efficient search of the virtual table.
**/
static int hashBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo) {
hash_vtab *pVtab = (hash_vtab*)tab;
if (pIdxInfo->nConstraint == 0) {
// the hash table requires you to have a where clause to specify the
// path. if nConstrain is 0, then there were no where clauses.
goto fail;
}
// iterate through all of the constraints (aka where clauses) and look for
// the constraint on the "path" column
for (int i = 0; i < pIdxInfo->nConstraint; i++) {
// if the "path" column is being queried and it's usable
if (pIdxInfo->aConstraint[i].iColumn == 0 &&
pIdxInfo->aConstraint[i].usable) {
// set the argvIndex of the "path" constraint so that it's usable to
// xBestIndex
pIdxInfo->aConstraintUsage[i].argvIndex =
pIdxInfo->aConstraint[i].iColumn + 1;
goto finish;
}
}
// if the code has gotten this far, it means that there were constrains in
// the query, but none of them were for the path column, which is required
goto fail;
finish:
return SQLITE_OK;
fail:
return SQLITE_MISUSE;
}
/*
** A virtual table module that merely echos method calls into TCL
** variables.
*/
static sqlite3_module hashModule = {
0, /* iVersion */
hashCreate, /* xCreate - create a new virtual table */
hashCreate, /* xConnect - connect to an existing vtab */
hashBestIndex, /* xBestIndex - find the best query index */
hashDestroy, /* xDisconnect - disconnect a vtab */
hashDestroy, /* xDestroy - destroy a vtab */
hashOpen, /* xOpen - open a cursor */
hashClose, /* xClose - close a cursor */
hashFilter, /* xFilter - configure scan constraints */
hashNext, /* xNext - advance a cursor */
hashEof, /* xEof */
hashColumn, /* xColumn - read data */
hashRowid, /* xRowid - read data */
0, /* xUpdate */
0, /* xBegin */
0, /* xSync */
0, /* xCommit */
0, /* xRollback */
0, /* xFindMethod */
0, /* xRename */
};
/*
** Invoke this routine to create a specific instance of an hash object.
** The new hash object is returned by the 3rd parameter.
**
** Each hash object corresponds to a virtual table in the TEMP table
** with a name of zName.
**
** Destroy the hash object by dropping the virtual table. If not done
** explicitly by the application, the virtual table will be dropped implicitly
** by the system when the database connection is closed.
*/
int sqlite3_hash_create(
sqlite3 *db,
const char *zName,
sqlite3_hash **ppReturn
) {
int rc = SQLITE_OK;
sqlite3_hash *p;
*ppReturn = p = new sqlite3_hash;
if(p==0) {
return SQLITE_NOMEM;
}
memset(p, 0, sizeof(*p));
rc = sqlite3_create_module_v2(db, zName, &hashModule, p,
(void(*)(void*))hashFree);
if(rc==SQLITE_OK) {
char *zSql;
zSql = sqlite3_mprintf("CREATE VIRTUAL TABLE temp.%Q USING %Q",
zName, zName);
rc = sqlite3_exec(db, zSql, 0, 0, 0);
sqlite3_free(zSql);
}
return rc;
}