Autopsy  4.20.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;
41 import org.sleuthkit.datamodel.TskCoreException;
42 import org.w3c.dom.Document;
43 import org.w3c.dom.Element;
44 import org.w3c.dom.NodeList;
47 
51 final class HashLookupSettings implements Serializable {
52  private static final String SET_ELEMENT = "hash_set"; //NON-NLS
53  private static final String SET_NAME_ATTRIBUTE = "name"; //NON-NLS
54  private static final String SET_TYPE_ATTRIBUTE = "type"; //NON-NLS
55  private static final String SEARCH_DURING_INGEST_ATTRIBUTE = "use_for_ingest"; //NON-NLS
56  private static final String SEND_INGEST_MESSAGES_ATTRIBUTE = "show_inbox_messages"; //NON-NLS
57  private static final String PATH_ELEMENT = "hash_set_path"; //NON-NLS
58  private static final String LEGACY_PATH_NUMBER_ATTRIBUTE = "number"; //NON-NLS
59 
60  private static final Logger logger = Logger.getLogger(HashDbManager.class.getName());
61 
62  private static final String USER_DIR_PLACEHOLDER = "[UserConfigFolder]";
63 
64  private static final long serialVersionUID = 1L;
65  private final List<HashDbInfo> hashDbInfoList;
66 
70  static String getSettingsPath() {
71  return HashConfigPaths.getInstance().getSettingsPath();
72  }
73 
77  static String getDefaultDbPath() {
78  return HashConfigPaths.getInstance().getDefaultDbPath();
79  }
80 
81 
85  static String getBaseHashsetConfigPath() {
86  return HashConfigPaths.getInstance().getBasePath();
87  }
88 
94  HashLookupSettings(List<HashDbInfo> hashDbInfoList) {
95  this.hashDbInfoList = hashDbInfoList;
96  }
97 
98  static List<HashDbInfo> convertHashSetList(List<HashDbManager.HashDb> hashSets) throws HashLookupSettingsException{
99  List<HashDbInfo> dbInfoList = new ArrayList<>();
100  for(HashDbManager.HashDb db:hashSets){
101  try{
102  dbInfoList.add(new HashDbInfo(db));
103  } catch (TskCoreException ex){
104  logger.log(Level.SEVERE, "Could not load hash set settings for {0}", db.getHashSetName());
105  }
106  }
107  return dbInfoList;
108  }
109 
115  List<HashDbInfo> getHashDbInfo() {
116  return hashDbInfoList;
117  }
118 
127  static HashLookupSettings readSettings() throws HashLookupSettingsException {
128  File fileSetFile = new File(HashConfigPaths.getInstance().getSettingsPath());
129  if (fileSetFile.exists()) {
130  return readSerializedSettings();
131  }
132  return readXmlSettings();
133 
134  }
135 
145  private static HashLookupSettings readSerializedSettings() throws HashLookupSettingsException {
146  try {
147  try (NbObjectInputStream in = new NbObjectInputStream(new FileInputStream(HashConfigPaths.getInstance().getSettingsPath()))) {
148  HashLookupSettings filesSetsSettings = (HashLookupSettings) in.readObject();
149 
150  /* NOTE: to support JIRA-4177, we need to check if any of the hash
151  database paths are in Windows user directory. If so, we replace the path
152  with USER_DIR_PLACEHOLDER before saving to disk. When reading from disk,
153  USER_DIR_PLACEHOLDER needs to be replaced with current user directory path.
154  */
155  convertPlaceholderToPath(filesSetsSettings);
156  return filesSetsSettings;
157  }
158  } catch (IOException | ClassNotFoundException ex) {
159  throw new HashLookupSettingsException("Could not read hash set settings.", ex);
160  }
161  }
162 
172  private static HashLookupSettings readXmlSettings() throws HashLookupSettingsException {
173  File xmlFile = new File(HashConfigPaths.getInstance().getXmlSettingsPath());
174  if (xmlFile.exists()) {
175  boolean updatedSchema = false;
176 
177  // Open the XML document that implements the configuration file.
178  final Document doc = XMLUtil.loadDoc(HashDbManager.class, HashConfigPaths.getInstance().getXmlSettingsPath());
179  if (doc == null) {
180  throw new HashLookupSettingsException("Could not open xml document.");
181  }
182 
183  // Get the root element.
184  Element root = doc.getDocumentElement();
185  if (root == null) {
186  throw new HashLookupSettingsException("Error loading hash sets: invalid file format.");
187  }
188 
189  // Get the hash set elements.
190  NodeList setsNList = root.getElementsByTagName(SET_ELEMENT);
191  int numSets = setsNList.getLength();
192 
193  // Create HashDbInfo objects for each hash set element. Throws on malformed xml.
194  String attributeErrorMessage = "Missing %s attribute"; //NON-NLS
195  String elementErrorMessage = "Empty %s element"; //NON-NLS
196  List<String> hashSetNames = new ArrayList<>();
197  List<HashDbInfo> hashDbInfoList = new ArrayList<>();
198  for (int i = 0; i < numSets; ++i) {
199  Element setEl = (Element) setsNList.item(i);
200 
201  String hashSetName = setEl.getAttribute(SET_NAME_ATTRIBUTE);
202  if (hashSetName.isEmpty()) {
203  throw new HashLookupSettingsException(String.format(attributeErrorMessage, SET_NAME_ATTRIBUTE));
204  }
205 
206  // Handle configurations saved before duplicate hash set names were not permitted.
207  if (hashSetNames.contains(hashSetName)) {
208  int suffix = 0;
209  String newHashSetName;
210  do {
211  ++suffix;
212  newHashSetName = hashSetName + suffix;
213  } while (hashSetNames.contains(newHashSetName));
214  logger.log(Level.INFO, "Duplicate hash set name " + hashSetName + " found. Replacing with " + newHashSetName + ".");
215  if (RuntimeProperties.runningWithGUI()) {
216  JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(),
217  NbBundle.getMessage(HashLookupSettings.class,
218  "HashDbManager.replacingDuplicateHashsetNameMsg",
219  hashSetName, newHashSetName),
220  NbBundle.getMessage(HashLookupSettings.class, "HashDbManager.openHashDbErr"),
221  JOptionPane.ERROR_MESSAGE);
222  hashSetName = newHashSetName;
223  }
224  }
225 
226  String knownFilesType = setEl.getAttribute(SET_TYPE_ATTRIBUTE);
227  if (knownFilesType.isEmpty()) {
228  throw new HashLookupSettingsException(String.format(attributeErrorMessage, SET_TYPE_ATTRIBUTE));
229  }
230 
231  // Handle legacy known files types.
232  if (knownFilesType.equals("NSRL")) { //NON-NLS
233  knownFilesType = HashDbManager.HashDb.KnownFilesType.KNOWN.toString();
234  updatedSchema = true;
235  }
236 
237  final String searchDuringIngest = setEl.getAttribute(SEARCH_DURING_INGEST_ATTRIBUTE);
238  if (searchDuringIngest.isEmpty()) {
239  throw new HashLookupSettingsException(String.format(attributeErrorMessage, SEND_INGEST_MESSAGES_ATTRIBUTE));
240  }
241  Boolean searchDuringIngestFlag = Boolean.parseBoolean(searchDuringIngest);
242 
243  final String sendIngestMessages = setEl.getAttribute(SEND_INGEST_MESSAGES_ATTRIBUTE);
244  if (searchDuringIngest.isEmpty()) {
245  throw new HashLookupSettingsException(String.format(attributeErrorMessage, SEND_INGEST_MESSAGES_ATTRIBUTE));
246  }
247  Boolean sendIngestMessagesFlag = Boolean.parseBoolean(sendIngestMessages);
248 
249  String dbPath;
250  NodeList pathsNList = setEl.getElementsByTagName(PATH_ELEMENT);
251  if (pathsNList.getLength() > 0) {
252  Element pathEl = (Element) pathsNList.item(0); // Shouldn't be more than one.
253 
254  // Check for legacy path number attribute.
255  String legacyPathNumber = pathEl.getAttribute(LEGACY_PATH_NUMBER_ATTRIBUTE);
256  if (null != legacyPathNumber && !legacyPathNumber.isEmpty()) {
257  updatedSchema = true;
258  }
259 
260  dbPath = pathEl.getTextContent();
261  if (dbPath.isEmpty()) {
262  throw new HashLookupSettingsException(String.format(elementErrorMessage, PATH_ELEMENT));
263  }
264  } else {
265  throw new HashLookupSettingsException(String.format(elementErrorMessage, PATH_ELEMENT));
266  }
267  hashDbInfoList.add(new HashDbInfo(hashSetName, HashDbManager.HashDb.KnownFilesType.valueOf(knownFilesType),
268  searchDuringIngestFlag, sendIngestMessagesFlag, dbPath));
269  hashSetNames.add(hashSetName);
270  }
271 
272  if (updatedSchema) {
273  String backupFilePath = HashConfigPaths.getInstance().getXmlSettingsPath() + ".v1_backup"; //NON-NLS
274  String messageBoxTitle = NbBundle.getMessage(HashLookupSettings.class,
275  "HashDbManager.msgBoxTitle.confFileFmtChanged");
276  String baseMessage = NbBundle.getMessage(HashLookupSettings.class,
277  "HashDbManager.baseMessage.updatedFormatHashDbConfig");
278  try {
279  FileUtils.copyFile(new File(HashConfigPaths.getInstance().getXmlSettingsPath()), new File(backupFilePath));
280  logger.log(Level.INFO, "Updated the schema, backup saved at: " + backupFilePath);
281  if (RuntimeProperties.runningWithGUI()) {
282  JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(),
283  NbBundle.getMessage(HashLookupSettings.class,
284  "HashDbManager.savedBackupOfOldConfigMsg",
285  baseMessage, backupFilePath),
286  messageBoxTitle,
287  JOptionPane.INFORMATION_MESSAGE);
288  }
289  } catch (IOException ex) {
290  logger.log(Level.WARNING, "Failed to save backup of old format configuration file to " + backupFilePath, ex); //NON-NLS
291  JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(), baseMessage, messageBoxTitle, JOptionPane.INFORMATION_MESSAGE);
292  }
293  HashLookupSettings settings;
294  settings = new HashLookupSettings(hashDbInfoList);
295  HashLookupSettings.writeSettings(settings);
296  }
297  return new HashLookupSettings(hashDbInfoList);
298  } else {
299  return new HashLookupSettings(new ArrayList<>());
300  }
301  }
302 
310  static boolean writeSettings(HashLookupSettings settings) {
311 
312  /* NOTE: to support JIRA-4177, we need to check if any of the hash
313  database paths are in Windows user directory. If so, replace the path
314  with USER_DIR_PLACEHOLDER so that when it is read, it gets updated to be
315  the current user directory path.
316  */
317  convertPathToPlaceholder(settings);
318  try (NbObjectOutputStream out = new NbObjectOutputStream(new FileOutputStream(HashConfigPaths.getInstance().getSettingsPath()))) {
319  out.writeObject(settings);
320  // restore the paths, in case they are going to be used somewhere
321  convertPlaceholderToPath(settings);
322  return true;
323  } catch (Exception ex) {
324  logger.log(Level.SEVERE, "Could not write hash set settings.");
325  return false;
326  }
327  }
328 
336  static void convertPathToPlaceholder(HashLookupSettings settings) {
337  for (HashDbInfo hashDbInfo : settings.getHashDbInfo()) {
338  if (hashDbInfo.isFileDatabaseType()) {
339  String dbPath = hashDbInfo.getPath();
340  if (dbPath.startsWith(HashConfigPaths.getInstance().getBasePath())) {
341  // replace the current user directory with place holder
342  String remainingPath = dbPath.substring(HashConfigPaths.getInstance().getBasePath().length());
343  hashDbInfo.setPath(USER_DIR_PLACEHOLDER + remainingPath);
344  }
345  }
346  }
347  }
348 
356  static void convertPlaceholderToPath(HashLookupSettings settings) {
357  for (HashDbInfo hashDbInfo : settings.getHashDbInfo()) {
358  if (hashDbInfo.isFileDatabaseType()) {
359  String dbPath = hashDbInfo.getPath();
360  if (dbPath.startsWith(USER_DIR_PLACEHOLDER)) {
361  // replace the place holder with current user directory
362  String remainingPath = dbPath.substring(USER_DIR_PLACEHOLDER.length());
363  hashDbInfo.setPath(HashConfigPaths.getInstance().getBasePath() + remainingPath);
364  }
365  }
366  }
367  }
368 
369 
375  static final class HashDbInfo implements Serializable {
376 
377  enum DatabaseType{
378  FILE,
379  CENTRAL_REPOSITORY
380  };
381 
382  private static final long serialVersionUID = 1L;
383  private final String hashSetName;
384  private final HashDbManager.HashDb.KnownFilesType knownFilesType;
385  private boolean searchDuringIngest;
386  private final boolean sendIngestMessages;
387  private String path;
388  private final String version;
389  private final boolean readOnly;
390  private final int referenceSetID;
391  private DatabaseType dbType;
392 
393 
394 
409  HashDbInfo(String hashSetName, HashDbManager.HashDb.KnownFilesType knownFilesType, boolean searchDuringIngest, boolean sendIngestMessages,
410  String path, int referenceSetID, String version, boolean readOnly, boolean isCRType) {
411  this.hashSetName = hashSetName;
412  this.knownFilesType = knownFilesType;
413  this.searchDuringIngest = searchDuringIngest;
414  this.sendIngestMessages = sendIngestMessages;
415  this.path = path;
416  this.referenceSetID = referenceSetID;
417  this.version = version;
418  this.readOnly = readOnly;
419  this.dbType = isCRType ? DatabaseType.CENTRAL_REPOSITORY : DatabaseType.FILE;
420  }
421 
432  HashDbInfo(String hashSetName, HashDbManager.HashDb.KnownFilesType knownFilesType, boolean searchDuringIngest, boolean sendIngestMessages, String path) {
433  this.hashSetName = hashSetName;
434  this.knownFilesType = knownFilesType;
435  this.searchDuringIngest = searchDuringIngest;
436  this.sendIngestMessages = sendIngestMessages;
437  this.path = path;
438  this.referenceSetID = -1;
439  this.version = "";
440  this.readOnly = false;
441  this.dbType = DatabaseType.FILE;
442  }
443 
444  HashDbInfo(String hashSetName, String version, int referenceSetID, HashDbManager.HashDb.KnownFilesType knownFilesType, boolean readOnly, boolean searchDuringIngest, boolean sendIngestMessages){
445  this.hashSetName = hashSetName;
446  this.version = version;
447  this.referenceSetID = referenceSetID;
448  this.knownFilesType = knownFilesType;
449  this.readOnly = readOnly;
450  this.searchDuringIngest = searchDuringIngest;
451  this.sendIngestMessages = sendIngestMessages;
452  this.path = "";
453  dbType = DatabaseType.CENTRAL_REPOSITORY;
454  }
455 
456  HashDbInfo(HashDbManager.HashDb db) throws TskCoreException{
457  if(db instanceof HashDbManager.SleuthkitHashSet){
458  HashDbManager.SleuthkitHashSet fileTypeDb = (HashDbManager.SleuthkitHashSet)db;
459  this.hashSetName = fileTypeDb.getHashSetName();
460  this.knownFilesType = fileTypeDb.getKnownFilesType();
461  this.searchDuringIngest = fileTypeDb.getSearchDuringIngest();
462  this.sendIngestMessages = fileTypeDb.getSendIngestMessages();
463  this.referenceSetID = -1;
464  this.version = "";
465  this.readOnly = false;
466  this.dbType = DatabaseType.FILE;
467  if (fileTypeDb.hasIndexOnly()) {
468  this.path = fileTypeDb.getIndexPath();
469  } else {
470  this.path = fileTypeDb.getDatabasePath();
471  }
472  } else {
473  HashDbManager.CentralRepoHashSet centralRepoDb = (HashDbManager.CentralRepoHashSet)db;
474  this.hashSetName = centralRepoDb.getHashSetName();
475  this.version = centralRepoDb.getVersion();
476  this.knownFilesType = centralRepoDb.getKnownFilesType();
477  this.readOnly = ! centralRepoDb.isUpdateable();
478  this.searchDuringIngest = centralRepoDb.getSearchDuringIngest();
479  this.sendIngestMessages = centralRepoDb.getSendIngestMessages();
480  this.path = "";
481  this.referenceSetID = centralRepoDb.getReferenceSetID();
482  this.dbType = DatabaseType.CENTRAL_REPOSITORY;
483  }
484  }
485 
491  String getHashSetName() {
492  return hashSetName;
493  }
494 
499  String getVersion(){
500  return version;
501  }
502 
507  boolean isReadOnly(){
508  return readOnly;
509  }
510 
516  HashDbManager.HashDb.KnownFilesType getKnownFilesType() {
517  return knownFilesType;
518  }
519 
525  boolean getSearchDuringIngest() {
526  return searchDuringIngest;
527  }
528 
533  void setSearchDuringIngest(boolean searchDuringIngest) {
534  this.searchDuringIngest = searchDuringIngest;
535  }
536 
542  boolean getSendIngestMessages() {
543  return sendIngestMessages;
544  }
545 
551  String getPath() {
552  return path;
553  }
554 
559  public void setPath(String path) {
560  this.path = path;
561  }
562 
563  int getReferenceSetID(){
564  return referenceSetID;
565  }
566 
571  boolean isFileDatabaseType(){
572  return dbType == DatabaseType.FILE;
573  }
574 
575  boolean isCentralRepoDatabaseType(){
576  return dbType == DatabaseType.CENTRAL_REPOSITORY;
577  }
578 
579  boolean matches(HashDb hashDb){
580  if(hashDb == null){
581  return false;
582  }
583 
584  if( ! this.knownFilesType.equals(hashDb.getKnownFilesType())){
585  return false;
586  }
587 
588  if((this.dbType == DatabaseType.CENTRAL_REPOSITORY) && (! (hashDb instanceof CentralRepoHashSet))
589  || (this.dbType == DatabaseType.FILE) && (! (hashDb instanceof SleuthkitHashSet))){
590  return false;
591  }
592 
593  if( ! this.hashSetName.equals(hashDb.getHashSetName())){
594  return false;
595  }
596 
597  if(hashDb instanceof CentralRepoHashSet){
598  CentralRepoHashSet crDb = (CentralRepoHashSet) hashDb;
599  if(this.referenceSetID != crDb.getReferenceSetID()){
600  return false;
601  }
602 
603  if(! version.equals(crDb.getVersion())){
604  return false;
605  }
606  }
607 
608  return true;
609  }
610 
611  @Override
612  public boolean equals(Object obj) {
613  if (obj == null) {
614  return false;
615  }
616 
617  if (getClass() != obj.getClass()) {
618  return false;
619  }
620 
621  final HashDbInfo other = (HashDbInfo) obj;
622 
623  if(! this.dbType.equals(other.dbType)){
624  return false;
625  }
626 
627  if(this.dbType.equals(DatabaseType.FILE)){
628  // For files, we expect the name and known type to match
629  return (this.hashSetName.equals(other.hashSetName)
630  && this.knownFilesType.equals(other.knownFilesType));
631  } else {
632  // For central repo, the name, index, and known files type should match
633  return (this.hashSetName.equals(other.hashSetName)
634  && (this.referenceSetID == other.referenceSetID)
635  && this.knownFilesType.equals(other.knownFilesType));
636  }
637  }
638 
639  @Override
640  public int hashCode() {
641  int hash = 5;
642  hash = 89 * hash + Objects.hashCode(this.hashSetName);
643  hash = 89 * hash + Objects.hashCode(this.knownFilesType);
644  hash = 89 * hash + Objects.hashCode(this.dbType);
645  if(this.dbType.equals(DatabaseType.CENTRAL_REPOSITORY)){
646  hash = 89 * hash + this.referenceSetID;
647  }
648 
649  return hash;
650  }
651 
659  private void readObject(java.io.ObjectInputStream stream)
660  throws IOException, ClassNotFoundException {
661  stream.defaultReadObject();
662 
663  if(dbType == null){
664  dbType = DatabaseType.FILE;
665  }
666  }
667  }
668 
674  static class HashLookupSettingsException extends Exception {
675 
676  private static final long serialVersionUID = 1L;
677 
678  HashLookupSettingsException(String message) {
679  super(message);
680  }
681 
682  HashLookupSettingsException(String message, Throwable throwable) {
683  super(message, throwable);
684  }
685  }
686 }

Copyright © 2012-2022 Basis Technology. Generated on: Tue Aug 1 2023
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.