diff --git a/bindings/java/src/org/sleuthkit/datamodel/Blackboard.java b/bindings/java/src/org/sleuthkit/datamodel/Blackboard.java index f305846b33c68e2da51994200c1289aae448ba61..932f0890ab520bca2ce337b51678d719c9dd1525 100755 --- a/bindings/java/src/org/sleuthkit/datamodel/Blackboard.java +++ b/bindings/java/src/org/sleuthkit/datamodel/Blackboard.java @@ -1835,61 +1835,62 @@ public List<BlackboardArtifact> getExactMatchKeywordSearchResults(String keyword * database query to obtain the keyword hits. */ public List<BlackboardArtifact> getKeywordSearchResults(String keyword, String regex, TskData.KeywordSearchQueryType searchType, String kwsListName, Long dataSourceId) throws TskCoreException { - + String dataSourceClause = dataSourceId == null - ? "" - : " AND artifacts.data_source_obj_id = ? "; // dataSourceId + ? "" + : " AND artifacts.data_source_obj_id = ? "; // dataSourceId String kwsListClause = (kwsListName == null || kwsListName.isEmpty() - ? " AND set_name IS NULL " - : " AND set_name = ? "); + ? " WHERE r.set_name IS NULL " + : " WHERE r.set_name = ? "); String keywordClause = (keyword == null || keyword.isEmpty() - ? "" - : " AND keyword = ? "); + ? "" + : " AND r.keyword = ? "); String searchTypeClause = (searchType == null ? "" - : " AND search_type = ? "); - + : " AND r.search_type = ? "); + String regexClause = (regex == null || regex.isEmpty() - ? "" - : " AND regexp_str = ? "); - - String query = "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, " - + " types.type_name AS type_name, " - + " types.display_name AS display_name, " - + " types.category_type as category_type," - + " artifacts.review_status_id AS review_status_id, " - + " results.conclusion AS conclusion, " - + " results.significance AS significance, " - + " results.priority AS priority, " - + " results.configuration AS configuration, " - + " results.justification AS justification, " - + " (SELECT value_text FROM blackboard_attributes attr WHERE attr.artifact_id = artifacts.artifact_id AND attr.attribute_type_id = " - + BlackboardAttribute.Type.TSK_SET_NAME.getTypeID() + " LIMIT 1) AS set_name, " - + " (SELECT value_int32 FROM blackboard_attributes attr WHERE attr.artifact_id = artifacts.artifact_id AND attr.attribute_type_id = " + ? "" + : " AND r.regexp_str = ? "); + + String query = "SELECT r.* FROM ( " + + " 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, " + + " types.type_name AS type_name, " + + " types.display_name AS display_name, " + + " types.category_type as category_type," + + " artifacts.review_status_id AS review_status_id, " + + " results.conclusion AS conclusion, " + + " results.significance AS significance, " + + " results.priority AS priority, " + + " results.configuration AS configuration, " + + " results.justification AS justification, " + + " (SELECT value_text FROM blackboard_attributes attr WHERE attr.artifact_id = artifacts.artifact_id AND attr.attribute_type_id = " + + BlackboardAttribute.Type.TSK_SET_NAME.getTypeID() + " LIMIT 1) AS set_name, " + + " (SELECT value_int32 FROM blackboard_attributes attr WHERE attr.artifact_id = artifacts.artifact_id AND attr.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD_SEARCH_TYPE.getTypeID() + " LIMIT 1) AS search_type, " - + " (SELECT value_text FROM blackboard_attributes attr WHERE attr.artifact_id = artifacts.artifact_id AND attr.attribute_type_id = " - + BlackboardAttribute.Type.TSK_KEYWORD_REGEXP.getTypeID() + " LIMIT 1) AS regexp_str, " - + " (SELECT value_text FROM blackboard_attributes attr WHERE attr.artifact_id = artifacts.artifact_id AND attr.attribute_type_id = " - + BlackboardAttribute.Type.TSK_KEYWORD.getTypeID() + " LIMIT 1) AS keyword " - + " FROM blackboard_artifacts artifacts " - + " JOIN blackboard_artifact_types AS types " - + " ON artifacts.artifact_type_id = types.artifact_type_id " - + " LEFT JOIN tsk_analysis_results AS results " - + " ON artifacts.artifact_obj_id = results.artifact_obj_id " - + " WHERE types.category_type = " + BlackboardArtifact.Category.ANALYSIS_RESULT.getID() - + " AND artifacts.artifact_type_id = " + BlackboardArtifact.Type.TSK_KEYWORD_HIT.getTypeID() + " " - + dataSourceClause - + searchTypeClause - + kwsListClause - + keywordClause - + regexClause; + + " (SELECT value_text FROM blackboard_attributes attr WHERE attr.artifact_id = artifacts.artifact_id AND attr.attribute_type_id = " + + BlackboardAttribute.Type.TSK_KEYWORD_REGEXP.getTypeID() + " LIMIT 1) AS regexp_str, " + + " (SELECT value_text FROM blackboard_attributes attr WHERE attr.artifact_id = artifacts.artifact_id AND attr.attribute_type_id = " + + BlackboardAttribute.Type.TSK_KEYWORD.getTypeID() + " LIMIT 1) AS keyword " + + " FROM blackboard_artifacts artifacts " + + " JOIN blackboard_artifact_types AS types " + + " ON artifacts.artifact_type_id = types.artifact_type_id " + + " LEFT JOIN tsk_analysis_results AS results " + + " ON artifacts.artifact_obj_id = results.artifact_obj_id " + + " WHERE types.category_type = " + BlackboardArtifact.Category.ANALYSIS_RESULT.getID() + + " AND artifacts.artifact_type_id = " + BlackboardArtifact.Type.TSK_KEYWORD_HIT.getTypeID() + " " + + dataSourceClause + " ) r " + + kwsListClause + + keywordClause + + searchTypeClause + + regexClause; List<BlackboardArtifact> artifacts = new ArrayList<>(); caseDb.acquireSingleUserCaseReadLock(); @@ -1902,11 +1903,7 @@ public List<BlackboardArtifact> getKeywordSearchResults(String keyword, String r if (dataSourceId != null) { preparedStatement.setLong(++paramIdx, dataSourceId); } - - if (searchType != null) { - preparedStatement.setInt(++paramIdx, searchType.getType()); - } - + if (!(kwsListName == null || kwsListName.isEmpty())) { preparedStatement.setString(++paramIdx, kwsListName); } @@ -1915,6 +1912,10 @@ public List<BlackboardArtifact> getKeywordSearchResults(String keyword, String r preparedStatement.setString(++paramIdx, keyword); } + if (searchType != null) { + preparedStatement.setInt(++paramIdx, searchType.getType()); + } + if (!(regex == null || regex.isEmpty())) { preparedStatement.setString(++paramIdx, regex); } diff --git a/bindings/java/src/org/sleuthkit/datamodel/BlackboardArtifact.java b/bindings/java/src/org/sleuthkit/datamodel/BlackboardArtifact.java index b3990eb92e6f87470f3a4d7decf4a45ffa228aca..fba83e40ebd9c4d3d87817773af9ba14ae281ccd 100644 --- a/bindings/java/src/org/sleuthkit/datamodel/BlackboardArtifact.java +++ b/bindings/java/src/org/sleuthkit/datamodel/BlackboardArtifact.java @@ -900,6 +900,21 @@ public String toString() { return "BlackboardArtifact{" + "artifactID=" + artifactId + ", objID=" + getObjectID() + ", artifactObjID=" + artifactObjId + ", artifactTypeID=" + artifactTypeId + ", artifactTypeName=" + artifactTypeName + ", displayName=" + displayName + ", Case=" + getSleuthkitCase() + '}'; //NON-NLS } + /** + * Accepts a visitor SleuthkitItemVisitor that will perform an operation on + * this artifact type and return some object as the result of the operation. + * + * @param visitor The visitor, where the type parameter of the visitor is + * the type of the object that will be returned as the result + * of the visit operation. + * + * @return An object of type T. + */ + @Override + public <T> T accept(SleuthkitItemVisitor<T> visitor) { + return visitor.visit(this); + } + /** * Get the (reported) size of the content object. Artifact content is a * string dump of all its attributes. @@ -1579,7 +1594,7 @@ public int hashCode() { * http://sleuthkit.org/sleuthkit/docs/jni-docs/latest/artifact_catalog_page.html * for details on the standard attributes for each artifact type. */ - public enum ARTIFACT_TYPE { + public enum ARTIFACT_TYPE implements SleuthkitVisitableItem { /** * A generic information artifact. @@ -2101,6 +2116,23 @@ static public ARTIFACT_TYPE fromID(int id) { public String getDisplayName() { return displayName; } + + /** + * Accepts a visitor SleuthkitItemVisitor that will perform an operation + * on this artifact type and return some object as the result of the + * operation. + * + * @param visitor The visitor, where the type parameter of the visitor + * is the type of the object that will be returned as the + * result of the visit operation. + * + * @return An object of type T. + */ + @Override + public <T> T accept(SleuthkitItemVisitor<T> visitor) { + return visitor.visit(this); + } + } /** diff --git a/bindings/java/src/org/sleuthkit/datamodel/CaseDbAccessManager.java b/bindings/java/src/org/sleuthkit/datamodel/CaseDbAccessManager.java index c009c77b00224cc11fd3ec8ed151d54583555ec7..8e417fdf39c81a99bf83d0cc593153db0626925a 100644 --- a/bindings/java/src/org/sleuthkit/datamodel/CaseDbAccessManager.java +++ b/bindings/java/src/org/sleuthkit/datamodel/CaseDbAccessManager.java @@ -629,7 +629,7 @@ public void select(final String sql, final CaseDbAccessQueryCallback queryCallba public CaseDbPreparedStatement prepareSelect(String sql) throws TskCoreException { String selectSQL = "SELECT " + sql; // NON-NLS try { - return new CaseDbPreparedStatement(selectSQL, false); + return new CaseDbPreparedStatement(StatementType.SELECT, selectSQL, false); } catch (SQLException ex) { throw new TskCoreException("Error creating select prepared statement for query:\n" + selectSQL, ex); } @@ -645,12 +645,70 @@ public CaseDbPreparedStatement prepareSelect(String sql) throws TskCoreException */ @Beta public void select(CaseDbPreparedStatement preparedStatement, CaseDbAccessQueryCallback queryCallback) throws TskCoreException { + if (!preparedStatement.getType().equals(StatementType.SELECT)) { + throw new TskCoreException("CaseDbPreparedStatement has incorrect type for select operation"); + } + try (ResultSet resultSet = preparedStatement.getStatement().executeQuery()) { queryCallback.process(resultSet); } catch (SQLException ex) { throw new TskCoreException(MessageFormat.format("Error running SELECT query:\n{0}", preparedStatement.getOriginalSql()), ex); } } + + /** + * Creates a prepared statement object for the purposes of running an insert + * statement. The given SQL should not include the starting "INSERT INTO" + * or the name of the table. + * + * For PostGreSQL, the caller must include the ON CONFLICT DO NOTHING clause + * + * @param tableName The name of the table being updated. + * @param sql The insert statement without the starting "INSERT INTO (table name)" part. + * @param trans The open transaction. + * + * @return The prepared statement object. + * + * @throws TskCoreException + */ + @Beta + public CaseDbPreparedStatement prepareInsert(String tableName, String sql, CaseDbTransaction trans) throws TskCoreException { + validateTableName(tableName); + validateSQL(sql); + + String insertSQL = "INSERT"; + if (DbType.SQLITE == tskDB.getDatabaseType()) { + insertSQL += " OR IGNORE"; + } + insertSQL = insertSQL + " INTO " + tableName + " " + sql; // NON-NLS + + try { + return new CaseDbPreparedStatement(StatementType.INSERT, insertSQL, trans); + } catch (SQLException ex) { + throw new TskCoreException("Error creating insert prepared statement for query:\n" + insertSQL, ex); + } + } + + /** + * Performs a insert statement query with the given case prepared statement. + * + * @param preparedStatement The case prepared statement. + * + * @throws TskCoreException + */ + @Beta + public void insert(CaseDbPreparedStatement preparedStatement) throws TskCoreException { + + if (!preparedStatement.getType().equals(StatementType.INSERT)) { + throw new TskCoreException("CaseDbPreparedStatement has incorrect type for insert operation"); + } + + try { + preparedStatement.getStatement().executeUpdate(); + } catch (SQLException ex) { + throw new TskCoreException("Error inserting row in table " + "" + " with sql = "+ "", ex); + } + } /** * Deletes a row in the specified table. @@ -726,6 +784,22 @@ private void validateSQL(String sql) throws TskCoreException { */ } + /** + * Enum to track which type of lock the CaseDbPreparedStatement holds. + */ + private enum LockType { + READ, + WRITE, + NONE; + } + + /** + * Enum to track which type of statement the CaseDbPreparedStatement holds. + */ + private enum StatementType { + SELECT, + INSERT; + } /** * A wrapper around a PreparedStatement to execute queries against the @@ -737,10 +811,12 @@ public class CaseDbPreparedStatement implements AutoCloseable { private final CaseDbConnection connection; private final PreparedStatement preparedStatement; private final String originalSql; - private final boolean hasWriteLock; - + private final LockType lockType; + private final StatementType type; + /** - * Main constructor. + * Construct a prepared statement. This should not be used if a transaction + * is already open. * * NOTE: Creating the CaseDbPreparedStatement opens a connection and * acquires a read lock on the case database. For this reason, it is @@ -750,6 +826,7 @@ public class CaseDbPreparedStatement implements AutoCloseable { * the database should be avoided while the prepared statement is open * to prevent possible deadlocks. * + * @param type The type of statement. * @param query The query string. * @param isWriteLockRequired Whether or not a write lock is required. * If a write lock is not required, just a @@ -758,18 +835,36 @@ public class CaseDbPreparedStatement implements AutoCloseable { * @throws SQLException * @throws TskCoreException */ - private CaseDbPreparedStatement(String query, boolean isWriteLockRequired) throws SQLException, TskCoreException { + private CaseDbPreparedStatement(StatementType type, String query, boolean isWriteLockRequired) throws SQLException, TskCoreException { if (isWriteLockRequired) { - CaseDbAccessManager.this.tskDB.acquireSingleUserCaseWriteLock(); + CaseDbAccessManager.this.tskDB.acquireSingleUserCaseWriteLock(); + this.lockType = LockType.WRITE; } else { - CaseDbAccessManager.this.tskDB.acquireSingleUserCaseReadLock(); + CaseDbAccessManager.this.tskDB.acquireSingleUserCaseReadLock(); + this.lockType = LockType.READ; } - - this.hasWriteLock = isWriteLockRequired; this.connection = tskDB.getConnection(); this.preparedStatement = connection.getPreparedStatement(query, Statement.NO_GENERATED_KEYS); this.originalSql = query; - + this.type = type; + } + + /** + * Construct a prepared statement using an already open transaction. + * + * @param type The type of statement. + * @param query The query string. + * @param trans The open transaction. + * + * @throws SQLException + * @throws TskCoreException + */ + private CaseDbPreparedStatement(StatementType type, String query, CaseDbTransaction trans) throws SQLException, TskCoreException { + this.lockType = LockType.NONE; + this.connection = trans.getConnection(); + this.preparedStatement = connection.getPreparedStatement(query, Statement.NO_GENERATED_KEYS); + this.originalSql = query; + this.type = type; } /** @@ -780,6 +875,15 @@ private CaseDbPreparedStatement(String query, boolean isWriteLockRequired) throw private PreparedStatement getStatement() { return preparedStatement; } + + /** + * Get the type of statement. + * + * @return The statement type (select or insert). + */ + private StatementType getType() { + return type; + } /** * Returns the original sql query. @@ -967,11 +1071,15 @@ public void setObject(int parameterIndex, Object x) throws TskCoreException { @Override public void close() throws SQLException { - - preparedStatement.close(); + + // Don't close the statement/connection or release a lock if we were supplied a transaction. + // Everything will be handled when the transaction is closed. + if (lockType.equals(LockType.NONE)) { + return; + } + connection.close(); - - if (hasWriteLock) { + if (lockType.equals(LockType.WRITE)) { CaseDbAccessManager.this.tskDB.releaseSingleUserCaseWriteLock(); } else { CaseDbAccessManager.this.tskDB.releaseSingleUserCaseReadLock(); diff --git a/bindings/java/src/org/sleuthkit/datamodel/Content.java b/bindings/java/src/org/sleuthkit/datamodel/Content.java index a8779ad1e6cd6f8abb451e840e316f54c2fa0a6e..5993ac460165046abba1e0ec0cf1603f181e397a 100644 --- a/bindings/java/src/org/sleuthkit/datamodel/Content.java +++ b/bindings/java/src/org/sleuthkit/datamodel/Content.java @@ -30,7 +30,7 @@ * interface defines the basic methods for reading the content associated with * this object, the parent and children, and adding artifacts. */ -public interface Content { +public interface Content extends SleuthkitVisitableItem { /** * Reads data that this content object is associated with (file contents, diff --git a/bindings/java/src/org/sleuthkit/datamodel/ContentVisitor.java b/bindings/java/src/org/sleuthkit/datamodel/ContentVisitor.java index 875f9bad1345c4f950b256fee791e071e9f295d3..7d922b9f126955745f0717dcdfcaccbde3728d71 100755 --- a/bindings/java/src/org/sleuthkit/datamodel/ContentVisitor.java +++ b/bindings/java/src/org/sleuthkit/datamodel/ContentVisitor.java @@ -66,7 +66,7 @@ public interface ContentVisitor<T> { * @return result of the visit */ T visit(Image i); - + /** * Act on (visit) a Pool content object * @@ -74,7 +74,7 @@ public interface ContentVisitor<T> { * * @return result of the visit */ - T visit(Pool p); + T visit(Pool p); /** * Act on (visit) a Volume content object @@ -111,7 +111,7 @@ public interface ContentVisitor<T> { * @return result of the visit */ T visit(VirtualDirectory vd); - + /** * Act on (visit) a LocalDirectory content object * @@ -119,7 +119,7 @@ public interface ContentVisitor<T> { * * @return result of the visit */ - T visit(LocalDirectory ld); + T visit(LocalDirectory ld); /** * Act on (visit) a DerivedFile content object @@ -138,7 +138,7 @@ public interface ContentVisitor<T> { * @return result of the visit */ T visit(LocalFile df); - + /** * Act on (visit) a SlackFile content object * @@ -146,7 +146,7 @@ public interface ContentVisitor<T> { * * @return result of the visit */ - T visit(SlackFile sf); + T visit(SlackFile sf); /** * Act on (visit) a blackboard artifact object @@ -155,17 +155,8 @@ public interface ContentVisitor<T> { * * @return result of the visit */ - T visit(BlackboardArtifact ba); - - /** - * Act on (visit) a local files data source object - * - * @param ds The local files data source object - * - * @return result of the visit - */ - T visit(LocalFilesDataSource lfds); - + T visit(BlackboardArtifact ba); + /** * Act on (visit) a Report object * @@ -174,7 +165,7 @@ public interface ContentVisitor<T> { * @return result of the visit */ T visit(Report r); - + /** * Act on (visit) a OsAccount object * @@ -183,7 +174,7 @@ public interface ContentVisitor<T> { * @return result of the visit */ T visit(OsAccount act); - + /** * Act on (visit) an UnsupportedContent object * @@ -231,7 +222,7 @@ public T visit(Image i) { public T visit(Volume v) { return defaultVisit(v); } - + @Override public T visit(Pool p) { return defaultVisit(p); @@ -251,7 +242,7 @@ public T visit(LayoutFile lf) { public T visit(VirtualDirectory ld) { return defaultVisit(ld); } - + @Override public T visit(LocalDirectory ld) { return defaultVisit(ld); @@ -266,32 +257,27 @@ public T visit(DerivedFile df) { public T visit(LocalFile lf) { return defaultVisit(lf); } - + @Override public T visit(SlackFile sf) { return defaultVisit(sf); } - + @Override public T visit(BlackboardArtifact ba) { return defaultVisit(ba); } - @Override - public T visit(LocalFilesDataSource lfds) { - return defaultVisit(lfds); - } - @Override public T visit(Report r) { return defaultVisit(r); } - + @Override public T visit(OsAccount act) { return defaultVisit(act); } - + @Override public T visit(UnsupportedContent uc) { return defaultVisit(uc); diff --git a/bindings/java/src/org/sleuthkit/datamodel/DerivedFile.java b/bindings/java/src/org/sleuthkit/datamodel/DerivedFile.java index c06dab73054d4c161d5816d1f692d30e3675ab5e..274100654d9bd2a3787e447f96822a6def9f8172 100644 --- a/bindings/java/src/org/sleuthkit/datamodel/DerivedFile.java +++ b/bindings/java/src/org/sleuthkit/datamodel/DerivedFile.java @@ -150,6 +150,19 @@ public synchronized DerivedMethod getDerivedMethod() throws TskCoreException { return derivedMethod; } + /** + * Accepts a content visitor (Visitor design pattern). + * + * @param visitor A ContentVisitor supplying an algorithm to run using this + * derived file as input. + * + * @return The output of the algorithm. + */ + @Override + public <T> T accept(SleuthkitItemVisitor<T> v) { + return v.visit(this); + } + /** * Accepts a Sleuthkit item visitor (Visitor design pattern). * diff --git a/bindings/java/src/org/sleuthkit/datamodel/Directory.java b/bindings/java/src/org/sleuthkit/datamodel/Directory.java index ffebd6b2ce59d7859936b889f6bad51f45d016bb..f0d376b381093971ca36fde7624365117502bda0 100644 --- a/bindings/java/src/org/sleuthkit/datamodel/Directory.java +++ b/bindings/java/src/org/sleuthkit/datamodel/Directory.java @@ -94,6 +94,19 @@ public class Directory extends FsContent { super(db, objId, dataSourceObjectId, fsObjId, attrType, attrId, name, TskData.TSK_DB_FILES_TYPE_ENUM.FS, metaAddr, metaSeq, dirType, metaType, dirFlag, metaFlags, size, ctime, crtime, atime, mtime, modes, uid, gid, md5Hash, sha256Hash, knownState, parentPath, null, null, ownerUid, osAccountObjId, Collections.emptyList()); } + /** + * Accepts a content visitor (Visitor design pattern). + * + * @param visitor A ContentVisitor supplying an algorithm to run using this + * directory as input. + * + * @return The output of the algorithm. + */ + @Override + public <T> T accept(SleuthkitItemVisitor<T> v) { + return v.visit(this); + } + /** * Accepts a Sleuthkit item visitor (Visitor design pattern). * diff --git a/bindings/java/src/org/sleuthkit/datamodel/File.java b/bindings/java/src/org/sleuthkit/datamodel/File.java index f3da36861ea3eaf75bfced09b56fb68d50238d4c..659ed70acdee84c323b760f6a1a7f7915be6196b 100644 --- a/bindings/java/src/org/sleuthkit/datamodel/File.java +++ b/bindings/java/src/org/sleuthkit/datamodel/File.java @@ -102,6 +102,19 @@ public class File extends FsContent { super(db, objId, dataSourceObjectId, fsObjId, attrType, attrId, name, TskData.TSK_DB_FILES_TYPE_ENUM.FS, metaAddr, metaSeq, dirType, metaType, dirFlag, metaFlags, size, ctime, crtime, atime, mtime, modes, uid, gid, md5Hash, sha256Hash, knownState, parentPath, mimeType, extension, ownerUid, osAccountObjId, fileAttributes); } + /** + * Accepts a content visitor (Visitor design pattern). + * + * @param visitor A ContentVisitor supplying an algorithm to run using this + * file as input. + * + * @return The output of the algorithm. + */ + @Override + public <T> T accept(SleuthkitItemVisitor<T> visitor) { + return visitor.visit(this); + } + /** * Accepts a Sleuthkit item visitor (Visitor design pattern). * diff --git a/bindings/java/src/org/sleuthkit/datamodel/FileSystem.java b/bindings/java/src/org/sleuthkit/datamodel/FileSystem.java index 25ce946a4ae0ccacd1a0a4703b98115beadcddac..d20f8b7df1b3dc5c8a62bef7677201a5badfca50 100644 --- a/bindings/java/src/org/sleuthkit/datamodel/FileSystem.java +++ b/bindings/java/src/org/sleuthkit/datamodel/FileSystem.java @@ -207,6 +207,11 @@ public void finalize() throws Throwable { } } + @Override + public <T> T accept(SleuthkitItemVisitor<T> v) { + return v.visit(this); + } + @Override public <T> T accept(ContentVisitor<T> v) { return v.visit(this); diff --git a/bindings/java/src/org/sleuthkit/datamodel/HostAddress.java b/bindings/java/src/org/sleuthkit/datamodel/HostAddress.java index 191c759dec969d1b20ebac8b6b5a774471e77a01..4b545baee03e78c3acffffa4bc79432bcdafb160 100644 --- a/bindings/java/src/org/sleuthkit/datamodel/HostAddress.java +++ b/bindings/java/src/org/sleuthkit/datamodel/HostAddress.java @@ -128,6 +128,12 @@ public <T> T accept(ContentVisitor<T> v) { throw new UnsupportedOperationException("Not supported yet."); } + @Override + public <T> T accept(SleuthkitItemVisitor<T> v) { + // TODO + throw new UnsupportedOperationException("Not supported yet."); + } + /** * A host may have different types of addresses at a given point in time. */ diff --git a/bindings/java/src/org/sleuthkit/datamodel/Image.java b/bindings/java/src/org/sleuthkit/datamodel/Image.java index 670449a00b34979270bc8f34b58b25782b395b04..7e2d793a29e1749b0d4b4aa192334d73fa4def6b 100644 --- a/bindings/java/src/org/sleuthkit/datamodel/Image.java +++ b/bindings/java/src/org/sleuthkit/datamodel/Image.java @@ -275,6 +275,11 @@ public String getTimeZone() { return timezone; } + @Override + public <T> T accept(SleuthkitItemVisitor<T> v) { + return v.visit(this); + } + @Override public <T> T accept(ContentVisitor<T> v) { return v.visit(this); diff --git a/bindings/java/src/org/sleuthkit/datamodel/LayoutFile.java b/bindings/java/src/org/sleuthkit/datamodel/LayoutFile.java index 39a4709c42457df76017af04db46ccea745c5256..311dc8318c13ede3efd642e1a5ba462b760a35f1 100644 --- a/bindings/java/src/org/sleuthkit/datamodel/LayoutFile.java +++ b/bindings/java/src/org/sleuthkit/datamodel/LayoutFile.java @@ -230,6 +230,19 @@ public <T> T accept(ContentVisitor<T> visitor) { return visitor.visit(this); } + /** + * Accepts a Sleuthkit item visitor (Visitor design pattern). + * + * @param visitor A SleuthkitItemVisitor supplying an algorithm to run using + * this file as input. + * + * @return The output of the algorithm. + */ + @Override + public <T> T accept(SleuthkitItemVisitor<T> visitor) { + return visitor.visit(this); + } + /** * Provides a string representation of this file. * diff --git a/bindings/java/src/org/sleuthkit/datamodel/LocalDirectory.java b/bindings/java/src/org/sleuthkit/datamodel/LocalDirectory.java index 1183899ead5d5c68e832dd2fc54a54f53277052e..2b90de8309f22a8eb2bb955eed25fcad127cdb94 100644 --- a/bindings/java/src/org/sleuthkit/datamodel/LocalDirectory.java +++ b/bindings/java/src/org/sleuthkit/datamodel/LocalDirectory.java @@ -109,6 +109,19 @@ public <T> T accept(ContentVisitor<T> visitor) { return visitor.visit(this); } + /** + * Accepts a Sleuthkit item visitor (Visitor design pattern). + * + * @param visitor A SleuthkitItemVisitor supplying an algorithm to run using + * this local directory as input. + * + * @return The output of the algorithm. + */ + @Override + public <T> T accept(SleuthkitItemVisitor<T> visitor) { + return visitor.visit(this); + } + /** * Provides a string representation of this local directory. * diff --git a/bindings/java/src/org/sleuthkit/datamodel/LocalFile.java b/bindings/java/src/org/sleuthkit/datamodel/LocalFile.java index 405e2e19959e0acd2c43b15e2eff8a34779f37d7..1e5dcf6bfebe504e85e335473b01821de1e1c99d 100644 --- a/bindings/java/src/org/sleuthkit/datamodel/LocalFile.java +++ b/bindings/java/src/org/sleuthkit/datamodel/LocalFile.java @@ -146,6 +146,20 @@ public <T> T accept(ContentVisitor<T> visitor) { return visitor.visit(this); } + /** + * Accepts a Sleuthkit item visitor (Visitor design pattern). + * + * @param <T> The type returned by the visitor. + * @param visitor A SleuthkitItemVisitor supplying an algorithm to run using + * this local file as input. + * + * @return The output of the algorithm. + */ + @Override + public <T> T accept(SleuthkitItemVisitor<T> visitor) { + return visitor.visit(this); + } + /** * Provides a string representation of this local file. * diff --git a/bindings/java/src/org/sleuthkit/datamodel/LocalFilesDataSource.java b/bindings/java/src/org/sleuthkit/datamodel/LocalFilesDataSource.java index 96a99de8d63c403cc464c95d4bde2c4f92c82c35..871d292c908370887f1e28fd124a4beebfc03b9a 100755 --- a/bindings/java/src/org/sleuthkit/datamodel/LocalFilesDataSource.java +++ b/bindings/java/src/org/sleuthkit/datamodel/LocalFilesDataSource.java @@ -333,6 +333,20 @@ public <T> T accept(ContentVisitor<T> visitor) { return visitor.visit(this); } + /** + * Accepts a Sleuthkit item visitor (Visitor design pattern). + * + * @param <T> The type returned by the visitor. + * @param visitor A SleuthkitItemVisitor supplying an algorithm to run using + * this virtual directory as input. + * + * @return The output of the algorithm. + */ + @Override + public <T> T accept(SleuthkitItemVisitor<T> visitor) { + return visitor.visit(this); + } + /** * Constructs a local/logical files and/or directories data source. * diff --git a/bindings/java/src/org/sleuthkit/datamodel/OsAccount.java b/bindings/java/src/org/sleuthkit/datamodel/OsAccount.java index feb54bfc48ae340f088e78bd70f665325531db41..d78434f4c3e7efcc660b4b4df9ca32d33b6c770a 100644 --- a/bindings/java/src/org/sleuthkit/datamodel/OsAccount.java +++ b/bindings/java/src/org/sleuthkit/datamodel/OsAccount.java @@ -399,6 +399,11 @@ public <T> T accept(ContentVisitor<T> v) { throw new UnsupportedOperationException("Not supported yet."); } + @Override + public <T> T accept(SleuthkitItemVisitor<T> v) { + return v.visit(this); + } + /** * Abstracts attributes of an OS account. An attribute may be specific to a * host, or applicable across all hosts. diff --git a/bindings/java/src/org/sleuthkit/datamodel/Pool.java b/bindings/java/src/org/sleuthkit/datamodel/Pool.java index 436d58a215db637f435a5aaf0e04271a5a54d8e4..2a6e712e73b78ab596db12640e3b52d01e6f2c2e 100644 --- a/bindings/java/src/org/sleuthkit/datamodel/Pool.java +++ b/bindings/java/src/org/sleuthkit/datamodel/Pool.java @@ -133,6 +133,11 @@ protected void finalize() throws Throwable { } } + @Override + public <T> T accept(SleuthkitItemVisitor<T> v) { + return v.visit(this); + } + @Override public <T> T accept(ContentVisitor<T> v) { return v.visit(this); diff --git a/bindings/java/src/org/sleuthkit/datamodel/Report.java b/bindings/java/src/org/sleuthkit/datamodel/Report.java index 089363d4acfac0999b1cc9cc1ca0b3a20d28c4e6..3b6928cfa7a6f8338fceca799502a1d28efce196 100644 --- a/bindings/java/src/org/sleuthkit/datamodel/Report.java +++ b/bindings/java/src/org/sleuthkit/datamodel/Report.java @@ -403,4 +403,9 @@ public long getArtifactsCount(BlackboardArtifact.ARTIFACT_TYPE type) throws TskC public long getAllArtifactsCount() throws TskCoreException { return db.getBlackboardArtifactsCount(objectId); } + + @Override + public <T> T accept(SleuthkitItemVisitor<T> v) { + return v.visit(this); + } } diff --git a/bindings/java/src/org/sleuthkit/datamodel/SlackFile.java b/bindings/java/src/org/sleuthkit/datamodel/SlackFile.java index e0350d5a91f17856519c4b76134f6d61cd67d327..8cf9407e5c3209787c76fbf27684b59824041c33 100644 --- a/bindings/java/src/org/sleuthkit/datamodel/SlackFile.java +++ b/bindings/java/src/org/sleuthkit/datamodel/SlackFile.java @@ -124,6 +124,19 @@ protected int readInt(byte[] buf, long offset, long len) throws TskCoreException return SleuthkitJNI.readFileSlack(fileHandle, buf, offset, len); } + /** + * Accepts a content visitor (Visitor design pattern). + * + * @param v A ContentVisitor supplying an algorithm to run using this file + * as input. + * + * @return The output of the algorithm. + */ + @Override + public <T> T accept(SleuthkitItemVisitor<T> v) { + return v.visit(this); + } + /** * Accepts a Sleuthkit item visitor (Visitor design pattern). * diff --git a/bindings/java/src/org/sleuthkit/datamodel/SleuthkitCase.java b/bindings/java/src/org/sleuthkit/datamodel/SleuthkitCase.java index 16d0cfc4562ba31f2eba51fcbcf4093078e8bfab..5e14f322bb27afb25740363badf0eecda9738934 100644 --- a/bindings/java/src/org/sleuthkit/datamodel/SleuthkitCase.java +++ b/bindings/java/src/org/sleuthkit/datamodel/SleuthkitCase.java @@ -13228,7 +13228,7 @@ PreparedStatement getPreparedStatement(PREPARED_STATEMENT statementKey, int gene PreparedStatement getPreparedStatement(String sqlStatement, int generateKeys) throws SQLException { PreparedStatement statement; String statementKey = "SQL:" + sqlStatement + " Key:" + generateKeys; - if (adHocPreparedStatements.containsKey(statementKey)) { + if (adHocPreparedStatements.containsKey(statementKey) && !adHocPreparedStatements.get(statementKey).isClosed()) { statement = this.adHocPreparedStatements.get(statementKey); } else { statement = prepareStatement(sqlStatement, generateKeys); diff --git a/bindings/java/src/org/sleuthkit/datamodel/SleuthkitItemVisitor.java b/bindings/java/src/org/sleuthkit/datamodel/SleuthkitItemVisitor.java new file mode 100644 index 0000000000000000000000000000000000000000..5e90113f99b0647e451fb2d5cef4c80cca79a0b2 --- /dev/null +++ b/bindings/java/src/org/sleuthkit/datamodel/SleuthkitItemVisitor.java @@ -0,0 +1,316 @@ +/* + * Sleuth Kit Data Model + * + * Copyright 2011-2021 Basis Technology Corp. + * Contact: carrier <at> sleuthkit <dot> org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sleuthkit.datamodel; + +/** + * Interface for implementing a visitor pattern on all displayable items: + * Content implementations and blackboard artifacts. + * + * Visitor implements an algorithm on the content and blackboard artifacts + * objects. The algorithm is completely decoupled from the data object. The + * visitor pattern emulates double dispatch mechanism. It allows to act + * differently depending on the instance type, without need to test what the + * actual type is. E.g. it allows for processing an object hierarchy without + * using instanceof statements. Generic type parameter T is a return type from + * the visit methods. + * + * @param <T> return type of visit methods + */ +public interface SleuthkitItemVisitor<T> { + + /** + * Act on (visit) a Directory content object + * + * @param d the directory to visit / act on + * + * @return result of the visit + */ + T visit(Directory d); + + /** + * Act on (visit) a File content object + * + * @param f the file to visit / act on + * + * @return result of the visit + */ + T visit(File f); + + /** + * Act on (visit) a FileSystem content object + * + * @param fs the filesystem to visit / act on + * + * @return result of the visit + */ + T visit(FileSystem fs); + + /** + * Act on (visit) an Image content object + * + * @param i the image to visit / act on + * + * @return result of the visit + */ + T visit(Image i); + + /** + * Act on (visit) a Volume content object + * + * @param v the volume to visit / act on + * + * @return result of the visit + */ + T visit(Volume v); + + /** + * Act on (visit) a VolumeSystem content object + * + * @param vs the volume system to visit / act on + * + * @return result of the visit + */ + T visit(VolumeSystem vs); + + /** + * Act on (visit) a Pool content object + * + * @param pool the volume system to visit / act on + * + * @return result of the visit + */ + T visit(Pool pool); + + /** + * Act on (visit) a blackboard artifact object + * + * @param ba blackboard artifact object to visit / act on + * + * @return result of the visit + */ + T visit(BlackboardArtifact ba); + + /** + * Act on (visit) a blackboard artifact type + * + * @param tw blackboard artifact type to visit / act on + * + * @return result of the visit + */ + T visit(BlackboardArtifact.ARTIFACT_TYPE tw); + + /** + * Act on (visit) a layout file content object + * + * @param lf layout file to visit / act on + * + * @return result of the visit + */ + T visit(LayoutFile lf); + + /** + * Act on (visit) a VirtualDirectory content object + * + * @param ld layout dir to visit / act on + * + * @return result of the visit + */ + T visit(VirtualDirectory ld); + + /** + * Act on (visit) a LocalDirectory content object + * + * @param ld layout dir to visit / act on + * + * @return result of the visit + */ + T visit(LocalDirectory ld); + + /** + * Act on (visit) a DerivedFile content object + * + * @param df derived file to visit / act on + * + * @return result of the visit + */ + T visit(DerivedFile df); + + /** + * Act on (visit) a LocalFile content object + * + * @param lf local file to visit / act on + * + * @return result of the visit + */ + T visit(LocalFile lf); + + /** + * Act on (visit) a SlackFile content object + * + * @param sf slack file to visit / act on + * + * @return result of the visit + */ + T visit(SlackFile sf); + + /** + * Act on (visit) a Report content object + * + * @param report report to visit / act on + * + * @return result of the visit + */ + T visit(Report report); + + /** + * Act on (visit) a OsAccount content object + * + * @param account report to visit / act on + * + * @return result of the visit + */ + T visit(OsAccount account); + + /** + * Act on (visit) an UnsupportedContent object + * + * @param unsupportedContent content to visit / act on + * + * @return result of the visit + */ + T visit(UnsupportedContent unsupportedContent); + + /** + * Act on (visit) a LocalFilesDataSource content object + * + * @param localFilesDataSource report to visit / act on + * + * @return result of the visit + */ + T visit(LocalFilesDataSource localFilesDataSource); + + /** + * The default visitor - quickest method for implementing a custom visitor. + * Every visit method delegates to the defaultVisit method, the only + * required method to be implemented. Then, implement the specific visit + * methods for the objects on which the algorithm needs to act differently. + * + * @param <T> generic type, signifies the object type to be returned from + * visit() + */ + static abstract public class Default<T> implements SleuthkitItemVisitor<T> { + + protected abstract T defaultVisit(SleuthkitVisitableItem s); + + @Override + public T visit(Directory d) { + return defaultVisit(d); + } + + @Override + public T visit(File f) { + return defaultVisit(f); + } + + @Override + public T visit(FileSystem fs) { + return defaultVisit(fs); + } + + @Override + public T visit(Image i) { + return defaultVisit(i); + } + + @Override + public T visit(Volume v) { + return defaultVisit(v); + } + + @Override + public T visit(VolumeSystem vs) { + return defaultVisit(vs); + } + + @Override + public T visit(Pool p) { + return defaultVisit(p); + } + + @Override + public T visit(BlackboardArtifact ba) { + return defaultVisit(ba); + } + + @Override + public T visit(BlackboardArtifact.ARTIFACT_TYPE tw) { + return defaultVisit(tw); + } + + @Override + public T visit(LayoutFile lf) { + return defaultVisit(lf); + } + + @Override + public T visit(VirtualDirectory vd) { + return defaultVisit(vd); + } + + @Override + public T visit(LocalDirectory ld) { + return defaultVisit(ld); + } + + @Override + public T visit(DerivedFile df) { + return defaultVisit(df); + } + + @Override + public T visit(LocalFile lf) { + return defaultVisit(lf); + } + + @Override + public T visit(SlackFile sf) { + return defaultVisit(sf); + } + + @Override + public T visit(Report report) { + return defaultVisit(report); + } + + @Override + public T visit(OsAccount account) { + return defaultVisit(account); + } + + @Override + public T visit(UnsupportedContent unsupportedContent) { + return defaultVisit(unsupportedContent); + } + + @Override + public T visit(LocalFilesDataSource localFilesDataSource) { + return defaultVisit(localFilesDataSource); + } + } +} diff --git a/bindings/java/src/org/sleuthkit/datamodel/SleuthkitVisitableItem.java b/bindings/java/src/org/sleuthkit/datamodel/SleuthkitVisitableItem.java new file mode 100644 index 0000000000000000000000000000000000000000..ed17d61d71d567db2bea40745c95888fceaf41e0 --- /dev/null +++ b/bindings/java/src/org/sleuthkit/datamodel/SleuthkitVisitableItem.java @@ -0,0 +1,35 @@ +/* + * Sleuth Kit Data Model + * + * Copyright 2011 Basis Technology Corp. + * Contact: carrier <at> sleuthkit <dot> org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sleuthkit.datamodel; + +/** + * Interface for all visitable datatypes that can be found in the tsk database + */ +public interface SleuthkitVisitableItem { + + /** + * visitor pattern support + * + * @param v visitor + * + * @return visitor return value + */ + public <T> T accept(SleuthkitItemVisitor<T> v); + +} diff --git a/bindings/java/src/org/sleuthkit/datamodel/UnsupportedContent.java b/bindings/java/src/org/sleuthkit/datamodel/UnsupportedContent.java index 120a4988c75a89cf8d11476501c3338585c40492..d9bbca1bdd7b59ada02ad29b51d762789b4ceedd 100644 --- a/bindings/java/src/org/sleuthkit/datamodel/UnsupportedContent.java +++ b/bindings/java/src/org/sleuthkit/datamodel/UnsupportedContent.java @@ -55,4 +55,9 @@ public long getSize() { public <T> T accept(ContentVisitor<T> v) { return v.visit(this); } + + @Override + public <T> T accept(SleuthkitItemVisitor<T> v) { + return v.visit(this); + } } diff --git a/bindings/java/src/org/sleuthkit/datamodel/VirtualDirectory.java b/bindings/java/src/org/sleuthkit/datamodel/VirtualDirectory.java index 4c22cc7288557c4da8f49331160b02f81cb10747..7d2c4519e411cc90be76db10743113d220046501 100644 --- a/bindings/java/src/org/sleuthkit/datamodel/VirtualDirectory.java +++ b/bindings/java/src/org/sleuthkit/datamodel/VirtualDirectory.java @@ -117,6 +117,20 @@ public <T> T accept(ContentVisitor<T> visitor) { return visitor.visit(this); } + /** + * Accepts a Sleuthkit item visitor (Visitor design pattern). + * + * @param <T> The type returned by the visitor. + * @param visitor A SleuthkitItemVisitor supplying an algorithm to run using + * this virtual directory as input. + * + * @return The output of the algorithm. + */ + @Override + public <T> T accept(SleuthkitItemVisitor<T> visitor) { + return visitor.visit(this); + } + /** * Provides a string representation of this virtual directory. * diff --git a/bindings/java/src/org/sleuthkit/datamodel/Volume.java b/bindings/java/src/org/sleuthkit/datamodel/Volume.java index 6d07dd9f433a9089c38cf280959de24ee815dd78..f63557cda0ff82367d2a5a5cc83984def62236e1 100644 --- a/bindings/java/src/org/sleuthkit/datamodel/Volume.java +++ b/bindings/java/src/org/sleuthkit/datamodel/Volume.java @@ -255,6 +255,11 @@ public static String vsFlagToString(long vsFlag) { return result; } + @Override + public <T> T accept(SleuthkitItemVisitor<T> v) { + return v.visit(this); + } + @Override public <T> T accept(ContentVisitor<T> v) { return v.visit(this); diff --git a/bindings/java/src/org/sleuthkit/datamodel/VolumeSystem.java b/bindings/java/src/org/sleuthkit/datamodel/VolumeSystem.java index 7b1f74d7dd606b157f6052106a65408a00804d25..e403576142eb9d389bcf627e1395b82cdf9b396f 100644 --- a/bindings/java/src/org/sleuthkit/datamodel/VolumeSystem.java +++ b/bindings/java/src/org/sleuthkit/datamodel/VolumeSystem.java @@ -131,6 +131,11 @@ public void finalize() throws Throwable { super.finalize(); } } + + @Override + public <T> T accept(SleuthkitItemVisitor<T> v) { + return v.visit(this); + } @Override public <T> T accept(ContentVisitor<T> v) { diff --git a/tests/Makefile.am b/tests/Makefile.am index 8921e125815af1e56bc84a92a3a4c3653996a379..b0590939feed42af837222a5d96b4708b4ffb0f1 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -3,7 +3,7 @@ AM_CFLAGS += $(PTHREAD_CFLAGS) AM_CXXFLAGS += -Wno-unused-command-line-argument $(PTHREAD_CFLAGS) LDADD = ../tsk/libtsk.la LDFLAGS += -static $(PTHREAD_LIBS) -EXTRA_DIST = .indent.pro runtests.sh +EXTRA_DIST = .indent.pro runtests.sh test_libraries.sh check_SCRIPTS = runtests.sh test_libraries.sh diff --git a/tsk/auto/auto_db.cpp b/tsk/auto/auto_db.cpp index 6e4864702196ce3495a2876a93933a76fb41c204..bac4d7f88a3e8917c4de961eba6c431ad7d098c8 100755 --- a/tsk/auto/auto_db.cpp +++ b/tsk/auto/auto_db.cpp @@ -209,9 +209,9 @@ TskAutoDb::openImage(const char* a_deviceId) uint8_t TskAutoDb::addImageDetails(const char* deviceId) { - string md5 = ""; - string sha1 = ""; - string collectionDetails = ""; + std::string md5 = ""; + std::string sha1 = ""; + std::string collectionDetails = ""; #if HAVE_LIBEWF if (m_img_info->itype == TSK_IMG_TYPE_EWF_EWF) { // @@@ This should really probably be inside of a tsk_img_ method @@ -227,7 +227,7 @@ TskAutoDb::addImageDetails(const char* deviceId) } #endif - string devId; + std::string devId; if (NULL != deviceId) { devId = deviceId; } else { @@ -824,7 +824,7 @@ TskAutoDb::commitAddImage() * Set the current image's timezone */ void -TskAutoDb::setTz(string tzone) +TskAutoDb::setTz(std::string tzone) { m_curImgTZone = tzone; } @@ -849,7 +849,7 @@ TskAutoDb::processFile(TSK_FS_FILE * fs_file, const char *path) if (isDir(fs_file)) { m_curDirAddr = fs_file->name->meta_addr; tsk_take_lock(&m_curDirPathLock); - m_curDirPath = string(path) + fs_file->name->name; + m_curDirPath = std::string(path) + fs_file->name->name; tsk_release_lock(&m_curDirPathLock); } else if (m_curDirAddr != fs_file->name->par_addr) { @@ -1406,7 +1406,7 @@ TSK_RETVAL_ENUM TskAutoDb::addUnallocBlockFileInChunks(uint64_t byteStart, TSK_O * @returns curDirPath string representing currently analyzed directory */ const std::string TskAutoDb::getCurDir() { - string curDirPath; + std::string curDirPath; tsk_take_lock(&m_curDirPathLock); curDirPath = m_curDirPath; tsk_release_lock(&m_curDirPathLock); diff --git a/tsk/auto/tsk_case_db.h b/tsk/auto/tsk_case_db.h index 4b29e156e68360371d212f111be9b21eb22e8b3a..18d98fc46f01a7a1281f69b26f6dc76e939933ab 100644 --- a/tsk/auto/tsk_case_db.h +++ b/tsk/auto/tsk_case_db.h @@ -40,7 +40,7 @@ class TskAutoDb:public TskAuto { virtual uint8_t openImageUtf8(int, const char *const images[], TSK_IMG_TYPE_ENUM, unsigned int a_ssize, const char* deviceId = NULL); virtual void closeImage(); - virtual void setTz(string tzone); + virtual void setTz(std::string tzone); virtual TSK_FILTER_ENUM filterVs(const TSK_VS_INFO * vs_info); virtual TSK_FILTER_ENUM filterVol(const TSK_VS_PART_INFO * vs_part); @@ -133,9 +133,9 @@ class TskAutoDb:public TskAuto { int64_t m_curFileId; ///< Object ID of file currently being processed TSK_INUM_T m_curDirAddr; ///< Meta address the directory currently being processed int64_t m_curUnallocDirId; - string m_curDirPath; //< Path of the current directory being processed + std::string m_curDirPath; //< Path of the current directory being processed tsk_lock_t m_curDirPathLock; //< protects concurrent access to m_curDirPath - string m_curImgTZone; + std::string m_curImgTZone; bool m_blkMapFlag; bool m_fileHashFlag; bool m_vsFound; diff --git a/tsk/fs/fs_dir.c b/tsk/fs/fs_dir.c index 873994712636d436572cd780dc27739fef5a8f24..1739792420af849797d97cb8c312cbced5f24b91 100644 --- a/tsk/fs/fs_dir.c +++ b/tsk/fs/fs_dir.c @@ -1401,6 +1401,12 @@ tsk_fs_dir_find_orphans(TSK_FS_INFO * a_fs, TSK_FS_DIR * a_fs_dir) for (i = 0; i < a_fs_dir->names_used; i++) { if (tsk_list_find(data.orphan_subdir_list, a_fs_dir->names[i].meta_addr)) { + + // Unclear what should happen in this situation, but it can happen, + // So skipping over this situation for now. + if (a_fs_dir->names_used == i + 1) { + continue; + } if (a_fs_dir->names_used > 1) { tsk_fs_name_copy(&a_fs_dir->names[i], &a_fs_dir->names[a_fs_dir->names_used - 1]); diff --git a/tsk/fs/hfs_dent.c b/tsk/fs/hfs_dent.c index 8399ebeb8b66a1f99960cad97e643d4ade0d2867..9a69dd7138bb0c0d87844403419692f3755357f3 100644 --- a/tsk/fs/hfs_dent.c +++ b/tsk/fs/hfs_dent.c @@ -258,6 +258,11 @@ hfs_dir_open_meta_cb(HFS_INFO * hfs, int8_t level_type, /* This will link the folder to its parent, which is the ".." entry */ else if (rec_type == HFS_FOLDER_THREAD) { + if ((nodesize < sizeof(hfs_thread)) || (rec_off2 > nodesize - sizeof(hfs_thread))) { + 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; + } hfs_thread *thread = (hfs_thread *) & rec_buf[rec_off2]; strcpy(info->fs_name->name, ".."); info->fs_name->meta_addr = diff --git a/tsk/fs/iso9660.c b/tsk/fs/iso9660.c index 6fa2c2c1e1373ac8ad67e345f5e2b5c190181690..64a142c3f8842113cab0d3f5036dbe4bb8aae7fe 100755 --- a/tsk/fs/iso9660.c +++ b/tsk/fs/iso9660.c @@ -641,14 +641,13 @@ iso9660_load_inodes_dir(TSK_FS_INFO * fs, TSK_OFF_T a_offs, int count, file_ver = NULL; } - // if no extension, remove the final '.' size_t name8_len = strnlen(in_node->inode.fn, ISO9660_MAXNAMLEN); if (name8_len > 0 && in_node->inode.fn[name8_len - 1] == '.') { + // if no extension, remove the final '.' in_node->inode.fn[name8_len - 1] = '\0'; + name8_len -= 1; } - - - if (strlen(in_node->inode.fn) == 0) { + if (name8_len == 0) { if (tsk_verbose) tsk_fprintf(stderr, "iso9660_load_inodes_dir: length of name after processing is 0. bailing\n"); diff --git a/tsk/fs/ntfs.c b/tsk/fs/ntfs.c index 236b0c559830c2725dadae2495cc03dd2d077488..f87712c6c2ebf1546a02dbedcd4a07cfd3861c61 100644 --- a/tsk/fs/ntfs.c +++ b/tsk/fs/ntfs.c @@ -574,6 +574,7 @@ is_clustalloc(NTFS_INFO * ntfs, TSK_DADDR_T addr) * @param ntfs File system that attribute is located in. * @param start_vcn The starting VCN for this run. * @param runlist The raw runlist data from the MFT entry. + * @param runlist_size The size of the raw runlist data from the MFT entry. * @param a_data_run_head [out] Pointer to pointer of run that is created. (NULL on error and for $BadClust - special case because it is a sparse file for the entire FS). * @param totlen [out] Pointer to location where total length of run (in bytes) can be returned (or NULL) * @param mnum MFT entry address @@ -582,7 +583,7 @@ is_clustalloc(NTFS_INFO * ntfs, TSK_DADDR_T addr) */ static TSK_RETVAL_ENUM ntfs_make_data_run(NTFS_INFO * ntfs, TSK_OFF_T start_vcn, - ntfs_runlist * runlist_head, TSK_FS_ATTR_RUN ** a_data_run_head, + ntfs_runlist * runlist_head, uint32_t runlist_size, TSK_FS_ATTR_RUN ** a_data_run_head, TSK_OFF_T * totlen, TSK_INUM_T mnum) { TSK_FS_INFO *fs = (TSK_FS_INFO *) ntfs; @@ -591,6 +592,7 @@ ntfs_make_data_run(NTFS_INFO * ntfs, TSK_OFF_T start_vcn, unsigned int i, idx; TSK_DADDR_T prev_addr = 0; TSK_OFF_T file_offset = start_vcn; + uint32_t runlist_offset = 0; run = runlist_head; *a_data_run_head = NULL; @@ -599,11 +601,15 @@ ntfs_make_data_run(NTFS_INFO * ntfs, TSK_OFF_T start_vcn, if (totlen) *totlen = 0; + if (runlist_size < 1) { + return TSK_ERR; + } + /* Cycle through each run in the runlist * We go until we find an entry with no length * An entry with offset of 0 is for a sparse run */ - while (NTFS_RUNL_LENSZ(run) != 0) { + while ((runlist_offset < runlist_size) && NTFS_RUNL_LENSZ(run) != 0) { int64_t addr_offset = 0; /* allocate a new tsk_fs_attr_run */ @@ -630,7 +636,7 @@ ntfs_make_data_run(NTFS_INFO * ntfs, TSK_OFF_T start_vcn, * A length of more than eight bytes will not fit in the * 64-bit length field (and is likely corrupt) */ - if (NTFS_RUNL_LENSZ(run) > 8) { + if (NTFS_RUNL_LENSZ(run) > 8 || NTFS_RUNL_LENSZ(run) > runlist_size - runlist_offset - 1) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_FS_INODE_COR); tsk_error_set_errstr @@ -758,8 +764,14 @@ ntfs_make_data_run(NTFS_INFO * ntfs, TSK_OFF_T start_vcn, } /* Advance run */ - run = (ntfs_runlist *) ((uintptr_t) run + (1 + NTFS_RUNL_LENSZ(run) - + NTFS_RUNL_OFFSZ(run))); + uint32_t run_size = 1 + NTFS_RUNL_LENSZ(run) + NTFS_RUNL_OFFSZ(run); + run = (ntfs_runlist *) ((uintptr_t) run + run_size); + + // Abritrary limit runlist_offset at INT32_MAX ((1 << 31) - 1) + if (run_size > (((uint32_t) 1UL << 31 ) -1) - runlist_offset) { + return TSK_ERR; + } + runlist_offset += run_size; } /* special case for $BADCLUST, which is a sparse file whose size is @@ -1446,7 +1458,7 @@ ntfs_attr_walk_special(const TSK_FS_ATTR * fs_attr, /* if we've passed the initialized size while reading this block, * zero out the buffer beyond the initialized size. */ - if (has_init_size) { + if (has_init_size && (off < fs_attr->nrd.initsize)) { 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); @@ -2048,19 +2060,23 @@ ntfs_proc_attrseq(NTFS_INFO * ntfs, return TSK_COR; } + uint32_t attr_len = tsk_getu32(fs->endian, attr->len); + uint64_t run_start_vcn = tsk_getu64(fs->endian, attr->c.nr.start_vcn); + uint16_t run_off = tsk_getu16(fs->endian, attr->c.nr.run_off); + // sanity check - if (tsk_getu16(fs->endian, attr->c.nr.run_off) > tsk_getu32(fs->endian, attr->len)) { + if ((run_off < 48) || (run_off >= attr_len)) { if (tsk_verbose) - tsk_fprintf(stderr, "ntfs_proc_attrseq: run offset too big\n"); + tsk_fprintf(stderr, "ntfs_proc_attrseq: run offset out of bounds\n"); break; } /* convert the run to generic form */ retval = ntfs_make_data_run(ntfs, - tsk_getu64(fs->endian, attr->c.nr.start_vcn), - (ntfs_runlist *) ((uintptr_t) - attr + tsk_getu16(fs->endian, - attr->c.nr.run_off)), &fs_attr_run, NULL, + run_start_vcn, + (ntfs_runlist *) ((uintptr_t) attr + run_off), + attr_len - run_off, + &fs_attr_run, NULL, a_attrinum); if (retval != TSK_OK) { tsk_error_errstr2_concat(" - proc_attrseq"); @@ -2343,10 +2359,6 @@ ntfs_proc_attrseq(NTFS_INFO * ntfs, ("proc_attrseq: resident data of File Name Attribute is too small!"); return TSK_COR; } - TSK_FS_META_NAME_LIST *fs_name; - UTF16 *name16; - UTF8 *name8; - ntfs_attr_fname *fname = (ntfs_attr_fname *) ((uintptr_t) attr + attr_off); if (fname->nspace == NTFS_FNAME_DOS) { continue; @@ -2374,6 +2386,7 @@ ntfs_proc_attrseq(NTFS_INFO * ntfs, fs_file->meta->time2.ntfs.fn_id = id; + TSK_FS_META_NAME_LIST *fs_name; /* Seek to the end of the fs_name structures in TSK_FS_META */ if (fs_file->meta->name2) { @@ -2400,9 +2413,16 @@ ntfs_proc_attrseq(NTFS_INFO * ntfs, } fs_name->next = NULL; } + if (fname->nlen > attr_len - 66) { + tsk_error_reset(); + tsk_error_set_errno(TSK_ERR_FS_INODE_COR); + tsk_error_set_errstr + ("proc_attrseq: invalid name value size out of bounds!"); + return TSK_COR; + } + UTF16 *name16 = (UTF16 *) & fname->name; + UTF8 *name8 = (UTF8 *) fs_name->name; - name16 = (UTF16 *) & fname->name; - name8 = (UTF8 *) fs_name->name; retVal = tsk_UTF16toUTF8(fs->endian, (const UTF16 **) &name16, (UTF16 *) ((uintptr_t) name16 + @@ -3243,23 +3263,26 @@ ntfs_load_bmap(NTFS_INFO * ntfs) tsk_getu16(fs->endian, mft->attr_off)); data_attr = NULL; + uint32_t attr_len = 0; + uint32_t attr_type = 0; + /* cycle through them */ while ((uintptr_t) attr + sizeof (ntfs_attr) <= ((uintptr_t) mft + (uintptr_t) ntfs->mft_rsize_b)) { - if ((tsk_getu32(fs->endian, attr->len) == 0) || - (tsk_getu32(fs->endian, attr->type) == 0xffffffff)) { + attr_len = tsk_getu32(fs->endian, attr->len); + attr_type = tsk_getu32(fs->endian, attr->type); + + if ((attr_len == 0) || (attr_type == 0xffffffff)) { break; } - if (tsk_getu32(fs->endian, attr->type) == NTFS_ATYPE_DATA) { + if (attr_type == NTFS_ATYPE_DATA) { data_attr = attr; break; } - attr = - (ntfs_attr *) ((uintptr_t) attr + tsk_getu32(fs->endian, - attr->len)); + attr = (ntfs_attr *) ((uintptr_t) attr + attr_len); } /* did we get it? */ @@ -3269,18 +3292,26 @@ ntfs_load_bmap(NTFS_INFO * ntfs) tsk_error_set_errstr("Error Finding Bitmap Data Attribute"); goto on_error; } - uint32_t attr_len = tsk_getu32(fs->endian, data_attr->len); + attr_len = tsk_getu32(fs->endian, data_attr->len); if (attr_len > ntfs->mft_rsize_b) { goto on_error; } - /* convert to generic form */ + uint64_t run_start_vcn = tsk_getu64(fs->endian, data_attr->c.nr.start_vcn); + uint16_t run_off = tsk_getu16(fs->endian, data_attr->c.nr.run_off); + + if ((run_off < 48) || (run_off >= attr_len)) { + tsk_error_reset(); + tsk_error_set_errno(TSK_ERR_FS_INODE_COR); + tsk_error_set_errstr("Invalid run_off of Bitmap Data Attribute - value out of bounds"); + goto on_error; + } + /* convert data run to generic form */ if ((ntfs_make_data_run(ntfs, - tsk_getu64(fs->endian, data_attr->c.nr.start_vcn), - (ntfs_runlist - *) ((uintptr_t) data_attr + tsk_getu16(fs->endian, - data_attr->c.nr.run_off)), &(ntfs->bmap), - NULL, NTFS_MFT_BMAP)) != TSK_OK) { + run_start_vcn, + (ntfs_runlist *) ((uintptr_t) data_attr + run_off), + attr_len - run_off, + &(ntfs->bmap), NULL, NTFS_MFT_BMAP)) != TSK_OK) { goto on_error; } ntfs->bmap_buf = (char *) tsk_malloc(fs->block_size); diff --git a/tsk/fs/tsk_apfs.hpp b/tsk/fs/tsk_apfs.hpp index fc0bd169580b7b76895d69784f34b2fa3c7d48b3..fbfb3da2bbfdf8bb8fe1732b249e7637678e402e 100755 --- a/tsk/fs/tsk_apfs.hpp +++ b/tsk/fs/tsk_apfs.hpp @@ -131,8 +131,11 @@ class APFSBtreeNodeIterator { } template <typename Void = void> - auto init_value() + auto init_value(int recursion_depth) -> std::enable_if_t<Node::is_variable_kv_node::value, Void> { + if ((recursion_depth < 0) || (recursion_depth > 64)) { + throw std::runtime_error("init_value exceeds recursion depth"); + } if (this->_node->has_fixed_kv_size()) { throw std::runtime_error("btree does not have variable sized keys"); } @@ -150,12 +153,15 @@ class APFSBtreeNodeIterator { const auto block_num = *((apfs_block_num *)val_data); _child_it = std::make_unique<typename Node::iterator>( - own_node(_node.get(), block_num), 0); + own_node(_node.get(), block_num), 0, recursion_depth); } } template <typename Void = void> - auto init_value() -> std::enable_if_t<Node::is_fixed_kv_node::value, Void> { + auto init_value(int recursion_depth) -> std::enable_if_t<Node::is_fixed_kv_node::value, Void> { + if ((recursion_depth < 0) || (recursion_depth > 64)) { + throw std::runtime_error("init_value exceeds recursion depth"); + } if (!this->_node->has_fixed_kv_size()) { throw std::runtime_error("btree does not have fixed sized keys"); } @@ -170,7 +176,7 @@ class APFSBtreeNodeIterator { const auto block_num = *((apfs_block_num *)val_data); _child_it = std::make_unique<typename Node::iterator>( - own_node(_node.get(), block_num), 0); + own_node(_node.get(), block_num), 0, recursion_depth); } } @@ -178,12 +184,12 @@ class APFSBtreeNodeIterator { // Forward iterators must be DefaultConstructible APFSBtreeNodeIterator() = default; - APFSBtreeNodeIterator(const Node *node, uint32_t index); + APFSBtreeNodeIterator(const Node *node, uint32_t index, int recursion_depth); - APFSBtreeNodeIterator(lw_shared_ptr<Node> &&node, uint32_t index); + APFSBtreeNodeIterator(lw_shared_ptr<Node> &&node, uint32_t index, int recursion_depth); APFSBtreeNodeIterator(const Node *node, uint32_t index, - typename Node::iterator &&child); + typename Node::iterator &&child, int recursion_depth); virtual ~APFSBtreeNodeIterator() = default; @@ -270,7 +276,7 @@ class APFSBtreeNodeIterator { auto index{_index}; this->~APFSBtreeNodeIterator(); - new (this) APFSBtreeNodeIterator(std::move(node), index); + new (this) APFSBtreeNodeIterator(std::move(node), index, 0); } return (*this); } @@ -287,7 +293,7 @@ class APFSBtreeNodeIterator { auto index{_index}; this->~APFSBtreeNodeIterator(); - new (this) APFSBtreeNodeIterator(std::move(node), index); + new (this) APFSBtreeNodeIterator(std::move(node), index, 0); return (*this); } @@ -484,8 +490,8 @@ class APFSBtreeNode : public APFSObject, public APFSOmap::node_tag { public: using iterator = APFSBtreeNodeIterator<APFSBtreeNode>; - iterator begin() const { return {this, 0}; } - iterator end() const { return {this, key_count()}; } + iterator begin() const { return {this, 0, 0}; } + iterator end() const { return {this, key_count(), 0}; } template <typename T, typename Compare> iterator find(const T &value, Compare comp) const { @@ -505,7 +511,7 @@ class APFSBtreeNode : public APFSObject, public APFSOmap::node_tag { if (res == 0) { // We've found it! - return {this, i - 1}; + return {this, i - 1, 0}; } if (res < 0) { @@ -526,14 +532,14 @@ class APFSBtreeNode : public APFSObject, public APFSOmap::node_tag { const auto &k = key(i - 1); if (comp(k, value) <= 0) { - iterator it{this, i - 1}; + iterator it{this, i - 1, 0}; auto ret = it._child_it->_node->find(value, comp); if (ret == it._child_it->_node->end()) { return end(); } - return {this, i - 1, std::move(ret)}; + return {this, i - 1, std::move(ret), 0}; } } @@ -580,8 +586,8 @@ class APFSJObjBtreeNode : public APFSBtreeNode<> { inline bool is_leaf() const noexcept { return (bn()->level == 0); } - inline iterator begin() const { return {this, 0}; } - inline iterator end() const { return {this, key_count()}; } + inline iterator begin() const { return {this, 0, 0}; } + inline iterator end() const { return {this, key_count(), 0}; } template <typename T, typename Compare> inline iterator find(const T &value, Compare comp) const { @@ -595,7 +601,7 @@ class APFSJObjBtreeNode : public APFSBtreeNode<> { if (res == 0) { // We've found it! - return {this, i}; + return {this, i, 0}; } if (res > 0) { @@ -627,11 +633,11 @@ class APFSJObjBtreeNode : public APFSBtreeNode<> { if (v == 0) { // We need to see if the jobj might be in the last node if (last != 0) { - iterator it{this, last - 1}; + iterator it{this, last - 1, 0}; auto ret = it._child_it->_node->find(value, comp); if (ret != it._child_it->_node->end()) { - return {this, last - 1, std::move(ret)}; + return {this, last - 1, std::move(ret), 0}; } } @@ -644,14 +650,14 @@ class APFSJObjBtreeNode : public APFSBtreeNode<> { return end(); } - iterator it{this, last}; + iterator it{this, last, 0}; auto ret = it._child_it->_node->find(value, comp); if (ret == it._child_it->_node->end()) { return end(); } - return {this, last, std::move(ret)}; + return {this, last, std::move(ret), 0}; } template <typename T, typename Compare> @@ -1135,7 +1141,7 @@ APFSBtreeNodeIterator<APFSJObjBtreeNode>::own_node( template <> template <> -inline void APFSBtreeNodeIterator<APFSJObjBtreeNode>::init_value<void>() { +inline void APFSBtreeNodeIterator<APFSJObjBtreeNode>::init_value<void>(int recursion_depth) { const auto &t = _node->_table_data.toc.variable[_index]; const auto key_data = _node->_table_data.koff + t.key_offset; const auto val_data = _node->_table_data.voff - t.val_offset; @@ -1156,37 +1162,37 @@ inline void APFSBtreeNodeIterator<APFSJObjBtreeNode>::init_value<void>() { } _child_it = std::make_unique<typename APFSJObjBtreeNode::iterator>( - own_node(_node.get(), it->value->paddr), 0); + own_node(_node.get(), it->value->paddr), 0, recursion_depth); } } template <typename Node> APFSBtreeNodeIterator<Node>::APFSBtreeNodeIterator(const Node *node, - uint32_t index) + uint32_t index, int recursion_depth) : _node{own_node(node)}, _index{index} { // If we're the end, then there's nothing to do if (index >= _node->key_count()) { return; } - init_value(); + init_value(recursion_depth + 1); } template <typename Node> APFSBtreeNodeIterator<Node>::APFSBtreeNodeIterator(lw_shared_ptr<Node> &&node, - uint32_t index) + uint32_t index, int recursion_depth) : _node{std::forward<lw_shared_ptr<Node>>(node)}, _index{index} { // If we're the end, then there's nothing to do if (index >= _node->key_count()) { return; } - init_value(); + init_value(recursion_depth + 1); } template <typename Node> APFSBtreeNodeIterator<Node>::APFSBtreeNodeIterator( - const Node *node, uint32_t index, typename Node::iterator &&child) + const Node *node, uint32_t index, typename Node::iterator &&child, int recursion_depth) : _node{own_node(node)}, _index{index} { _child_it = std::make_unique<typename Node::iterator>( std::forward<typename Node::iterator>(child));