Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 21 additions & 7 deletions Core/src/org/sleuthkit/autopsy/casemodule/AddImageTask.java
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,11 @@ public void run() {
try {
synchronized (tskAddImageProcessLock) {
if (!tskAddImageProcessStopped) {
tskAddImageProcess = currentCase.getSleuthkitCase().makeAddImageProcess(imageDetails.timeZone, true, imageDetails.ignoreFatOrphanFiles, imageWriterPath, imageDetails.password);
if (imageDetails.passwords != null && !imageDetails.passwords.isEmpty()) {
tskAddImageProcess = currentCase.getSleuthkitCase().makeAddImageProcess(imageDetails.timeZone, true, imageDetails.ignoreFatOrphanFiles, imageWriterPath, imageDetails.passwords);
} else {
tskAddImageProcess = currentCase.getSleuthkitCase().makeAddImageProcess(imageDetails.timeZone, true, imageDetails.ignoreFatOrphanFiles, imageWriterPath, imageDetails.password);
}
} else {
return;
}
Expand Down Expand Up @@ -316,24 +320,34 @@ static class ImageDetails {
String timeZone;
boolean ignoreFatOrphanFiles;
String md5;
String sha1;
String sha1;
String sha256;
ImageWriterSettings imageWriterSettings;
String password;

List<String> passwords;

ImageDetails(String deviceId, Image image, int sectorSize, String timeZone, boolean ignoreFatOrphanFiles, String md5, String sha1, String sha256, ImageWriterSettings imageWriterSettings, String password) {
this(deviceId, image, sectorSize, timeZone, ignoreFatOrphanFiles, md5, sha1, sha256, imageWriterSettings, (List<String>) null);
// Store the single password directly (not as a one-element
// candidate list) so existing single-password callers keep going
// through the legacy makeAddImageProcess(String) path.
this.password = password;
}

ImageDetails(String deviceId, Image image, int sectorSize, String timeZone, boolean ignoreFatOrphanFiles, String md5, String sha1, String sha256, ImageWriterSettings imageWriterSettings, List<String> passwords) {
this.deviceId = deviceId;
this.image = image;
this.sectorSize = sectorSize;
this.timeZone = timeZone;
this.ignoreFatOrphanFiles = ignoreFatOrphanFiles;
this.md5 = md5;
this.sha1 = sha1;
this.sha256 = sha256;
this.sha1 = sha1;
this.sha256 = sha256;
this.imageWriterSettings = imageWriterSettings;
this.password = password;
this.password = (passwords != null && !passwords.isEmpty()) ? passwords.get(0) : null;
this.passwords = passwords;
}

String getImagePath() {
if (image.getPaths().length > 0) {
return image.getPaths()[0];
Expand Down
4 changes: 1 addition & 3 deletions Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -269,7 +269,5 @@ LocalFilesPanel.jLabel2.text=NOTE: Time stamps may have changed when the files w
LocalFilesPanel.timestampToIncludeLabel.text=Timestamps To Include:
LocalFilesPanel.accessTimeCheckBox.text=Access Time - Can be changed when the file is opened
LocalFilesPanel.timeStampToIncludeLabel.text=Timestamps To Include:
LocalFilesPanel.timeStampNoteLabel.text=NOTE: Time stamps may have changed when the files were copied to the current location.
ImageFilePanel.passwordLabel.text=Bitlocker Password (optional):
ImageFilePanel.passwordTextField.text=
LocalFilesPanel.timeStampNoteLabel.text=NOTE: Time stamps may have changed when the files were copied to the current location.
ImageFilePanel.loadingLabel.text=loading...
18 changes: 14 additions & 4 deletions Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties-MERGED
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ AddImageWizardSelectHostVisual_title=Select Host
# {0} - exception message
Case.closeException.couldNotCloseCase=Error closing case: {0}
# {0} - provider name
Case.contentProviderLoadFailed.message=The content provider plugin (''{0}'') is installed but could not be loaded. Check the module for errors.
Case.contentProviderLoadFailed.title=Content Provider Load Failed
# {0} - provider name
Case.contentProviderNotFound.message=This case requires a content provider plugin (''{0}'') that is not installed. Please install the appropriate plugin.
Case.contentProviderNotFound.title=Content Provider Not Found
Case.creationException.couldNotAcquireResourcesLock=Failed to get lock on case resources
Expand All @@ -20,6 +23,7 @@ Case.exceptionMessage.cannotGetLockToDeleteCase=Cannot delete case because it is
Case.exceptionMessage.cannotLocateMainWindow=Cannot locate main application window
Case.exceptionMessage.cannotOpenMultiUserCaseNoSettings=Multi-user settings are missing (see Tools, Options, Multi-user tab), cannot open a multi-user case.
Case.exceptionMessage.contentProviderCouldNotBeFound=Content provider was specified for the case but could not be loaded.
Case.exceptionMessage.contentProviderLoadFailed=The content provider plugin is installed but failed to load.
Case.exceptionMessage.contentProviderVersionMismatch=The installed content provider plugin is not compatible with this case.
# {0} - exception message
Case.exceptionMessage.couldNotCreatCollaborationMonitor=Failed to create collaboration monitor:\n{0}.
Expand Down Expand Up @@ -166,8 +170,16 @@ ImageFilePanel.validatePanel.dataSourceOnCDriveError=Warning: Path to multi-user
ImageFilePanel.validatePanel.invalidMD5=Invalid MD5 hash
ImageFilePanel.validatePanel.invalidSHA1=Invalid SHA1 hash
ImageFilePanel.validatePanel.invalidSHA256=Invalid SHA256 hash
# {0} - volumeOffset
ImageFilePanel_bitlockerVolume_labelNoId=BitLocker volume at offset {0} (user password):
ImageFilePanel_bitlockerVolume_labelPlain=BitLocker volume password:
# {0} - recoveryKeyId
ImageFilePanel_bitlockerVolume_labelWithId=BitLocker volume \u2014 Recovery key ID: {0}:
ImageFilePanel_bitlockerVolume_statusLocked=Password required
ImageFilePanel_bitlockerVolume_statusUnlocked=\u2713 Unlocked
ImageFilePanel_validatePanel_bitlockerLocked=<html><body><p>One or more BitLocker volumes require a password to open this image. Enter a password or recovery key for each locked volume below.</p></body></html>
# {0} - imageOpenError
ImageFilePanel_validatePanel_imageOpenError=<html><body><p>An error occurred while opening the image:{0}</p></body></html>
ImageFilePanel_validatePanel_imageOpenError=<html><body style="width:450px"><p>An error occurred while opening the image:{0}</p></body></html>
ImageFilePanel_validatePanel_unknownError=<html><body><p>An unknown error occurred while attempting to validate the image</p></body></html>
ImageFilePanel_validatePanel_unknownErrorMsg=<unknown>
IngestJobInfoPanel.IngestJobTableModel.EndTime.header=End Time
Expand Down Expand Up @@ -514,7 +526,5 @@ LocalFilesPanel.jLabel2.text=NOTE: Time stamps may have changed when the files w
LocalFilesPanel.timestampToIncludeLabel.text=Timestamps To Include:
LocalFilesPanel.accessTimeCheckBox.text=Access Time - Can be changed when the file is opened
LocalFilesPanel.timeStampToIncludeLabel.text=Timestamps To Include:
LocalFilesPanel.timeStampNoteLabel.text=NOTE: Time stamps may have changed when the files were copied to the current location.
ImageFilePanel.passwordLabel.text=Bitlocker Password (optional):
ImageFilePanel.passwordTextField.text=
LocalFilesPanel.timeStampNoteLabel.text=NOTE: Time stamps may have changed when the files were copied to the current location.
ImageFilePanel.loadingLabel.text=loading...
68 changes: 52 additions & 16 deletions Core/src/org/sleuthkit/autopsy/casemodule/ImageDSProcessor.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import javax.swing.JPanel;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.List;
import java.util.logging.Level;
import java.util.UUID;
Expand Down Expand Up @@ -84,6 +85,7 @@ public class ImageDSProcessor implements DataSourceProcessor, AutoIngestDataSour
private String sha256;
private Host host = null;
private String password;
private List<String> passwords;

static {
filtersList.add(allFilter);
Expand Down Expand Up @@ -213,9 +215,10 @@ public void run(String password, Host host, DataSourceProcessorProgressMonitor p
readConfigSettings();
this.host = host;
this.password = Objects.toString(password, this.password);
List<String> candidatePasswords = buildCandidatePasswords();
try {
image = SleuthkitJNI.addImageToDatabase(Case.getCurrentCase().getSleuthkitCase(),
new String[]{imagePath}, sectorSize, timeZone, md5, sha1, sha256, deviceId, this.password, this.host);
new String[]{imagePath}, sectorSize, timeZone, md5, sha1, sha256, deviceId, candidatePasswords, this.host);
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, "Error adding data source with path " + imagePath + " to database", ex);
final List<String> errors = new ArrayList<>();
Expand All @@ -224,7 +227,7 @@ public void run(String password, Host host, DataSourceProcessorProgressMonitor p
return;
}

doAddImageProcess(deviceId, imagePath, sectorSize, timeZone, ignoreFatOrphanFiles, md5, sha1, sha256, this.password, progressMonitor, callback);
doAddImageProcess(deviceId, imagePath, sectorSize, timeZone, ignoreFatOrphanFiles, md5, sha1, sha256, candidatePasswords, progressMonitor, callback);
}


Expand Down Expand Up @@ -253,7 +256,7 @@ public void run(String deviceId, String imagePath, String timeZone, boolean igno
ingestStream = new DefaultIngestStream();
try {
image = SleuthkitJNI.addImageToDatabase(Case.getCurrentCase().getSleuthkitCase(),
new String[]{imagePath}, sectorSize, timeZone, "", "", "", deviceId, null, null);
new String[]{imagePath}, sectorSize, timeZone, "", "", "", deviceId, (String) null, null);
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, "Error adding data source with path " + imagePath + " to database", ex);
final List<String> errors = new ArrayList<>();
Expand All @@ -262,7 +265,10 @@ public void run(String deviceId, String imagePath, String timeZone, boolean igno
return;
}

doAddImageProcess(deviceId, imagePath, 0, timeZone, ignoreFatOrphanFiles, null, null, null, this.password, progressMonitor, callback);
// This overload uses only the settings given by the caller, never
// the candidate passwords collected from the configuration panel.
doAddImageProcess(deviceId, imagePath, 0, timeZone, ignoreFatOrphanFiles, null, null, null,
(password != null) ? Collections.singletonList(password) : null, progressMonitor, callback);
}


Expand Down Expand Up @@ -316,18 +322,19 @@ public void runWithIngestStream(Host host, IngestJobSettings settings, DataSourc


@Override
public void runWithIngestStream(String password, Host host, IngestJobSettings settings,
public void runWithIngestStream(String password, Host host, IngestJobSettings settings,
DataSourceProcessorProgressMonitor progress, DataSourceProcessorCallback callBack) {

// Read the settings from the wizard
// Read the settings from the wizard
readConfigSettings();
this.host = host;
this.password = Objects.toString(password, this.password);
List<String> candidatePasswords = buildCandidatePasswords();

// Set up the data source before creating the ingest stream
try {
image = SleuthkitJNI.addImageToDatabase(Case.getCurrentCase().getSleuthkitCase(),
new String[]{imagePath}, sectorSize, timeZone, md5, sha1, sha256, deviceId, this.password, this.host);
new String[]{imagePath}, sectorSize, timeZone, md5, sha1, sha256, deviceId, candidatePasswords, this.host);
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, "Error adding data source with path " + imagePath + " to database", ex);
final List<String> errors = new ArrayList<>();
Expand All @@ -347,7 +354,7 @@ public void runWithIngestStream(String password, Host host, IngestJobSettings se
ingestStream = new DefaultIngestStream();
}

doAddImageProcess(deviceId, imagePath, sectorSize, timeZone, ignoreFatOrphanFiles, md5, sha1, sha256, this.password, progress, callBack);
doAddImageProcess(deviceId, imagePath, sectorSize, timeZone, ignoreFatOrphanFiles, md5, sha1, sha256, candidatePasswords, progress, callBack);
}


Expand All @@ -373,12 +380,37 @@ private void readConfigSettings() {
if (sha256.isEmpty()) {
sha256 = null;
}
this.password = configPanel.getPassword();
if (this.password.isEmpty()) {
password = null;
// The panel no longer offers a single/main password field, only the
// per-volume BitLocker fields harvested into `passwords` below.
this.password = null;
this.passwords = configPanel.getPasswords();
if (this.passwords.isEmpty()) {
this.passwords = null;
}
}

/**
* Combines the single password (possibly supplied by a caller) with the
* candidate passwords collected from the configuration panel. Each
* candidate will be tried when opening encrypted volumes in the image.
*
* @return The combined candidate password list, or null if there are none.
*/
private List<String> buildCandidatePasswords() {
List<String> candidates = new ArrayList<>();
if (password != null) {
candidates.add(password);
}
if (passwords != null) {
for (String candidate : passwords) {
if (!candidates.contains(candidate)) {
candidates.add(candidate);
}
}
}
return candidates.isEmpty() ? null : candidates;
}

/**
* Check if this DSP supports ingest streams.
*
Expand Down Expand Up @@ -415,12 +447,13 @@ public boolean supportsIngestStream() {
* @param md5 The MD5 hash of the image, may be null.
* @param sha1 The SHA-1 hash of the image, may be null.
* @param sha256 The SHA-256 hash of the image, may be null.
* @param password Password for image decryption. May be null.
* @param passwords Candidate passwords for image decryption.
* May be null or empty.
* @param progressMonitor Progress monitor for reporting progress
* during processing.
* @param callback Callback to call when processing is done.
*/
private void doAddImageProcess(String deviceId, String imagePath, int sectorSize, String timeZone, boolean ignoreFatOrphanFiles, String md5, String sha1, String sha256, String password, DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) {
private void doAddImageProcess(String deviceId, String imagePath, int sectorSize, String timeZone, boolean ignoreFatOrphanFiles, String md5, String sha1, String sha256, List<String> passwords, DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) {

// If the data source or ingest stream haven't been initialized, stop processing
if (ingestStream == null) {
Expand All @@ -440,7 +473,7 @@ private void doAddImageProcess(String deviceId, String imagePath, int sectorSize
return;
}

AddImageTask.ImageDetails imageDetails = new AddImageTask.ImageDetails(deviceId, image, sectorSize, timeZone, ignoreFatOrphanFiles, md5, sha1, sha256, null, password);
AddImageTask.ImageDetails imageDetails = new AddImageTask.ImageDetails(deviceId, image, sectorSize, timeZone, ignoreFatOrphanFiles, md5, sha1, sha256, null, passwords);
addImageTask = new AddImageTask(imageDetails,
progressMonitor,
new StreamingAddDataSourceCallbacks(ingestStream),
Expand Down Expand Up @@ -477,6 +510,7 @@ public void reset() {
ignoreFatOrphanFiles = false;
host = null;
password = null;
passwords = null;
configPanel.reset();
}

Expand Down Expand Up @@ -561,7 +595,8 @@ public void process(String deviceId, Path dataSourcePath, String password, Host
return;
}

doAddImageProcess(deviceId, dataSourcePath.toString(), sectorSize, timeZone, ignoreFatOrphanFiles, null, null, null, this.password, progressMonitor, callBack);
doAddImageProcess(deviceId, dataSourcePath.toString(), sectorSize, timeZone, ignoreFatOrphanFiles, null, null, null,
(this.password != null) ? Collections.singletonList(this.password) : null, progressMonitor, callBack);
}


Expand Down Expand Up @@ -610,7 +645,8 @@ public IngestStream processWithIngestStream(String deviceId, Path dataSourcePath
return null;
}

doAddImageProcess(deviceId, dataSourcePath.toString(), sectorSize, timeZone, ignoreFatOrphanFiles, null, null, null, password, progressMonitor, callBack);
doAddImageProcess(deviceId, dataSourcePath.toString(), sectorSize, timeZone, ignoreFatOrphanFiles, null, null, null,
(password != null) ? Collections.singletonList(password) : null, progressMonitor, callBack);

return ingestStream;
}
Expand Down
Loading