mirror of
https://github.com/keepassxreboot/keepassxc.git
synced 2024-10-01 01:26:01 -04:00
parent
e1c82a4453
commit
5c84aa308d
@ -56,6 +56,9 @@ KeePass1Reader::KeePass1Reader()
|
||||
Database* KeePass1Reader::readDatabase(QIODevice* device, const QString& password,
|
||||
QIODevice* keyfileDevice)
|
||||
{
|
||||
m_error = false;
|
||||
m_errorStr.clear();
|
||||
|
||||
QByteArray keyfileData;
|
||||
FileKey newFileKey;
|
||||
|
||||
@ -63,13 +66,16 @@ Database* KeePass1Reader::readDatabase(QIODevice* device, const QString& passwor
|
||||
keyfileData = readKeyfile(keyfileDevice);
|
||||
|
||||
if (keyfileData.isEmpty()) {
|
||||
raiseError(tr("Unable to read keyfile.").append("\n").append(keyfileDevice->errorString()));
|
||||
return Q_NULLPTR;
|
||||
}
|
||||
if (!keyfileDevice->seek(0)) {
|
||||
raiseError(tr("Unable to read keyfile.").append("\n").append(keyfileDevice->errorString()));
|
||||
return Q_NULLPTR;
|
||||
}
|
||||
|
||||
if (!newFileKey.load(keyfileDevice)) {
|
||||
raiseError(tr("Unable to read keyfile.").append("\n").append(keyfileDevice->errorString()));
|
||||
return Q_NULLPTR;
|
||||
}
|
||||
}
|
||||
@ -79,8 +85,6 @@ Database* KeePass1Reader::readDatabase(QIODevice* device, const QString& passwor
|
||||
m_db = db.data();
|
||||
m_tmpParent = tmpParent.data();
|
||||
m_device = device;
|
||||
m_error = false;
|
||||
m_errorStr = QString();
|
||||
|
||||
bool ok;
|
||||
|
||||
@ -111,43 +115,43 @@ Database* KeePass1Reader::readDatabase(QIODevice* device, const QString& passwor
|
||||
|
||||
m_masterSeed = m_device->read(16);
|
||||
if (m_masterSeed.size() != 16) {
|
||||
// TODO: error
|
||||
raiseError("Unable to read master seed");
|
||||
return Q_NULLPTR;
|
||||
}
|
||||
|
||||
m_encryptionIV = m_device->read(16);
|
||||
if (m_encryptionIV.size() != 16) {
|
||||
// TODO: error
|
||||
raiseError("Unable to read encryption IV");
|
||||
return Q_NULLPTR;
|
||||
}
|
||||
|
||||
quint32 numGroups = Endian::readUInt32(m_device, KeePass1::BYTEORDER, &ok);
|
||||
if (!ok) {
|
||||
// TODO: error
|
||||
raiseError("Invalid number of groups");
|
||||
return Q_NULLPTR;
|
||||
}
|
||||
|
||||
quint32 numEntries = Endian::readUInt32(m_device, KeePass1::BYTEORDER, &ok);
|
||||
if (!ok) {
|
||||
// TODO: error
|
||||
raiseError("Invalid number of entries");
|
||||
return Q_NULLPTR;
|
||||
}
|
||||
|
||||
m_contentHashHeader = m_device->read(32);
|
||||
if (m_contentHashHeader.size() != 32) {
|
||||
// TODO: error
|
||||
raiseError("Invalid content hash size");
|
||||
return Q_NULLPTR;
|
||||
}
|
||||
|
||||
m_transformSeed = m_device->read(32);
|
||||
if (m_transformSeed.size() != 32) {
|
||||
// TODO: error
|
||||
raiseError("Invalid transform seed size");
|
||||
return Q_NULLPTR;
|
||||
}
|
||||
|
||||
m_transformRounds = Endian::readUInt32(m_device, KeePass1::BYTEORDER, &ok);
|
||||
if (!ok) {
|
||||
// TODO: error
|
||||
raiseError("Invalid number of transform rounds");
|
||||
return Q_NULLPTR;
|
||||
}
|
||||
m_db->setTransformRounds(m_transformRounds);
|
||||
@ -157,7 +161,7 @@ Database* KeePass1Reader::readDatabase(QIODevice* device, const QString& passwor
|
||||
QScopedPointer<SymmetricCipherStream> cipherStream(testKeys(password, keyfileData, contentPos));
|
||||
|
||||
if (!cipherStream) {
|
||||
// TODO: error
|
||||
raiseError("Unable to create cipher stream");
|
||||
return Q_NULLPTR;
|
||||
}
|
||||
|
||||
@ -180,6 +184,7 @@ Database* KeePass1Reader::readDatabase(QIODevice* device, const QString& passwor
|
||||
}
|
||||
|
||||
if (!constructGroupTree(groups)) {
|
||||
raiseError("Unable to construct group tree");
|
||||
return Q_NULLPTR;
|
||||
}
|
||||
|
||||
@ -337,7 +342,12 @@ SymmetricCipherStream* KeePass1Reader::testKeys(const QString& password, const Q
|
||||
cipherStream->reset();
|
||||
cipherStream->close();
|
||||
if (!m_device->seek(contentPos)) {
|
||||
// TODO: error
|
||||
QString msg = "unable to seek to content position";
|
||||
if (!m_device->errorString().isEmpty()) {
|
||||
msg.append("\n").append(m_device->errorString());
|
||||
}
|
||||
raiseError(msg);
|
||||
|
||||
return Q_NULLPTR;
|
||||
}
|
||||
cipherStream->open(QIODevice::ReadOnly);
|
||||
@ -402,17 +412,19 @@ Group* KeePass1Reader::readGroup(QIODevice* cipherStream)
|
||||
do {
|
||||
quint16 fieldType = Endian::readUInt16(cipherStream, KeePass1::BYTEORDER, &ok);
|
||||
if (!ok) {
|
||||
raiseError("Invalid group field type number");
|
||||
return Q_NULLPTR;
|
||||
}
|
||||
|
||||
int fieldSize = static_cast<int>(Endian::readUInt32(cipherStream, KeePass1::BYTEORDER, &ok));
|
||||
if (!ok) {
|
||||
raiseError("Invalid group field size");
|
||||
return Q_NULLPTR;
|
||||
}
|
||||
|
||||
QByteArray fieldData = cipherStream->read(fieldSize);
|
||||
if (fieldData.size() != fieldSize) {
|
||||
// TODO: error
|
||||
raiseError("Read group field data doesn't match size");
|
||||
return Q_NULLPTR;
|
||||
}
|
||||
|
||||
@ -422,6 +434,7 @@ Group* KeePass1Reader::readGroup(QIODevice* cipherStream)
|
||||
break;
|
||||
case 0x0001:
|
||||
if (fieldSize != 4) {
|
||||
raiseError("Incorrect group id field size");
|
||||
return Q_NULLPTR;
|
||||
}
|
||||
groupId = Endian::bytesToUInt32(fieldData, KeePass1::BYTEORDER);
|
||||
@ -433,6 +446,7 @@ Group* KeePass1Reader::readGroup(QIODevice* cipherStream)
|
||||
case 0x0003:
|
||||
{
|
||||
if (fieldSize != 5) {
|
||||
raiseError("Incorrect group creation time field size");
|
||||
return Q_NULLPTR;
|
||||
}
|
||||
QDateTime dateTime = dateFromPackedStruct(fieldData);
|
||||
@ -444,6 +458,7 @@ Group* KeePass1Reader::readGroup(QIODevice* cipherStream)
|
||||
case 0x0004:
|
||||
{
|
||||
if (fieldSize != 5) {
|
||||
raiseError("Incorrect group modification time field size");
|
||||
return Q_NULLPTR;
|
||||
}
|
||||
QDateTime dateTime = dateFromPackedStruct(fieldData);
|
||||
@ -455,7 +470,7 @@ Group* KeePass1Reader::readGroup(QIODevice* cipherStream)
|
||||
case 0x0005:
|
||||
{
|
||||
if (fieldSize != 5) {
|
||||
return Q_NULLPTR;
|
||||
raiseError("Incorrect group access time field size");
|
||||
}
|
||||
QDateTime dateTime = dateFromPackedStruct(fieldData);
|
||||
if (dateTime.isValid()) {
|
||||
@ -466,7 +481,7 @@ Group* KeePass1Reader::readGroup(QIODevice* cipherStream)
|
||||
case 0x0006:
|
||||
{
|
||||
if (fieldSize != 5) {
|
||||
return Q_NULLPTR;
|
||||
raiseError("Incorrect group expiry time field size");
|
||||
}
|
||||
QDateTime dateTime = dateFromPackedStruct(fieldData);
|
||||
if (dateTime.isValid()) {
|
||||
@ -478,6 +493,7 @@ Group* KeePass1Reader::readGroup(QIODevice* cipherStream)
|
||||
case 0x0007:
|
||||
{
|
||||
if (fieldSize != 4) {
|
||||
raiseError("Incorrect group icon field size");
|
||||
return Q_NULLPTR;
|
||||
}
|
||||
quint32 iconNumber = Endian::bytesToUInt32(fieldData, KeePass1::BYTEORDER);
|
||||
@ -487,6 +503,7 @@ Group* KeePass1Reader::readGroup(QIODevice* cipherStream)
|
||||
case 0x0008:
|
||||
{
|
||||
if (fieldSize != 2) {
|
||||
raiseError("Incorrect group level field size");
|
||||
return Q_NULLPTR;
|
||||
}
|
||||
groupLevel = Endian::bytesToUInt16(fieldData, KeePass1::BYTEORDER);
|
||||
@ -501,11 +518,13 @@ Group* KeePass1Reader::readGroup(QIODevice* cipherStream)
|
||||
break;
|
||||
default:
|
||||
// invalid field
|
||||
raiseError("Invalid group field type");
|
||||
return Q_NULLPTR;
|
||||
}
|
||||
} while (!reachedEnd);
|
||||
|
||||
if (!groupIdSet || !groupLevelSet) {
|
||||
raiseError("Missing group id or level");
|
||||
return Q_NULLPTR;
|
||||
}
|
||||
|
||||
@ -531,17 +550,19 @@ Entry* KeePass1Reader::readEntry(QIODevice* cipherStream)
|
||||
do {
|
||||
quint16 fieldType = Endian::readUInt16(cipherStream, KeePass1::BYTEORDER, &ok);
|
||||
if (!ok) {
|
||||
raiseError("Missing entry field type number");
|
||||
return Q_NULLPTR;
|
||||
}
|
||||
|
||||
int fieldSize = static_cast<int>(Endian::readUInt32(cipherStream, KeePass1::BYTEORDER, &ok));
|
||||
if (!ok) {
|
||||
raiseError("Invalid entry field size");
|
||||
return Q_NULLPTR;
|
||||
}
|
||||
|
||||
QByteArray fieldData = cipherStream->read(fieldSize);
|
||||
if (fieldData.size() != fieldSize) {
|
||||
// TODO: error
|
||||
raiseError("Read entry field data doesn't match size");
|
||||
return Q_NULLPTR;
|
||||
}
|
||||
|
||||
@ -551,6 +572,7 @@ Entry* KeePass1Reader::readEntry(QIODevice* cipherStream)
|
||||
break;
|
||||
case 0x0001:
|
||||
if (fieldSize != 16) {
|
||||
raiseError("Invalid entry uuid field size");
|
||||
return Q_NULLPTR;
|
||||
}
|
||||
m_entryUuids.insert(fieldData, entry.data());
|
||||
@ -558,6 +580,7 @@ Entry* KeePass1Reader::readEntry(QIODevice* cipherStream)
|
||||
case 0x0002:
|
||||
{
|
||||
if (fieldSize != 4) {
|
||||
raiseError("Invalid entry group id field size");
|
||||
return Q_NULLPTR;
|
||||
}
|
||||
quint32 groupId = Endian::bytesToUInt32(fieldData, KeePass1::BYTEORDER);
|
||||
@ -567,6 +590,7 @@ Entry* KeePass1Reader::readEntry(QIODevice* cipherStream)
|
||||
case 0x0003:
|
||||
{
|
||||
if (fieldSize != 4) {
|
||||
raiseError("Invalid entry icon field size");
|
||||
return Q_NULLPTR;
|
||||
}
|
||||
quint32 iconNumber = Endian::bytesToUInt32(fieldData, KeePass1::BYTEORDER);
|
||||
@ -591,6 +615,7 @@ Entry* KeePass1Reader::readEntry(QIODevice* cipherStream)
|
||||
case 0x0009:
|
||||
{
|
||||
if (fieldSize != 5) {
|
||||
raiseError("Invalid entry creation time field size");
|
||||
return Q_NULLPTR;
|
||||
}
|
||||
QDateTime dateTime = dateFromPackedStruct(fieldData);
|
||||
@ -602,6 +627,7 @@ Entry* KeePass1Reader::readEntry(QIODevice* cipherStream)
|
||||
case 0x000A:
|
||||
{
|
||||
if (fieldSize != 5) {
|
||||
raiseError("Invalid entry modification time field size");
|
||||
return Q_NULLPTR;
|
||||
}
|
||||
QDateTime dateTime = dateFromPackedStruct(fieldData);
|
||||
@ -613,6 +639,7 @@ Entry* KeePass1Reader::readEntry(QIODevice* cipherStream)
|
||||
case 0x000B:
|
||||
{
|
||||
if (fieldSize != 5) {
|
||||
raiseError("Invalid entry creation time field size");
|
||||
return Q_NULLPTR;
|
||||
}
|
||||
QDateTime dateTime = dateFromPackedStruct(fieldData);
|
||||
@ -624,6 +651,7 @@ Entry* KeePass1Reader::readEntry(QIODevice* cipherStream)
|
||||
case 0x000C:
|
||||
{
|
||||
if (fieldSize != 5) {
|
||||
raiseError("Invalid entry expiry time field size");
|
||||
return Q_NULLPTR;
|
||||
}
|
||||
QDateTime dateTime = dateFromPackedStruct(fieldData);
|
||||
@ -646,6 +674,7 @@ Entry* KeePass1Reader::readEntry(QIODevice* cipherStream)
|
||||
break;
|
||||
default:
|
||||
// invalid field
|
||||
raiseError("Invalid entry field type");
|
||||
return Q_NULLPTR;
|
||||
}
|
||||
} while (!reachedEnd);
|
||||
@ -870,10 +899,10 @@ bool KeePass1Reader::parseCustomIcons4(const QByteArray& data)
|
||||
return true;
|
||||
}
|
||||
|
||||
void KeePass1Reader::raiseError(const QString& str)
|
||||
void KeePass1Reader::raiseError(const QString& errorMessage)
|
||||
{
|
||||
m_error = true;
|
||||
m_errorStr = str;
|
||||
m_errorStr = errorMessage;
|
||||
}
|
||||
|
||||
QDateTime KeePass1Reader::dateFromPackedStruct(const QByteArray& data)
|
||||
|
@ -62,7 +62,7 @@ private:
|
||||
void parseMetaStream(const Entry* entry);
|
||||
bool parseGroupTreeState(const QByteArray& data);
|
||||
bool parseCustomIcons4(const QByteArray& data);
|
||||
void raiseError(const QString& str);
|
||||
void raiseError(const QString& errorMessage);
|
||||
static QByteArray readKeyfile(QIODevice* device);
|
||||
static QDateTime dateFromPackedStruct(const QByteArray& data);
|
||||
static bool isMetaStream(const Entry* entry);
|
||||
|
@ -33,8 +33,9 @@
|
||||
#include "streams/SymmetricCipherStream.h"
|
||||
|
||||
KeePass2Reader::KeePass2Reader()
|
||||
: m_error(false)
|
||||
, m_saveXml(false)
|
||||
{
|
||||
m_saveXml = false;
|
||||
}
|
||||
|
||||
Database* KeePass2Reader::readDatabase(QIODevice* device, const CompositeKey& key)
|
||||
@ -43,7 +44,7 @@ Database* KeePass2Reader::readDatabase(QIODevice* device, const CompositeKey& ke
|
||||
m_db = db.data();
|
||||
m_device = device;
|
||||
m_error = false;
|
||||
m_errorStr = QString();
|
||||
m_errorStr.clear();
|
||||
m_headerEnd = false;
|
||||
m_xmlData.clear();
|
||||
m_masterSeed.clear();
|
||||
@ -83,11 +84,15 @@ Database* KeePass2Reader::readDatabase(QIODevice* device, const CompositeKey& ke
|
||||
|
||||
headerStream.close();
|
||||
|
||||
if (hasError()) {
|
||||
return Q_NULLPTR;
|
||||
}
|
||||
|
||||
// check if all required headers were present
|
||||
if (m_masterSeed.isEmpty() || m_transformSeed.isEmpty() || m_encryptionIV.isEmpty()
|
||||
|| m_streamStartBytes.isEmpty() || m_protectedStreamKey.isEmpty()
|
||||
|| m_db->cipher().isNull()) {
|
||||
raiseError("");
|
||||
raiseError("missing database headers");
|
||||
return Q_NULLPTR;
|
||||
}
|
||||
|
||||
@ -149,7 +154,7 @@ Database* KeePass2Reader::readDatabase(QIODevice* device, const CompositeKey& ke
|
||||
if (!xmlReader.headerHash().isEmpty()) {
|
||||
QByteArray headerHash = CryptoHash::hash(headerStream.storedData(), CryptoHash::Sha256);
|
||||
if (headerHash != xmlReader.headerHash()) {
|
||||
raiseError("");
|
||||
raiseError("Head doesn't match hash");
|
||||
return Q_NULLPTR;
|
||||
}
|
||||
}
|
||||
@ -195,17 +200,17 @@ QByteArray KeePass2Reader::xmlData()
|
||||
return m_xmlData;
|
||||
}
|
||||
|
||||
void KeePass2Reader::raiseError(const QString& str)
|
||||
void KeePass2Reader::raiseError(const QString& errorMessage)
|
||||
{
|
||||
m_error = true;
|
||||
m_errorStr = str;
|
||||
m_errorStr = errorMessage;
|
||||
}
|
||||
|
||||
bool KeePass2Reader::readHeaderField()
|
||||
{
|
||||
QByteArray fieldIDArray = m_headerStream->read(1);
|
||||
if (fieldIDArray.size() != 1) {
|
||||
raiseError("");
|
||||
raiseError("Invalid header id size");
|
||||
return false;
|
||||
}
|
||||
quint8 fieldID = fieldIDArray.at(0);
|
||||
@ -213,7 +218,7 @@ bool KeePass2Reader::readHeaderField()
|
||||
bool ok;
|
||||
quint16 fieldLen = Endian::readUInt16(m_headerStream, KeePass2::BYTEORDER, &ok);
|
||||
if (!ok) {
|
||||
raiseError("");
|
||||
raiseError("Invalid header field length");
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -221,7 +226,7 @@ bool KeePass2Reader::readHeaderField()
|
||||
if (fieldLen != 0) {
|
||||
fieldData = m_headerStream->read(fieldLen);
|
||||
if (fieldData.size() != fieldLen) {
|
||||
raiseError("");
|
||||
raiseError("Invalid header data length");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -278,13 +283,13 @@ bool KeePass2Reader::readHeaderField()
|
||||
void KeePass2Reader::setCipher(const QByteArray& data)
|
||||
{
|
||||
if (data.size() != Uuid::Length) {
|
||||
raiseError("");
|
||||
raiseError("Invalid cipher uuid length");
|
||||
}
|
||||
else {
|
||||
Uuid uuid(data);
|
||||
|
||||
if (uuid != KeePass2::CIPHER_AES) {
|
||||
raiseError("");
|
||||
raiseError("Unsupported cipher");
|
||||
}
|
||||
else {
|
||||
m_db->setCipher(uuid);
|
||||
@ -295,13 +300,13 @@ void KeePass2Reader::setCipher(const QByteArray& data)
|
||||
void KeePass2Reader::setCompressionFlags(const QByteArray& data)
|
||||
{
|
||||
if (data.size() != 4) {
|
||||
raiseError("");
|
||||
raiseError("Invalid compression flags length");
|
||||
}
|
||||
else {
|
||||
quint32 id = Endian::bytesToUInt32(data, KeePass2::BYTEORDER);
|
||||
|
||||
if (id > Database::CompressionAlgorithmMax) {
|
||||
raiseError("");
|
||||
raiseError("Unsupported compression algorithm");
|
||||
}
|
||||
else {
|
||||
m_db->setCompressionAlgo(static_cast<Database::CompressionAlgorithm>(id));
|
||||
@ -312,7 +317,7 @@ void KeePass2Reader::setCompressionFlags(const QByteArray& data)
|
||||
void KeePass2Reader::setMasterSeed(const QByteArray& data)
|
||||
{
|
||||
if (data.size() != 32) {
|
||||
raiseError("");
|
||||
raiseError("Invalid master seed size");
|
||||
}
|
||||
else {
|
||||
m_masterSeed = data;
|
||||
@ -322,7 +327,7 @@ void KeePass2Reader::setMasterSeed(const QByteArray& data)
|
||||
void KeePass2Reader::setTransformSeed(const QByteArray& data)
|
||||
{
|
||||
if (data.size() != 32) {
|
||||
raiseError("");
|
||||
raiseError("Invalid transform seed size");
|
||||
}
|
||||
else {
|
||||
m_transformSeed = data;
|
||||
@ -332,7 +337,7 @@ void KeePass2Reader::setTransformSeed(const QByteArray& data)
|
||||
void KeePass2Reader::setTansformRounds(const QByteArray& data)
|
||||
{
|
||||
if (data.size() != 8) {
|
||||
raiseError("");
|
||||
raiseError("Invalid transform rounds size");
|
||||
}
|
||||
else {
|
||||
m_db->setTransformRounds(Endian::bytesToUInt64(data, KeePass2::BYTEORDER));
|
||||
@ -342,7 +347,7 @@ void KeePass2Reader::setTansformRounds(const QByteArray& data)
|
||||
void KeePass2Reader::setEncryptionIV(const QByteArray& data)
|
||||
{
|
||||
if (data.size() != 16) {
|
||||
raiseError("");
|
||||
raiseError("Invalid encryption iv size");
|
||||
}
|
||||
else {
|
||||
m_encryptionIV = data;
|
||||
@ -352,7 +357,7 @@ void KeePass2Reader::setEncryptionIV(const QByteArray& data)
|
||||
void KeePass2Reader::setProtectedStreamKey(const QByteArray& data)
|
||||
{
|
||||
if (data.size() != 32) {
|
||||
raiseError("");
|
||||
raiseError("Invalid stream key size");
|
||||
}
|
||||
else {
|
||||
m_protectedStreamKey = data;
|
||||
@ -362,7 +367,7 @@ void KeePass2Reader::setProtectedStreamKey(const QByteArray& data)
|
||||
void KeePass2Reader::setStreamStartBytes(const QByteArray& data)
|
||||
{
|
||||
if (data.size() != 32) {
|
||||
raiseError("");
|
||||
raiseError("Invalid start bytes size");
|
||||
}
|
||||
else {
|
||||
m_streamStartBytes = data;
|
||||
@ -372,13 +377,13 @@ void KeePass2Reader::setStreamStartBytes(const QByteArray& data)
|
||||
void KeePass2Reader::setInnerRandomStreamID(const QByteArray& data)
|
||||
{
|
||||
if (data.size() != 4) {
|
||||
raiseError("");
|
||||
raiseError("Invalid random stream id size");
|
||||
}
|
||||
else {
|
||||
quint32 id = Endian::bytesToUInt32(data, KeePass2::BYTEORDER);
|
||||
|
||||
if (id != KeePass2::Salsa20) {
|
||||
raiseError("");
|
||||
raiseError("Unsupported random stream algorithm");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -39,7 +39,7 @@ public:
|
||||
QByteArray xmlData();
|
||||
|
||||
private:
|
||||
void raiseError(const QString& str);
|
||||
void raiseError(const QString& errorMessage);
|
||||
|
||||
bool readHeaderField();
|
||||
|
||||
|
@ -43,7 +43,7 @@ KeePass2Writer::KeePass2Writer()
|
||||
void KeePass2Writer::writeDatabase(QIODevice* device, Database* db)
|
||||
{
|
||||
m_error = false;
|
||||
m_errorStr = QString();
|
||||
m_errorStr.clear();
|
||||
|
||||
QByteArray masterSeed = Random::randomArray(32);
|
||||
QByteArray encryptionIV = Random::randomArray(16);
|
||||
@ -117,8 +117,7 @@ void KeePass2Writer::writeDatabase(QIODevice* device, Database* db)
|
||||
bool KeePass2Writer::writeData(const QByteArray& data)
|
||||
{
|
||||
if (m_device->write(data) != data.size()) {
|
||||
m_error = true;
|
||||
m_errorStr = m_device->errorString();
|
||||
raiseError(m_device->errorString());
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
@ -143,11 +142,14 @@ bool KeePass2Writer::writeHeaderField(KeePass2::HeaderFieldID fieldId, const QBy
|
||||
void KeePass2Writer::writeDatabase(const QString& filename, Database* db)
|
||||
{
|
||||
QFile file(filename);
|
||||
file.open(QIODevice::WriteOnly|QIODevice::Truncate);
|
||||
if (!file.open(QIODevice::WriteOnly|QIODevice::Truncate)) {
|
||||
raiseError(file.errorString());
|
||||
return;
|
||||
}
|
||||
writeDatabase(&file, db);
|
||||
}
|
||||
|
||||
bool KeePass2Writer::error()
|
||||
bool KeePass2Writer::hasError()
|
||||
{
|
||||
return m_error;
|
||||
}
|
||||
@ -156,3 +158,9 @@ QString KeePass2Writer::errorString()
|
||||
{
|
||||
return m_errorStr;
|
||||
}
|
||||
|
||||
void KeePass2Writer::raiseError(const QString& errorMessage)
|
||||
{
|
||||
m_error = true;
|
||||
m_errorStr = errorMessage;
|
||||
}
|
||||
|
@ -30,12 +30,13 @@ public:
|
||||
KeePass2Writer();
|
||||
void writeDatabase(QIODevice* device, Database* db);
|
||||
void writeDatabase(const QString& filename, Database* db);
|
||||
bool error();
|
||||
bool hasError();
|
||||
QString errorString();
|
||||
|
||||
private:
|
||||
bool writeData(const QByteArray& data);
|
||||
bool writeHeaderField(KeePass2::HeaderFieldID fieldId, const QByteArray& data);
|
||||
void raiseError(const QString& errorMessage);
|
||||
|
||||
QIODevice* m_device;
|
||||
bool m_error;
|
||||
|
@ -34,11 +34,15 @@ KeePass2XmlReader::KeePass2XmlReader()
|
||||
: m_randomStream(Q_NULLPTR)
|
||||
, m_db(Q_NULLPTR)
|
||||
, m_meta(Q_NULLPTR)
|
||||
, m_error(false)
|
||||
{
|
||||
}
|
||||
|
||||
void KeePass2XmlReader::readDatabase(QIODevice* device, Database* db, KeePass2RandomStream* randomStream)
|
||||
{
|
||||
m_error = false;
|
||||
m_errorStr.clear();
|
||||
|
||||
m_xml.clear();
|
||||
m_xml.setDevice(device);
|
||||
|
||||
@ -60,7 +64,7 @@ void KeePass2XmlReader::readDatabase(QIODevice* device, Database* db, KeePass2Ra
|
||||
}
|
||||
|
||||
if (!m_xml.error() && !rootGroupParsed) {
|
||||
raiseError(28);
|
||||
raiseError("No root group");
|
||||
}
|
||||
|
||||
if (!m_xml.error()) {
|
||||
@ -81,7 +85,7 @@ void KeePass2XmlReader::readDatabase(QIODevice* device, Database* db, KeePass2Ra
|
||||
QSet<QString> unusedKeys = poolKeys - entryKeys;
|
||||
|
||||
if (!unmappedKeys.isEmpty()) {
|
||||
raiseError(17);
|
||||
raiseError("Unmapped keys left.");
|
||||
}
|
||||
|
||||
if (!m_xml.error()) {
|
||||
@ -131,15 +135,29 @@ Database* KeePass2XmlReader::readDatabase(const QString& filename)
|
||||
|
||||
bool KeePass2XmlReader::hasError()
|
||||
{
|
||||
return m_xml.hasError();
|
||||
return m_error || m_xml.hasError();
|
||||
}
|
||||
|
||||
QString KeePass2XmlReader::errorString()
|
||||
{
|
||||
return QString("%1\nLine %2, column %3")
|
||||
if (m_error) {
|
||||
return m_errorStr;
|
||||
}
|
||||
else 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();
|
||||
}
|
||||
}
|
||||
|
||||
void KeePass2XmlReader::raiseError(const QString& errorMessage)
|
||||
{
|
||||
m_error = true;
|
||||
m_errorStr = errorMessage;
|
||||
}
|
||||
|
||||
QByteArray KeePass2XmlReader::headerHash()
|
||||
@ -161,7 +179,7 @@ bool KeePass2XmlReader::parseKeePassFile()
|
||||
else if (m_xml.name() == "Root") {
|
||||
if (rootElementFound) {
|
||||
rootParsedSuccesfully = false;
|
||||
raiseError(29);
|
||||
raiseError("Multiple root elements");
|
||||
}
|
||||
else {
|
||||
rootParsedSuccesfully = parseRoot();
|
||||
@ -253,7 +271,7 @@ void KeePass2XmlReader::parseMeta()
|
||||
m_meta->setHistoryMaxItems(value);
|
||||
}
|
||||
else {
|
||||
raiseError(18);
|
||||
raiseError("HistoryMaxItems invalid number");
|
||||
}
|
||||
}
|
||||
else if (m_xml.name() == "HistoryMaxSize") {
|
||||
@ -262,7 +280,7 @@ void KeePass2XmlReader::parseMeta()
|
||||
m_meta->setHistoryMaxSize(value);
|
||||
}
|
||||
else {
|
||||
raiseError(19);
|
||||
raiseError("HistoryMaxSize invalid number");
|
||||
}
|
||||
}
|
||||
else if (m_xml.name() == "Binaries") {
|
||||
@ -347,7 +365,7 @@ void KeePass2XmlReader::parseIcon()
|
||||
m_meta->addCustomIcon(uuid, icon);
|
||||
}
|
||||
else {
|
||||
raiseError(20);
|
||||
raiseError("Missing icon uuid or data");
|
||||
}
|
||||
}
|
||||
|
||||
@ -423,7 +441,7 @@ void KeePass2XmlReader::parseCustomDataItem()
|
||||
m_meta->addCustomField(key, value);
|
||||
}
|
||||
else {
|
||||
raiseError(21);
|
||||
raiseError("Missing custom data key or value");
|
||||
}
|
||||
}
|
||||
|
||||
@ -438,7 +456,7 @@ bool KeePass2XmlReader::parseRoot()
|
||||
if (m_xml.name() == "Group") {
|
||||
if (groupElementFound) {
|
||||
groupParsedSuccesfully = false;
|
||||
raiseError(30);
|
||||
raiseError("Multiple group elements");
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -475,7 +493,7 @@ Group* KeePass2XmlReader::parseGroup()
|
||||
if (m_xml.name() == "UUID") {
|
||||
Uuid uuid = readUuid();
|
||||
if (uuid.isNull()) {
|
||||
raiseError(1);
|
||||
raiseError("Null group uuid");
|
||||
}
|
||||
else {
|
||||
group->setUuid(uuid);
|
||||
@ -490,7 +508,7 @@ Group* KeePass2XmlReader::parseGroup()
|
||||
else if (m_xml.name() == "IconID") {
|
||||
int iconId = readNumber();
|
||||
if (iconId < 0) {
|
||||
raiseError(2);
|
||||
raiseError("Invalid group icon number");
|
||||
}
|
||||
else {
|
||||
if (iconId >= DatabaseIcons::IconCount) {
|
||||
@ -527,7 +545,7 @@ Group* KeePass2XmlReader::parseGroup()
|
||||
group->setAutoTypeEnabled(Group::Disable);
|
||||
}
|
||||
else {
|
||||
raiseError(3);
|
||||
raiseError("Invalid EnableAutoType value");
|
||||
}
|
||||
}
|
||||
else if (m_xml.name() == "EnableSearching") {
|
||||
@ -543,7 +561,7 @@ Group* KeePass2XmlReader::parseGroup()
|
||||
group->setSearchingEnabled(Group::Disable);
|
||||
}
|
||||
else {
|
||||
raiseError(4);
|
||||
raiseError("Invalid EnableSearching value");
|
||||
}
|
||||
}
|
||||
else if (m_xml.name() == "LastTopVisibleEntry") {
|
||||
@ -573,8 +591,8 @@ Group* KeePass2XmlReader::parseGroup()
|
||||
group->setUpdateTimeinfo(false);
|
||||
delete tmpGroup;
|
||||
}
|
||||
else {
|
||||
raiseError(22);
|
||||
else if (!hasError()) {
|
||||
raiseError("No group uuid found");
|
||||
}
|
||||
|
||||
Q_FOREACH (Group* child, children) {
|
||||
@ -612,7 +630,7 @@ void KeePass2XmlReader::parseDeletedObject()
|
||||
if (m_xml.name() == "UUID") {
|
||||
Uuid uuid = readUuid();
|
||||
if (uuid.isNull()) {
|
||||
raiseError(5);
|
||||
raiseError("Null DeleteObject uuid");
|
||||
}
|
||||
else {
|
||||
delObj.uuid = uuid;
|
||||
@ -630,7 +648,7 @@ void KeePass2XmlReader::parseDeletedObject()
|
||||
m_db->addDeletedObject(delObj);
|
||||
}
|
||||
else {
|
||||
raiseError(23);
|
||||
raiseError("Missing DeletedObject uuid or time");
|
||||
}
|
||||
}
|
||||
|
||||
@ -647,7 +665,7 @@ Entry* KeePass2XmlReader::parseEntry(bool history)
|
||||
if (m_xml.name() == "UUID") {
|
||||
Uuid uuid = readUuid();
|
||||
if (uuid.isNull()) {
|
||||
raiseError(6);
|
||||
raiseError("Null entry uuid");
|
||||
}
|
||||
else {
|
||||
entry->setUuid(uuid);
|
||||
@ -656,7 +674,7 @@ Entry* KeePass2XmlReader::parseEntry(bool history)
|
||||
else if (m_xml.name() == "IconID") {
|
||||
int iconId = readNumber();
|
||||
if (iconId < 0) {
|
||||
raiseError(7);
|
||||
raiseError("Invalud entry icon number");
|
||||
}
|
||||
else {
|
||||
entry->setIcon(iconId);
|
||||
@ -697,7 +715,7 @@ Entry* KeePass2XmlReader::parseEntry(bool history)
|
||||
}
|
||||
else if (m_xml.name() == "History") {
|
||||
if (history) {
|
||||
raiseError(8);
|
||||
raiseError("History element in history entry");
|
||||
}
|
||||
else {
|
||||
historyItems = parseEntryHistory();
|
||||
@ -708,10 +726,8 @@ Entry* KeePass2XmlReader::parseEntry(bool history)
|
||||
}
|
||||
}
|
||||
|
||||
if (entry->uuid().isNull()) {
|
||||
raiseError(24);
|
||||
}
|
||||
else if (history) {
|
||||
if (!entry->uuid().isNull()) {
|
||||
if (history) {
|
||||
entry->setUpdateTimeinfo(false);
|
||||
}
|
||||
else {
|
||||
@ -723,6 +739,10 @@ Entry* KeePass2XmlReader::parseEntry(bool history)
|
||||
|
||||
delete tmpEntry;
|
||||
}
|
||||
}
|
||||
else if (!hasError()) {
|
||||
raiseError("No entry uuid found");
|
||||
}
|
||||
|
||||
Q_FOREACH (Entry* historyItem, historyItems) {
|
||||
entry->addHistoryItem(historyItem);
|
||||
@ -762,7 +782,7 @@ void KeePass2XmlReader::parseEntryString(Entry* entry)
|
||||
value = QString::fromUtf8(m_randomStream->process(QByteArray::fromBase64(value.toAscii())));
|
||||
}
|
||||
else {
|
||||
raiseError(9);
|
||||
raiseError("Unable to decrypt entry string");
|
||||
}
|
||||
}
|
||||
|
||||
@ -778,7 +798,7 @@ void KeePass2XmlReader::parseEntryString(Entry* entry)
|
||||
entry->attributes()->set(key, value, protect);
|
||||
}
|
||||
else {
|
||||
raiseError(25);
|
||||
raiseError("Entry string key or value missing");
|
||||
}
|
||||
}
|
||||
|
||||
@ -827,7 +847,7 @@ QPair<QString, QString> KeePass2XmlReader::parseEntryBinary(Entry* entry)
|
||||
entry->attachments()->set(key, value);
|
||||
}
|
||||
else {
|
||||
raiseError(26);
|
||||
raiseError("Entry binary key or value missing");
|
||||
}
|
||||
|
||||
return poolRef;
|
||||
@ -882,7 +902,7 @@ void KeePass2XmlReader::parseAutoTypeAssoc(Entry* entry)
|
||||
entry->autoTypeAssociations()->add(assoc);
|
||||
}
|
||||
else {
|
||||
raiseError(27);
|
||||
raiseError("Auto-type association window or sequence missing");
|
||||
}
|
||||
}
|
||||
|
||||
@ -955,7 +975,7 @@ bool KeePass2XmlReader::readBool()
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
raiseError(10);
|
||||
raiseError("Invalid bool value");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -966,7 +986,7 @@ QDateTime KeePass2XmlReader::readDateTime()
|
||||
QDateTime dt = QDateTime::fromString(str, Qt::ISODate);
|
||||
|
||||
if (!dt.isValid()) {
|
||||
raiseError(11);
|
||||
raiseError("Invalid date time value");
|
||||
}
|
||||
|
||||
return dt;
|
||||
@ -981,7 +1001,7 @@ QColor KeePass2XmlReader::readColor()
|
||||
}
|
||||
|
||||
if (colorStr.length() != 7 || colorStr[0] != '#') {
|
||||
raiseError(12);
|
||||
raiseError("Invalid color value");
|
||||
return QColor();
|
||||
}
|
||||
|
||||
@ -991,7 +1011,7 @@ QColor KeePass2XmlReader::readColor()
|
||||
bool ok;
|
||||
int rgbPart = rgbPartStr.toInt(&ok, 16);
|
||||
if (!ok || rgbPart > 255) {
|
||||
raiseError(13);
|
||||
raiseError("Invalid color rgb part");
|
||||
return QColor();
|
||||
}
|
||||
|
||||
@ -1014,7 +1034,7 @@ int KeePass2XmlReader::readNumber()
|
||||
bool ok;
|
||||
int result = readString().toInt(&ok);
|
||||
if (!ok) {
|
||||
raiseError(14);
|
||||
raiseError("Invalid number value");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@ -1023,7 +1043,7 @@ Uuid KeePass2XmlReader::readUuid()
|
||||
{
|
||||
QByteArray uuidBin = readBinary();
|
||||
if (uuidBin.length() != Uuid::Length) {
|
||||
raiseError(15);
|
||||
raiseError("Invalid uuid value");
|
||||
return Uuid();
|
||||
}
|
||||
else {
|
||||
@ -1049,7 +1069,7 @@ QByteArray KeePass2XmlReader::readCompressedBinary()
|
||||
|
||||
QByteArray result;
|
||||
if (!Tools::readAllFromDevice(&compressor, result)) {
|
||||
raiseError(16);
|
||||
raiseError("Unable to decompress binary");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@ -1092,11 +1112,6 @@ Entry* KeePass2XmlReader::getEntry(const Uuid& uuid)
|
||||
}
|
||||
}
|
||||
|
||||
void KeePass2XmlReader::raiseError(int internalNumber)
|
||||
{
|
||||
m_xml.raiseError(tr("Invalid database file (error no %1).").arg(QString::number(internalNumber)));
|
||||
}
|
||||
|
||||
void KeePass2XmlReader::skipCurrentElement()
|
||||
{
|
||||
qWarning("KeePass2XmlReader::skipCurrentElement: skip element \"%s\"", qPrintable(m_xml.name().toString()));
|
||||
|
@ -80,7 +80,7 @@ private:
|
||||
|
||||
Group* getGroup(const Uuid& uuid);
|
||||
Entry* getEntry(const Uuid& uuid);
|
||||
void raiseError(int internalNumber);
|
||||
void raiseError(const QString& errorMessage);
|
||||
void skipCurrentElement();
|
||||
|
||||
QXmlStreamReader m_xml;
|
||||
@ -93,6 +93,8 @@ private:
|
||||
QHash<QString, QByteArray> m_binaryPool;
|
||||
QHash<QString, QPair<Entry*, QString> > m_binaryMap;
|
||||
QByteArray m_headerHash;
|
||||
bool m_error;
|
||||
QString m_errorStr;
|
||||
};
|
||||
|
||||
#endif // KEEPASSX_KEEPASS2XMLREADER_H
|
||||
|
@ -129,18 +129,21 @@ bool HashedBlockStream::readHashedBlock()
|
||||
quint32 index = Endian::readUInt32(m_baseDevice, ByteOrder, &ok);
|
||||
if (!ok || index != m_blockIndex) {
|
||||
m_error = true;
|
||||
setErrorString("Invalid block index.");
|
||||
return false;
|
||||
}
|
||||
|
||||
QByteArray hash = m_baseDevice->read(32);
|
||||
if (hash.size() != 32) {
|
||||
m_error = true;
|
||||
setErrorString("Invalid hash size.");
|
||||
return false;
|
||||
}
|
||||
|
||||
m_blockSize = Endian::readInt32(m_baseDevice, ByteOrder, &ok);
|
||||
if (!ok || m_blockSize < 0) {
|
||||
m_error = true;
|
||||
setErrorString("Invalid block size.");
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -157,6 +160,7 @@ bool HashedBlockStream::readHashedBlock()
|
||||
m_buffer = m_baseDevice->read(m_blockSize);
|
||||
if (m_buffer.size() != m_blockSize) {
|
||||
m_error = true;
|
||||
setErrorString("Block too short.");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -273,7 +273,7 @@ void TestKeePass1Reader::reopenDatabase(Database* db, const QString& password, c
|
||||
|
||||
KeePass2Writer writer;
|
||||
writer.writeDatabase(&buffer, db);
|
||||
QVERIFY(!writer.error());
|
||||
QVERIFY(!writer.hasError());
|
||||
QVERIFY(buffer.seek(0));
|
||||
|
||||
CompositeKey key;
|
||||
|
@ -61,7 +61,7 @@ void TestKeePass2Writer::initTestCase()
|
||||
|
||||
KeePass2Writer writer;
|
||||
writer.writeDatabase(&buffer, m_dbOrg);
|
||||
QVERIFY(!writer.error());
|
||||
QVERIFY(!writer.hasError());
|
||||
buffer.seek(0);
|
||||
KeePass2Reader reader;
|
||||
m_dbTest = reader.readDatabase(&buffer, key);
|
||||
|
Loading…
Reference in New Issue
Block a user