diff --git a/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashDbManager.java b/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashDbManager.java index ada9d6f899..a20104e495 100755 --- a/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashDbManager.java +++ b/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashDbManager.java @@ -38,14 +38,20 @@ import org.w3c.dom.Element; import org.w3c.dom.NodeList; import java.beans.PropertyChangeListener; import java.beans.PropertyChangeSupport; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.Serializable; import java.util.concurrent.ExecutionException; import java.util.logging.Level; +import javax.persistence.PersistenceException; import javax.swing.JOptionPane; import javax.swing.SwingWorker; import org.apache.commons.io.FilenameUtils; import org.apache.commons.io.FileUtils; import org.netbeans.api.progress.ProgressHandle; import org.netbeans.api.progress.ProgressHandleFactory; +import org.openide.util.io.NbObjectInputStream; +import org.openide.util.io.NbObjectOutputStream; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.Content; @@ -72,11 +78,13 @@ public class HashDbManager implements PropertyChangeListener { private static final String PATH_ELEMENT = "hash_set_path"; //NON-NLS private static final String LEGACY_PATH_NUMBER_ATTRIBUTE = "number"; //NON-NLS private static final String CONFIG_FILE_NAME = "hashsets.xml"; //NON-NLS + private static final String DB_SERIALIZATION_FILE_NAME = "hashDbs.settings"; private static final String XSD_FILE_NAME = "HashsetsSchema.xsd"; //NON-NLS private static final String ENCODING = "UTF-8"; //NON-NLS private static final String HASH_DATABASE_FILE_EXTENSON = "kdb"; //NON-NLS private static HashDbManager instance = null; private final String configFilePath = PlatformUtil.getUserConfigDirectory() + File.separator + CONFIG_FILE_NAME; + private final String DB_SERIALIZATION_FILE_PATH = PlatformUtil.getUserConfigDirectory() + File.separator + DB_SERIALIZATION_FILE_NAME; private List knownHashSets = new ArrayList<>(); private List knownBadHashSets = new ArrayList<>(); private Set hashSetNames = new HashSet<>(); @@ -109,9 +117,7 @@ public class HashDbManager implements PropertyChangeListener { } private HashDbManager() { - if (hashSetsConfigurationFileExists()) { - readHashSetsConfigurationFromDisk(); - } + readHashSetsConfigurationFromDisk(); } /** @@ -490,9 +496,7 @@ public class HashDbManager implements PropertyChangeListener { hashSetNames.clear(); hashSetPaths.clear(); - if (hashSetsConfigurationFileExists()) { - readHashSetsConfigurationFromDisk(); - } + readHashSetsConfigurationFromDisk(); } private void closeHashDatabases(List hashDatabases) { @@ -507,22 +511,17 @@ public class HashDbManager implements PropertyChangeListener { } private boolean writeHashSetConfigurationToDisk() { - boolean success = false; - DocumentBuilderFactory dbfac = DocumentBuilderFactory.newInstance(); - try { - DocumentBuilder docBuilder = dbfac.newDocumentBuilder(); - Document doc = docBuilder.newDocument(); - Element rootEl = doc.createElement(ROOT_ELEMENT); - doc.appendChild(rootEl); - - writeHashDbsToDisk(doc, rootEl, knownHashSets); - writeHashDbsToDisk(doc, rootEl, knownBadHashSets); - - success = XMLUtil.saveDoc(HashDbManager.class, configFilePath, ENCODING, doc); - } catch (ParserConfigurationException ex) { - Logger.getLogger(HashDbManager.class.getName()).log(Level.SEVERE, "Error saving hash databases", ex); //NON-NLS + HashDbSerializationSettings settings = new HashDbSerializationSettings(this.knownHashSets, this.knownBadHashSets); + try (NbObjectOutputStream out = new NbObjectOutputStream(new FileOutputStream(DB_SERIALIZATION_FILE_PATH))) { + out.writeObject(settings); + File xmlFile = new File(configFilePath); + if (xmlFile.exists()) { + xmlFile.delete(); + } + return true; + } catch (IOException ex) { + throw new PersistenceException(String.format("Failed to write settings to %s", DB_SERIALIZATION_FILE_PATH), ex); } - return success; } private static void writeHashDbsToDisk(Document doc, Element rootEl, List hashDbs) { @@ -559,42 +558,172 @@ public class HashDbManager implements PropertyChangeListener { } private boolean readHashSetsConfigurationFromDisk() { - boolean updatedSchema = false; + if (hashSetsConfigurationFileExists()) { + boolean updatedSchema = false; - // Open the XML document that implements the configuration file. - final Document doc = XMLUtil.loadDoc(HashDbManager.class, configFilePath); - if (doc == null) { - return false; - } - - // Get the root element. - Element root = doc.getDocumentElement(); - if (root == null) { - Logger.getLogger(HashDbManager.class.getName()).log(Level.SEVERE, "Error loading hash sets: invalid file format."); //NON-NLS - return false; - } - - // Get the hash set elements. - NodeList setsNList = root.getElementsByTagName(SET_ELEMENT); - int numSets = setsNList.getLength(); - if (numSets == 0) { - Logger.getLogger(HashDbManager.class.getName()).log(Level.WARNING, "No element hash_set exists."); //NON-NLS - } - - // Create HashDb objects for each hash set element. Skip to the next hash database if the definition of - // a particular hash database is not well-formed. - String attributeErrorMessage = " attribute was not set for hash_set at index {0}, cannot make instance of HashDb class"; //NON-NLS - String elementErrorMessage = " element was not set for hash_set at index {0}, cannot make instance of HashDb class"; //NON-NLS - for (int i = 0; i < numSets; ++i) { - Element setEl = (Element) setsNList.item(i); - - String hashSetName = setEl.getAttribute(SET_NAME_ATTRIBUTE); - if (hashSetName.isEmpty()) { - Logger.getLogger(HashDbManager.class.getName()).log(Level.SEVERE, SET_NAME_ATTRIBUTE + attributeErrorMessage, i); - continue; + // Open the XML document that implements the configuration file. + final Document doc = XMLUtil.loadDoc(HashDbManager.class, configFilePath); + if (doc == null) { + return false; } - // Handle configurations saved before duplicate hash set names were not permitted. + // Get the root element. + Element root = doc.getDocumentElement(); + if (root == null) { + Logger.getLogger(HashDbManager.class.getName()).log(Level.SEVERE, "Error loading hash sets: invalid file format."); //NON-NLS + return false; + } + + // Get the hash set elements. + NodeList setsNList = root.getElementsByTagName(SET_ELEMENT); + int numSets = setsNList.getLength(); + if (numSets == 0) { + Logger.getLogger(HashDbManager.class.getName()).log(Level.WARNING, "No element hash_set exists."); //NON-NLS + } + + // Create HashDb objects for each hash set element. Skip to the next hash database if the definition of + // a particular hash database is not well-formed. + String attributeErrorMessage = " attribute was not set for hash_set at index {0}, cannot make instance of HashDb class"; //NON-NLS + String elementErrorMessage = " element was not set for hash_set at index {0}, cannot make instance of HashDb class"; //NON-NLS + for (int i = 0; i < numSets; ++i) { + Element setEl = (Element) setsNList.item(i); + + String hashSetName = setEl.getAttribute(SET_NAME_ATTRIBUTE); + if (hashSetName.isEmpty()) { + Logger.getLogger(HashDbManager.class.getName()).log(Level.SEVERE, SET_NAME_ATTRIBUTE + attributeErrorMessage, i); + continue; + } + + // Handle configurations saved before duplicate hash set names were not permitted. + if (hashSetNames.contains(hashSetName)) { + int suffix = 0; + String newHashSetName; + do { + ++suffix; + newHashSetName = hashSetName + suffix; + } while (hashSetNames.contains(newHashSetName)); + JOptionPane.showMessageDialog(null, + NbBundle.getMessage(this.getClass(), + "HashDbManager.replacingDuplicateHashsetNameMsg", + hashSetName, newHashSetName), + NbBundle.getMessage(this.getClass(), "HashDbManager.openHashDbErr"), + JOptionPane.ERROR_MESSAGE); + hashSetName = newHashSetName; + } + + String knownFilesType = setEl.getAttribute(SET_TYPE_ATTRIBUTE); + if (knownFilesType.isEmpty()) { + Logger.getLogger(HashDbManager.class.getName()).log(Level.SEVERE, SET_TYPE_ATTRIBUTE + attributeErrorMessage, i); + continue; + } + + // Handle legacy known files types. + if (knownFilesType.equals("NSRL")) { //NON-NLS + knownFilesType = HashDb.KnownFilesType.KNOWN.toString(); + updatedSchema = true; + } + + final String searchDuringIngest = setEl.getAttribute(SEARCH_DURING_INGEST_ATTRIBUTE); + if (searchDuringIngest.isEmpty()) { + Logger.getLogger(HashDbManager.class.getName()).log(Level.SEVERE, SEARCH_DURING_INGEST_ATTRIBUTE + attributeErrorMessage, i); + continue; + } + Boolean seearchDuringIngestFlag = Boolean.parseBoolean(searchDuringIngest); + + final String sendIngestMessages = setEl.getAttribute(SEND_INGEST_MESSAGES_ATTRIBUTE); + if (searchDuringIngest.isEmpty()) { + Logger.getLogger(HashDbManager.class.getName()).log(Level.SEVERE, SEND_INGEST_MESSAGES_ATTRIBUTE + attributeErrorMessage, i); + continue; + } + Boolean sendIngestMessagesFlag = Boolean.parseBoolean(sendIngestMessages); + + String dbPath; + NodeList pathsNList = setEl.getElementsByTagName(PATH_ELEMENT); + if (pathsNList.getLength() > 0) { + Element pathEl = (Element) pathsNList.item(0); // Shouldn't be more than one. + + // Check for legacy path number attribute. + String legacyPathNumber = pathEl.getAttribute(LEGACY_PATH_NUMBER_ATTRIBUTE); + if (null != legacyPathNumber && !legacyPathNumber.isEmpty()) { + updatedSchema = true; + } + + dbPath = pathEl.getTextContent(); + if (dbPath.isEmpty()) { + Logger.getLogger(HashDbManager.class.getName()).log(Level.SEVERE, PATH_ELEMENT + elementErrorMessage, i); + continue; + } + } else { + Logger.getLogger(HashDbManager.class.getName()).log(Level.SEVERE, PATH_ELEMENT + elementErrorMessage, i); + continue; + } + dbPath = getValidFilePath(hashSetName, dbPath); + + if (null != dbPath) { + try { + addExistingHashDatabaseInternal(hashSetName, dbPath, seearchDuringIngestFlag, sendIngestMessagesFlag, HashDb.KnownFilesType.valueOf(knownFilesType)); + } catch (HashDbManagerException | TskCoreException ex) { + Logger.getLogger(HashDbManager.class.getName()).log(Level.SEVERE, "Error opening hash database", ex); //NON-NLS + JOptionPane.showMessageDialog(null, + NbBundle.getMessage(this.getClass(), + "HashDbManager.unableToOpenHashDbMsg", dbPath), + NbBundle.getMessage(this.getClass(), "HashDbManager.openHashDbErr"), + JOptionPane.ERROR_MESSAGE); + } + } else { + Logger.getLogger(HashDbManager.class.getName()).log(Level.WARNING, "No valid path for hash_set at index {0}, cannot make instance of HashDb class", i); //NON-NLS + } + } + + if (updatedSchema) { + String backupFilePath = configFilePath + ".v1_backup"; //NON-NLS + String messageBoxTitle = NbBundle.getMessage(this.getClass(), + "HashDbManager.msgBoxTitle.confFileFmtChanged"); + String baseMessage = NbBundle.getMessage(this.getClass(), + "HashDbManager.baseMessage.updatedFormatHashDbConfig"); + try { + FileUtils.copyFile(new File(configFilePath), new File(backupFilePath)); + JOptionPane.showMessageDialog(null, + NbBundle.getMessage(this.getClass(), + "HashDbManager.savedBackupOfOldConfigMsg", + baseMessage, backupFilePath), + messageBoxTitle, + JOptionPane.INFORMATION_MESSAGE); + } catch (IOException ex) { + Logger.getLogger(HashDbManager.class.getName()).log(Level.WARNING, "Failed to save backup of old format configuration file to " + backupFilePath, ex); //NON-NLS + JOptionPane.showMessageDialog(null, baseMessage, messageBoxTitle, JOptionPane.INFORMATION_MESSAGE); + } + + writeHashSetConfigurationToDisk(); + } + + return true; + } else { + File fileSetFile = new File(DB_SERIALIZATION_FILE_PATH); + if (fileSetFile.exists()) { + try { + try (NbObjectInputStream in = new NbObjectInputStream(new FileInputStream(DB_SERIALIZATION_FILE_PATH))) { + HashDbSerializationSettings filesSetsSettings = (HashDbSerializationSettings) in.readObject(); + this.setFields(filesSetsSettings); + return true; + } + } catch (IOException | ClassNotFoundException ex) { + throw new PersistenceException(String.format("Failed to read settings from %s", DB_SERIALIZATION_FILE_PATH), ex); + } + } else { + this.setFields(new HashDbSerializationSettings(new ArrayList<>(), new ArrayList<>())); + return true; + } + } + } + + private void setFields(HashDbSerializationSettings settings) throws TskCoreException { + this.knownHashSets = settings.getKnownHashSets(); + this.knownBadHashSets = settings.getKnownBadHashSets(); + this.hashSetNames = new HashSet<>(); + this.hashSetPaths = new HashSet<>(); + for (HashDbManager.HashDb hashDb : knownHashSets) { + String hashSetName = hashDb.getHashSetName(); if (hashSetNames.contains(hashSetName)) { int suffix = 0; String newHashSetName; @@ -602,102 +731,9 @@ public class HashDbManager implements PropertyChangeListener { ++suffix; newHashSetName = hashSetName + suffix; } while (hashSetNames.contains(newHashSetName)); - JOptionPane.showMessageDialog(null, - NbBundle.getMessage(this.getClass(), - "HashDbManager.replacingDuplicateHashsetNameMsg", - hashSetName, newHashSetName), - NbBundle.getMessage(this.getClass(), "HashDbManager.openHashDbErr"), - JOptionPane.ERROR_MESSAGE); - hashSetName = newHashSetName; - } - - String knownFilesType = setEl.getAttribute(SET_TYPE_ATTRIBUTE); - if (knownFilesType.isEmpty()) { - Logger.getLogger(HashDbManager.class.getName()).log(Level.SEVERE, SET_TYPE_ATTRIBUTE + attributeErrorMessage, i); - continue; - } - - // Handle legacy known files types. - if (knownFilesType.equals("NSRL")) { //NON-NLS - knownFilesType = HashDb.KnownFilesType.KNOWN.toString(); - updatedSchema = true; - } - - final String searchDuringIngest = setEl.getAttribute(SEARCH_DURING_INGEST_ATTRIBUTE); - if (searchDuringIngest.isEmpty()) { - Logger.getLogger(HashDbManager.class.getName()).log(Level.SEVERE, SEARCH_DURING_INGEST_ATTRIBUTE + attributeErrorMessage, i); - continue; - } - Boolean seearchDuringIngestFlag = Boolean.parseBoolean(searchDuringIngest); - - final String sendIngestMessages = setEl.getAttribute(SEND_INGEST_MESSAGES_ATTRIBUTE); - if (searchDuringIngest.isEmpty()) { - Logger.getLogger(HashDbManager.class.getName()).log(Level.SEVERE, SEND_INGEST_MESSAGES_ATTRIBUTE + attributeErrorMessage, i); - continue; - } - Boolean sendIngestMessagesFlag = Boolean.parseBoolean(sendIngestMessages); - - String dbPath; - NodeList pathsNList = setEl.getElementsByTagName(PATH_ELEMENT); - if (pathsNList.getLength() > 0) { - Element pathEl = (Element) pathsNList.item(0); // Shouldn't be more than one. - - // Check for legacy path number attribute. - String legacyPathNumber = pathEl.getAttribute(LEGACY_PATH_NUMBER_ATTRIBUTE); - if (null != legacyPathNumber && !legacyPathNumber.isEmpty()) { - updatedSchema = true; - } - - dbPath = pathEl.getTextContent(); - if (dbPath.isEmpty()) { - Logger.getLogger(HashDbManager.class.getName()).log(Level.SEVERE, PATH_ELEMENT + elementErrorMessage, i); - continue; - } - } else { - Logger.getLogger(HashDbManager.class.getName()).log(Level.SEVERE, PATH_ELEMENT + elementErrorMessage, i); - continue; - } - dbPath = getValidFilePath(hashSetName, dbPath); - - if (null != dbPath) { - try { - addExistingHashDatabaseInternal(hashSetName, dbPath, seearchDuringIngestFlag, sendIngestMessagesFlag, HashDb.KnownFilesType.valueOf(knownFilesType)); - } catch (HashDbManagerException | TskCoreException ex) { - Logger.getLogger(HashDbManager.class.getName()).log(Level.SEVERE, "Error opening hash database", ex); //NON-NLS - JOptionPane.showMessageDialog(null, - NbBundle.getMessage(this.getClass(), - "HashDbManager.unableToOpenHashDbMsg", dbPath), - NbBundle.getMessage(this.getClass(), "HashDbManager.openHashDbErr"), - JOptionPane.ERROR_MESSAGE); - } - } else { - Logger.getLogger(HashDbManager.class.getName()).log(Level.WARNING, "No valid path for hash_set at index {0}, cannot make instance of HashDb class", i); //NON-NLS } + this.hashSetPaths.add(hashDb.getDatabasePath()); } - - if (updatedSchema) { - String backupFilePath = configFilePath + ".v1_backup"; //NON-NLS - String messageBoxTitle = NbBundle.getMessage(this.getClass(), - "HashDbManager.msgBoxTitle.confFileFmtChanged"); - String baseMessage = NbBundle.getMessage(this.getClass(), - "HashDbManager.baseMessage.updatedFormatHashDbConfig"); - try { - FileUtils.copyFile(new File(configFilePath), new File(backupFilePath)); - JOptionPane.showMessageDialog(null, - NbBundle.getMessage(this.getClass(), - "HashDbManager.savedBackupOfOldConfigMsg", - baseMessage, backupFilePath), - messageBoxTitle, - JOptionPane.INFORMATION_MESSAGE); - } catch (IOException ex) { - Logger.getLogger(HashDbManager.class.getName()).log(Level.WARNING, "Failed to save backup of old format configuration file to " + backupFilePath, ex); //NON-NLS - JOptionPane.showMessageDialog(null, baseMessage, messageBoxTitle, JOptionPane.INFORMATION_MESSAGE); - } - - writeHashSetConfigurationToDisk(); - } - - return true; } private String getValidFilePath(String hashSetName, String configuredPath) { @@ -750,7 +786,7 @@ public class HashDbManager implements PropertyChangeListener { * Instances of this class represent hash databases used to classify files * as known or know bad. */ - public static class HashDb { + public static class HashDb implements Serializable { /** * Indicates how files with hashes stored in a particular hash database @@ -778,6 +814,7 @@ public class HashDbManager implements PropertyChangeListener { INDEXING_DONE } + private static final long serialVersionUID = 1L; private int handle; private String hashSetName; private boolean searchDuringIngest; diff --git a/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashDbSerializationSettings.java b/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashDbSerializationSettings.java new file mode 100755 index 0000000000..56fb7f2368 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashDbSerializationSettings.java @@ -0,0 +1,40 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.sleuthkit.autopsy.modules.hashdatabase; + +import java.io.Serializable; +import java.util.List; + +/** + * Class to represent the settings to be serialized for hash databases + */ +class HashDbSerializationSettings implements Serializable { + + private static final long serialVersionUID = 1L; + private final List knownHashSets; + private final List knownBadHashSets; + + /** + * Constructs a settings object to be serialized for hash dbs + * @param knownHashSets + * @param knownBadHashSets + */ + HashDbSerializationSettings(List knownHashSets, List knownBadHashSets) { + this.knownHashSets = knownHashSets; + this.knownBadHashSets = knownBadHashSets; + } + + List getKnownHashSets() { + return this.knownHashSets; + } + + /** + * @return the knownBadHashSets + */ + public List getKnownBadHashSets() { + return knownBadHashSets; + } +}