diff --git a/bindings/java/build.xml b/bindings/java/build.xml index ab734e61205edcd5a75998146471f1590c647568..c5d35abb6eafb3646da41a6ee1929dff3d109efc 100644 --- a/bindings/java/build.xml +++ b/bindings/java/build.xml @@ -11,7 +11,7 @@ <import file="build-${os.family}.xml"/> <!-- Careful changing this because release-windows.pl updates it by pattern --> -<property name="VERSION" value="4.11.0"/> +<property name="VERSION" value="4.11.1"/> <!-- set global properties for this build --> <property name="default-jar-location" location="/usr/share/java"/> diff --git a/bindings/java/doxygen/Doxyfile b/bindings/java/doxygen/Doxyfile index b20fc027b6c600a56253ada19199495d3e51d26f..a5d8817946c13cff8a6c7f596189432f76125639 100644 --- a/bindings/java/doxygen/Doxyfile +++ b/bindings/java/doxygen/Doxyfile @@ -39,7 +39,7 @@ PROJECT_NAME = "Sleuth Kit Java Bindings (JNI)" # control system is used. # NOTE: This is updated by the release-unix.pl script -PROJECT_NUMBER = 4.11.0 +PROJECT_NUMBER = 4.11.1 # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer a @@ -1056,7 +1056,7 @@ GENERATE_HTML = YES # This tag requires that the tag GENERATE_HTML is set to YES. # NOTE: This is updated by the release-unix.pl script -HTML_OUTPUT = jni-docs/4.11.0/ +HTML_OUTPUT = jni-docs/4.11.1/ # The HTML_FILE_EXTENSION tag can be used to specify the file extension for each # generated HTML page (for example: .htm, .php, .asp). diff --git a/bindings/java/doxygen/artifact_catalog.dox b/bindings/java/doxygen/artifact_catalog.dox index 5f780c912b1f0e28ab9a1e80a57bc2caf99346ca..95c019c8233e9ec0069af145d3c3753829f65ae6 100644 --- a/bindings/java/doxygen/artifact_catalog.dox +++ b/bindings/java/doxygen/artifact_catalog.dox @@ -121,6 +121,35 @@ Indicates that an object was detected in a media file. Typically used by compute - TSK_DESCRIPTION (Additional comments about the object or observer, e.g., what detected the object) +--- +## TSK_PREVIOUSLY_NOTABLE +Indicates that the file or artifact was previously tagged as "Notable" in another Autopsy case. + +### REQUIRED ATTRIBUTES +- TSK_CORRELATION_TYPE (The correlation type that was previously tagged as notable) +- TSK_CORRELATION_VALUE (The correlation value that was previously tagged as notable) +- TSK_OTHER_CASES (The list of cases containing this file or artifact at the time the artifact is created) + + +--- +## TSK_PREVIOUSLY_SEEN +Indicates that the file or artifact was previously seen in another Autopsy case. + +### REQUIRED ATTRIBUTES +- TSK_CORRELATION_TYPE (The correlation type that was previously seen) +- TSK_CORRELATION_VALUE (The correlation value that was previously seen) +- TSK_OTHER_CASES (The list of cases containing this file or artifact at the time the artifact is created) + + +--- +## TSK_PREVIOUSLY_UNSEEN +Indicates that the file or artifact was previously unseen in another Autopsy case. + +### REQUIRED ATTRIBUTES +- TSK_CORRELATION_TYPE (The correlation type that was previously seen) +- TSK_CORRELATION_VALUE (The correlation value that was previously seen) + + --- ## TSK_USER_CONTENT_SUSPECTED An indication that some media file content was generated by the user. @@ -170,16 +199,6 @@ Indicates that the some content of the file was a hit for a YARA rule match. ## TSK_METADATA_EXIF EXIF metadata found in an image or audio file. -### REQUIRED ATTRIBUTES -- At least one of: -- TSK_DATETIME_CREATED (Creation date of the file, in seconds since 1970-01-01T00:00:00Z) -- TSK_DEVICE_MAKE (Device make, generally the manufacturer, e.g., Apple) -- TSK_DEVICE_MODEL (Device model, generally the product, e.g., iPhone) -- TSK_GEO_ALTITUDE (The camera's altitude when the image/audio was taken) -- TSK_GEO_LATITUDE (The camera's latitude when the image/audio was taken) -- TSK_GEO_LONGITUDE (The camera's longitude when the image/audio was taken)## TSK_METADATA_EXIF -EXIF metadata found in an image or audio file. - ### REQUIRED ATTRIBUTES - At least one of: - TSK_DATETIME_CREATED (Creation date of the file, in seconds since 1970-01-01T00:00:00Z) diff --git a/bindings/java/doxygen/blackboard.dox b/bindings/java/doxygen/blackboard.dox index fc51e88b9699fcfbdcc4d6603340602e1f134876..b6e2bfd96d17aa7f00fd67cce9291011fe467905 100644 --- a/bindings/java/doxygen/blackboard.dox +++ b/bindings/java/doxygen/blackboard.dox @@ -6,7 +6,7 @@ The blackboard allows modules (in Autopsy or other frameworks) to communicate an \subsection jni_bb_concepts Concepts -The blackboard is a collection of <em>artifacts</em>. Each artifact has a type, such as web browser history, EXIF, or GPS route. The Sleuth Kit has many artifact types already defined (see org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE and the \ref artifact_catalog_page "artifact catalog") and you can also \ref jni_bb_artifact2 "create your own". +The blackboard is a collection of <em>artifacts</em>. Each artifact is a either a data artifact or an analysis result. In general, data artifacts record data found in the image (ex: a call log entry) while analysis results are more subjective (ex: a file matching a user-created interesting file set rule). Each artifact has a type, such as web browser history, EXIF, or GPS route. The Sleuth Kit has many artifact types already defined (see org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE and the \ref artifact_catalog_page "artifact catalog") and you can also \ref jni_bb_artifact2 "create your own". Each artifact has a set of name-value pairs called <em>attributes</em>. Attributes also have types, such as URL, created date, or device make. The Sleuth Kit has many attribute types already defined (see org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE) and you can also \ref jni_bb_artifact2 "create your own". @@ -31,7 +31,7 @@ First you need to decide what type of artifact you are making and what category <li>Analysis Result: Result from an analysis technique on a given object with a given configuration. Includes Conclusion, Relevance Score, and Confidence. <li>Data Artifact: Data that was originally embedded by an application/OS in a file or other data container. </ul> -Consult the \ref artifact_catalog_page "artifact catalog" for a list of built-in types and what categories they belong to. If you are creating a data artifact, you can optionally add an OS account to it. If you are creating an analysis result, you can optionally add a score and other notes about the result. +Consult the \ref artifact_catalog_page "artifact catalog" for a list of built-in types and what categories they belong to. If you are creating a data artifact, you can optionally add an OS account to it. If you are creating an analysis result, you can optionally add a score and other notes about the result. Note that you must use the category defined in the artifact catalog for each type or you will get an error. For example, you can't create a web bookmark analysis result. There are may ways to create artifacts, but we will focus on creating them through the Blackboard class or directly through a Content object. Regardless of how they are created, all artifacts must be associated with a Content object. @@ -109,7 +109,11 @@ We achieve this relationship by creating a TSK_ASSOCIATED_OBJECT artifact on the \subsection jni_bb_query Querying the Blackboard +You can find artifacts by querying the blackboard in a variety of ways. It is preferable to use the methods that specifically return either data artifacts or analysis results since these will contain the complete information for the artifact. You can use the more general "Artifact" or "BlackboardArtifact" methods to get both, however these results will only contain the blackboard attributes and not any associated OS account or score/justification. + You can find artifacts using a variety of ways: +- org.sleuthkit.datamodel.Content.getAllDataArtifacts() to get all data artifacts for a specific Content object. +- org.sleuthkit.datamodel.Content.getAnalysisResults() to get analysis results of a given type for a specific Content object. - org.sleuthkit.datamodel.Content.getArtifacts() in its various forms to get a specific type of artifact for a specific Content object. - org.sleuthkit.datamodel.Content.getGenInfoArtifact() to get the TSK_GEN_INFO artifact for a specific content object. - org.sleuthkit.datamodel.SleuthkitCase.getBlackboardArtifacts() in its various forms to get artifacts based on some combination of artifact type, attribute type and value, and content object. @@ -123,7 +127,7 @@ in the Autopsy UI alongside the built in artifacts and will also appear in the r \subsection jni_bb_custom_make Making Custom Artifacts and Attributes -org.sleuthkit.datamodel.SleuthkitCase.addBlackboardArtifactType() is used to create a custom artifact. Give it the display and unique name and it will return a org.sleuthkit.datamodel.BlackboardArtifact.Type object with a unique ID. You will need to call this once for each case to create the artifact ID. You can then use this ID to make an artifact of the given type. To check if the artifact type has already been added to the blackboard or to get the ID after it was created, use org.sleuthkit.datamodel.SleuthkitCase.getArtifactType(). +org.sleuthkit.datamodel.SleuthkitCase.addBlackboardArtifactType() is used to create a custom artifact. Give it the display name, unique name and category (data artifact or analysis result) and it will return a org.sleuthkit.datamodel.BlackboardArtifact.Type object with a unique ID. You will need to call this once for each case to create the artifact ID. You can then use this ID to make an artifact of the given type. To check if the artifact type has already been added to the blackboard or to get the ID after it was created, use org.sleuthkit.datamodel.SleuthkitCase.getArtifactType(). To create custom attributes, use org.sleuthkit.datamodel.SleuthkitCase.addArtifactAttributeType() to create the artifact type and get its ID. Like artifacts, you must create the attribute type for each new case. To get a type after it has been created in the case, use org.sleuthkit.datamodel.SleuthkitCase.getAttributeType(). Your attribute will be a name-value pair where the value is of the type you specified when creating it. The current types are: String, Integer, Long, Double, Byte, Datetime, and JSON. If you believe you need to create an attribute with type JSON, please read the \ref jni_bb_json_attr_overview "overview" and \ref jni_bb_json_attr "tutorial" sections below. diff --git a/bindings/java/jni/auto_db_java.cpp b/bindings/java/jni/auto_db_java.cpp index 20e915d81c21412a12f83536ad618421b4cfcc1a..033f28e07fa6b1f42f49a3ba2735ed53d5f631b6 100644 --- a/bindings/java/jni/auto_db_java.cpp +++ b/bindings/java/jni/auto_db_java.cpp @@ -1228,21 +1228,15 @@ TskAutoDbJava::addUnallocatedPoolBlocksToDb(size_t & numPool) { /* Create the unallocated space files */ TSK_FS_ATTR_RUN * unalloc_runs = tsk_pool_unallocated_runs(pool_info); TSK_FS_ATTR_RUN * current_run = unalloc_runs; - vector<TSK_DB_FILE_LAYOUT_RANGE> ranges; while (current_run != NULL) { - TSK_DB_FILE_LAYOUT_RANGE tempRange(current_run->addr * pool_info->block_size, current_run->len * pool_info->block_size, 0); - - ranges.push_back(tempRange); - int64_t fileObjId = 0; - if (TSK_ERR == addUnallocBlockFile(unallocVolObjId, 0, current_run->len * pool_info->block_size, ranges, fileObjId, m_curImgId)) { + if (addUnallocBlockFileInChunks(current_run->addr * pool_info->block_size, current_run->len * pool_info->block_size, unallocVolObjId, m_curImgId) == TSK_ERR) { registerError(); tsk_fs_attr_run_free(unalloc_runs); return TSK_ERR; } current_run = current_run->next; - ranges.clear(); } tsk_fs_attr_run_free(unalloc_runs); } @@ -1918,14 +1912,10 @@ TSK_RETVAL_ENUM TskAutoDbJava::addUnallocVsSpaceToDb(size_t & numVsP) { return TSK_ERR; } - // Create an unalloc file with unalloc part, with vs part as parent - vector<TSK_DB_FILE_LAYOUT_RANGE> ranges; + // Create an unalloc file (or files) with unalloc part, with vs part as parent const uint64_t byteStart = vsInfo->offset + vsInfo->block_size * vsPart.start; - const uint64_t byteLen = vsInfo->block_size * vsPart.len; - TSK_DB_FILE_LAYOUT_RANGE tempRange(byteStart, byteLen, 0); - ranges.push_back(tempRange); - int64_t fileObjId = 0; - if (addUnallocBlockFile(vsPart.objId, 0, tempRange.byteLen, ranges, fileObjId, m_curImgId) == TSK_ERR) { + const uint64_t byteLen = vsInfo->block_size * vsPart.len; + if (addUnallocBlockFileInChunks(byteStart, byteLen, vsPart.objId, m_curImgId) == TSK_ERR) { registerError(); return TSK_ERR; } @@ -1954,13 +1944,60 @@ TSK_RETVAL_ENUM TskAutoDbJava::addUnallocImageSpaceToDb() { vector<TSK_DB_FILE_LAYOUT_RANGE> ranges; ranges.push_back(tempRange); int64_t fileObjId = 0; - if (TSK_ERR == addUnallocBlockFile(m_curImgId, 0, imgSize, ranges, fileObjId, m_curImgId)) { + if (TSK_ERR == addUnallocBlockFileInChunks(0, imgSize, m_curImgId, m_curImgId)) { return TSK_ERR; } } return TSK_OK; } +/** +* Adds unallocated block files to the database, chunking if enabled. +* +* @returns TSK_OK on success, TSK_ERR on error +*/ +TSK_RETVAL_ENUM TskAutoDbJava::addUnallocBlockFileInChunks(uint64_t byteStart, TSK_OFF_T totalSize, int64_t parentObjId, int64_t dataSourceObjId) { + + if (m_maxChunkSize <= 0) { + // No chunking - write the entire file + TSK_DB_FILE_LAYOUT_RANGE tempRange(byteStart, totalSize, 0); + vector<TSK_DB_FILE_LAYOUT_RANGE> ranges; + ranges.push_back(tempRange); + int64_t fileObjId = 0; + return addUnallocBlockFile(parentObjId, 0, totalSize, ranges, fileObjId, dataSourceObjId); + } + + // We will chunk into separate files with max size m_maxChunkSize + uint64_t maxChunkSize = (uint64_t)m_maxChunkSize; + uint64_t bytesLeft = (uint64_t)totalSize; + uint64_t startingOffset = byteStart; + uint64_t chunkSize; + vector<TSK_DB_FILE_LAYOUT_RANGE> ranges; + while (bytesLeft > 0) { + + if (maxChunkSize >= bytesLeft) { + chunkSize = bytesLeft; + bytesLeft = 0; + } + else { + chunkSize = maxChunkSize; + bytesLeft -= maxChunkSize; + } + + TSK_DB_FILE_LAYOUT_RANGE tempRange(startingOffset, chunkSize, 0); + ranges.push_back(tempRange); + int64_t fileObjId = 0; + + TSK_RETVAL_ENUM retval = addUnallocBlockFile(parentObjId, 0, chunkSize, ranges, fileObjId, dataSourceObjId); + if (retval != TSK_OK) { + return retval; + } + ranges.clear(); + startingOffset += chunkSize; + } + return TSK_OK; +} + /** * Returns the directory currently being analyzed by processFile(). * Safe to use from another thread than processFile(). diff --git a/bindings/java/jni/auto_db_java.h b/bindings/java/jni/auto_db_java.h index b324a71c43445a88099b5901d061ce7c5e9a5812..6980a5dcfef1956c146a7259fd125f63af60e4be 100644 --- a/bindings/java/jni/auto_db_java.h +++ b/bindings/java/jni/auto_db_java.h @@ -205,6 +205,7 @@ class TskAutoDbJava :public TskAuto { TSK_RETVAL_ENUM addUnallocVsSpaceToDb(size_t & numVsP); TSK_RETVAL_ENUM addUnallocImageSpaceToDb(); TSK_RETVAL_ENUM addUnallocSpaceToDb(); + TSK_RETVAL_ENUM addUnallocBlockFileInChunks(uint64_t byteStart, TSK_OFF_T totalSize, int64_t parentObjId, int64_t dataSourceObjId); // JNI methods TSK_RETVAL_ENUM addImageInfo(int type, TSK_OFF_T ssize, int64_t & objId, const string & timezone, TSK_OFF_T size, const string &md5, diff --git a/bindings/java/src/org/sleuthkit/datamodel/AbstractContent.java b/bindings/java/src/org/sleuthkit/datamodel/AbstractContent.java index 9dd3dc02e2a467a50e3152c4b3be38b517faa2e1..27778032d5de2d7873bf14c5c1197e70d912dc9d 100644 --- a/bindings/java/src/org/sleuthkit/datamodel/AbstractContent.java +++ b/bindings/java/src/org/sleuthkit/datamodel/AbstractContent.java @@ -327,7 +327,7 @@ public BlackboardArtifact newArtifact(int artifactTypeID) throws TskCoreExceptio if (artifactTypeID == ARTIFACT_TYPE.TSK_GEN_INFO.getTypeID()) { return getGenInfoArtifact(true); } - BlackboardArtifact.Type artifactType = db.getArtifactType(artifactTypeID); + BlackboardArtifact.Type artifactType = db.getBlackboard().getArtifactType(artifactTypeID); switch (artifactType.getCategory()) { case DATA_ARTIFACT: return this.newDataArtifact(artifactType, Collections.emptyList()); @@ -411,7 +411,7 @@ public BlackboardArtifact newArtifact(BlackboardArtifact.ARTIFACT_TYPE type) thr @Override public ArrayList<BlackboardArtifact> getArtifacts(String artifactTypeName) throws TskCoreException { - return getArtifacts(db.getArtifactType(artifactTypeName).getTypeID()); + return getArtifacts(db.getBlackboard().getArtifactType(artifactTypeName).getTypeID()); } @Override diff --git a/bindings/java/src/org/sleuthkit/datamodel/AbstractFile.java b/bindings/java/src/org/sleuthkit/datamodel/AbstractFile.java index 3ff266e1ebb459efd2a2d523749e46d89e925a30..2909c6e10a6dd2015ab8413c79f329d17a6a0116 100644 --- a/bindings/java/src/org/sleuthkit/datamodel/AbstractFile.java +++ b/bindings/java/src/org/sleuthkit/datamodel/AbstractFile.java @@ -537,7 +537,7 @@ public String getSha256Hash() { public List<Attribute> getAttributes() throws TskCoreException { synchronized (this) { if (!loadedAttributesCacheFromDb) { - ArrayList<Attribute> attributes = getSleuthkitCase().getFileAttributes(this); + ArrayList<Attribute> attributes = getSleuthkitCase().getBlackboard().getFileAttributes(this); fileAttributesCache.clear(); fileAttributesCache.addAll(attributes); loadedAttributesCacheFromDb = true; diff --git a/bindings/java/src/org/sleuthkit/datamodel/Blackboard.java b/bindings/java/src/org/sleuthkit/datamodel/Blackboard.java index 3f029997c6c843ccc1453b86f5294f48c00fd5cf..482be9e929e3a58d72f4fea2d624244df44f8aaf 100755 --- a/bindings/java/src/org/sleuthkit/datamodel/Blackboard.java +++ b/bindings/java/src/org/sleuthkit/datamodel/Blackboard.java @@ -29,13 +29,18 @@ import java.util.Collections; import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.Objects; import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; import java.util.logging.Level; import java.util.logging.Logger; import java.util.stream.Collectors; import org.sleuthkit.datamodel.SleuthkitCase.CaseDbConnection; import org.sleuthkit.datamodel.SleuthkitCase.CaseDbTransaction; +import static org.sleuthkit.datamodel.SleuthkitCase.closeConnection; +import static org.sleuthkit.datamodel.SleuthkitCase.closeResultSet; +import static org.sleuthkit.datamodel.SleuthkitCase.closeStatement; /** * A representation of the blackboard, a place where artifacts and their @@ -45,6 +50,20 @@ public final class Blackboard { private static final Logger LOGGER = Logger.getLogger(Blackboard.class.getName()); + /* + * ConcurrentHashMap semantics are fine for these caches to which entries + * are added, but never removed. There is also no need to keep each pair of + * related caches strictly consistent with each other, because cache misses + * will be extremely rare (standard types are loaded when the case is + * opened), and the cost of a cache miss is low. + */ + private final Map<Integer, BlackboardArtifact.Type> typeIdToArtifactTypeMap = new ConcurrentHashMap<>(); + private final Map<Integer, BlackboardAttribute.Type> typeIdToAttributeTypeMap = new ConcurrentHashMap<>(); + private final Map<String, BlackboardArtifact.Type> typeNameToArtifactTypeMap = new ConcurrentHashMap<>(); + private final Map<String, BlackboardAttribute.Type> typeNameToAttributeTypeMap = new ConcurrentHashMap<>(); + + static final int MIN_USER_DEFINED_TYPE_ID = 10000; + private final SleuthkitCase caseDb; /** @@ -58,40 +77,76 @@ public final class Blackboard { } /** - * Posts the artifact. The artifact should be complete (all attributes have - * been added) before being posted. Posting the artifact includes making any - * timeline events that may be derived from it, and broadcasting a - * notification that the artifact is ready for further analysis. + * Posts an artifact to the blackboard. The artifact should be complete (all + * attributes have been added) before it is posted. Posting the artifact + * triggers the creation of appropriate timeline events, if any, and + * broadcast of a notification that the artifact is ready for further + * analysis. * - * @param artifact The artifact to be posted. - * @param moduleName The name of the module that is posting the artifacts. + * @param artifact The artifact. + * @param moduleName The display name of the module posting the artifact. * - * @throws BlackboardException If there is a problem posting the artifact. + * @throws BlackboardException The exception is thrown if there is an issue + * posting the artifact. */ + // RJCTODO: Deprecate public void postArtifact(BlackboardArtifact artifact, String moduleName) throws BlackboardException { - postArtifacts(Collections.singleton(artifact), moduleName); + postArtifacts(Collections.singleton(artifact), moduleName, null); } /** - * Posts a Collection of artifacts. The artifacts should be complete (all - * attributes have been added) before being posted. Posting the artifacts - * includes making any events that may be derived from them, and - * broadcasting notifications that the artifacts are ready for further - * analysis. - * + * Posts a collection of artifacts to the blackboard. The artifacts should + * be complete (all attributes have been added) before they are posted. + * Posting the artifacts triggers the creation of appropriate timeline + * events, if any, and broadcast of a notification that the artifacts are + * ready for further analysis. * - * @param artifacts The artifacts to be posted . - * @param moduleName The name of the module that is posting the artifacts. + * @param artifacts The artifacts. + * @param moduleName The display name of the module posting the artifacts. * + * @throws BlackboardException The exception is thrown if there is an issue + * posting the artifact. + */ + // RJCTODO: Deprecate + public void postArtifacts(Collection<BlackboardArtifact> artifacts, String moduleName) throws BlackboardException { + postArtifacts(artifacts, moduleName, null); + } + + /** + * Posts an artifact to the blackboard. The artifact should be complete (all + * attributes have been added) before it is posted. Posting the artifact + * triggers the creation of appropriate timeline events, if any, and + * broadcast of a notification that the artifact is ready for further + * analysis. * - * @throws BlackboardException If there is a problem posting the artifacts. + * @param artifact The artifact. + * @param moduleName The display name of the module posting the artifact. + * @param ingestJobId The numeric identifier of the ingest job within which + * the artifact was posted. * + * @throws BlackboardException The exception is thrown if there is an issue + * posting the artifact. */ - public void postArtifacts(Collection<BlackboardArtifact> artifacts, String moduleName) throws BlackboardException { - /* - * For now this just processes them one by one, but in the future it - * could be smarter and use transactions, etc. - */ + public void postArtifact(BlackboardArtifact artifact, String moduleName, Long ingestJobId) throws BlackboardException { + postArtifacts(Collections.singleton(artifact), moduleName, ingestJobId); + } + + /** + * Posts a collection of artifacts to the blackboard. The artifacts should + * be complete (all attributes have been added) before they are posted. + * Posting the artifacts triggers the creation of appropriate timeline + * events, if any, and broadcast of a notification that the artifacts are + * ready for further analysis. + * + * @param artifacts The artifacts. + * @param moduleName The display name of the module posting the artifacts. + * @param ingestJobId The numeric identifier of the ingest job within which + * the artifacts were posted. + * + * @throws BlackboardException The exception is thrown if there is an issue + * posting the artifact. + */ + public void postArtifacts(Collection<BlackboardArtifact> artifacts, String moduleName, Long ingestJobId) throws BlackboardException { for (BlackboardArtifact artifact : artifacts) { try { caseDb.getTimelineManager().addArtifactEvents(artifact); @@ -99,8 +154,7 @@ public void postArtifacts(Collection<BlackboardArtifact> artifacts, String modul throw new BlackboardException("Failed to add events for artifact: " + artifact, ex); } } - - caseDb.fireTSKEvent(new ArtifactsPostedEvent(artifacts, moduleName)); + caseDb.fireTSKEvent(new ArtifactsPostedEvent(artifacts, moduleName, ingestJobId)); } /** @@ -118,7 +172,6 @@ public void postArtifacts(Collection<BlackboardArtifact> artifacts, String modul * artifact type. */ public BlackboardArtifact.Type getOrAddArtifactType(String typeName, String displayName) throws BlackboardException { - return getOrAddArtifactType(typeName, displayName, BlackboardArtifact.Category.DATA_ARTIFACT); } @@ -140,16 +193,481 @@ public BlackboardArtifact.Type getOrAddArtifactType(String typeName, String disp throw new BlackboardException("Category provided must be non-null"); } + if (typeNameToArtifactTypeMap.containsKey(typeName)) { + return typeNameToArtifactTypeMap.get(typeName); + } + + Statement s = null; + ResultSet rs = null; + CaseDbTransaction trans = null; try { - return caseDb.addBlackboardArtifactType(typeName, displayName, category); - } catch (TskDataException typeExistsEx) { + trans = caseDb.beginTransaction(); + + CaseDbConnection connection = trans.getConnection(); + s = connection.createStatement(); + rs = connection.executeQuery(s, "SELECT artifact_type_id FROM blackboard_artifact_types WHERE type_name = '" + typeName + "'"); //NON-NLS + if (!rs.next()) { + rs.close(); + rs = connection.executeQuery(s, "SELECT MAX(artifact_type_id) AS highest_id FROM blackboard_artifact_types"); + int maxID = 0; + if (rs.next()) { + maxID = rs.getInt("highest_id"); + if (maxID < MIN_USER_DEFINED_TYPE_ID) { + maxID = MIN_USER_DEFINED_TYPE_ID; + } else { + maxID++; + } + } + connection.executeUpdate(s, "INSERT INTO blackboard_artifact_types (artifact_type_id, type_name, display_name, category_type) VALUES ('" + maxID + "', '" + typeName + "', '" + displayName + "', " + category.getID() + " )"); //NON-NLS + BlackboardArtifact.Type type = new BlackboardArtifact.Type(maxID, typeName, displayName, category); + this.typeIdToArtifactTypeMap.put(type.getTypeID(), type); + this.typeNameToArtifactTypeMap.put(type.getTypeName(), type); + trans.commit(); + trans = null; + return type; + } else { + trans.commit(); + trans = null; + try { + return getArtifactType(typeName); + } catch (TskCoreException ex) { + throw new BlackboardException("Failed to get or add artifact type: " + typeName, ex); + } + } + } catch (SQLException | TskCoreException ex) { try { - return caseDb.getArtifactType(typeName); - } catch (TskCoreException ex) { - throw new BlackboardException("Failed to get or add artifact type", ex); + if (trans != null) { + trans.rollback(); + trans = null; + } + } catch (TskCoreException ex2) { + LOGGER.log(Level.SEVERE, "Error rolling back transaction", ex2); } - } catch (TskCoreException ex) { - throw new BlackboardException("Failed to get or add artifact type", ex); + throw new BlackboardException("Error adding artifact type: " + typeName, ex); + } finally { + closeResultSet(rs); + closeStatement(s); + if (trans != null) { + try { + trans.rollback(); + } catch (TskCoreException ex) { + throw new BlackboardException("Error rolling back transaction", ex); + } + } + } + } + + /** + * Get the attribute type associated with an attribute type name. + * + * @param attrTypeName An attribute type name. + * + * @return An attribute type or null if the attribute type does not exist. + * + * @throws TskCoreException If an error occurs accessing the case database. + * + */ + public BlackboardAttribute.Type getAttributeType(String attrTypeName) throws TskCoreException { + if (this.typeNameToAttributeTypeMap.containsKey(attrTypeName)) { + return this.typeNameToAttributeTypeMap.get(attrTypeName); + } + CaseDbConnection connection = null; + Statement s = null; + ResultSet rs = null; + caseDb.acquireSingleUserCaseReadLock(); + try { + connection = caseDb.getConnection(); + s = connection.createStatement(); + rs = connection.executeQuery(s, "SELECT attribute_type_id, type_name, display_name, value_type FROM blackboard_attribute_types WHERE type_name = '" + attrTypeName + "'"); //NON-NLS + BlackboardAttribute.Type type = null; + if (rs.next()) { + type = new BlackboardAttribute.Type(rs.getInt("attribute_type_id"), rs.getString("type_name"), + rs.getString("display_name"), BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.fromType(rs.getLong("value_type"))); + this.typeIdToAttributeTypeMap.put(type.getTypeID(), type); + this.typeNameToAttributeTypeMap.put(attrTypeName, type); + } + return type; + } catch (SQLException ex) { + throw new TskCoreException("Error getting attribute type id", ex); + } finally { + closeResultSet(rs); + closeStatement(s); + closeConnection(connection); + caseDb.releaseSingleUserCaseReadLock(); + } + } + + /** + * Get the attribute type associated with an attribute type ID. + * + * @param typeID An attribute type ID. + * + * @return An attribute type or null if the attribute type does not exist. + * + * @throws TskCoreException If an error occurs accessing the case database. + * + */ + BlackboardAttribute.Type getAttributeType(int typeID) throws TskCoreException { + if (this.typeIdToAttributeTypeMap.containsKey(typeID)) { + return this.typeIdToAttributeTypeMap.get(typeID); + } + CaseDbConnection connection = null; + Statement s = null; + ResultSet rs = null; + caseDb.acquireSingleUserCaseReadLock(); + try { + connection = caseDb.getConnection(); + s = connection.createStatement(); + rs = connection.executeQuery(s, "SELECT attribute_type_id, type_name, display_name, value_type FROM blackboard_attribute_types WHERE attribute_type_id = " + typeID + ""); //NON-NLS + BlackboardAttribute.Type type = null; + if (rs.next()) { + type = new BlackboardAttribute.Type(rs.getInt("attribute_type_id"), rs.getString("type_name"), + rs.getString("display_name"), BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.fromType(rs.getLong("value_type"))); + this.typeIdToAttributeTypeMap.put(typeID, type); + this.typeNameToAttributeTypeMap.put(type.getTypeName(), type); + } + return type; + } catch (SQLException ex) { + throw new TskCoreException("Error getting attribute type id", ex); + } finally { + closeResultSet(rs); + closeStatement(s); + closeConnection(connection); + caseDb.releaseSingleUserCaseReadLock(); + } + } + + /** + * Get the artifact type associated with an artifact type name. + * + * @param artTypeName An artifact type name. + * + * @return An artifact type or null if the artifact type does not exist. + * + * @throws TskCoreException If an error occurs accessing the case database. + * + */ + public BlackboardArtifact.Type getArtifactType(String artTypeName) throws TskCoreException { + if (this.typeNameToArtifactTypeMap.containsKey(artTypeName)) { + return this.typeNameToArtifactTypeMap.get(artTypeName); + } + CaseDbConnection connection = null; + Statement s = null; + ResultSet rs = null; + caseDb.acquireSingleUserCaseReadLock(); + try { + connection = caseDb.getConnection(); + s = connection.createStatement(); + rs = connection.executeQuery(s, "SELECT artifact_type_id, type_name, display_name, category_type FROM blackboard_artifact_types WHERE type_name = '" + artTypeName + "'"); //NON-NLS + BlackboardArtifact.Type type = null; + if (rs.next()) { + type = new BlackboardArtifact.Type(rs.getInt("artifact_type_id"), + rs.getString("type_name"), rs.getString("display_name"), + BlackboardArtifact.Category.fromID(rs.getInt("category_type"))); + this.typeIdToArtifactTypeMap.put(type.getTypeID(), type); + this.typeNameToArtifactTypeMap.put(artTypeName, type); + } + return type; + } catch (SQLException ex) { + throw new TskCoreException("Error getting artifact type from the database", ex); + } finally { + closeResultSet(rs); + closeStatement(s); + closeConnection(connection); + caseDb.releaseSingleUserCaseReadLock(); + } + } + + /** + * Get the artifact type associated with an artifact type id. + * + * @param artTypeId An artifact type id. + * + * @return The artifact type. + * + * @throws TskCoreException If an error occurs accessing the case database + * or no value is found. + * + */ + public BlackboardArtifact.Type getArtifactType(int artTypeId) throws TskCoreException { + if (this.typeIdToArtifactTypeMap.containsKey(artTypeId)) { + return typeIdToArtifactTypeMap.get(artTypeId); + } + CaseDbConnection connection = null; + Statement s = null; + ResultSet rs = null; + caseDb.acquireSingleUserCaseReadLock(); + try { + connection = caseDb.getConnection(); + s = connection.createStatement(); + rs = connection.executeQuery(s, "SELECT artifact_type_id, type_name, display_name, category_type FROM blackboard_artifact_types WHERE artifact_type_id = " + artTypeId + ""); //NON-NLS + BlackboardArtifact.Type type = null; + if (rs.next()) { + type = new BlackboardArtifact.Type(rs.getInt("artifact_type_id"), + rs.getString("type_name"), rs.getString("display_name"), + BlackboardArtifact.Category.fromID(rs.getInt("category_type"))); + this.typeIdToArtifactTypeMap.put(artTypeId, type); + this.typeNameToArtifactTypeMap.put(type.getTypeName(), type); + return type; + } else { + throw new TskCoreException("No artifact type found matching id: " + artTypeId); + } + } catch (SQLException ex) { + throw new TskCoreException("Error getting artifact type from the database", ex); + } finally { + closeResultSet(rs); + closeStatement(s); + closeConnection(connection); + caseDb.releaseSingleUserCaseReadLock(); + } + } + + /** + * Get the list of attributes for the given artifact. + * + * @param artifact The artifact to load attributes for. + * + * @return The list of attributes. + * + * @throws TskCoreException + */ + public ArrayList<BlackboardAttribute> getBlackboardAttributes(final BlackboardArtifact artifact) throws TskCoreException { + CaseDbConnection connection = null; + Statement statement = null; + ResultSet rs = null; + caseDb.acquireSingleUserCaseReadLock(); + try { + connection = caseDb.getConnection(); + statement = connection.createStatement(); + rs = connection.executeQuery(statement, "SELECT attrs.artifact_id AS artifact_id, " + + "attrs.source AS source, attrs.context AS context, attrs.attribute_type_id AS attribute_type_id, " + + "attrs.value_type AS value_type, attrs.value_byte AS value_byte, " + + "attrs.value_text AS value_text, attrs.value_int32 AS value_int32, " + + "attrs.value_int64 AS value_int64, attrs.value_double AS value_double, " + + "types.type_name AS type_name, types.display_name AS display_name " + + "FROM blackboard_attributes AS attrs, blackboard_attribute_types AS types WHERE attrs.artifact_id = " + artifact.getArtifactID() + + " AND attrs.attribute_type_id = types.attribute_type_id"); + ArrayList<BlackboardAttribute> attributes = new ArrayList<>(); + while (rs.next()) { + int attributeTypeId = rs.getInt("attribute_type_id"); + String attributeTypeName = rs.getString("type_name"); + BlackboardAttribute.Type attributeType; + if (this.typeIdToAttributeTypeMap.containsKey(attributeTypeId)) { + attributeType = this.typeIdToAttributeTypeMap.get(attributeTypeId); + } else { + attributeType = new BlackboardAttribute.Type(attributeTypeId, attributeTypeName, + rs.getString("display_name"), + BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.fromType(rs.getInt("value_type"))); + this.typeIdToAttributeTypeMap.put(attributeTypeId, attributeType); + this.typeNameToAttributeTypeMap.put(attributeTypeName, attributeType); + } + + final BlackboardAttribute attr = new BlackboardAttribute( + rs.getLong("artifact_id"), + attributeType, + rs.getString("source"), + rs.getString("context"), + rs.getInt("value_int32"), + rs.getLong("value_int64"), + rs.getDouble("value_double"), + rs.getString("value_text"), + rs.getBytes("value_byte"), caseDb + ); + attr.setParentDataSourceID(artifact.getDataSourceObjectID()); + attributes.add(attr); + } + return attributes; + } catch (SQLException ex) { + throw new TskCoreException("Error getting attributes for artifact, artifact id = " + artifact.getArtifactID(), ex); + } finally { + closeResultSet(rs); + closeStatement(statement); + closeConnection(connection); + caseDb.releaseSingleUserCaseReadLock(); + } + } + + /** + * Get the attributes associated with the given file. + * + * @param file + * + * @return + * + * @throws TskCoreException + */ + ArrayList<Attribute> getFileAttributes(final AbstractFile file) throws TskCoreException { + CaseDbConnection connection = null; + Statement statement = null; + ResultSet rs = null; + caseDb.acquireSingleUserCaseReadLock(); + try { + connection = caseDb.getConnection(); + statement = connection.createStatement(); + rs = connection.executeQuery(statement, "SELECT attrs.id as id, attrs.obj_id AS obj_id, " + + "attrs.attribute_type_id AS attribute_type_id, " + + "attrs.value_type AS value_type, attrs.value_byte AS value_byte, " + + "attrs.value_text AS value_text, attrs.value_int32 AS value_int32, " + + "attrs.value_int64 AS value_int64, attrs.value_double AS value_double, " + + "types.type_name AS type_name, types.display_name AS display_name " + + "FROM tsk_file_attributes AS attrs " + + " INNER JOIN blackboard_attribute_types AS types " + + " ON attrs.attribute_type_id = types.attribute_type_id " + + " WHERE attrs.obj_id = " + file.getId()); + + ArrayList<Attribute> attributes = new ArrayList<Attribute>(); + while (rs.next()) { + int attributeTypeId = rs.getInt("attribute_type_id"); + String attributeTypeName = rs.getString("type_name"); + BlackboardAttribute.Type attributeType; + if (this.typeIdToAttributeTypeMap.containsKey(attributeTypeId)) { + attributeType = this.typeIdToAttributeTypeMap.get(attributeTypeId); + } else { + attributeType = new BlackboardAttribute.Type(attributeTypeId, attributeTypeName, + rs.getString("display_name"), + BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.fromType(rs.getInt("value_type"))); + this.typeIdToAttributeTypeMap.put(attributeTypeId, attributeType); + this.typeNameToAttributeTypeMap.put(attributeTypeName, attributeType); + } + + final Attribute attr = new Attribute( + rs.getLong("id"), + rs.getLong("obj_id"), + attributeType, + rs.getInt("value_int32"), + rs.getLong("value_int64"), + rs.getDouble("value_double"), + rs.getString("value_text"), + rs.getBytes("value_byte"), caseDb + ); + attributes.add(attr); + } + return attributes; + } catch (SQLException ex) { + throw new TskCoreException("Error getting attributes for file, file id = " + file.getId(), ex); + } finally { + closeResultSet(rs); + closeStatement(statement); + closeConnection(connection); + caseDb.releaseSingleUserCaseReadLock(); + } + } + + /** + * Adds the standard artifact types to the blackboard_artifact_types table + * and the artifact type caches. + * + * @param connection A connection to the case database. + * + * @throws SQLException Thrown if there is an error adding a type to the + * table. + */ + void initBlackboardArtifactTypes(CaseDbConnection connection) throws SQLException { + caseDb.acquireSingleUserCaseWriteLock(); + try (Statement statement = connection.createStatement()) { + /* + * Determine which types, if any, have already been added to the + * case database, and load them into the type caches. For a case + * that is being reopened, this should reduce the number of separate + * INSERT staements that will be executed below. + */ + ResultSet resultSet = connection.executeQuery(statement, "SELECT artifact_type_id, type_name, display_name, category_type FROM blackboard_artifact_types"); //NON-NLS + while (resultSet.next()) { + BlackboardArtifact.Type type = new BlackboardArtifact.Type(resultSet.getInt("artifact_type_id"), + resultSet.getString("type_name"), resultSet.getString("display_name"), + BlackboardArtifact.Category.fromID(resultSet.getInt("category_type"))); + typeIdToArtifactTypeMap.put(type.getTypeID(), type); + typeNameToArtifactTypeMap.put(type.getTypeName(), type); + } + + /* + * INSERT any missing standard types. A conflict clause is used to + * avoid a potential race condition. It also eliminates the need to + * add schema update code when new types are added. + * + * The use here of the soon to be deprecated + * BlackboardArtifact.ARTIFACT_TYPE enum instead of the + * BlackboardArtifact.Type.STANDARD_TYPES collection currently + * ensures that the deprecated types in the former, and not in the + * latter, are added to the case database. + */ + for (BlackboardArtifact.ARTIFACT_TYPE type : BlackboardArtifact.ARTIFACT_TYPE.values()) { + if (typeIdToArtifactTypeMap.containsKey(type.getTypeID())) { + continue; + } + if (caseDb.getDatabaseType() == TskData.DbType.POSTGRESQL) { + statement.execute("INSERT INTO blackboard_artifact_types (artifact_type_id, type_name, display_name, category_type) VALUES (" + type.getTypeID() + " , '" + type.getLabel() + "', '" + type.getDisplayName() + "' , " + type.getCategory().getID() + ") ON CONFLICT DO NOTHING"); //NON-NLS + } else { + statement.execute("INSERT OR IGNORE INTO blackboard_artifact_types (artifact_type_id, type_name, display_name, category_type) VALUES (" + type.getTypeID() + " , '" + type.getLabel() + "', '" + type.getDisplayName() + "' , " + type.getCategory().getID() + ")"); //NON-NLS + } + typeIdToArtifactTypeMap.put(type.getTypeID(), new BlackboardArtifact.Type(type)); + typeNameToArtifactTypeMap.put(type.getLabel(), new BlackboardArtifact.Type(type)); + } + if (caseDb.getDatabaseType() == TskData.DbType.POSTGRESQL) { + int newPrimaryKeyIndex = Collections.max(Arrays.asList(BlackboardArtifact.ARTIFACT_TYPE.values())).getTypeID() + 1; + statement.execute("ALTER SEQUENCE blackboard_artifact_types_artifact_type_id_seq RESTART WITH " + newPrimaryKeyIndex); //NON-NLS + } + } finally { + caseDb.releaseSingleUserCaseWriteLock(); + } + } + + /** + * Adds the standard attribute types to the blackboard_attribute_types table + * and the attribute type caches. + * + * @param connection A connection to the case database. + * + * @throws SQLException Thrown if there is an error adding a type to the + * table. + */ + void initBlackboardAttributeTypes(CaseDbConnection connection) throws SQLException { + caseDb.acquireSingleUserCaseWriteLock(); + try (Statement statement = connection.createStatement()) { + /* + * Determine which types, if any, have already been added to the + * case database, and load them into the type caches. For a case + * that is being reopened, this should reduce the number of separate + * INSERT staements that will be executed below. + */ + ResultSet resultSet = connection.executeQuery(statement, "SELECT attribute_type_id, type_name, display_name, value_type FROM blackboard_attribute_types"); //NON-NLS + while (resultSet.next()) { + BlackboardAttribute.Type type = new BlackboardAttribute.Type(resultSet.getInt("attribute_type_id"), + resultSet.getString("type_name"), resultSet.getString("display_name"), + BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.fromType(resultSet.getLong("value_type"))); + typeIdToAttributeTypeMap.put(type.getTypeID(), type); + typeNameToAttributeTypeMap.put(type.getTypeName(), type); + } + + /* + * INSERT any missing standard types. A conflict clause is used to + * avoid a potential race condition. It also eliminates the need to + * add schema update code when new types are added. + * + * The use here of the soon to be deprecated + * BlackboardAttribute.ATTRIBUTE_TYPE enum instead of the + * BlackboardAttribute.Type.STANDARD_TYPES collection currently + * ensures that the deprecated types in the former, and not in the + * latter, are added to the case database. + */ + for (BlackboardAttribute.ATTRIBUTE_TYPE type : BlackboardAttribute.ATTRIBUTE_TYPE.values()) { + if (typeIdToAttributeTypeMap.containsKey(type.getTypeID())) { + continue; + } + if (caseDb.getDatabaseType() == TskData.DbType.POSTGRESQL) { + statement.execute("INSERT INTO blackboard_attribute_types (attribute_type_id, type_name, display_name, value_type) VALUES (" + type.getTypeID() + ", '" + type.getLabel() + "', '" + type.getDisplayName() + "', '" + type.getValueType().getType() + "') ON CONFLICT DO NOTHING"); //NON-NLS + } else { + statement.execute("INSERT OR IGNORE INTO blackboard_attribute_types (attribute_type_id, type_name, display_name, value_type) VALUES (" + type.getTypeID() + ", '" + type.getLabel() + "', '" + type.getDisplayName() + "', '" + type.getValueType().getType() + "')"); //NON-NLS + } + typeIdToAttributeTypeMap.put(type.getTypeID(), new BlackboardAttribute.Type(type)); + typeNameToAttributeTypeMap.put(type.getLabel(), new BlackboardAttribute.Type(type)); + } + if (caseDb.getDatabaseType() == TskData.DbType.POSTGRESQL) { + int newPrimaryKeyIndex = Collections.max(Arrays.asList(BlackboardAttribute.ATTRIBUTE_TYPE.values())).getTypeID() + 1; + statement.execute("ALTER SEQUENCE blackboard_attribute_types_attribute_type_id_seq RESTART WITH " + newPrimaryKeyIndex); //NON-NLS + } + } finally { + caseDb.releaseSingleUserCaseWriteLock(); } } @@ -296,7 +814,7 @@ public Score deleteAnalysisResult(AnalysisResult analysisResult) throws TskCoreE */ public Score deleteAnalysisResult(long artifactObjId, CaseDbTransaction transaction) throws TskCoreException { - List<AnalysisResult> analysisResults = getAnalysisResultsWhere(" arts.artifact_obj_id = " + artifactObjId, transaction.getConnection()); + List<AnalysisResult> analysisResults = getAnalysisResultsWhere(" artifacts.artifact_obj_id = " + artifactObjId, transaction.getConnection()); if (analysisResults.isEmpty()) { throw new TskCoreException(String.format("Analysis Result not found for artifact obj id %d", artifactObjId)); @@ -342,18 +860,18 @@ private Score deleteAnalysisResult(AnalysisResult analysisResult, CaseDbTransact } } - private final static String ANALYSIS_RESULT_QUERY_STRING = "SELECT DISTINCT arts.artifact_id AS artifact_id, " //NON-NLS - + " arts.obj_id AS obj_id, arts.artifact_obj_id AS artifact_obj_id, arts.data_source_obj_id AS data_source_obj_id, arts.artifact_type_id AS artifact_type_id, " + private final static String ANALYSIS_RESULT_QUERY_STRING = "SELECT DISTINCT artifacts.artifact_id AS artifact_id, " //NON-NLS + + " artifacts.obj_id AS obj_id, artifacts.artifact_obj_id AS artifact_obj_id, artifacts.data_source_obj_id AS data_source_obj_id, artifacts.artifact_type_id AS artifact_type_id, " + " types.type_name AS type_name, types.display_name AS display_name, types.category_type as category_type,"//NON-NLS - + " arts.review_status_id AS review_status_id, " //NON-NLS + + " artifacts.review_status_id AS review_status_id, " //NON-NLS + " results.conclusion AS conclusion, results.significance AS significance, results.priority AS priority, " + " results.configuration AS configuration, results.justification AS justification " - + " FROM blackboard_artifacts AS arts " + + " FROM blackboard_artifacts AS artifacts " + " JOIN blackboard_artifact_types AS types " //NON-NLS - + " ON arts.artifact_type_id = types.artifact_type_id" //NON-NLS + + " ON artifacts.artifact_type_id = types.artifact_type_id" //NON-NLS + " LEFT JOIN tsk_analysis_results AS results " - + " ON arts.artifact_obj_id = results.artifact_obj_id " //NON-NLS - + " WHERE arts.review_status_id != " + BlackboardArtifact.ReviewStatus.REJECTED.getID() //NON-NLS + + " ON artifacts.artifact_obj_id = results.artifact_obj_id " //NON-NLS + + " WHERE artifacts.review_status_id != " + BlackboardArtifact.ReviewStatus.REJECTED.getID() //NON-NLS + " AND types.category_type = " + BlackboardArtifact.Category.ANALYSIS_RESULT.getID(); // NON-NLS /** @@ -367,7 +885,7 @@ private Score deleteAnalysisResult(AnalysisResult analysisResult, CaseDbTransact * within TSK core. */ public List<AnalysisResult> getAnalysisResultsByType(int artifactTypeId) throws TskCoreException { - return getAnalysisResultsWhere(" arts.artifact_type_id = " + artifactTypeId); + return getAnalysisResultsWhere(" artifacts.artifact_type_id = " + artifactTypeId); } /** @@ -382,7 +900,7 @@ public List<AnalysisResult> getAnalysisResultsByType(int artifactTypeId) throws * within TSK core. */ public List<AnalysisResult> getAnalysisResultsByType(int artifactTypeId, long dataSourceObjId) throws TskCoreException { - return getAnalysisResultsWhere(" arts.artifact_type_id = " + artifactTypeId + " AND arts.data_source_obj_id = " + dataSourceObjId); + return getAnalysisResultsWhere(" artifacts.artifact_type_id = " + artifactTypeId + " AND artifacts.data_source_obj_id = " + dataSourceObjId); } /** @@ -396,7 +914,7 @@ public List<AnalysisResult> getAnalysisResultsByType(int artifactTypeId, long da * within TSK core. */ public List<AnalysisResult> getAnalysisResults(long sourceObjId) throws TskCoreException { - return getAnalysisResultsWhere(" arts.obj_id = " + sourceObjId); + return getAnalysisResultsWhere(" artifacts.obj_id = " + sourceObjId); } /** @@ -412,7 +930,7 @@ public List<AnalysisResult> getAnalysisResults(long sourceObjId) throws TskCoreE List<DataArtifact> getDataArtifactsBySource(long sourceObjId) throws TskCoreException { caseDb.acquireSingleUserCaseReadLock(); try (CaseDbConnection connection = caseDb.getConnection()) { - return getDataArtifactsWhere(String.format(" artifacts.obj_id = " + sourceObjId), connection); + return getDataArtifactsWhere(String.format(" artifacts.obj_id = %d", sourceObjId), connection); } finally { caseDb.releaseSingleUserCaseReadLock(); } @@ -493,7 +1011,7 @@ private boolean hasArtifactsOfCategory(BlackboardArtifact.Category category, lon * within TSK core. */ List<AnalysisResult> getAnalysisResults(long sourceObjId, CaseDbConnection connection) throws TskCoreException { - return getAnalysisResultsWhere(" arts.obj_id = " + sourceObjId, connection); + return getAnalysisResultsWhere(" artifacts.obj_id = " + sourceObjId, connection); } /** @@ -509,13 +1027,13 @@ List<AnalysisResult> getAnalysisResults(long sourceObjId, CaseDbConnection conne */ public List<AnalysisResult> getAnalysisResults(long sourceObjId, int artifactTypeId) throws TskCoreException { // Get the artifact type to check that it in the analysis result category. - BlackboardArtifact.Type artifactType = caseDb.getArtifactType(artifactTypeId); + BlackboardArtifact.Type artifactType = getArtifactType(artifactTypeId); if (artifactType.getCategory() != BlackboardArtifact.Category.ANALYSIS_RESULT) { throw new TskCoreException(String.format("Artifact type id %d is not in analysis result catgeory.", artifactTypeId)); } String whereClause = " types.artifact_type_id = " + artifactTypeId - + " AND arts.obj_id = " + sourceObjId; + + " AND artifacts.obj_id = " + sourceObjId; return getAnalysisResultsWhere(whereClause); } @@ -577,7 +1095,7 @@ List<AnalysisResult> getAnalysisResultsWhere(String whereClause, CaseDbConnectio */ public AnalysisResult getAnalysisResultById(long artifactObjId) throws TskCoreException { - String whereClause = " arts.artifact_obj_id = " + artifactObjId; + String whereClause = " artifacts.artifact_obj_id = " + artifactObjId; List<AnalysisResult> results = getAnalysisResultsWhere(whereClause); if (results.isEmpty()) { // throw an error if no analysis result found by id. @@ -673,7 +1191,7 @@ public List<DataArtifact> getDataArtifacts(long dataSourceObjId, Integer artifac public List<DataArtifact> getDataArtifacts(int artifactTypeID, long dataSourceObjId) throws TskCoreException { // Get the artifact type to check that it in the data artifact category. - BlackboardArtifact.Type artifactType = caseDb.getArtifactType(artifactTypeID); + BlackboardArtifact.Type artifactType = getArtifactType(artifactTypeID); if (artifactType.getCategory() != BlackboardArtifact.Category.DATA_ARTIFACT) { throw new TskCoreException(String.format("Artifact type id %d is not in data artifact catgeory.", artifactTypeID)); } @@ -701,7 +1219,7 @@ public List<DataArtifact> getDataArtifacts(int artifactTypeID, long dataSourceOb */ public List<DataArtifact> getDataArtifacts(int artifactTypeID) throws TskCoreException { // Get the artifact type to check that it in the data artifact category. - BlackboardArtifact.Type artifactType = caseDb.getArtifactType(artifactTypeID); + BlackboardArtifact.Type artifactType = getArtifactType(artifactTypeID); if (artifactType.getCategory() != BlackboardArtifact.Category.DATA_ARTIFACT) { throw new TskCoreException(String.format("Artifact type id %d is not in data artifact catgeory.", artifactTypeID)); } @@ -745,6 +1263,25 @@ public DataArtifact getDataArtifactById(long artifactObjId) throws TskCoreExcept } } + /** + * Get all data artifacts matching the given where sub-clause. + * + * @param whereClause SQL Where sub-clause, specifies conditions to match. + * + * @return List of data artifacts. May be an empty list. + * + * @throws TskCoreException exception thrown if a critical error occurs + * within TSK core. + */ + List<DataArtifact> getDataArtifactsWhere(String whereClause) throws TskCoreException { + caseDb.acquireSingleUserCaseReadLock(); + try (CaseDbConnection connection = caseDb.getConnection()) { + return getDataArtifactsWhere(whereClause, connection); + } finally { + caseDb.releaseSingleUserCaseReadLock(); + } + } + /** * Get all data artifacts matching the given where sub-clause. Uses the * given database connection to execute the query. @@ -757,7 +1294,7 @@ public DataArtifact getDataArtifactById(long artifactObjId) throws TskCoreExcept * @throws TskCoreException exception thrown if a critical error occurs * within TSK core. */ - private List<DataArtifact> getDataArtifactsWhere(String whereClause, CaseDbConnection connection) throws TskCoreException { + List<DataArtifact> getDataArtifactsWhere(String whereClause, CaseDbConnection connection) throws TskCoreException { final String queryString = DATA_ARTIFACT_QUERY_STRING + " AND ( " + whereClause + " )"; @@ -808,21 +1345,6 @@ private List<DataArtifact> resultSetToDataArtifacts(ResultSet resultSet, CaseDbC return dataArtifacts; } - /** - * Get the artifact type associated with an artifact type id. - * - * @param artTypeId An artifact type id. - * - * @return The artifact type. - * - * @throws TskCoreException If an error occurs accessing the case database - * or no value is found. - * - */ - public BlackboardArtifact.Type getArtifactType(int artTypeId) throws TskCoreException { - return caseDb.getArtifactType(artTypeId); - } - /** * Gets an attribute type, creating it if it does not already exist. Use * this method to define custom attribute types. @@ -838,16 +1360,67 @@ public BlackboardArtifact.Type getArtifactType(int artTypeId) throws TskCoreExce */ public BlackboardAttribute.Type getOrAddAttributeType(String typeName, BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE valueType, String displayName) throws BlackboardException { + if (typeNameToAttributeTypeMap.containsKey(typeName)) { + return typeNameToAttributeTypeMap.get(typeName); + } + + CaseDbTransaction trans = null; + Statement s = null; + ResultSet rs = null; try { - return caseDb.addArtifactAttributeType(typeName, valueType, displayName); - } catch (TskDataException typeExistsEx) { + trans = caseDb.beginTransaction(); + CaseDbConnection connection = trans.getConnection(); + connection.beginTransaction(); + s = connection.createStatement(); + rs = connection.executeQuery(s, "SELECT attribute_type_id FROM blackboard_attribute_types WHERE type_name = '" + typeName + "'"); //NON-NLS + if (!rs.next()) { + rs.close(); + rs = connection.executeQuery(s, "SELECT MAX(attribute_type_id) AS highest_id FROM blackboard_attribute_types"); + int maxID = 0; + if (rs.next()) { + maxID = rs.getInt("highest_id"); + if (maxID < MIN_USER_DEFINED_TYPE_ID) { + maxID = MIN_USER_DEFINED_TYPE_ID; + } else { + maxID++; + } + } + connection.executeUpdate(s, "INSERT INTO blackboard_attribute_types (attribute_type_id, type_name, display_name, value_type) VALUES ('" + maxID + "', '" + typeName + "', '" + displayName + "', '" + valueType.getType() + "')"); //NON-NLS + BlackboardAttribute.Type type = new BlackboardAttribute.Type(maxID, typeName, displayName, valueType); + this.typeIdToAttributeTypeMap.put(type.getTypeID(), type); + this.typeNameToAttributeTypeMap.put(type.getTypeName(), type); + trans.commit(); + trans = null; + return type; + } else { + trans.commit(); + trans = null; + try { + return getAttributeType(typeName); + } catch (TskCoreException ex) { + throw new BlackboardException("Failed to get or add attribute type: " + typeName, ex); + } + } + } catch (SQLException | TskCoreException ex) { try { - return caseDb.getAttributeType(typeName); - } catch (TskCoreException ex) { - throw new BlackboardException("Failed to get or add attribute type", ex); + if (trans != null) { + trans.rollback(); + trans = null; + } + } catch (TskCoreException ex2) { + LOGGER.log(Level.SEVERE, "Error rolling back transaction", ex2); + } + throw new BlackboardException("Error adding attribute type: " + typeName, ex); + } finally { + closeResultSet(rs); + closeStatement(s); + if (trans != null) { + try { + trans.rollback(); + } catch (TskCoreException ex) { + throw new BlackboardException("Error rolling back transaction", ex); + } } - } catch (TskCoreException ex) { - throw new BlackboardException("Failed to get or add attribute type", ex); } } @@ -922,8 +1495,8 @@ public long getArtifactsCount(int artifactTypeID, long dataSourceObjId) throws T * within TSK core */ public List<BlackboardArtifact> getArtifacts(int artifactTypeID, long dataSourceObjId) throws TskCoreException { - return caseDb.getArtifactsHelper("blackboard_artifacts.data_source_obj_id = " + dataSourceObjId - + " AND blackboard_artifact_types.artifact_type_id = " + artifactTypeID + ";"); + String whereClause = String.format("artifacts.data_source_obj_id = %d", dataSourceObjId); + return getArtifactsWhere(getArtifactType(artifactTypeID), whereClause); } /** @@ -945,12 +1518,21 @@ public List<BlackboardArtifact> getArtifacts(Collection<BlackboardArtifact.Type> return new ArrayList<>(); } - String typeQuery = ""; + String analysisResultQuery = ""; + String dataArtifactQuery = ""; + for (BlackboardArtifact.Type type : artifactTypes) { - if (!typeQuery.isEmpty()) { - typeQuery += " OR "; + if (type.getCategory() == BlackboardArtifact.Category.ANALYSIS_RESULT) { + if (!analysisResultQuery.isEmpty()) { + analysisResultQuery += " OR "; + } + analysisResultQuery += "types.artifact_type_id = " + type.getTypeID(); + } else { + if (!dataArtifactQuery.isEmpty()) { + dataArtifactQuery += " OR "; + } + dataArtifactQuery += "types.artifact_type_id = " + type.getTypeID(); } - typeQuery += "blackboard_artifact_types.artifact_type_id = " + type.getTypeID(); } String dsQuery = ""; @@ -958,12 +1540,22 @@ public List<BlackboardArtifact> getArtifacts(Collection<BlackboardArtifact.Type> if (!dsQuery.isEmpty()) { dsQuery += " OR "; } - dsQuery += "blackboard_artifacts.data_source_obj_id = " + dsId; + dsQuery += "artifacts.data_source_obj_id = " + dsId; + } + + List<BlackboardArtifact> artifacts = new ArrayList<>(); + + if (!analysisResultQuery.isEmpty()) { + String fullQuery = "( " + analysisResultQuery + " ) AND (" + dsQuery + ") "; + artifacts.addAll(this.getAnalysisResultsWhere(fullQuery)); } - String fullQuery = "( " + typeQuery + " ) AND ( " + dsQuery + " );"; + if (!dataArtifactQuery.isEmpty()) { + String fullQuery = "( " + dataArtifactQuery + " ) AND (" + dsQuery + ") "; + artifacts.addAll(this.getDataArtifactsWhere(fullQuery)); + } - return caseDb.getArtifactsHelper(fullQuery); + return artifacts; } /** @@ -1260,24 +1852,101 @@ public DataArtifact newDataArtifact(BlackboardArtifact.Type artifactType, long s } /** - * Event published by SleuthkitCase when one or more artifacts are posted. A - * posted artifact is complete (all attributes have been added) and ready - * for further processing. + * Returns a list of BlackboardArtifacts of the given artifact type and + * source object id. + * + * @param artifactType The artifact type. + * @param sourceObjId The artifact parent source id (obj_id) + * + * @return A list of BlackboardArtifacts for the given parameters. + * + * @throws TskCoreException + */ + List<BlackboardArtifact> getArtifactsBySourceId(BlackboardArtifact.Type artifactType, long sourceObjId) throws TskCoreException { + String whereClause = String.format("artifacts.obj_id = %d", sourceObjId); + return getArtifactsWhere(artifactType, whereClause); + } + + /** + * Returns a list of artifacts of the given type. + * + * @param artifactType The type of artifacts to retrieve. + * + * @return A list of artifacts of the given type. + * + * @throws TskCoreException + */ + List<BlackboardArtifact> getArtifactsByType(BlackboardArtifact.Type artifactType) throws TskCoreException { + List<BlackboardArtifact> artifacts = new ArrayList<>(); + if (artifactType.getCategory() == BlackboardArtifact.Category.ANALYSIS_RESULT) { + artifacts.addAll(getAnalysisResultsByType(artifactType.getTypeID())); + } else { + artifacts.addAll(getDataArtifacts(artifactType.getTypeID())); + } + return artifacts; + } + + /** + * Returns a list of artifacts for the given artifact type with the given + * where clause. + * + * The Where clause will be added to the basic query for retrieving + * DataArtifacts or AnalysisResults from the DB. The where clause should not + * include the artifact type. This method will add the artifact type to the + * where clause. + * + * @param artifactType The artifact type. + * @param whereClause Additional where clause. + * + * @return A list of BlackboardArtifacts of the given type with the given + * conditional. + * + * @throws TskCoreException + */ + private List<BlackboardArtifact> getArtifactsWhere(BlackboardArtifact.Type artifactType, String whereClause) throws TskCoreException { + List<BlackboardArtifact> artifacts = new ArrayList<>(); + String whereWithType = whereClause + " AND artifacts.artifact_type_id = " + artifactType.getTypeID(); + + if (artifactType.getCategory() == BlackboardArtifact.Category.ANALYSIS_RESULT) { + artifacts.addAll(getAnalysisResultsWhere(whereWithType)); + } else { + artifacts.addAll(getDataArtifactsWhere(whereWithType)); + } + + return artifacts; + } + + /** + * An event published by SleuthkitCase when one or more artifacts are + * posted. Posted artifacts should be complete (all attributes have been + * added) and ready for further analysis. */ final public class ArtifactsPostedEvent { private final String moduleName; private final ImmutableSet<BlackboardArtifact.Type> artifactTypes; private final ImmutableSet<BlackboardArtifact> artifacts; + private final Long ingestJobId; - private ArtifactsPostedEvent(Collection<BlackboardArtifact> artifacts, String moduleName) throws BlackboardException { + /** + * Constructs an Event published by SleuthkitCase when one or more + * artifacts are posted. Posted artifacts should be complete (all + * attributes have been added) and ready for further analysis. + * + * @param artifacts The artifacts. + * @param moduleName The display name of the module posting the + * artifacts. + * @param ingestJobId The numeric identifier of the ingest job within + * which the artifacts were posted. + */ + private ArtifactsPostedEvent(Collection<BlackboardArtifact> artifacts, String moduleName, Long ingestJobId) throws BlackboardException { Set<Integer> typeIDS = artifacts.stream() .map(BlackboardArtifact::getArtifactTypeID) .collect(Collectors.toSet()); Set<BlackboardArtifact.Type> types = new HashSet<>(); for (Integer typeID : typeIDS) { try { - types.add(caseDb.getArtifactType(typeID)); + types.add(getArtifactType(typeID)); } catch (TskCoreException tskCoreException) { throw new BlackboardException("Error getting artifact type by id.", tskCoreException); } @@ -1285,13 +1954,25 @@ private ArtifactsPostedEvent(Collection<BlackboardArtifact> artifacts, String mo artifactTypes = ImmutableSet.copyOf(types); this.artifacts = ImmutableSet.copyOf(artifacts); this.moduleName = moduleName; - + this.ingestJobId = ingestJobId; } + /** + * Gets the posted artifacts. + * + * @return The artifacts (data artifacts and/or analysis results). + */ public Collection<BlackboardArtifact> getArtifacts() { return ImmutableSet.copyOf(artifacts); } + /** + * Gets the posted artifacts of a given type. + * + * @param artifactType The artifact type. + * + * @return The artifacts, if any. + */ public Collection<BlackboardArtifact> getArtifacts(BlackboardArtifact.Type artifactType) { Set<BlackboardArtifact> tempSet = artifacts.stream() .filter(artifact -> artifact.getArtifactTypeID() == artifactType.getTypeID()) @@ -1299,12 +1980,33 @@ public Collection<BlackboardArtifact> getArtifacts(BlackboardArtifact.Type artif return ImmutableSet.copyOf(tempSet); } + /** + * Gets the display name of the module that posted the artifacts. + * + * @return The display name. + */ public String getModuleName() { return moduleName; } + /** + * Gets the types of artifacts that were posted. + * + * @return The types. + */ public Collection<BlackboardArtifact.Type> getArtifactTypes() { return ImmutableSet.copyOf(artifactTypes); } + + /** + * Gets the numeric identifier of the ingest job within which the + * artifacts were posted. + * + * @return The ingest job ID, may be null. + */ + public Long getIngestJobId() { + return ingestJobId; + } + } } diff --git a/bindings/java/src/org/sleuthkit/datamodel/BlackboardArtifact.java b/bindings/java/src/org/sleuthkit/datamodel/BlackboardArtifact.java index b427c441ee9ada0003650e95d6195d6162e763b5..3ccef5d92647daeb01807a3f14a830f179810ce0 100644 --- a/bindings/java/src/org/sleuthkit/datamodel/BlackboardArtifact.java +++ b/bindings/java/src/org/sleuthkit/datamodel/BlackboardArtifact.java @@ -38,7 +38,6 @@ import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE; import org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE; import org.sleuthkit.datamodel.SleuthkitCase.CaseDbTransaction; -import org.sleuthkit.datamodel.SleuthkitCase.ObjectInfo; /** * An artifact that has been posted to the blackboard. Artifacts store analysis @@ -51,7 +50,7 @@ * IMPORTANT NOTE: No more than one attribute of a given type should be added to * an artifact. It is undefined about which will be used. */ -public class BlackboardArtifact implements Content { +public abstract class BlackboardArtifact implements Content { private static final ResourceBundle bundle = ResourceBundle.getBundle("org.sleuthkit.datamodel.Bundle"); private final long artifactId; @@ -190,18 +189,20 @@ Long getDataSourceObjectID() { public int getArtifactTypeID() { return this.artifactTypeId; } - + /** * Gets the artifact type for this artifact. - * + * * @return The artifact type. + * + * @throws TskCoreException */ public BlackboardArtifact.Type getType() throws TskCoreException { BlackboardArtifact.Type standardTypesValue = BlackboardArtifact.Type.STANDARD_TYPES.get(getArtifactTypeID()); if (standardTypesValue != null) { return standardTypesValue; } else { - return getSleuthkitCase().getArtifactType(getArtifactTypeID()); + return getSleuthkitCase().getBlackboard().getArtifactType(getArtifactTypeID()); } } @@ -233,44 +234,111 @@ public String getDisplayName() { public String getShortDescription() throws TskCoreException { BlackboardAttribute attr = null; StringBuilder shortDescription = new StringBuilder(""); - switch (ARTIFACT_TYPE.fromID(artifactTypeId)) { - case TSK_WEB_BOOKMARK: //web_bookmark, web_cookie, web_download, and web_history are the same attribute for now - case TSK_WEB_COOKIE: - case TSK_WEB_DOWNLOAD: - case TSK_WEB_HISTORY: - attr = getAttribute(new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_DOMAIN)); - break; - case TSK_KEYWORD_HIT: - attr = getAttribute(new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_KEYWORD_PREVIEW)); - break; - case TSK_DEVICE_ATTACHED: - attr = getAttribute(new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_DEVICE_ID)); - break; - case TSK_CONTACT: //contact, message, and calllog are the same attributes for now - case TSK_MESSAGE: - case TSK_CALLLOG: - //get the first of these attributes which exists and is non null - final ATTRIBUTE_TYPE[] typesThatCanHaveName = {ATTRIBUTE_TYPE.TSK_NAME, - ATTRIBUTE_TYPE.TSK_PHONE_NUMBER, - ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_FROM, - ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_TO, - ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_HOME, - ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_MOBILE, - ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_OFFICE, - ATTRIBUTE_TYPE.TSK_EMAIL, - ATTRIBUTE_TYPE.TSK_EMAIL_FROM, - ATTRIBUTE_TYPE.TSK_EMAIL_TO, - ATTRIBUTE_TYPE.TSK_EMAIL_HOME, - ATTRIBUTE_TYPE.TSK_EMAIL_OFFICE}; //in the order we want to use them - for (ATTRIBUTE_TYPE t : typesThatCanHaveName) { - attr = getAttribute(new BlackboardAttribute.Type(t)); - if (attr != null && !attr.getDisplayString().isEmpty()) { - break; + if (BlackboardArtifact.Type.STANDARD_TYPES.get(artifactTypeId) != null) { + switch (ARTIFACT_TYPE.fromID(artifactTypeId)) { + case TSK_WIFI_NETWORK_ADAPTER: + attr = getAttribute(new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_MAC_ADDRESS)); + break; + case TSK_WIFI_NETWORK: + attr = getAttribute(new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_SSID)); + break; + case TSK_REMOTE_DRIVE: + attr = getAttribute(new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_REMOTE_PATH)); + break; + case TSK_SERVICE_ACCOUNT: + case TSK_SCREEN_SHOTS: + case TSK_DELETED_PROG: + case TSK_METADATA: + case TSK_OS_INFO: + case TSK_PROG_NOTIFICATIONS: + case TSK_PROG_RUN: + case TSK_RECENT_OBJECT: + case TSK_USER_DEVICE_EVENT: + case TSK_WEB_SEARCH_QUERY: + attr = getAttribute(new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_PROG_NAME)); + break; + case TSK_BLUETOOTH_PAIRING: + attr = getAttribute(new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_DEVICE_NAME)); + break; + case TSK_ACCOUNT: + attr = getAttribute(new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_ID)); + if (attr == null) { + attr = getAttribute(new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_CARD_NUMBER)); } - } - break; - default: - break; + break; + case TSK_WEB_CATEGORIZATION: + case TSK_BLUETOOTH_ADAPTER: + case TSK_GPS_AREA: + case TSK_GPS_BOOKMARK: + case TSK_GPS_LAST_KNOWN_LOCATION: + case TSK_GPS_ROUTE: + case TSK_GPS_SEARCH: + case TSK_GPS_TRACK: + case TSK_WEB_FORM_AUTOFILL: + attr = getAttribute(new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_NAME)); + break; + case TSK_WEB_ACCOUNT_TYPE: + attr = getAttribute(new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_TEXT)); + break; + case TSK_HASHSET_HIT: + case TSK_INTERESTING_ARTIFACT_HIT: + case TSK_INTERESTING_FILE_HIT: + case TSK_YARA_HIT: + attr = getAttribute(new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_SET_NAME)); + break; + case TSK_ENCRYPTION_DETECTED: + case TSK_ENCRYPTION_SUSPECTED: + case TSK_OBJECT_DETECTED: + case TSK_USER_CONTENT_SUSPECTED: + case TSK_VERIFICATION_FAILED: + attr = getAttribute(new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_COMMENT)); + break; + case TSK_DATA_SOURCE_USAGE: + case TSK_CALENDAR_ENTRY: + attr = getAttribute(new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_DESCRIPTION)); + break; + case TSK_WEB_BOOKMARK: //web_bookmark, web_cookie, web_download, and web_history are the same attribute for now + case TSK_WEB_COOKIE: + case TSK_WEB_DOWNLOAD: + case TSK_WEB_HISTORY: + case TSK_WEB_CACHE: + attr = getAttribute(new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_DOMAIN)); + break; + case TSK_KEYWORD_HIT: + attr = getAttribute(new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_KEYWORD_PREVIEW)); + break; + case TSK_DEVICE_ATTACHED: + attr = getAttribute(new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_DEVICE_ID)); + break; + case TSK_CONTACT: //contact, message, and calllog are the same attributes for now + case TSK_MESSAGE: + case TSK_CALLLOG: + case TSK_SPEED_DIAL_ENTRY: + case TSK_WEB_FORM_ADDRESS: + //get the first of these attributes which exists and is non null + final ATTRIBUTE_TYPE[] typesThatCanHaveName = {ATTRIBUTE_TYPE.TSK_NAME, + ATTRIBUTE_TYPE.TSK_PHONE_NUMBER, + ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_FROM, + ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_TO, + ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_HOME, + ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_MOBILE, + ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_OFFICE, + ATTRIBUTE_TYPE.TSK_EMAIL, + ATTRIBUTE_TYPE.TSK_EMAIL_FROM, + ATTRIBUTE_TYPE.TSK_EMAIL_TO, + ATTRIBUTE_TYPE.TSK_EMAIL_HOME, + ATTRIBUTE_TYPE.TSK_EMAIL_OFFICE, + ATTRIBUTE_TYPE.TSK_LOCATION}; //in the order we want to use them + for (ATTRIBUTE_TYPE t : typesThatCanHaveName) { + attr = getAttribute(new BlackboardAttribute.Type(t)); + if (attr != null && !attr.getDisplayString().isEmpty()) { + break; + } + } + break; + default: + break; + } } if (attr != null) { shortDescription.append(attr.getAttributeType().getDisplayName()).append(": ").append(attr.getDisplayString()); @@ -350,12 +418,12 @@ public void addAttribute(BlackboardAttribute attribute) throws TskCoreException public List<BlackboardAttribute> getAttributes() throws TskCoreException { ArrayList<BlackboardAttribute> attributes; if (false == loadedCacheFromDb) { - attributes = getSleuthkitCase().getBlackboardAttributes(this); + attributes = getSleuthkitCase().getBlackboard().getBlackboardAttributes(this); attrsCache.clear(); attrsCache.addAll(attributes); loadedCacheFromDb = true; } else { - attributes = new ArrayList<BlackboardAttribute>(attrsCache); + attributes = new ArrayList<>(attrsCache); } return attributes; } @@ -494,7 +562,7 @@ public List<AnalysisResult> getAllAnalysisResults() throws TskCoreException { public List<DataArtifact> getAllDataArtifacts() throws TskCoreException { return sleuthkitCase.getBlackboard().getDataArtifactsBySource(artifactObjId); } - + @Override public Score getAggregateScore() throws TskCoreException { return sleuthkitCase.getScoringManager().getAggregateScore(artifactObjId); @@ -690,7 +758,8 @@ public Set<String> getHashSetNames() throws TskCoreException { * looked up from this) * * @throws TskCoreException if critical error occurred within tsk core - * @deprecated Use the Blackboard to create Data Artifacts and Analysis Results. + * @deprecated Use the Blackboard to create Data Artifacts and Analysis + * Results. */ @Deprecated @Override @@ -700,9 +769,12 @@ public BlackboardArtifact newArtifact(int artifactTypeID) throws TskCoreExceptio @Override public AnalysisResultAdded newAnalysisResult(BlackboardArtifact.Type artifactType, Score score, String conclusion, String configuration, String justification, Collection<BlackboardAttribute> attributesList) throws TskCoreException { + // Get the ID before starting the transaction + long dataSourceId = this.getDataSource().getId(); + CaseDbTransaction trans = sleuthkitCase.beginTransaction(); try { - AnalysisResultAdded resultAdded = sleuthkitCase.getBlackboard().newAnalysisResult(artifactType, this.getId(), this.getDataSource().getId(), score, conclusion, configuration, justification, attributesList, trans); + AnalysisResultAdded resultAdded = sleuthkitCase.getBlackboard().newAnalysisResult(artifactType, this.getId(), dataSourceId, score, conclusion, configuration, justification, attributesList, trans); trans.commit(); return resultAdded; @@ -730,12 +802,12 @@ public AnalysisResultAdded newAnalysisResult(BlackboardArtifact.Type artifactTyp public DataArtifact newDataArtifact(BlackboardArtifact.Type artifactType, Collection<BlackboardAttribute> attributesList, Long osAccountId) throws TskCoreException { throw new TskCoreException("Cannot create data artifact of an artifact. Not supported."); } - + @Override public DataArtifact newDataArtifact(BlackboardArtifact.Type artifactType, Collection<BlackboardAttribute> attributesList, Long osAccountId, long dataSourceId) throws TskCoreException { throw new TskCoreException("Cannot create data artifact of an artifact. Not supported."); } - + @Override public DataArtifact newDataArtifact(BlackboardArtifact.Type artifactType, Collection<BlackboardAttribute> attributesList) throws TskCoreException { return newDataArtifact(artifactType, attributesList, null); @@ -750,7 +822,8 @@ public DataArtifact newDataArtifact(BlackboardArtifact.Type artifactType, Collec * looked up from this) * * @throws TskCoreException if critical error occurred within tsk core - * @deprecated Use the Blackboard to create Data Artifacts and Analysis Results. + * @deprecated Use the Blackboard to create Data Artifacts and Analysis + * Results. */ @Deprecated @Override @@ -1280,17 +1353,20 @@ public static final class Type implements Serializable { public static final Type TSK_WEB_CATEGORIZATION = new BlackboardArtifact.Type(68, "TSK_WEB_CATEGORIZATION", bundle.getString("BlackboardArtifact.tskWebCategorization.text"), Category.ANALYSIS_RESULT); /** - * Indicates that the file or artifact was previously seen in another Autopsy case. + * Indicates that the file or artifact was previously seen in another + * Autopsy case. */ public static final Type TSK_PREVIOUSLY_SEEN = new BlackboardArtifact.Type(69, "TSK_PREVIOUSLY_SEEN", bundle.getString("BlackboardArtifact.tskPreviouslySeen.text"), Category.ANALYSIS_RESULT); - + /** - * Indicates that the file or artifact was previously unseen in another Autopsy case. + * Indicates that the file or artifact was previously unseen in another + * Autopsy case. */ public static final Type TSK_PREVIOUSLY_UNSEEN = new BlackboardArtifact.Type(70, "TSK_PREVIOUSLY_UNSEEN", bundle.getString("BlackboardArtifact.tskPreviouslyUnseen.text"), Category.ANALYSIS_RESULT); - + /** - * Indicates that the file or artifact was previously tagged as "Notable" in another Autopsy case. + * Indicates that the file or artifact was previously tagged as + * "Notable" in another Autopsy case. */ public static final Type TSK_PREVIOUSLY_NOTABLE = new BlackboardArtifact.Type(71, "TSK_PREVIOUSLY_NOTABLE", bundle.getString("BlackboardArtifact.tskPreviouslyNotable.text"), Category.ANALYSIS_RESULT); @@ -1299,8 +1375,19 @@ public static final class Type implements Serializable { */ public static final Type TSK_MATCHING_PERSONA = new BlackboardArtifact.Type(72, "TSK_MATCHING_PERSONA", bundle.getString("BlackboardArtifact.tskMatchingPersona.text"), Category.ANALYSIS_RESULT); - - // NOTE: When adding a new standard BlackboardArtifact.Type, add the instance and then add to the STANDARD_TYPES map. + /* + * IMPORTANT! + * + * Until BlackboardArtifact.ARTIFACT_TYPE is deprecated and/or removed, + * new standard artifact types need to be added to both + * BlackboardArtifact.ARTIFACT_TYPE and + * BlackboardArtifact.Type.STANDARD_TYPES. + * + * Also, ensure that new types have a one line JavaDoc description and + * are added to the standard artifacts catalog (artifact_catalog.dox). + * + */ + /** * All standard artifact types with ids mapped to the type. */ @@ -1376,7 +1463,7 @@ public static final class Type implements Serializable { private final int typeID; private final String displayName; private final Category category; - + /** * Constructs a custom artifact type. * @@ -1867,17 +1954,20 @@ public enum ARTIFACT_TYPE implements SleuthkitVisitableItem { TSK_WEB_CATEGORIZATION(68, "TSK_WEB_CATEGORIZATION", bundle.getString("BlackboardArtifact.tskWebCategorization.text"), Category.ANALYSIS_RESULT), /** - * Indicates that the file or artifact was previously seen in another Autopsy case. + * Indicates that the file or artifact was previously seen in another + * Autopsy case. */ TSK_PREVIOUSLY_SEEN(69, "TSK_PREVIOUSLY_SEEN", - bundle.getString("BlackboardArtifact.tskPreviouslySeen.text"), Category.ANALYSIS_RESULT), + bundle.getString("BlackboardArtifact.tskPreviouslySeen.text"), Category.ANALYSIS_RESULT), /** - * Indicates that the file or artifact was previously unseen in another Autopsy case. + * Indicates that the file or artifact was previously unseen in another + * Autopsy case. */ TSK_PREVIOUSLY_UNSEEN(70, "TSK_PREVIOUSLY_UNSEEN", bundle.getString("BlackboardArtifact.tskPreviouslyUnseen.text"), Category.ANALYSIS_RESULT), /** - * Indicates that the file or artifact was previously tagged as "Notable" in another Autopsy case. + * Indicates that the file or artifact was previously tagged as + * "Notable" in another Autopsy case. */ TSK_PREVIOUSLY_NOTABLE(71, "TSK_PREVIOUSLY_NOTABLE", bundle.getString("BlackboardArtifact.tskPreviouslyNotable.text"), Category.ANALYSIS_RESULT), @@ -1888,12 +1978,18 @@ public enum ARTIFACT_TYPE implements SleuthkitVisitableItem { TSK_MATCHING_PERSONA(72, "TSK_MATCHING_PERSONA", bundle.getString("BlackboardArtifact.tskMatchingPersona.text"), Category.ANALYSIS_RESULT); - /* - * To developers: For each new artifact, ensure that: - The enum value - * has 1-line JavaDoc description - The artifact catalog - * (artifact_catalog.dox) is updated to reflect the attributes it uses + * IMPORTANT! + * + * Until BlackboardArtifact.ARTIFACT_TYPE is deprecated and/or removed, + * new standard artifact types need to be added to both + * BlackboardArtifact.ARTIFACT_TYPE and + * BlackboardArtifact.Type.STANDARD_TYPES. + * + * Also, ensure that new types have a one line JavaDoc description and + * are added to the standard artifacts catalog (artifact_catalog.dox). */ + private final String label; private final int typeId; private final String displayName; diff --git a/bindings/java/src/org/sleuthkit/datamodel/BlackboardAttribute.java b/bindings/java/src/org/sleuthkit/datamodel/BlackboardAttribute.java index 508c45bd795a892d5e30a1f290957bfd28bd0359..fdc720da84b0157e67fcbd7684f76daeb158ad9f 100755 --- a/bindings/java/src/org/sleuthkit/datamodel/BlackboardAttribute.java +++ b/bindings/java/src/org/sleuthkit/datamodel/BlackboardAttribute.java @@ -612,12 +612,11 @@ public static final class Type implements Serializable { public static final Type TSK_CORRELATION_TYPE = new Type(157, "TSK_CORRELATION_TYPE", bundle.getString("BlackboardAttribute.tskCorrelationType.text"), TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.STRING); public static final Type TSK_CORRELATION_VALUE = new Type(158, "TSK_CORRELATION_VALUE", bundle.getString("BlackboardAttribute.tskCorrelationValue.text"), TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.STRING); public static final Type TSK_OTHER_CASES = new Type(159, "TSK_OTHER_CASES", bundle.getString("BlackboardAttribute.tskOtherCases.text"), TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.STRING); - + // NOTE: When adding a new standard BlackboardAttribute.Type, add the instance and then add to the STANDARD_TYPES list. /** * A list of all the standard attribute types. */ - static final List<Type> STANDARD_TYPES = Collections.unmodifiableList(Arrays.asList( TSK_URL, TSK_DATETIME, diff --git a/bindings/java/src/org/sleuthkit/datamodel/Bundle.properties b/bindings/java/src/org/sleuthkit/datamodel/Bundle.properties index 53b5520f0168a44da84dfc51ada2ea1bd59128ea..b167766a8f2d8e9bf5efab528a9b80e453c0c674 100644 --- a/bindings/java/src/org/sleuthkit/datamodel/Bundle.properties +++ b/bindings/java/src/org/sleuthkit/datamodel/Bundle.properties @@ -363,7 +363,6 @@ WebTypes.webDownloads.name=Web Downloads WebTypes.webCookies.name=Web Cookies Create WebTypes.webCookiesAccessed.name=Web Cookies Accessed WebTypes.webCookiesStart.name=Web Cookies Start -WebTypes.webCookiesEnd.name=Web Cookies End WebTypes.webBookmarks.name=Web Bookmarks WebTypes.webHistory.name=Web History Accessed WebTypes.webHistoryCreated.name=Web History Created @@ -398,9 +397,9 @@ OsAccountType.Interactive.text=Interactive OsAccountInstanceType.Launched.text=Launched OsAccountInstanceType.Accessed.text=Accessed OsAccountInstanceType.Referenced.text=Referenced -OsAccountInstanceType.Launched.descr.text=Account owner launched a program action on the host. -OsAccountInstanceType.Accessed.descr.text=Account owner accessed resources on the host for read/write via some service. -OsAccountInstanceType.Referenced.descr.text=Account owner was referenced in a log file on the host. +OsAccountInstanceType.Launched.descr.text=User launched a program or had an interactive session on the host. +OsAccountInstanceType.Accessed.descr.text=User accessed resources on the host via a service or created a file on the host. +OsAccountInstanceType.Referenced.descr.text=User was referenced on the host and it is unclear if they had any access. For example, if they are mentioned in a log file. OsAccountRealm.Known.text=Known OsAccountRealm.Inferred.text=Inferred OsAccountRealm.Unknown.text=Unknown @@ -438,4 +437,15 @@ TimelineEventType.WebCache.text=Web Cache TimelineEventType.BluetoothAdapter.txt=Bluetooth Adapter BaseTypes.geolocation.name=Geolocation BaseTypes.communication.name=Communication +TskData.ObjectType.IMG.name=Disk Image +TskData.ObjectType.VS.name=Volume System +TskData.ObjectType.VOL.name=Volume +TskData.ObjectType.FS.name=File System +TskData.ObjectType.AbstractFile.name=File +TskData.ObjectType.Artifact.name=Artifact +TskData.ObjectType.Report.name=Report +TskData.ObjectType.Pool.name=Pool +TskData.ObjectType.OsAccount.name=OS Account +TskData.ObjectType.HostAddress.name=Host Address +TskData.ObjectType.Unsupported.name=Unsupported diff --git a/bindings/java/src/org/sleuthkit/datamodel/Bundle.properties-MERGED b/bindings/java/src/org/sleuthkit/datamodel/Bundle.properties-MERGED index 53b5520f0168a44da84dfc51ada2ea1bd59128ea..b167766a8f2d8e9bf5efab528a9b80e453c0c674 100644 --- a/bindings/java/src/org/sleuthkit/datamodel/Bundle.properties-MERGED +++ b/bindings/java/src/org/sleuthkit/datamodel/Bundle.properties-MERGED @@ -363,7 +363,6 @@ WebTypes.webDownloads.name=Web Downloads WebTypes.webCookies.name=Web Cookies Create WebTypes.webCookiesAccessed.name=Web Cookies Accessed WebTypes.webCookiesStart.name=Web Cookies Start -WebTypes.webCookiesEnd.name=Web Cookies End WebTypes.webBookmarks.name=Web Bookmarks WebTypes.webHistory.name=Web History Accessed WebTypes.webHistoryCreated.name=Web History Created @@ -398,9 +397,9 @@ OsAccountType.Interactive.text=Interactive OsAccountInstanceType.Launched.text=Launched OsAccountInstanceType.Accessed.text=Accessed OsAccountInstanceType.Referenced.text=Referenced -OsAccountInstanceType.Launched.descr.text=Account owner launched a program action on the host. -OsAccountInstanceType.Accessed.descr.text=Account owner accessed resources on the host for read/write via some service. -OsAccountInstanceType.Referenced.descr.text=Account owner was referenced in a log file on the host. +OsAccountInstanceType.Launched.descr.text=User launched a program or had an interactive session on the host. +OsAccountInstanceType.Accessed.descr.text=User accessed resources on the host via a service or created a file on the host. +OsAccountInstanceType.Referenced.descr.text=User was referenced on the host and it is unclear if they had any access. For example, if they are mentioned in a log file. OsAccountRealm.Known.text=Known OsAccountRealm.Inferred.text=Inferred OsAccountRealm.Unknown.text=Unknown @@ -438,4 +437,15 @@ TimelineEventType.WebCache.text=Web Cache TimelineEventType.BluetoothAdapter.txt=Bluetooth Adapter BaseTypes.geolocation.name=Geolocation BaseTypes.communication.name=Communication +TskData.ObjectType.IMG.name=Disk Image +TskData.ObjectType.VS.name=Volume System +TskData.ObjectType.VOL.name=Volume +TskData.ObjectType.FS.name=File System +TskData.ObjectType.AbstractFile.name=File +TskData.ObjectType.Artifact.name=Artifact +TskData.ObjectType.Report.name=Report +TskData.ObjectType.Pool.name=Pool +TskData.ObjectType.OsAccount.name=OS Account +TskData.ObjectType.HostAddress.name=Host Address +TskData.ObjectType.Unsupported.name=Unsupported diff --git a/bindings/java/src/org/sleuthkit/datamodel/Bundle_ja.properties b/bindings/java/src/org/sleuthkit/datamodel/Bundle_ja.properties index 164f2f3cfbc2e60e4f74f68826d777f6377cff4d..e27bb2e94f77eabdac31ed30e100c36a692d2709 100644 --- a/bindings/java/src/org/sleuthkit/datamodel/Bundle_ja.properties +++ b/bindings/java/src/org/sleuthkit/datamodel/Bundle_ja.properties @@ -1,11 +1,10 @@ -#Thu Jul 01 12:01:30 UTC 2021 +#Thu Sep 30 10:23:46 UTC 2021 AbstractFile.readLocal.exception.msg1.text=\u30ed\u30fc\u30ab\u30eb\u30d5\u30a1\u30a4\u30eb\u306e\u8aad\u307f\u53d6\u308a\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002\u30ed\u30fc\u30ab\u30eb\u30d1\u30b9\u304c\u30bb\u30c3\u30c8\u3055\u308c\u3066\u3044\u307e\u305b\u3093\u3002 AbstractFile.readLocal.exception.msg2.text=\u30ed\u30fc\u30ab\u30eb\u30d5\u30a1\u30a4\u30eb\u306e\u8aad\u307f\u53d6\u308a\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002\u4e0b\u8a18\u306e\u30ed\u30fc\u30ab\u30eb\u30d1\u30b9\u306b\u306f\u5b58\u5728\u3057\u307e\u305b\u3093\uff1a{0} AbstractFile.readLocal.exception.msg3.text=\u30ed\u30fc\u30ab\u30eb\u30d5\u30a1\u30a4\u30eb\u306e\u8aad\u307f\u53d6\u308a\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002\u4e0b\u8a18\u306e\u30ed\u30fc\u30ab\u30eb\u30d1\u30b9\u3067\u306f\u8aad\u307f\u53d6\u308a\u3067\u304d\u307e\u305b\u3093\uff1a{0} AbstractFile.readLocal.exception.msg4.text=\u30d5\u30a1\u30a4\u30eb{0}\u306e\u8aad\u307f\u53d6\u308a\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f AbstractFile.readLocal.exception.msg5.text=\u30ed\u30fc\u30ab\u30eb\u30d5\u30a1\u30a4\u30eb{0}\u3092\u8aad\u307f\u53d6\u308c\u307e\u305b\u3093 BaseTypes.communication.name=\u30b3\u30df\u30e5\u30cb\u30b1\u30fc\u30b7\u30e7\u30f3 -BaseTypes.customTypes.name=\u305d\u306e\u4ed6 BaseTypes.fileSystem.name=\u30d5\u30a1\u30a4\u30eb\u30b7\u30b9\u30c6\u30e0 BaseTypes.geolocation.name=\u30b8\u30aa\u30ed\u30b1\u30fc\u30b7\u30e7\u30f3 BaseTypes.miscTypes.name=\u305d\u306e\u4ed6 @@ -52,6 +51,9 @@ BlackboardArtifact.tskMetadataExif.text=EXIF\u30e1\u30bf\u30c7\u30fc\u30bf BlackboardArtifact.tskObjectDetected.text=\u30aa\u30d6\u30b8\u30a7\u30af\u30c8\u304c\u691c\u51fa\u3055\u308c\u307e\u3057\u305f BlackboardArtifact.tskOsAccount.text=\u30aa\u30da\u30ec\u30fc\u30c6\u30a3\u30f3\u30b0\u30b7\u30b9\u30c6\u30e0\u30e6\u30fc\u30b6\u30a2\u30ab\u30a6\u30f3\u30c8 BlackboardArtifact.tskOsInfo.text=\u30aa\u30da\u30ec\u30fc\u30c6\u30a3\u30f3\u30b0\u30b7\u30b9\u30c6\u30e0\u60c5\u5831 +BlackboardArtifact.tskPreviouslyNotable.text=\u4ee5\u524d\u306b\u6ce8\u76ee\u306b\u5024\u3059\u308b +BlackboardArtifact.tskPreviouslySeen.text=\u4ee5\u524d\u306b\u8a8d\u8b58 +BlackboardArtifact.tskPreviouslyUnseen.text=\u4ee5\u524d\u306b\u306f\u672a\u8a8d\u8b58 BlackboardArtifact.tskProgNotifications.text=\u30d7\u30ed\u30b0\u30e9\u30e0\u901a\u77e5 BlackboardArtifact.tskProgRun.text=\u5b9f\u884c\u30d7\u30ed\u30b0\u30e9\u30e0 BlackboardArtifact.tskRemoteDrive.text=\u30ea\u30e2\u30fc\u30c8\u30c9\u30e9\u30a4\u30d6 @@ -96,6 +98,8 @@ BlackboardAttribute.tskCardType.text=\u30ab\u30fc\u30c9\u306e\u6709\u52b9\u671f\ BlackboardAttribute.tskCategory.text=\u30ab\u30c6\u30b4\u30ea\u30fc BlackboardAttribute.tskCity.text=\u5e02 BlackboardAttribute.tskComment.text=\u30b3\u30e1\u30f3\u30c8 +BlackboardAttribute.tskCorrelationType.text=\u76f8\u95a2\u30bf\u30a4\u30d7 +BlackboardAttribute.tskCorrelationValue.text=\u76f8\u95a2\u5024 BlackboardAttribute.tskCount.text=\u30ab\u30a6\u30f3\u30c8 BlackboardAttribute.tskCountry.text=\u56fd BlackboardAttribute.tskDateTimeAccessed.text=\u30a2\u30af\u30bb\u30b9\u65e5\u4ed8 @@ -174,6 +178,7 @@ BlackboardAttribute.tskMsgReplyId.text=\u30e1\u30c3\u30bb\u30fc\u30b8\u30ea\u30d BlackboardAttribute.tskName.text=\u540d\u524d BlackboardAttribute.tskNamePerson.text=\u4eba\u540d BlackboardAttribute.tskOrganization.text=\u7d44\u7e54 +BlackboardAttribute.tskOtherCases.text=\u305d\u306e\u4ed6\u306e\u30b1\u30fc\u30b9 BlackboardAttribute.tskOwner.text=\u4fdd\u6709\u8005 BlackboardAttribute.tskPassword.text=\u30d1\u30b9\u30ef\u30fc\u30c9 BlackboardAttribute.tskPath.text=\u30d1\u30b9 @@ -236,8 +241,9 @@ BlackboardAttribute.tskrule.text=\u30eb\u30fc\u30eb BlackboardAttribute.tskthreadid.text=\u30b9\u30ec\u30c3\u30c9ID CategoryType.AnalysisResult=\u5206\u6790\u7d50\u679c CategoryType.DataArtifact=\u30c7\u30fc\u30bf\u30a2\u30fc\u30c6\u30a3\u30d5\u30a1\u30af\u30c8 -CustomTypes.other.name=\u6a19\u6e96\u30bf\u30a4\u30d7 -CustomTypes.userCreated.name=\u30ab\u30b9\u30bf\u30e0\u30bf\u30a4\u30d7 +CustomTypes.customArtifact.name=\u30ab\u30b9\u30bf\u30e0\u30fb\u30a2\u30fc\u30c6\u30a3\u30d5\u30a1\u30af\u30c8\u30fb\u30a4\u30d9\u30f3\u30c8 +CustomTypes.other.name=\u6a19\u6e96\u30a2\u30fc\u30c6\u30a3\u30d5\u30a1\u30af\u30c8\u30fb\u30a4\u30d9\u30f3\u30c8 +CustomTypes.userCreated.name=\u624b\u52d5\u3067\u4f5c\u6210\u3055\u308c\u305f\u30a4\u30d9\u30f3\u30c8 DataSourcesFilter.displayName.text=\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u3092\u306b\u5236\u9650\u3059\u308b DatabaseConnectionCheck.Access=\u30e6\u30fc\u30b6\u30fc\u540d\u304b\u30d1\u30b9\u30ef\u30fc\u30c9\u304c\u7121\u52b9\u3067\u3059\u3002 DatabaseConnectionCheck.Authentication=\u30e6\u30fc\u30b6\u30fc\u540d\u304b\u30d1\u30b9\u30ef\u30fc\u30c9\u304c\u7121\u52b9\u3067\u3059\u3002 @@ -267,9 +273,9 @@ EventTypeZoomLevel.baseType=\u30d9\u30fc\u30b9\u30bf\u30a4\u30d7 EventTypeZoomLevel.rootType=\u30eb\u30fc\u30c8\u30bf\u30a4\u30d7 EventTypeZoomLevel.subType=\u30b5\u30d6\u30bf\u30a4\u30d7 FileSystemTypes.fileAccessed.name=\u30a2\u30af\u30bb\u30b9\u3055\u308c\u305f\u30d5\u30a1\u30a4\u30eb -FileSystemTypes.fileChanged.name=\u30d5\u30a1\u30a4\u30eb\u304c\u5909\u66f4\u3055\u308c\u307e\u3057\u305f -FileSystemTypes.fileCreated.name=\u30d5\u30a1\u30a4\u30eb\u4f5c\u6210 -FileSystemTypes.fileModified.name=\u30d5\u30a1\u30a4\u30eb\u304c\u5909\u66f4\u3055\u308c\u307e\u3057\u305f +FileSystemTypes.fileChanged.name=\u5909\u66f4\u3055\u308c\u305f\u30d5\u30a1\u30a4\u30eb +FileSystemTypes.fileCreated.name=\u4f5c\u6210\u3055\u308c\u305f\u30d5\u30a1\u30a4\u30eb +FileSystemTypes.fileModified.name=\u4fee\u6b63\u3055\u308c\u305f\u30d5\u30a1\u30a4\u30eb FileTypesFilter.displayName.text=\u30d5\u30a1\u30a4\u30eb\u30bf\u30a4\u30d7\u3092\u5236\u9650 FsContent.readInt.err.msg.text=\u753b\u50cf\u30d5\u30a1\u30a4\u30eb\u304c\u5b58\u5728\u3057\u306a\u3044\u304b\u3001\u30a2\u30af\u30bb\u30b9\u3067\u304d\u307e\u305b\u3093\u3002 Image.verifyImageSize.errStr1.text=\u4e0d\u5b8c\u5168\u306a\u753b\u50cf\u306e\u53ef\u80fd\u6027\uff1a\u30aa\u30d5\u30bb\u30c3\u30c8{0}\u3067\u30dc\u30ea\u30e5\u30fc\u30e0\u306e\u8aad\u53d6\u308a\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f @@ -279,8 +285,10 @@ Image.verifyImageSize.errStr4.text=\u4e0d\u5b8c\u5168\u306a\u753b\u50cf\u306e\u5 IngestJobInfo.IngestJobStatusType.Cancelled.displayName=\u30ad\u30e3\u30f3\u30bb\u30eb IngestJobInfo.IngestJobStatusType.Completed.displayName=\u5b8c\u4e86 IngestJobInfo.IngestJobStatusType.Started.displayName=\u958b\u59cb +IngestModuleInfo.IngestModuleType.DataArtifact.displayName=\u30c7\u30fc\u30bf\u30a2\u30fc\u30c6\u30a3\u30d5\u30a1\u30af\u30c8 IngestModuleInfo.IngestModuleType.DataSourceLevel.displayName=\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u30ec\u30d9\u30eb IngestModuleInfo.IngestModuleType.FileLevel.displayName=\u30d5\u30a1\u30a4\u30eb\u30ec\u30d9\u30eb +IngestModuleInfo.IngestModuleType.Multiple.displayName=\u591a\u6570 IntersectionFilter.displayName.text=\u4ea4\u5dee\u70b9 MiscTypes.Calls.name=\u901a\u8a71\u958b\u59cb MiscTypes.CallsEnd.name=\u901a\u8a71\u7d42\u4e86 diff --git a/bindings/java/src/org/sleuthkit/datamodel/CaseDatabaseFactory.java b/bindings/java/src/org/sleuthkit/datamodel/CaseDatabaseFactory.java index e6c08af852c8b49b830ef902e4d9cc853f9a4868..bde92860d852f57957ef5eaca9f0f048141e0b17 100644 --- a/bindings/java/src/org/sleuthkit/datamodel/CaseDatabaseFactory.java +++ b/bindings/java/src/org/sleuthkit/datamodel/CaseDatabaseFactory.java @@ -536,7 +536,7 @@ private void createAccountInstancesAndArtifacts(Statement stmt) throws SQLExcept + "os_account_obj_id " + dbQueryHelper.getBigIntType() + " NOT NULL, " + "data_source_obj_id " + dbQueryHelper.getBigIntType() + " NOT NULL, " + "instance_type INTEGER NOT NULL, " // PerformedActionOn/ReferencedOn - + "UNIQUE(os_account_obj_id, data_source_obj_id), " + + "UNIQUE(os_account_obj_id, data_source_obj_id, instance_type), " + "FOREIGN KEY(os_account_obj_id) REFERENCES tsk_os_accounts(os_account_obj_id) ON DELETE CASCADE, " + "FOREIGN KEY(data_source_obj_id) REFERENCES tsk_objects(obj_id) ON DELETE CASCADE ) "); diff --git a/bindings/java/src/org/sleuthkit/datamodel/CommunicationsManager.java b/bindings/java/src/org/sleuthkit/datamodel/CommunicationsManager.java index a5ff593837cc7fa8ce7b8166cef788c4786a70ab..ee7b5e55eb354f847ffd60ddccc3e34b910fc837 100644 --- a/bindings/java/src/org/sleuthkit/datamodel/CommunicationsManager.java +++ b/bindings/java/src/org/sleuthkit/datamodel/CommunicationsManager.java @@ -86,7 +86,7 @@ public final class CommunicationsManager { private void initAccountTypes() throws TskCoreException { db.acquireSingleUserCaseWriteLock(); try (CaseDbConnection connection = db.getConnection(); - Statement statement = connection.createStatement();) { + Statement statement = connection.createStatement();) { // Read the table int count = readAccountTypes(); if (0 == count) { @@ -133,7 +133,7 @@ private int readAccountTypes() throws TskCoreException { Statement statement = null; ResultSet resultSet = null; int count = 0; - + db.acquireSingleUserCaseReadLock(); try { connection = db.getConnection(); @@ -296,9 +296,9 @@ public Account getAccount(org.sleuthkit.datamodel.Account.Type accountType, Stri Account account = null; db.acquireSingleUserCaseReadLock(); try (CaseDbConnection connection = db.getConnection(); - Statement s = connection.createStatement(); - ResultSet rs = connection.executeQuery(s, "SELECT * FROM accounts WHERE account_type_id = " + getAccountTypeId(accountType) - + " AND account_unique_identifier = '" + normalizeAccountID(accountType, accountUniqueID) + "'");) { //NON-NLS + Statement s = connection.createStatement(); + ResultSet rs = connection.executeQuery(s, "SELECT * FROM accounts WHERE account_type_id = " + getAccountTypeId(accountType) + + " AND account_unique_identifier = '" + normalizeAccountID(accountType, accountUniqueID) + "'");) { //NON-NLS if (rs.next()) { account = new Account(rs.getInt("account_id"), accountType, @@ -501,7 +501,7 @@ private BlackboardArtifact getOrCreateAccountFileInstanceArtifact(Account.Type a ); accountArtifact = sourceFile.newDataArtifact(ACCOUNT_TYPE, attributes); - + try { db.getBlackboard().postArtifact(accountArtifact, moduleName); } catch (BlackboardException ex) { @@ -526,36 +526,38 @@ private BlackboardArtifact getOrCreateAccountFileInstanceArtifact(Account.Type a */ private BlackboardArtifact getAccountFileInstanceArtifact(Account.Type accountType, String accountUniqueID, Content sourceFile) throws TskCoreException { BlackboardArtifact accountArtifact = null; - + String queryStr = "SELECT artifacts.artifact_id AS artifact_id," - + " artifacts.obj_id AS obj_id," - + " artifacts.artifact_obj_id AS artifact_obj_id," - + " artifacts.data_source_obj_id AS data_source_obj_id," - + " artifacts.artifact_type_id AS artifact_type_id," - + " artifacts.review_status_id AS review_status_id" - + " FROM blackboard_artifacts AS artifacts" - + " JOIN blackboard_attributes AS attr_account_type" - + " ON artifacts.artifact_id = attr_account_type.artifact_id" - + " JOIN blackboard_attributes AS attr_account_id" - + " ON artifacts.artifact_id = attr_account_id.artifact_id" - + " AND attr_account_id.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ID.getTypeID() - + " AND attr_account_id.value_text = '" + accountUniqueID + "'" - + " WHERE artifacts.artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID() - + " AND attr_account_type.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ACCOUNT_TYPE.getTypeID() - + " AND attr_account_type.value_text = '" + accountType.getTypeName() + "'" - + " AND artifacts.obj_id = " + sourceFile.getId(); //NON-NLS - + + " artifacts.obj_id AS obj_id," + + " artifacts.artifact_obj_id AS artifact_obj_id," + + " artifacts.data_source_obj_id AS data_source_obj_id," + + " artifacts.artifact_type_id AS artifact_type_id," + + " artifacts.review_status_id AS review_status_id," + + " tsk_data_artifacts.os_account_obj_id AS os_account_obj_id" + + " FROM blackboard_artifacts AS artifacts" + + " JOIN blackboard_attributes AS attr_account_type" + + " ON artifacts.artifact_id = attr_account_type.artifact_id" + + " JOIN blackboard_attributes AS attr_account_id" + + " ON artifacts.artifact_id = attr_account_id.artifact_id" + + " AND attr_account_id.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ID.getTypeID() + + " AND attr_account_id.value_text = '" + accountUniqueID + "'" + + " LEFT JOIN tsk_data_artifacts ON tsk_data_artifacts.artifact_obj_id = artifacts.artifact_obj_id" + + " WHERE artifacts.artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID() + + " AND attr_account_type.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ACCOUNT_TYPE.getTypeID() + + " AND attr_account_type.value_text = '" + accountType.getTypeName() + "'" + + " AND artifacts.obj_id = " + sourceFile.getId(); //NON-NLS + db.acquireSingleUserCaseReadLock(); try (CaseDbConnection connection = db.getConnection(); - Statement s = connection.createStatement(); - ResultSet rs = connection.executeQuery(s, queryStr);) { //NON-NLS + Statement s = connection.createStatement(); + ResultSet rs = connection.executeQuery(s, queryStr);) { //NON-NLS if (rs.next()) { - BlackboardArtifact.Type bbartType = db.getArtifactType(rs.getInt("artifact_type_id")); + BlackboardArtifact.Type bbartType = db.getBlackboard().getArtifactType(rs.getInt("artifact_type_id")); - accountArtifact = new BlackboardArtifact(db, rs.getLong("artifact_id"), rs.getLong("obj_id"), rs.getLong("artifact_obj_id"), + accountArtifact = new DataArtifact(db, rs.getLong("artifact_id"), rs.getLong("obj_id"), rs.getLong("artifact_obj_id"), rs.getObject("data_source_obj_id") != null ? rs.getLong("data_source_obj_id") : null, bbartType.getTypeID(), bbartType.getTypeName(), bbartType.getDisplayName(), - BlackboardArtifact.ReviewStatus.withID(rs.getInt("review_status_id"))); + BlackboardArtifact.ReviewStatus.withID(rs.getInt("review_status_id")), rs.getLong("os_account_obj_id"), false); } } catch (SQLException ex) { throw new TskCoreException("Error getting account", ex); @@ -583,8 +585,8 @@ public org.sleuthkit.datamodel.Account.Type getAccountType(String accountTypeNam db.acquireSingleUserCaseReadLock(); try (CaseDbConnection connection = db.getConnection(); - Statement s = connection.createStatement(); - ResultSet rs = connection.executeQuery(s, "SELECT account_type_id, type_name, display_name FROM account_types WHERE type_name = '" + accountTypeName + "'");) { //NON-NLS + Statement s = connection.createStatement(); + ResultSet rs = connection.executeQuery(s, "SELECT account_type_id, type_name, display_name FROM account_types WHERE type_name = '" + accountTypeName + "'");) { //NON-NLS Account.Type accountType = null; if (rs.next()) { accountType = new Account.Type(accountTypeName, rs.getString("display_name")); @@ -779,13 +781,13 @@ public Map<AccountPair, Long> getRelationshipCountsPairwise(Set<AccountDeviceIns + " accounts2.account_id, " + " account_types2.type_name, " + " account_types2.display_name"; - + Map<AccountPair, Long> results = new HashMap<AccountPair, Long>(); - + db.acquireSingleUserCaseReadLock(); try (CaseDbConnection connection = db.getConnection(); - Statement s = connection.createStatement(); - ResultSet rs = connection.executeQuery(s, queryString);) { //NON-NLS + Statement s = connection.createStatement(); + ResultSet rs = connection.executeQuery(s, queryString);) { //NON-NLS while (rs.next()) { //make account 1 @@ -856,13 +858,13 @@ public long getRelationshipSourcesCount(AccountDeviceInstance accountDeviceInsta } String queryStr - = "SELECT count(DISTINCT relationships.relationship_source_obj_id) as count " - + " FROM" + innerQuery - + " WHERE relationships.data_source_obj_id IN ( " + datasourceObjIdsCSV + " )" - + " AND ( relationships.account1_id = " + account_id - + " OR relationships.account2_id = " + account_id + " )" - + (filterSQL.isEmpty() ? "" : " AND " + filterSQL); - + = "SELECT count(DISTINCT relationships.relationship_source_obj_id) as count " + + " FROM" + innerQuery + + " WHERE relationships.data_source_obj_id IN ( " + datasourceObjIdsCSV + " )" + + " AND ( relationships.account1_id = " + account_id + + " OR relationships.account2_id = " + account_id + " )" + + (filterSQL.isEmpty() ? "" : " AND " + filterSQL); + db.acquireSingleUserCaseReadLock(); try (CaseDbConnection connection = db.getConnection(); Statement s = connection.createStatement(); @@ -899,7 +901,7 @@ public Set<Content> getRelationshipSources(Set<AccountDeviceInstance> accountDev return Collections.emptySet(); } - Map<Long, Set<Long>> accountIdToDatasourceObjIdMap = new HashMap<Long, Set<Long>>(); + Map<Long, Set<Long>> accountIdToDatasourceObjIdMap = new HashMap<>(); for (AccountDeviceInstance accountDeviceInstance : accountDeviceInstanceList) { long accountID = accountDeviceInstance.getAccount().getAccountID(); List<Long> dataSourceObjIds = db.getDataSourceObjIds(accountDeviceInstance.getDeviceId()); @@ -907,11 +909,11 @@ public Set<Content> getRelationshipSources(Set<AccountDeviceInstance> accountDev if (accountIdToDatasourceObjIdMap.containsKey(accountID)) { accountIdToDatasourceObjIdMap.get(accountID).addAll(dataSourceObjIds); } else { - accountIdToDatasourceObjIdMap.put(accountID, new HashSet<Long>(dataSourceObjIds)); + accountIdToDatasourceObjIdMap.put(accountID, new HashSet<>(dataSourceObjIds)); } } - List<String> adiSQLClauses = new ArrayList<String>(); + List<String> adiSQLClauses = new ArrayList<>(); for (Map.Entry<Long, Set<Long>> entry : accountIdToDatasourceObjIdMap.entrySet()) { final Long accountID = entry.getKey(); String datasourceObjIdsCSV = StringUtils.buildCSVString(entry.getValue()); @@ -938,38 +940,30 @@ public Set<Content> getRelationshipSources(Set<AccountDeviceInstance> accountDev if (!limitStr.isEmpty()) { limitQuery = "(SELECT * FROM account_relationships as relationships " + limitStr + ") as relationships"; } - + String queryStr - = "SELECT DISTINCT artifacts.artifact_id AS artifact_id," - + " artifacts.obj_id AS obj_id," - + " artifacts.artifact_obj_id AS artifact_obj_id," - + " artifacts.data_source_obj_id AS data_source_obj_id, " - + " artifacts.artifact_type_id AS artifact_type_id, " - + " artifacts.review_status_id AS review_status_id " - + " FROM blackboard_artifacts as artifacts" - + " JOIN " + limitQuery - + " ON artifacts.artifact_obj_id = relationships.relationship_source_obj_id" - // append sql to restrict search to specified account device instances - + " WHERE (" + adiSQLClause + " )" - // plus other filters - + (filterSQL.isEmpty() ? "" : " AND (" + filterSQL + " )"); - - + = "SELECT DISTINCT artifacts.artifact_id AS artifact_id," + + " artifacts.obj_id AS obj_id," + + " artifacts.artifact_obj_id AS artifact_obj_id," + + " artifacts.data_source_obj_id AS data_source_obj_id, " + + " artifacts.artifact_type_id AS artifact_type_id, " + + " artifacts.review_status_id AS review_status_id," + + " tsk_data_artifacts.os_account_obj_id as os_account_obj_id" + + " FROM blackboard_artifacts as artifacts" + + " JOIN " + limitQuery + + " ON artifacts.artifact_obj_id = relationships.relationship_source_obj_id" + + " LEFT JOIN tsk_data_artifacts ON artifacts.artifact_obj_id = tsk_data_artifacts.artifact_obj_id" + // append sql to restrict search to specified account device instances + + " WHERE (" + adiSQLClause + " )" + // plus other filters + + (filterSQL.isEmpty() ? "" : " AND (" + filterSQL + " )"); + db.acquireSingleUserCaseReadLock(); try (CaseDbConnection connection = db.getConnection(); Statement s = connection.createStatement(); ResultSet rs = connection.executeQuery(s, queryStr);) { //NON-NLS - Set<Content> relationshipSources = new HashSet<Content>(); - while (rs.next()) { - BlackboardArtifact.Type bbartType = db.getArtifactType(rs.getInt("artifact_type_id")); - relationshipSources.add(new BlackboardArtifact(db, rs.getLong("artifact_id"), - rs.getLong("obj_id"), rs.getLong("artifact_obj_id"), - rs.getObject("data_source_obj_id") != null ? rs.getLong("data_source_obj_id") : null, - bbartType.getTypeID(), - bbartType.getTypeName(), bbartType.getDisplayName(), - BlackboardArtifact.ReviewStatus.withID(rs.getInt("review_status_id")))); - } - + Set<Content> relationshipSources = new HashSet<>(); + relationshipSources.addAll(getDataArtifactsFromResult(rs)); return relationshipSources; } catch (SQLException ex) { throw new TskCoreException("Error getting relationships for account. " + ex.getMessage(), ex); @@ -1101,7 +1095,7 @@ public List<AccountDeviceInstance> getRelatedAccountDeviceInstances(AccountDevic public List<Content> getRelationshipSources(AccountDeviceInstance account1, AccountDeviceInstance account2, CommunicationsFilter filter) throws TskCoreException { //set up applicable filters - Set<String> applicableFilters = new HashSet<String>(Arrays.asList( + Set<String> applicableFilters = new HashSet<>(Arrays.asList( CommunicationsFilter.DateRangeFilter.class.getName(), CommunicationsFilter.DeviceFilter.class.getName(), CommunicationsFilter.RelationshipTypeFilter.class.getName() @@ -1119,10 +1113,12 @@ public List<Content> getRelationshipSources(AccountDeviceInstance account1, Acco + " artifacts.artifact_obj_id AS artifact_obj_id," + " artifacts.data_source_obj_id AS data_source_obj_id," + " artifacts.artifact_type_id AS artifact_type_id," - + " artifacts.review_status_id AS review_status_id" + + " artifacts.review_status_id AS review_status_id," + + " tsk_data_artifacts.os_account_obj_id AS os_account_obj_id" + " FROM blackboard_artifacts AS artifacts" + " JOIN " + limitQuery + " ON artifacts.artifact_obj_id = relationships.relationship_source_obj_id" + + " LEFT JOIN tsk_data_artifacts ON artifacts.artifact_obj_id = tsk_data_artifacts.artifact_obj_id" + " WHERE (( relationships.account1_id = " + account1.getAccount().getAccountID() + " AND relationships.account2_id = " + account2.getAccount().getAccountID() + " ) OR ( relationships.account2_id = " + account1.getAccount().getAccountID() @@ -1134,15 +1130,8 @@ public List<Content> getRelationshipSources(AccountDeviceInstance account1, Acco Statement s = connection.createStatement(); ResultSet rs = connection.executeQuery(s, queryString);) { - ArrayList<Content> artifacts = new ArrayList<Content>(); - while (rs.next()) { - BlackboardArtifact.Type bbartType = db.getArtifactType(rs.getInt("artifact_type_id")); - artifacts.add(new BlackboardArtifact(db, rs.getLong("artifact_id"), rs.getLong("obj_id"), rs.getLong("artifact_obj_id"), - rs.getObject("data_source_obj_id") != null ? rs.getLong("data_source_obj_id") : null, - bbartType.getTypeID(), bbartType.getTypeName(), bbartType.getDisplayName(), - BlackboardArtifact.ReviewStatus.withID(rs.getInt("review_status_id")))); - } - + ArrayList<Content> artifacts = new ArrayList<>(); + artifacts.addAll(getDataArtifactsFromResult(rs)); return artifacts; } catch (SQLException ex) { throw new TskCoreException("Error getting relationships between accounts. " + ex.getMessage(), ex); @@ -1163,7 +1152,7 @@ public List<Content> getRelationshipSources(AccountDeviceInstance account1, Acco */ public List<AccountFileInstance> getAccountFileInstances(Account account) throws TskCoreException { List<AccountFileInstance> accountFileInstanceList = new ArrayList<>(); - + @SuppressWarnings("deprecation") List<BlackboardArtifact> artifactList = getSleuthkitCase().getBlackboardArtifacts(BlackboardArtifact.ARTIFACT_TYPE.TSK_ACCOUNT, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ID, account.getTypeSpecificID()); if (artifactList != null && !artifactList.isEmpty()) { @@ -1188,10 +1177,10 @@ public List<AccountFileInstance> getAccountFileInstances(Account account) throws * @throws TskCoreException */ public List<Account.Type> getAccountTypesInUse() throws TskCoreException { - + String query = "SELECT DISTINCT accounts.account_type_id, type_name, display_name FROM accounts JOIN account_types ON accounts.account_type_id = account_types.account_type_id"; List<Account.Type> inUseAccounts = new ArrayList<>(); - + db.acquireSingleUserCaseReadLock(); try (CaseDbConnection connection = db.getConnection(); Statement s = connection.createStatement(); @@ -1385,4 +1374,31 @@ private String getMostRecentFilterLimitSQL(CommunicationsFilter filter) { return limitStr; } + + /** + * A helper method that will return a set of BlackboardArtifact objects for + * the given ResultSet. + * + * @param resultSet The results of executing a query. + * + * @return A list of BlackboardArtifact objects. + * + * @throws SQLException + * @throws TskCoreException + */ + private List<BlackboardArtifact> getDataArtifactsFromResult(ResultSet resultSet) throws SQLException, TskCoreException { + List<BlackboardArtifact> artifacts = new ArrayList<>(); + while (resultSet.next()) { + BlackboardArtifact.Type bbartType = db.getBlackboard().getArtifactType(resultSet.getInt("artifact_type_id")); + artifacts.add(new DataArtifact(db, resultSet.getLong("artifact_id"), + resultSet.getLong("obj_id"), resultSet.getLong("artifact_obj_id"), + resultSet.getObject("data_source_obj_id") != null ? resultSet.getLong("data_source_obj_id") : null, + bbartType.getTypeID(), + bbartType.getTypeName(), bbartType.getDisplayName(), + BlackboardArtifact.ReviewStatus.withID(resultSet.getInt("review_status_id")), + resultSet.getLong("os_account_obj_id"), false)); + } + + return artifacts; + } } diff --git a/bindings/java/src/org/sleuthkit/datamodel/FileManager.java b/bindings/java/src/org/sleuthkit/datamodel/FileManager.java index 98f04c85586d6955d94628338d2e2e289d7f161b..624e55a88ff08665877971ad322780b76a4ebbae 100644 --- a/bindings/java/src/org/sleuthkit/datamodel/FileManager.java +++ b/bindings/java/src/org/sleuthkit/datamodel/FileManager.java @@ -83,16 +83,16 @@ public List<AbstractFile> findFilesExactName(long parentId, String name) throws } /** - * Find all files with the exact given name and exact parent path. - * + * Find all files with the exact given name and exact parent path. + * * @param dataSource The data source to search within. - * @param name Exact file name to match. + * @param name Exact file name to match. * @param path Exact parent path. - * - * @return A list of matching files. - * - * @throws TskCoreException - */ + * + * @return A list of matching files. + * + * @throws TskCoreException + */ public List<AbstractFile> findFilesExactNameExactPath(Content dataSource, String name, String path) throws TskCoreException { // Database paths will always start and end with a forward slash, so add those if not present diff --git a/bindings/java/src/org/sleuthkit/datamodel/OsAccountInstance.java b/bindings/java/src/org/sleuthkit/datamodel/OsAccountInstance.java index 0a39baaf982e51fb8a541f54b77cd1ec6a52de3e..b43b582019ced882dded55d8d922166d4851e926 100755 --- a/bindings/java/src/org/sleuthkit/datamodel/OsAccountInstance.java +++ b/bindings/java/src/org/sleuthkit/datamodel/OsAccountInstance.java @@ -18,6 +18,7 @@ */ package org.sleuthkit.datamodel; +import java.util.Arrays; import java.util.Objects; import java.util.ResourceBundle; @@ -165,16 +166,26 @@ public boolean equals(Object obj) { return false; } final OsAccountInstance other = (OsAccountInstance) obj; + + if(this.instanceId != other.instanceId) { + return false; + } + if (this.accountId != other.accountId) { return false; } - - return this.dataSourceId != other.dataSourceId; + + if(this.instanceType != other.instanceType) { + return false; + } + + return this.dataSourceId == other.getDataSourceId(); } @Override public int hashCode() { int hash = 7; + hash = 67 * hash + Objects.hashCode(this.instanceId); hash = 67 * hash + Objects.hashCode(this.dataSourceId); hash = 67 * hash + Objects.hashCode(this.accountId); hash = 67 * hash + Objects.hashCode(this.instanceType); @@ -186,12 +197,15 @@ public int hashCode() { * where the instance was found. * * Whether an os account actually performed any action on the host or if - * just a reference to it was found on the host (such as in a log file) + * just a reference to it was found on the host (such as in a log file). + * + * Note: lower ordinal value is more significant than higher ordinal value. + * Order of significance: LAUNCHED > ACCESSED > REFERENCED. */ public enum OsAccountInstanceType { - LAUNCHED(0, bundle.getString("OsAccountInstanceType.Launched.text"), bundle.getString("OsAccountInstanceType.Launched.descr.text")), // the user launched a program on the host - ACCESSED(1, bundle.getString("OsAccountInstanceType.Accessed.text"), bundle.getString("OsAccountInstanceType.Accessed.descr.text")), // user accesed a resource for read/write - REFERENCED(2, bundle.getString("OsAccountInstanceType.Referenced.text"), bundle.getString("OsAccountInstanceType.Referenced.descr.text")); // user was referenced, e.g. in a event log. + LAUNCHED(0, bundle.getString("OsAccountInstanceType.Launched.text"), bundle.getString("OsAccountInstanceType.Launched.descr.text")), // the user has interactive access or launched a program on the host + ACCESSED(1, bundle.getString("OsAccountInstanceType.Accessed.text"), bundle.getString("OsAccountInstanceType.Accessed.descr.text")), // user accesed a resource for read/write via some service + REFERENCED(2, bundle.getString("OsAccountInstanceType.Referenced.text"), bundle.getString("OsAccountInstanceType.Referenced.descr.text")); // user was referenced in a log file, e.g. in a event log. private final int id; private final String name; @@ -245,5 +259,18 @@ public static OsAccountInstanceType fromID(int typeId) { } return null; } + + /** + * Gets account instance type enum from name. + * + * @param name Name to look for. + * + * @return Account instance type enum, null if no match is found. + */ + public static OsAccountInstanceType fromString(String name) { + return Arrays.stream(values()) + .filter(val -> val.getName().equals(name)) + .findFirst().orElse(null); + } } } diff --git a/bindings/java/src/org/sleuthkit/datamodel/OsAccountManager.java b/bindings/java/src/org/sleuthkit/datamodel/OsAccountManager.java index 00d0a82cfab022fe974a14d2149db1edbd66aab4..35a581216b2b2cfa1347a490e4fe8d62dc62d9bb 100755 --- a/bindings/java/src/org/sleuthkit/datamodel/OsAccountManager.java +++ b/bindings/java/src/org/sleuthkit/datamodel/OsAccountManager.java @@ -28,11 +28,11 @@ import java.util.Collections; import java.util.ArrayList; import java.util.List; -import java.util.NavigableSet; +import java.util.NavigableMap; import java.util.Objects; import java.util.Optional; import java.util.UUID; -import java.util.concurrent.ConcurrentSkipListSet; +import java.util.concurrent.ConcurrentSkipListMap; import java.util.stream.Collectors; import org.sleuthkit.datamodel.BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE; import org.sleuthkit.datamodel.OsAccount.OsAccountStatus; @@ -41,6 +41,8 @@ import org.sleuthkit.datamodel.SleuthkitCase.CaseDbConnection; import org.sleuthkit.datamodel.SleuthkitCase.CaseDbTransaction; import org.sleuthkit.datamodel.TskEvent.OsAccountsUpdatedTskEvent; +import static org.sleuthkit.datamodel.WindowsAccountUtils.getWindowsSpecialSidName; +import static org.sleuthkit.datamodel.WindowsAccountUtils.isWindowsSpecialSid; /** * Responsible for creating/updating/retrieving the OS accounts for files and @@ -50,7 +52,7 @@ public final class OsAccountManager { private final SleuthkitCase db; private final Object osAcctInstancesCacheLock; - private final NavigableSet<OsAccountInstance> osAccountInstanceCache; + private final NavigableMap<OsAccountInstanceKey, OsAccountInstance> osAccountInstanceCache; /** * Construct a OsUserManager for the given SleuthkitCase. @@ -61,7 +63,7 @@ public final class OsAccountManager { OsAccountManager(SleuthkitCase skCase) { db = skCase; osAcctInstancesCacheLock = new Object(); - osAccountInstanceCache = new ConcurrentSkipListSet<>(); + osAccountInstanceCache = new ConcurrentSkipListMap<>(); } /** @@ -151,16 +153,18 @@ public OsAccount newWindowsOsAccount(String sid, String loginName, String realmN throw new TskCoreException("A referring host is required to create an account."); } - // ensure at least one of the two is supplied - unique id or a login name - if (StringUtils.isBlank(sid) && StringUtils.isBlank(loginName)) { + // ensure at least one of the two is supplied - a non-null unique id or a login name + if ((StringUtils.isBlank(sid) || sid.equalsIgnoreCase(WindowsAccountUtils.WINDOWS_NULL_SID)) + && StringUtils.isBlank(loginName)) { throw new TskCoreException("Cannot create OS account with both uniqueId and loginName as null."); } // Realm name is required if the sid is null. - if (StringUtils.isBlank(sid) && StringUtils.isBlank(realmName)) { + if ((StringUtils.isBlank(sid) || sid.equalsIgnoreCase(WindowsAccountUtils.WINDOWS_NULL_SID)) + && StringUtils.isBlank(realmName)) { throw new TskCoreException("Realm name or SID is required to create a Windows account."); } - if (!StringUtils.isBlank(sid) && !WindowsAccountUtils.isWindowsUserSid(sid)) { + if (!StringUtils.isBlank(sid) && !sid.equalsIgnoreCase(WindowsAccountUtils.WINDOWS_NULL_SID) && !WindowsAccountUtils.isWindowsUserSid(sid)) { throw new OsAccountManager.NotUserSIDException(String.format("SID = %s is not a user SID.", sid)); } @@ -199,12 +203,13 @@ public OsAccount newWindowsOsAccount(String sid, String loginName, String realmN */ public OsAccount newWindowsOsAccount(String sid, String loginName, OsAccountRealm realm) throws TskCoreException, NotUserSIDException { - // ensure at least one of the two is supplied - unique id or a login name - if (StringUtils.isBlank(sid) && StringUtils.isBlank(loginName)) { + // ensure at least one of the two is supplied - a non-null unique id or a login name + if ((StringUtils.isBlank(sid) || sid.equalsIgnoreCase(WindowsAccountUtils.WINDOWS_NULL_SID)) + && StringUtils.isBlank(loginName)) { throw new TskCoreException("Cannot create OS account with both uniqueId and loginName as null."); } - if (!StringUtils.isBlank(sid) && !WindowsAccountUtils.isWindowsUserSid(sid)) { + if (!StringUtils.isBlank(sid) && !sid.equalsIgnoreCase(WindowsAccountUtils.WINDOWS_NULL_SID) && !WindowsAccountUtils.isWindowsUserSid(sid)) { throw new OsAccountManager.NotUserSIDException(String.format("SID = %s is not a user SID.", sid)); } @@ -212,7 +217,19 @@ public OsAccount newWindowsOsAccount(String sid, String loginName, OsAccountReal try { // try to create account try { - OsAccount account = newOsAccount(sid, loginName, realm, OsAccount.OsAccountStatus.UNKNOWN, trans); + String uniqueId = (!StringUtils.isBlank(sid) && !sid.equalsIgnoreCase(WindowsAccountUtils.WINDOWS_NULL_SID)) ? sid : null; + OsAccount account = newOsAccount(uniqueId, loginName, realm, OsAccount.OsAccountStatus.UNKNOWN, trans); + + // If the SID indicates a special windows account, then set its full name. + if (!StringUtils.isBlank(sid) && !sid.equalsIgnoreCase(WindowsAccountUtils.WINDOWS_NULL_SID) && isWindowsSpecialSid(sid)) { + String fullName = getWindowsSpecialSidName(sid); + if (StringUtils.isNotBlank(fullName)) { + OsAccountUpdateResult updateResult = updateStandardOsAccountAttributes(account, fullName, null, null, null, trans); + if (updateResult.getUpdatedAccount().isPresent()) { + account = updateResult.getUpdatedAccount().get(); + } + } + } trans.commit(); trans = null; return account; @@ -510,10 +527,12 @@ OsAccount getOsAccountByObjectId(long osAccountObjId, CaseDbConnection connectio * @param dataSource Data source where the instance is found. * @param instanceType Instance type. * + * @return OsAccountInstance Existing or newly created account instance. + * * @throws TskCoreException If there is an error creating the account * instance. */ - public void newOsAccountInstance(OsAccount osAccount, DataSource dataSource, OsAccountInstance.OsAccountInstanceType instanceType) throws TskCoreException { + public OsAccountInstance newOsAccountInstance(OsAccount osAccount, DataSource dataSource, OsAccountInstance.OsAccountInstanceType instanceType) throws TskCoreException { if (osAccount == null) { throw new TskCoreException("Cannot create account instance with null account."); } @@ -521,21 +540,14 @@ public void newOsAccountInstance(OsAccount osAccount, DataSource dataSource, OsA throw new TskCoreException("Cannot create account instance with null data source."); } - /* - * Check the cache of OS account instances for an existing instance for - * this OS account and data source. Note that the account instance - * created here has a bogus instance ID. This is possible since the - * instance ID is not considered in the equals() and hashCode() methods - * of this class. - */ - synchronized (osAcctInstancesCacheLock) { - if (osAccountInstanceCache.contains(new OsAccountInstance(db, 0, osAccount.getId(), dataSource.getId(), instanceType))) { - return; - } + // check the cache first + Optional<OsAccountInstance> existingInstance = cachedAccountInstance(osAccount.getId(), dataSource.getId(), instanceType); + if (existingInstance.isPresent()) { + return existingInstance.get(); } try (CaseDbConnection connection = this.db.getConnection()) { - newOsAccountInstance(osAccount.getId(), dataSource.getId(), instanceType, connection); + return newOsAccountInstance(osAccount.getId(), dataSource.getId(), instanceType, connection); } } @@ -549,21 +561,16 @@ public void newOsAccountInstance(OsAccount osAccount, DataSource dataSource, OsA * @param instanceType Instance type. * @param connection The current database connection. * + * @return OsAccountInstance Existing or newly created account instance. + * * @throws TskCoreException If there is an error creating the account * instance. */ - void newOsAccountInstance(long osAccountId, long dataSourceObjId, OsAccountInstance.OsAccountInstanceType instanceType, CaseDbConnection connection) throws TskCoreException { - /* - * Check the cache of OS account instances for an existing instance for - * this OS account and data source. Note that the account instance - * created here has a bogus instance ID. This is possible since the - * instance ID is not considered in the equals() and hashCode() methods - * of this class. - */ - synchronized (osAcctInstancesCacheLock) { - if (osAccountInstanceCache.contains(new OsAccountInstance(db, 0, osAccountId, dataSourceObjId, instanceType))) { - return; - } + OsAccountInstance newOsAccountInstance(long osAccountId, long dataSourceObjId, OsAccountInstance.OsAccountInstanceType instanceType, CaseDbConnection connection) throws TskCoreException { + + Optional<OsAccountInstance> existingInstance = cachedAccountInstance(osAccountId, dataSourceObjId, instanceType); + if (existingInstance.isPresent()) { + return existingInstance.get(); } /* @@ -583,7 +590,15 @@ void newOsAccountInstance(long osAccountId, long dataSourceObjId, OsAccountInsta if (resultSet.next()) { OsAccountInstance accountInstance = new OsAccountInstance(db, resultSet.getLong(1), osAccountId, dataSourceObjId, instanceType); synchronized (osAcctInstancesCacheLock) { - osAccountInstanceCache.add(accountInstance); + OsAccountInstanceKey key = new OsAccountInstanceKey(osAccountId, dataSourceObjId); + // remove from cache any instances less significant (higher ordinal) than this instance + for (OsAccountInstance.OsAccountInstanceType type : OsAccountInstance.OsAccountInstanceType.values()) { + if (accountInstance.getInstanceType().compareTo(type) < 0) { + osAccountInstanceCache.remove(key); + } + } + // add the new most significant instance to the cache + osAccountInstanceCache.put(key, accountInstance); } /* * There is a potential issue here. The cache of OS account @@ -601,6 +616,10 @@ void newOsAccountInstance(long osAccountId, long dataSourceObjId, OsAccountInsta * from time to time. */ db.fireTSKEvent(new TskEvent.OsAcctInstancesAddedTskEvent(Collections.singletonList(accountInstance))); + + return accountInstance; + } else { + throw new TskCoreException(String.format("Could not get autogen key after row insert for OS account instance. OS account object id = %d, data source object id = %d", osAccountId, dataSourceObjId)); } } } catch (SQLException ex) { @@ -610,6 +629,44 @@ void newOsAccountInstance(long osAccountId, long dataSourceObjId, OsAccountInsta } } + /** + * Check if an account instance for exists in the cache for given account + * id, data source and instance type. + * + * Instance type does not need to be an exact match - an existing instance + * with an instance type more significant than the specified type is + * considered a match. + * + * @param osAccountId Account id. + * @param dataSourceObjId Data source object id. + * @param instanceType Account instance type. + * + * @return Optional with OsAccountInstance, Optional.empty if there is no + * matching instance in cache. + * + */ + private Optional<OsAccountInstance> cachedAccountInstance(long osAccountId, long dataSourceObjId, OsAccountInstance.OsAccountInstanceType instanceType) { + + /* + * Check the cache of OS account instances for an existing instance for + * this OS account and data source. Note that the account instance + * created here has a bogus instance ID. This is possible since the + * instance ID is not considered in the equals() and hashCode() methods + * of this class. + */ + synchronized (osAcctInstancesCacheLock) { + OsAccountInstanceKey key = new OsAccountInstanceKey(osAccountId, dataSourceObjId); + OsAccountInstance instance = osAccountInstanceCache.get(key); + if (instance != null) { + // if the new instance type same or less significant than the existing instance (i.e. same or higher ordinal value) it's a match. + if (instanceType.compareTo(instance.getInstanceType()) >= 0) { + return Optional.of(instance); + } + } + return Optional.empty(); + } + } + /** * Get all accounts that had an instance on the specified host. * @@ -620,14 +677,13 @@ void newOsAccountInstance(long osAccountId, long dataSourceObjId, OsAccountInsta * @throws org.sleuthkit.datamodel.TskCoreException */ public List<OsAccount> getOsAccounts(Host host) throws TskCoreException { - - String queryString = "SELECT * FROM tsk_os_accounts as accounts " - + " JOIN tsk_os_account_instances as instances " - + " ON instances.os_account_obj_id = accounts.os_account_obj_id " - + " JOIN data_source_info as datasources " - + " ON datasources.obj_id = instances.data_source_obj_id " - + " WHERE datasources.host_id = " + host.getHostId() - + " AND accounts.db_status = " + OsAccount.OsAccountDbStatus.ACTIVE.getId(); + String queryString = "SELECT * FROM tsk_os_accounts accounts " + + "WHERE accounts.os_account_obj_id IN " + + "(SELECT instances.os_account_obj_id " + + "FROM tsk_os_account_instances instances " + + "INNER JOIN data_source_info datasources ON datasources.obj_id = instances.data_source_obj_id " + + "WHERE datasources.host_id = " + host.getHostId() + ") " + + "AND accounts.db_status = " + OsAccount.OsAccountDbStatus.ACTIVE.getId(); db.acquireSingleUserCaseReadLock(); try (CaseDbConnection connection = this.db.getConnection(); @@ -646,6 +702,40 @@ public List<OsAccount> getOsAccounts(Host host) throws TskCoreException { } } + /** + * Get all accounts that had an instance on the specified data source. + * + * @param dataSourceId Data source id for which to look accounts for. + * + * @return Set of OsAccounts, may be empty. + * + * @throws org.sleuthkit.datamodel.TskCoreException + */ + public List<OsAccount> getOsAccountsByDataSourceObjId(long dataSourceId) throws TskCoreException { + String queryString = "SELECT * FROM tsk_os_accounts acc " + + "WHERE acc.os_account_obj_id IN " + + "(SELECT instance.os_account_obj_id " + + "FROM tsk_os_account_instances instance " + + "WHERE instance.data_source_obj_id = " + dataSourceId + ") " + + "AND acc.db_status = " + OsAccount.OsAccountDbStatus.ACTIVE.getId(); + + db.acquireSingleUserCaseReadLock(); + try (CaseDbConnection connection = this.db.getConnection(); + Statement s = connection.createStatement(); + ResultSet rs = connection.executeQuery(s, queryString)) { + + List<OsAccount> accounts = new ArrayList<>(); + while (rs.next()) { + accounts.add(osAccountFromResultSet(rs)); + } + return accounts; + } catch (SQLException ex) { + throw new TskCoreException(String.format("Error getting OS accounts for data source id = %d", dataSourceId), ex); + } finally { + db.releaseSingleUserCaseReadLock(); + } + } + /** * Merge all OS accounts from sourceRealm into destRealm. After this call: - * sourceRealm's accounts will have been moved or merged - References to @@ -748,7 +838,7 @@ private void mergeOsAccounts(OsAccount sourceAccount, OsAccount destAccount, Cas query = makeOsAccountUpdateQuery("tsk_os_account_attributes", sourceAccount, destAccount); s.executeUpdate(query); - // tsk_os_account_instances has a unique constraint on os_account_obj_id, data_source_obj_id, host_id, + // tsk_os_account_instances has a unique constraint on os_account_obj_id, data_source_obj_id, and instance_type, // so delete any rows that would be duplicates. query = "DELETE FROM tsk_os_account_instances " + "WHERE id IN ( " @@ -758,7 +848,9 @@ private void mergeOsAccounts(OsAccount sourceAccount, OsAccount destAccount, Cas + " tsk_os_account_instances destAccountInstance " + "INNER JOIN tsk_os_account_instances sourceAccountInstance ON destAccountInstance.data_source_obj_id = sourceAccountInstance.data_source_obj_id " + "WHERE destAccountInstance.os_account_obj_id = " + destAccount.getId() - + " AND sourceAccountInstance.os_account_obj_id = " + sourceAccount.getId() + " )"; + + " AND sourceAccountInstance.os_account_obj_id = " + sourceAccount.getId() + + " AND sourceAccountInstance.instance_type = destAccountInstance.instance_type" + ")"; + s.executeUpdate(query); query = makeOsAccountUpdateQuery("tsk_os_account_instances", sourceAccount, destAccount); @@ -946,8 +1038,8 @@ public Optional<OsAccount> getWindowsOsAccount(String sid, String loginName, Str throw new TskCoreException("A referring host is required to get an account."); } - // ensure at least one of the two is supplied - sid or a login name - if (StringUtils.isBlank(sid) && StringUtils.isBlank(loginName)) { + // ensure at least one of the two is supplied - a non-null sid or a login name + if ((StringUtils.isBlank(sid) || (sid.equalsIgnoreCase(WindowsAccountUtils.WINDOWS_NULL_SID)) ) && StringUtils.isBlank(loginName)) { throw new TskCoreException("Cannot get an OS account with both SID and loginName as null."); } @@ -958,7 +1050,7 @@ public Optional<OsAccount> getWindowsOsAccount(String sid, String loginName, Str } // search by SID - if (!Strings.isNullOrEmpty(sid)) { + if (!Strings.isNullOrEmpty(sid) && !(sid.equalsIgnoreCase(WindowsAccountUtils.WINDOWS_NULL_SID))) { if (!WindowsAccountUtils.isWindowsUserSid(sid)) { throw new OsAccountManager.NotUserSIDException(String.format("SID = %s is not a user SID.", sid)); } @@ -1095,7 +1187,7 @@ List<OsAccountAttribute> getOsAccountAttributes(OsAccount account) throws TskCor if (!rs.wasNull()) { sourceContent = this.db.getContentById(sourceObjId); } - BlackboardAttribute.Type attributeType = db.getAttributeType(rs.getInt("attribute_type_id")); + BlackboardAttribute.Type attributeType = db.getBlackboard().getAttributeType(rs.getInt("attribute_type_id")); OsAccountAttribute attribute = account.new OsAccountAttribute(attributeType, rs.getInt("value_int32"), rs.getLong("value_int64"), rs.getDouble("value_double"), rs.getString("value_text"), rs.getBytes("value_byte"), db, account, host, sourceContent); @@ -1119,7 +1211,7 @@ List<OsAccountAttribute> getOsAccountAttributes(OsAccount account) throws TskCor * * @throws TskCoreException */ - List<OsAccountInstance> getOsAccountInstances(OsAccount account) throws TskCoreException { + public List<OsAccountInstance> getOsAccountInstances(OsAccount account) throws TskCoreException { String whereClause = "tsk_os_account_instances.os_account_obj_id = " + account.getId(); return getOsAccountInstances(whereClause); } @@ -1136,13 +1228,33 @@ List<OsAccountInstance> getOsAccountInstances(OsAccount account) throws TskCoreE */ public List<OsAccountInstance> getOsAccountInstances(List<Long> instanceIDs) throws TskCoreException { String instanceIds = instanceIDs.stream().map(id -> id.toString()).collect(Collectors.joining(",")); - String whereClause = "tsk_os_account_instances.id IN (" + instanceIds + ")"; - return getOsAccountInstances(whereClause); + + List<OsAccountInstance> osAcctInstances = new ArrayList<>(); + + String querySQL = "SELECT * FROM tsk_os_account_instances " + + " WHERE tsk_os_account_instances.id IN (" + instanceIds + ")"; + + db.acquireSingleUserCaseReadLock(); + try (CaseDbConnection connection = db.getConnection(); + PreparedStatement preparedStatement = connection.getPreparedStatement(querySQL, Statement.NO_GENERATED_KEYS); + ResultSet results = connection.executeQuery(preparedStatement)) { + + osAcctInstances = getOsAccountInstancesFromResultSet(results); + + } catch (SQLException ex) { + throw new TskCoreException("Failed to get OsAccountInstances (SQL = " + querySQL + ")", ex); + } finally { + db.releaseSingleUserCaseReadLock(); + } + return osAcctInstances; } /** * Gets the OS account instances that satisfy the given SQL WHERE clause. * + * Note: this query returns only the most significant instance type (least + * ordinal) for each instance, that matches the specified WHERE clause. + * * @param whereClause The SQL WHERE clause. * * @return The OS account instances. @@ -1152,18 +1264,24 @@ public List<OsAccountInstance> getOsAccountInstances(List<Long> instanceIDs) thr */ private List<OsAccountInstance> getOsAccountInstances(String whereClause) throws TskCoreException { List<OsAccountInstance> osAcctInstances = new ArrayList<>(); - String querySQL = "SELECT * FROM tsk_os_account_instances WHERE " + whereClause; + + String querySQL + = "SELECT tsk_os_account_instances.* " + + " FROM tsk_os_account_instances " + + " INNER JOIN ( SELECT os_account_obj_id, data_source_obj_id, MIN(instance_type) AS min_instance_type " + + " FROM tsk_os_account_instances" + + " GROUP BY os_account_obj_id, data_source_obj_id ) grouped_instances " + + " ON tsk_os_account_instances.os_account_obj_id = grouped_instances.os_account_obj_id " + + " AND tsk_os_account_instances.instance_type = grouped_instances.min_instance_type " + + " WHERE " + whereClause; + db.acquireSingleUserCaseReadLock(); try (CaseDbConnection connection = db.getConnection(); PreparedStatement preparedStatement = connection.getPreparedStatement(querySQL, Statement.NO_GENERATED_KEYS); ResultSet results = connection.executeQuery(preparedStatement)) { - while (results.next()) { - long instanceId = results.getLong("id"); - long osAccountObjID = results.getLong("os_account_obj_id"); - long dataSourceObjId = results.getLong("data_source_obj_id"); - int instanceType = results.getInt("instance_type"); - osAcctInstances.add(new OsAccountInstance(db, instanceId, osAccountObjID, dataSourceObjId, OsAccountInstance.OsAccountInstanceType.fromID(instanceType))); - } + + osAcctInstances = getOsAccountInstancesFromResultSet(results); + } catch (SQLException ex) { throw new TskCoreException("Failed to get OsAccountInstances (SQL = " + querySQL + ")", ex); } finally { @@ -1172,6 +1290,29 @@ private List<OsAccountInstance> getOsAccountInstances(String whereClause) throws return osAcctInstances; } + /** + * Returns list of OS account instances from the given result set. + * + * @param results Result set from a SELECT tsk_os_account_instances.* query. + * + * @return List of OS account instances. + * + * @throws SQLException + */ + private List<OsAccountInstance> getOsAccountInstancesFromResultSet(ResultSet results) throws SQLException { + + List<OsAccountInstance> osAcctInstances = new ArrayList<>(); + while (results.next()) { + long instanceId = results.getLong("id"); + long osAccountObjID = results.getLong("os_account_obj_id"); + long dataSourceObjId = results.getLong("data_source_obj_id"); + int instanceType = results.getInt("instance_type"); + osAcctInstances.add(new OsAccountInstance(db, instanceId, osAccountObjID, dataSourceObjId, OsAccountInstance.OsAccountInstanceType.fromID(instanceType))); + } + + return osAcctInstances; + } + /** * Updates the properties of the specified account in the database. * @@ -1396,7 +1537,7 @@ public OsAccountUpdateResult updateCoreWindowsOsAccountAttributes(OsAccount osAc private OsAccountUpdateResult updateCoreWindowsOsAccountAttributes(OsAccount osAccount, String accountSid, String loginName, String realmName, Host referringHost, CaseDbTransaction trans) throws TskCoreException, NotUserSIDException { // first get and update the realm - if we have the info to find the realm - if (!StringUtils.isBlank(accountSid) || !StringUtils.isBlank(realmName)) { + if ((!StringUtils.isBlank(accountSid) && !accountSid.equalsIgnoreCase(WindowsAccountUtils.WINDOWS_NULL_SID)) || !StringUtils.isBlank(realmName)) { db.getOsAccountRealmManager().getAndUpdateWindowsRealm(accountSid, realmName, referringHost, trans.getConnection()); } @@ -1436,12 +1577,12 @@ private OsAccountUpdateResult updateOsAccountCore(OsAccount osAccount, String ad try { CaseDbConnection connection = trans.getConnection(); - // if a new addr is provided and the account already has an address, and they are not the same, throw an exception - if (!StringUtils.isBlank(address) && !StringUtils.isBlank(osAccount.getAddr().orElse(null)) && !address.equalsIgnoreCase(osAccount.getAddr().orElse(""))) { + // if a new non-null addr is provided and the account already has an address, and they are not the same, throw an exception + if (!StringUtils.isBlank(address) && !address.equalsIgnoreCase(WindowsAccountUtils.WINDOWS_NULL_SID) && !StringUtils.isBlank(osAccount.getAddr().orElse(null)) && !address.equalsIgnoreCase(osAccount.getAddr().orElse(""))) { throw new TskCoreException(String.format("Account (%d) already has an address (%s), address cannot be updated.", osAccount.getId(), osAccount.getAddr().orElse("NULL"))); } - if (StringUtils.isBlank(osAccount.getAddr().orElse(null)) && !StringUtils.isBlank(address)) { + if (StringUtils.isBlank(osAccount.getAddr().orElse(null)) && !StringUtils.isBlank(address) && !address.equalsIgnoreCase(WindowsAccountUtils.WINDOWS_NULL_SID)) { updateAccountColumn(osAccount.getId(), "addr", address, connection); updateStatusCode = OsAccountUpdateStatus.UPDATED; } @@ -1645,4 +1786,61 @@ public Optional<OsAccount> getUpdatedAccount() { return Optional.ofNullable(updatedAccount); } } + + /** + * Represents the osAccountId\dataSourceId pair for use with the cache of + * OsAccountInstances. + */ + private class OsAccountInstanceKey implements Comparable<OsAccountInstanceKey>{ + + private final long osAccountId; + private final long dataSourceId; + + OsAccountInstanceKey(long osAccountId, long dataSourceId) { + this.osAccountId = osAccountId; + this.dataSourceId = dataSourceId; + } + + @Override + public boolean equals(Object other) { + if (this == other) { + return true; + } + if (other == null) { + return false; + } + if (getClass() != other.getClass()) { + return false; + } + + final OsAccountInstanceKey otherKey = (OsAccountInstanceKey) other; + + if (osAccountId != otherKey.osAccountId) { + return false; + } + + return dataSourceId == otherKey.dataSourceId; + } + + @Override + public int hashCode() { + int hash = 5; + hash = 53 * hash + (int) (this.osAccountId ^ (this.osAccountId >>> 32)); + hash = 53 * hash + (int) (this.dataSourceId ^ (this.dataSourceId >>> 32)); + return hash; + } + + @Override + public int compareTo(OsAccountInstanceKey other) { + if(this.equals(other)) { + return 0; + } + + if (dataSourceId != other.dataSourceId) { + return Long.compare(dataSourceId, other.dataSourceId); + } + + return Long.compare(osAccountId, other.osAccountId); + } + } } diff --git a/bindings/java/src/org/sleuthkit/datamodel/OsAccountRealm.java b/bindings/java/src/org/sleuthkit/datamodel/OsAccountRealm.java index 79995728adb0cba555a38fe9f0382706839f745f..54b827da50cf02ba72b73d924364b2c25a55c764 100644 --- a/bindings/java/src/org/sleuthkit/datamodel/OsAccountRealm.java +++ b/bindings/java/src/org/sleuthkit/datamodel/OsAccountRealm.java @@ -47,7 +47,7 @@ public final class OsAccountRealm { private final String realmName; // realm name private final String realmAddr; // realm address - private String signature; // either realm address or name (if address is not known) + private String signature; // either realm address or name (if address is not known), plus a scope indicator private final Host host; // if the realm consists of a single host. Will be null if the realm is domain scoped. private final ScopeConfidence scopeConfidence; // confidence in realm scope. private final RealmDbStatus dbStatus; // Status of row in database. @@ -59,7 +59,7 @@ public final class OsAccountRealm { * @param realmName Realm name, may be null. * @param realmAddr Unique numeric address for realm, may be null only * if realm name is not null. - * @param signature Either the address or the name. + * @param signature Either the address or the name, plus a scope indicator. * @param host Host if the realm is host scoped. * @param scopeConfidence Scope confidence. */ diff --git a/bindings/java/src/org/sleuthkit/datamodel/OsAccountRealmManager.java b/bindings/java/src/org/sleuthkit/datamodel/OsAccountRealmManager.java index f3b3751415e00671e658fad7adef03e87590109c..53526ad63f38395b44626740d9551d183b1f3f7f 100644 --- a/bindings/java/src/org/sleuthkit/datamodel/OsAccountRealmManager.java +++ b/bindings/java/src/org/sleuthkit/datamodel/OsAccountRealmManager.java @@ -82,7 +82,8 @@ public OsAccountRealm newWindowsRealm(String accountSid, String realmName, Host if (referringHost == null) { throw new TskCoreException("A referring host is required to create a realm."); } - if (StringUtils.isBlank(accountSid) && StringUtils.isBlank(realmName)) { + if ((StringUtils.isBlank(accountSid) || accountSid.equalsIgnoreCase(WindowsAccountUtils.WINDOWS_NULL_SID)) + && StringUtils.isBlank(realmName)) { throw new TskCoreException("Either an address or a name is required to create a realm."); } @@ -116,7 +117,7 @@ public OsAccountRealm newWindowsRealm(String accountSid, String realmName, Host // get windows realm address from sid String realmAddr = null; - if (!Strings.isNullOrEmpty(accountSid)) { + if (!Strings.isNullOrEmpty(accountSid) && !accountSid.equalsIgnoreCase(WindowsAccountUtils.WINDOWS_NULL_SID)) { if (!WindowsAccountUtils.isWindowsUserSid(accountSid)) { throw new OsAccountManager.NotUserSIDException(String.format("SID = %s is not a user SID.", accountSid )); @@ -161,7 +162,8 @@ public Optional<OsAccountRealm> getWindowsRealm(String accountSid, String realmN } // need at least one of the two, the addr or name to look up - if (Strings.isNullOrEmpty(accountSid) && Strings.isNullOrEmpty(realmName)) { + if ((Strings.isNullOrEmpty(accountSid) || accountSid.equalsIgnoreCase(WindowsAccountUtils.WINDOWS_NULL_SID) ) + && Strings.isNullOrEmpty(realmName)) { throw new TskCoreException("Realm address or name is required get a realm."); } @@ -192,12 +194,13 @@ Optional<OsAccountRealm> getWindowsRealm(String accountSid, String realmName, Ho } // need at least one of the two, the addr or name to look up - if (StringUtils.isBlank(accountSid) && StringUtils.isBlank(realmName)) { + if ((StringUtils.isBlank(accountSid) || accountSid.equalsIgnoreCase(WindowsAccountUtils.WINDOWS_NULL_SID)) + && StringUtils.isBlank(realmName)) { throw new TskCoreException("Realm address or name is required get a realm."); } - // If an accountSID is provided search for realm by addr. - if (!Strings.isNullOrEmpty(accountSid)) { + // If a non null accountSID is provided search for realm by addr. + if (!Strings.isNullOrEmpty(accountSid) && !accountSid.equalsIgnoreCase(WindowsAccountUtils.WINDOWS_NULL_SID)) { if (!WindowsAccountUtils.isWindowsUserSid(accountSid)) { throw new OsAccountManager.NotUserSIDException(String.format("SID = %s is not a user SID.", accountSid )); @@ -212,8 +215,8 @@ Optional<OsAccountRealm> getWindowsRealm(String accountSid, String realmName, Ho // No realm addr so search by name. Optional<OsAccountRealm> realm = getRealmByName(realmName, referringHost, connection); - if (realm.isPresent() && !Strings.isNullOrEmpty(accountSid)) { - // If we were given an accountSID, make sure there isn't one set on the matching realm. + if (realm.isPresent() && !Strings.isNullOrEmpty(accountSid) && !accountSid.equalsIgnoreCase(WindowsAccountUtils.WINDOWS_NULL_SID)) { + // If we were given a non-null accountSID, make sure there isn't one set on the matching realm. // We know it won't match because the previous search by SID failed. if (realm.get().getRealmAddr().isPresent()) { return Optional.empty(); @@ -249,7 +252,7 @@ Optional<OsAccountRealm> getAndUpdateWindowsRealm(String accountSid, String real // if found, update it if needed if (realmOptional.isPresent()) { - String realmAddr = StringUtils.isNotBlank(accountSid) ? WindowsAccountUtils.getWindowsRealmAddress(accountSid) : null; + String realmAddr = (StringUtils.isNotBlank(accountSid) && !accountSid.equalsIgnoreCase(WindowsAccountUtils.WINDOWS_NULL_SID)) ? WindowsAccountUtils.getWindowsRealmAddress(accountSid) : null; OsRealmUpdateResult realmUpdateResult = updateRealm(realmOptional.get(), realmAddr, realmName, connection); // if realm was updated, return the updated realm @@ -292,6 +295,9 @@ public OsRealmUpdateResult updateRealm(OsAccountRealm realm, String realmAddr, S /** * Updates the realm address and/or name, if a non blank address/name is * specified and the current address/name is blank. + * + * The realm name will not be updated regardless of the value in realmName + * if the passed in realm has an address equal to SPECIAL_WINDOWS_REALM_ADDR. * * @param realm Realm to update. * @param realmAddr Realm address, may be null if the address doesn't need @@ -308,7 +314,8 @@ public OsRealmUpdateResult updateRealm(OsAccountRealm realm, String realmAddr, S private OsRealmUpdateResult updateRealm(OsAccountRealm realm, String realmAddr, String realmName, CaseDbConnection connection) throws TskCoreException { // need at least one of the two - if (StringUtils.isBlank(realmAddr) && StringUtils.isBlank(realmName)) { + if ( (StringUtils.isBlank(realmAddr) || realmAddr.equalsIgnoreCase(WindowsAccountUtils.WINDOWS_NULL_SID)) + && StringUtils.isBlank(realmName)) { throw new TskCoreException("Realm address or name is required to update realm."); } @@ -317,17 +324,24 @@ private OsRealmUpdateResult updateRealm(OsAccountRealm realm, String realmAddr, db.acquireSingleUserCaseWriteLock(); try { - List<String> realmNames = realm.getRealmNames(); - String currRealmName = realmNames.isEmpty() ? null : realmNames.get(0); // currently there is only one name. String currRealmAddr = realm.getRealmAddr().orElse(null); // set name and address to new values only if the current value is blank and the new value isn't. - if ((StringUtils.isBlank(currRealmAddr) && StringUtils.isNotBlank(realmAddr))) { + if ((StringUtils.isBlank(currRealmAddr) && StringUtils.isNotBlank(realmAddr) && !realmAddr.equalsIgnoreCase(WindowsAccountUtils.WINDOWS_NULL_SID))) { updateRealmColumn(realm.getRealmId(), "realm_addr", realmAddr, connection); + currRealmAddr = realmAddr; updateStatusCode = OsRealmUpdateStatus.UPDATED; } + + List<String> realmNames = realm.getRealmNames(); + String currRealmName = realmNames.isEmpty() ? null : realmNames.get(0); // currently there is only one name. - if (StringUtils.isBlank(currRealmName) && StringUtils.isNotBlank(realmName)) { + // Update realm name if: + // Current realm name is empty + // The passed in realm name is not empty + // The address is not a special windows address + if (StringUtils.isBlank(currRealmName) && StringUtils.isNotBlank(realmName) && + ((currRealmAddr == null || !currRealmAddr.equals(WindowsAccountUtils.SPECIAL_WINDOWS_REALM_ADDR)))) { updateRealmColumn(realm.getRealmId(), "realm_name", realmName, connection); updateStatusCode = OsRealmUpdateStatus.UPDATED; } diff --git a/bindings/java/src/org/sleuthkit/datamodel/Report.java b/bindings/java/src/org/sleuthkit/datamodel/Report.java index 51d6c930e75826befbcb324e33bd120d0f145d7a..6a080f7585c9a05662414df6d18e680d8e2b9b24 100644 --- a/bindings/java/src/org/sleuthkit/datamodel/Report.java +++ b/bindings/java/src/org/sleuthkit/datamodel/Report.java @@ -314,7 +314,7 @@ public BlackboardArtifact newArtifact(BlackboardArtifact.ARTIFACT_TYPE type) thr @Override public ArrayList<BlackboardArtifact> getArtifacts(String artifactTypeName) throws TskCoreException { - return getArtifacts(db.getArtifactType(artifactTypeName).getTypeID()); + return getArtifacts(db.getBlackboard().getArtifactType(artifactTypeName).getTypeID()); } @Override @@ -380,7 +380,7 @@ public Set<String> getHashSetNames() throws TskCoreException { @Override public long getArtifactsCount(String artifactTypeName) throws TskCoreException { - return getArtifactsCount(db.getArtifactType(artifactTypeName).getTypeID()); + return getArtifactsCount(db.getBlackboard().getArtifactType(artifactTypeName).getTypeID()); } @Override diff --git a/bindings/java/src/org/sleuthkit/datamodel/SleuthkitCase.java b/bindings/java/src/org/sleuthkit/datamodel/SleuthkitCase.java index 06658ee236441eaa9ec352bbe2bff1530dc1ea8f..ccd26de7b7f3ea2415dced80623a9b3a45de261f 100644 --- a/bindings/java/src/org/sleuthkit/datamodel/SleuthkitCase.java +++ b/bindings/java/src/org/sleuthkit/datamodel/SleuthkitCase.java @@ -51,6 +51,7 @@ import java.util.Arrays; import java.util.Collection; import java.util.Collections; +import java.util.concurrent.atomic.AtomicInteger; import java.util.Date; import java.util.EnumMap; import java.util.HashMap; @@ -72,6 +73,7 @@ import java.util.stream.Collectors; import org.apache.commons.lang3.StringUtils; import org.postgresql.util.PSQLState; +import org.sleuthkit.datamodel.Blackboard.BlackboardException; import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE; import org.sleuthkit.datamodel.BlackboardArtifact.Category; import org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE; @@ -105,7 +107,7 @@ public class SleuthkitCase { * tsk/auto/tsk_db.h. */ static final CaseDbSchemaVersionNumber CURRENT_DB_SCHEMA_VERSION - = new CaseDbSchemaVersionNumber(9, 1); + = new CaseDbSchemaVersionNumber(9, 2); private static final long BASE_ARTIFACT_ID = Long.MIN_VALUE; // Artifact ids will start at the lowest negative value private static final Logger logger = Logger.getLogger(SleuthkitCase.class.getName()); @@ -117,7 +119,6 @@ public class SleuthkitCase { private static final String SQL_ERROR_RESOURCE_GROUP = "53"; private static final String SQL_ERROR_LIMIT_GROUP = "54"; private static final String SQL_ERROR_INTERNAL_GROUP = "xx"; - private static final int MIN_USER_DEFINED_TYPE_ID = 10000; private static final Set<String> CORE_TABLE_NAMES = ImmutableSet.of( "tsk_events", @@ -184,7 +185,8 @@ public class SleuthkitCase { private final ConnectionPool connections; private final Object carvedFileDirsLock = new Object(); - private final Map<Long, VirtualDirectory> rootIdsToCarvedFileDirs = new HashMap<>(); + private final static int MAX_CARVED_FILES_PER_FOLDER = 2000; + private final Map<Long, CarvedFileDirInfo> rootIdsToCarvedFileDirs = new HashMap<>(); private final Map<Long, FileSystem> fileSystemIdMap = new HashMap<>(); // Cache for file system files. private final List<ErrorObserver> sleuthkitCaseErrorObservers = new ArrayList<>(); private final String databaseName; @@ -194,10 +196,7 @@ public class SleuthkitCase { private SleuthkitJNI.CaseDbHandle caseHandle; private final String caseHandleIdentifier; // Used to identify this case in the JNI cache. private String dbBackupPath; - private Map<Integer, BlackboardArtifact.Type> typeIdToArtifactTypeMap; - private Map<Integer, BlackboardAttribute.Type> typeIdToAttributeTypeMap; - private Map<String, BlackboardArtifact.Type> typeNameToArtifactTypeMap; - private Map<String, BlackboardAttribute.Type> typeNameToAttributeTypeMap; + private CaseDbSchemaVersionNumber caseDBSchemaCreationVersion; // Objects for caching the result of isRootDirectory(). Lock is for visibility only. @@ -380,21 +379,12 @@ private SleuthkitCase(String host, int port, String dbName, String userName, Str } private void init() throws Exception { - typeIdToArtifactTypeMap = new ConcurrentHashMap<>(); - typeIdToAttributeTypeMap = new ConcurrentHashMap<>(); - typeNameToArtifactTypeMap = new ConcurrentHashMap<>(); - typeNameToAttributeTypeMap = new ConcurrentHashMap<>(); - - /* - * The database schema must be updated before loading blackboard - * artifact/attribute types - */ + blackboard = new Blackboard(this); updateDatabaseSchema(null); - initBlackboardArtifactTypes(); - initBlackboardAttributeTypes(); - initNextArtifactId(); - try (CaseDbConnection connection = connections.getConnection()) { + blackboard.initBlackboardArtifactTypes(connection); + blackboard.initBlackboardAttributeTypes(connection); + initNextArtifactId(connection); initIngestModuleTypes(connection); initIngestStatusTypes(connection); initReviewStatuses(connection); @@ -404,7 +394,6 @@ private void init() throws Exception { initDBSchemaCreationVersion(connection); } - blackboard = new Blackboard(this); fileManager = new FileManager(this); communicationsMgr = new CommunicationsManager(this); timelineMgr = new TimelineManager(this); @@ -424,7 +413,7 @@ private void init() throws Exception { * @return set of core table names */ static Set<String> getCoreTableNames() { - return CORE_TABLE_NAMES; + return Collections.unmodifiableSet(CORE_TABLE_NAMES); } /** @@ -433,7 +422,7 @@ static Set<String> getCoreTableNames() { * @return set of core index names */ static Set<String> getCoreIndexNames() { - return CORE_INDEX_NAMES; + return Collections.unmodifiableSet(CORE_INDEX_NAMES); } /** @@ -496,10 +485,10 @@ public CommunicationsManager getCommunicationsManager() throws TskCoreException public Blackboard getBlackboard() { return blackboard; } - + /** * Gets the file manager for this case. - * + * * @return The per case FileManager object. */ public FileManager getFileManager() { @@ -604,99 +593,24 @@ public HostAddressManager getHostAddressManager() throws TskCoreException { } /** - * Make sure the predefined artifact types are in the artifact types table. - * - * @throws SQLException - * @throws TskCoreException - */ - private void initBlackboardArtifactTypes() throws SQLException, TskCoreException { - acquireSingleUserCaseWriteLock(); - try (CaseDbConnection connection = connections.getConnection(); - Statement statement = connection.createStatement();) { - for (ARTIFACT_TYPE type : ARTIFACT_TYPE.values()) { - try { - statement.execute("INSERT INTO blackboard_artifact_types (artifact_type_id, type_name, display_name, category_type) VALUES (" + type.getTypeID() + " , '" + type.getLabel() + "', '" + type.getDisplayName() + "' , " + type.getCategory().getID() + ")"); //NON-NLS - } catch (SQLException ex) { - try (ResultSet resultSet = connection.executeQuery(statement, "SELECT COUNT(*) AS count FROM blackboard_artifact_types WHERE artifact_type_id = '" + type.getTypeID() + "'")) { //NON-NLS - resultSet.next(); - if (resultSet.getLong("count") == 0) { - throw ex; - } - } - } - this.typeIdToArtifactTypeMap.put(type.getTypeID(), new BlackboardArtifact.Type(type)); - this.typeNameToArtifactTypeMap.put(type.getLabel(), new BlackboardArtifact.Type(type)); - } - if (dbType == DbType.POSTGRESQL) { - int newPrimaryKeyIndex = Collections.max(Arrays.asList(ARTIFACT_TYPE.values())).getTypeID() + 1; - statement.execute("ALTER SEQUENCE blackboard_artifact_types_artifact_type_id_seq RESTART WITH " + newPrimaryKeyIndex); //NON-NLS - } - } finally { - releaseSingleUserCaseWriteLock(); - } - } - - /** - * Make sure the predefined artifact attribute types are in the artifact - * attribute types table. - * - * @throws SQLException - * @throws TskCoreException - */ - private void initBlackboardAttributeTypes() throws SQLException, TskCoreException { - acquireSingleUserCaseWriteLock(); - try (CaseDbConnection connection = connections.getConnection(); - Statement statement = connection.createStatement();) { - for (ATTRIBUTE_TYPE type : ATTRIBUTE_TYPE.values()) { - try { - statement.execute("INSERT INTO blackboard_attribute_types (attribute_type_id, type_name, display_name, value_type) VALUES (" + type.getTypeID() + ", '" + type.getLabel() + "', '" + type.getDisplayName() + "', '" + type.getValueType().getType() + "')"); //NON-NLS - } catch (SQLException ex) { - try (ResultSet resultSet = connection.executeQuery(statement, "SELECT COUNT(*) AS count FROM blackboard_attribute_types WHERE attribute_type_id = '" + type.getTypeID() + "'")) { //NON-NLS - resultSet.next(); - if (resultSet.getLong("count") == 0) { - throw ex; - } - } - } - this.typeIdToAttributeTypeMap.put(type.getTypeID(), new BlackboardAttribute.Type(type)); - this.typeNameToAttributeTypeMap.put(type.getLabel(), new BlackboardAttribute.Type(type)); - } - if (this.dbType == DbType.POSTGRESQL) { - int newPrimaryKeyIndex = Collections.max(Arrays.asList(ATTRIBUTE_TYPE.values())).getTypeID() + 1; - statement.execute("ALTER SEQUENCE blackboard_attribute_types_attribute_type_id_seq RESTART WITH " + newPrimaryKeyIndex); //NON-NLS - } - } finally { - releaseSingleUserCaseWriteLock(); - } - } - - /** - * Initialize the next artifact id. If there are entries in the + * Initializes the next artifact id. If there are entries in the * blackboard_artifacts table we will use max(artifact_id) + 1 otherwise we * will initialize the value to 0x8000000000000000 (the maximum negative * signed long). * - * @throws SQLException - * @throws TskCoreException + * @throws SQLException Thrown if there is an error querying the + * blackboard_artifacts table. */ - private void initNextArtifactId() throws SQLException, TskCoreException { - CaseDbConnection connection = null; - Statement statement = null; - ResultSet resultSet = null; + private void initNextArtifactId(CaseDbConnection connection) throws SQLException { acquireSingleUserCaseReadLock(); - try { - connection = connections.getConnection(); - statement = connection.createStatement(); - resultSet = connection.executeQuery(statement, "SELECT MAX(artifact_id) AS max_artifact_id FROM blackboard_artifacts"); //NON-NLS + try (Statement statement = connection.createStatement()) { + ResultSet resultSet = connection.executeQuery(statement, "SELECT MAX(artifact_id) AS max_artifact_id FROM blackboard_artifacts"); //NON-NLS resultSet.next(); - this.nextArtifactId = resultSet.getLong("max_artifact_id") + 1; - if (this.nextArtifactId == 1) { - this.nextArtifactId = BASE_ARTIFACT_ID; + nextArtifactId = resultSet.getLong("max_artifact_id") + 1; + if (nextArtifactId == 1) { + nextArtifactId = BASE_ARTIFACT_ID; } } finally { - closeResultSet(resultSet); - closeStatement(statement); - closeConnection(connection); releaseSingleUserCaseReadLock(); } } @@ -1006,6 +920,7 @@ private void updateDatabaseSchema(String dbPath) throws Exception { dbSchemaVersion = updateFromSchema8dot5toSchema8dot6(dbSchemaVersion, connection); dbSchemaVersion = updateFromSchema8dot6toSchema9dot0(dbSchemaVersion, connection); dbSchemaVersion = updateFromSchema9dot0toSchema9dot1(dbSchemaVersion, connection); + dbSchemaVersion = updateFromSchema9dot1toSchema9dot2(dbSchemaVersion, connection); statement = connection.createStatement(); connection.executeUpdate(statement, "UPDATE tsk_db_info SET schema_ver = " + dbSchemaVersion.getMajor() + ", schema_minor_ver = " + dbSchemaVersion.getMinor()); //NON-NLS @@ -1356,7 +1271,7 @@ private CaseDbSchemaVersionNumber updateFromSchema3toSchema4(CaseDbSchemaVersion while (resultSet.next()) { int attributeTypeId = resultSet.getInt("attribute_type_id"); String attributeLabel = resultSet.getString("type_name"); - if (attributeTypeId < MIN_USER_DEFINED_TYPE_ID) { + if (attributeTypeId < Blackboard.MIN_USER_DEFINED_TYPE_ID) { updateStatement.executeUpdate( "UPDATE blackboard_attribute_types " //NON-NLS + "SET value_type = " + ATTRIBUTE_TYPE.fromLabel(attributeLabel).getValueType().getType() + " " //NON-NLS @@ -2425,9 +2340,12 @@ private CaseDbSchemaVersionNumber updateFromSchema8dot6toSchema9dot0(CaseDbSchem statement.execute("CREATE TABLE tsk_analysis_results (artifact_obj_id " + bigIntDataType + " PRIMARY KEY, " + "conclusion TEXT, " + "significance INTEGER NOT NULL, " - /* method_category was a column in a little distributed version of 9.0. - * It was renamed to priority before public release. The 9.1 upgrade code - * will add the priority column. This is commented out since it was never used. */ + /* + * method_category was a column in a little distributed + * version of 9.0. It was renamed to priority before public + * release. The 9.1 upgrade code will add the priority + * column. This is commented out since it was never used. + */ // + "method_category INTEGER NOT NULL, " + "configuration TEXT, justification TEXT, " + "ignore_score INTEGER DEFAULT 0 " // boolean @@ -2643,7 +2561,6 @@ private CaseDbSchemaVersionNumber updateFromSchema9dot0toSchema9dot1(CaseDbSchem + "FOREIGN KEY(obj_id) REFERENCES tsk_objects(obj_id) ON DELETE CASCADE, " + "FOREIGN KEY(data_source_obj_id) REFERENCES tsk_objects(obj_id) ON DELETE CASCADE " + ")"); - // Copy the data statement.execute("INSERT INTO temp_tsk_analysis_results(artifact_obj_id, " @@ -2653,17 +2570,14 @@ private CaseDbSchemaVersionNumber updateFromSchema9dot0toSchema9dot1(CaseDbSchem + "data_source_obj_id, significance) " + "SELECT obj_id, data_source_obj_id, significance FROM tsk_aggregate_score"); - - // Drop the old tables statement.execute("DROP TABLE tsk_analysis_results"); statement.execute("DROP TABLE tsk_aggregate_score"); - // Rename the new tables statement.execute("ALTER TABLE temp_tsk_analysis_results RENAME TO tsk_analysis_results"); statement.execute("ALTER TABLE temp_tsk_aggregate_score RENAME TO tsk_aggregate_score"); - + } break; default: @@ -2672,12 +2586,12 @@ private CaseDbSchemaVersionNumber updateFromSchema9dot0toSchema9dot1(CaseDbSchem // add an index on tsk_file_attributes table. statement.execute("CREATE INDEX tsk_file_attributes_obj_id ON tsk_file_attributes(obj_id)"); - + statement.execute("ALTER TABLE tsk_analysis_results ADD COLUMN priority INTEGER NOT NULL DEFAULT " + Score.Priority.NORMAL.getId()); statement.execute("ALTER TABLE tsk_aggregate_score ADD COLUMN priority INTEGER NOT NULL DEFAULT " + Score.Priority.NORMAL.getId()); - + statement.execute("UPDATE blackboard_artifact_types SET category_type = 1 WHERE artifact_type_id = 16"); - + return new CaseDbSchemaVersionNumber(9, 1); } finally { closeResultSet(results); @@ -2686,6 +2600,70 @@ private CaseDbSchemaVersionNumber updateFromSchema9dot0toSchema9dot1(CaseDbSchem } } + /** + * Upgrade the database schema from 9.1 to 9.2 This upgrade includes: - + * modify the UNIQUE constraint on tsk_os_account_instances to include the + * instance_type column. + * + * @param schemaVersion Current schema version - must be 9.1 + * @param connection Database connection to use. + * + * @return New schema version + * + * @throws SQLException + * @throws TskCoreException + */ + private CaseDbSchemaVersionNumber updateFromSchema9dot1toSchema9dot2(CaseDbSchemaVersionNumber schemaVersion, CaseDbConnection connection) throws SQLException, TskCoreException { + if (schemaVersion.getMajor() != 9) { + return schemaVersion; + } + + if (schemaVersion.getMinor() != 1) { + return schemaVersion; + } + + Statement updateSchemaStatement = connection.createStatement(); + ResultSet results = null; + acquireSingleUserCaseWriteLock(); + try { + + String bigIntDataType = "BIGINT"; + String primaryKeyType = "BIGSERIAL"; + + if (this.dbType.equals(DbType.SQLITE)) { + bigIntDataType = "INTEGER"; + primaryKeyType = "INTEGER"; + } + + // In 9.2 we modified the UNIQUE constraint on tsk_os_account_instances to include instance_type column. + // Since SQLite does not allow to drop or alter constraints, we will create a new table, copy the data and delete the old table. + // Rename existing table + updateSchemaStatement.execute("ALTER TABLE tsk_os_account_instances RENAME TO old_tsk_os_account_instances"); + + // New table + updateSchemaStatement.execute("CREATE TABLE tsk_os_account_instances (id " + primaryKeyType + " PRIMARY KEY, " + + "os_account_obj_id " + bigIntDataType + " NOT NULL, " + + "data_source_obj_id " + bigIntDataType + " NOT NULL, " + + "instance_type INTEGER NOT NULL, " // PerformedActionOn/ReferencedOn + + "UNIQUE(os_account_obj_id, data_source_obj_id, instance_type), " + + "FOREIGN KEY(os_account_obj_id) REFERENCES tsk_os_accounts(os_account_obj_id) ON DELETE CASCADE, " + + "FOREIGN KEY(data_source_obj_id) REFERENCES tsk_objects(obj_id) ON DELETE CASCADE ) "); + + // Copy the data from old table, order by id preserves the primary key. + updateSchemaStatement.execute("INSERT INTO tsk_os_account_instances(os_account_obj_id, " + + "data_source_obj_id, instance_type) SELECT os_account_obj_id, data_source_obj_id, instance_type FROM old_tsk_os_account_instances ORDER BY id ASC"); + + // delete old table + updateSchemaStatement.execute("DROP TABLE old_tsk_os_account_instances"); + + return new CaseDbSchemaVersionNumber(9, 2); + } finally { + closeResultSet(results); + closeStatement(updateSchemaStatement); + releaseSingleUserCaseWriteLock(); + } + } + /** * Inserts a row for the given account type in account_types table, if one * doesn't exist. @@ -3486,9 +3464,14 @@ public DataSource getDataSource(long objectId) throws TskDataException, TskCoreE * @return list of blackboard artifacts. * * @throws TskCoreException + * + * @deprecated Use Blackboard.getArtifactsByType instead. */ + @Deprecated public ArrayList<BlackboardArtifact> getBlackboardArtifacts(int artifactTypeID) throws TskCoreException { - return getArtifactsHelper("blackboard_artifacts.artifact_type_id = " + artifactTypeID); + ArrayList<BlackboardArtifact> artifacts = new ArrayList<>(); + artifacts.addAll(blackboard.getArtifactsByType(blackboard.getArtifactType(artifactTypeID))); + return artifacts; } /** @@ -3507,7 +3490,7 @@ public long getBlackboardArtifactsCount(long objId) throws TskCoreException { acquireSingleUserCaseReadLock(); try { connection = connections.getConnection(); - + // SELECT COUNT(*) AS count FROM blackboard_artifacts WHERE obj_id = ? PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.COUNT_ARTIFACTS_FROM_SOURCE); statement.clearParameters(); @@ -3543,7 +3526,7 @@ public long getBlackboardArtifactsTypeCount(int artifactTypeID) throws TskCoreEx acquireSingleUserCaseReadLock(); try { connection = connections.getConnection(); - + // SELECT COUNT(*) AS count FROM blackboard_artifacts WHERE artifact_type_id = ? PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.COUNT_ARTIFACTS_OF_TYPE); statement.clearParameters(); @@ -3580,7 +3563,7 @@ public long getBlackboardArtifactsTypeCount(int artifactTypeID, long dataSourceI acquireSingleUserCaseReadLock(); try { connection = connections.getConnection(); - + // SELECT COUNT(*) AS count FROM blackboard_artifacts WHERE artifact_type_id = ? PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.COUNT_ARTIFACTS_OF_TYPE_BY_DATA_SOURCE); statement.clearParameters(); @@ -3614,39 +3597,47 @@ public long getBlackboardArtifactsTypeCount(int artifactTypeID, long dataSourceI * @throws TskCoreException exception thrown if a critical error occurred * within tsk core and artifacts could not be * queried + * + * @deprecated Do not use. */ + @Deprecated public List<BlackboardArtifact> getBlackboardArtifacts(BlackboardAttribute.ATTRIBUTE_TYPE attrType, String value) throws TskCoreException { - CaseDbConnection connection = null; - Statement s = null; - ResultSet rs = null; acquireSingleUserCaseReadLock(); - try { - connection = connections.getConnection(); - s = connection.createStatement(); - rs = connection.executeQuery(s, "SELECT DISTINCT arts.artifact_id AS artifact_id, " //NON-NLS - + "arts.obj_id AS obj_id, arts.artifact_obj_id AS artifact_obj_id, arts.data_source_obj_id AS data_source_obj_id, arts.artifact_type_id AS artifact_type_id, " - + "types.type_name AS type_name, types.display_name AS display_name, "//NON-NLS - + " arts.review_status_id AS review_status_id " //NON-NLS - + "FROM blackboard_artifacts AS arts, blackboard_attributes AS attrs, blackboard_artifact_types AS types " //NON-NLS - + "WHERE arts.artifact_id = attrs.artifact_id " //NON-NLS - + " AND attrs.attribute_type_id = " + attrType.getTypeID() //NON-NLS - + " AND attrs.value_text = '" + value + "'" - + " AND types.artifact_type_id=arts.artifact_type_id" - + " AND arts.review_status_id !=" + BlackboardArtifact.ReviewStatus.REJECTED.getID()); //NON-NLS - ArrayList<BlackboardArtifact> artifacts = new ArrayList<BlackboardArtifact>(); - while (rs.next()) { - artifacts.add(new BlackboardArtifact(this, rs.getLong("artifact_id"), rs.getLong("obj_id"), rs.getLong("artifact_obj_id"), - rs.getObject("data_source_obj_id") != null ? rs.getLong("data_source_obj_id") : null, - rs.getInt("artifact_type_id"), rs.getString("type_name"), rs.getString("display_name"), - BlackboardArtifact.ReviewStatus.withID(rs.getInt("review_status_id")))); + try (CaseDbConnection connection = connections.getConnection(); Statement statement = connection.createStatement(); + ResultSet resultSet = connection.executeQuery(statement, "SELECT DISTINCT arts.artifact_id AS artifact_id, " //NON-NLS + + "arts.obj_id AS obj_id, arts.artifact_obj_id AS artifact_obj_id, arts.data_source_obj_id AS data_source_obj_id, arts.artifact_type_id AS artifact_type_id, " + + "types.type_name AS type_name, types.display_name AS display_name, "//NON-NLS + + " arts.review_status_id AS review_status_id " //NON-NLS + + "FROM blackboard_artifacts AS arts, blackboard_attributes AS attrs, blackboard_artifact_types AS types " //NON-NLS + + "WHERE arts.artifact_id = attrs.artifact_id " //NON-NLS + + " AND attrs.attribute_type_id = " + attrType.getTypeID() //NON-NLS + + " AND attrs.value_text = '" + value + "'" + + " AND types.artifact_type_id=arts.artifact_type_id" + + " AND arts.review_status_id !=" + BlackboardArtifact.ReviewStatus.REJECTED.getID());) { //NON-NLS + + List<Long> analysisArtifactObjIds = new ArrayList<>(); + List<Long> dataArtifactObjIds = new ArrayList<>(); + while (resultSet.next()) { + BlackboardArtifact.Type type = blackboard.getArtifactType(resultSet.getInt("artifact_type_id")); + if (type.getCategory() == BlackboardArtifact.Category.ANALYSIS_RESULT) { + analysisArtifactObjIds.add(resultSet.getLong("artifact_obj_id")); + } else { + dataArtifactObjIds.add(resultSet.getLong("artifact_obj_id")); + } + } + + ArrayList<BlackboardArtifact> artifacts = new ArrayList<>(); + if (!analysisArtifactObjIds.isEmpty()) { + artifacts.addAll(getArtifactsForValues(BlackboardArtifact.Category.ANALYSIS_RESULT, "artifacts.artifact_obj_id", analysisArtifactObjIds, connection)); + } + + if (!dataArtifactObjIds.isEmpty()) { + artifacts.addAll(getArtifactsForValues(BlackboardArtifact.Category.DATA_ARTIFACT, "artifacts.artifact_obj_id", dataArtifactObjIds, connection)); } return artifacts; } catch (SQLException ex) { throw new TskCoreException("Error getting blackboard artifacts by attribute", ex); } finally { - closeResultSet(rs); - closeStatement(s); - closeConnection(connection); releaseSingleUserCaseReadLock(); } } @@ -3667,43 +3658,50 @@ public List<BlackboardArtifact> getBlackboardArtifacts(BlackboardAttribute.ATTRI * @throws TskCoreException exception thrown if a critical error occurred * within tsk core and artifacts could not be * queried + * @deprecated Do not use. */ + @Deprecated public List<BlackboardArtifact> getBlackboardArtifacts(BlackboardAttribute.ATTRIBUTE_TYPE attrType, String subString, boolean startsWith) throws TskCoreException { String valSubStr = "%" + subString; //NON-NLS if (startsWith == false) { valSubStr += "%"; //NON-NLS } - CaseDbConnection connection = null; - Statement s = null; - ResultSet rs = null; + acquireSingleUserCaseReadLock(); - try { - connection = connections.getConnection(); - s = connection.createStatement(); - rs = connection.executeQuery(s, "SELECT DISTINCT arts.artifact_id AS artifact_id, " //NON-NLS - + " arts.obj_id AS obj_id, arts.artifact_obj_id AS artifact_obj_id, arts.data_source_obj_id AS data_source_obj_id, arts.artifact_type_id AS artifact_type_id, " //NON-NLS - + " types.type_name AS type_name, types.display_name AS display_name, " //NON-NLS - + " arts.review_status_id AS review_status_id " //NON-NLS - + " FROM blackboard_artifacts AS arts, blackboard_attributes AS attrs, blackboard_artifact_types AS types " //NON-NLS - + " WHERE arts.artifact_id = attrs.artifact_id " //NON-NLS - + " AND attrs.attribute_type_id = " + attrType.getTypeID() //NON-NLS - + " AND LOWER(attrs.value_text) LIKE LOWER('" + valSubStr + "')" - + " AND types.artifact_type_id=arts.artifact_type_id " - + " AND arts.review_status_id !=" + BlackboardArtifact.ReviewStatus.REJECTED.getID()); - ArrayList<BlackboardArtifact> artifacts = new ArrayList<BlackboardArtifact>(); - while (rs.next()) { - artifacts.add(new BlackboardArtifact(this, rs.getLong("artifact_id"), rs.getLong("obj_id"), rs.getLong("artifact_obj_id"), - rs.getObject("data_source_obj_id") != null ? rs.getLong("data_source_obj_id") : null, - rs.getInt("artifact_type_id"), rs.getString("type_name"), rs.getString("display_name"), - BlackboardArtifact.ReviewStatus.withID(rs.getInt("review_status_id")))); + try (CaseDbConnection connection = connections.getConnection(); Statement statement = connection.createStatement(); + ResultSet resultSet = connection.executeQuery(statement, "SELECT DISTINCT arts.artifact_id AS artifact_id, " //NON-NLS + + " arts.obj_id AS obj_id, arts.artifact_obj_id AS artifact_obj_id, arts.data_source_obj_id AS data_source_obj_id, arts.artifact_type_id AS artifact_type_id, " //NON-NLS + + " types.type_name AS type_name, types.display_name AS display_name, " //NON-NLS + + " arts.review_status_id AS review_status_id " //NON-NLS + + " FROM blackboard_artifacts AS arts, blackboard_attributes AS attrs, blackboard_artifact_types AS types " //NON-NLS + + " WHERE arts.artifact_id = attrs.artifact_id " //NON-NLS + + " AND attrs.attribute_type_id = " + attrType.getTypeID() //NON-NLS + + " AND LOWER(attrs.value_text) LIKE LOWER('" + valSubStr + "')" + + " AND types.artifact_type_id=arts.artifact_type_id " + + " AND arts.review_status_id !=" + BlackboardArtifact.ReviewStatus.REJECTED.getID());) { + List<Long> analysisArtifactObjIds = new ArrayList<>(); + List<Long> dataArtifactObjIds = new ArrayList<>(); + while (resultSet.next()) { + BlackboardArtifact.Type type = blackboard.getArtifactType(resultSet.getInt("artifact_type_id")); + if (type.getCategory() == BlackboardArtifact.Category.ANALYSIS_RESULT) { + analysisArtifactObjIds.add(resultSet.getLong("artifact_obj_id")); + } else { + dataArtifactObjIds.add(resultSet.getLong("artifact_obj_id")); + } + } + + ArrayList<BlackboardArtifact> artifacts = new ArrayList<>(); + if (!analysisArtifactObjIds.isEmpty()) { + artifacts.addAll(getArtifactsForValues(BlackboardArtifact.Category.ANALYSIS_RESULT, "artifacts.artifact_obj_id", analysisArtifactObjIds, connection)); + } + + if (!dataArtifactObjIds.isEmpty()) { + artifacts.addAll(getArtifactsForValues(BlackboardArtifact.Category.DATA_ARTIFACT, "artifacts.artifact_obj_id", dataArtifactObjIds, connection)); } return artifacts; } catch (SQLException ex) { throw new TskCoreException("Error getting blackboard artifacts by attribute. " + ex.getMessage(), ex); } finally { - closeResultSet(rs); - closeStatement(s); - closeConnection(connection); releaseSingleUserCaseReadLock(); } } @@ -3721,39 +3719,45 @@ public List<BlackboardArtifact> getBlackboardArtifacts(BlackboardAttribute.ATTRI * @throws TskCoreException exception thrown if a critical error occurred * within tsk core and artifacts could not be * queried + * @deprecated Do not use. */ + @Deprecated public List<BlackboardArtifact> getBlackboardArtifacts(BlackboardAttribute.ATTRIBUTE_TYPE attrType, int value) throws TskCoreException { - CaseDbConnection connection = null; - Statement s = null; - ResultSet rs = null; acquireSingleUserCaseReadLock(); - try { - connection = connections.getConnection(); - s = connection.createStatement(); - rs = connection.executeQuery(s, "SELECT DISTINCT arts.artifact_id AS artifact_id, " //NON-NLS - + " arts.obj_id AS obj_id, arts.artifact_obj_id AS artifact_obj_id, arts.data_source_obj_id AS data_source_obj_id, arts.artifact_type_id AS artifact_type_id, " - + " types.type_name AS type_name, types.display_name AS display_name, " - + " arts.review_status_id AS review_status_id "//NON-NLS - + " FROM blackboard_artifacts AS arts, blackboard_attributes AS attrs, blackboard_artifact_types AS types " //NON-NLS - + "WHERE arts.artifact_id = attrs.artifact_id " //NON-NLS - + " AND attrs.attribute_type_id = " + attrType.getTypeID() //NON-NLS - + " AND attrs.value_int32 = " + value //NON-NLS - + " AND types.artifact_type_id=arts.artifact_type_id " - + " AND arts.review_status_id !=" + BlackboardArtifact.ReviewStatus.REJECTED.getID()); - ArrayList<BlackboardArtifact> artifacts = new ArrayList<BlackboardArtifact>(); - while (rs.next()) { - artifacts.add(new BlackboardArtifact(this, rs.getLong("artifact_id"), rs.getLong("obj_id"), rs.getLong("artifact_obj_id"), - rs.getObject("data_source_obj_id") != null ? rs.getLong("data_source_obj_id") : null, - rs.getInt("artifact_type_id"), rs.getString("type_name"), rs.getString("display_name"), - BlackboardArtifact.ReviewStatus.withID(rs.getInt("review_status_id")))); + try (CaseDbConnection connection = connections.getConnection(); Statement statement = connection.createStatement(); + ResultSet resultSet = connection.executeQuery(statement, "SELECT DISTINCT arts.artifact_id AS artifact_id, " //NON-NLS + + " arts.obj_id AS obj_id, arts.artifact_obj_id AS artifact_obj_id, arts.data_source_obj_id AS data_source_obj_id, arts.artifact_type_id AS artifact_type_id, " + + " types.type_name AS type_name, types.display_name AS display_name, " + + " arts.review_status_id AS review_status_id "//NON-NLS + + " FROM blackboard_artifacts AS arts, blackboard_attributes AS attrs, blackboard_artifact_types AS types " //NON-NLS + + "WHERE arts.artifact_id = attrs.artifact_id " //NON-NLS + + " AND attrs.attribute_type_id = " + attrType.getTypeID() //NON-NLS + + " AND attrs.value_int32 = " + value //NON-NLS + + " AND types.artifact_type_id=arts.artifact_type_id " + + " AND arts.review_status_id !=" + BlackboardArtifact.ReviewStatus.REJECTED.getID());) { + List<Long> analysisArtifactObjIds = new ArrayList<>(); + List<Long> dataArtifactObjIds = new ArrayList<>(); + while (resultSet.next()) { + BlackboardArtifact.Type type = blackboard.getArtifactType(resultSet.getInt("artifact_type_id")); + if (type.getCategory() == BlackboardArtifact.Category.ANALYSIS_RESULT) { + analysisArtifactObjIds.add(resultSet.getLong("artifact_obj_id")); + } else { + dataArtifactObjIds.add(resultSet.getLong("artifact_obj_id")); + } + } + + ArrayList<BlackboardArtifact> artifacts = new ArrayList<>(); + if (!analysisArtifactObjIds.isEmpty()) { + artifacts.addAll(getArtifactsForValues(BlackboardArtifact.Category.ANALYSIS_RESULT, "artifacts.artifact_obj_id", analysisArtifactObjIds, connection)); + } + + if (!dataArtifactObjIds.isEmpty()) { + artifacts.addAll(getArtifactsForValues(BlackboardArtifact.Category.DATA_ARTIFACT, "artifacts.artifact_obj_id", dataArtifactObjIds, connection)); } return artifacts; } catch (SQLException ex) { throw new TskCoreException("Error getting blackboard artifacts by attribute", ex); } finally { - closeResultSet(rs); - closeStatement(s); - closeConnection(connection); releaseSingleUserCaseReadLock(); } } @@ -3771,39 +3775,46 @@ public List<BlackboardArtifact> getBlackboardArtifacts(BlackboardAttribute.ATTRI * @throws TskCoreException exception thrown if a critical error occurred * within tsk core and artifacts could not be * queried + * + * @deprecated Do not use. */ + @Deprecated public List<BlackboardArtifact> getBlackboardArtifacts(BlackboardAttribute.ATTRIBUTE_TYPE attrType, long value) throws TskCoreException { - CaseDbConnection connection = null; - Statement s = null; - ResultSet rs = null; acquireSingleUserCaseReadLock(); - try { - connection = connections.getConnection(); - s = connection.createStatement(); - rs = connection.executeQuery(s, "SELECT DISTINCT arts.artifact_id AS artifact_id, " //NON-NLS - + " arts.obj_id AS obj_id, arts.artifact_obj_id AS artifact_obj_id, arts.data_source_obj_id AS data_source_obj_id, arts.artifact_type_id AS artifact_type_id, " - + " types.type_name AS type_name, types.display_name AS display_name, " - + " arts.review_status_id AS review_status_id "//NON-NLS - + " FROM blackboard_artifacts AS arts, blackboard_attributes AS attrs, blackboard_artifact_types AS types " //NON-NLS - + " WHERE arts.artifact_id = attrs.artifact_id " //NON-NLS - + " AND attrs.attribute_type_id = " + attrType.getTypeID() //NON-NLS - + " AND attrs.value_int64 = " + value //NON-NLS - + " AND types.artifact_type_id=arts.artifact_type_id " - + " AND arts.review_status_id !=" + BlackboardArtifact.ReviewStatus.REJECTED.getID()); - ArrayList<BlackboardArtifact> artifacts = new ArrayList<BlackboardArtifact>(); - while (rs.next()) { - artifacts.add(new BlackboardArtifact(this, rs.getLong("artifact_id"), rs.getLong("obj_id"), rs.getLong("artifact_obj_id"), - rs.getObject("data_source_obj_id") != null ? rs.getLong("data_source_obj_id") : null, - rs.getInt("artifact_type_id"), rs.getString("type_name"), rs.getString("display_name"), - BlackboardArtifact.ReviewStatus.withID(rs.getInt("review_status_id")))); + try (CaseDbConnection connection = connections.getConnection(); Statement statement = connection.createStatement(); + ResultSet resultSet = connection.executeQuery(statement, "SELECT DISTINCT arts.artifact_id AS artifact_id, " //NON-NLS + + " arts.obj_id AS obj_id, arts.artifact_obj_id AS artifact_obj_id, arts.data_source_obj_id AS data_source_obj_id, arts.artifact_type_id AS artifact_type_id, " + + " types.type_name AS type_name, types.display_name AS display_name, " + + " arts.review_status_id AS review_status_id "//NON-NLS + + " FROM blackboard_artifacts AS arts, blackboard_attributes AS attrs, blackboard_artifact_types AS types " //NON-NLS + + " WHERE arts.artifact_id = attrs.artifact_id " //NON-NLS + + " AND attrs.attribute_type_id = " + attrType.getTypeID() //NON-NLS + + " AND attrs.value_int64 = " + value //NON-NLS + + " AND types.artifact_type_id=arts.artifact_type_id " + + " AND arts.review_status_id !=" + BlackboardArtifact.ReviewStatus.REJECTED.getID());) { + List<Long> analysisArtifactObjIds = new ArrayList<>(); + List<Long> dataArtifactObjIds = new ArrayList<>(); + while (resultSet.next()) { + BlackboardArtifact.Type type = blackboard.getArtifactType(resultSet.getInt("artifact_type_id")); + if (type.getCategory() == BlackboardArtifact.Category.ANALYSIS_RESULT) { + analysisArtifactObjIds.add(resultSet.getLong("artifact_obj_id")); + } else { + dataArtifactObjIds.add(resultSet.getLong("artifact_obj_id")); + } + } + + ArrayList<BlackboardArtifact> artifacts = new ArrayList<>(); + if (!analysisArtifactObjIds.isEmpty()) { + artifacts.addAll(getArtifactsForValues(BlackboardArtifact.Category.ANALYSIS_RESULT, "artifacts.artifact_obj_id", analysisArtifactObjIds, connection)); + } + + if (!dataArtifactObjIds.isEmpty()) { + artifacts.addAll(getArtifactsForValues(BlackboardArtifact.Category.DATA_ARTIFACT, "artifacts.artifact_obj_id", dataArtifactObjIds, connection)); } return artifacts; } catch (SQLException ex) { throw new TskCoreException("Error getting blackboard artifacts by attribute. " + ex.getMessage(), ex); } finally { - closeResultSet(rs); - closeStatement(s); - closeConnection(connection); releaseSingleUserCaseReadLock(); } } @@ -3821,39 +3832,46 @@ public List<BlackboardArtifact> getBlackboardArtifacts(BlackboardAttribute.ATTRI * @throws TskCoreException exception thrown if a critical error occurred * within tsk core and artifacts could not be * queried + * + * @deprecated Do not use. */ + @Deprecated public List<BlackboardArtifact> getBlackboardArtifacts(BlackboardAttribute.ATTRIBUTE_TYPE attrType, double value) throws TskCoreException { - CaseDbConnection connection = null; - Statement s = null; - ResultSet rs = null; acquireSingleUserCaseReadLock(); - try { - connection = connections.getConnection(); - s = connection.createStatement(); - rs = connection.executeQuery(s, "SELECT DISTINCT arts.artifact_id AS artifact_id, " //NON-NLS - + " arts.obj_id AS obj_id, arts.artifact_obj_id AS artifact_obj_id, arts.data_source_obj_id AS data_source_obj_id, arts.artifact_type_id AS artifact_type_id, " - + " types.type_name AS type_name, types.display_name AS display_name, " - + " arts.review_status_id AS review_status_id "//NON-NLS - + " FROM blackboard_artifacts AS arts, blackboard_attributes AS attrs, blackboard_artifact_types AS types " //NON-NLS - + " WHERE arts.artifact_id = attrs.artifact_id " //NON-NLS - + " AND attrs.attribute_type_id = " + attrType.getTypeID() //NON-NLS - + " AND attrs.value_double = " + value //NON-NLS - + " AND types.artifact_type_id=arts.artifact_type_id " - + " AND arts.review_status_id !=" + BlackboardArtifact.ReviewStatus.REJECTED.getID()); - ArrayList<BlackboardArtifact> artifacts = new ArrayList<BlackboardArtifact>(); - while (rs.next()) { - artifacts.add(new BlackboardArtifact(this, rs.getLong("artifact_id"), rs.getLong("obj_id"), rs.getLong("artifact_obj_id"), - rs.getObject("data_source_obj_id") != null ? rs.getLong("data_source_obj_id") : null, - rs.getInt("artifact_type_id"), rs.getString("type_name"), rs.getString("display_name"), - BlackboardArtifact.ReviewStatus.withID(rs.getInt("review_status_id")))); + try (CaseDbConnection connection = connections.getConnection(); Statement statement = connection.createStatement(); + ResultSet resultSet = connection.executeQuery(statement, "SELECT DISTINCT arts.artifact_id AS artifact_id, " //NON-NLS + + " arts.obj_id AS obj_id, arts.artifact_obj_id AS artifact_obj_id, arts.data_source_obj_id AS data_source_obj_id, arts.artifact_type_id AS artifact_type_id, " + + " types.type_name AS type_name, types.display_name AS display_name, " + + " arts.review_status_id AS review_status_id "//NON-NLS + + " FROM blackboard_artifacts AS arts, blackboard_attributes AS attrs, blackboard_artifact_types AS types " //NON-NLS + + " WHERE arts.artifact_id = attrs.artifact_id " //NON-NLS + + " AND attrs.attribute_type_id = " + attrType.getTypeID() //NON-NLS + + " AND attrs.value_double = " + value //NON-NLS + + " AND types.artifact_type_id=arts.artifact_type_id " + + " AND arts.review_status_id !=" + BlackboardArtifact.ReviewStatus.REJECTED.getID());) { + List<Long> analysisArtifactObjIds = new ArrayList<>(); + List<Long> dataArtifactObjIds = new ArrayList<>(); + while (resultSet.next()) { + BlackboardArtifact.Type type = blackboard.getArtifactType(resultSet.getInt("artifact_type_id")); + if (type.getCategory() == BlackboardArtifact.Category.ANALYSIS_RESULT) { + analysisArtifactObjIds.add(resultSet.getLong("artifact_obj_id")); + } else { + dataArtifactObjIds.add(resultSet.getLong("artifact_obj_id")); + } + } + + ArrayList<BlackboardArtifact> artifacts = new ArrayList<>(); + if (!analysisArtifactObjIds.isEmpty()) { + artifacts.addAll(getArtifactsForValues(BlackboardArtifact.Category.ANALYSIS_RESULT, "artifacts.artifact_obj_id", analysisArtifactObjIds, connection)); + } + + if (!dataArtifactObjIds.isEmpty()) { + artifacts.addAll(getArtifactsForValues(BlackboardArtifact.Category.DATA_ARTIFACT, "artifacts.artifact_obj_id", dataArtifactObjIds, connection)); } return artifacts; } catch (SQLException ex) { throw new TskCoreException("Error getting blackboard artifacts by attribute", ex); } finally { - closeResultSet(rs); - closeStatement(s); - closeConnection(connection); releaseSingleUserCaseReadLock(); } } @@ -3871,39 +3889,47 @@ public List<BlackboardArtifact> getBlackboardArtifacts(BlackboardAttribute.ATTRI * @throws TskCoreException exception thrown if a critical error occurred * within tsk core and artifacts could not be * queried + * + * @deprecated Do not use. */ + @Deprecated public List<BlackboardArtifact> getBlackboardArtifacts(BlackboardAttribute.ATTRIBUTE_TYPE attrType, byte value) throws TskCoreException { - CaseDbConnection connection = null; - Statement s = null; - ResultSet rs = null; + acquireSingleUserCaseReadLock(); - try { - connection = connections.getConnection(); - s = connection.createStatement(); - rs = connection.executeQuery(s, "SELECT DISTINCT arts.artifact_id AS artifact_id, " //NON-NLS - + " arts.obj_id AS obj_id, arts.artifact_obj_id AS artifact_obj_id, arts.data_source_obj_id AS data_source_obj_id, arts.artifact_type_id AS artifact_type_id, " - + " types.type_name AS type_name, types.display_name AS display_name, " - + " arts.review_status_id AS review_status_id "//NON-NLS - + " FROM blackboard_artifacts AS arts, blackboard_attributes AS attrs, blackboard_artifact_types AS types " //NON-NLS - + " WHERE arts.artifact_id = attrs.artifact_id " //NON-NLS - + " AND attrs.attribute_type_id = " + attrType.getTypeID() //NON-NLS - + " AND attrs.value_byte = " + value //NON-NLS - + " AND types.artifact_type_id=arts.artifact_type_id " - + " AND arts.review_status_id !=" + BlackboardArtifact.ReviewStatus.REJECTED.getID()); - ArrayList<BlackboardArtifact> artifacts = new ArrayList<BlackboardArtifact>(); - while (rs.next()) { - artifacts.add(new BlackboardArtifact(this, rs.getLong("artifact_id"), rs.getLong("obj_id"), rs.getLong("artifact_obj_id"), - rs.getObject("data_source_obj_id") != null ? rs.getLong("data_source_obj_id") : null, - rs.getInt("artifact_type_id"), rs.getString("type_name"), rs.getString("display_name"), - BlackboardArtifact.ReviewStatus.withID(rs.getInt("review_status_id")))); + try (CaseDbConnection connection = connections.getConnection(); Statement statement = connection.createStatement(); + ResultSet resultSet = connection.executeQuery(statement, "SELECT DISTINCT arts.artifact_id AS artifact_id, " //NON-NLS + + " arts.obj_id AS obj_id, arts.artifact_obj_id AS artifact_obj_id, arts.data_source_obj_id AS data_source_obj_id, arts.artifact_type_id AS artifact_type_id, " + + " types.type_name AS type_name, types.display_name AS display_name, " + + " arts.review_status_id AS review_status_id "//NON-NLS + + " FROM blackboard_artifacts AS arts, blackboard_attributes AS attrs, blackboard_artifact_types AS types " //NON-NLS + + " WHERE arts.artifact_id = attrs.artifact_id " //NON-NLS + + " AND attrs.attribute_type_id = " + attrType.getTypeID() //NON-NLS + + " AND attrs.value_byte = " + value //NON-NLS + + " AND types.artifact_type_id=arts.artifact_type_id " + + " AND arts.review_status_id !=" + BlackboardArtifact.ReviewStatus.REJECTED.getID());) { + List<Long> analysisArtifactObjIds = new ArrayList<>(); + List<Long> dataArtifactObjIds = new ArrayList<>(); + while (resultSet.next()) { + BlackboardArtifact.Type type = blackboard.getArtifactType(resultSet.getInt("artifact_type_id")); + if (type.getCategory() == BlackboardArtifact.Category.ANALYSIS_RESULT) { + analysisArtifactObjIds.add(resultSet.getLong("artifact_obj_id")); + } else { + dataArtifactObjIds.add(resultSet.getLong("artifact_obj_id")); + } + } + + ArrayList<BlackboardArtifact> artifacts = new ArrayList<>(); + if (!analysisArtifactObjIds.isEmpty()) { + artifacts.addAll(getArtifactsForValues(BlackboardArtifact.Category.ANALYSIS_RESULT, "artifacts.artifact_obj_id", analysisArtifactObjIds, connection)); + } + + if (!dataArtifactObjIds.isEmpty()) { + artifacts.addAll(getArtifactsForValues(BlackboardArtifact.Category.DATA_ARTIFACT, "artifacts.artifact_obj_id", dataArtifactObjIds, connection)); } return artifacts; } catch (SQLException ex) { throw new TskCoreException("Error getting blackboard artifacts by attribute", ex); } finally { - closeResultSet(rs); - closeStatement(s); - closeConnection(connection); releaseSingleUserCaseReadLock(); } } @@ -3927,7 +3953,7 @@ public Iterable<BlackboardArtifact.Type> getArtifactTypes() throws TskCoreExcept ArrayList<BlackboardArtifact.Type> artifactTypes = new ArrayList<BlackboardArtifact.Type>(); while (rs.next()) { artifactTypes.add(new BlackboardArtifact.Type(rs.getInt("artifact_type_id"), - rs.getString("type_name"), rs.getString("display_name"), + rs.getString("type_name"), rs.getString("display_name"), BlackboardArtifact.Category.fromID(rs.getInt("category_type")))); } return artifactTypes; @@ -4011,7 +4037,7 @@ public List<BlackboardArtifact.Type> getArtifactTypesInUse() throws TskCoreExcep List<BlackboardArtifact.Type> uniqueArtifactTypes = new ArrayList<BlackboardArtifact.Type>(); while (rs.next()) { uniqueArtifactTypes.add(new BlackboardArtifact.Type(rs.getInt("artifact_type_id"), - rs.getString("type_name"), rs.getString("display_name"), + rs.getString("type_name"), rs.getString("display_name"), BlackboardArtifact.Category.fromID(rs.getInt("category_type")))); } return uniqueArtifactTypes; @@ -4092,57 +4118,6 @@ public int getBlackboardAttributeTypesCount() throws TskCoreException { } } - /** - * Gets unrejected blackboard artifacts that match a given WHERE clause. - * Uses a SELECT * statement that does a join of the blackboard_artifacts - * and blackboard_artifact_types tables to get all of the required data. - * - * @param whereClause The WHERE clause to append to the SELECT statement. - * - * @return A list of BlackboardArtifact objects. - * - * @throws TskCoreException If there is a problem querying the case - * database. - */ - ArrayList<BlackboardArtifact> getArtifactsHelper(String whereClause) throws TskCoreException { - CaseDbConnection connection = null; - Statement statement = null; - ResultSet rs = null; - acquireSingleUserCaseReadLock(); - try { - connection = connections.getConnection(); - statement = connection.createStatement(); - String query = "SELECT blackboard_artifacts.artifact_id AS artifact_id, " - + "blackboard_artifacts.obj_id AS obj_id, " - + "blackboard_artifacts.artifact_obj_id AS artifact_obj_id, " - + "blackboard_artifacts.data_source_obj_id AS data_source_obj_id, " - + "blackboard_artifact_types.artifact_type_id AS artifact_type_id, " - + "blackboard_artifact_types.type_name AS type_name, " - + "blackboard_artifact_types.display_name AS display_name, " - + "blackboard_artifacts.review_status_id AS review_status_id " - + "FROM blackboard_artifacts, blackboard_artifact_types " - + "WHERE blackboard_artifacts.artifact_type_id = blackboard_artifact_types.artifact_type_id " - + " AND blackboard_artifacts.review_status_id !=" + BlackboardArtifact.ReviewStatus.REJECTED.getID() - + " AND " + whereClause; - rs = connection.executeQuery(statement, query); - ArrayList<BlackboardArtifact> artifacts = new ArrayList<BlackboardArtifact>(); - while (rs.next()) { - artifacts.add(new BlackboardArtifact(this, rs.getLong("artifact_id"), rs.getLong("obj_id"), rs.getLong("artifact_obj_id"), - rs.getObject("data_source_obj_id") != null ? rs.getLong("data_source_obj_id") : null, - rs.getInt("artifact_type_id"), rs.getString("type_name"), rs.getString("display_name"), - BlackboardArtifact.ReviewStatus.withID(rs.getInt("review_status_id")))); - } - return artifacts; - } catch (SQLException ex) { - throw new TskCoreException("Error getting or creating a blackboard artifact", ex); - } finally { - closeResultSet(rs); - closeStatement(statement); - closeConnection(connection); - releaseSingleUserCaseReadLock(); - } - } - /** * Helper method to get count of all artifacts matching the type id and * object id. Does not included rejected artifacts. @@ -4161,7 +4136,7 @@ private long getArtifactsCountHelper(int artifactTypeID, long obj_id) throws Tsk acquireSingleUserCaseReadLock(); try { connection = connections.getConnection(); - + // SELECT COUNT(*) AS count FROM blackboard_artifacts WHERE obj_id = ? AND artifact_type_id = ? PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.COUNT_ARTIFACTS_BY_SOURCE_AND_TYPE); statement.clearParameters(); @@ -4183,8 +4158,8 @@ private long getArtifactsCountHelper(int artifactTypeID, long obj_id) throws Tsk } /** - * Get all blackboard artifacts of a given type for the given object id. - * Does not included rejected artifacts. + * Get all blackboard artifacts of a given type for the given source (object + * id). Does not included rejected artifacts. * * @param artifactTypeName artifact type name * @param obj_id object id @@ -4195,7 +4170,9 @@ private long getArtifactsCountHelper(int artifactTypeID, long obj_id) throws Tsk * within TSK core */ public ArrayList<BlackboardArtifact> getBlackboardArtifacts(String artifactTypeName, long obj_id) throws TskCoreException { - return getArtifactsHelper("blackboard_artifacts.obj_id = " + obj_id + " AND blackboard_artifact_types.type_name = '" + artifactTypeName + "';"); + ArrayList<BlackboardArtifact> artifacts = new ArrayList<>(); + artifacts.addAll(blackboard.getArtifactsBySourceId(getArtifactType(artifactTypeName), obj_id)); + return artifacts; } /** @@ -4211,7 +4188,9 @@ public ArrayList<BlackboardArtifact> getBlackboardArtifacts(String artifactTypeN * within TSK core */ public ArrayList<BlackboardArtifact> getBlackboardArtifacts(int artifactTypeID, long obj_id) throws TskCoreException { - return getArtifactsHelper("blackboard_artifacts.obj_id = " + obj_id + " AND blackboard_artifact_types.artifact_type_id = " + artifactTypeID + ";"); + ArrayList<BlackboardArtifact> artifacts = new ArrayList<>(); + artifacts.addAll(blackboard.getArtifactsBySourceId(blackboard.getArtifactType(artifactTypeID), obj_id)); + return artifacts; } /** @@ -4294,7 +4273,9 @@ public long getBlackboardArtifactsCount(ARTIFACT_TYPE artifactType, long obj_id) * within TSK core */ public ArrayList<BlackboardArtifact> getBlackboardArtifacts(String artifactTypeName) throws TskCoreException { - return getArtifactsHelper("blackboard_artifact_types.type_name = '" + artifactTypeName + "';"); + ArrayList<BlackboardArtifact> artifacts = new ArrayList<>(); + artifacts.addAll(blackboard.getArtifactsByType(getArtifactType(artifactTypeName))); + return artifacts; } /** @@ -4309,7 +4290,9 @@ public ArrayList<BlackboardArtifact> getBlackboardArtifacts(String artifactTypeN * within TSK core */ public ArrayList<BlackboardArtifact> getBlackboardArtifacts(ARTIFACT_TYPE artifactType) throws TskCoreException { - return getArtifactsHelper("blackboard_artifact_types.artifact_type_id = " + artifactType.getTypeID() + ";"); + ArrayList<BlackboardArtifact> artifacts = new ArrayList<>(); + artifacts.addAll(blackboard.getArtifactsByType(blackboard.getArtifactType(artifactType.getTypeID()))); + return artifacts; } /** @@ -4324,40 +4307,69 @@ public ArrayList<BlackboardArtifact> getBlackboardArtifacts(ARTIFACT_TYPE artifa * * @throws TskCoreException exception thrown if a critical error occurs * within TSK core + * + * @deprecated Do not use. */ + @Deprecated public List<BlackboardArtifact> getBlackboardArtifacts(ARTIFACT_TYPE artifactType, BlackboardAttribute.ATTRIBUTE_TYPE attrType, String value) throws TskCoreException { - CaseDbConnection connection = null; - Statement s = null; - ResultSet rs = null; + + String dataArtifactJoin = "tsk_data_artifacts AS datarts ON datarts.artifact_obj_id = arts.artifact_obj_id"; + String analysisResultJoin = "tsk_analysis_results AS anresult ON anresult.artifact_obj_id = arts.artifact_obj_id"; + String dataArtifactColumns = ", datarts.os_account_obj_id AS os_account_obj_id"; + String analysResultColumns = ", anresult.conclusion AS conclusion, anresult.significance AS significance, anresult.priority AS priority, anresult.configuration AS configuration, anresult.justification AS justification "; + + String formatQuery = "SELECT DISTINCT arts.artifact_id AS artifact_id, " //NON-NLS + + "arts.obj_id AS obj_id, arts.artifact_obj_id as artifact_obj_id, arts.data_source_obj_id AS data_source_obj_id, arts.artifact_type_id AS artifact_type_id, " + + "types.type_name AS type_name, types.display_name AS display_name," + + "arts.review_status_id AS review_status_id %s "//NON-NLS + + "FROM blackboard_artifacts AS arts " + + "JOIN blackboard_attributes AS attrs ON arts.artifact_id = attrs.artifact_id " + + "JOIN blackboard_artifact_types AS types ON types.artifact_type_id = arts.artifact_type_id " //NON-NLS + + "LEFT JOIN %s " + + "WHERE arts.artifact_id = attrs.artifact_id " //NON-NLS + + "AND attrs.attribute_type_id = %d " + + " AND arts.artifact_type_id = %d " + + " AND attrs.value_text = '%s' " //NON-NLS + + " AND types.artifact_type_id=arts.artifact_type_id " + + " AND arts.review_status_id != %d"; + + String query = String.format(formatQuery, + (artifactType.getCategory() == BlackboardArtifact.Category.ANALYSIS_RESULT ? analysResultColumns : dataArtifactColumns), + (artifactType.getCategory() == BlackboardArtifact.Category.ANALYSIS_RESULT ? analysisResultJoin : dataArtifactJoin), + attrType.getTypeID(), + artifactType.getTypeID(), + value, + BlackboardArtifact.ReviewStatus.REJECTED.getID()); + acquireSingleUserCaseReadLock(); - try { - connection = connections.getConnection(); - s = connection.createStatement(); - rs = connection.executeQuery(s, "SELECT DISTINCT arts.artifact_id AS artifact_id, " //NON-NLS - + "arts.obj_id AS obj_id, arts.artifact_obj_id as artifact_obj_id, arts.data_source_obj_id AS data_source_obj_id, arts.artifact_type_id AS artifact_type_id, " - + "types.type_name AS type_name, types.display_name AS display_name," - + "arts.review_status_id AS review_status_id "//NON-NLS - + "FROM blackboard_artifacts AS arts, blackboard_attributes AS attrs, blackboard_artifact_types AS types " //NON-NLS - + "WHERE arts.artifact_id = attrs.artifact_id " //NON-NLS - + "AND attrs.attribute_type_id = " + attrType.getTypeID() //NON-NLS - + " AND arts.artifact_type_id = " + artifactType.getTypeID() //NON-NLS - + " AND attrs.value_text = '" + value + "'" //NON-NLS - + " AND types.artifact_type_id=arts.artifact_type_id" - + " AND arts.review_status_id !=" + BlackboardArtifact.ReviewStatus.REJECTED.getID()); - ArrayList<BlackboardArtifact> artifacts = new ArrayList<BlackboardArtifact>(); + try (CaseDbConnection connection = connections.getConnection(); Statement s = connection.createStatement(); ResultSet rs = connection.executeQuery(s, query)) { + ArrayList<BlackboardArtifact> artifacts = new ArrayList<>(); while (rs.next()) { - artifacts.add(new BlackboardArtifact(this, rs.getLong("artifact_id"), rs.getLong("obj_id"), rs.getLong("artifact_obj_id"), - rs.getObject("data_source_obj_id") != null ? rs.getLong("data_source_obj_id") : null, - rs.getInt("artifact_type_id"), rs.getString("type_name"), rs.getString("display_name"), - BlackboardArtifact.ReviewStatus.withID(rs.getInt("review_status_id")))); + if (artifactType.getCategory() == BlackboardArtifact.Category.DATA_ARTIFACT) { + Long osAccountObjId = rs.getLong("os_account_obj_id"); + if (rs.wasNull()) { + osAccountObjId = null; + } + + artifacts.add(new DataArtifact(this, rs.getLong("artifact_id"), rs.getLong("obj_id"), + rs.getLong("artifact_obj_id"), + rs.getObject("data_source_obj_id") != null ? rs.getLong("data_source_obj_id") : null, + rs.getInt("artifact_type_id"), rs.getString("type_name"), rs.getString("display_name"), + BlackboardArtifact.ReviewStatus.withID(rs.getInt("review_status_id")), osAccountObjId, false)); + } else { + artifacts.add(new AnalysisResult(this, rs.getLong("artifact_id"), rs.getLong("obj_id"), + rs.getLong("artifact_obj_id"), + rs.getObject("data_source_obj_id") != null ? rs.getLong("data_source_obj_id") : null, + rs.getInt("artifact_type_id"), rs.getString("type_name"), rs.getString("display_name"), + BlackboardArtifact.ReviewStatus.withID(rs.getInt("review_status_id")), + new Score(Score.Significance.fromID(rs.getInt("significance")), Score.Priority.fromID(rs.getInt("priority"))), + rs.getString("conclusion"), rs.getString("configuration"), rs.getString("justification"))); + } } return artifacts; } catch (SQLException ex) { throw new TskCoreException("Error getting blackboard artifacts by artifact type and attribute. " + ex.getMessage(), ex); } finally { - closeResultSet(rs); - closeStatement(s); - closeConnection(connection); releaseSingleUserCaseReadLock(); } } @@ -4374,40 +4386,17 @@ public List<BlackboardArtifact> getBlackboardArtifacts(ARTIFACT_TYPE artifactTyp * within TSK core */ public BlackboardArtifact getBlackboardArtifact(long artifactID) throws TskCoreException { - CaseDbConnection connection = null; - Statement s = null; - ResultSet rs = null; - acquireSingleUserCaseReadLock(); - try { - connection = connections.getConnection(); - s = connection.createStatement(); - rs = connection.executeQuery(s, "SELECT arts.artifact_id AS artifact_id, " - + "arts.obj_id AS obj_id, arts.artifact_obj_id as artifact_obj_id, arts.data_source_obj_id AS data_source_obj_id, arts.artifact_type_id AS artifact_type_id, " - + "types.type_name AS type_name, types.display_name AS display_name," - + "arts.review_status_id AS review_status_id "//NON-NLS - + "FROM blackboard_artifacts AS arts, blackboard_artifact_types AS types " - + "WHERE arts.artifact_id = " + artifactID - + " AND arts.artifact_type_id = types.artifact_type_id"); - if (rs.next()) { - return new BlackboardArtifact(this, rs.getLong("artifact_id"), rs.getLong("obj_id"), rs.getLong("artifact_obj_id"), - rs.getObject("data_source_obj_id") != null ? rs.getLong("data_source_obj_id") : null, - rs.getInt("artifact_type_id"), rs.getString("type_name"), rs.getString("display_name"), - BlackboardArtifact.ReviewStatus.withID(rs.getInt("review_status_id"))); - } else { - /* - * I think this should actually return null (or Optional) when - * there is no artifact with the given id, but it looks like - * existing code is not expecting that. -jm - */ - throw new TskCoreException("No blackboard artifact with id " + artifactID); - } - } catch (SQLException ex) { - throw new TskCoreException("Error getting a blackboard artifact. " + ex.getMessage(), ex); - } finally { - closeResultSet(rs); - closeConnection(connection); - releaseSingleUserCaseReadLock(); + List<DataArtifact> dataArtifacts = blackboard.getDataArtifactsWhere("artifacts.artifact_id = " + artifactID); + if (!dataArtifacts.isEmpty()) { + return dataArtifacts.get(0); + } + + List<AnalysisResult> analysisResults = blackboard.getAnalysisResultsWhere("artifacts.artifact_id = " + artifactID); + if (!analysisResults.isEmpty()) { + return analysisResults.get(0); } + + throw new TskCoreException("No blackboard artifact with id " + artifactID); } /** @@ -4685,49 +4674,15 @@ String addSourceToArtifactAttribute(BlackboardAttribute attr, String source) thr * * @throws TskCoreException exception thrown if a critical error occurs * within tsk core - * @throws TskDataException exception thrown if attribute type was already - * in the system + * + * @deprecated Use Blackboard.getOrAddAttributeType() instead. */ + @Deprecated public BlackboardAttribute.Type addArtifactAttributeType(String attrTypeString, TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE valueType, String displayName) throws TskCoreException, TskDataException { - CaseDbConnection connection = null; - acquireSingleUserCaseWriteLock(); - Statement s = null; - ResultSet rs = null; try { - connection = connections.getConnection(); - connection.beginTransaction(); - s = connection.createStatement(); - rs = connection.executeQuery(s, "SELECT attribute_type_id FROM blackboard_attribute_types WHERE type_name = '" + attrTypeString + "'"); //NON-NLS - if (!rs.next()) { - rs.close(); - rs = connection.executeQuery(s, "SELECT MAX(attribute_type_id) AS highest_id FROM blackboard_attribute_types"); - int maxID = 0; - if (rs.next()) { - maxID = rs.getInt("highest_id"); - if (maxID < MIN_USER_DEFINED_TYPE_ID) { - maxID = MIN_USER_DEFINED_TYPE_ID; - } else { - maxID++; - } - } - connection.executeUpdate(s, "INSERT INTO blackboard_attribute_types (attribute_type_id, type_name, display_name, value_type) VALUES ('" + maxID + "', '" + attrTypeString + "', '" + displayName + "', '" + valueType.getType() + "')"); //NON-NLS - BlackboardAttribute.Type type = new BlackboardAttribute.Type(maxID, attrTypeString, displayName, valueType); - this.typeIdToAttributeTypeMap.put(type.getTypeID(), type); - this.typeNameToAttributeTypeMap.put(type.getTypeName(), type); - connection.commitTransaction(); - return type; - } else { - throw new TskDataException("The attribute type that was added was already within the system."); - } - - } catch (SQLException ex) { - rollbackTransaction(connection); - throw new TskCoreException("Error adding attribute type", ex); - } finally { - closeResultSet(rs); - closeStatement(s); - closeConnection(connection); - releaseSingleUserCaseWriteLock(); + return blackboard.getOrAddAttributeType(attrTypeString, valueType, displayName); + } catch (BlackboardException ex) { + throw new TskCoreException("Error adding artifact type: " + attrTypeString, ex); } } @@ -4740,75 +4695,11 @@ public BlackboardAttribute.Type addArtifactAttributeType(String attrTypeString, * * @throws TskCoreException If an error occurs accessing the case database. * + * @deprecated Use Blackboard.getAttributeType instead */ + @Deprecated public BlackboardAttribute.Type getAttributeType(String attrTypeName) throws TskCoreException { - if (this.typeNameToAttributeTypeMap.containsKey(attrTypeName)) { - return this.typeNameToAttributeTypeMap.get(attrTypeName); - } - CaseDbConnection connection = null; - Statement s = null; - ResultSet rs = null; - acquireSingleUserCaseReadLock(); - try { - connection = connections.getConnection(); - s = connection.createStatement(); - rs = connection.executeQuery(s, "SELECT attribute_type_id, type_name, display_name, value_type FROM blackboard_attribute_types WHERE type_name = '" + attrTypeName + "'"); //NON-NLS - BlackboardAttribute.Type type = null; - if (rs.next()) { - type = new BlackboardAttribute.Type(rs.getInt("attribute_type_id"), rs.getString("type_name"), - rs.getString("display_name"), TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.fromType(rs.getLong("value_type"))); - this.typeIdToAttributeTypeMap.put(type.getTypeID(), type); - this.typeNameToAttributeTypeMap.put(attrTypeName, type); - } - return type; - } catch (SQLException ex) { - throw new TskCoreException("Error getting attribute type id", ex); - } finally { - closeResultSet(rs); - closeStatement(s); - closeConnection(connection); - releaseSingleUserCaseReadLock(); - } - } - - /** - * Get the attribute type associated with an attribute type ID. - * - * @param typeID An attribute type ID. - * - * @return An attribute type or null if the attribute type does not exist. - * - * @throws TskCoreException If an error occurs accessing the case database. - * - */ - BlackboardAttribute.Type getAttributeType(int typeID) throws TskCoreException { - if (this.typeIdToAttributeTypeMap.containsKey(typeID)) { - return this.typeIdToAttributeTypeMap.get(typeID); - } - CaseDbConnection connection = null; - Statement s = null; - ResultSet rs = null; - acquireSingleUserCaseReadLock(); - try { - connection = connections.getConnection(); - s = connection.createStatement(); - rs = connection.executeQuery(s, "SELECT attribute_type_id, type_name, display_name, value_type FROM blackboard_attribute_types WHERE attribute_type_id = " + typeID + ""); //NON-NLS - BlackboardAttribute.Type type = null; - if (rs.next()) { - type = new BlackboardAttribute.Type(rs.getInt("attribute_type_id"), rs.getString("type_name"), - rs.getString("display_name"), TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.fromType(rs.getLong("value_type"))); - this.typeIdToAttributeTypeMap.put(typeID, type); - this.typeNameToAttributeTypeMap.put(type.getTypeName(), type); - } - return type; - } catch (SQLException ex) { - throw new TskCoreException("Error getting attribute type id", ex); - } finally { - closeResultSet(rs); - closeStatement(s); - closeConnection(connection); - releaseSingleUserCaseReadLock(); - } + return blackboard.getAttributeType(attrTypeName); } /** @@ -4820,80 +4711,11 @@ BlackboardAttribute.Type getAttributeType(int typeID) throws TskCoreException { * * @throws TskCoreException If an error occurs accessing the case database. * + * @deprecated Use Blackboard.getArtifactType instead */ + @Deprecated public BlackboardArtifact.Type getArtifactType(String artTypeName) throws TskCoreException { - if (this.typeNameToArtifactTypeMap.containsKey(artTypeName)) { - return this.typeNameToArtifactTypeMap.get(artTypeName); - } - CaseDbConnection connection = null; - Statement s = null; - ResultSet rs = null; - acquireSingleUserCaseReadLock(); - try { - connection = connections.getConnection(); - s = connection.createStatement(); - rs = connection.executeQuery(s, "SELECT artifact_type_id, type_name, display_name, category_type FROM blackboard_artifact_types WHERE type_name = '" + artTypeName + "'"); //NON-NLS - BlackboardArtifact.Type type = null; - if (rs.next()) { - type = new BlackboardArtifact.Type(rs.getInt("artifact_type_id"), - rs.getString("type_name"), rs.getString("display_name"), - BlackboardArtifact.Category.fromID(rs.getInt("category_type"))); - this.typeIdToArtifactTypeMap.put(type.getTypeID(), type); - this.typeNameToArtifactTypeMap.put(artTypeName, type); - } - return type; - } catch (SQLException ex) { - throw new TskCoreException("Error getting artifact type from the database", ex); - } finally { - closeResultSet(rs); - closeStatement(s); - closeConnection(connection); - releaseSingleUserCaseReadLock(); - } - } - - /** - * Get the artifact type associated with an artifact type id. - * - * @param artTypeId An artifact type id. - * - * @return The artifact type. - * - * @throws TskCoreException If an error occurs accessing the case database - * or no value is found. - * - */ - BlackboardArtifact.Type getArtifactType(int artTypeId) throws TskCoreException { - if (this.typeIdToArtifactTypeMap.containsKey(artTypeId)) { - return typeIdToArtifactTypeMap.get(artTypeId); - } - CaseDbConnection connection = null; - Statement s = null; - ResultSet rs = null; - acquireSingleUserCaseReadLock(); - try { - connection = connections.getConnection(); - s = connection.createStatement(); - rs = connection.executeQuery(s, "SELECT artifact_type_id, type_name, display_name, category_type FROM blackboard_artifact_types WHERE artifact_type_id = " + artTypeId + ""); //NON-NLS - BlackboardArtifact.Type type = null; - if (rs.next()) { - type = new BlackboardArtifact.Type(rs.getInt("artifact_type_id"), - rs.getString("type_name"), rs.getString("display_name"), - BlackboardArtifact.Category.fromID(rs.getInt("category_type"))); - this.typeIdToArtifactTypeMap.put(artTypeId, type); - this.typeNameToArtifactTypeMap.put(type.getTypeName(), type); - return type; - } else { - throw new TskCoreException("No artifact type found matching id: " + artTypeId); - } - } catch (SQLException ex) { - throw new TskCoreException("Error getting artifact type from the database", ex); - } finally { - closeResultSet(rs); - closeStatement(s); - closeConnection(connection); - releaseSingleUserCaseReadLock(); - } + return blackboard.getArtifactType(artTypeName); } /** @@ -4910,9 +4732,10 @@ BlackboardArtifact.Type getArtifactType(int artTypeId) throws TskCoreException { * @throws TskCoreException exception thrown if a critical error occurs * @throws TskDataException exception thrown if given data is already in db * within tsk core + * @deprecated Use Blackboard.getOrAddArtifactType() instead. */ + @Deprecated public BlackboardArtifact.Type addBlackboardArtifactType(String artifactTypeName, String displayName) throws TskCoreException, TskDataException { - return addBlackboardArtifactType(artifactTypeName, displayName, BlackboardArtifact.Category.DATA_ARTIFACT); } @@ -4928,173 +4751,35 @@ public BlackboardArtifact.Type addBlackboardArtifactType(String artifactTypeName * @return Type of the artifact added. * * @throws TskCoreException exception thrown if a critical error occurs - * @throws TskDataException exception thrown if given data is already in db - * within tsk core + * + * @deprecated Use Blackboard.getOrAddArtifactType() instead. */ + @Deprecated BlackboardArtifact.Type addBlackboardArtifactType(String artifactTypeName, String displayName, BlackboardArtifact.Category category) throws TskCoreException, TskDataException { - CaseDbConnection connection = null; - acquireSingleUserCaseWriteLock(); - Statement s = null; - ResultSet rs = null; - try { - connection = connections.getConnection(); - connection.beginTransaction(); - s = connection.createStatement(); - rs = connection.executeQuery(s, "SELECT artifact_type_id FROM blackboard_artifact_types WHERE type_name = '" + artifactTypeName + "'"); //NON-NLS - if (!rs.next()) { - rs.close(); - rs = connection.executeQuery(s, "SELECT MAX(artifact_type_id) AS highest_id FROM blackboard_artifact_types"); - int maxID = 0; - if (rs.next()) { - maxID = rs.getInt("highest_id"); - if (maxID < MIN_USER_DEFINED_TYPE_ID) { - maxID = MIN_USER_DEFINED_TYPE_ID; - } else { - maxID++; - } - } - connection.executeUpdate(s, "INSERT INTO blackboard_artifact_types (artifact_type_id, type_name, display_name, category_type) VALUES ('" + maxID + "', '" + artifactTypeName + "', '" + displayName + "', " + category.getID() + " )"); //NON-NLS - BlackboardArtifact.Type type = new BlackboardArtifact.Type(maxID, artifactTypeName, displayName, category); - this.typeIdToArtifactTypeMap.put(type.getTypeID(), type); - this.typeNameToArtifactTypeMap.put(type.getTypeName(), type); - connection.commitTransaction(); - return type; - } else { - throw new TskDataException("The attribute type that was added was already within the system."); - } - } catch (SQLException ex) { - rollbackTransaction(connection); - throw new TskCoreException("Error adding artifact type", ex); - } finally { - closeResultSet(rs); - closeStatement(s); - closeConnection(connection); - releaseSingleUserCaseWriteLock(); - } - } - - public ArrayList<BlackboardAttribute> getBlackboardAttributes(final BlackboardArtifact artifact) throws TskCoreException { - CaseDbConnection connection = null; - Statement statement = null; - ResultSet rs = null; - acquireSingleUserCaseReadLock(); try { - connection = connections.getConnection(); - statement = connection.createStatement(); - rs = connection.executeQuery(statement, "SELECT attrs.artifact_id AS artifact_id, " - + "attrs.source AS source, attrs.context AS context, attrs.attribute_type_id AS attribute_type_id, " - + "attrs.value_type AS value_type, attrs.value_byte AS value_byte, " - + "attrs.value_text AS value_text, attrs.value_int32 AS value_int32, " - + "attrs.value_int64 AS value_int64, attrs.value_double AS value_double, " - + "types.type_name AS type_name, types.display_name AS display_name " - + "FROM blackboard_attributes AS attrs, blackboard_attribute_types AS types WHERE attrs.artifact_id = " + artifact.getArtifactID() - + " AND attrs.attribute_type_id = types.attribute_type_id"); - ArrayList<BlackboardAttribute> attributes = new ArrayList<BlackboardAttribute>(); - while (rs.next()) { - int attributeTypeId = rs.getInt("attribute_type_id"); - String attributeTypeName = rs.getString("type_name"); - BlackboardAttribute.Type attributeType; - if (this.typeIdToAttributeTypeMap.containsKey(attributeTypeId)) { - attributeType = this.typeIdToAttributeTypeMap.get(attributeTypeId); - } else { - attributeType = new BlackboardAttribute.Type(attributeTypeId, attributeTypeName, - rs.getString("display_name"), - BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.fromType(rs.getInt("value_type"))); - this.typeIdToAttributeTypeMap.put(attributeTypeId, attributeType); - this.typeNameToAttributeTypeMap.put(attributeTypeName, attributeType); - } - - final BlackboardAttribute attr = new BlackboardAttribute( - rs.getLong("artifact_id"), - attributeType, - rs.getString("source"), - rs.getString("context"), - rs.getInt("value_int32"), - rs.getLong("value_int64"), - rs.getDouble("value_double"), - rs.getString("value_text"), - rs.getBytes("value_byte"), this - ); - attr.setParentDataSourceID(artifact.getDataSourceObjectID()); - attributes.add(attr); - } - return attributes; - } catch (SQLException ex) { - throw new TskCoreException("Error getting attributes for artifact, artifact id = " + artifact.getArtifactID(), ex); - } finally { - closeResultSet(rs); - closeStatement(statement); - closeConnection(connection); - releaseSingleUserCaseReadLock(); + return blackboard.getOrAddArtifactType(displayName, displayName, category); + } catch (BlackboardException ex) { + throw new TskCoreException("Error getting or adding artifact type with name: " + artifactTypeName, ex); } } - + /** - * Get the attributes associated with the given file. - * - * @param file - * - * @return - * - * @throws TskCoreException + * Get the list of attributes for the given artifact. + * + * @param artifact The artifact to load attributes for. + * + * @return The list of attributes. + * + * @throws TskCoreException + * + * @deprecated Use Blackboard.getBlackboardAttributes instead */ - ArrayList<Attribute> getFileAttributes(final AbstractFile file) throws TskCoreException { - CaseDbConnection connection = null; - Statement statement = null; - ResultSet rs = null; - acquireSingleUserCaseReadLock(); - try { - connection = connections.getConnection(); - statement = connection.createStatement(); - rs = connection.executeQuery(statement, "SELECT attrs.id as id, attrs.obj_id AS obj_id, " - + "attrs.attribute_type_id AS attribute_type_id, " - + "attrs.value_type AS value_type, attrs.value_byte AS value_byte, " - + "attrs.value_text AS value_text, attrs.value_int32 AS value_int32, " - + "attrs.value_int64 AS value_int64, attrs.value_double AS value_double, " - + "types.type_name AS type_name, types.display_name AS display_name " - + "FROM tsk_file_attributes AS attrs " - + " INNER JOIN blackboard_attribute_types AS types " - + " ON attrs.attribute_type_id = types.attribute_type_id " - + " WHERE attrs.obj_id = " + file.getId()); - - ArrayList<Attribute> attributes = new ArrayList<Attribute>(); - while (rs.next()) { - int attributeTypeId = rs.getInt("attribute_type_id"); - String attributeTypeName = rs.getString("type_name"); - BlackboardAttribute.Type attributeType; - if (this.typeIdToAttributeTypeMap.containsKey(attributeTypeId)) { - attributeType = this.typeIdToAttributeTypeMap.get(attributeTypeId); - } else { - attributeType = new BlackboardAttribute.Type(attributeTypeId, attributeTypeName, - rs.getString("display_name"), - BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.fromType(rs.getInt("value_type"))); - this.typeIdToAttributeTypeMap.put(attributeTypeId, attributeType); - this.typeNameToAttributeTypeMap.put(attributeTypeName, attributeType); - } - - final Attribute attr = new Attribute( - rs.getLong("id"), - rs.getLong("obj_id"), - attributeType, - rs.getInt("value_int32"), - rs.getLong("value_int64"), - rs.getDouble("value_double"), - rs.getString("value_text"), - rs.getBytes("value_byte"), this - ); - attributes.add(attr); - } - return attributes; - } catch (SQLException ex) { - throw new TskCoreException("Error getting attributes for file, file id = " + file.getId(), ex); - } finally { - closeResultSet(rs); - closeStatement(statement); - closeConnection(connection); - releaseSingleUserCaseReadLock(); - } + @Deprecated + public ArrayList<BlackboardAttribute> getBlackboardAttributes(final BlackboardArtifact artifact) throws TskCoreException { + return blackboard.getBlackboardAttributes(artifact); } + /** * Get all attributes that match a where clause. The clause should begin * with "WHERE" or "JOIN". To use this method you must know the database @@ -5122,11 +4807,11 @@ public ArrayList<BlackboardAttribute> getMatchingAttributes(String whereClause) + "blackboard_attributes.value_text AS value_text, blackboard_attributes.value_int32 AS value_int32, " + "blackboard_attributes.value_int64 AS value_int64, blackboard_attributes.value_double AS value_double " + "FROM blackboard_attributes " + whereClause); //NON-NLS - ArrayList<BlackboardAttribute> matches = new ArrayList<BlackboardAttribute>(); + ArrayList<BlackboardAttribute> matches = new ArrayList<>(); while (rs.next()) { BlackboardAttribute.Type type; // attribute type is cached, so this does not necessarily call to the db - type = this.getAttributeType(rs.getInt("attribute_type_id")); + type = blackboard.getAttributeType(rs.getInt("attribute_type_id")); BlackboardAttribute attr = new BlackboardAttribute( rs.getLong("artifact_id"), type, @@ -5162,36 +4847,38 @@ public ArrayList<BlackboardAttribute> getMatchingAttributes(String whereClause) * @throws TskCoreException exception thrown if a critical error occurs * within tsk core \ref query_database_page */ - public ArrayList<BlackboardArtifact> getMatchingArtifacts(String whereClause) throws TskCoreException { - CaseDbConnection connection = null; - Statement s = null; - ResultSet rs = null; - acquireSingleUserCaseReadLock(); - try { - connection = connections.getConnection(); - s = connection.createStatement(); - rs = connection.executeQuery(s, "SELECT blackboard_artifacts.artifact_id AS artifact_id, " - + "blackboard_artifacts.obj_id AS obj_id, blackboard_artifacts.artifact_obj_id AS artifact_obj_id, blackboard_artifacts.data_source_obj_id AS data_source_obj_id, blackboard_artifacts.artifact_type_id AS artifact_type_id, " - + "blackboard_artifacts.review_status_id AS review_status_id " - + "FROM blackboard_artifacts " + whereClause); //NON-NLS - ArrayList<BlackboardArtifact> matches = new ArrayList<BlackboardArtifact>(); - while (rs.next()) { - BlackboardArtifact.Type type; - // artifact type is cached, so this does not necessarily call to the db - type = this.getArtifactType(rs.getInt("artifact_type_id")); - BlackboardArtifact artifact = new BlackboardArtifact(this, rs.getLong("artifact_id"), rs.getLong("obj_id"), rs.getLong("artifact_obj_id"), - rs.getObject("data_source_obj_id") != null ? rs.getLong("data_source_obj_id") : null, - type.getTypeID(), type.getTypeName(), type.getDisplayName(), - BlackboardArtifact.ReviewStatus.withID(rs.getInt("review_status_id"))); - matches.add(artifact); + public ArrayList<BlackboardArtifact> getMatchingArtifacts(String whereClause) throws TskCoreException { + String query = "SELECT blackboard_artifacts.artifact_id AS artifact_id, " + + "blackboard_artifacts.obj_id AS obj_id, blackboard_artifacts.artifact_obj_id AS artifact_obj_id, blackboard_artifacts.data_source_obj_id AS data_source_obj_id, blackboard_artifacts.artifact_type_id AS artifact_type_id, " + + "blackboard_artifacts.review_status_id AS review_status_id " + + "FROM blackboard_artifacts " + whereClause; + acquireSingleUserCaseReadLock(); + try (CaseDbConnection connection = connections.getConnection(); Statement statement = connection.createStatement(); ResultSet resultSet = statement.executeQuery(query)) { + + List<Long> analysisArtifactObjIds = new ArrayList<>(); + List<Long> dataArtifactObjIds = new ArrayList<>(); + while (resultSet.next()) { + BlackboardArtifact.Type type = blackboard.getArtifactType(resultSet.getInt("artifact_type_id")); + if (type.getCategory() == BlackboardArtifact.Category.ANALYSIS_RESULT) { + analysisArtifactObjIds.add(resultSet.getLong("artifact_obj_id")); + } else { + dataArtifactObjIds.add(resultSet.getLong("artifact_obj_id")); + } + } + + ArrayList<BlackboardArtifact> matches = new ArrayList<>(); + if (!analysisArtifactObjIds.isEmpty()) { + matches.addAll(getArtifactsForValues(BlackboardArtifact.Category.ANALYSIS_RESULT, "artifacts.artifact_obj_id", analysisArtifactObjIds, connection)); } + + if (!dataArtifactObjIds.isEmpty()) { + matches.addAll(getArtifactsForValues(BlackboardArtifact.Category.DATA_ARTIFACT, "artifacts.artifact_obj_id", dataArtifactObjIds, connection)); + } + return matches; } catch (SQLException ex) { throw new TskCoreException("Error getting attributes using this where clause: " + whereClause, ex); } finally { - closeResultSet(rs); - closeStatement(s); - closeConnection(connection); releaseSingleUserCaseReadLock(); } } @@ -5212,25 +4899,25 @@ public ArrayList<BlackboardArtifact> getMatchingArtifacts(String whereClause) th */ @Deprecated public BlackboardArtifact newBlackboardArtifact(int artifactTypeID, long obj_id) throws TskCoreException { - BlackboardArtifact.Type type = getArtifactType(artifactTypeID); + BlackboardArtifact.Type type = blackboard.getArtifactType(artifactTypeID); if (type == null) { throw new TskCoreException("Unknown artifact type for id: " + artifactTypeID); } - + Category category = type.getCategory(); if (category == null) { - throw new TskCoreException(String.format("No category for %s (id: %d)", + throw new TskCoreException(String.format("No category for %s (id: %d)", type.getDisplayName() == null ? "<null>" : type.getDisplayName(), type.getTypeID())); } - + Content content = getContentById(obj_id); if (content == null) { throw new TskCoreException("No content found for object id: " + obj_id); } - + switch (category) { - case ANALYSIS_RESULT: + case ANALYSIS_RESULT: return content.newAnalysisResult(type, Score.SCORE_UNKNOWN, null, null, null, Collections.emptyList()) .getAnalysisResult(); case DATA_ARTIFACT: @@ -5276,7 +4963,7 @@ public BlackboardArtifact newBlackboardArtifact(ARTIFACT_TYPE artifactType, long @Deprecated @SuppressWarnings("deprecation") BlackboardArtifact newBlackboardArtifact(int artifactTypeID, long obj_id, long data_source_obj_id) throws TskCoreException { - BlackboardArtifact.Type type = getArtifactType(artifactTypeID); + BlackboardArtifact.Type type = blackboard.getArtifactType(artifactTypeID); try (CaseDbConnection connection = connections.getConnection()) { return newBlackboardArtifact(artifactTypeID, obj_id, type.getTypeName(), type.getDisplayName(), data_source_obj_id, connection); } @@ -5314,23 +5001,33 @@ PreparedStatement createInsertArtifactStatement(int artifact_type_id, long obj_i return statement; } + /** + * Add a new blackboard artifact with the given type. + * + * @param artifact_type_id The type the given artifact should have. + * @param obj_id The parent content id. + * @param artifactTypeName The artifact type name. + * @param artifactDisplayName The artifact type display name. + * @param data_source_obj_id The id of the artifact data source. + * @param connection The CaseDBConnection. + * + * @return A new blackboard artifact. + * + * @throws TskCoreException + * + * @deprecated Use type specific methods in Blackboard. + */ @Deprecated - BlackboardArtifact newBlackboardArtifact(int artifact_type_id, long obj_id, String artifactTypeName, String artifactDisplayName, long data_source_obj_id, CaseDbConnection connection) throws TskCoreException { - acquireSingleUserCaseWriteLock(); + private BlackboardArtifact newBlackboardArtifact(int artifact_type_id, long obj_id, String artifactTypeName, String artifactDisplayName, long data_source_obj_id, CaseDbConnection connection) throws TskCoreException { + BlackboardArtifact.Type type = blackboard.getArtifactType(artifact_type_id); try { - long artifact_obj_id = addObject(obj_id, TskData.ObjectType.ARTIFACT.getObjectType(), connection); - PreparedStatement statement = createInsertArtifactStatement(artifact_type_id, obj_id, artifact_obj_id, data_source_obj_id, connection); - - connection.executeUpdate(statement); - try (ResultSet resultSet = statement.getGeneratedKeys()) { - resultSet.next(); - return new BlackboardArtifact(this, resultSet.getLong(1), //last_insert_rowid() - obj_id, artifact_obj_id, data_source_obj_id, artifact_type_id, artifactTypeName, artifactDisplayName, BlackboardArtifact.ReviewStatus.UNDECIDED, true); + if (type.getCategory() == BlackboardArtifact.Category.ANALYSIS_RESULT) { + return blackboard.newAnalysisResult(type, obj_id, data_source_obj_id, Score.SCORE_UNKNOWN, null, null, null, Collections.emptyList()).getAnalysisResult(); + } else { + return blackboard.newDataArtifact(type, obj_id, data_source_obj_id, Collections.emptyList(), null); } - } catch (SQLException ex) { + } catch (BlackboardException ex) { throw new TskCoreException("Error creating a blackboard artifact", ex); - } finally { - releaseSingleUserCaseWriteLock(); } } @@ -5430,7 +5127,7 @@ boolean getContentHasChildren(Content content) throws TskCoreException { acquireSingleUserCaseReadLock(); try { connection = connections.getConnection(); - + // SELECT COUNT(obj_id) AS count FROM tsk_objects WHERE par_obj_id = ? PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.COUNT_CHILD_OBJECTS_BY_PARENT); statement.clearParameters(); @@ -5473,7 +5170,7 @@ int getContentChildrenCount(Content content) throws TskCoreException { acquireSingleUserCaseReadLock(); try { connection = connections.getConnection(); - + // SELECT COUNT(obj_id) AS count FROM tsk_objects WHERE par_obj_id = ? PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.COUNT_CHILD_OBJECTS_BY_PARENT); statement.clearParameters(); @@ -5510,7 +5207,7 @@ List<Content> getAbstractFileChildren(Content parent, TSK_DB_FILES_TYPE_ENUM typ acquireSingleUserCaseReadLock(); try { connection = connections.getConnection(); - + PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.SELECT_FILES_BY_PARENT_AND_TYPE); statement.clearParameters(); long parentId = parent.getId(); @@ -5526,7 +5223,7 @@ List<Content> getAbstractFileChildren(Content parent, TSK_DB_FILES_TYPE_ENUM typ releaseSingleUserCaseReadLock(); } } - + /** * Returns the list of all AbstractFile Children for a given * AbstractFileParent @@ -5542,7 +5239,7 @@ List<Content> getAbstractFileChildren(Content parent) throws TskCoreException { acquireSingleUserCaseReadLock(); try { connection = connections.getConnection(); - + PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.SELECT_FILES_BY_PARENT); statement.clearParameters(); long parentId = parent.getId(); @@ -5575,7 +5272,7 @@ List<Long> getAbstractFileChildrenIds(Content parent, TSK_DB_FILES_TYPE_ENUM typ acquireSingleUserCaseReadLock(); try { connection = connections.getConnection(); - + PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.SELECT_FILE_IDS_BY_PARENT_AND_TYPE); statement.clearParameters(); statement.setLong(1, parent.getId()); @@ -5610,7 +5307,7 @@ List<Long> getAbstractFileChildrenIds(Content parent) throws TskCoreException { acquireSingleUserCaseReadLock(); try { connection = connections.getConnection(); - + PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.SELECT_FILE_IDS_BY_PARENT); statement.clearParameters(); statement.setLong(1, parent.getId()); @@ -5645,7 +5342,7 @@ List<Long> getBlackboardArtifactChildrenIds(Content parent) throws TskCoreExcept acquireSingleUserCaseReadLock(); try { connection = connections.getConnection(); - + PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.SELECT_ARTIFACT_OBJECTIDS_BY_PARENT); statement.clearParameters(); statement.setLong(1, parent.getId()); @@ -5674,12 +5371,10 @@ List<Long> getBlackboardArtifactChildrenIds(Content parent) throws TskCoreExcept * @throws TskCoreException */ List<Content> getBlackboardArtifactChildren(Content parent) throws TskCoreException { - long parentId = parent.getId(); - ArrayList<BlackboardArtifact> artsArray = getArtifactsHelper("blackboard_artifacts.obj_id = " + parentId + ";"); - - List<Content> lc = new ArrayList<Content>(); - lc.addAll(artsArray); + List<Content> lc = new ArrayList<>(); + lc.addAll(blackboard.getAnalysisResults(parentId)); + lc.addAll(blackboard.getDataArtifactsBySource(parentId)); return lc; } @@ -5819,7 +5514,7 @@ public Content getContentById(long id) throws TskCoreException { long parentId; TskData.ObjectType type; - + CaseDbConnection connection = null; Statement s = null; ResultSet rs = null; @@ -5908,7 +5603,7 @@ String getFilePath(long id) { acquireSingleUserCaseReadLock(); try { connection = connections.getConnection(); - + PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.SELECT_LOCAL_PATH_FOR_FILE); statement.clearParameters(); statement.setLong(1, id); @@ -6027,7 +5722,7 @@ String getFileName(long objectId, CaseDbConnection connection) { * method could not be queried */ DerivedFile.DerivedMethod getDerivedMethod(long id) throws TskCoreException { - + DerivedFile.DerivedMethod method = null; CaseDbConnection connection = null; ResultSet rs1 = null; @@ -6035,7 +5730,7 @@ DerivedFile.DerivedMethod getDerivedMethod(long id) throws TskCoreException { acquireSingleUserCaseReadLock(); try { connection = connections.getConnection(); - + PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.SELECT_DERIVED_FILE); statement.clearParameters(); statement.setLong(1, id); @@ -6136,7 +5831,7 @@ public BlackboardArtifact getArtifactById(long id) throws TskCoreException { acquireSingleUserCaseReadLock(); try { connection = connections.getConnection(); - + // get the artifact type. PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.SELECT_ARTIFACT_TYPE_BY_ARTIFACT_OBJ_ID); statement.clearParameters(); @@ -6148,7 +5843,7 @@ public BlackboardArtifact getArtifactById(long id) throws TskCoreException { } // based on the artifact type category, get the analysis result or the data artifact - BlackboardArtifact.Type artifactType = getArtifactType(rs.getInt("artifact_type_id")); + BlackboardArtifact.Type artifactType = blackboard.getArtifactType(rs.getInt("artifact_type_id")); switch (artifactType.getCategory()) { case ANALYSIS_RESULT: return blackboard.getAnalysisResultById(id); @@ -6176,29 +5871,32 @@ public BlackboardArtifact getArtifactById(long id) throws TskCoreException { * * @throws TskCoreException thrown if critical error occurred within tsk * core and file could not be queried + * + * @deprecated Use the type specific methods in Blackboard + * getAnalysisResultsById and getDataArtifactById */ + @Deprecated public BlackboardArtifact getArtifactByArtifactId(long id) throws TskCoreException { - CaseDbConnection connection = null; - ResultSet rs = null; + String query = "SELECT artifact_type_id, artifact_obj_id FROM blackboard_artifacts WHERE artifact_id = " + id; acquireSingleUserCaseReadLock(); - try { - connection = connections.getConnection(); - - PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.SELECT_ARTIFACT_BY_ARTIFACT_ID); - statement.clearParameters(); - statement.setLong(1, id); - rs = connection.executeQuery(statement); - List<BlackboardArtifact> artifacts = resultSetToArtifacts(rs); - if (artifacts.size() > 0) { - return artifacts.get(0); - } else { - return null; + + try (CaseDbConnection connection = connections.getConnection(); + Statement statement = connection.createStatement(); + ResultSet resultSet = statement.executeQuery(query);) { + if (resultSet != null && resultSet.next()) { + BlackboardArtifact.Type artifactType = blackboard.getArtifactType(resultSet.getInt("artifact_type_id")); + long artifactObjId = resultSet.getLong("artifact_obj_id"); + switch (artifactType.getCategory()) { + case ANALYSIS_RESULT: + return blackboard.getAnalysisResultById(artifactObjId); + case DATA_ARTIFACT: + return blackboard.getDataArtifactById(artifactObjId); + } } + return null; } catch (SQLException ex) { throw new TskCoreException("Error getting artifacts by artifact id, artifact id = " + id, ex); } finally { - closeResultSet(rs); - closeConnection(connection); releaseSingleUserCaseReadLock(); } } @@ -6271,13 +5969,14 @@ public boolean isFileFromSource(Content dataSource, long fileId) throws TskCoreE releaseSingleUserCaseReadLock(); } } - + /** - * Returns true if the string contains a SQL LIKE statement wild card based - * on https://www.postgresql.org/docs/9.5/functions-matching.html and + * Returns true if the string contains a SQL LIKE statement wild card based + * on https://www.postgresql.org/docs/9.5/functions-matching.html and * https://sqlite.org/lang_expr.html#the_like_glob_regexp_and_match_operators. - * + * * @param str The string. + * * @return True if it contains a LIKE wild card. */ private static boolean containsLikeWildcard(String str) { @@ -6302,16 +6001,16 @@ private static boolean containsLikeWildcard(String str) { public List<AbstractFile> findFiles(Content dataSource, String fileName) throws TskCoreException { String ext = ""; if (!containsLikeWildcard(fileName)) { - ext = SleuthkitCase.extractExtension(fileName); + ext = SleuthkitCase.extractExtension(fileName); } - + List<AbstractFile> files = new ArrayList<>(); CaseDbConnection connection = null; ResultSet resultSet = null; acquireSingleUserCaseReadLock(); try { connection = connections.getConnection(); - + PreparedStatement statement; if (ext.isEmpty()) { statement = connection.getPreparedStatement(PREPARED_STATEMENT.SELECT_FILES_BY_DATA_SOURCE_AND_NAME); @@ -6354,9 +6053,9 @@ public List<AbstractFile> findFiles(Content dataSource, String fileName) throws public List<AbstractFile> findFiles(Content dataSource, String fileName, String dirSubString) throws TskCoreException { String ext = ""; if (!containsLikeWildcard(fileName)) { - ext = SleuthkitCase.extractExtension(fileName); + ext = SleuthkitCase.extractExtension(fileName); } - + List<AbstractFile> files = new ArrayList<>(); CaseDbConnection connection = null; ResultSet resultSet = null; @@ -6378,7 +6077,7 @@ public List<AbstractFile> findFiles(Content dataSource, String fileName, String statement.setString(3, "%" + dirSubString.toLowerCase() + "%"); //NON-NLS statement.setLong(4, dataSource.getId()); } - + resultSet = connection.executeQuery(statement); files.addAll(resultSetToAbstractFiles(resultSet, connection)); } catch (SQLException e) { @@ -7498,6 +7197,114 @@ public final List<LayoutFile> addLayoutFiles(Content parent, List<TskFileRange> } } + /** + * Utility class to hold the current subfolder being used for carved files + * and a count of how many files are in the folder. Note that this count + * will not be accurate if multiple nodes are writing carved files to the + * same folder at once. + */ + private class CarvedFileDirInfo { + + final VirtualDirectory currentFolder; + AtomicInteger count; + + CarvedFileDirInfo(VirtualDirectory currentFolder) { + this.currentFolder = currentFolder; + count = new AtomicInteger(0); + } + + CarvedFileDirInfo(VirtualDirectory currentFolder, int count) { + this.currentFolder = currentFolder; + this.count = new AtomicInteger(count); + } + + /** + * Check if the folder is "full" and we should start a new subfolder. + * + * @return True if the maximum number of files have been written to the + * folder, false otherwise. + */ + boolean isFull() { + return count.get() >= MAX_CARVED_FILES_PER_FOLDER; + } + + /** + * Increment the file counter. + */ + void incrementFileCounter() { + count.incrementAndGet(); + } + } + + /** + * Find the newest subfolder of $CarvedFiles and load its data. + * + * @param carvedFilesBaseDir The $CarvedFiles directory + * + * @return The subfolder of $CarvedFiles with the highest object ID. + * + * @throws TskCoreException + */ + private CarvedFileDirInfo getMostRecentCarvedDirInfo(VirtualDirectory carvedFilesBaseDir) throws TskCoreException { + VirtualDirectory mostRecentDir = null; + for (Content child : carvedFilesBaseDir.getChildren()) { + if (isValidCarvedFileSubfolder(child)) { + if (mostRecentDir == null + || (mostRecentDir.getId() < child.getId())) { + mostRecentDir = (VirtualDirectory) child; + } + } + } + + if (mostRecentDir != null) { + return new CarvedFileDirInfo(mostRecentDir, mostRecentDir.getChildrenCount()); + } + return null; + } + + /** + * Check if the name of the folder matches the expected pattern for a + * subfolder and is a virtual directory. + * + * @param subfolder The subfolder to test. + * + * @return true if the format appears valid, false otherwise. + */ + private boolean isValidCarvedFileSubfolder(Content subfolder) { + if (!(subfolder instanceof VirtualDirectory)) { + return false; + } + return subfolder.getName().matches("^[0-9]+$"); + } + + /** + * Create the next carved files subfolder. If the current subfolder is + * given, the new subfolder will be one higher than the name of the current + * subfolder. + * + * @param carvedFilesBaseDir The base $CarvedFiles folder. + * @param currentSubfolderInfo Optional name of the current subfolder in use + * (can be null). + * + * @return The new subfolder for carved files. + * + * @throws TskCoreException + */ + private CarvedFileDirInfo createCarvedFilesSubfolder(Content carvedFilesBaseDir, CarvedFileDirInfo currentSubfolderInfo) throws TskCoreException { + int nextIndex = 1; + if (currentSubfolderInfo != null) { + try { + int currentIndex = Integer.parseInt(currentSubfolderInfo.currentFolder.getName()); + nextIndex = currentIndex + 1; + } catch (NumberFormatException ex) { + throw new TskCoreException("Unexpected name format for carved files subdirectory with ID: " + currentSubfolderInfo.currentFolder.getId() + " (" + currentSubfolderInfo.currentFolder.getName() + ")", ex); + } + } + + VirtualDirectory carvedFilesSubdir = addVirtualDirectory(carvedFilesBaseDir.getId(), Integer.toString(nextIndex)); + return new CarvedFileDirInfo(carvedFilesSubdir); + } + /** * Adds a carving result to the case database. * @@ -7548,10 +7355,20 @@ public final List<LayoutFile> addCarvedFiles(CarvingResult carvingResult) throws * Get or create the $CarvedFiles virtual directory for the root * ancestor. */ - VirtualDirectory carvedFilesDir; - synchronized(carvedFileDirsLock) { - carvedFilesDir = rootIdsToCarvedFileDirs.get(root.getId()); - if (null == carvedFilesDir) { + CarvedFileDirInfo carvedFilesDirInfo = null; + synchronized (carvedFileDirsLock) { + // Get the subfolder currently in use (if there is one) + carvedFilesDirInfo = rootIdsToCarvedFileDirs.get(root.getId()); + if (carvedFilesDirInfo != null) { + carvedFilesDirInfo.incrementFileCounter(); + + // If the current folder is full, create a new one. + if (carvedFilesDirInfo.isFull()) { + carvedFilesDirInfo = createCarvedFilesSubfolder(carvedFilesDirInfo.currentFolder.getParent(), carvedFilesDirInfo); + } + } + + if (null == carvedFilesDirInfo) { List<Content> rootChildren; if (root instanceof FileSystem) { rootChildren = ((FileSystem) root).getRootDirectory().getChildren(); @@ -7560,20 +7377,40 @@ public final List<LayoutFile> addCarvedFiles(CarvingResult carvingResult) throws } for (Content child : rootChildren) { if (child instanceof VirtualDirectory && child.getName().equals(VirtualDirectory.NAME_CARVED)) { - carvedFilesDir = (VirtualDirectory) child; + + VirtualDirectory baseDir = (VirtualDirectory) child; + + // Get the most recent subfolder in the carved files folder. + carvedFilesDirInfo = getMostRecentCarvedDirInfo(baseDir); + + // If there are no subfolders, create one. + if (carvedFilesDirInfo == null) { + carvedFilesDirInfo = createCarvedFilesSubfolder(baseDir, null); + } + + // If there are already too many files in the subfolder, create a new one. + if (carvedFilesDirInfo.isFull()) { + carvedFilesDirInfo = createCarvedFilesSubfolder(baseDir, carvedFilesDirInfo); + } + + rootIdsToCarvedFileDirs.put(root.getId(), carvedFilesDirInfo); break; } } - if (null == carvedFilesDir) { + if (carvedFilesDirInfo == null) { + // If we get here, we didn't have a carved files base folder in the case, so we need to make that and + // the first subfolder. + long parId = root.getId(); // $CarvedFiles should be a child of the root directory, not the file system if (root instanceof FileSystem) { Content rootDir = ((FileSystem) root).getRootDirectory(); parId = rootDir.getId(); } - carvedFilesDir = addVirtualDirectory(parId, VirtualDirectory.NAME_CARVED); + VirtualDirectory carvedFilesBaseDir = addVirtualDirectory(parId, VirtualDirectory.NAME_CARVED); + carvedFilesDirInfo = createCarvedFilesSubfolder(carvedFilesBaseDir, null); + rootIdsToCarvedFileDirs.put(root.getId(), carvedFilesDirInfo); } - rootIdsToCarvedFileDirs.put(root.getId(), carvedFilesDir); } } @@ -7581,17 +7418,48 @@ public final List<LayoutFile> addCarvedFiles(CarvingResult carvingResult) throws * Add the carved files to the database as children of the * $CarvedFile directory of the root ancestor. */ + VirtualDirectory carvedFilesBaseDir = (VirtualDirectory) carvedFilesDirInfo.currentFolder.getParent(); transaction = beginTransaction(); CaseDbConnection connection = transaction.getConnection(); - String parentPath = getFileParentPath(carvedFilesDir.getId(), connection) + carvedFilesDir.getName() + "/"; + String parentPath = getFileParentPath(carvedFilesDirInfo.currentFolder.getId(), connection) + carvedFilesDirInfo.currentFolder.getName() + "/"; List<LayoutFile> carvedFiles = new ArrayList<>(); for (CarvingResult.CarvedFile carvedFile : carvingResult.getCarvedFiles()) { + + /* + * Check if we need to change to a new subfolder. + */ + VirtualDirectory carvedFilesDir = carvedFilesDirInfo.currentFolder; + if (carvedFilesDirInfo.isFull()) { + // To prevent deadlocks involving the case write lock and the carvedFileDirsLock, + // commit the current transaction and then start a new one + // after switching to the new folder. + transaction.commit(); + + synchronized (carvedFileDirsLock) { + // Get the current copy from the map - another thread may have just created a new folder. + carvedFilesDirInfo = rootIdsToCarvedFileDirs.get(root.getId()); + if (carvedFilesDirInfo.isFull()) { + carvedFilesDirInfo = createCarvedFilesSubfolder(carvedFilesBaseDir, carvedFilesDirInfo); + rootIdsToCarvedFileDirs.put(root.getId(), carvedFilesDirInfo); + carvedFilesDir = carvedFilesDirInfo.currentFolder; + } + } + + // Start a new transaction. + transaction = beginTransaction(); + connection = transaction.getConnection(); + parentPath = getFileParentPath(carvedFilesDir.getId(), connection) + carvedFilesDir.getName() + "/"; + + } + carvedFilesDirInfo.incrementFileCounter(); + /* * Insert a row for the carved file into the tsk_objects table: * INSERT INTO tsk_objects (par_obj_id, type) VALUES (?, ?) */ long carvedFileId = addObject(carvedFilesDir.getId(), TskData.ObjectType.ABSTRACTFILE.getObjectType(), connection); + /* * Insert a row for the carved file into the tsk_files table: * INSERT INTO tsk_files (obj_id, fs_obj_id, name, type, @@ -7740,7 +7608,7 @@ public DerivedFile addDerivedFile(String fileName, String localPath, throw ex; } } - + public DerivedFile addDerivedFile(String fileName, String localPath, long size, long ctime, long crtime, long atime, long mtime, boolean isFile, Content parentObj, @@ -7886,7 +7754,7 @@ public DerivedFile updateDerivedFile(DerivedFile derivedFile, String localPath, CaseDbTransaction trans = null; try { Content parentObj = derivedFile.getParent(); - + trans = beginTransaction(); DerivedFile updatedFile = updateDerivedFile(derivedFile, localPath, size, ctime, crtime, atime, mtime, @@ -7901,18 +7769,18 @@ public DerivedFile updateDerivedFile(DerivedFile derivedFile, String localPath, } throw ex; } - } - + } + public DerivedFile updateDerivedFile(DerivedFile derivedFile, String localPath, long size, long ctime, long crtime, long atime, long mtime, boolean isFile, String mimeType, String rederiveDetails, String toolName, String toolVersion, - String otherDetails, TskData.EncodingType encodingType, - Content parentObj, CaseDbTransaction trans) throws TskCoreException { - + String otherDetails, TskData.EncodingType encodingType, + Content parentObj, CaseDbTransaction trans) throws TskCoreException { + // Strip off any leading slashes from the local path (leading slashes indicate absolute paths) localPath = localPath.replaceAll("^[/\\\\]+", ""); - + ResultSet rs = null; try { final long parentId = parentObj.getId(); @@ -8048,6 +7916,7 @@ public LocalFile addLocalFile(String fileName, String localPath, isFile, encodingType, parent, transaction); } + /** * Adds a local/logical file to the case database. The database operations * are done within a caller-managed transaction; the caller is responsible @@ -8081,12 +7950,13 @@ public LocalFile addLocalFile(String fileName, String localPath, String md5, String sha256, FileKnown known, String mimeType, boolean isFile, TskData.EncodingType encodingType, Content parent, CaseDbTransaction transaction) throws TskCoreException { - + return addLocalFile(fileName, localPath, size, ctime, crtime, atime, mtime, md5, sha256, known, mimeType, isFile, encodingType, OsAccount.NO_ACCOUNT, OsAccount.NO_OWNER_ID, parent, transaction); - + } + /** * Adds a local/logical file to the case database. The database operations * are done within a caller-managed transaction; the caller is responsible @@ -8190,7 +8060,7 @@ public LocalFile addLocalFile(String fileName, String localPath, } else { statement.setNull(22, java.sql.Types.VARCHAR); } - + if (osAccountId != null) { statement.setLong(23, osAccountId); // osAccountObjId } else { @@ -8346,7 +8216,7 @@ private boolean isRootDirectory(AbstractFile file, CaseDbTransaction transaction isRootDirectoryCache.put(file.getId(), true); return true; // The file has no parent - + } } catch (SQLException ex) { throw new TskCoreException(String.format("Failed to lookup parent of file (%s) with id %d", file.getName(), file.getId()), ex); @@ -8512,22 +8382,22 @@ public LayoutFile addLayoutFile(String fileName, } } } - + /** - * Given a Content object, return its data source object ID. - * For AbstractFiles, this simply returns the data source ID field. - * + * Given a Content object, return its data source object ID. For + * AbstractFiles, this simply returns the data source ID field. + * * @param connection A case database connection. * @param content The content to look up the data source object ID. - * - * @return + * + * @return A data source object id. */ private long getDataSourceObjectId(CaseDbConnection connection, Content content) throws TskCoreException { if (content == null) { throw new TskCoreException("Null Content parameter given"); } if (content instanceof AbstractFile) { - return ((AbstractFile)content).getDataSourceObjectId(); + return ((AbstractFile) content).getDataSourceObjectId(); } else { return getDataSourceObjectId(connection, content.getId()); } @@ -8631,29 +8501,29 @@ private void updateFilePath(CaseDbConnection connection, long objId, String path public List<AbstractFile> findFilesInFolder(String fileName, AbstractFile parentFile) throws TskCoreException { String ext = ""; if (!containsLikeWildcard(fileName)) { - ext = SleuthkitCase.extractExtension(fileName); + ext = SleuthkitCase.extractExtension(fileName); } - + CaseDbConnection connection = null; ResultSet rs = null; long parentId = parentFile.getId(); - + acquireSingleUserCaseReadLock(); try { connection = connections.getConnection(); - + PreparedStatement statement; if (ext.isEmpty()) { statement = connection.getPreparedStatement(PREPARED_STATEMENT.SELECT_FILES_BY_PARENT_AND_NAME); statement.clearParameters(); statement.setLong(1, parentId); - statement.setString(2, fileName); + statement.setString(2, fileName); } else { statement = connection.getPreparedStatement(PREPARED_STATEMENT.SELECT_FILES_BY_EXTENSION_AND_PARENT_AND_NAME); statement.clearParameters(); statement.setString(1, ext); statement.setLong(2, parentId); - statement.setString(3, fileName); + statement.setString(3, fileName); } rs = connection.executeQuery(statement); @@ -8889,7 +8759,7 @@ public Image getImageById(long id) throws TskCoreException { ResultSet rs = null; acquireSingleUserCaseReadLock(); try { - connection = connections.getConnection(); + connection = connections.getConnection(); s = connection.createStatement(); rs = connection.executeQuery(s, "SELECT tsk_image_info.type, tsk_image_info.ssize, tsk_image_info.tzone, tsk_image_info.size, tsk_image_info.md5, tsk_image_info.sha1, tsk_image_info.sha256, tsk_image_info.display_name, data_source_info.device_id, tsk_image_names.name " + "FROM tsk_image_info " @@ -8964,7 +8834,7 @@ VolumeSystem getVolumeSystemById(long id, Content parent) throws TskCoreExceptio ResultSet rs = null; acquireSingleUserCaseReadLock(); try { - connection = connections.getConnection(); + connection = connections.getConnection(); s = connection.createStatement(); rs = connection.executeQuery(s, "SELECT * FROM tsk_vs_info " //NON-NLS + "where obj_id = " + id); //NON-NLS @@ -9134,7 +9004,7 @@ private FileSystem getFileSystemByIdHelper(long id, Content parent) throws TskCo ResultSet rs = null; acquireSingleUserCaseReadLock(); try { - connection = connections.getConnection(); + connection = connections.getConnection(); s = connection.createStatement(); rs = connection.executeQuery(s, "SELECT * FROM tsk_fs_info " //NON-NLS + "where obj_id = " + id); //NON-NLS @@ -9179,7 +9049,7 @@ Volume getVolumeById(long id, VolumeSystem parent) throws TskCoreException { ResultSet rs = null; acquireSingleUserCaseReadLock(); try { - connection = connections.getConnection(); + connection = connections.getConnection(); s = connection.createStatement(); rs = connection.executeQuery(s, "SELECT * FROM tsk_vs_parts " //NON-NLS + "where obj_id = " + id); //NON-NLS @@ -9246,7 +9116,7 @@ Directory getDirectoryById(long id, FileSystem parentFs) throws TskCoreException ResultSet rs = null; acquireSingleUserCaseReadLock(); try { - connection = connections.getConnection(); + connection = connections.getConnection(); s = connection.createStatement(); rs = connection.executeQuery(s, "SELECT * FROM tsk_files " //NON-NLS + "WHERE obj_id = " + id); @@ -9287,7 +9157,7 @@ Directory getDirectoryById(long id, FileSystem parentFs) throws TskCoreException public Collection<FileSystem> getImageFileSystems(Image image) throws TskCoreException { List<FileSystem> fileSystems = new ArrayList<>(); String queryStr = "SELECT * FROM tsk_fs_info WHERE data_source_obj_id = " + image.getId(); - + CaseDbConnection connection = null; Statement s = null; ResultSet rs = null; @@ -9782,7 +9652,7 @@ public void setImagePaths(long obj_id, List<String> paths) throws TskCoreExcepti * within tsk core and the update fails */ void deleteDataSource(long dataSourceObjectId) throws TskCoreException { - + // Check if this data source is the only one associated with its host. If so, // we will delete the host and other associated data. // Note that the cascading deletes were only added in schema 9.1, so we @@ -9791,13 +9661,13 @@ void deleteDataSource(long dataSourceObjectId) throws TskCoreException { VersionNumber version = getDBSchemaCreationVersion(); int major = version.getMajor(); int minor = version.getMinor(); - if(major > 9 || (major == 9 && minor >= 1)) { + if (major > 9 || (major == 9 && minor >= 1)) { hostToDelete = getHostManager().getHostByDataSource(dataSourceObjectId); if (getHostManager().getDataSourcesForHost(hostToDelete).size() != 1) { hostToDelete = null; } } - + CaseDbConnection connection = null; Statement statement; acquireSingleUserCaseWriteLock(); @@ -9814,19 +9684,19 @@ void deleteDataSource(long dataSourceObjectId) throws TskCoreException { + "WHERE account_id NOT IN (SELECT account1_id FROM account_relationships) " + "AND account_id NOT IN (SELECT account2_id FROM account_relationships))"; statement.execute(accountSql); - + // Now delete any host that was only associated with this data source. This will cascade to delete // realms, os accounts, and os account attributes that were associated with the host. if (hostToDelete != null) { statement.execute("DELETE FROM tsk_hosts WHERE id = " + hostToDelete.getHostId()); - + // Clean up any stray OS Account objects - String deleteOsAcctObjectsQuery = "DELETE FROM tsk_objects " + - "WHERE type=" + TskData.ObjectType.OS_ACCOUNT.getObjectType() + " " + - "AND obj_id NOT IN (SELECT os_account_obj_id FROM tsk_os_accounts WHERE os_account_obj_id IS NOT NULL)"; + String deleteOsAcctObjectsQuery = "DELETE FROM tsk_objects " + + "WHERE type=" + TskData.ObjectType.OS_ACCOUNT.getObjectType() + " " + + "AND obj_id NOT IN (SELECT os_account_obj_id FROM tsk_os_accounts WHERE os_account_obj_id IS NOT NULL)"; statement.execute(deleteOsAcctObjectsQuery); } - + connection.commitTransaction(); } catch (SQLException ex) { rollbackTransaction(connection); @@ -10329,41 +10199,6 @@ List<Content> fileChildren(ResultSet rs, CaseDbConnection connection, long paren return children; } - /** - * Creates BlackboardArtifact objects for the result set of a - * blackboard_artifacts table query of the form "SELECT * FROM - * blackboard_artifacts WHERE XYZ". - * - * @param rs A result set from a query of the blackboard_artifacts table of - * the form "SELECT * FROM blackboard_artifacts WHERE XYZ". - * - * @return A list of BlackboardArtifact objects. - * - * @throws SQLException Thrown if there is a problem iterating through - * the result set. - * @throws TskCoreException Thrown if there is an error looking up the - * artifact type id - */ - private List<BlackboardArtifact> resultSetToArtifacts(ResultSet rs) throws SQLException, TskCoreException { - ArrayList<BlackboardArtifact> artifacts = new ArrayList<BlackboardArtifact>(); - try { - while (rs.next()) { - BlackboardArtifact.Type artifactType = getArtifactType(rs.getInt("artifact_type_id")); - if (artifactType != null) { - artifacts.add(new BlackboardArtifact(this, rs.getLong("artifact_id"), rs.getLong("obj_id"), rs.getLong("artifact_obj_id"), rs.getLong("data_source_obj_id"), - rs.getInt("artifact_type_id"), artifactType.getTypeName(), artifactType.getDisplayName(), - BlackboardArtifact.ReviewStatus.withID(rs.getInt("review_status_id")))); - } else { - throw new TskCoreException("Error looking up artifact type ID " + rs.getInt("artifact_type_id") + " from artifact " + rs.getLong("artifact_id")); - } - } //end for each resultSet - } catch (SQLException e) { - logger.log(Level.SEVERE, "Error getting artifacts from result set", e); //NON-NLS - } - - return artifacts; - } - /** * This method allows developers to run arbitrary SQL "SELECT" queries. The * CaseDbQuery object will take care of acquiring the necessary database @@ -10491,7 +10326,7 @@ public boolean setKnown(AbstractFile file, FileKnown fileKnown) throws TskCoreEx } acquireSingleUserCaseWriteLock(); try (CaseDbConnection connection = connections.getConnection(); - Statement statement = connection.createStatement();) { + Statement statement = connection.createStatement();) { connection.executeUpdate(statement, "UPDATE tsk_files " //NON-NLS + "SET known='" + fileKnown.getFileKnownValue() + "' " //NON-NLS + "WHERE obj_id=" + id); //NON-NLS @@ -10627,7 +10462,7 @@ public void setFileUnalloc(AbstractFile file) throws TskCoreException { acquireSingleUserCaseWriteLock(); try (CaseDbConnection connection = connections.getConnection(); - Statement statement = connection.createStatement();) { + Statement statement = connection.createStatement();) { connection.executeUpdate(statement, String.format("UPDATE tsk_files SET meta_flags = '%d', dir_flags = '%d' WHERE obj_id = %d", newMetaFlgs, newDirFlags, file.getId())); file.removeMetaFlag(TSK_FS_META_FLAG_ENUM.ALLOC); @@ -10717,7 +10552,7 @@ String getMd5ImageHash(Image img) throws TskCoreException { acquireSingleUserCaseReadLock(); try { connection = connections.getConnection(); - + PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.SELECT_IMAGE_MD5); statement.clearParameters(); statement.setLong(1, id); @@ -10781,7 +10616,7 @@ String getSha1ImageHash(Image img) throws TskCoreException { acquireSingleUserCaseReadLock(); try { connection = connections.getConnection(); - + PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.SELECT_IMAGE_SHA1); statement.clearParameters(); statement.setLong(1, id); @@ -10845,7 +10680,7 @@ String getSha256ImageHash(Image img) throws TskCoreException { acquireSingleUserCaseReadLock(); try { connection = connections.getConnection(); - + PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.SELECT_IMAGE_SHA256); statement.clearParameters(); statement.setLong(1, id); @@ -10957,7 +10792,7 @@ String getAcquisitionDetails(DataSource datasource) throws TskCoreException { acquireSingleUserCaseReadLock(); try { connection = connections.getConnection(); - + PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.SELECT_ACQUISITION_DETAILS); statement.clearParameters(); statement.setLong(1, id); @@ -10993,7 +10828,7 @@ String getDataSourceInfoString(DataSource datasource, String columnName) throws acquireSingleUserCaseReadLock(); try { connection = connections.getConnection(); - + PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.SELECT_ACQUISITION_TOOL_SETTINGS); statement.clearParameters(); statement.setLong(1, id); @@ -11029,7 +10864,7 @@ Long getDataSourceInfoLong(DataSource datasource, String columnName) throws TskC acquireSingleUserCaseReadLock(); try { connection = connections.getConnection(); - + PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.SELECT_ACQUISITION_TOOL_SETTINGS); statement.clearParameters(); statement.setLong(1, id); @@ -11063,7 +10898,7 @@ public void setReviewStatus(BlackboardArtifact artifact, BlackboardArtifact.Revi } acquireSingleUserCaseWriteLock(); try (CaseDbConnection connection = connections.getConnection(); - Statement statement = connection.createStatement();) { + Statement statement = connection.createStatement();) { connection.executeUpdate(statement, "UPDATE blackboard_artifacts " + " SET review_status_id=" + newStatus.getID() + " WHERE blackboard_artifacts.artifact_id = " + artifact.getArtifactID()); @@ -11136,7 +10971,7 @@ public List<AbstractFile> findFilesByMd5(String md5Hash) { if (md5Hash == null) { return Collections.<AbstractFile>emptyList(); } - + CaseDbConnection connection = null; Statement s = null; ResultSet rs = null; @@ -11167,7 +11002,7 @@ public List<AbstractFile> findFilesByMd5(String md5Hash) { */ public boolean allFilesMd5Hashed() { boolean allFilesAreHashed = false; - + CaseDbConnection connection = null; Statement s = null; ResultSet rs = null; @@ -11198,9 +11033,9 @@ public boolean allFilesMd5Hashed() { * * @return the number of files with an MD5 hash */ - public int countFilesMd5Hashed() { + public int countFilesMd5Hashed() { int count = 0; - + acquireSingleUserCaseReadLock(); CaseDbConnection connection = null; Statement s = null; @@ -11240,7 +11075,7 @@ public List<TagName> getAllTagNames() throws TskCoreException { acquireSingleUserCaseReadLock(); try { connection = connections.getConnection(); - + // SELECT * FROM tag_names PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.SELECT_TAG_NAMES); resultSet = connection.executeQuery(statement); @@ -11276,7 +11111,7 @@ public List<TagName> getTagNamesInUse() throws TskCoreException { acquireSingleUserCaseReadLock(); try { connection = connections.getConnection(); - + // SELECT * FROM tag_names WHERE tag_name_id IN (SELECT tag_name_id from content_tags UNION SELECT tag_name_id FROM blackboard_artifact_tags) PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.SELECT_TAG_NAMES_IN_USE); resultSet = connection.executeQuery(statement); @@ -11321,7 +11156,7 @@ public List<TagName> getTagNamesInUse(long dsObjId) throws TskCoreException { acquireSingleUserCaseReadLock(); try { connection = connections.getConnection(); - + PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.SELECT_TAG_NAMES_IN_USE_BY_DATASOURCE); statement.setLong(1, dsObjId); statement.setLong(2, dsObjId); @@ -11351,8 +11186,8 @@ public List<TagName> getTagNamesInUse(long dsObjId) throws TskCoreException { * @return A TagName data transfer object (DTO) for the new row. * * @throws TskCoreException - * @deprecated TaggingManager.addOrUpdateTagName should be used instead - * with the default knowStatus of TskData.FileKnown.UNKNOWN + * @deprecated TaggingManager.addOrUpdateTagName should be used instead with + * the default knowStatus of TskData.FileKnown.UNKNOWN */ @Deprecated @SuppressWarnings("deprecation") @@ -11373,7 +11208,8 @@ public TagName addTagName(String displayName, String description, TagName.HTML_C * @return A TagName data transfer object (DTO) for the new row. * * @throws TskCoreException - * @deprecated This method has been replaced by TaggingManager.addOrUpdateTagName. + * @deprecated This method has been replaced by + * TaggingManager.addOrUpdateTagName. */ @Deprecated public TagName addOrUpdateTagName(String displayName, String description, TagName.HTML_COLOR color, TskData.FileKnown knownStatus) throws TskCoreException { @@ -11412,15 +11248,15 @@ public void deleteContentTag(ContentTag tag) throws TskCoreException { statement.clearParameters(); statement.setLong(1, tag.getId()); trans.getConnection().executeUpdate(statement); - + // update the aggregate score for the content Long contentId = tag.getContent() != null ? tag.getContent().getId() : null; - Long dataSourceId = tag.getContent() != null && tag.getContent().getDataSource() != null - ? tag.getContent().getDataSource().getId() + Long dataSourceId = tag.getContent() != null && tag.getContent().getDataSource() != null + ? tag.getContent().getDataSource().getId() : null; - + this.getScoringManager().updateAggregateScoreAfterDeletion(contentId, dataSourceId, trans); - + trans.commit(); trans = null; } catch (SQLException ex) { @@ -11446,7 +11282,7 @@ public List<ContentTag> getAllContentTags() throws TskCoreException { acquireSingleUserCaseReadLock(); try { connection = connections.getConnection(); - + // SELECT content_tags.tag_id, content_tags.obj_id, content_tags.tag_name_id, content_tags.comment, content_tags.begin_byte_offset, content_tags.end_byte_offset, tag_names.display_name, tag_names.description, tag_names.color, tag_names.knownStatus, tsk_examiners.login_name // FROM content_tags // INNER JOIN tag_names ON content_tags.tag_name_id = tag_names.tag_name_id @@ -11491,7 +11327,7 @@ public long getContentTagsCountByTagName(TagName tagName) throws TskCoreExceptio acquireSingleUserCaseReadLock(); try { connection = connections.getConnection(); - + // SELECT COUNT(*) AS count FROM content_tags WHERE tag_name_id = ? PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.COUNT_CONTENT_TAGS_BY_TAG_NAME); statement.clearParameters(); @@ -11537,7 +11373,7 @@ public long getContentTagsCountByTagName(TagName tagName, long dsObjId) throws T acquireSingleUserCaseReadLock(); try { connection = connections.getConnection(); - + // "SELECT COUNT(*) AS count FROM content_tags as content_tags, tsk_files as tsk_files WHERE content_tags.obj_id = tsk_files.obj_id" // + " AND content_tags.tag_name_id = ? " // + " AND tsk_files.data_source_obj_id = ? " @@ -11579,7 +11415,7 @@ public ContentTag getContentTagByID(long contentTagID) throws TskCoreException { acquireSingleUserCaseReadLock(); try { connection = connections.getConnection(); - + // SELECT content_tags.tag_id, content_tags.obj_id, content_tags.tag_name_id, content_tags.comment, content_tags.begin_byte_offset, content_tags.end_byte_offset, tag_names.display_name, tag_names.description, tag_names.color, tag_names.knownStatus, tsk_examiners.login_name // FROM content_tags // INNER JOIN tag_names ON content_tags.tag_name_id = tag_names.tag_name_id @@ -11629,7 +11465,7 @@ public List<ContentTag> getContentTagsByTagName(TagName tagName) throws TskCoreE acquireSingleUserCaseReadLock(); try { connection = connections.getConnection(); - + // SELECT content_tags.tag_id, content_tags.obj_id, content_tags.tag_name_id, content_tags.comment, content_tags.begin_byte_offset, content_tags.end_byte_offset, tsk_examiners.login_name // FROM content_tags // LEFT OUTER JOIN tsk_examiners ON content_tags.examiner_id = tsk_examiners.examiner_id @@ -11720,7 +11556,7 @@ public List<ContentTag> getContentTagsByContent(Content content) throws TskCoreE acquireSingleUserCaseReadLock(); try { connection = connections.getConnection(); - + // SELECT content_tags.tag_id, content_tags.obj_id, content_tags.tag_name_id, content_tags.comment, content_tags.begin_byte_offset, content_tags.end_byte_offset, tag_names.display_name, tag_names.description, tag_names.color, tag_names.knownStatus, tsk_examiners.login_name // FROM content_tags // INNER JOIN tag_names ON content_tags.tag_name_id = tag_names.tag_name_id @@ -11781,15 +11617,15 @@ public void deleteBlackboardArtifactTag(BlackboardArtifactTag tag) throws TskCor statement.clearParameters(); statement.setLong(1, tag.getId()); trans.getConnection().executeUpdate(statement); - + // update the aggregate score for the artifact Long artifactObjId = tag.getArtifact().getId(); - Long dataSourceId = tag.getContent() != null && tag.getContent().getDataSource() != null - ? tag.getContent().getDataSource().getId() + Long dataSourceId = tag.getContent() != null && tag.getContent().getDataSource() != null + ? tag.getContent().getDataSource().getId() : null; - + this.getScoringManager().updateAggregateScoreAfterDeletion(artifactObjId, dataSourceId, trans); - + trans.commit(); trans = null; } catch (SQLException ex) { @@ -11816,7 +11652,7 @@ public List<BlackboardArtifactTag> getAllBlackboardArtifactTags() throws TskCore acquireSingleUserCaseReadLock(); try { connection = connections.getConnection(); - + // SELECT blackboard_artifact_tags.tag_id, blackboard_artifact_tags.artifact_id, blackboard_artifact_tags.tag_name_id, blackboard_artifact_tags.comment, tag_names.display_name, tag_names.description, tag_names.color, tag_names.knownStatus, tsk_examiners.login_name // FROM blackboard_artifact_tags // INNER JOIN tag_names ON blackboard_artifact_tags.tag_name_id = tag_names.tag_name_id @@ -11863,7 +11699,7 @@ public long getBlackboardArtifactTagsCountByTagName(TagName tagName) throws TskC acquireSingleUserCaseReadLock(); try { connection = connections.getConnection(); - + // SELECT COUNT(*) AS count FROM blackboard_artifact_tags WHERE tag_name_id = ? PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.COUNT_ARTIFACTS_BY_TAG_NAME); statement.clearParameters(); @@ -11908,7 +11744,7 @@ public long getBlackboardArtifactTagsCountByTagName(TagName tagName, long dsObjI acquireSingleUserCaseReadLock(); try { connection = connections.getConnection(); - + // "SELECT COUNT(*) AS count FROM blackboard_artifact_tags as artifact_tags, blackboard_artifacts AS arts WHERE artifact_tags.artifact_id = arts.artifact_id" // + " AND artifact_tags.tag_name_id = ?" // + " AND arts.data_source_obj_id = ? " @@ -11951,7 +11787,7 @@ public List<BlackboardArtifactTag> getBlackboardArtifactTagsByTagName(TagName ta acquireSingleUserCaseReadLock(); try { connection = connections.getConnection(); - + // SELECT blackboard_artifact_tags.tag_id, blackboard_artifact_tags.artifact_id, blackboard_artifact_tags.tag_name_id, blackboard_artifact_tags.comment, tsk_examiners.login_name // FROM blackboard_artifact_tags // LEFT OUTER JOIN tsk_examiners ON blackboard_artifact_tags.examiner_id = tsk_examiners.examiner_id @@ -12003,7 +11839,7 @@ public List<BlackboardArtifactTag> getBlackboardArtifactTagsByTagName(TagName ta acquireSingleUserCaseReadLock(); try { connection = connections.getConnection(); - + // SELECT artifact_tags.tag_id, artifact_tags.artifact_id, artifact_tags.tag_name_id, artifact_tags.comment, arts.obj_id, arts.artifact_obj_id, arts.data_source_obj_id, arts.artifact_type_id, arts.review_status_id, tsk_examiners.login_name // FROM blackboard_artifact_tags as artifact_tags, blackboard_artifacts AS arts // LEFT OUTER JOIN tsk_examiners ON artifact_tags.examiner_id = tsk_examiners.examiner_id @@ -12053,7 +11889,7 @@ public BlackboardArtifactTag getBlackboardArtifactTagByID(long artifactTagID) th acquireSingleUserCaseReadLock(); try { connection = connections.getConnection(); - + //SELECT blackboard_artifact_tags.tag_id, blackboard_artifact_tags.artifact_id, blackboard_artifact_tags.tag_name_id, blackboard_artifact_tags.comment, tag_names.display_name, tag_names.description, tag_names.color, tag_names.knownStatus, tsk_examiners.login_name // FROM blackboard_artifact_tags // INNER JOIN tag_names ON blackboard_artifact_tags.tag_name_id = tag_names.tag_name_id @@ -12103,7 +11939,7 @@ public List<BlackboardArtifactTag> getBlackboardArtifactTagsByArtifact(Blackboar acquireSingleUserCaseReadLock(); try { connection = connections.getConnection(); - + // SELECT blackboard_artifact_tags.tag_id, blackboard_artifact_tags.artifact_id, blackboard_artifact_tags.tag_name_id, blackboard_artifact_tags.comment, tag_names.display_name, tag_names.description, tag_names.color, tag_names.knownStatus, tsk_examiners.login_name // FROM blackboard_artifact_tags // INNER JOIN tag_names ON blackboard_artifact_tags.tag_name_id = tag_names.tag_name_id @@ -12270,7 +12106,7 @@ public List<Report> getAllReports() throws TskCoreException { acquireSingleUserCaseReadLock(); try { connection = connections.getConnection(); - + // SELECT * FROM reports statement = connection.getPreparedStatement(PREPARED_STATEMENT.SELECT_REPORTS); parentStatement = connection.createStatement(); @@ -12335,7 +12171,7 @@ public Report getReportById(long id) throws TskCoreException { acquireSingleUserCaseReadLock(); try { connection = connections.getConnection(); - + // SELECT * FROM reports WHERE obj_id = ? statement = connection.getPreparedStatement(PREPARED_STATEMENT.SELECT_REPORT_BY_ID); parentStatement = connection.createStatement(); @@ -12422,13 +12258,13 @@ static void closeStatement(Statement statement) { } } } - + static void closeConnection(CaseDbConnection connection) { if (connection != null) { connection.close(); } } - + private static void rollbackTransaction(CaseDbConnection connection) { if (connection != null) { connection.rollbackTransaction(); @@ -12458,7 +12294,7 @@ void setIngestJobEndDateTime(long ingestJobId, long endDateTime) throws TskCoreE void setIngestJobStatus(long ingestJobId, IngestJobStatusType status) throws TskCoreException { acquireSingleUserCaseWriteLock(); try (CaseDbConnection connection = connections.getConnection(); - Statement statement = connection.createStatement();) { + Statement statement = connection.createStatement();) { statement.executeUpdate("UPDATE ingest_jobs SET status_id=" + status.ordinal() + " WHERE ingest_job_id=" + ingestJobId + ";"); } catch (SQLException ex) { throw new TskCoreException("Error ingest job status (ingest_job_id = " + ingestJobId + ".", ex); @@ -12679,6 +12515,46 @@ String getInsertOrIgnoreSQL(String sql) { } } + /** + * Returns a list of Blackboard artifact whoes values in dbColumn match the + * list of values. The method will generate an SQL OR statement that can be + * used as part of a where clause to retrieve artifacts for a set of values. + * + * For example getArtifactsForValues("artifacts.artifact_obj_id", + * artifactObjIdList) will return a list of artifacts for the artifactObjID + * values in the given list. + * + * When using this method be sure to use the tables as nicknamed in + * DATA_ARTIFACT_QUERY_STRING and ANALYSIS_RESULT_QUERY_STRING; + * + * @param category The type of artifacts to return. + * @param dbColumn The database column. + * @param value List of values. + * + * @return A list of BlackboardArtifacts + * + * @throws TskCoreException + */ + private List<? extends BlackboardArtifact> getArtifactsForValues(BlackboardArtifact.Category category, String dbColumn, List<? extends Number> values, CaseDbConnection connection) throws TskCoreException { + String where = ""; + // This look creates the OR statment with the following format: + // <dbColumn> = <value> OR <dbColumn> = <value2> OR ... + for (Number value : values) { + if (!where.isEmpty()) { + where += " OR "; + } + where += dbColumn + " = " + value; + } + + // Base on the category pass the OR statement to the approprate method + // that will retrieve the artifacts. + if (category == BlackboardArtifact.Category.DATA_ARTIFACT) { + return blackboard.getDataArtifactsWhere(where, connection); + } else { + return blackboard.getAnalysisResultsWhere(where, connection); + } + } + /** * Stores a pair of object ID and its type */ @@ -13781,7 +13657,7 @@ private CaseDbQuery(String query, boolean allowWriteQuery) throws TskCoreExcepti throw new TskCoreException("Unsupported query: Only SELECT queries are supported."); } } - + SleuthkitCase.this.acquireSingleUserCaseReadLock(); try { connection = connections.getConnection(); @@ -13952,7 +13828,7 @@ public long getLastObjectId() throws TskCoreException { acquireSingleUserCaseReadLock(); try { connection = connections.getConnection(); - + // SELECT MAX(obj_id) AS max_obj_id FROM tsk_objects PreparedStatement statement = connection.getPreparedStatement(PREPARED_STATEMENT.SELECT_MAX_OBJECT_ID); rs = connection.executeQuery(statement); @@ -14073,7 +13949,7 @@ public ArrayList<BlackboardArtifact.ARTIFACT_TYPE> getBlackboardArtifactTypes() * * @throws TskCoreException If there is an error adding the type to the case * database. - * @deprecated Use SleuthkitCase.addBlackboardArtifactType instead. + * @deprecated Use SleuthkitCase.addBlackboardArtifactType() instead. */ @Deprecated public int addArtifactType(String artifactTypeName, String displayName) throws TskCoreException { @@ -14095,7 +13971,7 @@ public int addArtifactType(String artifactTypeName, String displayName) throws T * * @throws TskCoreException If there is an error adding the type to the case * database. - * @deprecated Use SleuthkitCase.addArtifactAttributeType instead. + * @deprecated Use SleuthkitCase.addArtifactAttributeType() instead. */ @Deprecated public int addAttrType(String attrTypeString, String displayName) throws TskCoreException { @@ -14114,7 +13990,7 @@ public int addAttrType(String attrTypeString, String displayName) throws TskCore * @return An attribute id or -1 if the attribute type does not exist. * * @throws TskCoreException If an error occurs accessing the case database. - * @deprecated Use SleuthkitCase.getAttributeType instead. + * @deprecated Use SleuthkitCase.getAttributeType() instead. */ @Deprecated public int getAttrTypeID(String attrTypeName) throws TskCoreException { @@ -14525,7 +14401,7 @@ public Collection<FileSystem> getFileSystems(Image image) { return new ArrayList<>(); } } - + /** * Find all files in the data source, by name and parent * @@ -14540,7 +14416,7 @@ public Collection<FileSystem> getFileSystems(Image image) { * parentFile. * * @throws org.sleuthkit.datamodel.TskCoreException - * + * * @deprecated Use findFilesInFolder() */ @Deprecated diff --git a/bindings/java/src/org/sleuthkit/datamodel/TimelineEventType.java b/bindings/java/src/org/sleuthkit/datamodel/TimelineEventType.java index 9eb81b796f8c12a3859ec0350bdd5de77f750ef2..4779e1a30e44f94b99b4d209950c0ad4e5e69cd4 100644 --- a/bindings/java/src/org/sleuthkit/datamodel/TimelineEventType.java +++ b/bindings/java/src/org/sleuthkit/datamodel/TimelineEventType.java @@ -212,8 +212,7 @@ public SortedSet< TimelineEventType> getChildren() { @Override public SortedSet< TimelineEventType> getChildren() { return ImmutableSortedSet.of(WEB_DOWNLOADS, WEB_COOKIE, - WEB_COOKIE_ACCESSED, - WEB_COOKIE_END, WEB_BOOKMARK, + WEB_COOKIE_ACCESSED, WEB_BOOKMARK, WEB_HISTORY, WEB_SEARCH, WEB_FORM_AUTOFILL, WEB_FORM_ADDRESSES, WEB_FORM_ADDRESSES_MODIFIED, WEB_FORM_AUTOFILL_ACCESSED, WEB_CACHE, WEB_HISTORY_CREATED); @@ -657,12 +656,7 @@ public SortedSet<TimelineEventType> getChildren() { new Type(TSK_DATETIME_ACCESSED), new Type(TSK_URL)); - TimelineEventType WEB_COOKIE_END = new URLArtifactEventType(42, - getBundle().getString("WebTypes.webCookiesEnd.name"),// NON-NLS - WEB_ACTIVITY, - new BlackboardArtifact.Type(TSK_WEB_COOKIE), - new Type(TSK_DATETIME_END), - new Type(TSK_URL)); +// WEB_COOKIE_END was id 42, but has been removed. TimelineEventType BACKUP_EVENT_START = new TimelineEventArtifactTypeImpl(43, getBundle().getString("TimelineEventType.BackupEventStart.txt"),// NON-NLS diff --git a/bindings/java/src/org/sleuthkit/datamodel/TskData.java b/bindings/java/src/org/sleuthkit/datamodel/TskData.java index 805770328239cff06fe6536eecb206862ede928b..2431930c45555769e7e864f7fff8b31c9a257b82 100644 --- a/bindings/java/src/org/sleuthkit/datamodel/TskData.java +++ b/bindings/java/src/org/sleuthkit/datamodel/TskData.java @@ -29,7 +29,7 @@ */ public class TskData { - private static ResourceBundle bundle = ResourceBundle.getBundle("org.sleuthkit.datamodel.Bundle"); + private final static ResourceBundle bundle = ResourceBundle.getBundle("org.sleuthkit.datamodel.Bundle"); /** * The type of the file system file, as reported in the name structure of @@ -625,22 +625,24 @@ public String getName() { */ public enum ObjectType { - IMG(0), ///< Disk Image - see tsk_image_info for more details - VS(1), ///< Volume System - see tsk_vs_info for more details - VOL(2), ///< Volume - see tsk_vs_parts for more details - FS(3), ///< File System - see tsk_fs_info for more details - ABSTRACTFILE(4), ///< File - see tsk_files for more details - ARTIFACT(5), /// Artifact - see blackboard_artifacts for more details - REPORT(6), ///< Report - see reports for more details - POOL(7), ///< Pool - OS_ACCOUNT(8), ///< OS Account - see tsk_os_accounts for more details - HOST_ADDRESS(9), ///< Host Address - see tsk_host_addresses for more details - UNSUPPORTED(-1) ///< Unsupported type + IMG(0, bundle.getString("TskData.ObjectType.IMG.name")), ///< Disk Image - see tsk_image_info for more details + VS(1, bundle.getString("TskData.ObjectType.VS.name")), ///< Volume System - see tsk_vs_info for more details + VOL(2, bundle.getString("TskData.ObjectType.VOL.name")), ///< Volume - see tsk_vs_parts for more details + FS(3, bundle.getString("TskData.ObjectType.FS.name")), ///< File System - see tsk_fs_info for more details + ABSTRACTFILE(4, bundle.getString("TskData.ObjectType.AbstractFile.name")), ///< File - see tsk_files for more details + ARTIFACT(5, bundle.getString("TskData.ObjectType.Artifact.name")), /// Artifact - see blackboard_artifacts for more details + REPORT(6, bundle.getString("TskData.ObjectType.Report.name")), ///< Report - see reports for more details + POOL(7, bundle.getString("TskData.ObjectType.Pool.name")), ///< Pool + OS_ACCOUNT(8, bundle.getString("TskData.ObjectType.OsAccount.name")), ///< OS Account - see tsk_os_accounts for more details + HOST_ADDRESS(9, bundle.getString("TskData.ObjectType.HostAddress.name")), ///< Host Address - see tsk_host_addresses for more details + UNSUPPORTED(-1, bundle.getString("TskData.ObjectType.Unsupported.name")) ///< Unsupported type ; private final short objectType; + private final String displayName; - private ObjectType(int objectType) { + private ObjectType(int objectType, String displayName) { this.objectType = (short) objectType; + this.displayName = displayName; } /** @@ -651,6 +653,11 @@ private ObjectType(int objectType) { public short getObjectType() { return objectType; } + + @Override + public String toString() { + return displayName; + } /** * Convert object type short value to the enum type diff --git a/bindings/java/src/org/sleuthkit/datamodel/WindowsAccountUtils.java b/bindings/java/src/org/sleuthkit/datamodel/WindowsAccountUtils.java index 102a73e22859a6a5231e174627df960a3f818df4..f9c2c972e350389eb42a7e7047d303d40436e89b 100644 --- a/bindings/java/src/org/sleuthkit/datamodel/WindowsAccountUtils.java +++ b/bindings/java/src/org/sleuthkit/datamodel/WindowsAccountUtils.java @@ -18,7 +18,11 @@ */ package org.sleuthkit.datamodel; +import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Optional; import java.util.Set; /** @@ -36,12 +40,14 @@ final class WindowsAccountUtils { final static String SPECIAL_WINDOWS_BACK_UP_POSTFIX = ".bak"; + // Windows sometimes uses a special NULL sid, when a users actual SID is unknown. + // Our SID comparisons should ignore it, and treat it as a null/blank. + final static String WINDOWS_NULL_SID = "S-1-0-0"; // Windows uses SIDs for groups as well as users. // We dont want to create "User" account for group SIDs. // The lists here help us identify and weed out group SIDs when creating accounts. private static final Set<String> GROUP_SIDS = ImmutableSet.of( - "S-1-0-0", // Null SID "S-1-1-0", // Everyone "S-1-2-0", // Local - anyone who has logged on locally "S-1-2-1", // Console Logon @@ -72,7 +78,8 @@ final class WindowsAccountUtils { // Any SIDs with the following prefixes are group SID and should be excluded. private static final Set<String> GROUP_SID_PREFIX = ImmutableSet.of( - "S-1-5-32" // Builtin + "S-1-5-32", // Builtin + "S-1-5-87" // Task ID prefix ); @@ -110,20 +117,21 @@ final class WindowsAccountUtils { // Some windows SID indicate special account. // These should be handled differently from regular user accounts. - private static final Set<String> SPECIAL_SIDS = ImmutableSet.of( - "S-1-5-18", // LOCAL_SYSTEM_ACCOUNT - "S-1-5-19", // LOCAL_SERVICE_ACCOUNT - "S-1-5-20" // NETWORK_SERVICE_ACCOUNT - ); - private static final Set<String> SPECIAL_SID_PREFIXES = ImmutableSet.of( - "S-1-5-80", // Virtual Service accounts - "S-1-5-82", // AppPoolIdentity Virtual accounts. - "S-1-5-83", // Virtual Machine Virtual Accounts. - "S-1-5-90", // Windows Manager Virtual Accounts. - "S-1-5-96" // Font Drive Host Virtual Accounts. - ); - - + private static final Map<String, String> SPECIAL_SIDS_MAP = ImmutableMap.<String, String>builder() + .put("S-1-5-18", "Local System Account") + .put("S-1-5-19", "Local Service Account") + .put("S-1-5-20", "Network Service Account") + .build(); + + private static final Map<String, String> SPECIAL_SID_PREFIXES_MAP = ImmutableMap.<String, String>builder() + .put("S-1-5-80", "Service Virtual Account") + .put("S-1-5-82", "IIS AppPool Virtual Account") + .put("S-1-5-83", "Virtual Machine Virtual Account") + .put("S-1-5-90", "Window Manager Virtual Account") + .put("S-1-5-94", "WinRM Virtual accountt") + .put("S-1-5-96", "Font Driver Host Virtual Account") + .build(); + /** * Checks if the given SID is a special Windows SID. * @@ -134,17 +142,47 @@ final class WindowsAccountUtils { static boolean isWindowsSpecialSid(String sid) { String tempSID = stripWindowsBackupPostfix(sid); - if (SPECIAL_SIDS.contains(tempSID)) { + if (SPECIAL_SIDS_MAP.containsKey(tempSID)) { return true; } - for (String specialPrefix: SPECIAL_SID_PREFIXES) { + for (String specialPrefix: SPECIAL_SID_PREFIXES_MAP.keySet()) { if (tempSID.startsWith(specialPrefix)) { return true; } } + + // All the prefixes in the range S-1-5-80 to S-1-5-111 are special + tempSID = tempSID.replaceFirst(DOMAIN_SID_PREFIX + "-", ""); + String subAuthStr = tempSID.substring(0, tempSID.indexOf('-')); + Integer subAuth = Optional.ofNullable(subAuthStr).map(Integer::valueOf).orElse(0); + if (subAuth >= 80 && subAuth <= 111) { + return true; + } + + return false; } + /** + * Get the name for the given special Windows SID. + * + * @param sid SID to check. + * + * @return Name for Windows special SID, an empty string if the SID is not a known special SID. + */ + static String getWindowsSpecialSidName(String sid) { + String tempSID = stripWindowsBackupPostfix(sid); + + if (SPECIAL_SIDS_MAP.containsKey(tempSID)) { + return SPECIAL_SIDS_MAP.get(tempSID); + } + for (Entry<String, String> specialPrefixEntry: SPECIAL_SID_PREFIXES_MAP.entrySet()) { + if (tempSID.startsWith(specialPrefixEntry.getKey())) { + return specialPrefixEntry.getValue(); + } + } + return ""; + } /** * Checks if the given SID is a user SID. diff --git a/bindings/java/src/org/sleuthkit/datamodel/blackboardutils/ArtifactHelperBase.java b/bindings/java/src/org/sleuthkit/datamodel/blackboardutils/ArtifactHelperBase.java index 426061af68d7aa82bbbd60bac035d7ab2eeda32e..cb022c3b7221f0b84bb53bc78823223c162dd99a 100644 --- a/bindings/java/src/org/sleuthkit/datamodel/blackboardutils/ArtifactHelperBase.java +++ b/bindings/java/src/org/sleuthkit/datamodel/blackboardutils/ArtifactHelperBase.java @@ -75,8 +75,8 @@ String getModuleName() { } /** - * Creates and adds a string attribute of specified type to the given list, if the - * attribute value is not empty or null. + * Creates and adds a string attribute of specified type to the given list, + * if the attribute value is not empty or null. * * @param attributeType Attribute type. * @param attrValue String attribute value. @@ -90,8 +90,8 @@ void addAttributeIfNotNull(BlackboardAttribute.ATTRIBUTE_TYPE attributeType, Str } /** - * Creates and adds a long attribute of specified type to the given list, if the - * attribute value is not 0. + * Creates and adds a long attribute of specified type to the given list, if + * the attribute value is not 0. * * @param attributeType Attribute type. * @param attrValue Long attribute value. @@ -102,10 +102,10 @@ void addAttributeIfNotZero(BlackboardAttribute.ATTRIBUTE_TYPE attributeType, lon attributes.add(new BlackboardAttribute(attributeType, getModuleName(), attrValue)); } } - + /** - * Creates and adds an integer attribute of specified type to the given list, if the - * attribute value is not 0. + * Creates and adds an integer attribute of specified type to the given + * list, if the attribute value is not 0. * * @param attributeType Attribute type. * @param attrValue Integer attribute value. diff --git a/bindings/java/src/org/sleuthkit/datamodel/blackboardutils/ArtifactsHelper.java b/bindings/java/src/org/sleuthkit/datamodel/blackboardutils/ArtifactsHelper.java index dbd5eee7fb219d3f87d5f7db1e8c05c1e9dd4b18..284e8a2e42e6b1268e593ef0a8b56f4712822e46 100644 --- a/bindings/java/src/org/sleuthkit/datamodel/blackboardutils/ArtifactsHelper.java +++ b/bindings/java/src/org/sleuthkit/datamodel/blackboardutils/ArtifactsHelper.java @@ -37,8 +37,9 @@ * */ public final class ArtifactsHelper extends ArtifactHelperBase { + private static final BlackboardArtifact.Type INSTALLED_PROG_TYPE = new BlackboardArtifact.Type(ARTIFACT_TYPE.TSK_INSTALLED_PROG); - + /** * Creates an artifact helper for modules to create artifacts. * @@ -59,7 +60,7 @@ public ArtifactsHelper(SleuthkitCase caseDb, String moduleName, Content srcConte * * @return Installed program artifact added. * - * @throws TskCoreException If there is an error creating the artifact. + * @throws TskCoreException If there is an error creating the artifact. * @throws BlackboardException If there is a problem posting the artifact. */ public BlackboardArtifact addInstalledProgram(String programName, long dateInstalled) throws TskCoreException, BlackboardException { @@ -78,14 +79,14 @@ public BlackboardArtifact addInstalledProgram(String programName, long dateInsta * * @return Installed program artifact added. * - * @throws TskCoreException If there is an error creating the artifact. + * @throws TskCoreException If there is an error creating the artifact. * @throws BlackboardException If there is a problem posting the artifact. */ public BlackboardArtifact addInstalledProgram(String programName, long dateInstalled, Collection<BlackboardAttribute> otherAttributesList) throws TskCoreException, BlackboardException { Collection<BlackboardAttribute> attributes = new ArrayList<>(); - + // construct attributes attributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PROG_NAME, getModuleName(), programName)); addAttributeIfNotZero(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME, dateInstalled, attributes); @@ -96,7 +97,7 @@ public BlackboardArtifact addInstalledProgram(String programName, long dateInsta // create artifact Content content = getContent(); BlackboardArtifact installedProgramArtifact = content.newDataArtifact(INSTALLED_PROG_TYPE, attributes); - + // post artifact getSleuthkitCase().getBlackboard().postArtifact(installedProgramArtifact, getModuleName()); diff --git a/bindings/java/src/org/sleuthkit/datamodel/blackboardutils/CommunicationArtifactsHelper.java b/bindings/java/src/org/sleuthkit/datamodel/blackboardutils/CommunicationArtifactsHelper.java index 7778f2dcaf03a9d8666ade5426028eb2a0bfeac6..44c4fc0c0e6f802a25a624129ad5cb7f4409dffc 100644 --- a/bindings/java/src/org/sleuthkit/datamodel/blackboardutils/CommunicationArtifactsHelper.java +++ b/bindings/java/src/org/sleuthkit/datamodel/blackboardutils/CommunicationArtifactsHelper.java @@ -545,7 +545,7 @@ public BlackboardArtifact addMessage(String messageType, try { recipientAccountsList.add(createAccountInstance(moduleAccountsType, recipient)); } catch (InvalidAccountIDException ex) { - LOGGER.log(Level.WARNING, String.format("Invalid account identifier %s", senderId)); + LOGGER.log(Level.WARNING, String.format("Invalid account identifier %s", recipient)); } } } diff --git a/bindings/java/src/org/sleuthkit/datamodel/blackboardutils/GeoArtifactsHelper.java b/bindings/java/src/org/sleuthkit/datamodel/blackboardutils/GeoArtifactsHelper.java index 4e51e3681cce21334c23aca8f1942fb370e4afc2..321b18a342fef91001dd7c91c41057df42796571 100755 --- a/bindings/java/src/org/sleuthkit/datamodel/blackboardutils/GeoArtifactsHelper.java +++ b/bindings/java/src/org/sleuthkit/datamodel/blackboardutils/GeoArtifactsHelper.java @@ -41,11 +41,11 @@ public final class GeoArtifactsHelper extends ArtifactHelperBase { private static final BlackboardAttribute.Type WAYPOINTS_ATTR_TYPE = new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_WAYPOINTS); private static final BlackboardAttribute.Type TRACKPOINTS_ATTR_TYPE = new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_TRACKPOINTS); private static final BlackboardAttribute.Type AREAPOINTS_ATTR_TYPE = new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_AREAPOINTS); - + private static final BlackboardArtifact.Type GPS_TRACK_TYPE = new BlackboardArtifact.Type(BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_TRACK); private static final BlackboardArtifact.Type GPS_ROUTE_TYPE = new BlackboardArtifact.Type(BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_ROUTE); private static final BlackboardArtifact.Type GPS_AREA_TYPE = new BlackboardArtifact.Type(BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_AREA); - + private final String programName; /** @@ -171,7 +171,7 @@ public BlackboardArtifact addRoute(String routeName, Long creationTime, GeoWaypo Content content = getContent(); BlackboardArtifact artifact = content.newDataArtifact(GPS_ROUTE_TYPE, attributes); - + getSleuthkitCase().getBlackboard().postArtifact(artifact, getModuleName()); return artifact; @@ -219,7 +219,7 @@ public BlackboardArtifact addArea(String areaName, GeoAreaPoints areaPoints, Lis Content content = getContent(); BlackboardArtifact artifact = content.newDataArtifact(GPS_AREA_TYPE, attributes); - + getSleuthkitCase().getBlackboard().postArtifact(artifact, getModuleName()); return artifact; diff --git a/bindings/java/src/org/sleuthkit/datamodel/blackboardutils/WebBrowserArtifactsHelper.java b/bindings/java/src/org/sleuthkit/datamodel/blackboardutils/WebBrowserArtifactsHelper.java index 28f9b1b21d487db5463ab3f02f71891126d22576..83b45f8758a96cc0d9098c6c4413bbc53e0904b2 100644 --- a/bindings/java/src/org/sleuthkit/datamodel/blackboardutils/WebBrowserArtifactsHelper.java +++ b/bindings/java/src/org/sleuthkit/datamodel/blackboardutils/WebBrowserArtifactsHelper.java @@ -41,8 +41,8 @@ /** * Class to help ingest modules create Web Browser artifacts. * - * These include bookmarks, cookies, downloads, history, and web form - * autofill data. + * These include bookmarks, cookies, downloads, history, and web form autofill + * data. * */ public final class WebBrowserArtifactsHelper extends ArtifactHelperBase { @@ -54,7 +54,7 @@ public final class WebBrowserArtifactsHelper extends ArtifactHelperBase { private static final BlackboardArtifact.Type WEB_FORM_ADDRESS_TYPE = new BlackboardArtifact.Type(BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_FORM_ADDRESS); private static final BlackboardArtifact.Type WEB_FORM_AUTOFILL_TYPE = new BlackboardArtifact.Type(BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_FORM_AUTOFILL); private static final BlackboardArtifact.Type WEB_HISTORY_TYPE = new BlackboardArtifact.Type(BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_HISTORY); - + /** * Creates a WebBrowserArtifactsHelper. * @@ -116,7 +116,7 @@ public BlackboardArtifact addWebBookmark(String url, String title, long creation // add attributes to artifact attributes.addAll(otherAttributesList); - + Content content = getContent(); BlackboardArtifact bookMarkArtifact = content.newDataArtifact(WEB_BOOKMARK_TYPE, attributes); @@ -185,10 +185,10 @@ public BlackboardArtifact addWebCookie(String url, // add attributes to artifact attributes.addAll(otherAttributesList); - + Content content = getContent(); BlackboardArtifact cookieArtifact = content.newDataArtifact(WEB_COOKIE_TYPE, attributes); - + // post artifact getSleuthkitCase().getBlackboard().postArtifact(cookieArtifact, getModuleName()); @@ -244,10 +244,10 @@ public BlackboardArtifact addWebDownload(String url, long startTime, String path // add attributes to artifact attributes.addAll(otherAttributesList); - + Content content = getContent(); BlackboardArtifact webDownloadArtifact = content.newDataArtifact(WEB_DOWNLOAD_TYPE, attributes); - + // post artifact getSleuthkitCase().getBlackboard().postArtifact(webDownloadArtifact, getModuleName()); @@ -302,25 +302,25 @@ public BlackboardArtifact addWebFormAddress(String personName, String email, Collection<BlackboardAttribute> otherAttributesList) throws TskCoreException, BlackboardException { Collection<BlackboardAttribute> attributes = new ArrayList<>(); - + CommunicationsManager commManager = this.getSleuthkitCase().getCommunicationsManager(); - + if (StringUtils.isNotEmpty(email)) { try { - commManager.createAccountFileInstance(Account.Type.EMAIL, email, this.getModuleName(), this.getContent()); + commManager.createAccountFileInstance(Account.Type.EMAIL, email, this.getModuleName(), this.getContent()); } catch (InvalidAccountIDException ex) { LOGGER.log(Level.WARNING, String.format("Invalid account identifier %s", email), ex); } } - if(StringUtils.isNotEmpty(phoneNumber)) { + if (StringUtils.isNotEmpty(phoneNumber)) { try { - commManager.createAccountFileInstance(Account.Type.PHONE, phoneNumber, this.getModuleName(), this.getContent()); + commManager.createAccountFileInstance(Account.Type.PHONE, phoneNumber, this.getModuleName(), this.getContent()); } catch (InvalidAccountIDException ex) { LOGGER.log(Level.WARNING, String.format("Invalid account identifier %s", phoneNumber), ex); } } - + // construct attributes attributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME, getModuleName(), personName)); @@ -335,7 +335,7 @@ public BlackboardArtifact addWebFormAddress(String personName, String email, // add artifact Content content = getContent(); BlackboardArtifact webFormAddressArtifact = content.newDataArtifact(WEB_FORM_ADDRESS_TYPE, attributes); - + // post artifact getSleuthkitCase().getBlackboard().postArtifact(webFormAddressArtifact, getModuleName()); @@ -383,7 +383,7 @@ public BlackboardArtifact addWebFormAutofill(String name, String value, public BlackboardArtifact addWebFormAutofill(String name, String value, long creationTime, long accessTime, int count, Collection<BlackboardAttribute> otherAttributesList) throws TskCoreException, BlackboardException { - + Collection<BlackboardAttribute> attributes = new ArrayList<>(); // construct attributes @@ -399,7 +399,7 @@ public BlackboardArtifact addWebFormAutofill(String name, String value, Content content = getContent(); BlackboardArtifact webFormAutofillArtifact = content.newDataArtifact(WEB_FORM_AUTOFILL_TYPE, attributes); - + // post artifact getSleuthkitCase().getBlackboard().postArtifact(webFormAutofillArtifact, getModuleName()); @@ -410,12 +410,12 @@ public BlackboardArtifact addWebFormAutofill(String name, String value, /** * Adds a Web History artifact. * - * @param url Url visited, required. - * @param accessTime Last access time, may be 0 if not available. - * @param referrer Referrer, may be empty or null. - * @param title Website title, may be empty or null. - * @param programName Application/program recording the history, may be - * empty or null. + * @param url Url visited, required. + * @param accessTime Last access time, may be 0 if not available. + * @param referrer Referrer, may be empty or null. + * @param title Website title, may be empty or null. + * @param programName Application/program recording the history, may be + * empty or null. * * @return Web history artifact created. * @@ -462,10 +462,10 @@ public BlackboardArtifact addWebHistory(String url, long accessTime, // add attributes to artifact attributes.addAll(otherAttributesList); - + Content content = getContent(); BlackboardArtifact webHistoryArtifact = content.newDataArtifact(WEB_HISTORY_TYPE, attributes); - + // post artifact getSleuthkitCase().getBlackboard().postArtifact(webHistoryArtifact, getModuleName()); diff --git a/bindings/java/src/org/sleuthkit/datamodel/localization/lastupdated.properties b/bindings/java/src/org/sleuthkit/datamodel/localization/lastupdated.properties index 8d365ea9d44c2e2da3f3bb1969d089e6d5c29bad..697a9a63b3bfb902bc30333961da84c3820a413d 100644 --- a/bindings/java/src/org/sleuthkit/datamodel/localization/lastupdated.properties +++ b/bindings/java/src/org/sleuthkit/datamodel/localization/lastupdated.properties @@ -1,2 +1,2 @@ -#Thu Jul 01 12:01:30 UTC 2021 -bundles.ja.lastupdated=8e19cd639b4cbc45f216c427008de0afb2ccbe02 +#Thu Sep 30 10:23:46 UTC 2021 +bundles.ja.lastupdated=751fd41efbf6b9f6f9188615ab78a4cc999c4cbf diff --git a/bindings/java/test/org/sleuthkit/datamodel/OsAccountTest.java b/bindings/java/test/org/sleuthkit/datamodel/OsAccountTest.java index f3f9f2952b1661619ce3e8b8c78a6bbbae69130f..542ca4947cd1a6a33e4def1dcaac3f316c78c7da 100644 --- a/bindings/java/test/org/sleuthkit/datamodel/OsAccountTest.java +++ b/bindings/java/test/org/sleuthkit/datamodel/OsAccountTest.java @@ -613,6 +613,15 @@ public void basicOsAccountTests() throws TskCoreException, OsAccountManager.NotU assertEquals(osAccount11.getLoginName().orElse("").equalsIgnoreCase(loginName1), true); + // try and get the account with null sid & login name. It should use the login name to find the account + Optional<OsAccount> osAccount21 = caseDB.getOsAccountManager().getWindowsOsAccount("S-1-0-0", loginName1, realmName1, host1); + + assertTrue(osAccount21.isPresent()); + assertEquals(osAccount21.get().getAddr().orElse("").equalsIgnoreCase(ownerUid1), true); + assertEquals(caseDB.getOsAccountRealmManager().getRealmByRealmId(osAccount21.get().getRealmId()).getRealmNames().get(0).equalsIgnoreCase(realmName1), true); + assertEquals(osAccount21.get().getLoginName().orElse("").equalsIgnoreCase(loginName1), true); + + // Let's update osAccount1 String fullName1 = "Johnny Depp"; Long creationTime1 = 1611858618L; @@ -742,7 +751,13 @@ public void windowsSpecialAccountTests() throws TskCoreException, OsAccountManag String specialSid3 = "S-1-5-90-0-2"; String specialSid4 = "S-1-5-96-0-3"; - + // All accounts in the range S-1-5-80 to S-1-5-111 are special and should be created with SPECIAL_WINDOWS_REALM_ADDR + String specialSid5 = "S-1-5-99-0-3"; + String specialSid6 = "S-1-5-100-0-3"; + String specialSid7 = "S-1-5-111-0-3"; + String specialSid8 = "S-1-5-112-0-3"; // NOT SPECIAL SID + String specialSid9 = "S-1-5-79-0-3"; // NOT SPECIAL SID + OsAccount specialAccount1 = caseDB.getOsAccountManager().newWindowsOsAccount(specialSid1, null, null, host4, OsAccountRealm.RealmScope.UNKNOWN); OsAccount specialAccount2 = caseDB.getOsAccountManager().newWindowsOsAccount(specialSid2, null, null, host4, OsAccountRealm.RealmScope.UNKNOWN); OsAccount specialAccount3 = caseDB.getOsAccountManager().newWindowsOsAccount(specialSid3, null, null, host4, OsAccountRealm.RealmScope.UNKNOWN); @@ -755,7 +770,18 @@ public void windowsSpecialAccountTests() throws TskCoreException, OsAccountManag assertEquals(caseDB.getOsAccountRealmManager().getRealmByRealmId(specialAccount4.getRealmId()).getRealmAddr().orElse("").equalsIgnoreCase(SPECIAL_WINDOWS_REALM_ADDR), true); - } + OsAccount specialAccount5 = caseDB.getOsAccountManager().newWindowsOsAccount(specialSid5, null, null, host4, OsAccountRealm.RealmScope.UNKNOWN); + OsAccount specialAccount6 = caseDB.getOsAccountManager().newWindowsOsAccount(specialSid6, null, null, host4, OsAccountRealm.RealmScope.UNKNOWN); + OsAccount specialAccount7 = caseDB.getOsAccountManager().newWindowsOsAccount(specialSid7, null, null, host4, OsAccountRealm.RealmScope.UNKNOWN); + OsAccount specialAccount8 = caseDB.getOsAccountManager().newWindowsOsAccount(specialSid8, null, null, host4, OsAccountRealm.RealmScope.UNKNOWN); + OsAccount specialAccount9 = caseDB.getOsAccountManager().newWindowsOsAccount(specialSid9, null, null, host4, OsAccountRealm.RealmScope.UNKNOWN); + + assertEquals(caseDB.getOsAccountRealmManager().getRealmByRealmId(specialAccount5.getRealmId()).getRealmAddr().orElse("").equalsIgnoreCase(SPECIAL_WINDOWS_REALM_ADDR), true); + assertEquals(caseDB.getOsAccountRealmManager().getRealmByRealmId(specialAccount6.getRealmId()).getRealmAddr().orElse("").equalsIgnoreCase(SPECIAL_WINDOWS_REALM_ADDR), true); + assertEquals(caseDB.getOsAccountRealmManager().getRealmByRealmId(specialAccount7.getRealmId()).getRealmAddr().orElse("").equalsIgnoreCase(SPECIAL_WINDOWS_REALM_ADDR), true); + assertEquals(caseDB.getOsAccountRealmManager().getRealmByRealmId(specialAccount8.getRealmId()).getRealmAddr().orElse("").equalsIgnoreCase(SPECIAL_WINDOWS_REALM_ADDR), false); // specialSid8 is NOT special. + assertEquals(caseDB.getOsAccountRealmManager().getRealmByRealmId(specialAccount9.getRealmId()).getRealmAddr().orElse("").equalsIgnoreCase(SPECIAL_WINDOWS_REALM_ADDR), false); // specialSid9 is NOT special. + } // TEST: create accounts with a invalid user SIDs - these should generate an exception { @@ -796,6 +822,34 @@ public void windowsSpecialAccountTests() throws TskCoreException, OsAccountManag // continue } + try { + // try to create account with NULL SID and null name - should fail. + String sid4 = "S-1-0-0"; // NULL SID + OsAccount osAccount4 = caseDB.getOsAccountManager().newWindowsOsAccount(sid4, null, realmName5, host5, OsAccountRealm.RealmScope.UNKNOWN); + + // above should raise an exception + assertEquals(true, false); + } + catch (TskCoreException ex) { + // continue + } + + try { + // try to create an account with "NULL SID" and valid login name. Should throw away the NULL SID And create account with login name. + String sid5 = "S-1-0-0"; // NULL SID + String loginName5 = "login5"; + OsAccount osAccount5 = caseDB.getOsAccountManager().newWindowsOsAccount(sid5, loginName5, realmName5, host5, OsAccountRealm.RealmScope.UNKNOWN); + + assertFalse(osAccount5.getAddr().isPresent()); // should NOT have a SID + assertEquals(caseDB.getOsAccountRealmManager().getRealmByRealmId(osAccount5.getRealmId()).getRealmNames().get(0).equalsIgnoreCase(realmName5), true); + assertEquals(osAccount5.getLoginName().orElse("").equalsIgnoreCase(loginName5), true); + + } + catch (OsAccountManager.NotUserSIDException ex) { + // DO NOT EXPECT this exception to be thrown here. + assertEquals(true, false); + } + } } @@ -819,11 +873,34 @@ public void osAccountInstanceTests() throws TskCoreException, OsAccountManager.N OsAccount osAccount1 = caseDB.getOsAccountManager().newWindowsOsAccount(ownerUid1, null, realmName1, host1, OsAccountRealm.RealmScope.LOCAL); // Test: add an instance - caseDB.getOsAccountManager().newOsAccountInstance(osAccount1, image, OsAccountInstance.OsAccountInstanceType.LAUNCHED); - - // Test: add an existing instance - should be a no-op. - caseDB.getOsAccountManager().newOsAccountInstance(osAccount1, image, OsAccountInstance.OsAccountInstanceType.LAUNCHED); + caseDB.getOsAccountManager().newOsAccountInstance(osAccount1, image, OsAccountInstance.OsAccountInstanceType.ACCESSED); + + // Verify + List<OsAccountInstance> account1Instances = caseDB.getOsAccountManager().getOsAccountInstances(osAccount1); + assertEquals(account1Instances.size(), 1); + assertEquals(account1Instances.get(0).getInstanceType().getId(), OsAccountInstance.OsAccountInstanceType.ACCESSED.getId()); + // Test: add an instance that already exists - with less significant instance type - this should be a no op. + caseDB.getOsAccountManager().newOsAccountInstance(osAccount1, image, OsAccountInstance.OsAccountInstanceType.REFERENCED); // since ACCESSED > REFERENCED - this should do nothing + account1Instances = caseDB.getOsAccountManager().getOsAccountInstances(osAccount1); + assertEquals(account1Instances.size(), 1); + assertEquals(account1Instances.get(0).getInstanceType().getId(), OsAccountInstance.OsAccountInstanceType.ACCESSED.getId()); + + + // Test: add an instance that already exists - with more significant instance type - this update the existing instance. + caseDB.getOsAccountManager().newOsAccountInstance(osAccount1, image, OsAccountInstance.OsAccountInstanceType.LAUNCHED); // since LAUNCHED > ACCESSED - this should update the existing instance + account1Instances = caseDB.getOsAccountManager().getOsAccountInstances(osAccount1); + assertEquals(account1Instances.size(), 1); + assertEquals(account1Instances.get(0).getInstanceType().getId(), OsAccountInstance.OsAccountInstanceType.LAUNCHED.getId()); + + + // Test: add an instance that already exists - with less significant instance type - should do nothing + caseDB.getOsAccountManager().newOsAccountInstance(osAccount1, image, OsAccountInstance.OsAccountInstanceType.REFERENCED); + caseDB.getOsAccountManager().newOsAccountInstance(osAccount1, image, OsAccountInstance.OsAccountInstanceType.ACCESSED); + account1Instances = caseDB.getOsAccountManager().getOsAccountInstances(osAccount1); + assertEquals(account1Instances.size(), 1); + assertEquals(account1Instances.get(0).getInstanceType().getId(), OsAccountInstance.OsAccountInstanceType.LAUNCHED.getId()); + // Test: create account instance on a new host String hostname2 = "host2222"; Host host2 = caseDB.getHostManager().newHost(hostname2); @@ -835,11 +912,11 @@ public void osAccountInstanceTests() throws TskCoreException, OsAccountManager.N // TBD: perhaps add some files to the case and then use one of the files as the source of attributes. - OsAccountAttribute attrib1 = osAccount1.new OsAccountAttribute(caseDB.getAttributeType(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_PASSWORD_RESET.getTypeID()), resetTime1, osAccount1, null, image); + OsAccountAttribute attrib1 = osAccount1.new OsAccountAttribute(caseDB.getBlackboard().getAttributeType(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_PASSWORD_RESET.getTypeID()), resetTime1, osAccount1, null, image); accountAttributes.add(attrib1); String hint = "HINT"; - OsAccountAttribute attrib2 = osAccount1.new OsAccountAttribute(caseDB.getAttributeType(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PASSWORD_HINT.getTypeID()), hint, osAccount1, host2, image); + OsAccountAttribute attrib2 = osAccount1.new OsAccountAttribute(caseDB.getBlackboard().getAttributeType(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PASSWORD_HINT.getTypeID()), hint, osAccount1, host2, image); accountAttributes.add(attrib2); // add attributes to account. diff --git a/bindings/java/test/org/sleuthkit/datamodel/TimelineEventTypesTest.java b/bindings/java/test/org/sleuthkit/datamodel/TimelineEventTypesTest.java index 4ae85e6b1d3eed21041682173965b1bfcc29f7a6..de4d2b3e04014c7bbf111fd8b273b6aa26a142c5 100644 --- a/bindings/java/test/org/sleuthkit/datamodel/TimelineEventTypesTest.java +++ b/bindings/java/test/org/sleuthkit/datamodel/TimelineEventTypesTest.java @@ -118,7 +118,7 @@ public void testArtifactAttributeEvents() { mapping.put(ARTIFACT_TYPE.TSK_WIFI_NETWORK, EnumSet.of(ATTRIBUTE_TYPE.TSK_DATETIME)); mapping.put(ARTIFACT_TYPE.TSK_WEB_FORM_ADDRESS, EnumSet.of(ATTRIBUTE_TYPE.TSK_DATETIME_ACCESSED, ATTRIBUTE_TYPE.TSK_DATETIME_MODIFIED)); mapping.put(ARTIFACT_TYPE.TSK_METADATA_EXIF, EnumSet.of(ATTRIBUTE_TYPE.TSK_DATETIME_CREATED)); - mapping.put(ARTIFACT_TYPE.TSK_WEB_COOKIE, EnumSet.of(ATTRIBUTE_TYPE.TSK_DATETIME_ACCESSED, ATTRIBUTE_TYPE.TSK_DATETIME_CREATED, ATTRIBUTE_TYPE.TSK_DATETIME_END)); + mapping.put(ARTIFACT_TYPE.TSK_WEB_COOKIE, EnumSet.of(ATTRIBUTE_TYPE.TSK_DATETIME_ACCESSED, ATTRIBUTE_TYPE.TSK_DATETIME_CREATED)); mapping.put(ARTIFACT_TYPE.TSK_WEB_DOWNLOAD, EnumSet.of(ATTRIBUTE_TYPE.TSK_DATETIME_ACCESSED)); mapping.put(ARTIFACT_TYPE.TSK_TL_EVENT, EnumSet.of(ATTRIBUTE_TYPE.TSK_DATETIME)); mapping.put(ARTIFACT_TYPE.TSK_METADATA, EnumSet.of(ATTRIBUTE_TYPE.TSK_DATETIME_CREATED, ATTRIBUTE_TYPE.TSK_DATETIME_MODIFIED, ATTRIBUTE_TYPE.TSK_LAST_PRINTED_DATETIME)); diff --git a/case-uco/java/nbproject/project.properties b/case-uco/java/nbproject/project.properties index 5f2078956e461645a8d47bec5e7ed657d7adc33b..d03d81642b9f752d6e148e7710fd6836c06d9232 100644 --- a/case-uco/java/nbproject/project.properties +++ b/case-uco/java/nbproject/project.properties @@ -35,7 +35,7 @@ dist.javadoc.dir=${dist.dir}/javadoc endorsed.classpath= excludes= file.reference.gson-2.8.5.jar=lib/gson-2.8.5.jar -file.reference.sleuthkit-4.11.0.jar=lib/sleuthkit-4.11.0.jar +file.reference.sleuthkit-4.11.1.jar=lib/sleuthkit-4.11.1.jar file.reference.junit-4.12.jar=lib/junit-4.12.jar file.reference.hamcrest-core-1.3.jar=lib/hamcrest-core-1.3.jar includes=** @@ -44,7 +44,7 @@ jar.compress=false jar.index=${jnlp.enabled} javac.classpath=\ ${file.reference.gson-2.8.5.jar}:\ -${file.reference.sleuthkit-4.11.0.jar} +${file.reference.sleuthkit-4.11.1.jar} # Space-separated list of extra javac options javac.compilerargs=-Xlint javac.deprecation=false diff --git a/configure.ac b/configure.ac index 24fe97e2c21f87ac41748424e45e57459e8941df..4d56a36b5a425bd1f5c5cd9c22c4bdc68af5b19c 100644 --- a/configure.ac +++ b/configure.ac @@ -4,7 +4,7 @@ dnl Process this file with autoconf to produce a configure script. AC_PREREQ(2.59) -AC_INIT(sleuthkit, 4.11.0) +AC_INIT(sleuthkit, 4.11.1) m4_include([m4/ax_pthread.m4]) dnl include the version from 1.12.1. This will work for m4_include([m4/cppunit.m4]) diff --git a/debian/changelog b/debian/changelog index d58b9047b8e9944e308a07e50856ca5b570c3f4f..f9063c027774e5ab4b10e1d04bc1a8dd0375a7af 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,4 +1,4 @@ -sleuthkit-java (4.11.0-1) unstable; urgency=medium +sleuthkit-java (4.11.1-1) unstable; urgency=medium * Initial release (Closes: #nnnn) <nnnn is the bug number of your ITP> diff --git a/debian/sleuthkit-java.install b/debian/sleuthkit-java.install index 1a26f0ab860346c33c0d5501e5453b5f1b9649c0..42f4adbcc5b6646253075329d2d8024da693a500 100644 --- a/debian/sleuthkit-java.install +++ b/debian/sleuthkit-java.install @@ -1,4 +1,4 @@ bindings/java/lib/sqlite-jdbc-3.25.2.jar /usr/share/java -bindings/java/dist/sleuthkit-4.11.0.jar /usr/share/java -case-uco/java/dist/sleuthkit-caseuco-4.11.0.jar /usr/share/java +bindings/java/dist/sleuthkit-4.11.1.jar /usr/share/java +case-uco/java/dist/sleuthkit-caseuco-4.11.1.jar /usr/share/java diff --git a/packages/sleuthkit.spec b/packages/sleuthkit.spec index 7c18c0e2ac5a916ebb72a01421c0e189ecf3c9c8..2a24796455f2183390361fc5d5aecbbdddb6e1e9 100644 --- a/packages/sleuthkit.spec +++ b/packages/sleuthkit.spec @@ -1,5 +1,5 @@ Name: sleuthkit -Version: 4.11.0 +Version: 4.11.1 Release: 1%{?dist} Summary: The Sleuth Kit (TSK) is a library and collection of command line tools that allow you to investigate volume and file system data. diff --git a/tsk/Makefile.am b/tsk/Makefile.am index 2fd5cfb5a9b86ff696db96d712d6c709ea05694e..cbfe4299d7df5617e1722a0aa7c5c85c5ba73fa6 100644 --- a/tsk/Makefile.am +++ b/tsk/Makefile.am @@ -8,7 +8,7 @@ libtsk_la_LIBADD = base/libtskbase.la img/libtskimg.la \ vs/libtskvs.la fs/libtskfs.la hashdb/libtskhashdb.la \ auto/libtskauto.la pool/libtskpool.la util/libtskutil.la # current:revision:age -libtsk_la_LDFLAGS = -version-info 20:6:1 $(LIBTSK_LDFLAGS) +libtsk_la_LDFLAGS = -version-info 20:7:1 $(LIBTSK_LDFLAGS) EXTRA_DIST = tsk_tools_i.h docs/Doxyfile docs/*.dox docs/*.html \ tsk.pc.in diff --git a/tsk/auto/auto.cpp b/tsk/auto/auto.cpp index e3c7397784c6c558ab46590047fb460283a210c3..c67ebfa39895f04d3807a6fe6ec79d6d20b5ef6c 100755 --- a/tsk/auto/auto.cpp +++ b/tsk/auto/auto.cpp @@ -323,11 +323,17 @@ TskAuto::findFilesInVs(TSK_OFF_T a_start, TSK_VS_TYPE_ENUM a_vtype) // Use mm_walk to get the volumes if ((vs_info = tsk_vs_open(m_img_info, a_start, a_vtype)) == NULL) { - /* If the error code is for encryption, we will register it. Otherwise, + /* If the error code is for encryption, we will register it. + * If the error code is for multiple volume systems found, register the error + * and return without trying to load a file system. Otherwise, * ignore this error to avoid confusion if the fs_open passes. */ if (tsk_error_get_errno() == TSK_ERR_VS_ENCRYPTED) { registerError(); } + else if (tsk_error_get_errno() == TSK_ERR_VS_MULTTYPE) { + registerError(); + return 1; + } tsk_error_reset(); if(tsk_verbose) diff --git a/tsk/auto/auto_db.cpp b/tsk/auto/auto_db.cpp index 0265c72c04e7b646ec9436f2913b447658167f12..6e4864702196ce3495a2876a93933a76fb41c204 100755 --- a/tsk/auto/auto_db.cpp +++ b/tsk/auto/auto_db.cpp @@ -377,21 +377,15 @@ TskAutoDb::addUnallocatedPoolBlocksToDb(size_t & numPool) { /* Create the unallocated space files */ TSK_FS_ATTR_RUN * unalloc_runs = tsk_pool_unallocated_runs(pool_info); TSK_FS_ATTR_RUN * current_run = unalloc_runs; - vector<TSK_DB_FILE_LAYOUT_RANGE> ranges; while (current_run != NULL) { - TSK_DB_FILE_LAYOUT_RANGE tempRange(current_run->addr * pool_info->block_size, current_run->len * pool_info->block_size, 0); - - ranges.push_back(tempRange); - int64_t fileObjId = 0; - if (m_db->addUnallocBlockFile(unallocVolObjId, 0, current_run->len * pool_info->block_size, ranges, fileObjId, m_curImgId)) { + if (addUnallocBlockFileInChunks(current_run->addr * pool_info->block_size, current_run->len * pool_info->block_size, unallocVolObjId, m_curImgId) == TSK_ERR) { registerError(); tsk_fs_attr_run_free(unalloc_runs); return TSK_ERR; } current_run = current_run->next; - ranges.clear(); } tsk_fs_attr_run_free(unalloc_runs); } @@ -1325,14 +1319,10 @@ TSK_RETVAL_ENUM TskAutoDb::addUnallocVsSpaceToDb(size_t & numVsP) { return TSK_ERR; } - //create an unalloc file with unalloc part, with vs part as parent - vector<TSK_DB_FILE_LAYOUT_RANGE> ranges; + //create an unalloc file (or files) with unalloc part, with vs part as parent const uint64_t byteStart = vsInfo.offset + vsInfo.block_size * vsPart.start; const uint64_t byteLen = vsInfo.block_size * vsPart.len; - TSK_DB_FILE_LAYOUT_RANGE tempRange(byteStart, byteLen, 0); - ranges.push_back(tempRange); - int64_t fileObjId = 0; - if (m_db->addUnallocBlockFile(vsPart.objId, 0, tempRange.byteLen, ranges, fileObjId, m_curImgId) == TSK_ERR) { + if (addUnallocBlockFileInChunks(byteStart, byteLen, vsPart.objId, m_curImgId) == TSK_ERR) { registerError(); return TSK_ERR; } @@ -1357,14 +1347,56 @@ TSK_RETVAL_ENUM TskAutoDb::addUnallocImageSpaceToDb() { retImgFile = TSK_ERR; } else { - TSK_DB_FILE_LAYOUT_RANGE tempRange(0, imgSize, 0); - //add unalloc block file for the entire image + retImgFile = addUnallocBlockFileInChunks(0, imgSize, m_curImgId, m_curImgId); + } + return retImgFile; +} + +/** +* Adds unallocated block files to the database, chunking if enabled. +* +* @returns TSK_OK on success, TSK_ERR on error +*/ +TSK_RETVAL_ENUM TskAutoDb::addUnallocBlockFileInChunks(uint64_t byteStart, TSK_OFF_T totalSize, int64_t parentObjId, int64_t dataSourceObjId) { + + if (m_maxChunkSize <= 0) { + // No chunking - write the entire file + TSK_DB_FILE_LAYOUT_RANGE tempRange(byteStart, totalSize, 0); vector<TSK_DB_FILE_LAYOUT_RANGE> ranges; ranges.push_back(tempRange); int64_t fileObjId = 0; - retImgFile = m_db->addUnallocBlockFile(m_curImgId, 0, imgSize, ranges, fileObjId, m_curImgId); + return m_db->addUnallocBlockFile(parentObjId, 0, totalSize, ranges, fileObjId, dataSourceObjId); } - return retImgFile; + + // We will chunk into separate files with max size m_maxChunkSize + uint64_t maxChunkSize = (uint64_t)m_maxChunkSize; + uint64_t bytesLeft = (uint64_t)totalSize; + uint64_t startingOffset = byteStart; + uint64_t chunkSize; + vector<TSK_DB_FILE_LAYOUT_RANGE> ranges; + while (bytesLeft > 0) { + + if (maxChunkSize > bytesLeft) { + chunkSize = bytesLeft; + bytesLeft = 0; + } + else { + chunkSize = maxChunkSize; + bytesLeft -= maxChunkSize; + } + + TSK_DB_FILE_LAYOUT_RANGE tempRange(startingOffset, chunkSize, 0); + ranges.push_back(tempRange); + int64_t fileObjId = 0; + + TSK_RETVAL_ENUM retval = m_db->addUnallocBlockFile(parentObjId, 0, chunkSize, ranges, fileObjId, dataSourceObjId); + if (retval != TSK_OK) { + return retval; + } + ranges.clear(); + startingOffset += chunkSize; + } + return TSK_OK; } /** diff --git a/tsk/auto/is_image_supported.cpp b/tsk/auto/is_image_supported.cpp index a5aced7ebc18a94ec918227be28c21c85d1007b9..14f6c8e205704adc46c553dfba6f629373801cdb 100644 --- a/tsk/auto/is_image_supported.cpp +++ b/tsk/auto/is_image_supported.cpp @@ -113,6 +113,19 @@ uint8_t TskIsImageSupported::handleError() strncpy(m_unsupportedDesc, lastError->errstr, 1024); m_wasUnsupported = true; } + else if (errCode == TSK_ERR_VS_MULTTYPE) { + // errstr only contains the "MAC or DOS" part, so add more context + strncpy(m_unsupportedDesc, "Multiple volume system types found - ", 1024); + strncat(m_unsupportedDesc, lastError->errstr, 950); + m_wasUnsupported = true; + } + else if (errCode == TSK_ERR_FS_MULTTYPE) { + // errstr only contains the "UFS or NTFS" part, so add more context + strncpy(m_unsupportedDesc, "Multiple file system types found - ", 1024); + strncat(m_unsupportedDesc, lastError->errstr, 950); + m_wasUnsupported = true; + } + } return 0; } diff --git a/tsk/auto/tsk_case_db.h b/tsk/auto/tsk_case_db.h index fb6bc765644125dd5445e1a437782c7c9b3374de..4b29e156e68360371d212f111be9b21eb22e8b3a 100644 --- a/tsk/auto/tsk_case_db.h +++ b/tsk/auto/tsk_case_db.h @@ -198,6 +198,7 @@ class TskAutoDb:public TskAuto { TSK_RETVAL_ENUM addUnallocVsSpaceToDb(size_t & numVsP); TSK_RETVAL_ENUM addUnallocImageSpaceToDb(); TSK_RETVAL_ENUM addUnallocSpaceToDb(); + TSK_RETVAL_ENUM addUnallocBlockFileInChunks(uint64_t byteStart, TSK_OFF_T totalSize, int64_t parentObjId, int64_t dataSourceObjId); }; diff --git a/tsk/base/tsk_base.h b/tsk/base/tsk_base.h index 9c61526894d00d0a0b47816829be143b6a59de51..30a595d8d32867376bb785deef9e5d16d6ec5353 100644 --- a/tsk/base/tsk_base.h +++ b/tsk/base/tsk_base.h @@ -39,11 +39,11 @@ * 3.1.2b1 would be 0x03010201. Snapshot from Jan 2, 2003 would be * 0xFF030102. * See TSK_VERSION_STR for string form. */ -#define TSK_VERSION_NUM 0x041100ff +#define TSK_VERSION_NUM 0x041101ff /** Version of code in string form. See TSK_VERSION_NUM for * integer form. */ -#define TSK_VERSION_STR "4.11.0" +#define TSK_VERSION_STR "4.11.1" /* include the TSK-specific header file that we created in autoconf @@ -333,7 +333,8 @@ extern "C" { #define TSK_ERR_VS_BLK_NUM (TSK_ERR_VS | 6) #define TSK_ERR_VS_ARG (TSK_ERR_VS | 7) #define TSK_ERR_VS_ENCRYPTED (TSK_ERR_VS | 8) -#define TSK_ERR_VS_MAX 9 +#define TSK_ERR_VS_MULTTYPE (TSK_ERR_VS | 9) +#define TSK_ERR_VS_MAX 10 #define TSK_ERR_POOL_UNKTYPE (TSK_ERR_POOL | 0) #define TSK_ERR_POOL_UNSUPTYPE (TSK_ERR_IMG | 1) @@ -361,7 +362,8 @@ extern "C" { #define TSK_ERR_FS_ATTR_NOTFOUND (TSK_ERR_FS | 17) #define TSK_ERR_FS_ENCRYPTED (TSK_ERR_FS | 18) #define TSK_ERR_FS_POSSIBLY_ENCRYPTED (TSK_ERR_FS | 19) -#define TSK_ERR_FS_MAX 20 +#define TSK_ERR_FS_MULTTYPE (TSK_ERR_FS | 20) +#define TSK_ERR_FS_MAX 21 #define TSK_ERR_HDB_UNKTYPE (TSK_ERR_HDB | 0) #define TSK_ERR_HDB_UNSUPTYPE (TSK_ERR_HDB | 1) diff --git a/tsk/base/tsk_error.c b/tsk/base/tsk_error.c index cda9c369397ce2bb639415eab431cbb6a15e6fcc..0e02cc48fbc5c8de9f79560eaf481621b2bb335f 100644 --- a/tsk/base/tsk_error.c +++ b/tsk/base/tsk_error.c @@ -56,6 +56,7 @@ static const char *tsk_err_mm_str[TSK_ERR_VS_MAX] = { "Invalid sector address", "Invalid API argument", "Encryption detected", + "Multiple volume system types detected", }; static const char *tsk_err_fs_str[TSK_ERR_FS_MAX] = { @@ -79,6 +80,7 @@ static const char *tsk_err_fs_str[TSK_ERR_FS_MAX] = { "Attribute not found in file", "Encryption detected", "Possible encryption detected", + "Multiple file system types detected", // 20 }; static const char *tsk_err_hdb_str[TSK_ERR_HDB_MAX] = { diff --git a/tsk/base/tsk_unicode.c b/tsk/base/tsk_unicode.c index da34c74aef578e4a06691393f6975d8e9fb03477..61f5d029c25432ae0f878c89401085754da31f9b 100644 --- a/tsk/base/tsk_unicode.c +++ b/tsk/base/tsk_unicode.c @@ -150,12 +150,15 @@ tsk_UTF16toUTF8(TSK_ENDIAN_ENUM endian, const UTF16 ** sourceStart, TSKConversionResult result = TSKconversionOK; const UTF16 *source = *sourceStart; UTF8 *target = *targetStart; + while (source < sourceEnd) { UTF32 ch; unsigned short bytesToWrite = 0; const UTF32 byteMask = 0xBF; const UTF32 byteMark = 0x80; const UTF16 *oldSource = source; /* In case we have to back up because of target overflow. */ + + // Need at least 2 bytes ch = tsk_getu16(endian, (uint8_t *) source); source++; @@ -163,6 +166,7 @@ tsk_UTF16toUTF8(TSK_ENDIAN_ENUM endian, const UTF16 ** sourceStart, if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_HIGH_END) { /* If the 16 bits following the high surrogate are in the source buffer... */ if (source < sourceEnd) { + // Need at least 2 bytes UTF32 ch2 = tsk_getu16(endian, (uint8_t *) source); ++source; diff --git a/tsk/docs/Doxyfile b/tsk/docs/Doxyfile index d0f18c89ee26546ab484c3b59396ba1c0c52db34..917d362f766aa7ff22a850627d988a8c6883240b 100644 --- a/tsk/docs/Doxyfile +++ b/tsk/docs/Doxyfile @@ -33,7 +33,7 @@ PROJECT_NAME = "The Sleuth Kit" # if some version control system is used. # This is automatically updated at release time. -PROJECT_NUMBER = 4.11.0 +PROJECT_NUMBER = 4.11.1 # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer @@ -883,7 +883,7 @@ GENERATE_HTML = YES # put in front of it. If left blank `html' will be used as the default path. # NOTE: This is automatically updated at release time. -HTML_OUTPUT = api-docs/4.11.0/ +HTML_OUTPUT = api-docs/4.11.1/ # The HTML_FILE_EXTENSION tag can be used to specify the file extension for # each generated HTML page (for example: .htm,.php,.asp). If it is left blank diff --git a/tsk/fs/ext2fs.c b/tsk/fs/ext2fs.c index 9039b696a45ec3168dc2b76a7e759083974067c1..5ca571b2746b4a8875121894e9cc4e33ae7ed3da 100755 --- a/tsk/fs/ext2fs.c +++ b/tsk/fs/ext2fs.c @@ -637,7 +637,7 @@ ext4_load_attrs_inline(TSK_FS_FILE *fs_file, const uint8_t * ea_buf, size_t ea_b // The offset is from the beginning of the entries, i.e., four bytes into the buffer. uint16_t offset = tsk_getu16(fs_file->fs_info->endian, ea_entry->val_off); uint32_t size = tsk_getu32(fs_file->fs_info->endian, ea_entry->val_size); - if (4 + offset + size <= ea_buf_len) { + if ((ea_buf_len >= 4) && (offset < ea_buf_len - 4) && (size <= ea_buf_len - 4 - offset)) { ea_inline_data = &(ea_buf[4 + offset]); ea_inline_data_len = size; break; diff --git a/tsk/fs/fs_open.c b/tsk/fs/fs_open.c index dc5eed72d52a01ec3a4edbe63b391f2b43eee7be..f7fbfbce2bdfb19ebf2879fd5355c5ac0faf426e 100644 --- a/tsk/fs/fs_open.c +++ b/tsk/fs/fs_open.c @@ -182,7 +182,7 @@ tsk_fs_open_img_decrypt(TSK_IMG_INFO * a_img_info, TSK_OFF_T a_offset, fs_first->close(fs_first); fs_info->close(fs_info); tsk_error_reset(); - tsk_error_set_errno(TSK_ERR_FS_UNKTYPE); + tsk_error_set_errno(TSK_ERR_FS_MULTTYPE); tsk_error_set_errstr( "%s or %s", FS_OPENERS[i].name, name_first); return NULL; diff --git a/tsk/fs/hfs_dent.c b/tsk/fs/hfs_dent.c index 17bde096ba8ec433beb2648da9e5314e70aecb33..8399ebeb8b66a1f99960cad97e643d4ade0d2867 100644 --- a/tsk/fs/hfs_dent.c +++ b/tsk/fs/hfs_dent.c @@ -300,7 +300,7 @@ hfs_dir_open_meta_cb(HFS_INFO * hfs, int8_t level_type, /* This is a normal file in the folder */ else if (rec_type == HFS_FILE_RECORD) { - if ((nodesize < sizeof(hfs_file)) || (rec_off2 >= nodesize - sizeof(hfs_file))) { + if ((nodesize < sizeof(hfs_file)) || (rec_off2 > nodesize - sizeof(hfs_file))) { tsk_error_set_errno(TSK_ERR_FS_GENFS); tsk_error_set_errstr("hfs_dir_open_meta: nodesize value out of bounds"); return HFS_BTREE_CB_ERR; diff --git a/tsk/fs/iso9660.c b/tsk/fs/iso9660.c index 3815517638aeea612adc6003858f6b55ba1590e7..6fa2c2c1e1373ac8ad67e345f5e2b5c190181690 100755 --- a/tsk/fs/iso9660.c +++ b/tsk/fs/iso9660.c @@ -556,6 +556,15 @@ iso9660_load_inodes_dir(TSK_FS_INFO * fs, TSK_OFF_T a_offs, int count, in_node = NULL; break; } + if (b_offs >= ISO9660_SSIZE_B - sizeof(iso9660_dentry)) { + if (tsk_verbose) + tsk_fprintf(stderr, + "iso9660_load_inodes_dir: b_offs out of bounds, bailing\n"); + free(in_node); + in_node = NULL; + break; + } + name16 = (UTF16 *) & buf[b_offs + sizeof(iso9660_dentry)]; @@ -570,13 +579,18 @@ iso9660_load_inodes_dir(TSK_FS_INFO * fs, TSK_OFF_T a_offs, int count, } name8 = (UTF8 *) in_node->inode.fn; + if ((dentry->fi_len % 2) != 0 || dentry->fi_len > ISO9660_SSIZE_B - sizeof(iso9660_dentry) - b_offs) { + if (tsk_verbose) + tsk_fprintf(stderr, + "iso9660_load_inodes_dir: UTF-16 name length out of bounds, bailing\n"); + free(in_node); + in_node = NULL; + break; + } retVal = tsk_UTF16toUTF8(fs->endian, - (const UTF16 **) &name16, - (UTF16 *) & buf[b_offs + sizeof(iso9660_dentry) + - dentry->fi_len], &name8, - (UTF8 *) ((uintptr_t) & in_node->inode. - fn[ISO9660_MAXNAMLEN_STD]), + (const UTF16 **) &name16, (UTF16 *) & buf[b_offs + sizeof(iso9660_dentry) + dentry->fi_len], + &name8, (UTF8 *) ((uintptr_t) & in_node->inode.fn[ISO9660_MAXNAMLEN_STD]), TSKlenientConversion); if (retVal != TSKconversionOK) { if (tsk_verbose) @@ -628,10 +642,10 @@ iso9660_load_inodes_dir(TSK_FS_INFO * fs, TSK_OFF_T a_offs, int count, } // if no extension, remove the final '.' - if (in_node->inode.fn[strlen(in_node->inode.fn) - 1] == - '.') - in_node->inode.fn[strlen(in_node->inode.fn) - 1] = - '\0'; + size_t name8_len = strnlen(in_node->inode.fn, ISO9660_MAXNAMLEN); + if (name8_len > 0 && in_node->inode.fn[name8_len - 1] == '.') { + in_node->inode.fn[name8_len - 1] = '\0'; + } if (strlen(in_node->inode.fn) == 0) { diff --git a/tsk/fs/ntfs.c b/tsk/fs/ntfs.c index ce46f1909a8e9cb6491c91ebbd1d2aaee9f8086a..eb9f0f48b0d5683364cd3f80d327263c839905fc 100644 --- a/tsk/fs/ntfs.c +++ b/tsk/fs/ntfs.c @@ -903,6 +903,7 @@ static uint8_t ntfs_uncompress_compunit(NTFS_COMP_INFO * comp) { size_t cl_index; + uint8_t recover_data = 0; tsk_error_reset(); @@ -943,14 +944,15 @@ ntfs_uncompress_compunit(NTFS_COMP_INFO * comp) blk_end = cl_index + blk_size; if (blk_end > comp->comp_len) { - tsk_error_set_errno(TSK_ERR_FS_FWALK); - tsk_error_set_errstr - ("ntfs_uncompress_compunit: Compression block length longer than buffer length: %" - PRIuSIZE "", blk_end); - return 1; + blk_end = comp->comp_len - 1; + if (tsk_verbose) + tsk_fprintf(stderr, + "WARNING: ntfs_uncompress_compunit: Compression block length longer than buffer length. Attempting to continue.\n"); + recover_data = 1; + // return 0; // zero out the entire block + // if we don't return 0, let the function continue to display as much decompressed data as possible } - /* The MSB identifies if the block is compressed */ iscomp = ((sb_header & 0x8000) != 0); @@ -1129,7 +1131,10 @@ ntfs_uncompress_compunit(NTFS_COMP_INFO * comp) } } } // end of loop inside of compression unit - + // if we are attempting to recover, we may not have decompressed an entire CU. Set uncomp_idx to the expected size. + if (recover_data) { + comp->uncomp_idx = comp->buf_size_b; + } return 0; } @@ -1299,6 +1304,8 @@ ntfs_attr_walk_special(const TSK_FS_ATTR * fs_attr, TSK_OFF_T off = 0; int retval; uint8_t stop_loop = 0; + uint8_t init_size_reached = 0; + uint8_t has_init_size = 0; if (fs_attr->nrd.compsize <= 0) { tsk_error_set_errno(TSK_ERR_FS_FWALK); @@ -1321,6 +1328,9 @@ ntfs_attr_walk_special(const TSK_FS_ATTR * fs_attr, return 1; } retval = TSK_WALK_CONT; + + if (fs_attr->nrd.initsize != fs_attr->fs_file->meta->size) + has_init_size = 1; /* cycle through the number of runs we have */ for (fs_attr_run = fs_attr->nrd.run; fs_attr_run; @@ -1418,19 +1428,36 @@ ntfs_attr_walk_special(const TSK_FS_ATTR * fs_attr, tsk_fprintf(stderr, "ntfs_proc_compunit: Decompressing at file offset %"PRIdOFF"\n", off); - // decompress the unit - if (ntfs_proc_compunit(ntfs, &comp, comp_unit, + // decompress the unit if we have not passed initsize yet. + if (!init_size_reached) { + if (ntfs_proc_compunit(ntfs, &comp, comp_unit, comp_unit_idx)) { - tsk_error_set_errstr2("%" PRIuINUM " - type: %" - PRIu32 " id: %d Status: %s", - fs_attr->fs_file->meta->addr, fs_attr->type, - fs_attr->id, - (fs_attr->fs_file->meta-> - flags & TSK_FS_META_FLAG_ALLOC) ? - "Allocated" : "Deleted"); - free(comp_unit); - ntfs_uncompress_done(&comp); - return 1; + tsk_error_set_errstr2("%" PRIuINUM " - type: %" + PRIu32 " id: %d Status: %s", + fs_attr->fs_file->meta->addr, fs_attr->type, + fs_attr->id, + (fs_attr->fs_file->meta-> + flags & TSK_FS_META_FLAG_ALLOC) ? + "Allocated" : "Deleted"); + free(comp_unit); + ntfs_uncompress_done(&comp); + return 1; + } + + /* if we've passed the initialized size while reading this block, + * zero out the buffer beyond the initialized size. */ + if (has_init_size) { + const int64_t prev_remanining_init_size = fs_attr->nrd.initsize - off; + if (prev_remanining_init_size < (int64_t)comp.buf_size_b) { + memset(&comp.uncomp_buf[prev_remanining_init_size], 0, comp.buf_size_b - prev_remanining_init_size); + init_size_reached = 1; + } + } + } + // set the buffers to 0s if we are past initsize + else { + ntfs_uncompress_reset(&comp); + comp.uncomp_idx = comp.buf_size_b; } // now call the callback with the uncompressed data @@ -1569,6 +1596,8 @@ ntfs_file_read_special(const TSK_FS_ATTR * a_fs_attr, uint32_t comp_unit_idx = 0; NTFS_COMP_INFO comp; size_t buf_idx = 0; + uint8_t init_size_reached = 0; + uint8_t has_init_size = 0; if (a_fs_attr->nrd.compsize <= 0) { tsk_error_set_errno(TSK_ERR_FS_FWALK); @@ -1603,6 +1632,9 @@ ntfs_file_read_special(const TSK_FS_ATTR * a_fs_attr, return len; } + if (a_fs_attr->nrd.initsize != a_fs_attr->fs_file->meta->size) + has_init_size = 1; + /* Allocate the buffers and state structure */ if (ntfs_uncompress_setup(fs, &comp, a_fs_attr->nrd.compsize)) { return -1; @@ -1662,19 +1694,36 @@ ntfs_file_read_special(const TSK_FS_ATTR * a_fs_attr, && (data_run_cur->next == NULL))) { size_t cpylen; - // decompress the unit - if (ntfs_proc_compunit(ntfs, &comp, comp_unit, + // decompress the unit if we are still in initsize + if (!init_size_reached) { + if (ntfs_proc_compunit(ntfs, &comp, comp_unit, comp_unit_idx)) { - tsk_error_set_errstr2("%" PRIuINUM " - type: %" - PRIu32 " id: %d Status: %s", - a_fs_attr->fs_file->meta->addr, - a_fs_attr->type, a_fs_attr->id, - (a_fs_attr->fs_file->meta-> - flags & TSK_FS_META_FLAG_ALLOC) ? - "Allocated" : "Deleted"); - free(comp_unit); - ntfs_uncompress_done(&comp); - return -1; + tsk_error_set_errstr2("%" PRIuINUM " - type: %" + PRIu32 " id: %d Status: %s", + a_fs_attr->fs_file->meta->addr, + a_fs_attr->type, a_fs_attr->id, + (a_fs_attr->fs_file->meta-> + flags & TSK_FS_META_FLAG_ALLOC) ? + "Allocated" : "Deleted"); + free(comp_unit); + ntfs_uncompress_done(&comp); + return -1; + } + + /* if we've passed the initialized size while reading this block, + * zero out the buffer beyond the initialized size + */ + if (has_init_size) { + const int64_t remanining_init_size = a_fs_attr->nrd.initsize - buf_idx - a_offset; + if (remanining_init_size < (int64_t)comp.buf_size_b) { + memset(comp.uncomp_buf + remanining_init_size, 0, comp.buf_size_b - remanining_init_size); + init_size_reached = 1; + } + } + } + else { + ntfs_uncompress_reset(&comp); + comp.uncomp_idx = comp.buf_size_b; } // copy uncompressed data to the output buffer @@ -1705,6 +1754,7 @@ ntfs_file_read_special(const TSK_FS_ATTR * a_fs_attr, byteoffset = 0; buf_idx += cpylen; comp_unit_idx = 0; + } /* If it is a sparse run, don't increment the addr so that * it remains 0 */ diff --git a/tsk/fs/ntfs_dent.cpp b/tsk/fs/ntfs_dent.cpp index ee3b772b447af7ae0062a034c6b12e9356769d92..32dd78df95c212473fac441b33cd72512f811cc6 100644 --- a/tsk/fs/ntfs_dent.cpp +++ b/tsk/fs/ntfs_dent.cpp @@ -1278,9 +1278,11 @@ ntfs_dir_open_meta(TSK_FS_INFO * a_fs, TSK_FS_DIR ** a_fs_dir, std::vector <NTFS_META_ADDR> &childFiles = ntfs_parent_map_get(ntfs, a_addr, seqToSrch); - if ((fs_name = tsk_fs_name_alloc(256, 0)) == NULL) + if ((fs_name = tsk_fs_name_alloc(256, 0)) == NULL){ + tsk_release_lock(&ntfs->orphan_map_lock); return TSK_ERR; - + } + fs_name->type = TSK_FS_NAME_TYPE_UNDEF; fs_name->par_addr = a_addr; fs_name->par_seq = fs_dir->fs_file->meta->seq; diff --git a/tsk/vs/mm_open.c b/tsk/vs/mm_open.c index ca1a413db66439100b16be15df08b8f4a3610ed6..ecc41ffd7a7c2c9329535fa6d51105036b929f90 100644 --- a/tsk/vs/mm_open.c +++ b/tsk/vs/mm_open.c @@ -122,7 +122,7 @@ tsk_vs_open(TSK_IMG_INFO * img_info, TSK_DADDR_T offset, vs_set->close(vs_set); vs->close(vs); tsk_error_reset(); - tsk_error_set_errno(TSK_ERR_VS_UNKTYPE); + tsk_error_set_errno(TSK_ERR_VS_MULTTYPE); tsk_error_set_errstr("GPT or %s at %" PRIuDADDR, set, offset); return NULL; @@ -145,7 +145,7 @@ tsk_vs_open(TSK_IMG_INFO * img_info, TSK_DADDR_T offset, vs_set->close(vs_set); vs->close(vs); tsk_error_reset(); - tsk_error_set_errno(TSK_ERR_VS_UNKTYPE); + tsk_error_set_errno(TSK_ERR_VS_MULTTYPE); tsk_error_set_errstr("Sun or %s at %" PRIuDADDR, set, offset); return NULL; @@ -164,7 +164,7 @@ tsk_vs_open(TSK_IMG_INFO * img_info, TSK_DADDR_T offset, vs_set->close(vs_set); vs->close(vs); tsk_error_reset(); - tsk_error_set_errno(TSK_ERR_VS_UNKTYPE); + tsk_error_set_errno(TSK_ERR_VS_MULTTYPE); tsk_error_set_errstr("Mac or %s at %" PRIuDADDR, set, offset); return NULL;