diff --git a/bindings/java/src/org/sleuthkit/datamodel/Account.java b/bindings/java/src/org/sleuthkit/datamodel/Account.java index bec66e453181c5f31af7afebf726adfb0ddb81c9..8db44d7d02c22993cea56f77e564d3b7eedbf7f5 100644 --- a/bindings/java/src/org/sleuthkit/datamodel/Account.java +++ b/bindings/java/src/org/sleuthkit/datamodel/Account.java @@ -99,7 +99,7 @@ public static final class Type { * @param typeName The type name. * @param displayName The display name for the type. */ - Type(String typeName, String displayName) { + public Type(String typeName, String displayName) { this.typeName = typeName; this.displayName = displayName; } diff --git a/bindings/java/src/org/sleuthkit/datamodel/BlackboardAttribute.java b/bindings/java/src/org/sleuthkit/datamodel/BlackboardAttribute.java index f448236346dcd6466fdadf4b59d6c98b73687473..58c85b7ec844400ec87cac021a2fc4a3c71b11dd 100755 --- a/bindings/java/src/org/sleuthkit/datamodel/BlackboardAttribute.java +++ b/bindings/java/src/org/sleuthkit/datamodel/BlackboardAttribute.java @@ -1373,17 +1373,13 @@ public enum ATTRIBUTE_TYPE { bundle.getString("BlackboardAttribute.tskattachments.text"), TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.JSON), - TSK_GEO_DISTANCE_FROM_HOME_POINT(142, "TSK_GEO_DISTANCE_FROM_HOME_POINT", //NON-NLS - bundle.getString("BlackboardAttribute.tskdronehpdistance.text"), - TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.DOUBLE), - - TSK_GEO_DISTANCE_TRAVELED(143, "TSK_GEO_DISTANCE_TRAVELED", //NON-NLS - bundle.getString("BlackboardAttribute.tskdronedistancetraveled.text"), - TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.DOUBLE), - - TSK_GEO_TRACKPOINTS(144, "TSK_GEO_TRACKPOINTS", + TSK_GEO_TRACKPOINTS(142, "TSK_GEO_TRACKPOINTS", bundle.getString("BlackboardAttribute.tskgeopath.text"), TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.JSON), + + TSK_GEO_WAYPOINTS(143, "TSK_GEO_WAYPOINTS", + bundle.getString("BlackboardAttribute.tskgeowaypoints.text"), + TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.JSON), ; diff --git a/bindings/java/src/org/sleuthkit/datamodel/Bundle.properties b/bindings/java/src/org/sleuthkit/datamodel/Bundle.properties index 3908626177bb626ce7028f5880bd815a01897b84..96584aa91e722ee42eeaae568c339dc7c13f736c 100644 --- a/bindings/java/src/org/sleuthkit/datamodel/Bundle.properties +++ b/bindings/java/src/org/sleuthkit/datamodel/Bundle.properties @@ -191,9 +191,8 @@ BlackboardAttribute.tskaccountsettings.text=Account Settings BlackboardAttribute.tskpasswordhint.text=Password Hint BlackboardAttribute.tskgroups.text=Groups BlackboardAttribute.tskattachments.text=Message Attachments -BlackboardAttribute.tskdronehpdistance.text=Distance From Home Point -BlackboardAttribute.tskdronedistancetraveled.text=Total Distance Traveled -BlackboardAttribute.tskgeopath.text=Waypoint List +BlackboardAttribute.tskgeopath.text=List of Track Points +BlackboardAttribute.tskgeowaypoints.text=List of Waypoints AbstractFile.readLocal.exception.msg4.text=Error reading local file\: {0} AbstractFile.readLocal.exception.msg1.text=Error reading local file, local path is not set AbstractFile.readLocal.exception.msg2.text=Error reading local file, it does not exist at local path\: {0} diff --git a/bindings/java/src/org/sleuthkit/datamodel/CommunicationsManager.java b/bindings/java/src/org/sleuthkit/datamodel/CommunicationsManager.java index f468d6a5f63239c9195fdd6e51362bc2aec87833..e4bdea274df1d5b1d1ce04ec707a93c08d278523 100644 --- a/bindings/java/src/org/sleuthkit/datamodel/CommunicationsManager.java +++ b/bindings/java/src/org/sleuthkit/datamodel/CommunicationsManager.java @@ -1,7 +1,7 @@ /* * Sleuth Kit Data Model * - * Copyright 2017-18 Basis Technology Corp. + * Copyright 2017-2020 Basis Technology Corp. * Contact: carrier <at> sleuthkit <dot> org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -33,6 +33,7 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.logging.Level; import java.util.logging.Logger; +import org.sleuthkit.datamodel.Blackboard.BlackboardException; import org.sleuthkit.datamodel.SleuthkitCase.CaseDbConnection; import static org.sleuthkit.datamodel.SleuthkitCase.closeResultSet; import static org.sleuthkit.datamodel.SleuthkitCase.closeStatement; @@ -48,20 +49,18 @@ public final class CommunicationsManager { private final SleuthkitCase db; private final Map<Account.Type, Integer> accountTypeToTypeIdMap - = new ConcurrentHashMap<Account.Type, Integer>(); + = new ConcurrentHashMap<>(); private final Map<String, Account.Type> typeNameToAccountTypeMap - = new ConcurrentHashMap<String, Account.Type>(); - - // Artifact types that represent a relationship between accounts - private final static Set<Integer> RELATIONSHIP_ARTIFACT_TYPE_IDS - = new HashSet<Integer>(Arrays.asList( - BlackboardArtifact.ARTIFACT_TYPE.TSK_MESSAGE.getTypeID(), - BlackboardArtifact.ARTIFACT_TYPE.TSK_EMAIL_MSG.getTypeID(), - BlackboardArtifact.ARTIFACT_TYPE.TSK_CONTACT.getTypeID(), - BlackboardArtifact.ARTIFACT_TYPE.TSK_CALLLOG.getTypeID() - )); - private static final String RELATIONSHIP_ARTIFACT_TYPE_IDS_CSV_STR - = StringUtils.buildCSVString(RELATIONSHIP_ARTIFACT_TYPE_IDS); + = new ConcurrentHashMap<>(); + + // Artifact types that can represent a relationship between accounts. + private static final Set<Integer> RELATIONSHIP_ARTIFACT_TYPE_IDS = new HashSet<Integer>(Arrays.asList( + BlackboardArtifact.ARTIFACT_TYPE.TSK_MESSAGE.getTypeID(), + BlackboardArtifact.ARTIFACT_TYPE.TSK_EMAIL_MSG.getTypeID(), + BlackboardArtifact.ARTIFACT_TYPE.TSK_CONTACT.getTypeID(), + BlackboardArtifact.ARTIFACT_TYPE.TSK_CALLLOG.getTypeID() + )); + private static final String RELATIONSHIP_ARTIFACT_TYPE_IDS_CSV_STR = StringUtils.buildCSVString(RELATIONSHIP_ARTIFACT_TYPE_IDS); /** * Construct a CommunicationsManager for the given SleuthkitCase. @@ -328,7 +327,7 @@ public Account getAccount(org.sleuthkit.datamodel.Account.Type accountType, Stri /** * Adds relationships between the sender and each of the recipient account - * instances and between all recipient account instances. All account + * instances and between all recipient account instances. All account * instances must be from the same data source. * * @param sender sender account @@ -450,36 +449,36 @@ private Account getOrCreateAccount(Account.Type accountType, String accountUniqu } /** - * Get the blackboard artifact for the given account type, account ID, and - * source file. Create an artifact if it doesn't already exist. + * Gets or creates an account artifact for an instance of an account found + * in a file. * - * @param accountType account type - * @param accountUniqueID Unique account ID (such as email address) - * @param moduleName module name that found this instance (for the - * artifact) - * @param sourceFile Source file (for the artifact) + * @param accountType The account type of the account instance. + * @param accountUniqueID The account ID of the account instance, should be + * unique for the account type (e.g., an email + * address for an email account). + * @param moduleName The name of the module that found the account + * instance. + * @param sourceFile The file in which the account instance was found. * - * @return blackboard artifact for the account file instance + * @return The account artifact. * - * @throws TskCoreException exception thrown if a critical error occurs - * within TSK core + * @throws TskCoreException If there is an error querying or updating the + * case database. */ - BlackboardArtifact getOrCreateAccountFileInstanceArtifact(Account.Type accountType, String accountUniqueID, String moduleName, Content sourceFile) throws TskCoreException { - - // see if it already exists + private BlackboardArtifact getOrCreateAccountFileInstanceArtifact(Account.Type accountType, String accountUniqueID, String moduleName, Content sourceFile) throws TskCoreException { BlackboardArtifact accountArtifact = getAccountFileInstanceArtifact(accountType, accountUniqueID, sourceFile); - if (null != accountArtifact) { - return accountArtifact; + if (accountArtifact == null) { + accountArtifact = db.newBlackboardArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_ACCOUNT, sourceFile.getId()); + Collection<BlackboardAttribute> attributes = new ArrayList<>(); + attributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ACCOUNT_TYPE, moduleName, accountType.getTypeName())); + attributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ID, moduleName, accountUniqueID)); + accountArtifact.addAttributes(attributes); + try { + db.getBlackboard().postArtifact(accountArtifact, moduleName); + } catch (BlackboardException ex) { + LOGGER.log(Level.SEVERE, String.format("Error posting new account artifact to the blackboard (object ID = %d)", accountArtifact.getId()), ex); + } } - - // Create a new artifact. - accountArtifact = db.newBlackboardArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_ACCOUNT, sourceFile.getId()); - - Collection<BlackboardAttribute> attributes = new ArrayList<BlackboardAttribute>(); - attributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ACCOUNT_TYPE, moduleName, accountType.getTypeName())); - attributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ID, moduleName, accountUniqueID)); - accountArtifact.addAttributes(attributes); - return accountArtifact; } @@ -662,11 +661,11 @@ public List<AccountDeviceInstance> getAccountDeviceInstancesWithRelationships(Co CommunicationsFilter.RelationshipTypeFilter.class.getName() )); String relationshipFilterSQL = getCommunicationsFilterSQL(filter, applicableInnerQueryFilters); - + String relationshipLimitSQL = getMostRecentFilterLimitSQL(filter); - - String relTblfilterQuery = - "SELECT * " + + String relTblfilterQuery + = "SELECT * " + "FROM account_relationships as relationships" + (relationshipFilterSQL.isEmpty() ? "" : " WHERE " + relationshipFilterSQL) + (relationshipLimitSQL.isEmpty() ? "" : relationshipLimitSQL); @@ -901,17 +900,17 @@ public long getRelationshipSourcesCount(AccountDeviceInstance accountDeviceInsta try { s = connection.createStatement(); - + String innerQuery = " account_relationships AS relationships"; String limitStr = getMostRecentFilterLimitSQL(filter); - - if(!limitStr.isEmpty()) { + + if (!limitStr.isEmpty()) { innerQuery = "(SELECT * FROM account_relationships as relationships " + limitStr + ") as relationships"; } String queryStr = "SELECT count(DISTINCT relationships.relationship_source_obj_id) as count " - + " FROM" + innerQuery + + " FROM" + innerQuery + " WHERE relationships.data_source_obj_id IN ( " + datasourceObjIdsCSV + " )" + " AND ( relationships.account1_id = " + account_id + " OR relationships.account2_id = " + account_id + " )" @@ -935,7 +934,8 @@ public long getRelationshipSourcesCount(AccountDeviceInstance accountDeviceInsta * with accounts on specific devices (AccountDeviceInstance) that meet the * filter criteria. * - * Applicable filters: RelationshipTypeFilter, DateRangeFilter, MostRecentFilter + * Applicable filters: RelationshipTypeFilter, DateRangeFilter, + * MostRecentFilter * * @param accountDeviceInstanceList set of account device instances for * which to get the relationship sources. @@ -985,10 +985,10 @@ public Set<Content> getRelationshipSources(Set<AccountDeviceInstance> accountDev .getName() )); String filterSQL = getCommunicationsFilterSQL(filter, applicableFilters); - + String limitQuery = " account_relationships AS relationships"; String limitStr = getMostRecentFilterLimitSQL(filter); - if(!limitStr.isEmpty()) { + if (!limitStr.isEmpty()) { limitQuery = "(SELECT * FROM account_relationships as relationships " + limitStr + ") as relationships"; } @@ -1152,8 +1152,8 @@ public List<AccountDeviceInstance> getRelatedAccountDeviceInstances(AccountDevic * Get the sources (artifacts, content) of relationships between the given * account device instances. * - * Applicable filters: DeviceFilter, DateRangeFilter, RelationshipTypeFilter, - * MostRecentFilter + * Applicable filters: DeviceFilter, DateRangeFilter, + * RelationshipTypeFilter, MostRecentFilter * * @param account1 First AccountDeviceInstance * @param account2 Second AccountDeviceInstance @@ -1172,13 +1172,13 @@ public List<Content> getRelationshipSources(AccountDeviceInstance account1, Acco CommunicationsFilter.DeviceFilter.class.getName(), CommunicationsFilter.RelationshipTypeFilter.class.getName() )); - + String limitQuery = " account_relationships AS relationships"; String limitStr = getMostRecentFilterLimitSQL(filter); - if(!limitStr.isEmpty()) { + if (!limitStr.isEmpty()) { limitQuery = "(SELECT * FROM account_relationships as relationships " + limitStr + ") as relationships"; } - + String filterSQL = getCommunicationsFilterSQL(filter, applicableFilters); final String queryString = "SELECT artifacts.artifact_id AS artifact_id," + " artifacts.obj_id AS obj_id," @@ -1187,7 +1187,7 @@ public List<Content> getRelationshipSources(AccountDeviceInstance account1, Acco + " artifacts.artifact_type_id AS artifact_type_id," + " artifacts.review_status_id AS review_status_id" + " FROM blackboard_artifacts AS artifacts" - + " JOIN " + limitQuery + + " JOIN " + limitQuery + " ON artifacts.artifact_obj_id = relationships.relationship_source_obj_id" + " WHERE (( relationships.account1_id = " + account1.getAccount().getAccountID() + " AND relationships.account2_id = " + account2.getAccount().getAccountID() @@ -1220,44 +1220,44 @@ public List<Content> getRelationshipSources(AccountDeviceInstance account1, Acco db.releaseSingleUserCaseReadLock(); } } - + /** * Get a list AccountFileInstance for the given accounts. - * + * * @param account List of accounts - * - * @return A lit of AccountFileInstances for the given accounts or null if - * none are found. - * - * @throws org.sleuthkit.datamodel.TskCoreException + * + * @return A lit of AccountFileInstances for the given accounts or null if + * none are found. + * + * @throws org.sleuthkit.datamodel.TskCoreException */ public List<AccountFileInstance> getAccountFileInstances(Account account) throws TskCoreException { List<AccountFileInstance> accountFileInstanceList = new ArrayList<>(); - + List<BlackboardArtifact> artifactList = getSleuthkitCase().getBlackboardArtifacts(BlackboardArtifact.ARTIFACT_TYPE.TSK_ACCOUNT, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ID, account.getTypeSpecificID()); - - if(artifactList != null && !artifactList.isEmpty()) { - for(BlackboardArtifact artifact : artifactList) { + + if (artifactList != null && !artifactList.isEmpty()) { + for (BlackboardArtifact artifact : artifactList) { accountFileInstanceList.add(new AccountFileInstance(artifact, account)); } } - - if(!accountFileInstanceList.isEmpty()) { + + if (!accountFileInstanceList.isEmpty()) { return accountFileInstanceList; } else { return null; } } - + /** * Gets a list of the distinct account types that can currently be found in * the case db. - * + * * @return A list of distinct accounts or an empty list. - * - * @throws TskCoreException + * + * @throws TskCoreException */ - public List<Account.Type> getAccountTypesInUse() throws TskCoreException{ + public List<Account.Type> getAccountTypesInUse() throws TskCoreException { CaseDbConnection connection = db.getConnection(); db.acquireSingleUserCaseReadLock(); Statement s = null; @@ -1272,12 +1272,12 @@ public List<Account.Type> getAccountTypesInUse() throws TskCoreException{ while (rs.next()) { String accountTypeName = rs.getString("type_name"); accountType = this.typeNameToAccountTypeMap.get(accountTypeName); - - if(accountType == null) { + + if (accountType == null) { accountType = new Account.Type(accountTypeName, rs.getString("display_name")); this.accountTypeToTypeIdMap.put(accountType, rs.getInt("account_type_id")); } - + inUseAccounts.add(accountType); } return inUseAccounts; @@ -1341,8 +1341,8 @@ private String normalizePhoneNum(String phoneNum) { if (phoneNum.startsWith("+")) { normailzedPhoneNum = "+" + normailzedPhoneNum; } - - if(normailzedPhoneNum.isEmpty()) { + + if (normailzedPhoneNum.isEmpty()) { normailzedPhoneNum = phoneNum; } @@ -1405,27 +1405,28 @@ private String getCommunicationsFilterSQL(CommunicationsFilter commFilter, Set<S } return sqlStr; } - + /** * Builds the SQL for the MostRecentFilter. - * + * * @param filter The CommunicationsFilter to get the SQL for. - * @return Order BY and LIMIT clause or empty - * string if no filter is available. + * + * @return Order BY and LIMIT clause or empty string if no filter is + * available. */ private String getMostRecentFilterLimitSQL(CommunicationsFilter filter) { String limitStr = ""; - + if (filter != null && !filter.getAndFilters().isEmpty()) { for (CommunicationsFilter.SubFilter subFilter : filter.getAndFilters()) { - if(subFilter.getClass().getName().equals(CommunicationsFilter.MostRecentFilter.class.getName())) { - limitStr = subFilter.getSQL(this); + if (subFilter.getClass().getName().equals(CommunicationsFilter.MostRecentFilter.class.getName())) { + limitStr = subFilter.getSQL(this); break; } } } - + return limitStr; } } diff --git a/bindings/java/src/org/sleuthkit/datamodel/SleuthkitCase.java b/bindings/java/src/org/sleuthkit/datamodel/SleuthkitCase.java index 0bae69b9c46d01dbad0208aaba3e9ea6266cb0ea..a8e66aa4b98b2800e87d78de353290531b2ba6ba 100644 --- a/bindings/java/src/org/sleuthkit/datamodel/SleuthkitCase.java +++ b/bindings/java/src/org/sleuthkit/datamodel/SleuthkitCase.java @@ -10710,6 +10710,7 @@ public void deleteReport(Report report) throws TskCoreException { } catch (SQLException ex) { throw new TskCoreException("Error querying reports table", ex); } finally { + connection.close(); releaseSingleUserCaseWriteLock(); } } diff --git a/bindings/java/src/org/sleuthkit/datamodel/TimelineEventTypes.java b/bindings/java/src/org/sleuthkit/datamodel/TimelineEventTypes.java index 2d062e02b5d5172652f64e8403222d50613d652a..1fb7cd76292126b94ace0eb621e79f904a04ded4 100644 --- a/bindings/java/src/org/sleuthkit/datamodel/TimelineEventTypes.java +++ b/bindings/java/src/org/sleuthkit/datamodel/TimelineEventTypes.java @@ -21,13 +21,12 @@ import com.google.common.net.InternetDomainName; import java.net.URI; import java.net.URISyntaxException; -import java.util.List; import java.util.stream.Collectors; import java.util.stream.Stream; import org.apache.commons.lang3.StringUtils; import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_TRACKPOINTS; -import org.sleuthkit.datamodel.blackboardutils.attributes.GeoTrackPoints; -import org.sleuthkit.datamodel.blackboardutils.attributes.GeoWaypoint.GeoTrackPoint; +import org.sleuthkit.datamodel.blackboardutils.attributes.TskGeoTrackpointsUtil; +import org.sleuthkit.datamodel.blackboardutils.attributes.TskGeoTrackpointsUtil.GeoTrackPointList; /** * Container class for various types of timeline events @@ -57,6 +56,7 @@ static class URLArtifactEventType extends TimelineEventArtifactTypeSingleDescrip super(typeID, displayName, superType, artifactType, timeAttribute, descriptionAttribute); } + @Override TimelineEventDescription parseDescription(String fullDescriptionRaw, String medDescriptionRaw, String shortDescriptionRaw) { /** * Parses the full description from db, which is the full URL, to a @@ -99,6 +99,7 @@ static class FilePathEventType extends TimelineEventTypeImpl { super(typeID, displayName, eventTypeZoomLevel, superType); } + @Override TimelineEventDescription parseDescription(String fullDescription, String medDescription, String shortDescription) { return parseFilePathDescription(fullDescription); } @@ -111,6 +112,7 @@ static class FilePathArtifactEventType extends TimelineEventArtifactTypeSingleDe super(typeID, displayName, superType, artifactType, timeAttribute, descriptionAttribute); } + @Override TimelineEventDescription parseDescription(String fullDescriptionRaw, String medDescriptionRaw, String shortDescriptionRaw) { return parseFilePathDescription(fullDescriptionRaw); } @@ -123,6 +125,8 @@ TimelineEventDescription parseDescription(String fullDescriptionRaw, String medD */ static class GPSTrackArtifactEventType extends TimelineEventArtifactTypeSingleDescription { + private final TskGeoTrackpointsUtil trackpointUtil = new TskGeoTrackpointsUtil(); + GPSTrackArtifactEventType(int typeID, String displayName, TimelineEventType superType, BlackboardArtifact.Type artifactType, BlackboardAttribute.Type descriptionAttribute) { // Passing TSK_GEO_TRACKPOINTS as the "time attribute" as more of a place filler, to avoid any null issues super(typeID, displayName, superType, artifactType, new BlackboardAttribute.Type(TSK_GEO_TRACKPOINTS), descriptionAttribute); @@ -138,15 +142,8 @@ public TimelineEventDescriptionWithTime makeEventDescription(BlackboardArtifact } // Get the waypoint list "start time" - List<GeoTrackPoint> points = GeoTrackPoints.deserializePoints(attribute.getValueString()); - Long startTime = null; - for (GeoTrackPoint point : points) { - // Points are in time order so return the first non-null time stamp - startTime = point.getTimeStamp(); - if (startTime != null) { - break; - } - } + GeoTrackPointList pointsList = trackpointUtil.fromAttribute(attribute); + Long startTime = pointsList.getStartTime(); // If we didn't find a startime do not create an event. if (startTime == null) { diff --git a/bindings/java/src/org/sleuthkit/datamodel/blackboardutils/GeoArtifactsHelper.java b/bindings/java/src/org/sleuthkit/datamodel/blackboardutils/GeoArtifactsHelper.java index 89af4887ff33a91ae54818b7bdef802c2aac6b2a..0e3f99f5a072d33678e470d91dfb72af8b68e6fe 100755 --- a/bindings/java/src/org/sleuthkit/datamodel/blackboardutils/GeoArtifactsHelper.java +++ b/bindings/java/src/org/sleuthkit/datamodel/blackboardutils/GeoArtifactsHelper.java @@ -18,7 +18,8 @@ */ package org.sleuthkit.datamodel.blackboardutils; -import org.sleuthkit.datamodel.blackboardutils.attributes.GeoTrackPoints; +import java.util.ArrayList; +import org.sleuthkit.datamodel.blackboardutils.attributes.TskGeoTrackpointsUtil.GeoTrackPointList; import java.util.List; import org.sleuthkit.datamodel.Blackboard.BlackboardException; import org.sleuthkit.datamodel.BlackboardArtifact; @@ -26,7 +27,9 @@ import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.SleuthkitCase; import org.sleuthkit.datamodel.TskCoreException; -import org.sleuthkit.datamodel.blackboardutils.attributes.GeoWaypoint.GeoTrackPoint; +import org.sleuthkit.datamodel.blackboardutils.attributes.TskGeoWaypointsUtil.GeoWaypointList; +import org.sleuthkit.datamodel.blackboardutils.attributes.TskGeoTrackpointsUtil; +import org.sleuthkit.datamodel.blackboardutils.attributes.TskGeoWaypointsUtil; /** * Class to help ingest modules create Geolocation artifacts. @@ -34,44 +37,112 @@ */ public final class GeoArtifactsHelper extends ArtifactHelperBase { + private final String programName; + private final TskGeoTrackpointsUtil trackPointAttributeUtil; + private final TskGeoWaypointsUtil waypointsAttributeUtil; + /** * Constructs a geolocation artifact helper for the given source file. * - * @param caseDb Sleuthkit case db. + * @param caseDb Sleuthkit case db. * @param moduleName Name of module using the helper. - * @param srcFile Source file being processed by the module. + * @param programName Optional program name for TSK_PROG_NAME attribute, + * nulls and empty string will be ignored. + * @param srcFile Source file being processed by the module. */ - public GeoArtifactsHelper(SleuthkitCase caseDb, String moduleName, Content srcFile) { + public GeoArtifactsHelper(SleuthkitCase caseDb, String moduleName, String programName, Content srcFile) { super(caseDb, moduleName, srcFile); + this.programName = programName; + trackPointAttributeUtil = new TskGeoTrackpointsUtil(); + waypointsAttributeUtil = new TskGeoWaypointsUtil(); } /** - * Add a Track from a GPS device to the database. A Track represents a series of points that the device - * has traveled on. This will create a TSK_GPS_TRACK artifact and add it to the case. + * Add a Track from a GPS device to the database. A Track represents a + * series of points that the device has traveled on. This will create a + * TSK_GPS_TRACK artifact and add it to the case. * - * @param trackName Name of GPS track, not required. Pass in null if unknown. - * @param points Set of GeoTrackPoints that the track traversed. Required. + * @param trackName Name of GPS track, not required. + * @param points List of GeoTrackPoints that the track traversed. + * Required. + * @param moreAttributes Optional list of other artifact attributes * * @return TSK_GPS_TRACK artifact * - * @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 addTrack(String trackName, List<GeoTrackPoint> points) throws TskCoreException, BlackboardException { - if (points == null) { - throw new IllegalArgumentException("GeoTrackPoint instance must be valid"); + public BlackboardArtifact addTrack(String trackName, GeoTrackPointList points, List<BlackboardAttribute> moreAttributes) throws TskCoreException, BlackboardException { + + if(points == null) { + throw new IllegalArgumentException(String.format("addTrack was passed a null list of track points")); } - + BlackboardArtifact artifact = getContent().newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_TRACK); + List<BlackboardAttribute> attributes = new ArrayList<>(); if (trackName != null) { - artifact.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME, getModuleName(), trackName)); + attributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME, getModuleName(), trackName)); + } + + attributes.add(trackPointAttributeUtil.toAttribute(getModuleName(), points)); + + if (programName != null) { + attributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PROG_NAME, getModuleName(), programName)); + } + + if (moreAttributes != null) { + attributes.addAll(moreAttributes); + } + + artifact.addAttributes(attributes); + + getSleuthkitCase().getBlackboard().postArtifact(artifact, getModuleName()); + + return artifact; + } + + /** + * Add a Route from a GPS device to the database. This will create a + * TSK_GPS_ROUTE artifact and add it to the case. + * + * @param routeName Optional route name + * @param creationTime Time the route was created, optional. + * @param points List of GeoWaypointList belonging to the route, required + * @param moreAttributes Optional list of other artifact attributes. + * + * @return TSK_GPS_ROUTE artifact + * + * @throws TskCoreException If there is an error creating the artifact. + * @throws BlackboardException If there is a problem posting the artifact. + */ + public BlackboardArtifact addRoute(String routeName, Long creationTime, GeoWaypointList points, List<BlackboardAttribute> moreAttributes) throws TskCoreException, BlackboardException { + + if (points == null) { + throw new IllegalArgumentException(String.format("addRoute was passed a null list of waypoints")); + } + + BlackboardArtifact artifact = getContent().newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_ROUTE); + List<BlackboardAttribute> attributes = new ArrayList<>(); + + attributes.add(waypointsAttributeUtil.toAttribute(getModuleName(), points)); + + if (routeName != null) { + attributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME, getModuleName(), routeName)); + } + + if (creationTime != null) { + attributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME, getModuleName(), creationTime)); + } + + if (programName != null) { + attributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PROG_NAME, getModuleName(), programName)); + } + + if (moreAttributes != null) { + attributes.addAll(moreAttributes); } - artifact.addAttribute( - new BlackboardAttribute( - BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_TRACKPOINTS, - getModuleName(), - GeoTrackPoints.serializePoints(points))); + artifact.addAttributes(attributes); getSleuthkitCase().getBlackboard().postArtifact(artifact, getModuleName()); diff --git a/bindings/java/src/org/sleuthkit/datamodel/blackboardutils/WebBrowserArtifactsHelper.java b/bindings/java/src/org/sleuthkit/datamodel/blackboardutils/WebBrowserArtifactsHelper.java index 40e3177e4649aa517609b7f233b608b98235ac64..1a4cb5f2cb808720b6fcefaac49efa1a59fdbd2b 100644 --- a/bindings/java/src/org/sleuthkit/datamodel/blackboardutils/WebBrowserArtifactsHelper.java +++ b/bindings/java/src/org/sleuthkit/datamodel/blackboardutils/WebBrowserArtifactsHelper.java @@ -1,7 +1,7 @@ /* * Sleuth Kit Data Model * - * Copyright 2019 Basis Technology Corp. + * Copyright 2019-2020 Basis Technology Corp. * Contact: carrier <at> sleuthkit <dot> org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -25,10 +25,11 @@ import java.util.Collections; import java.util.StringTokenizer; import org.apache.commons.lang3.StringUtils; -import org.sleuthkit.datamodel.AbstractFile; +import org.sleuthkit.datamodel.Account; import org.sleuthkit.datamodel.Blackboard.BlackboardException; import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardAttribute; +import org.sleuthkit.datamodel.CommunicationsManager; import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.SleuthkitCase; import org.sleuthkit.datamodel.TskCoreException; @@ -296,6 +297,15 @@ public BlackboardArtifact addWebFormAddress(String personName, String email, BlackboardArtifact webFormAddressArtifact; Collection<BlackboardAttribute> attributes = new ArrayList<>(); + + CommunicationsManager commManager = this.getSleuthkitCase().getCommunicationsManager(); + if(StringUtils.isNotBlank(email)) { + commManager.createAccountFileInstance(Account.Type.EMAIL, email, this.getModuleName(), this.getContent()); + } + + if(StringUtils.isNotBlank(phoneNumber)) { + commManager.createAccountFileInstance(Account.Type.PHONE, phoneNumber, this.getModuleName(), this.getContent()); + } // create artifact webFormAddressArtifact = getContent().newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_FORM_ADDRESS); diff --git a/bindings/java/src/org/sleuthkit/datamodel/blackboardutils/attributes/BlackboardAttributeUtil.java b/bindings/java/src/org/sleuthkit/datamodel/blackboardutils/attributes/BlackboardAttributeUtil.java new file mode 100755 index 0000000000000000000000000000000000000000..d32b264a56a6309498990d944c4125089b9bb48a --- /dev/null +++ b/bindings/java/src/org/sleuthkit/datamodel/blackboardutils/attributes/BlackboardAttributeUtil.java @@ -0,0 +1,48 @@ +/* + * Sleuth Kit Data Model + * + * Copyright 2020 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.blackboardutils.attributes; + +import org.sleuthkit.datamodel.BlackboardAttribute; + +/** + * An interface for Utility classes to implement for translating + * BlackboardAttributes to and from a particular format. Initial use case is for + * BlackboardAttributes of type TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.JSON. + */ +public interface BlackboardAttributeUtil<T> { + + /** + * Translates the value of type T to a attribute. + * + * @param moduleName Name of module creating the artifact + * @param value Object to Translate to attribute + * + * @return BlackboardAttribute created from value + */ + BlackboardAttribute toAttribute(String moduleName, T value); + + /** + * Translates a attribute to an object of type T. + * + * @param attribute The attribute to be translated to T + * + * @return A new instance of T created from the attribute + */ + T fromAttribute(BlackboardAttribute attribute); +} diff --git a/bindings/java/src/org/sleuthkit/datamodel/blackboardutils/attributes/GeoTrackPoints.java b/bindings/java/src/org/sleuthkit/datamodel/blackboardutils/attributes/GeoTrackPoints.java deleted file mode 100755 index 21474995e57360bf5102f1283424ec6ec86bcdfc..0000000000000000000000000000000000000000 --- a/bindings/java/src/org/sleuthkit/datamodel/blackboardutils/attributes/GeoTrackPoints.java +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Sleuth Kit Data Model - * - * Copyright 2020 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.blackboardutils.attributes; - -import com.google.gson.Gson; -import java.util.ArrayList; -import java.util.List; -import java.util.stream.Collectors; -import org.sleuthkit.datamodel.blackboardutils.attributes.GeoWaypoint.GeoTrackPoint; - -/** - * Helper class to make it easier to serialize and deserialize the list of track - * points with Gson. - * - */ -public final class GeoTrackPoints { - - private final List<GeoTrackPoint> points; - - /** - * Deserialize the given list of GeoTrackPoints. - * - * @param jsonString JSon string of track points. - * - * @return Timestamp ordered list of GeoTrackPoints, empty list will be - * returned if jsonString is null or empty. - */ - public static List<GeoTrackPoint> deserializePoints(String jsonString) { - if (jsonString == null || jsonString.isEmpty()) { - return new ArrayList<>(); - } - - GeoTrackPoints trackPoints = (new Gson()).fromJson(jsonString, GeoTrackPoints.class); - return trackPoints.getTimeOrderedPoints(); - } - - /** - * Serialize the given list of GeoTrackPoints. - * - * @param points List of GeoTrackPoints - * - * @return JSon formatted string is returned or empty string if points was - * null - */ - public static String serializePoints(List<GeoTrackPoint> points) { - if (points == null) { - return ""; - } - - Gson gson = new Gson(); - return gson.toJson(new GeoTrackPoints(points)); - } - - /** - * Constructs a new instance with the give list of GeoTrackPoints. - * - * @param points - */ - private GeoTrackPoints(List<GeoTrackPoint> points) { - if (points == null) { - throw new IllegalArgumentException("Invalid list of track points passed to constructor"); - } - - this.points = points; - } - - /** - * Returns a timestamp ordered copy of the points list. - * - * @return timestamp - */ - private List<GeoTrackPoint> getTimeOrderedPoints() { - return points.stream().sorted().collect(Collectors.toCollection(ArrayList::new)); - } -} diff --git a/bindings/java/src/org/sleuthkit/datamodel/blackboardutils/attributes/GeoWaypoint.java b/bindings/java/src/org/sleuthkit/datamodel/blackboardutils/attributes/GeoWaypoint.java deleted file mode 100755 index 14fb0358d991a7a3c495c849a02361337d9d6bde..0000000000000000000000000000000000000000 --- a/bindings/java/src/org/sleuthkit/datamodel/blackboardutils/attributes/GeoWaypoint.java +++ /dev/null @@ -1,179 +0,0 @@ -/* - * Sleuth Kit Data Model - * - * Copyright 2020 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.blackboardutils.attributes; - -import com.google.gson.annotations.SerializedName; - -/** - * Class that represents a single waypoint made up of longitude, latitude, and - * altitude. - */ -public class GeoWaypoint { - - @SerializedName("TSK_GEO_LATITUDE") - private final Double latitude; - @SerializedName("TSK_GEO_LONGITUDE") - private final Double longitude; - @SerializedName("TSK_GEO_ALTITUDE") - private final Double altitude; - - /** - * Creates a GeoWaypoint instance. - * - * @param latitude The latitude, required - * @param longitude The longitude, required - * @param altitude The altitude, can be null - */ - public GeoWaypoint(Double latitude, Double longitude, Double altitude) { - if (latitude == null || longitude == null) { - throw new IllegalArgumentException("Null cordinate value passed to waypoint constructor"); - } - - this.latitude = latitude; - this.longitude = longitude; - this.altitude = altitude; - } - - /** - * Returns latitude of the waypoint. - * - * @return Double latitude value - */ - public Double getLatitude() { - return latitude; - } - - /** - * Returns longitude of the waypoint. - * - * @return Double longitude value - */ - public Double getLongitude() { - return longitude; - } - - /** - * Get the altitude if available for this waypoint. - * - * @return Double altitude value, maybe null if not available or applicable - */ - public Double getAltitude() { - return altitude; - } - - /** - * A GeoTrackPoint is a Waypoint with more detailed information about the - * point. - * - */ - public final static class GeoTrackPoint extends GeoWaypoint implements Comparable<GeoTrackPoint> { - - @SerializedName("TSK_GEO_VELOCITY") - private final Double velocity; - @SerializedName("TSK_GEO_DISTANCE_FROM_HOME_POINT") - private final Double distanceFromHP; - @SerializedName("TSK_GEO_DISTANCE_TRAVELED") - private final Double distanceTraveled; - @SerializedName("TSK_DATETIME") - private final Long timestamp; - - /** - * Constructs a GeoTrackPoint with the given attributes. - * - * @param latitude Latitude of the trackpoint, required - * @param longitude Longitude of the trackpoint, required - * @param altitude Altitude of the trackpoint, maybe null - * @param velocity Velocity in meters/sec, maybe null - * @param distanceFromHP Trackpoint distance from an established "home - * point", maybe null if not applicable - * @param distanceTraveled Overall distance traveled in meters at the - * time this trackpoint was created, maybe null - * if not applicable - * @param timestamp Trackpoint creation time, maybe null if not - * applicable - */ - public GeoTrackPoint(Double latitude, - Double longitude, - Double altitude, - Double velocity, - Double distanceFromHP, - Double distanceTraveled, - Long timestamp) { - super(latitude, longitude, altitude); - this.velocity = velocity; - this.distanceFromHP = distanceFromHP; - this.distanceTraveled = distanceTraveled; - this.timestamp = timestamp; - } - - /** - * Returns velocity of the point. - * - * @return Double velocity value, maybe null if not available or - * applicable - */ - public Double getVelocity() { - return velocity; - } - - /** - * Returns distance from home point for the point. - * - * @return Double velocity distance from home point, maybe null if not - * available or applicable - */ - public Double getDistanceFromHP() { - return distanceFromHP; - } - - /** - * Returns distance traveled for the point. - * - * @return Double distance traveled value, maybe null if not available - * or applicable - */ - public Double getDistanceTraveled() { - return distanceTraveled; - } - - /** - * Returns the time stamp (seconds from java/unix epoch) of the track - * point. - * - * @return time stamp of the track point, or null if not available - */ - public Long getTimeStamp() { - return timestamp; - } - - @Override - public int compareTo(GeoTrackPoint otherTP) { - Long otherTimeStamp = otherTP.getTimeStamp(); - - if (timestamp == null && otherTimeStamp != null) { - return -1; - } else if (timestamp != null && otherTimeStamp == null) { - return 1; - } else { - return timestamp.compareTo(otherTP.getTimeStamp()); - } - } - } - -} diff --git a/bindings/java/src/org/sleuthkit/datamodel/blackboardutils/attributes/TskGeoTrackpointsUtil.java b/bindings/java/src/org/sleuthkit/datamodel/blackboardutils/attributes/TskGeoTrackpointsUtil.java new file mode 100755 index 0000000000000000000000000000000000000000..ad434f5c7d3e1da86cbf7620629b9fb0a9c9b835 --- /dev/null +++ b/bindings/java/src/org/sleuthkit/datamodel/blackboardutils/attributes/TskGeoTrackpointsUtil.java @@ -0,0 +1,330 @@ +/* + * Sleuth Kit Data Model + * + * Copyright 2020 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.blackboardutils.attributes; + +import com.google.gson.Gson; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.stream.Collectors; +import org.sleuthkit.datamodel.BlackboardAttribute; +import org.sleuthkit.datamodel.blackboardutils.attributes.TskGeoTrackpointsUtil.GeoTrackPointList; +import org.sleuthkit.datamodel.blackboardutils.attributes.TskGeoTrackpointsUtil.GeoTrackPointList.GeoTrackPoint; + +/** + * Utility class for translating TSK_GEO_TRACKPOINTS attribute values to + * GeoTrackPointList objects and GeoTrackPointList to BlackboardAttributes. + */ +public final class TskGeoTrackpointsUtil implements BlackboardAttributeUtil<GeoTrackPointList> { + + @Override + public BlackboardAttribute toAttribute(String moduleName, GeoTrackPointList value) { + + if (value == null) { + throw new IllegalArgumentException("toAttribute was passed a null list"); + } + + return new BlackboardAttribute( + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_TRACKPOINTS, + moduleName, + toJSON(value)); + } + + @Override + public GeoTrackPointList fromAttribute(BlackboardAttribute attribute) { + if (attribute == null) { + throw new IllegalArgumentException("fromAttribute was passed a null attribute"); + } + + BlackboardAttribute.ATTRIBUTE_TYPE type = BlackboardAttribute.ATTRIBUTE_TYPE.fromID(attribute.getAttributeType().getTypeID()); + if (type != BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_TRACKPOINTS) { + throw new IllegalArgumentException(String.format("Invalid attribute of type %s passed to fromAttribute method. Attribute of type TSK_GEO_TRACKPOINTS is required", type.getDisplayName())); + } + + return fromJSON(attribute.getValueString()); + } + + /** + * Creates a GeoTrackPointList from the given JSON string. + * + * @param jsonString JSon string of track points. + * + * @return Timestamp ordered list of GeoTrackPoints, empty list will be + * returned if jsonString is null or empty. + */ + private static GeoTrackPointList fromJSON(String jsonString) { + if (jsonString == null || jsonString.isEmpty()) { + return null; + } + + return (new Gson()).fromJson(jsonString, GeoTrackPointList.class); + } + + /** + * Returns a JSON string representing the given object. + * + * @return JSON string + */ + private static String toJSON(GeoTrackPointList pointList) { + Gson gson = new Gson(); + return gson.toJson(pointList); + } + + /** + * A list of GeoTrackPoints. + */ + public static class GeoTrackPointList implements Iterable<GeoTrackPointList.GeoTrackPoint> { + + private final List<GeoTrackPoint> pointList; + + /** + * Construct an empty GeoTrackPointList. + */ + public GeoTrackPointList() { + pointList = new ArrayList<>(); + } + + /** + * Construct a new instance with the given list of GeoTrackPoint + * objects. + * + * @param points List of track points, cannot be null. + */ + public GeoTrackPointList(List<GeoTrackPoint> points) { + if (points == null) { + throw new IllegalArgumentException("Constructor was passed a null list"); + } + + pointList = points; + } + + /** + * Add a point to the list of track points. + * + * @param point A point to add to the track point list, cannot be null. + */ + public void addPoint(GeoTrackPoint point) { + if (point == null) { + throw new IllegalArgumentException("addPoint was passed a null list"); + } + + pointList.add(point); + } + + /** + * Adds a new point with the given attributes. + * + * @param latitude Latitude of the trackpoint, required + * @param longitude Longitude of the trackpoint, required + * @param altitude Altitude of the trackpoint, maybe null + * @param name Name of trackpoint, maybe null + * @param velocity Velocity in meters/sec, maybe null + * @param distanceFromHomePoint Track point distance from an established + * "home point", may be null if not + * applicable + * @param distanceTraveled Overall distance traveled in meters at + * the time this trackpoint was created, + * maybe null if not applicable + * @param timestamp Trackpoint creation time, maybe null if + * not applicable + */ + public void addPoint(Double latitude, + Double longitude, + Double altitude, + String name, + Double velocity, + Double distanceFromHomePoint, + Double distanceTraveled, + Long timestamp) { + pointList.add(new GeoTrackPoint( + latitude, + longitude, + altitude, + name, + velocity, + distanceFromHomePoint, + distanceTraveled, + timestamp)); + } + + /** + * Returns an iterator over the points in this GeoTrackPointList. + * + * @return An iterator over the elements of the list. + */ + @Override + public Iterator<GeoTrackPoint> iterator() { + return pointList.iterator(); + } + + /** + * Returns true if this list contains no points. + * + * @return True if this list contains no points. + */ + public boolean isEmpty() { + return pointList.isEmpty(); + } + + /** + * Return the start time for the track. + * + * @return First non-null time stamp or null, if one was not found. + */ + public Long getStartTime() { + List<GeoTrackPoint> orderedPoints = getTimeOrderedPoints(); + if (orderedPoints != null) { + for (GeoTrackPoint point : orderedPoints) { + if (point.getTimeStamp() != null) { + return point.getTimeStamp(); + } + } + } + return null; + } + + /** + * Return the ends time for the track. + * + * @return First non-null time stamp or null, if one was not found. + */ + public Long getEndTime() { + List<GeoTrackPoint> orderedPoints = getTimeOrderedPoints(); + if (orderedPoints != null) { + for (int index = orderedPoints.size() - 1; index >= 0; index--) { + GeoTrackPoint point = orderedPoints.get(index); + if (point.getTimeStamp() != null) { + return point.getTimeStamp(); + } + } + } + return null; + } + + /** + * Returns a timestamp ordered copy of the points list. + * + * @return List of points sorted by timestamps. + */ + private List<GeoTrackPoint> getTimeOrderedPoints() { + return pointList.stream().sorted().collect(Collectors.toCollection(ArrayList::new)); + } + + /** + * A GeoTrackPoint is a Waypoint with more detailed information about + * the point. + * + */ + public final static class GeoTrackPoint extends TskGeoWaypointsUtil.GeoWaypointList.GeoWaypoint implements Comparable<GeoTrackPointList.GeoTrackPoint> { + + private final Double velocity; + private final Double distanceFromHomePoint; + private final Double distanceTraveled; + private final Long timestamp; + + /** + * Constructs a GeoTrackPoint with the given attributes. + * + * @param latitude Latitude of the track point, required + * @param longitude Longitude of the track point, + * required + * @param altitude Altitude of the track point, may be + * null + * @param name Name of track point, may be null + * @param velocity Velocity in meters/sec, may be null + * @param distanceFromHomePoint Track point distance from an + * established "home point", maybe null + * if not applicable + * @param distanceTraveled Overall distance traveled in meters + * at the time this track point was + * created, maybe null if not + * applicable + * @param timestamp Track point creation time, maybe + * null if not applicable + */ + public GeoTrackPoint(Double latitude, + Double longitude, + Double altitude, + String name, + Double velocity, + Double distanceFromHomePoint, + Double distanceTraveled, + Long timestamp) { + super(latitude, longitude, altitude, name); + this.velocity = velocity; + this.distanceFromHomePoint = distanceFromHomePoint; + this.distanceTraveled = distanceTraveled; + this.timestamp = timestamp; + } + + /** + * Returns velocity of the point. + * + * @return Double velocity value, maybe null if not available or + * applicable + */ + public Double getVelocity() { + return velocity; + } + + /** + * Returns distance from home point for the point. + * + * @return Double velocity distance from home point, maybe null if + * not available or applicable + */ + public Double getDistanceFromHomePoint() { + return distanceFromHomePoint; + } + + /** + * Returns distance traveled for the point. + * + * @return Double distance traveled value, maybe null if not + * available or applicable + */ + public Double getDistanceTraveled() { + return distanceTraveled; + } + + /** + * Returns the time stamp (seconds from java/unix epoch) of the + * track point. + * + * @return time stamp of the track point, or null if not available + */ + public Long getTimeStamp() { + return timestamp; + } + + @Override + public int compareTo(GeoTrackPointList.GeoTrackPoint otherTP) { + Long otherTimeStamp = otherTP.getTimeStamp(); + + if (timestamp == null && otherTimeStamp != null) { + return -1; + } else if (timestamp != null && otherTimeStamp == null) { + return 1; + } else { + return timestamp.compareTo(otherTP.getTimeStamp()); + } + } + } + } +} diff --git a/bindings/java/src/org/sleuthkit/datamodel/blackboardutils/attributes/TskGeoWaypointsUtil.java b/bindings/java/src/org/sleuthkit/datamodel/blackboardutils/attributes/TskGeoWaypointsUtil.java new file mode 100755 index 0000000000000000000000000000000000000000..08d5a1c6bd6a77ab67bf0992c5a73a2d4e58ebe5 --- /dev/null +++ b/bindings/java/src/org/sleuthkit/datamodel/blackboardutils/attributes/TskGeoWaypointsUtil.java @@ -0,0 +1,197 @@ +/* + * Sleuth Kit Data Model + * + * Copyright 2020 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.blackboardutils.attributes; + +import com.google.gson.Gson; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import org.sleuthkit.datamodel.BlackboardAttribute; +import org.sleuthkit.datamodel.blackboardutils.attributes.TskGeoWaypointsUtil.GeoWaypointList; +import org.sleuthkit.datamodel.blackboardutils.attributes.TskGeoWaypointsUtil.GeoWaypointList.GeoWaypoint; + +/** + * Utility class for Translating TSK_GEO_WAYPOINTS attribute values to + * GeoWaypointList objects and GeoWaypointList to BlackboardAttributes. + */ +public final class TskGeoWaypointsUtil implements BlackboardAttributeUtil<GeoWaypointList> { + + @Override + public BlackboardAttribute toAttribute(String moduleName, GeoWaypointList value) { + + if (value == null) { + throw new IllegalArgumentException("toAttribute was pass a null list"); + } + + return new BlackboardAttribute( + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_WAYPOINTS, + moduleName, + toJSON(value)); + } + + @Override + public GeoWaypointList fromAttribute(BlackboardAttribute attribute) { + if (attribute == null) { + throw new IllegalArgumentException("fromAttribute was pass a null list"); + } + + BlackboardAttribute.ATTRIBUTE_TYPE type = BlackboardAttribute.ATTRIBUTE_TYPE.fromID(attribute.getAttributeType().getTypeID()); + if (type != BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_WAYPOINTS) { + throw new IllegalArgumentException(String.format("Invalid attribute of type %s passed to fromAttribute method. Attribute of type TSK_GEO_WAYPOINTS is required", type.getDisplayName())); + } + + return fromJSON(attribute.getValueString()); + } + + /** + * Deserialize the given list of GeoTrackPoints. + * + * @param jsonString JSon string of track points. + * + * @return Timestamp ordered list of GeoTrackPoints, empty list will be + * returned if jsonString is null or empty. + */ + private static GeoWaypointList fromJSON(String jsonString) { + if (jsonString == null || jsonString.isEmpty()) { + return null; + } + + return (new Gson()).fromJson(jsonString, GeoWaypointList.class); + } + + /** + * Returns a JSON string can than be used as the TSK_GEO_TRACKPOINTS + * attribute of the TSK_GPS_TRACK artifact. + * + * @return JSON string + */ + private static String toJSON(GeoWaypointList pointList) { + Gson gson = new Gson(); + return gson.toJson(pointList); + } + + /** + * Helper class to make it easier to serialize and deserialize the list of + * waypoints points with json. + * + */ + public static final class GeoWaypointList implements Iterable<GeoWaypointList.GeoWaypoint> { + + private final List<GeoWaypoint> points; + + public GeoWaypointList() { + points = new ArrayList<>(); + } + + /** + * Adds a point to the list of waypoints. + * + * @param latitude The latitude, required + * @param longitude The longitude, required + * @param altitude The altitude, can be null + * @param name A name for the point, can be null + */ + public void addPoint(Double latitude, Double longitude, Double altitude, String name) { + points.add(new GeoWaypoint(latitude, longitude, altitude, name)); + } + + /** + * Returns true if this list contains no points. + * + * @return True if this list contains no points. + */ + public boolean isEmpty() { + return points.isEmpty(); + } + + @Override + public Iterator<GeoWaypointList.GeoWaypoint> iterator() { + return points.iterator(); + } + + /** + * Class that represents a single waypoint made up of longitude, + * latitude, and altitude. + */ + public static class GeoWaypoint { + + private final Double latitude; + private final Double longitude; + private final Double altitude; + private final String name; + + /** + * Creates a GeoWaypoint instance. + * + * @param latitude The latitude, required + * @param longitude The longitude, required + * @param altitude The altitude, can be null + * @param name A name for the waypoint, optional + */ + public GeoWaypoint(Double latitude, Double longitude, Double altitude, String name) { + if (latitude == null || longitude == null) { + throw new IllegalArgumentException("Constructor was passed null coordinate"); + } + + this.latitude = latitude; + this.longitude = longitude; + this.altitude = altitude; + this.name = name; + } + + /** + * Returns latitude of the waypoint. + * + * @return Double latitude value + */ + public Double getLatitude() { + return latitude; + } + + /** + * Returns longitude of the waypoint. + * + * @return Double longitude value + */ + public Double getLongitude() { + return longitude; + } + + /** + * Get the altitude if available for this waypoint. + * + * @return Double altitude value, may be null if not available or + * applicable + */ + public Double getAltitude() { + return altitude; + } + + /** + * Returns the name for this waypoint. + * + * @return Returns waypoint name, may be null if not available or + * applicable. + */ + public String getName() { + return name; + } + } + } +} diff --git a/tsk/auto/auto.cpp b/tsk/auto/auto.cpp index 6c08103fb23f4e25cb84cb76eb8653703033c919..fc95317e234d14153a54749a07718e83ba59eff9 100755 --- a/tsk/auto/auto.cpp +++ b/tsk/auto/auto.cpp @@ -146,6 +146,10 @@ uint8_t TskAuto::openImageHandle(TSK_IMG_INFO * a_img_info) void TskAuto::closeImage() { + for (int i = 0; i < m_poolInfos.size(); i++) { + tsk_pool_close(m_poolInfos[i]); + } + if ((m_img_info) && (m_internalOpen)) { tsk_img_close(m_img_info); } @@ -493,7 +497,10 @@ TskAuto::findFilesInPool(TSK_OFF_T start, TSK_POOL_TYPE_ENUM ptype) registerError(); return TSK_ERR; } - pool->close(pool); + + // Store the pool_info for later use. It will be closed at the end of the add image process. + m_poolInfos.push_back(pool); + return TSK_OK; } diff --git a/tsk/auto/auto_db.cpp b/tsk/auto/auto_db.cpp index 43b84169e186b4603470bb4dbb7f1b41a38e8d00..0fdfdf67090eb6468ae72143481e4f75e3f48afe 100755 --- a/tsk/auto/auto_db.cpp +++ b/tsk/auto/auto_db.cpp @@ -315,6 +315,8 @@ TskAutoDb::filterPool(const TSK_POOL_INFO * pool_info) registerError(); return TSK_FILTER_STOP; } + // Save the parent obj ID for the pool + m_poolOffsetToParentId[pool_info->img_offset] = m_curVolId; } else { // pool doesn't live in a volume, use image as parent @@ -322,13 +324,81 @@ TskAutoDb::filterPool(const TSK_POOL_INFO * pool_info) registerError(); return TSK_FILTER_STOP; } + // Save the parent obj ID for the pool + m_poolOffsetToParentId[pool_info->img_offset] = m_curImgId; } - + // Store the volume system object ID for later use + m_poolOffsetToVsId[pool_info->img_offset] = m_curPoolVs; return TSK_FILTER_CONT; } +/** +* Adds unallocated pool blocks to a new volume. +* +* @param numPool Will be updated with the number of pools processed +* +* @return Returns 0 for success, 1 for failure +*/ +TSK_RETVAL_ENUM +TskAutoDb::addUnallocatedPoolBlocksToDb(size_t & numPool) { + + for (int i = 0; i < m_poolInfos.size(); i++) { + const TSK_POOL_INFO * pool_info = m_poolInfos[i]; + if (m_poolOffsetToVsId.find(pool_info->img_offset) == m_poolOffsetToVsId.end()) { + tsk_error_reset(); + tsk_error_set_errno(TSK_ERR_AUTO_DB); + tsk_error_set_errstr("Error addUnallocatedPoolBlocksToDb() - could not find volume system object ID for pool at offset %lld", pool_info->img_offset); + return TSK_ERR; + } + int64_t curPoolVs = m_poolOffsetToVsId[pool_info->img_offset]; + + /* Make sure the pool_info is still allocated */ + if (pool_info->tag != TSK_POOL_INFO_TAG) { + tsk_error_reset(); + tsk_error_set_errno(TSK_ERR_AUTO_DB); + tsk_error_set_errstr("Error addUnallocatedPoolBlocksToDb() - pool_info is not allocated"); + return TSK_ERR; + } + + /* Only APFS pools are currently supported */ + if (pool_info->ctype != TSK_POOL_TYPE_APFS) { + continue; + } + + /* Increment the count of pools found */ + numPool++; + + /* Create the volume */ + int64_t unallocVolObjId; + m_db->addUnallocatedPoolVolume(pool_info->num_vols, curPoolVs, unallocVolObjId); + + /* 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, NULL, current_run->len * pool_info->block_size, ranges, fileObjId, m_curImgId)) { + 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); + } + + return TSK_OK; +} + TSK_FILTER_ENUM TskAutoDb::filterPoolVol(const TSK_POOL_VOLUME_INFO * pool_vol) { @@ -1032,7 +1102,7 @@ TSK_WALK_RET_ENUM TskAutoDb::fsWalkUnallocBlocksCb(const TSK_FS_BLOCK *a_block, */ TSK_RETVAL_ENUM TskAutoDb::addFsInfoUnalloc(const TSK_DB_FS_INFO & dbFsInfo) { - // Unalloc space is not yet implemented for APFS + // Unalloc space is handled separately for APFS if (dbFsInfo.fType == TSK_FS_TYPE_APFS) { return TSK_OK; } @@ -1103,18 +1173,20 @@ TSK_RETVAL_ENUM TskAutoDb::addUnallocSpaceToDb() { size_t numVsP = 0; size_t numFs = 0; + size_t numPool = 0; TSK_RETVAL_ENUM retFsSpace = addUnallocFsSpaceToDb(numFs); TSK_RETVAL_ENUM retVsSpace = addUnallocVsSpaceToDb(numVsP); + TSK_RETVAL_ENUM retPoolSpace = addUnallocatedPoolBlocksToDb(numPool); - //handle case when no fs and no vs partitions + //handle case when no fs and no vs partitions or pools TSK_RETVAL_ENUM retImgFile = TSK_OK; - if (numVsP == 0 && numFs == 0) { + if (numVsP == 0 && numFs == 0 && numPool == 0) { retImgFile = addUnallocImageSpaceToDb(); } - if (retFsSpace == TSK_ERR || retVsSpace == TSK_ERR || retImgFile == TSK_ERR) + if (retFsSpace == TSK_ERR || retVsSpace == TSK_ERR || retPoolSpace == TSK_ERR || retImgFile == TSK_ERR) return TSK_ERR; else return TSK_OK; @@ -1217,6 +1289,19 @@ TSK_RETVAL_ENUM TskAutoDb::addUnallocVsSpaceToDb(size_t & numVsP) { //skip processing this vspart continue; } + + // Check if the volume contains a pool + bool hasPool = false; + for (std::map<int64_t, int64_t>::iterator iter = m_poolOffsetToParentId.begin(); iter != m_poolOffsetToParentId.end(); ++iter) { + if (iter->second == vsPart.objId) { + hasPool = true; + } + } + if (hasPool) { + // Skip processing this vspart + continue; + } + } //end checking vspart flags //get sector size and image offset from parent vs info diff --git a/tsk/auto/db_postgresql.cpp b/tsk/auto/db_postgresql.cpp index 4afe0e00fa6e847e3ae87dca409565b229ca162a..b7bc126d7bcb8bff0cfb8f7140ac6a74cf621f07 100755 --- a/tsk/auto/db_postgresql.cpp +++ b/tsk/auto/db_postgresql.cpp @@ -1012,8 +1012,18 @@ int TskDbPostgreSQL::addImageName(int64_t objId, char const *imgName, int sequen return ret; } +/** +* Creates a new tsk_pool_info database entry and a new tsk_vs_info +* entry with the tsk_pool_info as its parent. +* +* @ param pool_info The pool to save to the database +* @ param parObjId The ID of the parent of the pool object +* @ param vsObjId Will be set to the object ID of the new volume system created as a child of +* the new pool. +* @returns 1 on error, 0 on success +*/ int -TskDbPostgreSQL::addPoolInfoAndVS(const TSK_POOL_INFO *pool_info, int64_t parObjId, int64_t& objId) { +TskDbPostgreSQL::addPoolInfoAndVS(const TSK_POOL_INFO *pool_info, int64_t parObjId, int64_t& vsObjId) { char stmt[1024]; @@ -1032,11 +1042,11 @@ TskDbPostgreSQL::addPoolInfoAndVS(const TSK_POOL_INFO *pool_info, int64_t parObj } // Add volume system - if (addObject(TSK_DB_OBJECT_TYPE_VS, poolObjId, objId)) + if (addObject(TSK_DB_OBJECT_TYPE_VS, poolObjId, vsObjId)) return 1; snprintf(stmt, 1024, - "INSERT INTO tsk_vs_info (obj_id, vs_type, img_offset, block_size) VALUES (%" PRId64 ", %d,%" PRIuDADDR ",%d)", objId, TSK_VS_TYPE_APFS, pool_info->img_offset, pool_info->block_size); + "INSERT INTO tsk_vs_info (obj_id, vs_type, img_offset, block_size) VALUES (%" PRId64 ", %d,%" PRIuDADDR ",%d)", vsObjId, TSK_VS_TYPE_APFS, pool_info->img_offset, pool_info->block_size); return attempt_exec(stmt, "Error adding data to tsk_vs_info table: %s\n"); @@ -1044,6 +1054,11 @@ TskDbPostgreSQL::addPoolInfoAndVS(const TSK_POOL_INFO *pool_info, int64_t parObj /** * Adds the sector addresses of the pool volumes into the db. +* +* @param pool_vol The pool volume to save to the DB +* @param parObjId The ID of the parent of the pool volume (should be a volume system) +* @param objId Will be set to the object ID of the new volume +* * @returns 1 on error, 0 on success */ int @@ -1073,6 +1088,41 @@ TskDbPostgreSQL::addPoolVolumeInfo(const TSK_POOL_VOLUME_INFO* pool_vol, return TSK_OK; } +/** +* Adds a fake volume that will hold the unallocated blocks for the pool. +* +* @param vol_index The index for the new volume (should be one higher than the number of pool volumes) +* @param parObjId The object ID of the parent volume system +* @param objId Will be set to the object ID of the new volume +* +* @returns 1 on error, 0 on success +*/ +int +TskDbPostgreSQL::addUnallocatedPoolVolume(int vol_index, int64_t parObjId, int64_t& objId) +{ + + char stmt[1024]; + + if (addObject(TSK_DB_OBJECT_TYPE_VOL, parObjId, objId)) + return 1; + + char *desc = "Unallocated Blocks"; + char *desc_sql = PQescapeLiteral(conn, desc, strlen(desc)); + + snprintf(stmt, 1024, + "INSERT INTO tsk_vs_parts (obj_id, addr, start, length, descr, flags)" + "VALUES (%lld, %" PRIuPNUM ",%d, %d, %s, %d)", + objId, vol_index, 0, 0, desc_sql, 0); + + if (attempt_exec(stmt, "Error adding data to tsk_vs_parts table: %s\n")) { + PQfreemem(desc_sql); + return TSK_ERR; + } + + PQfreemem(desc_sql); + return TSK_OK; +} + /** * @returns 1 on error, 0 on success diff --git a/tsk/auto/db_sqlite.cpp b/tsk/auto/db_sqlite.cpp index 2f82aa51bff69cb0af9aec79938993790515de57..2a0e8868f9ef1d5eee4e4726627b772c27ba3655 100644 --- a/tsk/auto/db_sqlite.cpp +++ b/tsk/auto/db_sqlite.cpp @@ -754,17 +754,17 @@ TskDbSqlite::addVsInfo(const TSK_VS_INFO* vs_info, int64_t parObjId, } /** -* Creats a new tsk_pool_info database entry and a new tsk_vs_info +* Creates a new tsk_pool_info database entry and a new tsk_vs_info * entry with the tsk_pool_info as its parent. * * @ param pool_info The pool to save to the database * @ param parObjId The ID of the parent of the pool object -* @ param objId Will be set to the object ID of the new volume system created as a child of +* @ param vsObjId Will be set to the object ID of the new volume system created as a child of * the new pool. * @returns 1 on error, 0 on success */ int -TskDbSqlite::addPoolInfoAndVS(const TSK_POOL_INFO *pool_info, int64_t parObjId, int64_t& objId) { +TskDbSqlite::addPoolInfoAndVS(const TSK_POOL_INFO *pool_info, int64_t parObjId, int64_t& vsObjId) { char stmt[1024]; @@ -784,22 +784,54 @@ TskDbSqlite::addPoolInfoAndVS(const TSK_POOL_INFO *pool_info, int64_t parObjId, } // Add volume system - if (addObject(TSK_DB_OBJECT_TYPE_VS, poolObjId, objId)) + if (addObject(TSK_DB_OBJECT_TYPE_VS, poolObjId, vsObjId)) return 1; snprintf(stmt, 1024, - "INSERT INTO tsk_vs_info (obj_id, vs_type, img_offset, block_size) VALUES (%" PRId64 ", %d,%" PRIuDADDR ",%d)", objId, TSK_VS_TYPE_APFS, pool_info->img_offset, pool_info->block_size); // TODO - offset + "INSERT INTO tsk_vs_info (obj_id, vs_type, img_offset, block_size) VALUES (%" PRId64 ", %d,%" PRIuDADDR ",%d)", vsObjId, TSK_VS_TYPE_APFS, pool_info->img_offset, pool_info->block_size); // TODO - offset return attempt_exec(stmt, "Error adding data to tsk_vs_info table: %s\n"); } /** -* Adds the sector addresses of the pool volumes into the db. +* Adds a fake volume that will hold the unallocated blocks for the pool. +* +* @param vol_index The index for the new volume (should be one higher than the number of pool volumes) +* @param parObjId The object ID of the parent volume system +* @param objId Will be set to the object ID of the new volume +* +* @returns 1 on error, 0 on success +*/ +int +TskDbSqlite::addUnallocatedPoolVolume(int vol_index, int64_t parObjId, int64_t& objId) +{ + char* zSQL; + int ret; + + if (addObject(TSK_DB_OBJECT_TYPE_VOL, parObjId, objId)) + return 1; + + char* desc = "Unallocated Blocks"; + zSQL = sqlite3_mprintf( + "INSERT INTO tsk_vs_parts (obj_id, addr, start, length, desc, flags)" + "VALUES (%lld, %" PRIuPNUM ",%" PRIuDADDR ",%" PRIuDADDR ",'%q',%d)", + objId, vol_index, 0, 0, + desc, 0); + ret = attempt_exec(zSQL, + "Error adding data to tsk_vs_parts table: %s\n"); + sqlite3_free(zSQL); + return ret; +} + +/** +* Adds the sector addresses of the pool volumes into the db. +* * @param pool_vol The pool volume to save to the DB * @param parObjId The ID of the parent of the pool volume (should be a volume system) -* @param objId Will be set to the object ID of the new volume +* @param objId Will be set to the object ID of the new volume +* * @returns 1 on error, 0 on success */ int diff --git a/tsk/auto/tsk_auto.h b/tsk/auto/tsk_auto.h index 631625614cc14882b82ad74c572af82fa00d4cb7..028a95d82b7a37ced4054ffb3c2ec9bdbd4afe50 100644 --- a/tsk/auto/tsk_auto.h +++ b/tsk/auto/tsk_auto.h @@ -263,10 +263,11 @@ class TskAuto { protected: TSK_IMG_INFO * m_img_info; + std::vector<const TSK_POOL_INFO*> m_poolInfos; + bool m_internalOpen; ///< True if m_img_info was opened in TskAuto and false if passed in bool m_stopAllProcessing; ///< True if no further processing should occur - uint8_t isNtfsSystemFiles(TSK_FS_FILE * fs_file, const char *path); uint8_t isFATSystemFiles(TSK_FS_FILE * fs_file); uint8_t isDotDir(TSK_FS_FILE * fs_file); diff --git a/tsk/auto/tsk_case_db.h b/tsk/auto/tsk_case_db.h index eee845990c82acf2f44045eb540398e6f53ce6e3..422ca330d66ab1bac894d558d5fc433bb02413e9 100644 --- a/tsk/auto/tsk_case_db.h +++ b/tsk/auto/tsk_case_db.h @@ -154,6 +154,12 @@ class TskAutoDb:public TskAuto { bool m_foundStructure; ///< Set to true when we find either a volume or file system bool m_attributeAdded; ///< Set to true when an attribute was added by processAttributes + // These are used to write unallocated blocks for pools at the end of the add image + // process. We can't load the pool_info objects directly from the database so we will + // store info about them here. + std::map<int64_t, int64_t> m_poolOffsetToParentId; + std::map<int64_t, int64_t> m_poolOffsetToVsId; + // prevent copying until we add proper logic to handle it TskAutoDb(const TskAutoDb&); TskAutoDb & operator=(const TskAutoDb&); @@ -186,7 +192,7 @@ class TskAutoDb:public TskAuto { TSK_OFF_T offset, TSK_DADDR_T addr, char *buf, size_t size, TSK_FS_BLOCK_FLAG_ENUM a_flags, void *ptr); int md5HashAttr(unsigned char md5Hash[16], const TSK_FS_ATTR * fs_attr); - + TSK_RETVAL_ENUM addUnallocatedPoolBlocksToDb(size_t & numPool); static TSK_WALK_RET_ENUM fsWalkUnallocBlocksCb(const TSK_FS_BLOCK *a_block, void *a_ptr); TSK_RETVAL_ENUM addFsInfoUnalloc(const TSK_DB_FS_INFO & dbFsInfo); TSK_RETVAL_ENUM addUnallocFsSpaceToDb(size_t & numFs); diff --git a/tsk/auto/tsk_db.h b/tsk/auto/tsk_db.h index 92478fa35c0577c58018fd7263adc45998593f6e..edb300dcee781cb2cb7462f1ca842f3e71507dfc 100755 --- a/tsk/auto/tsk_db.h +++ b/tsk/auto/tsk_db.h @@ -180,9 +180,10 @@ class TskDb { virtual int addImageName(int64_t objId, char const *imgName, int sequence) = 0; virtual int addVsInfo(const TSK_VS_INFO * vs_info, int64_t parObjId, int64_t & objId) = 0; virtual int addVolumeInfo(const TSK_VS_PART_INFO * vs_part, int64_t parObjId, int64_t & objId) = 0; - virtual int addPoolInfoAndVS(const TSK_POOL_INFO *pool_info, int64_t parObjId, int64_t& objId) = 0; + virtual int addPoolInfoAndVS(const TSK_POOL_INFO *pool_info, int64_t parObjId, int64_t& vsObjId) = 0; virtual int addPoolVolumeInfo(const TSK_POOL_VOLUME_INFO* pool_vol, int64_t parObjId, int64_t& objId) = 0; + virtual int addUnallocatedPoolVolume(int vol_index, int64_t parObjId, int64_t& objId) = 0; virtual int addFsInfo(const TSK_FS_INFO * fs_info, int64_t parObjId, int64_t & objId) = 0; virtual int addFsFile(TSK_FS_FILE * fs_file, const TSK_FS_ATTR * fs_attr, const char *path, const unsigned char *const md5, diff --git a/tsk/auto/tsk_db_postgresql.h b/tsk/auto/tsk_db_postgresql.h index 873b142babca2158d569deeb9c580a089da15d79..c4cfa763158af9f356f828f9e31cb0b6f708debf 100755 --- a/tsk/auto/tsk_db_postgresql.h +++ b/tsk/auto/tsk_db_postgresql.h @@ -58,9 +58,10 @@ class TskDbPostgreSQL : public TskDb { int64_t & objId); int addFsInfo(const TSK_FS_INFO * fs_info, int64_t parObjId, int64_t & objId); - int addPoolInfoAndVS(const TSK_POOL_INFO *pool_info, int64_t parObjId, int64_t& objId); + int addPoolInfoAndVS(const TSK_POOL_INFO *pool_info, int64_t parObjId, int64_t& vsObjId); int addPoolVolumeInfo(const TSK_POOL_VOLUME_INFO* pool_vol, int64_t parObjId, int64_t& objId); + int addUnallocatedPoolVolume(int vol_index, int64_t parObjId, int64_t& objId); int addFsFile(TSK_FS_FILE * fs_file, const TSK_FS_ATTR * fs_attr, const char *path, const unsigned char *const md5, const TSK_DB_FILES_KNOWN_ENUM known, int64_t fsObjId, diff --git a/tsk/auto/tsk_db_sqlite.h b/tsk/auto/tsk_db_sqlite.h index 7fa3abae9a08da0c338145d9b6ce865a174720e6..13d55412167e0b8e77de117e7c35f0afce86a3ca 100755 --- a/tsk/auto/tsk_db_sqlite.h +++ b/tsk/auto/tsk_db_sqlite.h @@ -50,9 +50,10 @@ class TskDbSqlite : public TskDb { int addImageName(int64_t objId, char const *imgName, int sequence); int addVsInfo(const TSK_VS_INFO * vs_info, int64_t parObjId, int64_t & objId); - int addPoolInfoAndVS(const TSK_POOL_INFO *pool_info, int64_t parObjId, int64_t& objId); + int addPoolInfoAndVS(const TSK_POOL_INFO *pool_info, int64_t parObjId, int64_t& vsObjId); int addPoolVolumeInfo(const TSK_POOL_VOLUME_INFO* pool_vol, int64_t parObjId, int64_t& objId); + int addUnallocatedPoolVolume(int vol_index, int64_t parObjId, int64_t& objId); int addVolumeInfo(const TSK_VS_PART_INFO * vs_part, int64_t parObjId, int64_t & objId); int addFsInfo(const TSK_FS_INFO * fs_info, int64_t parObjId, diff --git a/tsk/fs/fs_open.c b/tsk/fs/fs_open.c index e1d0cb4a62f194659a3d32c0fb5a2f50887677d6..3a564f0dc5ef2eefd612d7cbfb7d144e6b838c50 100755 --- a/tsk/fs/fs_open.c +++ b/tsk/fs/fs_open.c @@ -129,6 +129,8 @@ tsk_fs_open_img_decrypt(TSK_IMG_INFO * a_img_info, TSK_OFF_T a_offset, char* name; TSK_FS_INFO* (*open)(TSK_IMG_INFO*, TSK_OFF_T, TSK_FS_TYPE_ENUM, uint8_t); + // This type should be the _DETECT version because it used + // during autodetection TSK_FS_TYPE_ENUM type; } FS_OPENERS[] = { { "NTFS", ntfs_open, TSK_FS_TYPE_NTFS_DETECT }, diff --git a/tsk/fs/fs_types.c b/tsk/fs/fs_types.c index 6f656afa84b1308e8993bd7786d3de411680d03e..6ba88f0e6f1e1065fbdedb93fe4b512c4155f334 100644 --- a/tsk/fs/fs_types.c +++ b/tsk/fs/fs_types.c @@ -34,6 +34,9 @@ typedef struct { /** \internal * The table used to parse input strings - supports * legacy strings - in order of expected usage + * + * All unique TSK_FS_TYPE_ENUM values should be in here with a unique + * name so that we can map between values and names. */ static FS_TYPES fs_type_table[] = { {"ntfs", TSK_FS_TYPE_NTFS_DETECT, "NTFS"}, diff --git a/tsk/fs/hfs.c b/tsk/fs/hfs.c index fede915bdb45bba3648b8a890e2adc7c7fa3c8fd..22618534ee3f0411b88407eae8217a863d0b8eec 100644 --- a/tsk/fs/hfs.c +++ b/tsk/fs/hfs.c @@ -835,20 +835,10 @@ hfs_cat_traverse(HFS_INFO * hfs, return 1; } - if (sizeof(hfs_btree_key_cat) > nodesize - rec_off) { - tsk_error_set_errno(TSK_ERR_FS_GENFS); - tsk_error_set_errstr - ("hfs_cat_traverse: record %d in index node %d truncated", - rec, cur_node); - free(node); - return 1; - } - key = (hfs_btree_key_cat *) & node[rec_off]; - keylen = 2 + tsk_getu16(hfs->fs_info.endian, key->key_len); - - if (keylen >= nodesize - rec_off) { + + if (keylen > nodesize - rec_off) { tsk_error_set_errno(TSK_ERR_FS_GENFS); tsk_error_set_errstr ("hfs_cat_traverse: length of key %d in index node %d too large (%d vs %" @@ -857,7 +847,6 @@ hfs_cat_traverse(HFS_INFO * hfs, return 1; } - /* if (tsk_verbose) tsk_fprintf(stderr, @@ -956,19 +945,10 @@ hfs_cat_traverse(HFS_INFO * hfs, return 1; } - if (sizeof(hfs_btree_key_cat) > nodesize - rec_off) { - tsk_error_set_errno(TSK_ERR_FS_GENFS); - tsk_error_set_errstr - ("hfs_cat_traverse: record %d in leaf node %d truncated", - rec, cur_node); - free(node); - return 1; - } - key = (hfs_btree_key_cat *) & node[rec_off]; - keylen = 2 + tsk_getu16(hfs->fs_info.endian, key->key_len); - if ((keylen) > nodesize) { + + if (keylen > nodesize - rec_off) { tsk_error_set_errno(TSK_ERR_FS_GENFS); tsk_error_set_errstr ("hfs_cat_traverse: length of key %d in leaf node %d too large (%d vs %" diff --git a/tsk/fs/tsk_fs.h b/tsk/fs/tsk_fs.h index 1921ef79b56c3c4482bde997650d4df713090fd0..2158584563104b5158975835cc73a11ccfdf243b 100644 --- a/tsk/fs/tsk_fs.h +++ b/tsk/fs/tsk_fs.h @@ -780,7 +780,15 @@ extern "C" { /** * Values for the file system type. Each bit corresponds to a file - * system. + * system. The "[fs]_DETECT" value (such as TSK_FS_TYPE_NTSF_DETECT) is + * the OR of all of the subtypes that + * it could detect. If there is only one type of that file system, + * the [fs]_DETECT value will be the same as the type. + * + * The _DETECT values should not be stored in TSK_FS_INFO. Once + * tsk_fs_open() has detected the type, it should assign the specific + * version in TSK_FS_INFO. + * */ enum TSK_FS_TYPE_ENUM { TSK_FS_TYPE_DETECT = 0x00000000, ///< Use autodetection methods diff --git a/tsk/fs/yaffs.cpp b/tsk/fs/yaffs.cpp index 68d8b0219b0bf596f663f7d63b7495269b0312ad..f5ec30a09dde02577111ed506242ec1c48b2fd2c 100755 --- a/tsk/fs/yaffs.cpp +++ b/tsk/fs/yaffs.cpp @@ -2439,7 +2439,7 @@ static uint8_t YAFFSFS_INFO *yfs = (YAFFSFS_INFO *)fs; char ls[12]; YAFFSFS_PRINT_ADDR print; - char timeBuf[32]; + char timeBuf[128]; YaffsCacheObject * obj = NULL; YaffsCacheVersion * version = NULL; YaffsHeader * header = NULL;