diff --git a/Core/build.xml b/Core/build.xml index e75c217d15..9fcf7d5ace 100644 --- a/Core/build.xml +++ b/Core/build.xml @@ -30,7 +30,10 @@ - + + + + diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties b/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties index 1a49a107f4..3e445c7639 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties @@ -32,16 +32,6 @@ MissingImageDialog.selectButton.text=Select Image MissingImageDialog.titleLabel.text=Search for missing image MissingImageDialog.cancelButton.text=Cancel LocalDiskPanel.errorLabel.text=Error Label -LocalFilesPanel.infoLabel.text=Add local files and folders: -LocalFilesPanel.selectButton.text=Add -LocalFilesPanel.localFileChooser.dialogTitle=Select Local Files or Folders -LocalFilesPanel.selectButton.toolTipText=Add local files and folders as logical files -LocalFilesPanel.clearButton.text=Clear -LocalFilesPanel.clearButton.toolTipText=Clears currently selected local file paths -LocalFilesPanel.selectedPaths.toolTipText= -LocalFilesPanel.localFileChooser.approveButtonText=Select -LocalFilesPanel.localFileChooser.approveButtonToolTipText= -LocalFilesPanel.selectButton.actionCommand=Add AddImageWizardAddingProgressVisual.statusLabel.text=Data source has been added to the local database. Files are being analyzed. AddImageWizardAddingProgressVisual.progressLabel.text= AddImageWizardAddingProgressVisual.viewLogButton.text=View Log @@ -173,7 +163,6 @@ UpdateRecentCases.menuItem.clearRecentCases.text=Clear Recent Cases UpdateRecentCases.menuItem.empty=-Empty- AddImageWizardIngestConfigPanel.CANCEL_BUTTON.text=Cancel NewCaseVisualPanel1.CaseFolderOnCDriveError.text=Warning: Path to multi-user case folder is on \"C:\" drive -LocalFilesPanel.errorLabel.text=Error Label CollaborationMonitor.addingDataSourceStatus.msg={0} adding data source CollaborationMonitor.analyzingDataSourceStatus.msg={0} analyzing {1} MissingImageDialog.lbWarning.text= @@ -191,8 +180,6 @@ CloseCaseWhileIngesting.Warning=Ingest is running. Are you sure you want to clos CloseCaseWhileIngesting.Warning.title=Warning\: This will close the current case CasePropertiesForm.imagesTable.columnModel.title1=Remove CasePropertiesForm.imagesTable.columnModel.title0=Path -LocalFilesPanel.jButton1.text=Change -LocalFilesPanel.displayNameLabel.text=Logical File Set Display Name: Default IngestJobInfoPanel.jLabel1.text=Ingest Modules IngestJobInfoPanel.jLabel2.text=Ingest Jobs CaseInformationPanel.closeButton.text=Close @@ -234,3 +221,23 @@ ImageFilePanel.pathErrorLabel.text=Error Label ImageFilePanel.sectorSizeLabel.text=Sector size: LocalDiskPanel.sectorSizeLabel.text=Sector Size: LocalDiskPanel.refreshTableButton.text=Refresh Local Disks +LocalFilesPanel.displayNameLabel.text=Logical File Set Display Name: Default +LocalFilesPanel.errorLabel.text=Error Label +LocalFilesPanel.selectedPaths.toolTipText= +LocalFilesPanel.clearButton.toolTipText=Clears currently selected local file paths +LocalFilesPanel.clearButton.text=Clear +LocalFilesPanel.selectButton.actionCommand=Add +LocalFilesPanel.selectButton.toolTipText=Add local files and folders as logical files +LocalFilesPanel.selectButton.text=Add +LocalFilesPanel.localFileChooser.dialogTitle=Select Local Files or Folders +LocalFilesPanel.localFileChooser.approveButtonToolTipText= +LocalFilesPanel.localFileChooser.approveButtonText=Select +LogicalEvidenceFilePanel.selectButton.actionCommand=Add +LogicalEvidenceFilePanel.selectButton.toolTipText=Add local files and folders as logical files +LogicalEvidenceFilePanel.selectButton.text=Select +LogicalEvidenceFilePanel.errorLabel.text=Error Label +LogicalEvidenceFilePanel.logicalEvidenceFileChooser.dialogTitle=Select Local Files or Folders +LogicalEvidenceFilePanel.logicalEvidenceFileChooser.approveButtonToolTipText= +LogicalEvidenceFilePanel.logicalEvidenceFileChooser.approveButtonText=Select +LogicalEvidenceFilePanel.logicalEvidencePathField.text= +LocalFilesPanel.changeNameButton.text=Change diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/Bundle_ja.properties b/Core/src/org/sleuthkit/autopsy/casemodule/Bundle_ja.properties index 61e8b48e64..0400c3ac3e 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/Bundle_ja.properties +++ b/Core/src/org/sleuthkit/autopsy/casemodule/Bundle_ja.properties @@ -27,14 +27,6 @@ MissingImageDialog.selectButton.text=\u30a4\u30e1\u30fc\u30b8\u3092\u9078\u629e MissingImageDialog.titleLabel.text=\u6b20\u843d\u3057\u305f\u30a4\u30e1\u30fc\u30b8\u306e\u691c\u7d22 MissingImageDialog.cancelButton.text=\u30ad\u30e3\u30f3\u30bb\u30eb LocalDiskPanel.errorLabel.text=\u30a8\u30e9\u30fc\u30e9\u30d9\u30eb -LocalFilesPanel.infoLabel.text=\u30ed\u30fc\u30ab\u30eb\u30d5\u30a1\u30a4\u30eb\u304a\u3088\u3073\u30d5\u30a9\u30eb\u30c0\u3092\u8ffd\u52a0\uff1a -LocalFilesPanel.selectButton.text=\u8ffd\u52a0 -LocalFilesPanel.localFileChooser.dialogTitle=\u30ed\u30fc\u30ab\u30eb\u30d5\u30a1\u30a4\u30eb\u307e\u305f\u306f\u30d5\u30a9\u30eb\u30c0\u3092\u9078\u629e -LocalFilesPanel.selectButton.toolTipText=\u30ed\u30fc\u30ab\u30eb\u30d5\u30a1\u30a4\u30eb\u304a\u3088\u3073\u30d5\u30a9\u30eb\u30c0\u3092\u30ed\u30b8\u30ab\u30eb\u30d5\u30a1\u30a4\u30eb\u3068\u3057\u3066\u8ffd\u52a0\u3057\u307e\u3059 -LocalFilesPanel.clearButton.text=\u30af\u30ea\u30a2 -LocalFilesPanel.clearButton.toolTipText=\u73fe\u5728\u9078\u629e\u3055\u308c\u3066\u3044\u308b\u30ed\u30fc\u30ab\u30eb\u30d5\u30a1\u30a4\u30eb\u30d1\u30b9\u304c\u30af\u30ea\u30a2\u3055\u308c\u307e\u3059 -LocalFilesPanel.localFileChooser.approveButtonText=\u9078\u629e -LocalFilesPanel.selectButton.actionCommand=\u8ffd\u52a0 AddImageWizardAddingProgressVisual.statusLabel.text=\u30ed\u30fc\u30ab\u30eb\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u306b\u30d5\u30a1\u30a4\u30eb\u30b7\u30b9\u30c6\u30e0\u304c\u8ffd\u52a0\u3055\u308c\u307e\u3057\u305f\u3002\u30d5\u30a1\u30a4\u30eb\u3092\u89e3\u6790\u4e2d\u3067\u3059\u3002 AddImageWizardAddingProgressVisual.progressLabel.text=\uff1c\u30d7\u30ed\u30b0\u30ec\u30b9\uff1e AddImageWizardAddingProgressVisual.viewLogButton.text=\u30ed\u30b0\u3092\u8868\u793a @@ -143,6 +135,7 @@ ImageFilePanel.noFatOrphansCheckbox.text=FAT\u30d5\u30a1\u30a4\u30eb\u30b7\u30b9 LocalDiskPanel.noFatOrphansCheckbox.text=FAT\u30d5\u30a1\u30a4\u30eb\u30b7\u30b9\u30c6\u30e0\u306e\u30aa\u30fc\u30d5\u30a1\u30f3\u30d5\u30a1\u30a4\u30eb\u306f\u7121\u8996 AddImageWizardIngestConfigPanel.CANCEL_BUTTON.text=\u30ad\u30e3\u30f3\u30bb\u30eb LocalFilesPanel.errorLabel.text=\u30a8\u30e9\u30fc\u30e9\u30d9\u30eb +ImageFilePanel.errorLabel.text=\u30a8\u30e9\u30fc\u30e9\u30d9\u30eb NewCaseVisualPanel1.caseTypeLabel.text=\u30b1\u30fc\u30b9\u30bf\u30a4\u30d7\uff1a Case.databaseConnectionInfo.error.msg=\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u30b5\u30fc\u30d0\u30fc\u306e\u63a5\u7d9a\u60c5\u5831\u3092\u5165\u624b\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002\u30c4\u30fc\u30eb\u3001\u30aa\u30d7\u30b7\u30e7\u30f3\u3001\u8907\u6570\u30e6\u30fc\u30b6\u30fc\u3092\u78ba\u8a8d\u3057\u3066\u304f\u3060\u3055\u3044\u3002 Case.open.exception.multiUserCaseNotEnabled=\u8907\u6570\u30e6\u30fc\u30b6\u30fc\u306e\u30b1\u30fc\u30b9\u304c\u6709\u52b9\u5316\u3055\u308c\u3066\u3044\u306a\u3044\u3068\u3001\u8907\u6570\u30e6\u30fc\u30b6\u30fc\u306e\u30b1\u30fc\u30b9\u306f\u958b\u3051\u307e\u305b\u3093\u3002\u30c4\u30fc\u30eb\u3001\u30aa\u30d7\u30b7\u30e7\u30f3\u3001\u8907\u6570\u30e6\u30fc\u30b6\u30fc\u3092\u78ba\u8a8d\u3057\u3066\u304f\u3060\u3055\u3044\u3002 @@ -198,3 +191,17 @@ CueBannerPanel.newCaseLabel.text=\u65b0\u898f\u30b1\u30fc\u30b9\u3092\u4f5c\u621 CueBannerPanel.openCaseLabel.text=\u65e2\u5b58\u30b1\u30fc\u30b9\u3092\u958b\u304f ImageFilePanel.pathErrorLabel.text=\u30a8\u30e9\u30fc\u30e9\u30d9\u30eb ImageFilePanel.sectorSizeLabel.text=\u30a4\u30f3\u30d7\u30c3\u30c8\u30bf\u30a4\u30e0\u30be\u30fc\u30f3\u3092\u9078\u629e\u3057\u3066\u4e0b\u3055\u3044\uff1a +LocalFilesPanel.errorLabel.text=\u30a8\u30e9\u30fc\u30e9\u30d9\u30eb +LocalFilesPanel.clearButton.toolTipText=\u73fe\u5728\u9078\u629e\u3055\u308c\u3066\u3044\u308b\u30ed\u30fc\u30ab\u30eb\u30d5\u30a1\u30a4\u30eb\u30d1\u30b9\u304c\u30af\u30ea\u30a2\u3055\u308c\u307e\u3059 +LocalFilesPanel.clearButton.text=\u30af\u30ea\u30a2 +LocalFilesPanel.selectButton.actionCommand=\u8ffd\u52a0 +LocalFilesPanel.selectButton.toolTipText=\u30ed\u30fc\u30ab\u30eb\u30d5\u30a1\u30a4\u30eb\u304a\u3088\u3073\u30d5\u30a9\u30eb\u30c0\u3092\u30ed\u30b8\u30ab\u30eb\u30d5\u30a1\u30a4\u30eb\u3068\u3057\u3066\u8ffd\u52a0\u3057\u307e\u3059 +LocalFilesPanel.selectButton.text=\u8ffd\u52a0 +LocalFilesPanel.localFileChooser.dialogTitle=\u30ed\u30fc\u30ab\u30eb\u30d5\u30a1\u30a4\u30eb\u307e\u305f\u306f\u30d5\u30a9\u30eb\u30c0\u3092\u9078\u629e +LocalFilesPanel.localFileChooser.approveButtonText=\u9078\u629e +LogicalEvidenceFilePanel.selectButton.actionCommand=\u8ffd\u52a0 +LogicalEvidenceFilePanel.selectButton.toolTipText=\u30ed\u30fc\u30ab\u30eb\u30d5\u30a1\u30a4\u30eb\u304a\u3088\u3073\u30d5\u30a9\u30eb\u30c0\u3092\u30ed\u30b8\u30ab\u30eb\u30d5\u30a1\u30a4\u30eb\u3068\u3057\u3066\u8ffd\u52a0\u3057\u307e\u3059 +LogicalEvidenceFilePanel.selectButton.text=\u8ffd\u52a0 +LogicalEvidenceFilePanel.errorLabel.text=\u30a8\u30e9\u30fc\u30e9\u30d9\u30eb +LogicalEvidenceFilePanel.logicalEvidenceFileChooser.dialogTitle=\u30ed\u30fc\u30ab\u30eb\u30d5\u30a1\u30a4\u30eb\u307e\u305f\u306f\u30d5\u30a9\u30eb\u30c0\u3092\u9078\u629e +LogicalEvidenceFilePanel.logicalEvidenceFileChooser.approveButtonText=\u9078\u629e diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/LocalFilesDSProcessor.java b/Core/src/org/sleuthkit/autopsy/casemodule/LocalFilesDSProcessor.java index 974244bd30..c3a55cbfbb 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/LocalFilesDSProcessor.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/LocalFilesDSProcessor.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2016 Basis Technology Corp. + * Copyright 2011-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -18,33 +18,60 @@ */ package org.sleuthkit.autopsy.casemodule; +import java.io.File; +import java.io.IOException; import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.UUID; +import java.util.logging.Level; import javax.swing.JPanel; +import javax.swing.filechooser.FileFilter; +import org.apache.commons.io.FilenameUtils; +import org.openide.modules.InstalledFileLocator; import org.openide.util.NbBundle; +import org.openide.util.NbBundle.Messages; import org.openide.util.lookup.ServiceProvider; import org.openide.util.lookup.ServiceProviders; import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorCallback; import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorProgressMonitor; import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessor; +import org.sleuthkit.autopsy.coreutils.ExecUtil; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.autopsy.coreutils.PlatformUtil; import org.sleuthkit.autopsy.datasourceprocessors.AutoIngestDataSourceProcessor; /** - * A local/logical files and/or directories data source processor that - * implements the DataSourceProcessor service provider interface to allow - * integration with the add data source wizard. It also provides a run method - * overload to allow it to be used independently of the wizard. + * A local/logical files/logical evidence file(.lo1)/or directories data source + * processor that implements the DataSourceProcessor service provider interface + * to allow integration with the add data source wizard. It also provides a run + * method overload to allow it to be used independently of the wizard. */ -@ServiceProviders(value={ - @ServiceProvider(service=DataSourceProcessor.class), - @ServiceProvider(service=AutoIngestDataSourceProcessor.class)} +@ServiceProviders(value = { + @ServiceProvider(service = DataSourceProcessor.class) + , + @ServiceProvider(service = AutoIngestDataSourceProcessor.class)} ) +@Messages({ + "LocalFilesDSProcessor.logicalEvidenceFilter.desc=Logical Evidence Files (L01)" +}) public class LocalFilesDSProcessor implements DataSourceProcessor, AutoIngestDataSourceProcessor { private static final String DATA_SOURCE_TYPE = NbBundle.getMessage(LocalFilesDSProcessor.class, "LocalFilesDSProcessor.dsType"); - private final LocalFilesPanel configPanel; + private static final Logger logger = Logger.getLogger(LocalFilesDSProcessor.class.getName()); + private final LogicalFilesDspPanel configPanel; + private static final String L01_EXTRACTION_DIR = "L01"; + private static final String UNIQUENESS_CONSTRAINT_SEPERATOR = "_"; + private static final String EWFEXPORT_DIR = "ewfexport_exec"; // NON-NLS + private static final String EWFEXPORT_32_BIT_DIR = "32-bit"; // NON-NLS + private static final String EWFEXPORT_64_BIT_DIR = "64-bit"; // NON-NLS + private static final String EWFEXPORT_WINDOWS_EXE = "ewfexport.exe"; // NON-NLS + private static final String LOG_FILE_EXTENSION = ".txt"; + private static final List LOGICAL_EVIDENCE_EXTENSIONS = Arrays.asList(".l01"); + private static final String LOGICAL_EVIDENCE_DESC = Bundle.LocalFilesDSProcessor_logicalEvidenceFilter_desc(); + private static final GeneralFilter LOGICAL_EVIDENCE_FILTER = new GeneralFilter(LOGICAL_EVIDENCE_EXTENSIONS, LOGICAL_EVIDENCE_DESC); /* * TODO: Remove the setDataSourceOptionsCalled flag and the settings fields * when the deprecated method setDataSourceOptions is removed. @@ -59,7 +86,7 @@ public class LocalFilesDSProcessor implements DataSourceProcessor, AutoIngestDat * method overload to allow it to be used independently of the wizard. */ public LocalFilesDSProcessor() { - configPanel = LocalFilesPanel.getDefault(); + configPanel = LogicalFilesDspPanel.getDefault(); } /** @@ -129,10 +156,133 @@ public class LocalFilesDSProcessor implements DataSourceProcessor, AutoIngestDat public void run(DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) { if (!setDataSourceOptionsCalled) { localFilePaths = configPanel.getContentPaths(); + if (configPanel.subTypeIsLogicalEvidencePanel()) { + try { + //if the L01 option was chosen + localFilePaths = extractLogicalEvidenceFileContents(localFilePaths); + } catch (L01Exception ex) { + //contents of l01 could not be extracted don't add data source or run ingest + final List errors = new ArrayList<>(); + errors.add(ex.getMessage()); + callback.done(DataSourceProcessorCallback.DataSourceProcessorResult.CRITICAL_ERRORS, errors, new ArrayList<>()); + return; + } + } } run(UUID.randomUUID().toString(), configPanel.getFileSetName(), localFilePaths, progressMonitor, callback); } + /** + * Extract the contents of the logical evidence files and return the paths + * to those extracted files. + * + * @param logicalEvidenceFilePaths + * + * @return extractedPaths - the paths to all the files extracted from the + * logical evidence files + * + * @throws + * org.sleuthkit.autopsy.casemodule.LocalFilesDSProcessor.L01Exception + */ + private List extractLogicalEvidenceFileContents(final List logicalEvidenceFilePaths) throws L01Exception { + final List extractedPaths = new ArrayList<>(); + Path ewfexportPath; + ewfexportPath = locateEwfexportExecutable(); + List command = new ArrayList<>(); + for (final String l01Path : logicalEvidenceFilePaths) { + command.clear(); + command.add(ewfexportPath.toAbsolutePath().toString()); + command.add("-f"); + command.add("files"); + command.add("-t"); + File l01Dir = new File(Case.getCurrentCase().getModuleDirectory(), L01_EXTRACTION_DIR); //WJS-TODO change to getOpenCase() when that method exists + if (!l01Dir.exists()) { + l01Dir.mkdirs(); + } + Path dirPath = Paths.get(FilenameUtils.getBaseName(l01Path) + UNIQUENESS_CONSTRAINT_SEPERATOR + System.currentTimeMillis()); + + command.add(dirPath.toString()); + command.add(l01Path); + ProcessBuilder processBuilder = new ProcessBuilder(command); + processBuilder.directory(l01Dir); + try { + //redirect ewfexport stdout and stderr to txt file + Path logFileName = Paths.get(l01Dir.toString(), dirPath.toString() + LOG_FILE_EXTENSION); + File logFile = new File(logFileName.toString()); + Path errFileName = Paths.get(l01Dir.toString(), dirPath.toString() + LOG_FILE_EXTENSION); + File errFile = new File(errFileName.toString()); + processBuilder.redirectError(ProcessBuilder.Redirect.appendTo(errFile)); + processBuilder.redirectOutput(ProcessBuilder.Redirect.appendTo(logFile)); + // open the file with ewfexport to extract its contents + ExecUtil.execute(processBuilder, new ExecUtil.TimedProcessTerminator()); + if (l01Dir.toPath().resolve(dirPath).toFile().exists()) { + extractedPaths.add(l01Dir.toPath().resolve(dirPath).toString()); + } else { //if we failed to extract anything let the user know the L01 file was unable to be processed + throw new L01Exception("Can not process the selected L01 file, ewfExport was unable to extract any files from it."); + } + + } catch (SecurityException ex) { + throw new L01Exception("Security exception occcured while trying to extract l01 contents", ex); + } catch (IOException ex) { + throw new L01Exception("IOException occcured while trying to extract l01 contents", ex); + } + } + return extractedPaths; + } + + /** + * Get a file filter for logical evidence files. + * + * @return LOGICAL_EVIDENCE_FILTER + */ + static FileFilter getLogicalEvidenceFilter() { + return LOGICAL_EVIDENCE_FILTER; + } + + /** + * Gets the path for the ewfexport executable. + * + * @return the path to ewfexport.exe + * + * @throws + * org.sleuthkit.autopsy.casemodule.LocalFilesDSProcessor.L01Exception + */ + private Path locateEwfexportExecutable() throws L01Exception { + // Must be running under a Windows operating system. + if (!PlatformUtil.isWindowsOS()) { + throw new L01Exception("L01 files are only supported on windows currently"); + } + + // Build the expected path to either the 32-bit or 64-bit version of the + // ewfexport executable. + final File ewfRoot = InstalledFileLocator.getDefault().locate(EWFEXPORT_DIR, LocalFilesDSProcessor.class.getPackage().getName(), false); + + Path executablePath; + if (PlatformUtil.is64BitOS()) { + executablePath = Paths.get( + ewfRoot.getAbsolutePath(), + EWFEXPORT_64_BIT_DIR, + EWFEXPORT_WINDOWS_EXE); + } else { + executablePath = Paths.get( + ewfRoot.getAbsolutePath(), + EWFEXPORT_32_BIT_DIR, + EWFEXPORT_WINDOWS_EXE); + } + + // Make sure the executable exists at the expected location and that it + // can be run. + final File ewfexport = executablePath.toFile(); + if (null == ewfexport || !ewfexport.exists()) { + throw new LocalFilesDSProcessor.L01Exception("EWF export executable was not found"); + } + if (!ewfexport.canExecute()) { + throw new LocalFilesDSProcessor.L01Exception("EWF export executable can not be executed"); + } + + return executablePath; + } + /** * Adds a data source to the case database using a background task in a * separate thread and the given settings instead of those provided by the @@ -180,7 +330,7 @@ public class LocalFilesDSProcessor implements DataSourceProcessor, AutoIngestDat */ @Override public void reset() { - configPanel.reset(); + configPanel.select(); localFilePaths = null; setDataSourceOptionsCalled = false; } @@ -190,12 +340,29 @@ public class LocalFilesDSProcessor implements DataSourceProcessor, AutoIngestDat // Local files DSP can process any file by simply adding it as a logical file. // It should return lowest possible non-zero confidence level and be treated // as the "option of last resort" for auto ingest purposes + + this.localFilePaths = Arrays.asList(new String[]{dataSourcePath.toString()}); + //If there is only 1 file check if it is an L01 file and if it is extract the + //contents and replace the paths, if the contents can't be extracted return 0 + if (localFilePaths.size() == 1) { + for (final String path : localFilePaths) { + if (LOGICAL_EVIDENCE_FILTER.accept(new File(path))) { + try { + //if the L01 option was chosen + localFilePaths = extractLogicalEvidenceFileContents(localFilePaths); + } catch (L01Exception ex) { + logger.log(Level.WARNING, "File extension was .l01 but contents of logical evidence file were unable to be extracted", ex); + //contents of l01 could not be extracted don't add data source or run ingest + return 0; + } + } + } + } return 1; } @Override public void process(String deviceId, Path dataSourcePath, DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callBack) throws AutoIngestDataSourceProcessorException { - this.localFilePaths = Arrays.asList(new String[]{dataSourcePath.toString()}); run(deviceId, deviceId, this.localFilePaths, progressMonitor, callBack); } @@ -219,5 +386,21 @@ public class LocalFilesDSProcessor implements DataSourceProcessor, AutoIngestDat this.localFilePaths = Arrays.asList(paths.split(",")); setDataSourceOptionsCalled = true; } - + + /** + * A custom exception for the L01 processing. + */ + private final class L01Exception extends Exception { + + private static final long serialVersionUID = 1L; + + L01Exception(final String message) { + super(message); + } + + L01Exception(final String message, final Throwable cause) { + super(message, cause); + } + } + } diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/LocalFilesPanel.form b/Core/src/org/sleuthkit/autopsy/casemodule/LocalFilesPanel.form index 4a502d97bf..3872db93e2 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/LocalFilesPanel.form +++ b/Core/src/org/sleuthkit/autopsy/casemodule/LocalFilesPanel.form @@ -1,6 +1,6 @@ -
+ @@ -16,21 +16,6 @@ - - - - - - - - - - - - - - - @@ -47,141 +32,172 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - + - - - - - - - - - - - - - - - - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - - - + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/LocalFilesPanel.java b/Core/src/org/sleuthkit/autopsy/casemodule/LocalFilesPanel.java index 39485da30f..c4fedf6cac 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/LocalFilesPanel.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/LocalFilesPanel.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2016 Basis Technology Corp. + * Copyright 2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -24,44 +24,34 @@ import java.util.List; import java.util.Set; import java.util.TreeSet; import javax.swing.JFileChooser; -import javax.swing.JPanel; - import org.openide.util.NbBundle; import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessor; import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil; import java.util.logging.Level; import javax.swing.JOptionPane; -import org.sleuthkit.autopsy.casemodule.Case.CaseType; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.PathValidator; /** - * Add input wizard subpanel for adding local files / dirs to the case + * A panel which allows the user to select local files and/or directories. */ -final class LocalFilesPanel extends JPanel { +final class LocalFilesPanel extends javax.swing.JPanel { private static final long serialVersionUID = 1L; + private final Set currentFiles = new TreeSet<>(); //keep currents in a set to disallow duplicates per add private boolean enableNext = false; - private static LocalFilesPanel instance; private static final Logger logger = Logger.getLogger(LocalFilesPanel.class.getName()); private String displayName = ""; /** * Creates new form LocalFilesPanel */ - private LocalFilesPanel() { + LocalFilesPanel() { initComponents(); customInit(); } - static synchronized LocalFilesPanel getDefault() { - if (instance == null) { - instance = new LocalFilesPanel(); - } - return instance; - } - private void customInit() { localFileChooser.setMultiSelectionEnabled(true); errorLabel.setVisible(false); @@ -69,69 +59,6 @@ final class LocalFilesPanel extends JPanel { this.displayNameLabel.setText(NbBundle.getMessage(this.getClass(), "LocalFilesPanel.displayNameLabel.text")); } - public List getContentPaths() { - List pathsList = new ArrayList<>(); - if (currentFiles == null) { - return pathsList; - } - for (File f : currentFiles) { - pathsList.add(f.getAbsolutePath()); - } - return pathsList; - } - - public String getContentType() { - return NbBundle.getMessage(this.getClass(), "LocalFilesPanel.contentType.text"); - } - - public boolean validatePanel() { - // display warning if there is one (but don't disable "next" button) - warnIfPathIsInvalid(getContentPaths()); - return enableNext; - } - - /** - * Validates path to selected data source and displays warning if it is - * invalid. - * - * @param paths Absolute paths to the selected data source - */ - private void warnIfPathIsInvalid(List pathsList) { - errorLabel.setVisible(false); - - CaseType currentCaseType = Case.getCurrentCase().getCaseType(); - - for (String currentPath : pathsList) { - if (!PathValidator.isValid(currentPath, currentCaseType)) { - errorLabel.setVisible(true); - errorLabel.setText(NbBundle.getMessage(this.getClass(), "DataSourceOnCDriveError.text")); - return; - } - } - } - - public void select() { - reset(); - } - - public void reset() { - currentFiles.clear(); - selectedPaths.setText(""); - enableNext = false; - errorLabel.setVisible(false); - displayName = ""; - this.displayNameLabel.setText(NbBundle.getMessage(this.getClass(), "LocalFilesPanel.displayNameLabel.text")); - } - - public String getFileSetName() { - return this.displayName; - } - - @Override - public String toString() { - return NbBundle.getMessage(this.getClass(), "LocalFilesDSProcessor.toString.text"); - } - /** * This method is called from within the constructor to initialize the form. * WARNING: Do NOT modify this code. The content of this method is always @@ -142,15 +69,13 @@ final class LocalFilesPanel extends JPanel { private void initComponents() { localFileChooser = new javax.swing.JFileChooser(); - jScrollPane1 = new javax.swing.JScrollPane(); - jTextArea1 = new javax.swing.JTextArea(); + jPanel1 = new javax.swing.JPanel(); selectButton = new javax.swing.JButton(); - infoLabel = new javax.swing.JLabel(); clearButton = new javax.swing.JButton(); - jScrollPane2 = new javax.swing.JScrollPane(); + selectedPathsScrollPane = new javax.swing.JScrollPane(); selectedPaths = new javax.swing.JTextArea(); errorLabel = new javax.swing.JLabel(); - jButton1 = new javax.swing.JButton(); + changeNameButton = new javax.swing.JButton(); displayNameLabel = new javax.swing.JLabel(); localFileChooser.setApproveButtonText(org.openide.util.NbBundle.getMessage(LocalFilesPanel.class, "LocalFilesPanel.localFileChooser.approveButtonText")); // NOI18N @@ -158,103 +83,115 @@ final class LocalFilesPanel extends JPanel { localFileChooser.setDialogTitle(org.openide.util.NbBundle.getMessage(LocalFilesPanel.class, "LocalFilesPanel.localFileChooser.dialogTitle")); // NOI18N localFileChooser.setFileSelectionMode(javax.swing.JFileChooser.FILES_AND_DIRECTORIES); - jTextArea1.setColumns(20); - jTextArea1.setRows(5); - jScrollPane1.setViewportView(jTextArea1); - org.openide.awt.Mnemonics.setLocalizedText(selectButton, org.openide.util.NbBundle.getMessage(LocalFilesPanel.class, "LocalFilesPanel.selectButton.text")); // NOI18N selectButton.setToolTipText(org.openide.util.NbBundle.getMessage(LocalFilesPanel.class, "LocalFilesPanel.selectButton.toolTipText")); // NOI18N selectButton.setActionCommand(org.openide.util.NbBundle.getMessage(LocalFilesPanel.class, "LocalFilesPanel.selectButton.actionCommand")); // NOI18N + selectButton.setMaximumSize(new java.awt.Dimension(70, 23)); + selectButton.setMinimumSize(new java.awt.Dimension(70, 23)); + selectButton.setPreferredSize(new java.awt.Dimension(70, 23)); selectButton.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { selectButtonActionPerformed(evt); } }); - org.openide.awt.Mnemonics.setLocalizedText(infoLabel, org.openide.util.NbBundle.getMessage(LocalFilesPanel.class, "LocalFilesPanel.infoLabel.text")); // NOI18N - org.openide.awt.Mnemonics.setLocalizedText(clearButton, org.openide.util.NbBundle.getMessage(LocalFilesPanel.class, "LocalFilesPanel.clearButton.text")); // NOI18N clearButton.setToolTipText(org.openide.util.NbBundle.getMessage(LocalFilesPanel.class, "LocalFilesPanel.clearButton.toolTipText")); // NOI18N + clearButton.setMaximumSize(new java.awt.Dimension(70, 23)); + clearButton.setMinimumSize(new java.awt.Dimension(70, 23)); + clearButton.setPreferredSize(new java.awt.Dimension(70, 23)); clearButton.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { clearButtonActionPerformed(evt); } }); + selectedPathsScrollPane.setPreferredSize(new java.awt.Dimension(379, 96)); + selectedPaths.setEditable(false); selectedPaths.setColumns(20); selectedPaths.setRows(5); selectedPaths.setToolTipText(org.openide.util.NbBundle.getMessage(LocalFilesPanel.class, "LocalFilesPanel.selectedPaths.toolTipText")); // NOI18N - jScrollPane2.setViewportView(selectedPaths); + selectedPathsScrollPane.setViewportView(selectedPaths); errorLabel.setForeground(new java.awt.Color(255, 0, 0)); org.openide.awt.Mnemonics.setLocalizedText(errorLabel, org.openide.util.NbBundle.getMessage(LocalFilesPanel.class, "LocalFilesPanel.errorLabel.text")); // NOI18N - org.openide.awt.Mnemonics.setLocalizedText(jButton1, org.openide.util.NbBundle.getMessage(LocalFilesPanel.class, "LocalFilesPanel.jButton1.text")); // NOI18N - jButton1.addActionListener(new java.awt.event.ActionListener() { + org.openide.awt.Mnemonics.setLocalizedText(changeNameButton, org.openide.util.NbBundle.getMessage(LocalFilesPanel.class, "LocalFilesPanel.changeNameButton.text")); // NOI18N + changeNameButton.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { - jButton1ActionPerformed(evt); + changeNameButtonActionPerformed(evt); } }); org.openide.awt.Mnemonics.setLocalizedText(displayNameLabel, org.openide.util.NbBundle.getMessage(LocalFilesPanel.class, "LocalFilesPanel.displayNameLabel.text")); // NOI18N + javax.swing.GroupLayout jPanel1Layout = new javax.swing.GroupLayout(jPanel1); + jPanel1.setLayout(jPanel1Layout); + jPanel1Layout.setHorizontalGroup( + jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(jPanel1Layout.createSequentialGroup() + .addContainerGap() + .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, jPanel1Layout.createSequentialGroup() + .addComponent(selectedPathsScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false) + .addComponent(selectButton, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(clearButton, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + .addGap(2, 2, 2)) + .addGroup(jPanel1Layout.createSequentialGroup() + .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(jPanel1Layout.createSequentialGroup() + .addComponent(displayNameLabel) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(changeNameButton)) + .addComponent(errorLabel)) + .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)))) + ); + + jPanel1Layout.linkSize(javax.swing.SwingConstants.HORIZONTAL, new java.awt.Component[] {clearButton, selectButton}); + + jPanel1Layout.setVerticalGroup( + jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(jPanel1Layout.createSequentialGroup() + .addGap(0, 0, 0) + .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(jPanel1Layout.createSequentialGroup() + .addComponent(selectButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addGap(36, 36, 36) + .addComponent(clearButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addComponent(selectedPathsScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(changeNameButton, javax.swing.GroupLayout.PREFERRED_SIZE, 20, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(displayNameLabel)) + .addGap(13, 13, 13) + .addComponent(errorLabel) + .addContainerGap()) + ); + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); this.setLayout(layout); layout.setHorizontalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(layout.createSequentialGroup() - .addComponent(infoLabel) - .addGap(0, 0, Short.MAX_VALUE)) - .addComponent(jScrollPane2, javax.swing.GroupLayout.DEFAULT_SIZE, 389, Short.MAX_VALUE)) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false) - .addComponent(selectButton, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addComponent(clearButton, javax.swing.GroupLayout.DEFAULT_SIZE, 69, Short.MAX_VALUE)) - .addGap(2, 2, 2)) - .addGroup(layout.createSequentialGroup() - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(errorLabel) - .addGroup(layout.createSequentialGroup() - .addComponent(displayNameLabel) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) - .addComponent(jButton1))) - .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + .addComponent(jPanel1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) ); layout.setVerticalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(layout.createSequentialGroup() - .addComponent(infoLabel) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) - .addGroup(layout.createSequentialGroup() - .addComponent(selectButton) - .addGap(36, 36, 36) - .addComponent(clearButton)) - .addComponent(jScrollPane2, javax.swing.GroupLayout.PREFERRED_SIZE, 82, javax.swing.GroupLayout.PREFERRED_SIZE)) - .addGap(18, 18, 18) - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(jButton1, javax.swing.GroupLayout.PREFERRED_SIZE, 20, javax.swing.GroupLayout.PREFERRED_SIZE) - .addComponent(displayNameLabel)) - .addGap(13, 13, 13) - .addComponent(errorLabel) - .addGap(7, 7, 7)) + .addComponent(jPanel1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) ); }// //GEN-END:initComponents private void selectButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_selectButtonActionPerformed int returnVal = localFileChooser.showOpenDialog(this); + if (returnVal == JFileChooser.APPROVE_OPTION) { File[] files = localFileChooser.getSelectedFiles(); + StringBuilder allPaths = new StringBuilder(); for (File f : files) { currentFiles.add(f); - } - - //update label - StringBuilder allPaths = new StringBuilder(); - for (File f : currentFiles) { + //update label allPaths.append(f.getAbsolutePath()).append("\n"); } this.selectedPaths.setText(allPaths.toString()); @@ -275,28 +212,101 @@ final class LocalFilesPanel extends JPanel { private void clearButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_clearButtonActionPerformed reset(); - }//GEN-LAST:event_clearButtonActionPerformed - private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton1ActionPerformed - String displayName = JOptionPane.showInputDialog("New Display Name: "); - if (displayName != null && !displayName.equals("")) { - this.displayName = displayName; + private void changeNameButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_changeNameButtonActionPerformed + final String selectedDisplayName = JOptionPane.showInputDialog("New Display Name: "); + if (selectedDisplayName != null && !selectedDisplayName.isEmpty()) { + this.displayName = selectedDisplayName; this.displayNameLabel.setText("Display Name: " + this.displayName); } - }//GEN-LAST:event_jButton1ActionPerformed + }//GEN-LAST:event_changeNameButtonActionPerformed + + /** + * Clear the fields and undo any selection of files. + */ + void reset() { + currentFiles.clear(); + selectedPaths.setText(""); + enableNext = false; + errorLabel.setVisible(false); + displayName = ""; + this.displayNameLabel.setText(NbBundle.getMessage(this.getClass(), "LocalFilesPanel.displayNameLabel.text")); + try { + firePropertyChange(DataSourceProcessor.DSP_PANEL_EVENT.UPDATE_UI.toString(), false, true); + } catch (Exception e) { + logger.log(Level.SEVERE, "LocalFilesPanel listener threw exception", e); //NON-NLS + MessageNotifyUtil.Notify.show(NbBundle.getMessage(this.getClass(), "LocalFilesPanel.moduleErr"), + NbBundle.getMessage(this.getClass(), "LocalFilesPanel.moduleErr.msg"), + MessageNotifyUtil.MessageType.ERROR); + } + } + /** + * Get the path(s) which have been selected on this panel + * + * @return a List of Strings representing the path(s) for the selected files or directories + */ + List getContentPaths() { + List pathsList = new ArrayList<>(); + if (currentFiles == null) { + return pathsList; + } + for (File f : currentFiles) { + pathsList.add(f.getAbsolutePath()); + } + return pathsList; + } + + /** + * Validates path to selected data source and displays warning if it is + * invalid. + * + * @return enableNext - true if the panel is valid, false if invalid + */ + boolean validatePanel() { + // display warning if there is one (but don't disable "next" button) + warnIfPathIsInvalid(getContentPaths()); + return enableNext; + } + + /** + * Validates path to selected data source and displays warning if it is + * invalid. + * + * @param paths Absolute paths to the selected data source + */ + private void warnIfPathIsInvalid(final List pathsList) { + errorLabel.setVisible(false); + + final Case.CaseType currentCaseType = Case.getCurrentCase().getCaseType(); + + for (String currentPath : pathsList) { + if (!PathValidator.isValid(currentPath, currentCaseType)) { + errorLabel.setVisible(true); + errorLabel.setText(NbBundle.getMessage(this.getClass(), "DataSourceOnCDriveError.text")); + return; + } + } + } + + /** + * Get the name given to this collection of local files and directories + * + * @return a String which is the name for the file set. + */ + String getFileSetName() { + return this.displayName; + } // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JButton changeNameButton; private javax.swing.JButton clearButton; private javax.swing.JLabel displayNameLabel; private javax.swing.JLabel errorLabel; - private javax.swing.JLabel infoLabel; - private javax.swing.JButton jButton1; - private javax.swing.JScrollPane jScrollPane1; - private javax.swing.JScrollPane jScrollPane2; - private javax.swing.JTextArea jTextArea1; + private javax.swing.JPanel jPanel1; private javax.swing.JFileChooser localFileChooser; private javax.swing.JButton selectButton; private javax.swing.JTextArea selectedPaths; + private javax.swing.JScrollPane selectedPathsScrollPane; // End of variables declaration//GEN-END:variables } diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/LogicalEvidenceFilePanel.form b/Core/src/org/sleuthkit/autopsy/casemodule/LogicalEvidenceFilePanel.form new file mode 100644 index 0000000000..750ec656f9 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/casemodule/LogicalEvidenceFilePanel.form @@ -0,0 +1,114 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/LogicalEvidenceFilePanel.java b/Core/src/org/sleuthkit/autopsy/casemodule/LogicalEvidenceFilePanel.java new file mode 100644 index 0000000000..ffa5dab235 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/casemodule/LogicalEvidenceFilePanel.java @@ -0,0 +1,260 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2018 Basis Technology Corp. + * Contact: carrier sleuthkit 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.autopsy.casemodule; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; +import java.util.TreeSet; +import javax.swing.JFileChooser; +import org.openide.util.NbBundle; +import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessor; +import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil; +import java.util.logging.Level; +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentListener; +import org.apache.commons.io.FilenameUtils; +import org.apache.commons.lang3.StringUtils; +import org.openide.util.NbBundle.Messages; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.autopsy.coreutils.PathValidator; + +/** + * A panel which allows the user to select a Logical Evidence File (L01) + */ +final class LogicalEvidenceFilePanel extends javax.swing.JPanel implements DocumentListener { + + private static final long serialVersionUID = 1L; + + private final Set currentFiles = new TreeSet<>(); //keep currents in a set to disallow duplicates per add + private static final Logger logger = Logger.getLogger(LocalFilesPanel.class.getName()); + private String displayName = ""; + + /** + * Creates new form LogicalEvidenceFilePanel + */ + private LogicalEvidenceFilePanel() { + initComponents(); + logicalEvidenceFileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY); + logicalEvidenceFileChooser.setAcceptAllFileFilterUsed(false); + logicalEvidenceFileChooser.setMultiSelectionEnabled(false); + logicalEvidenceFileChooser.setFileFilter(LocalFilesDSProcessor.getLogicalEvidenceFilter()); + } + + /** + * Create a new LogicalEvidencePanel. + * + * @return + */ + static LogicalEvidenceFilePanel createInstance() { + synchronized (LogicalEvidenceFilePanel.class) { + final LogicalEvidenceFilePanel instance = new LogicalEvidenceFilePanel(); + // post-constructor initialization of listener support without leaking references of uninitialized objects + instance.logicalEvidencePathField.getDocument().addDocumentListener(instance); + return instance; + } + } + + /** + * This method is called from within the constructor to initialize the form. + * WARNING: Do NOT modify this code. The content of this method is always + * regenerated by the Form Editor. + */ + @SuppressWarnings("unchecked") + // //GEN-BEGIN:initComponents + private void initComponents() { + + logicalEvidenceFileChooser = new javax.swing.JFileChooser(); + selectButton = new javax.swing.JButton(); + logicalEvidencePathField = new javax.swing.JTextField(); + errorLabel = new javax.swing.JLabel(); + + logicalEvidenceFileChooser.setApproveButtonText(org.openide.util.NbBundle.getMessage(LogicalEvidenceFilePanel.class, "LogicalEvidenceFilePanel.logicalEvidenceFileChooser.approveButtonText")); // NOI18N + logicalEvidenceFileChooser.setApproveButtonToolTipText(org.openide.util.NbBundle.getMessage(LogicalEvidenceFilePanel.class, "LogicalEvidenceFilePanel.logicalEvidenceFileChooser.approveButtonToolTipText")); // NOI18N + logicalEvidenceFileChooser.setDialogTitle(org.openide.util.NbBundle.getMessage(LogicalEvidenceFilePanel.class, "LogicalEvidenceFilePanel.logicalEvidenceFileChooser.dialogTitle")); // NOI18N + logicalEvidenceFileChooser.setFileSelectionMode(javax.swing.JFileChooser.FILES_AND_DIRECTORIES); + + org.openide.awt.Mnemonics.setLocalizedText(selectButton, org.openide.util.NbBundle.getMessage(LogicalEvidenceFilePanel.class, "LogicalEvidenceFilePanel.selectButton.text")); // NOI18N + selectButton.setToolTipText(org.openide.util.NbBundle.getMessage(LogicalEvidenceFilePanel.class, "LogicalEvidenceFilePanel.selectButton.toolTipText")); // NOI18N + selectButton.setActionCommand(org.openide.util.NbBundle.getMessage(LogicalEvidenceFilePanel.class, "LogicalEvidenceFilePanel.selectButton.actionCommand")); // NOI18N + selectButton.setMaximumSize(new java.awt.Dimension(70, 23)); + selectButton.setMinimumSize(new java.awt.Dimension(70, 23)); + selectButton.setPreferredSize(new java.awt.Dimension(70, 23)); + selectButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + selectButtonActionPerformed(evt); + } + }); + + logicalEvidencePathField.setText(org.openide.util.NbBundle.getMessage(LogicalEvidenceFilePanel.class, "LogicalEvidenceFilePanel.logicalEvidencePathField.text")); // NOI18N + logicalEvidencePathField.setPreferredSize(new java.awt.Dimension(379, 20)); + + errorLabel.setForeground(new java.awt.Color(255, 0, 0)); + org.openide.awt.Mnemonics.setLocalizedText(errorLabel, org.openide.util.NbBundle.getMessage(LogicalEvidenceFilePanel.class, "LogicalEvidenceFilePanel.errorLabel.text")); // NOI18N + + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); + this.setLayout(layout); + layout.setHorizontalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addContainerGap() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addComponent(errorLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addContainerGap()) + .addGroup(layout.createSequentialGroup() + .addComponent(logicalEvidencePathField, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(selectButton, javax.swing.GroupLayout.PREFERRED_SIZE, 68, javax.swing.GroupLayout.PREFERRED_SIZE) + .addGap(4, 4, 4)))) + ); + layout.setVerticalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addGap(0, 0, 0) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(selectButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(logicalEvidencePathField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addGap(18, 18, 18) + .addComponent(errorLabel) + .addContainerGap(105, Short.MAX_VALUE)) + ); + }// //GEN-END:initComponents + + + private void selectButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_selectButtonActionPerformed + final int returnVal = logicalEvidenceFileChooser.showOpenDialog(this); + + if (returnVal == JFileChooser.APPROVE_OPTION) { + final File file = logicalEvidenceFileChooser.getSelectedFile(); + final StringBuilder allPaths = new StringBuilder(); + currentFiles.add(file); + allPaths.append(file.getAbsolutePath()); + logicalEvidencePathField.setText(allPaths.toString()); + logicalEvidencePathField.setToolTipText(allPaths.toString()); + } + fireChange(); + }//GEN-LAST:event_selectButtonActionPerformed + + /* + * Clear previously selected items on the panel. + */ + void reset() { + currentFiles.clear(); + logicalEvidencePathField.setText(""); + logicalEvidencePathField.setToolTipText(""); + } + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JLabel errorLabel; + private javax.swing.JFileChooser logicalEvidenceFileChooser; + private javax.swing.JTextField logicalEvidencePathField; + private javax.swing.JButton selectButton; + // End of variables declaration//GEN-END:variables + + /** + * Check if the current selection exists and is a logical evidence file and + * therefore the panel is valid. + * + * @return true for a valid selection, false for an invalid or empty + * selection + */ + @Messages({ + "LogicalEvidenceFilePanel.validatePanel.nonL01Error.text=Only files with the .l01 file extension are supported here." + }) + boolean validatePanel() { + errorLabel.setVisible(false); + // display warning if there is one (but don't disable "next" button) + final String path = logicalEvidencePathField.getText(); + if (StringUtils.isBlank(path)) { + return false; + } + // display warning if there is one (but don't disable "next" button) + if (!PathValidator.isValid(path, Case.getCurrentCase().getCaseType())) { + errorLabel.setVisible(true); + errorLabel.setText(Bundle.DataSourceOnCDriveError_text()); + return false; + } + //check the extension incase the path was manually entered + if (!LocalFilesDSProcessor.getLogicalEvidenceFilter().accept(new File(path))) { + errorLabel.setVisible(true); + errorLabel.setText(Bundle.LogicalEvidenceFilePanel_validatePanel_nonL01Error_text()); + return false; + } + + displayName = FilenameUtils.getName(path); + return new File(path).isFile(); + } + + /** + * Get the path(s) which have been selected on this panel + * + * @return a List of Strings representing the path(s) for the selected files + */ + List getContentPaths() { + final List pathsList = new ArrayList<>(); + if (currentFiles == null) { + return pathsList; + } + for (final File f : currentFiles) { + pathsList.add(f.getAbsolutePath()); + } + return pathsList; + } + + /** + * Get the name of the logical evidence file which was selected. + * + * @return the name of the logical evidence file + */ + String getFileSetName() { + return displayName; + } + + @Override + public void insertUpdate(final DocumentEvent docEvent) { + fireChange(); + } + + @Override + public void removeUpdate(final DocumentEvent docEvent) { + fireChange(); + } + + @Override + public void changedUpdate(final DocumentEvent docEvent) { + fireChange(); + } + + @Messages({ + "LogicalEvidenceFilePanel.moduleErr.name=Module Error", + "LogicalEvidenceFilePanel.moduleErr.msg=A module caused an error listening to LogicalEvidenceFilePanel updates. See log to determine which module. Some data could be incomplete." + }) + private void fireChange() { + try { + firePropertyChange(DataSourceProcessor.DSP_PANEL_EVENT.UPDATE_UI.toString(), false, true); + } catch (Exception e) { + logger.log(Level.SEVERE, "LogicalEvidenceFilePanel listener threw exception", e); //NON-NLS + MessageNotifyUtil.Notify.show(NbBundle.getMessage(this.getClass(), "LogicalEvidenceFilePanel.moduleErr"), + NbBundle.getMessage(this.getClass(), "LogicalEvidenceFilePanel.moduleErr.msg"), + MessageNotifyUtil.MessageType.ERROR); + } + } +} diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/LogicalFilesDspPanel.form b/Core/src/org/sleuthkit/autopsy/casemodule/LogicalFilesDspPanel.form new file mode 100644 index 0000000000..2358dedb22 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/casemodule/LogicalFilesDspPanel.form @@ -0,0 +1,83 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/LogicalFilesDspPanel.java b/Core/src/org/sleuthkit/autopsy/casemodule/LogicalFilesDspPanel.java new file mode 100644 index 0000000000..0298c98d69 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/casemodule/LogicalFilesDspPanel.java @@ -0,0 +1,233 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2011-2018 Basis Technology Corp. + * Contact: carrier sleuthkit 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.autopsy.casemodule; + +import java.beans.PropertyChangeListener; +import java.util.ArrayList; +import java.util.List; +import javax.swing.BoxLayout; +import javax.swing.JComboBox; +import javax.swing.JPanel; +import org.openide.util.NbBundle; +import org.openide.util.NbBundle.Messages; +import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessor; + +@Messages({ + "LogicalFilesDspPanel.subTypeComboBox.localFilesOption.text=Local files and folders", + "LogicalFilesDspPanel.subTypeComboBox.l01FileOption.text=Logical evidence file (L01)" +}) +/** + * Add input wizard subpanel for adding local files / dirs to the case + */ +final class LogicalFilesDspPanel extends JPanel { + + private static final long serialVersionUID = 1L; + + private final LocalFilesPanel localFilesPanel = new LocalFilesPanel(); + private final LogicalEvidenceFilePanel l01panel = LogicalEvidenceFilePanel.createInstance(); + private static LogicalFilesDspPanel instance; + + /** + * Creates new form LocalFilesPanel + */ + private LogicalFilesDspPanel() { + initComponents(); + dspSubtypePanel.setLayout(new BoxLayout(dspSubtypePanel, BoxLayout.Y_AXIS)); + dspSubtypePanel.add(l01panel); + dspSubtypePanel.add(localFilesPanel); + l01panel.setVisible(false); + } + + @Override + public void addPropertyChangeListener(final PropertyChangeListener listener) { + super.addPropertyChangeListener(listener); + localFilesPanel.addPropertyChangeListener(listener); + l01panel.addPropertyChangeListener(listener); + } + + @Override + public void removePropertyChangeListener(final PropertyChangeListener listener) { + super.removePropertyChangeListener(listener); + localFilesPanel.removePropertyChangeListener(listener); + l01panel.removePropertyChangeListener(listener); + } + + static LogicalFilesDspPanel getDefault() { + synchronized (LogicalFilesDspPanel.class) { + if (instance == null) { + instance = new LogicalFilesDspPanel(); + } + return instance; + } + } + + String getContentType() { + return NbBundle.getMessage(this.getClass(), "LocalFilesPanel.contentType.text"); + } + + void select() { + dspSubtypeComboBox.setSelectedIndex(0); + localFilesPanel.setVisible(true); + l01panel.setVisible(false); + localFilesPanel.reset(); + l01panel.reset(); + dspSubtypePanel.repaint(); + } + + @Override + public String toString() { + return NbBundle.getMessage(this.getClass(), "LocalFilesDSProcessor.toString.text"); + } + + /** + * This method is called from within the constructor to initialize the form. + * WARNING: Do NOT modify this code. The content of this method is always + * regenerated by the Form Editor. + */ + @SuppressWarnings("unchecked") + // //GEN-BEGIN:initComponents + private void initComponents() { + + dspSubtypePanel = new javax.swing.JPanel(); + dspSubtypeComboBox = new javax.swing.JComboBox<>(); + + dspSubtypePanel.setPreferredSize(new java.awt.Dimension(467, 160)); + + javax.swing.GroupLayout dspSubtypePanelLayout = new javax.swing.GroupLayout(dspSubtypePanel); + dspSubtypePanel.setLayout(dspSubtypePanelLayout); + dspSubtypePanelLayout.setHorizontalGroup( + dspSubtypePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGap(0, 466, Short.MAX_VALUE) + ); + dspSubtypePanelLayout.setVerticalGroup( + dspSubtypePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGap(0, 160, Short.MAX_VALUE) + ); + + dspSubtypeComboBox.setModel(new javax.swing.DefaultComboBoxModel<>(new String[] {Bundle.LogicalFilesDspPanel_subTypeComboBox_localFilesOption_text(), Bundle.LogicalFilesDspPanel_subTypeComboBox_l01FileOption_text()})); + dspSubtypeComboBox.setMinimumSize(new java.awt.Dimension(379, 20)); + dspSubtypeComboBox.setPreferredSize(new java.awt.Dimension(379, 20)); + dspSubtypeComboBox.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + dspSubtypeComboBoxActionPerformed(evt); + } + }); + + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); + this.setLayout(layout); + layout.setHorizontalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addContainerGap() + .addComponent(dspSubtypeComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addComponent(dspSubtypePanel, javax.swing.GroupLayout.PREFERRED_SIZE, 466, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + ); + layout.setVerticalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addComponent(dspSubtypeComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(dspSubtypePanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addGap(0, 0, 0)) + ); + }// //GEN-END:initComponents + + private void dspSubtypeComboBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_dspSubtypeComboBoxActionPerformed + if (evt.getSource() instanceof JComboBox) { + final String selectedSubType = dspSubtypeComboBox.getSelectedItem().toString(); + if (selectedSubType.equals(Bundle.LogicalFilesDspPanel_subTypeComboBox_localFilesOption_text())) { + localFilesPanel.setVisible(true); + l01panel.setVisible(false); + } else if (selectedSubType.equals(Bundle.LogicalFilesDspPanel_subTypeComboBox_l01FileOption_text())) { + localFilesPanel.setVisible(false); + l01panel.setVisible(true); + } + firePropertyChange(DataSourceProcessor.DSP_PANEL_EVENT.UPDATE_UI.toString(), false, true); + } + }//GEN-LAST:event_dspSubtypeComboBoxActionPerformed + + /** + * Validate the contents of the panel and its subtype. + * + * @return true if the panel is valid, false if it is not + */ + boolean validatePanel() { + // display warning if there is one (but don't disable "next" button) + + final String selectedSubType = dspSubtypeComboBox.getSelectedItem().toString(); + if (selectedSubType.equals(Bundle.LogicalFilesDspPanel_subTypeComboBox_localFilesOption_text())) { + return localFilesPanel.validatePanel(); + } else if (selectedSubType.equals(Bundle.LogicalFilesDspPanel_subTypeComboBox_l01FileOption_text())) { + return l01panel.validatePanel(); + } else { + return false; + } + } + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JComboBox dspSubtypeComboBox; + private javax.swing.JPanel dspSubtypePanel; + // End of variables declaration//GEN-END:variables + + /** + * Identify if the selected subtype panel is a LogicalEvidencePanel + * + * @return true if logical evidence files have been selected + */ + boolean subTypeIsLogicalEvidencePanel() { + final String selectedSubType = dspSubtypeComboBox.getSelectedItem().toString(); + return selectedSubType.equals(Bundle.LogicalFilesDspPanel_subTypeComboBox_l01FileOption_text()); + } + + /** + * Get the selected paths of the selected panel subtype. + * + * @return a list of strings which are paths to the selected files or + * directories + */ + List getContentPaths() { + String selectedSubType = dspSubtypeComboBox.getSelectedItem().toString(); + if (selectedSubType.equals(Bundle.LogicalFilesDspPanel_subTypeComboBox_localFilesOption_text())) { + return localFilesPanel.getContentPaths(); + } else if (selectedSubType.equals(Bundle.LogicalFilesDspPanel_subTypeComboBox_l01FileOption_text())) { + return l01panel.getContentPaths(); + } else { + return new ArrayList<>(); + } + + } + + /** + * Get the name of the file set based on its subtype. + * + * @return the name of the set of files to be ingested. + */ + String getFileSetName() { + String selectedSubType = dspSubtypeComboBox.getSelectedItem().toString(); + if (selectedSubType.equals(Bundle.LogicalFilesDspPanel_subTypeComboBox_localFilesOption_text())) { + return localFilesPanel.getFileSetName(); + } else if (selectedSubType.equals(Bundle.LogicalFilesDspPanel_subTypeComboBox_l01FileOption_text())) { + return l01panel.getFileSetName(); + } else { + return ""; + } + } +} diff --git a/Core/src/org/sleuthkit/autopsy/modules/encryptiondetection/Bundle.properties b/Core/src/org/sleuthkit/autopsy/modules/encryptiondetection/Bundle.properties index fdc7d9ed0d..95ce1719ef 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/encryptiondetection/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/modules/encryptiondetection/Bundle.properties @@ -2,7 +2,7 @@ EncryptionDetectionIngestJobSettingsPanel.minimumEntropyLabel.text=Minimum Entro EncryptionDetectionIngestJobSettingsPanel.minimumFileSizeLabel.text=Minimum File Size: EncryptionDetectionIngestJobSettingsPanel.fileSizeMultiplesEnforcedCheckbox.text=Consider only files with sizes that are multiples of 512. EncryptionDetectionIngestJobSettingsPanel.slackFilesAllowedCheckbox.text=Consider slack space files. -EncryptionDetectionIngestJobSettingsPanel.minimumEntropyTextbox.text= -EncryptionDetectionIngestJobSettingsPanel.minimumFileSizeTextbox.text= EncryptionDetectionIngestJobSettingsPanel.mbLabel.text=MB EncryptionDetectionIngestJobSettingsPanel.detectionSettingsLabel.text=Detection Settings +EncryptionDetectionIngestJobSettingsPanel.minimumFileSizeTextbox.text= +EncryptionDetectionIngestJobSettingsPanel.minimumEntropyTextbox.text= diff --git a/Core/src/org/sleuthkit/autopsy/modules/encryptiondetection/EncryptionDetectionFileIngestModule.java b/Core/src/org/sleuthkit/autopsy/modules/encryptiondetection/EncryptionDetectionFileIngestModule.java index 7dd8258263..685f85f6b2 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/encryptiondetection/EncryptionDetectionFileIngestModule.java +++ b/Core/src/org/sleuthkit/autopsy/modules/encryptiondetection/EncryptionDetectionFileIngestModule.java @@ -23,6 +23,7 @@ import java.io.IOException; import java.io.InputStream; import java.util.Collections; import java.util.logging.Level; +import org.openide.util.NbBundle; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.services.Blackboard; import org.sleuthkit.autopsy.coreutils.Logger; @@ -49,12 +50,16 @@ final class EncryptionDetectionFileIngestModule extends FileIngestModuleAdapter static final boolean DEFAULT_CONFIG_FILE_SIZE_MULTIPLE_ENFORCED = true; static final boolean DEFAULT_CONFIG_SLACK_FILES_ALLOWED = true; + static final double MINIMUM_ENTROPY_INPUT_RANGE_MIN = 6.0; + static final double MINIMUM_ENTROPY_INPUT_RANGE_MAX = 8.0; + static final int MINIMUM_FILE_SIZE_INPUT_RANGE_MIN = 1; + private static final int FILE_SIZE_MODULUS = 512; private static final double ONE_OVER_LOG2 = 1.4426950408889634073599246810019; // (1 / log(2)) private static final int BYTE_OCCURENCES_BUFFER_SIZE = 256; - private final IngestServices SERVICES = IngestServices.getInstance(); - private final Logger LOGGER = SERVICES.getLogger(EncryptionDetectionModuleFactory.getModuleName()); + private final IngestServices services = IngestServices.getInstance(); + private final Logger logger = services.getLogger(EncryptionDetectionModuleFactory.getModuleName()); private FileTypeDetector fileTypeDetector; private Blackboard blackboard; private double calculatedEntropy; @@ -79,8 +84,9 @@ final class EncryptionDetectionFileIngestModule extends FileIngestModuleAdapter @Override public void startUp(IngestJobContext context) throws IngestModule.IngestModuleException { - blackboard = Case.getCurrentCase().getServices().getBlackboard(); try { + validateSettings(); + blackboard = Case.getCurrentCase().getServices().getBlackboard(); fileTypeDetector = new FileTypeDetector(); } catch (FileTypeDetector.FileTypeDetectorInitException ex) { throw new IngestModule.IngestModuleException("Failed to create file type detector", ex); @@ -95,13 +101,33 @@ final class EncryptionDetectionFileIngestModule extends FileIngestModuleAdapter return flagFile(file); } } catch (IOException | TskCoreException ex) { - LOGGER.log(Level.SEVERE, String.format("Unable to process file '%s'", file.getParentPath() + file.getName()), ex); + logger.log(Level.SEVERE, String.format("Unable to process file '%s'", file.getParentPath() + file.getName()), ex); return IngestModule.ProcessResult.ERROR; } return IngestModule.ProcessResult.OK; } + /** + * Validate ingest module settings. + * + * @throws IngestModule.IngestModuleException If the input is empty, + * invalid, or out of range. + */ + @NbBundle.Messages({ + "EncryptionDetectionFileIngestModule.errorMessage.minimumEntropyInput=Minimum entropy input must be a number between 6.0 and 8.0.", + "EncryptionDetectionFileIngestModule.errorMessage.minimumFileSizeInput=Minimum file size input must be an integer (in megabytes) of 1 or greater." + }) + private void validateSettings() throws IngestModule.IngestModuleException { + if (minimumEntropy < MINIMUM_ENTROPY_INPUT_RANGE_MIN || minimumEntropy > MINIMUM_ENTROPY_INPUT_RANGE_MAX) { + throw new IngestModule.IngestModuleException(Bundle.EncryptionDetectionFileIngestModule_errorMessage_minimumEntropyInput()); + } + + if (minimumFileSize < MINIMUM_FILE_SIZE_INPUT_RANGE_MIN) { + throw new IngestModule.IngestModuleException(Bundle.EncryptionDetectionFileIngestModule_errorMessage_minimumFileSizeInput()); + } + } + /** * Create a blackboard artifact. * @@ -120,13 +146,13 @@ final class EncryptionDetectionFileIngestModule extends FileIngestModuleAdapter */ blackboard.indexArtifact(artifact); } catch (Blackboard.BlackboardException ex) { - LOGGER.log(Level.SEVERE, "Unable to index blackboard artifact " + artifact.getArtifactID(), ex); //NON-NLS + logger.log(Level.SEVERE, "Unable to index blackboard artifact " + artifact.getArtifactID(), ex); //NON-NLS } /* * Send an event to update the view with the new result. */ - SERVICES.fireModuleDataEvent(new ModuleDataEvent(EncryptionDetectionModuleFactory.getModuleName(), BlackboardArtifact.ARTIFACT_TYPE.TSK_ENCRYPTION_SUSPECTED, Collections.singletonList(artifact))); + services.fireModuleDataEvent(new ModuleDataEvent(EncryptionDetectionModuleFactory.getModuleName(), BlackboardArtifact.ARTIFACT_TYPE.TSK_ENCRYPTION_SUSPECTED, Collections.singletonList(artifact))); /* * Make an ingest inbox message. @@ -135,7 +161,7 @@ final class EncryptionDetectionFileIngestModule extends FileIngestModuleAdapter detailsSb.append("File: ").append(file.getParentPath()).append(file.getName()).append("
\n"); detailsSb.append("Entropy: ").append(calculatedEntropy); - SERVICES.postMessage(IngestMessage.createDataMessage(EncryptionDetectionModuleFactory.getModuleName(), + services.postMessage(IngestMessage.createDataMessage(EncryptionDetectionModuleFactory.getModuleName(), "Encryption Detected Match: " + file.getName(), detailsSb.toString(), file.getName(), @@ -143,7 +169,7 @@ final class EncryptionDetectionFileIngestModule extends FileIngestModuleAdapter return IngestModule.ProcessResult.OK; } catch (TskCoreException ex) { - LOGGER.log(Level.SEVERE, String.format("Failed to create blackboard artifact for '%s'.", file.getParentPath() + file.getName()), ex); //NON-NLS + logger.log(Level.SEVERE, String.format("Failed to create blackboard artifact for '%s'.", file.getParentPath() + file.getName()), ex); //NON-NLS return IngestModule.ProcessResult.ERROR; } } diff --git a/Core/src/org/sleuthkit/autopsy/modules/encryptiondetection/EncryptionDetectionIngestJobSettingsPanel.form b/Core/src/org/sleuthkit/autopsy/modules/encryptiondetection/EncryptionDetectionIngestJobSettingsPanel.form index 26c859fe4d..c2bb97607e 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/encryptiondetection/EncryptionDetectionIngestJobSettingsPanel.form +++ b/Core/src/org/sleuthkit/autopsy/modules/encryptiondetection/EncryptionDetectionIngestJobSettingsPanel.form @@ -21,23 +21,19 @@ + - - - - - - - - - - - - + + + - + + + + + @@ -50,14 +46,14 @@ - + - + @@ -69,20 +65,6 @@ - - - - - - - - - - - - - - @@ -128,5 +110,28 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Core/src/org/sleuthkit/autopsy/modules/encryptiondetection/EncryptionDetectionIngestJobSettingsPanel.java b/Core/src/org/sleuthkit/autopsy/modules/encryptiondetection/EncryptionDetectionIngestJobSettingsPanel.java index 123a62ec85..eb318ccdfd 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/encryptiondetection/EncryptionDetectionIngestJobSettingsPanel.java +++ b/Core/src/org/sleuthkit/autopsy/modules/encryptiondetection/EncryptionDetectionIngestJobSettingsPanel.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2017 Basis Technology Corp. + * Copyright 2017-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -18,8 +18,11 @@ */ package org.sleuthkit.autopsy.modules.encryptiondetection; -import org.openide.util.NbBundle; -import org.openide.util.NbBundle.Messages; +import java.text.ParseException; +import java.util.logging.Level; +import javax.swing.text.DefaultFormatter; +import javax.swing.text.DefaultFormatterFactory; +import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.ingest.IngestModuleIngestJobSettings; import org.sleuthkit.autopsy.ingest.IngestModuleIngestJobSettingsPanel; @@ -29,9 +32,12 @@ import org.sleuthkit.autopsy.ingest.IngestModuleIngestJobSettingsPanel; final class EncryptionDetectionIngestJobSettingsPanel extends IngestModuleIngestJobSettingsPanel { private static final int MEGABYTE_SIZE = 1048576; - private static final double MINIMUM_ENTROPY_INPUT_RANGE_MIN = 6.0; - private static final double MINIMUM_ENTROPY_INPUT_RANGE_MAX = 8.0; - private static final int MINIMUM_FILE_SIZE_INPUT_RANGE_MIN = 1; + private static final int INVALID_TEXT_FIELD_INPUT_RETURN = -1; + + private final Logger logger = Logger.getLogger(EncryptionDetectionIngestJobSettingsPanel.class.getName()); + + private final DefaultFormatterFactory minimumFileSizeTextFormatterFactory = new DefaultFormatterFactory(new StringIntegerFormatter()); + private final DefaultFormatterFactory minimumEntropyTextFormatterFactory = new DefaultFormatterFactory(new StringDoubleFormatter()); /** * Instantiate the ingest job settings panel. @@ -57,9 +63,6 @@ final class EncryptionDetectionIngestJobSettingsPanel extends IngestModuleIngest @Override public IngestModuleIngestJobSettings getSettings() { - validateMinimumEntropy(); - validateMinimumFileSize(); - return new EncryptionDetectionIngestJobSettings( Double.valueOf(minimumEntropyTextbox.getText()), Integer.valueOf(minimumFileSizeTextbox.getText()) * MEGABYTE_SIZE, @@ -68,42 +71,44 @@ final class EncryptionDetectionIngestJobSettingsPanel extends IngestModuleIngest } /** - * Validate the minimum entropy input. - * - * @throws IllegalArgumentException If the input is empty, invalid, or out - * of range. + * Formatter to handle conversion between String and Double. */ - @Messages({ - "EncryptionDetectionIngestJobSettingsPanel.minimumEntropyInput.validationError.text=Minimum entropy input must be a number between 6.0 and 8.0." - }) - private void validateMinimumEntropy() throws IllegalArgumentException { - try { - double minimumEntropy = Double.valueOf(minimumEntropyTextbox.getText()); - if (minimumEntropy < MINIMUM_ENTROPY_INPUT_RANGE_MIN || minimumEntropy > MINIMUM_ENTROPY_INPUT_RANGE_MAX) { - throw new IllegalArgumentException(NbBundle.getMessage(this.getClass(), "EncryptionDetectionIngestJobSettingsPanel.minimumEntropyInput.validationError.text")); + private final class StringDoubleFormatter extends DefaultFormatter { + + @Override + public Object stringToValue(String string) throws ParseException { + try { + return Double.parseDouble(string); + } catch (NumberFormatException ex) { + logger.log(Level.WARNING, String.format("The String input '%s' is not valid for Double conversion.", string), ex); + /* + * Return a valid number outside the acceptable value range so + * it can be run through the validator in the file ingest + * module. + */ + return new Double(INVALID_TEXT_FIELD_INPUT_RETURN); } - } catch (NumberFormatException ex) { - throw new IllegalArgumentException(NbBundle.getMessage(this.getClass(), "EncryptionDetectionIngestJobSettingsPanel.minimumEntropyInput.validationError.text")); } } /** - * Validate the minimum file size input. - * - * @throws IllegalArgumentException If the input is empty, invalid, or out - * of range. + * Formatter to handle conversion between String and Integer. */ - @Messages({ - "EncryptionDetectionIngestJobSettingsPanel.minimumFileSizeInput.validationError.text=Minimum file size input must be an integer (in megabytes) of 1 or greater." - }) - private void validateMinimumFileSize() throws IllegalArgumentException { - try { - int minimumFileSize = Integer.valueOf(minimumFileSizeTextbox.getText()); - if (minimumFileSize < MINIMUM_FILE_SIZE_INPUT_RANGE_MIN) { - throw new IllegalArgumentException(NbBundle.getMessage(this.getClass(), "EncryptionDetectionIngestJobSettingsPanel.minimumFileSizeInput.validationError.text")); + private final class StringIntegerFormatter extends DefaultFormatter { + + @Override + public Object stringToValue(String string) throws ParseException { + try { + return Integer.parseInt(string); + } catch (NumberFormatException ex) { + logger.log(Level.WARNING, String.format("The String input '%s' is not valid for Integer conversion.", string), ex); + /* + * Return a valid number outside the acceptable value range so + * it can still be run through the validator in the file ingest + * module. + */ + return INVALID_TEXT_FIELD_INPUT_RETURN; } - } catch (NumberFormatException ex) { - throw new IllegalArgumentException(NbBundle.getMessage(this.getClass(), "EncryptionDetectionIngestJobSettingsPanel.minimumFileSizeInput.validationError.text")); } } @@ -116,18 +121,14 @@ final class EncryptionDetectionIngestJobSettingsPanel extends IngestModuleIngest // //GEN-BEGIN:initComponents private void initComponents() { - minimumEntropyTextbox = new javax.swing.JTextField(); - minimumFileSizeTextbox = new javax.swing.JTextField(); fileSizeMultiplesEnforcedCheckbox = new javax.swing.JCheckBox(); slackFilesAllowedCheckbox = new javax.swing.JCheckBox(); minimumEntropyLabel = new javax.swing.JLabel(); minimumFileSizeLabel = new javax.swing.JLabel(); mbLabel = new javax.swing.JLabel(); detectionSettingsLabel = new javax.swing.JLabel(); - - minimumEntropyTextbox.setText(org.openide.util.NbBundle.getMessage(EncryptionDetectionIngestJobSettingsPanel.class, "EncryptionDetectionIngestJobSettingsPanel.minimumEntropyTextbox.text")); // NOI18N - - minimumFileSizeTextbox.setText(org.openide.util.NbBundle.getMessage(EncryptionDetectionIngestJobSettingsPanel.class, "EncryptionDetectionIngestJobSettingsPanel.minimumFileSizeTextbox.text")); // NOI18N + minimumFileSizeTextbox = new javax.swing.JFormattedTextField(); + minimumEntropyTextbox = new javax.swing.JFormattedTextField(); org.openide.awt.Mnemonics.setLocalizedText(fileSizeMultiplesEnforcedCheckbox, org.openide.util.NbBundle.getMessage(EncryptionDetectionIngestJobSettingsPanel.class, "EncryptionDetectionIngestJobSettingsPanel.fileSizeMultiplesEnforcedCheckbox.text")); // NOI18N @@ -142,6 +143,17 @@ final class EncryptionDetectionIngestJobSettingsPanel extends IngestModuleIngest detectionSettingsLabel.setFont(new java.awt.Font("Tahoma", 1, 11)); // NOI18N org.openide.awt.Mnemonics.setLocalizedText(detectionSettingsLabel, org.openide.util.NbBundle.getMessage(EncryptionDetectionIngestJobSettingsPanel.class, "EncryptionDetectionIngestJobSettingsPanel.detectionSettingsLabel.text")); // NOI18N + minimumFileSizeTextbox.setFormatterFactory(minimumFileSizeTextFormatterFactory); + minimumFileSizeTextbox.setText(org.openide.util.NbBundle.getMessage(EncryptionDetectionIngestJobSettingsPanel.class, "EncryptionDetectionIngestJobSettingsPanel.minimumFileSizeTextbox.text")); // NOI18N + minimumFileSizeTextbox.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + minimumFileSizeTextboxActionPerformed(evt); + } + }); + + minimumEntropyTextbox.setFormatterFactory(minimumEntropyTextFormatterFactory); + minimumEntropyTextbox.setText(org.openide.util.NbBundle.getMessage(EncryptionDetectionIngestJobSettingsPanel.class, "EncryptionDetectionIngestJobSettingsPanel.minimumEntropyTextbox.text")); // NOI18N + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); this.setLayout(layout); layout.setHorizontalGroup( @@ -151,19 +163,17 @@ final class EncryptionDetectionIngestJobSettingsPanel extends IngestModuleIngest .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addComponent(slackFilesAllowedCheckbox) .addComponent(detectionSettingsLabel) + .addComponent(fileSizeMultiplesEnforcedCheckbox) .addGroup(layout.createSequentialGroup() - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) - .addGroup(javax.swing.GroupLayout.Alignment.LEADING, layout.createSequentialGroup() - .addComponent(minimumFileSizeLabel) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) - .addComponent(minimumFileSizeTextbox, javax.swing.GroupLayout.PREFERRED_SIZE, 32, javax.swing.GroupLayout.PREFERRED_SIZE)) - .addGroup(javax.swing.GroupLayout.Alignment.LEADING, layout.createSequentialGroup() - .addComponent(minimumEntropyLabel) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) - .addComponent(minimumEntropyTextbox, javax.swing.GroupLayout.PREFERRED_SIZE, 32, javax.swing.GroupLayout.PREFERRED_SIZE))) + .addComponent(minimumFileSizeLabel) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(minimumFileSizeTextbox, javax.swing.GroupLayout.PREFERRED_SIZE, 32, javax.swing.GroupLayout.PREFERRED_SIZE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(mbLabel)) - .addComponent(fileSizeMultiplesEnforcedCheckbox)) + .addGroup(layout.createSequentialGroup() + .addComponent(minimumEntropyLabel) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(minimumEntropyTextbox, javax.swing.GroupLayout.PREFERRED_SIZE, 32, javax.swing.GroupLayout.PREFERRED_SIZE))) .addContainerGap(15, Short.MAX_VALUE)) ); layout.setVerticalGroup( @@ -173,13 +183,13 @@ final class EncryptionDetectionIngestJobSettingsPanel extends IngestModuleIngest .addComponent(detectionSettingsLabel) .addGap(16, 16, 16) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(minimumEntropyTextbox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addComponent(minimumEntropyLabel)) + .addComponent(minimumEntropyLabel) + .addComponent(minimumEntropyTextbox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(minimumFileSizeTextbox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(mbLabel) - .addComponent(minimumFileSizeLabel)) + .addComponent(minimumFileSizeLabel) + .addComponent(minimumFileSizeTextbox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) .addComponent(fileSizeMultiplesEnforcedCheckbox) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) @@ -188,14 +198,18 @@ final class EncryptionDetectionIngestJobSettingsPanel extends IngestModuleIngest ); }// //GEN-END:initComponents + private void minimumFileSizeTextboxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_minimumFileSizeTextboxActionPerformed + // TODO add your handling code here: + }//GEN-LAST:event_minimumFileSizeTextboxActionPerformed + // Variables declaration - do not modify//GEN-BEGIN:variables private javax.swing.JLabel detectionSettingsLabel; private javax.swing.JCheckBox fileSizeMultiplesEnforcedCheckbox; private javax.swing.JLabel mbLabel; private javax.swing.JLabel minimumEntropyLabel; - private javax.swing.JTextField minimumEntropyTextbox; + private javax.swing.JFormattedTextField minimumEntropyTextbox; private javax.swing.JLabel minimumFileSizeLabel; - private javax.swing.JTextField minimumFileSizeTextbox; + private javax.swing.JFormattedTextField minimumFileSizeTextbox; private javax.swing.JCheckBox slackFilesAllowedCheckbox; // End of variables declaration//GEN-END:variables } diff --git a/thirdparty/ewfexport_exec/32-bit/ewfexport.exe b/thirdparty/ewfexport_exec/32-bit/ewfexport.exe new file mode 100644 index 0000000000..35433e0537 Binary files /dev/null and b/thirdparty/ewfexport_exec/32-bit/ewfexport.exe differ diff --git a/thirdparty/ewfexport_exec/32-bit/libewf.dll b/thirdparty/ewfexport_exec/32-bit/libewf.dll new file mode 100644 index 0000000000..68a67f7973 Binary files /dev/null and b/thirdparty/ewfexport_exec/32-bit/libewf.dll differ diff --git a/thirdparty/ewfexport_exec/32-bit/zlib.dll b/thirdparty/ewfexport_exec/32-bit/zlib.dll new file mode 100644 index 0000000000..78223098d6 Binary files /dev/null and b/thirdparty/ewfexport_exec/32-bit/zlib.dll differ diff --git a/thirdparty/ewfexport_exec/64-bit/ewfexport.exe b/thirdparty/ewfexport_exec/64-bit/ewfexport.exe new file mode 100644 index 0000000000..c0de212df3 Binary files /dev/null and b/thirdparty/ewfexport_exec/64-bit/ewfexport.exe differ diff --git a/thirdparty/ewfexport_exec/64-bit/libewf.dll b/thirdparty/ewfexport_exec/64-bit/libewf.dll new file mode 100644 index 0000000000..013cadc5c6 Binary files /dev/null and b/thirdparty/ewfexport_exec/64-bit/libewf.dll differ diff --git a/thirdparty/ewfexport_exec/64-bit/zlib.dll b/thirdparty/ewfexport_exec/64-bit/zlib.dll new file mode 100644 index 0000000000..1aed167e61 Binary files /dev/null and b/thirdparty/ewfexport_exec/64-bit/zlib.dll differ