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

View File

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