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

Copyright © 2012-2020 Basis Technology. Generated on: Wed Apr 8 2020
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.