From 428d0bd90ae65f8ca235edfdd2a19f40967da450 Mon Sep 17 00:00:00 2001 From: Karl Mortensen Date: Tue, 28 Jul 2015 12:30:08 -0400 Subject: [PATCH] use hostname in UNC paths for photorec --- .../autopsy/coreutils/HandleUNC.java | 198 ++++++++++++++++++ .../PhotoRecCarverFileIngestModule.java | 43 ++-- 2 files changed, 219 insertions(+), 22 deletions(-) create mode 100644 Core/src/org/sleuthkit/autopsy/coreutils/HandleUNC.java diff --git a/Core/src/org/sleuthkit/autopsy/coreutils/HandleUNC.java b/Core/src/org/sleuthkit/autopsy/coreutils/HandleUNC.java new file mode 100644 index 0000000000..69fae50e8c --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/coreutils/HandleUNC.java @@ -0,0 +1,198 @@ +/* + * 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.concurrent.TimeUnit; +import java.util.logging.Level; + +public class HandleUNC { + + private static HandleUNC instance = null; + 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 int MAX_LINE_SCANS = 500; + 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; + + /** + * Access method for singleton + * + * @return HandleUNC singleton object + */ + public synchronized static HandleUNC getInstance() { + if (instance == null) { + instance = new HandleUNC(); + } + return instance; + } + + /** + * Private constructor for singleton + */ + private HandleUNC() { + drives = getMappedDrives(); + } + + /** + * Try to substitute in the path for a UNC string. + * + * @param path the path to substitute. + * @return returns the original string if unsuccessful, the substituted + * string if successful. + */ + public String attemptUNCSubstitution(String path) { + return attemptUNCSubstitution(Paths.get(path)).toString(); + } + + /** + * Try to substitute in the path for a UNC string. + * + * @param path the path to substitute. + * @return returns the original string if unsuccessful, the substituted + * string if successful. + */ + public Path attemptUNCSubstitution(Path path) { + String uncPath = path.toString(); + if (false == isUNC(path)) { + try { + String currentDrive = path.getRoot().toString().substring(STARTING_OFFSET, REPLACEMENT_SIZE); + String uncMapping = drives.get(currentDrive); + if (uncMapping != null) { + uncPath = uncMapping + uncPath.substring(REPLACEMENT_SIZE, uncPath.length()); + } + } catch (Exception ex) { + // Didn't work. Skip it. + } + } + return Paths.get(uncPath); + } + + /** + * Takes a UNC path that may have an IP address in it and converts it to + * hostname, if it can. + * + * @param inputPath the path to convert to a hostname UNC path + * @return the path that was passed in if it was hostname before, the + * converted path if it was successfully converted + */ + public Path ipToHostName(Path inputPath) { + return Paths.get(ipToHostName(inputPath.toString())); + } + + /** + * Takes a UNC path that may have an IP address in it and converts it to + * hostname, if it can. + * + * @param inputPath the path to convert to a hostname UNC path + * @return the path that was passed in if it was hostname before, the + * converted path if it was successfully converted + */ + public String ipToHostName(String inputPath) { + String result = inputPath; + 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) { + // We didn't find a hostname for this potential IP address. Move on. + } + return result; + } + + /** + * Test if a String is UNC + * + * @param inputPath + * @return true if UNC, false otherwise + */ + public static boolean isUNC(Path inputPath) { + return isUNC(inputPath.toString()); + } + + /** + * Test if a Path is UNC + * + * @param inputPath + * @return true if UNC, false otherwise + */ + public static boolean isUNC(String inputPath) { + return inputPath.startsWith(UNC_PATH_START); + } + + /** + * Populate the mapped drives + * + * @return the hashmap + */ + private static Map getMappedDrives() { + Map driveMap = new HashMap<>(); + try { + File mappedDrive = new File(MAPPED_DRIVES); + 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)) { + int safetyCount = 0; + + // scan past the header information until we find trigger + while (scanner.hasNext() && !scanner.nextLine().equals(DATA_TRIGGER)) { + if (++safetyCount > MAX_LINE_SCANS) { + break; + } + } + // parse the data and place it in the hashmap + while (scanner.hasNext()) { + String entry1 = scanner.next(); + String entry2 = scanner.next(); + String entry3 = scanner.next(); + 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 + } + } + } + Files.deleteIfExists(mappedDrive.toPath()); + } catch (IOException | InterruptedException ex) { + // if we couldn't do it, no big deal + Logger.getLogger(HandleUNC.class.getName()).log(Level.WARNING, "Unable to parse 'net use' output", ex); //NON-NLS + } + return driveMap; + } +} diff --git a/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverFileIngestModule.java b/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverFileIngestModule.java index dc7cd0eeff..98a9c3fef8 100755 --- a/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverFileIngestModule.java +++ b/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverFileIngestModule.java @@ -58,6 +58,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.HandleUNC; import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil; import org.sleuthkit.autopsy.coreutils.PlatformUtil; import org.sleuthkit.autopsy.ingest.ProcTerminationCode; @@ -67,7 +68,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 +87,7 @@ final class PhotoRecCarverFileIngestModule implements FileIngestModule { private Path rootOutputDirPath; private File executableFile; private IngestServices services; - + /** * @inheritDoc */ @@ -122,8 +124,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 +189,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 +225,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 +233,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 +245,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 +265,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 +291,23 @@ 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 { Path path = Paths.get(Case.getCurrentCase().getModuleDirectory(), PhotoRecCarverIngestModuleFactory.getModuleName()); + if (HandleUNC.isUNC(path)) { + // if the UNC path is using an IP address, convert to hostname + path = HandleUNC.getInstance().ipToHostName(path); + } 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 +331,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;