Autopsy  4.8.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 CorrelationAttributeInstance.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 = HealthMonitor.getTimingMetric("Correlation Engine: Notable artifact query");
139  List<String> caseDisplayNamesList = dbManager.getListCasesHavingArtifactInstancesKnownBad(filesType, md5);
140  HealthMonitor.submitTimingMetric(timingMetric);
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;
148  logger.log(Level.INFO, "Error searching database for artifact.", ex); // NON-NLS
149  return ProcessResult.ERROR;
150  }
151  }
152 
153  // insert this file into the central repository
154  try {
156  md5,
157  filesType,
158  eamCase,
159  eamDataSource,
160  abstractFile.getParentPath() + abstractFile.getName(),
161  null,
162  TskData.FileKnown.UNKNOWN // NOTE: Known status in the CR is based on tagging, not hashes like the Case Database.
163  );
164  dbManager.addAttributeInstanceBulk(cefi);
165  } catch (EamDbException ex) {
166  logger.log(Level.SEVERE, "Error adding artifact to bulk artifacts.", ex); // NON-NLS
167  return ProcessResult.ERROR;
169  logger.log(Level.INFO, "Error adding artifact to bulk artifacts.", ex); // NON-NLS
170  return ProcessResult.ERROR;
171  }
172 
173  return ProcessResult.OK;
174  }
175 
176  @Override
177  public void shutDown() {
179 
180  if ((EamDb.isEnabled() == false) || (eamCase == null) || (eamDataSource == null)) {
181  return;
182  }
183  EamDb dbManager;
184  try {
185  dbManager = EamDb.getInstance();
186  } catch (EamDbException ex) {
187  logger.log(Level.SEVERE, "Error connecting to Central Repository database.", ex);
188  return;
189  }
190  try {
191  dbManager.commitAttributeInstancesBulk();
192  } catch (EamDbException ex) {
193  logger.log(Level.SEVERE, "Error doing bulk insert of artifacts.", ex); // NON-NLS
194  }
195  try {
196  Long count = dbManager.getCountArtifactInstancesByCaseDataSource(eamCase.getCaseUUID(), eamDataSource.getDeviceID());
197  logger.log(Level.INFO, "{0} artifacts in db for case: {1} ds:{2}", new Object[]{count, eamCase.getDisplayName(), eamDataSource.getName()}); // NON-NLS
198  } catch (EamDbException ex) {
199  logger.log(Level.SEVERE, "Error counting artifacts.", ex); // NON-NLS
200  }
201 
202  // TODO: once we implement shared cache, if refCounter is 1, then submit data in bulk.
203  refCounter.decrementAndGet(jobId);
204  }
205 
206  // see ArtifactManagerTimeTester for details
207  @Messages({
208  "IngestModule.notfyBubble.title=Central Repository Not Initialized",
209  "IngestModule.errorMessage.isNotEnabled=Central repository settings are not initialized, cannot run Correlation Engine ingest module."
210  })
211  @Override
212  public void startUp(IngestJobContext context) throws IngestModuleException {
214 
215  /*
216  * Tell the IngestEventsListener to flag notable items based on the
217  * current module's configuration. This is a work around for the lack of
218  * an artifacts pipeline. Note that this can be changed by another
219  * module instance. All modules are affected by the value. While not
220  * ideal, this will be good enough until a better solution can be
221  * posited.
222  *
223  * Note: Flagging cannot be disabled if any other instances of the
224  * Correlation Engine module are running. This restriction is to prevent
225  * missing results in the case where the first module is flagging
226  * notable items, and the proceeding module (with flagging disabled)
227  * causes the first to stop flagging.
228  */
230  IngestEventsListener.setFlagNotableItems(flagTaggedNotableItems);
231  }
232 
233  if (EamDb.isEnabled() == false) {
234  /*
235  * Not throwing the customary exception for now. This is a
236  * workaround for the way all newly didscovered ingest modules are
237  * automatically anabled.
238  *
239  * TODO (JIRA-2731): Add isEnabled API for ingest modules.
240  */
242  if (1L == warningMsgRefCounter.incrementAndGet(jobId)) {
243  MessageNotifyUtil.Notify.warn(Bundle.IngestModule_notfyBubble_title(), Bundle.IngestModule_errorMessage_isNotEnabled());
244  }
245  }
246  return;
247  }
248  Case autopsyCase;
249  try {
250  autopsyCase = Case.getCurrentCaseThrows();
251  } catch (NoCurrentCaseException ex) {
252  logger.log(Level.SEVERE, "Exception while getting open case.", ex);
253  throw new IngestModuleException("Exception while getting open case.", ex);
254  }
255 
256  // Don't allow sqlite central repo databases to be used for multi user cases
257  if ((autopsyCase.getCaseType() == Case.CaseType.MULTI_USER_CASE)
259  logger.log(Level.SEVERE, "Cannot run correlation engine on a multi-user case with a SQLite central repository.");
260  throw new IngestModuleException("Cannot run on a multi-user case with a SQLite central repository."); // NON-NLS
261  }
262  jobId = context.getJobId();
263 
264  EamDb centralRepoDb;
265  try {
266  centralRepoDb = EamDb.getInstance();
267  } catch (EamDbException ex) {
268  logger.log(Level.SEVERE, "Error connecting to central repository database.", ex); // NON-NLS
269  throw new IngestModuleException("Error connecting to central repository database.", ex); // NON-NLS
270  }
271 
272  try {
274  } catch (EamDbException ex) {
275  logger.log(Level.SEVERE, "Error getting correlation type FILES in ingest module start up.", ex); // NON-NLS
276  throw new IngestModuleException("Error getting correlation type FILES in ingest module start up.", ex); // NON-NLS
277  }
278 
279  try {
280  eamCase = centralRepoDb.getCase(autopsyCase);
281  } catch (EamDbException ex) {
282  throw new IngestModuleException("Unable to get case from central repository database ", ex);
283  }
284  if (eamCase == null) {
285  // ensure we have this case defined in the EAM DB
286  try {
287  eamCase = centralRepoDb.newCase(autopsyCase);
288  } catch (EamDbException ex) {
289  logger.log(Level.SEVERE, "Error creating new case in ingest module start up.", ex); // NON-NLS
290  throw new IngestModuleException("Error creating new case in ingest module start up.", ex); // NON-NLS
291  }
292  }
293 
294  try {
295  eamDataSource = CorrelationDataSource.fromTSKDataSource(eamCase, context.getDataSource());
296  } catch (EamDbException ex) {
297  logger.log(Level.SEVERE, "Error getting data source info.", ex); // NON-NLS
298  throw new IngestModuleException("Error getting data source info.", ex); // NON-NLS
299  }
300  // TODO: once we implement a shared cache, load/init it here w/ syncronized and define reference counter
301  // if we are the first thread / module for this job, then make sure the case
302  // and image exist in the DB before we associate artifacts with it.
303  if (refCounter.incrementAndGet(jobId)
304  == 1) {
305  // ensure we have this data source in the EAM DB
306  try {
307  if (null == centralRepoDb.getDataSource(eamCase, eamDataSource.getDeviceID())) {
308  centralRepoDb.newDataSource(eamDataSource);
309  }
310  } catch (EamDbException ex) {
311  logger.log(Level.SEVERE, "Error adding data source to Central Repository.", ex); // NON-NLS
312  throw new IngestModuleException("Error adding data source to Central Repository.", ex); // NON-NLS
313  }
314 
315  }
316  }
317 
318  private void postCorrelatedBadFileToBlackboard(AbstractFile abstractFile, List<String> caseDisplayNames) {
319 
320  try {
321  String MODULE_NAME = IngestModuleFactory.getModuleName();
322  BlackboardArtifact tifArtifact = abstractFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT);
323  BlackboardAttribute att = new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME, MODULE_NAME,
324  Bundle.IngestModule_prevTaggedSet_text());
325  BlackboardAttribute att2 = new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_COMMENT, MODULE_NAME,
326  Bundle.IngestModule_prevCaseComment_text() + caseDisplayNames.stream().distinct().collect(Collectors.joining(",", "", "")));
327  tifArtifact.addAttribute(att);
328  tifArtifact.addAttribute(att2);
329 
330  try {
331  // index the artifact for keyword search
332  blackboard.indexArtifact(tifArtifact);
333  } catch (Blackboard.BlackboardException ex) {
334  logger.log(Level.SEVERE, "Unable to index blackboard artifact " + tifArtifact.getArtifactID(), ex); //NON-NLS
335  }
336 
337  // send inbox message
338  sendBadFileInboxMessage(tifArtifact, abstractFile.getName(), abstractFile.getMd5Hash());
339 
340  // fire event to notify UI of this new artifact
341  services.fireModuleDataEvent(new ModuleDataEvent(MODULE_NAME, BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT));
342  } catch (TskCoreException ex) {
343  logger.log(Level.SEVERE, "Failed to create BlackboardArtifact.", ex); // NON-NLS
344  } catch (IllegalStateException ex) {
345  logger.log(Level.SEVERE, "Failed to create BlackboardAttribute.", ex); // NON-NLS
346  }
347  }
348 
357  @Messages({"IngestModule.postToBB.fileName=File Name",
358  "IngestModule.postToBB.md5Hash=MD5 Hash",
359  "IngestModule.postToBB.hashSetSource=Source of Hash",
360  "IngestModule.postToBB.eamHit=Central Repository",
361  "# {0} - Name of file that is Notable",
362  "IngestModule.postToBB.knownBadMsg=Notable: {0}"})
363  public void sendBadFileInboxMessage(BlackboardArtifact artifact, String name, String md5Hash) {
364  StringBuilder detailsSb = new StringBuilder();
365  //details
366  detailsSb.append("<table border='0' cellpadding='4' width='280'>"); //NON-NLS
367  //hit
368  detailsSb.append("<tr>"); //NON-NLS
369  detailsSb.append("<th>") //NON-NLS
370  .append(Bundle.IngestModule_postToBB_fileName())
371  .append("</th>"); //NON-NLS
372  detailsSb.append("<td>") //NON-NLS
373  .append(name)
374  .append("</td>"); //NON-NLS
375  detailsSb.append("</tr>"); //NON-NLS
376 
377  detailsSb.append("<tr>"); //NON-NLS
378  detailsSb.append("<th>") //NON-NLS
379  .append(Bundle.IngestModule_postToBB_md5Hash())
380  .append("</th>"); //NON-NLS
381  detailsSb.append("<td>").append(md5Hash).append("</td>"); //NON-NLS
382  detailsSb.append("</tr>"); //NON-NLS
383 
384  detailsSb.append("<tr>"); //NON-NLS
385  detailsSb.append("<th>") //NON-NLS
386  .append(Bundle.IngestModule_postToBB_hashSetSource())
387  .append("</th>"); //NON-NLS
388  detailsSb.append("<td>").append(Bundle.IngestModule_postToBB_eamHit()).append("</td>"); //NON-NLS
389  detailsSb.append("</tr>"); //NON-NLS
390 
391  detailsSb.append("</table>"); //NON-NLS
392 
394  Bundle.IngestModule_postToBB_knownBadMsg(name),
395  detailsSb.toString(),
396  name + md5Hash,
397  artifact));
398  }
399 }
CorrelationDataSource getDataSource(CorrelationCase correlationCase, String dataSourceDeviceId)
static IngestMessage createDataMessage(String source, String subject, String detailsHtml, String uniqueKey, BlackboardArtifact data)
List< String > getListCasesHavingArtifactInstancesKnownBad(CorrelationAttributeInstance.Type aType, String value)
CorrelationCase newCase(CorrelationCase eamCase)
static CorrelationDataSource fromTSKDataSource(CorrelationCase correlationCase, Content dataSource)
static TimingMetric getTimingMetric(String name)
void addAttributeInstanceBulk(CorrelationAttributeInstance eamArtifact)
CorrelationAttributeInstance.Type getCorrelationTypeById(int typeId)
void postMessage(final IngestMessage message)
void fireModuleDataEvent(ModuleDataEvent moduleDataEvent)
Long getCountArtifactInstancesByCaseDataSource(String caseUUID, String dataSourceID)
static void submitTimingMetric(TimingMetric metric)
synchronized void indexArtifact(BlackboardArtifact artifact)
Definition: Blackboard.java:58
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-2018 Basis Technology. Generated on: Thu Oct 4 2018
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.