Autopsy  4.11.0
Graphical digital forensics platform for The Sleuth Kit and other tools.
CaseEventListener.java
Go to the documentation of this file.
1 /*
2  * Central Repository
3  *
4  * Copyright 2015-2018 Basis Technology Corp.
5  * Contact: carrier <at> sleuthkit <dot> org
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  * http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  */
19 package org.sleuthkit.autopsy.centralrepository.eventlisteners;
20 
21 import com.google.common.util.concurrent.ThreadFactoryBuilder;
22 import java.beans.PropertyChangeEvent;
23 import java.beans.PropertyChangeListener;
24 import java.util.List;
25 import java.util.concurrent.ExecutorService;
26 import java.util.concurrent.Executors;
27 import java.util.logging.Level;
28 import java.util.stream.Collectors;
29 import org.apache.commons.lang.StringUtils;
30 import org.openide.util.NbBundle.Messages;
48 import org.sleuthkit.datamodel.AbstractFile;
49 import org.sleuthkit.datamodel.BlackboardArtifact;
50 import org.sleuthkit.datamodel.BlackboardArtifactTag;
51 import org.sleuthkit.datamodel.Content;
52 import org.sleuthkit.datamodel.ContentTag;
53 import org.sleuthkit.datamodel.TagName;
54 import org.sleuthkit.datamodel.TskCoreException;
55 import org.sleuthkit.datamodel.TskData;
56 import org.sleuthkit.datamodel.TskDataException;
57 
62 @Messages({"caseeventlistener.evidencetag=Evidence"})
63 final class CaseEventListener implements PropertyChangeListener {
64 
65  private static final Logger LOGGER = Logger.getLogger(CaseEventListener.class.getName());
66  private final ExecutorService jobProcessingExecutor;
67  private static final String CASE_EVENT_THREAD_NAME = "Case-Event-Listener-%d";
68 
69  CaseEventListener() {
70  jobProcessingExecutor = Executors.newSingleThreadExecutor(new ThreadFactoryBuilder().setNameFormat(CASE_EVENT_THREAD_NAME).build());
71  }
72 
73  void shutdown() {
74  ThreadUtils.shutDownTaskExecutor(jobProcessingExecutor);
75  }
76 
77  @Override
78  public void propertyChange(PropertyChangeEvent evt) {
79  EamDb dbManager;
80  try {
81  dbManager = EamDb.getInstance();
82  } catch (EamDbException ex) {
83  LOGGER.log(Level.SEVERE, "Failed to get instance of db manager.", ex);
84  return;
85  }
86  switch (Case.Events.valueOf(evt.getPropertyName())) {
87  case CONTENT_TAG_ADDED:
88  case CONTENT_TAG_DELETED: {
89  jobProcessingExecutor.submit(new ContentTagTask(dbManager, evt));
90  }
91  break;
92 
93  case BLACKBOARD_ARTIFACT_TAG_DELETED:
94  case BLACKBOARD_ARTIFACT_TAG_ADDED: {
95  jobProcessingExecutor.submit(new BlackboardTagTask(dbManager, evt));
96  }
97  break;
98 
99  case DATA_SOURCE_ADDED: {
100  jobProcessingExecutor.submit(new DataSourceAddedTask(dbManager, evt));
101  }
102  break;
103  case TAG_DEFINITION_CHANGED: {
104  jobProcessingExecutor.submit(new TagDefinitionChangeTask(evt));
105  }
106  break;
107  case CURRENT_CASE: {
108  jobProcessingExecutor.submit(new CurrentCaseTask(dbManager, evt));
109  }
110  break;
111  case DATA_SOURCE_NAME_CHANGED: {
112  jobProcessingExecutor.submit(new DataSourceNameChangedTask(dbManager, evt));
113  }
114  break;
115  }
116  }
117 
118  private final class ContentTagTask implements Runnable {
119 
120  private final EamDb dbManager;
121  private final PropertyChangeEvent event;
122 
123  private ContentTagTask(EamDb db, PropertyChangeEvent evt) {
124  dbManager = db;
125  event = evt;
126  }
127 
128  @Override
129  public void run() {
130  if (!EamDb.isEnabled()) {
131  return;
132  }
133 
134  AbstractFile af;
135  TskData.FileKnown knownStatus;
136  String comment;
137  if (Case.Events.valueOf(event.getPropertyName()) == Case.Events.CONTENT_TAG_ADDED) {
138  // For added tags, we want to change the known status to BAD if the
139  // tag that was just added is in the list of central repo tags.
140  final ContentTagAddedEvent tagAddedEvent = (ContentTagAddedEvent) event;
141  final ContentTag tagAdded = tagAddedEvent.getAddedTag();
142 
143  if (TagsManager.getNotableTagDisplayNames().contains(tagAdded.getName().getDisplayName())) {
144  if (tagAdded.getContent() instanceof AbstractFile) {
145  af = (AbstractFile) tagAdded.getContent();
146  knownStatus = TskData.FileKnown.BAD;
147  comment = tagAdded.getComment();
148  } else {
149  LOGGER.log(Level.WARNING, "Error updating non-file object");
150  return;
151  }
152  } else {
153  // The added tag isn't flagged as bad in central repo, so do nothing
154  return;
155  }
156  } else { // CONTENT_TAG_DELETED
157  // For deleted tags, we want to set the file status to UNKNOWN if:
158  // - The tag that was just removed is notable in central repo
159  // - There are no remaining tags that are notable
160  final ContentTagDeletedEvent tagDeletedEvent = (ContentTagDeletedEvent) event;
161  long contentID = tagDeletedEvent.getDeletedTagInfo().getContentID();
162 
163  String tagName = tagDeletedEvent.getDeletedTagInfo().getName().getDisplayName();
164  if (!TagsManager.getNotableTagDisplayNames().contains(tagName)) {
165  // If the tag that got removed isn't on the list of central repo tags, do nothing
166  return;
167  }
168 
169  try {
170  // Get the remaining tags on the content object
171  Content content = Case.getCurrentCaseThrows().getSleuthkitCase().getContentById(contentID);
173  List<ContentTag> tags = tagsManager.getContentTagsByContent(content);
174 
175  if (tags.stream()
176  .map(tag -> tag.getName().getDisplayName())
177  .filter(TagsManager.getNotableTagDisplayNames()::contains)
178  .collect(Collectors.toList())
179  .isEmpty()) {
180 
181  // There are no more bad tags on the object
182  if (content instanceof AbstractFile) {
183  af = (AbstractFile) content;
184  knownStatus = TskData.FileKnown.UNKNOWN;
185  comment = "";
186  } else {
187  LOGGER.log(Level.WARNING, "Error updating non-file object");
188  return;
189  }
190  } else {
191  // There's still at least one bad tag, so leave the known status as is
192  return;
193  }
194  } catch (TskCoreException | NoCurrentCaseException ex) {
195  LOGGER.log(Level.SEVERE, "Failed to find content", ex);
196  return;
197  }
198  }
199 
201 
202  if (eamArtifact != null) {
203  // send update to Central Repository db
204  try {
205  dbManager.setAttributeInstanceKnownStatus(eamArtifact, knownStatus);
206  } catch (EamDbException ex) {
207  LOGGER.log(Level.SEVERE, "Error connecting to Central Repository database while setting artifact known status.", ex); //NON-NLS
208  }
209  }
210  } // CONTENT_TAG_ADDED, CONTENT_TAG_DELETED
211  }
212 
213  private final class BlackboardTagTask implements Runnable {
214 
215  private final EamDb dbManager;
216  private final PropertyChangeEvent event;
217 
218  private BlackboardTagTask(EamDb db, PropertyChangeEvent evt) {
219  dbManager = db;
220  event = evt;
221  }
222 
223  @Override
224  public void run() {
225  if (!EamDb.isEnabled()) {
226  return;
227  }
228 
229  Content content;
230  BlackboardArtifact bbArtifact;
231  TskData.FileKnown knownStatus;
232  String comment;
233  if (Case.Events.valueOf(event.getPropertyName()) == Case.Events.BLACKBOARD_ARTIFACT_TAG_ADDED) {
234  // For added tags, we want to change the known status to BAD if the
235  // tag that was just added is in the list of central repo tags.
237  final BlackboardArtifactTag tagAdded = tagAddedEvent.getAddedTag();
238 
239  if (TagsManager.getNotableTagDisplayNames().contains(tagAdded.getName().getDisplayName())) {
240  content = tagAdded.getContent();
241  bbArtifact = tagAdded.getArtifact();
242  knownStatus = TskData.FileKnown.BAD;
243  comment = tagAdded.getComment();
244  } else {
245  // The added tag isn't flagged as bad in central repo, so do nothing
246  return;
247  }
248  } else { //BLACKBOARD_ARTIFACT_TAG_DELETED
249  Case openCase;
250  try {
251  openCase = Case.getCurrentCaseThrows();
252  } catch (NoCurrentCaseException ex) {
253  LOGGER.log(Level.SEVERE, "Exception while getting open case.", ex);
254  return;
255  }
256  // For deleted tags, we want to set the file status to UNKNOWN if:
257  // - The tag that was just removed is notable in central repo
258  // - There are no remaining tags that are notable
260  long contentID = tagDeletedEvent.getDeletedTagInfo().getContentID();
261  long artifactID = tagDeletedEvent.getDeletedTagInfo().getArtifactID();
262 
263  String tagName = tagDeletedEvent.getDeletedTagInfo().getName().getDisplayName();
264  if (!TagsManager.getNotableTagDisplayNames().contains(tagName)) {
265  // If the tag that got removed isn't on the list of central repo tags, do nothing
266  return;
267  }
268 
269  try {
270  // Get the remaining tags on the artifact
271  content = openCase.getSleuthkitCase().getContentById(contentID);
272  bbArtifact = openCase.getSleuthkitCase().getBlackboardArtifact(artifactID);
273  TagsManager tagsManager = openCase.getServices().getTagsManager();
274  List<BlackboardArtifactTag> tags = tagsManager.getBlackboardArtifactTagsByArtifact(bbArtifact);
275 
276  if (tags.stream()
277  .map(tag -> tag.getName().getDisplayName())
278  .filter(TagsManager.getNotableTagDisplayNames()::contains)
279  .collect(Collectors.toList())
280  .isEmpty()) {
281 
282  // There are no more bad tags on the object
283  knownStatus = TskData.FileKnown.UNKNOWN;
284  comment = "";
285 
286  } else {
287  // There's still at least one bad tag, so leave the known status as is
288  return;
289  }
290  } catch (TskCoreException ex) {
291  LOGGER.log(Level.SEVERE, "Failed to find content", ex);
292  return;
293  }
294  }
295 
296  if ((content instanceof AbstractFile) && (((AbstractFile) content).getKnown() == TskData.FileKnown.KNOWN)) {
297  return;
298  }
299 
300  List<CorrelationAttributeInstance> convertedArtifacts = EamArtifactUtil.makeInstancesFromBlackboardArtifact(bbArtifact, true);
301  for (CorrelationAttributeInstance eamArtifact : convertedArtifacts) {
302  eamArtifact.setComment(comment);
303  try {
304  dbManager.setAttributeInstanceKnownStatus(eamArtifact, knownStatus);
305  } catch (EamDbException ex) {
306  LOGGER.log(Level.SEVERE, "Error connecting to Central Repository database while setting artifact known status.", ex); //NON-NLS
307  }
308  }
309  } // BLACKBOARD_ARTIFACT_TAG_ADDED, BLACKBOARD_ARTIFACT_TAG_DELETED
310 
311  }
312 
313  private final class TagDefinitionChangeTask implements Runnable {
314 
315  private final PropertyChangeEvent event;
316 
317  private TagDefinitionChangeTask(PropertyChangeEvent evt) {
318  event = evt;
319  }
320 
321  @Override
322  public void run() {
323  if (!EamDb.isEnabled()) {
324  return;
325  }
326  //get the display name of the tag that has had it's definition modified
327  String modifiedTagName = (String) event.getOldValue();
328 
329  /*
330  * Set knownBad status for all files/artifacts in the given case
331  * that are tagged with the given tag name.
332  */
333  try {
334  TagName tagName = Case.getCurrentCaseThrows().getServices().getTagsManager().getDisplayNamesToTagNamesMap().get(modifiedTagName);
335  //First update the artifacts
336  //Get all BlackboardArtifactTags with this tag name
337  List<BlackboardArtifactTag> artifactTags = Case.getCurrentCaseThrows().getSleuthkitCase().getBlackboardArtifactTagsByTagName(tagName);
338  for (BlackboardArtifactTag bbTag : artifactTags) {
339  //start with assumption that none of the other tags applied to this Correlation Attribute will prevent it's status from being changed
340  boolean hasTagWithConflictingKnownStatus = false;
341  // if the status of the tag has been changed to TskData.FileKnown.UNKNOWN
342  // we need to check the status of all other tags on this correlation attribute before changing
343  // the status of the correlation attribute in the central repository
344  if (tagName.getKnownStatus() == TskData.FileKnown.UNKNOWN) {
345  Content content = bbTag.getContent();
346  // If the content which this Blackboard Artifact Tag is linked to is an AbstractFile with KNOWN status then
347  // it's status in the central reporsitory should not be changed to UNKNOWN
348  if ((content instanceof AbstractFile) && (((AbstractFile) content).getKnown() == TskData.FileKnown.KNOWN)) {
349  continue;
350  }
351  //Get the BlackboardArtifact which this BlackboardArtifactTag has been applied to.
352  BlackboardArtifact bbArtifact = bbTag.getArtifact();
354  List<BlackboardArtifactTag> tags = tagsManager.getBlackboardArtifactTagsByArtifact(bbArtifact);
355  //get all tags which are on this blackboard artifact
356  for (BlackboardArtifactTag t : tags) {
357  //All instances of the modified tag name will be changed, they can not conflict with each other
358  if (t.getName().equals(tagName)) {
359  continue;
360  }
361  //if any other tags on this artifact are Notable in status then this artifact can not have its status changed
362  if (TskData.FileKnown.BAD == t.getName().getKnownStatus()) {
363  //a tag with a conflicting status has been found, the status of this correlation attribute can not be modified
364  hasTagWithConflictingKnownStatus = true;
365  break;
366  }
367  }
368  }
369  //if the Correlation Attribute will have no tags with a status which would prevent the current status from being changed
370  if (!hasTagWithConflictingKnownStatus) {
371  //Get the correlation atttributes that correspond to the current BlackboardArtifactTag if their status should be changed
372  //with the initial set of correlation attributes this should be a single correlation attribute
373  List<CorrelationAttributeInstance> convertedArtifacts = EamArtifactUtil.makeInstancesFromBlackboardArtifact(bbTag.getArtifact(), true);
374  for (CorrelationAttributeInstance eamArtifact : convertedArtifacts) {
375  EamDb.getInstance().setAttributeInstanceKnownStatus(eamArtifact, tagName.getKnownStatus());
376  }
377  }
378  }
379  // Next update the files
380 
381  List<ContentTag> fileTags = Case.getCurrentCaseThrows().getSleuthkitCase().getContentTagsByTagName(tagName);
382  //Get all ContentTags with this tag name
383  for (ContentTag contentTag : fileTags) {
384  //start with assumption that none of the other tags applied to this ContentTag will prevent it's status from being changed
385  boolean hasTagWithConflictingKnownStatus = false;
386  // if the status of the tag has been changed to TskData.FileKnown.UNKNOWN
387  // we need to check the status of all other tags on this file before changing
388  // the status of the file in the central repository
389  if (tagName.getKnownStatus() == TskData.FileKnown.UNKNOWN) {
390  Content content = contentTag.getContent();
392  List<ContentTag> tags = tagsManager.getContentTagsByContent(content);
393  //get all tags which are on this file
394  for (ContentTag t : tags) {
395  //All instances of the modified tag name will be changed, they can not conflict with each other
396  if (t.getName().equals(tagName)) {
397  continue;
398  }
399  //if any other tags on this file are Notable in status then this file can not have its status changed
400  if (TskData.FileKnown.BAD == t.getName().getKnownStatus()) {
401  //a tag with a conflicting status has been found, the status of this file can not be modified
402  hasTagWithConflictingKnownStatus = true;
403  break;
404  }
405  }
406  }
407  //if the file will have no tags with a status which would prevent the current status from being changed
408  if (!hasTagWithConflictingKnownStatus) {
409  final CorrelationAttributeInstance eamArtifact = EamArtifactUtil.makeInstanceFromContent(contentTag.getContent());
410  if (eamArtifact != null) {
412  }
413  }
414  }
415  } catch (TskCoreException ex) {
416  LOGGER.log(Level.SEVERE, "Cannot update known status in central repository for tag: " + modifiedTagName, ex); //NON-NLS
417  } catch (EamDbException ex) {
418  LOGGER.log(Level.SEVERE, "Cannot get central repository for tag: " + modifiedTagName, ex); //NON-NLS
419  } catch (NoCurrentCaseException ex) {
420  LOGGER.log(Level.SEVERE, "Exception while getting open case.", ex); //NON-NLS
421  }
422  } //TAG_STATUS_CHANGED
423  }
424 
425  private final class DataSourceAddedTask implements Runnable {
426 
427  private final EamDb dbManager;
428  private final PropertyChangeEvent event;
429 
430  private DataSourceAddedTask(EamDb db, PropertyChangeEvent evt) {
431  dbManager = db;
432  event = evt;
433  }
434 
435  @Override
436  public void run() {
437  if (!EamDb.isEnabled()) {
438  return;
439  }
440  Case openCase;
441  try {
442  openCase = Case.getCurrentCaseThrows();
443  } catch (NoCurrentCaseException ex) {
444  LOGGER.log(Level.SEVERE, "Exception while getting open case.", ex);
445  return;
446  }
447 
448  final DataSourceAddedEvent dataSourceAddedEvent = (DataSourceAddedEvent) event;
449  Content newDataSource = dataSourceAddedEvent.getDataSource();
450 
451  try {
452  CorrelationCase correlationCase = dbManager.getCase(openCase);
453  if (null == dbManager.getDataSource(correlationCase, newDataSource.getId())) {
454  CorrelationDataSource.fromTSKDataSource(correlationCase, newDataSource);
455  }
456  } catch (EamDbException ex) {
457  LOGGER.log(Level.SEVERE, "Error adding new data source to the central repository", ex); //NON-NLS
458  }
459  } // DATA_SOURCE_ADDED
460  }
461 
462  private final class CurrentCaseTask implements Runnable {
463 
464  private final EamDb dbManager;
465  private final PropertyChangeEvent event;
466 
467  private CurrentCaseTask(EamDb db, PropertyChangeEvent evt) {
468  dbManager = db;
469  event = evt;
470  }
471 
472  @Override
473  public void run() {
474  /*
475  * A case has been opened if evt.getOldValue() is null and
476  * evt.getNewValue() is a valid Case.
477  */
478  if ((null == event.getOldValue()) && (event.getNewValue() instanceof Case)) {
479  Case curCase = (Case) event.getNewValue();
480  IngestEventsListener.resetCeModuleInstanceCount();
481 
482  if (!EamDb.isEnabled()) {
483  return;
484  }
485 
486  try {
487  // NOTE: Cannot determine if the opened case is a new case or a reopened case,
488  // so check for existing name in DB and insert if missing.
489  if (dbManager.getCase(curCase) == null) {
490  dbManager.newCase(curCase);
491  }
492  } catch (EamDbException ex) {
493  LOGGER.log(Level.SEVERE, "Error connecting to Central Repository database.", ex); //NON-NLS
494  }
495  }
496  } // CURRENT_CASE
497  }
498 
499  private final class DataSourceNameChangedTask implements Runnable {
500 
501  private final EamDb dbManager;
502  private final PropertyChangeEvent event;
503 
504  private DataSourceNameChangedTask(EamDb db, PropertyChangeEvent evt) {
505  dbManager = db;
506  event = evt;
507  }
508 
509  @Override
510  public void run() {
511 
512  final DataSourceNameChangedEvent dataSourceNameChangedEvent = (DataSourceNameChangedEvent) event;
513  Content dataSource = dataSourceNameChangedEvent.getDataSource();
514  String newName = (String) event.getNewValue();
515 
516  if (! StringUtils.isEmpty(newName)) {
517 
518  if (!EamDb.isEnabled()) {
519  return;
520  }
521 
522  try {
523  CorrelationCase correlationCase = dbManager.getCase(Case.getCurrentCaseThrows());
524  CorrelationDataSource existingEamDataSource = dbManager.getDataSource(correlationCase, dataSource.getId());
525  dbManager.updateDataSourceName(existingEamDataSource, newName);
526  } catch (EamDbException ex) {
527  LOGGER.log(Level.SEVERE, "Error updating data source with ID " + dataSource.getId() + " to " + newName, ex); //NON-NLS
528  } catch (NoCurrentCaseException ex) {
529  LOGGER.log(Level.SEVERE, "No open case", ex);
530  }
531  }
532  } // DATA_SOURCE_NAME_CHANGED
533  }
534 }
static List< CorrelationAttributeInstance > makeInstancesFromBlackboardArtifact(BlackboardArtifact artifact, boolean checkEnabled)
CorrelationCase newCase(CorrelationCase eamCase)
static CorrelationDataSource fromTSKDataSource(CorrelationCase correlationCase, Content dataSource)
List< ContentTag > getContentTagsByContent(Content content)
static void shutDownTaskExecutor(ExecutorService executor)
CorrelationDataSource getDataSource(CorrelationCase correlationCase, Long caseDbDataSourceId)
void setAttributeInstanceKnownStatus(CorrelationAttributeInstance eamArtifact, TskData.FileKnown knownStatus)
static CorrelationAttributeInstance makeInstanceFromContent(Content content)
synchronized static Logger getLogger(String name)
Definition: Logger.java:124
void updateDataSourceName(CorrelationDataSource eamDataSource, String newName)
List< BlackboardArtifactTag > getBlackboardArtifactTagsByArtifact(BlackboardArtifact artifact)

Copyright © 2012-2018 Basis Technology. Generated on: Fri Jun 21 2019
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.