Autopsy  4.5.0
Graphical digital forensics platform for The Sleuth Kit and other tools.
ThunderbirdMboxFileIngestModule.java
Go to the documentation of this file.
1 /*
2  * Autopsy Forensic Browser
3  *
4  * Copyright 2011-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.thunderbirdparser;
20 
21 import java.io.File;
22 import java.io.IOException;
23 import java.util.ArrayList;
24 import java.util.Collection;
25 import java.util.HashSet;
26 import java.util.List;
27 import java.util.Set;
28 import java.util.logging.Level;
29 import java.util.regex.Matcher;
30 import java.util.regex.Pattern;
31 import org.openide.util.NbBundle;
32 import org.openide.util.NbBundle.Messages;
47 import org.sleuthkit.datamodel.AbstractFile;
48 import org.sleuthkit.datamodel.Account;
49 import org.sleuthkit.datamodel.AccountFileInstance;
50 import org.sleuthkit.datamodel.BlackboardArtifact;
51 import org.sleuthkit.datamodel.BlackboardAttribute;
52 import org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE;
53 import org.sleuthkit.datamodel.DerivedFile;
54 import org.sleuthkit.datamodel.Relationship;
55 import org.sleuthkit.datamodel.TskCoreException;
56 import org.sleuthkit.datamodel.TskData;
57 import org.sleuthkit.datamodel.TskDataException;
58 import org.sleuthkit.datamodel.TskException;
59 
65 public final class ThunderbirdMboxFileIngestModule implements FileIngestModule {
66 
67  private static final Logger logger = Logger.getLogger(ThunderbirdMboxFileIngestModule.class.getName());
72 
74  }
75 
76  @Override
77  public void startUp(IngestJobContext context) throws IngestModuleException {
78  this.context = context;
79  fileManager = Case.getCurrentCase().getServices().getFileManager();
80  }
81 
82  @Override
83  public ProcessResult process(AbstractFile abstractFile) {
84 
85  blackboard = Case.getCurrentCase().getServices().getBlackboard();
86 
87  // skip known
88  if (abstractFile.getKnown().equals(TskData.FileKnown.KNOWN)) {
89  return ProcessResult.OK;
90  }
91 
92  //skip unalloc
93  if ((abstractFile.getType().equals(TskData.TSK_DB_FILES_TYPE_ENUM.UNALLOC_BLOCKS)) ||
94  (abstractFile.getType().equals(TskData.TSK_DB_FILES_TYPE_ENUM.SLACK))) {
95  return ProcessResult.OK;
96  }
97 
98  if ((abstractFile.isFile() == false)) {
99  return ProcessResult.OK;
100  }
101 
102  // check its signature
103  boolean isMbox = false;
104  try {
105  byte[] t = new byte[64];
106  if (abstractFile.getSize() > 64) {
107  int byteRead = abstractFile.read(t, 0, 64);
108  if (byteRead > 0) {
109  isMbox = MboxParser.isValidMimeTypeMbox(t);
110  }
111  }
112  } catch (TskException ex) {
113  logger.log(Level.WARNING, null, ex);
114  }
115 
116  if (isMbox) {
117  return processMBox(abstractFile);
118  }
119 
120  if (PstParser.isPstFile(abstractFile)) {
121  return processPst(abstractFile);
122  }
123 
124  return ProcessResult.OK;
125  }
126 
134  @Messages({"ThunderbirdMboxFileIngestModule.processPst.indexError.message=Failed to index encryption detected artifact for keyword search."})
135  private ProcessResult processPst(AbstractFile abstractFile) {
136  String fileName = getTempPath() + File.separator + abstractFile.getName()
137  + "-" + String.valueOf(abstractFile.getId());
138  File file = new File(fileName);
139 
140  long freeSpace = services.getFreeDiskSpace();
141  if ((freeSpace != IngestMonitor.DISK_FREE_SPACE_UNKNOWN) && (abstractFile.getSize() >= freeSpace)) {
142  logger.log(Level.WARNING, "Not enough disk space to write file to disk."); //NON-NLS
144  NbBundle.getMessage(this.getClass(),
145  "ThunderbirdMboxFileIngestModule.processPst.errMsg.outOfDiskSpace",
146  abstractFile.getName()));
147  services.postMessage(msg);
148  return ProcessResult.OK;
149  }
150 
151  try {
152  ContentUtils.writeToFile(abstractFile, file, context::fileIngestIsCancelled);
153  } catch (IOException ex) {
154  logger.log(Level.WARNING, "Failed writing pst file to disk.", ex); //NON-NLS
155  return ProcessResult.OK;
156  }
157 
158  PstParser parser = new PstParser(services);
159  PstParser.ParseResult result = parser.parse(file, abstractFile.getId());
160 
161  if (result == PstParser.ParseResult.OK) {
162  // parse success: Process email and add artifacts
163  processEmails(parser.getResults(), abstractFile);
164  } else if (result == PstParser.ParseResult.ENCRYPT) {
165  // encrypted pst: Add encrypted file artifact
166  try {
167  BlackboardArtifact artifact = abstractFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_ENCRYPTION_DETECTED);
168  artifact.addAttribute(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_NAME, EmailParserModuleFactory.getModuleName(), NbBundle.getMessage(this.getClass(), "ThunderbirdMboxFileIngestModule.encryptionFileLevel")));
169 
170  try {
171  // index the artifact for keyword search
172  blackboard.indexArtifact(artifact);
173  } catch (Blackboard.BlackboardException ex) {
174  MessageNotifyUtil.Notify.error(Bundle.ThunderbirdMboxFileIngestModule_processPst_indexError_message(), artifact.getDisplayName());
175  logger.log(Level.SEVERE, "Unable to index blackboard artifact " + artifact.getArtifactID(), ex); //NON-NLS
176  }
177 
178  services.fireModuleDataEvent(new ModuleDataEvent(EmailParserModuleFactory.getModuleName(), BlackboardArtifact.ARTIFACT_TYPE.TSK_ENCRYPTION_DETECTED));
179  } catch (TskCoreException ex) {
180  logger.log(Level.INFO, "Failed to add encryption attribute to file: {0}", abstractFile.getName()); //NON-NLS
181  }
182  } else {
183  // parsing error: log message
184  postErrorMessage(
185  NbBundle.getMessage(this.getClass(), "ThunderbirdMboxFileIngestModule.processPst.errProcFile.msg",
186  abstractFile.getName()),
187  NbBundle.getMessage(this.getClass(),
188  "ThunderbirdMboxFileIngestModule.processPst.errProcFile.details"));
189  logger.log(Level.INFO, "PSTParser failed to parse {0}", abstractFile.getName()); //NON-NLS
190  return ProcessResult.ERROR;
191  }
192 
193  if (file.delete() == false) {
194  logger.log(Level.INFO, "Failed to delete temp file: {0}", file.getName()); //NON-NLS
195  }
196 
197  String errors = parser.getErrors();
198  if (errors.isEmpty() == false) {
199  postErrorMessage(
200  NbBundle.getMessage(this.getClass(), "ThunderbirdMboxFileIngestModule.processPst.errProcFile.msg2",
201  abstractFile.getName()), errors);
202  }
203 
204  return ProcessResult.OK;
205  }
206 
214  private ProcessResult processMBox(AbstractFile abstractFile) {
215  String mboxFileName = abstractFile.getName();
216  String mboxParentDir = abstractFile.getParentPath();
217  // use the local path to determine the e-mail folder structure
218  String emailFolder = "";
219  // email folder is everything after "Mail" or ImapMail
220  if (mboxParentDir.contains("/Mail/")) { //NON-NLS
221  emailFolder = mboxParentDir.substring(mboxParentDir.indexOf("/Mail/") + 5); //NON-NLS
222  } else if (mboxParentDir.contains("/ImapMail/")) { //NON-NLS
223  emailFolder = mboxParentDir.substring(mboxParentDir.indexOf("/ImapMail/") + 9); //NON-NLS
224  }
225  emailFolder = emailFolder + mboxFileName;
226  emailFolder = emailFolder.replaceAll(".sbd", ""); //NON-NLS
227 
228  String fileName = getTempPath() + File.separator + abstractFile.getName()
229  + "-" + String.valueOf(abstractFile.getId());
230  File file = new File(fileName);
231 
232  long freeSpace = services.getFreeDiskSpace();
233  if ((freeSpace != IngestMonitor.DISK_FREE_SPACE_UNKNOWN) && (abstractFile.getSize() >= freeSpace)) {
234  logger.log(Level.WARNING, "Not enough disk space to write file to disk."); //NON-NLS
235  postErrorMessage(
236  NbBundle.getMessage(this.getClass(), "ThunderbirdMboxFileIngestModule.processMBox.errProcFile.msg",
237  abstractFile.getName()),
238  NbBundle.getMessage(this.getClass(),
239  "ThunderbirdMboxFileIngestModule.processMBox.errProfFile.details"));
240  return ProcessResult.OK;
241  }
242 
243  try {
244  ContentUtils.writeToFile(abstractFile, file, context::fileIngestIsCancelled);
245  } catch (IOException ex) {
246  logger.log(Level.WARNING, "Failed writing mbox file to disk.", ex); //NON-NLS
247  return ProcessResult.OK;
248  }
249 
250  MboxParser parser = new MboxParser(services, emailFolder);
251  List<EmailMessage> emails = parser.parse(file, abstractFile.getId());
252  processEmails(emails, abstractFile);
253 
254  if (file.delete() == false) {
255  logger.log(Level.INFO, "Failed to delete temp file: {0}", file.getName()); //NON-NLS
256  }
257 
258  String errors = parser.getErrors();
259  if (errors.isEmpty() == false) {
260  postErrorMessage(
261  NbBundle.getMessage(this.getClass(), "ThunderbirdMboxFileIngestModule.processMBox.errProcFile.msg2",
262  abstractFile.getName()), errors);
263  }
264 
265  return ProcessResult.OK;
266  }
267 
273  public static String getTempPath() {
274  String tmpDir = Case.getCurrentCase().getTempDirectory() + File.separator
275  + "EmailParser"; //NON-NLS
276  File dir = new File(tmpDir);
277  if (dir.exists() == false) {
278  dir.mkdirs();
279  }
280  return tmpDir;
281  }
282 
283  public static String getModuleOutputPath() {
284  String outDir = Case.getCurrentCase().getModuleDirectory() + File.separator
285  + EmailParserModuleFactory.getModuleName();
286  File dir = new File(outDir);
287  if (dir.exists() == false) {
288  dir.mkdirs();
289  }
290  return outDir;
291  }
292 
293  public static String getRelModuleOutputPath() {
294  return Case.getCurrentCase().getModuleOutputDirectoryRelativePath() + File.separator
295  + EmailParserModuleFactory.getModuleName();
296  }
297 
305  private void processEmails(List<EmailMessage> emails, AbstractFile abstractFile) {
306  List<AbstractFile> derivedFiles = new ArrayList<>();
307 
308 
309 
310  for (EmailMessage email : emails) {
311  BlackboardArtifact msgArtifact = addArtifact(email, abstractFile);
312 
313  if ((msgArtifact != null) && (email.hasAttachment())) {
314  derivedFiles.addAll(handleAttachments(email.getAttachments(), abstractFile, msgArtifact ));
315  }
316  }
317 
318  if (derivedFiles.isEmpty() == false) {
319  for (AbstractFile derived : derivedFiles) {
320  services.fireModuleContentEvent(new ModuleContentEvent(derived));
321  }
322  }
323  context.addFilesToJob(derivedFiles);
324  services.fireModuleDataEvent(new ModuleDataEvent(EmailParserModuleFactory.getModuleName(), BlackboardArtifact.ARTIFACT_TYPE.TSK_EMAIL_MSG));
325  }
326 
337  private List<AbstractFile> handleAttachments(List<EmailMessage.Attachment> attachments, AbstractFile abstractFile, BlackboardArtifact messageArtifact) {
338  List<AbstractFile> files = new ArrayList<>();
339  for (EmailMessage.Attachment attach : attachments) {
340  String filename = attach.getName();
341  long crTime = attach.getCrTime();
342  long mTime = attach.getmTime();
343  long aTime = attach.getaTime();
344  long cTime = attach.getcTime();
345  String relPath = attach.getLocalPath();
346  long size = attach.getSize();
347  TskData.EncodingType encodingType = attach.getEncodingType();
348 
349  try {
350  DerivedFile df = fileManager.addDerivedFile(filename, relPath,
351  size, cTime, crTime, aTime, mTime, true, messageArtifact, "",
352  EmailParserModuleFactory.getModuleName(), EmailParserModuleFactory.getModuleVersion(), "", encodingType);
353  files.add(df);
354  } catch (TskCoreException ex) {
355  postErrorMessage(
356  NbBundle.getMessage(this.getClass(), "ThunderbirdMboxFileIngestModule.handleAttch.errMsg",
357  abstractFile.getName()),
358  NbBundle.getMessage(this.getClass(),
359  "ThunderbirdMboxFileIngestModule.handleAttch.errMsg.details", filename));
360  logger.log(Level.INFO, "", ex);
361  }
362  }
363  return files;
364  }
365 
373  private Set<String> findEmailAddresess(String input) {
374  Pattern p = Pattern.compile("\\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\\.[A-Z]{2,4}\\b",
375  Pattern.CASE_INSENSITIVE);
376  Matcher m = p.matcher(input);
377  Set<String> emailAddresses = new HashSet<String>();
378  while (m.find()) {
379  emailAddresses.add( m.group());
380  }
381  return emailAddresses;
382  }
383 
390  @Messages({"ThunderbirdMboxFileIngestModule.addArtifact.indexError.message=Failed to index email message detected artifact for keyword search."})
391  private BlackboardArtifact addArtifact(EmailMessage email, AbstractFile abstractFile) {
392  BlackboardArtifact bbart = null;
393  List<BlackboardAttribute> bbattributes = new ArrayList<>();
394  String to = email.getRecipients();
395  String cc = email.getCc();
396  String bcc = email.getBcc();
397  String from = email.getSender();
398  long dateL = email.getSentDate();
399  String headers = email.getHeaders();
400  String body = email.getTextBody();
401  String bodyHTML = email.getHtmlBody();
402  String rtf = email.getRtfBody();
403  String subject = email.getSubject();
404  long id = email.getId();
405  String localPath = email.getLocalPath();
406 
407  List<String> senderAddressList = new ArrayList<>();
408  String senderAddress;
409  senderAddressList.addAll(findEmailAddresess(from));
410 
411  AccountFileInstance senderAccountInstance = null;
412  if (senderAddressList.size() == 1) {
413  senderAddress = senderAddressList.get(0);
414  try {
415  senderAccountInstance = Case.getCurrentCase().getSleuthkitCase().getCommunicationsManager().createAccountFileInstance(Account.Type.EMAIL, senderAddress, EmailParserModuleFactory.getModuleName(), abstractFile);
416  }
417  catch(TskCoreException ex) {
418  logger.log(Level.WARNING, "Failed to create account for email address " + senderAddress, ex); //NON-NLS
419  }
420  }
421  else {
422  logger.log(Level.WARNING, "Failed to find sender address, from = "+ from); //NON-NLS
423  }
424 
425  List<String> recipientAddresses = new ArrayList<>();
426  recipientAddresses.addAll(findEmailAddresess(to));
427  recipientAddresses.addAll(findEmailAddresess(cc));
428  recipientAddresses.addAll(findEmailAddresess(bcc));
429 
430  List<AccountFileInstance> recipientAccountInstances = new ArrayList<>();
431  recipientAddresses.forEach((addr) -> {
432  try {
433  AccountFileInstance recipientAccountInstance =
434  Case.getCurrentCase().getSleuthkitCase().getCommunicationsManager().createAccountFileInstance(Account.Type.EMAIL, addr,
435  EmailParserModuleFactory.getModuleName(), abstractFile);
436  recipientAccountInstances.add(recipientAccountInstance);
437  }
438  catch(TskCoreException ex) {
439  logger.log(Level.WARNING, "Failed to create account for email address " + addr, ex); //NON-NLS
440  }
441  });
442 
443  addEmailAttribute(headers, ATTRIBUTE_TYPE.TSK_HEADERS, bbattributes);
444  addEmailAttribute(from, ATTRIBUTE_TYPE.TSK_EMAIL_FROM, bbattributes);
445  addEmailAttribute(to, ATTRIBUTE_TYPE.TSK_EMAIL_TO, bbattributes);
446  addEmailAttribute(subject, ATTRIBUTE_TYPE.TSK_SUBJECT, bbattributes);
447 
448  addEmailAttribute(dateL, ATTRIBUTE_TYPE.TSK_DATETIME_RCVD, bbattributes);
449  addEmailAttribute(dateL, ATTRIBUTE_TYPE.TSK_DATETIME_SENT, bbattributes);
450 
451  addEmailAttribute(body, ATTRIBUTE_TYPE.TSK_EMAIL_CONTENT_PLAIN, bbattributes);
452 
453  addEmailAttribute(((id < 0L) ? NbBundle.getMessage(this.getClass(), "ThunderbirdMboxFileIngestModule.notAvail") : String.valueOf(id)),
454  ATTRIBUTE_TYPE.TSK_MSG_ID, bbattributes);
455 
456  addEmailAttribute(((localPath.isEmpty() == false) ? localPath : "/foo/bar"),
457  ATTRIBUTE_TYPE.TSK_PATH, bbattributes);
458 
459  addEmailAttribute(cc, ATTRIBUTE_TYPE.TSK_EMAIL_CC, bbattributes);
460  addEmailAttribute(bodyHTML, ATTRIBUTE_TYPE.TSK_EMAIL_CONTENT_HTML, bbattributes);
461  addEmailAttribute(rtf, ATTRIBUTE_TYPE.TSK_EMAIL_CONTENT_RTF, bbattributes);
462 
463 
464  try {
465 
466  bbart = abstractFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_EMAIL_MSG);
467  bbart.addAttributes(bbattributes);
468 
469  // Add account relationships
470  Case.getCurrentCase().getSleuthkitCase().getCommunicationsManager().addRelationships(senderAccountInstance, recipientAccountInstances, bbart,Relationship.Type.MESSAGE, dateL);
471 
472  try {
473  // index the artifact for keyword search
474  blackboard.indexArtifact(bbart);
475  } catch (Blackboard.BlackboardException ex) {
476  logger.log(Level.SEVERE, "Unable to index blackboard artifact " + bbart.getArtifactID(), ex); //NON-NLS
477  MessageNotifyUtil.Notify.error(Bundle.ThunderbirdMboxFileIngestModule_addArtifact_indexError_message(), bbart.getDisplayName());
478  }
479  } catch (TskCoreException | TskDataException ex) {
480  logger.log(Level.WARNING, null, ex);
481  }
482 
483  return bbart;
484  }
485 
486  private void addEmailAttribute(String stringVal, ATTRIBUTE_TYPE attrType, Collection<BlackboardAttribute> bbattributes) {
487  if (stringVal.isEmpty() == false) {
488  bbattributes.add(new BlackboardAttribute(attrType, EmailParserModuleFactory.getModuleName(), stringVal));
489  }
490  }
491  private void addEmailAttribute(long longVal, ATTRIBUTE_TYPE attrType, Collection<BlackboardAttribute> bbattributes) {
492  if (longVal > 0) {
493  bbattributes.add(new BlackboardAttribute(attrType, EmailParserModuleFactory.getModuleName(), longVal));
494  }
495  }
496 
497  void postErrorMessage(String subj, String details) {
498  IngestMessage ingestMessage = IngestMessage.createErrorMessage(EmailParserModuleFactory.getModuleVersion(), subj, details);
499  services.postMessage(ingestMessage);
500  }
501 
502  IngestServices getServices() {
503  return services;
504  }
505 
506  @Override
507  public void shutDown() {
508  // nothing to shut down
509  }
510 }
static IngestMessage createErrorMessage(String source, String subject, String detailsHtml)
void addEmailAttribute(String stringVal, ATTRIBUTE_TYPE attrType, Collection< BlackboardAttribute > bbattributes)
BlackboardArtifact addArtifact(EmailMessage email, AbstractFile abstractFile)
static< T > long writeToFile(Content content, java.io.File outputFile, ProgressHandle progress, Future< T > worker, boolean source)
synchronized DerivedFile addDerivedFile(String fileName, String localPath, long size, long ctime, long crtime, long atime, long mtime, boolean isFile, Content parentObj, String rederiveDetails, String toolName, String toolVersion, String otherDetails, TskData.EncodingType encodingType)
void addEmailAttribute(long longVal, ATTRIBUTE_TYPE attrType, Collection< BlackboardAttribute > bbattributes)
void processEmails(List< EmailMessage > emails, AbstractFile abstractFile)
void addFilesToJob(List< AbstractFile > files)
void postMessage(final IngestMessage message)
void fireModuleDataEvent(ModuleDataEvent moduleDataEvent)
void fireModuleContentEvent(ModuleContentEvent moduleContentEvent)
static void error(String title, String message)
synchronized void indexArtifact(BlackboardArtifact artifact)
Definition: Blackboard.java:59
synchronized static Logger getLogger(String name)
Definition: Logger.java:124
List< AbstractFile > handleAttachments(List< EmailMessage.Attachment > attachments, AbstractFile abstractFile, BlackboardArtifact messageArtifact)
static synchronized IngestServices getInstance()

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