Autopsy  4.7.0
Graphical digital forensics platform for The Sleuth Kit and other tools.
IngestModule.java
Go to the documentation of this file.
1 /*
2  * Central Repository
3  *
4  * Copyright 2011-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.ingestmodule;
20 
23 import java.util.List;
24 import java.util.logging.Level;
25 import java.util.stream.Collectors;
26 import org.openide.util.NbBundle.Messages;
45 import org.sleuthkit.datamodel.AbstractFile;
46 import org.sleuthkit.datamodel.BlackboardArtifact;
47 import org.sleuthkit.datamodel.BlackboardAttribute;
48 import org.sleuthkit.datamodel.HashUtility;
49 import org.sleuthkit.datamodel.TskCoreException;
50 import org.sleuthkit.datamodel.TskData;
54 
59 @Messages({"IngestModule.prevTaggedSet.text=Previously Tagged As Notable (Central Repository)",
60  "IngestModule.prevCaseComment.text=Previous Case: "})
61 final class IngestModule implements FileIngestModule {
62 
63  static final boolean DEFAULT_FLAG_TAGGED_NOTABLE_ITEMS = true;
64 
65  private final static Logger logger = Logger.getLogger(IngestModule.class.getName());
66  private final IngestServices services = IngestServices.getInstance();
67  private static final IngestModuleReferenceCounter refCounter = new IngestModuleReferenceCounter();
68  private static final IngestModuleReferenceCounter warningMsgRefCounter = new IngestModuleReferenceCounter();
69  private long jobId;
70  private CorrelationCase eamCase;
71  private CorrelationDataSource eamDataSource;
72  private Blackboard blackboard;
73  private CorrelationAttribute.Type filesType;
74 
75  private final boolean flagTaggedNotableItems;
76 
82  IngestModule(IngestSettings settings) {
83  flagTaggedNotableItems = settings.isFlagTaggedNotableItems();
84  }
85 
86  @Override
87  public ProcessResult process(AbstractFile abstractFile) {
88  if (EamDb.isEnabled() == false) {
89  /*
90  * Not signaling an error for now. This is a workaround for the way
91  * all newly didscovered ingest modules are automatically anabled.
92  *
93  * TODO (JIRA-2731): Add isEnabled API for ingest modules.
94  */
95  return ProcessResult.OK;
96  }
97 
98  try {
100  } catch (NoCurrentCaseException ex) {
101  logger.log(Level.SEVERE, "Exception while getting open case.", ex);
102  return ProcessResult.ERROR;
103  }
104 
105  if (!EamArtifactUtil.isSupportedAbstractFileType(abstractFile)) {
106  return ProcessResult.OK;
107  }
108 
109  if (abstractFile.getKnown() == TskData.FileKnown.KNOWN) {
110  return ProcessResult.OK;
111  }
112 
113  EamDb dbManager;
114  try {
115  dbManager = EamDb.getInstance();
116  } catch (EamDbException ex) {
117  logger.log(Level.SEVERE, "Error connecting to Central Repository database.", ex);
118  return ProcessResult.ERROR;
119  }
120 
121  // only continue if we are correlating filesType
122  if (!filesType.isEnabled()) {
123  return ProcessResult.OK;
124  }
125 
126  // get the hash because we're going to correlate it
127  String md5 = abstractFile.getMd5Hash();
128  if ((md5 == null) || (HashUtility.isNoDataMd5(md5))) {
129  return ProcessResult.OK;
130  }
131 
132  /*
133  * Search the central repo to see if this file was previously marked as
134  * being bad. Create artifact if it was.
135  */
136  if (abstractFile.getKnown() != TskData.FileKnown.KNOWN && flagTaggedNotableItems) {
137  try {
138  TimingMetric timingMetric = EnterpriseHealthMonitor.getTimingMetric("Correlation Engine: Notable artifact query");
139  List<String> caseDisplayNamesList = dbManager.getListCasesHavingArtifactInstancesKnownBad(filesType, md5);
141  if (!caseDisplayNamesList.isEmpty()) {
142  postCorrelatedBadFileToBlackboard(abstractFile, caseDisplayNamesList);
143  }
144  } catch (EamDbException ex) {
145  logger.log(Level.SEVERE, "Error searching database for artifact.", ex); // NON-NLS
146  return ProcessResult.ERROR;
147  }
148  }
149 
150  // insert this file into the central repository
151  try {
152  CorrelationAttribute eamArtifact = new CorrelationAttribute(filesType, md5);
154  eamCase,
155  eamDataSource,
156  abstractFile.getParentPath() + abstractFile.getName(),
157  null,
158  TskData.FileKnown.UNKNOWN // NOTE: Known status in the CR is based on tagging, not hashes like the Case Database.
159  );
160  eamArtifact.addInstance(cefi);
161  dbManager.prepareBulkArtifact(eamArtifact);
162  } catch (EamDbException ex) {
163  logger.log(Level.SEVERE, "Error adding artifact to bulk artifacts.", ex); // NON-NLS
164  return ProcessResult.ERROR;
165  }
166 
167  return ProcessResult.OK;
168  }
169 
170  @Override
171  public void shutDown() {
173 
174  if ((EamDb.isEnabled() == false) || (eamCase == null) || (eamDataSource == null)) {
175  return;
176  }
177  EamDb dbManager;
178  try {
179  dbManager = EamDb.getInstance();
180  } catch (EamDbException ex) {
181  logger.log(Level.SEVERE, "Error connecting to Central Repository database.", ex);
182  return;
183  }
184  try {
185  dbManager.bulkInsertArtifacts();
186  } catch (EamDbException ex) {
187  logger.log(Level.SEVERE, "Error doing bulk insert of artifacts.", ex); // NON-NLS
188  }
189  try {
190  Long count = dbManager.getCountArtifactInstancesByCaseDataSource(eamCase.getCaseUUID(), eamDataSource.getDeviceID());
191  logger.log(Level.INFO, "{0} artifacts in db for case: {1} ds:{2}", new Object[]{count, eamCase.getDisplayName(), eamDataSource.getName()}); // NON-NLS
192  } catch (EamDbException ex) {
193  logger.log(Level.SEVERE, "Error counting artifacts.", ex); // NON-NLS
194  }
195 
196  // TODO: once we implement shared cache, if refCounter is 1, then submit data in bulk.
197  refCounter.decrementAndGet(jobId);
198  }
199 
200  // see ArtifactManagerTimeTester for details
201  @Messages({
202  "IngestModule.notfyBubble.title=Central Repository Not Initialized",
203  "IngestModule.errorMessage.isNotEnabled=Central repository settings are not initialized, cannot run Correlation Engine ingest module."
204  })
205  @Override
206  public void startUp(IngestJobContext context) throws IngestModuleException {
208 
209  /*
210  * Tell the IngestEventsListener to flag notable items based on the
211  * current module's configuration. This is a work around for the lack of
212  * an artifacts pipeline. Note that this can be changed by another
213  * module instance. All modules are affected by the value. While not
214  * ideal, this will be good enough until a better solution can be
215  * posited.
216  *
217  * Note: Flagging cannot be disabled if any other instances of the
218  * Correlation Engine module are running. This restriction is to prevent
219  * missing results in the case where the first module is flagging
220  * notable items, and the proceeding module (with flagging disabled)
221  * causes the first to stop flagging.
222  */
224  IngestEventsListener.setFlagNotableItems(flagTaggedNotableItems);
225  }
226 
227  if (EamDb.isEnabled() == false) {
228  /*
229  * Not throwing the customary exception for now. This is a
230  * workaround for the way all newly didscovered ingest modules are
231  * automatically anabled.
232  *
233  * TODO (JIRA-2731): Add isEnabled API for ingest modules.
234  */
236  if (1L == warningMsgRefCounter.incrementAndGet(jobId)) {
237  MessageNotifyUtil.Notify.warn(Bundle.IngestModule_notfyBubble_title(), Bundle.IngestModule_errorMessage_isNotEnabled());
238  }
239  }
240  return;
241  }
242  Case autopsyCase;
243  try {
244  autopsyCase = Case.getCurrentCaseThrows();
245  } catch (NoCurrentCaseException ex) {
246  logger.log(Level.SEVERE, "Exception while getting open case.", ex);
247  throw new IngestModuleException("Exception while getting open case.", ex);
248  }
249 
250  // Don't allow sqlite central repo databases to be used for multi user cases
251  if ((autopsyCase.getCaseType() == Case.CaseType.MULTI_USER_CASE)
253  logger.log(Level.SEVERE, "Cannot run correlation engine on a multi-user case with a SQLite central repository.");
254  throw new IngestModuleException("Cannot run on a multi-user case with a SQLite central repository."); // NON-NLS
255  }
256  jobId = context.getJobId();
257 
258  EamDb centralRepoDb;
259  try {
260  centralRepoDb = EamDb.getInstance();
261  } catch (EamDbException ex) {
262  logger.log(Level.SEVERE, "Error connecting to central repository database.", ex); // NON-NLS
263  throw new IngestModuleException("Error connecting to central repository database.", ex); // NON-NLS
264  }
265 
266  try {
267  filesType = centralRepoDb.getCorrelationTypeById(CorrelationAttribute.FILES_TYPE_ID);
268  } catch (EamDbException ex) {
269  logger.log(Level.SEVERE, "Error getting correlation type FILES in ingest module start up.", ex); // NON-NLS
270  throw new IngestModuleException("Error getting correlation type FILES in ingest module start up.", ex); // NON-NLS
271  }
272 
273  try {
274  eamCase = centralRepoDb.getCase(autopsyCase);
275  } catch (EamDbException ex) {
276  throw new IngestModuleException("Unable to get case from central repository database ", ex);
277  }
278  if (eamCase == null) {
279  // ensure we have this case defined in the EAM DB
280  try {
281  eamCase = centralRepoDb.newCase(autopsyCase);
282  } catch (EamDbException ex) {
283  logger.log(Level.SEVERE, "Error creating new case in ingest module start up.", ex); // NON-NLS
284  throw new IngestModuleException("Error creating new case in ingest module start up.", ex); // NON-NLS
285  }
286  }
287 
288  try {
289  eamDataSource = CorrelationDataSource.fromTSKDataSource(eamCase, context.getDataSource());
290  } catch (EamDbException ex) {
291  logger.log(Level.SEVERE, "Error getting data source info.", ex); // NON-NLS
292  throw new IngestModuleException("Error getting data source info.", ex); // NON-NLS
293  }
294  // TODO: once we implement a shared cache, load/init it here w/ syncronized and define reference counter
295  // if we are the first thread / module for this job, then make sure the case
296  // and image exist in the DB before we associate artifacts with it.
297  if (refCounter.incrementAndGet(jobId)
298  == 1) {
299  // ensure we have this data source in the EAM DB
300  try {
301  if (null == centralRepoDb.getDataSource(eamCase, eamDataSource.getDeviceID())) {
302  centralRepoDb.newDataSource(eamDataSource);
303  }
304  } catch (EamDbException ex) {
305  logger.log(Level.SEVERE, "Error adding data source to Central Repository.", ex); // NON-NLS
306  throw new IngestModuleException("Error adding data source to Central Repository.", ex); // NON-NLS
307  }
308 
309  }
310  }
311 
312  private void postCorrelatedBadFileToBlackboard(AbstractFile abstractFile, List<String> caseDisplayNames) {
313 
314  try {
315  String MODULE_NAME = IngestModuleFactory.getModuleName();
316  BlackboardArtifact tifArtifact = abstractFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT);
317  BlackboardAttribute att = new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME, MODULE_NAME,
318  Bundle.IngestModule_prevTaggedSet_text());
319  BlackboardAttribute att2 = new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_COMMENT, MODULE_NAME,
320  Bundle.IngestModule_prevCaseComment_text() + caseDisplayNames.stream().distinct().collect(Collectors.joining(",", "", "")));
321  tifArtifact.addAttribute(att);
322  tifArtifact.addAttribute(att2);
323 
324  try {
325  // index the artifact for keyword search
326  blackboard.indexArtifact(tifArtifact);
327  } catch (Blackboard.BlackboardException ex) {
328  logger.log(Level.SEVERE, "Unable to index blackboard artifact " + tifArtifact.getArtifactID(), ex); //NON-NLS
329  }
330 
331  // send inbox message
332  sendBadFileInboxMessage(tifArtifact, abstractFile.getName(), abstractFile.getMd5Hash());
333 
334  // fire event to notify UI of this new artifact
335  services.fireModuleDataEvent(new ModuleDataEvent(MODULE_NAME, BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT));
336  } catch (TskCoreException ex) {
337  logger.log(Level.SEVERE, "Failed to create BlackboardArtifact.", ex); // NON-NLS
338  } catch (IllegalStateException ex) {
339  logger.log(Level.SEVERE, "Failed to create BlackboardAttribute.", ex); // NON-NLS
340  }
341  }
342 
351  @Messages({"IngestModule.postToBB.fileName=File Name",
352  "IngestModule.postToBB.md5Hash=MD5 Hash",
353  "IngestModule.postToBB.hashSetSource=Source of Hash",
354  "IngestModule.postToBB.eamHit=Central Repository",
355  "# {0} - Name of file that is Notable",
356  "IngestModule.postToBB.knownBadMsg=Notable: {0}"})
357  public void sendBadFileInboxMessage(BlackboardArtifact artifact, String name, String md5Hash) {
358  StringBuilder detailsSb = new StringBuilder();
359  //details
360  detailsSb.append("<table border='0' cellpadding='4' width='280'>"); //NON-NLS
361  //hit
362  detailsSb.append("<tr>"); //NON-NLS
363  detailsSb.append("<th>") //NON-NLS
364  .append(Bundle.IngestModule_postToBB_fileName())
365  .append("</th>"); //NON-NLS
366  detailsSb.append("<td>") //NON-NLS
367  .append(name)
368  .append("</td>"); //NON-NLS
369  detailsSb.append("</tr>"); //NON-NLS
370 
371  detailsSb.append("<tr>"); //NON-NLS
372  detailsSb.append("<th>") //NON-NLS
373  .append(Bundle.IngestModule_postToBB_md5Hash())
374  .append("</th>"); //NON-NLS
375  detailsSb.append("<td>").append(md5Hash).append("</td>"); //NON-NLS
376  detailsSb.append("</tr>"); //NON-NLS
377 
378  detailsSb.append("<tr>"); //NON-NLS
379  detailsSb.append("<th>") //NON-NLS
380  .append(Bundle.IngestModule_postToBB_hashSetSource())
381  .append("</th>"); //NON-NLS
382  detailsSb.append("<td>").append(Bundle.IngestModule_postToBB_eamHit()).append("</td>"); //NON-NLS
383  detailsSb.append("</tr>"); //NON-NLS
384 
385  detailsSb.append("</table>"); //NON-NLS
386 
388  Bundle.IngestModule_postToBB_knownBadMsg(name),
389  detailsSb.toString(),
390  name + md5Hash,
391  artifact));
392  }
393 }
CorrelationDataSource getDataSource(CorrelationCase correlationCase, String dataSourceDeviceId)
CorrelationAttribute.Type getCorrelationTypeById(int typeId)
static IngestMessage createDataMessage(String source, String subject, String detailsHtml, String uniqueKey, BlackboardArtifact data)
void prepareBulkArtifact(CorrelationAttribute eamArtifact)
CorrelationCase newCase(CorrelationCase eamCase)
static CorrelationDataSource fromTSKDataSource(CorrelationCase correlationCase, Content dataSource)
List< String > getListCasesHavingArtifactInstancesKnownBad(CorrelationAttribute.Type aType, String value)
void addInstance(CorrelationAttributeInstance artifactInstance)
void postMessage(final IngestMessage message)
void fireModuleDataEvent(ModuleDataEvent moduleDataEvent)
Long getCountArtifactInstancesByCaseDataSource(String caseUUID, String dataSourceID)
synchronized void indexArtifact(BlackboardArtifact artifact)
Definition: Blackboard.java:59
synchronized static Logger getLogger(String name)
Definition: Logger.java:124
static void warn(String title, String message)
void newDataSource(CorrelationDataSource eamDataSource)
static synchronized IngestServices getInstance()

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