/* * Autopsy Forensic Browser * * Copyright 2011-2016 Basis Technology Corp. * Contact: carrier <at> sleuthkit <dot> org * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this localFile 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.externalresults; import java.io.File; import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.logging.Level; import org.openide.util.NbBundle; import org.openide.util.NbBundle.Messages; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.services.Blackboard; import org.sleuthkit.autopsy.casemodule.services.FileManager; import org.sleuthkit.autopsy.coreutils.ErrorInfo; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil; import org.sleuthkit.autopsy.ingest.IngestServices; import org.sleuthkit.autopsy.ingest.ModuleContentEvent; import org.sleuthkit.autopsy.ingest.ModuleDataEvent; import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardAttribute; import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.DerivedFile; import org.sleuthkit.datamodel.SleuthkitCase; import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskData; import org.sleuthkit.datamodel.TskDataException; /** * Uses a standard representation of results data (e.g., artifacts, derived * files, reports) to import results generated by a process external to Autopsy * into Autopsy. */ public final class ExternalResultsImporter { private static final Logger logger = Logger.getLogger(ExternalResultsImporter.class.getName()); private static final HashSet<Integer> standardArtifactTypeIds = new HashSet<>(); private final List<ErrorInfo> errors = new ArrayList<>(); private Blackboard blackboard; static { for (BlackboardArtifact.ARTIFACT_TYPE artifactType : BlackboardArtifact.ARTIFACT_TYPE.values()) { standardArtifactTypeIds.add(artifactType.getTypeID()); } } /** * Import results generated by a process external to Autopsy into Autopsy. * * @param results A standard representation of results data (e.g., * artifacts, derived files, reports)from the data source. * * @return A collection of error messages, possibly empty. The error * messages are already logged but are provided to allow the caller * to provide additional user feedback via the Autopsy user * interface. */ public List<ErrorInfo> importResults(ExternalResults results) { blackboard = Case.getCurrentCase().getServices().getBlackboard(); // Import files first, they may be artifactData sources. importDerivedFiles(results); importArtifacts(results); importReports(results); List<ErrorInfo> importErrors = new ArrayList<>(this.errors); this.errors.clear(); return importErrors; } private void importDerivedFiles(ExternalResults results) { FileManager fileManager = Case.getCurrentCase().getServices().getFileManager(); for (ExternalResults.DerivedFile fileData : results.getDerivedFiles()) { String localPath = fileData.getLocalPath(); try { File localFile = new File(localPath); if (localFile.exists()) { String relativePath = this.getPathRelativeToCaseFolder(localPath); if (!relativePath.isEmpty()) { String parentFilePath = fileData.getParentPath(); AbstractFile parentFile = findFileInCaseDatabase(parentFilePath); if (parentFile != null) { DerivedFile derivedFile = fileManager.addDerivedFile(localFile.getName(), relativePath, localFile.length(), 0, 0, 0, 0, // Do not currently have file times for derived files from external processes. true, parentFile, "", "", "", "", // Not currently providing derivation info for derived files from external processes. TskData.EncodingType.NONE); // Don't allow external encoded files IngestServices.getInstance().fireModuleContentEvent(new ModuleContentEvent(derivedFile)); } else { String errorMessage = NbBundle.getMessage(this.getClass(), "ExternalResultsImporter.importDerivedFiles.errMsg1.text", localPath, parentFilePath); ExternalResultsImporter.logger.log(Level.SEVERE, errorMessage); this.errors.add(new ErrorInfo(ExternalResultsImporter.class.getName(), errorMessage)); } } } else { String errorMessage = NbBundle.getMessage(this.getClass(), "ExternalResultsImporter.importDerivedFiles.errMsg2.text", localPath); ExternalResultsImporter.logger.log(Level.SEVERE, errorMessage); this.errors.add(new ErrorInfo(ExternalResultsImporter.class.getName(), errorMessage)); } } catch (TskCoreException ex) { String errorMessage = NbBundle.getMessage(this.getClass(), "ExternalResultsImporter.importDerivedFiles.errMsg3.text", localPath); ExternalResultsImporter.logger.log(Level.SEVERE, errorMessage, ex); this.errors.add(new ErrorInfo(ExternalResultsImporter.class.getName(), errorMessage, ex)); } } } @Messages({"ExternalResultsImporter.indexError.message=Failed to index imported artifact for keyword search."}) private void importArtifacts(ExternalResults results) { SleuthkitCase caseDb = Case.getCurrentCase().getSleuthkitCase(); for (ExternalResults.Artifact artifactData : results.getArtifacts()) { try { // Add the artifact to the case database. int artifactTypeId = caseDb.getArtifactType(artifactData.getType()).getTypeID(); if (artifactTypeId == -1) { artifactTypeId = caseDb.addBlackboardArtifactType(artifactData.getType(), artifactData.getType()).getTypeID(); } Content sourceFile = findFileInCaseDatabase(artifactData.getSourceFilePath()); if (sourceFile != null) { BlackboardArtifact artifact = sourceFile.newArtifact(artifactTypeId); // Add the artifact's attributes to the case database. Collection<BlackboardAttribute> attributes = new ArrayList<>(); for (ExternalResults.ArtifactAttribute attributeData : artifactData.getAttributes()) { BlackboardAttribute.Type attributeType = caseDb.getAttributeType(attributeData.getType()); if (attributeType == null) { switch (attributeData.getValueType()) { case "text": //NON-NLS attributeType = caseDb.addArtifactAttributeType(attributeData.getType(), BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.fromLabel("String"), attributeData.getType()); //NON-NLS break; case "int32": //NON-NLS attributeType = caseDb.addArtifactAttributeType(attributeData.getType(), BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.fromLabel("Integer"), attributeData.getType()); //NON-NLS break; case "int64": //NON-NLS attributeType = caseDb.addArtifactAttributeType(attributeData.getType(), BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.fromLabel("Long"), attributeData.getType()); //NON-NLS break; case "double": //NON-NLS attributeType = caseDb.addArtifactAttributeType(attributeData.getType(), BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.fromLabel("Double"), attributeData.getType()); //NON-NLS break; case "datetime": //NON-NLS attributeType = caseDb.addArtifactAttributeType(attributeData.getType(), BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.fromLabel("DateTime"), attributeData.getType()); //NON-NLS } } switch (attributeData.getValueType()) { case "text": //NON-NLS attributes.add(new BlackboardAttribute(attributeType, attributeData.getSourceModule(), attributeData.getValue())); break; case "int32": //NON-NLS int intValue = Integer.parseInt(attributeData.getValue()); attributes.add(new BlackboardAttribute(attributeType, attributeData.getSourceModule(), intValue)); break; case "int64": //NON-NLS long longValue = Long.parseLong(attributeData.getValue()); attributes.add(new BlackboardAttribute(attributeType, attributeData.getSourceModule(), longValue)); break; case "double": //NON-NLS double doubleValue = Double.parseDouble(attributeData.getValue()); attributes.add(new BlackboardAttribute(attributeType, attributeData.getSourceModule(), doubleValue)); break; case "datetime": //NON-NLS long dateTimeValue = Long.parseLong(attributeData.getValue()); attributes.add(new BlackboardAttribute(attributeType, attributeData.getSourceModule(), dateTimeValue)); break; default: String errorMessage = NbBundle.getMessage(this.getClass(), "ExternalResultsImporter.importArtifacts.caseErrMsg1.text", attributeData.getType(), attributeData.getValue(), artifactData.getType(), artifactData.getSourceFilePath(), attributeData.getValueType()); ExternalResultsImporter.logger.log(Level.SEVERE, errorMessage); this.errors.add(new ErrorInfo(ExternalResultsImporter.class.getName(), errorMessage)); break; } } artifact.addAttributes(attributes); try { // index the artifact for keyword search blackboard.indexArtifact(artifact); } catch (Blackboard.BlackboardException ex) { logger.log(Level.SEVERE, "Unable to index blackboard artifact " + artifact.getArtifactID(), ex); //NON-NLS MessageNotifyUtil.Notify.error( Bundle.ExternalResultsImporter_indexError_message(), artifact.getDisplayName()); } if (standardArtifactTypeIds.contains(artifactTypeId)) { IngestServices.getInstance().fireModuleDataEvent(new ModuleDataEvent(this.getClass().getSimpleName(), BlackboardArtifact.ARTIFACT_TYPE.fromID(artifactTypeId))); } } else { String errorMessage = NbBundle.getMessage(this.getClass(), "ExternalResultsImporter.importArtifacts.errMsg1.text", artifactData.getType(), artifactData.getSourceFilePath()); ExternalResultsImporter.logger.log(Level.SEVERE, errorMessage); this.errors.add(new ErrorInfo(ExternalResultsImporter.class.getName(), errorMessage)); } } catch (TskCoreException | TskDataException ex) { String errorMessage = NbBundle.getMessage(this.getClass(), "ExternalResultsImporter.importArtifacts.errMsg2.text", artifactData.getType(), artifactData.getSourceFilePath()); ExternalResultsImporter.logger.log(Level.SEVERE, errorMessage, ex); this.errors.add(new ErrorInfo(ExternalResultsImporter.class.getName(), errorMessage, ex)); } } } private void importReports(ExternalResults results) { for (ExternalResults.Report report : results.getReports()) { String reportPath = report.getLocalPath(); try { File reportFile = new File(reportPath); if (reportFile.exists()) { Case.getCurrentCase().addReport(reportPath, report.getSourceModuleName(), report.getReportName()); } else { String errorMessage = NbBundle.getMessage(this.getClass(), "ExternalResultsImporter.importReports.errMsg1.text", reportPath); ExternalResultsImporter.logger.log(Level.SEVERE, errorMessage); this.errors.add(new ErrorInfo(ExternalResultsImporter.class.getName(), errorMessage)); } } catch (TskCoreException ex) { String errorMessage = NbBundle.getMessage(this.getClass(), "ExternalResultsImporter.importReports.errMsg2.text", reportPath); ExternalResultsImporter.logger.log(Level.SEVERE, errorMessage, ex); this.errors.add(new ErrorInfo(ExternalResultsImporter.class.getName(), errorMessage, ex)); } } } private AbstractFile findFileInCaseDatabase(String filePath) throws TskCoreException { AbstractFile file = null; // Split the path into the file name and the parent path. String fileName = filePath; String parentPath = ""; int charPos = filePath.lastIndexOf("/"); if (charPos >= 0) { fileName = filePath.substring(charPos + 1); parentPath = filePath.substring(0, charPos + 1); } // Find the file. String condition = "name='" + fileName + "' AND parent_path='" + parentPath + "'"; //NON-NLS List<AbstractFile> files = Case.getCurrentCase().getSleuthkitCase().findAllFilesWhere(condition); if (!files.isEmpty()) { file = files.get(0); if (files.size() > 1) { String errorMessage = NbBundle.getMessage(this.getClass(), "ExternalResultsImporter.findFileInCaseDatabase.errMsg1.text", filePath); this.recordError(errorMessage); } } return file; } private String getPathRelativeToCaseFolder(String localPath) { String relativePath = ""; String caseDirectoryPath = Case.getCurrentCase().getCaseDirectory(); Path path = Paths.get(localPath); if (path.isAbsolute()) { Path pathBase = Paths.get(caseDirectoryPath); try { Path pathRelative = pathBase.relativize(path); relativePath = pathRelative.toString(); } catch (IllegalArgumentException ex) { String errorMessage = NbBundle.getMessage(this.getClass(), "ExternalResultsImporter.getPathRelativeToCaseFolder.errMsg1.text", localPath, caseDirectoryPath); this.recordError(errorMessage, ex); } } else { String errorMessage = NbBundle.getMessage(this.getClass(), "ExternalResultsImporter.getPathRelativeToCaseFolder.errMsg2.text", localPath, caseDirectoryPath); this.recordError(errorMessage); } return relativePath; } private void recordError(String errorMessage) { ExternalResultsImporter.logger.log(Level.SEVERE, errorMessage); this.errors.add(new ErrorInfo(this.getClass().getName(), errorMessage)); } private void recordError(String errorMessage, Exception ex) { ExternalResultsImporter.logger.log(Level.SEVERE, errorMessage, ex); this.errors.add(new ErrorInfo(this.getClass().getName(), errorMessage)); } }