mirror of
https://github.com/overcuriousity/autopsy-flatpak.git
synced 2025-07-06 21:00:22 +00:00
1355 lines
64 KiB
Java
1355 lines
64 KiB
Java
/*
|
|
* Autopsy Forensic Browser
|
|
*
|
|
* Copyright 2015 Basis Technology Corp.
|
|
* Contact: carrier <at> sleuthkit <dot> 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.experimental.configuration;
|
|
|
|
import java.io.File;
|
|
import java.io.IOException;
|
|
import java.io.PrintWriter;
|
|
import java.io.BufferedReader;
|
|
import java.io.FileReader;
|
|
import java.io.FileInputStream;
|
|
import java.io.FileNotFoundException;
|
|
import java.io.UnsupportedEncodingException;
|
|
import java.util.zip.CRC32;
|
|
import java.nio.file.Files;
|
|
import java.nio.file.Path;
|
|
import java.nio.file.Paths;
|
|
import java.util.logging.Level;
|
|
import java.util.ArrayList;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
import java.util.HashMap;
|
|
import java.util.concurrent.TimeUnit;
|
|
import java.util.prefs.BackingStoreException;
|
|
import org.apache.commons.io.FileUtils;
|
|
import org.sleuthkit.autopsy.core.UserPreferences;
|
|
import org.sleuthkit.autopsy.coreutils.Logger;
|
|
import org.sleuthkit.autopsy.coreutils.PlatformUtil;
|
|
import org.sleuthkit.autopsy.coreutils.FileUtil;
|
|
import org.sleuthkit.autopsy.ingest.IngestJobSettings;
|
|
import org.sleuthkit.autopsy.keywordsearch.KeywordListsManager;
|
|
import org.sleuthkit.autopsy.modules.hashdatabase.HashDbManager;
|
|
import org.sleuthkit.datamodel.TskCoreException;
|
|
import org.sleuthkit.autopsy.core.ServicesMonitor;
|
|
import org.sleuthkit.autopsy.experimental.configuration.AutoIngestSettingsPanel.UpdateConfigSwingWorker;
|
|
import org.sleuthkit.autopsy.coordinationservice.CoordinationService;
|
|
import org.sleuthkit.autopsy.coordinationservice.CoordinationService.CategoryNode;
|
|
import org.sleuthkit.autopsy.coordinationservice.CoordinationService.Lock;
|
|
import org.sleuthkit.autopsy.coordinationservice.CoordinationService.CoordinationServiceException;
|
|
|
|
/*
|
|
* A utility class for loading and saving shared configuration data
|
|
*/
|
|
public class SharedConfiguration {
|
|
|
|
// Files
|
|
private static final String AUTO_MODE_CONTEXT_FILE = "AutoModeContext.properties"; //NON-NLS
|
|
private static final String USER_DEFINED_TYPE_DEFINITIONS_FILE = "UserFileTypeDefinitions.settings"; //NON-NLS
|
|
private static final String USER_DEFINED_TYPE_DEFINITIONS_FILE_LEGACY = "UserFileTypeDefinitions.xml"; //NON-NLS
|
|
private static final String INTERESTING_FILES_SET_DEFS_FILE = "InterestingFileSets.settings"; //NON-NLS
|
|
private static final String INTERESTING_FILES_SET_DEFS_FILE_LEGACY = "InterestingFilesSetDefs.xml"; //NON-NLS
|
|
private static final String KEYWORD_SEARCH_SETTINGS = "keywords.settings"; //NON-NLS
|
|
private static final String KEYWORD_SEARCH_SETTINGS_LEGACY = "keywords.xml"; //NON-NLS
|
|
private static final String KEYWORD_SEARCH_GENERAL_LEGACY = "KeywordSearch.properties"; //NON-NLS
|
|
private static final String KEYWORD_SEARCH_NSRL_LEGACY = "KeywordSearch_NSRL.properties"; //NON-NLS
|
|
private static final String KEYWORD_SEARCH_OPTIONS_LEGACY = "KeywordSearch_Options.properties"; //NON-NLS
|
|
private static final String KEYWORD_SEARCH_SCRIPTS_LEGACY = "KeywordSearch_Scripts.properties"; //NON-NLS
|
|
private static final String FILE_EXT_MISMATCH_SETTINGS = "mismatch_config.settings"; //NON-NLS
|
|
private static final String FILE_EXT_MISMATCH_SETTINGS_LEGACY = "mismatch_config.xml"; //NON-NLS
|
|
private static final String ANDROID_TRIAGE = "AndroidTriage_Options.properties"; //NON-NLS
|
|
private static final String GENERAL_PROPERTIES = "core.properties"; //NON-NLS
|
|
private static final String AUTO_INGEST_PROPERTIES = "AutoIngest.properties"; //NON-NLS
|
|
private static final String HASHDB_CONFIG_FILE_NAME = "hashLookup.settings"; //NON-NLS
|
|
private static final String HASHDB_CONFIG_FILE_NAME_LEGACY = "hashsets.xml"; //NON-NLS
|
|
public static final String FILE_EXPORTER_SETTINGS_FILE = "fileexporter.settings"; //NON-NLS
|
|
private static final String CENTRAL_REPOSITORY_PROPERTIES_FILE = "CentralRepository.properties"; //NON-NLS
|
|
private static final String SHARED_CONFIG_VERSIONS = "SharedConfigVersions.txt"; //NON-NLS
|
|
|
|
// Folders
|
|
private static final String AUTO_MODE_FOLDER = "AutoModeContext"; //NON-NLS
|
|
private static final String REMOTE_HASH_FOLDER = "hashDb"; //NON-NLS
|
|
private static final String PREFERENCES_FOLDER = "Preferences"; //NON-NLS
|
|
public static final String FILE_EXPORTER_FOLDER = "Automated File Exporter"; //NON-NLS
|
|
|
|
private static final String UPLOAD_IN_PROGRESS_FILE = "uploadInProgress"; // NON-NLS
|
|
private static final String moduleDirPath = PlatformUtil.getUserConfigDirectory();
|
|
private static final Logger logger = Logger.getLogger(SharedConfiguration.class.getName());
|
|
|
|
private final UpdateConfigSwingWorker swingWorker;
|
|
private UserPreferences.SelectedMode mode;
|
|
private String sharedConfigFolder;
|
|
private int fileIngestThreads;
|
|
private boolean sharedConfigMaster;
|
|
private boolean showToolsWarning;
|
|
private boolean displayLocalTime;
|
|
private boolean hideKnownFilesInDataSource;
|
|
private boolean hideKnownFilesInViews;
|
|
private boolean hideSlackFilesInDataSource;
|
|
private boolean hideSlackFilesInViews;
|
|
private boolean keepPreferredViewer;
|
|
|
|
/**
|
|
* Exception type thrown by shared configuration.
|
|
*/
|
|
public final static class SharedConfigurationException extends Exception {
|
|
|
|
private static final long serialVersionUID = 1L;
|
|
|
|
private SharedConfigurationException(String message) {
|
|
super(message);
|
|
}
|
|
|
|
private SharedConfigurationException(String message, Throwable cause) {
|
|
super(message, cause);
|
|
}
|
|
}
|
|
|
|
// Using this so we can indicate whether a read/write failed because the lock file is present,
|
|
// which we need to know to display the correct message in the auto-ingest dashboard about why
|
|
// processing has been paused.
|
|
public enum SharedConfigResult {
|
|
|
|
SUCCESS, LOCKED
|
|
}
|
|
|
|
public SharedConfiguration() {
|
|
swingWorker = null;
|
|
}
|
|
|
|
/**
|
|
* Construct with a SwingWorker reference to allow status update messages to
|
|
* go to the GUI
|
|
*
|
|
* @param worker
|
|
*/
|
|
public SharedConfiguration(UpdateConfigSwingWorker worker) {
|
|
this.swingWorker = worker;
|
|
}
|
|
|
|
private void publishTask(String task) {
|
|
if (swingWorker != null) {
|
|
swingWorker.publishStatus(task);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Upload the current multi-user ingest settings to a shared folder.
|
|
*
|
|
* @throws SharedConfigurationException
|
|
* @throws CoordinationServiceException
|
|
* @throws InterruptedException
|
|
*/
|
|
public SharedConfigResult uploadConfiguration() throws SharedConfigurationException, CoordinationServiceException, InterruptedException {
|
|
publishTask("Starting shared configuration upload");
|
|
|
|
File remoteFolder = getSharedFolder();
|
|
|
|
try (Lock writeLock = CoordinationService.getInstance().tryGetExclusiveLock(CategoryNode.CONFIG, remoteFolder.getAbsolutePath(), 30, TimeUnit.MINUTES)) {
|
|
if (writeLock == null) {
|
|
logger.log(Level.INFO, String.format("Failed to lock %s - another node is currently uploading or downloading configuration", remoteFolder.getAbsolutePath()));
|
|
return SharedConfigResult.LOCKED;
|
|
}
|
|
|
|
// Make sure the local settings have been initialized
|
|
if (!isConfigFolderPopulated(new File(moduleDirPath), false)) {
|
|
logger.log(Level.INFO, "Local configuration has not been initialized.");
|
|
throw new SharedConfigurationException("Local configuration has not been initialized. Please verify/update and save the settings using the Ingest Module Settings button and then retry the upload.");
|
|
}
|
|
|
|
// Write a file to indicate that uploading is in progress. If we crash or
|
|
// have an error, this file will remain in the shared folder.
|
|
File uploadInProgress = new File(remoteFolder, UPLOAD_IN_PROGRESS_FILE);
|
|
if (!uploadInProgress.exists()) {
|
|
try {
|
|
Files.createFile(uploadInProgress.toPath());
|
|
} catch (IOException ex) {
|
|
throw new SharedConfigurationException(String.format("Failed to create %s", uploadInProgress.toPath()), ex);
|
|
}
|
|
}
|
|
|
|
// Make sure all recent changes are saved to the preference file
|
|
// Current testing suggests that we do not need to do this for the ingest settings
|
|
// because there is a longer delay between setting them and copying the files.
|
|
try {
|
|
// Make sure all recent changes are saved to the preference file
|
|
// Current testing suggests that we do not need to do this for the ingest settings
|
|
// because there is a longer delay between setting them and copying the files.
|
|
UserPreferences.saveToStorage();
|
|
} catch (BackingStoreException ex) {
|
|
throw new SharedConfigurationException("Failed to save shared configuration settings", ex);
|
|
}
|
|
|
|
uploadAutoModeContextSettings(remoteFolder);
|
|
uploadEnabledModulesSettings(remoteFolder);
|
|
uploadFileTypeSettings(remoteFolder);
|
|
uploadInterestingFilesSettings(remoteFolder);
|
|
uploadKeywordSearchSettings(remoteFolder);
|
|
uploadFileExtMismatchSettings(remoteFolder);
|
|
uploadAndroidTriageSettings(remoteFolder);
|
|
uploadMultiUserAndGeneralSettings(remoteFolder);
|
|
uploadHashDbSettings(remoteFolder);
|
|
uploadFileExporterSettings(remoteFolder);
|
|
uploadCentralRepositorySettings(remoteFolder);
|
|
uploadObjectDetectionClassifiers(remoteFolder);
|
|
uploadPythonModules(remoteFolder);
|
|
|
|
try {
|
|
Files.deleteIfExists(uploadInProgress.toPath());
|
|
} catch (IOException ex) {
|
|
throw new SharedConfigurationException(String.format("Failed to delete %s", uploadInProgress.toPath()), ex);
|
|
}
|
|
}
|
|
|
|
return SharedConfigResult.SUCCESS;
|
|
}
|
|
|
|
/**
|
|
* Download the multi-user settings from a shared folder.
|
|
*
|
|
* @throws SharedConfigurationException
|
|
* @throws InterruptedException
|
|
*/
|
|
public synchronized SharedConfigResult downloadConfiguration() throws SharedConfigurationException, InterruptedException {
|
|
publishTask("Starting shared configuration download");
|
|
|
|
// Save local settings that should not get overwritten
|
|
saveNonSharedSettings();
|
|
|
|
File remoteFolder = getSharedFolder();
|
|
|
|
try (Lock readLock = CoordinationService.getInstance().tryGetSharedLock(CategoryNode.CONFIG, remoteFolder.getAbsolutePath(), 30, TimeUnit.MINUTES)) {
|
|
if (readLock == null) {
|
|
return SharedConfigResult.LOCKED;
|
|
}
|
|
|
|
// Make sure the shared configuration was last uploaded successfully
|
|
File uploadInProgress = new File(remoteFolder, UPLOAD_IN_PROGRESS_FILE);
|
|
if (uploadInProgress.exists()) {
|
|
logger.log(Level.INFO, String.format("Shared configuration folder %s is corrupt - re-upload configuration", remoteFolder.getAbsolutePath()));
|
|
throw new SharedConfigurationException(String.format("Shared configuration folder %s is corrupt - re-upload configuration", remoteFolder.getAbsolutePath()));
|
|
}
|
|
|
|
// Make sure the shared configuration folder isn't empty
|
|
if (!isConfigFolderPopulated(remoteFolder, true)) {
|
|
logger.log(Level.INFO, String.format("Shared configuration folder %s is missing files / may be empty. Aborting download.", remoteFolder.getAbsolutePath()));
|
|
throw new SharedConfigurationException(String.format("Shared configuration folder %s is missing files / may be empty. Aborting download.", remoteFolder.getAbsolutePath()));
|
|
}
|
|
|
|
try {
|
|
/* Make sure all recent changes are saved to the preference file.
|
|
This also releases open file handles to the preference files. If this
|
|
is not done, then occasionally downloading of shared configuration
|
|
fails silently, likely because Java/OS is still holding the file handle.
|
|
The problem manifests itself by some of the old/original configuration files
|
|
sticking around after shared configuration has seemingly been successfully
|
|
updated. */
|
|
UserPreferences.saveToStorage();
|
|
} catch (BackingStoreException ex) {
|
|
throw new SharedConfigurationException("Failed to save shared configuration settings", ex);
|
|
}
|
|
|
|
downloadAutoModeContextSettings(remoteFolder);
|
|
downloadEnabledModuleSettings(remoteFolder);
|
|
downloadFileTypeSettings(remoteFolder);
|
|
downloadInterestingFilesSettings(remoteFolder);
|
|
downloadKeywordSearchSettings(remoteFolder);
|
|
downloadFileExtMismatchSettings(remoteFolder);
|
|
downloadAndroidTriageSettings(remoteFolder);
|
|
downloadFileExporterSettings(remoteFolder);
|
|
downloadCentralRepositorySettings(remoteFolder);
|
|
downloadObjectDetectionClassifiers(remoteFolder);
|
|
downloadPythonModules(remoteFolder);
|
|
|
|
// Download general settings, then restore the current
|
|
// values for the unshared fields
|
|
downloadMultiUserAndGeneralSettings(remoteFolder);
|
|
try {
|
|
UserPreferences.reloadFromStorage();
|
|
} catch (BackingStoreException ex) {
|
|
throw new SharedConfigurationException("Failed to read shared configuration settings", ex);
|
|
}
|
|
|
|
restoreNonSharedSettings();
|
|
downloadHashDbSettings(remoteFolder);
|
|
} catch (CoordinationServiceException ex) {
|
|
throw new SharedConfigurationException(String.format("Coordination service error acquiring exclusive lock on shared configuration source %s", remoteFolder.getAbsolutePath()), ex);
|
|
}
|
|
|
|
// Check Solr service
|
|
if (!isServiceUp(ServicesMonitor.Service.REMOTE_KEYWORD_SEARCH.toString())) {
|
|
throw new SharedConfigurationException("Keyword search service is down");
|
|
}
|
|
|
|
// Check PostgreSQL service
|
|
if (!isServiceUp(ServicesMonitor.Service.REMOTE_CASE_DATABASE.toString())) {
|
|
throw new SharedConfigurationException("Case database server is down");
|
|
}
|
|
|
|
// Check ActiveMQ service
|
|
if (!isServiceUp(ServicesMonitor.Service.MESSAGING.toString())) {
|
|
throw new SharedConfigurationException("Messaging service is down");
|
|
}
|
|
|
|
// Check input folder permissions
|
|
String inputFolder = AutoIngestUserPreferences.getAutoModeImageFolder();
|
|
if (!FileUtil.hasReadWriteAccess(Paths.get(inputFolder))) {
|
|
throw new SharedConfigurationException("Cannot read input folder " + inputFolder + ". Check that the folder exists and that you have permissions to access it.");
|
|
}
|
|
|
|
// Check output folder permissions
|
|
String outputFolder = AutoIngestUserPreferences.getAutoModeResultsFolder();
|
|
if (!FileUtil.hasReadWriteAccess(Paths.get(outputFolder))) {
|
|
throw new SharedConfigurationException("Cannot read output folder " + outputFolder + ". Check that the folder exists and that you have permissions to access it.");
|
|
}
|
|
|
|
return SharedConfigResult.SUCCESS;
|
|
}
|
|
|
|
/**
|
|
* Tests service of interest to verify that it is running.
|
|
*
|
|
* @param serviceName Name of the service.
|
|
*
|
|
* @return True if the service is running, false otherwise.
|
|
*/
|
|
private boolean isServiceUp(String serviceName) {
|
|
try {
|
|
return (ServicesMonitor.getInstance().getServiceStatus(serviceName).equals(ServicesMonitor.ServiceStatus.UP.toString()));
|
|
} catch (ServicesMonitor.ServicesMonitorException ex) {
|
|
logger.log(Level.SEVERE, String.format("Problem checking service status for %s", serviceName), ex);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Save any settings that should not be overwritten by the shared
|
|
* configuration.
|
|
*/
|
|
private void saveNonSharedSettings() {
|
|
sharedConfigMaster = AutoIngestUserPreferences.getSharedConfigMaster();
|
|
sharedConfigFolder = AutoIngestUserPreferences.getSharedConfigFolder();
|
|
showToolsWarning = AutoIngestUserPreferences.getShowToolsWarning();
|
|
displayLocalTime = UserPreferences.displayTimesInLocalTime();
|
|
hideKnownFilesInDataSource = UserPreferences.hideKnownFilesInDataSourcesTree();
|
|
hideKnownFilesInViews = UserPreferences.hideKnownFilesInViewsTree();
|
|
keepPreferredViewer = UserPreferences.keepPreferredContentViewer();
|
|
fileIngestThreads = UserPreferences.numberOfFileIngestThreads();
|
|
hideSlackFilesInDataSource = UserPreferences.hideSlackFilesInDataSourcesTree();
|
|
hideSlackFilesInViews = UserPreferences.hideSlackFilesInViewsTree();
|
|
}
|
|
|
|
/**
|
|
* Restore the settings that may have been overwritten.
|
|
*/
|
|
private void restoreNonSharedSettings() {
|
|
AutoIngestUserPreferences.setSharedConfigFolder(sharedConfigFolder);
|
|
AutoIngestUserPreferences.setSharedConfigMaster(sharedConfigMaster);
|
|
AutoIngestUserPreferences.setShowToolsWarning(showToolsWarning);
|
|
UserPreferences.setDisplayTimesInLocalTime(displayLocalTime);
|
|
UserPreferences.setHideKnownFilesInDataSourcesTree(hideKnownFilesInDataSource);
|
|
UserPreferences.setHideKnownFilesInViewsTree(hideKnownFilesInViews);
|
|
UserPreferences.setKeepPreferredContentViewer(keepPreferredViewer);
|
|
UserPreferences.setNumberOfFileIngestThreads(fileIngestThreads);
|
|
UserPreferences.setHideSlackFilesInDataSourcesTree(hideSlackFilesInDataSource);
|
|
UserPreferences.setHideSlackFilesInViewsTree(hideSlackFilesInViews);
|
|
}
|
|
|
|
/**
|
|
* Get the base folder being used to store the shared config settings.
|
|
*
|
|
* @return The shared configuration folder
|
|
*
|
|
* @throws SharedConfigurationException
|
|
*/
|
|
private static File getSharedFolder() throws SharedConfigurationException {
|
|
// Check that the shared folder is set and exists
|
|
String remoteConfigFolderPath = AutoIngestUserPreferences.getSharedConfigFolder();
|
|
if (remoteConfigFolderPath.isEmpty()) {
|
|
logger.log(Level.SEVERE, "Shared configuration folder is not set.");
|
|
throw new SharedConfigurationException("Shared configuration folder is not set.");
|
|
}
|
|
File remoteFolder = new File(remoteConfigFolderPath);
|
|
if (!remoteFolder.exists()) {
|
|
logger.log(Level.SEVERE, "Shared configuration folder {0} does not exist", remoteConfigFolderPath);
|
|
throw new SharedConfigurationException("Shared configuration folder " + remoteConfigFolderPath + " does not exist");
|
|
}
|
|
return remoteFolder;
|
|
}
|
|
|
|
/**
|
|
* Do a basic check to determine whether settings have been stored to the
|
|
* given folder. There may still be missing files/errors, but this will stop
|
|
* empty/corrupt settings from overwriting local settings or being uploaded.
|
|
*
|
|
* Currently we check for: - A non-empty AutoModeContext folder - An
|
|
* AutoModeContext properties file
|
|
*
|
|
* @param folder Folder to check the contents of
|
|
* @param isSharedFolder True if the folder being tested is the shared
|
|
* folder, false if its the local folder
|
|
*
|
|
* @return true if the folder appears to have been initialized, false
|
|
* otherwise
|
|
*/
|
|
private static boolean isConfigFolderPopulated(File folder, boolean isSharedFolder) {
|
|
|
|
if (!folder.exists()) {
|
|
return false;
|
|
}
|
|
|
|
// Check that the context directory exists and is not empty
|
|
File contextDir;
|
|
if (isSharedFolder) {
|
|
contextDir = new File(folder, AUTO_MODE_FOLDER);
|
|
} else {
|
|
IngestJobSettings ingestJobSettings = new IngestJobSettings(AutoIngestUserPreferences.getAutoModeIngestModuleContextString());
|
|
contextDir = ingestJobSettings.getSavedModuleSettingsFolder().toFile();
|
|
}
|
|
|
|
if ((!contextDir.exists()) || (!contextDir.isDirectory())) {
|
|
return false;
|
|
}
|
|
if (contextDir.listFiles().length == 0) {
|
|
return false;
|
|
}
|
|
|
|
// Check that the automode context properties file exists
|
|
File contextProperties = new File(folder, AUTO_MODE_CONTEXT_FILE);
|
|
return contextProperties.exists();
|
|
}
|
|
|
|
/**
|
|
* Copy a local settings file to the remote folder.
|
|
*
|
|
* @param fileName Name of the file to copy
|
|
* @param localFolder Local settings folder
|
|
* @param remoteFolder Shared settings folder
|
|
* @param missingFileOk True if it's not an error if the source file is not
|
|
* found
|
|
*
|
|
* @throws SharedConfigurationException
|
|
*/
|
|
private static void copyToRemoteFolder(String fileName, String localFolder, File remoteFolder, boolean missingFileOk) throws SharedConfigurationException {
|
|
logger.log(Level.INFO, "Uploading {0} to {1}", new Object[]{fileName, remoteFolder.getAbsolutePath()});
|
|
File localFile = new File(localFolder, fileName);
|
|
if (!localFile.exists()) {
|
|
Path deleteRemote = Paths.get(remoteFolder.toString(), fileName);
|
|
try {
|
|
if (deleteRemote.toFile().exists()) {
|
|
deleteRemote.toFile().delete();
|
|
}
|
|
} catch (SecurityException ex) {
|
|
logger.log(Level.SEVERE, "Shared configuration {0} does not exist on local node, but unable to remove remote copy", fileName);
|
|
throw new SharedConfigurationException("Shared configuration file " + deleteRemote.toString() + " could not be deleted.");
|
|
}
|
|
if (!missingFileOk) {
|
|
logger.log(Level.SEVERE, "Local configuration file {0} does not exist", localFile.getAbsolutePath());
|
|
throw new SharedConfigurationException("Local configuration file " + localFile.getAbsolutePath() + " does not exist");
|
|
} else {
|
|
logger.log(Level.INFO, "Local configuration file {0} does not exist", localFile.getAbsolutePath());
|
|
return;
|
|
}
|
|
}
|
|
|
|
try {
|
|
FileUtils.copyFileToDirectory(localFile, remoteFolder);
|
|
} catch (IOException ex) {
|
|
throw new SharedConfigurationException(String.format("Failed to copy %s to %s", localFile.getAbsolutePath(), remoteFolder.getAbsolutePath()), ex);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Copy a shared settings file to the local settings folder.
|
|
*
|
|
* @param fileName Name of the file to copy
|
|
* @param localFolder Local settings folder
|
|
* @param remoteFolder Shared settings folder
|
|
* @param missingFileOk True if it's not an error if the source file is not
|
|
* found
|
|
*
|
|
* @throws SharedConfigurationException
|
|
*/
|
|
private static void copyToLocalFolder(String fileName, String localFolder, File remoteFolder, boolean missingFileOk) throws SharedConfigurationException {
|
|
logger.log(Level.INFO, "Downloading {0} from {1}", new Object[]{fileName, remoteFolder.getAbsolutePath()});
|
|
|
|
File remoteFile = new File(remoteFolder, fileName);
|
|
if (!remoteFile.exists()) {
|
|
Path deleteLocal = Paths.get(localFolder, fileName);
|
|
try {
|
|
if (deleteLocal.toFile().exists()) {
|
|
deleteLocal.toFile().delete();
|
|
}
|
|
} catch (SecurityException ex) {
|
|
logger.log(Level.SEVERE, "Shared configuration {0} does not exist on remote node, but unable to remove local copy", fileName);
|
|
throw new SharedConfigurationException("Shared configuration file " + deleteLocal.toString() + " could not be deleted.");
|
|
}
|
|
if (!missingFileOk) {
|
|
logger.log(Level.SEVERE, "Shared configuration file {0} does not exist", remoteFile.getAbsolutePath());
|
|
throw new SharedConfigurationException("Shared configuration file " + remoteFile.getAbsolutePath() + " does not exist");
|
|
} else {
|
|
logger.log(Level.INFO, "Shared configuration file {0} does not exist", remoteFile.getAbsolutePath());
|
|
return;
|
|
}
|
|
}
|
|
|
|
File localSettingsFolder = new File(localFolder);
|
|
try {
|
|
FileUtils.copyFileToDirectory(remoteFile, localSettingsFolder);
|
|
} catch (IOException ex) {
|
|
throw new SharedConfigurationException(String.format("Failed to copy %s to %s", remoteFile.getAbsolutePath(), localSettingsFolder.getAbsolutePath()), ex);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Copy an entire local settings folder to the remote folder, deleting any existing files.
|
|
*
|
|
* @param localFolder The local folder to copy
|
|
* @param remoteBaseFolder The remote folder that will hold a copy of the original folder
|
|
*
|
|
* @throws SharedConfigurationException
|
|
*/
|
|
private void copyLocalFolderToRemoteFolder(File localFolder, File remoteBaseFolder) throws SharedConfigurationException {
|
|
logger.log(Level.INFO, "Uploading {0} to {1}", new Object[]{localFolder.getAbsolutePath(), remoteBaseFolder.getAbsolutePath()});
|
|
|
|
File newRemoteFolder = new File(remoteBaseFolder, localFolder.getName());
|
|
|
|
if(newRemoteFolder.exists()) {
|
|
try {
|
|
FileUtils.deleteDirectory(newRemoteFolder);
|
|
} catch (IOException ex) {
|
|
logger.log(Level.SEVERE, "Failed to delete remote folder {0}", newRemoteFolder.getAbsolutePath());
|
|
throw new SharedConfigurationException(String.format("Failed to delete remote folder {0}", newRemoteFolder.getAbsolutePath()), ex);
|
|
}
|
|
}
|
|
|
|
try {
|
|
FileUtils.copyDirectoryToDirectory(localFolder, remoteBaseFolder);
|
|
} catch (IOException ex) {
|
|
throw new SharedConfigurationException(String.format("Failed to copy %s to %s", localFolder, remoteBaseFolder.getAbsolutePath()), ex);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Copy an entire remote settings folder to the local folder, deleting any existing files.
|
|
* No error if the remote folder does not exist.
|
|
*
|
|
* @param localFolder The local folder that will be overwritten.
|
|
* @param remoteBaseFolder The remote folder holding the folder that will be copied
|
|
*
|
|
* @throws SharedConfigurationException
|
|
*/
|
|
private void copyRemoteFolderToLocalFolder(File localFolder, File remoteBaseFolder) throws SharedConfigurationException {
|
|
logger.log(Level.INFO, "Downloading {0} from {1}", new Object[]{localFolder.getAbsolutePath(), remoteBaseFolder.getAbsolutePath()});
|
|
|
|
// Clean out the local folder regardless of whether the remote version exists. leave the
|
|
// folder in place since Autopsy expects it to exist.
|
|
if(localFolder.exists()) {
|
|
try {
|
|
FileUtils.cleanDirectory(localFolder);
|
|
} catch (IOException ex) {
|
|
logger.log(Level.SEVERE, "Failed to delete files from local folder {0}", localFolder.getAbsolutePath());
|
|
throw new SharedConfigurationException(String.format("Failed to delete files from local folder {0}", localFolder.getAbsolutePath()), ex);
|
|
}
|
|
}
|
|
|
|
File remoteSubFolder = new File(remoteBaseFolder, localFolder.getName());
|
|
if(! remoteSubFolder.exists()) {
|
|
logger.log(Level.INFO, "{0} does not exist", remoteSubFolder.getAbsolutePath());
|
|
return;
|
|
}
|
|
|
|
try {
|
|
FileUtils.copyDirectory(remoteSubFolder, localFolder);
|
|
} catch (IOException ex) {
|
|
throw new SharedConfigurationException(String.format("Failed to copy %s from %s", localFolder, remoteBaseFolder.getAbsolutePath()), ex);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Upload the basic set of auto-ingest settings to the shared folder.
|
|
*
|
|
* @param remoteFolder Shared settings folder
|
|
*
|
|
* @throws Exception
|
|
*/
|
|
private void uploadAutoModeContextSettings(File remoteFolder) throws SharedConfigurationException {
|
|
logger.log(Level.INFO, "Uploading shared configuration to {0}", remoteFolder.getAbsolutePath());
|
|
publishTask("Uploading AutoModeContext configuration files");
|
|
|
|
// Make a subfolder
|
|
File remoteAutoConfFolder = new File(remoteFolder, AUTO_MODE_FOLDER);
|
|
try {
|
|
if (remoteAutoConfFolder.exists()) {
|
|
FileUtils.deleteDirectory(remoteAutoConfFolder);
|
|
}
|
|
Files.createDirectories(remoteAutoConfFolder.toPath());
|
|
} catch (IOException | SecurityException ex) {
|
|
logger.log(Level.SEVERE, "Failed to create clean shared configuration subfolder " + remoteAutoConfFolder.getAbsolutePath(), ex); //NON-NLS
|
|
throw new SharedConfigurationException("Failed to create clean shared configuration subfolder " + remoteAutoConfFolder.getAbsolutePath());
|
|
}
|
|
|
|
IngestJobSettings ingestJobSettings = new IngestJobSettings(AutoIngestUserPreferences.getAutoModeIngestModuleContextString());
|
|
File localFolder = ingestJobSettings.getSavedModuleSettingsFolder().toFile();
|
|
|
|
if (!localFolder.exists()) {
|
|
logger.log(Level.SEVERE, "Local configuration folder {0} does not exist", localFolder.getAbsolutePath());
|
|
throw new SharedConfigurationException("Local configuration folder " + localFolder.getAbsolutePath() + " does not exist");
|
|
}
|
|
|
|
try {
|
|
FileUtils.copyDirectory(localFolder, remoteAutoConfFolder);
|
|
} catch (IOException ex) {
|
|
throw new SharedConfigurationException(String.format("Failed to copy %s to %s", localFolder.getAbsolutePath(), remoteAutoConfFolder.getAbsolutePath()), ex);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Download the basic set of auto-ingest settings from the shared folder
|
|
*
|
|
* @param remoteFolder Shared settings folder
|
|
*
|
|
* @throws SharedConfigurationException
|
|
*/
|
|
private void downloadAutoModeContextSettings(File remoteFolder) throws SharedConfigurationException {
|
|
logger.log(Level.INFO, "Downloading shared configuration from {0}", remoteFolder.getAbsolutePath());
|
|
publishTask("Downloading AutoModeContext configuration files");
|
|
|
|
// Check that the remote subfolder exists
|
|
File remoteAutoConfFolder = new File(remoteFolder, AUTO_MODE_FOLDER);
|
|
if (!remoteAutoConfFolder.exists()) {
|
|
logger.log(Level.SEVERE, "Shared configuration folder {0} does not exist", remoteAutoConfFolder.getAbsolutePath());
|
|
throw new SharedConfigurationException("Shared configuration folder " + remoteAutoConfFolder.getAbsolutePath() + " does not exist");
|
|
}
|
|
|
|
// Get/create the local subfolder
|
|
IngestJobSettings ingestJobSettings = new IngestJobSettings(AutoIngestUserPreferences.getAutoModeIngestModuleContextString());
|
|
File localFolder = ingestJobSettings.getSavedModuleSettingsFolder().toFile();
|
|
|
|
try {
|
|
if (localFolder.exists()) {
|
|
FileUtils.deleteDirectory(localFolder);
|
|
}
|
|
Files.createDirectories(localFolder.toPath());
|
|
} catch (IOException | SecurityException ex) {
|
|
logger.log(Level.SEVERE, "Failed to create clean local configuration folder " + localFolder.getAbsolutePath(), ex); //NON-NLS
|
|
throw new SharedConfigurationException("Failed to create clean local configuration folder " + localFolder.getAbsolutePath());
|
|
}
|
|
|
|
try {
|
|
FileUtils.copyDirectory(remoteAutoConfFolder, localFolder);
|
|
} catch (IOException ex) {
|
|
throw new SharedConfigurationException(String.format("Failed to copy %s to %s", remoteFolder.getAbsolutePath(), localFolder.getAbsolutePath()), ex);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Upload settings file containing enabled ingest modules.
|
|
*
|
|
* @param remoteFolder Shared settings folder
|
|
*
|
|
* @throws SharedConfigurationException
|
|
*/
|
|
private void uploadEnabledModulesSettings(File remoteFolder) throws SharedConfigurationException {
|
|
publishTask("Uploading enabled module configuration");
|
|
copyToRemoteFolder(AUTO_MODE_CONTEXT_FILE, moduleDirPath, remoteFolder, false);
|
|
}
|
|
|
|
/**
|
|
* Download settings file containing enabled ingest modules.
|
|
*
|
|
* @param remoteFolder Shared settings folder
|
|
*
|
|
* @throws SharedConfigurationException
|
|
*/
|
|
private void downloadEnabledModuleSettings(File remoteFolder) throws SharedConfigurationException {
|
|
publishTask("Downloading enabled module configuration");
|
|
copyToLocalFolder(AUTO_MODE_CONTEXT_FILE, moduleDirPath, remoteFolder, false);
|
|
}
|
|
|
|
/**
|
|
* Upload settings file containing file type settings.
|
|
*
|
|
* @param remoteFolder Shared settings folder
|
|
*
|
|
* @throws SharedConfigurationException
|
|
*/
|
|
private void uploadFileTypeSettings(File remoteFolder) throws SharedConfigurationException {
|
|
publishTask("Uploading FileType module configuration");
|
|
copyToRemoteFolder(USER_DEFINED_TYPE_DEFINITIONS_FILE, moduleDirPath, remoteFolder, true);
|
|
copyToRemoteFolder(USER_DEFINED_TYPE_DEFINITIONS_FILE_LEGACY, moduleDirPath, remoteFolder, true);
|
|
}
|
|
|
|
/**
|
|
* Download settings file containing file type settings.
|
|
*
|
|
* @param remoteFolder Shared settings folder
|
|
*
|
|
* @throws SharedConfigurationException
|
|
*/
|
|
private void downloadFileTypeSettings(File remoteFolder) throws SharedConfigurationException {
|
|
publishTask("Downloading FileType module configuration");
|
|
copyToLocalFolder(USER_DEFINED_TYPE_DEFINITIONS_FILE, moduleDirPath, remoteFolder, true);
|
|
copyToLocalFolder(USER_DEFINED_TYPE_DEFINITIONS_FILE_LEGACY, moduleDirPath, remoteFolder, true);
|
|
}
|
|
|
|
/**
|
|
* Upload settings for the interesting files module.
|
|
*
|
|
* @param remoteFolder Shared settings folder
|
|
*
|
|
* @throws SharedConfigurationException
|
|
*/
|
|
private void uploadInterestingFilesSettings(File remoteFolder) throws SharedConfigurationException {
|
|
publishTask("Uploading InterestingFiles module configuration");
|
|
copyToRemoteFolder(INTERESTING_FILES_SET_DEFS_FILE_LEGACY, moduleDirPath, remoteFolder, true);
|
|
copyToRemoteFolder(INTERESTING_FILES_SET_DEFS_FILE, moduleDirPath, remoteFolder, true);
|
|
}
|
|
|
|
/**
|
|
* Download settings for the interesting files module.
|
|
*
|
|
* @param remoteFolder Shared settings folder
|
|
*
|
|
* @throws SharedConfigurationException
|
|
*/
|
|
private void downloadInterestingFilesSettings(File remoteFolder) throws SharedConfigurationException {
|
|
publishTask("Downloading InterestingFiles module configuration");
|
|
copyToLocalFolder(INTERESTING_FILES_SET_DEFS_FILE_LEGACY, moduleDirPath, remoteFolder, true);
|
|
copyToLocalFolder(INTERESTING_FILES_SET_DEFS_FILE, moduleDirPath, remoteFolder, true);
|
|
}
|
|
|
|
/**
|
|
* Upload settings for the keyword search module.
|
|
*
|
|
* @param remoteFolder Shared settings folder
|
|
*
|
|
* @throws SharedConfigurationException
|
|
*/
|
|
private void uploadKeywordSearchSettings(File remoteFolder) throws SharedConfigurationException {
|
|
publishTask("Uploading KeywordSearch module configuration");
|
|
copyToRemoteFolder(KEYWORD_SEARCH_SETTINGS, moduleDirPath, remoteFolder, true);
|
|
copyToRemoteFolder(KEYWORD_SEARCH_SETTINGS_LEGACY, moduleDirPath, remoteFolder, true);
|
|
copyToRemoteFolder(KEYWORD_SEARCH_GENERAL_LEGACY, moduleDirPath, remoteFolder, true);
|
|
copyToRemoteFolder(KEYWORD_SEARCH_NSRL_LEGACY, moduleDirPath, remoteFolder, true);
|
|
copyToRemoteFolder(KEYWORD_SEARCH_OPTIONS_LEGACY, moduleDirPath, remoteFolder, true);
|
|
copyToRemoteFolder(KEYWORD_SEARCH_SCRIPTS_LEGACY, moduleDirPath, remoteFolder, true);
|
|
}
|
|
|
|
/**
|
|
* Download settings for the keyword search module.
|
|
*
|
|
* @param remoteFolder Shared settings folder
|
|
*
|
|
* @throws SharedConfigurationException
|
|
*/
|
|
private void downloadKeywordSearchSettings(File remoteFolder) throws SharedConfigurationException {
|
|
publishTask("Downloading KeywordSearch module configuration");
|
|
copyToLocalFolder(KEYWORD_SEARCH_SETTINGS, moduleDirPath, remoteFolder, true);
|
|
copyToLocalFolder(KEYWORD_SEARCH_SETTINGS_LEGACY, moduleDirPath, remoteFolder, true);
|
|
copyToLocalFolder(KEYWORD_SEARCH_GENERAL_LEGACY, moduleDirPath, remoteFolder, true);
|
|
copyToLocalFolder(KEYWORD_SEARCH_NSRL_LEGACY, moduleDirPath, remoteFolder, true);
|
|
copyToLocalFolder(KEYWORD_SEARCH_OPTIONS_LEGACY, moduleDirPath, remoteFolder, true);
|
|
copyToLocalFolder(KEYWORD_SEARCH_SCRIPTS_LEGACY, moduleDirPath, remoteFolder, true);
|
|
KeywordListsManager.reloadKeywordLists();
|
|
}
|
|
|
|
/**
|
|
* Upload settings for the file extension mismatch module.
|
|
*
|
|
* @param remoteFolder Shared settings folder
|
|
*
|
|
* @throws SharedConfigurationException
|
|
*/
|
|
private void uploadFileExtMismatchSettings(File remoteFolder) throws SharedConfigurationException {
|
|
publishTask("Uploading File Extension Mismatch module configuration");
|
|
copyToRemoteFolder(FILE_EXT_MISMATCH_SETTINGS, moduleDirPath, remoteFolder, true);
|
|
copyToRemoteFolder(FILE_EXT_MISMATCH_SETTINGS_LEGACY, moduleDirPath, remoteFolder, false);
|
|
}
|
|
|
|
/**
|
|
* Download settings for the file extension mismatch module.
|
|
*
|
|
* @param remoteFolder Shared settings folder
|
|
*
|
|
* @throws SharedConfigurationException
|
|
*/
|
|
private void downloadFileExtMismatchSettings(File remoteFolder) throws SharedConfigurationException {
|
|
publishTask("Downloading File Extension Mismatch module configuration");
|
|
copyToLocalFolder(FILE_EXT_MISMATCH_SETTINGS, moduleDirPath, remoteFolder, true);
|
|
copyToLocalFolder(FILE_EXT_MISMATCH_SETTINGS_LEGACY, moduleDirPath, remoteFolder, false);
|
|
}
|
|
|
|
/**
|
|
* Upload settings for the android triage module.
|
|
*
|
|
* @param remoteFolder Shared settings folder
|
|
*
|
|
* @throws SharedConfigurationException
|
|
*/
|
|
private void uploadAndroidTriageSettings(File remoteFolder) throws SharedConfigurationException {
|
|
publishTask("Uploading Android Triage module configuration");
|
|
copyToRemoteFolder(ANDROID_TRIAGE, moduleDirPath, remoteFolder, true);
|
|
}
|
|
|
|
/**
|
|
* Download settings for the android triage module.
|
|
*
|
|
* @param remoteFolder Shared settings folder
|
|
*
|
|
* @throws SharedConfigurationException
|
|
*/
|
|
private void downloadAndroidTriageSettings(File remoteFolder) throws SharedConfigurationException {
|
|
publishTask("Downloading Android Triage module configuration");
|
|
copyToLocalFolder(ANDROID_TRIAGE, moduleDirPath, remoteFolder, true);
|
|
}
|
|
|
|
/**
|
|
* Upload File Exporter settings.
|
|
*
|
|
* @param remoteFolder Shared settings folder
|
|
*
|
|
* @throws SharedConfigurationException
|
|
*/
|
|
private void uploadFileExporterSettings(File remoteFolder) throws SharedConfigurationException {
|
|
publishTask("Uploading File Exporter configuration");
|
|
File fileExporterFolder = new File(moduleDirPath, FILE_EXPORTER_FOLDER);
|
|
copyToRemoteFolder(FILE_EXPORTER_SETTINGS_FILE, fileExporterFolder.getAbsolutePath(), remoteFolder, true);
|
|
}
|
|
|
|
/**
|
|
* Download File Exporter settings.
|
|
*
|
|
* @param remoteFolder Shared settings folder
|
|
*
|
|
* @throws SharedConfigurationException
|
|
*/
|
|
private void downloadFileExporterSettings(File remoteFolder) throws SharedConfigurationException {
|
|
publishTask("Downloading File Exporter configuration");
|
|
File fileExporterFolder = new File(moduleDirPath, FILE_EXPORTER_FOLDER);
|
|
copyToLocalFolder(FILE_EXPORTER_SETTINGS_FILE, fileExporterFolder.getAbsolutePath(), remoteFolder, true);
|
|
}
|
|
|
|
/**
|
|
* Upload central repository settings.
|
|
*
|
|
* @param remoteFolder Shared settings folder
|
|
*
|
|
* @throws SharedConfigurationException
|
|
*/
|
|
private void uploadCentralRepositorySettings(File remoteFolder) throws SharedConfigurationException {
|
|
publishTask("Uploading central repository configuration");
|
|
copyToRemoteFolder(CENTRAL_REPOSITORY_PROPERTIES_FILE, moduleDirPath, remoteFolder, true);
|
|
}
|
|
|
|
/**
|
|
* Download central repository settings.
|
|
*
|
|
* @param remoteFolder Shared settings folder
|
|
*
|
|
* @throws SharedConfigurationException
|
|
*/
|
|
private void downloadCentralRepositorySettings(File remoteFolder) throws SharedConfigurationException {
|
|
publishTask("Downloading central repository configuration");
|
|
copyToLocalFolder(CENTRAL_REPOSITORY_PROPERTIES_FILE, moduleDirPath, remoteFolder, true);
|
|
}
|
|
|
|
/**
|
|
* Upload multi-user settings and other general Autopsy settings
|
|
*
|
|
* @param remoteFolder Shared settings folder
|
|
*
|
|
* @throws SharedConfigurationException
|
|
*/
|
|
private void uploadMultiUserAndGeneralSettings(File remoteFolder) throws SharedConfigurationException {
|
|
publishTask("Uploading multi user configuration");
|
|
File generalSettingsFolder = Paths.get(moduleDirPath, PREFERENCES_FOLDER, "org", "sleuthkit", "autopsy").toFile();
|
|
copyToRemoteFolder(GENERAL_PROPERTIES, generalSettingsFolder.getAbsolutePath(), remoteFolder, false);
|
|
copyToRemoteFolder(AUTO_INGEST_PROPERTIES, moduleDirPath, remoteFolder, false);
|
|
}
|
|
|
|
/**
|
|
* Download multi-user settings and other general Autopsy settings
|
|
*
|
|
* @param remoteFolder Shared settings folder
|
|
*
|
|
* @throws SharedConfigurationException
|
|
*/
|
|
private void downloadMultiUserAndGeneralSettings(File remoteFolder) throws SharedConfigurationException {
|
|
publishTask("Downloading multi user configuration");
|
|
File generalSettingsFolder = Paths.get(moduleDirPath, PREFERENCES_FOLDER, "org", "sleuthkit", "autopsy").toFile();
|
|
copyToLocalFolder(GENERAL_PROPERTIES, generalSettingsFolder.getAbsolutePath(), remoteFolder, false);
|
|
copyToLocalFolder(AUTO_INGEST_PROPERTIES, moduleDirPath, remoteFolder, false);
|
|
}
|
|
|
|
/**
|
|
* Upload the object detection classifiers.
|
|
*
|
|
* @param remoteFolder Shared settings folder
|
|
*
|
|
* @throws SharedConfigurationException
|
|
*/
|
|
private void uploadObjectDetectionClassifiers(File remoteFolder) throws SharedConfigurationException {
|
|
publishTask("Uploading object detection classfiers");
|
|
File classifiersFolder = new File(PlatformUtil.getObjectDetectionClassifierPath());
|
|
copyLocalFolderToRemoteFolder(classifiersFolder, remoteFolder);
|
|
}
|
|
|
|
/**
|
|
* Download the object detection classifiers.
|
|
*
|
|
* @param remoteFolder Shared settings folder
|
|
*
|
|
* @throws SharedConfigurationException
|
|
*/
|
|
private void downloadObjectDetectionClassifiers(File remoteFolder) throws SharedConfigurationException {
|
|
publishTask("Downloading object detection classfiers");
|
|
File classifiersFolder = new File(PlatformUtil.getObjectDetectionClassifierPath());
|
|
copyRemoteFolderToLocalFolder(classifiersFolder, remoteFolder);
|
|
}
|
|
|
|
/**
|
|
* Upload the Python modules.
|
|
*
|
|
* @param remoteFolder Shared settings folder
|
|
*
|
|
* @throws SharedConfigurationException
|
|
*/
|
|
private void uploadPythonModules(File remoteFolder) throws SharedConfigurationException {
|
|
publishTask("Uploading python modules");
|
|
File classifiersFolder = new File(PlatformUtil.getUserPythonModulesPath());
|
|
copyLocalFolderToRemoteFolder(classifiersFolder, remoteFolder);
|
|
}
|
|
|
|
/**
|
|
* Download the Python modules.
|
|
*
|
|
* @param remoteFolder Shared settings folder
|
|
*
|
|
* @throws SharedConfigurationException
|
|
*/
|
|
private void downloadPythonModules(File remoteFolder) throws SharedConfigurationException {
|
|
publishTask("Downloading python modules");
|
|
File classifiersFolder = new File(PlatformUtil.getUserPythonModulesPath());
|
|
copyRemoteFolderToLocalFolder(classifiersFolder, remoteFolder);
|
|
}
|
|
|
|
/**
|
|
* Upload settings and hash databases to the shared folder. The general
|
|
* algorithm is: - Copy the general settings in hashsets.xml - For each hash
|
|
* database listed in hashsets.xml: - Calculate the CRC of the database - If
|
|
* the CRC does not match the one listed for that database in the shared
|
|
* folder, (or if no entry exists), copy the database - Store the CRCs for
|
|
* each database in the shared folder and locally
|
|
*
|
|
* @param remoteFolder Shared settings folder
|
|
*
|
|
* @throws SharedConfigurationException
|
|
*/
|
|
private void uploadHashDbSettings(File remoteFolder) throws SharedConfigurationException {
|
|
publishTask("Uploading HashDb module configuration");
|
|
|
|
// Keep track of everything being uploaded
|
|
File localVersionFile = new File(moduleDirPath, SHARED_CONFIG_VERSIONS);
|
|
File sharedVersionFile = new File(remoteFolder, SHARED_CONFIG_VERSIONS);
|
|
Map<String, String> newVersions = new HashMap<>();
|
|
Map<String, String> sharedVersions = readVersionsFromFile(sharedVersionFile);
|
|
|
|
// Copy the settings file
|
|
copyToRemoteFolder(HASHDB_CONFIG_FILE_NAME, moduleDirPath, remoteFolder, true);
|
|
copyToRemoteFolder(HASHDB_CONFIG_FILE_NAME_LEGACY, moduleDirPath, remoteFolder, true);
|
|
|
|
// Get the list of databases from the file
|
|
List<String> databases = getHashFileNamesFromSettingsFile();
|
|
for (String fullPathToDbFile : databases) {
|
|
|
|
// Compare the CRC of the local copy with what is stored in the shared folder
|
|
publishTask("Deciding whether to upload " + fullPathToDbFile);
|
|
String crc = calculateCRC(fullPathToDbFile);
|
|
|
|
// Determine full path to db file in remote folder
|
|
String sharedName = convertLocalDbPathToShared(fullPathToDbFile);
|
|
File sharedDbBaseFolder = new File(remoteFolder, REMOTE_HASH_FOLDER);
|
|
File sharedDb = new File(sharedDbBaseFolder, sharedName);
|
|
|
|
if (!(sharedVersions.containsKey(fullPathToDbFile)
|
|
&& sharedVersions.get(fullPathToDbFile).equals(crc)
|
|
&& sharedDb.exists())) {
|
|
|
|
publishTask("Uploading " + fullPathToDbFile);
|
|
File sharedDbPath = sharedDb.getParentFile();
|
|
|
|
if (!sharedDbPath.exists()) {
|
|
if (!sharedDbPath.mkdirs()) {
|
|
throw new SharedConfigurationException("Error creating shared hash set directory " + sharedDbPath.getAbsolutePath());
|
|
}
|
|
}
|
|
|
|
File dbFile = new File(fullPathToDbFile);
|
|
// copy hash db file to the remote folder
|
|
copyFile(sharedDbPath, dbFile);
|
|
|
|
// check whether the hash db has an index file (.idx) that should also be copied.
|
|
// NOTE: only text hash databases (.txt, .hash, .Hash) can have index file.
|
|
// it is possible that the hash db file itself is the index file
|
|
String fullPathToIndexFile = "";
|
|
if (fullPathToDbFile.endsWith(".txt") || fullPathToDbFile.endsWith(".hash") || fullPathToDbFile.endsWith(".Hash")) {
|
|
// check whether index file for this text database is present
|
|
// For example, if text db name is "hash_db.txt" then index file name will be "hash_db.txt-md5.idx"
|
|
fullPathToIndexFile = fullPathToDbFile + "-md5.idx";
|
|
|
|
// if index file exists, copy it to the remote location
|
|
File dbIndexFile = new File(fullPathToIndexFile);
|
|
if (dbIndexFile.exists()) {
|
|
// copy index file to the remote folder
|
|
copyFile(sharedDbPath, dbIndexFile);
|
|
} else {
|
|
fullPathToIndexFile = "";
|
|
}
|
|
} else if (fullPathToDbFile.endsWith(".idx")) {
|
|
// hash db file itself is the index file and it has already been copied to the remote location
|
|
fullPathToIndexFile = fullPathToDbFile;
|
|
}
|
|
|
|
// check whether "index of the index" file exists for this hash DB's index file.
|
|
// NOTE: "index of the index" file may only exist
|
|
// for text hash database index files (i.e ".idx" extension). The index of the
|
|
// index file will always have the same name as the index file,
|
|
// distinguished only by the "2" in the extension. "index of the index" file
|
|
// is optional and may not be present.
|
|
if (fullPathToIndexFile.endsWith(".idx")) {
|
|
String fullPathToIndexOfIndexFile = fullPathToIndexFile + "2"; // "index of the index" file has same file name and extension ".idx2"
|
|
File dbIndexOfIndexFile = new File(fullPathToIndexOfIndexFile);
|
|
if (dbIndexOfIndexFile.exists()) {
|
|
// copy index of the index file to the remote folder
|
|
copyFile(sharedDbPath, dbIndexOfIndexFile);
|
|
}
|
|
}
|
|
}
|
|
|
|
newVersions.put(fullPathToDbFile, crc);
|
|
}
|
|
|
|
// Write the versions of all uploaded files to a file (make local and shared copies)
|
|
writeVersionsToFile(localVersionFile, newVersions);
|
|
writeVersionsToFile(sharedVersionFile, newVersions);
|
|
}
|
|
|
|
/**
|
|
* Utility method to copy a file
|
|
*
|
|
* @param sharedDbPath File object of the folder to copy to
|
|
* @param dbFile File object of the file to copy
|
|
*
|
|
* @throws
|
|
* org.sleuthkit.autopsy.configuration.SharedConfiguration.SharedConfigurationException
|
|
*/
|
|
private void copyFile(File sharedDbPath, File dbFile) throws SharedConfigurationException {
|
|
try {
|
|
Path path = Paths.get(sharedDbPath.toString(), dbFile.getName());
|
|
if (path.toFile().exists()) {
|
|
path.toFile().delete();
|
|
}
|
|
FileUtils.copyFileToDirectory(dbFile, sharedDbPath);
|
|
} catch (IOException | SecurityException ex) {
|
|
throw new SharedConfigurationException(String.format("Failed to copy %s to %s", dbFile.getAbsolutePath(), sharedDbPath.getAbsolutePath()), ex);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Upload settings and hash databases to the shared folder. The general
|
|
* algorithm is: - Copy the general settings in hashsets.xml - For each hash
|
|
* database listed in hashsets.xml: - Compare the recorded CRC in the shared
|
|
* directory with the one in the local directory - If different, download
|
|
* the database - Update the local list of database CRCs Note that databases
|
|
* are downloaded to the exact path they were uploaded from.
|
|
*
|
|
* @param remoteFolder Shared settings folder
|
|
*
|
|
* @throws SharedConfigurationException
|
|
*/
|
|
private void downloadHashDbSettings(File remoteFolder) throws SharedConfigurationException {
|
|
publishTask("Downloading HashDb module configuration");
|
|
|
|
// Read in the current local and shared database versions
|
|
File localVersionFile = new File(moduleDirPath, SHARED_CONFIG_VERSIONS);
|
|
File remoteVersionFile = new File(remoteFolder, SHARED_CONFIG_VERSIONS);
|
|
Map<String, String> localVersions = readVersionsFromFile(localVersionFile);
|
|
Map<String, String> remoteVersions = readVersionsFromFile(remoteVersionFile);
|
|
|
|
/*
|
|
Iterate through remote list
|
|
If local needs it, download
|
|
|
|
Download remote settings files to local
|
|
Download remote versions file to local
|
|
HashDbManager reload
|
|
*/
|
|
File localDb = new File("");
|
|
File sharedDb = new File("");
|
|
try {
|
|
for (String path : remoteVersions.keySet()) {
|
|
localDb = new File(path);
|
|
if ((!localVersions.containsKey(path))
|
|
|| (!localVersions.get(path).equals(remoteVersions.get(path)))
|
|
|| !localDb.exists()) {
|
|
// Need to download a new copy if
|
|
// - We have no entry for the database in the local versions file
|
|
// - The CRC in the local versions file does not match the one in the shared file
|
|
// - Local copy of the database does not exist
|
|
|
|
if (localDb.exists()) {
|
|
String crc = calculateCRC(path);
|
|
if (crc.equals(remoteVersions.get(path))) {
|
|
// Can skip the download if the local disk has it
|
|
// but it's just not in the versions file. This will
|
|
// be populated just before refreshing HashDbManager.
|
|
continue;
|
|
}
|
|
}
|
|
|
|
publishTask("Downloading " + path);
|
|
String sharedName = convertLocalDbPathToShared(path);
|
|
File sharedDbBaseFolder = new File(remoteFolder, REMOTE_HASH_FOLDER);
|
|
sharedDb = new File(sharedDbBaseFolder, sharedName);
|
|
|
|
if (!localDb.getParentFile().exists()) {
|
|
if (!localDb.getParentFile().mkdirs()) {
|
|
throw new SharedConfigurationException("Error creating hash set directory " + localDb.getParentFile().getAbsolutePath());
|
|
}
|
|
}
|
|
|
|
// If a copy of the database is loaded, close it before deleting and copying.
|
|
if (localDb.exists()) {
|
|
List<HashDbManager.HashDb> hashDbs = HashDbManager.getInstance().getAllHashSets();
|
|
HashDbManager.HashDb matchingDb = null;
|
|
for (HashDbManager.HashDb db : hashDbs) {
|
|
try {
|
|
if (localDb.getAbsolutePath().equals(db.getDatabasePath()) || localDb.getAbsolutePath().equals(db.getIndexPath())) {
|
|
matchingDb = db;
|
|
break;
|
|
}
|
|
} catch (TskCoreException ex) {
|
|
throw new SharedConfigurationException(String.format("Error getting hash set path info for %s", localDb.getParentFile().getAbsolutePath()), ex);
|
|
}
|
|
}
|
|
|
|
if (matchingDb != null) {
|
|
try {
|
|
HashDbManager.getInstance().removeHashDatabase(matchingDb);
|
|
} catch (HashDbManager.HashDbManagerException ex) {
|
|
throw new SharedConfigurationException(String.format("Error updating hash set info for %s", localDb.getAbsolutePath()), ex);
|
|
}
|
|
|
|
}
|
|
if (localDb.exists()) {
|
|
localDb.delete();
|
|
}
|
|
}
|
|
FileUtils.copyFile(sharedDb, localDb);
|
|
|
|
// check whether the hash db has an index file (.idx) that should also be copied.
|
|
// NOTE: only text hash databases (.txt, .hash, .Hash) can have index file.
|
|
// it is possible that the hash db file itself is the index file
|
|
String fullPathToRemoteDbFile = sharedDb.getPath();
|
|
String fullPathToRemoteIndexFile = "";
|
|
String fullPathToLocalIndexFile = "";
|
|
if (fullPathToRemoteDbFile.endsWith(".txt") || fullPathToRemoteDbFile.toLowerCase().endsWith(".hash")) {
|
|
// check whether index file for this text database is present
|
|
// For example, if text db name is "hash_db.txt" then index file name will be "hash_db.txt-md5.idx"
|
|
fullPathToRemoteIndexFile = fullPathToRemoteDbFile + "-md5.idx";
|
|
|
|
// if index file exists, copy it to the remote location
|
|
File remoteDbIndexFile = new File(fullPathToRemoteIndexFile);
|
|
if (remoteDbIndexFile.exists()) {
|
|
// delete local copy of "index of the index" file if one exists
|
|
fullPathToLocalIndexFile = localDb.getPath() + "-md5.idx";
|
|
File localIndexFile = new File(fullPathToLocalIndexFile);
|
|
if (localIndexFile.exists()) {
|
|
localIndexFile.delete();
|
|
}
|
|
// copy index file to the remote folder
|
|
FileUtils.copyFile(remoteDbIndexFile, localIndexFile);
|
|
} else {
|
|
// index file doesn't exist at remote location
|
|
fullPathToRemoteIndexFile = "";
|
|
}
|
|
} else if (fullPathToRemoteDbFile.endsWith(".idx")) {
|
|
// hash db file itself is the index file and it has already been copied to the remote location
|
|
fullPathToRemoteIndexFile = fullPathToRemoteDbFile;
|
|
fullPathToLocalIndexFile = localDb.getPath();
|
|
}
|
|
|
|
// check whether "index of the index" file exists for this hash DB index file.
|
|
// NOTE: "index of the index" file may only exist for hash database index files (.idx files).
|
|
// For example, hash_db.txt-md5.idx index file will have hash_db.txt-md5.idx2 "index of the index" file.
|
|
// "index of the index" file is optional and may not be present.
|
|
if (fullPathToRemoteIndexFile.endsWith(".idx")) {
|
|
// check if "index of the index" file exists in remote shared config folder
|
|
String fullPathToRemoteIndexOfIndexFile = fullPathToRemoteIndexFile + "2"; // "index of the index" file has same file name with extension ".idx2"
|
|
File remoteIndexOfIndexFile = new File(fullPathToRemoteIndexOfIndexFile);
|
|
if (remoteIndexOfIndexFile.exists()) {
|
|
|
|
// delete local copy of "index of the index" file if one exists
|
|
String fullPathToLocalIndexOfIndexFile = fullPathToLocalIndexFile + "2"; // "index of the index" file has same file name with extension ".idx2"
|
|
File localIndexOfIndexFile = new File(fullPathToLocalIndexOfIndexFile);
|
|
if (localIndexOfIndexFile.exists()) {
|
|
localIndexOfIndexFile.delete();
|
|
}
|
|
// copy index of the index file to the local folder
|
|
FileUtils.copyFile(remoteIndexOfIndexFile, localIndexOfIndexFile);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} catch (IOException | SecurityException ex) {
|
|
throw new SharedConfigurationException(String.format("Failed to copy %s to %s", sharedDb.getAbsolutePath(), localDb.getAbsolutePath()), ex);
|
|
}
|
|
|
|
// Copy the settings filey
|
|
copyToLocalFolder(HASHDB_CONFIG_FILE_NAME, moduleDirPath, remoteFolder, true);
|
|
copyToLocalFolder(HASHDB_CONFIG_FILE_NAME_LEGACY, moduleDirPath, remoteFolder, true);
|
|
copyToLocalFolder(SHARED_CONFIG_VERSIONS, moduleDirPath, remoteFolder, true);
|
|
|
|
// Refresh HashDbManager with the new settings
|
|
HashDbManager.getInstance().loadLastSavedConfiguration();
|
|
}
|
|
|
|
/**
|
|
* Read in the hashsets settings to pull out the names of the databases.
|
|
*
|
|
* @return List of all hash databases
|
|
*
|
|
* @throws SharedConfigurationException
|
|
*/
|
|
private static List<String> getHashFileNamesFromSettingsFile() throws SharedConfigurationException {
|
|
List<String> results = new ArrayList<>();
|
|
try {
|
|
HashDbManager hashDbManager = HashDbManager.getInstance();
|
|
hashDbManager.loadLastSavedConfiguration();
|
|
for (HashDbManager.HashDb hashDb : hashDbManager.getAllHashSets()) {
|
|
if (hashDb.hasIndexOnly() && (!hashDb.getIndexPath().isEmpty())) {
|
|
results.add(hashDb.getIndexPath());
|
|
} else if (!hashDb.getDatabasePath().isEmpty()) {
|
|
results.add(hashDb.getDatabasePath());
|
|
}
|
|
}
|
|
} catch (TskCoreException ex) {
|
|
throw new SharedConfigurationException("Unable to read hash sets", ex);
|
|
}
|
|
return results;
|
|
}
|
|
|
|
/**
|
|
* Change the database path into a form that can be used to create
|
|
* subfolders in the shared folder.
|
|
*
|
|
* @param localName Database name from the XML file
|
|
*
|
|
* @return Path with the initial colon removed
|
|
*/
|
|
private static String convertLocalDbPathToShared(String localName) {
|
|
// Replace the colon
|
|
String sharedName = localName.replace(":", "__colon__");
|
|
return sharedName;
|
|
}
|
|
|
|
/**
|
|
* Write the list of database paths and versions to a file.
|
|
*
|
|
* @param versionFile File to write to
|
|
* @param versions Map of database name -> version (current using CRCs as
|
|
* versions)
|
|
*
|
|
* @throws SharedConfigurationException
|
|
*/
|
|
private static void writeVersionsToFile(File versionFile, Map<String, String> versions) throws SharedConfigurationException {
|
|
try (PrintWriter writer = new PrintWriter(versionFile.getAbsoluteFile(), "UTF-8")) {
|
|
for (String filename : versions.keySet()) {
|
|
writer.println(versions.get(filename) + " " + filename);
|
|
}
|
|
} catch (FileNotFoundException | UnsupportedEncodingException ex) {
|
|
throw new SharedConfigurationException(String.format("Failed to write version info to %s", versionFile), ex);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Read the map of database paths to versions from a file.
|
|
*
|
|
* @param versionFile File containing the version information
|
|
*
|
|
* @return Map of database name -> version
|
|
*
|
|
* @throws SharedConfigurationException
|
|
*/
|
|
private static Map<String, String> readVersionsFromFile(File versionFile) throws SharedConfigurationException {
|
|
Map<String, String> versions = new HashMap<>();
|
|
|
|
// If the file does not exist, return an empty map
|
|
if (!versionFile.exists()) {
|
|
return versions;
|
|
}
|
|
|
|
// Read in and store each pair
|
|
try (BufferedReader reader = new BufferedReader(new FileReader(versionFile))) {
|
|
String currentLine = reader.readLine();
|
|
while (null != currentLine) {
|
|
if (!currentLine.isEmpty()) {
|
|
int index = currentLine.indexOf(' '); // Find the first space
|
|
String crc = currentLine.substring(0, index);
|
|
String path = currentLine.substring(index + 1);
|
|
versions.put(path, crc);
|
|
}
|
|
currentLine = reader.readLine();
|
|
}
|
|
} catch (FileNotFoundException ex) {
|
|
throw new SharedConfigurationException(String.format("Failed to find version file %s", versionFile), ex);
|
|
} catch (IOException ex) {
|
|
throw new SharedConfigurationException(String.format("Failed to read version info from %s", versionFile), ex);
|
|
}
|
|
|
|
return versions;
|
|
}
|
|
|
|
/**
|
|
* Calculate the CRC of a file to use to determine if it has changed.
|
|
*
|
|
* @param filePath File to get the CRC for
|
|
*
|
|
* @return String containing the CRC
|
|
*
|
|
* @throws SharedConfigurationException
|
|
*/
|
|
private static String calculateCRC(String filePath) throws SharedConfigurationException {
|
|
File file = new File(filePath);
|
|
try {
|
|
FileInputStream fileStream = new FileInputStream(file);
|
|
CRC32 crc = new CRC32();
|
|
byte[] buffer = new byte[65536];
|
|
int bytesRead = fileStream.read(buffer);
|
|
while (-1 != bytesRead) {
|
|
crc.update(buffer, 0, bytesRead);
|
|
bytesRead = fileStream.read(buffer);
|
|
}
|
|
return String.valueOf(crc.getValue());
|
|
} catch (IOException ex) {
|
|
throw new SharedConfigurationException(String.format("Failed to calculate CRC for %s", file.getAbsolutePath()), ex);
|
|
}
|
|
}
|
|
}
|