/* * Autopsy Forensic Browser * * Copyright 2011 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.hashdatabase; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.logging.Level; import javax.swing.JFileChooser; import javax.swing.JOptionPane; import javax.swing.filechooser.FileNameExtensionFilter; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.PlatformUtil; import org.sleuthkit.autopsy.coreutils.XMLUtil; import org.sleuthkit.autopsy.hashdatabase.HashDb.DBType; import org.sleuthkit.datamodel.SleuthkitJNI; import org.sleuthkit.datamodel.TskCoreException; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.NodeList; public class HashDbXML { private static final String ROOT_EL = "hash_sets"; private static final String SET_EL = "hash_set"; private static final String SET_NAME_ATTR = "name"; private static final String SET_TYPE_ATTR = "type"; private static final String SET_USE_FOR_INGEST_ATTR = "use_for_ingest"; private static final String SET_SHOW_INBOX_MESSAGES = "show_inbox_messages"; private static final String PATH_EL = "hash_set_path"; private static final String PATH_NUMBER_ATTR = "number"; private static final String CUR_HASHSETS_FILE_NAME = "hashsets.xml"; private static final String XSDFILE = "HashsetsSchema.xsd"; private static final String ENCODING = "UTF-8"; private static final String CUR_HASHSET_FILE = PlatformUtil.getUserConfigDirectory() + File.separator + CUR_HASHSETS_FILE_NAME; private static final String SET_CALC = "hash_calculate"; private static final String SET_VALUE = "value"; private static final Logger logger = Logger.getLogger(HashDbXML.class.getName()); private static HashDbXML currentInstance; private List knownBadSets; private HashDb nsrlSet; private String xmlFile; private boolean calculate; private HashDbXML(String xmlFile) { knownBadSets = new ArrayList(); this.xmlFile = xmlFile; } /** * get instance for managing the current keyword list of the application */ static synchronized HashDbXML getCurrent() { if (currentInstance == null) { currentInstance = new HashDbXML(CUR_HASHSET_FILE); currentInstance.reload(); } return currentInstance; } /** * Get the hash sets */ public List getAllSets() { List ret = new ArrayList(); if(nsrlSet != null) { ret.add(nsrlSet); } ret.addAll(knownBadSets); return ret; } /** * Get the Known Bad sets */ public List getKnownBadSets() { return knownBadSets; } /** * Get the NSRL set */ public HashDb getNSRLSet() { return nsrlSet; } /** * Add a known bad hash set */ public void addKnownBadSet(HashDb set) { knownBadSets.add(set); //save(); } /** * Add a known bad hash set */ public void addKnownBadSet(int index, HashDb set) { knownBadSets.add(index, set); //save(); } /** * Set the NSRL hash set (override old set) */ public void setNSRLSet(HashDb set) { this.nsrlSet = set; //save(); } /** * Remove a hash known bad set */ public void removeKnownBadSetAt(int index) { knownBadSets.remove(index); //save(); } /** * Remove the NSRL database */ public void removeNSRLSet() { this.nsrlSet = null; //save(); } /** * load the file or create new */ public void reload() { boolean created = false; //TODO clearing the list causes a bug: we lose track of the state //whether db is being indexed, we should somehow preserve the state when loading new HashDb objects knownBadSets.clear(); nsrlSet = null; if (!this.setsFileExists()) { //create new if it doesn't exist save(); created = true; } //load, if fails to load create new; save regardless load(); if (!created) { //create new if failed to load save(); } } /** * Sets the local variable calculate to the given boolean. * @param set the state to make calculate */ public void setCalculate(boolean set) { this.calculate = set; //save(); } /** * Returns the value of the local boolean calculate. * @return true if calculate is true, false otherwise */ public boolean getCalculate() { return this.calculate; } /** * writes out current sets file replacing the last one */ public boolean save() { boolean success = false; DocumentBuilderFactory dbfac = DocumentBuilderFactory.newInstance(); try { DocumentBuilder docBuilder = dbfac.newDocumentBuilder(); Document doc = docBuilder.newDocument(); Element rootEl = doc.createElement(ROOT_EL); doc.appendChild(rootEl); for (HashDb set : knownBadSets) { String useForIngest = Boolean.toString(set.getUseForIngest()); String showInboxMessages = Boolean.toString(set.getShowInboxMessages()); List paths = set.getDatabasePaths(); String type = DBType.KNOWN_BAD.toString(); Element setEl = doc.createElement(SET_EL); setEl.setAttribute(SET_NAME_ATTR, set.getName()); setEl.setAttribute(SET_TYPE_ATTR, type); setEl.setAttribute(SET_USE_FOR_INGEST_ATTR, useForIngest); setEl.setAttribute(SET_SHOW_INBOX_MESSAGES, showInboxMessages); for (int i = 0; i < paths.size(); i++) { String path = paths.get(i); Element pathEl = doc.createElement(PATH_EL); pathEl.setAttribute(PATH_NUMBER_ATTR, Integer.toString(i)); pathEl.setTextContent(path); setEl.appendChild(pathEl); } rootEl.appendChild(setEl); } if(nsrlSet != null) { String useForIngest = Boolean.toString(nsrlSet.getUseForIngest()); String showInboxMessages = Boolean.toString(nsrlSet.getShowInboxMessages()); List paths = nsrlSet.getDatabasePaths(); String type = DBType.NSRL.toString(); Element setEl = doc.createElement(SET_EL); setEl.setAttribute(SET_NAME_ATTR, nsrlSet.getName()); setEl.setAttribute(SET_TYPE_ATTR, type); setEl.setAttribute(SET_USE_FOR_INGEST_ATTR, useForIngest); setEl.setAttribute(SET_SHOW_INBOX_MESSAGES, showInboxMessages); for (int i = 0; i < paths.size(); i++) { String path = paths.get(i); Element pathEl = doc.createElement(PATH_EL); pathEl.setAttribute(PATH_NUMBER_ATTR, Integer.toString(i)); pathEl.setTextContent(path); setEl.appendChild(pathEl); } rootEl.appendChild(setEl); } String calcValue = Boolean.toString(calculate); Element setCalc = doc.createElement(SET_CALC); setCalc.setAttribute(SET_VALUE, calcValue); rootEl.appendChild(setCalc); success = XMLUtil.saveDoc(HashDbXML.class, xmlFile, ENCODING, doc); } catch (ParserConfigurationException e) { logger.log(Level.SEVERE, "Error saving hash sets: can't initialize parser.", e); } return success; } /** * load and parse XML, then dispose */ public boolean load() { final Document doc = XMLUtil.loadDoc(HashDbXML.class, xmlFile, XSDFILE); if (doc == null) { return false; } Element root = doc.getDocumentElement(); if (root == null) { logger.log(Level.SEVERE, "Error loading hash sets: invalid file format."); return false; } NodeList setsNList = root.getElementsByTagName(SET_EL); int numSets = setsNList.getLength(); if(numSets==0) { logger.log(Level.WARNING, "No element hash_set exists."); } for (int i = 0; i < numSets; ++i) { Element setEl = (Element) setsNList.item(i); final String name = setEl.getAttribute(SET_NAME_ATTR); final String type = setEl.getAttribute(SET_TYPE_ATTR); final String useForIngest = setEl.getAttribute(SET_USE_FOR_INGEST_ATTR); final String showInboxMessages = setEl.getAttribute(SET_SHOW_INBOX_MESSAGES); Boolean useForIngestBool = Boolean.parseBoolean(useForIngest); Boolean showInboxMessagesBool = Boolean.parseBoolean(showInboxMessages); List paths = new ArrayList(); // Parse all paths NodeList pathsNList = setEl.getElementsByTagName(PATH_EL); final int numPaths = pathsNList.getLength(); for (int j = 0; j < numPaths; ++j) { Element pathEl = (Element) pathsNList.item(j); String number = pathEl.getAttribute(PATH_NUMBER_ATTR); String path = pathEl.getTextContent(); // If either the database or it's index exist File database = new File(path); File index = new File(HashDb.toIndexPath(path)); if(database.exists() || index.exists()) { paths.add(path); } else { // Ask for new path int ret = JOptionPane.showConfirmDialog(null, "Database " + name + " could not be found at location\n" + path + "\n" + " Would you like to search for the file?", "Missing Database", JOptionPane.YES_NO_OPTION); if (ret == JOptionPane.YES_OPTION) { String filePath = searchForFile(name); if(filePath!=null) { paths.add(filePath); } } } } // Check everything was properly set if(name.isEmpty()) { logger.log(Level.WARNING, "Name was not set for hash_set at index {0}.", i); } if(type.isEmpty()) { logger.log(Level.SEVERE, "Type was not set for hash_set at index {0}, cannot make instance of HashDb class.", i); return false; // exit because this causes a fatal error } if(useForIngest.isEmpty()) { logger.log(Level.WARNING, "UseForIngest was not set for hash_set at index {0}.", i); } if(showInboxMessages.isEmpty()) { logger.log(Level.WARNING, "ShowInboxMessages was not set for hash_set at index {0}.", i); } if(paths.isEmpty()) { logger.log(Level.WARNING, "No paths were set for hash_set at index {0}. Removing the database.", i); } else { // No paths for this entry, the user most likely declined to search for them DBType typeDBType = DBType.valueOf(type); HashDb set = new HashDb(name, paths, useForIngestBool, showInboxMessagesBool, typeDBType); if(typeDBType == DBType.KNOWN_BAD) { knownBadSets.add(set); } else if(typeDBType == DBType.NSRL) { this.nsrlSet = set; } } } NodeList calcList = root.getElementsByTagName(SET_CALC); int numCalc = calcList.getLength(); // Shouldn't be more than 1 if(numCalc==0) { logger.log(Level.WARNING, "No element hash_calculate exists."); } for(int i=0; i