diff --git a/tsk/hashdb/hdb_index.cpp b/tsk/hashdb/hdb_index.cpp index 6827c1223a5549504f6ef388f219c064249bd318..b96c2a33ffa9c0c2d058640df35635faef378e69 100644 --- a/tsk/hashdb/hdb_index.cpp +++ b/tsk/hashdb/hdb_index.cpp @@ -81,6 +81,30 @@ tsk_idx_close_file(FILE * idx) } } +/** + * Update the hash type. New indices can handle multiple hash types, so hash + * type is now dependent on what the client is doing (e.g. lookup md5). + * @return 1 on error, 0 on success + */ +static int +hdb_update_htype(TSK_HDB_INFO * hdb_info, uint8_t htype) +{ + /* Get hash type specific information */ + switch (htype) { + case TSK_HDB_HTYPE_MD5_ID: + hdb_info->hash_type = static_cast<TSK_HDB_HTYPE_ENUM>(htype); + hdb_info->hash_len = TSK_HDB_HTYPE_MD5_LEN; + break; + case TSK_HDB_HTYPE_SHA1_ID: + hdb_info->hash_type = static_cast<TSK_HDB_HTYPE_ENUM>(htype); + hdb_info->hash_len = TSK_HDB_HTYPE_SHA1_LEN; + break; + default: + return 1; + } + return 0; +} + /** * Open an index for the given hash db * We only create kdb (SQLite) files, but can open old indexes. @@ -117,27 +141,16 @@ tsk_idx_open(TSK_HDB_INFO * hdb_info, uint8_t htype, uint8_t create) return NULL; } - /* Get hash type specific information */ - switch (htype) { - case TSK_HDB_HTYPE_MD5_ID: - hdb_info->hash_type = static_cast<TSK_HDB_HTYPE_ENUM>(htype); - hdb_info->hash_len = TSK_HDB_HTYPE_MD5_LEN; - break; - case TSK_HDB_HTYPE_SHA1_ID: - hdb_info->hash_type = static_cast<TSK_HDB_HTYPE_ENUM>(htype); - hdb_info->hash_len = TSK_HDB_HTYPE_SHA1_LEN; - break; - default: - free(idx_info); - tsk_error_reset(); - tsk_error_set_errno(TSK_ERR_HDB_MISSING); - tsk_error_set_errstr( - "tsk_idx_open: Unknown hash type: %d\n", - (int)htype); - return NULL; + if (hdb_update_htype(hdb_info, htype) == 1) { + free(idx_info); + tsk_error_reset(); + tsk_error_set_errno(TSK_ERR_HDB_MISSING); + tsk_error_set_errstr( + "tsk_idx_open: Unknown hash type: %d\n", + (int)htype); + return NULL; } - // Verify the new SQLite index exists, get its size, and open it for header reading // Set SQLite index filename @@ -308,6 +321,9 @@ hdb_setupindex(TSK_HDB_INFO * hdb_info, uint8_t htype, uint8_t create) // already opened if (hdb_info->idx_info != NULL) { + // update htype + hdb_update_htype(hdb_info, htype); + tsk_release_lock(&hdb_info->lock); return 0; } diff --git a/tsk/hashdb/sqlite_index.cpp b/tsk/hashdb/sqlite_index.cpp index 7f7dbed781ad003068697513fe4af80707107657..e5316e76e7dce084fee646166c37c9024e3386f3 100644 --- a/tsk/hashdb/sqlite_index.cpp +++ b/tsk/hashdb/sqlite_index.cpp @@ -26,8 +26,10 @@ static bool need_SQL_index = false; /** * Prototypes */ -int8_t sqlite_v1_get_properties(TSK_HDB_INFO * hdb_info); - +//int8_t sqlite_v1_get_properties(TSK_HDB_INFO * hdb_info); +uint8_t sqlite_v1_addentry_bin(TSK_HDB_INFO * hdb_info, uint8_t* hvalue, int hlen, TSK_OFF_T offset); +uint8_t addentry_text(TSK_HDB_INFO * hdb_info, char* hvalue, TSK_OFF_T offset); +int8_t lookup_text(TSK_HDB_INFO * hdb_info, const char* hvalue, TSK_HDB_FLAG_ENUM flags, TSK_HDB_LOOKUP_FN action, void *ptr); static int attempt(int resultCode, int expectedResultCode, const char *errfmt, sqlite3 * sqlite) @@ -72,8 +74,10 @@ static int finalize_stmt(sqlite3_stmt * stmt) } return 0; } + static int prepare_stmt(const char *sql, sqlite3_stmt ** ppStmt, sqlite3 * sqlite) { + ///@todo possible performance increase by using strlen(sql)+1 instead of -1 if (sqlite3_prepare_v2(sqlite, sql, -1, ppStmt, NULL) != SQLITE_OK) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_AUTO_DB); @@ -166,11 +170,19 @@ sqlite_v1_initialize(TSK_HDB_INFO * hdb_info, TSK_TCHAR * htype) return 1; } +#ifdef IDX_SQLITE_STORE_TEXT + if (attempt_exec_nocallback + ("CREATE TABLE hashes (id INTEGER PRIMARY KEY AUTOINCREMENT, md5 TEXT UNIQUE, sha1 TEXT, sha2_256 TEXT, database_offset INTEGER);", + "Error creating hashes table %s\n", hdb_info->idx_info->idx_struct.idx_sqlite_v1->hIdx_sqlite)) { + return 1; + } +#else if (attempt_exec_nocallback ("CREATE TABLE hashes (id INTEGER PRIMARY KEY AUTOINCREMENT, md5 BINARY(16) UNIQUE, sha1 BINARY(20), sha2_256 BINARY(32), database_offset INTEGER);", "Error creating hashes table %s\n", hdb_info->idx_info->idx_struct.idx_sqlite_v1->hIdx_sqlite)) { return 1; } +#endif // The names table enables the user to optionally map one or many names to each hash. // "name" should be the filename without the path. @@ -194,14 +206,9 @@ sqlite_v1_initialize(TSK_HDB_INFO * hdb_info, TSK_TCHAR * htype) * @return 1 on error and 0 on success */ uint8_t -sqlite_v1_addentry(TSK_HDB_INFO * hdb_info, char *hvalue, +sqlite_v1_addentry(TSK_HDB_INFO * hdb_info, char* hvalue, TSK_OFF_T offset) { - const size_t len = (hdb_info->hash_len)/2; - uint8_t* hash = (uint8_t*) tsk_malloc(len+1); - - size_t count; - if (strlen(hvalue) != hdb_info->hash_len) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_AUTO_DB); @@ -210,6 +217,14 @@ sqlite_v1_addentry(TSK_HDB_INFO * hdb_info, char *hvalue, return 1; } +#ifdef IDX_SQLITE_STORE_TEXT + uint8_t ret = addentry_text(hdb_info, hvalue, offset); +#else + const size_t len = (hdb_info->hash_len)/2; + uint8_t* hash = (uint8_t*) tsk_malloc(len+1); + + size_t count; + // We use an intermediate short to be compatible with Microsoft's implementation of the scanf family format short unsigned int binval; for (count = 0; count < len; count++) { @@ -217,10 +232,10 @@ sqlite_v1_addentry(TSK_HDB_INFO * hdb_info, char *hvalue, hash[count] = (uint8_t) binval; hvalue += 2 * sizeof(char); } - - uint8_t ret = tsk_hdb_idxaddentry_bin(hdb_info, hash, len, offset); + uint8_t ret = sqlite_v1_addentry_bin(hdb_info, hash, len, offset); delete [] hash; +#endif return ret; } @@ -269,6 +284,49 @@ sqlite_v1_addentry_bin(TSK_HDB_INFO * hdb_info, uint8_t* hvalue, int hlen, return 0; } +/** + * Add a text representation of a hash value into the index. + * + * @param hdb_info Hash database state info + * @param hvalue String of hash value to add + * @param hlen Number of bytes in hvalue + * @param offset Byte offset of hash entry in original database. + * @return 1 on error and 0 on success + */ +uint8_t +addentry_text(TSK_HDB_INFO * hdb_info, char* hvalue, TSK_OFF_T offset) +{ + if (attempt(sqlite3_bind_text(m_stmt, 1, hvalue, strlen(hvalue), SQLITE_TRANSIENT), + SQLITE_OK, + "Error binding text: %s\n", + hdb_info->idx_info->idx_struct.idx_sqlite_v1->hIdx_sqlite) || + attempt(sqlite3_bind_int64(m_stmt, 2, offset), + SQLITE_OK, + "Error binding entry offset: %s\n", + hdb_info->idx_info->idx_struct.idx_sqlite_v1->hIdx_sqlite) ) { + return 1; + } + + // Don't report error on constraint -- we just will silently not add that duplicate hash + int r = sqlite3_step(m_stmt); + if ((r != SQLITE_DONE) && (r != SQLITE_CONSTRAINT) ) { + tsk_error_reset(); + tsk_error_set_errno(TSK_ERR_AUTO_DB); + tsk_error_set_errstr("Error stepping: %s\n", sqlite3_errmsg( hdb_info->idx_info->idx_struct.idx_sqlite_v1->hIdx_sqlite), r); + return 1; + } + + r = sqlite3_reset(m_stmt); + if ((r != SQLITE_OK) && (r != SQLITE_CONSTRAINT) ) { + tsk_error_reset(); + tsk_error_set_errno(TSK_ERR_AUTO_DB); + tsk_error_set_errstr("Error resetting: %s\n", sqlite3_errmsg( hdb_info->idx_info->idx_struct.idx_sqlite_v1->hIdx_sqlite), r); + return 1; + } + + return 0; +} + /** * Finalize index creation process * @@ -291,11 +349,11 @@ sqlite_v1_finalize(TSK_HDB_INFO * hdb_info) if (need_SQL_index) { need_SQL_index = false; return attempt_exec_nocallback - ("CREATE INDEX hashset_md5_index ON hashes(md5);", - "Error creating hashset_md5_index on md5: %s\n", hdb_info->idx_info->idx_struct.idx_sqlite_v1->hIdx_sqlite) || + ("CREATE INDEX md5_index ON hashes(md5);", + "Error creating md5_index on md5: %s\n", hdb_info->idx_info->idx_struct.idx_sqlite_v1->hIdx_sqlite) || attempt_exec_nocallback - ("CREATE INDEX hashset_sha1_index ON hashes(sha1);", - "Error creating hashset_sha1_index on sha1: %s\n", hdb_info->idx_info->idx_struct.idx_sqlite_v1->hIdx_sqlite); + ("CREATE INDEX sha1_index ON hashes(sha1);", + "Error creating sha1_index on sha1: %s\n", hdb_info->idx_info->idx_struct.idx_sqlite_v1->hIdx_sqlite); } else { return 0; } @@ -381,13 +439,18 @@ sqlite_v1_open(TSK_HDB_INFO * hdb_info, TSK_IDX_INFO * idx_info, uint8_t htype) * @return -1 on error, 0 if hash value not found, and 1 if value was found. */ int8_t -sqlite_v1_lookup_str(TSK_HDB_INFO * hdb_info, const char *hash, +sqlite_v1_lookup_str(TSK_HDB_INFO * hdb_info, const char* hvalue, TSK_HDB_FLAG_ENUM flags, TSK_HDB_LOOKUP_FN action, void *ptr) { - const size_t len = strlen(hash)/2; + int8_t ret = 0; + +#ifdef IDX_SQLITE_STORE_TEXT + ret = lookup_text(hdb_info, hvalue, flags, action, ptr); +#else + const size_t len = strlen(hvalue)/2; uint8_t * hashBlob = (uint8_t *) tsk_malloc(len+1); - const char * pos = hash; + const char * pos = hvalue; size_t count = 0; for(count = 0; count < len; count++) { @@ -395,18 +458,18 @@ sqlite_v1_lookup_str(TSK_HDB_INFO * hdb_info, const char *hash, pos += 2 * sizeof(char); } - int8_t ret = sqlite_v1_lookup_raw(hdb_info, hashBlob, len, flags, action, ptr); - + ret = sqlite_v1_lookup_raw(hdb_info, hashBlob, len, flags, action, ptr); +#endif + if ((ret == 1) && (hdb_info->db_type == TSK_HDB_DBTYPE_IDXONLY_ID) && !(flags & TSK_HDB_FLAG_QUICK) && (action != NULL)) { //name is blank because we don't have a name in this case ///@todo query the names table for associations char * name = ""; - action(hdb_info, hash, name, ptr); + action(hdb_info, hvalue, name, ptr); } return ret; - } /** @@ -424,7 +487,7 @@ sqlite_v1_lookup_str(TSK_HDB_INFO * hdb_info, const char *hash, * @return -1 on error, 0 if hash value not found, and 1 if value was found. */ int8_t -sqlite_v1_lookup_raw(TSK_HDB_INFO * hdb_info, uint8_t * hash, uint8_t len, +sqlite_v1_lookup_raw(TSK_HDB_INFO * hdb_info, uint8_t * hvalue, uint8_t len, TSK_HDB_FLAG_ENUM flags, TSK_HDB_LOOKUP_FN action, void *ptr) { @@ -443,7 +506,7 @@ sqlite_v1_lookup_raw(TSK_HDB_INFO * hdb_info, uint8_t * hash, uint8_t len, tsk_error_reset(); tsk_error_set_errno(TSK_ERR_HDB_ARG); tsk_error_set_errstr("hdb_lookup: Hash passed is different size than expected: %d vs %d", - hdb_info->hash_len, len); + hdb_info->hash_len, (len * 2)); ret = -1; } else { @@ -461,7 +524,7 @@ sqlite_v1_lookup_raw(TSK_HDB_INFO * hdb_info, uint8_t * hash, uint8_t len, if (ret != -1) { prepare_stmt(selectStmt, &stmt, hdb_info->idx_info->idx_struct.idx_sqlite_v1->hIdx_sqlite); - if (attempt(sqlite3_bind_blob(stmt, 1, hash, len, free), + if (attempt(sqlite3_bind_blob(stmt, 1, hvalue, len, free), SQLITE_OK, "Error binding binary blob: %s\n", hdb_info->idx_info->idx_struct.idx_sqlite_v1->hIdx_sqlite)) { @@ -478,8 +541,8 @@ sqlite_v1_lookup_raw(TSK_HDB_INFO * hdb_info, uint8_t * hash, uint8_t len, } else { // Use offset to get more info for (i = 0; i < len; i++) { - hashbuf[2 * i] = hex[(hash[i] >> 4) & 0xf]; - hashbuf[2 * i + 1] = hex[hash[i] & 0xf]; + hashbuf[2 * i] = hex[(hvalue[i] >> 4) & 0xf]; + hashbuf[2 * i + 1] = hex[hvalue[i] & 0xf]; } hashbuf[2 * len] = '\0'; @@ -509,6 +572,93 @@ sqlite_v1_lookup_raw(TSK_HDB_INFO * hdb_info, uint8_t * hash, uint8_t len, } +/** + * \ingroup hashdblib + * Search the index for the given hash value given (in string form). + * + * @param hdb_info Open hash database (with index) + * @param hash String hash value to search for + * @param flags Flags to use in lookup + * @param action Callback function to call for each hash db entry + * (not called if QUICK flag is given) + * @param ptr Pointer to data to pass to each callback + * + * @return -1 on error, 0 if hash value not found, and 1 if value was found. + */ +///@todo refactor so as not to duplicate code with sqlite_v1_lookup_raw() +int8_t +lookup_text(TSK_HDB_INFO * hdb_info, const char* hvalue, TSK_HDB_FLAG_ENUM flags, + TSK_HDB_LOOKUP_FN action, void *ptr) +{ + int8_t ret = 0; + TSK_OFF_T offset; + char selectStmt[1024]; + sqlite3_stmt* stmt = NULL; + int len = strlen(hvalue); + + tsk_take_lock(&hdb_info->lock); + + /* Sanity check */ + if (hdb_info->hash_len != len) { + tsk_error_reset(); + tsk_error_set_errno(TSK_ERR_HDB_ARG); + tsk_error_set_errstr("hdb_lookup: Hash passed is different size than expected: %d vs %d", + hdb_info->hash_len, len); + ret = -1; + } else { + if (hdb_info->hash_type == TSK_HDB_HTYPE_MD5_ID) { + snprintf(selectStmt, 1024, + "SELECT md5,database_offset from hashes where md5='%s' limit 1", + hvalue); + } else if (hdb_info->hash_type == TSK_HDB_HTYPE_SHA1_ID) { + snprintf(selectStmt, 1024, + "SELECT sha1,database_offset from hashes where sha1='%s' limit 1", + hvalue); + } else { + tsk_error_reset(); + tsk_error_set_errno(TSK_ERR_HDB_ARG); + tsk_error_set_errstr("Unknown hash type: %d\n", hdb_info->hash_type); + ret = -1; + } + + if (ret != -1) { + prepare_stmt(selectStmt, &stmt, hdb_info->idx_info->idx_struct.idx_sqlite_v1->hIdx_sqlite); + + // Found a match + if (sqlite3_step(stmt) == SQLITE_ROW) { + if ((flags & TSK_HDB_FLAG_QUICK) + || (hdb_info->db_type == TSK_HDB_DBTYPE_IDXONLY_ID)) { + + // There is just an index, so no other info to get + ///@todo Look up a name in the sqlite db + ret = 1; + } else { + // Use offset to get more info + offset = sqlite3_column_int64(stmt, 1); + + if (hdb_info->getentry(hdb_info, hvalue, offset, flags, action, ptr)) { + tsk_error_set_errstr2("hdb_lookup"); + ret = -1; + } else { + ret = 1; + } + } + } + + sqlite3_reset(stmt); + + if (stmt) { + finalize_stmt(stmt); + } + } + } + + tsk_release_lock(&hdb_info->lock); + + return ret; +} + + /** * \ingroup hashdblib * Sets the updateable flag in the hdb_info argument based on querying the index props table. @@ -570,6 +720,8 @@ sqlite_v1_close(TSK_IDX_INFO * idx_info) finalize_stmt(m_stmt); } + m_stmt = NULL; + if (idx_info->idx_struct.idx_sqlite_v1->hIdx_sqlite) { sqlite3_close(idx_info->idx_struct.idx_sqlite_v1->hIdx_sqlite); } diff --git a/tsk/hashdb/tsk_hashdb_i.h b/tsk/hashdb/tsk_hashdb_i.h index e3f63fd518cad4f1b8901ac2dc9e5915c60318d5..fc838eb80b39b7a41ad7a1cabccadea831153148 100644 --- a/tsk/hashdb/tsk_hashdb_i.h +++ b/tsk/hashdb/tsk_hashdb_i.h @@ -66,7 +66,8 @@ extern "C" { #define IDX_HASHSET_UPDATEABLE "Updateable" #define IDX_BINSRCH_HEADER "0000000000000000" #define IDX_SQLITE_V1_HEADER "SQLite format 3" - +// Warning: changing the hash storage type changes the Db schema +#define IDX_SQLITE_STORE_TEXT extern uint8_t hdb_setupindex(TSK_HDB_INFO * hdb_info, uint8_t htype, uint8_t create);