Autopsy  4.4.1
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-2017 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.Exceptions;
27 import org.openide.util.NbBundle.Messages;
44 import org.sleuthkit.datamodel.AbstractFile;
45 import org.sleuthkit.datamodel.BlackboardArtifact;
46 import org.sleuthkit.datamodel.BlackboardAttribute;
47 import org.sleuthkit.datamodel.HashUtility;
48 import org.sleuthkit.datamodel.TskCoreException;
49 import org.sleuthkit.datamodel.TskData;
52 import org.sleuthkit.datamodel.TskDataException;
53 
58 @Messages({"IngestModule.prevTaggedSet.text=Previously Tagged As Notable (Central Repository)",
59  "IngestModule.prevCaseComment.text=Previous Case: "})
60 class IngestModule implements FileIngestModule {
61 
62  private final static Logger LOGGER = Logger.getLogger(IngestModule.class.getName());
63  private final IngestServices services = IngestServices.getInstance();
64  private static final IngestModuleReferenceCounter refCounter = new IngestModuleReferenceCounter();
65  private static final IngestModuleReferenceCounter warningMsgRefCounter = new IngestModuleReferenceCounter();
66  private long jobId;
67  private CorrelationCase eamCase;
68  private CorrelationDataSource eamDataSource;
69  private Blackboard blackboard;
70  private CorrelationAttribute.Type filesType;
71 
72  @Override
73  public ProcessResult process(AbstractFile af) {
74  if (EamDb.isEnabled() == false) {
75  /*
76  * Not signaling an error for now. This is a workaround for the way
77  * all newly didscovered ingest modules are automatically anabled.
78  *
79  * TODO (JIRA-2731): Add isEnabled API for ingest modules.
80  */
81  return ProcessResult.OK;
82  }
83 
84  blackboard = Case.getCurrentCase().getServices().getBlackboard();
85 
86  if ((af.getType() == TskData.TSK_DB_FILES_TYPE_ENUM.UNALLOC_BLOCKS)
87  || (af.getType() == TskData.TSK_DB_FILES_TYPE_ENUM.UNUSED_BLOCKS)
88  || (af.getType() == TskData.TSK_DB_FILES_TYPE_ENUM.SLACK)
89  || (af.getKnown() == TskData.FileKnown.KNOWN)
90  || (af.isDir() == true)
91  || (!af.isMetaFlagSet(TskData.TSK_FS_META_FLAG_ENUM.ALLOC))) {
92  return ProcessResult.OK;
93  }
94 
95  EamDb dbManager;
96  try {
97  dbManager = EamDb.getInstance();
98  } catch (EamDbException ex) {
99  LOGGER.log(Level.SEVERE, "Error connecting to Central Repository database.", ex);
100  return ProcessResult.ERROR;
101  }
102 
103  // only continue if we are correlating filesType
104  if (!filesType.isEnabled()) {
105  return ProcessResult.OK;
106  }
107 
108  // get the hash because we're going to correlate it
109  String md5 = af.getMd5Hash();
110  if ((md5 == null) || (HashUtility.isNoDataMd5(md5))) {
111  return ProcessResult.OK;
112  }
113 
114  // If unknown to both the hash module and as a globally known artifact in the EAM DB, correlate to other cases
115  if (af.getKnown() == TskData.FileKnown.UNKNOWN) {
116  // query db for artifact instances having this MD5 and knownStatus = "Bad".
117  try {
118  // if af.getKnown() is "UNKNOWN" and this artifact instance was marked bad in a previous case,
119  // create TSK_INTERESTING_FILE artifact on BB.
120  List<String> caseDisplayNames = dbManager.getListCasesHavingArtifactInstancesKnownBad(filesType, md5);
121  if (!caseDisplayNames.isEmpty()) {
122  postCorrelatedBadFileToBlackboard(af, caseDisplayNames);
123  }
124  } catch (EamDbException ex) {
125  LOGGER.log(Level.SEVERE, "Error counting notable artifacts.", ex); // NON-NLS
126  return ProcessResult.ERROR;
127  }
128  }
129 
130  // Make a TSK_HASHSET_HIT blackboard artifact for global notable files
131  try {
132  if (dbManager.isArtifactlKnownBadByReference(filesType, md5)) {
133  postCorrelatedHashHitToBlackboard(af);
134  }
135  } catch (EamDbException ex) {
136  LOGGER.log(Level.SEVERE, "Error retrieving global known status.", ex); // NON-NLS
137  return ProcessResult.ERROR;
138  }
139 
140  try {
141  CorrelationAttribute eamArtifact = new CorrelationAttribute(filesType, md5);
143  eamCase,
144  eamDataSource,
145  af.getParentPath() + af.getName(),
146  null,
147  TskData.FileKnown.UNKNOWN,
149  );
150  eamArtifact.addInstance(cefi);
151  dbManager.prepareBulkArtifact(eamArtifact);
152  } catch (EamDbException ex) {
153  LOGGER.log(Level.SEVERE, "Error adding artifact to bulk artifacts.", ex); // NON-NLS
154  return ProcessResult.ERROR;
155  }
156 
157  return ProcessResult.OK;
158  }
159 
160  @Override
161  public void shutDown() {
163  if ((EamDb.isEnabled() == false) || (eamCase == null) || (eamDataSource == null)) {
164  return;
165  }
166  EamDb dbManager;
167  try {
168  dbManager = EamDb.getInstance();
169  } catch (EamDbException ex) {
170  LOGGER.log(Level.SEVERE, "Error connecting to Central Repository database.", ex);
171  return;
172  }
173  try {
174  dbManager.bulkInsertArtifacts();
175  } catch (EamDbException ex) {
176  LOGGER.log(Level.SEVERE, "Error doing bulk insert of artifacts.", ex); // NON-NLS
177  }
178  try {
179  Long count = dbManager.getCountArtifactInstancesByCaseDataSource(eamCase.getCaseUUID(), eamDataSource.getDeviceID());
180  LOGGER.log(Level.INFO, "{0} artifacts in db for case: {1} ds:{2}", new Object[]{count, eamCase.getDisplayName(), eamDataSource.getName()}); // NON-NLS
181  } catch (EamDbException ex) {
182  LOGGER.log(Level.SEVERE, "Error counting artifacts.", ex); // NON-NLS
183  }
184 
185  // TODO: once we implement shared cache, if refCounter is 1, then submit data in bulk.
186  refCounter.decrementAndGet(jobId);
187  }
188 
189  // see ArtifactManagerTimeTester for details
190  @Messages({
191  "IngestModule.notfyBubble.title=Central Repository Not Initialized",
192  "IngestModule.errorMessage.isNotEnabled=Central repository settings are not initialized, cannot run Correlation Engine ingest module."
193  })
194  @Override
195  public void startUp(IngestJobContext context) throws IngestModuleException {
197  if (EamDb.isEnabled() == false) {
198  /*
199  * Not throwing the customary exception for now. This is a
200  * workaround for the way all newly didscovered ingest modules are
201  * automatically anabled.
202  *
203  * TODO (JIRA-2731): Add isEnabled API for ingest modules.
204  */
206  if (1L == warningMsgRefCounter.incrementAndGet(jobId)) {
207  MessageNotifyUtil.Notify.warn(Bundle.IngestModule_notfyBubble_title(), Bundle.IngestModule_errorMessage_isNotEnabled());
208  }
209  }
210  return;
211  }
212  // Don't allow sqlite central repo databases to be used for multi user cases
215  LOGGER.log(Level.SEVERE, "Cannot run correlation engine on a multi-user case with a SQLite central repository.");
216  throw new IngestModuleException("Cannot run on a multi-user case with a SQLite central repository."); // NON-NLS
217  }
218  jobId = context.getJobId();
220 
221  try {
222  eamDataSource = CorrelationDataSource.fromTSKDataSource(context.getDataSource());
223  } catch (EamDbException ex) {
224  LOGGER.log(Level.SEVERE, "Error getting data source info.", ex); // NON-NLS
225  throw new IngestModuleException("Error getting data source info.", ex); // NON-NLS
226  }
227 
228  EamDb dbManager;
229  try {
230  dbManager = EamDb.getInstance();
231  } catch (EamDbException ex) {
232  LOGGER.log(Level.SEVERE, "Error connecting to central repository database.", ex); // NON-NLS
233  throw new IngestModuleException("Error connecting to central repository database.", ex); // NON-NLS
234  }
235 
236  try {
238  } catch (EamDbException ex) {
239  LOGGER.log(Level.SEVERE, "Error getting correlation type FILES in ingest module start up.", ex); // NON-NLS
240  throw new IngestModuleException("Error getting correlation type FILES in ingest module start up.", ex); // NON-NLS
241  }
242 
243  // TODO: once we implement a shared cache, load/init it here w/ syncronized and define reference counter
244  // if we are the first thread / module for this job, then make sure the case
245  // and image exist in the DB before we associate artifacts with it.
246  if (refCounter.incrementAndGet(jobId)
247  == 1) {
248  // ensure we have this data source in the EAM DB
249  try {
250  if (null == dbManager.getDataSourceDetails(eamDataSource.getDeviceID())) {
251  dbManager.newDataSource(eamDataSource);
252  }
253  } catch (EamDbException ex) {
254  LOGGER.log(Level.SEVERE, "Error creating new data source in ingest module start up.", ex); // NON-NLS
255  throw new IngestModuleException("Error creating new data source in ingest module start up.", ex); // NON-NLS
256  }
257 
258  // ensure we have this case defined in the EAM DB
259  CorrelationCase existingCase;
260  Case curCase = Case.getCurrentCase();
261  CorrelationCase curCeCase = new CorrelationCase(
262  -1,
263  curCase.getName(), // unique case ID
265  curCase.getDisplayName(),
266  curCase.getCreatedDate(),
267  curCase.getNumber(),
268  curCase.getExaminer(),
269  null,
270  null,
271  null);
272  try {
273  existingCase = dbManager.getCaseByUUID(curCeCase.getCaseUUID());
274  if (existingCase == null) {
275  dbManager.newCase(curCeCase);
276  }
277 
278  } catch (EamDbException ex) {
279  LOGGER.log(Level.SEVERE, "Error creating new case in ingest module start up.", ex); // NON-NLS
280  throw new IngestModuleException("Error creating new case in ingest module start up.", ex); // NON-NLS
281  }
282  }
283  }
284 
285  private void postCorrelatedBadFileToBlackboard(AbstractFile abstractFile, List<String> caseDisplayNames) {
286 
287  try {
288  String MODULE_NAME = IngestModuleFactory.getModuleName();
289  BlackboardArtifact tifArtifact = abstractFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT);
290  BlackboardAttribute att = new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME, MODULE_NAME,
291  Bundle.IngestModule_prevTaggedSet_text());
292  BlackboardAttribute att2 = new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_COMMENT, MODULE_NAME,
293  Bundle.IngestModule_prevCaseComment_text() + caseDisplayNames.stream().distinct().collect(Collectors.joining(",", "", "")));
294  tifArtifact.addAttribute(att);
295  tifArtifact.addAttribute(att2);
296 
297  try {
298  // index the artifact for keyword search
299  blackboard.indexArtifact(tifArtifact);
300  } catch (Blackboard.BlackboardException ex) {
301  LOGGER.log(Level.SEVERE, "Unable to index blackboard artifact " + tifArtifact.getArtifactID(), ex); //NON-NLS
302  }
303 
304  // send inbox message
305  sendBadFileInboxMessage(tifArtifact, abstractFile.getName(), abstractFile.getMd5Hash());
306 
307  // fire event to notify UI of this new artifact
308  services.fireModuleDataEvent(new ModuleDataEvent(MODULE_NAME, BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT));
309  } catch (TskCoreException ex) {
310  LOGGER.log(Level.SEVERE, "Failed to create BlackboardArtifact.", ex); // NON-NLS
311  } catch (IllegalStateException ex) {
312  LOGGER.log(Level.SEVERE, "Failed to create BlackboardAttribute.", ex); // NON-NLS
313  }
314  }
315 
316  private void postCorrelatedHashHitToBlackboard(AbstractFile abstractFile) {
317  try {
318  String MODULE_NAME = IngestModuleFactory.getModuleName();
319  BlackboardArtifact tifArtifact = abstractFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_HASHSET_HIT);
320  BlackboardAttribute att = new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME, MODULE_NAME,
321  Bundle.IngestModule_prevCaseComment_text());
322  tifArtifact.addAttribute(att);
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 }
CorrelationAttribute.Type getCorrelationTypeById(int typeId)
CorrelationCase getCaseByUUID(String caseUUID)
static IngestMessage createDataMessage(String source, String subject, String detailsHtml, String uniqueKey, BlackboardArtifact data)
void prepareBulkArtifact(CorrelationAttribute eamArtifact)
List< String > getListCasesHavingArtifactInstancesKnownBad(CorrelationAttribute.Type aType, String value)
void addInstance(CorrelationAttributeInstance artifactInstance)
CorrelationDataSource getDataSourceDetails(String dataSourceDeviceId)
void postMessage(final IngestMessage message)
void fireModuleDataEvent(ModuleDataEvent moduleDataEvent)
Long getCountArtifactInstancesByCaseDataSource(String caseUUID, String dataSourceID)
boolean isArtifactlKnownBadByReference(CorrelationAttribute.Type aType, String value)
synchronized void indexArtifact(BlackboardArtifact artifact)
Definition: Blackboard.java:59
synchronized static Logger getLogger(String name)
Definition: Logger.java:161
static void warn(String title, String message)
void newDataSource(CorrelationDataSource eamDataSource)
static synchronized IngestServices getInstance()

Copyright © 2012-2016 Basis Technology. Generated on: Fri Sep 29 2017
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.