Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
101 changes: 73 additions & 28 deletions iped-engine/src/main/java/iped/engine/datasource/UfedXmlReader.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
Expand Down Expand Up @@ -39,6 +38,8 @@
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.LogManager;
Expand Down Expand Up @@ -99,6 +100,12 @@ public class UfedXmlReader extends DataSourceReader {
private final Level CONSOLE = Level.getLevel("MSG"); //$NON-NLS-1$

private static final String[] HEADER_STRINGS = { "project id", "extractionType", "sourceExtractions" }; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
private static final byte[] UFDR_PROTECTION_NO_PASSWORD_REPORT_XML_INITIAL_BYTES = new byte[] { 0x2D, 0x06, 0x52, 0x6D };
private static final int UFDR_REPORT_XML_INITIAL_BYTES_TO_READ = 1024;

private static final String UFDR_EXTENSION = "ufdr";
private static final String XML_REPORT_EXTENSION = "xml";


private static final String AVATAR_PATH_META = ExtraProperties.UFED_META_PREFIX + "contactphoto_extracted_path"; //$NON-NLS-1$
private static final String ATTACH_PATH_META = ExtraProperties.UFED_META_PREFIX + "attachment_extracted_path"; //$NON-NLS-1$
Expand Down Expand Up @@ -152,41 +159,38 @@ public UfedXmlReader(ICaseData caseData, File output, boolean listOnly) {
@Override
public boolean isSupported(File datasource) {

if (datasource.getName().toLowerCase().endsWith(".ufdr")) {
if (FilenameUtils.isExtension(datasource.getName(), UFDR_EXTENSION)) {
return true;
}

// supports any folder with valid XML report inside
InputStream xmlReport = lookUpXmlReportInputStream(datasource);
IOUtil.closeQuietly(xmlReport);

if (xmlReport != null)
return true;

return false;
return xmlReport != null;
}

private InputStream getXmlInputStream(File file) {
if (file.getName().toLowerCase().endsWith(".xml")) { //$NON-NLS-1$
try (InputStreamReader reader = new InputStreamReader(new FileInputStream(file), "UTF-8")) { //$NON-NLS-1$
char[] cbuf = new char[1024];
int off = 0, i = 0;
while (off < cbuf.length && (i = reader.read(cbuf, off, cbuf.length - off)) != -1)
off += i;
String header = new String(cbuf, 0, off);
for (String str : HEADER_STRINGS)
if (!header.contains(str))
return null;
if (FilenameUtils.isExtension(file.getName(), XML_REPORT_EXTENSION)) {
try (InputStream fis = new FileInputStream(file)) {
byte[] initialBytes = fis.readNBytes(UFDR_REPORT_XML_INITIAL_BYTES_TO_READ);
if (!containsHeaderStrings(initialBytes)) {
return null;
}

return new FileInputStream(file);

} catch (IOException e) {
throw new RuntimeException(e);
}
} else if (file.getName().toLowerCase().endsWith(".ufdr")) {
} else if (FilenameUtils.isExtension(file.getName(), UFDR_EXTENSION)) {
try {
ufdrFile = file;
String xml = "report.xml";
if (!getUISF().entryExists(xml)) {
xml = "Report.xml";
if (!getUISF().entryExists(xml)) {
return null;
}
}
return getUISF().getSeekableInputStream(xml);

Expand All @@ -197,6 +201,21 @@ private InputStream getXmlInputStream(File file) {
return null;
}

private boolean isReportXmlProtected(byte[] initialBytes) {
int len = UFDR_PROTECTION_NO_PASSWORD_REPORT_XML_INITIAL_BYTES.length;
return Arrays.equals(initialBytes, 0, len, UFDR_PROTECTION_NO_PASSWORD_REPORT_XML_INITIAL_BYTES, 0, len);
}

private boolean containsHeaderStrings(byte[] initialBytes) {
String header = new String(initialBytes, StandardCharsets.UTF_8);
for (String str : HEADER_STRINGS)
if (header.contains(str))
return true;

return false;

}

private boolean entryExists(String entryPath) {
if (ufdrFile != null) {
return getUISF().entryExists(entryPath);
Expand Down Expand Up @@ -233,16 +252,13 @@ private FileInputStreamFactory getFISF() {
private InputStream lookUpXmlReportInputStream(File root) {
if (root.isFile())
return getXmlInputStream(root);
File[] files = root.listFiles();
if (files != null) {
for (File file : files) {
if (file.getName().toLowerCase().endsWith(".xml")) {
InputStream is = getXmlInputStream(file);
if (is != null)
return is;
}
}

for (File file : FileUtils.listFiles(root, new String[]{XML_REPORT_EXTENSION}, false)) {
InputStream is = getXmlInputStream(file);
if (is != null)
return is;
}

return null;
}

Expand All @@ -261,6 +277,8 @@ public void read(File root, Item parent) throws Exception {
try {
xmlStream = lookUpXmlReportInputStream(root);

validateXmlStream(xmlStream);

configureParsers();

SAXParserFactory spf = SAXParserFactory.newInstance();
Expand All @@ -275,6 +293,33 @@ public void read(File root, Item parent) throws Exception {
}
}

private void validateXmlStream(InputStream xmlStream) throws IOException {
if (xmlStream == null) {
if (root.isFile()) {
throw new RuntimeException("Invalid UFDR file: XML report not found. File: " + root.getAbsolutePath());
} else {
throw new RuntimeException("Invalid UFDR folder: No XML report has been found. Folder: " + root.getAbsolutePath());
}
}

if (root.isFile()) {
xmlStream.mark(0);
byte[] initialBytes = xmlStream.readNBytes(UFDR_REPORT_XML_INITIAL_BYTES_TO_READ);

if (isReportXmlProtected(initialBytes)) {
throw new RuntimeException(
"Unsupported UFDR file: protection is enabled. Generate the UFDR file again with protection disabled. File: "
+ root.getAbsolutePath());
}

if (!containsHeaderStrings(initialBytes)) {
throw new RuntimeException("Invalid UFDR file: XML report is not valid. "
+ "Protection may be enabled. Generate the UFDR file again with protection disabled. File: " + root.getAbsolutePath());
}
xmlStream.reset();
}
}

private void configureParsers() {
configureParsers(false);
}
Expand Down Expand Up @@ -317,7 +362,7 @@ private void addRootItem(IItem parent) throws InterruptedException {
rootItem.setDataSource(evidenceSource);
rootItem.setIdInDataSource("");
rootItem.setHasChildren(true);
if (root.getName().endsWith(".ufdr")) {
if (FilenameUtils.isExtension(root.getName(), UFDR_EXTENSION)) {
rootItem.setLength(root.length());
rootItem.setSumVolume(false);
}
Expand Down