KdbxXmlReader: Support Protected attribute on most nodes

Similar to KeePass2 most elements are checked for a

Protected="True"

attribute. Previously, files KDBX 3.1 files with protected Binary
entries, as seen in the wild, could not be read (mangled passwords and
file attachments).
This commit is contained in:
Thomas Luzat 2018-03-06 10:55:29 +01:00 committed by Janek Bevendorff
parent 084758908a
commit 4c9dcf5c98
2 changed files with 60 additions and 42 deletions

View File

@ -188,6 +188,12 @@ QString KdbxXmlReader::errorString() const
return QString();
}
bool KdbxXmlReader::isTrueValue(const QStringRef& value)
{
return value.compare(QLatin1String("true"), Qt::CaseInsensitive) == 0
|| value == "1";
}
void KdbxXmlReader::raiseError(const QString& errorMessage)
{
m_error = true;
@ -378,15 +384,9 @@ void KdbxXmlReader::parseBinaries()
}
QXmlStreamAttributes attr = m_xml.attributes();
QString id = attr.value("ID").toString();
QByteArray data;
if (attr.value("Compressed").compare(QLatin1String("True"), Qt::CaseInsensitive) == 0) {
data = readCompressedBinary();
} else {
data = readBinary();
}
QByteArray data = isTrueValue(attr.value("Compressed"))
? readCompressedBinary() : readBinary();
if (m_binaryPool.contains(id)) {
qWarning("KdbxXmlReader::parseBinaries: overwriting binary item \"%s\"",
@ -812,28 +812,9 @@ void KdbxXmlReader::parseEntryString(Entry* entry)
if (m_xml.name() == "Value") {
QXmlStreamAttributes attr = m_xml.attributes();
value = readString();
bool isProtected = attr.value("Protected") == "True";
bool protectInMemory = attr.value("ProtectInMemory") == "True";
if (isProtected && !value.isEmpty()) {
if (m_randomStream) {
QByteArray ciphertext = QByteArray::fromBase64(value.toLatin1());
bool ok;
QByteArray plaintext = m_randomStream->process(ciphertext, &ok);
if (!ok) {
value.clear();
raiseError(m_randomStream->errorString());
} else {
value = QString::fromUtf8(plaintext);
}
} else {
raiseError(tr("Unable to decrypt entry string"));
continue;
}
}
bool isProtected;
bool protectInMemory;
value = readString(isProtected, protectInMemory);
protect = isProtected || protectInMemory;
valueSet = true;
continue;
@ -881,14 +862,6 @@ QPair<QString, QString> KdbxXmlReader::parseEntryBinary(Entry* entry)
} else {
// format compatibility
value = readBinary();
bool isProtected = attr.hasAttribute("Protected")
&& (attr.value("Protected") == "True");
if (isProtected && !value.isEmpty()) {
if (!m_randomStream->processInPlace(value)) {
raiseError(m_randomStream->errorString());
}
}
}
valueSet = true;
@ -1003,17 +976,43 @@ TimeInfo KdbxXmlReader::parseTimes()
QString KdbxXmlReader::readString()
{
return m_xml.readElementText();
bool isProtected;
bool protectInMemory;
return readString(isProtected, protectInMemory);
}
QString KdbxXmlReader::readString(bool& isProtected, bool& protectInMemory)
{
QXmlStreamAttributes attr = m_xml.attributes();
isProtected = isTrueValue(attr.value("Protected"));
protectInMemory = isTrueValue(attr.value("ProtectInMemory"));
QString value = m_xml.readElementText();
if (isProtected && !value.isEmpty()) {
QByteArray ciphertext = QByteArray::fromBase64(value.toLatin1());
bool ok;
QByteArray plaintext = m_randomStream->process(ciphertext, &ok);
if (!ok) {
value.clear();
raiseError(m_randomStream->errorString());
return value;
}
value = QString::fromUtf8(plaintext);
}
return value;
}
bool KdbxXmlReader::readBool()
{
QString str = readString();
if (str.compare("True", Qt::CaseInsensitive) == 0) {
if (str.compare("true", Qt::CaseInsensitive) == 0) {
return true;
}
if (str.compare("False", Qt::CaseInsensitive) == 0) {
if (str.compare("false", Qt::CaseInsensitive) == 0) {
return false;
}
if (str.length() == 0) {
@ -1112,7 +1111,24 @@ Uuid KdbxXmlReader::readUuid()
QByteArray KdbxXmlReader::readBinary()
{
return QByteArray::fromBase64(readString().toLatin1());
QXmlStreamAttributes attr = m_xml.attributes();
bool isProtected = isTrueValue(attr.value("Protected"));
QString value = m_xml.readElementText();
QByteArray data = QByteArray::fromBase64(value.toLatin1());
if (isProtected && !data.isEmpty()) {
bool ok;
QByteArray plaintext = m_randomStream->process(data, &ok);
if (!ok) {
data.clear();
raiseError(m_randomStream->errorString());
return data;
}
data = plaintext;
}
return data;
}
QByteArray KdbxXmlReader::readCompressedBinary()

View File

@ -81,6 +81,7 @@ protected:
virtual TimeInfo parseTimes();
virtual QString readString();
virtual QString readString(bool& isProtected, bool& protectInMemory);
virtual bool readBool();
virtual QDateTime readDateTime();
virtual QColor readColor();
@ -94,6 +95,7 @@ protected:
virtual Group* getGroup(const Uuid& uuid);
virtual Entry* getEntry(const Uuid& uuid);
virtual bool isTrueValue(const QStringRef& value);
virtual void raiseError(const QString& errorMessage);
const quint32 m_kdbxVersion;