19 package org.sleuthkit.autopsy.modules.hashdatabase;
21 import java.beans.PropertyChangeEvent;
23 import java.io.IOException;
24 import java.util.ArrayList;
25 import java.util.HashSet;
26 import java.util.List;
28 import javax.swing.JFileChooser;
29 import javax.swing.filechooser.FileNameExtensionFilter;
30 import javax.xml.parsers.DocumentBuilder;
31 import javax.xml.parsers.DocumentBuilderFactory;
32 import javax.xml.parsers.ParserConfigurationException;
33 import org.openide.util.NbBundle;
36 import org.w3c.dom.Document;
37 import org.w3c.dom.Element;
38 import org.w3c.dom.NodeList;
39 import java.beans.PropertyChangeListener;
40 import java.beans.PropertyChangeSupport;
41 import java.util.concurrent.ExecutionException;
42 import java.util.logging.Level;
43 import javax.swing.JOptionPane;
44 import javax.swing.SwingWorker;
45 import org.apache.commons.io.FilenameUtils;
46 import org.apache.commons.io.FileUtils;
47 import org.netbeans.api.progress.ProgressHandle;
48 import org.netbeans.api.progress.ProgressHandleFactory;
76 private static final String
ENCODING =
"UTF-8";
84 PropertyChangeSupport changeSupport =
new PropertyChangeSupport(
HashDbManager.class);
94 DB_ADDED, DB_DELETED, DB_INDEXED
101 if (instance == null) {
108 changeSupport.addPropertyChangeListener(listener);
122 static String getHashDatabaseFileExtension() {
153 addExistingHashDatabaseInternal(hashSetName, path, searchDuringIngest, sendIngestMessages, knownFilesType);
155 throw new HashDbManagerException(ex.getMessage());
160 throw new HashDbManagerException(NbBundle.getMessage(
this.getClass(),
"HashDbManager.saveErrorExceptionMsg"));
186 if (!
new File(path).exists()) {
187 throw new HashDbManagerException(NbBundle.getMessage(
HashDbManager.class,
"HashDbManager.hashDbDoesNotExistExceptionMsg", path));
190 if (hashSetPaths.contains(path)) {
191 throw new HashDbManagerException(NbBundle.getMessage(
HashDbManager.class,
"HashDbManager.hashDbAlreadyAddedExceptionMsg", path));
194 if (hashSetNames.contains(hashSetName)) {
195 throw new HashDbManagerException(NbBundle.getMessage(
HashDbManager.class,
"HashDbManager.duplicateHashSetNameExceptionMsg", hashSetName));
198 return addHashDatabase(SleuthkitJNI.openHashDatabase(path), hashSetName, searchDuringIngest, sendIngestMessages, knownFilesType);
222 hashDb = addNewHashDatabaseInternal(hashSetName, path, searchDuringIngest, sendIngestMessages, knownFilesType);
223 }
catch (TskCoreException ex) {
224 throw new HashDbManagerException(ex.getMessage());
229 throw new HashDbManagerException(NbBundle.getMessage(
this.getClass(),
"HashDbManager.saveErrorExceptionMsg"));
253 synchronized HashDb addNewHashDatabaseInternal(String hashSetName, String path,
boolean searchDuringIngest,
boolean sendIngestMessages,
HashDb.
KnownFilesType knownFilesType) throws HashDbManagerException, TskCoreException {
254 File file =
new File(path);
256 throw new HashDbManagerException(NbBundle.getMessage(
HashDbManager.class,
"HashDbManager.hashDbFileExistsExceptionMsg", path));
258 if (!FilenameUtils.getExtension(file.getName()).equalsIgnoreCase(HASH_DATABASE_FILE_EXTENSON)) {
259 throw new HashDbManagerException(NbBundle.getMessage(
HashDbManager.class,
"HashDbManager.illegalHashDbFileNameExtensionMsg",
260 getHashDatabaseFileExtension()));
263 if (hashSetPaths.contains(path)) {
264 throw new HashDbManagerException(NbBundle.getMessage(
HashDbManager.class,
"HashDbManager.hashDbAlreadyAddedExceptionMsg", path));
267 if (hashSetNames.contains(hashSetName)) {
268 throw new HashDbManagerException(NbBundle.getMessage(
HashDbManager.class,
"HashDbManager.duplicateHashSetNameExceptionMsg", hashSetName));
271 return addHashDatabase(SleuthkitJNI.createHashDatabase(path), hashSetName, searchDuringIngest, sendIngestMessages, knownFilesType);
276 HashDb hashDb =
new HashDb(handle, hashSetName, searchDuringIngest, sendIngestMessages, knownFilesType);
286 if (!databasePath.equals(
"None")) {
287 hashSetPaths.add(databasePath);
289 if (!indexPath.equals(
"None")) {
290 hashSetPaths.add(indexPath);
295 knownHashSets.add(hashDb);
297 knownBadHashSets.add(hashDb);
302 changeSupport.firePropertyChange(
SetEvt.
DB_ADDED.toString(), null, hashSetName);
303 }
catch (Exception e) {
304 logger.log(Level.SEVERE,
"HashDbManager listener threw exception", e);
306 NbBundle.getMessage(
this.getClass(),
"HashDbManager.moduleErr"),
307 NbBundle.getMessage(
this.getClass(),
"HashDbManager.moduleErrorListeningToUpdatesMsg"),
313 synchronized void indexHashDatabase(
HashDb hashDb) {
315 HashDbIndexer creator =
new HashDbIndexer(hashDb);
323 if (null != hashDb) {
326 if (!indexPath.equals(
"None")) {
327 hashSetPaths.add(indexPath);
329 }
catch (TskCoreException ex) {
346 if (ingestIsRunning) {
347 throw new HashDbManagerException(NbBundle.getMessage(
this.getClass(),
"HashDbManager.ingestRunningExceptionMsg"));
349 removeHashDatabaseInternal(hashDb);
351 throw new HashDbManagerException(NbBundle.getMessage(
this.getClass(),
"HashDbManager.saveErrorExceptionMsg"));
363 synchronized void removeHashDatabaseInternal(
HashDb hashDb) {
369 knownHashSets.remove(hashDb);
370 knownBadHashSets.remove(hashDb);
371 hashSetNames.remove(hashSetName);
376 }
catch (TskCoreException ex) {
377 Logger.getLogger(
HashDbManager.class.getName()).log(Level.SEVERE,
"Error getting index path of " + hashDb.
getHashSetName() +
" hash database when removing the database", ex);
380 if (!hashDb.hasIndexOnly()) {
383 }
catch (TskCoreException ex) {
384 Logger.getLogger(
HashDbManager.class.getName()).log(Level.SEVERE,
"Error getting database path of " + hashDb.
getHashSetName() +
" hash database when removing the database", ex);
388 }
catch (TskCoreException ex) {
389 Logger.getLogger(
HashDbManager.class.getName()).log(Level.SEVERE,
"Error closing " + hashDb.
getHashSetName() +
" hash database when removing the database", ex);
395 changeSupport.firePropertyChange(SetEvt.DB_DELETED.toString(), null, hashSetName);
396 }
catch (Exception e) {
397 logger.log(Level.SEVERE,
"HashDbManager listener threw exception", e);
398 MessageNotifyUtil.Notify.show(
399 NbBundle.getMessage(
this.getClass(),
"HashDbManager.moduleErr"),
400 NbBundle.getMessage(
this.getClass(),
"HashDbManager.moduleErrorListeningToUpdatesMsg"),
401 MessageNotifyUtil.MessageType.ERROR);
412 List<HashDb> hashDbs =
new ArrayList<>();
413 hashDbs.addAll(knownHashSets);
414 hashDbs.addAll(knownBadHashSets);
424 List<HashDb> hashDbs =
new ArrayList<>();
425 hashDbs.addAll(knownHashSets);
435 List<HashDb> hashDbs =
new ArrayList<>();
436 hashDbs.addAll(knownBadHashSets);
448 return updateableDbs;
452 ArrayList<HashDb> updateableDbs =
new ArrayList<>();
453 for (
HashDb db : hashDbs) {
455 if (db.isUpdateable()) {
456 updateableDbs.add(db);
458 }
catch (TskCoreException ex) {
459 Logger.
getLogger(
HashDbManager.class.getName()).log(Level.SEVERE,
"Error checking updateable status of " + db.getHashSetName() +
" hash database", ex);
462 return updateableDbs;
471 synchronized boolean save() {
482 hashSetNames.clear();
483 hashSetPaths.clear();
491 for (
HashDb database : hashDatabases) {
494 }
catch (TskCoreException ex) {
498 hashDatabases.clear();
502 boolean success =
false;
503 DocumentBuilderFactory dbfac = DocumentBuilderFactory.newInstance();
505 DocumentBuilder docBuilder = dbfac.newDocumentBuilder();
506 Document doc = docBuilder.newDocument();
507 Element rootEl = doc.createElement(ROOT_ELEMENT);
508 doc.appendChild(rootEl);
514 }
catch (ParserConfigurationException ex) {
521 for (
HashDb db : hashDbs) {
526 if (db.hasIndexOnly()) {
527 path = db.getIndexPath();
529 path = db.getDatabasePath();
531 }
catch (TskCoreException ex) {
532 Logger.
getLogger(
HashDbManager.class.getName()).log(Level.SEVERE,
"Error getting path of hash database " + db.getHashSetName() +
", discarding from hash database configuration", ex);
536 Element setElement = doc.createElement(SET_ELEMENT);
537 setElement.setAttribute(SET_NAME_ATTRIBUTE, db.getHashSetName());
538 setElement.setAttribute(SET_TYPE_ATTRIBUTE, db.getKnownFilesType().toString());
539 setElement.setAttribute(SEARCH_DURING_INGEST_ATTRIBUTE, Boolean.toString(db.getSearchDuringIngest()));
540 setElement.setAttribute(SEND_INGEST_MESSAGES_ATTRIBUTE, Boolean.toString(db.getSendIngestMessages()));
541 Element pathElement = doc.createElement(PATH_ELEMENT);
542 pathElement.setTextContent(path);
543 setElement.appendChild(pathElement);
544 rootEl.appendChild(setElement);
549 File f =
new File(configFilePath);
550 return f.exists() && f.canRead() && f.canWrite();
554 boolean updatedSchema =
false;
563 Element root = doc.getDocumentElement();
570 NodeList setsNList = root.getElementsByTagName(SET_ELEMENT);
571 int numSets = setsNList.getLength();
578 String attributeErrorMessage =
" attribute was not set for hash_set at index {0}, cannot make instance of HashDb class";
579 String elementErrorMessage =
" element was not set for hash_set at index {0}, cannot make instance of HashDb class";
580 for (
int i = 0; i < numSets; ++i) {
581 Element setEl = (Element) setsNList.item(i);
583 String hashSetName = setEl.getAttribute(SET_NAME_ATTRIBUTE);
584 if (hashSetName.isEmpty()) {
590 if (hashSetNames.contains(hashSetName)) {
592 String newHashSetName;
595 newHashSetName = hashSetName + suffix;
596 }
while (hashSetNames.contains(newHashSetName));
597 JOptionPane.showMessageDialog(null,
598 NbBundle.getMessage(
this.getClass(),
599 "HashDbManager.replacingDuplicateHashsetNameMsg",
600 hashSetName, newHashSetName),
601 NbBundle.getMessage(
this.getClass(),
"HashDbManager.openHashDbErr"),
602 JOptionPane.ERROR_MESSAGE);
603 hashSetName = newHashSetName;
606 String knownFilesType = setEl.getAttribute(SET_TYPE_ATTRIBUTE);
607 if (knownFilesType.isEmpty()) {
613 if (knownFilesType.equals(
"NSRL")) {
615 updatedSchema =
true;
618 final String searchDuringIngest = setEl.getAttribute(SEARCH_DURING_INGEST_ATTRIBUTE);
619 if (searchDuringIngest.isEmpty()) {
623 Boolean seearchDuringIngestFlag = Boolean.parseBoolean(searchDuringIngest);
625 final String sendIngestMessages = setEl.getAttribute(SEND_INGEST_MESSAGES_ATTRIBUTE);
626 if (searchDuringIngest.isEmpty()) {
630 Boolean sendIngestMessagesFlag = Boolean.parseBoolean(sendIngestMessages);
633 NodeList pathsNList = setEl.getElementsByTagName(PATH_ELEMENT);
634 if (pathsNList.getLength() > 0) {
635 Element pathEl = (Element) pathsNList.item(0);
638 String legacyPathNumber = pathEl.getAttribute(LEGACY_PATH_NUMBER_ATTRIBUTE);
639 if (null != legacyPathNumber && !legacyPathNumber.isEmpty()) {
640 updatedSchema =
true;
643 dbPath = pathEl.getTextContent();
644 if (dbPath.isEmpty()) {
654 if (null != dbPath) {
656 addExistingHashDatabaseInternal(hashSetName, dbPath, seearchDuringIngestFlag, sendIngestMessagesFlag,
HashDb.
KnownFilesType.valueOf(knownFilesType));
657 }
catch (HashDbManagerException | TskCoreException ex) {
659 JOptionPane.showMessageDialog(null,
660 NbBundle.getMessage(
this.getClass(),
661 "HashDbManager.unableToOpenHashDbMsg", dbPath),
662 NbBundle.getMessage(
this.getClass(),
"HashDbManager.openHashDbErr"),
663 JOptionPane.ERROR_MESSAGE);
666 Logger.
getLogger(
HashDbManager.class.getName()).log(Level.WARNING,
"No valid path for hash_set at index {0}, cannot make instance of HashDb class", i);
671 String backupFilePath = configFilePath +
".v1_backup";
672 String messageBoxTitle = NbBundle.getMessage(this.getClass(),
673 "HashDbManager.msgBoxTitle.confFileFmtChanged");
674 String baseMessage = NbBundle.getMessage(this.getClass(),
675 "HashDbManager.baseMessage.updatedFormatHashDbConfig");
677 FileUtils.copyFile(
new File(configFilePath),
new File(backupFilePath));
678 JOptionPane.showMessageDialog(null,
679 NbBundle.getMessage(
this.getClass(),
680 "HashDbManager.savedBackupOfOldConfigMsg",
681 baseMessage, backupFilePath),
683 JOptionPane.INFORMATION_MESSAGE);
684 }
catch (IOException ex) {
685 Logger.
getLogger(
HashDbManager.class.getName()).log(Level.WARNING,
"Failed to save backup of old format configuration file to " + backupFilePath, ex);
686 JOptionPane.showMessageDialog(null, baseMessage, messageBoxTitle, JOptionPane.INFORMATION_MESSAGE);
697 File database =
new File(configuredPath);
698 if (database.exists()) {
699 return configuredPath;
703 String newPath = null;
704 if (JOptionPane.showConfirmDialog(null,
705 NbBundle.getMessage(
this.getClass(),
"HashDbManager.dlgMsg.dbNotFoundAtLoc",
706 hashSetName, configuredPath),
707 NbBundle.getMessage(
this.getClass(),
"HashDbManager.dlgTitle.MissingDb"),
708 JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION) {
710 if (null != newPath && !newPath.isEmpty()) {
711 database =
new File(newPath);
712 if (!database.exists()) {
721 String filePath = null;
722 JFileChooser fc =
new JFileChooser();
723 fc.setDragEnabled(
false);
724 fc.setFileSelectionMode(JFileChooser.FILES_ONLY);
725 String[] EXTENSION =
new String[]{
"txt",
"idx",
"hash",
"Hash",
"kdb"};
726 FileNameExtensionFilter filter =
new FileNameExtensionFilter(
727 NbBundle.getMessage(
this.getClass(),
"HashDbManager.fileNameExtensionFilter.title"), EXTENSION);
728 fc.setFileFilter(filter);
729 fc.setMultiSelectionEnabled(
false);
730 if (fc.showOpenDialog(null) == JFileChooser.APPROVE_OPTION) {
731 File f = fc.getSelectedFile();
733 filePath = f.getCanonicalPath();
734 }
catch (IOException ex) {
758 this.displayName = displayName;
762 return this.displayName;
781 private HashDb(
int handle, String hashSetName,
boolean useForIngest,
boolean sendHitMessages,
KnownFilesType knownFilesType) {
784 this.searchDuringIngest = useForIngest;
785 this.sendIngestMessages = sendHitMessages;
787 this.indexing =
false;
794 propertyChangeSupport.addPropertyChangeListener(pcl);
801 propertyChangeSupport.removePropertyChangeListener(pcl);
824 void setSearchDuringIngest(
boolean useForIngest) {
825 this.searchDuringIngest = useForIngest;
832 void setSendIngestMessages(
boolean showInboxMessages) {
833 this.sendIngestMessages = showInboxMessages;
868 if (content instanceof AbstractFile) {
869 AbstractFile file = (AbstractFile) content;
882 public void addHashes(List<HashEntry> hashes)
throws TskCoreException {
893 boolean result =
false;
895 if (content instanceof AbstractFile) {
896 AbstractFile file = (AbstractFile) content;
914 if (content instanceof AbstractFile) {
915 AbstractFile file = (AbstractFile) content;
924 boolean hasIndex() throws TskCoreException {
928 boolean hasIndexOnly() throws TskCoreException {
932 boolean canBeReIndexed() throws TskCoreException {
933 return SleuthkitJNI.hashDatabaseCanBeReindexed(handle);
936 boolean isIndexing() {
940 private void close() throws TskCoreException {
962 progress = ProgressHandleFactory.createHandle(
963 NbBundle.getMessage(
this.getClass(),
"HashDbManager.progress.indexingHashSet", hashDb.
hashSetName));
965 progress.switchToIndeterminate();
968 }
catch (TskCoreException ex) {
970 JOptionPane.showMessageDialog(null,
971 NbBundle.getMessage(
this.getClass(),
972 "HashDbManager.dlgMsg.errorIndexingHashSet",
974 NbBundle.getMessage(
this.getClass(),
"HashDbManager.hashDbIndexingErr"),
975 JOptionPane.ERROR_MESSAGE);
988 }
catch (InterruptedException | ExecutionException ex) {
989 logger.log(Level.SEVERE,
"Error creating index", ex);
991 NbBundle.getMessage(
this.getClass(),
"HashDbManager.errCreatingIndex.title"),
992 NbBundle.getMessage(
this.getClass(),
"HashDbManager.errCreatingIndex.msg", ex.getMessage()),
996 catch (java.util.concurrent.CancellationException ex ) { }
1001 }
catch (Exception e) {
1002 logger.log(Level.SEVERE,
"HashDbManager listener threw exception", e);
1004 NbBundle.getMessage(
this.getClass(),
"HashDbManager.moduleErr"),
1005 NbBundle.getMessage(
this.getClass(),
"HashDbManager.moduleErrorListeningToUpdatesMsg"),
boolean getSendIngestMessages()
Set< String > hashSetPaths
static String getHashDatabaseIndexPath(int dbHandle)
static void createLookupIndexForHashDatabase(int dbHandle)
static final Logger logger
static final String HASH_DATABASE_FILE_EXTENSON
static void addToHashDatabase(String filename, String md5, String sha1, String sha256, String comment, int dbHandle)
void addHashes(Content content, String comment)
static synchronized IngestManager getInstance()
static< T > Document loadDoc(Class< T > clazz, String xmlPath)
boolean getSearchDuringIngest()
synchronized void addPropertyChangeListener(PropertyChangeListener listener)
HashDb addExistingHashDatabase(String hashSetName, String path, boolean searchDuringIngest, boolean sendIngestMessages, HashDb.KnownFilesType knownFilesType)
boolean hashSetsConfigurationFileExists()
HashDbManagerException(String message)
static final String LEGACY_PATH_NUMBER_ATTRIBUTE
final PropertyChangeSupport propertyChangeSupport
HashDb addHashDatabase(int handle, String hashSetName, boolean searchDuringIngest, boolean sendIngestMessages, HashDb.KnownFilesType knownFilesType)
static HashHitInfo lookupInHashDatabaseVerbose(String hash, int dbHandle)
boolean readHashSetsConfigurationFromDisk()
KnownFilesType knownFilesType
boolean isIngestRunning()
static void writeHashDbsToDisk(Document doc, Element rootEl, List< HashDb > hashDbs)
static final String SET_NAME_ATTRIBUTE
List< HashDb > getUpdateableHashSets(List< HashDb > hashDbs)
static final String SET_TYPE_ATTRIBUTE
List< HashDb > knownHashSets
static final String ENCODING
static boolean hashDatabaseIsIndexOnly(int dbHandle)
HashDb addNewHashDatabase(String hashSetName, String path, boolean searchDuringIngest, boolean sendIngestMessages, HashDb.KnownFilesType knownFilesType)
boolean writeHashSetConfigurationToDisk()
static final String PATH_ELEMENT
synchronized void removeHashDatabase(HashDb hashDb)
KnownFilesType(String displayName)
void addPropertyChangeListener(PropertyChangeListener pcl)
List< HashDb > knownBadHashSets
final String configFilePath
synchronized List< HashDb > getKnownBadFileHashSets()
static HashDbManager instance
Set< String > hashSetNames
static synchronized HashDbManager getInstance()
static final String SEARCH_DURING_INGEST_ATTRIBUTE
String getValidFilePath(String hashSetName, String configuredPath)
void addHashes(List< HashEntry > hashes)
void addHashes(Content content)
HashHitInfo lookupMD5(Content content)
synchronized List< HashDb > getUpdateableHashSets()
synchronized List< HashDb > getAllHashSets()
static final String SET_ELEMENT
static boolean lookupInHashDatabase(String hash, int dbHandle)
static final String SEND_INGEST_MESSAGES_ATTRIBUTE
static boolean hashDatabaseHasLookupIndex(int dbHandle)
void propertyChange(PropertyChangeEvent event)
boolean sendIngestMessages
void closeHashDatabases(List< HashDb > hashDatabases)
boolean lookupMD5Quick(Content content)
boolean searchDuringIngest
synchronized void loadLastSavedConfiguration()
static void show(String title, String message, MessageType type, ActionListener actionListener)
static final String XSD_FILE_NAME
KnownFilesType getKnownFilesType()
static boolean isUpdateableHashDatabase(int dbHandle)
static final String CONFIG_FILE_NAME
static final String ROOT_ELEMENT
synchronized List< HashDb > getKnownFileHashSets()
void removePropertyChangeListener(PropertyChangeListener pcl)
HashDb(int handle, String hashSetName, boolean useForIngest, boolean sendHitMessages, KnownFilesType knownFilesType)
static String getHashDatabasePath(int dbHandle)
static Logger getLogger(String name)
static void closeHashDatabase(int dbHandle)
static< T > boolean saveDoc(Class< T > clazz, String xmlPath, String encoding, final Document doc)