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