Autopsy  4.19.0
Graphical digital forensics platform for The Sleuth Kit and other tools.
HashLookupSettings.java
Go to the documentation of this file.
1 /*
2  * Autopsy Forensic Browser
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.modules.hashdatabase;
20 
21 import java.io.File;
22 import java.io.FileInputStream;
23 import java.io.FileOutputStream;
24 import java.io.IOException;
25 import java.io.Serializable;
26 import java.util.ArrayList;
27 import java.util.List;
28 import java.util.Objects;
29 import java.util.logging.Level;
30 import javax.swing.JOptionPane;
31 import org.apache.commons.io.FileUtils;
32 import org.openide.util.NbBundle;
33 import org.openide.util.io.NbObjectInputStream;
34 import org.openide.util.io.NbObjectOutputStream;
35 import org.openide.windows.WindowManager;
42 import org.sleuthkit.datamodel.TskCoreException;
43 import org.w3c.dom.Document;
44 import org.w3c.dom.Element;
45 import org.w3c.dom.NodeList;
47 
51 final class HashLookupSettings implements Serializable {
52 
53  private static final String SERIALIZATION_FILE_NAME = "hashLookup.settings"; //NON-NLS
54  private static final String SERIALIZATION_FILE_PATH = PlatformUtil.getUserConfigDirectory() + File.separator + SERIALIZATION_FILE_NAME; //NON-NLS
55  private static final String SET_ELEMENT = "hash_set"; //NON-NLS
56  private static final String SET_NAME_ATTRIBUTE = "name"; //NON-NLS
57  private static final String SET_TYPE_ATTRIBUTE = "type"; //NON-NLS
58  private static final String SEARCH_DURING_INGEST_ATTRIBUTE = "use_for_ingest"; //NON-NLS
59  private static final String SEND_INGEST_MESSAGES_ATTRIBUTE = "show_inbox_messages"; //NON-NLS
60  private static final String PATH_ELEMENT = "hash_set_path"; //NON-NLS
61  private static final String LEGACY_PATH_NUMBER_ATTRIBUTE = "number"; //NON-NLS
62  private static final String CONFIG_FILE_NAME = "hashsets.xml"; //NON-NLS
63  private static final String configFilePath = PlatformUtil.getUserConfigDirectory() + File.separator + CONFIG_FILE_NAME;
64  private static final Logger logger = Logger.getLogger(HashDbManager.class.getName());
65 
66  private static final String USER_DIR_PLACEHOLDER = "[UserConfigFolder]";
67  private static final String CURRENT_USER_DIR = PlatformUtil.getUserConfigDirectory();
68 
69  private static final long serialVersionUID = 1L;
70  private final List<HashDbInfo> hashDbInfoList;
71 
77  HashLookupSettings(List<HashDbInfo> hashDbInfoList) {
78  this.hashDbInfoList = hashDbInfoList;
79  }
80 
81  static List<HashDbInfo> convertHashSetList(List<HashDbManager.HashDb> hashSets) throws HashLookupSettingsException{
82  List<HashDbInfo> dbInfoList = new ArrayList<>();
83  for(HashDbManager.HashDb db:hashSets){
84  try{
85  dbInfoList.add(new HashDbInfo(db));
86  } catch (TskCoreException ex){
87  logger.log(Level.SEVERE, "Could not load hash set settings for {0}", db.getHashSetName());
88  }
89  }
90  return dbInfoList;
91  }
92 
98  List<HashDbInfo> getHashDbInfo() {
99  return hashDbInfoList;
100  }
101 
110  static HashLookupSettings readSettings() throws HashLookupSettingsException {
111  File fileSetFile = new File(SERIALIZATION_FILE_PATH);
112  if (fileSetFile.exists()) {
113  return readSerializedSettings();
114  }
115  return readXmlSettings();
116 
117  }
118 
128  private static HashLookupSettings readSerializedSettings() throws HashLookupSettingsException {
129  try {
130  try (NbObjectInputStream in = new NbObjectInputStream(new FileInputStream(SERIALIZATION_FILE_PATH))) {
131  HashLookupSettings filesSetsSettings = (HashLookupSettings) in.readObject();
132 
133  /* NOTE: to support JIRA-4177, we need to check if any of the hash
134  database paths are in Windows user directory. If so, we replace the path
135  with USER_DIR_PLACEHOLDER before saving to disk. When reading from disk,
136  USER_DIR_PLACEHOLDER needs to be replaced with current user directory path.
137  */
138  convertPlaceholderToPath(filesSetsSettings);
139  return filesSetsSettings;
140  }
141  } catch (IOException | ClassNotFoundException ex) {
142  throw new HashLookupSettingsException("Could not read hash set settings.", ex);
143  }
144  }
145 
155  private static HashLookupSettings readXmlSettings() throws HashLookupSettingsException {
156  File xmlFile = new File(configFilePath);
157  if (xmlFile.exists()) {
158  boolean updatedSchema = false;
159 
160  // Open the XML document that implements the configuration file.
161  final Document doc = XMLUtil.loadDoc(HashDbManager.class, configFilePath);
162  if (doc == null) {
163  throw new HashLookupSettingsException("Could not open xml document.");
164  }
165 
166  // Get the root element.
167  Element root = doc.getDocumentElement();
168  if (root == null) {
169  throw new HashLookupSettingsException("Error loading hash sets: invalid file format.");
170  }
171 
172  // Get the hash set elements.
173  NodeList setsNList = root.getElementsByTagName(SET_ELEMENT);
174  int numSets = setsNList.getLength();
175 
176  // Create HashDbInfo objects for each hash set element. Throws on malformed xml.
177  String attributeErrorMessage = "Missing %s attribute"; //NON-NLS
178  String elementErrorMessage = "Empty %s element"; //NON-NLS
179  List<String> hashSetNames = new ArrayList<>();
180  List<HashDbInfo> hashDbInfoList = new ArrayList<>();
181  for (int i = 0; i < numSets; ++i) {
182  Element setEl = (Element) setsNList.item(i);
183 
184  String hashSetName = setEl.getAttribute(SET_NAME_ATTRIBUTE);
185  if (hashSetName.isEmpty()) {
186  throw new HashLookupSettingsException(String.format(attributeErrorMessage, SET_NAME_ATTRIBUTE));
187  }
188 
189  // Handle configurations saved before duplicate hash set names were not permitted.
190  if (hashSetNames.contains(hashSetName)) {
191  int suffix = 0;
192  String newHashSetName;
193  do {
194  ++suffix;
195  newHashSetName = hashSetName + suffix;
196  } while (hashSetNames.contains(newHashSetName));
197  logger.log(Level.INFO, "Duplicate hash set name " + hashSetName + " found. Replacing with " + newHashSetName + ".");
198  if (RuntimeProperties.runningWithGUI()) {
199  JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(),
200  NbBundle.getMessage(HashLookupSettings.class,
201  "HashDbManager.replacingDuplicateHashsetNameMsg",
202  hashSetName, newHashSetName),
203  NbBundle.getMessage(HashLookupSettings.class, "HashDbManager.openHashDbErr"),
204  JOptionPane.ERROR_MESSAGE);
205  hashSetName = newHashSetName;
206  }
207  }
208 
209  String knownFilesType = setEl.getAttribute(SET_TYPE_ATTRIBUTE);
210  if (knownFilesType.isEmpty()) {
211  throw new HashLookupSettingsException(String.format(attributeErrorMessage, SET_TYPE_ATTRIBUTE));
212  }
213 
214  // Handle legacy known files types.
215  if (knownFilesType.equals("NSRL")) { //NON-NLS
216  knownFilesType = HashDbManager.HashDb.KnownFilesType.KNOWN.toString();
217  updatedSchema = true;
218  }
219 
220  final String searchDuringIngest = setEl.getAttribute(SEARCH_DURING_INGEST_ATTRIBUTE);
221  if (searchDuringIngest.isEmpty()) {
222  throw new HashLookupSettingsException(String.format(attributeErrorMessage, SEND_INGEST_MESSAGES_ATTRIBUTE));
223  }
224  Boolean searchDuringIngestFlag = Boolean.parseBoolean(searchDuringIngest);
225 
226  final String sendIngestMessages = setEl.getAttribute(SEND_INGEST_MESSAGES_ATTRIBUTE);
227  if (searchDuringIngest.isEmpty()) {
228  throw new HashLookupSettingsException(String.format(attributeErrorMessage, SEND_INGEST_MESSAGES_ATTRIBUTE));
229  }
230  Boolean sendIngestMessagesFlag = Boolean.parseBoolean(sendIngestMessages);
231 
232  String dbPath;
233  NodeList pathsNList = setEl.getElementsByTagName(PATH_ELEMENT);
234  if (pathsNList.getLength() > 0) {
235  Element pathEl = (Element) pathsNList.item(0); // Shouldn't be more than one.
236 
237  // Check for legacy path number attribute.
238  String legacyPathNumber = pathEl.getAttribute(LEGACY_PATH_NUMBER_ATTRIBUTE);
239  if (null != legacyPathNumber && !legacyPathNumber.isEmpty()) {
240  updatedSchema = true;
241  }
242 
243  dbPath = pathEl.getTextContent();
244  if (dbPath.isEmpty()) {
245  throw new HashLookupSettingsException(String.format(elementErrorMessage, PATH_ELEMENT));
246  }
247  } else {
248  throw new HashLookupSettingsException(String.format(elementErrorMessage, PATH_ELEMENT));
249  }
250  hashDbInfoList.add(new HashDbInfo(hashSetName, HashDbManager.HashDb.KnownFilesType.valueOf(knownFilesType),
251  searchDuringIngestFlag, sendIngestMessagesFlag, dbPath));
252  hashSetNames.add(hashSetName);
253  }
254 
255  if (updatedSchema) {
256  String backupFilePath = configFilePath + ".v1_backup"; //NON-NLS
257  String messageBoxTitle = NbBundle.getMessage(HashLookupSettings.class,
258  "HashDbManager.msgBoxTitle.confFileFmtChanged");
259  String baseMessage = NbBundle.getMessage(HashLookupSettings.class,
260  "HashDbManager.baseMessage.updatedFormatHashDbConfig");
261  try {
262  FileUtils.copyFile(new File(configFilePath), new File(backupFilePath));
263  logger.log(Level.INFO, "Updated the schema, backup saved at: " + backupFilePath);
264  if (RuntimeProperties.runningWithGUI()) {
265  JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(),
266  NbBundle.getMessage(HashLookupSettings.class,
267  "HashDbManager.savedBackupOfOldConfigMsg",
268  baseMessage, backupFilePath),
269  messageBoxTitle,
270  JOptionPane.INFORMATION_MESSAGE);
271  }
272  } catch (IOException ex) {
273  logger.log(Level.WARNING, "Failed to save backup of old format configuration file to " + backupFilePath, ex); //NON-NLS
274  JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(), baseMessage, messageBoxTitle, JOptionPane.INFORMATION_MESSAGE);
275  }
276  HashLookupSettings settings;
277  settings = new HashLookupSettings(hashDbInfoList);
278  HashLookupSettings.writeSettings(settings);
279  }
280  return new HashLookupSettings(hashDbInfoList);
281  } else {
282  return new HashLookupSettings(new ArrayList<>());
283  }
284  }
285 
293  static boolean writeSettings(HashLookupSettings settings) {
294 
295  /* NOTE: to support JIRA-4177, we need to check if any of the hash
296  database paths are in Windows user directory. If so, replace the path
297  with USER_DIR_PLACEHOLDER so that when it is read, it gets updated to be
298  the current user directory path.
299  */
300  convertPathToPlaceholder(settings);
301  try (NbObjectOutputStream out = new NbObjectOutputStream(new FileOutputStream(SERIALIZATION_FILE_PATH))) {
302  out.writeObject(settings);
303  // restore the paths, in case they are going to be used somewhere
304  convertPlaceholderToPath(settings);
305  return true;
306  } catch (Exception ex) {
307  logger.log(Level.SEVERE, "Could not write hash set settings.");
308  return false;
309  }
310  }
311 
319  static void convertPathToPlaceholder(HashLookupSettings settings) {
320  for (HashDbInfo hashDbInfo : settings.getHashDbInfo()) {
321  if (hashDbInfo.isFileDatabaseType()) {
322  String dbPath = hashDbInfo.getPath();
323  if (dbPath.startsWith(CURRENT_USER_DIR)) {
324  // replace the current user directory with place holder
325  String remainingPath = dbPath.substring(CURRENT_USER_DIR.length());
326  hashDbInfo.setPath(USER_DIR_PLACEHOLDER + remainingPath);
327  }
328  }
329  }
330  }
331 
339  static void convertPlaceholderToPath(HashLookupSettings settings) {
340  for (HashDbInfo hashDbInfo : settings.getHashDbInfo()) {
341  if (hashDbInfo.isFileDatabaseType()) {
342  String dbPath = hashDbInfo.getPath();
343  if (dbPath.startsWith(USER_DIR_PLACEHOLDER)) {
344  // replace the place holder with current user directory
345  String remainingPath = dbPath.substring(USER_DIR_PLACEHOLDER.length());
346  hashDbInfo.setPath(CURRENT_USER_DIR + remainingPath);
347  }
348  }
349  }
350  }
351 
352 
358  static final class HashDbInfo implements Serializable {
359 
360  enum DatabaseType{
361  FILE,
362  CENTRAL_REPOSITORY
363  };
364 
365  private static final long serialVersionUID = 1L;
366  private final String hashSetName;
367  private final HashDbManager.HashDb.KnownFilesType knownFilesType;
368  private boolean searchDuringIngest;
369  private final boolean sendIngestMessages;
370  private String path;
371  private final String version;
372  private final boolean readOnly;
373  private final int referenceSetID;
374  private DatabaseType dbType;
375 
376 
377 
392  HashDbInfo(String hashSetName, HashDbManager.HashDb.KnownFilesType knownFilesType, boolean searchDuringIngest, boolean sendIngestMessages,
393  String path, int referenceSetID, String version, boolean readOnly, boolean isCRType) {
394  this.hashSetName = hashSetName;
395  this.knownFilesType = knownFilesType;
396  this.searchDuringIngest = searchDuringIngest;
397  this.sendIngestMessages = sendIngestMessages;
398  this.path = path;
399  this.referenceSetID = referenceSetID;
400  this.version = version;
401  this.readOnly = readOnly;
402  this.dbType = isCRType ? DatabaseType.CENTRAL_REPOSITORY : DatabaseType.FILE;
403  }
404 
415  HashDbInfo(String hashSetName, HashDbManager.HashDb.KnownFilesType knownFilesType, boolean searchDuringIngest, boolean sendIngestMessages, String path) {
416  this.hashSetName = hashSetName;
417  this.knownFilesType = knownFilesType;
418  this.searchDuringIngest = searchDuringIngest;
419  this.sendIngestMessages = sendIngestMessages;
420  this.path = path;
421  this.referenceSetID = -1;
422  this.version = "";
423  this.readOnly = false;
424  this.dbType = DatabaseType.FILE;
425  }
426 
427  HashDbInfo(String hashSetName, String version, int referenceSetID, HashDbManager.HashDb.KnownFilesType knownFilesType, boolean readOnly, boolean searchDuringIngest, boolean sendIngestMessages){
428  this.hashSetName = hashSetName;
429  this.version = version;
430  this.referenceSetID = referenceSetID;
431  this.knownFilesType = knownFilesType;
432  this.readOnly = readOnly;
433  this.searchDuringIngest = searchDuringIngest;
434  this.sendIngestMessages = sendIngestMessages;
435  this.path = "";
436  dbType = DatabaseType.CENTRAL_REPOSITORY;
437  }
438 
439  HashDbInfo(HashDbManager.HashDb db) throws TskCoreException{
440  if(db instanceof HashDbManager.SleuthkitHashSet){
441  HashDbManager.SleuthkitHashSet fileTypeDb = (HashDbManager.SleuthkitHashSet)db;
442  this.hashSetName = fileTypeDb.getHashSetName();
443  this.knownFilesType = fileTypeDb.getKnownFilesType();
444  this.searchDuringIngest = fileTypeDb.getSearchDuringIngest();
445  this.sendIngestMessages = fileTypeDb.getSendIngestMessages();
446  this.referenceSetID = -1;
447  this.version = "";
448  this.readOnly = false;
449  this.dbType = DatabaseType.FILE;
450  if (fileTypeDb.hasIndexOnly()) {
451  this.path = fileTypeDb.getIndexPath();
452  } else {
453  this.path = fileTypeDb.getDatabasePath();
454  }
455  } else {
456  HashDbManager.CentralRepoHashSet centralRepoDb = (HashDbManager.CentralRepoHashSet)db;
457  this.hashSetName = centralRepoDb.getHashSetName();
458  this.version = centralRepoDb.getVersion();
459  this.knownFilesType = centralRepoDb.getKnownFilesType();
460  this.readOnly = ! centralRepoDb.isUpdateable();
461  this.searchDuringIngest = centralRepoDb.getSearchDuringIngest();
462  this.sendIngestMessages = centralRepoDb.getSendIngestMessages();
463  this.path = "";
464  this.referenceSetID = centralRepoDb.getReferenceSetID();
465  this.dbType = DatabaseType.CENTRAL_REPOSITORY;
466  }
467  }
468 
474  String getHashSetName() {
475  return hashSetName;
476  }
477 
482  String getVersion(){
483  return version;
484  }
485 
490  boolean isReadOnly(){
491  return readOnly;
492  }
493 
499  HashDbManager.HashDb.KnownFilesType getKnownFilesType() {
500  return knownFilesType;
501  }
502 
508  boolean getSearchDuringIngest() {
509  return searchDuringIngest;
510  }
511 
516  void setSearchDuringIngest(boolean searchDuringIngest) {
517  this.searchDuringIngest = searchDuringIngest;
518  }
519 
525  boolean getSendIngestMessages() {
526  return sendIngestMessages;
527  }
528 
534  String getPath() {
535  return path;
536  }
537 
542  public void setPath(String path) {
543  this.path = path;
544  }
545 
546  int getReferenceSetID(){
547  return referenceSetID;
548  }
549 
554  boolean isFileDatabaseType(){
555  return dbType == DatabaseType.FILE;
556  }
557 
558  boolean isCentralRepoDatabaseType(){
559  return dbType == DatabaseType.CENTRAL_REPOSITORY;
560  }
561 
562  boolean matches(HashDb hashDb){
563  if(hashDb == null){
564  return false;
565  }
566 
567  if( ! this.knownFilesType.equals(hashDb.getKnownFilesType())){
568  return false;
569  }
570 
571  if((this.dbType == DatabaseType.CENTRAL_REPOSITORY) && (! (hashDb instanceof CentralRepoHashSet))
572  || (this.dbType == DatabaseType.FILE) && (! (hashDb instanceof SleuthkitHashSet))){
573  return false;
574  }
575 
576  if( ! this.hashSetName.equals(hashDb.getHashSetName())){
577  return false;
578  }
579 
580  if(hashDb instanceof CentralRepoHashSet){
581  CentralRepoHashSet crDb = (CentralRepoHashSet) hashDb;
582  if(this.referenceSetID != crDb.getReferenceSetID()){
583  return false;
584  }
585 
586  if(! version.equals(crDb.getVersion())){
587  return false;
588  }
589  }
590 
591  return true;
592  }
593 
594  @Override
595  public boolean equals(Object obj) {
596  if (obj == null) {
597  return false;
598  }
599 
600  if (getClass() != obj.getClass()) {
601  return false;
602  }
603 
604  final HashDbInfo other = (HashDbInfo) obj;
605 
606  if(! this.dbType.equals(other.dbType)){
607  return false;
608  }
609 
610  if(this.dbType.equals(DatabaseType.FILE)){
611  // For files, we expect the name and known type to match
612  return (this.hashSetName.equals(other.hashSetName)
613  && this.knownFilesType.equals(other.knownFilesType));
614  } else {
615  // For central repo, the name, index, and known files type should match
616  return (this.hashSetName.equals(other.hashSetName)
617  && (this.referenceSetID == other.referenceSetID)
618  && this.knownFilesType.equals(other.knownFilesType));
619  }
620  }
621 
622  @Override
623  public int hashCode() {
624  int hash = 5;
625  hash = 89 * hash + Objects.hashCode(this.hashSetName);
626  hash = 89 * hash + Objects.hashCode(this.knownFilesType);
627  hash = 89 * hash + Objects.hashCode(this.dbType);
628  if(this.dbType.equals(DatabaseType.CENTRAL_REPOSITORY)){
629  hash = 89 * hash + this.referenceSetID;
630  }
631 
632  return hash;
633  }
634 
642  private void readObject(java.io.ObjectInputStream stream)
643  throws IOException, ClassNotFoundException {
644  stream.defaultReadObject();
645 
646  if(dbType == null){
647  dbType = DatabaseType.FILE;
648  }
649  }
650  }
651 
657  static class HashLookupSettingsException extends Exception {
658 
659  private static final long serialVersionUID = 1L;
660 
661  HashLookupSettingsException(String message) {
662  super(message);
663  }
664 
665  HashLookupSettingsException(String message, Throwable throwable) {
666  super(message, throwable);
667  }
668  }
669 }

Copyright © 2012-2021 Basis Technology. Generated on: Fri Aug 6 2021
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.