Autopsy  4.12.0
Graphical digital forensics platform for The Sleuth Kit and other tools.
IngestEventsListener.java
Go to the documentation of this file.
1 /*
2  * Central Repository
3  *
4  * Copyright 2015-2019 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 static java.lang.Boolean.FALSE;
25 import java.util.ArrayList;
26 import java.util.Arrays;
27 import java.util.Collection;
28 import java.util.EnumSet;
29 import java.util.LinkedHashSet;
30 import java.util.List;
31 import java.util.Set;
32 import java.util.concurrent.ExecutorService;
33 import java.util.concurrent.Executors;
34 import java.util.logging.Level;
35 import java.util.stream.Collectors;
36 import org.apache.commons.lang3.StringUtils;
37 import org.openide.util.NbBundle;
49 import org.sleuthkit.datamodel.AbstractFile;
50 import org.sleuthkit.datamodel.Blackboard;
51 import org.sleuthkit.datamodel.BlackboardArtifact;
52 import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_ARTIFACT_HIT;
53 import org.sleuthkit.datamodel.BlackboardAttribute;
57 import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT;
58 import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_COMMENT;
59 import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME;
61 import org.sleuthkit.datamodel.Content;
62 import org.sleuthkit.datamodel.Image;
63 import org.sleuthkit.datamodel.SleuthkitCase;
64 import org.sleuthkit.datamodel.TskCoreException;
65 
70 @NbBundle.Messages({"IngestEventsListener.ingestmodule.name=Correlation Engine"})
71 public class IngestEventsListener {
72 
73  private static final Logger LOGGER = Logger.getLogger(CorrelationAttributeInstance.class.getName());
74  private static final Set<IngestManager.IngestJobEvent> INGEST_JOB_EVENTS_OF_INTEREST = EnumSet.of(IngestManager.IngestJobEvent.DATA_SOURCE_ANALYSIS_COMPLETED);
75  private static final Set<IngestManager.IngestModuleEvent> INGEST_MODULE_EVENTS_OF_INTEREST = EnumSet.of(DATA_ADDED);
76  private static final String MODULE_NAME = Bundle.IngestEventsListener_ingestmodule_name();
77  private static int correlationModuleInstanceCount;
78  private static boolean flagNotableItems;
79  private static boolean flagSeenDevices;
80  private static boolean createCrProperties;
81  private static final String INGEST_EVENT_THREAD_NAME = "Ingest-Event-Listener-%d";
82  private final ExecutorService jobProcessingExecutor;
83  private final PropertyChangeListener pcl1 = new IngestModuleEventListener();
84  private final PropertyChangeListener pcl2 = new IngestJobEventListener();
85  final Collection<String> recentlyAddedCeArtifacts = new LinkedHashSet<>();
86 
88  jobProcessingExecutor = Executors.newSingleThreadExecutor(new ThreadFactoryBuilder().setNameFormat(INGEST_EVENT_THREAD_NAME).build());
89  }
90 
91  void shutdown() {
92  ThreadUtils.shutDownTaskExecutor(jobProcessingExecutor);
93  }
94 
95  /*
96  * Add all of our Ingest Event Listeners to the IngestManager Instance.
97  */
98  public void installListeners() {
99  IngestManager.getInstance().addIngestModuleEventListener(INGEST_MODULE_EVENTS_OF_INTEREST, pcl1);
100  IngestManager.getInstance().addIngestJobEventListener(INGEST_JOB_EVENTS_OF_INTEREST, pcl2);
101  }
102 
103  /*
104  * Remove all of our Ingest Event Listeners from the IngestManager Instance.
105  */
106  public void uninstallListeners() {
109  }
110 
115  public synchronized static void incrementCorrelationEngineModuleCount() {
116  correlationModuleInstanceCount++; //Should be called once in the Correlation Engine module's startup method.
117  }
118 
123  public synchronized static void decrementCorrelationEngineModuleCount() {
124  if (getCeModuleInstanceCount() > 0) { //prevent it ingestJobCounter from going negative
125  correlationModuleInstanceCount--; //Should be called once in the Correlation Engine module's shutdown method.
126  }
127  }
128 
133  synchronized static void resetCeModuleInstanceCount() {
134  correlationModuleInstanceCount = 0; //called when a case is opened in case for some reason counter was not reset
135  }
136 
143  public synchronized static int getCeModuleInstanceCount() {
144  return correlationModuleInstanceCount;
145  }
146 
152  public synchronized static boolean isFlagNotableItems() {
153  return flagNotableItems;
154  }
155 
161  public synchronized static boolean isFlagSeenDevices() {
162  return flagSeenDevices;
163  }
164 
170  public synchronized static boolean shouldCreateCrProperties() {
171  return createCrProperties;
172  }
173 
179  public synchronized static void setFlagNotableItems(boolean value) {
180  flagNotableItems = value;
181  }
182 
188  public synchronized static void setFlagSeenDevices(boolean value) {
189  flagSeenDevices = value;
190  }
191 
197  public synchronized static void setCreateCrProperties(boolean value) {
198  createCrProperties = value;
199  }
200 
206  @NbBundle.Messages({"IngestEventsListener.prevTaggedSet.text=Previously Tagged As Notable (Central Repository)",
207  "IngestEventsListener.prevCaseComment.text=Previous Case: "})
208  static private void makeAndPostPreviousNotableArtifact(BlackboardArtifact originalArtifact, List<String> caseDisplayNames) {
209 
210  Collection<BlackboardAttribute> attributesForNewArtifact = Arrays.asList(new BlackboardAttribute(
211  TSK_SET_NAME, MODULE_NAME,
212  Bundle.IngestEventsListener_prevTaggedSet_text()),
213  new BlackboardAttribute(
214  TSK_COMMENT, MODULE_NAME,
215  Bundle.IngestEventsListener_prevCaseComment_text() + caseDisplayNames.stream().distinct().collect(Collectors.joining(","))),
216  new BlackboardAttribute(
217  TSK_ASSOCIATED_ARTIFACT, MODULE_NAME,
218  originalArtifact.getArtifactID()));
219  makeAndPostInterestingArtifact(originalArtifact, attributesForNewArtifact);
220  }
221 
228  @NbBundle.Messages({"IngestEventsListener.prevExists.text=Previously Seen Devices (Central Repository)",
229  "# {0} - typeName",
230  "# {1} - count",
231  "IngestEventsListener.prevCount.text=Number of previous {0}: {1}"})
232  static private void makeAndPostPreviousSeenArtifact(BlackboardArtifact originalArtifact) {
233  Collection<BlackboardAttribute> attributesForNewArtifact = Arrays.asList(new BlackboardAttribute(
234  TSK_SET_NAME, MODULE_NAME,
235  Bundle.IngestEventsListener_prevExists_text()),
236  new BlackboardAttribute(
237  TSK_ASSOCIATED_ARTIFACT, MODULE_NAME,
238  originalArtifact.getArtifactID()));
239  makeAndPostInterestingArtifact(originalArtifact, attributesForNewArtifact);
240  }
241 
247  private static void makeAndPostInterestingArtifact(BlackboardArtifact originalArtifact, Collection<BlackboardAttribute> attributesForNewArtifact) {
248  try {
249  SleuthkitCase tskCase = originalArtifact.getSleuthkitCase();
250  AbstractFile abstractFile = tskCase.getAbstractFileById(originalArtifact.getObjectID());
251  Blackboard blackboard = tskCase.getBlackboard();
252  // Create artifact if it doesn't already exist.
253  if (!blackboard.artifactExists(abstractFile, TSK_INTERESTING_ARTIFACT_HIT, attributesForNewArtifact)) {
254  BlackboardArtifact newInterestingArtifact = abstractFile.newArtifact(TSK_INTERESTING_ARTIFACT_HIT);
255  newInterestingArtifact.addAttributes(attributesForNewArtifact);
256 
257  try {
258  // index the artifact for keyword search
259  blackboard.postArtifact(newInterestingArtifact, MODULE_NAME);
260  } catch (Blackboard.BlackboardException ex) {
261  LOGGER.log(Level.SEVERE, "Unable to index blackboard artifact " + newInterestingArtifact.getArtifactID(), ex); //NON-NLS
262  }
263  }
264  } catch (TskCoreException ex) {
265  LOGGER.log(Level.SEVERE, "Failed to create BlackboardArtifact.", ex); // NON-NLS
266  } catch (IllegalStateException ex) {
267  LOGGER.log(Level.SEVERE, "Failed to create BlackboardAttribute.", ex); // NON-NLS
268  }
269  }
270 
271  private class IngestModuleEventListener implements PropertyChangeListener {
272 
273  @Override
274  public void propertyChange(PropertyChangeEvent evt) {
275  //if ingest is running we want there to check if there is a Correlation Engine module running
276  //sometimes artifacts are generated by DSPs or other sources while ingest is not running
277  //in these cases we still want to create correlation attributesForNewArtifact for those artifacts when appropriate
278  if (!IngestManager.getInstance().isIngestRunning() || getCeModuleInstanceCount() > 0) {
279  EamDb dbManager;
280  try {
281  dbManager = EamDb.getInstance();
282  } catch (EamDbException ex) {
283  LOGGER.log(Level.SEVERE, "Failed to connect to Central Repository database.", ex);
284  return;
285  }
286  switch (IngestManager.IngestModuleEvent.valueOf(evt.getPropertyName())) {
287  case DATA_ADDED: {
288  //if ingest isn't running create the interesting items otherwise use the ingest module setting to determine if we create interesting items
289  boolean flagNotable = !IngestManager.getInstance().isIngestRunning() || isFlagNotableItems();
290  boolean flagPrevious = !IngestManager.getInstance().isIngestRunning() || isFlagSeenDevices();
291  boolean createAttributes = !IngestManager.getInstance().isIngestRunning() || shouldCreateCrProperties();
292  jobProcessingExecutor.submit(new DataAddedTask(dbManager, evt, flagNotable, flagPrevious, createAttributes));
293  break;
294  }
295  default:
296  break;
297  }
298  }
299  }
300  }
301 
302  private class IngestJobEventListener implements PropertyChangeListener {
303 
304  @Override
305  public void propertyChange(PropertyChangeEvent evt) {
306  EamDb dbManager;
307  try {
308  dbManager = EamDb.getInstance();
309  } catch (EamDbException ex) {
310  LOGGER.log(Level.SEVERE, "Failed to connect to Central Repository database.", ex);
311  return;
312  }
313 
314  switch (IngestManager.IngestJobEvent.valueOf(evt.getPropertyName())) {
315  case DATA_SOURCE_ANALYSIS_COMPLETED: {
316  jobProcessingExecutor.submit(new AnalysisCompleteTask(dbManager, evt));
317  break;
318  }
319  default:
320  break;
321  }
322  }
323 
324  }
325 
326  private final class AnalysisCompleteTask implements Runnable {
327 
328  private final EamDb dbManager;
329  private final PropertyChangeEvent event;
330 
331  private AnalysisCompleteTask(EamDb db, PropertyChangeEvent evt) {
332  dbManager = db;
333  event = evt;
334  }
335 
336  @Override
337  public void run() {
338  // clear the tracker to reduce memory usage
339  if (getCeModuleInstanceCount() == 0) {
340  recentlyAddedCeArtifacts.clear();
341  }
342  //else another instance of the Correlation Engine Module is still being run.
343 
344  /*
345  * Ensure the data source in the Central Repository has hash values
346  * that match those in the case database.
347  */
348  if (!EamDb.isEnabled()) {
349  return;
350  }
351  Content dataSource;
352  String dataSourceName = "";
353  long dataSourceObjectId = -1;
354  try {
355  dataSource = ((DataSourceAnalysisEvent) event).getDataSource();
356  /*
357  * We only care about Images for the purpose of
358  * updating hash values.
359  */
360  if (!(dataSource instanceof Image)) {
361  return;
362  }
363 
364  dataSourceName = dataSource.getName();
365  dataSourceObjectId = dataSource.getId();
366 
367  Case openCase = Case.getCurrentCaseThrows();
368 
369  CorrelationCase correlationCase = dbManager.getCase(openCase);
370  if (null == correlationCase) {
371  correlationCase = dbManager.newCase(openCase);
372  }
373 
374  CorrelationDataSource correlationDataSource = dbManager.getDataSource(correlationCase, dataSource.getId());
375  if (correlationDataSource == null) {
376  // Add the data source.
377  CorrelationDataSource.fromTSKDataSource(correlationCase, dataSource);
378  } else {
379  // Sync the data source hash values if necessary.
380  if (dataSource instanceof Image) {
381  Image image = (Image) dataSource;
382 
383  String imageMd5Hash = image.getMd5();
384  if (imageMd5Hash == null) {
385  imageMd5Hash = "";
386  }
387  String crMd5Hash = correlationDataSource.getMd5();
388  if (StringUtils.equals(imageMd5Hash, crMd5Hash) == false) {
389  correlationDataSource.setMd5(imageMd5Hash);
390  }
391 
392  String imageSha1Hash = image.getSha1();
393  if (imageSha1Hash == null) {
394  imageSha1Hash = "";
395  }
396  String crSha1Hash = correlationDataSource.getSha1();
397  if (StringUtils.equals(imageSha1Hash, crSha1Hash) == false) {
398  correlationDataSource.setSha1(imageSha1Hash);
399  }
400 
401  String imageSha256Hash = image.getSha256();
402  if (imageSha256Hash == null) {
403  imageSha256Hash = "";
404  }
405  String crSha256Hash = correlationDataSource.getSha256();
406  if (StringUtils.equals(imageSha256Hash, crSha256Hash) == false) {
407  correlationDataSource.setSha256(imageSha256Hash);
408  }
409  }
410  }
411  } catch (EamDbException ex) {
412  LOGGER.log(Level.SEVERE, String.format(
413  "Unable to fetch data from the Central Repository for data source '%s' (obj_id=%d)",
414  dataSourceName, dataSourceObjectId), ex);
415  } catch (NoCurrentCaseException ex) {
416  LOGGER.log(Level.SEVERE, "No current case opened.", ex);
417  } catch (TskCoreException ex) {
418  LOGGER.log(Level.SEVERE, String.format(
419  "Unable to fetch data from the case database for data source '%s' (obj_id=%d)",
420  dataSourceName, dataSourceObjectId), ex);
421  }
422  } // DATA_SOURCE_ANALYSIS_COMPLETED
423  }
424 
425  private final class DataAddedTask implements Runnable {
426 
427  private final EamDb dbManager;
428  private final PropertyChangeEvent event;
429  private final boolean flagNotableItemsEnabled;
430  private final boolean flagPreviousItemsEnabled;
431  private final boolean createCorrelationAttributes;
432 
433  private DataAddedTask(EamDb db, PropertyChangeEvent evt, boolean flagNotableItemsEnabled, boolean flagPreviousItemsEnabled, boolean createCorrelationAttributes) {
434  this.dbManager = db;
435  this.event = evt;
436  this.flagNotableItemsEnabled = flagNotableItemsEnabled;
437  this.flagPreviousItemsEnabled = flagPreviousItemsEnabled;
438  this.createCorrelationAttributes = createCorrelationAttributes;
439  }
440 
441  @Override
442  public void run() {
443  if (!EamDb.isEnabled()) {
444  return;
445  }
446  final ModuleDataEvent mde = (ModuleDataEvent) event.getOldValue();
447  Collection<BlackboardArtifact> bbArtifacts = mde.getArtifacts();
448  if (null == bbArtifacts) { //the ModuleDataEvents don't always have a collection of artifacts set
449  return;
450  }
451  List<CorrelationAttributeInstance> eamArtifacts = new ArrayList<>();
452 
453  for (BlackboardArtifact bbArtifact : bbArtifacts) {
454  // eamArtifact will be null OR a EamArtifact containing one EamArtifactInstance.
455  List<CorrelationAttributeInstance> convertedArtifacts = EamArtifactUtil.makeInstancesFromBlackboardArtifact(bbArtifact, true);
456  for (CorrelationAttributeInstance eamArtifact : convertedArtifacts) {
457  try {
458  // Only do something with this artifact if it's unique within the job
459  if (recentlyAddedCeArtifacts.add(eamArtifact.toString())) {
460  // Was it previously marked as bad?
461  // query db for artifact instances having this TYPE/VALUE and knownStatus = "Bad".
462  // if getKnownStatus() is "Unknown" and this artifact instance was marked bad in a previous case,
463  // create TSK_INTERESTING_ARTIFACT_HIT artifact on BB.
464  if (flagNotableItemsEnabled) {
465  List<String> caseDisplayNames;
466  try {
467  caseDisplayNames = dbManager.getListCasesHavingArtifactInstancesKnownBad(eamArtifact.getCorrelationType(), eamArtifact.getCorrelationValue());
468  if (!caseDisplayNames.isEmpty()) {
469  makeAndPostPreviousNotableArtifact(bbArtifact,
470  caseDisplayNames);
471  }
473  LOGGER.log(Level.INFO, String.format("Unable to flag notable item: %s.", eamArtifact.toString()), ex);
474  }
475  }
476  if (flagPreviousItemsEnabled
477  && (eamArtifact.getCorrelationType().getId() == CorrelationAttributeInstance.USBID_TYPE_ID
478  || eamArtifact.getCorrelationType().getId() == CorrelationAttributeInstance.ICCID_TYPE_ID
479  || eamArtifact.getCorrelationType().getId() == CorrelationAttributeInstance.IMEI_TYPE_ID
480  || eamArtifact.getCorrelationType().getId() == CorrelationAttributeInstance.IMSI_TYPE_ID
481  || eamArtifact.getCorrelationType().getId() == CorrelationAttributeInstance.MAC_TYPE_ID)) {
482  try {
483  //only alert to previous instances when they were in another case
484  List<CorrelationAttributeInstance> previousOccurences = dbManager.getArtifactInstancesByTypeValue(eamArtifact.getCorrelationType(), eamArtifact.getCorrelationValue());
485  for (CorrelationAttributeInstance instance : previousOccurences) {
486  if (!instance.getCorrelationCase().getCaseUUID().equals(eamArtifact.getCorrelationCase().getCaseUUID())) {
487  makeAndPostPreviousSeenArtifact(bbArtifact);
488  break;
489  }
490  }
492  LOGGER.log(Level.INFO, String.format("Unable to flag notable item: %s.", eamArtifact.toString()), ex);
493  }
494  }
495  if (createCorrelationAttributes) {
496  eamArtifacts.add(eamArtifact);
497  }
498  }
499  } catch (EamDbException ex) {
500  LOGGER.log(Level.SEVERE, "Error counting notable artifacts.", ex);
501  }
502  }
503  }
504  if (FALSE == eamArtifacts.isEmpty()) {
505  for (CorrelationAttributeInstance eamArtifact : eamArtifacts) {
506  try {
507  dbManager.addArtifactInstance(eamArtifact);
508  } catch (EamDbException ex) {
509  LOGGER.log(Level.SEVERE, "Error adding artifact to database.", ex); //NON-NLS
510  }
511  }
512  } // DATA_ADDED
513  }
514  }
515 }
Collection< BlackboardArtifact > getArtifacts()
void removeIngestModuleEventListener(final PropertyChangeListener listener)
static List< CorrelationAttributeInstance > makeInstancesFromBlackboardArtifact(BlackboardArtifact artifact, boolean checkEnabled)
static synchronized IngestManager getInstance()
DataAddedTask(EamDb db, PropertyChangeEvent evt, boolean flagNotableItemsEnabled, boolean flagPreviousItemsEnabled, boolean createCorrelationAttributes)
List< String > getListCasesHavingArtifactInstancesKnownBad(CorrelationAttributeInstance.Type aType, String value)
CorrelationCase newCase(CorrelationCase eamCase)
static CorrelationDataSource fromTSKDataSource(CorrelationCase correlationCase, Content dataSource)
static void makeAndPostPreviousSeenArtifact(BlackboardArtifact originalArtifact)
static void makeAndPostInterestingArtifact(BlackboardArtifact originalArtifact, Collection< BlackboardAttribute > attributesForNewArtifact)
List< CorrelationAttributeInstance > getArtifactInstancesByTypeValue(CorrelationAttributeInstance.Type aType, String value)
void removeIngestJobEventListener(final PropertyChangeListener listener)
static void shutDownTaskExecutor(ExecutorService executor)
CorrelationDataSource getDataSource(CorrelationCase correlationCase, Long caseDbDataSourceId)
void addIngestJobEventListener(final PropertyChangeListener listener)
static void makeAndPostPreviousNotableArtifact(BlackboardArtifact originalArtifact, List< String > caseDisplayNames)
void addIngestModuleEventListener(final PropertyChangeListener listener)
synchronized static Logger getLogger(String name)
Definition: Logger.java:124
void addArtifactInstance(CorrelationAttributeInstance eamArtifact)

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