Autopsy  4.9.1
Graphical digital forensics platform for The Sleuth Kit and other tools.
ReportCaseUco.java
Go to the documentation of this file.
1  /*
2  *
3  * Autopsy Forensic Browser
4  *
5  * Copyright 2012-2018 Basis Technology Corp.
6  * Project Contact/Architect: carrier <at> sleuthkit <dot> org
7  *
8  * Licensed under the Apache License, Version 2.0 (the "License");
9  * you may not use this file except in compliance with the License.
10  * You may obtain a copy of the License at
11  *
12  * http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing, software
15  * distributed under the License is distributed on an "AS IS" BASIS,
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  * See the License for the specific language governing permissions and
18  * limitations under the License.
19  */
20 package org.sleuthkit.autopsy.modules.case_uco;
21 
22 import java.io.IOException;
23 import java.nio.file.Files;
24 import java.nio.file.Paths;
25 import java.util.logging.Level;
26 import javax.swing.JPanel;
27 import com.fasterxml.jackson.core.JsonEncoding;
28 import com.fasterxml.jackson.core.JsonFactory;
29 import com.fasterxml.jackson.core.JsonGenerator;
30 import com.fasterxml.jackson.core.util.DefaultIndenter;
31 import com.fasterxml.jackson.core.util.DefaultPrettyPrinter;
32 import java.sql.ResultSet;
33 import java.sql.SQLException;
34 import java.util.SimpleTimeZone;
35 import org.openide.util.NbBundle;
45 import org.sleuthkit.datamodel.*;
46 
51 class ReportCaseUco implements GeneralReportModule {
52 
53  private static final Logger logger = Logger.getLogger(ReportCaseUco.class.getName());
54  private static ReportCaseUco instance = null;
55  private ReportCaseUcoConfigPanel configPanel;
56 
57  private static final String REPORT_FILE_NAME = "CASE_UCO_output.json-ld";
58 
59  // Hidden constructor for the report
60  private ReportCaseUco() {
61  }
62 
63  // Get the default implementation of this report
64  public static synchronized ReportCaseUco getDefault() {
65  if (instance == null) {
66  instance = new ReportCaseUco();
67  }
68  return instance;
69  }
70 
77  @NbBundle.Messages({
78  "ReportCaseUco.notInitialized=CASE-UCO settings panel has not been initialized",
79  "ReportCaseUco.noDataSourceSelected=No data source selected for CASE-UCO report",
80  "ReportCaseUco.noCaseOpen=Unable to open currect case",
81  "ReportCaseUco.unableToCreateDirectories=Unable to create directory for CASE-UCO report",
82  "ReportCaseUco.initializing=Creating directories...",
83  "ReportCaseUco.querying=Querying files...",
84  "ReportCaseUco.ingestWarning=Warning, this report will be created before ingest services completed",
85  "ReportCaseUco.processing=Saving files in CASE-UCO format...",
86  "ReportCaseUco.srcModuleName.text=CASE-UCO Report"
87  })
88  @Override
89  @SuppressWarnings("deprecation")
90  public void generateReport(String baseReportDir, ReportProgressPanel progressPanel) {
91 
92  if (configPanel == null) {
93  logger.log(Level.SEVERE, "CASE-UCO settings panel has not been initialized"); //NON-NLS
94  MessageNotifyUtil.Message.error(Bundle.ReportCaseUco_notInitialized());
95  progressPanel.complete(ReportStatus.ERROR);
96  return;
97  }
98 
99  Long selectedDataSourceId = configPanel.getSelectedDataSourceId();
100  if (selectedDataSourceId == ReportCaseUcoConfigPanel.NO_DATA_SOURCE_SELECTED) {
101  logger.log(Level.SEVERE, "No data source selected for CASE-UCO report"); //NON-NLS
102  MessageNotifyUtil.Message.error(Bundle.ReportCaseUco_noDataSourceSelected());
103  progressPanel.complete(ReportStatus.ERROR);
104  return;
105  }
106 
107  // Start the progress bar and setup the report
108  progressPanel.setIndeterminate(false);
109  progressPanel.start();
110  progressPanel.updateStatusLabel(Bundle.ReportCaseUco_initializing());
111 
112  // Create the JSON generator
113  JsonFactory jsonGeneratorFactory = new JsonFactory();
114  String reportPath = baseReportDir + getRelativeFilePath();
115  java.io.File reportFile = Paths.get(reportPath).toFile();
116  try {
117  Files.createDirectories(Paths.get(reportFile.getParent()));
118  } catch (IOException ex) {
119  logger.log(Level.SEVERE, "Unable to create directory for CASE-UCO report", ex); //NON-NLS
120  MessageNotifyUtil.Message.error(Bundle.ReportCaseUco_unableToCreateDirectories());
121  progressPanel.complete(ReportStatus.ERROR);
122  return;
123  }
124 
125  // Check if ingest has finished
127  MessageNotifyUtil.Message.warn(Bundle.ReportCaseUco_ingestWarning());
128  }
129 
130 
131  JsonGenerator jsonGenerator = null;
132  SimpleTimeZone timeZone = new SimpleTimeZone(0, "GMT");
133  try {
134  jsonGenerator = jsonGeneratorFactory.createGenerator(reportFile, JsonEncoding.UTF8);
135  // instert \n after each field for more readable formatting
136  jsonGenerator.setPrettyPrinter(new DefaultPrettyPrinter().withObjectIndenter(new DefaultIndenter(" ", "\n")));
137 
138  SleuthkitCase skCase = Case.getCurrentCaseThrows().getSleuthkitCase();
139 
140  progressPanel.updateStatusLabel(Bundle.ReportCaseUco_querying());
141 
142  // create the required CASE-UCO entries at the beginning of the output file
143  initializeJsonOutputFile(jsonGenerator);
144 
145  // create CASE-UCO entry for the Autopsy case
146  String caseTraceId = saveCaseInfo(skCase, jsonGenerator);
147 
148  // create CASE-UCO data source entry
149  String dataSourceTraceId = saveDataSourceInfo(selectedDataSourceId, caseTraceId, skCase, jsonGenerator);
150 
151  // Run getAllFilesQuery to get all files, exclude directories
152  final String getAllFilesQuery = "select obj_id, name, size, crtime, atime, mtime, md5, parent_path, mime_type, extension from tsk_files where "
153  + "data_source_obj_id = " + Long.toString(selectedDataSourceId)
154  + " AND ((meta_type = " + TskData.TSK_FS_META_TYPE_ENUM.TSK_FS_META_TYPE_UNDEF.getValue()
155  + ") OR (meta_type = " + TskData.TSK_FS_META_TYPE_ENUM.TSK_FS_META_TYPE_REG.getValue()
156  + ") OR (meta_type = " + TskData.TSK_FS_META_TYPE_ENUM.TSK_FS_META_TYPE_VIRT.getValue() + "))"; //NON-NLS
157 
158  try (SleuthkitCase.CaseDbQuery queryResult = skCase.executeQuery(getAllFilesQuery)) {
159  ResultSet resultSet = queryResult.getResultSet();
160 
161  progressPanel.updateStatusLabel(Bundle.ReportCaseUco_processing());
162 
163  // Loop files and write info to CASE-UCO report
164  while (resultSet.next()) {
165 
166  if (progressPanel.getStatus() == ReportStatus.CANCELED) {
167  break;
168  }
169 
170  Long objectId = resultSet.getLong(1);
171  String fileName = resultSet.getString(2);
172  long size = resultSet.getLong("size");
173  String crtime = ContentUtils.getStringTimeISO8601(resultSet.getLong("crtime"), timeZone);
174  String atime = ContentUtils.getStringTimeISO8601(resultSet.getLong("atime"), timeZone);
175  String mtime = ContentUtils.getStringTimeISO8601(resultSet.getLong("mtime"), timeZone);
176  String md5Hash = resultSet.getString("md5");
177  String parent_path = resultSet.getString("parent_path");
178  String mime_type = resultSet.getString("mime_type");
179  String extension = resultSet.getString("extension");
180 
181  saveFileInCaseUcoFormat(objectId, fileName, parent_path, md5Hash, mime_type, size, crtime, atime, mtime, extension, jsonGenerator, dataSourceTraceId);
182  }
183  }
184 
185  // create the required CASE-UCO entries at the end of the output file
186  finilizeJsonOutputFile(jsonGenerator);
187 
188  Case.getCurrentCaseThrows().addReport(reportPath, Bundle.ReportCaseUco_srcModuleName_text(), "");
189 
190  progressPanel.complete(ReportStatus.COMPLETE);
191  } catch (TskCoreException ex) {
192  logger.log(Level.SEVERE, "Failed to get list of files from case database", ex); //NON-NLS
193  progressPanel.complete(ReportStatus.ERROR);
194  } catch (IOException ex) {
195  logger.log(Level.SEVERE, "Failed to create JSON output for the CASE-UCO report", ex); //NON-NLS
196  progressPanel.complete(ReportStatus.ERROR);
197  } catch (SQLException ex) {
198  logger.log(Level.WARNING, "Unable to read result set", ex); //NON-NLS
199  progressPanel.complete(ReportStatus.ERROR);
200  } catch (NoCurrentCaseException ex) {
201  logger.log(Level.SEVERE, "No current case open", ex); //NON-NLS
202  progressPanel.complete(ReportStatus.ERROR);
203  } finally {
204  if (jsonGenerator != null) {
205  try {
206  jsonGenerator.close();
207  } catch (IOException ex) {
208  logger.log(Level.WARNING, "Failed to close JSON output file", ex); //NON-NLS
209  }
210  }
211  }
212  }
213 
214  private void initializeJsonOutputFile(JsonGenerator catalog) throws IOException {
215  catalog.writeStartObject();
216  catalog.writeFieldName("@graph");
217  catalog.writeStartArray();
218  }
219 
220  private void finilizeJsonOutputFile(JsonGenerator catalog) throws IOException {
221  catalog.writeEndArray();
222  catalog.writeEndObject();
223  }
224 
225  private String saveCaseInfo(SleuthkitCase skCase, JsonGenerator catalog) throws TskCoreException, SQLException, IOException, NoCurrentCaseException {
226 
227  // create a "trace" entry for the Autopsy case iteself
228  String uniqueCaseName;
229  String dbFileName;
230  TskData.DbType dbType = skCase.getDatabaseType();
231  if (dbType == TskData.DbType.SQLITE) {
232  uniqueCaseName = Case.getCurrentCaseThrows().getName();
233  dbFileName = skCase.getDatabaseName();
234  } else {
235  uniqueCaseName = skCase.getDatabaseName();
236  dbFileName = "";
237  }
238 
239  String caseDirPath = skCase.getDbDirPath();
240  String caseTraceId = "case-" + uniqueCaseName;
241  catalog.writeStartObject();
242  catalog.writeStringField("@id", caseTraceId);
243  catalog.writeStringField("@type", "Trace");
244 
245  catalog.writeFieldName("propertyBundle");
246  catalog.writeStartArray();
247  catalog.writeStartObject();
248 
249  // replace double slashes with single ones
250  caseDirPath = caseDirPath.replaceAll("\\\\", "/");
251 
252  catalog.writeStringField("@type", "File");
253  if (dbType == TskData.DbType.SQLITE) {
254  catalog.writeStringField("filePath", caseDirPath + "/" + dbFileName);
255  catalog.writeBooleanField("isDirectory", false);
256  } else {
257  catalog.writeStringField("filePath", caseDirPath);
258  catalog.writeBooleanField("isDirectory", true);
259  }
260  catalog.writeEndObject();
261 
262  catalog.writeEndArray();
263  catalog.writeEndObject();
264 
265  return caseTraceId;
266  }
267 
268  private String saveDataSourceInfo(Long selectedDataSourceId, String caseTraceId, SleuthkitCase skCase, JsonGenerator jsonGenerator) throws TskCoreException, SQLException, IOException {
269 
270  Long imageSize = (long) 0;
271  String imageName = "";
272  boolean isImageDataSource = false;
273  String getImageDataSourceQuery = "select size from tsk_image_info where obj_id = " + selectedDataSourceId;
274  try (SleuthkitCase.CaseDbQuery queryResult = skCase.executeQuery(getImageDataSourceQuery)) {
275  ResultSet resultSet = queryResult.getResultSet();
276  // check if we got a result
277  while (resultSet.next()) {
278  // we got a result so the data source was an image data source
279  imageSize = resultSet.getLong(1);
280  isImageDataSource = true;
281  break;
282  }
283  }
284 
285  if (isImageDataSource) {
286  // get caseDirPath to image file
287  String getPathToDataSourceQuery = "select name from tsk_image_names where obj_id = " + selectedDataSourceId;
288  try (SleuthkitCase.CaseDbQuery queryResult = skCase.executeQuery(getPathToDataSourceQuery)) {
289  ResultSet resultSet = queryResult.getResultSet();
290  while (resultSet.next()) {
291  imageName = resultSet.getString(1);
292  break;
293  }
294  }
295  } else {
296  // logical file data source
297  String getLogicalDataSourceQuery = "select name from tsk_files where obj_id = " + selectedDataSourceId;
298  try (SleuthkitCase.CaseDbQuery queryResult = skCase.executeQuery(getLogicalDataSourceQuery)) {
299  ResultSet resultSet = queryResult.getResultSet();
300  while (resultSet.next()) {
301  imageName = resultSet.getString(1);
302  break;
303  }
304  }
305  }
306 
307  return saveDataSourceInCaseUcoFormat(jsonGenerator, imageName, imageSize, selectedDataSourceId, caseTraceId);
308  }
309 
310  private String saveDataSourceInCaseUcoFormat(JsonGenerator catalog, String imageName, Long imageSize, Long selectedDataSourceId, String caseTraceId) throws IOException {
311 
312  // create a "trace" entry for the data source
313  String dataSourceTraceId = "data-source-"+selectedDataSourceId;
314  catalog.writeStartObject();
315  catalog.writeStringField("@id", dataSourceTraceId);
316  catalog.writeStringField("@type", "Trace");
317 
318  catalog.writeFieldName("propertyBundle");
319  catalog.writeStartArray();
320 
321  catalog.writeStartObject();
322  catalog.writeStringField("@type", "File");
323 
324  // replace double back slashes with single ones
325  imageName = imageName.replaceAll("\\\\", "/");
326 
327  catalog.writeStringField("filePath", imageName);
328  catalog.writeEndObject();
329 
330  if (imageSize > 0) {
331  catalog.writeStartObject();
332  catalog.writeStringField("@type", "ContentData");
333  catalog.writeStringField("sizeInBytes", Long.toString(imageSize));
334  catalog.writeEndObject();
335  }
336 
337  catalog.writeEndArray();
338  catalog.writeEndObject();
339 
340  // create a "relationship" entry between the case and the data source
341  catalog.writeStartObject();
342  catalog.writeStringField("@id", "relationship-" + caseTraceId);
343  catalog.writeStringField("@type", "Relationship");
344  catalog.writeStringField("source", dataSourceTraceId);
345  catalog.writeStringField("target", caseTraceId);
346  catalog.writeStringField("kindOfRelationship", "contained-within");
347  catalog.writeBooleanField("isDirectional", true);
348 
349  catalog.writeFieldName("propertyBundle");
350  catalog.writeStartArray();
351  catalog.writeStartObject();
352  catalog.writeStringField("@type", "PathRelation");
353  catalog.writeStringField("path", imageName);
354  catalog.writeEndObject();
355  catalog.writeEndArray();
356 
357  catalog.writeEndObject();
358 
359  return dataSourceTraceId;
360  }
361 
362  private void saveFileInCaseUcoFormat(Long objectId, String fileName, String parent_path, String md5Hash, String mime_type, long size, String ctime,
363  String atime, String mtime, String extension, JsonGenerator catalog, String dataSourceTraceId) throws IOException {
364 
365  String fileTraceId = "file-" + objectId;
366 
367  // create a "trace" entry for the file
368  catalog.writeStartObject();
369  catalog.writeStringField("@id", fileTraceId);
370  catalog.writeStringField("@type", "Trace");
371 
372  catalog.writeFieldName("propertyBundle");
373  catalog.writeStartArray();
374 
375  catalog.writeStartObject();
376  catalog.writeStringField("@type", "File");
377  catalog.writeStringField("createdTime", ctime);
378  catalog.writeStringField("accessedTime", atime);
379  catalog.writeStringField("modifiedTime", mtime);
380  if (extension != null) {
381  catalog.writeStringField("extension", extension);
382  }
383  catalog.writeStringField("fileName", fileName);
384  if (parent_path != null) {
385  catalog.writeStringField("filePath", parent_path + fileName);
386  }
387  catalog.writeBooleanField("isDirectory", false);
388  catalog.writeStringField("sizeInBytes", Long.toString(size));
389  catalog.writeEndObject();
390 
391  catalog.writeStartObject();
392  catalog.writeStringField("@type", "ContentData");
393  if (mime_type != null) {
394  catalog.writeStringField("mimeType", mime_type);
395  }
396  if (md5Hash != null) {
397  catalog.writeFieldName("hash");
398  catalog.writeStartArray();
399  catalog.writeStartObject();
400  catalog.writeStringField("@type", "Hash");
401  catalog.writeStringField("hashMethod", "MD5");
402  catalog.writeStringField("hashValue", md5Hash);
403  catalog.writeEndObject();
404  catalog.writeEndArray();
405  }
406  catalog.writeStringField("sizeInBytes", Long.toString(size));
407 
408  catalog.writeEndObject();
409 
410  catalog.writeEndArray();
411  catalog.writeEndObject();
412 
413  // create a "relationship" entry between the file and the data source
414  catalog.writeStartObject();
415  catalog.writeStringField("@id", "relationship-" + objectId);
416  catalog.writeStringField("@type", "Relationship");
417  catalog.writeStringField("source", fileTraceId);
418  catalog.writeStringField("target", dataSourceTraceId);
419  catalog.writeStringField("kindOfRelationship", "contained-within");
420  catalog.writeBooleanField("isDirectional", true);
421 
422  catalog.writeFieldName("propertyBundle");
423  catalog.writeStartArray();
424  catalog.writeStartObject();
425  catalog.writeStringField("@type", "PathRelation");
426  if (parent_path != null) {
427  catalog.writeStringField("path", parent_path + fileName);
428  } else {
429  catalog.writeStringField("path", fileName);
430  }
431  catalog.writeEndObject();
432  catalog.writeEndArray();
433 
434  catalog.writeEndObject();
435  }
436 
437  @Override
438  public String getName() {
439  String name = NbBundle.getMessage(this.getClass(), "ReportCaseUco.getName.text");
440  return name;
441  }
442 
443  @Override
444  public String getRelativeFilePath() {
445  return REPORT_FILE_NAME;
446  }
447 
448  @Override
449  public String getDescription() {
450  String desc = NbBundle.getMessage(this.getClass(), "ReportCaseUco.getDesc.text");
451  return desc;
452  }
453 
454  @Override
455  public JPanel getConfigurationPanel() {
456  try {
457  configPanel = new ReportCaseUcoConfigPanel();
458  } catch (NoCurrentCaseException | TskCoreException | SQLException ex) {
459  logger.log(Level.SEVERE, "Failed to initialize CASE-UCO settings panel", ex); //NON-NLS
460  MessageNotifyUtil.Message.error(Bundle.ReportCaseUco_notInitialized());
461  configPanel = null;
462  }
463  return configPanel;
464  }
465 }
static synchronized IngestManager getInstance()
void addReport(String localPath, String srcModuleName, String reportName)
Definition: Case.java:1631
static String getStringTimeISO8601(long epochSeconds, TimeZone tzone)
synchronized static Logger getLogger(String name)
Definition: Logger.java:124

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