diff --git a/API-CHANGES.txt b/API-CHANGES.txt index a7e27b061071642596307752afebaefae6e7bf86..02329e843cb78a0ba18e12877f78a24023f1d942 100644 --- a/API-CHANGES.txt +++ b/API-CHANGES.txt @@ -1,9 +1,8 @@ Changes to make once we are ready to do a backwards incompatible change. - TSK_SERVICE_ACCOUNT to TSK_ACCOUNT - HashDB to use new TSK_BASE_HASHDB enum instead of its own ENUM -- Java SleuthkitCase.addArtifactType shoudl return different if artifact already exists or getArtifactId should.... -- Java SleuthkitCase.findFilesWhere should return AbstractFile liek findFiles +- Java SleuthkitCase.addArtifactType should return different if artifact already exists or getArtifactId should.... +- Java SleuthkitCase.findFilesWhere should return AbstractFile like findFiles - getUniquePath() should not throw exception. - findFilesInImage should return an enum like TskDB methods differentiating if any data was found or not. -- remove addImageInfo in db_Sqlite that does not take MD5, and/oor make it take IMG_INFO as argument - +- remove addImageInfo in db_Sqlite that does not take MD5, and/or make it take IMG_INFO as argument \ No newline at end of file diff --git a/bindings/java/jni/dataModel_SleuthkitJNI.cpp b/bindings/java/jni/dataModel_SleuthkitJNI.cpp index 58047852fcf26eb79a8a5e75c0b44ba506a8a395..11f431e9ccb1f4100ee8f90ceee33ec355f8a172 100644 --- a/bindings/java/jni/dataModel_SleuthkitJNI.cpp +++ b/bindings/java/jni/dataModel_SleuthkitJNI.cpp @@ -26,7 +26,7 @@ using std::stringstream; using std::for_each; static TSK_HDB_INFO * m_NSRLDb = NULL; -static std::vector<TSK_HDB_INFO *> m_knownbads; +static std::vector<TSK_HDB_INFO *> m_hashDbs; /* * JNI file handle structure encapsulates both @@ -274,7 +274,7 @@ JNIEXPORT jint JNICALL TSK_TCHAR pathT[1024]; toTCHAR(env, pathT, 1024, pathJ); - TSK_HDB_OPEN_ENUM flags = TSK_HDB_OPEN_IDXONLY; + TSK_HDB_OPEN_ENUM flags = TSK_HDB_OPEN_TRY; TSK_HDB_INFO * tempdb = tsk_hdb_open(pathT, flags); if(tempdb == NULL) @@ -302,7 +302,7 @@ JNIEXPORT jint JNICALL TSK_TCHAR pathT[1024]; toTCHAR(env, pathT, 1024, pathJ); - TSK_HDB_OPEN_ENUM flags = TSK_HDB_OPEN_IDXONLY; + TSK_HDB_OPEN_ENUM flags = TSK_HDB_OPEN_TRY; TSK_HDB_INFO * temp = tsk_hdb_open(pathT, flags); if(temp == NULL) @@ -311,19 +311,143 @@ JNIEXPORT jint JNICALL return -1; } - m_knownbads.push_back(temp); + m_hashDbs.push_back(temp); - return m_knownbads.size(); + return m_hashDbs.size(); } /* - * Get the name of the database pointed to by path + * Create a new hash db. * @param env pointer to java environment this was called from * @param obj the java object this was called from * @param pathJ the path to the database + * @return a handle for the database + */ +JNIEXPORT jint JNICALL + Java_org_sleuthkit_datamodel_SleuthkitJNI_newDbKnownBadNat(JNIEnv * env, + jclass obj, jstring pathJ) +{ + TSK_TCHAR pathT[1024]; + toTCHAR(env, pathT, 1024, pathJ); + + TSK_HDB_INFO * temp = tsk_hdb_new(pathT); + + if(temp == NULL) + { + setThrowTskCoreError(env); + return -1; + } + + m_hashDbs.push_back(temp); + + return m_hashDbs.size(); +} + +/* + * Add entry to hash db. + * @param env pointer to java environment this was called from + * @param obj the java object this was called from + * @param filenameJ Name of the file that was hashed (can be empty) + * @param hashMd5J Text of MD5 hash (can be empty) + * @param hashSha1J Text of SHA1 hash (can be empty) + * @param hashSha256J Text of SHA256 hash (can be empty) + * @param dbHandle Which DB. + * @return 1 on error and 0 on success + */ +JNIEXPORT jint JNICALL + Java_org_sleuthkit_datamodel_SleuthkitJNI_addStrDbKnownBadNat(JNIEnv * env, + jclass obj, jstring filenameJ, jstring hashMd5J, jstring hashSha1J, jstring hashSha256J, jint dbHandle) +{ + int8_t retval = 0; + + if((size_t) dbHandle > m_hashDbs.size()) { + setThrowTskCoreError(env, "Invalid database handle"); + retval = 1; + } else { + jboolean isCopy; + const char *md5 = (const char *) env->GetStringUTFChars(hashMd5J, &isCopy); + const char *sha1 = (const char *) env->GetStringUTFChars(hashSha1J, &isCopy); + const char *sha256 = (const char *) env->GetStringUTFChars(hashSha256J, &isCopy); + + TSK_TCHAR filenameT[1024]; + toTCHAR(env, filenameT, 1024, filenameJ); + + TSK_HDB_INFO * db = m_hashDbs.at(dbHandle-1); + + if(db != NULL) { + retval = tsk_hdb_add_str(db, filenameT, md5, sha1, sha256); + + if (retval == 1) { + setThrowTskCoreError(env); + } + } + + env->ReleaseStringUTFChars(hashMd5J, (const char *) md5); + env->ReleaseStringUTFChars(hashSha1J, (const char *) sha1); + env->ReleaseStringUTFChars(hashSha256J, (const char *) sha256); + } + + return retval; +} + +/* + * Get updateable state. + * @param env pointer to java environment this was called from + * @param obj the java object this was called from + * @param dbHandle Which DB. + * @return true if db can be updated + */ +JNIEXPORT jboolean JNICALL + Java_org_sleuthkit_datamodel_SleuthkitJNI_isUpdateableDbKnownBadNat(JNIEnv * env, + jclass obj, jint dbHandle) +{ + bool retval = false; + + if((size_t) dbHandle > m_hashDbs.size()) { + setThrowTskCoreError(env, "Invalid database handle"); + } else { + TSK_HDB_INFO * db = m_hashDbs.at(dbHandle-1); + + if(db != NULL) { + retval = (db->idx_info->updateable == 1) ? true : false; + } + } + return retval; +} + +/* + * Test for index only (no original Db file) legacy (IDX format). + * @param env pointer to java environment this was called from + * @param obj the java object this was called from + * @param dbHandle Which DB. + * @return true if index only AND is legacy + */ +JNIEXPORT jboolean JNICALL + Java_org_sleuthkit_datamodel_SleuthkitJNI_isIdxOnlyHashDbNat(JNIEnv * env, + jclass obj, jint dbHandle) +{ + bool retval = false; + + if((size_t) dbHandle > m_hashDbs.size()) { + setThrowTskCoreError(env, "Invalid database handle"); + } else { + TSK_HDB_INFO * db = m_hashDbs.at(dbHandle-1); + + if(db != NULL) { + retval = (tsk_hdb_is_idxonly(db) == 1) ? true : false; + } + } + return retval; +} + +/* + * Get the name of the database pointed to by path + * @param env pointer to java environment this was called from + * @param obj the java object this was called from + * @param pathJ the path to the database (expects the actual database path, not an index path) */ JNIEXPORT jstring JNICALL - Java_org_sleuthkit_datamodel_SleuthkitJNI_getDbName(JNIEnv * env, + Java_org_sleuthkit_datamodel_SleuthkitJNI_getDbNameByPath(JNIEnv * env, jclass obj, jstring pathJ) { TSK_HDB_OPEN_ENUM flags; @@ -350,6 +474,30 @@ JNIEXPORT jstring JNICALL return jname; } +/* + * Get the name of the database pointed to by path + * @param env pointer to java environment this was called from + * @param obj the java object this was called from + * @param dbHandle Which DB. + */ +JNIEXPORT jstring JNICALL Java_org_sleuthkit_datamodel_SleuthkitJNI_getDbName + (JNIEnv * env, jclass obj, jint dbHandle) +{ + if((size_t) dbHandle > m_hashDbs.size()) { + setThrowTskCoreError(env, "Invalid database handle"); + return env->NewStringUTF("-1"); + } else { + TSK_HDB_INFO * temp = m_hashDbs.at(dbHandle-1); + if (temp == NULL) { + setThrowTskCoreError(env, "Error: database object is null"); + return env->NewStringUTF("-1"); + } + + + jstring jname = env->NewStringUTF(temp->db_name); + return jname; + } +} JNIEXPORT void JNICALL Java_org_sleuthkit_datamodel_SleuthkitJNI_closeDbLookupsNat(JNIEnv * env, @@ -360,9 +508,9 @@ JNIEXPORT void JNICALL m_NSRLDb = NULL; } - for_each(m_knownbads.begin(), m_knownbads.end(), tsk_hdb_close); + for_each(m_hashDbs.begin(), m_hashDbs.end(), tsk_hdb_close); - m_knownbads.clear(); + m_hashDbs.clear(); } /* @@ -402,7 +550,7 @@ JNIEXPORT jint JNICALL Java_org_sleuthkit_datamodel_SleuthkitJNI_nsrlDbLookup JNIEXPORT jint JNICALL Java_org_sleuthkit_datamodel_SleuthkitJNI_knownBadDbLookup (JNIEnv * env, jclass obj, jstring hash, jint dbHandle){ - if((size_t) dbHandle > m_knownbads.size()) { + if((size_t) dbHandle > m_hashDbs.size()) { setThrowTskCoreError(env, "Invalid database handle"); return -1; } @@ -415,7 +563,7 @@ JNIEXPORT jint JNICALL Java_org_sleuthkit_datamodel_SleuthkitJNI_knownBadDbLooku - TSK_HDB_INFO * db = m_knownbads.at(dbHandle-1); + TSK_HDB_INFO * db = m_hashDbs.at(dbHandle-1); if(db != NULL) { int8_t retval = tsk_hdb_lookup_str(db, md5, TSK_HDB_FLAG_QUICK, NULL, NULL); @@ -1349,7 +1497,7 @@ Java_org_sleuthkit_datamodel_SleuthkitJNI_startVerboseLoggingNat * @param dbPathJ path for the database */ JNIEXPORT void JNICALL -Java_org_sleuthkit_datamodel_SleuthkitJNI_createLookupIndexNat (JNIEnv * env, +Java_org_sleuthkit_datamodel_SleuthkitJNI_createLookupIndexByPathNat (JNIEnv * env, jclass obj, jstring dbPathJ) { TSK_TCHAR dbPathT[1024]; @@ -1384,13 +1532,56 @@ Java_org_sleuthkit_datamodel_SleuthkitJNI_createLookupIndexNat (JNIEnv * env, tsk_hdb_close(temp); } +/* + * Create an index for the given database + * @param env pointer to java environment this was called from + * @param obj the java object this was called from + * @param dbHandle handle for the database + */ +JNIEXPORT void JNICALL +Java_org_sleuthkit_datamodel_SleuthkitJNI_createLookupIndexNat (JNIEnv * env, + jclass obj, jint dbHandle) +{ + if((size_t) dbHandle > m_hashDbs.size()) { + setThrowTskCoreError(env, "Invalid database handle"); + return; + } else { + TSK_HDB_INFO * temp = m_hashDbs.at(dbHandle-1); + if (temp == NULL) { + setThrowTskCoreError(env, "Error: database object is null"); + return; + } + + TSK_TCHAR dbType[1024]; + + if(temp->db_type == TSK_HDB_DBTYPE_MD5SUM_ID) { + TSNPRINTF(dbType, 1024, _TSK_T("%") PRIcTSK, TSK_HDB_DBTYPE_MD5SUM_STR); + } + else if(temp->db_type == TSK_HDB_DBTYPE_HK_ID) { + TSNPRINTF(dbType, 1024, _TSK_T("%") PRIcTSK, TSK_HDB_DBTYPE_HK_STR); + } + else if(temp->db_type == TSK_HDB_DBTYPE_ENCASE_ID) { + TSNPRINTF(dbType, 1024, _TSK_T("%") PRIcTSK, TSK_HDB_DBTYPE_ENCASE_STR); + } + else { + TSNPRINTF(dbType, 1024, _TSK_T("%") PRIcTSK, TSK_HDB_DBTYPE_NSRL_MD5_STR); + } + + if (tsk_hdb_makeindex(temp, dbType)) { + setThrowTskCoreError(env, "Error creating index"); + } + + return; + } +} + /* * Check if an index exists for the given database path. * @param env pointer to java environment this was called from * @param obj the java object this was called from * @param dbPathJ path for the database */ -JNIEXPORT jboolean JNICALL Java_org_sleuthkit_datamodel_SleuthkitJNI_lookupIndexExistsNat +JNIEXPORT jboolean JNICALL Java_org_sleuthkit_datamodel_SleuthkitJNI_lookupIndexExistsByPathNat (JNIEnv * env, jclass obj, jstring dbPathJ) { TSK_TCHAR dbPathT[1024]; @@ -1408,6 +1599,30 @@ JNIEXPORT jboolean JNICALL Java_org_sleuthkit_datamodel_SleuthkitJNI_lookupIndex return (jboolean) retval == 1; } +/* + * Check if an index exists for the given database. + * @param env pointer to java environment this was called from + * @param obj the java object this was called from + * @param dbHandle handle for the database + */ +JNIEXPORT jboolean JNICALL Java_org_sleuthkit_datamodel_SleuthkitJNI_lookupIndexExistsNat + (JNIEnv * env, jclass obj, jint dbHandle) { + + if((size_t) dbHandle > m_hashDbs.size()) { + setThrowTskCoreError(env, "Invalid database handle"); + return (jboolean) false; + } else { + TSK_HDB_INFO * temp = m_hashDbs.at(dbHandle-1); + if (temp == NULL) { + return (jboolean) false; + } + + uint8_t retval = tsk_hdb_hasindex(temp, TSK_HDB_HTYPE_MD5_ID); + + return (jboolean) retval == 1; + } +} + /* * Get the size of the index for the database at the given path * @param env pointer to java environment this was called from @@ -1428,7 +1643,7 @@ JNIEXPORT jint JNICALL Java_org_sleuthkit_datamodel_SleuthkitJNI_getIndexSizeNat } if(tsk_hdb_hasindex(temp, TSK_HDB_HTYPE_MD5_ID)) { - return (jint) ((temp->idx_size - temp->idx_off) / (temp->idx_llen)); + return (jint) ((temp->idx_info->idx_struct.idx_binsrch->idx_size - temp->idx_info->idx_struct.idx_binsrch->idx_off) / (temp->idx_info->idx_struct.idx_binsrch->idx_llen)); } diff --git a/bindings/java/jni/dataModel_SleuthkitJNI.h b/bindings/java/jni/dataModel_SleuthkitJNI.h index a61672bec3e934a9e5071fdc4a676e8388f79220..47261d57c71ad71f21d4c2fd2500a9cea62d9f55 100644 --- a/bindings/java/jni/dataModel_SleuthkitJNI.h +++ b/bindings/java/jni/dataModel_SleuthkitJNI.h @@ -65,14 +65,58 @@ JNIEXPORT jint JNICALL Java_org_sleuthkit_datamodel_SleuthkitJNI_setDbNSRLNat JNIEXPORT jint JNICALL Java_org_sleuthkit_datamodel_SleuthkitJNI_addDbKnownBadNat (JNIEnv *, jclass, jstring); +/* + * Class: org_sleuthkit_datamodel_SleuthkitJNI + * Method: newDbKnownBadNat + * Signature: (Ljava/lang/String;)I + */ +JNIEXPORT jint JNICALL + Java_org_sleuthkit_datamodel_SleuthkitJNI_newDbKnownBadNat(JNIEnv * env, + jclass obj, jstring pathJ); + +/* + * Class: org_sleuthkit_datamodel_SleuthkitJNI + * Method: addStrDbKnownBadNat + * Signature: + */ +JNIEXPORT jint JNICALL + Java_org_sleuthkit_datamodel_SleuthkitJNI_addStrDbKnownBadNat(JNIEnv * env, + jclass obj, jstring filenameJ, jstring hashMd5J, jstring hashSha1J, jstring hashSha256J, jint dbHandle); + +/* + * Class: org_sleuthkit_datamodel_SleuthkitJNI + * Method: isUpdateableDbKnownBadNat + * Signature: + */ +JNIEXPORT jboolean JNICALL + Java_org_sleuthkit_datamodel_SleuthkitJNI_isUpdateableDbKnownBadNat(JNIEnv * env, + jclass obj, jint dbHandle); + +/* + * Class: org_sleuthkit_datamodel_SleuthkitJNI + * Method: isIdxOnlyHashDbNat + * Signature: + */ +JNIEXPORT jboolean JNICALL + Java_org_sleuthkit_datamodel_SleuthkitJNI_isIdxOnlyHashDbNat(JNIEnv * env, + jclass obj, jint dbHandle); + /* * Class: org_sleuthkit_datamodel_SleuthkitJNI * Method: getDbName * Signature: (Ljava/lang/String;)Ljava/lang/String; */ -JNIEXPORT jstring JNICALL Java_org_sleuthkit_datamodel_SleuthkitJNI_getDbName +JNIEXPORT jstring JNICALL Java_org_sleuthkit_datamodel_SleuthkitJNI_getDbNameByPath (JNIEnv *, jclass, jstring); +/* + * Class: org_sleuthkit_datamodel_SleuthkitJNI + * Method: getHashDbName + * Signature: + */ +JNIEXPORT jstring JNICALL Java_org_sleuthkit_datamodel_SleuthkitJNI_getDbName + (JNIEnv *, jclass, jint dbHandle); + /* * Class: org_sleuthkit_datamodel_SleuthkitJNI * Method: closeDbLookupsNat @@ -259,19 +303,35 @@ JNIEXPORT void JNICALL Java_org_sleuthkit_datamodel_SleuthkitJNI_closeFileNat /* * Class: org_sleuthkit_datamodel_SleuthkitJNI - * Method: createLookupIndexNat + * Method: createLookupIndexByPathNat * Signature: (Ljava/lang/String;)V */ +JNIEXPORT void JNICALL Java_org_sleuthkit_datamodel_SleuthkitJNI_createLookupIndexByPathNat + (JNIEnv *, jclass, jstring); + +/* + * Class: org_sleuthkit_datamodel_SleuthkitJNI + * Method: createLookupIndexNat + * Signature: + */ JNIEXPORT void JNICALL Java_org_sleuthkit_datamodel_SleuthkitJNI_createLookupIndexNat + (JNIEnv *, jclass, jint); + +/* + * Class: org_sleuthkit_datamodel_SleuthkitJNI + * Method: lookupIndexExistsByPathNat + * Signature: (Ljava/lang/String;)Z + */ +JNIEXPORT jboolean JNICALL Java_org_sleuthkit_datamodel_SleuthkitJNI_lookupIndexExistsByPathNat (JNIEnv *, jclass, jstring); /* * Class: org_sleuthkit_datamodel_SleuthkitJNI * Method: lookupIndexExistsNat - * Signature: (Ljava/lang/String;)Z + * Signature: */ JNIEXPORT jboolean JNICALL Java_org_sleuthkit_datamodel_SleuthkitJNI_lookupIndexExistsNat - (JNIEnv *, jclass, jstring); + (JNIEnv *, jclass, jint); /* * Class: org_sleuthkit_datamodel_SleuthkitJNI diff --git a/bindings/java/src/org/sleuthkit/datamodel/SleuthkitCase.java b/bindings/java/src/org/sleuthkit/datamodel/SleuthkitCase.java index d0604c15532b0466298c232742875fad3779cb43..1ca4fe825b2dc6c6005b6760a8bd609764e2ba30 100644 --- a/bindings/java/src/org/sleuthkit/datamodel/SleuthkitCase.java +++ b/bindings/java/src/org/sleuthkit/datamodel/SleuthkitCase.java @@ -653,6 +653,9 @@ public AddImageProcess makeAddImageProcess(String timezone, boolean processUnall * @param path The path to the database * @return a handle for that database */ + // BC: This is called by IngestModule + // Use jni.openNSRLDatabase instead + @Deprecated public int setNSRLDatabase(String path) throws TskCoreException { return this.caseHandle.setNSRLDatabase(path); } @@ -663,6 +666,8 @@ public int setNSRLDatabase(String path) throws TskCoreException { * @param path The path to the database * @return a handle for that database */ + // use jni.openHashDatabase instead + @Deprecated public int addKnownBadDatabase(String path) throws TskCoreException { return this.caseHandle.addKnownBadDatabase(path); } @@ -673,6 +678,8 @@ public int addKnownBadDatabase(String path) throws TskCoreException { * @throws TskCoreException exception thrown if a critical error occurs * within tsk core */ + // use jni.closeHashDatabases instead + @Deprecated public void clearLookupDatabases() throws TskCoreException { this.caseHandle.clearLookupDatabases(); } @@ -4937,6 +4944,7 @@ void setMd5Hash(AbstractFile file, String md5Hash) throws TskCoreException { * @throws TskCoreException thrown if a critical error occurred within tsk * core */ + @Deprecated public TskData.FileKnown nsrlLookupMd5(String md5Hash) throws TskCoreException { return SleuthkitJNI.nsrlHashLookup(md5Hash); } @@ -4950,6 +4958,7 @@ public TskData.FileKnown nsrlLookupMd5(String md5Hash) throws TskCoreException { * @throws TskCoreException thrown if a critical error occurred within tsk * core */ + @Deprecated public TskData.FileKnown knownBadLookupMd5(String md5Hash, int dbHandle) throws TskCoreException { return SleuthkitJNI.knownBadHashLookup(md5Hash, dbHandle); } diff --git a/bindings/java/src/org/sleuthkit/datamodel/SleuthkitJNI.java b/bindings/java/src/org/sleuthkit/datamodel/SleuthkitJNI.java index 2d75ca64b1d27b8f81a40fdd461f3854154e284f..d0e6853ae2c8d1d4fefd3bcdff6428ebd70db897 100644 --- a/bindings/java/src/org/sleuthkit/datamodel/SleuthkitJNI.java +++ b/bindings/java/src/org/sleuthkit/datamodel/SleuthkitJNI.java @@ -54,7 +54,15 @@ public class SleuthkitJNI { private static native int addDbKnownBadNat(String hashDbPath) throws TskCoreException; - private static native String getDbName(String hashDbPath) throws TskCoreException; + private static native int newDbKnownBadNat(String hashDbPath) throws TskCoreException; + + private static native int addStrDbKnownBadNat(String filename, String hashMd5, String hashSha1, String hashSha256, int dbHandle) throws TskCoreException; + + private static native boolean isUpdateableDbKnownBadNat(int dbHandle); + + private static native String getDbNameByPath(String hashDbPath) throws TskCoreException; + + private static native String getDbName(int dbHandle) throws TskCoreException; private static native void closeDbLookupsNat() throws TskCoreException; @@ -107,10 +115,16 @@ public class SleuthkitJNI { private static native void closeFileNat(long fileHandle); //hash-lookup database functions - private static native void createLookupIndexNat(String dbPath) throws TskCoreException; + private static native void createLookupIndexByPathNat(String dbPath) throws TskCoreException; + + private static native void createLookupIndexNat(int dbHandle) throws TskCoreException; - private static native boolean lookupIndexExistsNat(String dbPath) throws TskCoreException; + private static native boolean lookupIndexExistsByPathNat(String dbPath) throws TskCoreException; + + private static native boolean lookupIndexExistsNat(int dbHandle) throws TskCoreException; + private static native boolean isIdxOnlyHashDbNat(int dbHandle) throws TskCoreException; + //util functions private static native long findDeviceSizeNat(String devicePath) throws TskCoreException; @@ -150,12 +164,16 @@ void free() throws TskCoreException { SleuthkitJNI.closeCaseDbNat(caseDbPointer); } + + /******************** Hash Database Methods ***********************/ /** - * Clear currently set lookup databases within TSK + * Close the currently open lookup databases * * @throws TskCoreException exception thrown if critical error occurs * within TSK */ + // use jni.closeHashDatabases() instead + @Deprecated void clearLookupDatabases() throws TskCoreException { closeDbLookupsNat(); } @@ -166,20 +184,27 @@ void clearLookupDatabases() throws TskCoreException { * @param path The path to the database * @return a handle for that database */ + // BC: Called by SleuthkitCase, which is called by IngestModule + // use jni.openNSRLDatabase() instead + @Deprecated int setNSRLDatabase(String path) throws TskCoreException { return setDbNSRLNat(path); } + /** - * Add the known bad database + * Open a hash bad database * * @param path The path to the database * @return a handle for that database */ + // use jni.openHashDatabase() instead + @Deprecated int addKnownBadDatabase(String path) throws TskCoreException { return addDbKnownBadNat(path); } - + + /** * Start the process of adding a disk image to the case * @@ -574,16 +599,31 @@ public static void closeFile(long fileHandle) { closeFileNat(fileHandle); } + + /****************************** Hash database methods *****************/ /** * Create an index for the given database path. * * @param dbPath The path to the database * @throws TskCoreException if a critical error occurs within TSK core */ + // BC: Called by HashDb. + // use createLookupIndexForHashDatabase instead + @Deprecated public static void createLookupIndex(String dbPath) throws TskCoreException { - createLookupIndexNat(dbPath); + createLookupIndexByPathNat(dbPath); } + /** + * Create an index for the given database path. + * + * @param dbPath The path to the database + * @throws TskCoreException if a critical error occurs within TSK core + */ + public static void createLookupIndexForHashDatabase(int dbHandle) throws TskCoreException { + createLookupIndexNat(dbHandle); + } + /** * Check if an index exists for the given database path. * @@ -591,10 +631,25 @@ public static void createLookupIndex(String dbPath) throws TskCoreException { * @return true if index exists * @throws TskCoreException if a critical error occurs within TSK core */ + // BC: Called by HashDB + // use lookupIndexForHashDatabaseExists instead + @Deprecated public static boolean lookupIndexExists(String dbPath) throws TskCoreException { - return lookupIndexExistsNat(dbPath); + return lookupIndexExistsByPathNat(dbPath); } + + /** + * Check if an index exists for the given database path. + * + * @param dbPath + * @return true if index exists + * @throws TskCoreException if a critical error occurs within TSK core + */ + public static boolean hashDatabaseHasLookupIndex(int dbHandle) throws TskCoreException { + return lookupIndexExistsNat(dbHandle); + } + /** * Set the NSRL database * @@ -602,31 +657,83 @@ public static boolean lookupIndexExists(String dbPath) throws TskCoreException { * @return a handle for that database * @throws TskCoreException if a critical error occurs within TSK core */ + // BC: Does not appear to be called. HashDBINgestModule calls SleuthkitCase.setNSRLDatabase, which calls the version of this in CaseHandle + // use openNSRLDatabase instead + @Deprecated public static int setNSRLDatabase(String path) throws TskCoreException { + return openNSRLDatabase(path); + } + + public static int openNSRLDatabase(String path) throws TskCoreException { return setDbNSRLNat(path); } + /** - * Add the known bad database + * Opens a hash database. Supports the various formats that TSK supports * * @param path The path to the database * @return a handle for that database * @throws TskCoreException if a critical error occurs within TSK core */ + // BC: Does not appear to be called. HashDBIngestModule calls SleuthkitCase.addKnownBad, which calls the version in CaseHandle + // use openHashDatabase instead + @Deprecated public static int addKnownBadDatabase(String path) throws TskCoreException { + return openHashDatabase(path); + } + + public static int openHashDatabase(String path) throws TskCoreException { return addDbKnownBadNat(path); } + + + /** + * Creates a hash database. Will be of the default TSK hash database type. + * + * @param path The path to the database + * @return a handle for that database + * @throws TskCoreException if a critical error occurs within TSK core + */ + public static int createHashDatabase(String path) throws TskCoreException { + return newDbKnownBadNat(path); + } + /** + * Close the currently open lookup databases + * + * @throws TskCoreException exception thrown if critical error occurs + * within TSK + */ + public static void closeHashDatabases() throws TskCoreException { + closeDbLookupsNat(); + } + /** * Get the name of the database * * @param path The path to the database * @throws TskCoreException if a critical error occurs within TSK core */ + // BC: Used by HasDBAddDatabaseDialog an HashDBXML + //use getHashDatabaseName instead + @Deprecated public static String getDatabaseName(String path) throws TskCoreException { - return getDbName(path); + return getDbNameByPath(path); } + + /** + * Get the name of the database + * + * @param dbHandle previously opened hash db handle + * @throws TskCoreException if a critical error occurs within TSK core + */ + public static String getHashDatabaseName(int dbHandle) throws TskCoreException { + return getDbName(dbHandle); + } + + /** * Look up the given hash in the NSRL database * @@ -634,7 +741,14 @@ public static String getDatabaseName(String path) throws TskCoreException { * @return the status of the hash in the NSRL * @throws TskCoreException if a critical error occurs within TSK core */ + // BC: Called by SleuthkitCase -> Deprecated + // use lookupNSRLDatabase instead + @Deprecated public static TskData.FileKnown nsrlHashLookup(String hash) throws TskCoreException { + return lookupInNSRLDatabase(hash); + } + + public static TskData.FileKnown lookupInNSRLDatabase(String hash) throws TskCoreException { return TskData.FileKnown.valueOf((byte) nsrlDbLookup(hash)); } @@ -646,10 +760,39 @@ public static TskData.FileKnown nsrlHashLookup(String hash) throws TskCoreExcept * @return the status of the hash in the known bad database * @throws TskCoreException if a critical error occurs within TSK core */ + // BC: Called by SleuthkitCase -> Deprecated + // use lookupInHashDatabase instead + @Deprecated public static TskData.FileKnown knownBadHashLookup(String hash, int dbHandle) throws TskCoreException { + return lookupInHashDatabase(hash, dbHandle); + } + + public static TskData.FileKnown lookupInHashDatabase(String hash, int dbHandle) throws TskCoreException { return TskData.FileKnown.valueOf((byte) knownBadDbLookup(hash, dbHandle)); } + + /** + * Adds a hash value to a hash database. + * @param filename Name of file (can be null) + * @param md5 Text of MD5 hash (can be null) + * @param sha1 Text of SHA1 hash (can be null) + * @param sha256 Text of SHA256 hash (can be null) + * @param dbHandle Handle to DB + * @throws TskCoreException + */ + public static void addToHashDatabase(String filename, String md5, String sha1, String sha256, int dbHandle) throws TskCoreException { + addStrDbKnownBadNat(filename, md5, sha1, sha256, dbHandle); + } + + public static boolean isUpdateableHashDatabase(int dbHandle) throws TskCoreException { + return isUpdateableDbKnownBadNat(dbHandle); + } + + public static boolean hashDatabaseHasLegacyLookupIndexOnly(int dbHandle) throws TskCoreException { + return isIdxOnlyHashDbNat(dbHandle); + } + /** * Get the size of the index of the given database * @@ -657,6 +800,8 @@ public static TskData.FileKnown knownBadHashLookup(String hash, int dbHandle) th * @return the size of the index or -1 if it doesn't exist * @throws TskCoreException */ + // BC: Does not appear to be used at all -> SHould probably go away. + @Deprecated public static int getIndexSize(String path) throws TskCoreException { return getIndexSizeNat(path); } diff --git a/tools/hashtools/hfind.cpp b/tools/hashtools/hfind.cpp index 0486c25b5f8b33cb456047d136017d77cae4d767..5614a269c83edbc8d15c08a29a6815c701030b8c 100644 --- a/tools/hashtools/hfind.cpp +++ b/tools/hashtools/hfind.cpp @@ -22,13 +22,15 @@ usage() { TFPRINTF(stderr, _TSK_T - ("usage: %s [-eqV] [-f lookup_file] [-i db_type] db_file [hashes]\n"), + ("usage: %s [-eqVa] [-c db_name] [-f lookup_file] [-i db_type] db_file [hashes]\n"), progname); tsk_fprintf(stderr, "\t-e: Extended mode - where values other than just the name are printed\n"); tsk_fprintf(stderr, "\t-q: Quick mode - where a 1 is printed if it is found, else 0\n"); tsk_fprintf(stderr, "\t-V: Print version to STDOUT\n"); + tsk_fprintf(stderr, "\t-c db_name: Create blank index with the given name.\n"); + tsk_fprintf(stderr, "\t-a: Add given hashes to the database.\n"); tsk_fprintf(stderr, "\t-f lookup_file: File with one hash per line to lookup\n"); tsk_fprintf(stderr, @@ -73,7 +75,9 @@ main(int argc, char ** argv1) unsigned int flags = 0; TSK_HDB_INFO *hdb_info; TSK_TCHAR **argv; - + bool create = false; + bool addHash = false; + #ifdef TSK_WIN32 // On Windows, get the wide arguments (mingw doesn't support wmain) argv = CommandLineToArgvW(GetCommandLineW(), &argc); @@ -88,7 +92,7 @@ main(int argc, char ** argv1) progname = argv[0]; setlocale(LC_ALL, ""); - while ((ch = GETOPT(argc, argv, _TSK_T("ef:i:qV"))) > 0) { + while ((ch = GETOPT(argc, argv, _TSK_T("cef:i:aqV"))) > 0) { switch (ch) { case _TSK_T('e'): flags |= TSK_HDB_FLAG_EXT; @@ -102,6 +106,14 @@ main(int argc, char ** argv1) idx_type = OPTARG; break; + case _TSK_T('c'): + create = true; + break; + + case _TSK_T('a'): + addHash = true; + break; + case _TSK_T('q'): flags |= TSK_HDB_FLAG_QUICK; break; @@ -123,9 +135,26 @@ main(int argc, char ** argv1) db_file = argv[OPTIND++]; - if ((hdb_info = tsk_hdb_open(db_file, TSK_HDB_OPEN_NONE)) == NULL) { - tsk_error_print(stderr); - return 1; + // Make a new database (creates an index from scratch) + if (create) { + if ((hdb_info = tsk_hdb_new(db_file)) == NULL) { + tsk_error_print(stderr); + return 1; + } else { + printf("New index %"PRIttocTSK" created.\n", db_file); + return 0; + } + } else { + // Open an existing database + TSK_HDB_OPEN_ENUM flags = TSK_HDB_OPEN_NONE; + if(addHash) { + flags = TSK_HDB_OPEN_IDXONLY; + } + + if ((hdb_info = tsk_hdb_open(db_file, flags)) == NULL) { + tsk_error_print(stderr); + return 1; + } } /* What mode are we going to run in @@ -191,19 +220,33 @@ main(int argc, char ** argv1) } htmp[i] = '\0'; - /* Perform lookup */ - retval = - tsk_hdb_lookup_str(hdb_info, (const char *)htmp, - (TSK_HDB_FLAG_ENUM)flags, lookup_act, NULL); - if (retval == -1) { - tsk_error_print(stderr); - return 1; - } - if (flags & TSK_HDB_FLAG_QUICK) { - printf("%d\n", retval); - } - else if (retval == 0) { - print_notfound(htmp); + if (addHash) { + // Write a new hash to the database/index, if it's updateable + //@todo support sha1 and sha2-256 + retval = tsk_hdb_add_str(hdb_info, NULL, (const char *)htmp, NULL, NULL); + if (retval == 1) { + printf("There was an error adding the hash.\n"); + tsk_error_print(stderr); + return 1; + } else if (retval == -1) { + printf("Database is not updateable.\n"); + } else if (retval == 0) { + printf("Hash %s added.\n", htmp); + } + } else { + /* Perform lookup */ + retval = tsk_hdb_lookup_str(hdb_info, (const char *)htmp, + (TSK_HDB_FLAG_ENUM)flags, lookup_act, NULL); + if (retval == -1) { + tsk_error_print(stderr); + return 1; + } + if (flags & TSK_HDB_FLAG_QUICK) { + printf("%d\n", retval); + } + else if (retval == 0) { + print_notfound(htmp); + } } OPTIND++; } diff --git a/tsk/hashdb/Makefile.am b/tsk/hashdb/Makefile.am index 7a1c074edfaca419310822544286298b7ac93333..7ab062132f0d2d6c5b11d30e513387ae0db6bb78 100644 --- a/tsk/hashdb/Makefile.am +++ b/tsk/hashdb/Makefile.am @@ -2,11 +2,13 @@ AM_CPPFLAGS = -I../.. -I$(srcdir)/../.. -Wall EXTRA_DIST = .indent.pro noinst_LTLIBRARIES = libtskhashdb.la -libtskhashdb_la_SOURCES = tm_lookup.c md5sum_index.c nsrl_index.c \ - hk_index.c idxonly_index.c encase_index.c tsk_hashdb_i.h +libtskhashdb_la_SOURCES = binsrch_index.c sqlite_index.cpp \ + tm_lookup.cpp md5sum.c nsrl.c \ + hdb_open.cpp hdb_index.cpp \ + hashkeeper.c idxonly.c encase.c tsk_hashdb_i.h indent: - indent *.c *.h + indent *.cpp *.c *.h clean-local: -rm -f *.c~ *.h~ diff --git a/tsk/hashdb/binsrch_index.c b/tsk/hashdb/binsrch_index.c new file mode 100644 index 0000000000000000000000000000000000000000..24f618037f20c0bc07458114120207f79d23071e --- /dev/null +++ b/tsk/hashdb/binsrch_index.c @@ -0,0 +1,700 @@ + +/* + * The Sleuth Kit + * + * Brian Carrier [carrier <at> sleuthkit [dot] org] + * Copyright (c) 2003-2011 Brian Carrier. All rights reserved + * + * + * This software is distributed under the Common Public License 1.0 + * + */ + +#include "tsk_hashdb_i.h" + +/** + * \file binsrch_index.c + * Contains functions for creating the original binary search / ASCII index + * and looking up values in it. + */ + +/** Initialize the TSK hash DB index file. This creates the intermediate file, + * which will have entries added to it. This file must be sorted before the + * process is finished. + * + * @param hdb_info Hash database state structure + * @param htype String of index type to create + * + * @return 1 on error and 0 on success + * + */ + uint8_t +binsrch_initialize(TSK_HDB_INFO * hdb_info, TSK_TCHAR * htype) +{ + // Creating plain text indices is unsupported + tsk_error_reset(); + tsk_error_set_errno(TSK_ERR_HDB_UNSUPTYPE); + tsk_error_set_errstr("binsrch_initialize: Creating plain text indices is unsupported."); + return 1; +} + +/** + * Add a string entry to the intermediate index file. + * + * @param hdb_info Hash database state info + * @param hvalue String of hash value to add + * @param offset Byte offset of hash entry in original database. + * @return 1 on error and 0 on success + */ + uint8_t +binsrch_addentry(TSK_HDB_INFO * hdb_info, char *hvalue, + TSK_OFF_T offset) +{ + // Creating plain text indices is unsupported + tsk_error_reset(); + tsk_error_set_errno(TSK_ERR_HDB_UNSUPTYPE); + tsk_error_set_errstr("binsrch_addentry: Creating plain text indices is unsupported."); + return 1; +} + +/** + * Add a binary entry to the intermediate index file. + * + * @param hdb_info Hash database state info + * @param hvalue Array of integers 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 +binsrch_addentry_bin(TSK_HDB_INFO * hdb_info, unsigned char *hvalue, int hlen, + TSK_OFF_T offset) +{ + // Creating plain text indices is unsupported + tsk_error_reset(); + tsk_error_set_errno(TSK_ERR_HDB_UNSUPTYPE); + tsk_error_set_errstr("binsrch_addentry_bin: Creating plain text indices is unsupported."); + return 1; +} + +/** + * Finalize index creation process by sorting the index and removing the + * intermediate temp file. + * + * @param hdb_info Hash database state info structure. + * @return 1 on error and 0 on success + */ + uint8_t +binsrch_finalize(TSK_HDB_INFO * hdb_info) +{ + // Creating plain text indices is unsupported + tsk_error_reset(); + tsk_error_set_errno(TSK_ERR_HDB_UNSUPTYPE); + tsk_error_set_errstr("binsrch_finalize: Creating plain text indices is unsupported."); + return 1; +} + + +/** \internal + * Setup the internal variables to read an index. This + * opens the index and sets the needed size information. + * + * @param hdb_info Hash database to analyze + * @param hash The hash type that was used to make the index. + * + * @return 1 on error and 0 on success + */ + uint8_t +binsrch_open(TSK_HDB_INFO * hdb_info, TSK_IDX_INFO * idx_info, uint8_t htype) +{ + char head[TSK_HDB_MAXLEN]; + char head2[TSK_HDB_MAXLEN]; + char *ptr; + + + if ((idx_info->idx_struct.idx_binsrch = + (TSK_IDX_BINSRCH *) tsk_malloc + (sizeof(TSK_IDX_BINSRCH))) == NULL) { + tsk_error_reset(); + tsk_error_set_errno(TSK_ERR_HDB_ARG); + tsk_error_set_errstr( + "binsrch_open: Malloc error"); + return 1; + } + + if ((htype != TSK_HDB_HTYPE_MD5_ID) + && (htype != TSK_HDB_HTYPE_SHA1_ID)) { + tsk_error_reset(); + tsk_error_set_errno(TSK_ERR_HDB_ARG); + tsk_error_set_errstr( + "binsrch_open: Invalid hash type : %d", htype); + return 1; + } + + idx_info->idx_struct.idx_binsrch->idx_llen = TSK_HDB_IDX_LEN(htype); + + /* Verify the index exists, get its size, and open it */ +#ifdef TSK_WIN32 + { + HANDLE hWin; + DWORD szLow, szHi; + + if (-1 == GetFileAttributes(idx_info->idx_fname)) { + tsk_error_reset(); + tsk_error_set_errno(TSK_ERR_HDB_MISSING); + tsk_error_set_errstr( + "binsrch_open: Error finding index file: %"PRIttocTSK, + idx_info->idx_fname); + return 1; + } + + if ((hWin = CreateFile(idx_info->idx_fname, GENERIC_READ, + FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0)) == + INVALID_HANDLE_VALUE) { + tsk_error_reset(); + tsk_error_set_errno(TSK_ERR_HDB_OPEN); + tsk_error_set_errstr( + "binsrch_open: Error opening index file: %"PRIttocTSK, + idx_info->idx_fname); + return 1; + } + idx_info->idx_struct.idx_binsrch->hIdx = + _fdopen(_open_osfhandle((intptr_t) hWin, _O_RDONLY), "r"); + if (idx_info->idx_struct.idx_binsrch->hIdx == NULL) { + tsk_error_reset(); + tsk_error_set_errno(TSK_ERR_HDB_OPEN); + tsk_error_set_errstr( + "binsrch_open: Error converting Windows handle to C handle"); + return 1; + } + + szLow = GetFileSize(hWin, &szHi); + if (szLow == 0xffffffff) { + tsk_error_reset(); + tsk_error_set_errno(TSK_ERR_HDB_OPEN); + tsk_error_set_errstr( + "binsrch_open: Error getting size of index file: %"PRIttocTSK" - %d", + idx_info->idx_fname, (int)GetLastError()); + return 1; + } + idx_info->idx_struct.idx_binsrch->idx_size = szLow | ((uint64_t) szHi << 32); + } + +#else + { + struct stat sb; + if (stat(idx_info->idx_fname, &sb) < 0) { + tsk_error_reset(); + tsk_error_set_errno(TSK_ERR_HDB_MISSING); + tsk_error_set_errstr( + "binsrch_open: Error finding index file: %s", + idx_info->idx_fname); + return 1; + } + idx_info->idx_struct.idx_binsrch->idx_size = sb.st_size; + + if (NULL == (idx_info->idx_struct.idx_binsrch->hIdx = fopen(idx_info->idx_fname, "r"))) { + tsk_error_reset(); + tsk_error_set_errno(TSK_ERR_HDB_OPEN); + tsk_error_set_errstr( + "binsrch_open: Error opening index file: %s", + idx_info->idx_fname); + return 1; + } + } +#endif + + + /* Do some testing on the first line */ + if (NULL == fgets(head, TSK_HDB_MAXLEN, idx_info->idx_struct.idx_binsrch->hIdx)) { + tsk_error_reset(); + tsk_error_set_errno(TSK_ERR_HDB_READIDX); + tsk_error_set_errstr( + "hdb_setupindex: Header line of index file"); + return 1; + } + + if (strncmp(head, TSK_HDB_IDX_HEAD_TYPE_STR, strlen(TSK_HDB_IDX_HEAD_TYPE_STR)) + != 0) { + tsk_error_reset(); + tsk_error_set_errno(TSK_ERR_HDB_UNKTYPE); + tsk_error_set_errstr( + "hdb_setupindex: Invalid index file: Missing header line"); + return 1; + } + + /* Do some testing on the second line */ + if (NULL == fgets(head2, TSK_HDB_MAXLEN, idx_info->idx_struct.idx_binsrch->hIdx)) { + tsk_error_reset(); + tsk_error_set_errno(TSK_ERR_HDB_READIDX); + tsk_error_set_errstr( + "hdb_setupindex: Error reading line 2 of index file"); + return 1; + } + + /* Set the offset to the start of the index entries */ + if (strncmp(head2, TSK_HDB_IDX_HEAD_NAME_STR, strlen(TSK_HDB_IDX_HEAD_NAME_STR)) + != 0) { + idx_info->idx_struct.idx_binsrch->idx_off = (uint16_t) (strlen(head)); + } else { + idx_info->idx_struct.idx_binsrch->idx_off = (uint16_t) (strlen(head) + strlen(head2)); + } + + + /* Skip the space */ + ptr = &head[strlen(TSK_HDB_IDX_HEAD_TYPE_STR) + 1]; + + ptr[strlen(ptr) - 1] = '\0'; + if ((ptr[strlen(ptr) - 1] == 10) || (ptr[strlen(ptr) - 1] == 13)) { + ptr[strlen(ptr) - 1] = '\0'; + idx_info->idx_struct.idx_binsrch->idx_llen++; // make the expected index length longer to account for different cr/nl/etc. + } + + /* Verify the header value in the index */ + if (strcmp(ptr, TSK_HDB_DBTYPE_NSRL_STR) == 0) { + if ((hdb_info->db_type != TSK_HDB_DBTYPE_NSRL_ID) && + (hdb_info->db_type != TSK_HDB_DBTYPE_IDXONLY_ID)) { + tsk_error_reset(); + tsk_error_set_errno(TSK_ERR_HDB_UNKTYPE); + tsk_error_set_errstr( + "hdb_indexsetup: DB detected as %s, index type has NSRL", + ptr); + return 1; + } + } + else if (strcmp(ptr, TSK_HDB_DBTYPE_MD5SUM_STR) == 0) { + if ((hdb_info->db_type != TSK_HDB_DBTYPE_MD5SUM_ID) && + (hdb_info->db_type != TSK_HDB_DBTYPE_IDXONLY_ID)) { + tsk_error_reset(); + tsk_error_set_errno(TSK_ERR_HDB_UNKTYPE); + tsk_error_set_errstr( + "hdb_indexsetup: DB detected as %s, index type has MD5SUM", + ptr); + return 1; + } + } + else if (strcmp(ptr, TSK_HDB_DBTYPE_HK_STR) == 0) { + if ((hdb_info->db_type != TSK_HDB_DBTYPE_HK_ID) && + (hdb_info->db_type != TSK_HDB_DBTYPE_IDXONLY_ID)) { + tsk_error_reset(); + tsk_error_set_errno(TSK_ERR_HDB_UNKTYPE); + tsk_error_set_errstr( + "hdb_indexsetup: DB detected as %s, index type has hashkeeper", + ptr); + return 1; + } + } + else if (hdb_info->db_type != TSK_HDB_DBTYPE_IDXONLY_ID) { + tsk_error_reset(); + tsk_error_set_errno(TSK_ERR_HDB_UNKTYPE); + tsk_error_set_errstr( + "hdb_setupindex: Unknown Database Type in index header: %s", + ptr); + return 1; + } + + /* Do some sanity checking */ + if (((idx_info->idx_struct.idx_binsrch->idx_size - idx_info->idx_struct.idx_binsrch->idx_off) % idx_info->idx_struct.idx_binsrch->idx_llen) != + 0) { + tsk_error_reset(); + tsk_error_set_errno(TSK_ERR_HDB_CORRUPT); + tsk_error_set_errstr( + "hdb_setupindex: Error, size of index file is not a multiple of row size"); + return 1; + } + + /* allocate a buffer for a row */ + if ((idx_info->idx_struct.idx_binsrch->idx_lbuf = tsk_malloc(idx_info->idx_struct.idx_binsrch->idx_llen + 1)) == NULL) { + return 1; + } + + return 0; +} + + + +/** + * \ingroup hashdblib + * Search the index for a text/ASCII hash value + * + * @param hdb_info Open hash database (with index) + * @param hash Hash value to search for (NULL terminated string) + * @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. + */ +int8_t +binsrch_lookup_str(TSK_HDB_INFO * hdb_info, const char *hash, + TSK_HDB_FLAG_ENUM flags, TSK_HDB_LOOKUP_FN action, + void *ptr) +{ + TSK_OFF_T poffset; + TSK_OFF_T up; // Offset of the first byte past the upper limit that we are looking in + TSK_OFF_T low; // offset of the first byte of the lower limit that we are looking in + int cmp; + uint8_t wasFound = 0; + size_t i; + uint8_t htype; + + + /* Sanity checks on the hash input */ + if (strlen(hash) == TSK_HDB_HTYPE_MD5_LEN) { + htype = TSK_HDB_HTYPE_MD5_ID; + } + else if (strlen(hash) == TSK_HDB_HTYPE_SHA1_LEN) { + htype = TSK_HDB_HTYPE_SHA1_ID; + } + else { + tsk_error_reset(); + tsk_error_set_errno(TSK_ERR_HDB_ARG); + tsk_error_set_errstr( + "hdb_lookup: Invalid hash length: %s", hash); + return -1; + } + + for (i = 0; i < strlen(hash); i++) { + if (isxdigit((int) hash[i]) == 0) { + tsk_error_reset(); + tsk_error_set_errno(TSK_ERR_HDB_ARG); + tsk_error_set_errstr( + "hdb_lookup: Invalid hash value (hex only): %s", + hash); + return -1; + } + } + + /* Sanity check */ + if (hdb_info->hash_len != strlen(hash)) { + 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 %Zd)", + hdb_info->hash_len, strlen(hash)); + return -1; + } + + + low = hdb_info->idx_info->idx_struct.idx_binsrch->idx_off; + up = hdb_info->idx_info->idx_struct.idx_binsrch->idx_size; + + poffset = 0; + + // We have to lock access to idx_info->idx_struct.idx_binsrch->idx_lbuf, but since we're in a loop, + // I'm assuming one lock up front is better than many inside. + tsk_take_lock(&hdb_info->lock); + + while (1) { + TSK_OFF_T offset; + + /* If top and bottom are the same, it's not there */ + if (up == low) { + tsk_release_lock(&hdb_info->lock); + return 0; + } + + /* Get the middle of the windows that we are looking at */ + offset = rounddown(((up - low) / 2), hdb_info->idx_info->idx_struct.idx_binsrch->idx_llen); + + /* Sanity Check */ + if ((offset % hdb_info->idx_info->idx_struct.idx_binsrch->idx_llen) != 0) { + tsk_release_lock(&hdb_info->lock); + tsk_error_reset(); + tsk_error_set_errno(TSK_ERR_HDB_CORRUPT); + tsk_error_set_errstr( + "hdb_lookup: Error, new offset is not a multiple of the line length"); + return -1; + } + + /* The middle offset is relative to the low offset, so add them */ + offset += low; + + /* If we didn't move, then it's not there */ + if (poffset == offset) { + tsk_release_lock(&hdb_info->lock); + return 0; + } + + /* Seek to the offset and read it */ + if (0 != fseeko(hdb_info->idx_info->idx_struct.idx_binsrch->hIdx, offset, SEEK_SET)) { + tsk_release_lock(&hdb_info->lock); + tsk_error_reset(); + tsk_error_set_errno(TSK_ERR_HDB_READIDX); + tsk_error_set_errstr( + "hdb_lookup: Error seeking in search: %" PRIuOFF, + offset); + return -1; + } + + if (NULL == + fgets(hdb_info->idx_info->idx_struct.idx_binsrch->idx_lbuf, (int) hdb_info->idx_info->idx_struct.idx_binsrch->idx_llen + 1, + hdb_info->idx_info->idx_struct.idx_binsrch->hIdx)) { + if (feof(hdb_info->idx_info->idx_struct.idx_binsrch->hIdx)) { + tsk_release_lock(&hdb_info->lock); + return 0; + } + tsk_release_lock(&hdb_info->lock); + tsk_error_reset(); + tsk_error_set_errno(TSK_ERR_HDB_READIDX); + tsk_error_set_errstr( + "Error reading index file: %lu", + (unsigned long) offset); + return -1; + } + + /* Sanity Check */ + if ((strlen(hdb_info->idx_info->idx_struct.idx_binsrch->idx_lbuf) < hdb_info->idx_info->idx_struct.idx_binsrch->idx_llen) || + (hdb_info->idx_info->idx_struct.idx_binsrch->idx_lbuf[hdb_info->hash_len] != '|')) { + tsk_release_lock(&hdb_info->lock); + tsk_error_reset(); + tsk_error_set_errno(TSK_ERR_HDB_CORRUPT); + tsk_error_set_errstr( + "Invalid line in index file: %lu (%s)", + (unsigned long) (offset / hdb_info->idx_info->idx_struct.idx_binsrch->idx_llen), + hdb_info->idx_info->idx_struct.idx_binsrch->idx_lbuf); + return -1; + } + + /* Set the delimter to NULL so we can treat the hash as a string */ + hdb_info->idx_info->idx_struct.idx_binsrch->idx_lbuf[hdb_info->hash_len] = '\0'; + cmp = strcasecmp(hdb_info->idx_info->idx_struct.idx_binsrch->idx_lbuf, hash); + + /* The one we just read is too small, so set the new lower bound + * at the start of the next row */ + if (cmp < 0) { + low = offset + hdb_info->idx_info->idx_struct.idx_binsrch->idx_llen; + } + + /* The one we just read is too big, so set the upper bound at this + * entry */ + else if (cmp > 0) { + up = offset; + } + + /* We found it */ + else { + wasFound = 1; + + if ((flags & TSK_HDB_FLAG_QUICK) + || (hdb_info->db_type == TSK_HDB_DBTYPE_IDXONLY_ID)) { + tsk_release_lock(&hdb_info->lock); + return 1; + } + else { + TSK_OFF_T tmpoff, db_off; + +#ifdef TSK_WIN32 + db_off = + _atoi64(&hdb_info->idx_info->idx_struct.idx_binsrch->idx_lbuf[hdb_info->hash_len + 1]); +#else + db_off = + strtoull(&hdb_info->idx_info->idx_struct.idx_binsrch->idx_lbuf[hdb_info->hash_len + 1], + NULL, 10); +#endif + + /* Print the one that we found first */ + if (hdb_info-> + getentry(hdb_info, hash, db_off, flags, action, ptr)) { + tsk_release_lock(&hdb_info->lock); + tsk_error_set_errstr2( "hdb_lookup"); + return -1; + } + + + /* there could be additional entries both before and after + * this entry - but we can restrict ourselves to the up + * and low bounds from our previous hunting + */ + + tmpoff = offset - hdb_info->idx_info->idx_struct.idx_binsrch->idx_llen; + while (tmpoff >= low) { + + /* Break if we are at the header */ + if (tmpoff <= 0) + break; + + if (0 != fseeko(hdb_info->idx_info->idx_struct.idx_binsrch->hIdx, tmpoff, SEEK_SET)) { + tsk_release_lock(&hdb_info->lock); + tsk_error_reset(); + tsk_error_set_errno(TSK_ERR_HDB_READIDX); + tsk_error_set_errstr( + "hdb_lookup: Error seeking for prev entries: %" + PRIuOFF, tmpoff); + return -1; + } + + if (NULL == + fgets(hdb_info->idx_info->idx_struct.idx_binsrch->idx_lbuf, + (int) hdb_info->idx_info->idx_struct.idx_binsrch->idx_llen + 1, + hdb_info->idx_info->idx_struct.idx_binsrch->hIdx)) { + tsk_release_lock(&hdb_info->lock); + tsk_error_reset(); + tsk_error_set_errno(TSK_ERR_HDB_READIDX); + tsk_error_set_errstr( + "Error reading index file (prev): %lu", + (unsigned long) tmpoff); + return -1; + } + else if (strlen(hdb_info->idx_info->idx_struct.idx_binsrch->idx_lbuf) < + hdb_info->idx_info->idx_struct.idx_binsrch->idx_llen) { + tsk_release_lock(&hdb_info->lock); + tsk_error_reset(); + tsk_error_set_errno(TSK_ERR_HDB_CORRUPT); + tsk_error_set_errstr( + "Invalid index file line (prev): %lu", + (unsigned long) tmpoff); + return -1; + } + + hdb_info->idx_info->idx_struct.idx_binsrch->idx_lbuf[hdb_info->hash_len] = '\0'; + if (strcasecmp(hdb_info->idx_info->idx_struct.idx_binsrch->idx_lbuf, hash) != 0) { + break; + } + +#ifdef TSK_WIN32 + db_off = + _atoi64(&hdb_info-> + idx_info->idx_struct.idx_binsrch->idx_lbuf[hdb_info->hash_len + 1]); +#else + + db_off = + strtoull(&hdb_info-> + idx_info->idx_struct.idx_binsrch->idx_lbuf[hdb_info->hash_len + 1], NULL, + 10); +#endif + if (hdb_info-> + getentry(hdb_info, hash, db_off, flags, action, + ptr)) { + tsk_release_lock(&hdb_info->lock); + return -1; + } + tmpoff -= hdb_info->idx_info->idx_struct.idx_binsrch->idx_llen; + } + + /* next entries */ + tmpoff = offset + hdb_info->idx_info->idx_struct.idx_binsrch->idx_llen; + while (tmpoff < up) { + + if (0 != fseeko(hdb_info->idx_info->idx_struct.idx_binsrch->hIdx, tmpoff, SEEK_SET)) { + tsk_release_lock(&hdb_info->lock); + tsk_error_reset(); + tsk_error_set_errno(TSK_ERR_HDB_READIDX); + tsk_error_set_errstr( + "hdb_lookup: Error seeking for next entries: %" + PRIuOFF, tmpoff); + return -1; + } + + if (NULL == + fgets(hdb_info->idx_info->idx_struct.idx_binsrch->idx_lbuf, + (int) hdb_info->idx_info->idx_struct.idx_binsrch->idx_llen + 1, + hdb_info->idx_info->idx_struct.idx_binsrch->hIdx)) { + if (feof(hdb_info->idx_info->idx_struct.idx_binsrch->hIdx)) + break; + tsk_release_lock(&hdb_info->lock); + tsk_error_reset(); + tsk_error_set_errno(TSK_ERR_HDB_READIDX); + tsk_error_set_errstr( + "Error reading index file (next): %lu", + (unsigned long) tmpoff); + return -1; + } + else if (strlen(hdb_info->idx_info->idx_struct.idx_binsrch->idx_lbuf) < + hdb_info->idx_info->idx_struct.idx_binsrch->idx_llen) { + tsk_release_lock(&hdb_info->lock); + tsk_error_reset(); + tsk_error_set_errno(TSK_ERR_HDB_CORRUPT); + tsk_error_set_errstr( + "Invalid index file line (next): %lu", + (unsigned long) tmpoff); + return -1; + } + + hdb_info->idx_info->idx_struct.idx_binsrch->idx_lbuf[hdb_info->hash_len] = '\0'; + if (strcasecmp(hdb_info->idx_info->idx_struct.idx_binsrch->idx_lbuf, hash) != 0) { + break; + } +#ifdef TSK_WIN32 + db_off = + _atoi64(&hdb_info-> + idx_info->idx_struct.idx_binsrch->idx_lbuf[hdb_info->hash_len + 1]); +#else + db_off = + strtoull(&hdb_info-> + idx_info->idx_struct.idx_binsrch->idx_lbuf[hdb_info->hash_len + 1], NULL, + 10); +#endif + if (hdb_info-> + getentry(hdb_info, hash, db_off, flags, action, + ptr)) { + tsk_release_lock(&hdb_info->lock); + return -1; + } + + tmpoff += hdb_info->idx_info->idx_struct.idx_binsrch->idx_llen; + } + } + break; + } + poffset = offset; + } + tsk_release_lock(&hdb_info->lock); + + return wasFound; +} + +/** + * \ingroup hashdblib + * Search the index for the given hash value given (in binary form). + * + * @param hdb_info Open hash database (with index) + * @param hash Array with binary hash value to search for + * @param len Number of bytes in binary hash value + * @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. + */ +int8_t +binsrch_lookup_raw(TSK_HDB_INFO * hdb_info, uint8_t * hash, uint8_t len, + TSK_HDB_FLAG_ENUM flags, + TSK_HDB_LOOKUP_FN action, void *ptr) +{ + char hashbuf[TSK_HDB_HTYPE_SHA1_LEN + 1]; + int i; + static const char hex[] = "0123456789abcdef"; + + if (2 * len > TSK_HDB_HTYPE_SHA1_LEN) { + tsk_error_reset(); + tsk_error_set_errno(TSK_ERR_HDB_ARG); + tsk_error_set_errstr( + "tsk_hdb_lookup_raw: hash value too long\n"); + return -1; + } + + for (i = 0; i < len; i++) { + hashbuf[2 * i] = hex[(hash[i] >> 4) & 0xf]; + hashbuf[2 * i + 1] = hex[hash[i] & 0xf]; + } + hashbuf[2 * len] = '\0'; + + return tsk_hdb_lookup_str(hdb_info, hashbuf, flags, action, ptr); +} + +int8_t +binsrch_get_properties(TSK_HDB_INFO * hdb_info) +{ + // Always false + hdb_info->idx_info->updateable = 0; + return 0; +} + +void +binsrch_close(TSK_IDX_INFO * idx_info) +{ + //Nothing to do here... +} diff --git a/tsk/hashdb/encase_index.c b/tsk/hashdb/encase.c similarity index 100% rename from tsk/hashdb/encase_index.c rename to tsk/hashdb/encase.c diff --git a/tsk/hashdb/hk_index.c b/tsk/hashdb/hashkeeper.c similarity index 99% rename from tsk/hashdb/hk_index.c rename to tsk/hashdb/hashkeeper.c index 4fc284e5c461d415f7b97a556118fbc5caf492b7..c7776b6b9bda726ed0d9117844fadd1e968a70e2 100644 --- a/tsk/hashdb/hk_index.c +++ b/tsk/hashdb/hashkeeper.c @@ -401,7 +401,7 @@ hk_getentry(TSK_HDB_INFO * hdb_info, const char *hash, TSK_OFF_T offset, } if (NULL == - fgets(hdb_info->idx_lbuf, TSK_HDB_MAXLEN, hdb_info->hDb)) { + fgets(buf, TSK_HDB_MAXLEN, hdb_info->hDb)) { if (feof(hdb_info->hDb)) { break; } diff --git a/tsk/hashdb/hdb_index.cpp b/tsk/hashdb/hdb_index.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b96c2a33ffa9c0c2d058640df35635faef378e69 --- /dev/null +++ b/tsk/hashdb/hdb_index.cpp @@ -0,0 +1,702 @@ +/* + * The Sleuth Kit + * + * Brian Carrier [carrier <at> sleuthkit [dot] org] + * Copyright (c) 2003-2013 Brian Carrier. All rights reserved + * + * + * This software is distributed under the Common Public License 1.0 + */ + +#include "tsk_hashdb_i.h" + + +/** + * \file hdb_index.cpp + * Contains the code to make indexes for databases. + */ + + +/** + * Open a file and return a handle to it. + */ +static FILE * +tsk_idx_open_file(TSK_TCHAR *idx_fname) +{ + if (idx_fname == NULL) { + return NULL; + } + + FILE * idx = NULL; + +#ifdef TSK_WIN32 + { + HANDLE hWin; + //DWORD szLow, szHi; + + if (-1 == GetFileAttributes(idx_fname)) { + //tsk_release_lock(&idx_info->lock); + tsk_error_reset(); + tsk_error_set_errno(TSK_ERR_HDB_MISSING); + tsk_error_set_errstr( + "tsk_idx_open_file: Error finding index file: %"PRIttocTSK, + idx_fname); + return NULL; + } + + if ((hWin = CreateFile(idx_fname, GENERIC_READ, + FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0)) == + INVALID_HANDLE_VALUE) { + //tsk_release_lock(&idx_info->lock); + tsk_error_reset(); + tsk_error_set_errno(TSK_ERR_HDB_OPEN); + tsk_error_set_errstr( + "tsk_idx_open_file: Error opening index file: %"PRIttocTSK, + idx_fname); + return NULL; + } + + idx = _fdopen(_open_osfhandle((intptr_t) hWin, _O_RDONLY), "r"); + } +#else + { + idx = fopen(idx_fname, "r"); + } +#endif + + return idx; +} + + +static void +tsk_idx_close_file(FILE * idx) +{ + if (idx == NULL) { + return; + } + + // fclose should work on all platforms: + if (fclose(idx) != 0) { + tsk_error_set_errstr2("tsk_idx_close_file: Error closing index file object."); + } +} + +/** + * 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. + * @return NULL on error, TSK_IDX_INFO instance on success + */ +// @@@ htype should be enum +static TSK_IDX_INFO * +tsk_idx_open(TSK_HDB_INFO * hdb_info, uint8_t htype, uint8_t create) +{ + TSK_IDX_INFO * idx_info; + size_t flen; + const int header_size = 16; + char header[header_size]; + FILE * idx = NULL; + + if (hdb_info->idx_info != NULL) { + return hdb_info->idx_info; + } + + if ((idx_info = + (TSK_IDX_INFO *) tsk_malloc(sizeof(TSK_IDX_INFO))) == NULL) { + return NULL; + } + + hdb_info->idx_info = idx_info; + + /* Make the name for the index file */ + flen = TSTRLEN(hdb_info->db_fname) + 32; + idx_info->idx_fname = + (TSK_TCHAR *) tsk_malloc(flen * sizeof(TSK_TCHAR)); + if (idx_info->idx_fname == NULL) { + free(idx_info); + // @@@ ERROR INFO NEEDED + 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 + + // Do not auto-add .kdb extension if we are creating a blank kdb file. + bool newBlank = (create == 1) && (hdb_info->db_type == TSK_HDB_DBTYPE_IDXONLY_ID); + + // Check if it already has a .kdb extension + TSK_TCHAR * c; + c = TSTRRCHR(hdb_info->db_fname, _TSK_T('.')); + if (newBlank || ((c != NULL) && (TSTRLEN(c) >= 4) + && (TSTRCMP(c, _TSK_T(".kdb")) == 0))) { + + TSTRNCPY(idx_info->idx_fname, hdb_info->db_fname, TSTRLEN(hdb_info->db_fname)); + } else { + TSNPRINTF(idx_info->idx_fname, flen, + _TSK_T("%s.kdb"), hdb_info->db_fname); + } + + if (((idx = tsk_idx_open_file(idx_info->idx_fname)) == NULL) && (create == 0)) { + + // Try opening an old format index file + + // Clear index filename + free(idx_info->idx_fname); + idx_info->idx_fname = (TSK_TCHAR *) tsk_malloc(flen * sizeof(TSK_TCHAR)); + if (idx_info->idx_fname == NULL) { + free(idx_info); + // @@@ ERROR INFO NEEDED + return NULL; + } + + // Check if it already has an .idx extension + TSK_TCHAR * c; + c = TSTRRCHR(hdb_info->db_fname, _TSK_T('.')); + if ((c != NULL) && (TSTRLEN(c) >= 4) + && (TSTRCMP(c, _TSK_T(".idx")) == 0)) { + + // Use given db filename as the index filename + TSTRNCPY(idx_info->idx_fname, hdb_info->db_fname, TSTRLEN(hdb_info->db_fname)); + } else { + // Change the filename to the old format + switch (htype) { + case TSK_HDB_HTYPE_MD5_ID: + TSNPRINTF(idx_info->idx_fname, flen, + _TSK_T("%s-%") PRIcTSK _TSK_T(".idx"), + hdb_info->db_fname, TSK_HDB_HTYPE_MD5_STR); + break; + case TSK_HDB_HTYPE_SHA1_ID: + TSNPRINTF(idx_info->idx_fname, flen, + _TSK_T("%s-%") PRIcTSK _TSK_T(".idx"), + hdb_info->db_fname, TSK_HDB_HTYPE_SHA1_STR); + break; + } + } + + idx = tsk_idx_open_file(idx_info->idx_fname); + + if (!idx) { + free(idx_info); + tsk_error_reset(); + tsk_error_set_errno(TSK_ERR_HDB_MISSING); + tsk_error_set_errstr( "tsk_idx_open: Error opening index file"); + return NULL; + } + + if (1 != fread(header, header_size, 1, idx)) { + tsk_error_reset(); + tsk_error_set_errno(TSK_ERR_HDB_MISSING); + tsk_error_set_errstr( + "tsk_idx_open: Error reading header: %"PRIttocTSK, + idx_info->idx_fname); + return NULL; + } + else if (strncmp(header, + IDX_BINSRCH_HEADER, + strlen(IDX_BINSRCH_HEADER)) == 0) { + idx_info->index_type = TSK_HDB_ITYPE_BINSRCH; + idx_info->open = binsrch_open; + idx_info->close = binsrch_close; + idx_info->initialize = binsrch_initialize; + idx_info->addentry = binsrch_addentry; + idx_info->addentry_bin = binsrch_addentry_bin; + idx_info->finalize = binsrch_finalize; + idx_info->lookup_str = binsrch_lookup_str; + idx_info->lookup_raw = binsrch_lookup_raw; + idx_info->get_properties = binsrch_get_properties; + } + else { + tsk_error_reset(); + tsk_error_set_errno(TSK_ERR_HDB_MISSING); + tsk_error_set_errstr( + "tsk_idx_open: Unrecognized header format: %"PRIttocTSK, + idx_info->idx_fname); + free(idx_info); + return NULL; + } + } + // kdb extension + else { + if (idx) { + if (1 != fread(header, header_size, 1, idx)) { + ///@todo should this actually be an error? + idx_info->index_type = TSK_HDB_ITYPE_SQLITE_V1; + } + else if (strncmp(header, + IDX_SQLITE_V1_HEADER, + strlen(IDX_SQLITE_V1_HEADER)) == 0) { + idx_info->index_type = TSK_HDB_ITYPE_SQLITE_V1; + } + else { + tsk_error_reset(); + tsk_error_set_errno(TSK_ERR_HDB_MISSING); + tsk_error_set_errstr( + "tsk_idx_open: Unrecognized header format: %"PRIttocTSK, + idx_info->idx_fname); + tsk_idx_close_file(idx); + free(idx_info); + return NULL; + } + } + + idx_info->open = sqlite_v1_open; + idx_info->close = sqlite_v1_close; + idx_info->initialize = sqlite_v1_initialize; + idx_info->addentry = sqlite_v1_addentry; + idx_info->addentry_bin = sqlite_v1_addentry_bin; + idx_info->finalize = sqlite_v1_finalize; + idx_info->lookup_str = sqlite_v1_lookup_str; + idx_info->lookup_raw = sqlite_v1_lookup_raw; + idx_info->get_properties = sqlite_v1_get_properties; + } + + tsk_idx_close_file(idx); + + // Open + if (idx_info->open(hdb_info, idx_info, htype) == 0) { + if (create == 1) { + idx_info->updateable = 1; + } else { + idx_info->get_properties(hdb_info); + } + + return idx_info; + } + + tsk_error_reset(); + tsk_error_set_errno(TSK_ERR_HDB_ARG); + tsk_error_set_errstr( + "Error setting up idx_info struct: %d\n", htype); + free(idx_info); + return NULL; +} + + +/** + * Ensures that the index is already opened or can be opened. + * @param hdb_info Database handle + * @param htype TSK_HDB_HTYPE_ENUM value + * @param + * @return 0 if already set up or if setup successful, 1 otherwise + */ +uint8_t +hdb_setupindex(TSK_HDB_INFO * hdb_info, uint8_t htype, uint8_t create) +{ + // Lock for lazy load of idx_info and lazy alloc of idx_lbuf. + tsk_take_lock(&hdb_info->lock); + + // already opened + if (hdb_info->idx_info != NULL) { + // update htype + hdb_update_htype(hdb_info, htype); + + tsk_release_lock(&hdb_info->lock); + return 0; + } + + hdb_info->idx_info = tsk_idx_open(hdb_info, htype, create); + + if (hdb_info->idx_info != NULL) { + tsk_release_lock(&hdb_info->lock); + return 0; + } + + tsk_release_lock(&hdb_info->lock); + return 1; +} + + +/** + * Creates and initialize a new TSK hash DB index file. + * + * @param hdb_info Hash database state structure + * @param a_dbtype String of index type to create + * + * @return 1 on error and 0 on success + */ +uint8_t +tsk_hdb_idxinitialize(TSK_HDB_INFO * hdb_info, TSK_TCHAR * a_dbtype) +{ + char dbtmp[32]; + int i; + uint8_t create = 1; //create new file if it doesn't already exist + + /* Use the string of the index/hash type to figure out some + * settings */ + + // convert to char -- cheating way to deal with WCHARs.. + for (i = 0; i < 31 && a_dbtype[i] != '\0'; i++) { + dbtmp[i] = (char) a_dbtype[i]; + } + dbtmp[i] = '\0'; + + // MD5 index for NSRL file + if (strcmp(dbtmp, TSK_HDB_DBTYPE_NSRL_MD5_STR) == 0) { + + if (hdb_info->db_type != TSK_HDB_DBTYPE_NSRL_ID) { + tsk_error_reset(); + tsk_error_set_errno(TSK_ERR_HDB_ARG); + tsk_error_set_errstr( + "hdb_idxinitialize: database detected as: %d index creation as: %d", + hdb_info->db_type, TSK_HDB_DBTYPE_NSRL_ID); + return 1; + } + hdb_info->hash_type = TSK_HDB_HTYPE_MD5_ID; + } + // SHA1 index for NSRL file + else if (strcmp(dbtmp, TSK_HDB_DBTYPE_NSRL_SHA1_STR) == 0) { + if (hdb_info->db_type != TSK_HDB_DBTYPE_NSRL_ID) { + tsk_error_reset(); + tsk_error_set_errno(TSK_ERR_HDB_ARG); + tsk_error_set_errstr( + "hdb_idxinitialize: database detected as: %d index creation as: %d", + hdb_info->db_type, TSK_HDB_DBTYPE_NSRL_ID); + return 1; + } + hdb_info->hash_type = TSK_HDB_HTYPE_SHA1_ID; + } + else if (strcmp(dbtmp, TSK_HDB_DBTYPE_MD5SUM_STR) == 0) { + if ((hdb_info->db_type != TSK_HDB_DBTYPE_MD5SUM_ID) && (hdb_info->db_type != TSK_HDB_DBTYPE_IDXONLY_ID)) { + tsk_error_reset(); + tsk_error_set_errno(TSK_ERR_HDB_ARG); + tsk_error_set_errstr( + "hdb_idxinitialize: database detected as: %d index creation as: %d", + hdb_info->db_type, TSK_HDB_DBTYPE_MD5SUM_ID); + return 1; + } + hdb_info->hash_type = TSK_HDB_HTYPE_MD5_ID; + } + else if (strcmp(dbtmp, TSK_HDB_DBTYPE_HK_STR) == 0) { + if (hdb_info->db_type != TSK_HDB_DBTYPE_HK_ID) { + tsk_error_reset(); + tsk_error_set_errno(TSK_ERR_HDB_ARG); + tsk_error_set_errstr( + "hdb_idxinitialize: database detected as: %d index creation as: %d", + hdb_info->db_type, TSK_HDB_DBTYPE_HK_ID); + return 1; + } + hdb_info->hash_type = TSK_HDB_HTYPE_MD5_ID; + } + else if (strcmp(dbtmp, TSK_HDB_DBTYPE_ENCASE_STR) == 0) { + if (hdb_info->db_type != TSK_HDB_DBTYPE_ENCASE_ID) { + tsk_error_reset(); + tsk_error_set_errno(TSK_ERR_HDB_ARG); + tsk_error_set_errstr( + "hdb_idxinitialize: database detected as: %d index creation as: %d", + hdb_info->db_type, TSK_HDB_DBTYPE_ENCASE_ID); + return 1; + } + hdb_info->hash_type = TSK_HDB_HTYPE_MD5_ID; + } + else { + tsk_error_reset(); + tsk_error_set_errno(TSK_ERR_HDB_ARG); + tsk_error_set_errstr( + "hdb_idxinitialize: Unknown database type request: %s", + dbtmp); + return 1; + } + + /* Setup the internal hash information */ + if (hdb_setupindex(hdb_info, hdb_info->hash_type, create)) { + return 1; + } + + /* Call db-specific initialize function */ + return hdb_info->idx_info->initialize(hdb_info, a_dbtype); +} + +/** + * Add a string hash entry to the index + * + * @param hdb_info Hash database state info + * @param hvalue String of hash value to add + * @param offset Byte offset of hash entry in original database. + * @return 1 on error and 0 on success + */ +uint8_t +tsk_hdb_idxaddentry(TSK_HDB_INFO * hdb_info, char *hvalue, + TSK_OFF_T offset) +{ + return hdb_info->idx_info->addentry(hdb_info, hvalue, offset); +} + +/** + * Add a binary hash entry to the index + * + * @param hdb_info Hash database state info + * @param hvalue Array of integers 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 +tsk_hdb_idxaddentry_bin(TSK_HDB_INFO * hdb_info, unsigned char *hvalue, int hlen, + TSK_OFF_T offset) +{ + return hdb_info->idx_info->addentry_bin(hdb_info, hvalue, hlen, offset); +} + +/** + * Finalize index creation process. + * + * @param hdb_info Hash database state info structure. + * @return 1 on error and 0 on success + */ +uint8_t +tsk_hdb_idxfinalize(TSK_HDB_INFO * hdb_info) +{ + return hdb_info->idx_info->finalize(hdb_info); +} + + + +/** + * \ingroup hashdblib + * Determine if the open hash database has an index. + * + * @param hdb_info Hash database to consider + * @param htype Hash type that index should be of + * + * @return 1 if index exists and 0 if not + */ +uint8_t +tsk_hdb_hasindex(TSK_HDB_INFO * hdb_info, uint8_t htype) +{ + /* Check if the index is already open, and + * try to open it if not */ + if (hdb_setupindex(hdb_info, htype, 0) == 0) { + return 1; + } else { + return 0; + } +} + +/** + * \ingroup hashdblib + * Test for index only (legacy) + * Assumes that the db was opened using the TSK_HDB_OPEN_TRY option. + * + * @param hdb_info Hash database to consider + * + * @return 1 if there is only a legacy index AND no db, 0 otherwise + */ +uint8_t +tsk_hdb_is_idxonly(TSK_HDB_INFO * hdb_info) +{ + if (hdb_info->db_type == TSK_HDB_DBTYPE_IDXONLY_ID) { + return (hdb_info->idx_info->index_type == TSK_HDB_ITYPE_BINSRCH) ? 1 : 0; + } else { + return 0; + } +} + +/** + * \ingroup hashdblib + * Close an open hash index + * + * @param idx_info index to close + */ +void tsk_idx_close(TSK_IDX_INFO * idx_info) +{ + if (idx_info->idx_fname) { + free(idx_info->idx_fname); + } + + idx_info->close(idx_info); +} + + +/** + * \ingroup hashdblib + * Create an index for an open hash database. + * @param a_hdb_info Open hash database to index + * @param a_type Text of hash database type + * @returns 1 on error + */ +uint8_t +tsk_hdb_makeindex(TSK_HDB_INFO * a_hdb_info, TSK_TCHAR * a_type) +{ + return a_hdb_info->makeindex(a_hdb_info, a_type); +} + +/** + * \ingroup hashdblib + * Create an empty index. + * @param db_file Filename. For a new index from scratch, the db name == idx name. + * @returns NULL on error + */ +TSK_HDB_INFO * +tsk_hdb_new(TSK_TCHAR * db_file) +{ + TSK_HDB_OPEN_ENUM flags = TSK_HDB_OPEN_IDXONLY; + TSK_HDB_INFO * hdb_info = tsk_hdb_open(db_file, flags); + if (hdb_info != NULL) { + TSK_TCHAR * dbtype = NULL; //ignored for IDX only + if (hdb_info->makeindex(hdb_info, dbtype) != 0) { + tsk_hdb_close(hdb_info); + hdb_info = NULL; + tsk_error_reset(); + tsk_error_set_errno(TSK_ERR_HDB_CREATE); + tsk_error_set_errstr("tsk_hdb_new: making new index failed"); + } else { + if (tsk_hdb_idxfinalize(hdb_info) != 0) { + tsk_hdb_close(hdb_info); + hdb_info = NULL; + tsk_error_reset(); + tsk_error_set_errno(TSK_ERR_HDB_WRITE); + tsk_error_set_errstr("tsk_hdb_new: finalizing new index failed"); + } + } + } + return hdb_info; +} + +/** + * \ingroup hashdblib + * Add a binary hash entry to the index + * + * @param hdb_info the hash database object + * @param filename Name of the file that was hashed (can be null) + * @param md5 Text of MD5 hash (can be null) + * @param sha1 Text of SHA1 hash (can be null) + * @param sha256 Text of SHA256 hash (can be null) + * @return 1 on error, 0 on success, -1 if not updateable + */ +int8_t +tsk_hdb_add_str(TSK_HDB_INFO * hdb_info, + const TSK_TCHAR * filename, + const char * md5, + const char * sha1, + const char * sha256) +{ + if(hdb_info == NULL) { + tsk_error_set_errstr2("tsk_hdb_add_str: null hdb_info"); + return 1; + } else { + uint8_t htype = TSK_HDB_HTYPE_MD5_ID; + if (hdb_setupindex(hdb_info, htype, 0)) { + return 1; + } + + if(hdb_info->idx_info->updateable == 1) { + ///@todo also allow use of other htypes + char * hvalue = (char *)md5; + + // @todo Could set up a polymorphic mechanism like with finalize() but + // we know it's going to be sqlite in this function. + if (sqlite_v1_begin(hdb_info) == 1) { + tsk_error_set_errstr2("tsk_hdb_add_str: sqlite_v1_begin failed"); + return 1; + } + + // Attempt to add a new row to the hash index + TSK_OFF_T offset = 0; //not needed since there might not be an original DB + if (tsk_hdb_idxaddentry(hdb_info, hvalue, offset) != 0) { + tsk_error_reset(); + tsk_error_set_errno(TSK_ERR_HDB_WRITE); + tsk_error_set_errstr("tsk_hdb_add_str: adding entry failed"); + return 1; + } else { + // Close the index + if (tsk_hdb_idxfinalize(hdb_info) != 0) { + tsk_error_reset(); + tsk_error_set_errno(TSK_ERR_HDB_WRITE); + tsk_error_set_errstr("tsk_hdb_add_str: finalizing index failed"); + return 1; + } + return 0; + } + } else { + return -1; + } + } +} + +/** + * Set db_name to the name of the database file + * + * @param hdb_info the hash database object + */ +void +tsk_hdb_name_from_path(TSK_HDB_INFO * hdb_info) +{ +#ifdef TSK_WIN32 + const char PATH_CHAR = '\\'; +#else + const char PATH_CHAR = '/'; +#endif + TSK_TCHAR * begin; + TSK_TCHAR * end; + int i; + + hdb_info->db_name[0] = '\0'; + + begin = TSTRRCHR(hdb_info->db_fname, PATH_CHAR); +#ifdef TSK_WIN32 + // cygwin can have forward slashes, so try that too on Windows + if (!begin) { + begin = TSTRRCHR(hdb_info->db_fname, '/'); + } +#endif + + if (!begin) { + begin = hdb_info->db_fname; + } + else { + // unlikely since this means that the dbname is "/" + if (TSTRLEN(begin) == 1) + return; + else + begin++; + } + + // end points to the byte after the last one we want to use + if ((TSTRLEN(hdb_info->db_fname) > 4) && (TSTRICMP(&hdb_info->db_fname[TSTRLEN(hdb_info->db_fname)-4], _TSK_T(".idx")) == 0)) + end = &hdb_info->db_fname[TSTRLEN(hdb_info->db_fname)-4]; + else + end = begin + TSTRLEN(begin); + + + // @@@ TODO: Use TskUTF16_to_UTF8 to properly convert for Windows + for(i = 0; i < (end-begin); i++) + { + hdb_info->db_name[i] = (char) begin[i]; + } + + hdb_info->db_name[i] = '\0'; +} diff --git a/tsk/hashdb/hdb_open.cpp b/tsk/hashdb/hdb_open.cpp new file mode 100644 index 0000000000000000000000000000000000000000..7c86ae748cab6acd9e5989abc96e0cc6bf92fb1c --- /dev/null +++ b/tsk/hashdb/hdb_open.cpp @@ -0,0 +1,232 @@ +/* + * The Sleuth Kit + * + * Brian Carrier [carrier <at> sleuthkit [dot] org] + * Copyright (c) 2003-2013 Brian Carrier. All rights reserved + * + * + * This software is distributed under the Common Public License 1.0 + */ + +#include "tsk_hashdb_i.h" + + +/** + * \file hdb_open.c + * Contains the generic hash database creation and lookup code. + */ + + + +/** + * \ingroup hashdblib + * Open an existing hash database. + * + * @param db_file Path to database (even if only an index exists, in which case db path should still be listed). + * @param flags Flags for opening the database. + * + * @return Pointer to hash database state structure or NULL on error + */ +TSK_HDB_INFO * +tsk_hdb_open(TSK_TCHAR * db_file, TSK_HDB_OPEN_ENUM flags) +{ + TSK_HDB_INFO *hdb_info; + size_t flen; + FILE *hDb; + uint8_t dbtype = 0; + + if (flags == TSK_HDB_OPEN_TRY) { + TSK_HDB_OPEN_ENUM tryflag = TSK_HDB_OPEN_NONE; + + if ((hdb_info = tsk_hdb_open(db_file, tryflag)) != NULL) { + // success (and there is a src db file existent) + return hdb_info; + } else { + // if null then maybe it's IDX only + flags = TSK_HDB_OPEN_IDXONLY; + // continue this function in IDXONLY mode + } + } + + if ((flags & TSK_HDB_OPEN_IDXONLY) == 0) { + /* Open the database file */ +#ifdef TSK_WIN32 + { + HANDLE hWin; + + if ((hWin = CreateFile(db_file, GENERIC_READ, + FILE_SHARE_READ, 0, OPEN_EXISTING, 0, + 0)) == INVALID_HANDLE_VALUE) { + tsk_error_reset(); + tsk_error_set_errno(TSK_ERR_HDB_OPEN); + tsk_error_set_errstr( + "hdb_open: Error opening database file: %S", + db_file); + return NULL; + } + hDb = + _fdopen(_open_osfhandle((intptr_t) hWin, _O_RDONLY), "r"); + if (hDb == NULL) { + tsk_error_reset(); + tsk_error_set_errno(TSK_ERR_HDB_OPEN); + tsk_error_set_errstr( + "hdb_open: Error converting Windows handle to C handle"); + return NULL; + } + } +#else + if (NULL == (hDb = fopen(db_file, "r"))) { + tsk_error_reset(); + tsk_error_set_errno(TSK_ERR_HDB_OPEN); + tsk_error_set_errstr( + "hdb_open: Error opening database file: %s", db_file); + return NULL; + } +#endif + + /* Try to figure out what type of DB it is */ + if (sqlite3_test(hDb)) { + dbtype = TSK_HDB_DBTYPE_IDXONLY_ID; + fclose(hDb); + hDb = NULL; + + } else { + if (nsrl_test(hDb)) { + dbtype = TSK_HDB_DBTYPE_NSRL_ID; + } + if (md5sum_test(hDb)) { + if (dbtype != 0) { + tsk_error_reset(); + tsk_error_set_errno(TSK_ERR_HDB_UNKTYPE); + tsk_error_set_errstr( + "hdb_open: Error determining DB type (MD5sum)"); + return NULL; + } + dbtype = TSK_HDB_DBTYPE_MD5SUM_ID; + } + if (encase_test(hDb)) { + if (dbtype != 0) { + tsk_error_reset(); + tsk_error_set_errno(TSK_ERR_HDB_UNKTYPE); + tsk_error_set_errstr( + "hdb_open: Error determining DB type (EnCase)"); + return NULL; + } + dbtype = TSK_HDB_DBTYPE_ENCASE_ID; + } + if (hk_test(hDb)) { + if (dbtype != 0) { + tsk_error_reset(); + tsk_error_set_errno(TSK_ERR_HDB_UNKTYPE); + tsk_error_set_errstr( + "hdb_open: Error determining DB type (HK)"); + return NULL; + } + dbtype = TSK_HDB_DBTYPE_HK_ID; + } + if (dbtype == 0) { + tsk_error_reset(); + tsk_error_set_errno(TSK_ERR_HDB_UNKTYPE); + tsk_error_set_errstr( + "hdb_open: Error determining DB type"); + return NULL; + } + fseeko(hDb, 0, SEEK_SET); + } + } + else { + dbtype = TSK_HDB_DBTYPE_IDXONLY_ID; + hDb = NULL; + } + + if ((hdb_info = + (TSK_HDB_INFO *) tsk_malloc(sizeof(TSK_HDB_INFO))) == NULL) + return NULL; + + hdb_info->hDb = hDb; + + /* Copy the database name into the structure */ + flen = TSTRLEN(db_file) + 8; // + 32; + + hdb_info->db_fname = + (TSK_TCHAR *) tsk_malloc(flen * sizeof(TSK_TCHAR)); + if (hdb_info->db_fname == NULL) { + free(hdb_info); + return NULL; + } + + TSTRNCPY(hdb_info->db_fname, db_file, flen); + + hdb_info->hash_type = static_cast<TSK_HDB_HTYPE_ENUM>(0); + hdb_info->hash_len = 0; + hdb_info->idx_info = NULL; + + // Initialize mutex (or critical section) obj + tsk_init_lock(&hdb_info->lock); + + /* Get database specific information */ + hdb_info->db_type = static_cast<TSK_HDB_DBTYPE_ENUM>(dbtype); + switch (dbtype) { + case TSK_HDB_DBTYPE_NSRL_ID: + nsrl_name(hdb_info); + hdb_info->getentry = nsrl_getentry; + hdb_info->makeindex = nsrl_makeindex; + break; + + case TSK_HDB_DBTYPE_MD5SUM_ID: + md5sum_name(hdb_info); + hdb_info->getentry = md5sum_getentry; + hdb_info->makeindex = md5sum_makeindex; + break; + + case TSK_HDB_DBTYPE_ENCASE_ID: + encase_name(hdb_info); + hdb_info->getentry = encase_getentry; + hdb_info->makeindex = encase_makeindex; + break; + + case TSK_HDB_DBTYPE_HK_ID: + hk_name(hdb_info); + hdb_info->getentry = hk_getentry; + hdb_info->makeindex = hk_makeindex; + break; + + case TSK_HDB_DBTYPE_IDXONLY_ID: + idxonly_name(hdb_info); + hdb_info->getentry = idxonly_getentry; + hdb_info->makeindex = idxonly_makeindex; + break; + + default: + free(hdb_info); + return NULL; + } + + return hdb_info; +} + + + +/** + * \ingroup hashdblib + * Close an open hash database. + * + * @param hdb_info database to close + */ +void +tsk_hdb_close(TSK_HDB_INFO * hdb_info) +{ + if (hdb_info->db_fname) + free(hdb_info->db_fname); + + if (hdb_info->hDb) + fclose(hdb_info->hDb); + + if (hdb_info->idx_info) { + tsk_idx_close(hdb_info->idx_info); + } + + tsk_deinit_lock(&hdb_info->lock); + + free(hdb_info); +} diff --git a/tsk/hashdb/idxonly_index.c b/tsk/hashdb/idxonly.c similarity index 54% rename from tsk/hashdb/idxonly_index.c rename to tsk/hashdb/idxonly.c index a6c4713f8218b5d04b050962ff6c003d07ed2fc9..3c3dff904e2e304ac57d7cd6b4d4bf89eb8b1cf0 100644 --- a/tsk/hashdb/idxonly_index.c +++ b/tsk/hashdb/idxonly.c @@ -29,6 +29,8 @@ idxonly_name(TSK_HDB_INFO * hdb_info) char *bufptr = buf; size_t i = 0; memset(hdb_info->db_name, '\0', TSK_HDB_NAME_MAXLEN); + + //Calling tsk_hdb_hasindex() has the side effect to set up the index structure (if the index exist) if(tsk_hdb_hasindex(hdb_info, TSK_HDB_HTYPE_MD5_ID) == 0) { if (tsk_verbose) fprintf(stderr, @@ -36,46 +38,64 @@ idxonly_name(TSK_HDB_INFO * hdb_info) tsk_hdb_name_from_path(hdb_info); return; } - hFile = hdb_info->hIdx; - fseeko(hFile, 0, 0); - if(NULL == fgets(buf, TSK_HDB_NAME_MAXLEN, hFile) || - NULL == fgets(buf, TSK_HDB_NAME_MAXLEN, hFile) || - strncmp(buf, - TSK_HDB_IDX_HEAD_NAME_STR, - strlen(TSK_HDB_IDX_HEAD_NAME_STR)) != 0) { - if (tsk_verbose) - fprintf(stderr, - "Failed to read name from index; using file name instead"); + + // Get the DB name. Alternatively, we could query the index for Hashset Name. + if (hdb_info->idx_info->index_type == TSK_HDB_ITYPE_SQLITE_V1) { tsk_hdb_name_from_path(hdb_info); return; - } - bufptr = strchr(buf, '|'); - bufptr++; - while(bufptr[i] != '\r' && bufptr[i] != '\n' && i < strlen(bufptr)) - { - hdb_info->db_name[i] = bufptr[i]; - i++; + } else { + // Get name from legacy (bin search text) index + hFile = hdb_info->idx_info->idx_struct.idx_binsrch->hIdx; + fseeko(hFile, 0, 0); + if(NULL == fgets(buf, TSK_HDB_NAME_MAXLEN, hFile) || + NULL == fgets(buf, TSK_HDB_NAME_MAXLEN, hFile) || + strncmp(buf, + TSK_HDB_IDX_HEAD_NAME_STR, + strlen(TSK_HDB_IDX_HEAD_NAME_STR)) != 0) { + if (tsk_verbose) + fprintf(stderr, + "Failed to read name from index; using file name instead"); + tsk_hdb_name_from_path(hdb_info); + return; + } + bufptr = strchr(buf, '|'); + bufptr++; + while(bufptr[i] != '\r' && bufptr[i] != '\n' && i < strlen(bufptr)) + { + hdb_info->db_name[i] = bufptr[i]; + i++; + } } } /** - * This function should process the database to create a sorted index of it, - * but in this case we do not have a database, so just make an error... + * This function creates an empty * * @param hdb_info Hash database to make index of. - * @param dbtype Type of hash database + * @param dbtype Type of hash database. Ignored for IDX only. * * @return 1 on error and 0 on success. */ uint8_t idxonly_makeindex(TSK_HDB_INFO * hdb_info, TSK_TCHAR * dbtype) { - tsk_error_reset(); - tsk_error_set_errno(TSK_ERR_HDB_ARG); - tsk_error_set_errstr( - "idxonly_makeindex: Make index not supported when INDEX ONLY option is used"); - return 1; + //tsk_error_reset(); + //tsk_error_set_errno(TSK_ERR_HDB_ARG); + //tsk_error_set_errstr( + // "idxonly_makeindex: Make index not supported when INDEX ONLY option is used"); + + ///@temporary until we exorcise all the htype conditionals out + TSK_TCHAR dbtype_default[1024]; + TSNPRINTF(dbtype_default, 1024, _TSK_T("%") PRIcTSK, TSK_HDB_DBTYPE_MD5SUM_STR); + + /* Initialize the TSK index file */ + if (tsk_hdb_idxinitialize(hdb_info, dbtype_default)) { + tsk_error_set_errstr2( "idxonly_makeindex"); + return 1; + } + + return 0; } @@ -103,4 +123,4 @@ idxonly_getentry(TSK_HDB_INFO * hdb_info, const char *hash, tsk_error_set_errstr( "idxonly_getentry: Not supported when INDEX ONLY option is used"); return 1; -} \ No newline at end of file +} diff --git a/tsk/hashdb/md5sum_index.c b/tsk/hashdb/md5sum.c similarity index 100% rename from tsk/hashdb/md5sum_index.c rename to tsk/hashdb/md5sum.c diff --git a/tsk/hashdb/nsrl_index.c b/tsk/hashdb/nsrl.c similarity index 100% rename from tsk/hashdb/nsrl_index.c rename to tsk/hashdb/nsrl.c diff --git a/tsk/hashdb/sqlite_index.cpp b/tsk/hashdb/sqlite_index.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e5316e76e7dce084fee646166c37c9024e3386f3 --- /dev/null +++ b/tsk/hashdb/sqlite_index.cpp @@ -0,0 +1,757 @@ + +/* + * The Sleuth Kit + * + * Brian Carrier [carrier <at> sleuthkit [dot] org] + * Copyright (c) 2003-2011 Brian Carrier. All rights reserved + * + * + * This software is distributed under the Common Public License 1.0 + * + */ + +#include "tsk_hashdb_i.h" + +/** + * \file sqlite_index.c + * Contains functions for creating a SQLite format hash index + */ + +/** + * Static sqlite statements, prepared initially and bound before each use + */ +static sqlite3_stmt *m_stmt = NULL; +static bool need_SQL_index = false; + +/** + * Prototypes + */ +//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) +{ + if (resultCode != expectedResultCode) { + tsk_error_reset(); + tsk_error_set_errno(TSK_ERR_AUTO_DB); + tsk_error_set_errstr(errfmt, sqlite3_errmsg(sqlite), resultCode); + return 1; + } + return 0; +} + +static int attempt_exec(const char *sql, int (*callback) (void *, int, char **, char **), + void *callback_arg, const char *errfmt, sqlite3 * sqlite) +{ + char * errmsg; + + if(sqlite3_exec(sqlite, sql, callback, callback_arg, &errmsg) != SQLITE_OK) { + tsk_error_reset(); + tsk_error_set_errno(TSK_ERR_AUTO_DB); + tsk_error_set_errstr(errfmt, errmsg); + sqlite3_free(errmsg); + return 1; + } + return 0; +} + +static int attempt_exec_nocallback(const char *sql, const char *errfmt, sqlite3 * sqlite) +{ + return attempt_exec(sql, NULL, NULL, errfmt, sqlite); +} + +static int finalize_stmt(sqlite3_stmt * stmt) +{ + if (sqlite3_finalize(stmt) != SQLITE_OK) { + tsk_error_reset(); + tsk_error_set_errno(TSK_ERR_AUTO_DB); + tsk_error_set_errstr("Error finalizing SQL statement\n"); + tsk_error_print(stderr); + return 1; + } + 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); + tsk_error_set_errstr("Error preparing SQL statement: %s\n", sql); + tsk_error_print(stderr); + return 1; + } + return 0; +} + +static uint8_t tsk_hdb_begin_transaction(TSK_IDX_INFO * idx_info) { + return attempt_exec_nocallback("BEGIN", "Error beginning transaction %s\n", idx_info->idx_struct.idx_sqlite_v1->hIdx_sqlite); +} + +static uint8_t tsk_hdb_commit_transaction(TSK_IDX_INFO * idx_info) { + return attempt_exec_nocallback("COMMIT", "Error committing transaction %s\n", idx_info->idx_struct.idx_sqlite_v1->hIdx_sqlite); +} + +/** Init prepared statements. Call before adding to the database. Call finalize() when done. + * + * @param hdb_info Hash database state structure + * + * @return 1 on error and 0 on success + * + */ +uint8_t +sqlite_v1_begin(TSK_HDB_INFO * hdb_info) +{ + char * insertStmt; + + if (hdb_info->hash_type == TSK_HDB_HTYPE_MD5_ID) { + insertStmt = "INSERT INTO hashes (md5, database_offset) VALUES (?, ?)"; + } else if (hdb_info->hash_type == TSK_HDB_HTYPE_SHA1_ID) { + insertStmt = "INSERT INTO hashes (sha1, database_offset) VALUES (?, ?)"; + } else { + return 1; + } + + prepare_stmt(insertStmt, &m_stmt, hdb_info->idx_info->idx_struct.idx_sqlite_v1->hIdx_sqlite); + + if (tsk_hdb_begin_transaction(hdb_info->idx_info)) { + return 1; + } else { + return 0; + } +} + +/** Initialize the TSK hash DB index file by creating tables, etc.. + * + * @param hdb_info Hash database state structure + * @param htype String of index type to create + * + * @return 1 on error and 0 on success + * + */ +uint8_t +sqlite_v1_initialize(TSK_HDB_INFO * hdb_info, TSK_TCHAR * htype) +{ + char stmt[1024]; + + if (attempt_exec_nocallback("PRAGMA synchronous = OFF;", + "Error setting PRAGMA synchronous: %s\n", hdb_info->idx_info->idx_struct.idx_sqlite_v1->hIdx_sqlite)) { + return 1; + } + + if (attempt_exec_nocallback + ("CREATE TABLE properties (name TEXT, value TEXT);", + "Error creating properties table %s\n", hdb_info->idx_info->idx_struct.idx_sqlite_v1->hIdx_sqlite)) { + return 1; + } + + snprintf(stmt, 1024, + "INSERT INTO properties (name, value) VALUES ('%s', '%s');", + IDX_SCHEMA_VER, IDX_VERSION_NUM); + if (attempt_exec_nocallback(stmt, "Error adding schema info to properties: %s\n", hdb_info->idx_info->idx_struct.idx_sqlite_v1->hIdx_sqlite)) { + return 1; + } + + snprintf(stmt, 1024, + "INSERT INTO properties (name, value) VALUES ('%s', '%s');", + IDX_HASHSET_NAME, hdb_info->db_name); + if (attempt_exec_nocallback(stmt, "Error adding name to properties: %s\n", hdb_info->idx_info->idx_struct.idx_sqlite_v1->hIdx_sqlite)) { + return 1; + } + + snprintf(stmt, 1024, + "INSERT INTO properties (name, value) VALUES ('%s', '%s');", + IDX_HASHSET_UPDATEABLE, (hdb_info->idx_info->updateable == 1) ? "true" : "false"); + if (attempt_exec_nocallback(stmt, "Error adding updateable to properties: %s\n", hdb_info->idx_info->idx_struct.idx_sqlite_v1->hIdx_sqlite)) { + 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. + if (attempt_exec_nocallback + ("CREATE TABLE names (name TEXT, hash_id INTEGER);", + "Error creating names table %s\n", hdb_info->idx_info->idx_struct.idx_sqlite_v1->hIdx_sqlite)) { + return 1; + } + + need_SQL_index = true; + + return sqlite_v1_begin(hdb_info); +} + +/** + * Add a string representation of a hash value to the index. + * + * @param hdb_info Hash database state info + * @param hvalue String of hash value to add + * @param offset Byte offset of hash entry in original database. + * @return 1 on error and 0 on success + */ +uint8_t +sqlite_v1_addentry(TSK_HDB_INFO * hdb_info, char* hvalue, + TSK_OFF_T offset) +{ + if (strlen(hvalue) != hdb_info->hash_len) { + tsk_error_reset(); + tsk_error_set_errno(TSK_ERR_AUTO_DB); + tsk_error_set_errstr("Hash length doesn't match index type: %s\n", hvalue); + tsk_error_print(stderr); + 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++) { + int r = sscanf(hvalue, "%2hx", &binval); + hash[count] = (uint8_t) binval; + hvalue += 2 * sizeof(char); + } + uint8_t ret = sqlite_v1_addentry_bin(hdb_info, hash, len, offset); + + delete [] hash; +#endif + + return ret; +} + +/** + * Add a binary representation of a hash value into the index. + * + * @param hdb_info Hash database state info + * @param hvalue Array of integers 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 +sqlite_v1_addentry_bin(TSK_HDB_INFO * hdb_info, uint8_t* hvalue, int hlen, + TSK_OFF_T offset) +{ + if (attempt(sqlite3_bind_blob(m_stmt, 1, hvalue, hlen, SQLITE_TRANSIENT), + SQLITE_OK, + "Error binding binary blob: %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; +} + +/** + * 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 + * + * @param hdb_info Hash database state info structure. + * @return 1 on error and 0 on success + */ +uint8_t +sqlite_v1_finalize(TSK_HDB_INFO * hdb_info) +{ + if (tsk_hdb_commit_transaction(hdb_info->idx_info)) { + tsk_error_reset(); + tsk_error_set_errno(TSK_ERR_AUTO_DB); + tsk_error_set_errstr("Failed to commit transaction\n"); + tsk_error_print(stderr); + return 1; + } + + // We create the indexes at the end in order to make adding the initial batch of data (e.g. indexing an NSRL db) + // faster. Updates after indexing can be slower since the index has to update as well. + if (need_SQL_index) { + need_SQL_index = false; + return attempt_exec_nocallback + ("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 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; + } +} + +/** \internal + * Setup the internal variables to read an index or database. This + * opens the index and sets the needed size information. + * + * @param hdb_info Hash database to analyze + * @param htype The hash type that was used to make the index. + * + * @return 1 on error and 0 on success + */ +uint8_t +sqlite_v1_open(TSK_HDB_INFO * hdb_info, TSK_IDX_INFO * idx_info, uint8_t htype) +{ + sqlite3 * sqlite = NULL; + + if ((idx_info->idx_struct.idx_sqlite_v1 = + (TSK_IDX_SQLITE_V1 *) tsk_malloc + (sizeof(TSK_IDX_SQLITE_V1))) == NULL) { + tsk_error_reset(); + tsk_error_set_errno(TSK_ERR_HDB_ARG); + tsk_error_set_errstr( + "sqlite_v1_open: Malloc error"); + return 1; + } + + + if ((htype != TSK_HDB_HTYPE_MD5_ID) + && (htype != TSK_HDB_HTYPE_SHA1_ID) + && (htype != TSK_HDB_HTYPE_SHA2_256_ID)) { + tsk_error_reset(); + tsk_error_set_errno(TSK_ERR_HDB_ARG); + tsk_error_set_errstr( + "hdb_setupindex: Invalid hash type : %d", htype); + return 1; + } + +#ifdef TSK_WIN32 + { + if (attempt(sqlite3_open16(idx_info->idx_fname, &sqlite), + SQLITE_OK, + "Can't open index: %s\n", sqlite)) { + sqlite3_close(sqlite); + return 1; + } + } +#else + { + if (attempt(sqlite3_open(idx_info->idx_fname, &sqlite), + SQLITE_OK, + "Can't open index: %s\n", sqlite)) { + sqlite3_close(sqlite); + return 1; + } + } +#endif + + sqlite3_extended_result_codes(sqlite, 1); + + idx_info->idx_struct.idx_sqlite_v1->hIdx_sqlite = sqlite; + + return 0; +} + + + + + +/** + * \ingroup hashdblib + * Search the index for a text/ASCII hash value + * + * @param hdb_info Open hash database (with index) + * @param hash Hash value to search for (NULL terminated string) + * @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. + */ +int8_t +sqlite_v1_lookup_str(TSK_HDB_INFO * hdb_info, const char* hvalue, + TSK_HDB_FLAG_ENUM flags, TSK_HDB_LOOKUP_FN action, + void *ptr) +{ + 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 = hvalue; + size_t count = 0; + + for(count = 0; count < len; count++) { + sscanf(pos, "%2hx", (short unsigned int *) &(hashBlob[count])); + pos += 2 * sizeof(char); + } + + 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, hvalue, name, ptr); + } + + return ret; +} + +/** + * \ingroup hashdblib + * Search the index for the given hash value given (in binary form). + * + * @param hdb_info Open hash database (with index) + * @param hash Array with binary hash value to search for + * @param len Number of bytes in binary hash value + * @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. + */ +int8_t +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) +{ + char hashbuf[TSK_HDB_HTYPE_SHA1_LEN + 1]; + int8_t ret = 0; + int i; + static const char hex[] = "0123456789abcdef"; + TSK_OFF_T offset; + char * selectStmt; + sqlite3_stmt* stmt = NULL; + + tsk_take_lock(&hdb_info->lock); + + /* Sanity check */ + if ((hdb_info->hash_len)/2 != 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 * 2)); + ret = -1; + } else { + + if (hdb_info->hash_type == TSK_HDB_HTYPE_MD5_ID) { + selectStmt = "SELECT md5,database_offset from hashes where md5=? limit 1"; + } else if (hdb_info->hash_type == TSK_HDB_HTYPE_SHA1_ID) { + selectStmt = "SELECT sha1,database_offset from hashes where sha1=? limit 1"; + } 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); + + 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)) { + ret = -1; + } else { + // 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 + for (i = 0; i < len; i++) { + hashbuf[2 * i] = hex[(hvalue[i] >> 4) & 0xf]; + hashbuf[2 * i + 1] = hex[hvalue[i] & 0xf]; + } + hashbuf[2 * len] = '\0'; + + offset = sqlite3_column_int64(stmt, 1); + + if (hdb_info->getentry(hdb_info, hashbuf, 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 + * 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. + * + * @param hdb_info Open hash database (with index) + * @return -1 on error, 0 on success. + */ +int8_t +sqlite_v1_get_properties(TSK_HDB_INFO * hdb_info) +{ + int8_t ret = 0; + sqlite3_stmt* stmt = NULL; + char selectStmt[1024]; + + tsk_take_lock(&hdb_info->lock); + + snprintf(selectStmt, 1024, "SELECT value from properties where name='%s'", IDX_HASHSET_UPDATEABLE); + prepare_stmt(selectStmt, &stmt, hdb_info->idx_info->idx_struct.idx_sqlite_v1->hIdx_sqlite); + + if (sqlite3_step(stmt) == SQLITE_ROW) { + const char* value = (const char *)sqlite3_column_text(stmt, 0); + + if (value == NULL) { + tsk_error_set_errstr2("sqlite_v1_get_properties: null value"); + ret = -1; + } else { + // Set the updateable flag + if (strcmp(value, "true") == 0) { + hdb_info->idx_info->updateable = 1; + } + } + } else { + tsk_error_set_errstr2("sqlite_v1_get_properties"); + ret = -1; + } + + sqlite3_reset(stmt); + + if (stmt) { + finalize_stmt(stmt); + } + + + ///@todo load db name property as well? + + tsk_release_lock(&hdb_info->lock); + + return ret; +} + +/* + * Close the sqlite index handle + * @param idx_info the index to close + */ +void +sqlite_v1_close(TSK_IDX_INFO * idx_info) +{ + if (m_stmt) { + 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); + } +} + +/** + * Test the file to see if it is an sqlite database (== index only) + * + * @param hFile File handle to hash database + * + * @return 1 if sqlite and 0 if not + */ +uint8_t +sqlite3_test(FILE * hFile) +{ + const int header_size = 16; + char header[header_size]; + + if (hFile) { + if (1 != fread(header, header_size, 1, hFile)) { + ///@todo should this actually be an error? + return 0; + } + else if (strncmp(header, + IDX_SQLITE_V1_HEADER, + strlen(IDX_SQLITE_V1_HEADER)) == 0) { + return 1; + } + } + + return 0; +} + diff --git a/tsk/hashdb/tm_lookup.c b/tsk/hashdb/tm_lookup.c deleted file mode 100644 index 9a60263bb517277f70e0b35e4a9f54cee60a21b8..0000000000000000000000000000000000000000 --- a/tsk/hashdb/tm_lookup.c +++ /dev/null @@ -1,1353 +0,0 @@ -/* - * The Sleuth Kit - * - * Brian Carrier [carrier <at> sleuthkit [dot] org] - * Copyright (c) 2003-2011 Brian Carrier. All rights reserved - * - * - * This software is distributed under the Common Public License 1.0 - */ - -#include "tsk_hashdb_i.h" - -/** - * \file tm_lookup.c - * Contains the generic hash database creation and lookup code. - */ - - - -/** - * Setup the hash-type specific information (such as length, index entry - * sizes, index name etc.) in the HDB_INFO structure. - * - * @param hdb_info Structure to fill in. - * @param htype Hash type being used - * @return 1 on error and 0 on success - */ -static uint8_t -hdb_setuphash(TSK_HDB_INFO * hdb_info, uint8_t htype) -{ - size_t flen; - - if (hdb_info->hash_type != 0) { - return 0; - } - - /* Make the name for the index file */ - flen = TSTRLEN(hdb_info->db_fname) + 32; - hdb_info->idx_fname = - (TSK_TCHAR *) tsk_malloc(flen * sizeof(TSK_TCHAR)); - if (hdb_info->idx_fname == NULL) { - return 1; - } - - /* Get hash type specific information */ - switch (htype) { - case TSK_HDB_HTYPE_MD5_ID: - hdb_info->hash_type = htype; - hdb_info->hash_len = TSK_HDB_HTYPE_MD5_LEN; - hdb_info->idx_llen = TSK_HDB_IDX_LEN(htype); - TSNPRINTF(hdb_info->idx_fname, flen, - _TSK_T("%s-%") PRIcTSK _TSK_T(".idx"), - hdb_info->db_fname, TSK_HDB_HTYPE_MD5_STR); - return 0; - case TSK_HDB_HTYPE_SHA1_ID: - hdb_info->hash_type = htype; - hdb_info->hash_len = TSK_HDB_HTYPE_SHA1_LEN; - hdb_info->idx_llen = TSK_HDB_IDX_LEN(htype); - TSNPRINTF(hdb_info->idx_fname, flen, - _TSK_T("%s-%") PRIcTSK _TSK_T(".idx"), - hdb_info->db_fname, TSK_HDB_HTYPE_SHA1_STR); - return 0; - } - - tsk_error_reset(); - tsk_error_set_errno(TSK_ERR_HDB_ARG); - tsk_error_set_errstr( - "hdb_setuphash: Invalid hash type as argument: %d", htype); - return 1; -} - - -/** Initialize the TSK hash DB index file. This creates the intermediate file, - * which will have entries added to it. This file must be sorted before the - * process is finished. - * - * @param hdb_info Hash database state structure - * @param htype String of index type to create - * - * @return 1 on error and 0 on success - * - */ -uint8_t -tsk_hdb_idxinitialize(TSK_HDB_INFO * hdb_info, TSK_TCHAR * htype) -{ - size_t flen; - char dbtmp[32]; - int i; - - - /* Use the string of the index/hash type to figure out some - * settings */ - - // convert to char -- cheating way to deal with WCHARs.. - for (i = 0; i < 31 && htype[i] != '\0'; i++) { - dbtmp[i] = (char) htype[i]; - } - dbtmp[i] = '\0'; - - if (strcmp(dbtmp, TSK_HDB_DBTYPE_NSRL_MD5_STR) == 0) { - - if (hdb_info->db_type != TSK_HDB_DBTYPE_NSRL_ID) { - tsk_error_reset(); - tsk_error_set_errno(TSK_ERR_HDB_ARG); - tsk_error_set_errstr( - "hdb_idxinitialize: database detected as: %d index creation as: %d", - hdb_info->db_type, TSK_HDB_DBTYPE_NSRL_ID); - return 1; - } - hdb_setuphash(hdb_info, TSK_HDB_HTYPE_MD5_ID); - } - else if (strcmp(dbtmp, TSK_HDB_DBTYPE_NSRL_SHA1_STR) == 0) { - if (hdb_info->db_type != TSK_HDB_DBTYPE_NSRL_ID) { - tsk_error_reset(); - tsk_error_set_errno(TSK_ERR_HDB_ARG); - tsk_error_set_errstr( - "hdb_idxinitialize: database detected as: %d index creation as: %d", - hdb_info->db_type, TSK_HDB_DBTYPE_NSRL_ID); - return 1; - } - hdb_setuphash(hdb_info, TSK_HDB_HTYPE_SHA1_ID); - } - else if (strcmp(dbtmp, TSK_HDB_DBTYPE_MD5SUM_STR) == 0) { - if (hdb_info->db_type != TSK_HDB_DBTYPE_MD5SUM_ID) { - tsk_error_reset(); - tsk_error_set_errno(TSK_ERR_HDB_ARG); - tsk_error_set_errstr( - "hdb_idxinitialize: database detected as: %d index creation as: %d", - hdb_info->db_type, TSK_HDB_DBTYPE_MD5SUM_ID); - return 1; - } - hdb_setuphash(hdb_info, TSK_HDB_HTYPE_MD5_ID); - } - else if (strcmp(dbtmp, TSK_HDB_DBTYPE_HK_STR) == 0) { - if (hdb_info->db_type != TSK_HDB_DBTYPE_HK_ID) { - tsk_error_reset(); - tsk_error_set_errno(TSK_ERR_HDB_ARG); - tsk_error_set_errstr( - "hdb_idxinitialize: database detected as: %d index creation as: %d", - hdb_info->db_type, TSK_HDB_DBTYPE_HK_ID); - return 1; - } - hdb_setuphash(hdb_info, TSK_HDB_HTYPE_MD5_ID); - } - else if (strcmp(dbtmp, TSK_HDB_DBTYPE_ENCASE_STR) == 0) { - if (hdb_info->db_type != TSK_HDB_DBTYPE_ENCASE_ID) { - tsk_error_reset(); - tsk_error_set_errno(TSK_ERR_HDB_ARG); - tsk_error_set_errstr( - "hdb_idxinitialize: database detected as: %d index creation as: %d", - hdb_info->db_type, TSK_HDB_DBTYPE_ENCASE_ID); - return 1; - } - hdb_setuphash(hdb_info, TSK_HDB_HTYPE_MD5_ID); - } - else { - tsk_error_reset(); - tsk_error_set_errno(TSK_ERR_HDB_ARG); - tsk_error_set_errstr( - "hdb_idxinitialize: Unknown database type request: %s", - dbtmp); - return 1; - } - - /* Setup the internal hash information */ - if (hdb_setuphash(hdb_info, hdb_info->hash_type)) { - return 1; - } - - /* Make the name for the unsorted intermediate index file */ - flen = TSTRLEN(hdb_info->db_fname) + 32; - hdb_info->uns_fname = - (TSK_TCHAR *) tsk_malloc(flen * sizeof(TSK_TCHAR)); - if (hdb_info->uns_fname == NULL) { - return 1; - } - TSNPRINTF(hdb_info->uns_fname, flen, - _TSK_T("%s-%") PRIcTSK _TSK_T("-ns.idx"), hdb_info->db_fname, - TSK_HDB_HTYPE_STR(hdb_info->hash_type)); - - - /* Create temp unsorted file of offsets */ -#ifdef TSK_WIN32 - { - HANDLE hWin; - - if ((hWin = CreateFile(hdb_info->uns_fname, GENERIC_WRITE, - 0, 0, CREATE_ALWAYS, 0, 0)) == - INVALID_HANDLE_VALUE) { - tsk_error_reset(); - tsk_error_set_errno(TSK_ERR_HDB_CREATE); - tsk_error_set_errstr( - "hdb_idxinitialize: %"PRIttocTSK" GetFileSize: %d", - hdb_info->uns_fname, (int)GetLastError()); - return 1; - } - - hdb_info->hIdxTmp = - _fdopen(_open_osfhandle((intptr_t) hWin, _O_WRONLY), "wb"); - if (hdb_info->hIdxTmp == NULL) { - tsk_error_reset(); - tsk_error_set_errno(TSK_ERR_HDB_OPEN); - tsk_error_set_errstr( - "hdb_idxinitialize: Error converting Windows handle to C handle"); - free(hdb_info); - return 1; - } - } -#else - if (NULL == (hdb_info->hIdxTmp = fopen(hdb_info->uns_fname, "w"))) { - tsk_error_reset(); - tsk_error_set_errno(TSK_ERR_HDB_CREATE); - tsk_error_set_errstr( - "Error creating temp index file: %s", - hdb_info->uns_fname); - return 1; - } -#endif - - /* Print the header */ - fprintf(hdb_info->hIdxTmp, "%s|%s\n", TSK_HDB_IDX_HEAD_NAME_STR, - hdb_info->db_name); - switch (hdb_info->db_type) { - case TSK_HDB_DBTYPE_NSRL_ID: - fprintf(hdb_info->hIdxTmp, "%s|%s\n", TSK_HDB_IDX_HEAD_TYPE_STR, - TSK_HDB_DBTYPE_NSRL_STR); - break; - case TSK_HDB_DBTYPE_MD5SUM_ID: - fprintf(hdb_info->hIdxTmp, "%s|%s\n", TSK_HDB_IDX_HEAD_TYPE_STR, - TSK_HDB_DBTYPE_MD5SUM_STR); - break; - case TSK_HDB_DBTYPE_HK_ID: - fprintf(hdb_info->hIdxTmp, "%s|%s\n", TSK_HDB_IDX_HEAD_TYPE_STR, - TSK_HDB_DBTYPE_HK_STR); - break; - case TSK_HDB_DBTYPE_ENCASE_ID: - fprintf(hdb_info->hIdxTmp, "%s|%s\n", TSK_HDB_IDX_HEAD_TYPE_STR, - TSK_HDB_DBTYPE_ENCASE_STR); - break; - /* Used to stop warning messages about missing enum value */ - case TSK_HDB_DBTYPE_IDXONLY_ID: - default: - tsk_error_reset(); - tsk_error_set_errno(TSK_ERR_HDB_CREATE); - tsk_error_set_errstr("idxinit: Invalid db type\n"); - return 1; - } - - return 0; -} - -/** - * Add a string entry to the intermediate index file. - * - * @param hdb_info Hash database state info - * @param hvalue String of hash value to add - * @param offset Byte offset of hash entry in original database. - * @return 1 on error and 0 on success - */ -uint8_t -tsk_hdb_idxaddentry(TSK_HDB_INFO * hdb_info, char *hvalue, - TSK_OFF_T offset) -{ - int i; - // make the hashes all upper case - for (i = 0; hvalue[i] != '\0'; i++) { - if (islower((int) hvalue[i])) - fprintf(hdb_info->hIdxTmp, "%c", toupper((int) hvalue[i])); - else - fprintf(hdb_info->hIdxTmp, "%c", hvalue[i]); - } - - /* Print the entry to the unsorted index file */ - fprintf(hdb_info->hIdxTmp, "|%.16llu\n", (unsigned long long) offset); - - return 0; -} - -/** - * Add a binary entry to the intermediate index file. - * - * @param hdb_info Hash database state info - * @param hvalue Array of integers 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 -tsk_hdb_idxaddentry_bin(TSK_HDB_INFO * hdb_info, unsigned char *hvalue, int hlen, - TSK_OFF_T offset) -{ - int i; - for (i = 0; i < hlen; i++) { - fprintf(hdb_info->hIdxTmp, "%02X", hvalue[i]); - } - - /* Print the entry to the unsorted index file */ - fprintf(hdb_info->hIdxTmp, "|%.16llu\n", (unsigned long long) offset); - - return 0; -} - -/** - * Finalize index creation process by sorting the index and removing the - * intermediate temp file. - * - * @param hdb_info Hash database state info structure. - * @return 1 on error and 0 on success - */ -uint8_t -tsk_hdb_idxfinalize(TSK_HDB_INFO * hdb_info) -{ -#ifdef TSK_WIN32 - wchar_t buf[TSK_HDB_MAXLEN]; - /// @@ Expand this to be SYSTEM_ROOT -- GetWindowsDirectory() - wchar_t *sys32 = _TSK_T("C:\\WINDOWS\\System32\\sort.exe"); - DWORD stat; - STARTUPINFO myStartInfo; - PROCESS_INFORMATION pinfo; - - /* Close the unsorted file */ - fclose(hdb_info->hIdxTmp); - hdb_info->hIdxTmp = NULL; - - /* Close the existing index if it is open */ - if (hdb_info->hIdx) { - fclose(hdb_info->hIdx); - hdb_info->hIdx = NULL; - } - - if (tsk_verbose) - tsk_fprintf(stderr, "hdb_idxfinalize: Sorting index\n"); - - stat = GetFileAttributes(sys32); - if ((stat != -1) && ((stat & FILE_ATTRIBUTE_DIRECTORY) == 0)) { - TSNPRINTF(buf, TSK_HDB_MAXLEN, _TSK_T("%s /o \"%s\" \"%s\""), - sys32, hdb_info->idx_fname, hdb_info->uns_fname); - } - else { - tsk_error_reset(); - tsk_error_set_errno(TSK_ERR_HDB_MISSING); - tsk_error_set_errstr("Cannot find sort executable"); - return 1; - } - - GetStartupInfo(&myStartInfo); - - if (FALSE == - CreateProcess(NULL, buf, NULL, NULL, FALSE, 0, NULL, NULL, - &myStartInfo, &pinfo)) { - tsk_error_reset(); - tsk_error_set_errno(TSK_ERR_HDB_PROC); - tsk_error_set_errstr( - "Error starting sorting index file using %S", buf); - return 1; - } - - if (WAIT_FAILED == WaitForSingleObject(pinfo.hProcess, INFINITE)) { - tsk_error_reset(); - tsk_error_set_errno(TSK_ERR_HDB_PROC); - tsk_error_set_errstr( - "Error (waiting) sorting index file using %S", buf); - return 1; - } - - if (FALSE == DeleteFile(hdb_info->uns_fname)) { - tsk_error_reset(); - tsk_error_set_errno(TSK_ERR_HDB_DELETE); - tsk_error_set_errstr( - "Error deleting temp file: %d", (int)GetLastError()); - return 1; - } -#else - char buf[TSK_HDB_MAXLEN]; - char *root = "/bin/sort"; - char *usr = "/usr/bin/sort"; - char *local = "/usr/local/bin/sort"; - struct stat stats; - - if (tsk_verbose) - tsk_fprintf(stderr, "hdb_idxfinalize: Sorting index\n"); - - /* Close the unsorted file */ - fclose(hdb_info->hIdxTmp); - hdb_info->hIdxTmp = NULL; - - /* Close the existing index if it is open */ - if (hdb_info->hIdx) { - fclose(hdb_info->hIdx); - hdb_info->hIdx = NULL; - } - - if (0 == stat(local, &stats)) { - snprintf(buf, TSK_HDB_MAXLEN, "%s -o %s %s", local, - hdb_info->idx_fname, hdb_info->uns_fname); - } - else if (0 == stat(usr, &stats)) { - snprintf(buf, TSK_HDB_MAXLEN, "%s -o \"%s\" \"%s\"", - usr, hdb_info->idx_fname, hdb_info->uns_fname); - } - else if (0 == stat(root, &stats)) { - snprintf(buf, TSK_HDB_MAXLEN, "%s -o \"%s\" \"%s\"", - root, hdb_info->idx_fname, hdb_info->uns_fname); - } - else { - tsk_error_reset(); - tsk_error_set_errno(TSK_ERR_HDB_MISSING); - tsk_error_set_errstr("Cannot find sort executable"); - return 1; - } - - if (0 != system(buf)) { - tsk_error_reset(); - tsk_error_set_errno(TSK_ERR_HDB_PROC); - tsk_error_set_errstr( - "Error sorting index file using %s", buf); - return 1; - } - - unlink(hdb_info->uns_fname); -#endif - - return 0; -} - - -/** \internal - * Setup the internal variables to read an index. This - * opens the index and sets the needed size information. - * - * @param hdb_info Hash database to analyze - * @param hash The hash type that was used to make the index. - * - * @return 1 on error and 0 on success - */ -static uint8_t -hdb_setupindex(TSK_HDB_INFO * hdb_info, uint8_t htype) -{ - char head[TSK_HDB_MAXLEN]; - char head2[TSK_HDB_MAXLEN]; - char *ptr; - - // Lock for lazy load of hIdx and lazy alloc of idx_lbuf. - tsk_take_lock(&hdb_info->lock); - - if (hdb_info->hIdx != NULL) { - tsk_release_lock(&hdb_info->lock); - return 0; - } - - if ((htype != TSK_HDB_HTYPE_MD5_ID) - && (htype != TSK_HDB_HTYPE_SHA1_ID)) { - tsk_release_lock(&hdb_info->lock); - tsk_error_reset(); - tsk_error_set_errno(TSK_ERR_HDB_ARG); - tsk_error_set_errstr( - "hdb_setupindex: Invalid hash type : %d", htype); - return 1; - } - - if (hdb_setuphash(hdb_info, htype)) { - tsk_release_lock(&hdb_info->lock); - return 1; - } - - /* Verify the index exists, get its size, and open it */ -#ifdef TSK_WIN32 - { - HANDLE hWin; - DWORD szLow, szHi; - - if (-1 == GetFileAttributes(hdb_info->idx_fname)) { - tsk_release_lock(&hdb_info->lock); - tsk_error_reset(); - tsk_error_set_errno(TSK_ERR_HDB_MISSING); - tsk_error_set_errstr( - "hdb_setupindex: Error finding index file: %"PRIttocTSK, - hdb_info->idx_fname); - return 1; - } - - if ((hWin = CreateFile(hdb_info->idx_fname, GENERIC_READ, - FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0)) == - INVALID_HANDLE_VALUE) { - tsk_release_lock(&hdb_info->lock); - tsk_error_reset(); - tsk_error_set_errno(TSK_ERR_HDB_OPEN); - tsk_error_set_errstr( - "hdb_setupindex: Error opening index file: %"PRIttocTSK, - hdb_info->idx_fname); - return 1; - } - hdb_info->hIdx = - _fdopen(_open_osfhandle((intptr_t) hWin, _O_RDONLY), "r"); - if (hdb_info->hIdx == NULL) { - tsk_release_lock(&hdb_info->lock); - tsk_error_reset(); - tsk_error_set_errno(TSK_ERR_HDB_OPEN); - tsk_error_set_errstr( - "hdb_setupindex: Error converting Windows handle to C handle"); - return 1; - } - - szLow = GetFileSize(hWin, &szHi); - if (szLow == 0xffffffff) { - tsk_release_lock(&hdb_info->lock); - tsk_error_reset(); - tsk_error_set_errno(TSK_ERR_HDB_OPEN); - tsk_error_set_errstr( - "hdb_setupindex: Error getting size of index file: %"PRIttocTSK" - %d", - hdb_info->idx_fname, (int)GetLastError()); - return 1; - } - hdb_info->idx_size = szLow | ((uint64_t) szHi << 32); - } - -#else - { - struct stat sb; - if (stat(hdb_info->idx_fname, &sb) < 0) { - tsk_release_lock(&hdb_info->lock); - tsk_error_reset(); - tsk_error_set_errno(TSK_ERR_HDB_MISSING); - tsk_error_set_errstr( - "hdb_setupindex: Error finding index file: %s", - hdb_info->idx_fname); - return 1; - } - hdb_info->idx_size = sb.st_size; - - if (NULL == (hdb_info->hIdx = fopen(hdb_info->idx_fname, "r"))) { - tsk_release_lock(&hdb_info->lock); - tsk_error_reset(); - tsk_error_set_errno(TSK_ERR_HDB_OPEN); - tsk_error_set_errstr( - "hdb_setupindex: Error opening index file: %s", - hdb_info->idx_fname); - return 1; - } - } -#endif - - /* Do some testing on the first line */ - if (NULL == fgets(head, TSK_HDB_MAXLEN, hdb_info->hIdx)) { - tsk_release_lock(&hdb_info->lock); - tsk_error_reset(); - tsk_error_set_errno(TSK_ERR_HDB_READIDX); - tsk_error_set_errstr( - "hdb_setupindex: Header line of index file"); - return 1; - } - - if (strncmp(head, TSK_HDB_IDX_HEAD_TYPE_STR, strlen(TSK_HDB_IDX_HEAD_TYPE_STR)) - != 0) { - tsk_release_lock(&hdb_info->lock); - tsk_error_reset(); - tsk_error_set_errno(TSK_ERR_HDB_UNKTYPE); - tsk_error_set_errstr( - "hdb_setupindex: Invalid index file: Missing header line"); - return 1; - } - - /* Do some testing on the second line */ - if (NULL == fgets(head2, TSK_HDB_MAXLEN, hdb_info->hIdx)) { - tsk_release_lock(&hdb_info->lock); - tsk_error_reset(); - tsk_error_set_errno(TSK_ERR_HDB_READIDX); - tsk_error_set_errstr( - "hdb_setupindex: Error reading line 2 of index file"); - return 1; - } - - /* Set the offset to the start of the index entries */ - if (strncmp(head2, TSK_HDB_IDX_HEAD_NAME_STR, strlen(TSK_HDB_IDX_HEAD_NAME_STR)) - != 0) { - hdb_info->idx_off = (uint16_t) (strlen(head)); - } else { - hdb_info->idx_off = (uint16_t) (strlen(head) + strlen(head2)); - } - - - /* Skip the space */ - ptr = &head[strlen(TSK_HDB_IDX_HEAD_TYPE_STR) + 1]; - - ptr[strlen(ptr) - 1] = '\0'; - if ((ptr[strlen(ptr) - 1] == 10) || (ptr[strlen(ptr) - 1] == 13)) { - ptr[strlen(ptr) - 1] = '\0'; - hdb_info->idx_llen++; // make the expected index length longer to account for different cr/nl/etc. - } - - /* Verify the header value in the index */ - if (strcmp(ptr, TSK_HDB_DBTYPE_NSRL_STR) == 0) { - if ((hdb_info->db_type != TSK_HDB_DBTYPE_NSRL_ID) && - (hdb_info->db_type != TSK_HDB_DBTYPE_IDXONLY_ID)) { - tsk_release_lock(&hdb_info->lock); - tsk_error_reset(); - tsk_error_set_errno(TSK_ERR_HDB_UNKTYPE); - tsk_error_set_errstr( - "hdb_indexsetup: DB detected as %s, index type has NSRL", - ptr); - return 1; - } - } - else if (strcmp(ptr, TSK_HDB_DBTYPE_MD5SUM_STR) == 0) { - if ((hdb_info->db_type != TSK_HDB_DBTYPE_MD5SUM_ID) && - (hdb_info->db_type != TSK_HDB_DBTYPE_IDXONLY_ID)) { - tsk_release_lock(&hdb_info->lock); - tsk_error_reset(); - tsk_error_set_errno(TSK_ERR_HDB_UNKTYPE); - tsk_error_set_errstr( - "hdb_indexsetup: DB detected as %s, index type has MD5SUM", - ptr); - return 1; - } - } - else if (strcmp(ptr, TSK_HDB_DBTYPE_HK_STR) == 0) { - if ((hdb_info->db_type != TSK_HDB_DBTYPE_HK_ID) && - (hdb_info->db_type != TSK_HDB_DBTYPE_IDXONLY_ID)) { - tsk_release_lock(&hdb_info->lock); - tsk_error_reset(); - tsk_error_set_errno(TSK_ERR_HDB_UNKTYPE); - tsk_error_set_errstr( - "hdb_indexsetup: DB detected as %s, index type has hashkeeper", - ptr); - return 1; - } - } - else if (strcmp(ptr, TSK_HDB_DBTYPE_ENCASE_STR) == 0) { - if ((hdb_info->db_type != TSK_HDB_DBTYPE_ENCASE_ID) && - (hdb_info->db_type != TSK_HDB_DBTYPE_IDXONLY_ID)) { - tsk_release_lock(&hdb_info->lock); - tsk_error_reset(); - tsk_error_set_errno(TSK_ERR_HDB_UNKTYPE); - tsk_error_set_errstr( - "hdb_indexsetup: DB detected as %s, index type has EnCase", - ptr); - return 1; - } - } - else if (hdb_info->db_type != TSK_HDB_DBTYPE_IDXONLY_ID) { - tsk_release_lock(&hdb_info->lock); - tsk_error_reset(); - tsk_error_set_errno(TSK_ERR_HDB_UNKTYPE); - tsk_error_set_errstr( - "hdb_setupindex: Unknown Database Type in index header: %s", - ptr); - return 1; - } - - /* Do some sanity checking */ - if (((hdb_info->idx_size - hdb_info->idx_off) % hdb_info->idx_llen) != - 0) { - tsk_release_lock(&hdb_info->lock); - tsk_error_reset(); - tsk_error_set_errno(TSK_ERR_HDB_CORRUPT); - tsk_error_set_errstr( - "hdb_setupindex: Error, size of index file is not a multiple of row size"); - return 1; - } - - /* allocate a buffer for a row */ - if ((hdb_info->idx_lbuf = tsk_malloc(hdb_info->idx_llen + 1)) == NULL) { - tsk_release_lock(&hdb_info->lock); - return 1; - } - - tsk_release_lock(&hdb_info->lock); - - return 0; -} - - - - - - - - -/** - * \ingroup hashdblib - * Search the index for a text/ASCII hash value - * - * @param hdb_info Open hash database (with index) - * @param hash Hash value to search for (NULL terminated string) - * @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. - */ -int8_t -tsk_hdb_lookup_str(TSK_HDB_INFO * hdb_info, const char *hash, - TSK_HDB_FLAG_ENUM flags, TSK_HDB_LOOKUP_FN action, - void *ptr) -{ - TSK_OFF_T poffset; - TSK_OFF_T up; // Offset of the first byte past the upper limit that we are looking in - TSK_OFF_T low; // offset of the first byte of the lower limit that we are looking in - int cmp; - uint8_t wasFound = 0; - size_t i; - uint8_t htype; - - - /* Sanity checks on the hash input */ - if (strlen(hash) == TSK_HDB_HTYPE_MD5_LEN) { - htype = TSK_HDB_HTYPE_MD5_ID; - } - else if (strlen(hash) == TSK_HDB_HTYPE_SHA1_LEN) { - htype = TSK_HDB_HTYPE_SHA1_ID; - } - else { - tsk_error_reset(); - tsk_error_set_errno(TSK_ERR_HDB_ARG); - tsk_error_set_errstr( - "hdb_lookup: Invalid hash length: %s", hash); - return -1; - } - - for (i = 0; i < strlen(hash); i++) { - if (isxdigit((int) hash[i]) == 0) { - tsk_error_reset(); - tsk_error_set_errno(TSK_ERR_HDB_ARG); - tsk_error_set_errstr( - "hdb_lookup: Invalid hash value (hex only): %s", - hash); - return -1; - } - } - - if (hdb_setupindex(hdb_info, htype)) - return -1; - - - /* Sanity check */ - if (hdb_info->hash_len != strlen(hash)) { - 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 %Zd)", - hdb_info->hash_len, strlen(hash)); - return -1; - } - - - low = hdb_info->idx_off; - up = hdb_info->idx_size; - - poffset = 0; - - // We have to lock access to idx_lbuf, but since we're in a loop, - // I'm assuming one lock up front is better than many inside. - tsk_take_lock(&hdb_info->lock); - - while (1) { - TSK_OFF_T offset; - - /* If top and bottom are the same, it's not there */ - if (up == low) { - tsk_release_lock(&hdb_info->lock); - return 0; - } - - /* Get the middle of the windows that we are looking at */ - offset = rounddown(((up - low) / 2), hdb_info->idx_llen); - - /* Sanity Check */ - if ((offset % hdb_info->idx_llen) != 0) { - tsk_release_lock(&hdb_info->lock); - tsk_error_reset(); - tsk_error_set_errno(TSK_ERR_HDB_CORRUPT); - tsk_error_set_errstr( - "hdb_lookup: Error, new offset is not a multiple of the line length"); - return -1; - } - - /* The middle offset is relative to the low offset, so add them */ - offset += low; - - /* If we didn't move, then it's not there */ - if (poffset == offset) { - tsk_release_lock(&hdb_info->lock); - return 0; - } - - /* Seek to the offset and read it */ - if (0 != fseeko(hdb_info->hIdx, offset, SEEK_SET)) { - tsk_release_lock(&hdb_info->lock); - tsk_error_reset(); - tsk_error_set_errno(TSK_ERR_HDB_READIDX); - tsk_error_set_errstr( - "hdb_lookup: Error seeking in search: %" PRIuOFF, - offset); - return -1; - } - - if (NULL == - fgets(hdb_info->idx_lbuf, (int) hdb_info->idx_llen + 1, - hdb_info->hIdx)) { - if (feof(hdb_info->hIdx)) { - tsk_release_lock(&hdb_info->lock); - return 0; - } - tsk_release_lock(&hdb_info->lock); - tsk_error_reset(); - tsk_error_set_errno(TSK_ERR_HDB_READIDX); - tsk_error_set_errstr( - "Error reading index file: %lu", - (unsigned long) offset); - return -1; - } - - /* Sanity Check */ - if ((strlen(hdb_info->idx_lbuf) < hdb_info->idx_llen) || - (hdb_info->idx_lbuf[hdb_info->hash_len] != '|')) { - tsk_release_lock(&hdb_info->lock); - tsk_error_reset(); - tsk_error_set_errno(TSK_ERR_HDB_CORRUPT); - tsk_error_set_errstr( - "Invalid line in index file: %lu (%s)", - (unsigned long) (offset / hdb_info->idx_llen), - hdb_info->idx_lbuf); - return -1; - } - - /* Set the delimter to NULL so we can treat the hash as a string */ - hdb_info->idx_lbuf[hdb_info->hash_len] = '\0'; - cmp = strcasecmp(hdb_info->idx_lbuf, hash); - - /* The one we just read is too small, so set the new lower bound - * at the start of the next row */ - if (cmp < 0) { - low = offset + hdb_info->idx_llen; - } - - /* The one we just read is too big, so set the upper bound at this - * entry */ - else if (cmp > 0) { - up = offset; - } - - /* We found it */ - else { - wasFound = 1; - - if ((flags & TSK_HDB_FLAG_QUICK) - || (hdb_info->db_type == TSK_HDB_DBTYPE_IDXONLY_ID)) { - tsk_release_lock(&hdb_info->lock); - return 1; - } - else { - TSK_OFF_T tmpoff, db_off; - -#ifdef TSK_WIN32 - db_off = - _atoi64(&hdb_info->idx_lbuf[hdb_info->hash_len + 1]); -#else - db_off = - strtoull(&hdb_info->idx_lbuf[hdb_info->hash_len + 1], - NULL, 10); -#endif - - /* Print the one that we found first */ - if (hdb_info-> - getentry(hdb_info, hash, db_off, flags, action, ptr)) { - tsk_release_lock(&hdb_info->lock); - tsk_error_set_errstr2( "hdb_lookup"); - return -1; - } - - - /* there could be additional entries both before and after - * this entry - but we can restrict ourselves to the up - * and low bounds from our previous hunting - */ - - tmpoff = offset - hdb_info->idx_llen; - while (tmpoff >= low) { - - /* Break if we are at the header */ - if (tmpoff <= 0) - break; - - if (0 != fseeko(hdb_info->hIdx, tmpoff, SEEK_SET)) { - tsk_release_lock(&hdb_info->lock); - tsk_error_reset(); - tsk_error_set_errno(TSK_ERR_HDB_READIDX); - tsk_error_set_errstr( - "hdb_lookup: Error seeking for prev entries: %" - PRIuOFF, tmpoff); - return -1; - } - - if (NULL == - fgets(hdb_info->idx_lbuf, - (int) hdb_info->idx_llen + 1, - hdb_info->hIdx)) { - tsk_release_lock(&hdb_info->lock); - tsk_error_reset(); - tsk_error_set_errno(TSK_ERR_HDB_READIDX); - tsk_error_set_errstr( - "Error reading index file (prev): %lu", - (unsigned long) tmpoff); - return -1; - } - else if (strlen(hdb_info->idx_lbuf) < - hdb_info->idx_llen) { - tsk_release_lock(&hdb_info->lock); - tsk_error_reset(); - tsk_error_set_errno(TSK_ERR_HDB_CORRUPT); - tsk_error_set_errstr( - "Invalid index file line (prev): %lu", - (unsigned long) tmpoff); - return -1; - } - - hdb_info->idx_lbuf[hdb_info->hash_len] = '\0'; - if (strcasecmp(hdb_info->idx_lbuf, hash) != 0) { - break; - } - -#ifdef TSK_WIN32 - db_off = - _atoi64(&hdb_info-> - idx_lbuf[hdb_info->hash_len + 1]); -#else - - db_off = - strtoull(&hdb_info-> - idx_lbuf[hdb_info->hash_len + 1], NULL, - 10); -#endif - if (hdb_info-> - getentry(hdb_info, hash, db_off, flags, action, - ptr)) { - tsk_release_lock(&hdb_info->lock); - return -1; - } - tmpoff -= hdb_info->idx_llen; - } - - /* next entries */ - tmpoff = offset + hdb_info->idx_llen; - while (tmpoff < up) { - - if (0 != fseeko(hdb_info->hIdx, tmpoff, SEEK_SET)) { - tsk_release_lock(&hdb_info->lock); - tsk_error_reset(); - tsk_error_set_errno(TSK_ERR_HDB_READIDX); - tsk_error_set_errstr( - "hdb_lookup: Error seeking for next entries: %" - PRIuOFF, tmpoff); - return -1; - } - - if (NULL == - fgets(hdb_info->idx_lbuf, - (int) hdb_info->idx_llen + 1, - hdb_info->hIdx)) { - if (feof(hdb_info->hIdx)) - break; - tsk_release_lock(&hdb_info->lock); - tsk_error_reset(); - tsk_error_set_errno(TSK_ERR_HDB_READIDX); - tsk_error_set_errstr( - "Error reading index file (next): %lu", - (unsigned long) tmpoff); - return -1; - } - else if (strlen(hdb_info->idx_lbuf) < - hdb_info->idx_llen) { - tsk_release_lock(&hdb_info->lock); - tsk_error_reset(); - tsk_error_set_errno(TSK_ERR_HDB_CORRUPT); - tsk_error_set_errstr( - "Invalid index file line (next): %lu", - (unsigned long) tmpoff); - return -1; - } - - hdb_info->idx_lbuf[hdb_info->hash_len] = '\0'; - if (strcasecmp(hdb_info->idx_lbuf, hash) != 0) { - break; - } -#ifdef TSK_WIN32 - db_off = - _atoi64(&hdb_info-> - idx_lbuf[hdb_info->hash_len + 1]); -#else - db_off = - strtoull(&hdb_info-> - idx_lbuf[hdb_info->hash_len + 1], NULL, - 10); -#endif - if (hdb_info-> - getentry(hdb_info, hash, db_off, flags, action, - ptr)) { - tsk_release_lock(&hdb_info->lock); - return -1; - } - - tmpoff += hdb_info->idx_llen; - } - } - break; - } - poffset = offset; - } - tsk_release_lock(&hdb_info->lock); - - return wasFound; -} - -/** - * \ingroup hashdblib - * Search the index for the given hash value given (in binary form). - * - * @param hdb_info Open hash database (with index) - * @param hash Array with binary hash value to search for - * @param len Number of bytes in binary hash value - * @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. - */ -int8_t -tsk_hdb_lookup_raw(TSK_HDB_INFO * hdb_info, uint8_t * hash, uint8_t len, - TSK_HDB_FLAG_ENUM flags, - TSK_HDB_LOOKUP_FN action, void *ptr) -{ - char hashbuf[TSK_HDB_HTYPE_SHA1_LEN + 1]; - int i; - static const char hex[] = "0123456789abcdef"; - - if (2 * len > TSK_HDB_HTYPE_SHA1_LEN) { - tsk_error_reset(); - tsk_error_set_errno(TSK_ERR_HDB_ARG); - tsk_error_set_errstr( - "tsk_hdb_lookup_raw: hash value too long\n"); - return -1; - } - - for (i = 0; i < len; i++) { - hashbuf[2 * i] = hex[(hash[i] >> 4) & 0xf]; - hashbuf[2 * i + 1] = hex[hash[i] & 0xf]; - } - hashbuf[2 * len] = '\0'; - - return tsk_hdb_lookup_str(hdb_info, hashbuf, flags, action, ptr); -} - -/** - * \ingroup hashdblib - * Determine if the open hash database has an index. - * - * @param hdb_info Hash database to consider - * @param htype Hash type that index should be of - * - * @return 1 if index exists and 0 if not - */ -uint8_t -tsk_hdb_hasindex(TSK_HDB_INFO * hdb_info, uint8_t htype) -{ - /* Check if the index is already open, and - * try to open it if not */ - if (hdb_setupindex(hdb_info, htype)) - return 0; - else - return 1; -} - - - -/** - * \ingroup hashdblib - * Open a hash database. - * - * @param db_file Path to database (even if only an index exists). - * @param flags Flags for opening the database. - * - * @return Poiner to hash database state structure or NULL on error - */ -TSK_HDB_INFO * -tsk_hdb_open(TSK_TCHAR * db_file, TSK_HDB_OPEN_ENUM flags) -{ - TSK_HDB_INFO *hdb_info; - size_t flen; - FILE *hDb; - uint8_t dbtype = 0; - - if ((flags & TSK_HDB_OPEN_IDXONLY) == 0) { - /* Open the database file */ -#ifdef TSK_WIN32 - { - HANDLE hWin; - - if ((hWin = CreateFile(db_file, GENERIC_READ, - FILE_SHARE_READ, 0, OPEN_EXISTING, 0, - 0)) == INVALID_HANDLE_VALUE) { - tsk_error_reset(); - tsk_error_set_errno(TSK_ERR_HDB_OPEN); - tsk_error_set_errstr( - "hdb_open: Error opening database file: %S", - db_file); - return NULL; - } - hDb = - _fdopen(_open_osfhandle((intptr_t) hWin, _O_RDONLY), "r"); - if (hDb == NULL) { - tsk_error_reset(); - tsk_error_set_errno(TSK_ERR_HDB_OPEN); - tsk_error_set_errstr( - "hdb_open: Error converting Windows handle to C handle"); - return NULL; - } - } -#else - if (NULL == (hDb = fopen(db_file, "r"))) { - tsk_error_reset(); - tsk_error_set_errno(TSK_ERR_HDB_OPEN); - tsk_error_set_errstr( - "hdb_open: Error opening database file: %s", db_file); - return NULL; - } -#endif - - /* Try to figure out what type of DB it is */ - if (nsrl_test(hDb)) { - dbtype = TSK_HDB_DBTYPE_NSRL_ID; - } - if (md5sum_test(hDb)) { - if (dbtype != 0) { - tsk_error_reset(); - tsk_error_set_errno(TSK_ERR_HDB_UNKTYPE); - tsk_error_set_errstr( - "hdb_open: Error determining DB type (MD5sum)"); - return NULL; - } - dbtype = TSK_HDB_DBTYPE_MD5SUM_ID; - } - if (encase_test(hDb)) { - if (dbtype != 0) { - tsk_error_reset(); - tsk_error_set_errno(TSK_ERR_HDB_UNKTYPE); - tsk_error_set_errstr( - "hdb_open: Error determining DB type (EnCase)"); - return NULL; - } - dbtype = TSK_HDB_DBTYPE_ENCASE_ID; - } - if (hk_test(hDb)) { - if (dbtype != 0) { - tsk_error_reset(); - tsk_error_set_errno(TSK_ERR_HDB_UNKTYPE); - tsk_error_set_errstr( - "hdb_open: Error determining DB type (HK)"); - return NULL; - } - dbtype = TSK_HDB_DBTYPE_HK_ID; - } - if (dbtype == 0) { - tsk_error_reset(); - tsk_error_set_errno(TSK_ERR_HDB_UNKTYPE); - tsk_error_set_errstr( - "hdb_open: Error determining DB type"); - return NULL; - } - fseeko(hDb, 0, SEEK_SET); - } - else { - dbtype = TSK_HDB_DBTYPE_IDXONLY_ID; - hDb = NULL; - } - - if ((hdb_info = - (TSK_HDB_INFO *) tsk_malloc(sizeof(TSK_HDB_INFO))) == NULL) - return NULL; - - hdb_info->hDb = hDb; - - /* Copy the database name into the structure */ - flen = TSTRLEN(db_file) + 8; // + 32; - - hdb_info->db_fname = - (TSK_TCHAR *) tsk_malloc(flen * sizeof(TSK_TCHAR)); - if (hdb_info->db_fname == NULL) { - free(hdb_info); - return NULL; - } - TSTRNCPY(hdb_info->db_fname, db_file, flen); - - - hdb_info->hash_type = 0; - hdb_info->hash_len = 0; - hdb_info->idx_fname = NULL; - - hdb_info->uns_fname = NULL; - hdb_info->hIdxTmp = NULL; - hdb_info->hIdx = NULL; - - hdb_info->idx_size = 0; - hdb_info->idx_off = 0; - - hdb_info->idx_lbuf = NULL; - - tsk_init_lock(&hdb_info->lock); - - /* Get database specific information */ - hdb_info->db_type = dbtype; - switch (dbtype) { - case TSK_HDB_DBTYPE_NSRL_ID: - nsrl_name(hdb_info); - hdb_info->getentry = nsrl_getentry; - hdb_info->makeindex = nsrl_makeindex; - break; - - case TSK_HDB_DBTYPE_MD5SUM_ID: - md5sum_name(hdb_info); - hdb_info->getentry = md5sum_getentry; - hdb_info->makeindex = md5sum_makeindex; - break; - - case TSK_HDB_DBTYPE_ENCASE_ID: - encase_name(hdb_info); - hdb_info->getentry = encase_getentry; - hdb_info->makeindex = encase_makeindex; - break; - - case TSK_HDB_DBTYPE_HK_ID: - hk_name(hdb_info); - hdb_info->getentry = hk_getentry; - hdb_info->makeindex = hk_makeindex; - break; - - case TSK_HDB_DBTYPE_IDXONLY_ID: - idxonly_name(hdb_info); - hdb_info->getentry = idxonly_getentry; - hdb_info->makeindex = idxonly_makeindex; - break; - - default: - return NULL; - } - - - return hdb_info; -} - -/** - * \ingroup hashdblib - * Close an open hash database. - * - * @param hdb_info database to close - */ -void -tsk_hdb_close(TSK_HDB_INFO * hdb_info) -{ - if (hdb_info->hIdx) - fclose(hdb_info->hIdx); - - if (hdb_info->hIdxTmp) - fclose(hdb_info->hIdxTmp); - // @@@ Could delete temp file too... - - if (hdb_info->idx_lbuf != NULL) - free(hdb_info->idx_lbuf); - - if (hdb_info->db_fname) - free(hdb_info->db_fname); - - if (hdb_info->uns_fname) - free(hdb_info->uns_fname); - - if (hdb_info->idx_fname) - free(hdb_info->idx_fname); - - if (hdb_info->hDb) - fclose(hdb_info->hDb); - - tsk_deinit_lock(&hdb_info->lock); - - free(hdb_info); -} - -/** - * \ingroup hashdblib - * Create an index for an open hash database. - * @param a_hdb_info Open hash database to index - * @param a_type Text of hash database type - * @returns 1 on error - */ -uint8_t -tsk_hdb_makeindex(TSK_HDB_INFO * a_hdb_info, TSK_TCHAR * a_type) -{ - return a_hdb_info->makeindex(a_hdb_info, a_type); -} - -/** - * Set db_name to the name of the database file - * - * @param hdb_info the hash database object - */ -void -tsk_hdb_name_from_path(TSK_HDB_INFO * hdb_info) -{ -#ifdef TSK_WIN32 - const char PATH_CHAR = '\\'; -#else - const char PATH_CHAR = '/'; -#endif - TSK_TCHAR * begin; - TSK_TCHAR * end; - int i; - - hdb_info->db_name[0] = '\0'; - - begin = TSTRRCHR(hdb_info->db_fname, PATH_CHAR); -#ifdef TSK_WIN32 - // cygwin can have forward slashes, so try that too on Windows - if (!begin) { - begin = TSTRRCHR(hdb_info->db_fname, '/'); - } -#endif - - if (!begin) { - begin = hdb_info->db_fname; - } - else { - // unlikely since this means that the dbname is "/" - if (TSTRLEN(begin) == 1) - return; - else - begin++; - } - - // end points to the byte after the last one we want to use - if ((TSTRLEN(hdb_info->db_fname) > 4) && (TSTRICMP(&hdb_info->db_fname[TSTRLEN(hdb_info->db_fname)-4], _TSK_T(".idx")) == 0)) - end = &hdb_info->db_fname[TSTRLEN(hdb_info->db_fname)-4]; - else - end = begin + TSTRLEN(begin); - - - // @@@ TODO: Use TskUTF16_to_UTF8 to properly convert for Windows - for(i = 0; i < (end-begin); i++) - { - hdb_info->db_name[i] = (char) begin[i]; - } - - hdb_info->db_name[i] = '\0'; -} diff --git a/tsk/hashdb/tm_lookup.cpp b/tsk/hashdb/tm_lookup.cpp new file mode 100644 index 0000000000000000000000000000000000000000..cc663b0b60e641961a21ee65ca504e51582e887d --- /dev/null +++ b/tsk/hashdb/tm_lookup.cpp @@ -0,0 +1,104 @@ +/* + * The Sleuth Kit + * + * Brian Carrier [carrier <at> sleuthkit [dot] org] + * Copyright (c) 2003-2013 Brian Carrier. All rights reserved + * + * + * This software is distributed under the Common Public License 1.0 + */ + +#include "tsk_hashdb_i.h" + + +/** + * \file tm_lookup.cpp + * Contains the generic hash database lookup code. + */ + + + +/** + * \ingroup hashdblib + * Search the index for a text/ASCII hash value + * + * @param hdb_info Open hash database (with index) + * @param hash Hash value to search for (NULL terminated string) + * @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. + */ +int8_t +tsk_hdb_lookup_str(TSK_HDB_INFO * hdb_info, const char *hash, + TSK_HDB_FLAG_ENUM flags, TSK_HDB_LOOKUP_FN action, + void *ptr) +{ + uint8_t htype; + + /* Sanity checks on the hash input */ + if (strlen(hash) == TSK_HDB_HTYPE_MD5_LEN) { + htype = TSK_HDB_HTYPE_MD5_ID; + } + else if (strlen(hash) == TSK_HDB_HTYPE_SHA1_LEN) { + htype = TSK_HDB_HTYPE_SHA1_ID; + } + else { + tsk_error_reset(); + tsk_error_set_errno(TSK_ERR_HDB_ARG); + tsk_error_set_errstr( + "hdb_lookup_str: Invalid hash length: %s", hash); + return -1; + } + + if (hdb_setupindex(hdb_info, htype, 0)) { + return -1; + } + + return hdb_info->idx_info->lookup_str(hdb_info, hash, flags, action, ptr); +} + +/** + * \ingroup hashdblib + * Search the index for the given hash value given (in binary form). + * + * @param hdb_info Open hash database (with index) + * @param hash Array with binary hash value to search for + * @param len Number of bytes in binary hash value + * @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. + */ +int8_t +tsk_hdb_lookup_raw(TSK_HDB_INFO * hdb_info, uint8_t * hash, uint8_t len, + TSK_HDB_FLAG_ENUM flags, + TSK_HDB_LOOKUP_FN action, void *ptr) +{ + uint8_t htype; + + /* Sanity checks on the hash input */ + if (len/2 == TSK_HDB_HTYPE_MD5_LEN) { + htype = TSK_HDB_HTYPE_MD5_ID; + } + else if (len/2 == TSK_HDB_HTYPE_SHA1_LEN) { + htype = TSK_HDB_HTYPE_SHA1_ID; + } + else { + tsk_error_reset(); + tsk_error_set_errno(TSK_ERR_HDB_ARG); + tsk_error_set_errstr( + "hdb_lookup_raw: Invalid hash length: %s", hash); + return -1; + } + + if (hdb_setupindex(hdb_info, htype, 0)) { + return -1; + } + + return hdb_info->idx_info->lookup_raw(hdb_info, hash, len, flags, action, ptr); +} diff --git a/tsk/hashdb/tsk_hashdb.h b/tsk/hashdb/tsk_hashdb.h index 66f01a47f80dd509b9f25837d203990f4e34be8b..e3059edd117909d9ed6492a7d3974900d2e00756 100644 --- a/tsk/hashdb/tsk_hashdb.h +++ b/tsk/hashdb/tsk_hashdb.h @@ -17,6 +17,8 @@ * \defgroup hashdblib_cpp C++ Hash Database Classes */ +#include "tsk/auto/sqlite3.h" + #ifndef _TSK_HDB_H #define _TSK_HDB_H @@ -43,12 +45,15 @@ extern "C" { TSK_HDB_HTYPE_INVALID_ID = 0, ///< Invalid algorithm signals error. TSK_HDB_HTYPE_MD5_ID = 1, ///< MD5 Algorithm TSK_HDB_HTYPE_SHA1_ID = 2, ///< SHA1 Algorithm + TSK_HDB_HTYPE_SHA2_256_ID = 4, ///< SHA2-256 (aka SHA-256) Algorithm }; typedef enum TSK_HDB_HTYPE_ENUM TSK_HDB_HTYPE_ENUM; #define TSK_HDB_HTYPE_MD5_STR "md5" ///< String name for MD5 algorithm #define TSK_HDB_HTYPE_SHA1_STR "sha1" ///< String name for SHA1 algorithm +#define TSK_HDB_HTYPE_SHA2_256_STR "sha2_256" ///< String name for SHA256 algorithm +#define TSK_HDB_HTYPE_SHA2_256_LEN 64 ///< Length of SHA256 hash #define TSK_HDB_HTYPE_SHA1_LEN 40 ///< Length of SHA1 hash #define TSK_HDB_HTYPE_MD5_LEN 32 ///< Length of MD5 hash #define TSK_HDB_HTYPE_CRC32_LEN 8 ///< Length of CRC hash @@ -59,16 +64,27 @@ extern "C" { */ #define TSK_HDB_HTYPE_STR(x) \ ( ((x) & TSK_HDB_HTYPE_MD5_ID) ? (TSK_HDB_HTYPE_MD5_STR) : ( \ - ( ((x) & TSK_HDB_HTYPE_SHA1_ID) ? TSK_HDB_HTYPE_SHA1_STR : "") ) ) + ( ((x) & TSK_HDB_HTYPE_SHA1_ID) ? (TSK_HDB_HTYPE_SHA1_STR) : ( \ + ( ((x) & TSK_HDB_HTYPE_SHA2_256_ID) ? TSK_HDB_HTYPE_SHA2_256_STR : "") ) ) ) ) /** * Return the length of a hash, given its ID */ #define TSK_HDB_HTYPE_LEN(x) \ ( ((x) & TSK_HDB_HTYPE_MD5_ID) ? (TSK_HDB_HTYPE_MD5_LEN) : ( \ - ( ((x) & TSK_HDB_HTYPE_SHA1_ID) ? TSK_HDB_HTYPE_SHA1_LEN : 0) ) ) + ( ((x) & TSK_HDB_HTYPE_SHA1_ID) ? (TSK_HDB_HTYPE_SHA1_LEN) : ( \ + ( ((x) & TSK_HDB_HTYPE_SHA2_256_ID) ? TSK_HDB_HTYPE_SHA2_256_LEN : 0) ) ) ) ) + + /** + * Hash Index types + */ + enum TSK_HDB_ITYPE_ENUM { + TSK_HDB_ITYPE_BINSRCH = 1, ///< Original binary search text format + TSK_HDB_ITYPE_SQLITE_V1 = 2 ///< Sqlite database format + }; + typedef enum TSK_HDB_ITYPE_ENUM TSK_HDB_ITYPE_ENUM; /** * Hash Database types @@ -85,12 +101,12 @@ extern "C" { /* String versions of DB types */ -#define TSK_HDB_DBTYPE_NSRL_STR "nsrl" ///< NSRL String name -#define TSK_HDB_DBTYPE_NSRL_MD5_STR "nsrl-md5" ///< NSRL md5 string name -#define TSK_HDB_DBTYPE_NSRL_SHA1_STR "nsrl-sha1" ///< NSRL SHA1 string name -#define TSK_HDB_DBTYPE_MD5SUM_STR "md5sum" ///< md5sum db string n ame -#define TSK_HDB_DBTYPE_HK_STR "hk" ///< hash keeper string name -#define TSK_HDB_DBTYPE_ENCASE_STR "encase" ///< encase string name +#define TSK_HDB_DBTYPE_NSRL_STR "nsrl" ///< NSRL database +#define TSK_HDB_DBTYPE_NSRL_MD5_STR "nsrl-md5" ///< NSRL database with MD5 index +#define TSK_HDB_DBTYPE_NSRL_SHA1_STR "nsrl-sha1" ///< NSRL database with SHA1 index +#define TSK_HDB_DBTYPE_MD5SUM_STR "md5sum" ///< md5sum database +#define TSK_HDB_DBTYPE_HK_STR "hk" ///< hash keeper index +#define TSK_HDB_DBTYPE_ENCASE_STR "encase" ///< encase index /// List of supported data base types #define TSK_HDB_DBTYPE_SUPPORT_STR "nsrl-md5, nsrl-sha1, md5sum, encase, hk" @@ -98,12 +114,63 @@ extern "C" { typedef struct TSK_HDB_INFO TSK_HDB_INFO; + typedef struct TSK_IDX_INFO TSK_IDX_INFO; typedef TSK_WALK_RET_ENUM(*TSK_HDB_LOOKUP_FN) (TSK_HDB_INFO *, const char *hash, const char *name, void *); + /** + * Holds information about a sqlite index + */ + struct TSK_IDX_SQLITE_V1 { + sqlite3 *hIdx_sqlite; ///< Sqlite DB if index is using sqlite schema + }; + typedef struct TSK_IDX_SQLITE_V1 TSK_IDX_SQLITE_V1; + + /** + * Holds information about a plain text / binary search index + */ + struct TSK_IDX_BINSRCH { + FILE *hIdx; ///< File handle to index (only open during lookups) + FILE *hIdxTmp; ///< File handle to temp (unsorted) index file (only open during index creation) + TSK_TCHAR *uns_fname; ///< Name of unsorted index file + + TSK_OFF_T idx_size; ///< Size of index file + uint16_t idx_off; ///< Offset in index file to first index entry + size_t idx_llen; ///< Length of each line in index + char *idx_lbuf; ///< Buffer to hold a line from the index (r/w shared - lock) + }; + typedef struct TSK_IDX_BINSRCH TSK_IDX_BINSRCH; + + /** + * Holds information about a hash index. Created by idx_open. + */ + struct TSK_IDX_INFO { + TSK_HDB_ITYPE_ENUM index_type; ///< Type of index + TSK_TCHAR *idx_fname; ///< Name of index file + uint8_t updateable; ///< Allow new entries to be added? + + union { + TSK_IDX_SQLITE_V1 * idx_sqlite_v1; + TSK_IDX_BINSRCH * idx_binsrch; + }idx_struct; + + uint8_t(*open) (TSK_HDB_INFO *, TSK_IDX_INFO *, uint8_t); + uint8_t(*initialize) (TSK_HDB_INFO *, TSK_TCHAR *); + uint8_t(*addentry) (TSK_HDB_INFO *, char *, TSK_OFF_T); + uint8_t(*addentry_bin) (TSK_HDB_INFO *, unsigned char *, int, TSK_OFF_T); + uint8_t(*finalize) (TSK_HDB_INFO *); + int8_t(*lookup_str) (TSK_HDB_INFO *, const char *, TSK_HDB_FLAG_ENUM, + TSK_HDB_LOOKUP_FN, void *); + int8_t(*lookup_raw) (TSK_HDB_INFO *, uint8_t *, uint8_t, + TSK_HDB_FLAG_ENUM, TSK_HDB_LOOKUP_FN, void *); + int8_t(*get_properties) (TSK_HDB_INFO *); + void(*close) (TSK_IDX_INFO *); + + }; + /** * Holds information about an open hash database. Created by * hdb_open and used for making an index and looking up values. @@ -114,25 +181,16 @@ extern "C" { TSK_TCHAR *db_fname; ///< Name of the database file - TSK_TCHAR *uns_fname; ///< Name of unsorted index file - FILE *hDb; ///< File handle to database (always open) - FILE *hIdxTmp; ///< File handle to temp (unsorted) index file (only open during index creation) - FILE *hIdx; ///< File handle to index (only open during lookups) - - TSK_OFF_T idx_size; ///< Size of index file - uint16_t idx_off; ///< Offset in index file to first index entry - size_t idx_llen; ///< Length of each line in index - - /* lock protects idx_lbuf and lazy loading of hIdx */ - tsk_lock_t lock; ///< Lock for lazy loading and idx_lbuf - char *idx_lbuf; ///< Buffer to hold a line from the index (r/w shared - lock) - TSK_TCHAR *idx_fname; ///< Name of index file TSK_HDB_HTYPE_ENUM hash_type; ///< Type of hash used in index uint16_t hash_len; ///< Length of hash TSK_HDB_DBTYPE_ENUM db_type; ///< Type of database + TSK_IDX_INFO * idx_info; ///< The index for the hdb info + + /* lock protects idx_lbuf and lazy loading of idx_info */ + tsk_lock_t lock; ///< Lock for lazy loading and idx_lbuf uint8_t(*getentry) (TSK_HDB_INFO *, const char *, TSK_OFF_T, TSK_HDB_FLAG_ENUM, TSK_HDB_LOOKUP_FN, void *); ///< \internal Database-specific function to find entry at a given offset uint8_t(*makeindex) (TSK_HDB_INFO *, TSK_TCHAR *); ///< \internal Database-specific function to make index @@ -143,25 +201,36 @@ extern "C" { */ enum TSK_HDB_OPEN_ENUM { TSK_HDB_OPEN_NONE = 0, ///< No special flags - TSK_HDB_OPEN_IDXONLY = (0x1 << 0) ///< Open only the index -- do not look for the original DB + TSK_HDB_OPEN_IDXONLY = (0x1 << 0), ///< Open only the index -- do not look for the original DB + TSK_HDB_OPEN_TRY = (0x1 << 1) ///< Try to open original db. If that fails, try TSK_HDB_OPEN_IDXONLY. }; typedef enum TSK_HDB_OPEN_ENUM TSK_HDB_OPEN_ENUM; - + /* Functions */ extern TSK_HDB_INFO *tsk_hdb_open(TSK_TCHAR * db_file, TSK_HDB_OPEN_ENUM flags); + extern void tsk_hdb_close(TSK_HDB_INFO * hdb); extern uint8_t tsk_hdb_hasindex(TSK_HDB_INFO *, uint8_t htype); + + extern uint8_t tsk_hdb_is_idxonly(TSK_HDB_INFO *); + extern uint8_t tsk_hdb_makeindex(TSK_HDB_INFO *, TSK_TCHAR *); + extern TSK_HDB_INFO * tsk_hdb_new(TSK_TCHAR * db_file); + + extern int8_t tsk_hdb_add_str(TSK_HDB_INFO * hdb_info, + const TSK_TCHAR * filename, + const char * md5, + const char * sha1, + const char * sha256); - /* Functions */ extern int8_t tsk_hdb_lookup_str(TSK_HDB_INFO *, const char *, TSK_HDB_FLAG_ENUM, TSK_HDB_LOOKUP_FN, void *); - extern int8_t tsk_hdb_lookup_raw(TSK_HDB_INFO *, uint8_t * hash, + extern int8_t tsk_hdb_lookup_raw(TSK_HDB_INFO * hdb_info, uint8_t * hash, uint8_t len, TSK_HDB_FLAG_ENUM, TSK_HDB_LOOKUP_FN, void *); diff --git a/tsk/hashdb/tsk_hashdb_i.h b/tsk/hashdb/tsk_hashdb_i.h index b6066f03d27be1175c9ece9d801350f5d9ff0d56..fc838eb80b39b7a41ad7a1cabccadea831153148 100644 --- a/tsk/hashdb/tsk_hashdb_i.h +++ b/tsk/hashdb/tsk_hashdb_i.h @@ -57,7 +57,22 @@ extern "C" { #define TSK_HDB_IDX_HEAD_TYPE_STR "00000000000000000000000000000000000000000" #define TSK_HDB_IDX_HEAD_NAME_STR "00000000000000000000000000000000000000001" +/** + * Properties for the sqlite hash database index + */ +#define IDX_SCHEMA_VER "Index Schema Version" +#define IDX_VERSION_NUM "1" +#define IDX_HASHSET_NAME "Hashset Name" +#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); + + extern void tsk_idx_close(TSK_IDX_INFO * idx_info); extern uint8_t tsk_hdb_idxinitialize(TSK_HDB_INFO *, TSK_TCHAR * dbname); @@ -105,6 +120,34 @@ extern "C" { extern uint8_t idxonly_getentry(TSK_HDB_INFO *, const char *, TSK_OFF_T, TSK_HDB_FLAG_ENUM, TSK_HDB_LOOKUP_FN, void *); + + extern uint8_t binsrch_open(TSK_HDB_INFO *, TSK_IDX_INFO *, uint8_t); + extern void binsrch_close(TSK_IDX_INFO *); + extern uint8_t binsrch_initialize(TSK_HDB_INFO *, TSK_TCHAR *); + extern uint8_t binsrch_addentry(TSK_HDB_INFO *, char *, TSK_OFF_T); + extern uint8_t binsrch_addentry_bin(TSK_HDB_INFO *, + unsigned char *, int, TSK_OFF_T); + extern uint8_t binsrch_finalize(TSK_HDB_INFO *); + extern int8_t binsrch_lookup_str(TSK_HDB_INFO *, const char *, + TSK_HDB_FLAG_ENUM, TSK_HDB_LOOKUP_FN, void *); + extern int8_t binsrch_lookup_raw(TSK_HDB_INFO *, uint8_t *, uint8_t, + TSK_HDB_FLAG_ENUM, TSK_HDB_LOOKUP_FN, void *); + extern int8_t binsrch_get_properties(TSK_HDB_INFO * hdb_info); + + extern uint8_t sqlite_v1_open(TSK_HDB_INFO *, TSK_IDX_INFO *, uint8_t); + extern void sqlite_v1_close(TSK_IDX_INFO *); + extern uint8_t sqlite_v1_initialize(TSK_HDB_INFO *, TSK_TCHAR *); + extern uint8_t sqlite_v1_begin(TSK_HDB_INFO *); + extern uint8_t sqlite_v1_addentry(TSK_HDB_INFO *, char *, TSK_OFF_T); + extern uint8_t sqlite_v1_addentry_bin(TSK_HDB_INFO *, + unsigned char *, int, TSK_OFF_T); + extern uint8_t sqlite_v1_finalize(TSK_HDB_INFO *); + extern int8_t sqlite_v1_lookup_str(TSK_HDB_INFO *, const char *, + TSK_HDB_FLAG_ENUM, TSK_HDB_LOOKUP_FN, void *); + extern int8_t sqlite_v1_lookup_raw(TSK_HDB_INFO *, uint8_t *, uint8_t, + TSK_HDB_FLAG_ENUM, TSK_HDB_LOOKUP_FN, void *); + extern int8_t sqlite_v1_get_properties(TSK_HDB_INFO * hdb_info); + extern uint8_t sqlite3_test(FILE *); #ifdef __cplusplus } #endif diff --git a/win32/libtsk/libtsk.vcxproj b/win32/libtsk/libtsk.vcxproj index cb68eefb5219718f2c80ad4ae426e2570b253417..8daa257d6473f02ba90d66307d34b43c37a33114 100755 --- a/win32/libtsk/libtsk.vcxproj +++ b/win32/libtsk/libtsk.vcxproj @@ -273,12 +273,16 @@ copy "$(LIBEWF_HOME)\msvscpp\x64\release\zlib.dll" "$(OutDir)" <ClCompile Include="..\..\tsk\base\tsk_unicode.c" /> <ClCompile Include="..\..\tsk\base\tsk_version.c" /> <ClCompile Include="..\..\tsk\base\XGetopt.c" /> - <ClCompile Include="..\..\tsk\hashdb\encase_index.c" /> - <ClCompile Include="..\..\tsk\hashdb\hk_index.c" /> - <ClCompile Include="..\..\tsk\hashdb\idxonly_index.c" /> - <ClCompile Include="..\..\tsk\hashdb\md5sum_index.c" /> - <ClCompile Include="..\..\tsk\hashdb\nsrl_index.c" /> - <ClCompile Include="..\..\tsk\hashdb\tm_lookup.c" /> + <ClCompile Include="..\..\tsk\hashdb\encase.c" /> + <ClCompile Include="..\..\tsk\hashdb\hashkeeper.c" /> + <ClCompile Include="..\..\tsk\hashdb\idxonly.c" /> + <ClCompile Include="..\..\tsk\hashdb\md5sum.c" /> + <ClCompile Include="..\..\tsk\hashdb\nsrl.c" /> + <ClCompile Include="..\..\tsk\hashdb\hdb_open.cpp" /> + <ClCompile Include="..\..\tsk\hashdb\hdb_index.cpp" /> + <ClCompile Include="..\..\tsk\hashdb\sqlite_index.cpp" /> + <ClCompile Include="..\..\tsk\hashdb\binsrch_index.c" /> + <ClCompile Include="..\..\tsk\hashdb\tm_lookup.cpp" /> <ClCompile Include="..\..\tsk\img\aff.c" /> <ClCompile Include="..\..\tsk\img\ewf.c" /> <ClCompile Include="..\..\tsk\img\img_io.c" /> @@ -323,4 +327,4 @@ copy "$(LIBEWF_HOME)\msvscpp\x64\release\zlib.dll" "$(OutDir)" <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> <ImportGroup Label="ExtensionTargets"> </ImportGroup> -</Project> \ No newline at end of file +</Project> diff --git a/win32/libtsk/libtsk.vcxproj.filters b/win32/libtsk/libtsk.vcxproj.filters index c64d1b2ea867bc4d6cdfb87547c007517ea6f2f0..247cfc9b145ed6f3648847cc121f6426d0ada3c3 100755 --- a/win32/libtsk/libtsk.vcxproj.filters +++ b/win32/libtsk/libtsk.vcxproj.filters @@ -222,24 +222,6 @@ <ClCompile Include="..\..\tsk\base\XGetopt.c"> <Filter>base</Filter> </ClCompile> - <ClCompile Include="..\..\tsk\hashdb\encase_index.c"> - <Filter>hash</Filter> - </ClCompile> - <ClCompile Include="..\..\tsk\hashdb\hk_index.c"> - <Filter>hash</Filter> - </ClCompile> - <ClCompile Include="..\..\tsk\hashdb\idxonly_index.c"> - <Filter>hash</Filter> - </ClCompile> - <ClCompile Include="..\..\tsk\hashdb\md5sum_index.c"> - <Filter>hash</Filter> - </ClCompile> - <ClCompile Include="..\..\tsk\hashdb\nsrl_index.c"> - <Filter>hash</Filter> - </ClCompile> - <ClCompile Include="..\..\tsk\hashdb\tm_lookup.c"> - <Filter>hash</Filter> - </ClCompile> <ClCompile Include="..\..\tsk\img\aff.c"> <Filter>img</Filter> </ClCompile> @@ -271,6 +253,36 @@ <Filter>fs</Filter> </ClCompile> <ClCompile Include="..\..\tsk\fs\yaffs.cpp" /> + <ClCompile Include="..\..\tsk\hashdb\tm_lookup.cpp"> + <Filter>hash</Filter> + </ClCompile> + <ClCompile Include="..\..\tsk\hashdb\sqlite_index.cpp"> + <Filter>hash</Filter> + </ClCompile> + <ClCompile Include="..\..\tsk\hashdb\binsrch_index.c"> + <Filter>hash</Filter> + </ClCompile> + <ClCompile Include="..\..\tsk\hashdb\hashkeeper.c"> + <Filter>hash</Filter> + </ClCompile> + <ClCompile Include="..\..\tsk\hashdb\encase.c"> + <Filter>hash</Filter> + </ClCompile> + <ClCompile Include="..\..\tsk\hashdb\hdb_index.cpp"> + <Filter>hash</Filter> + </ClCompile> + <ClCompile Include="..\..\tsk\hashdb\hdb_open.cpp"> + <Filter>hash</Filter> + </ClCompile> + <ClCompile Include="..\..\tsk\hashdb\idxonly.c"> + <Filter>hash</Filter> + </ClCompile> + <ClCompile Include="..\..\tsk\hashdb\md5sum.c"> + <Filter>hash</Filter> + </ClCompile> + <ClCompile Include="..\..\tsk\hashdb\nsrl.c"> + <Filter>hash</Filter> + </ClCompile> </ItemGroup> <ItemGroup> <ClInclude Include="..\..\tsk\vs\tsk_bsd.h">