Autopsy  3.1
Graphical digital forensics platform for The Sleuth Kit and other tools.
InterestingItemDefsManager.java
Go to the documentation of this file.
1 /*
2  * Autopsy Forensic Browser
3  *
4  * Copyright 2014 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.interestingitems;
20 
21 import java.io.File;
22 import java.util.ArrayList;
23 import java.util.Arrays;
24 import java.util.Collections;
25 import java.util.HashMap;
26 import java.util.List;
27 import java.util.Map;
28 import java.util.Observable;
29 import java.util.logging.Level;
30 import java.util.regex.Pattern;
31 import java.util.regex.PatternSyntaxException;
32 import javax.xml.parsers.DocumentBuilder;
33 import javax.xml.parsers.DocumentBuilderFactory;
34 import javax.xml.parsers.ParserConfigurationException;
38 import org.w3c.dom.Document;
39 import org.w3c.dom.Element;
40 import org.w3c.dom.NodeList;
41 
48 final class InterestingItemDefsManager extends Observable {
49 
50  private static final List<String> ILLEGAL_FILE_NAME_CHARS = Collections.unmodifiableList(new ArrayList<>(Arrays.asList("\\", "/", ":", "*", "?", "\"", "<", ">")));
51  private static final List<String> ILLEGAL_FILE_PATH_CHARS = Collections.unmodifiableList(new ArrayList<>(Arrays.asList("\\", ":", "*", "?", "\"", "<", ">")));
52  private static final String INTERESTING_FILES_SET_DEFS_FILE_NAME = "InterestingFilesSetDefs.xml"; //NON-NLS
53  private static final String DEFAULT_FILE_SET_DEFS_PATH = PlatformUtil.getUserConfigDirectory() + File.separator + INTERESTING_FILES_SET_DEFS_FILE_NAME;
54  private static InterestingItemDefsManager instance;
55 
59  synchronized static InterestingItemDefsManager getInstance() {
60  if (instance == null) {
61  instance = new InterestingItemDefsManager();
62  }
63  return instance;
64  }
65 
71  static List<String> getIllegalFileNameChars() {
72  return InterestingItemDefsManager.ILLEGAL_FILE_NAME_CHARS;
73  }
74 
80  static List<String> getIllegalFilePathChars() {
81  return InterestingItemDefsManager.ILLEGAL_FILE_PATH_CHARS;
82  }
83 
90  synchronized Map<String, FilesSet> getInterestingFilesSets() {
91  return FilesSetXML.readDefinitionsFile(DEFAULT_FILE_SET_DEFS_PATH);
92  }
93 
101  synchronized void setInterestingFilesSets(Map<String, FilesSet> filesSets) {
102  FilesSetXML.writeDefinitionsFile(DEFAULT_FILE_SET_DEFS_PATH, filesSets);
103  this.setChanged();
104  this.notifyObservers();
105  }
106 
111  private final static class FilesSetXML {
112 
113  private static final Logger logger = Logger.getLogger(FilesSetXML.class.getName());
114  private static final String XML_ENCODING = "UTF-8"; //NON-NLS
115  private static final List<String> illegalFileNameChars = InterestingItemDefsManager.getIllegalFileNameChars();
116 
117  // The following tags and attributes are identical to those used in the
118  // TSK Framework interesting files set definitions file schema.
119  private static final String FILE_SETS_ROOT_TAG = "INTERESTING_FILE_SETS"; //NON-NLS
120  private static final String FILE_SET_TAG = "INTERESTING_FILE_SET"; //NON-NLS
121  private static final String NAME_RULE_TAG = "NAME"; //NON-NLS
122  private static final String EXTENSION_RULE_TAG = "EXTENSION"; //NON-NLS
123  private static final String NAME_ATTR = "name"; //NON-NLS
124  private static final String RULE_UUID_ATTR = "ruleUUID"; //NON-NLS
125  private static final String DESC_ATTR = "description"; //NON-NLS
126  private static final String IGNORE_KNOWN_FILES_ATTR = "ignoreKnown"; //NON-NLS
127  private static final String TYPE_FILTER_ATTR = "typeFilter"; //NON-NLS
128  private static final String PATH_FILTER_ATTR = "pathFilter"; //NON-NLS
129  private static final String TYPE_FILTER_VALUE_FILES = "file"; //NON-NLS
130  private static final String TYPE_FILTER_VALUE_DIRS = "dir"; //NON-NLS
131 
132  // The following tags and attributes are currently specific to the
133  // Autopsy implementation of interesting files set definitions. Autopsy
134  // definitions that use these will not be able to be used by TSK
135  // Framework. However, Autopsy can accept TSK Framework definitions:
136  //
137  // 1. Rules do not have names in the TSK Framework schema, but rules do
138  // have names in the Autopsy schema. Names will be synthesized as needed
139  // to allow Autopsy to use TSK Framework interesting files set
140  // definitions.
141  // 2. The TSK Framework has an interesting files module that supports
142  // simple globbing with "*" characters. Name rules and path filters with
143  // "*" characters will be converted to regexes to allow Autopsy to use
144  // TSK Framework interesting files set definitions.
145  // 3. Type filters are required by Autopsy, but not by TSK Frmaework.
146  // Missing type filters will defualt to "files" filters.
147  private static final String REGEX_ATTR = "regex"; //NON-NLS
148  private static final String PATH_REGEX_ATTR = "pathRegex"; //NON-NLS
149  private static final String TYPE_FILTER_VALUE_FILES_AND_DIRS = "files_and_dirs"; //NON-NLS
150  private static final String UNNAMED_LEGACY_RULE_PREFIX = "Unnamed Rule "; // NON-NLS
151  private static int unnamedLegacyRuleCounter;
152 
159  // Note: This method takes a file path to support the possibility of
160  // multiple intersting files set definition files, e.g., one for
161  // definitions that ship with Autopsy and one for user definitions.
162  static Map<String, FilesSet> readDefinitionsFile(String filePath) {
163  Map<String, FilesSet> filesSets = new HashMap<>();
164 
165  // Check if the file exists.
166  File defsFile = new File(filePath);
167  if (!defsFile.exists()) {
168  return filesSets;
169  }
170 
171  // Check if the file can be read.
172  if (!defsFile.canRead()) {
173  logger.log(Level.SEVERE, "Interesting file sets definition file at {0} exists, but cannot be read", filePath); // NON-NLS
174  return filesSets;
175  }
176 
177  // Parse the XML in the file.
178  Document doc = XMLUtil.loadDoc(FilesSetXML.class, filePath);
179  if (doc == null) {
180  logger.log(Level.SEVERE, "Failed to parse interesting file sets definition file at {0}", filePath); // NON-NLS
181  return filesSets;
182  }
183 
184  // Get the root element.
185  Element root = doc.getDocumentElement();
186  if (root == null) {
187  logger.log(Level.SEVERE, "Failed to get root {0} element tag of interesting file sets definition file at {1}", new Object[]{FilesSetXML.FILE_SETS_ROOT_TAG, filePath}); // NON-NLS
188  return filesSets;
189  }
190 
191  // Read in the files set definitions.
192  NodeList setElems = root.getElementsByTagName(FILE_SET_TAG);
193  for (int i = 0; i < setElems.getLength(); ++i) {
194  readFilesSet((Element) setElems.item(i), filesSets, filePath);
195  }
196 
197  return filesSets;
198  }
199 
207  private static void readFilesSet(Element setElem, Map<String, FilesSet> filesSets, String filePath) {
208  // The file set must have a unique name.
209  String setName = setElem.getAttribute(FilesSetXML.NAME_ATTR);
210  if (setName.isEmpty()) {
211  logger.log(Level.SEVERE, "Found {0} element without required {1} attribute, ignoring malformed file set definition in interesting file sets definition file at {2}", new Object[]{FilesSetXML.FILE_SET_TAG, FilesSetXML.NAME_ATTR, filePath}); // NON-NLS
212  return;
213  }
214  if (filesSets.containsKey(setName)) {
215  logger.log(Level.SEVERE, "Found duplicate definition of set named {0} in interesting file sets definition file at {1}, discarding duplicate set", new Object[]{setName, filePath}); // NON-NLS
216  return;
217  }
218 
219  // The file set may have a description. The empty string is o.k.
220  String description = setElem.getAttribute(FilesSetXML.DESC_ATTR);
221 
222  // The file set may or may not ignore known files. The default behavior
223  // is to not ignore them.
224  String ignoreKnown = setElem.getAttribute(FilesSetXML.IGNORE_KNOWN_FILES_ATTR);
225  boolean ignoreKnownFiles = false;
226  if (!ignoreKnown.isEmpty()) {
227  ignoreKnownFiles = Boolean.parseBoolean(ignoreKnown);
228  }
229 
230  // Read file name set membership rules, if any.
232  Map<String, FilesSet.Rule> rules = new HashMap<>();
233  NodeList nameRuleElems = setElem.getElementsByTagName(FilesSetXML.NAME_RULE_TAG);
234  for (int j = 0; j < nameRuleElems.getLength(); ++j) {
235  Element elem = (Element) nameRuleElems.item(j);
236  FilesSet.Rule rule = FilesSetXML.readFileNameRule(elem);
237  if (rule != null) {
238  if (!rules.containsKey(rule.getUuid())) {
239  rules.put(rule.getUuid(), rule);
240  } else {
241  logger.log(Level.SEVERE, "Found duplicate rule {0} for set named {1} in interesting file sets definition file at {2}, discarding malformed set", new Object[]{rule.getUuid(), setName, filePath}); // NON-NLS
242  return;
243  }
244  } else {
245  logger.log(Level.SEVERE, "Found malformed rule for set named {0} in interesting file sets definition file at {1}, discarding malformed set", new Object[]{setName, filePath}); // NON-NLS
246  return;
247  }
248  }
249 
250  // Read file extension set membership rules, if any.
251  NodeList extRuleElems = setElem.getElementsByTagName(FilesSetXML.EXTENSION_RULE_TAG);
252  for (int j = 0; j < extRuleElems.getLength(); ++j) {
253  Element elem = (Element) extRuleElems.item(j);
254  FilesSet.Rule rule = FilesSetXML.readFileExtensionRule(elem);
255  if (rule != null) {
256  if (!rules.containsKey(rule.getUuid())) {
257  rules.put(rule.getUuid(), rule);
258  } else {
259  logger.log(Level.SEVERE, "Found duplicate rule {0} for set named {1} in interesting file sets definition file at {2}, discarding malformed set", new Object[]{rule.getUuid(), setName, filePath}); //NOI18N
260  return;
261  }
262  } else {
263  logger.log(Level.SEVERE, "Found malformed rule for set named {0} in interesting file sets definition file at {1}, discarding malformed set", new Object[]{setName, filePath}); //NOI18N
264  return;
265  }
266  }
267 
268  // Make the files set. Note that degenerate sets with no rules are
269  // allowed to facilitate the separation of set definition and rule
270  // definitions. A set without rules is simply the empty set.
271  FilesSet set = new FilesSet(setName, description, ignoreKnownFiles, rules);
272  filesSets.put(set.getName(), set);
273  }
274 
285  private static FilesSet.Rule readFileNameRule(Element elem) {
286  String ruleName = FilesSetXML.readRuleName(elem);
287 
288  // The content of the rule tag is a file name filter. It may be a
289  // regex, or it may be from a TSK Framework rule definition with a
290  // "*" globbing char, or it may be simple text.
291  String content = elem.getTextContent();
292  FilesSet.Rule.FullNameFilter nameFilter;
293  String regex = elem.getAttribute(FilesSetXML.REGEX_ATTR);
294  if ((!regex.isEmpty() && regex.equalsIgnoreCase("true")) || content.contains("*")) { // NON_NLS
295  Pattern pattern = compileRegex(content);
296  if (pattern != null) {
297  nameFilter = new FilesSet.Rule.FullNameFilter(pattern);
298  } else {
299  logger.log(Level.SEVERE, "Error compiling " + FilesSetXML.NAME_RULE_TAG + " regex, ignoring malformed '{0}' rule definition", ruleName); // NON-NLS
300  return null;
301  }
302  } else {
303  for (String illegalChar : illegalFileNameChars) {
304  if (content.contains(illegalChar)) {
305  logger.log(Level.SEVERE, FilesSetXML.NAME_RULE_TAG + " content has illegal chars, ignoring malformed '{0}' rule definition", new Object[]{FilesSetXML.NAME_RULE_TAG, ruleName}); // NON-NLS
306  return null;
307  }
308  }
309  nameFilter = new FilesSet.Rule.FullNameFilter(content);
310  }
311 
312  // Read in the type filter.
313  FilesSet.Rule.MetaTypeFilter metaTypeFilter = FilesSetXML.readMetaTypeFilter(elem);
314  if (metaTypeFilter == null) {
315  // Malformed attribute.
316  return null;
317  }
318 
319  // Read in the optional path filter. Null is o.k., but if the attribute
320  // is there, be sure it is not malformed.
321  FilesSet.Rule.ParentPathFilter pathFilter = null;
322  if (!elem.getAttribute(FilesSetXML.PATH_FILTER_ATTR).isEmpty()
323  || !elem.getAttribute(FilesSetXML.PATH_REGEX_ATTR).isEmpty()) {
324  pathFilter = FilesSetXML.readPathFilter(elem);
325  if (pathFilter == null) {
326  // Malformed attribute.
327  return null;
328  }
329  }
330 
331  return new FilesSet.Rule(ruleName, nameFilter, metaTypeFilter, pathFilter);
332  }
333 
342  private static FilesSet.Rule readFileExtensionRule(Element elem) {
343  String ruleName = FilesSetXML.readRuleName(elem);
344 
345  // The content of the rule tag is a file name extension filter. It may
346  // be a regex, or it may be from a TSK Framework rule definition
347  // with a "*" globbing char.
348  String content = elem.getTextContent();
349  FilesSet.Rule.ExtensionFilter extFilter;
350  String regex = elem.getAttribute(FilesSetXML.REGEX_ATTR);
351  if ((!regex.isEmpty() && regex.equalsIgnoreCase("true")) || content.contains("*")) { // NON_NLS
352  Pattern pattern = compileRegex(content);
353  if (pattern != null) {
354  extFilter = new FilesSet.Rule.ExtensionFilter(pattern);
355  } else {
356  logger.log(Level.SEVERE, "Error compiling " + FilesSetXML.EXTENSION_RULE_TAG + " regex, ignoring malformed {0} rule definition", ruleName); // NON-NLS
357  return null;
358  }
359  } else {
360  for (String illegalChar : illegalFileNameChars) {
361  if (content.contains(illegalChar)) {
362  logger.log(Level.SEVERE, "{0} content has illegal chars, ignoring malformed {1} rule definition", ruleName); // NON-NLS
363  return null;
364  }
365  }
366  extFilter = new FilesSet.Rule.ExtensionFilter(content);
367  }
368 
369  // The rule must have a meta-type filter, unless a TSK Framework
370  // definitions file is being read.
371  FilesSet.Rule.MetaTypeFilter metaTypeFilter = null;
372  if (!elem.getAttribute(FilesSetXML.TYPE_FILTER_ATTR).isEmpty()) {
373  metaTypeFilter = FilesSetXML.readMetaTypeFilter(elem);
374  if (metaTypeFilter == null) {
375  // Malformed attribute.
376  return null;
377  }
378  } else {
379  metaTypeFilter = new FilesSet.Rule.MetaTypeFilter(FilesSet.Rule.MetaTypeFilter.Type.FILES);
380  }
381 
382  // The rule may have a path filter. Null is o.k., but if the attribute
383  // is there, it must not be malformed.
384  FilesSet.Rule.ParentPathFilter pathFilter = null;
385  if (!elem.getAttribute(FilesSetXML.PATH_FILTER_ATTR).isEmpty()
386  || !elem.getAttribute(FilesSetXML.PATH_REGEX_ATTR).isEmpty()) {
387  pathFilter = FilesSetXML.readPathFilter(elem);
388  if (pathFilter == null) {
389  // Malformed attribute.
390  return null;
391  }
392  }
393 
394  return new FilesSet.Rule(ruleName, extFilter, metaTypeFilter, pathFilter);
395  }
396 
403  private static String readRuleName(Element elem) {
404  // The rule must have a name.
405  String ruleName = elem.getAttribute(FilesSetXML.NAME_ATTR);
406  return ruleName;
407  }
408 
415  private static Pattern compileRegex(String regex) {
416  try {
417  return Pattern.compile(regex);
418  } catch (PatternSyntaxException ex) {
419  logger.log(Level.SEVERE, "Error compiling rule regex: " + ex.getMessage(), ex); // NON-NLS
420  return null;
421  }
422  }
423 
431  private static FilesSet.Rule.MetaTypeFilter readMetaTypeFilter(Element ruleElement) {
432  FilesSet.Rule.MetaTypeFilter filter = null;
433  String filterAttribute = ruleElement.getAttribute(FilesSetXML.TYPE_FILTER_ATTR);
434  if (!filterAttribute.isEmpty()) {
435  switch (filterAttribute) {
437  filter = new FilesSet.Rule.MetaTypeFilter(FilesSet.Rule.MetaTypeFilter.Type.FILES);
438  break;
440  filter = new FilesSet.Rule.MetaTypeFilter(FilesSet.Rule.MetaTypeFilter.Type.DIRECTORIES);
441  break;
443  filter = new FilesSet.Rule.MetaTypeFilter(FilesSet.Rule.MetaTypeFilter.Type.FILES_AND_DIRECTORIES);
444  break;
445  default:
446  logger.log(Level.SEVERE, "Found {0} " + FilesSetXML.TYPE_FILTER_ATTR + " attribute with unrecognized value ''{0}'', ignoring malformed rule definition", filterAttribute); // NON-NLS
447  break;
448  }
449  } else {
450  // Accept TSK Framework interesting files set definitions,
451  // default to files.
452  filter = new FilesSet.Rule.MetaTypeFilter(FilesSet.Rule.MetaTypeFilter.Type.FILES);
453  }
454  return filter;
455  }
456 
464  private static FilesSet.Rule.ParentPathFilter readPathFilter(Element ruleElement) {
465  FilesSet.Rule.ParentPathFilter filter = null;
466  String path = ruleElement.getAttribute(FilesSetXML.PATH_FILTER_ATTR);
467  String pathRegex = ruleElement.getAttribute(FilesSetXML.PATH_REGEX_ATTR);
468  if (!pathRegex.isEmpty() && path.isEmpty()) {
469  try {
470  Pattern pattern = Pattern.compile(pathRegex);
471  filter = new FilesSet.Rule.ParentPathFilter(pattern);
472  } catch (PatternSyntaxException ex) {
473  logger.log(Level.SEVERE, "Error compiling " + FilesSetXML.PATH_REGEX_ATTR + " regex, ignoring malformed path filter definition", ex); // NON-NLS
474  }
475  } else if (!path.isEmpty() && pathRegex.isEmpty()) {
476  filter = new FilesSet.Rule.ParentPathFilter(path);
477  }
478  return filter;
479  }
480 
489  // Note: This method takes a file path to support the possibility of
490  // multiple intersting files set definition files, e.g., one for
491  // definitions that ship with Autopsy and one for user definitions.
492  static boolean writeDefinitionsFile(String filePath, Map<String, FilesSet> interestingFilesSets) {
493  DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance();
494  try {
495  // Create the new XML document.
496  DocumentBuilder docBuilder = docBuilderFactory.newDocumentBuilder();
497  Document doc = docBuilder.newDocument();
498  Element rootElement = doc.createElement(FilesSetXML.FILE_SETS_ROOT_TAG);
499  doc.appendChild(rootElement);
500 
501  // Add the interesting files sets to the document.
502  for (FilesSet set : interestingFilesSets.values()) {
503  // Add the files set element and its attributes.
504  Element setElement = doc.createElement(FilesSetXML.FILE_SET_TAG);
505  setElement.setAttribute(FilesSetXML.NAME_ATTR, set.getName());
506  setElement.setAttribute(FilesSetXML.DESC_ATTR, set.getDescription());
507  setElement.setAttribute(FilesSetXML.IGNORE_KNOWN_FILES_ATTR, Boolean.toString(set.ignoresKnownFiles()));
508 
509  // Add the child elements for the set membership rules.
510  for (FilesSet.Rule rule : set.getRules().values()) {
511  // Add a rule element with the appropriate name filter
512  // type tag.
513  FilesSet.Rule.FileNameFilter nameFilter = rule.getFileNameFilter();
514  Element ruleElement;
515  if (nameFilter instanceof FilesSet.Rule.FullNameFilter) {
516  ruleElement = doc.createElement(FilesSetXML.NAME_RULE_TAG);
517  } else {
518  ruleElement = doc.createElement(FilesSetXML.EXTENSION_RULE_TAG);
519  }
520 
521 
522  // Add the rule name attribute.
523  ruleElement.setAttribute(FilesSetXML.NAME_ATTR, rule.getName());
524 
525  // Add the name filter regex attribute
526  ruleElement.setAttribute(FilesSetXML.REGEX_ATTR, Boolean.toString(nameFilter.isRegex()));
527 
528  // Add the type filter attribute.
529  FilesSet.Rule.MetaTypeFilter typeFilter = rule.getMetaTypeFilter();
530  switch (typeFilter.getMetaType()) {
531  case FILES:
532  ruleElement.setAttribute(FilesSetXML.TYPE_FILTER_ATTR, FilesSetXML.TYPE_FILTER_VALUE_FILES);
533  break;
534  case DIRECTORIES:
535  ruleElement.setAttribute(FilesSetXML.TYPE_FILTER_ATTR, FilesSetXML.TYPE_FILTER_VALUE_DIRS);
536  break;
537  default:
538  ruleElement.setAttribute(FilesSetXML.TYPE_FILTER_ATTR, FilesSetXML.TYPE_FILTER_VALUE_FILES_AND_DIRS);
539  break;
540  }
541 
542  // Add the optional path filter.
543  FilesSet.Rule.ParentPathFilter pathFilter = rule.getPathFilter();
544  if (pathFilter != null) {
545  if (pathFilter.isRegex()) {
546  ruleElement.setAttribute(FilesSetXML.PATH_REGEX_ATTR, pathFilter.getTextToMatch());
547  } else {
548  ruleElement.setAttribute(FilesSetXML.PATH_FILTER_ATTR, pathFilter.getTextToMatch());
549  }
550  }
551 
552  // Add the name filter text as the rule element content.
553  ruleElement.setTextContent(nameFilter.getTextToMatch());
554 
555  setElement.appendChild(ruleElement);
556  }
557 
558  rootElement.appendChild(setElement);
559  }
560 
561  // Overwrite the previous definitions file. Note that the utility
562  // method logs an error on failure.
563  return XMLUtil.saveDoc(FilesSetXML.class, filePath, XML_ENCODING, doc);
564 
565  } catch (ParserConfigurationException ex) {
566  logger.log(Level.SEVERE, "Error writing interesting files definition file to " + filePath, ex); // NON-NLS
567  return false;
568  }
569 
570  }
571 
572  }
573 
574 }
static< T > Document loadDoc(Class< T > clazz, String xmlPath)
Definition: XMLUtil.java:221
static void readFilesSet(Element setElem, Map< String, FilesSet > filesSets, String filePath)
static Logger getLogger(String name)
Definition: Logger.java:131

Copyright © 2012-2015 Basis Technology. Generated on: Mon Oct 19 2015
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.