Autopsy 4.22.1
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 */
19package org.sleuthkit.autopsy.modules.hashdatabase;
20
21import java.io.File;
22import java.io.FileInputStream;
23import java.io.FileOutputStream;
24import java.io.IOException;
25import java.io.Serializable;
26import java.util.ArrayList;
27import java.util.List;
28import java.util.Objects;
29import java.util.logging.Level;
30import javax.swing.JOptionPane;
31import org.apache.commons.io.FileUtils;
32import org.openide.util.NbBundle;
33import org.openide.util.io.NbObjectInputStream;
34import org.openide.util.io.NbObjectOutputStream;
35import org.openide.windows.WindowManager;
36import org.sleuthkit.autopsy.core.RuntimeProperties;
37import org.sleuthkit.autopsy.coreutils.Logger;
38import org.sleuthkit.autopsy.coreutils.XMLUtil;
39import org.sleuthkit.autopsy.modules.hashdatabase.HashDbManager.CentralRepoHashSet;
40import org.sleuthkit.autopsy.modules.hashdatabase.HashDbManager.SleuthkitHashSet;
41import org.sleuthkit.datamodel.TskCoreException;
42import org.w3c.dom.Document;
43import org.w3c.dom.Element;
44import org.w3c.dom.NodeList;
45import org.sleuthkit.autopsy.modules.hashdatabase.HashDbManager.HashDb;
46import org.sleuthkit.autopsy.modules.hashdatabase.infrastructure.HashConfigPaths;
47
51final 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
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-2024 Sleuth Kit Labs. Generated on:
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.