diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties b/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties index c3cfeb9b7a..56db39bd55 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties @@ -244,16 +244,16 @@ CollaborationMonitor.addingDataSourceStatus.msg={0} adding data source CollaborationMonitor.analyzingDataSourceStatus.msg={0} analyzing {1} MissingImageDialog.lbWarning.text= MissingImageDialog.lbWarning.toolTipText= -CaseConverter.AlreadyMultiUser=Case is already multi-user! -CaseConverter.FinishedConverting=Finished converting -CaseConverter.To= to -CaseConverter.BadCaseSourceFolder=Case source folder does not exist! -CaseConverter.BadImageSourceFolder=Image source folder does not exist! -CaseConverter.BadDatabaseFileName=Database file does not exist! -CaseConverter.NonUniqueOutputFolder=Output folder not unique. Skipping -CaseConverter.NonUniqueDatabaseName=Database name not unique. Skipping. -CaseConverter.PotentiallyNonUniqueDatabaseName=Unclear if database name unique. Moving ahead. -CaseConverter.ConvertedToMultiUser=This case was converted to a Multi-user collaborative case on -CaseConverter.UnableToCopySourceImages=Unable to copy source images -CaseConverter.ConversionSuccessful=. Conversion successful: -CaseConverter.DeletingCase=Deleting original case folder +SingleUserCaseImporter.AlreadyMultiUser=Case is already multi-user! +SingleUserCaseImporter.FinishedConverting=Finished converting +SingleUserCaseImporter.To=to +SingleUserCaseImporter.BadCaseSourceFolder=Case source folder does not exist! +SingleUserCaseImporter.BadImageSourceFolder=Image source folder does not exist! +SingleUserCaseImporter.BadDatabaseFileName=Database file does not exist! +SingleUserCaseImporter.NonUniqueOutputFolder=Output folder not unique. Skipping +SingleUserCaseImporter.NonUniqueDatabaseName=Database name not unique. Skipping. +SingleUserCaseImporter.PotentiallyNonUniqueDatabaseName=Unclear if database name unique. Moving ahead. +SingleUserCaseImporter.ConvertedToMultiUser=This case was converted to a Multi-user collaborative case on +SingleUserCaseImporter.UnableToCopySourceImages=Unable to copy source images +SingleUserCaseImporter.ConversionSuccessful=. Conversion successful: +SingleUserCaseImporter.DeletingCase=Deleting original case folder diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java index af785d02e2..ccc23952c4 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java @@ -255,7 +255,7 @@ public class Case { private final static String CACHE_FOLDER = "Cache"; //NON-NLS private final static String EXPORT_FOLDER = "Export"; //NON-NLS private final static String LOG_FOLDER = "Log"; //NON-NLS - private final static String MODULE_FOLDER = "ModuleOutput"; //NON-NLS + final static String MODULE_FOLDER = "ModuleOutput"; //NON-NLS private final static String REPORTS_FOLDER = "Reports"; //NON-NLS private final static String TEMP_FOLDER = "Temp"; //NON-NLS diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/SingleUserCaseImporter.java b/Core/src/org/sleuthkit/autopsy/casemodule/SingleUserCaseImporter.java index 41fa68894a..55c1f7bce7 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/SingleUserCaseImporter.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/SingleUserCaseImporter.java @@ -44,6 +44,7 @@ import org.apache.commons.io.FileUtils; import org.openide.util.Exceptions; import org.openide.util.NbBundle; import org.sleuthkit.autopsy.casemodule.Case.CaseType; +import static org.sleuthkit.autopsy.casemodule.Case.MODULE_FOLDER; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.datamodel.CaseDbConnectionInfo; import org.sleuthkit.datamodel.SleuthkitCase; @@ -59,9 +60,10 @@ public class SingleUserCaseImporter implements Runnable { private static final String DOTAUT = ".aut"; //NON-NLS public static final String CASE_CONVERSION_LOG_FILE = "case_import_log.txt"; //NON-NLS private static final String logDateFormat = "yyyy/MM/dd HH:mm:ss"; //NON-NLS + //If TIMELINE_FOLDER changes, also update TIMELINE in EventsRepository private static final String TIMELINE_FOLDER = "Timeline"; //NON-NLS + //If TIMELINE_FILE changes, also update TIMELINE_FILE in EventDB private final static String TIMELINE_FILE = "events.db"; //NON-NLS - private static final String MODULE_FOLDER = "ModuleOutput"; //NON-NLS private final static String AIM_LOG_FILE_NAME = "auto_ingest_log.txt"; //NON-NLS private static final SimpleDateFormat simpleDateFormat = new SimpleDateFormat(logDateFormat); private static final int MAX_DB_NAME_LENGTH = 63; @@ -79,7 +81,7 @@ public class SingleUserCaseImporter implements Runnable { private XMLCaseManagement newXmlCaseManagement; /** - * CaseConverter constructor + * SingleUserCaseImporter constructor * * @param caseInput the folder to start our case search from. Will find * valid cases from this folder down, and process them. @@ -138,7 +140,7 @@ public class SingleUserCaseImporter implements Runnable { // read old xml config oldXmlCaseManagement.open(input.resolve(oldCaseName + DOTAUT).toString()); if (oldXmlCaseManagement.getCaseType() == CaseType.MULTI_USER_CASE) { - throw new Exception(NbBundle.getMessage(SingleUserCaseImporter.class, "CaseConverter.AlreadyMultiUser")); + throw new Exception(NbBundle.getMessage(SingleUserCaseImporter.class, "SingleUserCaseImporter.AlreadyMultiUser")); } String newCaseFolder = prepareOutput(caseOutputFolder, oldCaseFolder); @@ -177,12 +179,12 @@ public class SingleUserCaseImporter implements Runnable { // and database in the given directory so the user shouldn't be able to accidently blow away // their C drive. if (deleteCase) { - log(NbBundle.getMessage(SingleUserCaseImporter.class, "CaseConverter.DeletingCase") + " " + input); + log(NbBundle.getMessage(SingleUserCaseImporter.class, "SingleUserCaseImporter.DeletingCase") + " " + input); FileUtils.deleteDirectory(input.toFile()); } - log(NbBundle.getMessage(SingleUserCaseImporter.class, "CaseConverter.FinishedConverting") - + input.toString() + NbBundle.getMessage(SingleUserCaseImporter.class, "CaseConverter.To") + log(NbBundle.getMessage(SingleUserCaseImporter.class, "SingleUserCaseImporter.FinishedConverting") + + input.toString() + " " + NbBundle.getMessage(SingleUserCaseImporter.class, "SingleUserCaseImporter.To") + caseOutputFolder + File.separatorChar + newCaseFolder); } catch (Exception exp) { /// clean up here @@ -202,13 +204,13 @@ public class SingleUserCaseImporter implements Runnable { */ private void checkInput(File caseInput, File imageInput) throws Exception { if (false == caseInput.exists()) { - throw new Exception(NbBundle.getMessage(SingleUserCaseImporter.class, "CaseConverter.BadCaseSourceFolder")); + throw new Exception(NbBundle.getMessage(SingleUserCaseImporter.class, "SingleUserCaseImporter.BadCaseSourceFolder")); } else if ((imageInput != null) && (false == imageInput.exists())) { - throw new Exception(NbBundle.getMessage(SingleUserCaseImporter.class, "CaseConverter.BadImageSourceFolder")); + throw new Exception(NbBundle.getMessage(SingleUserCaseImporter.class, "SingleUserCaseImporter.BadImageSourceFolder")); } Path path = Paths.get(caseInput.toString(), AUTOPSY_DB_FILE); if (false == path.toFile().exists()) { - throw new Exception(NbBundle.getMessage(SingleUserCaseImporter.class, "CaseConverter.BadDatabaseFileName")); + throw new Exception(NbBundle.getMessage(SingleUserCaseImporter.class, "SingleUserCaseImporter.BadDatabaseFileName")); } } @@ -236,7 +238,7 @@ public class SingleUserCaseImporter implements Runnable { while (specificOutputFolder.exists()) { if (number == Integer.MAX_VALUE) { // oops. it never became unique. give up. - throw new Exception(NbBundle.getMessage(SingleUserCaseImporter.class, "CaseConverter.NonUniqueOutputFolder") + caseFolder); + throw new Exception(NbBundle.getMessage(SingleUserCaseImporter.class, "SingleUserCaseImporter.NonUniqueOutputFolder") + caseFolder); } temp = sanitizedCaseName + "_" + Integer.toString(number) + timeStamp; //NON-NLS specificOutputFolder = Paths.get(caseOutputFolder, temp).toFile(); @@ -263,7 +265,7 @@ public class SingleUserCaseImporter implements Runnable { String hostName = NetworkUtils.getLocalHostName(); Path destination; Path source; - + if (input.toFile().exists()) { destination = Paths.get(caseOutputFolder, newCaseFolder, hostName); FileUtils.copyDirectory(input.toFile(), destination.toFile()); @@ -280,12 +282,12 @@ public class SingleUserCaseImporter implements Runnable { destination = Paths.get(caseOutputFolder, newCaseFolder, AIM_LOG_FILE_NAME); FileUtils.copyFile(source.toFile(), destination.toFile()); try (PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter(destination.toString(), true)))) { - out.println(NbBundle.getMessage(SingleUserCaseImporter.class, "CaseConverter.ConvertedToMultiUser") + new Date()); + out.println(NbBundle.getMessage(SingleUserCaseImporter.class, "SingleUserCaseImporter.ConvertedToMultiUser") + new Date()); } catch (IOException e) { // if unable to log it, no problem } } - + // Remove the single-user .aut file, database, Timeline database and log File oldDatabaseFile = Paths.get(caseOutputFolder, newCaseFolder, hostName, caseName + DOTAUT).toFile(); if (oldDatabaseFile.exists()) { @@ -959,7 +961,7 @@ public class SingleUserCaseImporter implements Runnable { // not unique. add numbers before dbName. if (number == Integer.MAX_VALUE) { // oops. it never became unique. give up. - throw new Exception(NbBundle.getMessage(SingleUserCaseImporter.class, "CaseConverter.NonUniqueDatabaseName")); + throw new Exception(NbBundle.getMessage(SingleUserCaseImporter.class, "SingleUserCaseImporter.NonUniqueDatabaseName")); } sanitizedDbName = "_" + Integer.toString(number) + "_" + baseDbName; //NON-NLS @@ -974,7 +976,7 @@ public class SingleUserCaseImporter implements Runnable { } else { // Could be caused by database credentials, using user accounts that // can not check if other databases exist, so allow it to continue - log(NbBundle.getMessage(SingleUserCaseImporter.class, "CaseConverter.PotentiallyNonUniqueDatabaseName")); + log(NbBundle.getMessage(SingleUserCaseImporter.class, "SingleUserCaseImporter.PotentiallyNonUniqueDatabaseName")); } return sanitizedDbName; @@ -1007,7 +1009,7 @@ public class SingleUserCaseImporter implements Runnable { if (chosenInput != null && chosenInput.exists()) { FileUtils.copyDirectory(chosenInput, output); } else { - log(NbBundle.getMessage(SingleUserCaseImporter.class, "CaseConverter.UnableToCopySourceImages")); + log(NbBundle.getMessage(SingleUserCaseImporter.class, "SingleUserCaseImporter.UnableToCopySourceImages")); } return chosenInput; } @@ -1195,11 +1197,11 @@ public class SingleUserCaseImporter implements Runnable { * not. True if all was successful, false otherwise. */ private void closeLog(boolean result) { - log(NbBundle.getMessage(SingleUserCaseImporter.class, "CaseConverter.FinishedConverting") - + caseInputFolder - + NbBundle.getMessage(SingleUserCaseImporter.class, "CaseConverter.To") + log(NbBundle.getMessage(SingleUserCaseImporter.class, "SingleUserCaseImporter.FinishedConverting") + + caseInputFolder + " " + + NbBundle.getMessage(SingleUserCaseImporter.class, "SingleUserCaseImporter.To") + caseOutputFolder - + NbBundle.getMessage(SingleUserCaseImporter.class, "CaseConverter.ConversionSuccessful") + + NbBundle.getMessage(SingleUserCaseImporter.class, "SingleUserCaseImporter.ConversionSuccessful") + result); if (writer != null) { diff --git a/Core/src/org/sleuthkit/autopsy/coreutils/UNCPathUtilities.java b/Core/src/org/sleuthkit/autopsy/coreutils/UNCPathUtilities.java new file mode 100644 index 0000000000..dcc6c6b0ae --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/coreutils/UNCPathUtilities.java @@ -0,0 +1,299 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2015 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.coreutils; + +import java.io.File; +import java.io.IOException; +import java.net.InetAddress; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.HashMap; +import java.util.Map; +import java.util.Scanner; +import java.util.UUID; +import java.util.concurrent.TimeUnit; +import java.util.logging.Level; + +public class UNCPathUtilities { + + private static Map drives; + private static final String MAPPED_DRIVES = "_mapped_drives.txt"; //NON-NLS + private static final String DATA_TRIGGER = "----------"; //NON-NLS + private static final String OK_TXT = "OK"; //NON-NLS + private static final String COLON = ":"; //NON-NLS + private static final String UNC_PATH_START = "\\\\"; //NON-NLS + private static final String C_DRIVE = "C:"; //NON-NLS + private static final int DRIVE_LEN = 2; + private static final int STARTING_OFFSET = 0; + private static final int REPLACEMENT_SIZE = 2; + private static final int FIRST_ITEM = 0; + private final String nameString; + + /** + * Constructor + */ + public UNCPathUtilities() { + // get UUID for this instance + this.nameString = UUID.randomUUID().toString(); + drives = getMappedDrives(); + } + + /** + * This method converts a passed in path to UNC if it is not already UNC. + * The UNC path will end up in one of the following two forms: + * \\hostname\somefolder\otherfolder or \\IP_ADDRESS\somefolder\otherfolder + * + * This is accomplished by checking the mapped drives list the operating + * system maintains and substituting where required. If the drive of the + * path passed in does not exist in the cached mapped drives list, you can + * force a rescan of the mapped drives list with rescanDrives(), then call + * this method again. This would be of use if the end user added a mapped + * drive while your dialog was up, for example. + * + * @param inputPath a String of the path to convert + * @return returns a successfully converted inputPath or null if unable to + * find a matching drive and convert it to UNC + */ + synchronized public String mappedDriveToUNC(String inputPath) { + if (inputPath != null) { + // If it is a C:, do not attempt to convert. This is for the single-user case. + if (inputPath.toUpperCase().startsWith(C_DRIVE)) { + return null; + } + if (false == isUNC(inputPath)) { + String uncPath = null; + try { + String currentDrive = Paths.get(inputPath).getRoot().toString().substring(STARTING_OFFSET, REPLACEMENT_SIZE); + String uncMapping = drives.get(currentDrive); + if (uncMapping != null) { + uncPath = uncMapping + inputPath.substring(REPLACEMENT_SIZE, inputPath.length()); + } + } catch (Exception ex) { + // Didn't work. Skip it. + } + return uncPath; + } else { + return inputPath; + } + } else { + return null; + } + } + + /** + * This method converts a passed in path to UNC if it is not already UNC. + * The UNC path will end up in one of the following two forms: + * \\hostname\somefolder\otherfolder or \\IP_ADDRESS\somefolder\otherfolder + * + * This is accomplished by checking the mapped drives list the operating + * system maintains and substituting where required. If the drive of the + * path passed in does not exist in the cached mapped drives list, you can + * force a rescan of the mapped drives list with rescanDrives(), then call + * this method again. This would be of use if the end user added a mapped + * drive while your dialog was up, for example. + * + * @param inputPath the path to convert + * @return returns a successfully converted inputPath or null if unable to + * find a matching drive and convert it to UNC + */ + synchronized public Path mappedDriveToUNC(Path inputPath) { + if (inputPath != null) { + String uncPath = UNCPathUtilities.this.mappedDriveToUNC(inputPath.toString()); + if (uncPath == null) { + return null; + } else { + return Paths.get(uncPath); + } + } else { + return null; + } + } + + /** + * Tests if the drive in the passed in path is a mapped drive. + * + * @param inputPath the Path to test. + * @return true if the passed in drive is mapped, false otherwise + */ + synchronized public boolean isDriveMapped(Path inputPath) { + if (inputPath != null) { + return isDriveMapped(inputPath.toString()); + } else { + return false; + } + } + + /** + * Tests if the drive in the passed in path is a mapped drive. + * + * @param inputPath the Path to test. + * @return true if the passed in drive is mapped, false otherwise + */ + synchronized public boolean isDriveMapped(String inputPath) { + if (inputPath != null) { + String shortenedPath = inputPath.substring(STARTING_OFFSET, DRIVE_LEN); + for (String s : drives.keySet()) { + if (shortenedPath.equals(s)) { + return true; + } + } + } + return false; + } + + /** + * Takes a UNC path that may have an IP address in it and converts it to + * hostname, if it can resolve the hostname. Given + * \\10.11.12.13\some\folder, the result will be \\TEDS_COMPUTER\some\folder + * if the IP address 10.11.12.13 belongs to a machine with the hostname + * TEDS_COMPUTER and the local machine is able to resolve the hostname. + * + * @param inputPath the path to convert to a hostname UNC path + * @return the successfully converted path or null if unable to resolve + */ + synchronized public Path ipToHostName(Path inputPath) { + if (inputPath != null) { + return Paths.get(ipToHostName(inputPath.toString())); + } else { + return null; + } + } + + /** + * Takes a UNC path that may have an IP address in it and converts it to + * hostname, if it can resolve the hostname. Given + * \\10.11.12.13\some\folder, the result will be \\TEDS_COMPUTER\some\folder + * if the IP address 10.11.12.13 belongs to a machine with the hostname + * TEDS_COMPUTER and the local machine is able to resolve the hostname. + * + * @param inputPath a String of the path to convert to a hostname UNC path + * @return the successfully converted path or null if unable to resolve + */ + synchronized public String ipToHostName(String inputPath) { + if (inputPath != null) { + String result = null; + try { + if (isUNC(Paths.get(inputPath))) { + String potentialIP = Paths.get(inputPath.substring(REPLACEMENT_SIZE)).getName(FIRST_ITEM).toString(); + String hostname = InetAddress.getByName(potentialIP).getHostName(); + result = inputPath.replaceAll(potentialIP, hostname); + } + } catch (Exception ex) { + // Could not resolve hostname for IP address, return null result + } + return result; + } else { + return null; + } + } + + /** + * Test if a Path is UNC. It is considered UNC if it begins with \\ + * + * @param inputPath the path to check + * @return true if the passed in Path is UNC, false otherwise + */ + synchronized public static boolean isUNC(Path inputPath) { + if (inputPath != null) { + return isUNC(inputPath.toString()); + } else { + return false; + } + } + + /** + * Test if a String path is UNC. It is considered UNC if it begins with \\ + * + * @param inputPath the String of the path to check + * @return true if the passed in Path is UNC, false otherwise + */ + synchronized public static boolean isUNC(String inputPath) { + if (inputPath != null) { + return inputPath.startsWith(UNC_PATH_START); + } else { + return false; + } + } + + /** + * Updates the list of mapped drives this class contains. This list is used + * to resolve mappedDriveToUNC and isDriveMapped calls. This is useful to + * call if the user has potentially added mapped drives to their system + * after the module calling mappedDriveToUNC has already begun running. Note + * this uses system I/O, so call it with some care. + * + */ + synchronized public void rescanDrives() { + drives = getMappedDrives(); + } + + /** + * Populates the list of mapped drives this class contains. The list is used + * to resolve mappedDriveToUNC and isDriveMapped calls. Note this uses + * system I/O, so call it with some care. + * + * @return the hashmap + */ + synchronized private Map getMappedDrives() { + Map driveMap = new HashMap<>(); + File mappedDrive = new File(nameString + MAPPED_DRIVES); + try { + Files.deleteIfExists(mappedDrive.toPath()); + ProcessBuilder builder = new ProcessBuilder("cmd", "/c", "net", "use"); //NON-NLS + builder.redirectOutput(mappedDrive); + builder.redirectError(mappedDrive); + Process p = builder.start(); // throws IOException + p.waitFor(10, TimeUnit.SECONDS); + try (Scanner scanner = new Scanner(mappedDrive)) { + // parse the data and place it in the hashmap + while (scanner.hasNext()) { + String entry1 = scanner.next(); + if (entry1.startsWith(DATA_TRIGGER)) { + continue; + } + String entry2 = scanner.next(); + if (entry2.startsWith(DATA_TRIGGER)) { + continue; + } + String entry3 = scanner.next(); + if (entry3.startsWith(DATA_TRIGGER)) { + continue; + } + scanner.nextLine(); + if (entry1.length() == DRIVE_LEN && !entry1.equals(OK_TXT) && entry1.endsWith(COLON)) { + driveMap.put(entry1, entry2); // if there was no leading status, populate drive + } else if (entry2.length() == DRIVE_LEN && entry2.endsWith(COLON)) { + driveMap.put(entry2, entry3); // if there was a leading status, populate drive + } + } + } + } catch (IOException | InterruptedException ex) { + // if we couldn't do it, no big deal + Logger.getLogger(UNCPathUtilities.class.getName()).log(Level.WARNING, "Unable to parse 'net use' output", ex); //NON-NLS + } finally { + try { + Files.deleteIfExists(mappedDrive.toPath()); + } catch (IOException ex) { + // if we couldn't do it, no big deal + } + } + return driveMap; + } +} diff --git a/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/Bundle.properties b/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/Bundle.properties index 41acbcf61d..c925bc9c47 100755 --- a/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/Bundle.properties @@ -10,6 +10,7 @@ unsupportedOS.message=PhotoRec Module is supported only on Windows platforms missingExecutable.message=Unable to locate PhotoRec executable. cannotRunExecutable.message=Unable to execute PhotoRec cannotCreateOutputDir.message=Unable to create output directory: {0} +PhotoRecIngestModule.nonHostnameUNCPathUsed=Photorec cannot operate on a UNC path containing IP addresses PhotoRecIngestModule.processTerminated=PhotoRec Carver ingest module was terminated due to exceeding max allowable run time when scanning PhotoRecIngestModule.moduleError=PhotoRec Carver Module Error PhotoRecIngestModule.UnableToCarve=Unable to carve file: {0} diff --git a/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverFileIngestModule.java b/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverFileIngestModule.java index dc7cd0eeff..1aff083373 100755 --- a/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverFileIngestModule.java +++ b/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverFileIngestModule.java @@ -38,18 +38,12 @@ import org.openide.modules.InstalledFileLocator; import org.openide.util.NbBundle; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.coreutils.ExecUtil; -import org.sleuthkit.autopsy.coreutils.FileUtil; import org.sleuthkit.autopsy.coreutils.Logger; -import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil; -import org.sleuthkit.autopsy.coreutils.PlatformUtil; import org.sleuthkit.autopsy.datamodel.ContentUtils; import org.sleuthkit.autopsy.ingest.FileIngestModule; -import org.sleuthkit.autopsy.ingest.FileIngestModuleProcessTerminator; import org.sleuthkit.autopsy.ingest.IngestJobContext; import org.sleuthkit.autopsy.ingest.IngestModule; import org.sleuthkit.autopsy.ingest.IngestModuleReferenceCounter; -import org.sleuthkit.autopsy.ingest.IngestServices; -import org.sleuthkit.autopsy.ingest.ModuleContentEvent; import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.Image; @@ -58,6 +52,7 @@ import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskData; import org.sleuthkit.datamodel.Volume; import org.sleuthkit.autopsy.coreutils.FileUtil; +import org.sleuthkit.autopsy.coreutils.UNCPathUtilities; import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil; import org.sleuthkit.autopsy.coreutils.PlatformUtil; import org.sleuthkit.autopsy.ingest.ProcTerminationCode; @@ -67,7 +62,8 @@ import org.sleuthkit.autopsy.ingest.IngestServices; import org.sleuthkit.autopsy.ingest.ModuleContentEvent; /** - * A file ingest module that runs the Unallocated Carver executable with unallocated space files as input. + * A file ingest module that runs the Unallocated Carver executable with + * unallocated space files as input. */ final class PhotoRecCarverFileIngestModule implements FileIngestModule { @@ -85,7 +81,8 @@ final class PhotoRecCarverFileIngestModule implements FileIngestModule { private Path rootOutputDirPath; private File executableFile; private IngestServices services; - + private UNCPathUtilities uncPathUtilities = new UNCPathUtilities(); + /** * @inheritDoc */ @@ -102,7 +99,7 @@ final class PhotoRecCarverFileIngestModule implements FileIngestModule { throw new IngestModule.IngestModuleException(NbBundle.getMessage(this.getClass(), "unallocatedSpaceProcessingSettingsError.message")); } - this.rootOutputDirPath = PhotoRecCarverFileIngestModule.createModuleOutputDirectoryForCase(); + this.rootOutputDirPath = createModuleOutputDirectoryForCase(); Path execName = Paths.get(PHOTOREC_DIRECTORY, PHOTOREC_EXECUTABLE); executableFile = locateExecutable(execName.toString()); @@ -122,8 +119,7 @@ final class PhotoRecCarverFileIngestModule implements FileIngestModule { // Save the directories for the current job. PhotoRecCarverFileIngestModule.pathsByJob.put(this.context.getJobId(), new WorkingPaths(outputDirPath, tempDirPath)); - } - catch (SecurityException | IOException | UnsupportedOperationException ex) { + } catch (SecurityException | IOException | UnsupportedOperationException ex) { throw new IngestModule.IngestModuleException(NbBundle.getMessage(this.getClass(), "cannotCreateOutputDir.message", ex.getLocalizedMessage())); } } @@ -188,10 +184,10 @@ final class PhotoRecCarverFileIngestModule implements FileIngestModule { processAndSettings.environment().put("__COMPAT_LAYER", "RunAsInvoker"); //NON-NLS processAndSettings.redirectErrorStream(true); processAndSettings.redirectOutput(Redirect.appendTo(log)); - + FileIngestModuleProcessTerminator terminator = new FileIngestModuleProcessTerminator(this.context, true); int exitValue = ExecUtil.execute(processAndSettings, terminator); - + if (this.context.fileIngestIsCancelled() == true) { // if it was cancelled by the user, result is OK cleanup(outputDirPath, tempFilePath); @@ -224,7 +220,7 @@ final class PhotoRecCarverFileIngestModule implements FileIngestModule { } } } - + // Now that we've cleaned up the folders and data files, parse the xml output file to add carved items into the database PhotoRecCarverOutputParser parser = new PhotoRecCarverOutputParser(outputDirPath); List theList = parser.parse(newAuditFile, id, file); @@ -232,13 +228,10 @@ final class PhotoRecCarverFileIngestModule implements FileIngestModule { context.addFilesToJob(new ArrayList<>(theList)); services.fireModuleContentEvent(new ModuleContentEvent(theList.get(0))); // fire an event to update the tree } - } - catch (IOException ex) { + } catch (IOException ex) { logger.log(Level.SEVERE, "Error processing " + file.getName() + " with PhotoRec carver", ex); // NON-NLS return IngestModule.ProcessResult.ERROR; - } - - finally { + } finally { if (null != tempFilePath && Files.exists(tempFilePath)) { // Get rid of the unallocated space file. tempFilePath.toFile().delete(); @@ -247,7 +240,7 @@ final class PhotoRecCarverFileIngestModule implements FileIngestModule { return IngestModule.ProcessResult.OK; } - + private void cleanup(Path outputDirPath, Path tempFilePath) { // cleanup the output path FileUtil.deleteDir(new File(outputDirPath.toString())); @@ -267,8 +260,7 @@ final class PhotoRecCarverFileIngestModule implements FileIngestModule { // the working paths map entry for the job and deletes the temp dir. WorkingPaths paths = PhotoRecCarverFileIngestModule.pathsByJob.remove(this.context.getJobId()); FileUtil.deleteDir(new File(paths.getTempDirPath().toString())); - } - catch (SecurityException ex) { + } catch (SecurityException ex) { logger.log(Level.SEVERE, "Error shutting down PhotoRec carver module", ex); // NON-NLS } } @@ -294,20 +286,26 @@ final class PhotoRecCarverFileIngestModule implements FileIngestModule { } /** - * Creates the output directory for this module for the current case, if it does not already exist. + * Creates the output directory for this module for the current case, if it + * does not already exist. * * @return The absolute path of the output directory. * @throws org.sleuthkit.autopsy.ingest.IngestModule.IngestModuleException */ - synchronized static Path createModuleOutputDirectoryForCase() throws IngestModule.IngestModuleException { + synchronized Path createModuleOutputDirectoryForCase() throws IngestModule.IngestModuleException { Path path = Paths.get(Case.getCurrentCase().getModuleDirectory(), PhotoRecCarverIngestModuleFactory.getModuleName()); + if (UNCPathUtilities.isUNC(path)) { + // if the UNC path is using an IP address, convert to hostname + path = uncPathUtilities.ipToHostName(path); + if (path == null) { + throw new IngestModule.IngestModuleException(NbBundle.getMessage(PhotoRecCarverFileIngestModule.class, "PhotoRecIngestModule.nonHostnameUNCPathUsed")); + } + } try { Files.createDirectory(path); - } - catch (FileAlreadyExistsException ex) { + } catch (FileAlreadyExistsException ex) { // No worries. - } - catch (IOException | SecurityException | UnsupportedOperationException ex) { + } catch (IOException | SecurityException | UnsupportedOperationException ex) { throw new IngestModule.IngestModuleException(NbBundle.getMessage(PhotoRecCarverFileIngestModule.class, "cannotCreateOutputDir.message", ex.getLocalizedMessage())); } return path; @@ -331,8 +329,7 @@ final class PhotoRecCarverFileIngestModule implements FileIngestModule { } parent = parent.getParent(); } - } - catch (TskCoreException ex) { + } catch (TskCoreException ex) { logger.log(Level.SEVERE, "PhotoRec carver exception while trying to get parent of AbstractFile.", ex); //NON-NLS } return id; diff --git a/Core/src/org/sleuthkit/autopsy/timeline/events/db/EventDB.java b/Core/src/org/sleuthkit/autopsy/timeline/events/db/EventDB.java index f5c20036a2..f9c7db11dc 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/events/db/EventDB.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/events/db/EventDB.java @@ -108,6 +108,9 @@ public class EventDB { private static final String WAS_INGEST_RUNNING_KEY = "was_ingest_running"; // NON-NLS + //If EVENTS_DATABASE changes, also update TIMELINE_FILE in SingleUserCaseImporter + private static final String TIMELINE_FILE = "events.db"; // NON-NLS + static { //make sure sqlite driver is loaded // possibly redundant try { @@ -128,7 +131,7 @@ public class EventDB { */ public static EventDB getEventDB(String dbPath) { try { - EventDB eventDB = new EventDB(dbPath + File.separator + "events.db"); // NON-NLS + EventDB eventDB = new EventDB(dbPath + File.separator + TIMELINE_FILE); // NON-NLS return eventDB; } catch (SQLException ex) { diff --git a/Core/src/org/sleuthkit/autopsy/timeline/events/db/EventsRepository.java b/Core/src/org/sleuthkit/autopsy/timeline/events/db/EventsRepository.java index d07824c96f..ba46e03ad1 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/events/db/EventsRepository.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/events/db/EventsRepository.java @@ -94,6 +94,7 @@ public class EventsRepository { private final LoadingCache> aggregateEventsCache; + //If TIMELINE changes, also update TIMELINE_FOLDER in SingleUserCaseImporter private static final String TIMELINE = "Timeline"; public Interval getBoundingEventsInterval(Interval timeRange, Filter filter) { diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Server.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Server.java index 5ff73f4f98..f1b9eb315d 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Server.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Server.java @@ -63,6 +63,7 @@ import org.apache.solr.client.solrj.response.CoreAdminResponse; import org.apache.solr.common.SolrDocument; import org.apache.solr.common.SolrException; import org.sleuthkit.autopsy.casemodule.Case.CaseType; +import org.sleuthkit.autopsy.coreutils.UNCPathUtilities; import org.sleuthkit.autopsy.core.UserPreferences; import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil; @@ -167,7 +168,8 @@ public class Server { private int currentSolrServerPort = 0; private int currentSolrStopPort = 0; private static final boolean DEBUG = false;//(Version.getBuildType() == Version.Type.DEVELOPMENT); - + private final UNCPathUtilities uncPathUtilities = new UNCPathUtilities(); + public enum CORE_EVT_STATES { STOPPED, STARTED @@ -650,9 +652,16 @@ public class Server { * @return absolute path to index dir */ String getIndexDirPath(Case theCase) { - String indexDir = theCase.getModuleDirectory() + - File.separator + "keywordsearch" + File.separator + "data"; //NON-NLS - return indexDir; + String indexDir = theCase.getModuleDirectory() + File.separator + "keywordsearch" + File.separator + "data"; //NON-NLS + String result = uncPathUtilities.mappedDriveToUNC(indexDir); + if (result == null) { + uncPathUtilities.rescanDrives(); + result = uncPathUtilities.mappedDriveToUNC(indexDir); + } + if (result == null) { + return indexDir; + } + return result; } /**