diff options
Diffstat (limited to 'src/format/Kdbx4XmlReader.cpp')
-rw-r--r-- | src/format/Kdbx4XmlReader.cpp | 355 |
1 files changed, 210 insertions, 145 deletions
diff --git a/src/format/Kdbx4XmlReader.cpp b/src/format/Kdbx4XmlReader.cpp index 10dfe6475..9f6ce02c3 100644 --- a/src/format/Kdbx4XmlReader.cpp +++ b/src/format/Kdbx4XmlReader.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 Felix Geyer <debfx@fobos.de> + * Copyright (C) 2017 KeePassXC Team <team@keepassxc.org> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -19,12 +19,9 @@ #include <QBuffer> #include <QFile> -#include <QRegularExpression> #include "core/Endian.h" -#include "core/Database.h" #include "core/DatabaseIcons.h" -#include "core/Group.h" #include "core/Metadata.h" #include "core/Tools.h" #include "format/KeePass2RandomStream.h" @@ -68,30 +65,32 @@ void Kdbx4XmlReader::readDatabase(QIODevice* device, Database* db, KeePass2Rando m_randomStream = randomStream; m_headerHash.clear(); - m_tmpParent = new Group(); + m_tmpParent.reset(new Group()); bool rootGroupParsed = false; - if (!m_xml.hasError() && m_xml.readNextStartElement()) { - if (m_xml.name() == "KeePassFile") { - rootGroupParsed = parseKeePassFile(); - } + if (m_xml.hasError()) { + raiseError(QString("XML parsing failure: %1").arg(m_xml.error())); + return; + } + + if (m_xml.readNextStartElement() && m_xml.name() == "KeePassFile") { + rootGroupParsed = parseKeePassFile(); } - if (!m_xml.hasError() && !rootGroupParsed) { + if (!rootGroupParsed) { raiseError("No root group"); + return; } - if (!m_xml.hasError()) { - if (!m_tmpParent->children().isEmpty()) { - qWarning("Kdbx4XmlReader::readDatabase: found %d invalid group reference(s)", - m_tmpParent->children().size()); - } + if (!m_tmpParent->children().isEmpty()) { + qWarning("Kdbx4XmlReader::readDatabase: found %d invalid group reference(s)", + m_tmpParent->children().size()); + } - if (!m_tmpParent->entries().isEmpty()) { - qWarning("Kdbx4XmlReader::readDatabase: found %d invalid entry reference(s)", - m_tmpParent->children().size()); - } + if (!m_tmpParent->entries().isEmpty()) { + qWarning("Kdbx4XmlReader::readDatabase: found %d invalid entry reference(s)", + m_tmpParent->children().size()); } const QSet<QString> poolKeys = m_binaryPool.keys().toSet(); @@ -100,13 +99,11 @@ void Kdbx4XmlReader::readDatabase(QIODevice* device, Database* db, KeePass2Rando const QSet<QString> unusedKeys = poolKeys - entryKeys; if (!unmappedKeys.isEmpty()) { - raiseError("Unmapped keys left."); + qWarning("Unmapped keys left."); } - if (!m_xml.hasError()) { - for (const QString& key : unusedKeys) { - qWarning("Kdbx4XmlReader::readDatabase: found unused key \"%s\"", qPrintable(key)); - } + for (const QString& key : unusedKeys) { + qWarning("Kdbx4XmlReader::readDatabase: found unused key \"%s\"", qPrintable(key)); } QHash<QString, QPair<Entry*, QString> >::const_iterator i; @@ -131,13 +128,11 @@ void Kdbx4XmlReader::readDatabase(QIODevice* device, Database* db, KeePass2Rando histEntry->setUpdateTimeinfo(true); } } - - delete m_tmpParent; } Database* Kdbx4XmlReader::readDatabase(QIODevice* device) { - Database* db = new Database(); + auto db = new Database(); readDatabase(device, db); return db; } @@ -158,14 +153,16 @@ QString Kdbx4XmlReader::errorString() { if (m_error) { return m_errorStr; - } else if (m_xml.hasError()) { + } + + if (m_xml.hasError()) { return QString("XML error:\n%1\nLine %2, column %3") .arg(m_xml.errorString()) .arg(m_xml.lineNumber()) .arg(m_xml.columnNumber()); - } else { - return QString(); } + + return QString(); } void Kdbx4XmlReader::raiseError(const QString& errorMessage) @@ -189,17 +186,21 @@ bool Kdbx4XmlReader::parseKeePassFile() while (!m_xml.hasError() && m_xml.readNextStartElement()) { if (m_xml.name() == "Meta") { parseMeta(); - } else if (m_xml.name() == "Root") { + continue; + } + + if (m_xml.name() == "Root") { if (rootElementFound) { rootParsedSuccessfully = false; - raiseError("Multiple root elements"); + qWarning("Multiple root elements"); } else { rootParsedSuccessfully = parseRoot(); rootElementFound = true; } - } else { - skipCurrentElement(); + continue; } + + skipCurrentElement(); } return rootParsedSuccessfully; @@ -259,14 +260,14 @@ void Kdbx4XmlReader::parseMeta() if (value >= -1) { m_meta->setHistoryMaxItems(value); } else { - raiseError("HistoryMaxItems invalid number"); + qWarning("HistoryMaxItems invalid number"); } } else if (m_xml.name() == "HistoryMaxSize") { int value = readNumber(); if (value >= -1) { m_meta->setHistoryMaxSize(value); } else { - raiseError("HistoryMaxSize invalid number"); + qWarning("HistoryMaxSize invalid number"); } } else if (m_xml.name() == "Binaries") { parseBinaries(); @@ -337,9 +338,10 @@ void Kdbx4XmlReader::parseIcon() if (uuidSet && iconSet) { m_meta->addCustomIcon(uuid, icon); - } else { - raiseError("Missing icon uuid or data"); + return; } + + raiseError("Missing icon uuid or data"); } void Kdbx4XmlReader::parseBinaries() @@ -347,27 +349,28 @@ void Kdbx4XmlReader::parseBinaries() Q_ASSERT(m_xml.isStartElement() && m_xml.name() == "Binaries"); while (!m_xml.hasError() && m_xml.readNextStartElement()) { - if (m_xml.name() == "Binary") { - QXmlStreamAttributes attr = m_xml.attributes(); + if (m_xml.name() != "Binary") { + skipCurrentElement(); + continue; + } - QString id = attr.value("ID").toString(); + QXmlStreamAttributes attr = m_xml.attributes(); - QByteArray data; - if (attr.value("Compressed").compare(QLatin1String("True"), Qt::CaseInsensitive) == 0) { - data = readCompressedBinary(); - } else { - data = readBinary(); - } + QString id = attr.value("ID").toString(); - if (m_binaryPool.contains(id)) { - qWarning("Kdbx4XmlReader::parseBinaries: overwriting binary item \"%s\"", - qPrintable(id)); - } - - m_binaryPool.insert(id, data); + QByteArray data; + if (attr.value("Compressed").compare(QLatin1String("True"), Qt::CaseInsensitive) == 0) { + data = readCompressedBinary(); } else { - skipCurrentElement(); + data = readBinary(); } + + if (m_binaryPool.contains(id)) { + qWarning("Kdbx4XmlReader::parseBinaries: overwriting binary item \"%s\"", + qPrintable(id)); + } + + m_binaryPool.insert(id, data); } } @@ -378,9 +381,9 @@ void Kdbx4XmlReader::parseCustomData() while (!m_xml.hasError() && m_xml.readNextStartElement()) { if (m_xml.name() == "Item") { parseCustomDataItem(); - } else { - skipCurrentElement(); + continue; } + skipCurrentElement(); } } @@ -407,9 +410,10 @@ void Kdbx4XmlReader::parseCustomDataItem() if (keySet && valueSet) { m_meta->addCustomField(key, value); - } else { - raiseError("Missing custom data key or value"); + return; } + + raiseError("Missing custom data key or value"); } bool Kdbx4XmlReader::parseRoot() @@ -450,7 +454,7 @@ Group* Kdbx4XmlReader::parseGroup() { Q_ASSERT(m_xml.isStartElement() && m_xml.name() == "Group"); - Group* group = new Group(); + auto group = new Group(); group->setUpdateTimeinfo(false); QList<Group*> children; QList<Entry*> entries; @@ -466,11 +470,17 @@ Group* Kdbx4XmlReader::parseGroup() } else { group->setUuid(uuid); } - } else if (m_xml.name() == "Name") { + continue; + } + if (m_xml.name() == "Name") { group->setName(readString()); - } else if (m_xml.name() == "Notes") { + continue; + } + if (m_xml.name() == "Notes") { group->setNotes(readString()); - } else if (m_xml.name() == "IconID") { + continue; + } + if (m_xml.name() == "IconID") { int iconId = readNumber(); if (iconId < 0) { if (m_strictMode) { @@ -483,18 +493,28 @@ Group* Kdbx4XmlReader::parseGroup() } group->setIcon(iconId); - } else if (m_xml.name() == "CustomIconUUID") { + continue; + } + if (m_xml.name() == "CustomIconUUID") { Uuid uuid = readUuid(); if (!uuid.isNull()) { group->setIcon(uuid); } - } else if (m_xml.name() == "Times") { + continue; + } + if (m_xml.name() == "Times") { group->setTimeInfo(parseTimes()); - } else if (m_xml.name() == "IsExpanded") { + continue; + } + if (m_xml.name() == "IsExpanded") { group->setExpanded(readBool()); - } else if (m_xml.name() == "DefaultAutoTypeSequence") { + continue; + } + if (m_xml.name() == "DefaultAutoTypeSequence") { group->setDefaultAutoTypeSequence(readString()); - } else if (m_xml.name() == "EnableAutoType") { + continue; + } + if (m_xml.name() == "EnableAutoType") { QString str = readString(); if (str.compare("null", Qt::CaseInsensitive) == 0) { @@ -506,7 +526,9 @@ Group* Kdbx4XmlReader::parseGroup() } else { raiseError("Invalid EnableAutoType value"); } - } else if (m_xml.name() == "EnableSearching") { + continue; + } + if (m_xml.name() == "EnableSearching") { QString str = readString(); if (str.compare("null", Qt::CaseInsensitive) == 0) { @@ -518,21 +540,28 @@ Group* Kdbx4XmlReader::parseGroup() } else { raiseError("Invalid EnableSearching value"); } - } else if (m_xml.name() == "LastTopVisibleEntry") { + continue; + } + if (m_xml.name() == "LastTopVisibleEntry") { group->setLastTopVisibleEntry(getEntry(readUuid())); - } else if (m_xml.name() == "Group") { + continue; + } + if (m_xml.name() == "Group") { Group* newGroup = parseGroup(); if (newGroup) { children.append(newGroup); } - } else if (m_xml.name() == "Entry") { + continue; + } + if (m_xml.name() == "Entry") { Entry* newEntry = parseEntry(false); if (newEntry) { entries.append(newEntry); } - } else { - skipCurrentElement(); + continue; } + + skipCurrentElement(); } if (group->uuid().isNull() && !m_strictMode) { @@ -577,7 +606,7 @@ void Kdbx4XmlReader::parseDeletedObject() { Q_ASSERT(m_xml.isStartElement() && m_xml.name() == "DeletedObject"); - DeletedObject delObj; + DeletedObject delObj{}; while (!m_xml.hasError() && m_xml.readNextStartElement()) { if (m_xml.name() == "UUID") { @@ -586,19 +615,24 @@ void Kdbx4XmlReader::parseDeletedObject() if (m_strictMode) { raiseError("Null DeleteObject uuid"); } - } else { - delObj.uuid = uuid; + continue; } - } else if (m_xml.name() == "DeletionTime") { + delObj.uuid = uuid; + continue; + } + if (m_xml.name() == "DeletionTime") { delObj.deletionTime = readDateTime(); - } else { - skipCurrentElement(); + continue; } + skipCurrentElement(); } if (!delObj.uuid.isNull() && !delObj.deletionTime.isNull()) { m_db->addDeletedObject(delObj); - } else if (m_strictMode) { + return; + } + + if (m_strictMode) { raiseError("Missing DeletedObject uuid or time"); } } @@ -607,7 +641,7 @@ Entry* Kdbx4XmlReader::parseEntry(bool history) { Q_ASSERT(m_xml.isStartElement() && m_xml.name() == "Entry"); - Entry* entry = new Entry(); + auto entry = new Entry(); entry->setUpdateTimeinfo(false); QList<Entry*> historyItems; QList<StringPair> binaryRefs; @@ -624,7 +658,9 @@ Entry* Kdbx4XmlReader::parseEntry(bool history) } else { entry->setUuid(uuid); } - } else if (m_xml.name() == "IconID") { + continue; + } + if (m_xml.name() == "IconID") { int iconId = readNumber(); if (iconId < 0) { if (m_strictMode) { @@ -633,39 +669,59 @@ Entry* Kdbx4XmlReader::parseEntry(bool history) iconId = 0; } entry->setIcon(iconId); - } else if (m_xml.name() == "CustomIconUUID") { + continue; + } + if (m_xml.name() == "CustomIconUUID") { Uuid uuid = readUuid(); if (!uuid.isNull()) { entry->setIcon(uuid); } - } else if (m_xml.name() == "ForegroundColor") { + continue; + }if (m_xml.name() == "ForegroundColor") { entry->setForegroundColor(readColor()); - } else if (m_xml.name() == "BackgroundColor") { + continue; + } + if (m_xml.name() == "BackgroundColor") { entry->setBackgroundColor(readColor()); - } else if (m_xml.name() == "OverrideURL") { + continue; + } + if (m_xml.name() == "OverrideURL") { entry->setOverrideUrl(readString()); - } else if (m_xml.name() == "Tags") { + continue; + } + if (m_xml.name() == "Tags") { entry->setTags(readString()); - } else if (m_xml.name() == "Times") { + continue; + } + if (m_xml.name() == "Times") { entry->setTimeInfo(parseTimes()); - } else if (m_xml.name() == "String") { + continue; + } + if (m_xml.name() == "String") { parseEntryString(entry); - } else if (m_xml.name() == "Binary") { + continue; + } + if (m_xml.name() == "Binary") { QPair<QString, QString> ref = parseEntryBinary(entry); if (!ref.first.isNull() && !ref.second.isNull()) { binaryRefs.append(ref); } - } else if (m_xml.name() == "AutoType") { + continue; + } + if (m_xml.name() == "AutoType") { parseAutoType(entry); - } else if (m_xml.name() == "History") { + continue; + } + if (m_xml.name() == "History") { if (history) { raiseError("History element in history entry"); } else { historyItems = parseEntryHistory(); } - } else { - skipCurrentElement(); + continue; } + + skipCurrentElement(); } if (entry->uuid().isNull() && !m_strictMode) { @@ -720,7 +776,10 @@ void Kdbx4XmlReader::parseEntryString(Entry* entry) if (m_xml.name() == "Key") { key = readString(); keySet = true; - } else if (m_xml.name() == "Value") { + continue; + } + + if (m_xml.name() == "Value") { QXmlStreamAttributes attr = m_xml.attributes(); value = readString(); @@ -740,26 +799,29 @@ void Kdbx4XmlReader::parseEntryString(Entry* entry) } } else { raiseError("Unable to decrypt entry string"); + continue; } } protect = isProtected || protectInMemory; valueSet = true; - } else { - skipCurrentElement(); + continue; } + + skipCurrentElement(); } if (keySet && valueSet) { // the default attributes are always there so additionally check if it's empty if (entry->attributes()->hasKey(key) && !entry->attributes()->value(key).isEmpty()) { raiseError("Duplicate custom attribute found"); - } else { - entry->attributes()->set(key, value, protect); + return; } - } else { - raiseError("Entry string key or value missing"); + entry->attributes()->set(key, value, protect); + return; } + + raiseError("Entry string key or value missing"); } QPair<QString, QString> Kdbx4XmlReader::parseEntryBinary(Entry* entry) @@ -777,7 +839,9 @@ QPair<QString, QString> Kdbx4XmlReader::parseEntryBinary(Entry* entry) if (m_xml.name() == "Key") { key = readString(); keySet = true; - } else if (m_xml.name() == "Value") { + continue; + } + if (m_xml.name() == "Value") { QXmlStreamAttributes attr = m_xml.attributes(); if (attr.hasAttribute("Ref")) { @@ -797,9 +861,9 @@ QPair<QString, QString> Kdbx4XmlReader::parseEntryBinary(Entry* entry) } valueSet = true; - } else { - skipCurrentElement(); + continue; } + skipCurrentElement(); } if (keySet && valueSet) { @@ -856,9 +920,9 @@ void Kdbx4XmlReader::parseAutoTypeAssoc(Entry* entry) if (windowSet && sequenceSet) { entry->autoTypeAssociations()->add(assoc); - } else { - raiseError("Auto-type association window or sequence missing"); + return; } + raiseError("Auto-type association window or sequence missing"); } QList<Entry*> Kdbx4XmlReader::parseEntryHistory() @@ -917,14 +981,15 @@ bool Kdbx4XmlReader::readBool() if (str.compare("True", Qt::CaseInsensitive) == 0) { return true; - } else if (str.compare("False", Qt::CaseInsensitive) == 0) { - return false; - } else if (str.length() == 0) { + } + if (str.compare("False", Qt::CaseInsensitive) == 0) { return false; - } else { - raiseError("Invalid bool value"); + } + if (str.length() == 0) { return false; } + raiseError("Invalid bool value"); + return false; } QDateTime Kdbx4XmlReader::readDateTime() @@ -936,18 +1001,18 @@ QDateTime Kdbx4XmlReader::readDateTime() QByteArray secsBytes = QByteArray::fromBase64(str.toUtf8()).leftJustified(8, '\0', true).left(8); qint64 secs = Endian::bytesToSizedInt<quint64>(secsBytes, KeePass2::BYTEORDER); return QDateTime(QDate(1, 1, 1), QTime(0, 0, 0, 0), Qt::UTC).addSecs(secs); - } else { - QDateTime dt = QDateTime::fromString(str, Qt::ISODate); - if (dt.isValid()) { - return dt; - } else { - if (m_strictMode) { - raiseError("Invalid date time value"); - } + } - return QDateTime::currentDateTimeUtc(); - } + QDateTime dt = QDateTime::fromString(str, Qt::ISODate); + if (dt.isValid()) { + return dt; } + + if (m_strictMode) { + raiseError("Invalid date time value"); + } + + return QDateTime::currentDateTimeUtc(); } QColor Kdbx4XmlReader::readColor() @@ -955,26 +1020,26 @@ QColor Kdbx4XmlReader::readColor() QString colorStr = readString(); if (colorStr.isEmpty()) { - return QColor(); + return {}; } if (colorStr.length() != 7 || colorStr[0] != '#') { if (m_strictMode) { raiseError("Invalid color value"); } - return QColor(); + return {}; } QColor color; - for (int i = 0; i <= 2; i++) { - QString rgbPartStr = colorStr.mid(1 + 2*i, 2); + for (int i = 0; i <= 2; ++i) { + QString rgbPartStr = colorStr.mid(1 + 2 * i, 2); bool ok; int rgbPart = rgbPartStr.toInt(&ok, 16); if (!ok || rgbPart > 255) { if (m_strictMode) { raiseError("Invalid color rgb part"); } - return QColor(); + return {}; } if (i == 0) { @@ -1003,15 +1068,15 @@ Uuid Kdbx4XmlReader::readUuid() { QByteArray uuidBin = readBinary(); if (uuidBin.isEmpty()) { - return Uuid(); - } else if (uuidBin.length() != Uuid::Length) { + return {}; + } + if (uuidBin.length() != Uuid::Length) { if (m_strictMode) { raiseError("Invalid uuid value"); } - return Uuid(); - } else { - return Uuid(uuidBin); + return {}; } + return Uuid(uuidBin); } QByteArray Kdbx4XmlReader::readBinary() @@ -1045,14 +1110,14 @@ Group* Kdbx4XmlReader::getGroup(const Uuid& uuid) if (m_groups.contains(uuid)) { return m_groups.value(uuid); - } else { - Group* group = new Group(); - group->setUpdateTimeinfo(false); - group->setUuid(uuid); - group->setParent(m_tmpParent); - m_groups.insert(uuid, group); - return group; } + + auto group = new Group(); + group->setUpdateTimeinfo(false); + group->setUuid(uuid); + group->setParent(m_tmpParent.data()); + m_groups.insert(uuid, group); + return group; } Entry* Kdbx4XmlReader::getEntry(const Uuid& uuid) @@ -1063,14 +1128,14 @@ Entry* Kdbx4XmlReader::getEntry(const Uuid& uuid) if (m_entries.contains(uuid)) { return m_entries.value(uuid); - } else { - Entry* entry = new Entry(); - entry->setUpdateTimeinfo(false); - entry->setUuid(uuid); - entry->setGroup(m_tmpParent); - m_entries.insert(uuid, entry); - return entry; } + + auto entry = new Entry(); + entry->setUpdateTimeinfo(false); + entry->setUuid(uuid); + entry->setGroup(m_tmpParent.data()); + m_entries.insert(uuid, entry); + return entry; } void Kdbx4XmlReader::skipCurrentElement() |