diff --git a/Core/src/org/sleuthkit/autopsy/report/taggedhashes/AddTaggedHashesToHashDb.java b/Core/src/org/sleuthkit/autopsy/report/taggedhashes/AddTaggedHashesToHashDb.java new file mode 100755 index 0000000000..3348fdcafd --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/report/taggedhashes/AddTaggedHashesToHashDb.java @@ -0,0 +1,132 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2011-2016 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.report.taggedhashes; + +import java.util.ArrayList; +import java.util.List; +import java.util.logging.Level; +import org.sleuthkit.autopsy.coreutils.Logger; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import org.openide.util.lookup.ServiceProvider; +import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.services.TagsManager; +import org.sleuthkit.autopsy.modules.hashdatabase.HashDbManager.HashDb; +import org.sleuthkit.autopsy.report.GeneralReportModule; +import org.sleuthkit.autopsy.report.ReportProgressPanel; +import org.sleuthkit.datamodel.AbstractFile; +import org.sleuthkit.datamodel.Content; +import org.sleuthkit.datamodel.ContentTag; +import org.sleuthkit.datamodel.TagName; +import org.sleuthkit.datamodel.TskCoreException; + +/** + * Instances of this class plug in to the reporting infrastructure to provide a + * convenient way to add content hashes to hash set databases. + */ +@ServiceProvider(service = GeneralReportModule.class) +public class AddTaggedHashesToHashDb implements GeneralReportModule { + + private AddTaggedHashesToHashDbConfigPanel configPanel; + + public AddTaggedHashesToHashDb() { + } + + @Override + public String getName() { + return "Add Tagged Hashes"; + } + + @Override + public String getDescription() { + return "Adds hashes of tagged files to a hash database."; + } + + @Override + public String getRelativeFilePath() { + return ""; + } + + @Override + public void generateReport(String reportPath, ReportProgressPanel progressPanel) { + progressPanel.setIndeterminate(true); + progressPanel.start(); + progressPanel.updateStatusLabel("Adding hashes..."); + + HashDb hashSet = configPanel.getSelectedHashDatabase(); + if (hashSet != null) { + progressPanel.updateStatusLabel("Adding hashes to " + hashSet.getHashSetName() + " hash set..."); + + TagsManager tagsManager = Case.getCurrentCase().getServices().getTagsManager(); + List tagNames = configPanel.getSelectedTagNames(); + ArrayList failedExports = new ArrayList<>(); + for (TagName tagName : tagNames) { + if (progressPanel.getStatus() == ReportProgressPanel.ReportStatus.CANCELED) { + break; + } + + progressPanel.updateStatusLabel("Adding " + tagName.getDisplayName() + " hashes to " + hashSet.getHashSetName() + " hash set..."); + try { + List tags = tagsManager.getContentTagsByTagName(tagName); + for (ContentTag tag : tags) { + // TODO: Currently only AbstractFiles have md5 hashes. Here only files matter. + Content content = tag.getContent(); + if (content instanceof AbstractFile) { + if (null != ((AbstractFile) content).getMd5Hash()) { + try { + hashSet.addHashes(tag.getContent(), Case.getCurrentCase().getName()); + } catch (TskCoreException ex) { + Logger.getLogger(AddTaggedHashesToHashDb.class.getName()).log(Level.SEVERE, "Error adding hash for obj_id = " + tag.getContent().getId() + " to hash database " + hashSet.getHashSetName(), ex); + failedExports.add(tag.getContent().getName()); + } + } else { + JOptionPane.showMessageDialog(null, "Unable to add the " + (tags.size() > 1 ? "files" : "file") + " to the hash database. Hashes have not been calculated. Please configure and run an appropriate ingest module.", "Add to Hash Database Error", JOptionPane.ERROR_MESSAGE); + break; + } + } + } + } catch (TskCoreException ex) { + Logger.getLogger(AddTaggedHashesToHashDb.class.getName()).log(Level.SEVERE, "Error adding to hash database", ex); + JOptionPane.showMessageDialog(null, "Error getting selected tags for case.", "Hash Export Error", JOptionPane.ERROR_MESSAGE); + } + } + if (!failedExports.isEmpty()) { + StringBuilder errorMessage = new StringBuilder("Failed to export hashes for the following files: "); + for (int i = 0; i < failedExports.size(); ++i) { + errorMessage.append(failedExports.get(i)); + if (failedExports.size() > 1 && i < failedExports.size() - 1) { + errorMessage.append(","); + } + if (i == failedExports.size() - 1) { + errorMessage.append("."); + } + } + JOptionPane.showMessageDialog(null, errorMessage.toString(), "Hash Export Error", JOptionPane.ERROR_MESSAGE); + } + } + progressPanel.setIndeterminate(false); + progressPanel.complete(ReportProgressPanel.ReportStatus.COMPLETE); + } + + @Override + public JPanel getConfigurationPanel() { + configPanel = new AddTaggedHashesToHashDbConfigPanel(); + return configPanel; + } +} diff --git a/Core/src/org/sleuthkit/autopsy/report/taggedhashes/AddTaggedHashesToHashDbConfigPanel.form b/Core/src/org/sleuthkit/autopsy/report/taggedhashes/AddTaggedHashesToHashDbConfigPanel.form new file mode 100755 index 0000000000..377dad7826 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/report/taggedhashes/AddTaggedHashesToHashDbConfigPanel.form @@ -0,0 +1,148 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/Core/src/org/sleuthkit/autopsy/report/taggedhashes/AddTaggedHashesToHashDbConfigPanel.java b/Core/src/org/sleuthkit/autopsy/report/taggedhashes/AddTaggedHashesToHashDbConfigPanel.java new file mode 100755 index 0000000000..93dad76f89 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/report/taggedhashes/AddTaggedHashesToHashDbConfigPanel.java @@ -0,0 +1,326 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2011-2016 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.report.taggedhashes; + +import java.awt.Component; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.logging.Level; +import javax.swing.JCheckBox; +import javax.swing.JLabel; +import javax.swing.JList; +import javax.swing.JOptionPane; +import javax.swing.ListCellRenderer; +import javax.swing.ListModel; +import javax.swing.event.ListDataListener; +import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.autopsy.modules.hashdatabase.HashDbManager.HashDb; +import org.sleuthkit.autopsy.modules.hashdatabase.HashDbManager; +import org.sleuthkit.autopsy.modules.hashdatabase.HashLookupSettingsPanel; +import org.sleuthkit.datamodel.TagName; +import org.sleuthkit.datamodel.TskCoreException; + +/** + * Instances of this class are used to configure the report module plug in that + * provides a convenient way to add content hashes to hash set databases. + */ +class AddTaggedHashesToHashDbConfigPanel extends javax.swing.JPanel { + + private static final long serialVersionUID = 1L; + private final static String NO_DATABASES_TEXT = "No updateable hash sets"; + private List tagNames; + private final Map tagNameSelections = new LinkedHashMap<>(); + private final TagNamesListModel tagsNamesListModel = new TagNamesListModel(); + private final TagsNamesListCellRenderer tagsNamesRenderer = new TagsNamesListCellRenderer(); + private final Map hashSets = new HashMap<>(); + private HashDb selectedHashSet = null; + + AddTaggedHashesToHashDbConfigPanel() { + initComponents(); + customizeComponents(); + } + + private void customizeComponents() { + populateTagNameComponents(); + populateHashSetComponents(); + } + + private void populateTagNameComponents() { + // Get the tag names in use for the current case. + try { + tagNames = Case.getCurrentCase().getServices().getTagsManager().getTagNamesInUse(); + } catch (TskCoreException ex) { + Logger.getLogger(AddTaggedHashesToHashDbConfigPanel.class.getName()).log(Level.SEVERE, "Failed to get tag names", ex); + JOptionPane.showMessageDialog(null, "Error getting tag names for case.", "Tag Names Not Found", JOptionPane.ERROR_MESSAGE); + } + + // Mark the tag names as unselected. Note that tagNameSelections is a + // LinkedHashMap so that order is preserved and the tagNames and tagNameSelections + // containers are "parallel" containers. + for (TagName tagName : tagNames) { + tagNameSelections.put(tagName.getDisplayName(), Boolean.FALSE); + } + + // Set up the tag names JList component to be a collection of check boxes + // for selecting tag names. The mouse click listener updates tagNameSelections + // to reflect user choices. + tagNamesListBox.setModel(tagsNamesListModel); + tagNamesListBox.setCellRenderer(tagsNamesRenderer); + tagNamesListBox.setVisibleRowCount(-1); + tagNamesListBox.addMouseListener(new MouseAdapter() { + @Override + public void mousePressed(MouseEvent evt) { + JList list = (JList) evt.getSource(); + int index = list.locationToIndex(evt.getPoint()); + String value = tagsNamesListModel.getElementAt(index); + tagNameSelections.put(value, !tagNameSelections.get(value)); + list.repaint(); + } + }); + } + + private void populateHashSetComponents() { + // Clear the components because this method is called both during construction + // and when the user changes the hash set configuration. + hashSets.clear(); + hashSetsComboBox.removeAllItems(); + + // Get the updateable hash databases and add their hash set names to the + // JComboBox component. + List updateableHashSets = HashDbManager.getInstance().getUpdateableHashSets(); + if (!updateableHashSets.isEmpty()) { + for (HashDb hashDb : updateableHashSets) { + hashSets.put(hashDb.getHashSetName(), hashDb); + hashSetsComboBox.addItem(hashDb.getHashSetName()); + } + hashSetsComboBox.setEnabled(true); + } else { + hashSetsComboBox.addItem(NO_DATABASES_TEXT); + hashSetsComboBox.setEnabled(false); + } + } + + /** + * Gets the subset of the tag names in use selected by the user. + * + * @return A list, possibly empty, of TagName data transfer objects (DTOs). + */ + List getSelectedTagNames() { + List selectedTagNames = new ArrayList<>(); + for (TagName tagName : tagNames) { + if (tagNameSelections.get(tagName.getDisplayName())) { + selectedTagNames.add(tagName); + } + } + return selectedTagNames; + } + + /** + * Gets the hash set database selected by the user. + * + * @return A HashDb object representing the database or null. + */ + HashDb getSelectedHashDatabase() { + return selectedHashSet; + } + + // This class is a list model for the tag names JList component. + private class TagNamesListModel implements ListModel { + + @Override + public int getSize() { + return tagNames.size(); + } + + @Override + public String getElementAt(int index) { + return tagNames.get(index).getDisplayName(); + } + + @Override + public void addListDataListener(ListDataListener l) { + } + + @Override + public void removeListDataListener(ListDataListener l) { + } + } + + // This class renders the items in the tag names JList component as JCheckbox components. + private class TagsNamesListCellRenderer extends JCheckBox implements ListCellRenderer { + private static final long serialVersionUID = 1L; + + @Override + public Component getListCellRendererComponent(JList list, String value, int index, boolean isSelected, boolean cellHasFocus) { + if (value != null) { + setEnabled(list.isEnabled()); + setSelected(tagNameSelections.get(value)); + setFont(list.getFont()); + setBackground(list.getBackground()); + setForeground(list.getForeground()); + setText(value); + return this; + } + return new JLabel(); + } + } + + /** + * This method is called from within the constructor to initialize the form. + * WARNING: Do NOT modify this code. The content of this method is always + * regenerated by the Form Editor. + */ + @SuppressWarnings("unchecked") + // //GEN-BEGIN:initComponents + private void initComponents() { + + jScrollPane1 = new javax.swing.JScrollPane(); + tagNamesListBox = new javax.swing.JList<>(); + selectAllButton = new javax.swing.JButton(); + deselectAllButton = new javax.swing.JButton(); + jLabel1 = new javax.swing.JLabel(); + hashSetsComboBox = new javax.swing.JComboBox<>(); + configureHashDatabasesButton = new javax.swing.JButton(); + jLabel2 = new javax.swing.JLabel(); + + jScrollPane1.setViewportView(tagNamesListBox); + + org.openide.awt.Mnemonics.setLocalizedText(selectAllButton, org.openide.util.NbBundle.getMessage(AddTaggedHashesToHashDbConfigPanel.class, "AddTaggedHashesToHashDbConfigPanel.selectAllButton.text")); // NOI18N + selectAllButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + selectAllButtonActionPerformed(evt); + } + }); + + org.openide.awt.Mnemonics.setLocalizedText(deselectAllButton, org.openide.util.NbBundle.getMessage(AddTaggedHashesToHashDbConfigPanel.class, "AddTaggedHashesToHashDbConfigPanel.deselectAllButton.text")); // NOI18N + deselectAllButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + deselectAllButtonActionPerformed(evt); + } + }); + + org.openide.awt.Mnemonics.setLocalizedText(jLabel1, org.openide.util.NbBundle.getMessage(AddTaggedHashesToHashDbConfigPanel.class, "AddTaggedHashesToHashDbConfigPanel.jLabel1.text")); // NOI18N + + hashSetsComboBox.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + hashSetsComboBoxActionPerformed(evt); + } + }); + + org.openide.awt.Mnemonics.setLocalizedText(configureHashDatabasesButton, org.openide.util.NbBundle.getMessage(AddTaggedHashesToHashDbConfigPanel.class, "AddTaggedHashesToHashDbConfigPanel.configureHashDatabasesButton.text")); // NOI18N + configureHashDatabasesButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + configureHashDatabasesButtonActionPerformed(evt); + } + }); + + org.openide.awt.Mnemonics.setLocalizedText(jLabel2, org.openide.util.NbBundle.getMessage(AddTaggedHashesToHashDbConfigPanel.class, "AddTaggedHashesToHashDbConfigPanel.jLabel2.text")); // NOI18N + + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); + this.setLayout(layout); + layout.setHorizontalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addContainerGap() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(jLabel2) + .addComponent(jLabel1) + .addGroup(layout.createSequentialGroup() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(jScrollPane1) + .addGroup(layout.createSequentialGroup() + .addComponent(hashSetsComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, 159, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(configureHashDatabasesButton))) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false) + .addComponent(deselectAllButton, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(selectAllButton, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)))) + .addContainerGap()) + ); + layout.setVerticalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addContainerGap() + .addComponent(jLabel1) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addComponent(selectAllButton) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(deselectAllButton)) + .addComponent(jScrollPane1)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(jLabel2) + .addGap(4, 4, 4) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(hashSetsComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(configureHashDatabasesButton)) + .addContainerGap()) + ); + }// //GEN-END:initComponents + + private void selectAllButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_selectAllButtonActionPerformed + for (TagName tagName : tagNames) { + tagNameSelections.put(tagName.getDisplayName(), Boolean.TRUE); + } + tagNamesListBox.repaint(); + }//GEN-LAST:event_selectAllButtonActionPerformed + + private void hashSetsComboBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_hashSetsComboBoxActionPerformed + String key = (String)hashSetsComboBox.getSelectedItem(); + selectedHashSet = hashSets.get(key); + }//GEN-LAST:event_hashSetsComboBoxActionPerformed + + private void deselectAllButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_deselectAllButtonActionPerformed + for (TagName tagName : tagNames) { + tagNameSelections.put(tagName.getDisplayName(), Boolean.FALSE); + } + tagNamesListBox.repaint(); + }//GEN-LAST:event_deselectAllButtonActionPerformed + + private void configureHashDatabasesButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_configureHashDatabasesButtonActionPerformed + HashLookupSettingsPanel configPanel = new HashLookupSettingsPanel(); + configPanel.load(); + if (JOptionPane.showConfirmDialog(null, configPanel, "Hash Set Configuration", JOptionPane.OK_CANCEL_OPTION, JOptionPane.PLAIN_MESSAGE) == JOptionPane.OK_OPTION) { + configPanel.store(); + populateHashSetComponents(); + } else { + configPanel.cancel(); + populateHashSetComponents(); + } + }//GEN-LAST:event_configureHashDatabasesButtonActionPerformed + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JButton configureHashDatabasesButton; + private javax.swing.JButton deselectAllButton; + private javax.swing.JComboBox hashSetsComboBox; + private javax.swing.JLabel jLabel1; + private javax.swing.JLabel jLabel2; + private javax.swing.JScrollPane jScrollPane1; + private javax.swing.JButton selectAllButton; + private javax.swing.JList tagNamesListBox; + // End of variables declaration//GEN-END:variables +} diff --git a/Core/src/org/sleuthkit/autopsy/report/taggedhashes/Bundle.properties b/Core/src/org/sleuthkit/autopsy/report/taggedhashes/Bundle.properties new file mode 100755 index 0000000000..82da454a0c --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/report/taggedhashes/Bundle.properties @@ -0,0 +1,7 @@ +HashDbConfigDialog.okButton.text=OK +HashDbConfigDialog.cancelButton.text=Cancel +AddTaggedHashesToHashDbConfigPanel.selectAllButton.text=Select All +AddTaggedHashesToHashDbConfigPanel.jLabel2.text=Export to hash set: +AddTaggedHashesToHashDbConfigPanel.configureHashDatabasesButton.text=Configure Hash Sets... +AddTaggedHashesToHashDbConfigPanel.jLabel1.text=Export hashes of files tagged as: +AddTaggedHashesToHashDbConfigPanel.deselectAllButton.text=Deselect All