Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • irt/sleuthkit
1 result
Show changes
Showing
with 5163 additions and 0 deletions
/*
* SleuthKit Java Bindings
*
* 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;
import java.util.List;
/**
* Do-nothing version of AddDataSourceCallbacks
*/
public class DefaultAddDataSourceCallbacks implements AddDataSourceCallbacks {
@Override
public void onFilesAdded(List<Long> fileObjectIds) {
// Do nothing
}
}
/*
* SleuthKit Java Bindings
*
* Copyright 2011-2022 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.text.MessageFormat;
import java.util.Collections;
import java.util.ResourceBundle;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.sleuthkit.datamodel.TskData.FileKnown;
import org.sleuthkit.datamodel.TskData.TSK_DB_FILES_TYPE_ENUM;
import org.sleuthkit.datamodel.TskData.TSK_FS_META_TYPE_ENUM;
import org.sleuthkit.datamodel.TskData.TSK_FS_NAME_FLAG_ENUM;
import org.sleuthkit.datamodel.TskData.TSK_FS_NAME_TYPE_ENUM;
/**
* A representation of a file or directory that has been derived from another
* file and is stored outside of the data source (e.g., on a user's machine). A
* typical example of a derived file is a file extracted from an archive file.
*/
public class DerivedFile extends AbstractFile {
private volatile DerivedMethod derivedMethod;
private static final Logger logger = Logger.getLogger(DerivedFile.class.getName());
private static ResourceBundle bundle = ResourceBundle.getBundle("org.sleuthkit.datamodel.Bundle");
private boolean hasDerivedMethod = true;
/**
* Constructs a representation of a file or directory that has been derived
* from another file and is stored outside of the data source (e.g., on a
* user's machine). A typical example of a derived file is a file extracted
* from an archive file.
*
* @param db The case database to which the file has been
* added.
* @param objId The object id of the file in the case database.
* @param dataSourceObjectId The object id of the data source for the file.
* @param fileSystemObjectId The object id of the file system. May be null.
* @param name The name of the file.
* @param dirType The type of the file, usually as reported in
* the name structure of the file system. May be
* set to TSK_FS_NAME_TYPE_ENUM.UNDEF.
* @param metaType The type of the file, usually as reported in
* the metadata structure of the file system. May
* be set to
* TSK_FS_META_TYPE_ENUM.TSK_FS_META_TYPE_UNDEF.
* @param dirFlag The allocated status of the file, usually as
* reported in the name structure of the file
* system.
* @param metaFlags The allocated status of the file, usually as
* reported in the metadata structure of the file
* system.
* @param size The size of the file.
* @param ctime The changed time of the file.
* @param crtime The created time of the file.
* @param atime The accessed time of the file.
* @param mtime The modified time of the file.
* @param md5Hash The MD5 hash of the file, null if not yet
* calculated.
* @param sha256Hash sha256 hash of the file, or null if not present
* @param sha1Hash SHA-1 hash of the file, or null if not present
* @param knownState The known state of the file from a hash
* database lookup, null if not yet looked up.
* @param parentPath The path of the parent of the file.
* @param localPath The absolute path of the file in secondary
* storage.
* @param parentId The object id of parent of the file.
* @param mimeType The MIME type of the file, null if it has not
* yet been determined.
* @param encodingType The encoding type of the file.
* @param extension The extension part of the file name (not
* including the '.'), can be null.
* @param ownerUid UID of the file owner as found in the file
* system, can be null.
* @param osAccountObjId Obj id of the owner OS account, may be null.
*/
DerivedFile(SleuthkitCase db,
long objId,
long dataSourceObjectId,
Long fileSystemObjectId,
String name,
TSK_FS_NAME_TYPE_ENUM dirType, TSK_FS_META_TYPE_ENUM metaType,
TSK_FS_NAME_FLAG_ENUM dirFlag, short metaFlags,
long size,
long ctime, long crtime, long atime, long mtime,
String md5Hash, String sha256Hash, String sha1Hash,
FileKnown knownState,
String parentPath,
String localPath,
long parentId,
String mimeType,
TskData.EncodingType encodingType,
String extension,
String ownerUid,
Long osAccountObjId) {
// TODO (AUT-1904): The parent id should be passed to AbstractContent
// through the class hierarchy contructors.
super(db, objId, dataSourceObjectId, fileSystemObjectId, TskData.TSK_FS_ATTR_TYPE_ENUM.TSK_FS_ATTR_TYPE_DEFAULT, 0,
name, TSK_DB_FILES_TYPE_ENUM.DERIVED, 0L, 0, dirType, metaType, dirFlag,
metaFlags, size, ctime, crtime, atime, mtime, (short) 0, 0, 0, md5Hash, sha256Hash, sha1Hash, knownState, parentPath, mimeType, extension, ownerUid, osAccountObjId, TskData.CollectedStatus.UNKNOWN, Collections.emptyList());
setLocalFilePath(localPath);
setEncodingType(encodingType);
}
/**
* Indicates whether or not this derived file is the root of a file system,
* always returns false.
*
* @return False.
*/
@Override
public boolean isRoot() {
return false;
}
/**
* Gets the method used to derive this file, if it has been recorded.
*
* @return Derived method or null.
*
* @throws TskCoreException if there was an error querying the case
* database.
*/
public synchronized DerivedMethod getDerivedMethod() throws TskCoreException {
if (derivedMethod == null && hasDerivedMethod == true) {
try {
derivedMethod = getSleuthkitCase().getDerivedMethod(getId());
if (derivedMethod == null) {
hasDerivedMethod = false; //do not attempt to lazy load
}
} catch (TskCoreException e) {
String msg = MessageFormat.format(bundle.getString("DerviedFile.derivedMethod.exception.msg1.text"), getId());
logger.log(Level.WARNING, msg, e);
throw new TskCoreException(msg, e);
}
}
return derivedMethod;
}
/**
* Accepts a content visitor (Visitor design pattern).
*
* @param visitor A ContentVisitor supplying an algorithm to run using this
* derived file as input.
*
* @return The output of the algorithm.
*/
@Override
public <T> T accept(SleuthkitItemVisitor<T> v) {
return v.visit(this);
}
/**
* Accepts a Sleuthkit item visitor (Visitor design pattern).
*
* @param visitor A SleuthkitItemVisitor supplying an algorithm to run using
* this derived file as input.
*
* @return The output of the algorithm.
*/
@Override
public <T> T accept(ContentVisitor<T> v) {
return v.visit(this);
}
/**
* Closes this derived file, if it was open.
*
* @throws Throwable
*/
@Override
protected void finalize() throws Throwable {
try {
close();
} finally {
super.finalize();
}
}
/**
* Provides a string representation of this derived file.
*
* @param preserveState True if state should be included in the string
* representation of this object.
*
*/
@Override
public String toString(boolean preserveState) {
return super.toString(preserveState) + "DerivedFile{" //NON-NLS
+ "derivedMethod=" + derivedMethod //NON-NLS
+ ", hasDerivedMethod=" + hasDerivedMethod //NON-NLS
+ '}';
}
/**
* A description of the method used to derive a file.
*/
public static class DerivedMethod {
private final int derivedId;
private String toolName;
private String toolVersion;
private String other;
private String rederiveDetails;
public DerivedMethod(int derivedId, String rederiveDetails) {
this.derivedId = derivedId;
this.rederiveDetails = rederiveDetails;
if (this.rederiveDetails == null) {
this.rederiveDetails = "";
}
this.toolName = "";
this.toolVersion = "";
this.other = "";
}
void setToolName(String toolName) {
this.toolName = toolName;
}
void setToolVersion(String toolVersion) {
this.toolVersion = toolVersion;
}
void setOther(String other) {
this.other = other;
}
public int getDerivedId() {
return derivedId;
}
public String getToolName() {
return toolName;
}
public String getToolVersion() {
return toolVersion;
}
public String getOther() {
return other;
}
public String getRederiveDetails() {
return rederiveDetails;
}
@Override
public String toString() {
return "DerivedMethod{" + "derived_id=" + derivedId + ", toolName=" + toolName + ", toolVersion=" + toolVersion + ", other=" + other + ", rederiveDetails=" + rederiveDetails + '}'; //NON-NLS
}
}
}
/*
* SleuthKit Java Bindings
*
* Copyright 2011-2022 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 org.sleuthkit.datamodel.TskData.FileKnown;
import org.sleuthkit.datamodel.TskData.TSK_FS_ATTR_TYPE_ENUM;
import org.sleuthkit.datamodel.TskData.TSK_FS_META_TYPE_ENUM;
import org.sleuthkit.datamodel.TskData.TSK_FS_NAME_FLAG_ENUM;
import org.sleuthkit.datamodel.TskData.TSK_FS_NAME_TYPE_ENUM;
/**
* A representation of a file system directory that has been added to a case.
*/
public class Directory extends FsContent {
/**
* Constructs a representation of a file system directory that has been
* added to a case.
*
* @param db The case database to which the file has been
* added.
* @param objId The object id of the file in the case database.
* @param dataSourceObjectId The object id of the data source for the file.
* @param fsObjId The object id of the file system to which this
* file belongs.
* @param attrType The type attribute given to the file by the
* file system.
* @param attrId The type id given to the file by the file
* system.
* @param name The name of the file.
* @param metaAddr The meta address of the file.
* @param metaSeq The meta sequence number of the file.
* @param dirType The type of the file, usually as reported in
* the name structure of the file system. May be
* set to TSK_FS_NAME_TYPE_ENUM.UNDEF.
* @param metaType The type of the file, usually as reported in
* the metadata structure of the file system. May
* be set to
* TSK_FS_META_TYPE_ENUM.TSK_FS_META_TYPE_UNDEF.
* @param dirFlag The allocated status of the file, usually as
* reported in the name structure of the file
* system.
* @param metaFlags The allocated status of the file, usually as
* reported in the metadata structure of the file
* system.
* @param size The size of the file.
* @param ctime The changed time of the file.
* @param crtime The created time of the file.
* @param atime The accessed time of the file.
* @param mtime The modified time of the file.
* @param modes The modes for the file.
* @param uid The UID for the file.
* @param gid The GID for the file.
* @param md5Hash The MD5 hash of the file, null if not yet
* calculated.
* @param sha256Hash sha256 hash of the file, or null if not present
* @param sha1Hash SHA-1 hash of the file, or null if not present
* @param knownState The known state of the file from a hash
* database lookup, null if not yet looked up.
* @param parentPath The path of the parent of the file.
* @param ownerUid UID of the file owner as found in the file
* system, can be null.
* @param osAccountObjId Obj id of the owner OS account, may be null.
*/
Directory(SleuthkitCase db,
long objId,
long dataSourceObjectId,
long fsObjId,
TSK_FS_ATTR_TYPE_ENUM attrType, int attrId,
String name,
long metaAddr, int metaSeq,
TSK_FS_NAME_TYPE_ENUM dirType, TSK_FS_META_TYPE_ENUM metaType,
TSK_FS_NAME_FLAG_ENUM dirFlag, short metaFlags,
long size,
long ctime, long crtime, long atime, long mtime,
short modes, int uid, int gid,
String md5Hash, String sha256Hash, String sha1Hash,
FileKnown knownState, String parentPath,
String ownerUid, Long osAccountObjId) {
super(db, objId, dataSourceObjectId, fsObjId, attrType, attrId, name, TskData.TSK_DB_FILES_TYPE_ENUM.FS, metaAddr, metaSeq, dirType, metaType, dirFlag, metaFlags, size, ctime, crtime, atime, mtime, modes, uid, gid, md5Hash, sha256Hash, sha1Hash, knownState, parentPath, null, null, ownerUid, osAccountObjId, TskData.CollectedStatus.UNKNOWN, Collections.emptyList());
}
/**
* Accepts a content visitor (Visitor design pattern).
*
* @param visitor A ContentVisitor supplying an algorithm to run using this
* directory as input.
*
* @return The output of the algorithm.
*/
@Override
public <T> T accept(SleuthkitItemVisitor<T> v) {
return v.visit(this);
}
/**
* Accepts a Sleuthkit item visitor (Visitor design pattern).
*
* @param visitor A SleuthkitItemVisitor supplying an algorithm to run using
* this directory as input.
*
* @return The output of the algorithm.
*/
@Override
public <T> T accept(ContentVisitor<T> v) {
return v.visit(this);
}
/**
* Provides a string representation of this directory.
*
* @param preserveState True if state should be included in the string
* representation of this object.
*
* @throws TskCoreException if there was an error querying the case
* database.
*/
@Override
public String toString(boolean preserveState) {
return super.toString(preserveState) + "Directory [\t" + "]\t"; //NON-NLS
}
/**
* Constructs a representation of a file system directory that has been
* added to a case.
*
* @param db The case database to which the file has been added.
* @param objId The object id of the file in the case database.
* @param fsObjId The object id of the file system to which this file
* belongs.
* @param attrType The type attribute given to the file by the file
* system.
* @param attrId The type id given to the file by the file system.
* @param name The name of the file.
* @param metaAddr The meta address of the file.
* @param metaSeq The meta sequence number of the file.
* @param dirType The type of the file, usually as reported in the name
* structure of the file system. May be set to
* TSK_FS_NAME_TYPE_ENUM.UNDEF.
* @param metaType The type of the file, usually as reported in the
* metadata structure of the file system. May be set to
* TSK_FS_META_TYPE_ENUM.TSK_FS_META_TYPE_UNDEF.
* @param dirFlag The allocated status of the file, usually as reported
* in the name structure of the file system.
* @param metaFlags The allocated status of the file, usually as reported
* in the metadata structure of the file system.
* @param size The size of the file.
* @param ctime The changed time of the file.
* @param crtime The created time of the file.
* @param atime The accessed time of the file.
* @param mtime The modified time of the file.
* @param modes The modes for the file.
* @param uid The UID for the file.
* @param gid The GID for the file.
* @param md5Hash The MD5 hash of the file, null if not yet calculated.
* @param knownState The known state of the file from a hash database
* lookup, null if not yet looked up.
* @param parentPath The path of the parent of the file.
*
* @deprecated Do not make subclasses outside of this package.
*/
@Deprecated
@SuppressWarnings("deprecation")
protected Directory(SleuthkitCase db,
long objId,
long fsObjId,
TSK_FS_ATTR_TYPE_ENUM attrType, short attrId,
String name,
long metaAddr, int metaSeq,
TSK_FS_NAME_TYPE_ENUM dirType, TSK_FS_META_TYPE_ENUM metaType,
TSK_FS_NAME_FLAG_ENUM dirFlag, short metaFlags,
long size,
long ctime, long crtime, long atime, long mtime,
short modes, int uid, int gid,
String md5Hash, FileKnown knownState, String parentPath) {
this(db, objId, db.getDataSourceObjectId(objId), fsObjId, attrType, attrId, name, metaAddr, metaSeq, dirType, metaType, dirFlag, metaFlags, size, ctime, crtime, atime, mtime, modes, uid, gid, md5Hash, knownState, parentPath);
}
/**
* Constructs a representation of a file system directory that has been
* added to a case. This deprecated version has attrId filed defined as a
* short which has since been changed to an int.
*
* @param db The case database to which the file has been
* added.
* @param objId The object id of the file in the case database.
* @param dataSourceObjectId The object id of the data source for the file.
* @param fsObjId The object id of the file system to which this
* file belongs.
* @param attrType The type attribute given to the file by the
* file system.
* @param attrId The type id given to the file by the file
* system.
* @param name The name of the file.
* @param metaAddr The meta address of the file.
* @param metaSeq The meta sequence number of the file.
* @param dirType The type of the file, usually as reported in
* the name structure of the file system. May be
* set to TSK_FS_NAME_TYPE_ENUM.UNDEF.
* @param metaType The type of the file, usually as reported in
* the metadata structure of the file system. May
* be set to
* TSK_FS_META_TYPE_ENUM.TSK_FS_META_TYPE_UNDEF.
* @param dirFlag The allocated status of the file, usually as
* reported in the name structure of the file
* system.
* @param metaFlags The allocated status of the file, usually as
* reported in the metadata structure of the file
* system.
* @param size The size of the file.
* @param ctime The changed time of the file.
* @param crtime The created time of the file.
* @param atime The accessed time of the file.
* @param mtime The modified time of the file.
* @param modes The modes for the file.
* @param uid The UID for the file.
* @param gid The GID for the file.
* @param md5Hash The MD5 hash of the file, null if not yet
* calculated.
* @param knownState The known state of the file from a hash
* database lookup, null if not yet looked up.
* @param parentPath The path of the parent of the file.
*
* @deprecated Do not make subclasses outside of this package.
*/
@Deprecated
@SuppressWarnings("deprecation")
Directory(SleuthkitCase db,
long objId,
long dataSourceObjectId,
long fsObjId,
TSK_FS_ATTR_TYPE_ENUM attrType, short attrId,
String name,
long metaAddr, int metaSeq,
TSK_FS_NAME_TYPE_ENUM dirType, TSK_FS_META_TYPE_ENUM metaType,
TSK_FS_NAME_FLAG_ENUM dirFlag, short metaFlags,
long size,
long ctime, long crtime, long atime, long mtime,
short modes, int uid, int gid,
String md5Hash, FileKnown knownState, String parentPath) {
this(db, objId, dataSourceObjectId, fsObjId, attrType, (int) attrId, name, metaAddr, metaSeq, dirType, metaType, dirFlag, metaFlags, size, ctime, crtime, atime, mtime, modes, uid, gid, md5Hash, null, null, knownState, parentPath, OsAccount.NO_OWNER_ID, OsAccount.NO_ACCOUNT);
}
}
/*
* SleuthKit Java Bindings
*
* Copyright 2011-2016 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.io.BufferedOutputStream;
import java.io.IOException;
import java.io.OutputStream;
/**
* Output stream wrapper for encoding files being written to disk. The idea is
* to prevent malicious files from getting extracted onto the user's hard drive
* in their original form. The encoding type used here should match the one used
* to create the derived file database entry for this file.
*/
public class EncodedFileOutputStream extends BufferedOutputStream {
private final TskData.EncodingType type;
private long encodedDataLength;
/**
* Create an encoded output stream using the specified encoding.
*
* @param out
* @param type
*
* @throws IOException
*/
public EncodedFileOutputStream(OutputStream out, TskData.EncodingType type) throws IOException {
super(out);
this.type = type;
encodedDataLength = 0;
writeHeader();
}
/**
* Create an encoded output stream using the specified encoding and buffer
* size.
*
* @param out
* @param size
* @param type
*
* @throws IOException
*/
public EncodedFileOutputStream(OutputStream out, int size, TskData.EncodingType type) throws IOException {
super(out, size);
this.type = type;
writeHeader();
}
private void writeHeader() throws IOException {
// We get the encoded header here so it will be in plaintext after encoding
write(EncodedFileUtil.getEncodedHeader(type), 0, EncodedFileUtil.getHeaderLength());
encodedDataLength -= EncodedFileUtil.getHeaderLength();
}
@Override
public void write(int b) throws IOException {
super.write((int) EncodedFileUtil.encodeByte((byte) b, type));
encodedDataLength++;
}
@Override
public void write(byte[] b,
int off,
int len)
throws IOException {
byte[] encodedData = new byte[b.length];
for (int i = 0; i < b.length; i++) {
encodedData[i] = EncodedFileUtil.encodeByte(b[i], type);
}
super.write(encodedData, off, len);
encodedDataLength += len;
}
/**
* Get the number of bytes written to the file, excluding header bytes.
* This is needed for storing the original length of the file in the
* tsk_files table in cases where we don't know the size in advance.
*
* @return the number of bytes written to the stream, excluding the header.
*/
public long getBytesWritten() {
return encodedDataLength;
}
}
\ No newline at end of file
/*
* SleuthKit Java Bindings
*
* Copyright 2011-2016 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.io.IOException;
import java.io.RandomAccessFile;
import java.util.Arrays;
/**
* Utility methods to support encoding/decoding files written to disk.
*/
class EncodedFileUtil {
final static private int HEADER_LENGTH = 32; // All headers must be this long
final static private String XOR1_HEADER = "TSK_CONTAINER_XOR1_xxxxxxxxxxxxx";
/**
* Get the header for the given encoding type.
* @param type
* @return
* @throws IOException
*/
static String getHeader(TskData.EncodingType type) throws IOException{
switch (type){
case XOR1:
return XOR1_HEADER;
default:
throw new IOException("Can not get header for " + type.toString());
}
}
/**
* Get the encoded version of the given type's header.
* Used by EncodedFileStream so that after the header is fed through the encoding
* scheme, the original plaintext header will appear at the beginning of the file.
* This should not be used for testing which encoding scheme was used on a file.
* @param type
* @return
* @throws IOException
*/
static byte [] getEncodedHeader(TskData.EncodingType type) throws IOException{
if(type.equals(TskData.EncodingType.NONE)){
throw new IOException("Can not get encoded header for " + type.toString());
}
byte [] encHeader = new byte[HEADER_LENGTH];
byte [] plainHeader = getHeader(type).getBytes();
for(int i = 0;i < HEADER_LENGTH;i++){
encHeader[i] = encodeByte(plainHeader[i], type);
}
return encHeader;
}
/**
* Returns the length of the encoded header.
* This is a fixed length to allow easier detection.
* @return
*/
static int getHeaderLength(){
return HEADER_LENGTH;
}
/**
* Encode a byte using the given encoding scheme.
* @param b
* @param type
* @return
* @throws IOException
*/
static byte encodeByte(byte b, TskData.EncodingType type) throws IOException{
switch (type){
case XOR1:
return ((byte)(b ^ 0xca));
default:
throw new IOException("Can not encode byte with encoding type " + type.toString());
}
}
/**
* Decode a byte using the given encoding scheme.
* @param b
* @param type
* @return
* @throws IOException
*/
static byte decodeByte(byte b, TskData.EncodingType type) throws IOException{
switch (type){
case XOR1:
return ((byte)(b ^ 0xca));
default:
throw new IOException("Can not decode byte with encoding type " + type.toString());
}
}
/**
* Determine whether a file was encoded and which type of encoding was used.
* @param fileHandle
* @return
* @throws IOException
*/
static TskData.EncodingType getEncoding(RandomAccessFile fileHandle){
try{
long curOffset = fileHandle.getFilePointer();
if (curOffset != 0) {
fileHandle.seek(0);
}
byte[] header = new byte[HEADER_LENGTH];
int bytesRead = fileHandle.read(header, 0, HEADER_LENGTH);
if(bytesRead != HEADER_LENGTH){
return TskData.EncodingType.NONE;
}
return(getTypeFromHeader(header));
} catch (IOException ex){
return TskData.EncodingType.NONE;
}
}
/**
* Compare the buffer containing the potential header against the encoding headers.
* @param header
* @return
*/
static private TskData.EncodingType getTypeFromHeader(byte[] header){
if(header.length != HEADER_LENGTH){
return TskData.EncodingType.NONE;
}
if(Arrays.equals(header, XOR1_HEADER.getBytes())){
return TskData.EncodingType.XOR1;
} else {
return TskData.EncodingType.NONE;
}
}
}
/*
* Sleuth Kit Data Model
*
* Copyright 2018-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;
/**
* Encapsulates the concept of an examiner associated with a case.
*/
final public class Examiner {
private final long id;
private final String loginName;
private final String displayName;
Examiner(long id, String loginName, String displayName) {
this.id = id;
this.loginName = loginName;
this.displayName = displayName;
}
/**
* Returns the id
*
* @return id
*/
public long getId() {
return id;
}
/**
* Returns the login name of examiner
*
* @return login name
*/
public String getLoginName() {
return this.loginName;
}
/**
* Returns the display name of examiner
*
* @return display name, may be a blank string
*/
public String getDisplayName() {
if (displayName == null) {
return "";
}
return this.displayName;
}
}
/*
* Sleuth Kit Data Model
*
* Copyright 2012-2018 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.Examples;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.Image;
import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.SleuthkitJNI.CaseDbHandle.AddImageProcess;
import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.datamodel.TskDataException;
/**
*
*/
public class Sample {
public static void run(String imagePath) {
try {
SleuthkitCase sk = SleuthkitCase.newCase(imagePath + ".db");
// initialize the case with an image
String timezone = "";
AddImageProcess process = sk.makeAddImageProcess(timezone, true, false, "");
ArrayList<String> paths = new ArrayList<String>();
paths.add(imagePath);
try {
process.run(UUID.randomUUID().toString(), paths.toArray(new String[paths.size()]), 0);
} catch (TskDataException ex) {
Logger.getLogger(Sample.class.getName()).log(Level.SEVERE, null, ex);
}
// print out all the images found, and their children
List<Image> images = sk.getImages();
for (Image image : images) {
System.out.println("Found image: " + image.getName());
System.out.println("There are " + image.getChildren().size() + " children.");
for (Content content : image.getChildren()) {
System.out.println('"' + content.getName() + '"' + " is a child of " + image.getName());
}
}
// print out all .txt files found
List<AbstractFile> files = sk.findAllFilesWhere("LOWER(name) LIKE LOWER('%.txt')");
for (AbstractFile file : files) {
System.out.println("Found text file: " + file.getName());
}
} catch (TskCoreException e) {
System.out.println("Exception caught: " + e.getMessage());
Sample.usage(e.getMessage());
}
}
public static void usage(String error) {
System.out.println("Usage: ant -Dimage:{image string} run-sample");
if (error.contains("deleted first")) {
System.out.println("A database for the image already exists. Delete it to run this sample again.");
} else if (error.contains("unable to open database")) {
System.out.println("Image must be encapsulated by double quotes. Ex: ant -Dimage=\"C:\\Users\\You\\image.E01\" run-sample");
}
}
public static void main(String[] args) {
Sample.run(args[0]);
}
}
/*
* SleuthKit Java Bindings
*
* Copyright 2011-2022 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;
import org.sleuthkit.datamodel.TskData.FileKnown;
import org.sleuthkit.datamodel.TskData.TSK_FS_ATTR_TYPE_ENUM;
import org.sleuthkit.datamodel.TskData.TSK_FS_META_TYPE_ENUM;
import org.sleuthkit.datamodel.TskData.TSK_FS_NAME_FLAG_ENUM;
import org.sleuthkit.datamodel.TskData.TSK_FS_NAME_TYPE_ENUM;
/**
* A representation of a file system file that has been added to a case.
*/
public class File extends FsContent {
/**
* Constructs a representation of a file system file that has been added to
* the case.
*
* @param db The case database to which the file has been
* added.
* @param objId The object id of the file in the case database.
* @param dataSourceObjectId The object id of the data source for the file.
* @param fsObjId The object id of the file system to which this
* file belongs.
* @param attrType The type attribute given to the file by the
* file system.
* @param attrId The type id given to the file by the file
* system.
* @param name The name of the file.
* @param metaAddr The meta address of the file.
* @param metaSeq The meta sequence number of the file.
* @param dirType The type of the file, usually as reported in
* the name structure of the file system. May be
* set to TSK_FS_NAME_TYPE_ENUM.UNDEF.
* @param metaType The type of the file, usually as reported in
* the metadata structure of the file system. May
* be set to
* TSK_FS_META_TYPE_ENUM.TSK_FS_META_TYPE_UNDEF.
* @param dirFlag The allocated status of the file, usually as
* reported in the name structure of the file
* system.
* @param metaFlags The allocated status of the file, usually as
* reported in the metadata structure of the file
* system.
* @param size The size of the file.
* @param ctime The changed time of the file.
* @param crtime The created time of the file.
* @param atime The accessed time of the file.
* @param mtime The modified time of the file.
* @param modes The modes for the file.
* @param uid The UID for the file.
* @param gid The GID for the file.
* @param md5Hash The MD5 hash of the file, null if not yet
* calculated.
* @param sha256Hash sha256 hash of the file, or null if not present
* @param sha1Hash SHA-1 hash of the file, or null if not present
* @param knownState The known state of the file from a hash
* database lookup, null if not yet looked up.
* @param parentPath The path of the parent of the file.
* @param mimeType The MIME type of the file, null if it has not
* yet been determined.
* @param extension The extension part of the file name (not
* including the '.'), can be null.
* @param ownerUid UID of the file owner as found in the file
* system, can be null.
* @param osAccountObjId Obj id of the owner OS account, may be null.
*/
File(SleuthkitCase db,
long objId,
long dataSourceObjectId,
long fsObjId,
TSK_FS_ATTR_TYPE_ENUM attrType, int attrId,
String name,
long metaAddr, int metaSeq,
TSK_FS_NAME_TYPE_ENUM dirType, TSK_FS_META_TYPE_ENUM metaType,
TSK_FS_NAME_FLAG_ENUM dirFlag, short metaFlags,
long size,
long ctime, long crtime, long atime, long mtime,
short modes, int uid, int gid,
String md5Hash, String sha256Hash, String sha1Hash,
FileKnown knownState, String parentPath, String mimeType,
String extension,
String ownerUid,
Long osAccountObjId,
TskData.CollectedStatus collected,
List<Attribute> fileAttributes) {
super(db, objId, dataSourceObjectId, fsObjId, attrType, attrId, name, TskData.TSK_DB_FILES_TYPE_ENUM.FS,
metaAddr, metaSeq, dirType, metaType, dirFlag, metaFlags, size, ctime, crtime, atime, mtime,
modes, uid, gid, md5Hash, sha256Hash, sha1Hash, knownState, parentPath, mimeType, extension,
ownerUid, osAccountObjId, collected, fileAttributes);
}
/**
* Accepts a content visitor (Visitor design pattern).
*
* @param visitor A ContentVisitor supplying an algorithm to run using this
* file as input.
*
* @return The output of the algorithm.
*/
@Override
public <T> T accept(SleuthkitItemVisitor<T> visitor) {
return visitor.visit(this);
}
/**
* Accepts a Sleuthkit item visitor (Visitor design pattern).
*
* @param visitor A SleuthkitItemVisitor supplying an algorithm to run using
* this file as input.
*
* @return The output of the algorithm.
*/
@Override
public <T> T accept(ContentVisitor<T> v) {
return v.visit(this);
}
/**
* Provides a string representation of this file.
*
* @param preserveState True if state should be included in the string
* representation of this object.
*
* @throws TskCoreException if there was an error querying the case
* database.
*/
@Override
public String toString(boolean preserveState) {
return super.toString(preserveState) + "File [\t" + "]\t"; //NON-NLS
}
/**
* Constructs a representation of a file system file that has been added to
* the case.
*
* @param db The case database to which the file has been added.
* @param objId The object id of the file in the case database.
* @param fsObjId The object id of the file system to which this file
* belongs.
* @param attrType The type attribute given to the file by the file
* system.
* @param attrId The type id given to the file by the file system.
* @param name The name of the file.
* @param metaAddr The meta address of the file.
* @param metaSeq The meta sequence number of the file.
* @param dirType The type of the file, usually as reported in the name
* structure of the file system. May be set to
* TSK_FS_NAME_TYPE_ENUM.UNDEF.
* @param metaType The type of the file, usually as reported in the
* metadata structure of the file system. May be set to
* TSK_FS_META_TYPE_ENUM.TSK_FS_META_TYPE_UNDEF.
* @param dirFlag The allocated status of the file, usually as reported
* in the name structure of the file system.
* @param metaFlags The allocated status of the file, usually as reported
* in the metadata structure of the file system.
* @param size The size of the file.
* @param ctime The changed time of the file.
* @param crtime The created time of the file.
* @param atime The accessed time of the file.
* @param mtime The modified time of the file.
* @param modes The modes for the file.
* @param uid The UID for the file.
* @param gid The GID for the file.
* @param md5Hash The MD5 hash of the file, null if not yet calculated.
* @param knownState The known state of the file from a hash database
* lookup, null if not yet looked up.
* @param parentPath The path of the parent of the file.
*
* @deprecated Do not make subclasses outside of this package.
*/
@Deprecated
@SuppressWarnings("deprecation")
protected File(SleuthkitCase db,
long objId,
long fsObjId,
TSK_FS_ATTR_TYPE_ENUM attrType, short attrId,
String name,
long metaAddr, int metaSeq,
TSK_FS_NAME_TYPE_ENUM dirType, TSK_FS_META_TYPE_ENUM metaType,
TSK_FS_NAME_FLAG_ENUM dirFlag, short metaFlags,
long size,
long ctime, long crtime, long atime, long mtime,
short modes, int uid, int gid,
String md5Hash, FileKnown knownState, String parentPath) {
this(db, objId, db.getDataSourceObjectId(objId), fsObjId, attrType, attrId, name, metaAddr, metaSeq, dirType, metaType, dirFlag, metaFlags, size, ctime, crtime, atime, mtime, modes, uid, gid, md5Hash, knownState, parentPath, null);
}
/**
* Constructs a representation of a file system file that has been added to
* the case. This deprecated version has attrId field defined as a short
* which has since been changed to an int.
*
* @param db The case database to which the file has been
* added.
* @param objId The object id of the file in the case database.
* @param dataSourceObjectId The object id of the data source for the file.
* @param fsObjId The object id of the file system to which this
* file belongs.
* @param attrType The type attribute given to the file by the
* file system.
* @param attrId The type id given to the file by the file
* system.
* @param name The name of the file.
* @param metaAddr The meta address of the file.
* @param metaSeq The meta sequence number of the file.
* @param dirType The type of the file, usually as reported in
* the name structure of the file system. May be
* set to TSK_FS_NAME_TYPE_ENUM.UNDEF.
* @param metaType The type of the file, usually as reported in
* the metadata structure of the file system. May
* be set to
* TSK_FS_META_TYPE_ENUM.TSK_FS_META_TYPE_UNDEF.
* @param dirFlag The allocated status of the file, usually as
* reported in the name structure of the file
* system.
* @param metaFlags The allocated status of the file, usually as
* reported in the metadata structure of the file
* system.
* @param size The size of the file.
* @param ctime The changed time of the file.
* @param crtime The created time of the file.
* @param atime The accessed time of the file.
* @param mtime The modified time of the file.
* @param modes The modes for the file.
* @param uid The UID for the file.
* @param gid The GID for the file.
* @param md5Hash The MD5 hash of the file, null if not yet
* calculated.
* @param knownState The known state of the file from a hash
* database lookup, null if not yet looked up.
* @param parentPath The path of the parent of the file.
* @param mimeType The MIME type of the file, null if it has not
* yet been determined.
*
* @deprecated Do not make subclasses outside of this package.
*/
@Deprecated
@SuppressWarnings("deprecation")
File(SleuthkitCase db, long objId, long dataSourceObjectId, long fsObjId, TSK_FS_ATTR_TYPE_ENUM attrType, short attrId,
String name, long metaAddr, int metaSeq, TSK_FS_NAME_TYPE_ENUM dirType, TSK_FS_META_TYPE_ENUM metaType,
TSK_FS_NAME_FLAG_ENUM dirFlag, short metaFlags, long size, long ctime, long crtime, long atime, long mtime,
short modes, int uid, int gid, String md5Hash, FileKnown knownState, String parentPath, String mimeType) {
this(db, objId, dataSourceObjectId, fsObjId, attrType, (int) attrId, name, metaAddr, metaSeq, dirType, metaType, dirFlag, metaFlags, size, ctime, crtime, atime, mtime, modes, uid, gid, md5Hash, null, null, knownState, parentPath, mimeType, null, OsAccount.NO_OWNER_ID, OsAccount.NO_ACCOUNT, Collections.emptyList());
}
/**
* Constructs a representation of a file system file that has been added to
* the case.
*
* @param db The case database to which the file has been
* added.
* @param objId The object id of the file in the case database.
* @param dataSourceObjectId The object id of the data source for the file.
* @param fsObjId The object id of the file system to which this
* file belongs.
* @param attrType The type attribute given to the file by the
* file system.
* @param attrId The type id given to the file by the file
* system.
* @param name The name of the file.
* @param metaAddr The meta address of the file.
* @param metaSeq The meta sequence number of the file.
* @param dirType The type of the file, usually as reported in
* the name structure of the file system. May be
* set to TSK_FS_NAME_TYPE_ENUM.UNDEF.
* @param metaType The type of the file, usually as reported in
* the metadata structure of the file system. May
* be set to
* TSK_FS_META_TYPE_ENUM.TSK_FS_META_TYPE_UNDEF.
* @param dirFlag The allocated status of the file, usually as
* reported in the name structure of the file
* system.
* @param metaFlags The allocated status of the file, usually as
* reported in the metadata structure of the file
* system.
* @param size The size of the file.
* @param ctime The changed time of the file.
* @param crtime The created time of the file.
* @param atime The accessed time of the file.
* @param mtime The modified time of the file.
* @param modes The modes for the file.
* @param uid The UID for the file.
* @param gid The GID for the file.
* @param md5Hash The MD5 hash of the file, null if not yet
* calculated.
* @param sha256Hash sha256 hash of the file, or null if not present
* @param sha1Hash SHA-1 hash of the file, or null if not present
* @param knownState The known state of the file from a hash
* database lookup, null if not yet looked up.
* @param parentPath The path of the parent of the file.
* @param mimeType The MIME type of the file, null if it has not
* yet been determined.
* @param extension The extension part of the file name (not
* including the '.'), can be null.
* @param ownerUid UID of the file owner as found in the file
* system, can be null.
* @param osAccountObjId Obj id of the owner OS account, may be null.
* @deprecated Do not make subclasses outside of this package.
*/
@Deprecated
@SuppressWarnings("deprecation")
File(SleuthkitCase db,
long objId,
long dataSourceObjectId,
long fsObjId,
TSK_FS_ATTR_TYPE_ENUM attrType, int attrId,
String name,
long metaAddr, int metaSeq,
TSK_FS_NAME_TYPE_ENUM dirType, TSK_FS_META_TYPE_ENUM metaType,
TSK_FS_NAME_FLAG_ENUM dirFlag, short metaFlags,
long size,
long ctime, long crtime, long atime, long mtime,
short modes, int uid, int gid,
String md5Hash, String sha256Hash, String sha1Hash,
FileKnown knownState, String parentPath, String mimeType,
String extension,
String ownerUid,
Long osAccountObjId,
List<Attribute> fileAttributes) {
this(db, objId, dataSourceObjectId, fsObjId, attrType, attrId, name,
metaAddr, metaSeq, dirType, metaType, dirFlag, metaFlags, size, ctime, crtime, atime, mtime,
modes, uid, gid, md5Hash, sha256Hash, sha1Hash, knownState, parentPath, mimeType, extension,
ownerUid, osAccountObjId, null, fileAttributes);
}
}
/*
* SleuthKit Java Bindings
*
* 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.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.List;
import java.util.Objects;
/**
* Utility class for file-based database queries.
*/
public class FileManager {
private final SleuthkitCase skCase;
/**
* Constructs a FileManager.
*
* @param casedb The case database.
*/
FileManager(SleuthkitCase skCase) {
this.skCase = Objects.requireNonNull(skCase, "Cannot create Blackboard for null SleuthkitCase");
}
/**
* Find all files with the exact given name and parentId.
*
* @param parentId Id of the parent folder to search.
* @param name Exact file name to match.
*
* @return A list of matching files.
*
* @throws TskCoreException
*/
public List<AbstractFile> findFilesExactName(long parentId, String name) throws TskCoreException {
String ext = SleuthkitCase.extractExtension(name);
String query = "SELECT tsk_files.* FROM tsk_files JOIN tsk_objects ON tsk_objects.obj_id = tsk_files.obj_id "
+ " WHERE tsk_objects.par_obj_id = ? AND tsk_files.name = ? ";
if (!ext.isEmpty()) {
query += " AND tsk_files.extension = ? ";
}
skCase.acquireSingleUserCaseReadLock();
try (SleuthkitCase.CaseDbConnection connection = skCase.getConnection()) {
PreparedStatement statement = connection.getPreparedStatement(query, Statement.RETURN_GENERATED_KEYS);
statement.clearParameters();
statement.setLong(1, parentId);
statement.setString(2, name);
if (!ext.isEmpty()) {
statement.setString(3, ext);
}
try (ResultSet rs = connection.executeQuery(statement)) {
return skCase.resultSetToAbstractFiles(rs, connection);
}
} catch (SQLException ex) {
throw new TskCoreException("SQLException thrown when calling query: " + query + " for parentID = " + parentId + " and name " + name, ex);
} finally {
skCase.releaseSingleUserCaseReadLock();
}
}
/**
* Find all files with the exact given name and exact parent path.
*
* @param dataSource The data source to search within.
* @param name Exact file name to match.
* @param path Exact parent path.
*
* @return A list of matching files.
*
* @throws TskCoreException
*/
public List<AbstractFile> findFilesExactNameExactPath(Content dataSource, String name, String path) throws TskCoreException {
// Database paths will always start and end with a forward slash, so add those if not present
String normalizedPath = path;
if (!normalizedPath.startsWith("/")) {
normalizedPath = "/" + normalizedPath;
}
if (!normalizedPath.endsWith("/")) {
normalizedPath = normalizedPath + "/";
}
String ext = SleuthkitCase.extractExtension(name);
String query = "";
skCase.acquireSingleUserCaseReadLock();
try (SleuthkitCase.CaseDbConnection connection = skCase.getConnection()) {
PreparedStatement statement;
if (ext.isEmpty()) {
query = "SELECT tsk_files.* FROM tsk_files JOIN tsk_objects ON tsk_objects.obj_id = tsk_files.obj_id WHERE parent_path = ? AND name = ? AND data_source_obj_id = ?";
statement = connection.getPreparedStatement(query, Statement.RETURN_GENERATED_KEYS);
statement.clearParameters();
statement.setString(1, normalizedPath);
statement.setString(2, name);
statement.setLong(3, dataSource.getId());
} else {
// This is done as an optimization since the extension column in tsk_files is indexed
query = "SELECT tsk_files.* FROM tsk_files JOIN tsk_objects ON tsk_objects.obj_id = tsk_files.obj_id WHERE extension = ? AND parent_path = ? AND name = ? AND data_source_obj_id = ?";
statement = connection.getPreparedStatement(query, Statement.RETURN_GENERATED_KEYS);
statement.clearParameters();
statement.setString(1, ext);
statement.setString(2, normalizedPath);
statement.setString(3, name);
statement.setLong(4, dataSource.getId());
}
try (ResultSet rs = connection.executeQuery(statement)) {
return skCase.resultSetToAbstractFiles(rs, connection);
}
} catch (SQLException ex) {
throw new TskCoreException("SQLException thrown when calling query: " + query + " for parent path = " + path + " and name " + name, ex);
} finally {
skCase.releaseSingleUserCaseReadLock();
}
}
}
/*
* Sleuth Kit Data Model
*
* Copyright 2011-2017 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.List;
import org.apache.commons.lang3.ArrayUtils;
/**
* Represents a file system object stored in tsk_fs_info table FileSystem has a
* parent content object (volume or image) and children content objects (files
* and directories) and fs-specific attributes. The object also maintains a
* handle to internal file-system structures and the handle is reused across
* reads.
*/
public class FileSystem extends AbstractContent {
private long imgOffset, blockSize, blockCount, rootInum,
firstInum, lastInum;
private TskData.TSK_FS_TYPE_ENUM fsType;
private Content parent;
private volatile long filesystemHandle = 0;
/**
* Constructor most inputs are from the database
*
* @param db the case handle
* @param obj_id the unique object id
* @param name filesystem name
* @param img_offset image offset
* @param fs_type filesystem type
* @param block_size block size in this fs
* @param block_count number of blocks in this fs
* @param root_inum the root inum
* @param first_inum the first inum
* @param last_inum the last inum
*/
protected FileSystem(SleuthkitCase db, long obj_id, String name, long img_offset,
TskData.TSK_FS_TYPE_ENUM fs_type, long block_size, long block_count, long root_inum,
long first_inum, long last_inum) {
super(db, obj_id, name);
this.imgOffset = img_offset;
this.fsType = fs_type;
this.blockSize = block_size;
this.blockCount = block_count;
this.rootInum = root_inum;
this.firstInum = first_inum;
this.lastInum = last_inum;
}
@Override
public void close() {
//does nothing currently, we are caching the fs handles
}
@Override
public int read(byte[] buf, long offset, long len) throws TskCoreException {
Content dataSource = getDataSource();
if (dataSource instanceof Image && ArrayUtils.isEmpty(((Image) dataSource).getPaths())) {
return 0;
}
return SleuthkitJNI.readFs(getFileSystemHandle(), buf, offset, len);
}
@Override
public long getSize() {
return blockSize * blockCount;
}
/**
* Lazily loads the internal file system structure: won't be loaded until
* this is called and maintains the handle to it to reuse it
*
* @return a filesystem pointer from the sleuthkit
*
* @throws TskCoreException exception throw if an internal tsk core error
* occurs
*/
long getFileSystemHandle() throws TskCoreException {
if (filesystemHandle == 0) {
synchronized (this) {
if (filesystemHandle == 0) {
Content dataSource = getDataSource();
if ((dataSource == null) || ( !(dataSource instanceof Image))) {
throw new TskCoreException("Data Source of File System is not an image");
}
Image image = (Image) dataSource;
// Check if this file system is in a pool
if (isPoolContent()) {
Pool pool = getPool();
if (pool == null) {
throw new TskCoreException("Error finding pool for file system");
}
Volume poolVolume = getPoolVolume();
if (poolVolume == null) {
throw new TskCoreException("File system is in a pool but has no volume");
}
filesystemHandle = SleuthkitJNI.openFsPool(image.getImageHandle(), imgOffset, pool.getPoolHandle(), poolVolume.getStart(), getSleuthkitCase());
} else {
filesystemHandle = SleuthkitJNI.openFs(image.getImageHandle(), imgOffset, getSleuthkitCase());
}
}
}
}
return this.filesystemHandle;
}
public Directory getRootDirectory() throws TskCoreException {
List<Content> children = getChildren();
if (children.size() != 1) {
throw new TskCoreException("FileSystem must have only one child.");
}
if (!(children.get(0) instanceof Directory)) {
throw new TskCoreException("Child of FileSystem must be a Directory.");
}
return (Directory) children.get(0);
}
/**
* Get the byte offset of this file system in the image
*
* @return offset
*/
public long getImageOffset() {
return imgOffset;
}
/**
* Get the file system type
*
* @return enum value of fs type
*/
public TskData.TSK_FS_TYPE_ENUM getFsType() {
return fsType;
}
/**
* Get the block size
*
* @return block size
*/
public long getBlock_size() {
return blockSize;
}
/**
* Get the number of blocks
*
* @return block count
*/
public long getBlock_count() {
return blockCount;
}
/**
* Get the inum of the root directory
*
* @return Root metadata address of the file system
*/
public long getRoot_inum() {
return rootInum;
}
/**
* Get the first inum in this file system
*
* @return first inum
*/
public long getFirst_inum() {
return firstInum;
}
/**
* Get the last inum
*
* @return last inum
*/
public long getLastInum() {
return lastInum;
}
@SuppressWarnings("deprecation")
@Override
public void finalize() throws Throwable {
try {
if (filesystemHandle != 0) {
// SleuthkitJNI.closeFs(filesystemHandle); // closeFs is currently a no-op
filesystemHandle = 0;
}
} finally {
super.finalize();
}
}
@Override
public <T> T accept(SleuthkitItemVisitor<T> v) {
return v.visit(this);
}
@Override
public <T> T accept(ContentVisitor<T> v) {
return v.visit(this);
}
@Override
public String toString(boolean preserveState) {
return super.toString(preserveState) + "FileSystem [\t" + " blockCount " + blockCount + "\t" + "blockSize " + blockSize + "\t" + "firstInum " + firstInum + "\t" + "fsType " + fsType + "\t" + "imgOffset " + imgOffset + "\t" + "lastInum " + lastInum + "\t" + "rootInum " + rootInum + "\t" + "]"; //NON-NLS
}
}
/*
* SleuthKit Java Bindings
*
* Copyright 2011-2022 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.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.apache.commons.lang3.ArrayUtils;
import org.sleuthkit.datamodel.TskData.FileKnown;
import org.sleuthkit.datamodel.TskData.TSK_DB_FILES_TYPE_ENUM;
import org.sleuthkit.datamodel.TskData.TSK_FS_ATTR_TYPE_ENUM;
import org.sleuthkit.datamodel.TskData.TSK_FS_META_TYPE_ENUM;
import org.sleuthkit.datamodel.TskData.TSK_FS_NAME_FLAG_ENUM;
import org.sleuthkit.datamodel.TskData.TSK_FS_NAME_TYPE_ENUM;
/**
* An abstract base class for representations of a file system files or
* directories that have been added to a case.
*
* TODO move common getters to AbstractFile class
*/
public abstract class FsContent extends AbstractFile {
private static final Logger logger = Logger.getLogger(FsContent.class.getName());
private List<String> metaDataText = null;
/**
*
* @deprecated Use getFileHandle instead.
*/
// TODO: Make private.
@Deprecated
protected volatile long fileHandle = 0;
/**
* Constructs an abstract base class for representations of a file system
* files or directories that have been added to a case.
*
* @param db The case database to which the file has been
* added.
* @param objId The object id of the file in the case database.
* @param dataSourceObjectId The object id of the data source for the file.
* @param fsObjId The object id of the file system to which this
* file belongs.
* @param attrType The type attribute given to the file by the
* file system.
* @param attrId The type id given to the file by the file
* system.
* @param name The name of the file.
* @param fileType The type of file
* @param metaAddr The meta address of the file.
* @param metaSeq The meta sequence number of the file.
* @param dirType The type of the file, usually as reported in
* the name structure of the file system. May be
* set to TSK_FS_NAME_TYPE_ENUM.UNDEF.
* @param metaType The type of the file, usually as reported in
* the metadata structure of the file system. May
* be set to
* TSK_FS_META_TYPE_ENUM.TSK_FS_META_TYPE_UNDEF.
* @param dirFlag The allocated status of the file, usually as
* reported in the name structure of the file
* system.
* @param metaFlags The allocated status of the file, usually as
* reported in the metadata structure of the file
* system.
* @param size The size of the file.
* @param ctime The changed time of the file.
* @param crtime The created time of the file.
* @param atime The accessed time of the file.
* @param mtime The modified time of the file.
* @param modes The modes for the file.
* @param uid The UID for the file.
* @param gid The GID for the file.
* @param md5Hash The MD5 hash of the file, null if not yet
* calculated.
* @param sha256Hash sha256 hash of the file, or null if not present
* @param sha1Hash SHA-1 hash of the file, or null if not present
* @param knownState The known state of the file from a hash
* database lookup, null if not yet looked up.
* @param parentPath The path of the parent of the file.
* @param mimeType The MIME type of the file, null if it has not
* yet been determined.
* @param extension The extension part of the file name (not
* including the '.'), can be null.
* @param ownerUid UID of the file owner as found in the file
* system, can be null.
* @param osAccountObjId Obj id of the owner OS account, may be null.
* @param collected Collected status of the file data
*/
FsContent(SleuthkitCase db,
long objId,
long dataSourceObjectId,
long fsObjId,
TSK_FS_ATTR_TYPE_ENUM attrType, int attrId,
String name,
TSK_DB_FILES_TYPE_ENUM fileType,
long metaAddr, int metaSeq,
TSK_FS_NAME_TYPE_ENUM dirType, TSK_FS_META_TYPE_ENUM metaType,
TSK_FS_NAME_FLAG_ENUM dirFlag, short metaFlags,
long size,
long ctime, long crtime, long atime, long mtime,
short modes, int uid, int gid,
String md5Hash, String sha256Hash, String sha1Hash,
FileKnown knownState,
String parentPath,
String mimeType,
String extension,
String ownerUid,
Long osAccountObjId,
TskData.CollectedStatus collected,
List<Attribute> fileAttributes) {
super(db, objId, dataSourceObjectId, Long.valueOf(fsObjId), attrType, attrId, name, fileType, metaAddr, metaSeq, dirType, metaType, dirFlag, metaFlags, size, ctime, crtime, atime, mtime, modes, uid, gid, md5Hash, sha256Hash, sha1Hash, knownState, parentPath, mimeType, extension, ownerUid, osAccountObjId, collected, fileAttributes);
}
/**
* Get the object id of the parent file system of this file or directory.
*
* @return the parent file system id
*/
public long getFileSystemId() {
return getFileSystemObjectId().orElse(0L);
}
/**
* Opens a JNI file handle for this file or directory.
*
* @throws TskCoreException if there is a problem opening the handle.
*/
@SuppressWarnings("deprecation")
void loadFileHandle() throws TskCoreException {
if (fileHandle == 0) {
synchronized (this) {
if (fileHandle == 0) {
fileHandle = SleuthkitJNI.openFile(getFileSystem().getFileSystemHandle(), metaAddr, attrType, attrId, getSleuthkitCase());
}
}
}
}
/**
* Gets the JNI file handle for this file or directory, zero if the file has
* not been opened by calling loadHandle.
*
* @return The JNI file handle.
*/
@SuppressWarnings("deprecation")
long getFileHandle() {
return fileHandle;
}
/**
* Reads bytes from this file or directory.
*
* @param buf Buffer to read into.
* @param offset Start position in the file.
* @param len Number of bytes to read.
*
* @return Number of bytes read.
*
* @throws TskCoreException if there is a problem reading the file.
*/
@Override
@SuppressWarnings("deprecation")
protected synchronized int readInt(byte[] buf, long offset, long len) throws TskCoreException {
if (offset == 0 && size == 0) {
//special case for 0-size file
return 0;
}
Content dataSource = getDataSource();
if (dataSource instanceof Image && ArrayUtils.isEmpty(((Image) dataSource).getPaths())) {
return 0;
}
loadFileHandle();
return SleuthkitJNI.readFile(fileHandle, buf, offset, len);
}
@Override
public boolean isRoot() {
try {
FileSystem fs = getFileSystem();
return fs.getRoot_inum() == this.getMetaAddr();
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, "Exception while calling 'getFileSystem' on " + this, ex); //NON-NLS
return false;
}
}
/**
* Gets the parent directory of this file or directory.
*
* @return The parent directory or null if there isn't one
*
* @throws TskCoreException if there was an error querying the case
* database.
*/
public AbstractFile getParentDirectory() throws TskCoreException {
return getSleuthkitCase().getParentDirectory(this);
}
/**
* Gets the data source (image) for this file or directory directory.
*
* @return The data source.
*
* @throws TskCoreException if there is an error querying the case database.
*/
@Override
public Content getDataSource() throws TskCoreException {
return getFileSystem().getDataSource();
}
/**
* Gets a text-based description of the file's metadata. This is the same
* content as the TSK istat tool produces and is different information for
* each type of file system.
*
* @return List of text, one element per line.
*
* @throws TskCoreException
*/
public synchronized List<String> getMetaDataText() throws TskCoreException {
if (metaDataText != null) {
return metaDataText;
}
// if there is no metadata for this file, return empty string
if (metaAddr == 0) {
metaDataText = new ArrayList<String>();
metaDataText.add("");
return metaDataText;
}
loadFileHandle();
metaDataText = SleuthkitJNI.getFileMetaDataText(fileHandle);
return metaDataText;
}
/**
* Closes the JNI file handle for this file or directory.
*/
@Override
@SuppressWarnings("deprecation")
public synchronized void close() {
if (fileHandle != 0) {
SleuthkitJNI.closeFile(fileHandle);
fileHandle = 0;
}
}
/**
* Closes the JNI file handle for this file or directory when the FsContent
* object is garbage-collected.
*/
@Override
public void finalize() throws Throwable {
try {
close();
} finally {
super.finalize();
}
}
/**
* Provides a string representation of this file or directory.
*
* @param preserveState True if state should be included in the string
* representation of this object.
*/
@Override
public String toString(boolean preserveState) {
String path = "";
try {
path = getUniquePath();
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, "Error loading unique path for object ID: {0}", this.getId());
}
return super.toString(preserveState)
+ "FsContent [\t" //NON-NLS
+ "fsObjId " + getFileSystemId() //NON-NLS
+ "\t" + "uniquePath " + path //NON-NLS
+ "\t" + "fileHandle " + getFileHandle() //NON-NLS
+ "]\t";
}
/**
* Constructs an abstract base class for representations of a file system
* files or directories that have been added to a case.
*
* @param db The case database to which the file has been added.
* @param objId The object id of the file in the case database.
* @param fsObjId The object id of the file system to which this file
* belongs.
* @param attrType The type attribute given to the file by the file
* system.
* @param attrId The type id given to the file by the file system.
* @param name The name of the file.
* @param metaAddr The meta address of the file.
* @param metaSeq The meta sequence number of the file.
* @param dirType The type of the file, usually as reported in the name
* structure of the file system. May be set to
* TSK_FS_NAME_TYPE_ENUM.UNDEF.
* @param metaType The type of the file, usually as reported in the
* metadata structure of the file system. May be set to
* TSK_FS_META_TYPE_ENUM.TSK_FS_META_TYPE_UNDEF.
* @param dirFlag The allocated status of the file, usually as reported
* in the name structure of the file system.
* @param metaFlags The allocated status of the file, usually as reported
* in the metadata structure of the file system.
* @param size The size of the file.
* @param ctime The changed time of the file.
* @param crtime The created time of the file.
* @param atime The accessed time of the file.
* @param mtime The modified time of the file.
* @param modes The modes for the file.
* @param uid The UID for the file.
* @param gid The GID for the file.
* @param md5Hash The MD5 hash of the file, null if not yet calculated.
* @param knownState The known state of the file from a hash database
* lookup, null if not yet looked up.
* @param parentPath The path of the parent of the file.
*
* @deprecated Do not make subclasses outside of this package.
*/
@Deprecated
@SuppressWarnings("deprecation")
FsContent(SleuthkitCase db, long objId, long fsObjId, TSK_FS_ATTR_TYPE_ENUM attrType, short attrId,
String name, long metaAddr, int metaSeq, TSK_FS_NAME_TYPE_ENUM dirType, TSK_FS_META_TYPE_ENUM metaType,
TSK_FS_NAME_FLAG_ENUM dirFlag, short metaFlags, long size, long ctime, long crtime, long atime, long mtime,
short modes, int uid, int gid, String md5Hash, FileKnown knownState, String parentPath) {
this(db, objId, db.getDataSourceObjectId(objId), fsObjId, attrType, (int) attrId, name, TSK_DB_FILES_TYPE_ENUM.FS, metaAddr, metaSeq, dirType, metaType, dirFlag, metaFlags, size, ctime, crtime, atime, mtime, modes, uid, gid, md5Hash, null, null, knownState, parentPath, null, null, OsAccount.NO_OWNER_ID, OsAccount.NO_ACCOUNT, Collections.emptyList());
}
/**
* Constructs an abstract base class for representations of a file system
* files or directories that have been added to a case. This deprecated
* version has attrId filed defined as a short which has since been changed
* to an int.
*
* @param db The case database to which the file has been
* added.
* @param objId The object id of the file in the case database.
* @param dataSourceObjectId The object id of the data source for the file.
* @param fsObjId The object id of the file system to which this
* file belongs.
* @param attrType The type attribute given to the file by the
* file system.
* @param attrId The type id given to the file by the file
* system.
* @param name The name of the file.
* @param metaAddr The meta address of the file.
* @param metaSeq The meta sequence number of the file.
* @param dirType The type of the file, usually as reported in
* the name structure of the file system. May be
* set to TSK_FS_NAME_TYPE_ENUM.UNDEF.
* @param metaType The type of the file, usually as reported in
* the metadata structure of the file system. May
* be set to
* TSK_FS_META_TYPE_ENUM.TSK_FS_META_TYPE_UNDEF.
* @param dirFlag The allocated status of the file, usually as
* reported in the name structure of the file
* system.
* @param metaFlags The allocated status of the file, usually as
* reported in the metadata structure of the file
* system.
* @param size The size of the file.
* @param ctime The changed time of the file.
* @param crtime The created time of the file.
* @param atime The accessed time of the file.
* @param mtime The modified time of the file.
* @param modes The modes for the file.
* @param uid The UID for the file.
* @param gid The GID for the file.
* @param md5Hash The MD5 hash of the file, null if not yet
* calculated.
* @param knownState The known state of the file from a hash
* database lookup, null if not yet looked up.
* @param parentPath The path of the parent of the file.
* @param mimeType The MIME type of the file, null if it has not
* yet been determined.
*
* @deprecated Do not make subclasses outside of this package.
*/
@Deprecated
@SuppressWarnings("deprecation")
FsContent(SleuthkitCase db, long objId, long dataSourceObjectId, long fsObjId, TSK_FS_ATTR_TYPE_ENUM attrType, short attrId,
String name, long metaAddr, int metaSeq, TSK_FS_NAME_TYPE_ENUM dirType, TSK_FS_META_TYPE_ENUM metaType,
TSK_FS_NAME_FLAG_ENUM dirFlag, short metaFlags, long size, long ctime, long crtime, long atime, long mtime,
short modes, int uid, int gid, String md5Hash, FileKnown knownState, String parentPath, String mimeType) {
this(db, objId, dataSourceObjectId, fsObjId, attrType, (int) attrId, name, TSK_DB_FILES_TYPE_ENUM.FS, metaAddr, metaSeq, dirType, metaType, dirFlag, metaFlags, size, ctime, crtime, atime, mtime, modes, uid, gid, md5Hash, null, null, knownState, parentPath, mimeType, null, OsAccount.NO_OWNER_ID, OsAccount.NO_ACCOUNT, Collections.emptyList());
}
/**
* Constructs an abstract base class for representations of a file system
* files or directories that have been added to a case.
*
* @param db The case database to which the file has been
* added.
* @param objId The object id of the file in the case database.
* @param dataSourceObjectId The object id of the data source for the file.
* @param fsObjId The object id of the file system to which this
* file belongs.
* @param attrType The type attribute given to the file by the
* file system.
* @param attrId The type id given to the file by the file
* system.
* @param name The name of the file.
* @param fileType The type of file
* @param metaAddr The meta address of the file.
* @param metaSeq The meta sequence number of the file.
* @param dirType The type of the file, usually as reported in
* the name structure of the file system. May be
* set to TSK_FS_NAME_TYPE_ENUM.UNDEF.
* @param metaType The type of the file, usually as reported in
* the metadata structure of the file system. May
* be set to
* TSK_FS_META_TYPE_ENUM.TSK_FS_META_TYPE_UNDEF.
* @param dirFlag The allocated status of the file, usually as
* reported in the name structure of the file
* system.
* @param metaFlags The allocated status of the file, usually as
* reported in the metadata structure of the file
* system.
* @param size The size of the file.
* @param ctime The changed time of the file.
* @param crtime The created time of the file.
* @param atime The accessed time of the file.
* @param mtime The modified time of the file.
* @param modes The modes for the file.
* @param uid The UID for the file.
* @param gid The GID for the file.
* @param md5Hash The MD5 hash of the file, null if not yet
* calculated.
* @param sha256Hash sha256 hash of the file, or null if not present
* @param sha1Hash SHA-1 hash of the file, or null if not present
* @param knownState The known state of the file from a hash
* database lookup, null if not yet looked up.
* @param parentPath The path of the parent of the file.
* @param mimeType The MIME type of the file, null if it has not
* yet been determined.
* @param extension The extension part of the file name (not
* including the '.'), can be null.
* @param ownerUid UID of the file owner as found in the file
* system, can be null.
* @param osAccountObjId Obj id of the owner OS account, may be null.
* @deprecated Do not make subclasses outside of this package.
*/
@Deprecated
@SuppressWarnings("deprecation")
FsContent(SleuthkitCase db,
long objId,
long dataSourceObjectId,
long fsObjId,
TSK_FS_ATTR_TYPE_ENUM attrType, int attrId,
String name,
TSK_DB_FILES_TYPE_ENUM fileType,
long metaAddr, int metaSeq,
TSK_FS_NAME_TYPE_ENUM dirType, TSK_FS_META_TYPE_ENUM metaType,
TSK_FS_NAME_FLAG_ENUM dirFlag, short metaFlags,
long size,
long ctime, long crtime, long atime, long mtime,
short modes, int uid, int gid,
String md5Hash, String sha256Hash, String sha1Hash,
FileKnown knownState,
String parentPath,
String mimeType,
String extension,
String ownerUid,
Long osAccountObjId,
List<Attribute> fileAttributes) {
this(db, objId, dataSourceObjectId, fsObjId, attrType, attrId, name, TSK_DB_FILES_TYPE_ENUM.FS, metaAddr, metaSeq, dirType, metaType, dirFlag, metaFlags, size, ctime, crtime, atime, mtime, modes, uid, gid, md5Hash, null, null, knownState, parentPath, mimeType, null, OsAccount.NO_OWNER_ID, OsAccount.NO_ACCOUNT, TskData.CollectedStatus.UNKNOWN, Collections.emptyList());
}
}
/*
* Sleuth Kit Data Model
*
* Copyright 2014 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;
/**
* Used to pass info about a hash so that it can be added into the TSK-db from
* Autopsy. HashHitInfo is for the reverse direction.
*/
public class HashEntry {
private String fileName;
private String md5Hash;
private String sha1Hash;
private String sha256Hash;
private String comment;
public HashEntry(String fileName, String md5Hash, String sha1Hash, String sha256Hash, String comment) {
this.fileName = fileName;
this.md5Hash = md5Hash;
this.sha1Hash = sha1Hash;
this.sha256Hash = sha256Hash;
this.comment = comment;
}
public String getFileName() {
return fileName;
}
public String getMd5Hash() {
return md5Hash;
}
public String getSha1Hash() {
return sha1Hash;
}
public String getSha256Hash() {
return sha256Hash;
}
public String getComment() {
return comment;
}
}
/*
* Sleuth Kit Data Model
*
* Copyright 2013 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.ArrayList;
/**
* Used to transmit hashDb information about a particular hash hit from the TSK
* lookup code to Autopsy. HashEntry is for the reverse direction (adding hashes
* to DB).
*/
public class HashHitInfo {
private String hashMd5;
private String hashSha1;
private String hashSha2_256;
private ArrayList<String> names = new ArrayList<String>();
private ArrayList<String> comments = new ArrayList<String>();
/**
* Default constructor when error message is not available
*/
public HashHitInfo(String hashMd5, String hashSha1, String hashSha2_256) {
this.hashMd5 = hashMd5;
this.hashSha1 = hashSha1;
this.hashSha2_256 = hashSha2_256;
}
/**
* Add file name associated with this hash
*
* @param name
*/
public void addName(String name) {
names.add(name);
}
/**
* Add comment associated with this hash
*
* @param comment
*/
public void addComment(String comment) {
comments.add(comment);
}
public String getHashMd5() {
return hashMd5;
}
public String getHashSha1() {
return hashSha1;
}
public String getHashSha256() {
return hashSha2_256;
}
public ArrayList<String> getNames() {
return names;
}
public ArrayList<String> getComments() {
return comments;
}
}
/*
* Sleuth Kit Data Model
*
* Copyright 2011-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;
import java.io.IOException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.Map;
import java.util.HashMap;
import java.util.Arrays;
/**
* Utility to calculate a hash for FsContent and store in TSK database
*/
public class HashUtility {
private final static int BUFFER_SIZE = 16 * 1024;
/**
* Calculate hashes of the content object.
*
* @param content The content object to hash
* @param hashTypes The types of hash to compute
*
* @return A list of the hash results
*
* @throws TskCoreException
*/
static public List<HashResult> calculateHashes(Content content, Collection<HashType> hashTypes) throws TskCoreException {
Map<HashType, MessageDigest> digests = new HashMap<>();
for (HashType type : hashTypes) {
try {
digests.put(type, MessageDigest.getInstance(type.getName()));
} catch (NoSuchAlgorithmException ex) {
throw new TskCoreException("No algorithm found matching name " + type.getName(), ex);
}
}
// Read in byte size chunks and update the hash value with the data.
byte[] data = new byte[BUFFER_SIZE];
int totalChunks = (int) Math.ceil((double) content.getSize() / (double) BUFFER_SIZE);
int read;
for (long i = 0; i < totalChunks; i++) {
try {
read = content.read(data, i * BUFFER_SIZE, BUFFER_SIZE);
} catch (TskCoreException ex) {
throw new TskCoreException("Error reading data at address " + i * BUFFER_SIZE + " from content with ID: " + content.getId(), ex);
}
// Check for EOF
if (read == -1) {
break;
}
// Only update with the read bytes.
if (read == BUFFER_SIZE) {
for (HashType type : hashTypes) {
digests.get(type).update(data);
}
} else {
byte[] subData = Arrays.copyOfRange(data, 0, read);
for (HashType type : hashTypes) {
digests.get(type).update(subData);
}
}
}
List<HashResult> results = new ArrayList<>();
for (HashType type : hashTypes) {
byte hashData[] = digests.get(type).digest();
StringBuilder sb = new StringBuilder();
for (byte b : hashData) {
sb.append(String.format("%02x", b));
}
results.add(new HashResult(type, sb.toString()));
}
return results;
}
/**
* Determines whether a string representation of an MD5 hash is valid.
*
* @param md5Hash The hash.
*
* @return True or false.
*/
public static boolean isValidMd5Hash(String md5Hash) {
return md5Hash.matches("^[A-Fa-f0-9]{32}$");
}
/**
* Determines whether a string representation of a SHA-1 hash is valid.
*
* @param sha1Hash The hash.
*
* @return True or false.
*/
public static boolean isValidSha1Hash(String sha1Hash) {
return sha1Hash.matches("^[A-Fa-f0-9]{40}$");
}
/**
* Determines whether a string representation of a SHA-256 hash is valid.
*
* @param sha256Hash The hash.
*
* @return True or false.
*/
public static boolean isValidSha256Hash(String sha256Hash) {
return sha256Hash.matches("^[A-Fa-f0-9]{64}$");
}
/**
* Determine if the passed in Hash value is that for no data (i.e. an empty
* file). Looking these values up or correlating on them causes lots of
* false positives.
*
* @param md5
*
* @return True if it is the empty hash value
*/
public static boolean isNoDataMd5(String md5) {
return md5.toLowerCase().equals("d41d8cd98f00b204e9800998ecf8427e"); //NON-NLS
}
/**
* Utility class to hold a hash value along with its type.
*/
public static class HashResult {
private final HashType type;
private final String value;
public HashResult(HashType type, String value) {
this.type = type;
this.value = value;
}
public HashType getType() {
return type;
}
public String getValue() {
return value;
}
}
/**
* Hash types that can be calculated.
*/
public enum HashType {
MD5("MD5"),
SHA256("SHA-256");
private final String name; // This should be the string expected by MessageDigest
HashType(String name) {
this.name = name;
}
String getName() {
return name;
}
}
/**
* Calculate the MD5 hash for the given FsContent and store it in the
* database
*
* @param file file object whose md5 hash we want to calculate
*
* @return md5 of the given FsContent object
*
* @throws java.io.IOException
*
* @deprecated Use calculateHashes() instead
*/
@Deprecated
static public String calculateMd5(AbstractFile file) throws IOException {
Logger logger = Logger.getLogger(HashUtility.class.getName());
String md5Hash = calculateMd5Hash(file);
try {
file.getSleuthkitCase().setMd5Hash(file, md5Hash);
} catch (TskCoreException ex) {
logger.log(Level.WARNING, "Error updating content's md5 in database", ex); //NON-NLS
}
return md5Hash;
}
/**
* Calculate the MD5 hash for the given FsContent
*
* @param content content object whose md5 hash we want to calculate
*
* @return md5 of the given FsContent object
*
* @throws java.io.IOException
*
* @deprecated Use calculateHashes() instead
*/
@Deprecated
static public String calculateMd5Hash(Content content) throws IOException {
try {
List<HashResult> results = calculateHashes(content, Arrays.asList(HashType.MD5));
return results.stream()
.filter(result -> result.getType().equals(HashType.MD5))
.findFirst().get().getValue();
} catch (TskCoreException ex) {
// Wrap in an IOException to retain the current method signature
throw new IOException(ex);
}
}
}
\ No newline at end of file
/*
* Sleuth Kit Data Model
*
* Copyright 2021-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.Objects;
/**
* Encapsulates a host.
*/
public final class Host {
private final long id;
private final String name;
private final HostDbStatus status;
Host(long id, String name) {
this(id, name, HostDbStatus.ACTIVE);
}
Host(long id, String name, HostDbStatus status) {
this.id = id;
this.name = name;
this.status = status;
}
/**
* Gets the row id for the host.
*
* @return Row id.
*/
public long getHostId() {
return id;
}
/**
* Gets the name for the host.
*
* @return Host name.
*/
public String getName() {
return name;
}
/**
* Gets the status for the host.
*
* @return Host status.
*/
HostDbStatus getStatus() {
return status;
}
@Override
public int hashCode() {
int hash = 5;
hash = 67 * hash + (int) (this.id ^ (this.id >>> 32));
hash = 67 * hash + Objects.hashCode(this.name);
return hash;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final Host other = (Host) obj;
if (this.id != other.id) {
return false;
}
if ((this.name == null) ? (other.name != null) : !this.name.equals(other.name)) {
return false;
}
return true;
}
/**
* Encapsulates status of host row.
*/
enum HostDbStatus {
ACTIVE(0, "Active"),
MERGED(1, "Merged"),
DELETED(2, "Deleted");
private final int id;
private final String name;
HostDbStatus(int id, String name) {
this.id = id;
this.name = name;
}
int getId() {
return id;
}
String getName() {
return name;
}
static HostDbStatus fromID(int typeId) {
for (HostDbStatus type : HostDbStatus.values()) {
if (type.ordinal() == typeId) {
return type;
}
}
return null;
}
}
}
/*
* 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.Objects;
/**
* Abstracts an address associated with a host. A host may have multiple
* addressed of different types associated with it at a given time.
*/
public final class HostAddress extends AbstractContent {
private final SleuthkitCase sleuthkitCase;
private final long id;
private final HostAddressType addressType;
private final String address;
/**
* Create a HostAddress object.
*
* @param skCase Case the host address belongs to.
* @param id Id of the host address in the database.
* @param type Type of host address.
* @param address The host address value.
*/
HostAddress(SleuthkitCase skCase, long id, HostAddressType type, String address) {
super(skCase, id, address + "(" + type.getName() + ")");
this.sleuthkitCase = skCase;
this.id = id;
this.addressType = type;
this.address = address;
}
@Override
public long getId() {
return id;
}
public HostAddressType getAddressType() {
return addressType;
}
public String getAddress() {
return address;
}
@Override
public int hashCode() {
int hash = 7;
hash = 53 * hash + (int) (this.id ^ (this.id >>> 32));
hash = 53 * hash + Objects.hashCode(this.addressType);
hash = 53 * hash + Objects.hashCode(this.address);
return hash;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final HostAddress other = (HostAddress) obj;
if (this.id != other.id) {
return false;
}
if (this.addressType != other.addressType) {
return false;
}
if ((this.address == null) ? (other.address != null) : !this.address.equals(other.address)) {
return false;
}
return true;
}
/**
* Gets the SleuthKit case database for this account.
*
* @return The SleuthKit case object.
*/
@Override
public SleuthkitCase getSleuthkitCase() {
return sleuthkitCase;
}
@Override
public int read(byte[] buf, long offset, long len) throws TskCoreException {
// No data to read.
return 0;
}
@Override
public void close() {
// Nothing to close
}
@Override
public long getSize() {
return 0;
}
@Override
public <T> T accept(ContentVisitor<T> v) {
// TODO
throw new UnsupportedOperationException("Not supported yet.");
}
@Override
public <T> T accept(SleuthkitItemVisitor<T> v) {
// TODO
throw new UnsupportedOperationException("Not supported yet.");
}
/**
* A host may have different types of addresses at a given point in time.
*/
public enum HostAddressType {
DNS_AUTO(0, "DNS Auto Detection"), // Used to auto-select the DNS type from HOSTNAME, IPV4, and IPV6 when creating HostAddresses
HOSTNAME(1, "Host Name"),
IPV4(2, "IPv4"),
IPV6(3, "IPv6"),
ETHERNET_MAC(4, "Ethernet MAC"),
WIFI_MAC(5, "WiFi MAC"),
BLUETOOTH_MAC(6, "BlueTooth MAC");
private final int id;
private final String name;
HostAddressType(int id, String name) {
this.id = id;
this.name = name;
}
public int getId() {
return id;
}
String getName() {
return name;
}
public static HostAddressType fromID(int typeId) {
for (HostAddressType type : HostAddressType.values()) {
if (type.ordinal() == typeId) {
return type;
}
}
return null;
}
}
}
/*
* 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 com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Pattern;
import org.sleuthkit.datamodel.SleuthkitCase.CaseDbConnection;
import org.sleuthkit.datamodel.HostAddress.HostAddressType;
/**
* Responsible for creating/updating/retrieving host addresses.
*/
public class HostAddressManager {
private static final Logger LOGGER = Logger.getLogger(HostAddressManager.class.getName());
private final SleuthkitCase db;
private final static byte DEFAULT_MAPPING_CACHE_VALUE = 1;
/**
* An HostAddress Object Id entry is maintained in this cache when a
* hostaddress and ip mapping is added. This is here to improve the
* performance of {@link #hostNameAndIpMappingExists(long) } check.
*/
private final Cache<Long, Byte> recentHostNameAndIpMappingCache = CacheBuilder.newBuilder().maximumSize(200000).build();
/**
* Recently added or accessed Host Address Objects are cached. This is
* here to improve performance of the
* {@link #hostAddressExists(org.sleuthkit.datamodel.HostAddress.HostAddressType, java.lang.String)}
* check as well as the {@link #getHostAddress(org.sleuthkit.datamodel.HostAddress.HostAddressType, java.lang.String) }
*/
private final Cache<String, HostAddress> recentHostAddressCache = CacheBuilder.newBuilder().maximumSize(200000).build();
/**
* Recently added host address usage is cached. This is intended to improve
* the performance of {@link #addUsage(org.sleuthkit.datamodel.Content, org.sleuthkit.datamodel.HostAddress) }
* Key: DatasourceId # Host Id # Content Id. Value has no significance. it will be set to true if there is
* a value in cache for the key.
*/
private final Cache<String, Boolean> hostAddressUsageCache = CacheBuilder.newBuilder().maximumSize(200000).build();
/**
* Construct a HostAddressManager for the given SleuthkitCase.
*
* @param skCase The SleuthkitCase
*
*/
HostAddressManager(SleuthkitCase skCase) {
this.db = skCase;
}
/**
* Gets an address record with given type and address.
*
* @param type Address type.
* @param address Address.
*
* @return Matching address.
*
* @throws TskCoreException
*/
public Optional<HostAddress> getHostAddress(HostAddress.HostAddressType type, String address) throws TskCoreException {
db.acquireSingleUserCaseReadLock();
try (CaseDbConnection connection = this.db.getConnection()) {
return HostAddressManager.this.getHostAddress(type, address, connection);
} finally {
db.releaseSingleUserCaseReadLock();
}
}
/**
* Gets an address record with given type and address.
*
* @param type Address type.
* @param address Address.
* @param connection Connection to use for DB operation.
*
* @return Matching address.
*
* @throws TskCoreException
*/
private Optional<HostAddress> getHostAddress(HostAddress.HostAddressType type, String address, CaseDbConnection connection) throws TskCoreException {
HostAddress hostAddress = recentHostAddressCache.getIfPresent(createRecentHostAddressKey(type, address));
if (Objects.nonNull(hostAddress)) {
return Optional.of(hostAddress);
}
HostAddress.HostAddressType addressType = type;
if (type.equals(HostAddress.HostAddressType.DNS_AUTO)) {
addressType = getDNSType(address);
}
String normalizedAddress = getNormalizedAddress(address);
String queryString = "SELECT * FROM tsk_host_addresses"
+ " WHERE address = ? AND address_type = ?";
try {
PreparedStatement query = connection.getPreparedStatement(queryString, Statement.NO_GENERATED_KEYS);
query.clearParameters();
query.setString(1, normalizedAddress.toLowerCase());
query.setInt(2, addressType.getId());
try (ResultSet rs = query.executeQuery()) {
if (!rs.next()) {
return Optional.empty(); // no match found
} else {
HostAddress newHostAddress = new HostAddress(db, rs.getLong("id"), HostAddressType.fromID(rs.getInt("address_type")), rs.getString("address"));
recentHostAddressCache.put(createRecentHostAddressKey(newHostAddress.getAddressType(), normalizedAddress), newHostAddress);
return Optional.of(newHostAddress);
}
}
} catch (SQLException ex) {
throw new TskCoreException(String.format("Error getting host address with type = %s and address = %s", type.getName(), address), ex);
}
}
/**
* Create a key string for use as a cache key.
*
* @param type Address type.
* @param address Address.
*
* @return Cache key defined as typeId + # + address lowercased.
*/
private String createRecentHostAddressKey(HostAddressType type, String address) {
return createRecentHostAddressKey(type.getId(), address);
}
/**
* Create a key string for use as a cache key.
*
* @param typeId Address type Id.
* @param address Address.
*
* @return Cache key defined as typeId + # + address lowercased.
*/
private String createRecentHostAddressKey(int typeId, String address) {
return typeId + "#" + address.toLowerCase();
}
/**
* Add a new address with the given type and address. If the address already
* exists in the database, the existing entry will be returned.
*
* @param type Address type.
* @param address Address (case-insensitive).
*
* @return HostAddress
*
* @throws TskCoreException
*/
public HostAddress newHostAddress(HostAddress.HostAddressType type, String address) throws TskCoreException {
db.acquireSingleUserCaseWriteLock();
CaseDbConnection connection = this.db.getConnection();
try {
return HostAddressManager.this.newHostAddress(type, address, connection);
} catch (TskCoreException ex) {
// The insert may have failed because the HostAddress already exists, so
// try loading it from the database.
Optional<HostAddress> hostAddress = HostAddressManager.this.getHostAddress(type, address, connection);
if (hostAddress.isPresent()) {
return hostAddress.get();
}
throw ex;
} finally {
connection.close();
db.releaseSingleUserCaseWriteLock();
}
}
/**
* Insert a row in the tsk_host_addresses with the given type and address.
*
* @param type Address type.
* @param address Address.
* @param connection Database connection to use.
*
* @return HostAddress.
*
* @throws TskCoreException
*/
private HostAddress newHostAddress(HostAddress.HostAddressType type, String address, CaseDbConnection connection) throws TskCoreException {
HostAddress.HostAddressType addressType = type;
if (type.equals(HostAddress.HostAddressType.DNS_AUTO)) {
addressType = getDNSType(address);
}
String normalizedAddress = getNormalizedAddress(address);
try {
// TODO: need to get the correct parent obj id.
long parentObjId = 0;
int objTypeId = TskData.ObjectType.HOST_ADDRESS.getObjectType();
long objId = db.addObject(parentObjId, objTypeId, connection);
String hostAddressInsertSQL = "INSERT INTO tsk_host_addresses(id, address_type, address) VALUES (?, ?, ?)"; // NON-NLS
PreparedStatement preparedStatement = connection.getPreparedStatement(hostAddressInsertSQL, Statement.RETURN_GENERATED_KEYS);
preparedStatement.clearParameters();
preparedStatement.setLong(1, objId);
preparedStatement.setInt(2, addressType.getId());
preparedStatement.setString(3, normalizedAddress.toLowerCase());
connection.executeUpdate(preparedStatement);
HostAddress hostAddress = new HostAddress(db, objId, addressType, normalizedAddress);
recentHostAddressCache.put(createRecentHostAddressKey(addressType, normalizedAddress), hostAddress);
return hostAddress;
} catch (SQLException ex) {
throw new TskCoreException(String.format("Error adding host address of type = %s, with address = %s", type.getName(), address), ex);
}
}
/**
* Add a host to address mapping.
*
* @param host Host.
* @param hostAddress Address.
* @param time Time at which the mapping was valid.
* @param source Content from where this mapping was derived.
*
* @throws TskCoreException
*/
public void assignHostToAddress(Host host, HostAddress hostAddress, Long time, Content source) throws TskCoreException {
String insertSQL = db.getInsertOrIgnoreSQL(" INTO tsk_host_address_map(host_id, addr_obj_id, source_obj_id, time) "
+ " VALUES(?, ?, ?, ?) ");
db.acquireSingleUserCaseWriteLock();
try (CaseDbConnection connection = this.db.getConnection()) {
PreparedStatement preparedStatement = connection.getPreparedStatement(insertSQL, Statement.NO_GENERATED_KEYS);
preparedStatement.clearParameters();
preparedStatement.setLong(1, host.getHostId());
preparedStatement.setLong(2, hostAddress.getId());
preparedStatement.setLong(3, source.getId());
if (time != null) {
preparedStatement.setLong(4, time);
} else {
preparedStatement.setNull(4, java.sql.Types.BIGINT);
}
connection.executeUpdate(preparedStatement);
} catch (SQLException ex) {
LOGGER.log(Level.SEVERE, null, ex);
throw new TskCoreException(String.format("Error adding host address mapping for host name = %s, with address = %s", host.getName(), hostAddress.getAddress()), ex);
} finally {
db.releaseSingleUserCaseWriteLock();
}
}
/**
* Get all the addresses that have been assigned to the given host.
*
* @param host Host to get addresses for.
*
* @return List of addresses, may be empty.
*/
public List<HostAddress> getHostAddressesAssignedTo(Host host) throws TskCoreException {
String queryString = "SELECT addr_obj_id FROM tsk_host_address_map "
+ " WHERE host_id = " + host.getHostId();
List<HostAddress> addresses = new ArrayList<>();
db.acquireSingleUserCaseReadLock();
try (CaseDbConnection connection = this.db.getConnection();
Statement s = connection.createStatement();
ResultSet rs = connection.executeQuery(s, queryString)) {
while (rs.next()) {
addresses.add(HostAddressManager.this.getHostAddress(rs.getLong("addr_obj_id"), connection));
}
return addresses;
} catch (SQLException ex) {
throw new TskCoreException(String.format("Error getting host addresses for host " + host.getName()), ex);
} finally {
db.releaseSingleUserCaseReadLock();
}
}
/**
* Gets an address for the given object id.
*
* @param id Object id.
*
* @return The corresponding HostAddress object.
*
* @throws TskCoreException
*/
public HostAddress getHostAddress(long id) throws TskCoreException {
db.acquireSingleUserCaseReadLock();
try (CaseDbConnection connection = this.db.getConnection()) {
return HostAddressManager.this.getHostAddress(id, connection);
} finally {
db.releaseSingleUserCaseReadLock();
}
}
/**
* Gets an address for the given object id.
*
* @param id Id of the host address.
* @param connection Current connection
*
* @return The corresponding HostAddress.
*
* @throws TskCoreException
*/
private HostAddress getHostAddress(long id, CaseDbConnection connection) throws TskCoreException {
String queryString = "SELECT * FROM tsk_host_addresses"
+ " WHERE id = " + id;
try (Statement s = connection.createStatement();
ResultSet rs = connection.executeQuery(s, queryString)) {
if (!rs.next()) {
throw new TskCoreException(String.format("No address found with id = %d", id));
} else {
long objId = rs.getLong("id");
int type = rs.getInt("address_type");
String address = rs.getString("address");
HostAddress hostAddress = new HostAddress(db, objId, HostAddress.HostAddressType.fromID(type), address);
recentHostAddressCache.put(createRecentHostAddressKey(type, address), hostAddress);
return hostAddress;
}
} catch (SQLException ex) {
throw new TskCoreException(String.format("Error getting host address with id = %d", id), ex);
}
}
/**
* Adds a row to the ipAddress table.
*
* @param dnsNameAddress The DNS name.
* @param ipAddress An IP address associated with the DNS name.
* @param time Timestamp when this relationship was true.
* @param source The source.
*
* @throws TskCoreException
*/
public void addHostNameAndIpMapping(HostAddress dnsNameAddress, HostAddress ipAddress, Long time, Content source) throws TskCoreException {
db.acquireSingleUserCaseWriteLock();
try (CaseDbConnection connection = this.db.getConnection()) {
addHostNameAndIpMapping(dnsNameAddress, ipAddress, time, source, connection);
} catch (SQLException ex) {
throw new TskCoreException(String.format("Error adding host DNS address mapping for DNS name = %s, and IP address = %s", dnsNameAddress.getAddress(), ipAddress.getAddress()), ex);
} finally {
db.releaseSingleUserCaseWriteLock();
}
}
/**
* Adds a row to the host address dns ip map table.
*
* @param dnsNameAddress The DNS name.
* @param ipAddress An IP address associated with the DNS name.
* @param time Timestamp when this relationship was true.
* @param source The source.
* @param caseDbTransaction The transaction in the scope of which the
* operation is to be performed, managed by the
* caller. Null is not permitted.
*
* @throws TskCoreException
*/
public void addHostNameAndIpMapping(HostAddress dnsNameAddress, HostAddress ipAddress, Long time, Content source, final SleuthkitCase.CaseDbTransaction caseDbTransaction) throws TskCoreException {
if (Objects.isNull(caseDbTransaction)) {
throw new TskCoreException(String.format("Error adding host DNS address mapping for DNS name = %s, and IP address = %s, null caseDbTransaction passed to addHostNameAndIpMapping", dnsNameAddress.getAddress(), ipAddress.getAddress()));
}
try {
addHostNameAndIpMapping(dnsNameAddress, ipAddress, time, source, caseDbTransaction.getConnection());
} catch (SQLException ex) {
throw new TskCoreException(String.format("Error adding host DNS address mapping for DNS name = %s, and IP address = %s", dnsNameAddress.getAddress(), ipAddress.getAddress()), ex);
}
}
/**
* Adds a row to the host address dns ip map table.
*
* @param dnsNameAddress The DNS name.
* @param ipAddress An IP address associated with the DNS name.
* @param time Timestamp when this relationship was true.
* @param source The source.
* @param connection The db connection. Null is not permitted.
*
* @throws TskCoreException
*/
private void addHostNameAndIpMapping(HostAddress dnsNameAddress, HostAddress ipAddress, Long time, Content source, final CaseDbConnection connection) throws SQLException, TskCoreException {
if (dnsNameAddress.getAddressType() != HostAddress.HostAddressType.HOSTNAME) {
throw new TskCoreException("IllegalArguments passed to addHostNameAndIpMapping: A host name address is expected.");
}
if ((ipAddress.getAddressType() != HostAddress.HostAddressType.IPV4) && (ipAddress.getAddressType() != HostAddress.HostAddressType.IPV6)) {
throw new TskCoreException("IllegalArguments passed to addHostNameAndIpMapping:An IPv4/IPv6 address is expected.");
}
if (Objects.isNull(connection)) {
throw new TskCoreException("IllegalArguments passed to addHostNameAndIpMapping: null connection passed to addHostNameAndIpMapping");
}
String insertSQL = db.getInsertOrIgnoreSQL(" INTO tsk_host_address_dns_ip_map(dns_address_id, ip_address_id, source_obj_id, time) "
+ " VALUES(?, ?, ?, ?) ");
PreparedStatement preparedStatement = connection.getPreparedStatement(insertSQL, Statement.NO_GENERATED_KEYS);
preparedStatement.clearParameters();
preparedStatement.setLong(1, dnsNameAddress.getId());
preparedStatement.setLong(2, ipAddress.getId());
preparedStatement.setLong(3, source.getId());
if (time != null) {
preparedStatement.setLong(4, time);
} else {
preparedStatement.setNull(4, java.sql.Types.BIGINT);
}
connection.executeUpdate(preparedStatement);
recentHostNameAndIpMappingCache.put(ipAddress.getId(), DEFAULT_MAPPING_CACHE_VALUE);
recentHostNameAndIpMappingCache.put(dnsNameAddress.getId(), DEFAULT_MAPPING_CACHE_VALUE);
}
/**
* Returns true if addressObjectId is used as either IP or host name
* <br>
* <b>Note:</b> This api call uses a database connection. Do not invoke
* within a transaction.
*
* @param addressObjectId
*
* @return
*
* @throws TskCoreException
*/
public boolean hostNameAndIpMappingExists(long addressObjectId) throws TskCoreException {
Byte isPresent = recentHostNameAndIpMappingCache.getIfPresent(addressObjectId);
if (Objects.nonNull(isPresent)) {
return true;
}
String queryString = "SELECT count(*) as mappingCount FROM tsk_host_address_dns_ip_map WHERE ip_address_id = ? OR dns_address_id = ? ";
db.acquireSingleUserCaseReadLock();
try (CaseDbConnection connection = this.db.getConnection();
PreparedStatement ps = connection.getPreparedStatement(queryString, Statement.NO_GENERATED_KEYS);) {
ps.clearParameters();
ps.setLong(1, addressObjectId);
ps.setLong(2, addressObjectId);
try (ResultSet rs = ps.executeQuery()) {
if (!rs.next()) {
return false;
} else {
boolean status = rs.getLong("mappingCount") > 0;
if (status) {
recentHostNameAndIpMappingCache.put(addressObjectId, DEFAULT_MAPPING_CACHE_VALUE);
}
return status;
}
}
} catch (SQLException ex) {
throw new TskCoreException("Error looking up host address / Ip mapping for address = " + addressObjectId, ex);
} finally {
db.releaseSingleUserCaseReadLock();
}
}
/**
* Returns ObjectId of HostAddress if it exists.
* <br>
* <b>Note:</b> This api call uses a database connection. Do not invoke
* within a transaction.
*
* @param type
* @param address
*
* @return
*
* @throws TskCoreException
*/
public Optional<Long> hostAddressExists(HostAddress.HostAddressType type, String address) throws TskCoreException {
HostAddress hostAddress = recentHostAddressCache.getIfPresent(createRecentHostAddressKey(type, address));
if (Objects.nonNull(hostAddress)) {
return Optional.of(hostAddress.getId());
}
HostAddress.HostAddressType addressType = type;
if (type.equals(HostAddress.HostAddressType.DNS_AUTO)) {
addressType = getDNSType(address);
}
String normalizedAddress = getNormalizedAddress(address);
String queryString = "SELECT id, address_type, address FROM tsk_host_addresses"
+ " WHERE address = ? AND address_type = ?";
db.acquireSingleUserCaseReadLock();
try (CaseDbConnection connection = this.db.getConnection();
PreparedStatement query = connection.getPreparedStatement(queryString, Statement.NO_GENERATED_KEYS);) {
query.clearParameters();
query.setString(1, normalizedAddress.toLowerCase());
query.setInt(2, addressType.getId());
try (ResultSet rs = query.executeQuery()) {
if (!rs.next()) {
return Optional.empty(); // no match found
} else {
long objId = rs.getLong("id");
int addrType = rs.getInt("address_type");
String addr = rs.getString("address");
HostAddress hostAddr = new HostAddress(db, objId, HostAddress.HostAddressType.fromID(addrType), addr);
recentHostAddressCache.put(createRecentHostAddressKey(addrType, normalizedAddress), hostAddr);
return Optional.of(objId);
}
}
} catch (SQLException ex) {
throw new TskCoreException(String.format("Error getting host address with type = %s and address = %s", type.getName(), address), ex);
} finally {
db.releaseSingleUserCaseReadLock();
}
}
/**
* Gets the IP addresses for a given HOSTNAME name.
*
* @param hostname HOSTNAME name to look for.
*
* @return List of IP Addresses mapped to this dns name. May be empty.
*
* @throws TskCoreException
*/
public List<HostAddress> getIpAddress(String hostname) throws TskCoreException {
String queryString = "SELECT ip_address_id FROM tsk_host_address_dns_ip_map as map "
+ " JOIN tsk_host_addresses as addresses "
+ " ON map.dns_address_id = addresses.id "
+ " WHERE addresses.address_type = " + HostAddress.HostAddressType.HOSTNAME.getId()
+ " AND addresses.address = ?";
db.acquireSingleUserCaseReadLock();
try (CaseDbConnection connection = this.db.getConnection()) {
List<HostAddress> IpAddresses = new ArrayList<>();
PreparedStatement query = connection.getPreparedStatement(queryString, Statement.NO_GENERATED_KEYS);
query.clearParameters();
query.setString(1, hostname.toLowerCase());
try (ResultSet rs = query.executeQuery()) {
while (rs.next()) {
long ipAddressObjId = rs.getLong("ip_address_id");
IpAddresses.add(HostAddressManager.this.getHostAddress(ipAddressObjId, connection));
recentHostNameAndIpMappingCache.put(ipAddressObjId, DEFAULT_MAPPING_CACHE_VALUE);
}
return IpAddresses;
}
} catch (SQLException ex) {
throw new TskCoreException(String.format("Error getting host addresses for host name: " + hostname), ex);
} finally {
db.releaseSingleUserCaseReadLock();
}
}
/**
* Gets the host names for a given IP address.
*
* @param ipAddress IP address to look for.
*
* @return All corresponding host names.
*
* @throws TskCoreException
*/
List<HostAddress> getHostNameByIp(String ipAddress) throws TskCoreException {
String queryString = "SELECT dns_address_id FROM tsk_host_address_dns_ip_map as map "
+ " JOIN tsk_host_addresses as addresses "
+ " ON map.ip_address_id = addresses.id "
+ " WHERE ( addresses.address_type = " + HostAddress.HostAddressType.IPV4.getId()
+ " OR addresses.address_type = " + HostAddress.HostAddressType.IPV6.getId() + ")"
+ " AND addresses.address = ?";
db.acquireSingleUserCaseReadLock();
try (CaseDbConnection connection = this.db.getConnection()) {
List<HostAddress> dnsNames = new ArrayList<>();
PreparedStatement query = connection.getPreparedStatement(queryString, Statement.NO_GENERATED_KEYS);
query.clearParameters();
query.setString(1, ipAddress.toLowerCase());
try (ResultSet rs = query.executeQuery()) {
while (rs.next()) {
long dnsAddressId = rs.getLong("dns_address_id");
dnsNames.add(HostAddressManager.this.getHostAddress(dnsAddressId, connection));
recentHostNameAndIpMappingCache.put(dnsAddressId, DEFAULT_MAPPING_CACHE_VALUE);
}
return dnsNames;
}
} catch (SQLException ex) {
throw new TskCoreException(String.format("Error getting host addresses for IP address: " + ipAddress), ex);
} finally {
db.releaseSingleUserCaseReadLock();
}
}
/**
* Associate the given artifact with a HostAddress.
*
* @param content The content/item using the address.
* @param hostAddress The host address.
*/
public void addUsage(Content content, HostAddress hostAddress) throws TskCoreException {
String key = content.getDataSource().getId() + "#" + hostAddress.getId() + "#" + content.getId();
Boolean cachedValue = hostAddressUsageCache.getIfPresent(key);
if (null != cachedValue) {
return;
}
final String insertSQL = db.getInsertOrIgnoreSQL(" INTO tsk_host_address_usage(addr_obj_id, obj_id, data_source_obj_id) "
+ " VALUES(" + hostAddress.getId() + ", " + content.getId() + ", " + content.getDataSource().getId() + ") ");
db.acquireSingleUserCaseWriteLock();
try (CaseDbConnection connection = this.db.getConnection();
Statement s = connection.createStatement()) {
connection.executeUpdate(s, insertSQL);
hostAddressUsageCache.put(key, true);
} catch (SQLException ex) {
throw new TskCoreException(String.format("Error associating host address %s with artifact with id %d", hostAddress.getAddress(), content.getId()), ex);
} finally {
db.releaseSingleUserCaseWriteLock();
}
}
private final String ADDRESS_USAGE_QUERY = "SELECT addresses.id as id, addresses.address_type as address_type, addresses.address as address "
+ " FROM tsk_host_address_usage as usage "
+ " JOIN tsk_host_addresses as addresses "
+ " ON usage.addr_obj_id = addresses.id ";
/**
* Get all the addresses that have been used by the given content.
*
* @param content Content to get addresses used for.
*
* @return List of addresses, may be empty.
*
* @throws TskCoreException
*/
public List<HostAddress> getHostAddressesUsedByContent(Content content) throws TskCoreException {
String queryString = ADDRESS_USAGE_QUERY
+ " WHERE usage.obj_id = " + content.getId();
return getHostAddressesUsed(queryString);
}
/**
* Get all the addresses that have been used by the given data source.
*
* @param dataSource Data source to get addresses used for.
*
* @return List of addresses, may be empty.
*
* @throws TskCoreException
*/
public List<HostAddress> getHostAddressesUsedOnDataSource(Content dataSource) throws TskCoreException {
String queryString = ADDRESS_USAGE_QUERY
+ " WHERE usage.data_source_obj_id = " + dataSource.getId();
return getHostAddressesUsed(queryString);
}
/**
* Gets the host addresses used by running the given query.
*
* @param addressesUsedSQL SQL query to run.
*
* @return List of addresses, may be empty.
*
* @throws TskCoreException
*/
private List<HostAddress> getHostAddressesUsed(String addressesUsedSQL) throws TskCoreException {
List<HostAddress> addressesUsed = new ArrayList<>();
db.acquireSingleUserCaseReadLock();
try (CaseDbConnection connection = this.db.getConnection();
Statement s = connection.createStatement();
ResultSet rs = connection.executeQuery(s, addressesUsedSQL)) {
while (rs.next()) {
addressesUsed.add(new HostAddress(db, rs.getLong("id"), HostAddress.HostAddressType.fromID(rs.getInt("address_type")), rs.getString("address")));
}
return addressesUsed;
} catch (SQLException ex) {
throw new TskCoreException(String.format("Error getting host addresses used with query string = %s", addressesUsedSQL), ex);
} finally {
db.releaseSingleUserCaseReadLock();
}
}
/**
* Detects format of address.
*
* @param address The address.
*
* @return The detected type.
*/
private HostAddress.HostAddressType getDNSType(String address) {
if (isIPv4(address)) {
return HostAddress.HostAddressType.IPV4;
} else if (isIPv6(address)) {
return HostAddress.HostAddressType.IPV6;
} else {
return HostAddress.HostAddressType.HOSTNAME;
}
}
private static final Pattern IPV4_PATTERN =
Pattern.compile("^(([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])(\\.(?!$)|$)){4}$");
/**
* Test if an address is IPv4.
*
* @param ipAddress The address.
*
* @return true if it is IPv4 format, false otherwise.
*/
private static boolean isIPv4(String ipAddress) {
if (ipAddress != null) {
return IPV4_PATTERN.matcher(ipAddress).matches();
}
return false;
}
// IPV6 address examples:
// Standard: 684D:1111:222:3333:4444:5555:6:77
// Compressed: 1234:fd2:5621:1:89::4500
// With zone/interface specifier: fe80::1ff:fe23:4567:890a%eth2
// fe80::1ff:fe23:4567:890a%3
private static final Pattern IPV6_STD_PATTERN =
Pattern.compile("^(?:[0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}(%.+)?$");
private static final Pattern IPV6_HEX_COMPRESSED_PATTERN =
Pattern.compile("^((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?)::((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?)(%.+)?$");
private static boolean isIPv6StdAddress(final String input) {
return IPV6_STD_PATTERN.matcher(input).matches();
}
private static boolean isIPv6HexCompressedAddress(final String input) {
return IPV6_HEX_COMPRESSED_PATTERN.matcher(input).matches();
}
/**
* Test if an address is IPv6.
*
* @param ipAddress The address.
*
* @return true if it is IPv6 format, false otherwise.
*/
private static boolean isIPv6(String ipAddress) {
if (ipAddress != null) {
return isIPv6StdAddress(ipAddress) || isIPv6HexCompressedAddress(ipAddress);
}
return false;
}
/**
* Normalizes an address.
*
* It intentionally does NOT convert to lowercase so that the case may be
* preserved, and only converted where needed.
*
* @param address
*
* @return Normalized address.
*/
private static String getNormalizedAddress(String address) {
String normalizedAddress = address;
if (isIPv6(address)) {
normalizedAddress = getNormalizedIPV6Address(address);
}
return normalizedAddress;
}
/**
* Normalize an IPv6 address:
* - removing the zone/interface specifier if one exists.
*
* It intentionally does NOT convert to lowercase so that the case may be
* preserved, and only converted where needed.
*
* @param address Address to normalize.
*
* @return Normalized IPv6 address.
*/
private static String getNormalizedIPV6Address(String address) {
String normalizedAddress = address;
if ( normalizedAddress.contains("%") ) {
normalizedAddress = normalizedAddress.substring(0, normalizedAddress.indexOf("%"));
}
return normalizedAddress;
}
}
/*
* Sleuth Kit Data Model
*
* 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.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.datamodel;
import com.google.common.base.Strings;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Savepoint;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
import org.sleuthkit.datamodel.Host.HostDbStatus;
import org.sleuthkit.datamodel.SleuthkitCase.CaseDbConnection;
import org.sleuthkit.datamodel.SleuthkitCase.CaseDbTransaction;
import org.sleuthkit.datamodel.TskEvent.HostsUpdatedTskEvent;
import org.sleuthkit.datamodel.TskEvent.HostsDeletedTskEvent;
/**
* Responsible for creating/updating/retrieving Hosts.
*/
public final class HostManager {
private final SleuthkitCase db;
/**
* Construct a HostManager for the given SleuthkitCase.
*
* @param skCase The SleuthkitCase
*
*/
HostManager(SleuthkitCase skCase) {
this.db = skCase;
}
/**
* Create a host with specified name. If a host already exists with the
* given name, it returns the existing host.
*
* @param name Host name.
*
* @return Host with the specified name.
*
* @throws TskCoreException
*/
public Host newHost(String name) throws TskCoreException {
CaseDbTransaction transaction = db.beginTransaction();
try {
Host host = newHost(name, transaction);
transaction.commit();
transaction = null;
return host;
} finally {
if (transaction != null) {
transaction.rollback();
}
}
}
/**
* Create a host with given name. If the host already exists, the existing
* host will be returned.
*
* NOTE: Whenever possible, create hosts as part of a single step
* transaction so that it can quickly determine a host of the same name
* already exists. If you call this as part of a multi-step
* CaseDbTransaction, then this method may think it can insert the host
* name, but then when it comes time to call CaseDbTransaction.commit(),
* there could be a uniqueness constraint violation and other inserts in the
* same transaction could have problems.
*
* This method should never be made public and exists only because we need
* to support APIs that do not take in a host and we must make one. Ensure
* that if you call this method that the host name you give will be unique.
*
* @param name Host name that must be unique if this is called as part of a
* multi-step transaction
* @param trans Database transaction to use.
*
* @return Newly created host.
*
* @throws TskCoreException
*/
Host newHost(String name, CaseDbTransaction trans) throws TskCoreException {
// must have a name
if (Strings.isNullOrEmpty(name)) {
throw new TskCoreException("Illegal argument passed to createHost: Host name is required.");
}
CaseDbConnection connection = trans.getConnection();
Savepoint savepoint = null;
try {
savepoint = connection.getConnection().setSavepoint();
String hostInsertSQL = "INSERT INTO tsk_hosts(name) VALUES (?)"; // NON-NLS
PreparedStatement preparedStatement = connection.getPreparedStatement(hostInsertSQL, Statement.RETURN_GENERATED_KEYS);
preparedStatement.clearParameters();
preparedStatement.setString(1, name);
connection.executeUpdate(preparedStatement);
// Read back the row id
Host host = null;
try (ResultSet resultSet = preparedStatement.getGeneratedKeys();) {
if (resultSet.next()) {
host = new Host(resultSet.getLong(1), name); //last_insert_rowid()
} else {
throw new SQLException("Error executing " + hostInsertSQL);
}
}
if (host != null) {
trans.registerAddedHost(host);
}
return host;
} catch (SQLException ex) {
if (savepoint != null) {
try {
connection.getConnection().rollback(savepoint);
} catch (SQLException ex2) {
throw new TskCoreException(String.format("Error adding host with name = %s and unable to rollback", name), ex);
}
}
// It may be the case that the host already exists, so try to get it.
Optional<Host> optHost = getHostByName(name, connection);
if (optHost.isPresent()) {
return optHost.get();
}
throw new TskCoreException(String.format("Error adding host with name = %s", name), ex);
}
}
/**
* Updates the name of the provided host.
*
* @param host The host to be updated.
* @param newName The new name of the host.
*
* @return The updated host.
*
* @throws TskCoreException
*/
public Host updateHostName(Host host, String newName) throws TskCoreException {
if (host == null) {
throw new TskCoreException("Illegal argument passed to updateHost: No host argument provided.");
} else if (newName == null) {
throw new TskCoreException(String.format("Illegal argument passed to updateHost: Host with id %d has no name", host.getHostId()));
}
long hostId = host.getHostId();
Host updatedHost = null;
db.acquireSingleUserCaseWriteLock();
try (CaseDbConnection connection = db.getConnection()) {
// Don't update the name for non-active hosts
String hostInsertSQL = "UPDATE tsk_hosts "
+ "SET name = "
+ " CASE WHEN db_status = " + Host.HostDbStatus.ACTIVE.getId() + " THEN ? ELSE name END "
+ "WHERE id = ?";
PreparedStatement preparedStatement = connection.getPreparedStatement(hostInsertSQL, Statement.RETURN_GENERATED_KEYS);
preparedStatement.clearParameters();
preparedStatement.setString(1, newName);
preparedStatement.setLong(2, hostId);
connection.executeUpdate(preparedStatement);
updatedHost = getHostById(hostId, connection).orElseThrow(()
-> new TskCoreException((String.format("Error while fetching newly updated host with id: %d, "))));
} catch (SQLException ex) {
throw new TskCoreException(String.format("Error updating host with name = %s", newName), ex);
} finally {
db.releaseSingleUserCaseWriteLock();
}
if (updatedHost != null) {
fireChangeEvent(updatedHost);
}
return updatedHost;
}
/**
* Delete a host. Name comparison is case-insensitive.
*
* @param name Name of the host to delete.
*
* @return The id of the deleted host or null if no host was deleted.
*
* @throws TskCoreException
*/
public Long deleteHost(String name) throws TskCoreException {
if (name == null) {
throw new TskCoreException("Illegal argument passed to deleteHost: Name provided must be non-null");
}
// query to check if there are any dependencies on this host. If so, don't delete.
String queryString = "SELECT COUNT(*) AS count FROM\n"
+ "(SELECT obj_id AS id, host_id FROM data_source_info\n"
+ "UNION\n"
+ "SELECT id, scope_host_id AS host_id FROM tsk_os_account_realms\n"
+ "UNION\n"
+ "SELECT id, host_id FROM tsk_os_account_attributes\n"
+ "UNION\n"
+ "SELECT id, host_id FROM tsk_host_address_map) children\n"
+ "INNER JOIN tsk_hosts h ON children.host_id = h.id WHERE LOWER(h.name)=LOWER(?)";
String deleteString = "DELETE FROM tsk_hosts WHERE LOWER(name) = LOWER(?)";
CaseDbTransaction trans = this.db.beginTransaction();
try {
// check if host has any child data sources. if so, don't delete and throw exception.
PreparedStatement query = trans.getConnection().getPreparedStatement(queryString, Statement.NO_GENERATED_KEYS);
query.clearParameters();
query.setString(1, name);
try (ResultSet queryResults = query.executeQuery()) {
if (queryResults.next() && queryResults.getLong("count") > 0) {
throw new TskCoreException(String.format("Host with name '%s' has child data and cannot be deleted.", name));
}
}
// otherwise, delete the host
PreparedStatement update = trans.getConnection().getPreparedStatement(deleteString, Statement.RETURN_GENERATED_KEYS);
update.clearParameters();
update.setString(1, name);
int numUpdated = update.executeUpdate();
// get ids for deleted.
Long hostId = null;
if (numUpdated > 0) {
try (ResultSet updateResult = update.getGeneratedKeys()) {
if (updateResult.next()) {
hostId = updateResult.getLong(1);
}
}
}
trans.commit();
trans = null;
fireDeletedEvent(new Host(hostId, name));
return hostId;
} catch (SQLException ex) {
throw new TskCoreException(String.format("Error deleting host with name %s", name), ex);
} finally {
if (trans != null) {
trans.rollback();
}
}
}
/**
* Get all data sources associated with a given host.
*
* @param host The host.
*
* @return The list of data sources corresponding to the host.
*
* @throws TskCoreException
*/
public List<DataSource> getDataSourcesForHost(Host host) throws TskCoreException {
String queryString = "SELECT * FROM data_source_info WHERE host_id = " + host.getHostId();
List<DataSource> dataSources = new ArrayList<>();
db.acquireSingleUserCaseReadLock();
try (CaseDbConnection connection = this.db.getConnection();
Statement s = connection.createStatement();
ResultSet rs = connection.executeQuery(s, queryString)) {
while (rs.next()) {
dataSources.add(db.getDataSource(rs.getLong("obj_id")));
}
return dataSources;
} catch (SQLException | TskDataException ex) {
throw new TskCoreException(String.format("Error getting data sources for host %s", host.getName()), ex);
} finally {
db.releaseSingleUserCaseReadLock();
}
}
/**
* Get active host with given name.
*
* @param name Host name to look for.
*
* @return Optional with host. Optional.empty if no matching host is found.
*
* @throws TskCoreException
*/
public Optional<Host> getHostByName(String name) throws TskCoreException {
try (CaseDbConnection connection = db.getConnection()) {
return getHostByName(name, connection);
}
}
/**
* Get active host with given name.
*
* @param name Host name to look for.
* @param connection Database connection to use.
*
* @return Optional with host. Optional.empty if no matching host is found.
*
* @throws TskCoreException
*/
private Optional<Host> getHostByName(String name, CaseDbConnection connection) throws TskCoreException {
String queryString = "SELECT * FROM tsk_hosts"
+ " WHERE LOWER(name) = LOWER(?)"
+ " AND db_status = " + Host.HostDbStatus.ACTIVE.getId();
db.acquireSingleUserCaseReadLock();
try {
PreparedStatement s = connection.getPreparedStatement(queryString, Statement.RETURN_GENERATED_KEYS);
s.clearParameters();
s.setString(1, name);
try (ResultSet rs = s.executeQuery()) {
if (!rs.next()) {
return Optional.empty(); // no match found
} else {
return Optional.of(new Host(rs.getLong("id"), rs.getString("name"), Host.HostDbStatus.fromID(rs.getInt("db_status"))));
}
}
} catch (SQLException ex) {
throw new TskCoreException(String.format("Error getting host with name = %s", name), ex);
} finally {
db.releaseSingleUserCaseReadLock();
}
}
/**
* Get host with the given id.
*
* @param id The id of the host.
*
* @return Optional with host. Optional.empty if no matching host is found.
*
* @throws TskCoreException
*/
public Optional<Host> getHostById(long id) throws TskCoreException {
try (CaseDbConnection connection = db.getConnection()) {
return getHostById(id, connection);
}
}
/**
* Get host with given id.
*
* @param id The id of the host.
* @param connection Database connection to use.
*
* @return Optional with host. Optional.empty if no matching host is found.
*
* @throws TskCoreException
*/
private Optional<Host> getHostById(long id, CaseDbConnection connection) throws TskCoreException {
String queryString = "SELECT * FROM tsk_hosts WHERE id = " + id;
db.acquireSingleUserCaseReadLock();
try (Statement s = connection.createStatement();
ResultSet rs = connection.executeQuery(s, queryString)) {
if (rs.next()) {
return Optional.of(new Host(rs.getLong("id"), rs.getString("name"), Host.HostDbStatus.fromID(rs.getInt("db_status"))));
} else {
return Optional.empty();
}
} catch (SQLException ex) {
throw new TskCoreException(String.format("Error getting host with id: " + id), ex);
} finally {
db.releaseSingleUserCaseReadLock();
}
}
/**
* Get all hosts that have a status of ACTIVE.
*
* @return Collection of hosts that have ACTIVE status.
*
* @throws TskCoreException
*/
public List<Host> getAllHosts() throws TskCoreException {
String queryString = "SELECT * FROM tsk_hosts WHERE db_status = " + HostDbStatus.ACTIVE.getId();
List<Host> hosts = new ArrayList<>();
db.acquireSingleUserCaseReadLock();
try (CaseDbConnection connection = this.db.getConnection();
Statement s = connection.createStatement();
ResultSet rs = connection.executeQuery(s, queryString)) {
while (rs.next()) {
hosts.add(new Host(rs.getLong("id"), rs.getString("name"), Host.HostDbStatus.fromID(rs.getInt("db_status"))));
}
return hosts;
} catch (SQLException ex) {
throw new TskCoreException(String.format("Error getting hosts"), ex);
} finally {
db.releaseSingleUserCaseReadLock();
}
}
/**
* Get host for the given data source.
*
* @param dataSource The data source to look up the host for.
*
* @return The host for this data source (will not be null).
*
* @throws TskCoreException if no host is found or an error occurs.
*/
public Host getHostByDataSource(DataSource dataSource) throws TskCoreException {
return getHostByDataSource(dataSource.getId());
}
/**
* Get host for the given data source ID.
*
* @param dataSourceId The data source ID to look up the host for.
*
* @return The host for this data source (will not be null).
*
* @throws TskCoreException if no host is found or an error occurs.
*/
Host getHostByDataSource(long dataSourceId) throws TskCoreException {
String queryString = "SELECT tsk_hosts.id AS hostId, tsk_hosts.name AS name, tsk_hosts.db_status AS db_status FROM \n"
+ "tsk_hosts INNER JOIN data_source_info \n"
+ "ON tsk_hosts.id = data_source_info.host_id \n"
+ "WHERE data_source_info.obj_id = " + dataSourceId;
db.acquireSingleUserCaseReadLock();
try (CaseDbConnection connection = this.db.getConnection();
Statement s = connection.createStatement();
ResultSet rs = connection.executeQuery(s, queryString)) {
if (!rs.next()) {
throw new TskCoreException(String.format("Host not found for data source with ID = %d", dataSourceId));
} else {
return new Host(rs.getLong("hostId"), rs.getString("name"), Host.HostDbStatus.fromID(rs.getInt("db_status")));
}
} catch (SQLException ex) {
throw new TskCoreException(String.format("Error getting host for data source with ID = %d", dataSourceId), ex);
} finally {
db.releaseSingleUserCaseReadLock();
}
}
/**
* Merge source host into destination host. When complete: - All realms will
* have been moved into the destination host or merged with existing realms
* in the destination host. - All references to the source host will be
* updated to reference the destination host. - The source host will be
* updated so that it will no longer be returned by any methods apart from
* get by host id.
*
* @param sourceHost The source host.
* @param destHost The destination host.
*
* @throws TskCoreException
*/
public void mergeHosts(Host sourceHost, Host destHost) throws TskCoreException {
String query = "";
CaseDbTransaction trans = null;
try {
trans = db.beginTransaction();
// Merge or move any realms associated with the source host
List<OsAccountRealm> realms = db.getOsAccountRealmManager().getRealmsByHost(sourceHost, trans.getConnection());
for (OsAccountRealm realm : realms) {
db.getOsAccountRealmManager().moveOrMergeRealm(realm, destHost, trans);
}
try (Statement s = trans.getConnection().createStatement()) {
// Update references to the source host
// tsk_host_address_map has a unique constraint on host_id, addr_obj_id, time,
// so delete any rows that would be duplicates.
query = "DELETE FROM tsk_host_address_map "
+ "WHERE id IN ( "
+ "SELECT "
+ " sourceMapRow.id "
+ "FROM "
+ " tsk_host_address_map destMapRow "
+ "INNER JOIN tsk_host_address_map sourceMapRow ON destMapRow.addr_obj_id = sourceMapRow.addr_obj_id AND destMapRow.time = sourceMapRow.time "
+ "WHERE destMapRow.host_id = " + destHost.getHostId()
+ " AND sourceMapRow.host_id = " + sourceHost.getHostId() + " )";
s.executeUpdate(query);
query = makeOsAccountUpdateQuery("tsk_host_address_map", "host_id", sourceHost, destHost);
s.executeUpdate(query);
query = makeOsAccountUpdateQuery("tsk_os_account_attributes", "host_id", sourceHost, destHost);
s.executeUpdate(query);
query = makeOsAccountUpdateQuery("data_source_info", "host_id", sourceHost, destHost);
s.executeUpdate(query);
// Mark the source host as merged and change the name to a random string.
String mergedName = makeMergedHostName();
query = "UPDATE tsk_hosts SET merged_into = " + destHost.getHostId()
+ ", db_status = " + Host.HostDbStatus.MERGED.getId()
+ ", name = '" + mergedName + "' "
+ " WHERE id = " + sourceHost.getHostId();
s.executeUpdate(query);
}
trans.commit();
trans = null;
// Fire events for updated and deleted hosts
fireChangeEvent(sourceHost);
fireDeletedEvent(destHost);
} catch (SQLException ex) {
throw new TskCoreException("Error executing query: " + query, ex);
} finally {
if (trans != null) {
trans.rollback();
}
}
}
/**
* Create the query to update the host id column to the merged host.
*
* @param tableName Name of table to update.
* @param columnName Name of the column containing the host id.
* @param sourceHost The source host.
* @param destHost The destination host.
*
* @return The query.
*/
private String makeOsAccountUpdateQuery(String tableName, String columnName, Host sourceHost, Host destHost) {
return "UPDATE " + tableName + " SET " + columnName + " = " + destHost.getHostId() + " WHERE " + columnName + " = " + sourceHost.getHostId();
}
/**
* Create a random name for hosts that have been merged.
*
* @return The random signature.
*/
private String makeMergedHostName() {
return "MERGED " + UUID.randomUUID().toString();
}
/**
* Fires an event that a host has changed. Do not call this with an open
* transaction.
*
* @param newValue The new value for the host.
*/
private void fireChangeEvent(Host newValue) {
db.fireTSKEvent(new HostsUpdatedTskEvent(Collections.singletonList(newValue)));
}
/**
* Fires an event that a host has been deleted. Do not call this with an
* open transaction.
*
* @param deleted The deleted host.
*/
private void fireDeletedEvent(Host deleted) {
db.fireTSKEvent(new HostsDeletedTskEvent(Collections.singletonList(deleted.getHostId())));
}
}
/*
* Sleuth Kit Data Model
*
* Copyright 2011-2018 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.text.MessageFormat;
import java.util.ResourceBundle;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.io.File;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
/**
* Represents a disk image file, stored in tsk_image_info. Populated based on
* data in database.
*
* Caches internal tsk image handle and reuses it for reads
*/
public class Image extends AbstractContent implements DataSource {
//data about image
private final long type, ssize;
private long size;
private final String[] paths;
private volatile long imageHandle = 0;
private volatile Host host = null;
private final String deviceId, timezone;
private String md5, sha1, sha256;
private static ResourceBundle bundle = ResourceBundle.getBundle("org.sleuthkit.datamodel.Bundle");
private static final Logger LOGGER = Logger.getLogger(Image.class.getName());
/**
* Create a disk image.
*
* Note: Most inputs originate from the database.
*
* @param db Case database.
* @param obj_id Object ID.
* @param type Image type.
* @param ssize Sector size.
* @param name Display name.
* @param paths Image paths.
* @param timezone Timezone.
* @param md5 MD5 hash.
*
* @throws TskCoreException
*
* @deprecated Use the constructor that takes a device ID and size.
*/
@Deprecated
protected Image(SleuthkitCase db, long obj_id, long type, long ssize, String name, String[] paths, String timezone, String md5) throws TskCoreException {
super(db, obj_id, name);
this.deviceId = "";
this.type = type;
this.ssize = ssize;
this.paths = paths;
this.timezone = timezone;
this.size = 0;
this.md5 = md5;
this.sha1 = "";
this.sha256 = "";
}
/**
* Create a disk image.
*
* Note: Most inputs originate from the database.
*
* @param db Case database.
* @param obj_id Object ID.
* @param type Image type.
* @param deviceId Device ID.
* @param ssize Sector size.
* @param name Display name.
* @param paths Image paths.
* @param timezone Timezone.
* @param md5 MD5 hash.
* @param size Size.
*/
Image(SleuthkitCase db, long obj_id, long type, String deviceId, long ssize, String name, String[] paths, String timezone,
String md5, String sha1, String sha256, long size) throws TskCoreException {
super(db, obj_id, name);
this.deviceId = deviceId;
this.type = type;
this.ssize = ssize;
this.paths = paths;
this.timezone = timezone;
this.size = size;
this.md5 = md5;
this.sha1 = sha1;
this.sha256 = sha256;
}
/**
* Get the handle to the sleuthkit image info object
*
* @return the object pointer
*
* @throws TskCoreException
*/
public synchronized long getImageHandle() throws TskCoreException {
if (paths.length == 0) {
throw new TskCoreException("Image has no associated paths");
}
if (imageHandle == 0) {
imageHandle = SleuthkitJNI.openImage(paths, (int)ssize, getSleuthkitCase());
}
return imageHandle;
}
synchronized void setImageHandle(long imageHandle) {
this.imageHandle = imageHandle;
}
@Override
public Content getDataSource() {
return this;
}
@Override
public void close() {
//frees nothing, as we are caching image handles
}
@SuppressWarnings("deprecation")
@Override
public void finalize() throws Throwable {
try {
if (imageHandle != 0) {
// SleuthkitJNI.closeImg(imageHandle); // closeImg is currently a no-op
imageHandle = 0;
}
} finally {
super.finalize();
}
}
@Override
public int read(byte[] buf, long offset, long len) throws TskCoreException {
// If there are no paths, don't attempt to read the image
if (paths.length == 0) {
return 0;
}
// read from the image
return SleuthkitJNI.readImg(getImageHandle(), buf, offset, len);
}
@Override
public long getSize() {
if (size == 0) {
try {
if (paths.length > 0) {
//should always had at least one path
size = SleuthkitJNI.findDeviceSize(paths[0]);
}
} catch (TskCoreException ex) {
Logger.getLogger(Image.class.getName()).log(Level.SEVERE, "Could not find image size, image: " + this.getId(), ex); //NON-NLS
}
}
return size;
}
//Methods for retrieval of meta-data attributes
/**
* Get the image type
*
* @return image type
*/
public TskData.TSK_IMG_TYPE_ENUM getType() {
return TskData.TSK_IMG_TYPE_ENUM.valueOf(type);
}
/**
* Get the sector size
*
* @return sector size
*/
public long getSsize() {
return ssize;
}
@Override
public String getUniquePath() throws TskCoreException {
return "/img_" + getName(); //NON-NLS
}
/**
* Get the image path
*
* @return image path
*/
public String[] getPaths() {
return paths;
}
/**
* @return a list of VolumeSystem associated with this Image.
*
* @throws TskCoreException
*/
public List<VolumeSystem> getVolumeSystems() throws TskCoreException {
List<Content> children = getChildren();
List<VolumeSystem> vs = new ArrayList<VolumeSystem>();
for (Content child : children) {
if (child instanceof VolumeSystem) {
vs.add((VolumeSystem) child);
}
}
return vs;
}
/**
* @return a list of Volume associated with this Image.
*
* @throws TskCoreException
*/
public List<Volume> getVolumes() throws TskCoreException {
List<Content> children = getChildren();
List<Volume> volumes = new ArrayList<Volume>();
for (Content child : children) {
if (child instanceof Volume) {
volumes.add((Volume) child);
}
}
return volumes;
}
/**
* @return a list of FileSystems in this Image. This includes FileSystems
* that are both children of this Image as well as children of
* Volumes in this image.
*
* @throws TskCoreException
*/
public List<FileSystem> getFileSystems() throws TskCoreException {
List<FileSystem> fs = new ArrayList<>();
fs.addAll(getSleuthkitCase().getImageFileSystems(this));
return fs;
}
/**
* Get the timezone set for the image
*
* @return timezone string representation
*/
@Override
public String getTimeZone() {
return timezone;
}
@Override
public <T> T accept(SleuthkitItemVisitor<T> v) {
return v.visit(this);
}
@Override
public <T> T accept(ContentVisitor<T> v) {
return v.visit(this);
}
@Override
public List<Content> getChildren() throws TskCoreException {
return getSleuthkitCase().getImageChildren(this);
}
@Override
public List<Long> getChildrenIds() throws TskCoreException {
return getSleuthkitCase().getImageChildrenIds(this);
}
@Override
public String toString(boolean preserveState) {
return super.toString(preserveState) + "Image [\t" + "\t" + "paths " + Arrays.toString(paths) + "\t" + "size " + size + "\t" + "ssize " + ssize + "\t" + "timezone " + timezone + "\t" + "type " + type + "]\t"; //NON-NLS
}
/**
* Test if the file that created this image exists on disk. Does not work on
* local disks - will always return false
*
* @return True if the file still exists
*/
public Boolean imageFileExists() {
if (paths.length > 0) {
File imageFile = new File(paths[0]);
return imageFile.exists();
}
return false;
}
/**
* Perform some sanity checks on the bounds of the image contents to
* determine if we could be missing some pieces of the image.
*
* @return String of error messages to display to user or empty string if
* there are no errors
*/
public String verifyImageSize() {
Logger logger1 = Logger.getLogger("verifyImageSizes"); //NON-NLS
String errorString = "";
try {
List<VolumeSystem> volumeSystems = getVolumeSystems();
for (VolumeSystem vs : volumeSystems) {
List<Volume> volumes = vs.getVolumes();
for (Volume v : volumes) {
byte[] buf = new byte[512];
long endOffset = (v.getStart() + v.getLength()) * 512 - 512;
try {
int readBytes = read(buf, endOffset, 512);
if (readBytes < 0) {
logger1.log(Level.WARNING, "Possible Incomplete Image: Error reading volume at offset {0}", endOffset); //NON-NLS
errorString = MessageFormat.format(bundle.getString("Image.verifyImageSize.errStr1.text"), endOffset);
}
} catch (TskCoreException ex) {
logger1.log(Level.WARNING, "Possible Incomplete Image: Error reading volume at offset {0}: {1}", new Object[]{endOffset, ex.getLocalizedMessage()}); //NON-NLS
errorString = MessageFormat.format(bundle.getString("Image.verifyImageSize.errStr2.text"), endOffset);
}
}
}
List<FileSystem> fileSystems = getFileSystems();
for (FileSystem fs : fileSystems) {
long block_size = fs.getBlock_size();
long endOffset = fs.getImageOffset() + fs.getSize() - block_size;
try {
byte[] buf = new byte[(int) block_size];
int readBytes = read(buf, endOffset, block_size);
if (readBytes < 0) {
logger1.log(Level.WARNING, "Possible Incomplete Image: Error reading file system at offset {0}", endOffset); //NON-NLS
errorString = MessageFormat.format(bundle.getString("Image.verifyImageSize.errStr3.text"), endOffset);
}
} catch (TskCoreException ex) {
logger1.log(Level.WARNING, "Possible Incomplete Image: Error reading file system at offset {0}: {1}", new Object[]{endOffset, ex.getLocalizedMessage()}); //NON-NLS
errorString = MessageFormat.format(bundle.getString("Image.verifyImageSize.errStr4.text"), endOffset);
}
}
} catch (TskException ex) {
// do nothing if we got an exception from trying to get file systems and volume systems
}
return errorString;
}
/**
* Gets the md5 hash value
*
* @return md5 hash if attained(from database), empty string otherwise
*
* @throws TskCoreException
*/
public String getMd5() throws TskCoreException {
if (md5 == null || md5.isEmpty()) {
md5 = getSleuthkitCase().getMd5ImageHash(this);
}
return md5;
}
/**
* gets the SHA1 hash value
*
* @return SHA1 hash if attained(from database), empty string otherwise
*
* @throws TskCoreException on DB error.
*/
public String getSha1() throws TskCoreException {
if (sha1 == null || sha1.isEmpty()) {
sha1 = getSleuthkitCase().getSha1ImageHash(this);
}
return sha1;
}
/**
* gets the SHA256 hash value
*
* @return SHA256 hash if attained(from database), empty string otherwise
*
* @throws TskCoreException
*/
public String getSha256() throws TskCoreException {
if (sha256 == null || sha256.isEmpty()) {
sha256 = getSleuthkitCase().getSha256ImageHash(this);
}
return sha256;
}
/**
*
* @param md5
* @throws TskCoreException On DB errors
* @throws TskDataException If hash has already been set
*/
public void setMD5(String md5) throws TskCoreException, TskDataException {
if (getMd5().isEmpty() == false) {
throw new TskDataException("MD5 value has already been set");
}
getSleuthkitCase().setMd5ImageHash(this, md5);
this.md5 = md5;
}
/**
*
* @param sha1
* @throws TskCoreException On DB errors
* @throws TskDataException If hash has already been set
*/
public void setSha1(String sha1) throws TskCoreException, TskDataException {
if (getSha1().isEmpty() == false) {
throw new TskDataException("SHA1 value has already been set");
}
getSleuthkitCase().setSha1ImageHash(this, sha1);
this.sha1 = sha1;
}
/**
*
* @param sha256
* @throws TskCoreException On DB errors
* @throws TskDataException If hash has already been set
*/
public void setSha256(String sha256) throws TskCoreException, TskDataException {
if (getSha256().isEmpty() == false) {
throw new TskDataException("SHA256 value has already been set");
}
getSleuthkitCase().setSha256ImageHash(this, sha256);
this.sha256 = sha256;
}
/**
* Gets the ASCII-printable identifier for the device associated with the
* data source. This identifier is intended to be unique across multiple
* cases (e.g., a UUID).
*
* @return The device id.
*/
@Override
public String getDeviceId() {
return deviceId;
}
/**
* Set the name for this data source.
*
* @param newName The new name for the data source
*
* @throws TskCoreException Thrown if an error occurs while updating the database
*/
@Override
public void setDisplayName(String newName) throws TskCoreException {
this.getSleuthkitCase().setImageName(newName, getId());
}
/**
* Gets the size of the contents of the data source in bytes. This size can
* change as archive files within the data source are expanded, files are
* carved, etc., and is different from the size of the data source as
* returned by Content.getSize, which is the size of the data source as a
* file.
*
* @param sleuthkitCase The sleuthkit case instance from which to make calls
* to the database.
*
* @return The size in bytes.
*
* @throws TskCoreException Thrown when there is an issue trying to retrieve
* data from the database.
*/
@Override
public long getContentSize(SleuthkitCase sleuthkitCase) throws TskCoreException {
SleuthkitCase.CaseDbConnection connection;
Statement statement = null;
ResultSet resultSet = null;
long contentSize = 0;
connection = sleuthkitCase.getConnection();
try {
statement = connection.createStatement();
resultSet = connection.executeQuery(statement, "SELECT SUM (size) FROM tsk_image_info WHERE tsk_image_info.obj_id = " + getId());
if (resultSet.next()) {
contentSize = resultSet.getLong("sum");
}
} catch (SQLException ex) {
throw new TskCoreException(String.format("There was a problem while querying the database for size data for object ID %d.", getId()), ex);
} finally {
closeResultSet(resultSet);
closeStatement(statement);
connection.close();
}
return contentSize;
}
/**
* Sets the acquisition details field in the case database.
*
* @param details The acquisition details
*
* @throws TskCoreException Thrown if the data can not be written
*/
@Override
public void setAcquisitionDetails(String details) throws TskCoreException {
getSleuthkitCase().setAcquisitionDetails(this, details);
}
/**
* Sets the acquisition tool details such as its name, version number and
* any settings used during the acquisition to acquire data.
*
* @param name The name of the acquisition tool. May be NULL.
* @param version The acquisition tool version number. May be NULL.
* @param settings The settings used by the acquisition tool. May be NULL.
*
* @throws TskCoreException Thrown if the data can not be written
*/
@Override
public void setAcquisitionToolDetails(String name, String version, String settings) throws TskCoreException {
getSleuthkitCase().setAcquisitionToolDetails(this, name, version, settings);
}
/**
* Gets the acquisition tool settings field from the case database.
*
* @return The acquisition tool settings. May be Null if not set.
*
* @throws TskCoreException Thrown if the data can not be read
*/
public String getAcquisitionToolSettings() throws TskCoreException {
return getSleuthkitCase().getDataSourceInfoString(this, "acquisition_tool_settings");
}
/**
* Gets the acquisition tool name field from the case database.
*
* @return The acquisition tool name. May be Null if not set.
*
* @throws TskCoreException Thrown if the data can not be read
*/
public String getAcquisitionToolName() throws TskCoreException{
return getSleuthkitCase().getDataSourceInfoString(this, "acquisition_tool_name");
}
/**
* Gets the acquisition tool version field from the case database.
*
* @return The acquisition tool version. May be Null if not set.
*
* @throws TskCoreException Thrown if the data can not be read
*/
public String getAcquisitionToolVersion() throws TskCoreException {
return getSleuthkitCase().getDataSourceInfoString(this, "acquisition_tool_version");
}
/**
* Gets the added date field from the case database.
*
* @return The date time when the image was added in epoch seconds.
*
* @throws TskCoreException Thrown if the data can not be read
*/
public Long getDateAdded() throws TskCoreException {
return getSleuthkitCase().getDataSourceInfoLong(this, "added_date_time");
}
/**
* Gets the acquisition details field from the case database.
*
* @return The acquisition details
*
* @throws TskCoreException Thrown if the data can not be read
*/
@Override
public String getAcquisitionDetails() throws TskCoreException {
return getSleuthkitCase().getAcquisitionDetails(this);
}
/**
* Gets the host for this data source.
*
* @return The host
*
* @throws TskCoreException
*/
@Override
public Host getHost() throws TskCoreException {
// This is a check-then-act race condition that may occasionally result
// in additional processing but is safer than using locks.
if (host == null) {
host = getSleuthkitCase().getHostManager().getHostByDataSource(this);
}
return host;
}
/**
* Updates the image's total size and sector size.This function may be used
* to update the sizes after the image was created.
*
* Can only update the sizes if they were not set before. Will throw
* TskCoreException if the values in the db are not 0 prior to this call.
*
* @param totalSize The total size
* @param sectorSize The sector size
*
* @throws TskCoreException If there is an error updating the case database.
*
*/
public void setSizes(long totalSize, long sectorSize) throws TskCoreException {
getSleuthkitCase().setImageSizes(this, totalSize, sectorSize);
}
/**
* Close a ResultSet.
*
* @param resultSet The ResultSet to be closed.
*/
private static void closeResultSet(ResultSet resultSet) {
if (resultSet != null) {
try {
resultSet.close();
} catch (SQLException ex) {
LOGGER.log(Level.SEVERE, "Error closing ResultSet", ex); //NON-NLS
}
}
}
/**
* Close a Statement.
*
* @param statement The Statement to be closed.
*/
private static void closeStatement(Statement statement) {
if (statement != null) {
try {
statement.close();
} catch (SQLException ex) {
LOGGER.log(Level.SEVERE, "Error closing Statement", ex); //NON-NLS
}
}
}
}
/*
* Sleuth Kit Data Model
*
* Copyright 2011-2016 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.Date;
import java.util.List;
import java.util.ResourceBundle;
/**
* Represents information for an ingest job.
*/
public final class IngestJobInfo {
private static final ResourceBundle bundle = ResourceBundle.getBundle("org.sleuthkit.datamodel.Bundle");
public enum IngestJobStatusType {
//DO NOT CHANGE ORDER
STARTED(bundle.getString("IngestJobInfo.IngestJobStatusType.Started.displayName")),
CANCELLED(bundle.getString("IngestJobInfo.IngestJobStatusType.Cancelled.displayName")),
COMPLETED(bundle.getString("IngestJobInfo.IngestJobStatusType.Completed.displayName"));
private String displayName;
private IngestJobStatusType(String displayName) {
this.displayName = displayName;
}
public static IngestJobStatusType fromID(int typeId) {
for (IngestJobStatusType statusType : IngestJobStatusType.values()) {
if (statusType.ordinal() == typeId) {
return statusType;
}
}
return null;
}
/**
* @return the displayName
*/
public String getDisplayName() {
return displayName;
}
}
private final long ingestJobId;
private final long objectId;
private final String hostName;
private final Date startDateTime;
private Date endDateTime = new Date(0);
private final String settingsDir;
private final List<IngestModuleInfo> ingestModuleInfo;
private final SleuthkitCase skCase;
private IngestJobStatusType status;
/**
* Constructs an IngestJobInfo that has not ended
*
* @param ingestJobId The id of the ingest job
* @param objectId The data source the job is being run on
* @param hostName The host on which the job was executed
* @param startDateTime The date time the job was started
* @param settingsDir The directory of the job settings
* @param ingestModuleInfo The ingest modules being run for this job
* @param skCase A reference to sleuthkit case
*/
IngestJobInfo(long ingestJobId, long objectId, String hostName, Date startDateTime, String settingsDir, List<IngestModuleInfo> ingestModuleInfo, SleuthkitCase skCase) {
this.ingestJobId = ingestJobId;
this.objectId = objectId;
this.hostName = hostName;
this.startDateTime = startDateTime;
this.settingsDir = settingsDir;
this.skCase = skCase;
this.ingestModuleInfo = ingestModuleInfo;
this.status = IngestJobStatusType.STARTED;
}
/**
* Constructs an IngestJobInfo that has already ended
*
* @param ingestJobId The id of the ingest job
* @param dataSourceId The data source the job is being run on
* @param hostName The host on which the job was executed
* @param startDateTime The date time the job was started
* @param endDateTime The date time the job was ended (if it ended)
* @param status The status of the job
* @param settingsDir The directory of the job settings
* @param ingestModuleInfo The ingest modules being run for this job
* @param skCase A reference to sleuthkit case
*/
IngestJobInfo(long ingestJobId, long dataSourceId, String hostName, Date startDateTime, Date endDateTime, IngestJobStatusType status, String settingsDir, List<IngestModuleInfo> ingestModuleInfo, SleuthkitCase skCase) {
this.ingestJobId = ingestJobId;
this.objectId = dataSourceId;
this.hostName = hostName;
this.startDateTime = startDateTime;
this.endDateTime = endDateTime;
this.settingsDir = settingsDir;
this.skCase = skCase;
this.ingestModuleInfo = ingestModuleInfo;
this.status = status;
}
/**
* @return the end date time of the job (equal to the epoch if it has not
* been set yet).
*/
public Date getEndDateTime() {
return endDateTime;
}
/**
* Sets the end date for the ingest job info, and updates the database.
*
* @param endDateTime the endDateTime to set
*
* @throws org.sleuthkit.datamodel.TskCoreException
*/
public void setEndDateTime(Date endDateTime) throws TskCoreException {
Date oldDate = this.endDateTime;
this.endDateTime = endDateTime;
try {
skCase.setIngestJobEndDateTime(getIngestJobId(), endDateTime.getTime());
} catch (TskCoreException ex) {
this.endDateTime = oldDate;
throw ex;
}
}
/**
* Sets the ingest status for the ingest job info, and updates the database.
*
* @param status The new status
*
* @throws TskCoreException
*/
public void setIngestJobStatus(IngestJobStatusType status) throws TskCoreException {
IngestJobStatusType oldStatus = this.getStatus();
this.status = status;
try {
skCase.setIngestJobStatus(getIngestJobId(), status);
} catch (TskCoreException ex) {
this.status = oldStatus;
throw ex;
}
}
/**
* @return the ingestJobId
*/
public long getIngestJobId() {
return ingestJobId;
}
/**
* @return the objectId
*/
public long getObjectId() {
return objectId;
}
/**
* @return the hostName
*/
public String getHostName() {
return hostName;
}
/**
* @return the startDateTime
*/
public Date getStartDateTime() {
return startDateTime;
}
/**
* @return the settingsDir
*/
public String getSettingsDir() {
return settingsDir;
}
/**
* @return the ingestModuleInfo
*/
public List<IngestModuleInfo> getIngestModuleInfo() {
return ingestModuleInfo;
}
/**
* @return the status
*/
public IngestJobStatusType getStatus() {
return status;
}
}