Browse Source

#66115 - Some Password protected XLS files are not read

git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1901996 13f79535-47bb-0310-9956-ffa450edef68
tags/REL_5_2_3
Andreas Beeker 1 year ago
parent
commit
c41176f207

+ 2
- 2
poi-integration/src/test/java/org/apache/poi/stress/ExcInfo.java View File

@@ -90,7 +90,7 @@ public class ExcInfo {
public boolean isValid(String testName, String handler) {
return
!IGNORED_TESTS.equals(tests) &&
(tests == null || tests.contains(testName)) &&
(this.handler == null || this.handler.contains(handler));
(tests == null || (tests.contains(testName) && !tests.contains("!"+testName))) &&
(this.handler == null || (this.handler.contains(handler) && !this.handler.contains("!"+handler)));
}
}

+ 5
- 0
poi/src/main/java/org/apache/poi/hssf/record/RecordInputStream.java View File

@@ -533,4 +533,9 @@ public final class RecordInputStream implements LittleEndianInput {
((InputStream)_dataInput).reset();
_currentDataOffset = _markedDataOffset;
}

@Internal
public boolean isEncrypted() {
return _dataInput instanceof Biff8DecryptingStream && ((Biff8DecryptingStream)_dataInput).isCurrentRecordEncrypted();
}
}

+ 62
- 38
poi/src/main/java/org/apache/poi/hssf/record/WriteAccessRecord.java View File

@@ -17,11 +17,17 @@

package org.apache.poi.hssf.record;

import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Map;
import java.util.function.Supplier;

import org.apache.poi.util.BitField;
import org.apache.poi.util.BitFieldFactory;
import org.apache.poi.util.GenericRecordUtil;
import org.apache.poi.util.IOUtils;
import org.apache.poi.util.LittleEndian;
import org.apache.poi.util.LittleEndianOutput;
import org.apache.poi.util.RecordFormatException;
@@ -36,10 +42,14 @@ import org.apache.poi.util.StringUtil;
public final class WriteAccessRecord extends StandardRecord {
public static final short sid = 0x005C;

private static final BitField UTF16FLAG = BitFieldFactory.getInstance(1);

private static final byte PAD_CHAR = (byte) ' ';
private static final int DATA_SIZE = 112;
private static final int STRING_SIZE = DATA_SIZE - 3;

/** this record is always padded to a constant length */
private static final byte[] PADDING = new byte[DATA_SIZE];
private static final byte[] PADDING = new byte[STRING_SIZE];
static {
Arrays.fill(PADDING, PAD_CHAR);
}
@@ -58,42 +68,57 @@ public final class WriteAccessRecord extends StandardRecord {

public WriteAccessRecord(RecordInputStream in) {
if (in.remaining() > DATA_SIZE) {
throw new RecordFormatException("Expected data size (" + DATA_SIZE + ") but got ("
+ in.remaining() + ")");
throw new RecordFormatException("Expected data size (" + DATA_SIZE + ") but got (" + in.remaining() + ")");
}
// The string is always 112 characters (padded with spaces), therefore

// The string is always 109 characters (padded with spaces), therefore
// this record can not be continued.

int nChars = in.readUShort();
int is16BitFlag = in.readUByte();
if (nChars > DATA_SIZE || (is16BitFlag & 0xFE) != 0) {
// String header looks wrong (probably missing)
// OOO doc says this is optional anyway.
// reconstruct data
byte[] data = new byte[3 + in.remaining()];
LittleEndian.putUShort(data, 0, nChars);
LittleEndian.putByte(data, 2, is16BitFlag);
in.readFully(data, 3, data.length-3);
String rawValue = new String(data, StringUtil.UTF8);
setUsername(rawValue.trim());
return;
}

String rawText;
if ((is16BitFlag & 0x01) == 0x00) {
rawText = StringUtil.readCompressedUnicode(in, nChars);
final byte[] data;
final Charset charset;
final int byteCnt;
if (nChars > STRING_SIZE || (is16BitFlag & 0xFE) != 0) {
// something is wrong - reconstruct data
if (in.isEncrypted()) {
// WPS Office seems to generate files with this record unencrypted (#66115)
// Libre Office/Excel can read those, but Excel will convert those back to encrypted
data = IOUtils.safelyAllocate(in.remaining(), STRING_SIZE);
in.readPlain(data, 0, data.length);
int i = data.length;
// PAD_CHAR is filled for every byte even for UTF16 strings
while (i>0 && data[i-1] == PAD_CHAR) {
i--;
}
byteCnt = i;
// poor mans utf16 detection ...
charset = (data.length > 1 && data[1] == 0) ? StandardCharsets.UTF_16LE : StandardCharsets.ISO_8859_1;
} else {
// String header looks wrong (probably missing)
// OOO doc says this is optional anyway.
byteCnt = 3 + in.remaining();
data = IOUtils.safelyAllocate(byteCnt, DATA_SIZE);
LittleEndian.putUShort(data, 0, nChars);
LittleEndian.putByte(data, 2, is16BitFlag);
in.readFully(data, 3, byteCnt-3);
charset = StandardCharsets.UTF_8;
}
} else {
rawText = StringUtil.readUnicodeLE(in, nChars);
}
field_1_username = rawText.trim();

// consume padding
int padSize = in.remaining();
while (padSize > 0) {
// in some cases this seems to be garbage (non spaces)
in.readUByte();
padSize--;
// the normal case ...
data = IOUtils.safelyAllocate(in.remaining(), STRING_SIZE);
in.readFully(data);
if (UTF16FLAG.isSet(is16BitFlag)) {
byteCnt = Math.min(nChars * 2, data.length);
charset = StandardCharsets.UTF_16LE;
} else {
byteCnt = Math.min(nChars, data.length);
charset = StandardCharsets.ISO_8859_1;
}
}

String rawValue = new String(data, 0, byteCnt, charset);
setUsername(rawValue.trim());
}

/**
@@ -104,9 +129,8 @@ public final class WriteAccessRecord extends StandardRecord {
*/
public void setUsername(String username) {
boolean is16bit = StringUtil.hasMultibyte(username);
int encodedByteCount = 3 + username.length() * (is16bit ? 2 : 1);
int paddingSize = DATA_SIZE - encodedByteCount;
if (paddingSize < 0) {
int encodedByteCount = username.length() * (is16bit ? 2 : 1);
if (encodedByteCount > STRING_SIZE) {
throw new IllegalArgumentException("Name is too long: " + username);
}

@@ -131,14 +155,14 @@ public final class WriteAccessRecord extends StandardRecord {

out.writeShort(username.length());
out.writeByte(is16bit ? 0x01 : 0x00);

byte[] buf = PADDING.clone();
if (is16bit) {
StringUtil.putUnicodeLE(username, out);
StringUtil.putUnicodeLE(username, buf, 0);
} else {
StringUtil.putCompressedUnicode(username, out);
StringUtil.putCompressedUnicode(username, buf, 0);
}
int encodedByteCount = 3 + username.length() * (is16bit ? 2 : 1);
int paddingSize = DATA_SIZE - encodedByteCount;
out.write(PADDING, 0, paddingSize);
out.write(buf);
}

@Override

+ 5
- 0
poi/src/main/java/org/apache/poi/hssf/record/crypto/Biff8DecryptingStream.java View File

@@ -26,6 +26,7 @@ import org.apache.poi.poifs.crypt.ChunkedCipherInputStream;
import org.apache.poi.poifs.crypt.Decryptor;
import org.apache.poi.poifs.crypt.EncryptionInfo;
import org.apache.poi.util.IOUtils;
import org.apache.poi.util.Internal;
import org.apache.poi.util.LittleEndian;
import org.apache.poi.util.LittleEndianConsts;
import org.apache.poi.util.LittleEndianInput;
@@ -208,4 +209,8 @@ public final class Biff8DecryptingStream implements BiffHeaderInput, LittleEndia
ccis.readPlain(b, off, len);
}

@Internal
public boolean isCurrentRecordEncrypted() {
return !shouldSkipEncryptionOnCurrentRecord;
}
}

+ 1
- 0
poi/src/test/java/org/apache/poi/hssf/dev/TestBiffDrawingToXml.java View File

@@ -53,6 +53,7 @@ class TestBiffDrawingToXml extends BaseTestIteratingXLS {
// HSSFWorkbook cannot open it as well
excludes.put("43493.xls", RecordInputStream.LeftoverDataException.class);
excludes.put("44958_1.xls", RecordInputStream.LeftoverDataException.class);
excludes.put("protected_66115.xls", EncryptedDocumentException.class);
return excludes;
}


+ 2
- 0
poi/src/test/java/org/apache/poi/hssf/dev/TestBiffViewer.java View File

@@ -20,6 +20,7 @@ import java.io.File;
import java.io.IOException;
import java.util.Map;

import org.apache.poi.EncryptedDocumentException;
import org.apache.poi.util.RecordFormatException;

class TestBiffViewer extends BaseTestIteratingXLS {
@@ -41,6 +42,7 @@ class TestBiffViewer extends BaseTestIteratingXLS {

excludes.put("61300.xls", IndexOutOfBoundsException.class);
excludes.put("poi-fuzz.xls", RecordFormatException.class);
excludes.put("protected_66115.xls", RecordFormatException.class);

return excludes;
}

+ 2
- 0
poi/src/test/java/org/apache/poi/hssf/dev/TestEFBiffViewer.java View File

@@ -26,6 +26,7 @@ import org.apache.poi.hssf.eventusermodel.HSSFEventFactory;
import org.apache.poi.hssf.eventusermodel.HSSFRequest;
import org.apache.poi.hssf.record.RecordInputStream;
import org.apache.poi.poifs.filesystem.POIFSFileSystem;
import org.apache.poi.util.RecordFormatException;
import org.junit.jupiter.api.Assertions;

class TestEFBiffViewer extends BaseTestIteratingXLS {
@@ -37,6 +38,7 @@ class TestEFBiffViewer extends BaseTestIteratingXLS {
excludes.put("51832.xls", EncryptedDocumentException.class);
excludes.put("xor-encryption-abc.xls", EncryptedDocumentException.class);
excludes.put("password.xls", EncryptedDocumentException.class);
excludes.put("protected_66115.xls", EncryptedDocumentException.class);
// HSSFWorkbook cannot open it as well
excludes.put("43493.xls", RecordInputStream.LeftoverDataException.class);
excludes.put("44958_1.xls", RecordInputStream.LeftoverDataException.class);

+ 1
- 0
poi/src/test/java/org/apache/poi/hssf/dev/TestFormulaViewer.java View File

@@ -44,6 +44,7 @@ class TestFormulaViewer extends BaseTestIteratingXLS {
excludes.put("password.xls", EncryptedDocumentException.class);
excludes.put("43493.xls", RecordInputStream.LeftoverDataException.class); // HSSFWorkbook cannot open it as well
excludes.put("44958_1.xls", RecordInputStream.LeftoverDataException.class);
excludes.put("protected_66115.xls", EncryptedDocumentException.class);
return excludes;
}


BIN
test-data/spreadsheet/stress.xls View File


Loading…
Cancel
Save