Autopsy  4.19.1
Graphical digital forensics platform for The Sleuth Kit and other tools.
CentralRepoIngestModule.java
Go to the documentation of this file.
1 /*
2  * Central Repository
3  *
4  * Copyright 2011-2021 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.ingestmodule;
20 
21 import java.util.Arrays;
22 import java.util.Collection;
23 import java.util.List;
24 import java.util.logging.Level;
25 import java.util.stream.Collectors;
26 import org.openide.util.NbBundle.Messages;
48 import org.sleuthkit.datamodel.AbstractFile;
49 import org.sleuthkit.datamodel.Blackboard;
50 import org.sleuthkit.datamodel.BlackboardArtifact;
51 import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_PREVIOUSLY_NOTABLE;
52 import org.sleuthkit.datamodel.BlackboardAttribute;
53 import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME;
54 import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_CORRELATION_TYPE;
55 import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_CORRELATION_VALUE;
56 import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_OTHER_CASES;
57 import org.sleuthkit.datamodel.HashUtility;
58 import org.sleuthkit.datamodel.TskCoreException;
59 import org.sleuthkit.datamodel.TskData;
61 import org.sleuthkit.datamodel.Score;
62 
67 @Messages({"CentralRepoIngestModule.prevTaggedSet.text=Previously Tagged As Notable (Central Repository)",
68  "CentralRepoIngestModule.prevCaseComment.text=Previous Case: "})
69 final class CentralRepoIngestModule implements FileIngestModule {
70 
71  private static final String MODULE_NAME = CentralRepoIngestModuleFactory.getModuleName();
72  static final boolean DEFAULT_FLAG_TAGGED_NOTABLE_ITEMS = false;
73  static final boolean DEFAULT_FLAG_PREVIOUS_DEVICES = false;
74  static final boolean DEFAULT_FLAG_UNIQUE_DEVICES = false;
75  static final boolean DEFAULT_CREATE_CR_PROPERTIES = true;
76 
77  private final static Logger logger = Logger.getLogger(CentralRepoIngestModule.class.getName());
78  private final IngestServices services = IngestServices.getInstance();
79  private static final IngestModuleReferenceCounter refCounter = new IngestModuleReferenceCounter();
80  private static final IngestModuleReferenceCounter warningMsgRefCounter = new IngestModuleReferenceCounter();
81  private long jobId;
82  private CorrelationCase eamCase;
83  private CorrelationDataSource eamDataSource;
84  private CorrelationAttributeInstance.Type filesType;
85  private final boolean flagTaggedNotableItems;
86  private final boolean flagPreviouslySeenDevices;
87  private Blackboard blackboard;
88  private final boolean createCorrelationProperties;
89  private final boolean flagUniqueArtifacts;
90 
96  CentralRepoIngestModule(IngestSettings settings) {
97  flagTaggedNotableItems = settings.isFlagTaggedNotableItems();
98  flagPreviouslySeenDevices = settings.isFlagPreviousDevices();
99  createCorrelationProperties = settings.shouldCreateCorrelationProperties();
100  flagUniqueArtifacts = settings.isFlagUniqueArtifacts();
101  }
102 
103  @Override
104  public ProcessResult process(AbstractFile abstractFile) {
105  if (CentralRepository.isEnabled() == false) {
106  /*
107  * Not signaling an error for now. This is a workaround for the way
108  * all newly didscovered ingest modules are automatically anabled.
109  *
110  * TODO (JIRA-2731): Add isEnabled API for ingest modules.
111  */
112  return ProcessResult.OK;
113  }
114 
115  try {
116  blackboard = Case.getCurrentCaseThrows().getSleuthkitCase().getBlackboard();
117  } catch (NoCurrentCaseException ex) {
118  logger.log(Level.SEVERE, "Exception while getting open case.", ex);
119  return ProcessResult.ERROR;
120  }
121 
122  if (!CorrelationAttributeUtil.isSupportedAbstractFileType(abstractFile)) {
123  return ProcessResult.OK;
124  }
125 
126  if (abstractFile.getKnown() == TskData.FileKnown.KNOWN) {
127  return ProcessResult.OK;
128  }
129 
130  CentralRepository dbManager;
131  try {
132  dbManager = CentralRepository.getInstance();
133  } catch (CentralRepoException ex) {
134  logger.log(Level.SEVERE, "Error connecting to Central Repository database.", ex);
135  return ProcessResult.ERROR;
136  }
137 
138  // only continue if we are correlating filesType
139  if (!filesType.isEnabled()) {
140  return ProcessResult.OK;
141  }
142 
143  // get the hash because we're going to correlate it
144  String md5 = abstractFile.getMd5Hash();
145  if ((md5 == null) || (HashUtility.isNoDataMd5(md5))) {
146  return ProcessResult.OK;
147  }
148 
149  /*
150  * Search the central repo to see if this file was previously marked as
151  * being bad. Create artifact if it was.
152  */
153  if (abstractFile.getKnown() != TskData.FileKnown.KNOWN && flagTaggedNotableItems) {
154  try {
155  TimingMetric timingMetric = HealthMonitor.getTimingMetric("Central Repository: Notable artifact query");
156  List<String> caseDisplayNamesList = dbManager.getListCasesHavingArtifactInstancesKnownBad(filesType, md5);
157  HealthMonitor.submitTimingMetric(timingMetric);
158  if (!caseDisplayNamesList.isEmpty()) {
159  postCorrelatedBadFileToBlackboard(abstractFile, caseDisplayNamesList, filesType, md5);
160  }
161  } catch (CentralRepoException ex) {
162  logger.log(Level.SEVERE, "Error searching database for artifact.", ex); // NON-NLS
163  return ProcessResult.ERROR;
164  } catch (CorrelationAttributeNormalizationException ex) {
165  logger.log(Level.INFO, "Error searching database for artifact.", ex); // NON-NLS
166  return ProcessResult.ERROR;
167  }
168  }
169 
170  // insert this file into the central repository
171  if (createCorrelationProperties) {
172  try {
173  CorrelationAttributeInstance cefi = new CorrelationAttributeInstance(
174  filesType,
175  md5,
176  eamCase,
177  eamDataSource,
178  abstractFile.getParentPath() + abstractFile.getName(),
179  null,
180  TskData.FileKnown.UNKNOWN // NOTE: Known status in the CR is based on tagging, not hashes like the Case Database.
181  ,
182  abstractFile.getId());
183  dbManager.addAttributeInstanceBulk(cefi);
184  } catch (CentralRepoException ex) {
185  logger.log(Level.SEVERE, "Error adding artifact to bulk artifacts.", ex); // NON-NLS
186  return ProcessResult.ERROR;
187  } catch (CorrelationAttributeNormalizationException ex) {
188  logger.log(Level.INFO, "Error adding artifact to bulk artifacts.", ex); // NON-NLS
189  return ProcessResult.ERROR;
190  }
191  }
192  return ProcessResult.OK;
193  }
194 
195  @Override
196  public void shutDown() {
197  IngestEventsListener.decrementCorrelationEngineModuleCount();
198 
199  if ((CentralRepository.isEnabled() == false) || (eamCase == null) || (eamDataSource == null)) {
200  return;
201  }
202  CentralRepository dbManager;
203  try {
204  dbManager = CentralRepository.getInstance();
205  } catch (CentralRepoException ex) {
206  logger.log(Level.SEVERE, "Error connecting to Central Repository database.", ex);
207  return;
208  }
209  try {
210  dbManager.commitAttributeInstancesBulk();
211  } catch (CentralRepoException ex) {
212  logger.log(Level.SEVERE, "Error doing bulk insert of artifacts.", ex); // NON-NLS
213  }
214  try {
215  Long count = dbManager.getCountArtifactInstancesByCaseDataSource(eamDataSource);
216  logger.log(Level.INFO, "{0} artifacts in db for case: {1} ds:{2}", new Object[]{count, eamCase.getDisplayName(), eamDataSource.getName()}); // NON-NLS
217  } catch (CentralRepoException ex) {
218  logger.log(Level.SEVERE, "Error counting artifacts.", ex); // NON-NLS
219  }
220 
221  // TODO: once we implement shared cache, if refCounter is 1, then submit data in bulk.
222  refCounter.decrementAndGet(jobId);
223  }
224 
225  // see ArtifactManagerTimeTester for details
226  @Messages({
227  "CentralRepoIngestModule.notfyBubble.title=Central Repository Not Initialized",
228  "CentralRepoIngestModule.errorMessage.isNotEnabled=Central repository settings are not initialized, cannot run Central Repository ingest module."
229  })
230  @Override
231  public void startUp(IngestJobContext context) throws IngestModuleException {
232  IngestEventsListener.incrementCorrelationEngineModuleCount();
233 
234  /*
235  * Tell the IngestEventsListener to flag notable items based on the
236  * current module's configuration. This is a work around for the lack of
237  * an artifacts pipeline. Note that this can be changed by another
238  * module instance. All modules are affected by the value. While not
239  * ideal, this will be good enough until a better solution can be
240  * posited.
241  *
242  * Note: Flagging cannot be disabled if any other instances of the
243  * Central Repository module are running. This restriction is to prevent
244  * missing results in the case where the first module is flagging
245  * notable items, and the proceeding module (with flagging disabled)
246  * causes the first to stop flagging.
247  */
248  if (IngestEventsListener.getCeModuleInstanceCount() == 1 || !IngestEventsListener.isFlagNotableItems()) {
249  IngestEventsListener.setFlagNotableItems(flagTaggedNotableItems);
250  }
251  if (IngestEventsListener.getCeModuleInstanceCount() == 1 || !IngestEventsListener.isFlagSeenDevices()) {
252  IngestEventsListener.setFlagSeenDevices(flagPreviouslySeenDevices);
253  }
254  if (IngestEventsListener.getCeModuleInstanceCount() == 1 || !IngestEventsListener.shouldCreateCrProperties()) {
255  IngestEventsListener.setCreateCrProperties(createCorrelationProperties);
256  }
257  if (IngestEventsListener.getCeModuleInstanceCount() == 1 || !IngestEventsListener.isFlagUniqueArtifacts()) {
258  IngestEventsListener.setFlagUniqueArtifacts(flagUniqueArtifacts);
259  }
260 
261  if (CentralRepository.isEnabled() == false) {
262  /*
263  * Not throwing the customary exception for now. This is a
264  * workaround for the way all newly didscovered ingest modules are
265  * automatically anabled.
266  *
267  * TODO (JIRA-2731): Add isEnabled API for ingest modules.
268  */
269  if (RuntimeProperties.runningWithGUI()) {
270  if (1L == warningMsgRefCounter.incrementAndGet(jobId)) {
271  MessageNotifyUtil.Notify.warn(Bundle.CentralRepoIngestModule_notfyBubble_title(), Bundle.CentralRepoIngestModule_errorMessage_isNotEnabled());
272  }
273  }
274  return;
275  }
276  Case autopsyCase;
277  try {
278  autopsyCase = Case.getCurrentCaseThrows();
279  } catch (NoCurrentCaseException ex) {
280  logger.log(Level.SEVERE, "Exception while getting open case.", ex);
281  throw new IngestModuleException("Exception while getting open case.", ex);
282  }
283 
284  // Don't allow sqlite central repo databases to be used for multi user cases
285  if ((autopsyCase.getCaseType() == Case.CaseType.MULTI_USER_CASE)
286  && (CentralRepoDbManager.getSavedDbChoice().getDbPlatform() == CentralRepoPlatforms.SQLITE)) {
287  logger.log(Level.SEVERE, "Cannot run Central Repository ingest module on a multi-user case with a SQLite central repository.");
288  throw new IngestModuleException("Cannot run on a multi-user case with a SQLite central repository."); // NON-NLS
289  }
290  jobId = context.getJobId();
291 
292  CentralRepository centralRepoDb;
293  try {
294  centralRepoDb = CentralRepository.getInstance();
295  } catch (CentralRepoException ex) {
296  logger.log(Level.SEVERE, "Error connecting to central repository database.", ex); // NON-NLS
297  throw new IngestModuleException("Error connecting to central repository database.", ex); // NON-NLS
298  }
299 
300  try {
301  filesType = centralRepoDb.getCorrelationTypeById(CorrelationAttributeInstance.FILES_TYPE_ID);
302  } catch (CentralRepoException ex) {
303  logger.log(Level.SEVERE, "Error getting correlation type FILES in ingest module start up.", ex); // NON-NLS
304  throw new IngestModuleException("Error getting correlation type FILES in ingest module start up.", ex); // NON-NLS
305  }
306 
307  try {
308  eamCase = centralRepoDb.getCase(autopsyCase);
309  } catch (CentralRepoException ex) {
310  throw new IngestModuleException("Unable to get case from central repository database ", ex);
311  }
312 
313  try {
314  eamDataSource = CorrelationDataSource.fromTSKDataSource(eamCase, context.getDataSource());
315  } catch (CentralRepoException ex) {
316  logger.log(Level.SEVERE, "Error getting data source info.", ex); // NON-NLS
317  throw new IngestModuleException("Error getting data source info.", ex); // NON-NLS
318  }
319  // TODO: once we implement a shared cache, load/init it here w/ syncronized and define reference counter
320  // if we are the first thread / module for this job, then make sure the case
321  // and image exist in the DB before we associate artifacts with it.
322  if (refCounter.incrementAndGet(jobId)
323  == 1) {
324  // ensure we have this data source in the EAM DB
325  try {
326  if (null == centralRepoDb.getDataSource(eamCase, eamDataSource.getDataSourceObjectID())) {
327  centralRepoDb.newDataSource(eamDataSource);
328  }
329  } catch (CentralRepoException ex) {
330  logger.log(Level.SEVERE, "Error adding data source to Central Repository.", ex); // NON-NLS
331  throw new IngestModuleException("Error adding data source to Central Repository.", ex); // NON-NLS
332  }
333 
334  }
335  }
336 
343  private void postCorrelatedBadFileToBlackboard(AbstractFile abstractFile, List<String> caseDisplayNames, CorrelationAttributeInstance.Type aType, String value) {
344  String prevCases = caseDisplayNames.stream().distinct().collect(Collectors.joining(","));
345  String justification = "Previously marked as notable in cases " + prevCases;
346  Collection<BlackboardAttribute> attributes = Arrays.asList(
347  new BlackboardAttribute(
348  TSK_SET_NAME, MODULE_NAME,
349  Bundle.CentralRepoIngestModule_prevTaggedSet_text()),
350  new BlackboardAttribute(
351  TSK_CORRELATION_TYPE, MODULE_NAME,
352  aType.getDisplayName()),
353  new BlackboardAttribute(
354  TSK_CORRELATION_VALUE, MODULE_NAME,
355  value),
356  new BlackboardAttribute(
357  TSK_OTHER_CASES, MODULE_NAME,
358  prevCases));
359  try {
360  // Create artifact if it doesn't already exist.
361  if (!blackboard.artifactExists(abstractFile, TSK_PREVIOUSLY_NOTABLE, attributes)) {
362  BlackboardArtifact tifArtifact = abstractFile.newAnalysisResult(
363  BlackboardArtifact.Type.TSK_PREVIOUSLY_NOTABLE, Score.SCORE_NOTABLE,
364  null, Bundle.CentralRepoIngestModule_prevTaggedSet_text(), justification, attributes)
365  .getAnalysisResult();
366  try {
367  // index the artifact for keyword search
368  blackboard.postArtifact(tifArtifact, MODULE_NAME);
369  } catch (Blackboard.BlackboardException ex) {
370  logger.log(Level.SEVERE, "Unable to index blackboard artifact " + tifArtifact.getArtifactID(), ex); //NON-NLS
371  }
372  // send inbox message
373  sendBadFileInboxMessage(tifArtifact, abstractFile.getName(), abstractFile.getMd5Hash(), caseDisplayNames);
374  }
375  } catch (TskCoreException ex) {
376  logger.log(Level.SEVERE, "Failed to create BlackboardArtifact.", ex); // NON-NLS
377  } catch (IllegalStateException ex) {
378  logger.log(Level.SEVERE, "Failed to create BlackboardAttribute.", ex); // NON-NLS
379  }
380  }
381 
382  @Messages({
383  "CentralRepoIngestModule_notable_message_header=<html>A file in this data source was previously seen and tagged as Notable.<br>",
384  "CentralRepoIngestModel_name_header=Name:<br>",
385  "CentralRepoIngestModel_previous_case_header=<br>Previous Cases:<br>",
386  "# {0} - Name of file that is Notable",
387  "CentralRepoIngestModule_postToBB_knownBadMsg=Notable: {0}"
388  })
389 
399  private void sendBadFileInboxMessage(BlackboardArtifact artifact, String name, String md5Hash, List<String> caseDisplayNames) {
400  StringBuilder detailsSb = new StringBuilder(1024);
401 
402  detailsSb.append(Bundle.CentralRepoIngestModule_notable_message_header()).append(Bundle.CentralRepoIngestModel_name_header());
403  detailsSb.append(name).append(Bundle.CentralRepoIngestModel_previous_case_header());
404  for (String str : caseDisplayNames) {
405  detailsSb.append(str).append("<br>");
406  }
407  detailsSb.append("</html>");
408  services.postMessage(IngestMessage.createDataMessage(CentralRepoIngestModuleFactory.getModuleName(),
409  Bundle.CentralRepoIngestModule_postToBB_knownBadMsg(name),
410  detailsSb.toString(),
411  name + md5Hash,
412  artifact));
413  }
414 }

Copyright © 2012-2021 Basis Technology. Generated on: Thu Sep 30 2021
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.