diff --git a/bindings/java/doxygen/Doxyfile b/bindings/java/doxygen/Doxyfile index aaf864421b52d7360770338d405df7e4ce2b2921..d5819781d442546e15c2b7332002ada14acb07cf 100644 --- a/bindings/java/doxygen/Doxyfile +++ b/bindings/java/doxygen/Doxyfile @@ -767,6 +767,7 @@ INPUT = main.dox \ communications.dox \ schema/schema_list.dox \ schema/db_schema_8_6.dox \ + schema/db_schema_9_0.dox \ ../src # This tag can be used to specify the character encoding of the source files diff --git a/bindings/java/doxygen/footer.html b/bindings/java/doxygen/footer.html index de28741e239b3ac00796b7be6903c316ea092a8a..db83baa9051f977a4eb5ec10fd20eb1d4d7dba0d 100644 --- a/bindings/java/doxygen/footer.html +++ b/bindings/java/doxygen/footer.html @@ -1,5 +1,5 @@ <hr/> -<p><i>Copyright © 2011-2020 Brian Carrier. (carrier -at- sleuthkit -dot- org)<br/> +<p><i>Copyright © 2011-2021 Brian Carrier. (carrier -at- sleuthkit -dot- org)<br/> This work is licensed under a <a rel="license" href="http://creativecommons.org/licenses/by-sa/3.0/us/">Creative Commons Attribution-Share Alike 3.0 United States License</a>. </i></p> diff --git a/bindings/java/doxygen/main.dox b/bindings/java/doxygen/main.dox index b49013b516d9adce62cb84f32d8414bf7769edce..eb50213687db25d2b37a56bcc808f9cbd05e5ae6 100644 --- a/bindings/java/doxygen/main.dox +++ b/bindings/java/doxygen/main.dox @@ -45,7 +45,7 @@ You can also access the data in its tree form by starting with org.sleuthkit.dat The Sleuth Kit has its own database schema that is shared with Autopsy and other tools. The primary way it gets populated is via the Java code. - Database Schema Documentation: - - \subpage db_schema_8_6_page + - \subpage db_schema_9_0_page - \subpage db_schema_page "Older schemas" - Refer to \subpage query_database_page if you are going to use one of the SleuthkitCase methods that requires you to specify a query. - Refer to \subpage insert_and_update_database_page if you are a Sleuth Kit developer and want to avoid database issues. diff --git a/bindings/java/doxygen/schema/db_schema_9_0.dox b/bindings/java/doxygen/schema/db_schema_9_0.dox new file mode 100644 index 0000000000000000000000000000000000000000..28be7c5c5b93f7f37740b4ca1f19643b46000084 --- /dev/null +++ b/bindings/java/doxygen/schema/db_schema_9_0.dox @@ -0,0 +1,543 @@ +/*! \page db_schema_9_0_page TSK & Autopsy Database Schema (Schema version 9.0) + +[TOC] + +# Introduction + +This page outlines version 9.0 the database that is used by The Sleuth Kit and Autopsy. The goal of this page is to provide short descriptions for each table and column and not focus on foreign key requirements, etc. If you want that level of detail, then refer to the actual schema in addition to this. + +Each Autopsy release is associated with a schema version with a major and minor version number. If a case with an older schema version is opened in a new version of Autopsy, the case will automatically be updated to the current schema. Going the other direction (opening a case that was created with a newer version of Autopsy), two things may happen: +- If the case database has the same major number as the version of Autopsy being used, the case should generally be able to be opened and used. +- If the case database has a higher major number than the version of Autopsy being used, an error will be displayed when attempting to open the case. + +You can find a basic graphic of some of the table relationships <a href="https://docs.google.com/drawings/d/1omR_uUAp1fQt720oJ-kk8C48BXmVa3PNjPZCDdT0Tb4/edit?usp#sharing">here</a> + + +Some general notes on this schema: +- Nearly every type of data is assigned a unique ID, called the Object ID +- The objects form a hierarchy, that shows where data came from. A child comes from its parent. + - For example, disk images are the root, with a volume system below it, then a file system, and then files and directories. +- This schema has been designed to store data beyond the file system data that The Sleuth Kit supports. It can store carved files, a folder full of local files, etc. +- The Blackboard is used to store artifacts, which contain attributes (name/value pairs). Artifacts are used to store data types that do not have more formal tables. Module writers can make whatever artifact types they want. See \ref mod_bbpage for more details. +- The Sleuth Kit will make virtual files to span the unallocated space. They will have a naming format of 'Unalloc_[PARENT-OBJECT-ID]_[BYTE-START]_[BYTE-END]'. + +# Schema Information + +This was a big change. Tables were added to support analsis results, OS accounts, Hosts & Person strcture of data sources, and host addresses (IPs, DNS, etc.). It has a major version change because there are new Content enum types (OsAccount and HostAddress). + +<ul> +<li><b>Autopsy versions: </b> Autopsy 4.19 +<li><b>Changes from version 8.6:</b> +<ul> +<li> New columns: +<ul> +<li>host_id, added_date_time, acquisition_tool_settings, acquisition_tool_name, acquisition_tool_version in data_source_info +<li>category_type in artifact_types +<li>owner_uid, os_account_obj_id in tsk_files +</ul> +<li> New tables: +<ul> +<li>tsk_aggregate_score +<li>tsk_analysis_results +<li>tsk_data_artifacts +<li>tsk_file_attributes +<li>tsk_hosts +<li>tsk_host_addresses +<li>tsk_host_address_dns_ip_map +<li>tsk_host_address_usage +<li>tsk_os_accounts +<li>tsk_os_account_attributes +<li>tsk_os_account_instances +<li>tsk_os_account_realms +<li>tsk_persons +</ul> +</ul> +</ul> + + +# General Information Tables +## tsk_db_info +Metadata about the database. +- **schema_ver** - Major version number of the current database schema +- **tsk_ver** - Version of TSK used to create database +- **schema_minor_version** - Minor version number of the current database schema + +## tsk_db_info_extended +Name & Value pair table to store any information about the database. For example, which schema it was created with. etc. +- **name** - Any string name +- **value** - Any string value + + +# Object Tables +## tsk_objects +Every object (image, volume system, file, etc.) has an entry in this table. This table allows you to find the parent of a given object and allows objects to be tagged and have children. This table provides items with a unique object id. The details of the object are in other tables. +- **obj_id** - Unique id +- **par_obj_id** - The object id of the parent object (NULL for root objects). The parent of a volume system is an image, the parent of a directory is a directory or filesystem, the parent of a filesystem is a volume or an image, etc. +- **type** - Object type (as org.sleuthkit.datamodel.TskData.ObjectType enum) + + +# Hosts / Persons +Stores data related to hosts and persons, which can help organize data sources. + +## tsk_persons +Stores persons for the case. A peron is someone who owns or used a data source in the case. +- **id** - Id of the person +- **name** - Name of the person (should be human readable) + +## tsk_hosts +Stores hosts that have a data source in the case. Each data source must be associated with a host. These are NOT created for a reference to an external host (such as a web domain). +- **id** - Id of the host +- **name** - Name of the host (should be human readable) +- **db_status** - Status of the host (active/merged/deleted as org.sleuthkit.datamodel.Host.HostDbStatus) +- **person_id** - Optional id of associated person +- **merged_into** - Stores the host ID that this host was merged into + +# Data Source / Device Tables +## data_source_info +Contains information about a data source, which could be an image. This is where we group data sources into devices (based on device ID). +- **obj_id** - Id of image/data source in tsk_objects +- **device_id** - Unique ID (GUID) for the device that contains the data source +- **time_zone** - Timezone that the data source was originally located in +- **acquisition_details** - Notes on the acquisition of the data source +- **added_date_time** - Timestamp of when the data source was added +- **acquisition_tool_name** - Name of the tool used to acquire the image +- **acquisition_tool_settings** - Specific settings used by the tool to acquire the image +- **acquisition_tool_version** - Version of the acquisition tool +- **host_id** - Host associated with this image (must be set) + + +# Disk Image Tables + +## tsk_image_info +Contains information about each set of images that is stored in the database. +- **obj_id** - Id of image in tsk_objects +- **type** - Type of disk image format (as org.sleuthkit.datamodel.TskData.TSK_IMG_TYPE_ENUM) +- **ssize** - Sector size of device in bytes +- **tzone** - Timezone where image is from (the same format that TSK tools want as input) +- **size** - Size of the original image (in bytes) +- **md5** - MD5 hash of the image (for compressed data such as E01, the hashes are of the decompressed image, not the E01 itself) +- **sha1** - SHA-1 hash of the image +- **sha256** - SHA-256 hash of the image +- **display_name** - Display name of the image + +## tsk_image_names +Stores path(s) to file(s) on disk that make up an image set. +- **obj_id** - Id of image in tsk_objects +- **name** - Path to location of image file on disk +- **sequence** - Position in sequence of image parts + + +# Volume System Tables +## tsk_vs_info +Contains one row for every volume system found in the images. +- **obj_id** - Id of volume system in tsk_objects +- **vs_type** - Type of volume system / media management (as org.sleuthkit.datamodel.TskData.TSK_VS_TYPE_ENUM) +- **img_offset** - Byte offset where VS starts in disk image +- **block_size** - Size of blocks in bytes + +## tsk_vs_parts +Contains one row for every volume / partition in the images. +- **obj_id** - Id of volume in tsk_objects +- **addr** - Address of the partition +- **start** - Sector offset of start of partition +- **length** - Number of sectors in partition +- **desc** - Description of partition (volume system type-specific) +- **flags** - Flags for partition (as org.sleuthkit.datamodel.TskData.TSK_VS_PART_FLAG_ENUM) + +## tsk_pool_info +Contains information about pools (for APFS, logical disk management, etc.) +- **obj_id** - Id of pool in tsk_objects +- **pool_type** - Type of pool (as org.sleuthkit.datamodel.TskData.TSK_POOL_TYPE_ENUM) + +# File System Tables +## tsk_fs_info +Contains one for for every file system in the images. +- **obj_id** - Id of filesystem in tsk_objects +- **data_source_obj_id** - Id of the data source for the file system +- **img_offset** - Byte offset that filesystem starts at +- **fs_type** - Type of file system (as org.sleuthkit.datamodel.TskData.TSK_FS_TYPE_ENUM) +- **block_size** - Size of each block (in bytes) +- **block_count** - Number of blocks in filesystem +- **root_inum** - Metadata address of root directory +- **first_inum** - First valid metadata address +- **last_inum** - Last valid metadata address +- **display_name** - Display name of file system (could be volume label) + +## tsk_files +Contains one for for every file found in the images. Has the basic metadata for the file. +- **obj_id** - Id of file in tsk_objects +- **fs_obj_id** - Id of filesystem in tsk_objects (NULL if file is not located in a file system -- carved in unpartitioned space, etc.) +- **data_source_obj_id** - Id of the data source for the file +- **attr_type** - Type of attribute (as org.sleuthkit.datamodel.TskData.TSK_FS_ATTR_TYPE_ENUM) +- **attr_id** - Id of attribute +- **name** - Name of attribute. Will be NULL if attribute doesn't have a name. Must not have any slashes in it. +- **meta_addr** - Address of the metadata structure that the name points to +- **meta_seq** - Sequence of the metadata address +- **type** - Type of file: filesystem, carved, etc. (as org.sleuthkit.datamodel.TskData.TSK_DB_FILES_TYPE_ENUM enum) +- **has_layout** - True if file has an entry in tsk_file_layout +- **has_path** - True if file has an entry in tsk_files_path +- **dir_type** - File type information: directory, file, etc. (as org.sleuthkit.datamodel.TskData.TSK_FS_NAME_TYPE_ENUM) +- **meta_type** - File type (as org.sleuthkit.datamodel.TskData.TSK_FS_META_TYPE_ENUM) +- **dir_flags** - Flags that describe allocation status etc. (as org.sleuthkit.datamodel.TskData.TSK_FS_NAME_FLAG_ENUM) +- **meta_flags** - Flags for the file for its allocation status etc. (as org.sleuthkit.datamodel.TskData.TSK_FS_META_FLAG_ENUM) +- **size** - File size in bytes +- **ctime** - Last file / metadata status change time (stored in number of seconds since Jan 1, 1970 UTC) +- **crtime** - Created time +- **atime** - Last file content accessed time +- **mtime** - Last file content modification time +- **mode** - Unix-style permissions (as org.sleuthkit.datamodel.TskData.TSK_FS_META_MODE_ENUM) +- **uid** - Owner id +- **gid** - Group id +- **md5** - MD5 hash of file contents +- **sha256** - SHA-256 hash of file contents +- **known** - Known status of file (as org.sleuthkit.datamodel.TskData.FileKnown) +- **parent_path** - Full path of parent folder. Must begin and end with a '/' (Note that a single '/' is valid) +- **mime_type** - MIME type of the file content, if it has been detected. +- **extension** - File extension +- **owner_uid** - Unique ID of the owner (SID in Windows) +- **os_account_obj_id** - ID of optional associated OS account + +## tsk_file_layout +Stores the layout of a file within the image. A file will have one or more rows in this table depending on how fragmented it was. All file types use this table (file system, carved, unallocated blocks, etc.). +- **obj_id** - Id of file in tsk_objects +- **sequence** - Position of the run in the file (0-based and the obj_id and sequence pair will be unique in the table) +- **byte_start** - Byte offset of fragment relative to the start of the image file +- **byte_len** - Length of fragment in bytes + + +## tsk_files_path +If a "locally-stored" file has been imported into the database for analysis, then this table stores its path. Used for derived files and other files that are not directly in the image file. +- **obj_id** - Id of file in tsk_objects +- **path** - Path to where the file is locally stored in a file system +- **encoding_type** - Method used to store the file on the disk + +## file_encoding_types +Methods that can be used to store files on local disks to prevent them from being quarantined by antivirus +- **encoding_type** - ID of method used to store data. See org.sleuthkit.datamodel.TskData.EncodingType enum +- **name** - Display name of technique + +## tsk_file_attributes +Stores extended attributes for a particular file that do not have a column in tsk_files. Custom BlackboardAttribute types can be defined. +- **id** - Id of the attribute +- **obj_id** - File this attribute is associated with (references tsk_files) +- **attribute_type_id** - Id for the type of attribute (can be looked up in the blackboard_attribute_types) +- **value_type** - The type of the value (see org.sleuthkit.datamodel.BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE) +- **value_byte** - A blob of binary data (should be NULL unless the value type is byte) +- **value_text** - A string of text (should be NULL unless the value type is string) +- **value_int32** - An integer (should be NULL unless the value type is int) +- **value_int64** - A long integer / timestamp (should be NULL unless the value type is long) +- **value_double** - A double (should be NULL unless the value type is double) + +## tsk_files_derived_method +Derived files are those that result from analyzing another file. For example, files that are extracted from a ZIP file will be considered derived. This table keeps track of the derivation techniques that were used to make the derived files. + +NOTE: This table is not used in any code. + +- **derived_id** - Unique id for the derivation method. +- **tool_name** - Name of derivation method/tool +- **tool_version** - Version of tool used in derivation method +- **other** - Other details + +## tsk_files_derived +Each derived file has a row that captures the information needed to re-derive it + +NOTE: This table is not used in any code. + +- **obj_id** - Id of file in tsk_objects +- **derived_id** - Id of derivation method in tsk_files_derived_method +- **rederive** - Details needed to re-derive file (will be specific to the derivation method) + + +# Blackboard Tables +The \ref mod_bbpage "Blackboard" is used to store results and derived data from analysis modules. + +## blackboard_artifacts +Stores artifacts associated with objects. +- **artifact_id** - Id of the artifact (assigned by the database) +- **obj_id** - Id of the associated object +- **artifact_obj_id** - Object id of the artifact +- **artifact_type_id** - Id for the type of artifact (can be looked up in the blackboard_artifact_types table) +- **data_source_obj_id** - Id of the data source for the artifact +- **artifact_type_id** - Type of artifact (references artifact_type_id in blackboard_artifact_types) +- **review_status_id** - Review status (references review_status_id in review_statuses) + +## tsk_analysis_results +Additional information for artifacts that are analysis results +- **artifact_obj_id** - Object id of the associated artifact (artifact_obj_id column in blackboard_artifacts) +- **significance** - Significance to show if the result shows the object is relevant (as org.sleuthkit.datamodel.Score.Significance enum) +- **method_category** - Category of the analysis method used (as org.sleuthkit.datamodel.Score.MethodCategory enum) +- **conclusion** - Optional, text description of the conclusion of the analysis method. +- **configuration** - Otional, text description of the analysis method configuration (such as what hash set or keyword list was used) +- **justification** - Optional, text description of justification of the conclusion and significance. +- **ignore_score** - True (1) if score should be ignored when calculating aggregate score, false (0) otherwise. This allows users to ignore a false positive. + +## tsk_data_artifacts +Additional information for artifacts that store extracted data. +- **artifact_obj_id** - Object id of the associated artifact (artifact_obj_id column in blackboard_artifacts) +- **os_account_obj_id** - Object id of the associated OS account + +## blackboard_artifact_types +Types of artifacts +- **artifact_type_id** - Id for the type (this is used by the blackboard_artifacts table) +- **type_name** - A string identifier for the type (unique) +- **display_name** - A display name for the type (not unique, should be human readable) +- **category_type** - Indicates whether this is a data artifact or an analysis result + +## blackboard_attributes +Stores name value pairs associated with an artifact. Only one of the value columns should be populated. +- **artifact_id** - Id of the associated artifact +- **artifact_type_id** - Artifact type of the associated artifact +- **source** - Source string, should be module name that created the entry +- **context** - Additional context string +- **attribute_type_id** - Id for the type of attribute (can be looked up in the blackboard_attribute_types) +- **value_type** - The type of the value (see org.sleuthkit.datamodel.BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE) +- **value_byte** - A blob of binary data (should be NULL unless the value type is byte) +- **value_text** - A string of text (should be NULL unless the value type is string) +- **value_int32** - An integer (should be NULL unless the value type is int) +- **value_int64** - A long integer / timestamp (should be NULL unless the value type is long) +- **value_double** - A double (should be NULL unless the value type is double) + +## blackboard_attribute_types +Types of attribute +- **attribute_type_id** - Id for the type (this is used by the blackboard_attributes table) +- **type_name** - A string identifier for the type (unique) +- **display_name** - A display name for the type (not unique, should be human readable) +- **value_type** - Expected type of data for the attribute type (see blackboard_attributes) + +## review_statuses +Review status of an artifact. Should mirror the org.sleuthkit.datamodel.BlackboardArtifact.ReviewStatus enum. +- **review_status_id** - Id of the status +- **review_status_name** - Internal name of the status +- **display_name** - Display name (should be human readable) + +## tsk_aggregate_score +Stores the score of an object that is a combination of the various analysis result scores +- **obj_id** - Id of the object that corresponds to this score +- **data_source_obj_id** - Id of the data source the object belongs to +- **significance** - Significance (as org.sleuthkit.datamodel.Score.Significance enum) +- **method_category** - Category of the method used (as org.sleuthkit.datamodel.Score.MethodCategory enum) + + + +# Host Addresses +Host addresses are various forms of identifiers assigned to a computer, such as host names or MAC addresses. These tables store data that is also stored in the data artifacts, but these tables allow for correlation and scoring of specific hosts. + +## tsk_host_addresses +One entry is created in this table for each host address found in the data source. Examples include domain names (www.sleuthkit.org), IP addresses, and BlueTooth MAC addresses. +- **id** - Id of the host address +- **address_type** - Type of address (as org.sleuthkit.datamodel.HostAddress.HostAddressType enum) +- **address** - Address (must be unique within the scope of address_type). + +## tsk_host_address_dns_ip_map +Stores data if host names and IP addresses were resolved between each other. +- **id** - Id of the mapping +- **dns_address_id** - Id of the DNS address in tsk_host_addresses +- **ip_address_id** - Id of the IP address in tsk_host_addresses +- **source_obj_id** - Id of the object used to determine this mapping (references tsk_objects) +- **time** - Timestamp when this mapping was recorded + +## tsk_host_address_usage +Tracks which artifacts and files had a reference to a given host address. This is used to show what other artifacts used the same address. +- **id** - Id of the usage +- **addr_obj_id** - Id of the host address +- **obj_id** - Id of the object that had a reference/usage to the address (references tsk_objects) +- **data_source_obj_id** - Id of the data source associated with the usage + + +# Operating System Accounts +Stores data related to operating system accounts. Communication-related accounts (such as email or social media) are stored in other tables (see Communication Acccounts below). + + +## tsk_os_account_realms +Every OS Account must belong to a realm, which defines the scope of the account. Realms can be local to a given computer or domain-based. +- **realm_name** - Display bame of the realm (realm_name or realm_addr must be set) +- **realm_addr** - Address/ID of the realm (realm_name or realm_addr must be set) +- **realm_signature** - Used internally for unique clause. realm_addr if it is set. Otherwise, realm_name. +- **scope_host_id** - Optional host that this realm is scoped to. By default, realms are scoped to a given host. +- **scope_confidence** - Confidence of the scope of the realm (as org.sleuthkit.datamodel.OsAccountRealm.ScopeConfidence enum) +- **db_status** - Status of this realm in the database (as org.sleuthkit.datamodel.OsAccountRealm.RealmDbStatus enum) +- **merged_into** - For merged realms, set to the id of the realm they were merged in to. + +## tsk_os_accounts +Stores operating system accounts +- **os_account_obj_id** - Id of the OS account +- **realm_id** - Id of the associated realm (references tsk_os_account_realms) +- **login_name** - Login name (login name or addr must be present) +- **addr** - Address/ID of account (login name or addr must be present) +- **signature** - Used internally for unique clause +- **full_name** - Full name +- **status** - Status of the account (as org.sleuthkit.datamodel.OsAccount.OsAccountStatus enum) +- **type** - Type of account (as org.sleuthkit.datamodel.OsAccount.OsAccountType enum) +- **created_date** - Timestamp of account creation +- **db_status** - Status of this account in the database (active/merged/deleted) +- **merged_into** - For merged accounts, set to the id of the account they were merged in to. + +## tsk_os_account_attributes +Stores additional attributes for an OS account. Similar to blackboard_attributes. Attributes can either be specific to a host or domain-scoped. +- **id** - Id of the attribute +- **os_account_obj_id** - Id of the associated OS account +- **host_id** - Host Id if the attribute is scoped to the host. NULL if the attribute is domain-scoped. +- **source_obj_id** - Optional object id of where the attribute data was derived from (such as a registry hive) (references tsk_objects) +- **attribute_type_id** - Type of attribute (see org.sleuthkit.datamodel.BlackboardAttribute.BlackboardAttribute.Type) +- **value_type** - The type of the value (see org.sleuthkit.datamodel.BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE) +- **value_byte** - A blob of binary data (should be NULL unless the value type is byte) +- **value_text** - A string of text (should be NULL unless the value type is string) +- **value_int32** - An integer (should be NULL unless the value type is int) +- **value_int64** - A long integer / timestamp (should be NULL unless the value type is long) +- **value_double** - A double (should be NULL unless the value type is double) + +## tsk_os_account_instances +Records that an OS account is associated with a specific data source. For example, the account logged in, accessed data, etc. +- **id** - Id of the OS account instance +- **os_account_obj_id** - Id of the OS account that was referenced +- **data_source_obj_id** - Id of the data source +- **instance_type** - Type of instance (as org.sleuthkit.datamodel.OsAccountInstance.OsAccountInstanceType enum) + + +# Communication Accounts +Stores data related to communications between two parties. It is highly recommended to use +the org.sleuthkit.datamodel.CommunicationsManager API to create/access this type of data +(see the \ref mod_compage page). + +## accounts +Stores communication accounts (email, phone number, etc.). Note that this does not include OS accounts. +- **account_id** - Id for the account within the scope of the database (i.e. Row Id) (used in the account_relationships table) +- **account_type_id** - The type of account (must match an account_type_id entry from the account_types table) +- **account_unique_identifier** - The phone number/email/other identifier associated with the account that is unique within the Account Type + +## account_types +Types of accounts and service providers (Phone, email, Twitter, Facebook, etc.) +- **account_type_id** - Id for the type (this is used by the accounts table) +- **type_name** - A string identifier for the type (unique) +- **display_name** - A display name for the type (not unique, should be human readable) + +## account_relationships +Stores non-directional relationships between two accounts if they communicated or had references to each other (such as contact book) +- **relationship_id** - Id for the relationship +- **account1_id** - Id of the first participant (from account_id column in accounts table) +- **account2_id** - Id of the second participant (from account_id column in accounts table) +- **relationship_source_obj_id** - Id of the artifact this relationship was derived from (artifact_id column from the blackboard_artifacts) +- **date_time** - Time the communication took place, stored in number of seconds since Jan 1, 1970 UTC (NULL if unknown) +- **relationship_type** - The type of relationship (as org.sleuthkit.datamodel.Relationship.Type) +- **data_source_obj_id** - Id of the data source this relationship came from (from obj_id in data_source_info) + +# Timeline +Stores data used to populate various timelines. Two tables are used to reduce data duplication. It is highly recommended to use +the org.sleuthkit.datamodel.TimelineManager API to create/access this type of data. + +## tsk_event_types +Stores the types for events. The super_type_id column is used to arrange the types into a tree. +- **event_type_id** - Id for the type +- **display_name** - Display name for the type (unique, should be human readable) +- **super_type_id** - Parent type for the type (used for building heirarchy; references the event_type_id in this table) + +## tsk_event_descriptions +Stores descriptions of an event. This table exists to reduce duplicate data that is common to events. For example, a file will have only one row in tsk_event_descriptions, but could have 4+ rows in tsk_events that all refer to the same description. Note that the combination of the full_description, content_obj_id, and artifact_id columns must be unique. +- **event_description_id** - Id for the event description +- **full_description** - Full length description of the event (required). For example, the full file path including file name. +- **med_description** - Medium length description of the event (may be null). For example, a file may have only the first three folder names. +- **short_description** - Short length description of the event (may be null). For example, a file may have only its first folder name. +- **data_source_obj_id** - Object id of the data source for the event source (references obj_id column in data_source_info) +- **content_obj_id** - If the event is from a non-artifact, then this is the object id from that source. If the event is from an artifact, then this is the object id of the artifact's source. (references obj_id column in tsk_objects) +- **artifact_id** - If the event is from a non-artifact, this is null. If the event is from an artifact, then this is the id of the artifact (references artifact_id column in blackboard_artifacts) (may be null) +- **hash_hit** - 1 if the file associated with the event has a hash set hit, 0 otherwise +- **tagged** - 1 if the direct source of the event has been tagged, 0 otherwise + +## tsk_events +Stores each event. A file, artifact, or other type of content can have several rows in this table. One for each time stamp. +- **event_id** - Id for the event +- **event_type_id** - Event type id (references event_type_id column in tsk_event_types) +- **event_description_id** - Event description id (references event_description_id column in tsk_event_descriptions) +- **time** - Time the event occurred, in seconds from the UNIX epoch + +# Examiners and Reports + +## tsk_examiners +Encapsulates the concept of an examiner associated with a case. +- **examiner_id** - Id for the examiner +- **login_name** - Login name for the examiner (must be unique) +- **display_name** - Display name for the examiner (may be null) + +## reports +Stores information on generated reports. +- **obj_id** - Id of the report +- **path** - Full path to the report (including file name) +- **crtime** - Time the report was created, in seconds from the UNIX epoch +- **src_module_name** - Name of the module that created the report +- **report_name** - Name of the report (can be empty string) + +# Tags + +## tag_names +Defines what tag names the user has created and can therefore be applied. +- **tag_name_id** - Unique ID for each tag name +- **display_name** - Display name of tag +- **description** - Description (can be empty string) +- **color** - Color choice for tag (can be empty string) +- **knownStatus** - Stores whether a tag is notable/bad (as org.sleuthkit.datamodel.TskData.FileKnown enum) +- **tag_set_id** - Id of the tag set the tag name belongs to (references tag_set_id in tsk_tag_sets, may be null) +- **rank** - Used to order the tag names for a given tag set for display purposes + +## tsk_tag_sets +Used to group entries from the tag_names table. An object can have only one tag from a tag set at a time. +- **tag_set_id** - Id of the tag set +- **name** - Name of the tag set (unique, should be human readable) + +## content_tags +One row for each file tagged. +- **tag_id** - unique ID +- **obj_id** - object id of Content that has been tagged +- **tag_name_id** - Tag name that was used +- **comment** - optional comment +- **begin_byte_offset** - optional byte offset into file that was tagged +- **end_byte_offset** - optional byte ending offset into file that was tagged +- **examiner_id** - Examiner that tagged the artifact (references examiner_id in tsk_examiners) + +## blackboard_artifact_tags +One row for each artifact that is tagged. +- **tag_id** - unique ID +- **artifact_id** - Artifact ID of artifact that was tagged +- **tag_name_id** - Tag name that was used +- **comment** - Optional comment +- **examiner_id** - Examiner that tagged the artifact (references examiner_id in tsk_examiners) + + +# Ingest Module Status +These tables keep track in Autopsy which modules were run on the data sources. + +## ingest_module_types +Defines the types of ingest modules supported. Must exactly match the names and ordering in the org.sleuthkit.datamodel.IngestModuleInfo.IngestModuleType enum. +- **type_id** - Id for the ingest module type +- **type_name** - Internal name for the ingest module type + +## ingest_modules +Defines which modules were installed and run on at least one data source. One row for each module. +- **ingest_module_id** - Id of the ingest module +- **display_name** - Display name for the ingest module (should be human readable) +- **unique_name** - Unique name for the ingest module +- **type_id** - Type of ingest module (references type_id from ingest_module_types) +- **version** - Version of the ingest module + +## ingest_job_status_types +Defines the status options for ingest jobs. Must match the names and ordering in the org.sleuthkit.datamodel.IngestJobInfo.IngestJobStatusType enum. +- **type_id** - Id for the ingest job status type +- **type_name** - Internal name for the ingest job status type + +## ingest_jobs +One row is created each time ingest is started, which is a set of modules in a pipeline. +- **ingest_job_id** - Id of the ingest job +- **obj_id** - Id of the data source ingest is being run on +- **host_name** - Name of the host that is running the ingest job +- **start_date_time** - Time the ingest job started (stored in number of milliseconds since Jan 1, 1970 UTC) +- **end_date_time** - Time the ingest job finished (stored in number of milliseconds since Jan 1, 1970 UTC) +- **status_id** - Ingest job status (references type_id from ingest_job_status_types) +- **settings_dir** - Directory of the job's settings (may be an empty string) + +## ingest_job_modules +Defines the order of the modules in a given pipeline (i.e. ingest_job). +- **ingest_job_id** - Id for the ingest job (references ingest_job_id in ingest_jobs) +- **ingest_module_id** - Id of the ingest module (references ingest_module_id in ingest_modules) +- **pipeline_position** - Order that the ingest module was run + + +*/ diff --git a/bindings/java/doxygen/schema/schema_list.dox b/bindings/java/doxygen/schema/schema_list.dox index 54a0b37f8393c3dd6d7555feb42e357c5ada25cc..088b447872f1ffdf6dbf247e93ae8ef73abc10ad 100644 --- a/bindings/java/doxygen/schema/schema_list.dox +++ b/bindings/java/doxygen/schema/schema_list.dox @@ -3,9 +3,10 @@ This page contians links to the documention for selected versions of the TSK & Autopsy database schema. - Current Schema - - \subpage db_schema_8_6_page + - \subpage db_schema_9_0_page - Older Schemas + - \subpage db_schema_8_6_page - <a href="https://wiki.sleuthkit.org/index.php?title=Database_v7.2_Schema">Schema version 7.2</a> - <a href="https://wiki.sleuthkit.org/index.php?title=SQLite_Database_v6_Schema">Schema version 6</a> - <a href="https://wiki.sleuthkit.org/index.php?title=SQLite_Database_v3_Schema">Schema version 3</a> diff --git a/bindings/java/src/org/sleuthkit/datamodel/AnalysisResultsDeletedEvent.java b/bindings/java/src/org/sleuthkit/datamodel/AnalysisResultsDeletedEvent.java deleted file mode 100644 index 5950430189c51be5716b0886faa63a5149228f0c..0000000000000000000000000000000000000000 --- a/bindings/java/src/org/sleuthkit/datamodel/AnalysisResultsDeletedEvent.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Sleuth Kit Data Model - * - * Copyright 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; - -import java.util.Collections; -import java.util.List; - -/** - * Event to indicate that analysis results were deleted. - */ -public class AnalysisResultsDeletedEvent implements TskEvent { - - private final List<Long> deletedResultObjIds; - - /** - * Constructs a new AnalysisResultsDeletedEvent. - * - * @param deletedResults List of deleted results. - */ - AnalysisResultsDeletedEvent(List<Long> deletedResultObjIds) { - this.deletedResultObjIds = deletedResultObjIds; - } - - /** - * Returns a list of deleted results. - * - * @return List of AnalysisResult. - */ - public List<Long> getObjectIds() { - return Collections.unmodifiableList(deletedResultObjIds); - } - -} diff --git a/bindings/java/src/org/sleuthkit/datamodel/HostManager.java b/bindings/java/src/org/sleuthkit/datamodel/HostManager.java old mode 100644 new mode 100755 index 40ad7529bc9269dfdc917e8ab1cf7f7c2650e023..71c1b56166b976836c05b3eacda10b4a907b593a --- a/bindings/java/src/org/sleuthkit/datamodel/HostManager.java +++ b/bindings/java/src/org/sleuthkit/datamodel/HostManager.java @@ -32,6 +32,8 @@ import org.sleuthkit.datamodel.Host.HostDbStatus; import org.sleuthkit.datamodel.SleuthkitCase.CaseDbConnection; import org.sleuthkit.datamodel.SleuthkitCase.CaseDbTransaction; +import org.sleuthkit.datamodel.TskEvent.HostsChangedTskEvent; +import org.sleuthkit.datamodel.TskEvent.HostsDeletedTskEvent; /** * Responsible for creating/updating/retrieving Hosts. @@ -560,7 +562,7 @@ private String makeMergedHostName() { * @param newValue The new value for the host. */ private void fireChangeEvent(Host newValue) { - db.fireTSKEvent(new HostsUpdateEvent(Collections.singletonList(newValue))); + db.fireTSKEvent(new HostsChangedTskEvent(Collections.singletonList(newValue))); } /** @@ -570,77 +572,6 @@ private void fireChangeEvent(Host newValue) { * @param deleted The deleted host. */ private void fireDeletedEvent(Host deleted) { - db.fireTSKEvent(new HostsDeletionEvent(Collections.singletonList(deleted))); - } - - /** - * Base event for all host events - */ - static class BaseHostEvent { - - private final List<Host> hosts; - - /** - * Main constructor. - * - * @param hosts The hosts that are objects of the event. - */ - BaseHostEvent(List<Host> hosts) { - this.hosts = Collections.unmodifiableList(new ArrayList<>(hosts)); - } - - /** - * Returns the hosts affected in the event. - * - * @return The hosts affected in the event. - */ - public List<Host> getHosts() { - return hosts; - } - } - - /** - * Event fired when hosts are created. - */ - public static final class HostsCreationEvent extends BaseHostEvent { - - /** - * Main constructor. - * - * @param hosts The added hosts. - */ - HostsCreationEvent(List<Host> hosts) { - super(hosts); - } - } - - /** - * Event fired when hosts are updated. - */ - public static final class HostsUpdateEvent extends BaseHostEvent { - - /** - * Main constructor. - * - * @param hosts The new values for the hosts that were changed. - */ - HostsUpdateEvent(List<Host> hosts) { - super(hosts); - } - } - - /** - * Event fired when hosts are deleted. - */ - public static final class HostsDeletionEvent extends BaseHostEvent { - - /** - * Main constructor. - * - * @param hosts The hosts that were deleted. - */ - HostsDeletionEvent(List<Host> hosts) { - super(hosts); - } + db.fireTSKEvent(new HostsDeletedTskEvent(Collections.singletonList(deleted))); } } diff --git a/bindings/java/src/org/sleuthkit/datamodel/OsAccount.java b/bindings/java/src/org/sleuthkit/datamodel/OsAccount.java index 4652057d528c2ac5f6a301765e8245c3a9752754..97e03d45aff8ac04eb63495245bd3e17bb6dddfc 100644 --- a/bindings/java/src/org/sleuthkit/datamodel/OsAccount.java +++ b/bindings/java/src/org/sleuthkit/datamodel/OsAccount.java @@ -242,12 +242,22 @@ public static OsAccountType fromID(int typeId) { * This function is used by OsAccountManger to update the list of OsAccount * attributes. * - * @param osAccountAttribute The osAccount Attribute that is to be added. + * @param osAccountAttributes The osAccount attributes that are to be added. */ synchronized void setAttributesInternal(List<OsAccountAttribute> osAccountAttributes) { this.osAccountAttributes = osAccountAttributes; } + /** + * This function is used by OsAccountManger to update the list of OsAccount + * instances. + * + * @param osAccountInstanes The osAccount instances that are to be added. + */ + synchronized void setInstancesInternal(List<OsAccountInstance> osAccountInstances) { + this.osAccountInstances = osAccountInstances; + } + /** * Get the account Object Id that is unique within the scope of the case. * diff --git a/bindings/java/src/org/sleuthkit/datamodel/OsAccountManager.java b/bindings/java/src/org/sleuthkit/datamodel/OsAccountManager.java old mode 100644 new mode 100755 index 45660617238713bb3182875b91c6ebf752adb967..24297c6f4cc51398de0b39ef1b48b023a64c34c0 --- a/bindings/java/src/org/sleuthkit/datamodel/OsAccountManager.java +++ b/bindings/java/src/org/sleuthkit/datamodel/OsAccountManager.java @@ -40,6 +40,7 @@ import org.sleuthkit.datamodel.OsAccount.OsAccountAttribute; import org.sleuthkit.datamodel.SleuthkitCase.CaseDbConnection; import org.sleuthkit.datamodel.SleuthkitCase.CaseDbTransaction; +import org.sleuthkit.datamodel.TskEvent.OsAccountsChangedTskEvent; /** * Responsible for creating/updating/retrieving the OS accounts for files and @@ -599,6 +600,11 @@ void newOsAccountInstance(OsAccount osAccount, long dataSourceObjId, OsAccountIn // add to the cache. osAccountInstanceCache.add(accountInstance); + // update account instances + List<OsAccountInstance> currentInstancesList = getOsAccountInstances(osAccount, connection); + currentInstancesList.add(accountInstance); + osAccount.setInstancesInternal(currentInstancesList); + } catch (SQLException ex) { throw new TskCoreException(String.format("Error adding os account instance for account = %s, data source object id = %d", osAccount.getAddr().orElse(osAccount.getLoginName().orElse("UNKNOWN")), dataSourceObjId), ex); } finally { @@ -1044,8 +1050,8 @@ public void addExtendedOsAccountAttributes(OsAccount account, List<OsAccountAttr } finally { db.releaseSingleUserCaseWriteLock(); } + // set the atrribute list in account to the most current list from the database List<OsAccountAttribute> currentAttribsList = getOsAccountAttributes(account); - currentAttribsList.addAll(accountAttributes); account.setAttributesInternal(currentAttribsList); } fireChangeEvent(account); @@ -1116,12 +1122,28 @@ List<OsAccountAttribute> getOsAccountAttributes(OsAccount account) throws TskCor * @throws TskCoreException */ List<OsAccountInstance> getOsAccountInstances(OsAccount account) throws TskCoreException { + try (CaseDbConnection connection = db.getConnection()) { + return getOsAccountInstances(account, connection); + } + } + + /** + * Get a list of OsAccountInstances for the give OsAccount. + * + * @param account Account to retrieve instance for. + * @param connection Database connection to use. + * + * @return List of OsAccountInstances, the list maybe empty if none were + * found. + * + * @throws TskCoreException + */ + private List<OsAccountInstance> getOsAccountInstances(OsAccount account, CaseDbConnection connection ) throws TskCoreException { List<OsAccountInstance> instanceList = new ArrayList<>(); String queryString = String.format("SELECT * FROM tsk_os_account_instances WHERE os_account_obj_id = %d", account.getId()); db.acquireSingleUserCaseReadLock(); - try (CaseDbConnection connection = db.getConnection(); - Statement s = connection.createStatement(); + try ( Statement s = connection.createStatement(); ResultSet rs = connection.executeQuery(s, queryString)) { while (rs.next()) { @@ -1255,26 +1277,31 @@ private <T> void updateAccountColumn(long accountObjId, String colName, T colVal + " SET " + colName + " = ? " + " WHERE os_account_obj_id = ?"; - PreparedStatement preparedStatement = connection.getPreparedStatement(updateSQL, Statement.NO_GENERATED_KEYS); - preparedStatement.clearParameters(); + db.acquireSingleUserCaseWriteLock(); + try { + PreparedStatement preparedStatement = connection.getPreparedStatement(updateSQL, Statement.NO_GENERATED_KEYS); + preparedStatement.clearParameters(); - if (Objects.isNull(colValue)) { - preparedStatement.setNull(1, Types.NULL); // handle null value - } else { - if (colValue instanceof String) { - preparedStatement.setString(1, (String) colValue); - } else if (colValue instanceof Long) { - preparedStatement.setLong(1, (Long) colValue); - } else if (colValue instanceof Integer) { - preparedStatement.setInt(1, (Integer) colValue); + if (Objects.isNull(colValue)) { + preparedStatement.setNull(1, Types.NULL); // handle null value } else { - throw new TskCoreException(String.format("Unhandled column data type received while updating the account (%d) ", accountObjId)); + if (colValue instanceof String) { + preparedStatement.setString(1, (String) colValue); + } else if (colValue instanceof Long) { + preparedStatement.setLong(1, (Long) colValue); + } else if (colValue instanceof Integer) { + preparedStatement.setInt(1, (Integer) colValue); + } else { + throw new TskCoreException(String.format("Unhandled column data type received while updating the account (%d) ", accountObjId)); + } } - } - preparedStatement.setLong(2, accountObjId); - - connection.executeUpdate(preparedStatement); + preparedStatement.setLong(2, accountObjId); + + connection.executeUpdate(preparedStatement); + } finally { + db.releaseSingleUserCaseWriteLock(); + } } /** @@ -1422,16 +1449,19 @@ private OsAccountUpdateResult updateOsAccountCore(OsAccount osAccount, String ad updateStatusCode = OsAccountUpdateStatus.UPDATED; } - // update signature if needed - if (updateStatusCode == OsAccountUpdateStatus.UPDATED) { - String newSignature = getOsAccountSignature(address, loginName); - updateAccountSignature(osAccount.getId(), newSignature, connection); - } - // if nothing is changed, return if (updateStatusCode == OsAccountUpdateStatus.NO_CHANGE) { return new OsAccountUpdateResult(updateStatusCode, osAccount); } + + // update signature if needed, based on the most current addr/loginName + OsAccount currAccount = getOsAccountByObjectId(osAccount.getId(), connection); + String newAddress = currAccount.getAddr().orElse(null); + String newLoginName = currAccount.getLoginName().orElse(null); + + String newSignature = getOsAccountSignature(newAddress, newLoginName); + updateAccountSignature(osAccount.getId(), newSignature, connection); + // get the updated account from database updatedAccount = getOsAccountByObjectId(osAccount.getId(), connection); @@ -1445,7 +1475,6 @@ private OsAccountUpdateResult updateOsAccountCore(OsAccount osAccount, String ad } } - /** * Returns a list of hosts where the OsAccount has appeared. * @@ -1510,16 +1539,6 @@ private OsAccount osAccountFromResultSet(ResultSet rs) throws SQLException { } - /** - * Fires an OsAccountAddedEvent for the given OsAccount. Do not call this - * with an open transaction. - * - * @param account Newly created account. - */ - private void fireCreationEvent(OsAccount account) { - db.fireTSKEvent(new OsAccountsCreationEvent(Collections.singletonList(account))); - } - /** * Fires an OsAccountChangeEvent for the given OsAccount. Do not call this * with an open transaction. @@ -1527,20 +1546,9 @@ private void fireCreationEvent(OsAccount account) { * @param account Updated account. */ private void fireChangeEvent(OsAccount account) { - db.fireTSKEvent(new OsAccountsUpdateEvent(Collections.singletonList(account))); + db.fireTSKEvent(new OsAccountsChangedTskEvent(Collections.singletonList(account))); } - /** - * Fires an OsAccountDeleteEvent for the given OsAccount. Do not call this - * with an open transaction. - * - * @param account Deleted account. - */ - private void fireDeleteEvent(OsAccount account) { - db.fireTSKEvent(new OsAccountsDeleteEvent(Collections.singletonList(account.getId()))); - } - - /** * Created an account signature for an OS Account. This signature is simply * to prevent duplicate accounts from being created. Signature is set to: @@ -1568,87 +1576,6 @@ static String getOsAccountSignature(String uniqueId, String loginName) throws Ts return signature; } - /** - * Event fired by OsAccountManager to indicate that a new OsAccount was - * created. - */ - public static final class OsAccountsCreationEvent { - - private final List<OsAccount> accountList; - - /** - * Constructs a new AddedEvent - * - * @param accountList List newly created accounts. - */ - OsAccountsCreationEvent(List<OsAccount> accountList) { - this.accountList = accountList; - } - - /** - * Returns a list of the added OsAccounts. - * - * @return List of OsAccounts. - */ - public List<OsAccount> getOsAcounts() { - return Collections.unmodifiableList(accountList); - } - } - - /** - * Event fired by OsAccount Manager to indicate that an OsAccount was - * updated. - */ - public static final class OsAccountsUpdateEvent { - - private final List<OsAccount> accountList; - - /** - * Constructs a new ChangeEvent - * - * @param accountList List newly created accounts. - */ - OsAccountsUpdateEvent(List<OsAccount> accountList) { - this.accountList = accountList; - } - - /** - * Returns a list of the updated OsAccounts. - * - * @return List of OsAccounts. - */ - public List<OsAccount> getOsAcounts() { - return Collections.unmodifiableList(accountList); - } - } - - /** - * Event fired by OsAccount Manager to indicate that an OsAccount was - * deleted. - */ - public static final class OsAccountsDeleteEvent { - - private final List<Long> accountObjectIds; - - /** - * Constructs a new DeleteEvent - * - * @param accountList List newly deleted accounts. - */ - OsAccountsDeleteEvent(List<Long> accountObjectIds) { - this.accountObjectIds = accountObjectIds; - } - - /** - * Returns a list of the deleted OsAccounts. - * - * @return List of OsAccounts. - */ - public List<Long> getOsAcountObjectIds() { - return Collections.unmodifiableList(accountObjectIds); - } - } - /** * Exception thrown if a given SID is a valid SID but is a group SID, and * not an individual user SID. diff --git a/bindings/java/src/org/sleuthkit/datamodel/OsAccountRealmManager.java b/bindings/java/src/org/sleuthkit/datamodel/OsAccountRealmManager.java index c9ebe104eb3b02a50ea099822e1a590233a0bea0..f3b3751415e00671e658fad7adef03e87590109c 100644 --- a/bindings/java/src/org/sleuthkit/datamodel/OsAccountRealmManager.java +++ b/bindings/java/src/org/sleuthkit/datamodel/OsAccountRealmManager.java @@ -24,8 +24,10 @@ import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; +import java.sql.Types; import java.util.ArrayList; import java.util.List; +import java.util.Objects; import java.util.Optional; import java.util.UUID; import java.util.logging.Logger; @@ -303,74 +305,113 @@ public OsRealmUpdateResult updateRealm(OsAccountRealm realm, String realmAddr, S * @throws TskCoreException If there is a database error or if a realm * already exists with that information. */ - private OsRealmUpdateResult updateRealm(OsAccountRealm realm, String realmAddr, String realmName, CaseDbConnection connection) throws TskCoreException { - + private OsRealmUpdateResult updateRealm(OsAccountRealm realm, String realmAddr, String realmName, CaseDbConnection connection) throws TskCoreException { + // need at least one of the two if (StringUtils.isBlank(realmAddr) && StringUtils.isBlank(realmName)) { throw new TskCoreException("Realm address or name is required to update realm."); } - + OsRealmUpdateStatus updateStatusCode = OsRealmUpdateStatus.NO_CHANGE; OsAccountRealm updatedRealm = null; - - List<String> realmNames = realm.getRealmNames(); - String currRealmName = realmNames.isEmpty() ? null : realmNames.get(0); // currently there is only one name. - String currRealmAddr = realm.getRealmAddr().orElse(null); - - - // set name and address to new values only if the current value is blank and the new value isn't. - String newRealmAddr; - if ( (StringUtils.isBlank(currRealmAddr) && StringUtils.isNotBlank(realmAddr))) { - newRealmAddr = realmAddr; - updateStatusCode = OsRealmUpdateStatus.UPDATED; - } else { - newRealmAddr = currRealmAddr; - } - - String newRealmName; - if (StringUtils.isBlank(currRealmName) && StringUtils.isNotBlank(realmName)) { - newRealmName = realmName; - updateStatusCode = OsRealmUpdateStatus.UPDATED; - } else { - newRealmName = currRealmName; - } - - // make new signature - String newSignature = makeRealmSignature(newRealmAddr, newRealmName, realm.getScopeHost().orElse(null)); - - // if nothing is to be changed, return - if ( updateStatusCode == OsRealmUpdateStatus.NO_CHANGE) { - return new OsRealmUpdateResult(updateStatusCode, realm); - } - - + db.acquireSingleUserCaseWriteLock(); try { - // We only alow realm addr, name and signature to be updated at this time. + List<String> realmNames = realm.getRealmNames(); + String currRealmName = realmNames.isEmpty() ? null : realmNames.get(0); // currently there is only one name. + String currRealmAddr = realm.getRealmAddr().orElse(null); + + // set name and address to new values only if the current value is blank and the new value isn't. + if ((StringUtils.isBlank(currRealmAddr) && StringUtils.isNotBlank(realmAddr))) { + updateRealmColumn(realm.getRealmId(), "realm_addr", realmAddr, connection); + updateStatusCode = OsRealmUpdateStatus.UPDATED; + } + + if (StringUtils.isBlank(currRealmName) && StringUtils.isNotBlank(realmName)) { + updateRealmColumn(realm.getRealmId(), "realm_name", realmName, connection); + updateStatusCode = OsRealmUpdateStatus.UPDATED; + } + + // if nothing is to be changed, return + if (updateStatusCode == OsRealmUpdateStatus.NO_CHANGE) { + return new OsRealmUpdateResult(updateStatusCode, realm); + } + + // update realm signature - based on the most current address and name + OsAccountRealm currRealm = getRealmByRealmId(realm.getRealmId(), connection); + String newRealmAddr = currRealm.getRealmAddr().orElse(null); + String newRealmName = (currRealm.getRealmNames().isEmpty() == false) ? currRealm.getRealmNames().get(0) : null; + + // make new signature + String newSignature = makeRealmSignature(newRealmAddr, newRealmName, realm.getScopeHost().orElse(null)); + // Use a random string as the signature if the realm is not active. - String updateSQL = "UPDATE tsk_os_account_realms SET realm_name = ?, realm_addr = ?, " - + " realm_signature = " + String updateSQL = "UPDATE tsk_os_account_realms SET " + + " realm_signature = " + " CASE WHEN db_status = " + OsAccountRealm.RealmDbStatus.ACTIVE.getId() + " THEN ? ELSE realm_signature END " + " WHERE id = ?"; PreparedStatement preparedStatement = connection.getPreparedStatement(updateSQL, Statement.NO_GENERATED_KEYS); preparedStatement.clearParameters(); - - preparedStatement.setString(1, newRealmName); - preparedStatement.setString(2, newRealmAddr); - preparedStatement.setString(3, newSignature); // Is only set for active accounts - preparedStatement.setLong(4, realm.getRealmId()); + + preparedStatement.setString(1, newSignature); // Is only set for active accounts + preparedStatement.setLong(2, realm.getRealmId()); connection.executeUpdate(preparedStatement); - + // read the updated realm updatedRealm = this.getRealmByRealmId(realm.getRealmId(), connection); - + return new OsRealmUpdateResult(updateStatusCode, updatedRealm); } catch (SQLException ex) { - throw new TskCoreException(String.format("Error updating realm with id = %d, name = %s, addr = %s", realm.getRealmId(), realmName != null ? realmName : "Null", realm.getRealmAddr().orElse("Null") ), ex); + throw new TskCoreException(String.format("Error updating realm with id = %d, name = %s, addr = %s", realm.getRealmId(), realmName != null ? realmName : "Null", realm.getRealmAddr().orElse("Null")), ex); + } finally { + db.releaseSingleUserCaseWriteLock(); + } + + } + + /** + * Updates specified column in the tsk_os_account_realms table to the specified value. + * + * @param <T> Type of value - must be a String, Long or an Integer. + * @param realmId Id of the realm to be updated. + * @param colName Name of column o be updated. + * @param colValue New column value. + * @param connection Database connection to use. + * + * @throws SQLException If there is an error updating the database. + * @throws TskCoreException If the value type is not handled. + */ + private <T> void updateRealmColumn(long realmId, String colName, T colValue, CaseDbConnection connection) throws SQLException, TskCoreException { + + String updateSQL = "UPDATE tsk_os_account_realms " + + " SET " + colName + " = ? " + + " WHERE id = ?"; + + db.acquireSingleUserCaseWriteLock(); + try { + PreparedStatement preparedStatement = connection.getPreparedStatement(updateSQL, Statement.NO_GENERATED_KEYS); + preparedStatement.clearParameters(); + + if (Objects.isNull(colValue)) { + preparedStatement.setNull(1, Types.NULL); // handle null value + } else { + if (colValue instanceof String) { + preparedStatement.setString(1, (String) colValue); + } else if (colValue instanceof Long) { + preparedStatement.setLong(1, (Long) colValue); + } else if (colValue instanceof Integer) { + preparedStatement.setInt(1, (Integer) colValue); + } else { + throw new TskCoreException(String.format("Unhandled column data type received while updating the realm (id = %d) ", realmId)); + } + } + + preparedStatement.setLong(2, realmId); + + connection.executeUpdate(preparedStatement); } finally { db.releaseSingleUserCaseWriteLock(); } - } private final static String REALM_QUERY_STRING = "SELECT realms.id as realm_id, realms.realm_name as realm_name," diff --git a/bindings/java/src/org/sleuthkit/datamodel/PersonManager.java b/bindings/java/src/org/sleuthkit/datamodel/PersonManager.java old mode 100644 new mode 100755 index 2f60210216f5f22c468beccfc7c59314c29b27f6..a0153c78a50b424acf09c3ad401dd385702c27dc --- a/bindings/java/src/org/sleuthkit/datamodel/PersonManager.java +++ b/bindings/java/src/org/sleuthkit/datamodel/PersonManager.java @@ -28,6 +28,7 @@ import java.util.List; import java.util.Optional; import org.sleuthkit.datamodel.SleuthkitCase.CaseDbConnection; +import org.sleuthkit.datamodel.TskEvent.PersonsAddedTskEvent; /** * Responsible for creating/updating/retrieving Persons. @@ -393,7 +394,7 @@ public void setPerson(Host host, Person person) throws TskCoreException { * @param added The person that was created. */ private void fireCreationEvent(Person added) { - db.fireTSKEvent(new PersonsCreationEvent(Collections.singletonList(added))); + db.fireTSKEvent(new PersonsAddedTskEvent(Collections.singletonList(added))); } /** @@ -402,81 +403,10 @@ private void fireCreationEvent(Person added) { * @param newValue The person value that has changed. */ void fireChangeEvent(Person newValue) { - db.fireTSKEvent(new PersonsUpdateEvent(Collections.singletonList(newValue))); + db.fireTSKEvent(new TskEvent.PersonsChangedTskEvent(Collections.singletonList(newValue))); } private void fireDeletedEvent(Person deleted) { - db.fireTSKEvent(new PersonsDeletionEvent(Collections.singletonList(deleted))); - } - - /** - * Base event for all person events - */ - static class BasePersonEvent { - - private final List<Person> persons; - - /** - * Main constructor. - * - * @param persons The persons that are objects of the event. - */ - BasePersonEvent(List<Person> persons) { - this.persons = Collections.unmodifiableList(new ArrayList<>(persons)); - } - - /** - * Returns the persons affected in the event. - * - * @return The persons affected in the event. - */ - public List<Person> getPersons() { - return persons; - } - } - - /** - * Event fired when persons are created. - */ - public static final class PersonsCreationEvent extends BasePersonEvent { - - /** - * Main constructor. - * - * @param persons The added persons. - */ - PersonsCreationEvent(List<Person> persons) { - super(persons); - } - } - - /** - * Event fired when persons are updated. - */ - public static final class PersonsUpdateEvent extends BasePersonEvent { - - /** - * Main constructor. - * - * @param persons The new values for the persons that were changed. - */ - PersonsUpdateEvent(List<Person> persons) { - super(persons); - } - } - - /** - * Event fired when persons are deleted. - */ - public static final class PersonsDeletionEvent extends BasePersonEvent { - - /** - * Main constructor. - * - * @param persons The persons that were deleted. - */ - PersonsDeletionEvent(List<Person> persons) { - super(persons); - } + db.fireTSKEvent(new TskEvent.PersonsDeletedTskEvent(Collections.singletonList(deleted))); } } diff --git a/bindings/java/src/org/sleuthkit/datamodel/SleuthkitCase.java b/bindings/java/src/org/sleuthkit/datamodel/SleuthkitCase.java old mode 100644 new mode 100755 index ddc96594ca05efdccc7c23ef8da740f780d31d88..25e0ccbedb8bb16254e0e957027deafd4b098ce2 --- a/bindings/java/src/org/sleuthkit/datamodel/SleuthkitCase.java +++ b/bindings/java/src/org/sleuthkit/datamodel/SleuthkitCase.java @@ -100,7 +100,7 @@ public class SleuthkitCase { * tsk/auto/tsk_db.h. */ static final CaseDbSchemaVersionNumber CURRENT_DB_SCHEMA_VERSION - = new CaseDbSchemaVersionNumber(8, 7); + = new CaseDbSchemaVersionNumber(9, 0); private static final long BASE_ARTIFACT_ID = Long.MIN_VALUE; // Artifact ids will start at the lowest negative value private static final Logger logger = Logger.getLogger(SleuthkitCase.class.getName()); @@ -994,7 +994,7 @@ private void updateDatabaseSchema(String dbPath) throws Exception { dbSchemaVersion = updateFromSchema8dot3toSchema8dot4(dbSchemaVersion, connection); dbSchemaVersion = updateFromSchema8dot4toSchema8dot5(dbSchemaVersion, connection); dbSchemaVersion = updateFromSchema8dot5toSchema8dot6(dbSchemaVersion, connection); - dbSchemaVersion = updateFromSchema8dot6toSchema8dot7(dbSchemaVersion, connection); + dbSchemaVersion = updateFromSchema8dot6toSchema9dot0(dbSchemaVersion, connection); statement = connection.createStatement(); connection.executeUpdate(statement, "UPDATE tsk_db_info SET schema_ver = " + dbSchemaVersion.getMajor() + ", schema_minor_ver = " + dbSchemaVersion.getMinor()); //NON-NLS connection.executeUpdate(statement, "UPDATE tsk_db_info_extended SET value = " + dbSchemaVersion.getMajor() + " WHERE name = '" + SCHEMA_MAJOR_VERSION_KEY + "'"); //NON-NLS @@ -2345,7 +2345,7 @@ private CaseDbSchemaVersionNumber updateFromSchema8dot5toSchema8dot6(CaseDbSchem } @SuppressWarnings("deprecation") - private CaseDbSchemaVersionNumber updateFromSchema8dot6toSchema8dot7(CaseDbSchemaVersionNumber schemaVersion, CaseDbConnection connection) throws SQLException, TskCoreException { + private CaseDbSchemaVersionNumber updateFromSchema8dot6toSchema9dot0(CaseDbSchemaVersionNumber schemaVersion, CaseDbConnection connection) throws SQLException, TskCoreException { if (schemaVersion.getMajor() != 8) { return schemaVersion; } @@ -2564,7 +2564,7 @@ private CaseDbSchemaVersionNumber updateFromSchema8dot6toSchema8dot7(CaseDbSchem + "FOREIGN KEY(obj_id) REFERENCES tsk_objects(obj_id) ON DELETE CASCADE )"); - return new CaseDbSchemaVersionNumber(8, 7); + return new CaseDbSchemaVersionNumber(9, 0); } finally { closeStatement(statement); @@ -13171,19 +13171,19 @@ public void commit() throws TskCoreException { // Fire events for any new or changed objects if (!hostsAdded.isEmpty()) { - sleuthkitCase.fireTSKEvent(new HostManager.HostsCreationEvent(hostsAdded)); + sleuthkitCase.fireTSKEvent(new TskEvent.HostsAddedTskEvent(hostsAdded)); } if (!accountsAdded.isEmpty()) { - sleuthkitCase.fireTSKEvent(new OsAccountManager.OsAccountsCreationEvent(accountsAdded)); + sleuthkitCase.fireTSKEvent(new TskEvent.OsAccountsAddedTskEvent(accountsAdded)); } if (!accountsChanged.isEmpty()) { - sleuthkitCase.fireTSKEvent(new OsAccountManager.OsAccountsUpdateEvent(accountsChanged)); + sleuthkitCase.fireTSKEvent(new TskEvent.OsAccountsChangedTskEvent(accountsChanged)); } if (!deletedOsAccountObjectIds.isEmpty()) { - sleuthkitCase.fireTSKEvent(new OsAccountManager.OsAccountsDeleteEvent(deletedOsAccountObjectIds)); + sleuthkitCase.fireTSKEvent(new TskEvent.OsAccountsDeletedTskEvent(deletedOsAccountObjectIds)); } if (!deletedResultObjectIds.isEmpty()) { - sleuthkitCase.fireTSKEvent(new AnalysisResultsDeletedEvent(deletedResultObjectIds)); + sleuthkitCase.fireTSKEvent(new TskEvent.AnalysisResultsDeletedTskEvent(deletedResultObjectIds)); } } } diff --git a/bindings/java/src/org/sleuthkit/datamodel/TskEvent.java b/bindings/java/src/org/sleuthkit/datamodel/TskEvent.java old mode 100644 new mode 100755 index 089daf46d6b8749b5a050da32c6a9c3286527750..0feb6fbcb57af15b1ff010a78c52515ab0704968 --- a/bindings/java/src/org/sleuthkit/datamodel/TskEvent.java +++ b/bindings/java/src/org/sleuthkit/datamodel/TskEvent.java @@ -1,15 +1,15 @@ /* * Sleuth Kit Data Model - * - * Copyright 2020 Basis Technology Corp. + * + * Copyright 2020-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. @@ -18,10 +18,262 @@ */ package org.sleuthkit.datamodel; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + /** * - * A marker interface for events published by SleuthKit. + * A marker interface for events published by SleuthKit. */ public interface TskEvent { - + + /** + * Event to indicate that analysis results were deleted. + */ + final public static class AnalysisResultsDeletedTskEvent { + + private final List<Long> deletedResultObjIds; + + /** + * Constructs a new AnalysisResultsDeletedEvent. + * + * @param deletedResults List of deleted results. + */ + AnalysisResultsDeletedTskEvent(List<Long> deletedResultObjIds) { + this.deletedResultObjIds = deletedResultObjIds; + } + + /** + * Returns a list of deleted results. + * + * @return List of AnalysisResult. + */ + public List<Long> getObjectIds() { + return Collections.unmodifiableList(deletedResultObjIds); + } + } + + /** + * Base event for all host events + */ + static class HostTskEvent { + + private final List<Host> hosts; + + /** + * Main constructor. + * + * @param hosts The hosts that are objects of the event. + */ + HostTskEvent(List<Host> hosts) { + this.hosts = hosts; + } + + /** + * Returns the hosts affected in the event. + * + * @return The hosts affected in the event. + */ + public List<Host> getHosts() { + return Collections.unmodifiableList(new ArrayList<>(hosts)); + } + } + + /** + * Event fired when hosts are created. + */ + public static final class HostsAddedTskEvent extends HostTskEvent { + + /** + * Main constructor. + * + * @param hosts The added hosts. + */ + HostsAddedTskEvent(List<Host> hosts) { + super(hosts); + } + } + + /** + * Event fired when hosts are updated. + */ + public static final class HostsChangedTskEvent extends HostTskEvent { + + /** + * Main constructor. + * + * @param hosts The new values for the hosts that were changed. + */ + HostsChangedTskEvent(List<Host> hosts) { + super(hosts); + } + } + + /** + * Event fired when hosts are deleted. + */ + public static final class HostsDeletedTskEvent extends HostTskEvent { + + /** + * Main constructor. + * + * @param hosts The hosts that were deleted. + */ + HostsDeletedTskEvent(List<Host> hosts) { + super(hosts); + } + } + + /** + * Event fired by OsAccountManager to indicate that a new OsAccount was + * created. + */ + public static final class OsAccountsAddedTskEvent { + + private final List<OsAccount> accountList; + + /** + * Constructs a new AddedEvent + * + * @param accountList List newly created accounts. + */ + OsAccountsAddedTskEvent(List<OsAccount> accountList) { + this.accountList = accountList; + } + + /** + * Returns a list of the added OsAccounts. + * + * @return List of OsAccounts. + */ + public List<OsAccount> getOsAcounts() { + return Collections.unmodifiableList(accountList); + } + } + + /** + * Event fired by OsAccount Manager to indicate that an OsAccount was + * updated. + */ + public static final class OsAccountsChangedTskEvent { + + private final List<OsAccount> accountList; + + /** + * Constructs a new ChangeEvent + * + * @param accountList List newly created accounts. + */ + OsAccountsChangedTskEvent(List<OsAccount> accountList) { + this.accountList = accountList; + } + + /** + * Returns a list of the updated OsAccounts. + * + * @return List of OsAccounts. + */ + public List<OsAccount> getOsAcounts() { + return Collections.unmodifiableList(accountList); + } + } + + /** + * Event fired by OsAccount Manager to indicate that an OsAccount was + * deleted. + */ + public static final class OsAccountsDeletedTskEvent { + + private final List<Long> accountObjectIds; + + /** + * Constructs a new DeleteEvent + * + * @param accountList List newly deleted accounts. + */ + OsAccountsDeletedTskEvent(List<Long> accountObjectIds) { + this.accountObjectIds = accountObjectIds; + } + + /** + * Returns a list of the deleted OsAccounts. + * + * @return List of OsAccounts. + */ + public List<Long> getOsAcountObjectIds() { + return Collections.unmodifiableList(accountObjectIds); + } + } + + /** + * Base event for all person events + */ + static class PersonsTskEvent { + + private final List<Person> persons; + + /** + * Main constructor. + * + * @param persons The persons that are objects of the event. + */ + PersonsTskEvent(List<Person> persons) { + this.persons = persons; + } + + /** + * Returns the persons affected in the event. + * + * @return The persons affected in the event. + */ + public List<Person> getPersons() { + return Collections.unmodifiableList(new ArrayList<>(persons)); + } + } + + /** + * Event fired when persons are created. + */ + final public static class PersonsAddedTskEvent extends PersonsTskEvent { + + /** + * Main constructor. + * + * @param persons The added persons. + */ + PersonsAddedTskEvent(List<Person> persons) { + super(persons); + } + } + + /** + * Event fired when persons are updated. + */ + final public static class PersonsChangedTskEvent extends PersonsTskEvent { + + /** + * Main constructor. + * + * @param persons The new values for the persons that were changed. + */ + PersonsChangedTskEvent(List<Person> persons) { + super(persons); + } + } + + /** + * Event fired when persons are deleted. + */ + final public static class PersonsDeletedTskEvent extends PersonsTskEvent { + + /** + * Main constructor. + * + * @param persons The persons that were deleted. + */ + PersonsDeletedTskEvent(List<Person> persons) { + super(persons); + } + } } diff --git a/bindings/java/test/org/sleuthkit/datamodel/OsAccountTest.java b/bindings/java/test/org/sleuthkit/datamodel/OsAccountTest.java index eeb5d566e5c381dc38112b6787574e8f8c1ad34c..7c14f3e124b799ad10e4d468b58166d0f3021974 100644 --- a/bindings/java/test/org/sleuthkit/datamodel/OsAccountTest.java +++ b/bindings/java/test/org/sleuthkit/datamodel/OsAccountTest.java @@ -906,4 +906,70 @@ public void windowsAccountRealmUpdateTests() throws TskCoreException, OsAccountM } + + @Test + public void windowsAccountUpdateTests() throws TskCoreException, OsAccountManager.NotUserSIDException { + + + String hostname1 = "host55555"; + Host host1 = caseDB.getHostManager().newHost(hostname1); + + + // Test 1: create an account with a SID alone. Then update the loginName. + + String ownerUid1 = "S-1-5-21-111111111-222222222-555555555-0001"; + OsAccount osAccount1 = caseDB.getOsAccountManager().newWindowsOsAccount(ownerUid1, null, null, host1, OsAccountRealm.RealmScope.DOMAIN); + + + // now update the account login name + String loginname1 = "jbravo"; + + OsAccountUpdateResult updateResult = caseDB.getOsAccountManager().updateCoreWindowsOsAccountAttributes(osAccount1, null, loginname1, null, host1); + assertEquals(updateResult.getUpdateStatusCode(), OsAccountManager.OsAccountUpdateStatus.UPDATED); + assertEquals(updateResult.getUpdatedAccount().isPresent(), true); + OsAccount updatedAccount = updateResult.getUpdatedAccount().orElseThrow(() -> new TskCoreException("Updated account not found.")); + + // verify that account has both addr and loginName, and that signature is the addr + assertTrue(updatedAccount.getAddr().orElse("").equalsIgnoreCase(ownerUid1)); + assertTrue(updatedAccount.getLoginName().orElse("").equalsIgnoreCase(loginname1)); + assertTrue(updatedAccount.getSignature().equalsIgnoreCase(ownerUid1)); // account signature should not change + + + String realmAddr1 = "S-1-5-21-111111111-222222222-555555555"; + String realmSignature1 = realmAddr1 + "_DOMAIN"; // for a domain realm - signature is sid/name + "_DOMAIN" + + OsAccountRealm realm1 = caseDB.getOsAccountRealmManager().getRealmByRealmId(updatedAccount.getRealmId()); + assertTrue(realm1.getRealmAddr().orElse("").equalsIgnoreCase(realmAddr1)); + assertTrue(realm1.getSignature().equalsIgnoreCase(realmSignature1)); + + + // TBD Test2: create an account with realmName/loginname and then update the SID + + String loginname2 = "janeB"; + String realmName2 = "realm55555"; + OsAccount osAccount2 = caseDB.getOsAccountManager().newWindowsOsAccount(null, loginname2, realmName2, host1, OsAccountRealm.RealmScope.DOMAIN); + + assertFalse(osAccount2.getAddr().isPresent()); + assertTrue(osAccount2.getLoginName().orElse("").equalsIgnoreCase(loginname2)); + assertTrue(osAccount2.getSignature().equalsIgnoreCase(loginname2)); // account signature should be the login name + + // now update the account SID + String ownerUid2 = "S-1-5-21-111111111-222222222-555555555-0007"; + OsAccountUpdateResult updateResult2 = caseDB.getOsAccountManager().updateCoreWindowsOsAccountAttributes(osAccount2, ownerUid2, null, realmName2, host1); + assertEquals(updateResult2.getUpdateStatusCode(), OsAccountManager.OsAccountUpdateStatus.UPDATED); + assertEquals(updateResult2.getUpdatedAccount().isPresent(), true); + OsAccount updatedAccount2 = updateResult2.getUpdatedAccount().orElseThrow(() -> new TskCoreException("Updated account not found.")); + + // verify that account has both addr and loginName, and that signature is the addr + assertTrue(updatedAccount2.getAddr().orElse("").equalsIgnoreCase(ownerUid2)); + assertTrue(updatedAccount2.getLoginName().orElse("").equalsIgnoreCase(loginname2)); + assertTrue(updatedAccount2.getSignature().equalsIgnoreCase(ownerUid2)); // account signature should now be addr + + // RAMAN TODO: CT-4284 +// OsAccountRealm realm2 = caseDB.getOsAccountRealmManager().getRealmByRealmId(updatedAccount2.getRealmId()); +// assertTrue(realm2.getRealmAddr().orElse("").equalsIgnoreCase(realmAddr1)); +// assertTrue(realm2.getSignature().equalsIgnoreCase(realmSignature1)); + } + + }