From 9bdb41a72716edfe0a3113227a3b9b63b43b9e56 Mon Sep 17 00:00:00 2001 From: Kyle Manna Date: Mon, 26 May 2014 00:14:39 -0700 Subject: [PATCH 01/93] keys: Add ChallengeResponseKey header * Add initial header file for forthcoming challenge response support. * A ChallengeResponseKey operates by submitting some challenge data and getting a deterministic result. * In the case of the forthcoming YubiKey integration, the master seed is submitted as the challenge to the YubiKey hardware and the YubiKey returns a HMAC-SHA1 response. Signed-off-by: Kyle Manna --- src/keys/ChallengeResponseKey.h | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 src/keys/ChallengeResponseKey.h diff --git a/src/keys/ChallengeResponseKey.h b/src/keys/ChallengeResponseKey.h new file mode 100644 index 00000000..e03a2f9f --- /dev/null +++ b/src/keys/ChallengeResponseKey.h @@ -0,0 +1,32 @@ +/* +* Copyright (C) 2014 Kyle Manna +* +* 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 +* the Free Software Foundation, either version 2 or (at your option) +* version 3 of the License. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +*/ + +#ifndef KEEPASSX_CHALLENGE_RESPONSE_KEY_H +#define KEEPASSX_CHALLENGE_RESPONSE_KEY_H + +#include + +class ChallengeResponseKey +{ +public: + virtual ~ChallengeResponseKey() {} + virtual QByteArray rawKey() const = 0; + virtual ChallengeResponseKey* clone() const = 0; + virtual bool challenge(const QByteArray& challenge) = 0; +}; + +#endif // KEEPASSX_CHALLENGE_RESPONSE_KEY_H From ccd6704b8f2b0fac4101f15baeb18c01cdf09192 Mon Sep 17 00:00:00 2001 From: Kyle Manna Date: Mon, 26 May 2014 01:38:07 -0700 Subject: [PATCH 02/93] keys: CompositeKey: Add ChallengeResponseKey support * Each Challenge Response Key consists of a list of regular keys and now challenge response keys. * Copy ChallengeResponseKeys when copying the object. * Challenge consists of challenging each driver in the list and hashing the concatenated data result using SHA256. Signed-off-by: Kyle Manna --- src/keys/CompositeKey.cpp | 30 +++++++++++++++++++++++++++++- src/keys/CompositeKey.h | 5 +++++ 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/src/keys/CompositeKey.cpp b/src/keys/CompositeKey.cpp index 16b48592..2270d96e 100644 --- a/src/keys/CompositeKey.cpp +++ b/src/keys/CompositeKey.cpp @@ -17,6 +17,7 @@ #include "CompositeKey.h" #include "CompositeKey_p.h" +#include "ChallengeResponseKey.h" #include #include @@ -47,7 +48,7 @@ void CompositeKey::clear() bool CompositeKey::isEmpty() const { - return m_keys.isEmpty(); + return m_keys.isEmpty() && m_challengeResponseKeys.isEmpty(); } CompositeKey* CompositeKey::clone() const @@ -67,6 +68,9 @@ CompositeKey& CompositeKey::operator=(const CompositeKey& key) for (const Key* subKey : asConst(key.m_keys)) { addKey(*subKey); } + Q_FOREACH (const ChallengeResponseKey* subKey, key.m_challengeResponseKeys) { + addChallengeResponseKey(*subKey); + } return *this; } @@ -142,11 +146,35 @@ QByteArray CompositeKey::transformKeyRaw(const QByteArray& key, const QByteArray return result; } +QByteArray CompositeKey::challenge(const QByteArray& seed) const +{ + /* If no challenge response was requested, return nothing to + * maintain backwards compatability with regular databases. + */ + if (m_challengeResponseKeys.length() == 0) { + return QByteArray(); + } + + CryptoHash cryptoHash(CryptoHash::Sha256); + + Q_FOREACH (ChallengeResponseKey* key, m_challengeResponseKeys) { + key->challenge(seed); + cryptoHash.addData(key->rawKey()); + } + + return cryptoHash.result(); +} + void CompositeKey::addKey(const Key& key) { m_keys.append(key.clone()); } +void CompositeKey::addChallengeResponseKey(const ChallengeResponseKey& key) +{ + m_challengeResponseKeys.append(key.clone()); +} + int CompositeKey::transformKeyBenchmark(int msec) { TransformKeyBenchmarkThread thread1(msec); diff --git a/src/keys/CompositeKey.h b/src/keys/CompositeKey.h index 3290d367..66ad91ad 100644 --- a/src/keys/CompositeKey.h +++ b/src/keys/CompositeKey.h @@ -21,6 +21,7 @@ #include #include "keys/Key.h" +#include "keys/ChallengeResponseKey.h" class CompositeKey : public Key { @@ -36,7 +37,10 @@ public: QByteArray rawKey() const; QByteArray transform(const QByteArray& seed, quint64 rounds, bool* ok, QString* errorString) const; + QByteArray challenge(const QByteArray& seed) const; + void addKey(const Key& key); + void addChallengeResponseKey(const ChallengeResponseKey& key); static int transformKeyBenchmark(int msec); @@ -45,6 +49,7 @@ private: quint64 rounds, bool* ok, QString* errorString); QList m_keys; + QList m_challengeResponseKeys; }; #endif // KEEPASSX_COMPOSITEKEY_H From e354a0ee0eb01bedf18931fb0f65bdfb50f0d66f Mon Sep 17 00:00:00 2001 From: Kyle Manna Date: Mon, 26 May 2014 01:40:38 -0700 Subject: [PATCH 03/93] database: Pass master seed to challenge response keys * Pass the master seed from the database to CompositeKey::challenge() function which will in turn issue challenges to all selected drivers. Signed-off-by: Kyle Manna --- src/core/Database.cpp | 5 +++++ src/core/Database.h | 1 + 2 files changed, 6 insertions(+) diff --git a/src/core/Database.cpp b/src/core/Database.cpp index 33682038..db13e249 100644 --- a/src/core/Database.cpp +++ b/src/core/Database.cpp @@ -176,6 +176,11 @@ QByteArray Database::transformedMasterKey() const return m_data.transformedMasterKey; } +QByteArray Database::challengeMasterSeed(const QByteArray& masterSeed) const +{ + return m_data.key.challenge(masterSeed); +} + void Database::setCipher(const Uuid& cipher) { Q_ASSERT(!cipher.isNull()); diff --git a/src/core/Database.h b/src/core/Database.h index 3cd5ed1b..60779233 100644 --- a/src/core/Database.h +++ b/src/core/Database.h @@ -89,6 +89,7 @@ public: quint64 transformRounds() const; QByteArray transformedMasterKey() const; const CompositeKey & key() const; + QByteArray challengeMasterSeed(const QByteArray& masterSeed) const; void setCipher(const Uuid& cipher); void setCompressionAlgo(Database::CompressionAlgorithm algo); From add4846d799315d4149b96ca09c65db0dd7675eb Mon Sep 17 00:00:00 2001 From: Kyle Manna Date: Mon, 26 May 2014 00:29:41 -0700 Subject: [PATCH 04/93] format: Add challenge response result to final key hash * The challengeMasterSeed() function return empty if not present maintaining backwards compatability. * This commit is where the challenge response result is computed into the final key used to encrypt or decrypt the database. Signed-off-by: Kyle Manna --- src/format/KeePass2Reader.cpp | 1 + src/format/KeePass2Writer.cpp | 1 + 2 files changed, 2 insertions(+) diff --git a/src/format/KeePass2Reader.cpp b/src/format/KeePass2Reader.cpp index b45cefa6..17e007d7 100644 --- a/src/format/KeePass2Reader.cpp +++ b/src/format/KeePass2Reader.cpp @@ -115,6 +115,7 @@ Database* KeePass2Reader::readDatabase(QIODevice* device, const CompositeKey& ke CryptoHash hash(CryptoHash::Sha256); hash.addData(m_masterSeed); + hash.addData(m_db->challengeMasterSeed(m_masterSeed)); hash.addData(m_db->transformedMasterKey()); QByteArray finalKey = hash.result(); diff --git a/src/format/KeePass2Writer.cpp b/src/format/KeePass2Writer.cpp index dfbbf353..3a3195a0 100644 --- a/src/format/KeePass2Writer.cpp +++ b/src/format/KeePass2Writer.cpp @@ -53,6 +53,7 @@ void KeePass2Writer::writeDatabase(QIODevice* device, Database* db) CryptoHash hash(CryptoHash::Sha256); hash.addData(masterSeed); + hash.addData(db->challengeMasterSeed(masterSeed)); Q_ASSERT(!db->transformedMasterKey().isEmpty()); hash.addData(db->transformedMasterKey()); QByteArray finalKey = hash.result(); From 82aed2caabf94836a13dfb476601b5640e60d4c2 Mon Sep 17 00:00:00 2001 From: Kyle Manna Date: Mon, 26 May 2014 00:41:54 -0700 Subject: [PATCH 05/93] keys: yk: Add YubiKey hardware driver support * Use compile time detection of the YubiKey libraries and link against the libraries if present. Can be disabled with: $ cmake -DCMAKE_DISABLE_FIND_PACKAGE_YubiKey=FALSE * A stub file provides empty calls for all the function calls integrated in to the UI to support this. In the future a more modular approach maybe better, but opting for simplicity initially. Signed-off-by: Kyle Manna --- CMakeLists.txt | 8 + cmake/FindYubiKey.cmake | 29 ++++ src/CMakeLists.txt | 11 ++ src/keys/drivers/YubiKey.cpp | 245 +++++++++++++++++++++++++++++++ src/keys/drivers/YubiKey.h | 70 +++++++++ src/keys/drivers/YubiKeyStub.cpp | 71 +++++++++ 6 files changed, 434 insertions(+) create mode 100644 cmake/FindYubiKey.cmake create mode 100644 src/keys/drivers/YubiKey.cpp create mode 100644 src/keys/drivers/YubiKey.h create mode 100644 src/keys/drivers/YubiKeyStub.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 883f462e..ff7b05e5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -191,6 +191,14 @@ if(NOT ZLIB_SUPPORTS_GZIP) message(FATAL_ERROR "zlib 1.2.x or higher is required to use the gzip format") endif() +# Optional +find_package(YubiKey) + +if(YUBIKEY_FOUND) + include_directories(SYSTEM ${YUBIKEY_INCLUDE_DIRS}) +endif() + + if(UNIX) check_cxx_source_compiles("#include int main() { prctl(PR_SET_DUMPABLE, 0); return 0; }" diff --git a/cmake/FindYubiKey.cmake b/cmake/FindYubiKey.cmake new file mode 100644 index 00000000..297b6838 --- /dev/null +++ b/cmake/FindYubiKey.cmake @@ -0,0 +1,29 @@ +# Copyright (C) 2014 Kyle Manna +# +# 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 +# the Free Software Foundation, either version 2 or (at your option) +# version 3 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +find_path(YUBIKEY_CORE_INCLUDE_DIR yubikey.h) +find_path(YUBIKEY_PERS_INCLUDE_DIR ykcore.h PATH_SUFFIXES ykpers-1) +set(YUBIKEY_INCLUDE_DIRS ${YUBIKEY_CORE_INCLUDE_DIR} ${YUBIKEY_PERS_INCLUDE_DIR}) + +find_library(YUBIKEY_CORE_LIBRARY yubikey) +find_library(YUBIKEY_PERS_LIBRARY ykpers-1) +set(YUBIKEY_LIBRARIES ${YUBIKEY_CORE_LIBRARY} ${YUBIKEY_PERS_LIBRARY}) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(YubiKey DEFAULT_MSG YUBIKEY_LIBRARIES YUBIKEY_INCLUDE_DIRS) + +# TODO: Is mark_as_advanced() necessary? It's used in many examples with +# little explanation. Disable for now in favor of simplicity. +#mark_as_advanced(YUBIKEY_LIBRARIES YUBIKEY_INCLUDE_DIRS) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 30332c71..5e5e7d58 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -111,6 +111,7 @@ set(keepassx_SOURCES gui/group/GroupView.cpp keys/CompositeKey.cpp keys/CompositeKey_p.h + keys/drivers/YubiKey.h keys/FileKey.cpp keys/Key.h keys/PasswordKey.cpp @@ -190,6 +191,12 @@ if(MINGW) ${CMAKE_SOURCE_DIR}/share/windows/icon.rc) endif() +if(YUBIKEY_FOUND) + set(keepassx_SOURCES ${keepassx_SOURCES} keys/drivers/YubiKey.cpp) +else() + set(keepassx_SOURCES ${keepassx_SOURCES} keys/drivers/YubiKeyStub.cpp) +endif() + qt5_wrap_ui(keepassx_SOURCES ${keepassx_FORMS}) add_library(zxcvbn STATIC zxcvbn/zxcvbn.cpp) @@ -220,6 +227,10 @@ target_link_libraries(${PROGNAME} ${GCRYPT_LIBRARIES} ${ZLIB_LIBRARIES}) +if(YUBIKEY_FOUND) + target_link_libraries(keepassx_core ${YUBIKEY_LIBRARIES}) +endif() + set_target_properties(${PROGNAME} PROPERTIES ENABLE_EXPORTS ON) if(APPLE) diff --git a/src/keys/drivers/YubiKey.cpp b/src/keys/drivers/YubiKey.cpp new file mode 100644 index 00000000..1d69bb5b --- /dev/null +++ b/src/keys/drivers/YubiKey.cpp @@ -0,0 +1,245 @@ +/* +* Copyright (C) 2014 Kyle Manna +* +* 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 +* the Free Software Foundation, either version 2 or (at your option) +* version 3 of the License. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +*/ + +#include + +#include + +#include +#include +#include +#include + +#include "core/Global.h" +#include "crypto/Random.h" + +#include "YubiKey.h" + +/* Cast the void pointer from the generalized class definition + * to the proper pointer type from the now included system headers + */ +#define m_yk (static_cast(m_yk_void)) +#define m_ykds (static_cast(m_ykds_void)) + +YubiKey::YubiKey() : m_yk_void(NULL), m_ykds_void(NULL) +{ +} + +YubiKey* YubiKey::m_instance(Q_NULLPTR); + +/** + * @brief YubiKey::instance - get instance of singleton + * @return + */ +YubiKey* YubiKey::instance() +{ + if (!m_instance) { + m_instance = new YubiKey(); + } + + return m_instance; +} + +/** + * @brief YubiKey::init - initialize yubikey library and hardware + * @return + */ +bool YubiKey::init() +{ + /* Previously initalized */ + if (m_yk != NULL && m_ykds != NULL) { + + if (yk_get_status(m_yk, m_ykds)) { + /* Still connected */ + return true; + } else { + /* Initialized but not connected anymore, re-init */ + deinit(); + } + } + + if (!yk_init()) { + return false; + } + + /* TODO: handle multiple attached hardware devices, currently own one */ + m_yk_void = static_cast(yk_open_first_key()); + if (m_yk == NULL) { + return false; + } + + m_ykds_void = static_cast(ykds_alloc()); + if (m_ykds == NULL) { + yk_close_key(m_yk); + m_yk_void = NULL; + return false; + } + + return true; +} + +/** + * @brief YubiKey::deinit - cleanup after init + * @return true on success + */ +bool YubiKey::deinit() +{ + if (m_yk) { + yk_close_key(m_yk); + m_yk_void = NULL; + } + + if (m_ykds) { + ykds_free(m_ykds); + m_ykds_void = NULL; + } + + return true; +} + +/** + * @brief YubiKey::detect - probe for attached YubiKeys + */ +void YubiKey::detect() +{ + if (init()) { + + for (int i = 1; i < 3; i++) { + YubiKey::ChallengeResult result; + QByteArray rand = randomGen()->randomArray(1); + QByteArray resp; + + result = challenge(i, false, rand, resp); + + if (result != YubiKey::ERROR) { + Q_EMIT detected(i, result == YubiKey::WOULDBLOCK ? true : false); + } + } + } +} + +/** + * @brief YubiKey::getSerial - serial number of yubikey + * @param serial + * @return + */ +bool YubiKey::getSerial(unsigned int& serial) const +{ + if (!yk_get_serial(m_yk, 1, 0, &serial)) { + return false; + } + + return true; +} + +#ifdef QT_DEBUG +/** + * @brief printByteArray - debug raw data + * @param a array input + * @return string representation of array + */ +static inline QString printByteArray(const QByteArray& a) +{ + QString s; + for (int i = 0; i < a.size(); i++) + s.append(QString::number(a[i] & 0xff, 16).rightJustified(2, '0')); + return s; +} +#endif + +/** + * @brief YubiKey::challenge - issue a challenge + * + * This operation could block if the YubiKey requires a touch to trigger. + * + * TODO: Signal to the UI that the system is waiting for challenge response + * touch. + * + * @param slot YubiKey configuration slot + * @param mayBlock operation is allowed to block + * @param chal challenge input to YubiKey + * @param resp response output from YubiKey + * @return SUCCESS when successful + */ +YubiKey::ChallengeResult YubiKey::challenge(int slot, bool mayBlock, + const QByteArray& chal, + QByteArray& resp) const +{ + int yk_cmd = (slot == 1) ? SLOT_CHAL_HMAC1 : SLOT_CHAL_HMAC2; + QByteArray paddedChal = chal; + + /* yk_challenge_response() insists on 64 byte response buffer */ + resp.resize(64); + + /* The challenge sent to the yubikey should always be 64 bytes for + * compatibility with all configurations. Follow PKCS7 padding. + * + * There is some question whether or not 64 byte fixed length + * configurations even work, some docs say avoid it. + */ + const int padLen = 64 - paddedChal.size(); + if (padLen > 0) { + paddedChal.append(QByteArray(padLen, padLen)); + } + + const unsigned char *c; + unsigned char *r; + c = reinterpret_cast(paddedChal.constData()); + r = reinterpret_cast(resp.data()); + +#ifdef QT_DEBUG + qDebug().nospace() << __func__ << "(" << slot << ") c = " + << printByteArray(paddedChal); +#endif + + int ret = yk_challenge_response(m_yk, yk_cmd, mayBlock, + paddedChal.size(), c, + resp.size(), r); + + if(!ret) { + if (yk_errno == YK_EWOULDBLOCK) { + return WOULDBLOCK; + } else if (yk_errno == YK_ETIMEOUT) { + return ERROR; + } else if (yk_errno) { + + /* Something went wrong, close the key, so that the next call to + * can try to re-open. + * + * Likely caused by the YubiKey being unplugged. + */ + + if (yk_errno == YK_EUSBERR) { + qWarning() << "USB error:" << yk_usb_strerror(); + } else { + qWarning() << "YubiKey core error:" << yk_strerror(yk_errno); + } + + return ERROR; + } + } + + /* Actual HMAC-SHA1 response is only 20 bytes */ + resp.resize(20); + +#ifdef QT_DEBUG + qDebug().nospace() << __func__ << "(" << slot << ") r = " + << printByteArray(resp) << ", ret = " << ret; +#endif + + return SUCCESS; +} diff --git a/src/keys/drivers/YubiKey.h b/src/keys/drivers/YubiKey.h new file mode 100644 index 00000000..492fba01 --- /dev/null +++ b/src/keys/drivers/YubiKey.h @@ -0,0 +1,70 @@ +/* +* Copyright (C) 2014 Kyle Manna +* +* 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 +* the Free Software Foundation, either version 2 or (at your option) +* version 3 of the License. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +*/ + +#ifndef KEEPASSX_YUBIKEY_H +#define KEEPASSX_YUBIKEY_H + +#include + +/** + * Singleton class to manage the interface to the hardware + */ +class YubiKey : public QObject +{ + Q_OBJECT + +public: + enum ChallengeResult { ERROR = -1, SUCCESS = 0, WOULDBLOCK }; + + static YubiKey* instance(); + + /** Initialize the underlying yubico libraries */ + bool init(); + bool deinit(); + + /** Issue a challenge to the hardware */ + ChallengeResult challenge(int slot, bool mayBlock, + const QByteArray& chal, + QByteArray& resp) const; + + /** Read the serial number from the hardware */ + bool getSerial(unsigned int& serial) const; + + /** Start looking for attached hardware devices */ + void detect(); + +Q_SIGNALS: + /** Emitted in response to detect() when a device is found + * + * @slot is the slot number detected + * @blocking signifies if the YK is setup in passive mode or if requires + * the user to touch it for a response + */ + void detected(int slot, bool blocking); + +private: + explicit YubiKey(); + static YubiKey* m_instance; + + /* Create void ptr here to avoid ifdef header include mess */ + void *m_yk_void; + void *m_ykds_void; + + Q_DISABLE_COPY(YubiKey) +}; + +#endif // KEEPASSX_YUBIKEY_H diff --git a/src/keys/drivers/YubiKeyStub.cpp b/src/keys/drivers/YubiKeyStub.cpp new file mode 100644 index 00000000..c00790f3 --- /dev/null +++ b/src/keys/drivers/YubiKeyStub.cpp @@ -0,0 +1,71 @@ +/* +* Copyright (C) 2014 Kyle Manna +* +* 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 +* the Free Software Foundation, either version 2 or (at your option) +* version 3 of the License. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +*/ + +#include + +#include "core/Global.h" +#include "crypto/Random.h" + +#include "YubiKey.h" + +YubiKey::YubiKey() : m_yk_void(NULL), m_ykds_void(NULL) +{ +} + +YubiKey* YubiKey::m_instance(Q_NULLPTR); + +YubiKey* YubiKey::instance() +{ + if (!m_instance) { + m_instance = new YubiKey(); + } + + return m_instance; +} + +bool YubiKey::init() +{ + return false; +} + +bool YubiKey::deinit() +{ + return false; +} + +void YubiKey::detect() +{ +} + +bool YubiKey::getSerial(unsigned int& serial) const +{ + Q_UNUSED(serial); + + return false; +} + +YubiKey::ChallengeResult YubiKey::challenge(int slot, bool mayBlock, + const QByteArray& chal, + QByteArray& resp) const +{ + Q_UNUSED(slot); + Q_UNUSED(mayBlock); + Q_UNUSED(chal); + Q_UNUSED(resp); + + return ERROR; +} From 5b8b4c8c7b877227cb7b6602450aaaa3e859a14e Mon Sep 17 00:00:00 2001 From: Kyle Manna Date: Mon, 26 May 2014 02:06:26 -0700 Subject: [PATCH 06/93] keys: yk: Implement ChallengeResponseKey for YubiKey * Implement a YubiKey challenge response class. One object will be created for each challenge response key available. Signed-off-by: Kyle Manna --- src/CMakeLists.txt | 1 + src/keys/YkChallengeResponseKey.cpp | 72 +++++++++++++++++++++++++++++ src/keys/YkChallengeResponseKey.h | 44 ++++++++++++++++++ 3 files changed, 117 insertions(+) create mode 100644 src/keys/YkChallengeResponseKey.cpp create mode 100644 src/keys/YkChallengeResponseKey.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 5e5e7d58..cf4d593a 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -115,6 +115,7 @@ set(keepassx_SOURCES keys/FileKey.cpp keys/Key.h keys/PasswordKey.cpp + keys/YkChallengeResponseKey.cpp streams/HashedBlockStream.cpp streams/LayeredStream.cpp streams/qtiocompressor.cpp diff --git a/src/keys/YkChallengeResponseKey.cpp b/src/keys/YkChallengeResponseKey.cpp new file mode 100644 index 00000000..5f495a34 --- /dev/null +++ b/src/keys/YkChallengeResponseKey.cpp @@ -0,0 +1,72 @@ +/* +* Copyright (C) 2014 Kyle Manna +* +* 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 +* the Free Software Foundation, either version 2 or (at your option) +* version 3 of the License. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +*/ + + +#include +#include + +#include "core/Tools.h" +#include "crypto/CryptoHash.h" +#include "crypto/Random.h" + +#include "keys/YkChallengeResponseKey.h" +#include "keys/drivers/YubiKey.h" + +YkChallengeResponseKey::YkChallengeResponseKey(int slot, + bool blocking) + : m_slot(slot), + m_blocking(blocking) +{ +} + +QByteArray YkChallengeResponseKey::rawKey() const +{ + return m_key; +} + +YkChallengeResponseKey* YkChallengeResponseKey::clone() const +{ + return new YkChallengeResponseKey(*this); +} + + +/** Assumes yubikey()->init() was called */ +bool YkChallengeResponseKey::challenge(const QByteArray& chal) +{ + if (YubiKey::instance()->challenge(m_slot, true, chal, m_key) != YubiKey::ERROR) { + return true; + } + + return false; +} + +QString YkChallengeResponseKey::getName() const +{ + unsigned int serial; + QString fmt("YubiKey[%1] Challenge Response - Slot %2 - %3"); + + YubiKey::instance()->getSerial(serial); + + return fmt.arg(QString::number(serial), + QString::number(m_slot), + (m_blocking) ? "Press" : "Passive"); +} + +bool YkChallengeResponseKey::isBlocking() const +{ + return m_blocking; +} diff --git a/src/keys/YkChallengeResponseKey.h b/src/keys/YkChallengeResponseKey.h new file mode 100644 index 00000000..a5236748 --- /dev/null +++ b/src/keys/YkChallengeResponseKey.h @@ -0,0 +1,44 @@ +/* +* Copyright (C) 2011 Felix Geyer +* +* 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 +* the Free Software Foundation, either version 2 or (at your option) +* version 3 of the License. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +*/ + +#ifndef KEEPASSX_YK_CHALLENGERESPONSEKEY_H +#define KEEPASSX_YK_CHALLENGERESPONSEKEY_H + +#include "core/Global.h" +#include "keys/ChallengeResponseKey.h" +#include "keys/drivers/YubiKey.h" + +class YkChallengeResponseKey : public ChallengeResponseKey +{ +public: + + YkChallengeResponseKey(int slot = -1, + bool blocking = false); + + QByteArray rawKey() const; + YkChallengeResponseKey* clone() const; + bool challenge(const QByteArray& challenge); + QString getName() const; + bool isBlocking() const; + +private: + QByteArray m_key; + int m_slot; + bool m_blocking; +}; + +#endif // KEEPASSX_YK_CHALLENGERESPONSEKEY_H From 9556d8e6dad2143a8af0bbd9620ae06f8329c953 Mon Sep 17 00:00:00 2001 From: Kyle Manna Date: Mon, 26 May 2014 00:46:41 -0700 Subject: [PATCH 07/93] tests: Add YubiKey Tests * Basic testing for YubiKey code. Signed-off-by: Kyle Manna --- tests/CMakeLists.txt | 8 ++ tests/TestYkChallengeResponseKey.cpp | 108 +++++++++++++++++++++++++++ tests/TestYkChallengeResponseKey.h | 54 ++++++++++++++ 3 files changed, 170 insertions(+) create mode 100644 tests/TestYkChallengeResponseKey.cpp create mode 100644 tests/TestYkChallengeResponseKey.h diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 0ea73b2f..7edf8939 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -99,6 +99,10 @@ set(testsupport_SOURCES modeltest.cpp FailDevice.cpp) add_library(testsupport STATIC ${testsupport_SOURCES}) target_link_libraries(testsupport ${MHD_LIBRARIES} Qt5::Core Qt5::Concurrent Qt5::Widgets Qt5::Test) +if(YUBIKEY_FOUND) + set(TEST_LIBRARIES ${TEST_LIBRARIES} ${YUBIKEY_LIBRARIES}) +endif() + add_unit_test(NAME testgroup SOURCES TestGroup.cpp LIBS ${TEST_LIBRARIES}) @@ -165,6 +169,10 @@ add_unit_test(NAME testexporter SOURCES TestExporter.cpp add_unit_test(NAME testcsvexporter SOURCES TestCsvExporter.cpp LIBS ${TEST_LIBRARIES}) +add_unit_test(NAME testykchallengeresponsekey + SOURCES TestYkChallengeResponseKey.cpp TestYkChallengeResponseKey.h + LIBS ${TEST_LIBRARIES}) + if(WITH_GUI_TESTS) add_subdirectory(gui) endif(WITH_GUI_TESTS) diff --git a/tests/TestYkChallengeResponseKey.cpp b/tests/TestYkChallengeResponseKey.cpp new file mode 100644 index 00000000..91fba83c --- /dev/null +++ b/tests/TestYkChallengeResponseKey.cpp @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2014 Kyle Manna + * + * + * 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 + * the Free Software Foundation, either version 2 or (at your option) + * version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "TestYkChallengeResponseKey.h" + +#include +#include + +#include "keys/YkChallengeResponseKey.h" + +QTEST_GUILESS_MAIN(TestYubiKeyChalResp) + +void TestYubiKeyChalResp::initTestCase() +{ + m_detected = 0; + m_key = NULL; +} + +void TestYubiKeyChalResp::cleanupTestCase() +{ + if (m_key) + delete m_key; +} + +void TestYubiKeyChalResp::init() +{ + bool result = YubiKey::instance()->init(); + + if (!result) { + QSKIP("Unable to connect to YubiKey", SkipAll); + } +} + +void TestYubiKeyChalResp::detectDevices() +{ + connect(YubiKey::instance(), SIGNAL(detected(int,bool)), + SLOT(ykDetected(int,bool)), + Qt::QueuedConnection); + QtConcurrent::run(YubiKey::instance(), &YubiKey::detect); + + /* Need to wait for the hardware (that's hopefully plugged in)... */ + QTest::qWait(2000); + QVERIFY2(m_detected > 0, "Is a YubiKey attached?"); +} + +void TestYubiKeyChalResp::getSerial() +{ + unsigned int serial; + QVERIFY(YubiKey::instance()->getSerial(serial)); +} + +void TestYubiKeyChalResp::keyGetName() +{ + QVERIFY(m_key); + QVERIFY(m_key->getName().length() > 0); +} + +void TestYubiKeyChalResp::keyIssueChallenge() +{ + QVERIFY(m_key); + if (m_key->isBlocking()) { + /* Testing active mode in unit tests is unreasonable */ + QSKIP("YubiKey not in passive mode", SkipSingle); + } + + QByteArray ba("UnitTest"); + QVERIFY(m_key->challenge(ba)); + + /* TODO Determine if it's reasonable to provide a fixed secret key for + * verification testing. Obviously simple technically, but annoying + * if devs need to re-program their yubikeys or have a spare test key + * for unit tests to past. + * + * Might be worth it for integrity verification though. + */ +} + +void TestYubiKeyChalResp::ykDetected(int slot, bool blocking) +{ + Q_UNUSED(blocking); + + if (slot > 0) + m_detected++; + + /* Key used for later testing */ + if (!m_key) + m_key = new YkChallengeResponseKey(slot, blocking); +} + +void TestYubiKeyChalResp::deinit() +{ + QVERIFY(YubiKey::instance()->deinit()); +} diff --git a/tests/TestYkChallengeResponseKey.h b/tests/TestYkChallengeResponseKey.h new file mode 100644 index 00000000..4699b910 --- /dev/null +++ b/tests/TestYkChallengeResponseKey.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2014 Kyle Manna + * + * 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 + * the Free Software Foundation, either version 2 or (at your option) + * version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef KEEPASSX_TESTYUBIKEYCHALRESP_H +#define KEEPASSX_TESTYUBIKEYCHALRESP_H + +#include + +#include "keys/YkChallengeResponseKey.h" + +class TestYubiKeyChalResp: public QObject +{ + Q_OBJECT + +private Q_SLOTS: + void initTestCase(); + void cleanupTestCase(); + + void init(); + + /* Order is important! + * Need to init and detectDevices() before proceeding + */ + void detectDevices(); + + void getSerial(); + void keyGetName(); + void keyIssueChallenge(); + + void deinit(); + + /* Callback for detectDevices() */ + void ykDetected(int slot, bool blocking); + +private: + int m_detected; + YkChallengeResponseKey *m_key; +}; + +#endif // KEEPASSX_TESTYUBIKEYCHALRESP_H From ba8fd256045ae1ec83b8aa00b1dce2fd44771102 Mon Sep 17 00:00:00 2001 From: Kyle Manna Date: Mon, 26 May 2014 00:49:28 -0700 Subject: [PATCH 08/93] gui: Add YubiKey support to widgets * Add YubiKey support to the GUI widgets. Signed-off-by: Kyle Manna --- src/gui/ChangeMasterKeyWidget.cpp | 29 ++++++++++++++++++ src/gui/ChangeMasterKeyWidget.h | 1 + src/gui/ChangeMasterKeyWidget.ui | 25 ++++++++++++++++ src/gui/DatabaseOpenWidget.cpp | 49 +++++++++++++++++++++++++++++++ src/gui/DatabaseOpenWidget.h | 3 ++ src/gui/DatabaseOpenWidget.ui | 17 +++++++++++ 6 files changed, 124 insertions(+) diff --git a/src/gui/ChangeMasterKeyWidget.cpp b/src/gui/ChangeMasterKeyWidget.cpp index 3e346bc1..c69cf2dc 100644 --- a/src/gui/ChangeMasterKeyWidget.cpp +++ b/src/gui/ChangeMasterKeyWidget.cpp @@ -15,14 +15,18 @@ * along with this program. If not, see . */ +#include + #include "ChangeMasterKeyWidget.h" #include "ui_ChangeMasterKeyWidget.h" #include "core/FilePath.h" #include "keys/FileKey.h" #include "keys/PasswordKey.h" +#include "keys/YkChallengeResponseKey.h" #include "gui/FileDialog.h" #include "gui/MessageBox.h" +#include "crypto/Random.h" ChangeMasterKeyWidget::ChangeMasterKeyWidget(QWidget* parent) : DialogyWidget(parent) @@ -81,6 +85,15 @@ void ChangeMasterKeyWidget::clearForms() m_ui->togglePasswordButton->setChecked(false); // TODO: clear m_ui->keyFileCombo + m_ui->challengeResponseGroup->setChecked(false); + m_ui->challengeResponseCombo->clear(); + + /* YubiKey init is slow */ + connect(YubiKey::instance(), SIGNAL(detected(int,bool)), + SLOT(ykDetected(int,bool)), + Qt::QueuedConnection); + QtConcurrent::run(YubiKey::instance(), &YubiKey::detect); + m_ui->enterPasswordEdit->setFocus(); } @@ -128,6 +141,14 @@ void ChangeMasterKeyWidget::generateKey() m_key.addKey(fileKey); } + if (m_ui->challengeResponseGroup->isChecked()) { + int i = m_ui->challengeResponseCombo->currentIndex(); + i = m_ui->challengeResponseCombo->itemData(i).toInt(); + YkChallengeResponseKey key(i); + + m_key.addChallengeResponseKey(key); + } + Q_EMIT editFinished(true); } @@ -136,3 +157,11 @@ void ChangeMasterKeyWidget::reject() { Q_EMIT editFinished(false); } + + +void ChangeMasterKeyWidget::ykDetected(int slot, bool blocking) +{ + YkChallengeResponseKey yk(slot, blocking); + m_ui->challengeResponseCombo->addItem(yk.getName(), QVariant(slot)); + m_ui->challengeResponseGroup->setEnabled(true); +} diff --git a/src/gui/ChangeMasterKeyWidget.h b/src/gui/ChangeMasterKeyWidget.h index 8985ff7a..9b765c20 100644 --- a/src/gui/ChangeMasterKeyWidget.h +++ b/src/gui/ChangeMasterKeyWidget.h @@ -47,6 +47,7 @@ private Q_SLOTS: void reject(); void createKeyFile(); void browseKeyFile(); + void ykDetected(int slot, bool blocking); private: const QScopedPointer m_ui; diff --git a/src/gui/ChangeMasterKeyWidget.ui b/src/gui/ChangeMasterKeyWidget.ui index d14941cc..335a67c9 100644 --- a/src/gui/ChangeMasterKeyWidget.ui +++ b/src/gui/ChangeMasterKeyWidget.ui @@ -123,6 +123,31 @@ + + + + false + + + Challenge Response + + + true + + + + + + + + + Challenge Response: + + + + + + diff --git a/src/gui/DatabaseOpenWidget.cpp b/src/gui/DatabaseOpenWidget.cpp index 781b836f..5d381596 100644 --- a/src/gui/DatabaseOpenWidget.cpp +++ b/src/gui/DatabaseOpenWidget.cpp @@ -15,6 +15,8 @@ * along with this program. If not, see . */ +#include + #include "DatabaseOpenWidget.h" #include "ui_DatabaseOpenWidget.h" @@ -27,6 +29,9 @@ #include "format/KeePass2Reader.h" #include "keys/FileKey.h" #include "keys/PasswordKey.h" +#include "keys/YkChallengeResponseKey.h" +#include "crypto/Random.h" + DatabaseOpenWidget::DatabaseOpenWidget(QWidget* parent) : DialogyWidget(parent) @@ -49,6 +54,13 @@ DatabaseOpenWidget::DatabaseOpenWidget(QWidget* parent) connect(m_ui->editPassword, SIGNAL(textChanged(QString)), SLOT(activatePassword())); connect(m_ui->comboKeyFile, SIGNAL(editTextChanged(QString)), SLOT(activateKeyFile())); + connect(m_ui->comboChallengeResponse, SIGNAL(activated(int)), SLOT(activateChallengeResponse())); + + connect(m_ui->checkPassword, SIGNAL(toggled(bool)), SLOT(setOkButtonEnabled())); + connect(m_ui->checkKeyFile, SIGNAL(toggled(bool)), SLOT(setOkButtonEnabled())); + connect(m_ui->comboKeyFile, SIGNAL(editTextChanged(QString)), SLOT(setOkButtonEnabled())); + connect(m_ui->checkChallengeResponse, SIGNAL(toggled(bool)), SLOT(setOkButtonEnabled())); + connect(m_ui->comboChallengeResponse, SIGNAL(activated(int)), SLOT(setOkButtonEnabled())); connect(m_ui->buttonBox, SIGNAL(accepted()), SLOT(openDatabase())); connect(m_ui->buttonBox, SIGNAL(rejected()), SLOT(reject())); @@ -79,6 +91,13 @@ void DatabaseOpenWidget::load(const QString& filename) } m_ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(true); + + /* YubiKey init is slow */ + connect(YubiKey::instance(), SIGNAL(detected(int,bool)), + SLOT(ykDetected(int,bool)), + Qt::QueuedConnection); + QtConcurrent::run(YubiKey::instance(), &YubiKey::detect); + m_ui->editPassword->setFocus(); } @@ -156,6 +175,15 @@ CompositeKey DatabaseOpenWidget::databaseKey() config()->set("LastKeyFiles", lastKeyFiles); } + + if (m_ui->checkChallengeResponse->isChecked()) { + int i = m_ui->comboChallengeResponse->currentIndex(); + i = m_ui->comboChallengeResponse->itemData(i).toInt(); + YkChallengeResponseKey key(i); + + masterKey.addChallengeResponseKey(key); + } + return masterKey; } @@ -174,6 +202,19 @@ void DatabaseOpenWidget::activateKeyFile() m_ui->checkKeyFile->setChecked(true); } +void DatabaseOpenWidget::activateChallengeResponse() +{ + m_ui->checkChallengeResponse->setChecked(true); +} + +void DatabaseOpenWidget::setOkButtonEnabled() +{ + bool enable = m_ui->checkPassword->isChecked() || m_ui->checkChallengeResponse->isChecked() + || (m_ui->checkKeyFile->isChecked() && !m_ui->comboKeyFile->currentText().isEmpty()); + + m_ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(enable); +} + void DatabaseOpenWidget::browseKeyFile() { QString filters = QString("%1 (*);;%2 (*.key)").arg(tr("All files"), tr("Key files")); @@ -183,3 +224,11 @@ void DatabaseOpenWidget::browseKeyFile() m_ui->comboKeyFile->lineEdit()->setText(filename); } } + +void DatabaseOpenWidget::ykDetected(int slot, bool blocking) +{ + YkChallengeResponseKey yk(slot, blocking); + m_ui->comboChallengeResponse->addItem(yk.getName(), QVariant(slot)); + m_ui->comboChallengeResponse->setEnabled(true); + m_ui->checkChallengeResponse->setEnabled(true); +} diff --git a/src/gui/DatabaseOpenWidget.h b/src/gui/DatabaseOpenWidget.h index 34f401a0..fadb5ee7 100644 --- a/src/gui/DatabaseOpenWidget.h +++ b/src/gui/DatabaseOpenWidget.h @@ -55,7 +55,10 @@ protected Q_SLOTS: private Q_SLOTS: void activatePassword(); void activateKeyFile(); + void activateChallengeResponse(); + void setOkButtonEnabled(); void browseKeyFile(); + void ykDetected(int slot, bool blocking); protected: const QScopedPointer m_ui; diff --git a/src/gui/DatabaseOpenWidget.ui b/src/gui/DatabaseOpenWidget.ui index 4aae5faf..3d651ee8 100644 --- a/src/gui/DatabaseOpenWidget.ui +++ b/src/gui/DatabaseOpenWidget.ui @@ -111,6 +111,23 @@ + + + + false + + + Challenge Response: + + + + + + + false + + + From faa055010f247d37c73154f1ecbad00e6b632e9e Mon Sep 17 00:00:00 2001 From: Kyle Manna Date: Sat, 6 Sep 2014 17:49:39 -0700 Subject: [PATCH 09/93] challenge: Propagate failed challenge to caller * If a removed Yubikey is to blame, re-inserting the Yubikey won't resolve the issue. Hot plug isn't supported at this point. * The caller should detect the error and cancel the database write. Signed-off-by: Kyle Manna --- src/core/Database.cpp | 4 ++-- src/core/Database.h | 2 +- src/format/KeePass2Reader.cpp | 8 +++++++- src/format/KeePass2Writer.cpp | 8 +++++++- src/keys/CompositeKey.cpp | 12 ++++++++---- src/keys/CompositeKey.h | 2 +- 6 files changed, 26 insertions(+), 10 deletions(-) diff --git a/src/core/Database.cpp b/src/core/Database.cpp index db13e249..fd9e02dd 100644 --- a/src/core/Database.cpp +++ b/src/core/Database.cpp @@ -176,9 +176,9 @@ QByteArray Database::transformedMasterKey() const return m_data.transformedMasterKey; } -QByteArray Database::challengeMasterSeed(const QByteArray& masterSeed) const +bool Database::challengeMasterSeed(const QByteArray& masterSeed, QByteArray& result) const { - return m_data.key.challenge(masterSeed); + return m_data.key.challenge(masterSeed, result); } void Database::setCipher(const Uuid& cipher) diff --git a/src/core/Database.h b/src/core/Database.h index 60779233..e2eb0fb1 100644 --- a/src/core/Database.h +++ b/src/core/Database.h @@ -89,7 +89,7 @@ public: quint64 transformRounds() const; QByteArray transformedMasterKey() const; const CompositeKey & key() const; - QByteArray challengeMasterSeed(const QByteArray& masterSeed) const; + bool challengeMasterSeed(const QByteArray& masterSeed, QByteArray& result) const; void setCipher(const Uuid& cipher); void setCompressionAlgo(Database::CompressionAlgorithm algo); diff --git a/src/format/KeePass2Reader.cpp b/src/format/KeePass2Reader.cpp index 17e007d7..8ac98327 100644 --- a/src/format/KeePass2Reader.cpp +++ b/src/format/KeePass2Reader.cpp @@ -113,9 +113,15 @@ Database* KeePass2Reader::readDatabase(QIODevice* device, const CompositeKey& ke return nullptr; } + QByteArray challengeResult; + if (m_db->challengeMasterSeed(m_masterSeed, challengeResult) == false) { + raiseError(tr("Unable to issue challenge-response.")); + return Q_NULLPTR; + } + CryptoHash hash(CryptoHash::Sha256); hash.addData(m_masterSeed); - hash.addData(m_db->challengeMasterSeed(m_masterSeed)); + hash.addData(challengeResult); hash.addData(m_db->transformedMasterKey()); QByteArray finalKey = hash.result(); diff --git a/src/format/KeePass2Writer.cpp b/src/format/KeePass2Writer.cpp index 3a3195a0..6c0bf2e1 100644 --- a/src/format/KeePass2Writer.cpp +++ b/src/format/KeePass2Writer.cpp @@ -51,9 +51,15 @@ void KeePass2Writer::writeDatabase(QIODevice* device, Database* db) QByteArray startBytes = randomGen()->randomArray(32); QByteArray endOfHeader = "\r\n\r\n"; + QByteArray challengeResult; + if (db->challengeMasterSeed(masterSeed, challengeResult) == false) { + raiseError("Unable to issue challenge-response."); + return; + } + CryptoHash hash(CryptoHash::Sha256); hash.addData(masterSeed); - hash.addData(db->challengeMasterSeed(masterSeed)); + hash.addData(challengeResult); Q_ASSERT(!db->transformedMasterKey().isEmpty()); hash.addData(db->transformedMasterKey()); QByteArray finalKey = hash.result(); diff --git a/src/keys/CompositeKey.cpp b/src/keys/CompositeKey.cpp index 2270d96e..ed64b8ef 100644 --- a/src/keys/CompositeKey.cpp +++ b/src/keys/CompositeKey.cpp @@ -146,23 +146,27 @@ QByteArray CompositeKey::transformKeyRaw(const QByteArray& key, const QByteArray return result; } -QByteArray CompositeKey::challenge(const QByteArray& seed) const +bool CompositeKey::challenge(const QByteArray& seed, QByteArray& result) const { /* If no challenge response was requested, return nothing to * maintain backwards compatability with regular databases. */ if (m_challengeResponseKeys.length() == 0) { - return QByteArray(); + return true; } CryptoHash cryptoHash(CryptoHash::Sha256); Q_FOREACH (ChallengeResponseKey* key, m_challengeResponseKeys) { - key->challenge(seed); + /* If the device isn't present or fails, return an error */ + if (key->challenge(seed) == false) { + return false; + } cryptoHash.addData(key->rawKey()); } - return cryptoHash.result(); + result = cryptoHash.result(); + return true; } void CompositeKey::addKey(const Key& key) diff --git a/src/keys/CompositeKey.h b/src/keys/CompositeKey.h index 66ad91ad..b5f973d2 100644 --- a/src/keys/CompositeKey.h +++ b/src/keys/CompositeKey.h @@ -37,7 +37,7 @@ public: QByteArray rawKey() const; QByteArray transform(const QByteArray& seed, quint64 rounds, bool* ok, QString* errorString) const; - QByteArray challenge(const QByteArray& seed) const; + bool challenge(const QByteArray& seed, QByteArray &result) const; void addKey(const Key& key); void addChallengeResponseKey(const ChallengeResponseKey& key); From f7ee528d41325d335b5cada4b4dfe514d76cc9e8 Mon Sep 17 00:00:00 2001 From: Kyle Manna Date: Sat, 6 Sep 2014 22:09:06 -0700 Subject: [PATCH 10/93] YubiKey: Retry to recover hotplugging * Attempt one retry in the event the event the device was removed and re-inserted. Signed-off-by: Kyle Manna --- src/keys/YkChallengeResponseKey.cpp | 22 ++++++++++++++++++++++ src/keys/YkChallengeResponseKey.h | 3 ++- src/keys/drivers/YubiKey.cpp | 5 +++++ 3 files changed, 29 insertions(+), 1 deletion(-) diff --git a/src/keys/YkChallengeResponseKey.cpp b/src/keys/YkChallengeResponseKey.cpp index 5f495a34..17982ab6 100644 --- a/src/keys/YkChallengeResponseKey.cpp +++ b/src/keys/YkChallengeResponseKey.cpp @@ -26,6 +26,8 @@ #include "keys/YkChallengeResponseKey.h" #include "keys/drivers/YubiKey.h" +#include + YkChallengeResponseKey::YkChallengeResponseKey(int slot, bool blocking) : m_slot(slot), @@ -46,11 +48,31 @@ YkChallengeResponseKey* YkChallengeResponseKey::clone() const /** Assumes yubikey()->init() was called */ bool YkChallengeResponseKey::challenge(const QByteArray& chal) +{ + return challenge(chal, 1); +} + +bool YkChallengeResponseKey::challenge(const QByteArray& chal, int retries) { if (YubiKey::instance()->challenge(m_slot, true, chal, m_key) != YubiKey::ERROR) { return true; } + /* If challenge failed, retry to detect YubiKeys int the event the YubiKey + * was un-plugged and re-plugged */ + while (retries > 0) { + qDebug() << "Attempt" << retries << "to re-detect YubiKey(s)"; + retries--; + + if (YubiKey::instance()->init() != true) { + continue; + } + + if (YubiKey::instance()->challenge(m_slot, true, chal, m_key) != YubiKey::ERROR) { + return true; + } + } + return false; } diff --git a/src/keys/YkChallengeResponseKey.h b/src/keys/YkChallengeResponseKey.h index a5236748..8acb0f9e 100644 --- a/src/keys/YkChallengeResponseKey.h +++ b/src/keys/YkChallengeResponseKey.h @@ -31,7 +31,8 @@ public: QByteArray rawKey() const; YkChallengeResponseKey* clone() const; - bool challenge(const QByteArray& challenge); + bool challenge(const QByteArray& chal); + bool challenge(const QByteArray& chal, int retries); QString getName() const; bool isBlocking() const; diff --git a/src/keys/drivers/YubiKey.cpp b/src/keys/drivers/YubiKey.cpp index 1d69bb5b..9c87ec15 100644 --- a/src/keys/drivers/YubiKey.cpp +++ b/src/keys/drivers/YubiKey.cpp @@ -182,6 +182,11 @@ YubiKey::ChallengeResult YubiKey::challenge(int slot, bool mayBlock, int yk_cmd = (slot == 1) ? SLOT_CHAL_HMAC1 : SLOT_CHAL_HMAC2; QByteArray paddedChal = chal; + /* Ensure that YubiKey::init() succeeded */ + if (m_yk == NULL) { + return ERROR; + } + /* yk_challenge_response() insists on 64 byte response buffer */ resp.resize(64); From 62190d79be49771d003ca1a507a1a371c7f27334 Mon Sep 17 00:00:00 2001 From: Kyle Manna Date: Sat, 6 Sep 2014 22:19:05 -0700 Subject: [PATCH 11/93] YubiKey: Whitespace clean-up * This was bugging me. Oops. * No functional changes. Signed-off-by: Kyle Manna --- src/keys/drivers/YubiKey.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/keys/drivers/YubiKey.cpp b/src/keys/drivers/YubiKey.cpp index 9c87ec15..5ca17583 100644 --- a/src/keys/drivers/YubiKey.cpp +++ b/src/keys/drivers/YubiKey.cpp @@ -104,8 +104,8 @@ bool YubiKey::deinit() } if (m_ykds) { - ykds_free(m_ykds); - m_ykds_void = NULL; + ykds_free(m_ykds); + m_ykds_void = NULL; } return true; @@ -215,7 +215,7 @@ YubiKey::ChallengeResult YubiKey::challenge(int slot, bool mayBlock, paddedChal.size(), c, resp.size(), r); - if(!ret) { + if (!ret) { if (yk_errno == YK_EWOULDBLOCK) { return WOULDBLOCK; } else if (yk_errno == YK_ETIMEOUT) { From 77cc99acd3de0ff1e3df24403eeb136e5faaeb0a Mon Sep 17 00:00:00 2001 From: Kyle Manna Date: Sun, 7 Sep 2014 16:37:46 -0700 Subject: [PATCH 12/93] YubiKey: Clean-up master seed challenge * Tweak the logic so it more closely resembles other code (i.e. trasnformKey()). Matches existing style better. * Save the challengeResponseKey in the database structure so that it can be referred to later (i.e. database unlocking). Signed-off-by: Kyle Manna --- src/core/Database.cpp | 9 +++++++-- src/core/Database.h | 4 +++- src/format/KeePass2Reader.cpp | 5 ++--- src/format/KeePass2Writer.cpp | 5 ++--- 4 files changed, 14 insertions(+), 9 deletions(-) diff --git a/src/core/Database.cpp b/src/core/Database.cpp index fd9e02dd..5297c2ad 100644 --- a/src/core/Database.cpp +++ b/src/core/Database.cpp @@ -176,9 +176,14 @@ QByteArray Database::transformedMasterKey() const return m_data.transformedMasterKey; } -bool Database::challengeMasterSeed(const QByteArray& masterSeed, QByteArray& result) const +QByteArray Database::challengeResponseKey() const { - return m_data.key.challenge(masterSeed, result); + return m_data.challengeResponseKey; +} + +bool Database::challengeMasterSeed(const QByteArray& masterSeed) +{ + return m_data.key.challenge(masterSeed, m_data.challengeResponseKey); } void Database::setCipher(const Uuid& cipher) diff --git a/src/core/Database.h b/src/core/Database.h index e2eb0fb1..be022ae3 100644 --- a/src/core/Database.h +++ b/src/core/Database.h @@ -59,6 +59,7 @@ public: QByteArray transformedMasterKey; CompositeKey key; bool hasKey; + QByteArray challengeResponseKey; }; Database(); @@ -89,7 +90,8 @@ public: quint64 transformRounds() const; QByteArray transformedMasterKey() const; const CompositeKey & key() const; - bool challengeMasterSeed(const QByteArray& masterSeed, QByteArray& result) const; + QByteArray challengeResponseKey() const; + bool challengeMasterSeed(const QByteArray& masterSeed); void setCipher(const Uuid& cipher); void setCompressionAlgo(Database::CompressionAlgorithm algo); diff --git a/src/format/KeePass2Reader.cpp b/src/format/KeePass2Reader.cpp index 8ac98327..73960a7a 100644 --- a/src/format/KeePass2Reader.cpp +++ b/src/format/KeePass2Reader.cpp @@ -113,15 +113,14 @@ Database* KeePass2Reader::readDatabase(QIODevice* device, const CompositeKey& ke return nullptr; } - QByteArray challengeResult; - if (m_db->challengeMasterSeed(m_masterSeed, challengeResult) == false) { + if (m_db->challengeMasterSeed(m_masterSeed) == false) { raiseError(tr("Unable to issue challenge-response.")); return Q_NULLPTR; } CryptoHash hash(CryptoHash::Sha256); hash.addData(m_masterSeed); - hash.addData(challengeResult); + hash.addData(m_db->challengeResponseKey()); hash.addData(m_db->transformedMasterKey()); QByteArray finalKey = hash.result(); diff --git a/src/format/KeePass2Writer.cpp b/src/format/KeePass2Writer.cpp index 6c0bf2e1..6bb316ba 100644 --- a/src/format/KeePass2Writer.cpp +++ b/src/format/KeePass2Writer.cpp @@ -51,15 +51,14 @@ void KeePass2Writer::writeDatabase(QIODevice* device, Database* db) QByteArray startBytes = randomGen()->randomArray(32); QByteArray endOfHeader = "\r\n\r\n"; - QByteArray challengeResult; - if (db->challengeMasterSeed(masterSeed, challengeResult) == false) { + if (db->challengeMasterSeed(masterSeed) == false) { raiseError("Unable to issue challenge-response."); return; } CryptoHash hash(CryptoHash::Sha256); hash.addData(masterSeed); - hash.addData(challengeResult); + hash.addData(db->challengeResponseKey()); Q_ASSERT(!db->transformedMasterKey().isEmpty()); hash.addData(db->transformedMasterKey()); QByteArray finalKey = hash.result(); From 951fa968480a861718371e25d7ca3f8106821100 Mon Sep 17 00:00:00 2001 From: Kyle Manna Date: Sun, 7 Sep 2014 16:43:04 -0700 Subject: [PATCH 13/93] YubiKey: Fix database locking * Save the master seed upon first challenge so it can be used as a challenge at a later point. * When verifyKey() is called, verify that the challenge is successful. * Uncheck YubiKey box to not leak information about how the database is protected. Signed-off-by: Kyle Manna --- src/core/Database.cpp | 17 +++++++++++++++++ src/core/Database.h | 1 + src/gui/UnlockDatabaseWidget.cpp | 1 + 3 files changed, 19 insertions(+) diff --git a/src/core/Database.cpp b/src/core/Database.cpp index 5297c2ad..22fc0723 100644 --- a/src/core/Database.cpp +++ b/src/core/Database.cpp @@ -183,6 +183,7 @@ QByteArray Database::challengeResponseKey() const bool Database::challengeMasterSeed(const QByteArray& masterSeed) { + m_data.masterSeed = masterSeed; return m_data.key.challenge(masterSeed, m_data.challengeResponseKey); } @@ -256,6 +257,22 @@ bool Database::verifyKey(const CompositeKey& key) const { Q_ASSERT(hasKey()); + /* If the database has challenge response keys, then the the verification + * key better as well */ + if (!m_data.challengeResponseKey.isEmpty()) { + QByteArray result; + + if (!key.challenge(m_data.masterSeed, result)) { + /* Challenge failed, (YubiKey?) removed? */ + return false; + } + + if (m_data.challengeResponseKey != result) { + /* Wrong response from challenged device(s) */ + return false; + } + } + return (m_data.key.rawKey() == key.rawKey()); } diff --git a/src/core/Database.h b/src/core/Database.h index be022ae3..3f946a1c 100644 --- a/src/core/Database.h +++ b/src/core/Database.h @@ -59,6 +59,7 @@ public: QByteArray transformedMasterKey; CompositeKey key; bool hasKey; + QByteArray masterSeed; QByteArray challengeResponseKey; }; diff --git a/src/gui/UnlockDatabaseWidget.cpp b/src/gui/UnlockDatabaseWidget.cpp index a005d0e6..d6beb133 100644 --- a/src/gui/UnlockDatabaseWidget.cpp +++ b/src/gui/UnlockDatabaseWidget.cpp @@ -33,6 +33,7 @@ void UnlockDatabaseWidget::clearForms() m_ui->comboKeyFile->clear(); m_ui->checkPassword->setChecked(false); m_ui->checkKeyFile->setChecked(false); + m_ui->checkChallengeResponse->setChecked(false); m_ui->buttonTogglePassword->setChecked(false); m_db = nullptr; } From d398d367c1e2b6fe9534f42769cc6e05cd4ceafe Mon Sep 17 00:00:00 2001 From: Pedro Alves Date: Tue, 27 Jan 2015 17:38:26 +0000 Subject: [PATCH 14/93] Allow a previously yubikey protected database to be saved without the yubikey challenge-response code. --- src/keys/CompositeKey.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/keys/CompositeKey.cpp b/src/keys/CompositeKey.cpp index ed64b8ef..d2334e3c 100644 --- a/src/keys/CompositeKey.cpp +++ b/src/keys/CompositeKey.cpp @@ -43,7 +43,9 @@ CompositeKey::~CompositeKey() void CompositeKey::clear() { qDeleteAll(m_keys); + qDeleteAll(m_challengeResponseKeys); m_keys.clear(); + m_challengeResponseKeys.clear(); } bool CompositeKey::isEmpty() const @@ -152,6 +154,7 @@ bool CompositeKey::challenge(const QByteArray& seed, QByteArray& result) const * maintain backwards compatability with regular databases. */ if (m_challengeResponseKeys.length() == 0) { + result.clear(); return true; } From ef06165ea2f4998e8c04c6c62b04a2fe504e5656 Mon Sep 17 00:00:00 2001 From: Kyle Manna Date: Sun, 8 Jan 2017 18:45:02 -0800 Subject: [PATCH 15/93] keys: CompositeKey: Change Q_FOREACH to C++11 for() * Use the C++11 range based loop as recommended from https://github.com/keepassxreboot/keepassxc/pull/119 Signed-off-by: Kyle Manna --- src/keys/CompositeKey.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/keys/CompositeKey.cpp b/src/keys/CompositeKey.cpp index d2334e3c..ae654eb7 100644 --- a/src/keys/CompositeKey.cpp +++ b/src/keys/CompositeKey.cpp @@ -70,7 +70,7 @@ CompositeKey& CompositeKey::operator=(const CompositeKey& key) for (const Key* subKey : asConst(key.m_keys)) { addKey(*subKey); } - Q_FOREACH (const ChallengeResponseKey* subKey, key.m_challengeResponseKeys) { + for (const ChallengeResponseKey* subKey : asConst(key.m_challengeResponseKeys)) { addChallengeResponseKey(*subKey); } @@ -160,7 +160,7 @@ bool CompositeKey::challenge(const QByteArray& seed, QByteArray& result) const CryptoHash cryptoHash(CryptoHash::Sha256); - Q_FOREACH (ChallengeResponseKey* key, m_challengeResponseKeys) { + for (ChallengeResponseKey* key : m_challengeResponseKeys) { /* If the device isn't present or fails, return an error */ if (key->challenge(seed) == false) { return false; From 05774854efb4e32280aac7d7bcd420779c4ec375 Mon Sep 17 00:00:00 2001 From: Kyle Manna Date: Sat, 14 Jan 2017 16:07:02 -0800 Subject: [PATCH 16/93] travis-ci: Add YubiKey development libraries * With these packages cmake will detect the YubiKey headers and libraries and build in YubiKey support. --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index a7807b61..adb8afdd 100644 --- a/.travis.yml +++ b/.travis.yml @@ -22,7 +22,7 @@ git: before_install: - if [ "$TRAVIS_OS_NAME" = "linux" ]; then sudo apt-get -qq update; fi - - if [ "$TRAVIS_OS_NAME" = "linux" ]; then sudo apt-get -qq install cmake libmicrohttpd10 libmicrohttpd-dev libxi-dev qtbase5-dev libqt5x11extras5-dev qttools5-dev qttools5-dev-tools libgcrypt20-dev zlib1g-dev libxtst-dev xvfb; fi + - if [ "$TRAVIS_OS_NAME" = "linux" ]; then sudo apt-get -qq install cmake libmicrohttpd10 libmicrohttpd-dev libxi-dev qtbase5-dev libqt5x11extras5-dev qttools5-dev qttools5-dev-tools libgcrypt20-dev zlib1g-dev libxtst-dev xvfb libyubikey-dev libykpers-1-dev; fi - if [ "$TRAVIS_OS_NAME" = "osx" ]; then brew update; fi - if [ "$TRAVIS_OS_NAME" = "osx" ]; then brew ls | grep -wq cmake || brew install cmake; fi - if [ "$TRAVIS_OS_NAME" = "osx" ]; then brew ls | grep -wq qt5 || brew install qt5; fi From f33cd1541941a088b90f463d457f4a9f86aa712a Mon Sep 17 00:00:00 2001 From: Kyle Manna Date: Sat, 14 Jan 2017 17:08:48 -0800 Subject: [PATCH 17/93] gui: Clear YubiKeys detected on each load * Clear the YubiKey detected list on each load. * In the event the YubiKey was removed, it will no longer be displayed. * If it's still present it won't be duplicated. --- src/gui/DatabaseOpenWidget.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/gui/DatabaseOpenWidget.cpp b/src/gui/DatabaseOpenWidget.cpp index 5d381596..0b63bc0f 100644 --- a/src/gui/DatabaseOpenWidget.cpp +++ b/src/gui/DatabaseOpenWidget.cpp @@ -64,6 +64,10 @@ DatabaseOpenWidget::DatabaseOpenWidget(QWidget* parent) connect(m_ui->buttonBox, SIGNAL(accepted()), SLOT(openDatabase())); connect(m_ui->buttonBox, SIGNAL(rejected()), SLOT(reject())); + + connect(YubiKey::instance(), SIGNAL(detected(int,bool)), + SLOT(ykDetected(int,bool)), + Qt::QueuedConnection); } DatabaseOpenWidget::~DatabaseOpenWidget() @@ -92,10 +96,8 @@ void DatabaseOpenWidget::load(const QString& filename) m_ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(true); - /* YubiKey init is slow */ - connect(YubiKey::instance(), SIGNAL(detected(int,bool)), - SLOT(ykDetected(int,bool)), - Qt::QueuedConnection); + /* YubiKey init is slow, detect asynchronously to not block the UI */ + m_ui->comboChallengeResponse->clear(); QtConcurrent::run(YubiKey::instance(), &YubiKey::detect); m_ui->editPassword->setFocus(); From a7cf39c7cd07fd27eb3d521403ad92d69ef4cf74 Mon Sep 17 00:00:00 2001 From: Kyle Manna Date: Sat, 14 Jan 2017 17:36:14 -0800 Subject: [PATCH 18/93] gui: ChangeMasterKeyWidget: Clear YubiKeys detected * Clear the YubiKey detected list on each load. * In the event the YubiKey was removed, it will no longer be displayed. * If it's still present it won't be duplicated. --- src/gui/ChangeMasterKeyWidget.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/gui/ChangeMasterKeyWidget.cpp b/src/gui/ChangeMasterKeyWidget.cpp index c69cf2dc..f2cf3b8d 100644 --- a/src/gui/ChangeMasterKeyWidget.cpp +++ b/src/gui/ChangeMasterKeyWidget.cpp @@ -41,6 +41,10 @@ ChangeMasterKeyWidget::ChangeMasterKeyWidget(QWidget* parent) m_ui->repeatPasswordEdit->enableVerifyMode(m_ui->enterPasswordEdit); connect(m_ui->createKeyFileButton, SIGNAL(clicked()), SLOT(createKeyFile())); connect(m_ui->browseKeyFileButton, SIGNAL(clicked()), SLOT(browseKeyFile())); + + connect(YubiKey::instance(), SIGNAL(detected(int,bool)), + SLOT(ykDetected(int,bool)), + Qt::QueuedConnection); } ChangeMasterKeyWidget::~ChangeMasterKeyWidget() @@ -88,10 +92,8 @@ void ChangeMasterKeyWidget::clearForms() m_ui->challengeResponseGroup->setChecked(false); m_ui->challengeResponseCombo->clear(); - /* YubiKey init is slow */ - connect(YubiKey::instance(), SIGNAL(detected(int,bool)), - SLOT(ykDetected(int,bool)), - Qt::QueuedConnection); + /* YubiKey init is slow, detect asynchronously to not block the UI */ + m_ui->challengeResponseCombo->clear(); QtConcurrent::run(YubiKey::instance(), &YubiKey::detect); m_ui->enterPasswordEdit->setFocus(); From 5a3ed27fedd5668bab0b9ffab7a815d1c112884e Mon Sep 17 00:00:00 2001 From: Kyle Manna Date: Sat, 14 Jan 2017 17:58:05 -0800 Subject: [PATCH 19/93] tests: yk: Initialize Crypto class Initialize the Crypto class before running the tests as YubiKey detection code depends on it for random data. --- tests/TestYkChallengeResponseKey.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/TestYkChallengeResponseKey.cpp b/tests/TestYkChallengeResponseKey.cpp index 91fba83c..bd58ac01 100644 --- a/tests/TestYkChallengeResponseKey.cpp +++ b/tests/TestYkChallengeResponseKey.cpp @@ -21,6 +21,7 @@ #include #include +#include "crypto/Crypto.h" #include "keys/YkChallengeResponseKey.h" QTEST_GUILESS_MAIN(TestYubiKeyChalResp) @@ -44,6 +45,9 @@ void TestYubiKeyChalResp::init() if (!result) { QSKIP("Unable to connect to YubiKey", SkipAll); } + + /* Crypto subsystem needs to be initalized for YubiKey testing */ + QVERIFY(Crypto::init()); } void TestYubiKeyChalResp::detectDevices() From 7174549441d79f6f7de17caf9253d1fac5eafbb0 Mon Sep 17 00:00:00 2001 From: Janek Bevendorff Date: Mon, 20 Feb 2017 20:35:28 +0100 Subject: [PATCH 20/93] Align YubiKey combobox with rest of interface --- src/gui/DatabaseOpenWidget.ui | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/src/gui/DatabaseOpenWidget.ui b/src/gui/DatabaseOpenWidget.ui index b3abbb28..01b82d40 100644 --- a/src/gui/DatabaseOpenWidget.ui +++ b/src/gui/DatabaseOpenWidget.ui @@ -7,7 +7,7 @@ 0 0 596 - 250 + 262 @@ -85,7 +85,7 @@ - + 5 @@ -118,7 +118,7 @@ - + 5 @@ -142,7 +142,7 @@ - + false @@ -152,12 +152,22 @@ - - - - false + + + + 5 - + + 5 + + + + + false + + + + From eb23dda99b187871745b274c34f1d1cd15d4ded9 Mon Sep 17 00:00:00 2001 From: Janek Bevendorff Date: Mon, 20 Feb 2017 22:07:01 +0100 Subject: [PATCH 21/93] Remember if challenge-response was used for each database and allow to re-detect Yubikeys without closing the database first --- src/gui/DatabaseOpenWidget.cpp | 47 ++++++++++++++++++++++++++++------ src/gui/DatabaseOpenWidget.h | 5 +++- src/gui/DatabaseOpenWidget.ui | 21 ++++++++++++++- src/keys/drivers/YubiKey.cpp | 5 ++-- src/keys/drivers/YubiKey.h | 5 ++++ 5 files changed, 71 insertions(+), 12 deletions(-) diff --git a/src/gui/DatabaseOpenWidget.cpp b/src/gui/DatabaseOpenWidget.cpp index f5d582c6..83488317 100644 --- a/src/gui/DatabaseOpenWidget.cpp +++ b/src/gui/DatabaseOpenWidget.cpp @@ -67,9 +67,10 @@ DatabaseOpenWidget::DatabaseOpenWidget(QWidget* parent) connect(m_ui->buttonBox, SIGNAL(accepted()), SLOT(openDatabase())); connect(m_ui->buttonBox, SIGNAL(rejected()), SLOT(reject())); - connect(YubiKey::instance(), SIGNAL(detected(int,bool)), - SLOT(ykDetected(int,bool)), - Qt::QueuedConnection); + connect(m_ui->buttonRedetectYubikey, SIGNAL(clicked()), SLOT(pollYubikey())); + + connect(YubiKey::instance(), SIGNAL(detected(int,bool)), SLOT(yubikeyDetected(int,bool)), Qt::QueuedConnection); + connect(YubiKey::instance(), SIGNAL(notFound()), SLOT(noYubikeyFound()), Qt::QueuedConnection); #ifdef Q_OS_MACOS // add random padding to layouts to align widgets properly @@ -105,9 +106,8 @@ void DatabaseOpenWidget::load(const QString& filename) m_ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(true); - /* YubiKey init is slow, detect asynchronously to not block the UI */ m_ui->comboChallengeResponse->clear(); - QtConcurrent::run(YubiKey::instance(), &YubiKey::detect); + pollYubikey(); m_ui->editPassword->setFocus(); } @@ -169,6 +169,7 @@ CompositeKey DatabaseOpenWidget::databaseKey() } QHash lastKeyFiles = config()->get("LastKeyFiles").toHash(); + QHash lastChallengeResponse = config()->get("LastChallengeResponse").toHash(); if (m_ui->checkKeyFile->isChecked()) { FileKey key; @@ -181,13 +182,19 @@ CompositeKey DatabaseOpenWidget::databaseKey() } masterKey.addKey(key); lastKeyFiles[m_filename] = keyFilename; - } - else { + } else { lastKeyFiles.remove(m_filename); } + if (m_ui->checkChallengeResponse->isChecked()) { + lastChallengeResponse[m_filename] = true; + } else { + lastChallengeResponse.remove(m_filename); + } + if (config()->get("RememberLastKeyFiles").toBool()) { config()->set("LastKeyFiles", lastKeyFiles); + config()->set("LastChallengeResponse", lastChallengeResponse); } @@ -240,10 +247,34 @@ void DatabaseOpenWidget::browseKeyFile() } } -void DatabaseOpenWidget::ykDetected(int slot, bool blocking) +void DatabaseOpenWidget::pollYubikey() +{ + // YubiKey init is slow, detect asynchronously to not block the UI + m_ui->buttonRedetectYubikey->setEnabled(false); + m_ui->checkChallengeResponse->setEnabled(false); + m_ui->checkChallengeResponse->setChecked(false); + m_ui->comboChallengeResponse->setEnabled(false); + m_ui->comboChallengeResponse->clear(); + QtConcurrent::run(YubiKey::instance(), &YubiKey::detect); +} + +void DatabaseOpenWidget::yubikeyDetected(int slot, bool blocking) { YkChallengeResponseKey yk(slot, blocking); m_ui->comboChallengeResponse->addItem(yk.getName(), QVariant(slot)); m_ui->comboChallengeResponse->setEnabled(true); m_ui->checkChallengeResponse->setEnabled(true); + m_ui->buttonRedetectYubikey->setEnabled(true); + + if (config()->get("RememberLastKeyFiles").toBool()) { + QHash lastChallengeResponse = config()->get("LastChallengeResponse").toHash(); + if (lastChallengeResponse.contains(m_filename)) { + m_ui->checkChallengeResponse->setChecked(true); + } + } +} + +void DatabaseOpenWidget::noYubikeyFound() +{ + m_ui->buttonRedetectYubikey->setEnabled(true); } diff --git a/src/gui/DatabaseOpenWidget.h b/src/gui/DatabaseOpenWidget.h index fadb5ee7..d0234605 100644 --- a/src/gui/DatabaseOpenWidget.h +++ b/src/gui/DatabaseOpenWidget.h @@ -58,9 +58,12 @@ private Q_SLOTS: void activateChallengeResponse(); void setOkButtonEnabled(); void browseKeyFile(); - void ykDetected(int slot, bool blocking); + void yubikeyDetected(int slot, bool blocking); + void noYubikeyFound(); + void pollYubikey(); protected: + const QScopedPointer m_ui; Database* m_db; QString m_filename; diff --git a/src/gui/DatabaseOpenWidget.ui b/src/gui/DatabaseOpenWidget.ui index 01b82d40..f835fc2f 100644 --- a/src/gui/DatabaseOpenWidget.ui +++ b/src/gui/DatabaseOpenWidget.ui @@ -7,7 +7,7 @@ 0 0 596 - 262 + 264 @@ -165,6 +165,25 @@ false + + + 0 + 0 + + + + false + + + + + + + false + + + Refresh + diff --git a/src/keys/drivers/YubiKey.cpp b/src/keys/drivers/YubiKey.cpp index 5ca17583..20e91237 100644 --- a/src/keys/drivers/YubiKey.cpp +++ b/src/keys/drivers/YubiKey.cpp @@ -117,7 +117,6 @@ bool YubiKey::deinit() void YubiKey::detect() { if (init()) { - for (int i = 1; i < 3; i++) { YubiKey::ChallengeResult result; QByteArray rand = randomGen()->randomArray(1); @@ -126,10 +125,12 @@ void YubiKey::detect() result = challenge(i, false, rand, resp); if (result != YubiKey::ERROR) { - Q_EMIT detected(i, result == YubiKey::WOULDBLOCK ? true : false); + emit detected(i, result == YubiKey::WOULDBLOCK ? true : false); + return; } } } + emit notFound(); } /** diff --git a/src/keys/drivers/YubiKey.h b/src/keys/drivers/YubiKey.h index 492fba01..0441e69a 100644 --- a/src/keys/drivers/YubiKey.h +++ b/src/keys/drivers/YubiKey.h @@ -56,6 +56,11 @@ Q_SIGNALS: */ void detected(int slot, bool blocking); + /** + * Emitted when no Yubikey could be found. + */ + void notFound(); + private: explicit YubiKey(); static YubiKey* m_instance; From c7defdc06f698118569ad687c0c0ae34fa607542 Mon Sep 17 00:00:00 2001 From: Janek Bevendorff Date: Mon, 20 Feb 2017 22:41:39 +0100 Subject: [PATCH 22/93] Add redetect button to ChangeMasterKeyWidget and only poll for Yubikeys when the challenge response group is enabled --- src/gui/ChangeMasterKeyWidget.cpp | 50 +++++++++++++++++++++---------- src/gui/ChangeMasterKeyWidget.h | 5 +++- src/gui/ChangeMasterKeyWidget.ui | 30 ++++++++++++------- 3 files changed, 58 insertions(+), 27 deletions(-) diff --git a/src/gui/ChangeMasterKeyWidget.cpp b/src/gui/ChangeMasterKeyWidget.cpp index 7f0fb066..bb2b201d 100644 --- a/src/gui/ChangeMasterKeyWidget.cpp +++ b/src/gui/ChangeMasterKeyWidget.cpp @@ -44,9 +44,11 @@ ChangeMasterKeyWidget::ChangeMasterKeyWidget(QWidget* parent) connect(m_ui->createKeyFileButton, SIGNAL(clicked()), SLOT(createKeyFile())); connect(m_ui->browseKeyFileButton, SIGNAL(clicked()), SLOT(browseKeyFile())); - connect(YubiKey::instance(), SIGNAL(detected(int,bool)), - SLOT(ykDetected(int,bool)), - Qt::QueuedConnection); + connect(m_ui->challengeResponseGroup, SIGNAL(clicked(bool)), SLOT(challengeResponseGroupToggled(bool))); + connect(m_ui->buttonRedetectYubikey, SIGNAL(clicked()), SLOT(pollYubikey())); + + connect(YubiKey::instance(), SIGNAL(detected(int,bool)), SLOT(yubikeyDetected(int,bool)), Qt::QueuedConnection); + connect(YubiKey::instance(), SIGNAL(notFound()), SLOT(noYubikeyFound()), Qt::QueuedConnection); } ChangeMasterKeyWidget::~ChangeMasterKeyWidget() @@ -89,14 +91,9 @@ void ChangeMasterKeyWidget::clearForms() m_ui->repeatPasswordEdit->setText(""); m_ui->keyFileGroup->setChecked(false); m_ui->togglePasswordButton->setChecked(false); - // TODO: clear m_ui->keyFileCombo m_ui->challengeResponseGroup->setChecked(false); - m_ui->challengeResponseCombo->clear(); - - /* YubiKey init is slow, detect asynchronously to not block the UI */ - m_ui->challengeResponseCombo->clear(); - QtConcurrent::run(YubiKey::instance(), &YubiKey::detect); + m_ui->comboChallengeResponse->clear(); m_ui->enterPasswordEdit->setFocus(); } @@ -146,29 +143,50 @@ void ChangeMasterKeyWidget::generateKey() } if (m_ui->challengeResponseGroup->isChecked()) { - int i = m_ui->challengeResponseCombo->currentIndex(); - i = m_ui->challengeResponseCombo->itemData(i).toInt(); + int i = m_ui->comboChallengeResponse->currentIndex(); + i = m_ui->comboChallengeResponse->itemData(i).toInt(); YkChallengeResponseKey key(i); m_key.addChallengeResponseKey(key); } m_ui->messageWidget->hideMessage(); - Q_EMIT editFinished(true); + emit editFinished(true); } void ChangeMasterKeyWidget::reject() { - Q_EMIT editFinished(false); + emit editFinished(false); } +void ChangeMasterKeyWidget::challengeResponseGroupToggled(bool checked) +{ + if (checked) + pollYubikey(); +} -void ChangeMasterKeyWidget::ykDetected(int slot, bool blocking) +void ChangeMasterKeyWidget::pollYubikey() +{ + m_ui->buttonRedetectYubikey->setEnabled(false); + m_ui->comboChallengeResponse->setEnabled(false); + m_ui->comboChallengeResponse->clear(); + + // YubiKey init is slow, detect asynchronously to not block the UI + QtConcurrent::run(YubiKey::instance(), &YubiKey::detect); +} + +void ChangeMasterKeyWidget::yubikeyDetected(int slot, bool blocking) { YkChallengeResponseKey yk(slot, blocking); - m_ui->challengeResponseCombo->addItem(yk.getName(), QVariant(slot)); - m_ui->challengeResponseGroup->setEnabled(true); + m_ui->comboChallengeResponse->addItem(yk.getName(), QVariant(slot)); + m_ui->comboChallengeResponse->setEnabled(m_ui->challengeResponseGroup->isChecked()); + m_ui->buttonRedetectYubikey->setEnabled(m_ui->challengeResponseGroup->isChecked()); +} + +void ChangeMasterKeyWidget::noYubikeyFound() +{ + m_ui->buttonRedetectYubikey->setEnabled(m_ui->challengeResponseGroup->isChecked()); } void ChangeMasterKeyWidget::setCancelEnabled(bool enabled) diff --git a/src/gui/ChangeMasterKeyWidget.h b/src/gui/ChangeMasterKeyWidget.h index cbf67c1f..0a99037f 100644 --- a/src/gui/ChangeMasterKeyWidget.h +++ b/src/gui/ChangeMasterKeyWidget.h @@ -48,7 +48,10 @@ private Q_SLOTS: void reject(); void createKeyFile(); void browseKeyFile(); - void ykDetected(int slot, bool blocking); + void yubikeyDetected(int slot, bool blocking); + void noYubikeyFound(); + void challengeResponseGroupToggled(bool checked); + void pollYubikey(); private: const QScopedPointer m_ui; diff --git a/src/gui/ChangeMasterKeyWidget.ui b/src/gui/ChangeMasterKeyWidget.ui index c867e197..5640364e 100644 --- a/src/gui/ChangeMasterKeyWidget.ui +++ b/src/gui/ChangeMasterKeyWidget.ui @@ -7,7 +7,7 @@ 0 0 818 - 397 + 424 @@ -90,7 +90,7 @@ - Key file + &Key file true @@ -129,22 +129,32 @@ - false + true - Challenge Response + Cha&llenge Response true - - - - + + true + + - + + + + 0 + 0 + + + + + + - Challenge Response: + Refresh From c49aa6beef446d04966d9b6bddf975722e732065 Mon Sep 17 00:00:00 2001 From: Janek Bevendorff Date: Mon, 20 Feb 2017 22:50:12 +0100 Subject: [PATCH 23/93] Show error message when trying to use challenge response without YubiKey --- src/gui/ChangeMasterKeyWidget.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/gui/ChangeMasterKeyWidget.cpp b/src/gui/ChangeMasterKeyWidget.cpp index bb2b201d..b5a465af 100644 --- a/src/gui/ChangeMasterKeyWidget.cpp +++ b/src/gui/ChangeMasterKeyWidget.cpp @@ -145,6 +145,13 @@ void ChangeMasterKeyWidget::generateKey() if (m_ui->challengeResponseGroup->isChecked()) { int i = m_ui->comboChallengeResponse->currentIndex(); i = m_ui->comboChallengeResponse->itemData(i).toInt(); + + if (0 == i) { + m_ui->messageWidget->showMessage(tr("Changing master key failed: no YubiKey inserted."), + MessageWidget::Error); + return; + } + YkChallengeResponseKey key(i); m_key.addChallengeResponseKey(key); From 5d068dfb2359fa8cecc750aaf792cd65e366e039 Mon Sep 17 00:00:00 2001 From: Janek Bevendorff Date: Mon, 20 Feb 2017 23:20:32 +0100 Subject: [PATCH 24/93] Show busy indicator while scanning for YubiKeys --- src/gui/ChangeMasterKeyWidget.cpp | 13 +++++- src/gui/ChangeMasterKeyWidget.ui | 56 +++++++++++++++++++------- src/gui/DatabaseOpenWidget.cpp | 8 ++++ src/gui/DatabaseOpenWidget.ui | 67 +++++++++++++++++++++++-------- 4 files changed, 111 insertions(+), 33 deletions(-) diff --git a/src/gui/ChangeMasterKeyWidget.cpp b/src/gui/ChangeMasterKeyWidget.cpp index b5a465af..8f9839c9 100644 --- a/src/gui/ChangeMasterKeyWidget.cpp +++ b/src/gui/ChangeMasterKeyWidget.cpp @@ -36,11 +36,17 @@ ChangeMasterKeyWidget::ChangeMasterKeyWidget(QWidget* parent) m_ui->messageWidget->setHidden(true); + m_ui->togglePasswordButton->setIcon(filePath()->onOffIcon("actions", "password-show")); + m_ui->repeatPasswordEdit->enableVerifyMode(m_ui->enterPasswordEdit); + + m_ui->yubikeyProgress->setVisible(false); + QSizePolicy sp = m_ui->yubikeyProgress->sizePolicy(); + sp.setRetainSizeWhenHidden(true); + m_ui->yubikeyProgress->setSizePolicy(sp); + connect(m_ui->buttonBox, SIGNAL(accepted()), SLOT(generateKey())); connect(m_ui->buttonBox, SIGNAL(rejected()), SLOT(reject())); - m_ui->togglePasswordButton->setIcon(filePath()->onOffIcon("actions", "password-show")); connect(m_ui->togglePasswordButton, SIGNAL(toggled(bool)), m_ui->enterPasswordEdit, SLOT(setShowPassword(bool))); - m_ui->repeatPasswordEdit->enableVerifyMode(m_ui->enterPasswordEdit); connect(m_ui->createKeyFileButton, SIGNAL(clicked()), SLOT(createKeyFile())); connect(m_ui->browseKeyFileButton, SIGNAL(clicked()), SLOT(browseKeyFile())); @@ -178,6 +184,7 @@ void ChangeMasterKeyWidget::pollYubikey() m_ui->buttonRedetectYubikey->setEnabled(false); m_ui->comboChallengeResponse->setEnabled(false); m_ui->comboChallengeResponse->clear(); + m_ui->yubikeyProgress->setVisible(true); // YubiKey init is slow, detect asynchronously to not block the UI QtConcurrent::run(YubiKey::instance(), &YubiKey::detect); @@ -189,11 +196,13 @@ void ChangeMasterKeyWidget::yubikeyDetected(int slot, bool blocking) m_ui->comboChallengeResponse->addItem(yk.getName(), QVariant(slot)); m_ui->comboChallengeResponse->setEnabled(m_ui->challengeResponseGroup->isChecked()); m_ui->buttonRedetectYubikey->setEnabled(m_ui->challengeResponseGroup->isChecked()); + m_ui->yubikeyProgress->setVisible(false); } void ChangeMasterKeyWidget::noYubikeyFound() { m_ui->buttonRedetectYubikey->setEnabled(m_ui->challengeResponseGroup->isChecked()); + m_ui->yubikeyProgress->setVisible(false); } void ChangeMasterKeyWidget::setCancelEnabled(bool enabled) diff --git a/src/gui/ChangeMasterKeyWidget.ui b/src/gui/ChangeMasterKeyWidget.ui index 5640364e..693d8ac1 100644 --- a/src/gui/ChangeMasterKeyWidget.ui +++ b/src/gui/ChangeMasterKeyWidget.ui @@ -7,7 +7,7 @@ 0 0 818 - 424 + 471 @@ -142,21 +142,47 @@ - - - - 0 - 0 - + + + 0 - - - - - - Refresh - - + + + + Refresh + + + + + + + + 0 + 0 + + + + + + + + + 16777215 + 2 + + + + 0 + + + -1 + + + false + + + + diff --git a/src/gui/DatabaseOpenWidget.cpp b/src/gui/DatabaseOpenWidget.cpp index 83488317..d9dd78f0 100644 --- a/src/gui/DatabaseOpenWidget.cpp +++ b/src/gui/DatabaseOpenWidget.cpp @@ -49,6 +49,11 @@ DatabaseOpenWidget::DatabaseOpenWidget(QWidget* parent) m_ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false); + m_ui->yubikeyProgress->setVisible(false); + QSizePolicy sp = m_ui->yubikeyProgress->sizePolicy(); + sp.setRetainSizeWhenHidden(true); + m_ui->yubikeyProgress->setSizePolicy(sp); + m_ui->buttonTogglePassword->setIcon(filePath()->onOffIcon("actions", "password-show")); connect(m_ui->buttonTogglePassword, SIGNAL(toggled(bool)), m_ui->editPassword, SLOT(setShowPassword(bool))); @@ -255,6 +260,7 @@ void DatabaseOpenWidget::pollYubikey() m_ui->checkChallengeResponse->setChecked(false); m_ui->comboChallengeResponse->setEnabled(false); m_ui->comboChallengeResponse->clear(); + m_ui->yubikeyProgress->setVisible(true); QtConcurrent::run(YubiKey::instance(), &YubiKey::detect); } @@ -265,6 +271,7 @@ void DatabaseOpenWidget::yubikeyDetected(int slot, bool blocking) m_ui->comboChallengeResponse->setEnabled(true); m_ui->checkChallengeResponse->setEnabled(true); m_ui->buttonRedetectYubikey->setEnabled(true); + m_ui->yubikeyProgress->setVisible(false); if (config()->get("RememberLastKeyFiles").toBool()) { QHash lastChallengeResponse = config()->get("LastChallengeResponse").toHash(); @@ -277,4 +284,5 @@ void DatabaseOpenWidget::yubikeyDetected(int slot, bool blocking) void DatabaseOpenWidget::noYubikeyFound() { m_ui->buttonRedetectYubikey->setEnabled(true); + m_ui->yubikeyProgress->setVisible(false); } diff --git a/src/gui/DatabaseOpenWidget.ui b/src/gui/DatabaseOpenWidget.ui index f835fc2f..7a97d008 100644 --- a/src/gui/DatabaseOpenWidget.ui +++ b/src/gui/DatabaseOpenWidget.ui @@ -7,7 +7,7 @@ 0 0 596 - 264 + 302 @@ -142,25 +142,28 @@ - - - - false - - - Challenge Response: - - - - - + + 5 5 - + + 0 + + + + + false + + + Refresh + + + + false @@ -176,13 +179,45 @@ + + + + + 16777215 + 2 + + + + 0 + + + 0 + + + -1 + + + false + + + + + + + + + 0 + + + 2 + - + false - Refresh + Challenge Response: From 2d4e08e060ce18d0040ffcc0c9a2af17e28a81ff Mon Sep 17 00:00:00 2001 From: Janek Bevendorff Date: Mon, 20 Feb 2017 23:35:03 +0100 Subject: [PATCH 25/93] Warn user when no authentication factor was chosen --- src/gui/ChangeMasterKeyWidget.cpp | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/src/gui/ChangeMasterKeyWidget.cpp b/src/gui/ChangeMasterKeyWidget.cpp index 8f9839c9..051ff76a 100644 --- a/src/gui/ChangeMasterKeyWidget.cpp +++ b/src/gui/ChangeMasterKeyWidget.cpp @@ -118,12 +118,24 @@ void ChangeMasterKeyWidget::generateKey() { m_key.clear(); + bool anyChecked = m_ui->passwordGroup->isChecked(); + anyChecked |= m_ui->keyFileGroup->isChecked(); + anyChecked |= m_ui->challengeResponseGroup->isChecked(); + + if (!anyChecked) { + if (MessageBox::warning(this, tr("No authentication factor chosen"), + tr("Your database will be completely unprotected!
Do you really want to continue?"), + QMessageBox::Yes | QMessageBox::No) != QMessageBox::Yes) { + return; + } + } + if (m_ui->passwordGroup->isChecked()) { if (m_ui->enterPasswordEdit->text() == m_ui->repeatPasswordEdit->text()) { if (m_ui->enterPasswordEdit->text().isEmpty()) { - if (MessageBox::question(this, tr("Question"), - tr("Do you really want to use an empty string as password?"), - QMessageBox::Yes | QMessageBox::No) != QMessageBox::Yes) { + if (MessageBox::warning(this, tr("Empty password"), + tr("Do you really want to use an empty string as password?"), + QMessageBox::Yes | QMessageBox::No) != QMessageBox::Yes) { return; } } From 8d3e0687a0e54a5fab5866929ae38005277280c2 Mon Sep 17 00:00:00 2001 From: Janek Bevendorff Date: Tue, 21 Feb 2017 00:28:01 +0100 Subject: [PATCH 26/93] Restructure doc comments and make hard-coded strings translatable --- src/keys/YkChallengeResponseKey.cpp | 9 +++-- src/keys/drivers/YubiKey.cpp | 55 +++++------------------------ src/keys/drivers/YubiKey.h | 45 +++++++++++++++++++---- 3 files changed, 53 insertions(+), 56 deletions(-) diff --git a/src/keys/YkChallengeResponseKey.cpp b/src/keys/YkChallengeResponseKey.cpp index 17982ab6..51520e3d 100644 --- a/src/keys/YkChallengeResponseKey.cpp +++ b/src/keys/YkChallengeResponseKey.cpp @@ -27,6 +27,7 @@ #include "keys/drivers/YubiKey.h" #include +#include YkChallengeResponseKey::YkChallengeResponseKey(int slot, bool blocking) @@ -58,10 +59,12 @@ bool YkChallengeResponseKey::challenge(const QByteArray& chal, int retries) return true; } - /* If challenge failed, retry to detect YubiKeys int the event the YubiKey + /* If challenge failed, retry to detect YubiKeys in the event the YubiKey * was un-plugged and re-plugged */ while (retries > 0) { +#ifdef QT_DEBUG qDebug() << "Attempt" << retries << "to re-detect YubiKey(s)"; +#endif retries--; if (YubiKey::instance()->init() != true) { @@ -79,13 +82,13 @@ bool YkChallengeResponseKey::challenge(const QByteArray& chal, int retries) QString YkChallengeResponseKey::getName() const { unsigned int serial; - QString fmt("YubiKey[%1] Challenge Response - Slot %2 - %3"); + QString fmt(QObject::tr("YubiKey[%1] Challenge Response - Slot %2 - %3")); YubiKey::instance()->getSerial(serial); return fmt.arg(QString::number(serial), QString::number(m_slot), - (m_blocking) ? "Press" : "Passive"); + (m_blocking) ? QObject::tr("Press") : QObject::tr("Passive")); } bool YkChallengeResponseKey::isBlocking() const diff --git a/src/keys/drivers/YubiKey.cpp b/src/keys/drivers/YubiKey.cpp index 20e91237..b287c13b 100644 --- a/src/keys/drivers/YubiKey.cpp +++ b/src/keys/drivers/YubiKey.cpp @@ -29,9 +29,8 @@ #include "YubiKey.h" -/* Cast the void pointer from the generalized class definition - * to the proper pointer type from the now included system headers - */ +// Cast the void pointer from the generalized class definition +// to the proper pointer type from the now included system headers #define m_yk (static_cast(m_yk_void)) #define m_ykds (static_cast(m_ykds_void)) @@ -41,10 +40,6 @@ YubiKey::YubiKey() : m_yk_void(NULL), m_ykds_void(NULL) YubiKey* YubiKey::m_instance(Q_NULLPTR); -/** - * @brief YubiKey::instance - get instance of singleton - * @return - */ YubiKey* YubiKey::instance() { if (!m_instance) { @@ -54,20 +49,16 @@ YubiKey* YubiKey::instance() return m_instance; } -/** - * @brief YubiKey::init - initialize yubikey library and hardware - * @return - */ bool YubiKey::init() { - /* Previously initalized */ + // previously initialized if (m_yk != NULL && m_ykds != NULL) { if (yk_get_status(m_yk, m_ykds)) { - /* Still connected */ + // Still connected return true; } else { - /* Initialized but not connected anymore, re-init */ + // Initialized but not connected anymore, re-init deinit(); } } @@ -76,7 +67,7 @@ bool YubiKey::init() return false; } - /* TODO: handle multiple attached hardware devices, currently own one */ + // TODO: handle multiple attached hardware devices m_yk_void = static_cast(yk_open_first_key()); if (m_yk == NULL) { return false; @@ -92,10 +83,6 @@ bool YubiKey::init() return true; } -/** - * @brief YubiKey::deinit - cleanup after init - * @return true on success - */ bool YubiKey::deinit() { if (m_yk) { @@ -111,9 +98,6 @@ bool YubiKey::deinit() return true; } -/** - * @brief YubiKey::detect - probe for attached YubiKeys - */ void YubiKey::detect() { if (init()) { @@ -133,11 +117,6 @@ void YubiKey::detect() emit notFound(); } -/** - * @brief YubiKey::getSerial - serial number of yubikey - * @param serial - * @return - */ bool YubiKey::getSerial(unsigned int& serial) const { if (!yk_get_serial(m_yk, 1, 0, &serial)) { @@ -162,23 +141,7 @@ static inline QString printByteArray(const QByteArray& a) } #endif -/** - * @brief YubiKey::challenge - issue a challenge - * - * This operation could block if the YubiKey requires a touch to trigger. - * - * TODO: Signal to the UI that the system is waiting for challenge response - * touch. - * - * @param slot YubiKey configuration slot - * @param mayBlock operation is allowed to block - * @param chal challenge input to YubiKey - * @param resp response output from YubiKey - * @return SUCCESS when successful - */ -YubiKey::ChallengeResult YubiKey::challenge(int slot, bool mayBlock, - const QByteArray& chal, - QByteArray& resp) const +YubiKey::ChallengeResult YubiKey::challenge(int slot, bool mayBlock, const QByteArray& chal, QByteArray& resp) const { int yk_cmd = (slot == 1) ? SLOT_CHAL_HMAC1 : SLOT_CHAL_HMAC2; QByteArray paddedChal = chal; @@ -188,7 +151,7 @@ YubiKey::ChallengeResult YubiKey::challenge(int slot, bool mayBlock, return ERROR; } - /* yk_challenge_response() insists on 64 byte response buffer */ + // yk_challenge_response() insists on 64 byte response buffer */ resp.resize(64); /* The challenge sent to the yubikey should always be 64 bytes for @@ -239,7 +202,7 @@ YubiKey::ChallengeResult YubiKey::challenge(int slot, bool mayBlock, } } - /* Actual HMAC-SHA1 response is only 20 bytes */ + // actual HMAC-SHA1 response is only 20 bytes resp.resize(20); #ifdef QT_DEBUG diff --git a/src/keys/drivers/YubiKey.h b/src/keys/drivers/YubiKey.h index 0441e69a..acf4feb7 100644 --- a/src/keys/drivers/YubiKey.h +++ b/src/keys/drivers/YubiKey.h @@ -30,21 +30,52 @@ class YubiKey : public QObject public: enum ChallengeResult { ERROR = -1, SUCCESS = 0, WOULDBLOCK }; + /** + * @brief YubiKey::instance - get instance of singleton + * @return instance + */ static YubiKey* instance(); - /** Initialize the underlying yubico libraries */ + /** + * @brief YubiKey::init - initialize yubikey library and hardware + * @return true on success + */ bool init(); + + /** + * @brief YubiKey::deinit - cleanup after init + * @return true on success + */ bool deinit(); - /** Issue a challenge to the hardware */ + /** + * @brief YubiKey::challenge - issue a challenge + * + * This operation could block if the YubiKey requires a touch to trigger. + * + * TODO: Signal to the UI that the system is waiting for challenge response + * touch. + * + * @param slot YubiKey configuration slot + * @param mayBlock operation is allowed to block + * @param chal challenge input to YubiKey + * @param resp response output from YubiKey + * @return true on success + */ ChallengeResult challenge(int slot, bool mayBlock, const QByteArray& chal, QByteArray& resp) const; - /** Read the serial number from the hardware */ + /** + * @brief YubiKey::getSerial - serial number of YubiKey + * @param serial serial number + * @return true on success + */ bool getSerial(unsigned int& serial) const; - /** Start looking for attached hardware devices */ + /** + * @brief YubiKey::detect - probe for attached YubiKeys + */ void detect(); Q_SIGNALS: @@ -65,9 +96,9 @@ private: explicit YubiKey(); static YubiKey* m_instance; - /* Create void ptr here to avoid ifdef header include mess */ - void *m_yk_void; - void *m_ykds_void; + // Create void ptr here to avoid ifdef header include mess + void* m_yk_void; + void* m_ykds_void; Q_DISABLE_COPY(YubiKey) }; From b2650c5a965a9da902dd294a0133f21ca8707ce5 Mon Sep 17 00:00:00 2001 From: Janek Bevendorff Date: Tue, 21 Feb 2017 01:06:32 +0100 Subject: [PATCH 27/93] Hide UI elements when KeePassXC was compiled without -DWITH_XC_YUBIKEY --- CMakeLists.txt | 5 ++-- src/CMakeLists.txt | 9 +++---- src/gui/ChangeMasterKeyWidget.cpp | 17 +++++++++--- src/gui/DatabaseOpenWidget.cpp | 45 ++++++++++++++++++++----------- src/keys/drivers/YubiKeyStub.cpp | 4 +-- 5 files changed, 50 insertions(+), 30 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 2c3bf58c..ecce4f0a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -195,13 +195,12 @@ if(NOT ZLIB_SUPPORTS_GZIP) endif() # Optional -find_package(YubiKey) +if(WITH_XC_YUBIKEY) + find_package(YubiKey REQUIRED) -if(YUBIKEY_FOUND) include_directories(SYSTEM ${YUBIKEY_INCLUDE_DIRS}) endif() - if(UNIX) check_cxx_source_compiles("#include int main() { prctl(PR_SET_DUMPABLE, 0); return 0; }" diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 45c711b7..e27c2492 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -179,7 +179,7 @@ if(MINGW) ${CMAKE_SOURCE_DIR}/share/windows/icon.rc) endif() -if(YUBIKEY_FOUND) +if(WITH_XC_YUBIKEY) set(keepassx_SOURCES ${keepassx_SOURCES} keys/drivers/YubiKey.cpp) else() set(keepassx_SOURCES ${keepassx_SOURCES} keys/drivers/YubiKeyStub.cpp) @@ -209,7 +209,8 @@ target_link_libraries(keepassx_core Qt5::Network ${GCRYPT_LIBRARIES} ${GPGERROR_LIBRARIES} - ${ZLIB_LIBRARIES}) + ${ZLIB_LIBRARIES} + ${YUBIKEY_LIBRARIES}) if (UNIX AND NOT APPLE) target_link_libraries(keepassx_core Qt5::DBus) endif() @@ -234,10 +235,6 @@ endif() add_executable(${PROGNAME} WIN32 MACOSX_BUNDLE ${keepassx_SOURCES_MAINEXE} ${WIN32_ProductVersionFiles}) target_link_libraries(${PROGNAME} keepassx_core) -if(YUBIKEY_FOUND) - target_link_libraries(keepassx_core ${YUBIKEY_LIBRARIES}) -endif() - set_target_properties(${PROGNAME} PROPERTIES ENABLE_EXPORTS ON) if(APPLE) diff --git a/src/gui/ChangeMasterKeyWidget.cpp b/src/gui/ChangeMasterKeyWidget.cpp index 051ff76a..32048e34 100644 --- a/src/gui/ChangeMasterKeyWidget.cpp +++ b/src/gui/ChangeMasterKeyWidget.cpp @@ -28,6 +28,8 @@ #include "gui/MessageBox.h" #include "crypto/Random.h" +#include "config-keepassx.h" + ChangeMasterKeyWidget::ChangeMasterKeyWidget(QWidget* parent) : DialogyWidget(parent) , m_ui(new Ui::ChangeMasterKeyWidget()) @@ -50,11 +52,15 @@ ChangeMasterKeyWidget::ChangeMasterKeyWidget(QWidget* parent) connect(m_ui->createKeyFileButton, SIGNAL(clicked()), SLOT(createKeyFile())); connect(m_ui->browseKeyFileButton, SIGNAL(clicked()), SLOT(browseKeyFile())); +#ifdef WITH_XC_YUBIKEY connect(m_ui->challengeResponseGroup, SIGNAL(clicked(bool)), SLOT(challengeResponseGroupToggled(bool))); connect(m_ui->buttonRedetectYubikey, SIGNAL(clicked()), SLOT(pollYubikey())); connect(YubiKey::instance(), SIGNAL(detected(int,bool)), SLOT(yubikeyDetected(int,bool)), Qt::QueuedConnection); connect(YubiKey::instance(), SIGNAL(notFound()), SLOT(noYubikeyFound()), Qt::QueuedConnection); +#else + m_ui->challengeResponseGroup->setVisible(false); +#endif } ChangeMasterKeyWidget::~ChangeMasterKeyWidget() @@ -98,8 +104,10 @@ void ChangeMasterKeyWidget::clearForms() m_ui->keyFileGroup->setChecked(false); m_ui->togglePasswordButton->setChecked(false); +#ifdef WITH_XC_YUBIKEY m_ui->challengeResponseGroup->setChecked(false); m_ui->comboChallengeResponse->clear(); +#endif m_ui->enterPasswordEdit->setFocus(); } @@ -118,9 +126,10 @@ void ChangeMasterKeyWidget::generateKey() { m_key.clear(); - bool anyChecked = m_ui->passwordGroup->isChecked(); - anyChecked |= m_ui->keyFileGroup->isChecked(); - anyChecked |= m_ui->challengeResponseGroup->isChecked(); + bool anyChecked = m_ui->passwordGroup->isChecked() | m_ui->keyFileGroup->isChecked(); +#ifdef WITH_XC_YUBIKEY + anyChecked |= m_ui->challengeResponseGroup->isChecked(); +#endif if (!anyChecked) { if (MessageBox::warning(this, tr("No authentication factor chosen"), @@ -160,6 +169,7 @@ void ChangeMasterKeyWidget::generateKey() m_key.addKey(fileKey); } +#ifdef WITH_XC_YUBIKEY if (m_ui->challengeResponseGroup->isChecked()) { int i = m_ui->comboChallengeResponse->currentIndex(); i = m_ui->comboChallengeResponse->itemData(i).toInt(); @@ -174,6 +184,7 @@ void ChangeMasterKeyWidget::generateKey() m_key.addChallengeResponseKey(key); } +#endif m_ui->messageWidget->hideMessage(); emit editFinished(true); diff --git a/src/gui/DatabaseOpenWidget.cpp b/src/gui/DatabaseOpenWidget.cpp index d9dd78f0..bac987c0 100644 --- a/src/gui/DatabaseOpenWidget.cpp +++ b/src/gui/DatabaseOpenWidget.cpp @@ -29,14 +29,16 @@ #include "format/KeePass2Reader.h" #include "keys/FileKey.h" #include "keys/PasswordKey.h" -#include "keys/YkChallengeResponseKey.h" #include "crypto/Random.h" +#include "keys/YkChallengeResponseKey.h" + +#include "config-keepassx.h" DatabaseOpenWidget::DatabaseOpenWidget(QWidget* parent) - : DialogyWidget(parent) - , m_ui(new Ui::DatabaseOpenWidget()) - , m_db(nullptr) + : DialogyWidget(parent), + m_ui(new Ui::DatabaseOpenWidget()), + m_db(nullptr) { m_ui->setupUi(this); @@ -49,11 +51,6 @@ DatabaseOpenWidget::DatabaseOpenWidget(QWidget* parent) m_ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false); - m_ui->yubikeyProgress->setVisible(false); - QSizePolicy sp = m_ui->yubikeyProgress->sizePolicy(); - sp.setRetainSizeWhenHidden(true); - m_ui->yubikeyProgress->setSizePolicy(sp); - m_ui->buttonTogglePassword->setIcon(filePath()->onOffIcon("actions", "password-show")); connect(m_ui->buttonTogglePassword, SIGNAL(toggled(bool)), m_ui->editPassword, SLOT(setShowPassword(bool))); @@ -61,21 +58,33 @@ DatabaseOpenWidget::DatabaseOpenWidget(QWidget* parent) connect(m_ui->editPassword, SIGNAL(textChanged(QString)), SLOT(activatePassword())); connect(m_ui->comboKeyFile, SIGNAL(editTextChanged(QString)), SLOT(activateKeyFile())); - connect(m_ui->comboChallengeResponse, SIGNAL(activated(int)), SLOT(activateChallengeResponse())); connect(m_ui->checkPassword, SIGNAL(toggled(bool)), SLOT(setOkButtonEnabled())); connect(m_ui->checkKeyFile, SIGNAL(toggled(bool)), SLOT(setOkButtonEnabled())); connect(m_ui->comboKeyFile, SIGNAL(editTextChanged(QString)), SLOT(setOkButtonEnabled())); - connect(m_ui->checkChallengeResponse, SIGNAL(toggled(bool)), SLOT(setOkButtonEnabled())); - connect(m_ui->comboChallengeResponse, SIGNAL(activated(int)), SLOT(setOkButtonEnabled())); connect(m_ui->buttonBox, SIGNAL(accepted()), SLOT(openDatabase())); connect(m_ui->buttonBox, SIGNAL(rejected()), SLOT(reject())); +#ifdef WITH_XC_YUBIKEY + m_ui->yubikeyProgress->setVisible(false); + QSizePolicy sp = m_ui->yubikeyProgress->sizePolicy(); + sp.setRetainSizeWhenHidden(true); + m_ui->yubikeyProgress->setSizePolicy(sp); + connect(m_ui->buttonRedetectYubikey, SIGNAL(clicked()), SLOT(pollYubikey())); + connect(m_ui->comboChallengeResponse, SIGNAL(activated(int)), SLOT(activateChallengeResponse())); + connect(m_ui->checkChallengeResponse, SIGNAL(toggled(bool)), SLOT(setOkButtonEnabled())); + connect(m_ui->comboChallengeResponse, SIGNAL(activated(int)), SLOT(setOkButtonEnabled())); connect(YubiKey::instance(), SIGNAL(detected(int,bool)), SLOT(yubikeyDetected(int,bool)), Qt::QueuedConnection); connect(YubiKey::instance(), SIGNAL(notFound()), SLOT(noYubikeyFound()), Qt::QueuedConnection); +#else + m_ui->checkChallengeResponse->setVisible(false); + m_ui->buttonRedetectYubikey->setVisible(false); + m_ui->comboChallengeResponse->setVisible(false); + m_ui->yubikeyProgress->setVisible(false); +#endif #ifdef Q_OS_MACOS // add random padding to layouts to align widgets properly @@ -109,10 +118,12 @@ void DatabaseOpenWidget::load(const QString& filename) } } - m_ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(true); - +#ifdef WITH_XC_YUBIKEY m_ui->comboChallengeResponse->clear(); pollYubikey(); +#endif + + m_ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(true); m_ui->editPassword->setFocus(); } @@ -199,9 +210,12 @@ CompositeKey DatabaseOpenWidget::databaseKey() if (config()->get("RememberLastKeyFiles").toBool()) { config()->set("LastKeyFiles", lastKeyFiles); - config()->set("LastChallengeResponse", lastChallengeResponse); } +#ifdef WITH_XC_YUBIKEY + if (config()->get("RememberLastKeyFiles").toBool()) { + config()->set("LastChallengeResponse", lastChallengeResponse); + } if (m_ui->checkChallengeResponse->isChecked()) { int i = m_ui->comboChallengeResponse->currentIndex(); @@ -210,6 +224,7 @@ CompositeKey DatabaseOpenWidget::databaseKey() masterKey.addChallengeResponseKey(key); } +#endif return masterKey; } diff --git a/src/keys/drivers/YubiKeyStub.cpp b/src/keys/drivers/YubiKeyStub.cpp index c00790f3..e93099bf 100644 --- a/src/keys/drivers/YubiKeyStub.cpp +++ b/src/keys/drivers/YubiKeyStub.cpp @@ -58,9 +58,7 @@ bool YubiKey::getSerial(unsigned int& serial) const return false; } -YubiKey::ChallengeResult YubiKey::challenge(int slot, bool mayBlock, - const QByteArray& chal, - QByteArray& resp) const +YubiKey::ChallengeResult YubiKey::challenge(int slot, bool mayBlock, const QByteArray& chal, QByteArray& resp) const { Q_UNUSED(slot); Q_UNUSED(mayBlock); From 8e91a89a3735b3e14597a2537542bacb9f05508d Mon Sep 17 00:00:00 2001 From: Janek Bevendorff Date: Tue, 21 Feb 2017 01:28:50 +0100 Subject: [PATCH 28/93] Force at least one encryption key (no more unencrypted databases) --- src/gui/ChangeMasterKeyWidget.cpp | 45 +++++++++++++++++-------------- src/gui/ChangeMasterKeyWidget.h | 7 +++-- src/gui/DatabaseOpenWidget.cpp | 2 -- 3 files changed, 30 insertions(+), 24 deletions(-) diff --git a/src/gui/ChangeMasterKeyWidget.cpp b/src/gui/ChangeMasterKeyWidget.cpp index 32048e34..8c17ff57 100644 --- a/src/gui/ChangeMasterKeyWidget.cpp +++ b/src/gui/ChangeMasterKeyWidget.cpp @@ -41,19 +41,25 @@ ChangeMasterKeyWidget::ChangeMasterKeyWidget(QWidget* parent) m_ui->togglePasswordButton->setIcon(filePath()->onOffIcon("actions", "password-show")); m_ui->repeatPasswordEdit->enableVerifyMode(m_ui->enterPasswordEdit); + connect(m_ui->passwordGroup, SIGNAL(clicked(bool)), SLOT(setOkEnabled())); + connect(m_ui->togglePasswordButton, SIGNAL(toggled(bool)), m_ui->enterPasswordEdit, SLOT(setShowPassword(bool))); + + connect(m_ui->keyFileGroup, SIGNAL(clicked(bool)), SLOT(setOkEnabled())); + connect(m_ui->createKeyFileButton, SIGNAL(clicked()), SLOT(createKeyFile())); + connect(m_ui->browseKeyFileButton, SIGNAL(clicked()), SLOT(browseKeyFile())); + connect(m_ui->keyFileCombo, SIGNAL(editTextChanged(QString)), SLOT(setOkEnabled())); + + connect(m_ui->buttonBox, SIGNAL(accepted()), SLOT(generateKey())); + connect(m_ui->buttonBox, SIGNAL(rejected()), SLOT(reject())); + +#ifdef WITH_XC_YUBIKEY m_ui->yubikeyProgress->setVisible(false); QSizePolicy sp = m_ui->yubikeyProgress->sizePolicy(); sp.setRetainSizeWhenHidden(true); m_ui->yubikeyProgress->setSizePolicy(sp); - connect(m_ui->buttonBox, SIGNAL(accepted()), SLOT(generateKey())); - connect(m_ui->buttonBox, SIGNAL(rejected()), SLOT(reject())); - connect(m_ui->togglePasswordButton, SIGNAL(toggled(bool)), m_ui->enterPasswordEdit, SLOT(setShowPassword(bool))); - connect(m_ui->createKeyFileButton, SIGNAL(clicked()), SLOT(createKeyFile())); - connect(m_ui->browseKeyFileButton, SIGNAL(clicked()), SLOT(browseKeyFile())); - -#ifdef WITH_XC_YUBIKEY connect(m_ui->challengeResponseGroup, SIGNAL(clicked(bool)), SLOT(challengeResponseGroupToggled(bool))); + connect(m_ui->challengeResponseGroup, SIGNAL(clicked(bool)), SLOT(setOkEnabled())); connect(m_ui->buttonRedetectYubikey, SIGNAL(clicked()), SLOT(pollYubikey())); connect(YubiKey::instance(), SIGNAL(detected(int,bool)), SLOT(yubikeyDetected(int,bool)), Qt::QueuedConnection); @@ -126,19 +132,6 @@ void ChangeMasterKeyWidget::generateKey() { m_key.clear(); - bool anyChecked = m_ui->passwordGroup->isChecked() | m_ui->keyFileGroup->isChecked(); -#ifdef WITH_XC_YUBIKEY - anyChecked |= m_ui->challengeResponseGroup->isChecked(); -#endif - - if (!anyChecked) { - if (MessageBox::warning(this, tr("No authentication factor chosen"), - tr("Your database will be completely unprotected!
Do you really want to continue?"), - QMessageBox::Yes | QMessageBox::No) != QMessageBox::Yes) { - return; - } - } - if (m_ui->passwordGroup->isChecked()) { if (m_ui->enterPasswordEdit->text() == m_ui->repeatPasswordEdit->text()) { if (m_ui->enterPasswordEdit->text().isEmpty()) { @@ -208,6 +201,7 @@ void ChangeMasterKeyWidget::pollYubikey() m_ui->comboChallengeResponse->setEnabled(false); m_ui->comboChallengeResponse->clear(); m_ui->yubikeyProgress->setVisible(true); + setOkEnabled(); // YubiKey init is slow, detect asynchronously to not block the UI QtConcurrent::run(YubiKey::instance(), &YubiKey::detect); @@ -220,12 +214,23 @@ void ChangeMasterKeyWidget::yubikeyDetected(int slot, bool blocking) m_ui->comboChallengeResponse->setEnabled(m_ui->challengeResponseGroup->isChecked()); m_ui->buttonRedetectYubikey->setEnabled(m_ui->challengeResponseGroup->isChecked()); m_ui->yubikeyProgress->setVisible(false); + setOkEnabled(); } void ChangeMasterKeyWidget::noYubikeyFound() { m_ui->buttonRedetectYubikey->setEnabled(m_ui->challengeResponseGroup->isChecked()); m_ui->yubikeyProgress->setVisible(false); + setOkEnabled(); +} + +void ChangeMasterKeyWidget::setOkEnabled() +{ + bool ok = m_ui->passwordGroup->isChecked() || + (m_ui->challengeResponseGroup->isChecked() && !m_ui->comboChallengeResponse->currentText().isEmpty()) || + (m_ui->keyFileGroup->isChecked() && !m_ui->keyFileCombo->currentText().isEmpty()); + + m_ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(ok); } void ChangeMasterKeyWidget::setCancelEnabled(bool enabled) diff --git a/src/gui/ChangeMasterKeyWidget.h b/src/gui/ChangeMasterKeyWidget.h index 0a99037f..b3e09727 100644 --- a/src/gui/ChangeMasterKeyWidget.h +++ b/src/gui/ChangeMasterKeyWidget.h @@ -38,12 +38,15 @@ public: void clearForms(); CompositeKey newMasterKey(); QLabel* headlineLabel(); + +public slots: + void setOkEnabled(); void setCancelEnabled(bool enabled); -Q_SIGNALS: +signals: void editFinished(bool accepted); -private Q_SLOTS: +private slots: void generateKey(); void reject(); void createKeyFile(); diff --git a/src/gui/DatabaseOpenWidget.cpp b/src/gui/DatabaseOpenWidget.cpp index bac987c0..10730426 100644 --- a/src/gui/DatabaseOpenWidget.cpp +++ b/src/gui/DatabaseOpenWidget.cpp @@ -123,8 +123,6 @@ void DatabaseOpenWidget::load(const QString& filename) pollYubikey(); #endif - m_ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(true); - m_ui->editPassword->setFocus(); } From 91761a2beab80b408e43b92cecf8e1e167950d27 Mon Sep 17 00:00:00 2001 From: Janek Bevendorff Date: Tue, 21 Feb 2017 02:19:11 +0100 Subject: [PATCH 29/93] Only poll YubiKey for currently visible tab --- src/gui/DatabaseOpenWidget.cpp | 11 +++++------ src/gui/DatabaseOpenWidget.h | 11 ++++++----- src/gui/DatabaseOpenWidget.ui | 2 +- src/gui/DatabaseTabWidget.cpp | 4 ++-- src/gui/DatabaseTabWidget.h | 2 +- 5 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/gui/DatabaseOpenWidget.cpp b/src/gui/DatabaseOpenWidget.cpp index 10730426..475d00c7 100644 --- a/src/gui/DatabaseOpenWidget.cpp +++ b/src/gui/DatabaseOpenWidget.cpp @@ -102,6 +102,10 @@ void DatabaseOpenWidget::showEvent(QShowEvent* event) { DialogyWidget::showEvent(event); m_ui->editPassword->setFocus(); + +#ifdef WITH_XC_YUBIKEY + pollYubikey(); +#endif } void DatabaseOpenWidget::load(const QString& filename) @@ -118,11 +122,6 @@ void DatabaseOpenWidget::load(const QString& filename) } } -#ifdef WITH_XC_YUBIKEY - m_ui->comboChallengeResponse->clear(); - pollYubikey(); -#endif - m_ui->editPassword->setFocus(); } @@ -229,7 +228,7 @@ CompositeKey DatabaseOpenWidget::databaseKey() void DatabaseOpenWidget::reject() { - Q_EMIT editFinished(false); + emit editFinished(false); } void DatabaseOpenWidget::activatePassword() diff --git a/src/gui/DatabaseOpenWidget.h b/src/gui/DatabaseOpenWidget.h index d0234605..975a530c 100644 --- a/src/gui/DatabaseOpenWidget.h +++ b/src/gui/DatabaseOpenWidget.h @@ -41,18 +41,21 @@ public: void enterKey(const QString& pw, const QString& keyFile); Database* database(); -Q_SIGNALS: +public slots: + void pollYubikey(); + +signals: void editFinished(bool accepted); protected: void showEvent(QShowEvent* event) override; CompositeKey databaseKey(); -protected Q_SLOTS: +protected slots: virtual void openDatabase(); void reject(); -private Q_SLOTS: +private slots: void activatePassword(); void activateKeyFile(); void activateChallengeResponse(); @@ -60,10 +63,8 @@ private Q_SLOTS: void browseKeyFile(); void yubikeyDetected(int slot, bool blocking); void noYubikeyFound(); - void pollYubikey(); protected: - const QScopedPointer m_ui; Database* m_db; QString m_filename; diff --git a/src/gui/DatabaseOpenWidget.ui b/src/gui/DatabaseOpenWidget.ui index 7a97d008..dba71d0f 100644 --- a/src/gui/DatabaseOpenWidget.ui +++ b/src/gui/DatabaseOpenWidget.ui @@ -156,7 +156,7 @@ - false + true Refresh diff --git a/src/gui/DatabaseTabWidget.cpp b/src/gui/DatabaseTabWidget.cpp index 3a48098d..734dc3f2 100644 --- a/src/gui/DatabaseTabWidget.cpp +++ b/src/gui/DatabaseTabWidget.cpp @@ -53,7 +53,7 @@ const int DatabaseTabWidget::LastDatabasesCount = 5; DatabaseTabWidget::DatabaseTabWidget(QWidget* parent) : QTabWidget(parent) - , m_dbWidgetSateSync(new DatabaseWidgetStateSync(this)) + , m_dbWidgetStateSync(new DatabaseWidgetStateSync(this)) { DragTabBar* tabBar = new DragTabBar(this); setTabBar(tabBar); @@ -61,7 +61,7 @@ DatabaseTabWidget::DatabaseTabWidget(QWidget* parent) connect(this, SIGNAL(tabCloseRequested(int)), SLOT(closeDatabase(int))); connect(this, SIGNAL(currentChanged(int)), SLOT(emitActivateDatabaseChanged())); - connect(this, SIGNAL(activateDatabaseChanged(DatabaseWidget*)), m_dbWidgetSateSync, SLOT(setActive(DatabaseWidget*))); + connect(this, SIGNAL(activateDatabaseChanged(DatabaseWidget*)), m_dbWidgetStateSync, SLOT(setActive(DatabaseWidget*))); connect(autoType(), SIGNAL(globalShortcutTriggered()), SLOT(performGlobalAutoType())); } diff --git a/src/gui/DatabaseTabWidget.h b/src/gui/DatabaseTabWidget.h index 8f01a987..6eabcd49 100644 --- a/src/gui/DatabaseTabWidget.h +++ b/src/gui/DatabaseTabWidget.h @@ -116,7 +116,7 @@ private: KeePass2Writer m_writer; QHash m_dbList; - DatabaseWidgetStateSync* m_dbWidgetSateSync; + DatabaseWidgetStateSync* m_dbWidgetStateSync; }; #endif // KEEPASSX_DATABASETABWIDGET_H From e93e4a99315cb011bd8794a94ce2d79928c42a42 Mon Sep 17 00:00:00 2001 From: Janek Bevendorff Date: Tue, 21 Feb 2017 02:40:23 +0100 Subject: [PATCH 30/93] Allow opening of unprotected databases (but don't allow creating them) --- src/gui/DatabaseOpenWidget.cpp | 16 ---------------- src/gui/DatabaseOpenWidget.h | 1 - 2 files changed, 17 deletions(-) diff --git a/src/gui/DatabaseOpenWidget.cpp b/src/gui/DatabaseOpenWidget.cpp index 475d00c7..1543dc43 100644 --- a/src/gui/DatabaseOpenWidget.cpp +++ b/src/gui/DatabaseOpenWidget.cpp @@ -49,8 +49,6 @@ DatabaseOpenWidget::DatabaseOpenWidget(QWidget* parent) font.setPointSize(font.pointSize() + 2); m_ui->labelHeadline->setFont(font); - m_ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false); - m_ui->buttonTogglePassword->setIcon(filePath()->onOffIcon("actions", "password-show")); connect(m_ui->buttonTogglePassword, SIGNAL(toggled(bool)), m_ui->editPassword, SLOT(setShowPassword(bool))); @@ -59,10 +57,6 @@ DatabaseOpenWidget::DatabaseOpenWidget(QWidget* parent) connect(m_ui->editPassword, SIGNAL(textChanged(QString)), SLOT(activatePassword())); connect(m_ui->comboKeyFile, SIGNAL(editTextChanged(QString)), SLOT(activateKeyFile())); - connect(m_ui->checkPassword, SIGNAL(toggled(bool)), SLOT(setOkButtonEnabled())); - connect(m_ui->checkKeyFile, SIGNAL(toggled(bool)), SLOT(setOkButtonEnabled())); - connect(m_ui->comboKeyFile, SIGNAL(editTextChanged(QString)), SLOT(setOkButtonEnabled())); - connect(m_ui->buttonBox, SIGNAL(accepted()), SLOT(openDatabase())); connect(m_ui->buttonBox, SIGNAL(rejected()), SLOT(reject())); @@ -74,8 +68,6 @@ DatabaseOpenWidget::DatabaseOpenWidget(QWidget* parent) connect(m_ui->buttonRedetectYubikey, SIGNAL(clicked()), SLOT(pollYubikey())); connect(m_ui->comboChallengeResponse, SIGNAL(activated(int)), SLOT(activateChallengeResponse())); - connect(m_ui->checkChallengeResponse, SIGNAL(toggled(bool)), SLOT(setOkButtonEnabled())); - connect(m_ui->comboChallengeResponse, SIGNAL(activated(int)), SLOT(setOkButtonEnabled())); connect(YubiKey::instance(), SIGNAL(detected(int,bool)), SLOT(yubikeyDetected(int,bool)), Qt::QueuedConnection); connect(YubiKey::instance(), SIGNAL(notFound()), SLOT(noYubikeyFound()), Qt::QueuedConnection); @@ -246,14 +238,6 @@ void DatabaseOpenWidget::activateChallengeResponse() m_ui->checkChallengeResponse->setChecked(true); } -void DatabaseOpenWidget::setOkButtonEnabled() -{ - bool enable = m_ui->checkPassword->isChecked() || m_ui->checkChallengeResponse->isChecked() - || (m_ui->checkKeyFile->isChecked() && !m_ui->comboKeyFile->currentText().isEmpty()); - - m_ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(enable); -} - void DatabaseOpenWidget::browseKeyFile() { QString filters = QString("%1 (*);;%2 (*.key)").arg(tr("All files"), tr("Key files")); diff --git a/src/gui/DatabaseOpenWidget.h b/src/gui/DatabaseOpenWidget.h index 975a530c..caba70a6 100644 --- a/src/gui/DatabaseOpenWidget.h +++ b/src/gui/DatabaseOpenWidget.h @@ -59,7 +59,6 @@ private slots: void activatePassword(); void activateKeyFile(); void activateChallengeResponse(); - void setOkButtonEnabled(); void browseKeyFile(); void yubikeyDetected(int slot, bool blocking); void noYubikeyFound(); From 093fe5c7ef9a513f18b7f258ffcf6711e7346ba8 Mon Sep 17 00:00:00 2001 From: Janek Bevendorff Date: Thu, 23 Feb 2017 23:52:36 +0100 Subject: [PATCH 31/93] Use QSharedPointer instead of cloning YkChallengeResponseKey and make it a QObject to allow emitting signals --- src/core/Database.cpp | 6 ++-- src/format/KeePass2Reader.cpp | 2 +- src/gui/ChangeMasterKeyWidget.cpp | 13 +++---- src/gui/DatabaseOpenWidget.cpp | 16 +++++---- src/keys/ChallengeResponseKey.h | 1 - src/keys/CompositeKey.cpp | 21 ++++++----- src/keys/CompositeKey.h | 5 +-- src/keys/YkChallengeResponseKey.cpp | 56 ++++++++++++++--------------- src/keys/YkChallengeResponseKey.h | 24 ++++++++++--- 9 files changed, 78 insertions(+), 66 deletions(-) diff --git a/src/core/Database.cpp b/src/core/Database.cpp index 22fc0723..a441910d 100644 --- a/src/core/Database.cpp +++ b/src/core/Database.cpp @@ -257,18 +257,16 @@ bool Database::verifyKey(const CompositeKey& key) const { Q_ASSERT(hasKey()); - /* If the database has challenge response keys, then the the verification - * key better as well */ if (!m_data.challengeResponseKey.isEmpty()) { QByteArray result; if (!key.challenge(m_data.masterSeed, result)) { - /* Challenge failed, (YubiKey?) removed? */ + // challenge failed, (YubiKey?) removed? return false; } if (m_data.challengeResponseKey != result) { - /* Wrong response from challenged device(s) */ + // wrong response from challenged device(s) return false; } } diff --git a/src/format/KeePass2Reader.cpp b/src/format/KeePass2Reader.cpp index 33bea620..ffe4e94f 100644 --- a/src/format/KeePass2Reader.cpp +++ b/src/format/KeePass2Reader.cpp @@ -115,7 +115,7 @@ Database* KeePass2Reader::readDatabase(QIODevice* device, const CompositeKey& ke if (m_db->challengeMasterSeed(m_masterSeed) == false) { raiseError(tr("Unable to issue challenge-response.")); - return Q_NULLPTR; + return nullptr; } CryptoHash hash(CryptoHash::Sha256); diff --git a/src/gui/ChangeMasterKeyWidget.cpp b/src/gui/ChangeMasterKeyWidget.cpp index 8c17ff57..453498a1 100644 --- a/src/gui/ChangeMasterKeyWidget.cpp +++ b/src/gui/ChangeMasterKeyWidget.cpp @@ -15,8 +15,6 @@ * along with this program. If not, see . */ -#include - #include "ChangeMasterKeyWidget.h" #include "ui_ChangeMasterKeyWidget.h" @@ -30,6 +28,9 @@ #include "config-keepassx.h" +#include +#include + ChangeMasterKeyWidget::ChangeMasterKeyWidget(QWidget* parent) : DialogyWidget(parent) , m_ui(new Ui::ChangeMasterKeyWidget()) @@ -172,9 +173,9 @@ void ChangeMasterKeyWidget::generateKey() MessageWidget::Error); return; } - - YkChallengeResponseKey key(i); - + bool blocking = i & true; + int slot = i >> 1; + auto key = QSharedPointer(new YkChallengeResponseKey(slot, blocking)); m_key.addChallengeResponseKey(key); } #endif @@ -210,7 +211,7 @@ void ChangeMasterKeyWidget::pollYubikey() void ChangeMasterKeyWidget::yubikeyDetected(int slot, bool blocking) { YkChallengeResponseKey yk(slot, blocking); - m_ui->comboChallengeResponse->addItem(yk.getName(), QVariant(slot)); + m_ui->comboChallengeResponse->addItem(yk.getName(), QVariant((slot << 1) | blocking)); m_ui->comboChallengeResponse->setEnabled(m_ui->challengeResponseGroup->isChecked()); m_ui->buttonRedetectYubikey->setEnabled(m_ui->challengeResponseGroup->isChecked()); m_ui->yubikeyProgress->setVisible(false); diff --git a/src/gui/DatabaseOpenWidget.cpp b/src/gui/DatabaseOpenWidget.cpp index 1543dc43..2eebcae3 100644 --- a/src/gui/DatabaseOpenWidget.cpp +++ b/src/gui/DatabaseOpenWidget.cpp @@ -15,8 +15,6 @@ * along with this program. If not, see . */ -#include - #include "DatabaseOpenWidget.h" #include "ui_DatabaseOpenWidget.h" @@ -34,6 +32,9 @@ #include "config-keepassx.h" +#include +#include + DatabaseOpenWidget::DatabaseOpenWidget(QWidget* parent) : DialogyWidget(parent), @@ -156,7 +157,7 @@ void DatabaseOpenWidget::openDatabase() if (m_ui->messageWidget->isVisible()) { m_ui->messageWidget->animatedHide(); } - Q_EMIT editFinished(true); + emit editFinished(true); } else { m_ui->messageWidget->showMessage(tr("Unable to open the database.") @@ -209,8 +210,10 @@ CompositeKey DatabaseOpenWidget::databaseKey() if (m_ui->checkChallengeResponse->isChecked()) { int i = m_ui->comboChallengeResponse->currentIndex(); i = m_ui->comboChallengeResponse->itemData(i).toInt(); - YkChallengeResponseKey key(i); + bool blocking = i & true; + int slot = i >> 1; + auto key = QSharedPointer(new YkChallengeResponseKey(slot, blocking)); masterKey.addChallengeResponseKey(key); } #endif @@ -250,20 +253,21 @@ void DatabaseOpenWidget::browseKeyFile() void DatabaseOpenWidget::pollYubikey() { - // YubiKey init is slow, detect asynchronously to not block the UI m_ui->buttonRedetectYubikey->setEnabled(false); m_ui->checkChallengeResponse->setEnabled(false); m_ui->checkChallengeResponse->setChecked(false); m_ui->comboChallengeResponse->setEnabled(false); m_ui->comboChallengeResponse->clear(); m_ui->yubikeyProgress->setVisible(true); + + // YubiKey init is slow, detect asynchronously to not block the UI QtConcurrent::run(YubiKey::instance(), &YubiKey::detect); } void DatabaseOpenWidget::yubikeyDetected(int slot, bool blocking) { YkChallengeResponseKey yk(slot, blocking); - m_ui->comboChallengeResponse->addItem(yk.getName(), QVariant(slot)); + m_ui->comboChallengeResponse->addItem(yk.getName(), QVariant((slot << 1) | blocking)); m_ui->comboChallengeResponse->setEnabled(true); m_ui->checkChallengeResponse->setEnabled(true); m_ui->buttonRedetectYubikey->setEnabled(true); diff --git a/src/keys/ChallengeResponseKey.h b/src/keys/ChallengeResponseKey.h index e03a2f9f..ac8c8165 100644 --- a/src/keys/ChallengeResponseKey.h +++ b/src/keys/ChallengeResponseKey.h @@ -25,7 +25,6 @@ class ChallengeResponseKey public: virtual ~ChallengeResponseKey() {} virtual QByteArray rawKey() const = 0; - virtual ChallengeResponseKey* clone() const = 0; virtual bool challenge(const QByteArray& challenge) = 0; }; diff --git a/src/keys/CompositeKey.cpp b/src/keys/CompositeKey.cpp index 4e79cd05..6114fd36 100644 --- a/src/keys/CompositeKey.cpp +++ b/src/keys/CompositeKey.cpp @@ -46,7 +46,6 @@ CompositeKey::~CompositeKey() void CompositeKey::clear() { qDeleteAll(m_keys); - qDeleteAll(m_challengeResponseKeys); m_keys.clear(); m_challengeResponseKeys.clear(); } @@ -73,8 +72,8 @@ CompositeKey& CompositeKey::operator=(const CompositeKey& key) for (const Key* subKey : asConst(key.m_keys)) { addKey(*subKey); } - for (const ChallengeResponseKey* subKey : asConst(key.m_challengeResponseKeys)) { - addChallengeResponseKey(*subKey); + for (const auto subKey : asConst(key.m_challengeResponseKeys)) { + addChallengeResponseKey(subKey); } return *this; @@ -176,9 +175,8 @@ QByteArray CompositeKey::transformKeyRaw(const QByteArray& key, const QByteArray bool CompositeKey::challenge(const QByteArray& seed, QByteArray& result) const { - /* If no challenge response was requested, return nothing to - * maintain backwards compatability with regular databases. - */ + // if no challenge response was requested, return nothing to + // maintain backwards compatibility with regular databases. if (m_challengeResponseKeys.length() == 0) { result.clear(); return true; @@ -186,9 +184,9 @@ bool CompositeKey::challenge(const QByteArray& seed, QByteArray& result) const CryptoHash cryptoHash(CryptoHash::Sha256); - for (ChallengeResponseKey* key : m_challengeResponseKeys) { - /* If the device isn't present or fails, return an error */ - if (key->challenge(seed) == false) { + for (const auto key : m_challengeResponseKeys) { + // if the device isn't present or fails, return an error + if (!key->challenge(seed)) { return false; } cryptoHash.addData(key->rawKey()); @@ -203,11 +201,12 @@ void CompositeKey::addKey(const Key& key) m_keys.append(key.clone()); } -void CompositeKey::addChallengeResponseKey(const ChallengeResponseKey& key) +void CompositeKey::addChallengeResponseKey(QSharedPointer key) { - m_challengeResponseKeys.append(key.clone()); + m_challengeResponseKeys.append(key); } + int CompositeKey::transformKeyBenchmark(int msec) { TransformKeyBenchmarkThread thread1(msec); diff --git a/src/keys/CompositeKey.h b/src/keys/CompositeKey.h index 531c2d9b..50b2f699 100644 --- a/src/keys/CompositeKey.h +++ b/src/keys/CompositeKey.h @@ -20,6 +20,7 @@ #include #include +#include #include "keys/Key.h" #include "keys/ChallengeResponseKey.h" @@ -41,7 +42,7 @@ public: bool challenge(const QByteArray& seed, QByteArray &result) const; void addKey(const Key& key); - void addChallengeResponseKey(const ChallengeResponseKey& key); + void addChallengeResponseKey(QSharedPointer key); static int transformKeyBenchmark(int msec); static CompositeKey readFromLine(QString line); @@ -51,7 +52,7 @@ private: quint64 rounds, bool* ok, QString* errorString); QList m_keys; - QList m_challengeResponseKeys; + QList> m_challengeResponseKeys; }; #endif // KEEPASSX_COMPOSITEKEY_H diff --git a/src/keys/YkChallengeResponseKey.cpp b/src/keys/YkChallengeResponseKey.cpp index 51520e3d..a6e5cdbc 100644 --- a/src/keys/YkChallengeResponseKey.cpp +++ b/src/keys/YkChallengeResponseKey.cpp @@ -15,7 +15,6 @@ * along with this program. If not, see . */ - #include #include @@ -26,11 +25,7 @@ #include "keys/YkChallengeResponseKey.h" #include "keys/drivers/YubiKey.h" -#include -#include - -YkChallengeResponseKey::YkChallengeResponseKey(int slot, - bool blocking) +YkChallengeResponseKey::YkChallengeResponseKey(int slot, bool blocking) : m_slot(slot), m_blocking(blocking) { @@ -41,40 +36,41 @@ QByteArray YkChallengeResponseKey::rawKey() const return m_key; } -YkChallengeResponseKey* YkChallengeResponseKey::clone() const -{ - return new YkChallengeResponseKey(*this); -} - - -/** Assumes yubikey()->init() was called */ +/** + * Assumes yubikey()->init() was called + */ bool YkChallengeResponseKey::challenge(const QByteArray& chal) { return challenge(chal, 1); } - -bool YkChallengeResponseKey::challenge(const QByteArray& chal, int retries) +#include +bool YkChallengeResponseKey::challenge(const QByteArray& chal, unsigned retries) { - if (YubiKey::instance()->challenge(m_slot, true, chal, m_key) != YubiKey::ERROR) { - return true; - } + Q_ASSERT(retries > 0); - /* If challenge failed, retry to detect YubiKeys in the event the YubiKey - * was un-plugged and re-plugged */ - while (retries > 0) { -#ifdef QT_DEBUG - qDebug() << "Attempt" << retries << "to re-detect YubiKey(s)"; -#endif - retries--; + do { + --retries; - if (YubiKey::instance()->init() != true) { + if (m_blocking) { + emit userInteractionRequired(); + } + + auto result = YubiKey::instance()->challenge(m_slot, true, chal, m_key); + + if (m_blocking) { + emit userConfirmed(); + } + + if (result != YubiKey::ERROR) { + return true; + } + + // if challenge failed, retry to detect YubiKeys in the event the YubiKey was un-plugged and re-plugged + if (retries > 0 && YubiKey::instance()->init() != true) { continue; } - if (YubiKey::instance()->challenge(m_slot, true, chal, m_key) != YubiKey::ERROR) { - return true; - } - } + } while (retries > 0); return false; } diff --git a/src/keys/YkChallengeResponseKey.h b/src/keys/YkChallengeResponseKey.h index 8acb0f9e..96e44220 100644 --- a/src/keys/YkChallengeResponseKey.h +++ b/src/keys/YkChallengeResponseKey.h @@ -22,20 +22,34 @@ #include "keys/ChallengeResponseKey.h" #include "keys/drivers/YubiKey.h" -class YkChallengeResponseKey : public ChallengeResponseKey +#include + +class YkChallengeResponseKey : public QObject, public ChallengeResponseKey { + Q_OBJECT + public: - YkChallengeResponseKey(int slot = -1, - bool blocking = false); + YkChallengeResponseKey(int slot = -1, bool blocking = false); QByteArray rawKey() const; - YkChallengeResponseKey* clone() const; bool challenge(const QByteArray& chal); - bool challenge(const QByteArray& chal, int retries); + bool challenge(const QByteArray& chal, unsigned retries); QString getName() const; bool isBlocking() const; +signals: + /** + * Emitted whenever user interaction is required to proceed with the challenge-response protocol. + * You can use this to show a helpful dialog informing the user that his assistance is required. + */ + void userInteractionRequired(); + + /** + * Emitted when the user has provided their required input. + */ + void userConfirmed(); + private: QByteArray m_key; int m_slot; From 70816f90b2c4488c9636ca60124eb897c7da6371 Mon Sep 17 00:00:00 2001 From: Janek Bevendorff Date: Fri, 24 Feb 2017 00:15:57 +0100 Subject: [PATCH 32/93] Make challenge() member thread-safe --- src/keys/drivers/YubiKey.cpp | 27 ++++++++++++++++----------- src/keys/drivers/YubiKey.h | 12 ++++++++++-- 2 files changed, 26 insertions(+), 13 deletions(-) diff --git a/src/keys/drivers/YubiKey.cpp b/src/keys/drivers/YubiKey.cpp index b287c13b..1d4445cd 100644 --- a/src/keys/drivers/YubiKey.cpp +++ b/src/keys/drivers/YubiKey.cpp @@ -107,8 +107,10 @@ void YubiKey::detect() QByteArray resp; result = challenge(i, false, rand, resp); - - if (result != YubiKey::ERROR) { + if (result == YubiKey::ALREADY_RUNNING) { + emit alreadyRunning(); + return; + } else if (result != YubiKey::ERROR) { emit detected(i, result == YubiKey::WOULDBLOCK ? true : false); return; } @@ -141,13 +143,18 @@ static inline QString printByteArray(const QByteArray& a) } #endif -YubiKey::ChallengeResult YubiKey::challenge(int slot, bool mayBlock, const QByteArray& chal, QByteArray& resp) const +YubiKey::ChallengeResult YubiKey::challenge(int slot, bool mayBlock, const QByteArray& chal, QByteArray& resp) { + if (!m_mutex.tryLock()) { + return ALREADY_RUNNING; + } + int yk_cmd = (slot == 1) ? SLOT_CHAL_HMAC1 : SLOT_CHAL_HMAC2; QByteArray paddedChal = chal; - /* Ensure that YubiKey::init() succeeded */ + // ensure that YubiKey::init() succeeded if (m_yk == NULL) { + m_mutex.unlock(); return ERROR; } @@ -171,13 +178,12 @@ YubiKey::ChallengeResult YubiKey::challenge(int slot, bool mayBlock, const QByte r = reinterpret_cast(resp.data()); #ifdef QT_DEBUG - qDebug().nospace() << __func__ << "(" << slot << ") c = " - << printByteArray(paddedChal); + qDebug().nospace() << __func__ << "(" << slot << ") c = " << printByteArray(paddedChal); #endif - int ret = yk_challenge_response(m_yk, yk_cmd, mayBlock, - paddedChal.size(), c, - resp.size(), r); + int ret = yk_challenge_response(m_yk, yk_cmd, mayBlock, paddedChal.size(), c, resp.size(), r); + + m_mutex.unlock(); if (!ret) { if (yk_errno == YK_EWOULDBLOCK) { @@ -206,8 +212,7 @@ YubiKey::ChallengeResult YubiKey::challenge(int slot, bool mayBlock, const QByte resp.resize(20); #ifdef QT_DEBUG - qDebug().nospace() << __func__ << "(" << slot << ") r = " - << printByteArray(resp) << ", ret = " << ret; + qDebug().nospace() << __func__ << "(" << slot << ") r = " << printByteArray(resp) << ", ret = " << ret; #endif return SUCCESS; diff --git a/src/keys/drivers/YubiKey.h b/src/keys/drivers/YubiKey.h index acf4feb7..6c3504ed 100644 --- a/src/keys/drivers/YubiKey.h +++ b/src/keys/drivers/YubiKey.h @@ -19,6 +19,7 @@ #define KEEPASSX_YUBIKEY_H #include +#include /** * Singleton class to manage the interface to the hardware @@ -28,7 +29,7 @@ class YubiKey : public QObject Q_OBJECT public: - enum ChallengeResult { ERROR = -1, SUCCESS = 0, WOULDBLOCK }; + enum ChallengeResult { ERROR = -1, SUCCESS = 0, WOULDBLOCK, ALREADY_RUNNING }; /** * @brief YubiKey::instance - get instance of singleton @@ -64,7 +65,7 @@ public: */ ChallengeResult challenge(int slot, bool mayBlock, const QByteArray& chal, - QByteArray& resp) const; + QByteArray& resp); /** * @brief YubiKey::getSerial - serial number of YubiKey @@ -92,6 +93,11 @@ Q_SIGNALS: */ void notFound(); + /** + * Emitted when detection is already running. + */ + void alreadyRunning(); + private: explicit YubiKey(); static YubiKey* m_instance; @@ -100,6 +106,8 @@ private: void* m_yk_void; void* m_ykds_void; + QMutex m_mutex; + Q_DISABLE_COPY(YubiKey) }; From 44ac7d152b4024c2355cb83c09ce3bf41491f0bf Mon Sep 17 00:00:00 2001 From: Janek Bevendorff Date: Fri, 24 Feb 2017 01:06:09 +0100 Subject: [PATCH 33/93] Use better variable names --- src/keys/drivers/YubiKey.cpp | 24 ++++++++++++------------ src/keys/drivers/YubiKey.h | 8 +++----- 2 files changed, 15 insertions(+), 17 deletions(-) diff --git a/src/keys/drivers/YubiKey.cpp b/src/keys/drivers/YubiKey.cpp index 1d4445cd..e6894939 100644 --- a/src/keys/drivers/YubiKey.cpp +++ b/src/keys/drivers/YubiKey.cpp @@ -111,7 +111,7 @@ void YubiKey::detect() emit alreadyRunning(); return; } else if (result != YubiKey::ERROR) { - emit detected(i, result == YubiKey::WOULDBLOCK ? true : false); + emit detected(i, result == YubiKey::WOULDBLOCK); return; } } @@ -143,14 +143,14 @@ static inline QString printByteArray(const QByteArray& a) } #endif -YubiKey::ChallengeResult YubiKey::challenge(int slot, bool mayBlock, const QByteArray& chal, QByteArray& resp) +YubiKey::ChallengeResult YubiKey::challenge(int slot, bool mayBlock, const QByteArray& challenge, QByteArray& response) { if (!m_mutex.tryLock()) { return ALREADY_RUNNING; } int yk_cmd = (slot == 1) ? SLOT_CHAL_HMAC1 : SLOT_CHAL_HMAC2; - QByteArray paddedChal = chal; + QByteArray paddedChallenge = challenge; // ensure that YubiKey::init() succeeded if (m_yk == NULL) { @@ -159,7 +159,7 @@ YubiKey::ChallengeResult YubiKey::challenge(int slot, bool mayBlock, const QByte } // yk_challenge_response() insists on 64 byte response buffer */ - resp.resize(64); + response.resize(64); /* The challenge sent to the yubikey should always be 64 bytes for * compatibility with all configurations. Follow PKCS7 padding. @@ -167,21 +167,21 @@ YubiKey::ChallengeResult YubiKey::challenge(int slot, bool mayBlock, const QByte * There is some question whether or not 64 byte fixed length * configurations even work, some docs say avoid it. */ - const int padLen = 64 - paddedChal.size(); + const int padLen = 64 - paddedChallenge.size(); if (padLen > 0) { - paddedChal.append(QByteArray(padLen, padLen)); + paddedChallenge.append(QByteArray(padLen, padLen)); } const unsigned char *c; unsigned char *r; - c = reinterpret_cast(paddedChal.constData()); - r = reinterpret_cast(resp.data()); + c = reinterpret_cast(paddedChallenge.constData()); + r = reinterpret_cast(response.data()); #ifdef QT_DEBUG - qDebug().nospace() << __func__ << "(" << slot << ") c = " << printByteArray(paddedChal); + qDebug().nospace() << __func__ << "(" << slot << ") c = " << printByteArray(paddedChallenge); #endif - int ret = yk_challenge_response(m_yk, yk_cmd, mayBlock, paddedChal.size(), c, resp.size(), r); + int ret = yk_challenge_response(m_yk, yk_cmd, mayBlock, paddedChallenge.size(), c, response.size(), r); m_mutex.unlock(); @@ -209,10 +209,10 @@ YubiKey::ChallengeResult YubiKey::challenge(int slot, bool mayBlock, const QByte } // actual HMAC-SHA1 response is only 20 bytes - resp.resize(20); + response.resize(20); #ifdef QT_DEBUG - qDebug().nospace() << __func__ << "(" << slot << ") r = " << printByteArray(resp) << ", ret = " << ret; + qDebug().nospace() << __func__ << "(" << slot << ") r = " << printByteArray(response) << ", ret = " << ret; #endif return SUCCESS; diff --git a/src/keys/drivers/YubiKey.h b/src/keys/drivers/YubiKey.h index 6c3504ed..2382b69c 100644 --- a/src/keys/drivers/YubiKey.h +++ b/src/keys/drivers/YubiKey.h @@ -59,13 +59,11 @@ public: * * @param slot YubiKey configuration slot * @param mayBlock operation is allowed to block - * @param chal challenge input to YubiKey - * @param resp response output from YubiKey + * @param challenge challenge input to YubiKey + * @param response response output from YubiKey * @return true on success */ - ChallengeResult challenge(int slot, bool mayBlock, - const QByteArray& chal, - QByteArray& resp); + ChallengeResult challenge(int slot, bool mayBlock, const QByteArray& challenge, QByteArray& response); /** * @brief YubiKey::getSerial - serial number of YubiKey From d6c48a5cf1144e6d67a76fdf95708d2443451a36 Mon Sep 17 00:00:00 2001 From: Janek Bevendorff Date: Fri, 24 Feb 2017 03:25:08 +0100 Subject: [PATCH 34/93] Show message when user needs to touch their YubiKey (still buggy when using multiple databases) --- src/gui/Application.cpp | 5 +++++ src/gui/Application.h | 1 + src/gui/ChangeMasterKeyWidget.cpp | 15 +++++++++++++ src/gui/ChangeMasterKeyWidget.h | 2 ++ src/gui/DatabaseOpenWidget.cpp | 14 ++++++++++++ src/gui/DatabaseOpenWidget.h | 2 ++ src/gui/MainWindow.h | 9 +++++--- src/keys/YkChallengeResponseKey.cpp | 33 +++++++++++++++++++---------- src/keys/YkChallengeResponseKey.h | 4 ++-- src/keys/drivers/YubiKey.cpp | 1 + src/keys/drivers/YubiKey.h | 5 +++++ 11 files changed, 75 insertions(+), 16 deletions(-) diff --git a/src/gui/Application.cpp b/src/gui/Application.cpp index 26d9d228..4c84fda8 100644 --- a/src/gui/Application.cpp +++ b/src/gui/Application.cpp @@ -87,6 +87,11 @@ Application::Application(int& argc, char** argv) #endif } +QWidget* Application::mainWindow() const +{ + return m_mainWindow; +} + void Application::setMainWindow(QWidget* mainWindow) { m_mainWindow = mainWindow; diff --git a/src/gui/Application.h b/src/gui/Application.h index 9bfe4d54..c6f4aae6 100644 --- a/src/gui/Application.h +++ b/src/gui/Application.h @@ -29,6 +29,7 @@ class Application : public QApplication public: Application(int& argc, char** argv); + QWidget* mainWindow() const; void setMainWindow(QWidget* mainWindow); bool event(QEvent* event) override; diff --git a/src/gui/ChangeMasterKeyWidget.cpp b/src/gui/ChangeMasterKeyWidget.cpp index 453498a1..b223d392 100644 --- a/src/gui/ChangeMasterKeyWidget.cpp +++ b/src/gui/ChangeMasterKeyWidget.cpp @@ -25,6 +25,7 @@ #include "gui/FileDialog.h" #include "gui/MessageBox.h" #include "crypto/Random.h" +#include "MainWindow.h" #include "config-keepassx.h" @@ -176,6 +177,8 @@ void ChangeMasterKeyWidget::generateKey() bool blocking = i & true; int slot = i >> 1; auto key = QSharedPointer(new YkChallengeResponseKey(slot, blocking)); + connect(key.data(), SIGNAL(userInteractionRequired()), SLOT(showYubiKeyPopup())); + connect(key.data(), SIGNAL(userConfirmed()), SLOT(hideYubiKeyPopup())); m_key.addChallengeResponseKey(key); } #endif @@ -238,3 +241,15 @@ void ChangeMasterKeyWidget::setCancelEnabled(bool enabled) { m_ui->buttonBox->button(QDialogButtonBox::Cancel)->setEnabled(enabled); } + +void ChangeMasterKeyWidget::showYubiKeyPopup() +{ + KEEPASSXC_MAIN_WINDOW->displayGlobalMessage(tr("Please touch the button on your YubiKey!"), MessageWidget::Information); + KEEPASSXC_MAIN_WINDOW->setEnabled(false); +} + +void ChangeMasterKeyWidget::hideYubiKeyPopup() +{ + KEEPASSXC_MAIN_WINDOW->hideGlobalMessage(); + KEEPASSXC_MAIN_WINDOW->setEnabled(true); +} diff --git a/src/gui/ChangeMasterKeyWidget.h b/src/gui/ChangeMasterKeyWidget.h index b3e09727..fdc7f952 100644 --- a/src/gui/ChangeMasterKeyWidget.h +++ b/src/gui/ChangeMasterKeyWidget.h @@ -55,6 +55,8 @@ private slots: void noYubikeyFound(); void challengeResponseGroupToggled(bool checked); void pollYubikey(); + void showYubiKeyPopup(); + void hideYubiKeyPopup(); private: const QScopedPointer m_ui; diff --git a/src/gui/DatabaseOpenWidget.cpp b/src/gui/DatabaseOpenWidget.cpp index 2eebcae3..03a560c2 100644 --- a/src/gui/DatabaseOpenWidget.cpp +++ b/src/gui/DatabaseOpenWidget.cpp @@ -214,6 +214,8 @@ CompositeKey DatabaseOpenWidget::databaseKey() bool blocking = i & true; int slot = i >> 1; auto key = QSharedPointer(new YkChallengeResponseKey(slot, blocking)); + connect(key.data(), SIGNAL(userInteractionRequired()), SLOT(showYubiKeyPopup())); + connect(key.data(), SIGNAL(userConfirmed()), SLOT(hideYubiKeyPopup())); masterKey.addChallengeResponseKey(key); } #endif @@ -264,6 +266,18 @@ void DatabaseOpenWidget::pollYubikey() QtConcurrent::run(YubiKey::instance(), &YubiKey::detect); } +void DatabaseOpenWidget::showYubiKeyPopup() +{ + m_ui->messageWidget->showMessage(tr("Please touch the button on your YubiKey!"), MessageWidget::Information); + KEEPASSXC_MAIN_WINDOW->setEnabled(false); +} + +void DatabaseOpenWidget::hideYubiKeyPopup() +{ + m_ui->messageWidget->hideMessage(); + KEEPASSXC_MAIN_WINDOW->setEnabled(true); +} + void DatabaseOpenWidget::yubikeyDetected(int slot, bool blocking) { YkChallengeResponseKey yk(slot, blocking); diff --git a/src/gui/DatabaseOpenWidget.h b/src/gui/DatabaseOpenWidget.h index caba70a6..eee705a7 100644 --- a/src/gui/DatabaseOpenWidget.h +++ b/src/gui/DatabaseOpenWidget.h @@ -53,6 +53,8 @@ protected: protected slots: virtual void openDatabase(); + void showYubiKeyPopup(); + void hideYubiKeyPopup(); void reject(); private slots: diff --git a/src/gui/MainWindow.h b/src/gui/MainWindow.h index 694b38e7..c3262a3c 100644 --- a/src/gui/MainWindow.h +++ b/src/gui/MainWindow.h @@ -24,6 +24,7 @@ #include "core/SignalMultiplexer.h" #include "gui/DatabaseWidget.h" +#include "gui/Application.h" namespace Ui { class MainWindow; @@ -43,6 +44,9 @@ public Q_SLOTS: void openDatabase(const QString& fileName, const QString& pw = QString(), const QString& keyFile = QString()); void appExit(); + void displayGlobalMessage(const QString& text, MessageWidget::MessageType type); + void displayTabMessage(const QString& text, MessageWidget::MessageType type); + void hideGlobalMessage(); protected: void closeEvent(QCloseEvent* event) override; @@ -75,9 +79,6 @@ private Q_SLOTS: void toggleWindow(); void lockDatabasesAfterInactivity(); void repairDatabase(); - void displayGlobalMessage(const QString& text, MessageWidget::MessageType type); - void displayTabMessage(const QString& text, MessageWidget::MessageType type); - void hideGlobalMessage(); void hideTabMessage(); private: @@ -106,4 +107,6 @@ private: bool appExitCalled; }; +#define KEEPASSXC_MAIN_WINDOW qobject_cast(qobject_cast(qApp)->mainWindow()) + #endif // KEEPASSX_MAINWINDOW_H diff --git a/src/keys/YkChallengeResponseKey.cpp b/src/keys/YkChallengeResponseKey.cpp index a6e5cdbc..039d7a1e 100644 --- a/src/keys/YkChallengeResponseKey.cpp +++ b/src/keys/YkChallengeResponseKey.cpp @@ -14,16 +14,19 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ - -#include -#include +#include "keys/YkChallengeResponseKey.h" +#include "keys/drivers/YubiKey.h" #include "core/Tools.h" #include "crypto/CryptoHash.h" #include "crypto/Random.h" -#include "keys/YkChallengeResponseKey.h" -#include "keys/drivers/YubiKey.h" +#include +#include +#include +#include +#include +#include YkChallengeResponseKey::YkChallengeResponseKey(int slot, bool blocking) : m_slot(slot), @@ -39,12 +42,12 @@ QByteArray YkChallengeResponseKey::rawKey() const /** * Assumes yubikey()->init() was called */ -bool YkChallengeResponseKey::challenge(const QByteArray& chal) +bool YkChallengeResponseKey::challenge(const QByteArray& challenge) { - return challenge(chal, 1); + return this->challenge(challenge, 1); } -#include -bool YkChallengeResponseKey::challenge(const QByteArray& chal, unsigned retries) + +bool YkChallengeResponseKey::challenge(const QByteArray& challenge, unsigned retries) { Q_ASSERT(retries > 0); @@ -55,13 +58,21 @@ bool YkChallengeResponseKey::challenge(const QByteArray& chal, unsigned retries) emit userInteractionRequired(); } - auto result = YubiKey::instance()->challenge(m_slot, true, chal, m_key); + QFuture future = QtConcurrent::run([this, challenge]() { + return YubiKey::instance()->challenge(m_slot, true, challenge, m_key); + }); + + QEventLoop loop; + QFutureWatcher watcher; + watcher.setFuture(future); + connect(&watcher, SIGNAL(finished()), &loop, SLOT(quit())); + loop.exec(); if (m_blocking) { emit userConfirmed(); } - if (result != YubiKey::ERROR) { + if (future.result() != YubiKey::ERROR) { return true; } diff --git a/src/keys/YkChallengeResponseKey.h b/src/keys/YkChallengeResponseKey.h index 96e44220..8c566ca4 100644 --- a/src/keys/YkChallengeResponseKey.h +++ b/src/keys/YkChallengeResponseKey.h @@ -33,8 +33,8 @@ public: YkChallengeResponseKey(int slot = -1, bool blocking = false); QByteArray rawKey() const; - bool challenge(const QByteArray& chal); - bool challenge(const QByteArray& chal, unsigned retries); + bool challenge(const QByteArray& challenge); + bool challenge(const QByteArray& challenge, unsigned retries); QString getName() const; bool isBlocking() const; diff --git a/src/keys/drivers/YubiKey.cpp b/src/keys/drivers/YubiKey.cpp index e6894939..bab0d721 100644 --- a/src/keys/drivers/YubiKey.cpp +++ b/src/keys/drivers/YubiKey.cpp @@ -182,6 +182,7 @@ YubiKey::ChallengeResult YubiKey::challenge(int slot, bool mayBlock, const QByte #endif int ret = yk_challenge_response(m_yk, yk_cmd, mayBlock, paddedChallenge.size(), c, response.size(), r); + emit challenged(); m_mutex.unlock(); diff --git a/src/keys/drivers/YubiKey.h b/src/keys/drivers/YubiKey.h index 2382b69c..b938ff86 100644 --- a/src/keys/drivers/YubiKey.h +++ b/src/keys/drivers/YubiKey.h @@ -86,6 +86,11 @@ Q_SIGNALS: */ void detected(int slot, bool blocking); + /** + * Emitted when the YubiKey was challenged and has returned a response. + */ + void challenged(); + /** * Emitted when no Yubikey could be found. */ From b10cb1c83c1b8ac1b94becf0c8af94ca68af04f6 Mon Sep 17 00:00:00 2001 From: Janek Bevendorff Date: Fri, 24 Feb 2017 17:27:27 +0100 Subject: [PATCH 35/93] Show YubiKey message from MainWindow to ensure it's always shown when a challenge is generated --- src/gui/ChangeMasterKeyWidget.cpp | 14 -------------- src/gui/ChangeMasterKeyWidget.h | 2 -- src/gui/DatabaseOpenWidget.cpp | 14 -------------- src/gui/DatabaseOpenWidget.h | 2 -- src/gui/MainWindow.cpp | 11 +++++++++++ src/gui/MainWindow.h | 2 ++ src/keys/YkChallengeResponseKey.cpp | 4 ++++ 7 files changed, 17 insertions(+), 32 deletions(-) diff --git a/src/gui/ChangeMasterKeyWidget.cpp b/src/gui/ChangeMasterKeyWidget.cpp index b223d392..bb963d3c 100644 --- a/src/gui/ChangeMasterKeyWidget.cpp +++ b/src/gui/ChangeMasterKeyWidget.cpp @@ -177,8 +177,6 @@ void ChangeMasterKeyWidget::generateKey() bool blocking = i & true; int slot = i >> 1; auto key = QSharedPointer(new YkChallengeResponseKey(slot, blocking)); - connect(key.data(), SIGNAL(userInteractionRequired()), SLOT(showYubiKeyPopup())); - connect(key.data(), SIGNAL(userConfirmed()), SLOT(hideYubiKeyPopup())); m_key.addChallengeResponseKey(key); } #endif @@ -241,15 +239,3 @@ void ChangeMasterKeyWidget::setCancelEnabled(bool enabled) { m_ui->buttonBox->button(QDialogButtonBox::Cancel)->setEnabled(enabled); } - -void ChangeMasterKeyWidget::showYubiKeyPopup() -{ - KEEPASSXC_MAIN_WINDOW->displayGlobalMessage(tr("Please touch the button on your YubiKey!"), MessageWidget::Information); - KEEPASSXC_MAIN_WINDOW->setEnabled(false); -} - -void ChangeMasterKeyWidget::hideYubiKeyPopup() -{ - KEEPASSXC_MAIN_WINDOW->hideGlobalMessage(); - KEEPASSXC_MAIN_WINDOW->setEnabled(true); -} diff --git a/src/gui/ChangeMasterKeyWidget.h b/src/gui/ChangeMasterKeyWidget.h index fdc7f952..b3e09727 100644 --- a/src/gui/ChangeMasterKeyWidget.h +++ b/src/gui/ChangeMasterKeyWidget.h @@ -55,8 +55,6 @@ private slots: void noYubikeyFound(); void challengeResponseGroupToggled(bool checked); void pollYubikey(); - void showYubiKeyPopup(); - void hideYubiKeyPopup(); private: const QScopedPointer m_ui; diff --git a/src/gui/DatabaseOpenWidget.cpp b/src/gui/DatabaseOpenWidget.cpp index 03a560c2..2eebcae3 100644 --- a/src/gui/DatabaseOpenWidget.cpp +++ b/src/gui/DatabaseOpenWidget.cpp @@ -214,8 +214,6 @@ CompositeKey DatabaseOpenWidget::databaseKey() bool blocking = i & true; int slot = i >> 1; auto key = QSharedPointer(new YkChallengeResponseKey(slot, blocking)); - connect(key.data(), SIGNAL(userInteractionRequired()), SLOT(showYubiKeyPopup())); - connect(key.data(), SIGNAL(userConfirmed()), SLOT(hideYubiKeyPopup())); masterKey.addChallengeResponseKey(key); } #endif @@ -266,18 +264,6 @@ void DatabaseOpenWidget::pollYubikey() QtConcurrent::run(YubiKey::instance(), &YubiKey::detect); } -void DatabaseOpenWidget::showYubiKeyPopup() -{ - m_ui->messageWidget->showMessage(tr("Please touch the button on your YubiKey!"), MessageWidget::Information); - KEEPASSXC_MAIN_WINDOW->setEnabled(false); -} - -void DatabaseOpenWidget::hideYubiKeyPopup() -{ - m_ui->messageWidget->hideMessage(); - KEEPASSXC_MAIN_WINDOW->setEnabled(true); -} - void DatabaseOpenWidget::yubikeyDetected(int slot, bool blocking) { YkChallengeResponseKey yk(slot, blocking); diff --git a/src/gui/DatabaseOpenWidget.h b/src/gui/DatabaseOpenWidget.h index eee705a7..caba70a6 100644 --- a/src/gui/DatabaseOpenWidget.h +++ b/src/gui/DatabaseOpenWidget.h @@ -53,8 +53,6 @@ protected: protected slots: virtual void openDatabase(); - void showYubiKeyPopup(); - void hideYubiKeyPopup(); void reject(); private slots: diff --git a/src/gui/MainWindow.cpp b/src/gui/MainWindow.cpp index e9a05e5d..9a9ef3dc 100644 --- a/src/gui/MainWindow.cpp +++ b/src/gui/MainWindow.cpp @@ -871,3 +871,14 @@ void MainWindow::hideTabMessage() } } +void MainWindow::showYubiKeyPopup() +{ + displayGlobalMessage(tr("Please touch the button on your YubiKey!"), MessageWidget::Information); + setEnabled(false); +} + +void MainWindow::hideYubiKeyPopup() +{ + hideGlobalMessage(); + setEnabled(true); +} diff --git a/src/gui/MainWindow.h b/src/gui/MainWindow.h index c3262a3c..eb677fbb 100644 --- a/src/gui/MainWindow.h +++ b/src/gui/MainWindow.h @@ -47,6 +47,8 @@ public Q_SLOTS: void displayGlobalMessage(const QString& text, MessageWidget::MessageType type); void displayTabMessage(const QString& text, MessageWidget::MessageType type); void hideGlobalMessage(); + void showYubiKeyPopup(); + void hideYubiKeyPopup(); protected: void closeEvent(QCloseEvent* event) override; diff --git a/src/keys/YkChallengeResponseKey.cpp b/src/keys/YkChallengeResponseKey.cpp index 039d7a1e..31ad1d86 100644 --- a/src/keys/YkChallengeResponseKey.cpp +++ b/src/keys/YkChallengeResponseKey.cpp @@ -20,6 +20,7 @@ #include "core/Tools.h" #include "crypto/CryptoHash.h" #include "crypto/Random.h" +#include "gui/MainWindow.h" #include #include @@ -32,6 +33,9 @@ YkChallengeResponseKey::YkChallengeResponseKey(int slot, bool blocking) : m_slot(slot), m_blocking(blocking) { + + connect(this, SIGNAL(userInteractionRequired()), KEEPASSXC_MAIN_WINDOW, SLOT(showYubiKeyPopup())); + connect(this, SIGNAL(userConfirmed()), KEEPASSXC_MAIN_WINDOW, SLOT(hideYubiKeyPopup())); } QByteArray YkChallengeResponseKey::rawKey() const From 18844d096a4e0348120040368d53542fad5cca6e Mon Sep 17 00:00:00 2001 From: Janek Bevendorff Date: Fri, 24 Feb 2017 17:50:19 +0100 Subject: [PATCH 36/93] Make other YubiKey driver methods thread-safe --- src/keys/YkChallengeResponseKey.cpp | 1 - src/keys/drivers/YubiKey.cpp | 21 ++++++++++++++++++--- src/keys/drivers/YubiKey.h | 2 +- 3 files changed, 19 insertions(+), 5 deletions(-) diff --git a/src/keys/YkChallengeResponseKey.cpp b/src/keys/YkChallengeResponseKey.cpp index 31ad1d86..60db4282 100644 --- a/src/keys/YkChallengeResponseKey.cpp +++ b/src/keys/YkChallengeResponseKey.cpp @@ -33,7 +33,6 @@ YkChallengeResponseKey::YkChallengeResponseKey(int slot, bool blocking) : m_slot(slot), m_blocking(blocking) { - connect(this, SIGNAL(userInteractionRequired()), KEEPASSXC_MAIN_WINDOW, SLOT(showYubiKeyPopup())); connect(this, SIGNAL(userConfirmed()), KEEPASSXC_MAIN_WINDOW, SLOT(hideYubiKeyPopup())); } diff --git a/src/keys/drivers/YubiKey.cpp b/src/keys/drivers/YubiKey.cpp index bab0d721..a490f069 100644 --- a/src/keys/drivers/YubiKey.cpp +++ b/src/keys/drivers/YubiKey.cpp @@ -34,7 +34,7 @@ #define m_yk (static_cast(m_yk_void)) #define m_ykds (static_cast(m_ykds_void)) -YubiKey::YubiKey() : m_yk_void(NULL), m_ykds_void(NULL) +YubiKey::YubiKey() : m_yk_void(NULL), m_ykds_void(NULL), m_mutex(QMutex::Recursive) { } @@ -51,11 +51,14 @@ YubiKey* YubiKey::instance() bool YubiKey::init() { + m_mutex.lock(); + // previously initialized if (m_yk != NULL && m_ykds != NULL) { if (yk_get_status(m_yk, m_ykds)) { // Still connected + m_mutex.unlock(); return true; } else { // Initialized but not connected anymore, re-init @@ -64,12 +67,14 @@ bool YubiKey::init() } if (!yk_init()) { + m_mutex.unlock(); return false; } // TODO: handle multiple attached hardware devices m_yk_void = static_cast(yk_open_first_key()); if (m_yk == NULL) { + m_mutex.unlock(); return false; } @@ -77,14 +82,18 @@ bool YubiKey::init() if (m_ykds == NULL) { yk_close_key(m_yk); m_yk_void = NULL; + m_mutex.unlock(); return false; } + m_mutex.unlock(); return true; } bool YubiKey::deinit() { + m_mutex.lock(); + if (m_yk) { yk_close_key(m_yk); m_yk_void = NULL; @@ -95,6 +104,8 @@ bool YubiKey::deinit() m_ykds_void = NULL; } + m_mutex.unlock(); + return true; } @@ -119,9 +130,13 @@ void YubiKey::detect() emit notFound(); } -bool YubiKey::getSerial(unsigned int& serial) const +bool YubiKey::getSerial(unsigned int& serial) { - if (!yk_get_serial(m_yk, 1, 0, &serial)) { + m_mutex.lock(); + int result = yk_get_serial(m_yk, 1, 0, &serial); + m_mutex.unlock(); + + if (!result) { return false; } diff --git a/src/keys/drivers/YubiKey.h b/src/keys/drivers/YubiKey.h index b938ff86..47341f9a 100644 --- a/src/keys/drivers/YubiKey.h +++ b/src/keys/drivers/YubiKey.h @@ -70,7 +70,7 @@ public: * @param serial serial number * @return true on success */ - bool getSerial(unsigned int& serial) const; + bool getSerial(unsigned int& serial); /** * @brief YubiKey::detect - probe for attached YubiKeys From 2721317fc3187f66f2007f11ab8439deaeffeb64 Mon Sep 17 00:00:00 2001 From: Janek Bevendorff Date: Fri, 24 Feb 2017 18:43:15 +0100 Subject: [PATCH 37/93] Block and unblock autoreload in timed mutex style to prevent a double challenge when saving the database and the YubiKey requires user interaction --- src/gui/DatabaseTabWidget.cpp | 28 ++++++++++++------------- src/gui/DatabaseWidget.cpp | 39 ++++++++++++++++++++--------------- src/gui/DatabaseWidget.h | 13 ++++++------ 3 files changed, 42 insertions(+), 38 deletions(-) diff --git a/src/gui/DatabaseTabWidget.cpp b/src/gui/DatabaseTabWidget.cpp index 734dc3f2..0503531a 100644 --- a/src/gui/DatabaseTabWidget.cpp +++ b/src/gui/DatabaseTabWidget.cpp @@ -322,17 +322,18 @@ bool DatabaseTabWidget::closeAllDatabases() bool DatabaseTabWidget::saveDatabase(Database* db) { DatabaseManagerStruct& dbStruct = m_dbList[db]; - // temporarily disable autoreload - dbStruct.dbWidget->ignoreNextAutoreload(); if (dbStruct.saveToFilename) { QSaveFile saveFile(dbStruct.canonicalFilePath); if (saveFile.open(QIODevice::WriteOnly)) { // write the database to the file + dbStruct.dbWidget->blockAutoReload(true); m_writer.writeDatabase(&saveFile, db); + dbStruct.dbWidget->blockAutoReload(false); + if (m_writer.hasError()) { - Q_EMIT messageTab(tr("Writing the database failed.").append("\n") - .append(m_writer.errorString()), MessageWidget::Error); + emit messageTab(tr("Writing the database failed.").append("\n") + .append(m_writer.errorString()), MessageWidget::Error); return false; } @@ -341,22 +342,19 @@ bool DatabaseTabWidget::saveDatabase(Database* db) dbStruct.modified = false; dbStruct.dbWidget->databaseSaved(); updateTabName(db); - Q_EMIT messageDismissTab(); + emit messageDismissTab(); return true; - } - else { - Q_EMIT messageTab(tr("Writing the database failed.").append("\n") - .append(saveFile.errorString()), MessageWidget::Error); + } else { + emit messageTab(tr("Writing the database failed.").append("\n") + .append(saveFile.errorString()), MessageWidget::Error); return false; } - } - else { - Q_EMIT messageTab(tr("Writing the database failed.").append("\n") - .append(saveFile.errorString()), MessageWidget::Error); + } else { + emit messageTab(tr("Writing the database failed.").append("\n") + .append(saveFile.errorString()), MessageWidget::Error); return false; } - } - else { + } else { return saveDatabaseAs(db); } } diff --git a/src/gui/DatabaseWidget.cpp b/src/gui/DatabaseWidget.cpp index a67646ef..9af79f77 100644 --- a/src/gui/DatabaseWidget.cpp +++ b/src/gui/DatabaseWidget.cpp @@ -168,14 +168,14 @@ DatabaseWidget::DatabaseWidget(Database* db, QWidget* parent) connect(m_unlockDatabaseDialog, SIGNAL(unlockDone(bool)), SLOT(unlockDatabase(bool))); connect(&m_fileWatcher, SIGNAL(fileChanged(QString)), this, SLOT(onWatchedFileChanged())); connect(&m_fileWatchTimer, SIGNAL(timeout()), this, SLOT(reloadDatabaseFile())); - connect(&m_ignoreWatchTimer, SIGNAL(timeout()), this, SLOT(onWatchedFileChanged())); + connect(&m_fileWatchUnblockTimer, SIGNAL(timeout()), this, SLOT(unblockAutoReload())); connect(this, SIGNAL(currentChanged(int)), this, SLOT(emitCurrentModeChanged())); m_databaseModified = false; m_fileWatchTimer.setSingleShot(true); - m_ignoreWatchTimer.setSingleShot(true); - m_ignoreNextAutoreload = false; + m_fileWatchUnblockTimer.setSingleShot(true); + m_ignoreAutoReload = false; m_searchCaseSensitive = false; @@ -1001,7 +1001,7 @@ void DatabaseWidget::lock() void DatabaseWidget::updateFilename(const QString& fileName) { - if (! m_filename.isEmpty()) { + if (!m_filename.isEmpty()) { m_fileWatcher.removePath(m_filename); } @@ -1009,26 +1009,31 @@ void DatabaseWidget::updateFilename(const QString& fileName) m_filename = fileName; } -void DatabaseWidget::ignoreNextAutoreload() +void DatabaseWidget::blockAutoReload(bool block) { - m_ignoreNextAutoreload = true; - m_ignoreWatchTimer.start(100); + if (block) { + m_ignoreAutoReload = true; + m_fileWatchTimer.stop(); + } else { + m_fileWatchUnblockTimer.start(500); + } +} + +void DatabaseWidget::unblockAutoReload() +{ + m_ignoreAutoReload = false; + updateFilename(m_filename); } void DatabaseWidget::onWatchedFileChanged() { - if (m_ignoreNextAutoreload) { - // Reset the watch - m_ignoreNextAutoreload = false; - m_ignoreWatchTimer.stop(); - m_fileWatcher.addPath(m_filename); + if (m_ignoreAutoReload) { + return; } - else { - if (m_fileWatchTimer.isActive()) - return; + if (m_fileWatchTimer.isActive()) + return; - m_fileWatchTimer.start(500); - } + m_fileWatchTimer.start(500); } void DatabaseWidget::reloadDatabaseFile() diff --git a/src/gui/DatabaseWidget.h b/src/gui/DatabaseWidget.h index 78b6131d..6e946250 100644 --- a/src/gui/DatabaseWidget.h +++ b/src/gui/DatabaseWidget.h @@ -98,9 +98,9 @@ public: EntryView* entryView(); void showUnlockDialog(); void closeUnlockDialog(); - void ignoreNextAutoreload(); + void blockAutoReload(bool block = true); -Q_SIGNALS: +signals: void closeRequest(); void currentModeChanged(DatabaseWidget::Mode mode); void groupChanged(); @@ -118,7 +118,7 @@ Q_SIGNALS: void entryColumnSizesChanged(); void updateSearch(QString text); -public Q_SLOTS: +public slots: void createEntry(); void cloneEntry(); void deleteEntries(); @@ -154,7 +154,7 @@ public Q_SLOTS: void showMessage(const QString& text, MessageWidget::MessageType type); void hideMessage(); -private Q_SLOTS: +private slots: void entryActivationSignalReceived(Entry* entry, EntryModel::ModelColumn column); void switchBackToEntryEdit(); void switchToHistoryView(Entry* entry); @@ -172,6 +172,7 @@ private Q_SLOTS: void onWatchedFileChanged(); void reloadDatabaseFile(); void restoreGroupEntryFocus(Uuid groupUuid, Uuid EntryUuid); + void unblockAutoReload(); private: void setClipboardTextAndMinimize(const QString& text); @@ -209,8 +210,8 @@ private: // Autoreload QFileSystemWatcher m_fileWatcher; QTimer m_fileWatchTimer; - bool m_ignoreNextAutoreload; - QTimer m_ignoreWatchTimer; + QTimer m_fileWatchUnblockTimer; + bool m_ignoreAutoReload; bool m_databaseModified; }; From 46942413db9a9b41e86180e19b9f13fd01c5be3e Mon Sep 17 00:00:00 2001 From: Janek Bevendorff Date: Fri, 24 Feb 2017 19:47:03 +0100 Subject: [PATCH 38/93] Fix unit test crash --- src/gui/MainWindow.h | 3 ++- src/keys/YkChallengeResponseKey.cpp | 6 ++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/gui/MainWindow.h b/src/gui/MainWindow.h index eb677fbb..4435c13b 100644 --- a/src/gui/MainWindow.h +++ b/src/gui/MainWindow.h @@ -109,6 +109,7 @@ private: bool appExitCalled; }; -#define KEEPASSXC_MAIN_WINDOW qobject_cast(qobject_cast(qApp)->mainWindow()) +#define KEEPASSXC_MAIN_WINDOW (qobject_cast(qApp) ? \ + qobject_cast(qobject_cast(qApp)->mainWindow()) : nullptr) #endif // KEEPASSX_MAINWINDOW_H diff --git a/src/keys/YkChallengeResponseKey.cpp b/src/keys/YkChallengeResponseKey.cpp index 60db4282..dcd58335 100644 --- a/src/keys/YkChallengeResponseKey.cpp +++ b/src/keys/YkChallengeResponseKey.cpp @@ -33,8 +33,10 @@ YkChallengeResponseKey::YkChallengeResponseKey(int slot, bool blocking) : m_slot(slot), m_blocking(blocking) { - connect(this, SIGNAL(userInteractionRequired()), KEEPASSXC_MAIN_WINDOW, SLOT(showYubiKeyPopup())); - connect(this, SIGNAL(userConfirmed()), KEEPASSXC_MAIN_WINDOW, SLOT(hideYubiKeyPopup())); + if (KEEPASSXC_MAIN_WINDOW) { + connect(this, SIGNAL(userInteractionRequired()), KEEPASSXC_MAIN_WINDOW, SLOT(showYubiKeyPopup())); + connect(this, SIGNAL(userConfirmed()), KEEPASSXC_MAIN_WINDOW, SLOT(hideYubiKeyPopup())); + } } QByteArray YkChallengeResponseKey::rawKey() const From 9a94c6d85e9d3c3dc9b3ef0acb7715c2b6a6129d Mon Sep 17 00:00:00 2001 From: Janek Bevendorff Date: Fri, 24 Feb 2017 20:44:06 +0100 Subject: [PATCH 39/93] Remove debug output to reduce console spam when running in debug mode --- src/keys/drivers/YubiKey.cpp | 8 -------- tests/TestYkChallengeResponseKey.cpp | 4 ++-- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/src/keys/drivers/YubiKey.cpp b/src/keys/drivers/YubiKey.cpp index a490f069..ffb48fc7 100644 --- a/src/keys/drivers/YubiKey.cpp +++ b/src/keys/drivers/YubiKey.cpp @@ -192,10 +192,6 @@ YubiKey::ChallengeResult YubiKey::challenge(int slot, bool mayBlock, const QByte c = reinterpret_cast(paddedChallenge.constData()); r = reinterpret_cast(response.data()); -#ifdef QT_DEBUG - qDebug().nospace() << __func__ << "(" << slot << ") c = " << printByteArray(paddedChallenge); -#endif - int ret = yk_challenge_response(m_yk, yk_cmd, mayBlock, paddedChallenge.size(), c, response.size(), r); emit challenged(); @@ -227,9 +223,5 @@ YubiKey::ChallengeResult YubiKey::challenge(int slot, bool mayBlock, const QByte // actual HMAC-SHA1 response is only 20 bytes response.resize(20); -#ifdef QT_DEBUG - qDebug().nospace() << __func__ << "(" << slot << ") r = " << printByteArray(response) << ", ret = " << ret; -#endif - return SUCCESS; } diff --git a/tests/TestYkChallengeResponseKey.cpp b/tests/TestYkChallengeResponseKey.cpp index bd58ac01..046c0666 100644 --- a/tests/TestYkChallengeResponseKey.cpp +++ b/tests/TestYkChallengeResponseKey.cpp @@ -46,7 +46,7 @@ void TestYubiKeyChalResp::init() QSKIP("Unable to connect to YubiKey", SkipAll); } - /* Crypto subsystem needs to be initalized for YubiKey testing */ + // crypto subsystem needs to be initialized for YubiKey testing QVERIFY(Crypto::init()); } @@ -57,7 +57,7 @@ void TestYubiKeyChalResp::detectDevices() Qt::QueuedConnection); QtConcurrent::run(YubiKey::instance(), &YubiKey::detect); - /* Need to wait for the hardware (that's hopefully plugged in)... */ + // need to wait for the hardware (that's hopefully plugged in)... QTest::qWait(2000); QVERIFY2(m_detected > 0, "Is a YubiKey attached?"); } From a001553c5ecdd7540109c02ce7b0c4b7bddb1314 Mon Sep 17 00:00:00 2001 From: Janek Bevendorff Date: Fri, 24 Feb 2017 21:00:48 +0100 Subject: [PATCH 40/93] Fix warnings about Crypto already having been initialized --- tests/TestYkChallengeResponseKey.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/TestYkChallengeResponseKey.cpp b/tests/TestYkChallengeResponseKey.cpp index 046c0666..40eda3bf 100644 --- a/tests/TestYkChallengeResponseKey.cpp +++ b/tests/TestYkChallengeResponseKey.cpp @@ -30,6 +30,9 @@ void TestYubiKeyChalResp::initTestCase() { m_detected = 0; m_key = NULL; + + // crypto subsystem needs to be initialized for YubiKey testing + QVERIFY(Crypto::init()); } void TestYubiKeyChalResp::cleanupTestCase() @@ -45,9 +48,6 @@ void TestYubiKeyChalResp::init() if (!result) { QSKIP("Unable to connect to YubiKey", SkipAll); } - - // crypto subsystem needs to be initialized for YubiKey testing - QVERIFY(Crypto::init()); } void TestYubiKeyChalResp::detectDevices() From 44206cf088fb0b7d600a8a819e6d384a785e8741 Mon Sep 17 00:00:00 2001 From: Janek Bevendorff Date: Sat, 25 Feb 2017 17:04:00 +0100 Subject: [PATCH 41/93] Fix stub compilation --- src/keys/drivers/YubiKeyStub.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/keys/drivers/YubiKeyStub.cpp b/src/keys/drivers/YubiKeyStub.cpp index e93099bf..15eef27a 100644 --- a/src/keys/drivers/YubiKeyStub.cpp +++ b/src/keys/drivers/YubiKeyStub.cpp @@ -51,14 +51,14 @@ void YubiKey::detect() { } -bool YubiKey::getSerial(unsigned int& serial) const +bool YubiKey::getSerial(unsigned int& serial) { Q_UNUSED(serial); return false; } -YubiKey::ChallengeResult YubiKey::challenge(int slot, bool mayBlock, const QByteArray& chal, QByteArray& resp) const +YubiKey::ChallengeResult YubiKey::challenge(int slot, bool mayBlock, const QByteArray& chal, QByteArray& resp) { Q_UNUSED(slot); Q_UNUSED(mayBlock); From 48366d245cd0972fa0ae3c7726afaaa1c5fd0fce Mon Sep 17 00:00:00 2001 From: Janek Bevendorff Date: Sat, 25 Feb 2017 17:11:02 +0100 Subject: [PATCH 42/93] Add CMake feature description --- CMakeLists.txt | 6 +++--- src/CMakeLists.txt | 5 +++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ecce4f0a..0c2fa267 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -34,9 +34,9 @@ option(WITH_GUI_TESTS "Enable building of GUI tests" OFF) option(WITH_DEV_BUILD "Use only for development. Disables/warns about deprecated methods." OFF) option(WITH_COVERAGE "Use to build with coverage tests. (GCC ONLY)." OFF) -option(WITH_XC_AUTOTYPE "Include Autotype." OFF) -option(WITH_XC_HTTP "Include KeePassHTTP." OFF) -option(WITH_XC_YUBIKEY "Include Yubikey support." OFF) +option(WITH_XC_AUTOTYPE "Include Auto-Type." OFF) +option(WITH_XC_HTTP "Include KeePassHTTP support." OFF) +option(WITH_XC_YUBIKEY "Include YubiKey support." OFF) set(KEEPASSXC_VERSION "2.1.2") set(KEEPASSXC_VERSION_NUM "2.1.2") diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 380a6c2d..418f1e79 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -152,8 +152,9 @@ set(keepassx_FORMS gui/group/EditGroupWidgetMain.ui ) -add_feature_info(KeePassHTTP WITH_XC_HTTP "KeePassHTTP support for ChromeIPass and PassIFox") -add_feature_info(Autotype WITH_XC_AUTOTYPE "Auto-type passwords in Input fields") +add_feature_info(AutoType WITH_XC_AUTOTYPE "Automatic password typing") +add_feature_info(KeePassHTTP WITH_XC_HTTP "Browser integration compatible with ChromeIPass and PassIFox") +add_feature_info(YubiKey WITH_XC_YUBIKEY "YubiKey HMAC-SHA1 challenge-response") add_subdirectory(http) if(WITH_XC_HTTP) From 3715286eba54970802b85eb4f759b6e1992a7f7c Mon Sep 17 00:00:00 2001 From: Janek Bevendorff Date: Sat, 25 Feb 2017 22:09:55 +0100 Subject: [PATCH 43/93] Hide close button on YubiKey user interaction message --- src/gui/MainWindow.cpp | 8 +++++--- src/gui/MainWindow.h | 4 ++-- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/gui/MainWindow.cpp b/src/gui/MainWindow.cpp index 1258d9f6..ff63d7b3 100644 --- a/src/gui/MainWindow.cpp +++ b/src/gui/MainWindow.cpp @@ -862,13 +862,15 @@ bool MainWindow::isTrayIconEnabled() const #endif } -void MainWindow::displayGlobalMessage(const QString& text, MessageWidget::MessageType type) +void MainWindow::displayGlobalMessage(const QString& text, MessageWidget::MessageType type, bool showClosebutton) { + m_ui->globalMessageWidget->setCloseButtonVisible(showClosebutton); m_ui->globalMessageWidget->showMessage(text, type); } -void MainWindow::displayTabMessage(const QString& text, MessageWidget::MessageType type) +void MainWindow::displayTabMessage(const QString& text, MessageWidget::MessageType type, bool showClosebutton) { + m_ui->globalMessageWidget->setCloseButtonVisible(showClosebutton); m_ui->tabWidget->currentDatabaseWidget()->showMessage(text, type); } @@ -886,7 +888,7 @@ void MainWindow::hideTabMessage() void MainWindow::showYubiKeyPopup() { - displayGlobalMessage(tr("Please touch the button on your YubiKey!"), MessageWidget::Information); + displayGlobalMessage(tr("Please touch the button on your YubiKey!"), MessageWidget::Information, false); setEnabled(false); } diff --git a/src/gui/MainWindow.h b/src/gui/MainWindow.h index 4435c13b..131ccc22 100644 --- a/src/gui/MainWindow.h +++ b/src/gui/MainWindow.h @@ -44,8 +44,8 @@ public Q_SLOTS: void openDatabase(const QString& fileName, const QString& pw = QString(), const QString& keyFile = QString()); void appExit(); - void displayGlobalMessage(const QString& text, MessageWidget::MessageType type); - void displayTabMessage(const QString& text, MessageWidget::MessageType type); + void displayGlobalMessage(const QString& text, MessageWidget::MessageType type, bool showClosebutton = true); + void displayTabMessage(const QString& text, MessageWidget::MessageType type, bool showClosebutton = true); void hideGlobalMessage(); void showYubiKeyPopup(); void hideYubiKeyPopup(); From 0a85279bcb2e3d4beff9cdca210e3d12ee1f6885 Mon Sep 17 00:00:00 2001 From: Janek Bevendorff Date: Sun, 26 Feb 2017 00:08:48 +0100 Subject: [PATCH 44/93] Enable Yubikey in release-tool by default --- release-tool | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/release-tool b/release-tool index a508d79f..e5a49b05 100755 --- a/release-tool +++ b/release-tool @@ -37,7 +37,7 @@ DOCKER_CONTAINER_NAME="keepassxc-build-container" CMAKE_OPTIONS="" COMPILER="g++" MAKE_OPTIONS="-j8" -BUILD_PLUGINS="autotype http" +BUILD_PLUGINS="autotype http yubikey" INSTALL_PREFIX="/usr/local" BUILD_SOURCE_TARBALL=true ORIG_BRANCH="" From 6125988f35032bdfb88cc5eaf30a3d76371aa879 Mon Sep 17 00:00:00 2001 From: Janek Bevendorff Date: Sun, 26 Feb 2017 18:39:03 +0100 Subject: [PATCH 45/93] Mark CMake library variables as advanced --- cmake/FindYubiKey.cmake | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/cmake/FindYubiKey.cmake b/cmake/FindYubiKey.cmake index 297b6838..e5e0bb68 100644 --- a/cmake/FindYubiKey.cmake +++ b/cmake/FindYubiKey.cmake @@ -24,6 +24,4 @@ set(YUBIKEY_LIBRARIES ${YUBIKEY_CORE_LIBRARY} ${YUBIKEY_PERS_LIBRARY}) include(FindPackageHandleStandardArgs) find_package_handle_standard_args(YubiKey DEFAULT_MSG YUBIKEY_LIBRARIES YUBIKEY_INCLUDE_DIRS) -# TODO: Is mark_as_advanced() necessary? It's used in many examples with -# little explanation. Disable for now in favor of simplicity. -#mark_as_advanced(YUBIKEY_LIBRARIES YUBIKEY_INCLUDE_DIRS) +mark_as_advanced(YUBIKEY_LIBRARIES YUBIKEY_INCLUDE_DIRS) From 2ec500f926246531d9b3d80a4cf70058462b2761 Mon Sep 17 00:00:00 2001 From: Janek Bevendorff Date: Mon, 6 Mar 2017 13:51:52 +0100 Subject: [PATCH 46/93] Reorder link dependencies --- src/CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 677d2380..5e221b91 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -206,14 +206,14 @@ set_target_properties(keepassx_core PROPERTIES COMPILE_DEFINITIONS KEEPASSX_BUIL target_link_libraries(keepassx_core ${keepasshttp_LIB} ${autotype_LIB} + ${YUBIKEY_LIBRARIES} zxcvbn Qt5::Core Qt5::Concurrent Qt5::Widgets ${GCRYPT_LIBRARIES} ${GPGERROR_LIBRARIES} - ${ZLIB_LIBRARIES} - ${YUBIKEY_LIBRARIES}) + ${ZLIB_LIBRARIES}) if (UNIX AND NOT APPLE) target_link_libraries(keepassx_core Qt5::DBus) endif() From da0afd39395ed5aaae5ebf35d1905ccc9f94d186 Mon Sep 17 00:00:00 2001 From: Janek Bevendorff Date: Fri, 10 Mar 2017 14:28:26 +0100 Subject: [PATCH 47/93] Fix compiler warnings --- src/gui/Application.cpp | 4 ++-- src/gui/EditWidgetIcons.cpp | 3 ++- src/gui/MainWindow.ui | 4 +--- src/gui/entry/EditEntryWidgetAutoType.ui | 5 ----- 4 files changed, 5 insertions(+), 11 deletions(-) diff --git a/src/gui/Application.cpp b/src/gui/Application.cpp index 26d9d228..34d1b868 100644 --- a/src/gui/Application.cpp +++ b/src/gui/Application.cpp @@ -148,7 +148,7 @@ void Application::handleUnixSignal(int sig) case SIGTERM: { char buf = 0; - ::write(unixSignalSocket[0], &buf, sizeof(buf)); + Q_UNUSED(::write(unixSignalSocket[0], &buf, sizeof(buf))); return; } case SIGHUP: @@ -160,7 +160,7 @@ void Application::quitBySignal() { m_unixSignalNotifier->setEnabled(false); char buf; - ::read(unixSignalSocket[1], &buf, sizeof(buf)); + Q_UNUSED(::read(unixSignalSocket[1], &buf, sizeof(buf))); if (nullptr != m_mainWindow) static_cast(m_mainWindow)->appExit(); diff --git a/src/gui/EditWidgetIcons.cpp b/src/gui/EditWidgetIcons.cpp index 1e1f5db2..d789eb49 100644 --- a/src/gui/EditWidgetIcons.cpp +++ b/src/gui/EditWidgetIcons.cpp @@ -48,9 +48,9 @@ EditWidgetIcons::EditWidgetIcons(QWidget* parent) , m_defaultIconModel(new DefaultIconModel(this)) , m_customIconModel(new CustomIconModel(this)) #ifdef WITH_XC_HTTP - , m_httpClient(nullptr) , m_fallbackToGoogle(true) , m_redirectCount(0) + , m_httpClient(nullptr) #endif { m_ui->setupUi(this); @@ -149,6 +149,7 @@ void EditWidgetIcons::setUrl(const QString& url) m_ui->faviconButton->setVisible(!url.isEmpty()); resetFaviconDownload(); #else + Q_UNUSED(url); m_ui->faviconButton->setVisible(false); #endif } diff --git a/src/gui/MainWindow.ui b/src/gui/MainWindow.ui index 6e3ecf68..1d57d557 100644 --- a/src/gui/MainWindow.ui +++ b/src/gui/MainWindow.ui @@ -123,9 +123,7 @@ - - horizontalSpacer_2 - + diff --git a/src/gui/entry/EditEntryWidgetAutoType.ui b/src/gui/entry/EditEntryWidgetAutoType.ui index 21e102bf..a8090f76 100644 --- a/src/gui/entry/EditEntryWidgetAutoType.ui +++ b/src/gui/entry/EditEntryWidgetAutoType.ui @@ -259,11 +259,6 @@
- - - - -
From 2872f1706c08b89709ab80821da40a4f53f1c336 Mon Sep 17 00:00:00 2001 From: Janek Bevendorff Date: Fri, 10 Mar 2017 15:05:02 +0100 Subject: [PATCH 48/93] Fix Qt deprecation warnings --- src/http/Service.cpp | 4 +++- tests/TestGroupModel.cpp | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/http/Service.cpp b/src/http/Service.cpp index 1a89b732..676bb892 100644 --- a/src/http/Service.cpp +++ b/src/http/Service.cpp @@ -29,6 +29,8 @@ #include "core/Uuid.h" #include "core/PasswordGenerator.h" +#include + static const unsigned char KEEPASSHTTP_UUID_DATA[] = { 0x34, 0x69, 0x7a, 0x40, 0x8a, 0x5b, 0x41, 0xc0, 0x9f, 0x36, 0x89, 0x7d, 0x62, 0x3e, 0xcb, 0x31 @@ -387,7 +389,7 @@ QList Service::findMatchingEntries(const QString& /* priorities.insert(entry, sortPriority(entry, host, submitUrl, baseSubmitURL)); //Sort by priorities - qSort(pwEntries.begin(), pwEntries.end(), SortEntries(priorities, HttpSettings::sortByTitle() ? "Title" : "UserName")); + std::sort(pwEntries.begin(), pwEntries.end(), SortEntries(priorities, HttpSettings::sortByTitle() ? "Title" : "UserName")); } //if (pwEntries.count() > 0) diff --git a/tests/TestGroupModel.cpp b/tests/TestGroupModel.cpp index 3608cc47..1faf82aa 100644 --- a/tests/TestGroupModel.cpp +++ b/tests/TestGroupModel.cpp @@ -131,7 +131,7 @@ void TestGroupModel::test() QCOMPARE(spyMoved.count(), 3); QVERIFY(index12.isValid()); QCOMPARE(model->data(index12).toString(), QString("group12")); - QCOMPARE(model->data(index12.child(0, 0)).toString(), QString("group121")); + QCOMPARE(model->data(index12.model()->index(0, 0, index12)).toString(), QString("group121")); delete group12; QCOMPARE(spyAboutToAdd.count(), 1); From cb51ec61f739b16710071e8c022f5c7418fdab6e Mon Sep 17 00:00:00 2001 From: Janek Bevendorff Date: Fri, 10 Mar 2017 15:45:00 +0100 Subject: [PATCH 49/93] Replace remaining instances of Q_FOREACH with C++11 range-based for loops --- src/core/PasswordGenerator.cpp | 4 +- src/gui/KMessageWidget.cpp | 8 ++-- src/gui/SettingsWidget.cpp | 7 ++- src/http/AccessControlDialog.cpp | 5 +- src/http/Protocol.cpp | 13 ++++-- src/http/Service.cpp | 79 +++++++++++++++++++------------- 6 files changed, 72 insertions(+), 44 deletions(-) diff --git a/src/core/PasswordGenerator.cpp b/src/core/PasswordGenerator.cpp index aea237a2..cee1c55b 100644 --- a/src/core/PasswordGenerator.cpp +++ b/src/core/PasswordGenerator.cpp @@ -97,11 +97,11 @@ QString PasswordGenerator::generatePassword() const int PasswordGenerator::getbits() const { - QVector groups = passwordGroups(); + const QVector groups = passwordGroups(); int bits = 0; QVector passwordChars; - Q_FOREACH (const PasswordGroup& group, groups) { + for (const PasswordGroup& group: groups) { bits += group.size(); } diff --git a/src/gui/KMessageWidget.cpp b/src/gui/KMessageWidget.cpp index b88c3bc1..7f4cb94f 100644 --- a/src/gui/KMessageWidget.cpp +++ b/src/gui/KMessageWidget.cpp @@ -21,6 +21,7 @@ #include "KMessageWidget.h" #include "core/FilePath.h" +#include "core/Global.h" #include #include @@ -117,7 +118,8 @@ void KMessageWidgetPrivate::createLayout() qDeleteAll(buttons); buttons.clear(); - Q_FOREACH (QAction *action, q->actions()) { + const auto actions = q->actions(); + for (QAction *action: actions) { QToolButton *button = new QToolButton(content); button->setDefaultAction(action); button->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); @@ -137,7 +139,7 @@ void KMessageWidgetPrivate::createLayout() QHBoxLayout *buttonLayout = new QHBoxLayout; buttonLayout->addStretch(); - Q_FOREACH (QToolButton *button, buttons) { + for (QToolButton* button: asConst(buttons)) { // For some reason, calling show() is necessary if wordwrap is true, // otherwise the buttons do not show up. It is not needed if // wordwrap is false. @@ -151,7 +153,7 @@ void KMessageWidgetPrivate::createLayout() layout->addWidget(iconLabel); layout->addWidget(textLabel); - Q_FOREACH (QToolButton *button, buttons) { + for (QToolButton* button: asConst(buttons)) { layout->addWidget(button); } diff --git a/src/gui/SettingsWidget.cpp b/src/gui/SettingsWidget.cpp index 3ec9674f..8aa6982d 100644 --- a/src/gui/SettingsWidget.cpp +++ b/src/gui/SettingsWidget.cpp @@ -23,6 +23,7 @@ #include "core/Config.h" #include "core/Translator.h" #include "core/FilePath.h" +#include "core/Global.h" class SettingsWidget::ExtraPage { @@ -144,8 +145,9 @@ void SettingsWidget::loadSettings() m_secUi->passwordRepeatCheckBox->setChecked(config()->get("security/passwordsrepeat").toBool()); - Q_FOREACH (const ExtraPage& page, m_extraPages) + for (const ExtraPage& page: asConst(m_extraPages)) { page.loadSettings(); + } setCurrentPage(0); } @@ -190,8 +192,9 @@ void SettingsWidget::saveSettings() config()->set("security/passwordscleartext", m_secUi->passwordCleartextCheckBox->isChecked()); config()->set("security/passwordsrepeat", m_secUi->passwordRepeatCheckBox->isChecked()); - Q_FOREACH (const ExtraPage& page, m_extraPages) + for (const ExtraPage& page: asConst(m_extraPages)) { page.saveSettings(); + } Q_EMIT editFinished(true); } diff --git a/src/http/AccessControlDialog.cpp b/src/http/AccessControlDialog.cpp index 60422be4..4d21aa95 100644 --- a/src/http/AccessControlDialog.cpp +++ b/src/http/AccessControlDialog.cpp @@ -34,10 +34,11 @@ void AccessControlDialog::setUrl(const QString &url) "Please select whether you want to allow access.")).arg(QUrl(url).host())); } -void AccessControlDialog::setItems(const QList &items) +void AccessControlDialog::setItems(const QList &items) { - Q_FOREACH (Entry * entry, items) + for (Entry* entry: items) { ui->itemsList->addItem(entry->title() + " - " + entry->username()); + } } bool AccessControlDialog::remember() const diff --git a/src/http/Protocol.cpp b/src/http/Protocol.cpp index bcb30f0b..7b37013f 100644 --- a/src/http/Protocol.cpp +++ b/src/http/Protocol.cpp @@ -17,6 +17,7 @@ #include "crypto/Random.h" #include "crypto/SymmetricCipher.h" #include "crypto/SymmetricCipherGcrypt.h" +#include "core/Global.h" namespace KeepassHttpProtocol { @@ -370,8 +371,9 @@ QVariant Response::getEntries() const QList res; res.reserve(m_entries.size()); - Q_FOREACH (const Entry &entry, m_entries) + for (const Entry& entry: asConst(m_entries)) { res.append(qobject2qvariant(&entry)); + } return res; } @@ -383,14 +385,16 @@ void Response::setEntries(const QList &entries) QList encryptedEntries; encryptedEntries.reserve(m_count); - Q_FOREACH (const Entry &entry, entries) { + for (const Entry& entry: entries) { Entry encryptedEntry(encrypt(entry.name(), m_cipher), encrypt(entry.login(), m_cipher), entry.password().isNull() ? QString() : encrypt(entry.password(), m_cipher), encrypt(entry.uuid(), m_cipher)); - Q_FOREACH (const StringField & field, entry.stringFields()) + const auto stringFields = entry.stringFields(); + for (const StringField& field: stringFields) { encryptedEntry.addStringField(encrypt(field.key(), m_cipher), encrypt(field.value(), m_cipher)); + } encryptedEntries << encryptedEntry; } m_entries = encryptedEntries; @@ -508,8 +512,9 @@ QVariant Entry::getStringFields() const QList res; res.reserve(m_stringFields.size()); - Q_FOREACH (const StringField &stringfield, m_stringFields) + for (const StringField& stringfield: asConst(m_stringFields)) { res.append(qobject2qvariant(&stringfield)); + } return res; } diff --git a/src/http/Service.cpp b/src/http/Service.cpp index 676bb892..25c69f61 100644 --- a/src/http/Service.cpp +++ b/src/http/Service.cpp @@ -23,6 +23,7 @@ #include "core/Database.h" #include "core/Entry.h" +#include "core/Global.h" #include "core/Group.h" #include "core/EntrySearcher.h" #include "core/Metadata.h" @@ -189,8 +190,9 @@ bool Service::removeFirstDomain(QString & hostname) QList Service::searchEntries(Database* db, const QString& hostname) { QList entries; - if (Group* rootGroup = db->rootGroup()) - Q_FOREACH (Entry* entry, EntrySearcher().search(hostname, rootGroup, Qt::CaseInsensitive)) { + if (Group* rootGroup = db->rootGroup()) { + const auto results = EntrySearcher().search(hostname, rootGroup, Qt::CaseInsensitive); + for (Entry* entry: results) { QString title = entry->title(); QString url = entry->url(); @@ -201,6 +203,7 @@ QList Service::searchEntries(Database* db, const QString& hostname) || (matchUrlScheme(url) && hostname.endsWith(QUrl(url).host())) ) entries.append(entry); } + } return entries; } @@ -223,8 +226,9 @@ QList Service::searchEntries(const QString& text) QString hostname = QUrl(text).host(); QList entries; do { - Q_FOREACH (Database* db, databases) + for (Database* db: asConst(databases)) { entries << searchEntries(db, hostname); + } } while(entries.isEmpty() && removeFirstDomain(hostname)); return entries; @@ -249,9 +253,12 @@ KeepassHttpProtocol::Entry Service::prepareEntry(const Entry* entry) KeepassHttpProtocol::Entry res(entry->resolvePlaceholder(entry->title()), entry->resolvePlaceholder(entry->username()), entry->resolvePlaceholder(entry->password()), entry->uuid().toHex()); if (HttpSettings::supportKphFields()) { const EntryAttributes * attr = entry->attributes(); - Q_FOREACH (const QString& key, attr->keys()) - if (key.startsWith(QLatin1String("KPH: "))) + const auto keys = attr->keys(); + for (const QString& key: keys) { + if (key.startsWith(QLatin1String("KPH: "))) { res.addStringField(key, attr->value(key)); + } + } } return res; } @@ -318,7 +325,8 @@ QList Service::findMatchingEntries(const QString& /* //Check entries for authorization QList pwEntriesToConfirm; QList pwEntries; - Q_FOREACH (Entry * entry, searchEntries(url)) { + const auto entries = searchEntries(url); + for (Entry* entry: entries) { switch(checkAccess(entry, host, submitHost, realm)) { case Denied: continue; @@ -352,7 +360,7 @@ QList Service::findMatchingEntries(const QString& /* int res = dlg.exec(); if (dlg.remember()) { - Q_FOREACH (Entry * entry, pwEntriesToConfirm) { + for (Entry* entry: asConst(pwEntriesToConfirm)) { EntryConfig config; config.load(entry); if (res == QDialog::Accepted) { @@ -383,28 +391,22 @@ QList Service::findMatchingEntries(const QString& /* const QString baseSubmitURL = url.toString(QUrl::StripTrailingSlash | QUrl::RemovePath | QUrl::RemoveQuery | QUrl::RemoveFragment); //Cache priorities - QHash priorities; + QHash priorities; priorities.reserve(pwEntries.size()); - Q_FOREACH (const Entry * entry, pwEntries) + for (const Entry* entry: asConst(pwEntries)) { priorities.insert(entry, sortPriority(entry, host, submitUrl, baseSubmitURL)); + } //Sort by priorities std::sort(pwEntries.begin(), pwEntries.end(), SortEntries(priorities, HttpSettings::sortByTitle() ? "Title" : "UserName")); } - //if (pwEntries.count() > 0) - //{ - // var names = (from e in resp.Entries select e.Name).Distinct(); - // var n = String.Join("\n ", names.ToArray()); - // if (HttpSettings::receiveCredentialNotification()) - // ShowNotification(QString("%0: %1 is receiving credentials for:\n%2").arg(Id).arg(host).arg(n))); - //} - //Fill the list QList result; result.reserve(pwEntries.count()); - Q_FOREACH (Entry * entry, pwEntries) + for (Entry* entry: asConst(pwEntries)) { result << prepareEntry(entry); + } return result; } @@ -416,12 +418,19 @@ int Service::countMatchingEntries(const QString &, const QString &url, const QSt QList Service::searchAllEntries(const QString &) { QList result; - if (DatabaseWidget * dbWidget = m_dbTabWidget->currentDatabaseWidget()) - if (Database * db = dbWidget->database()) - if (Group * rootGroup = db->rootGroup()) - Q_FOREACH (Entry * entry, rootGroup->entriesRecursive()) - if (!entry->url().isEmpty() || QUrl(entry->title()).isValid()) - result << KeepassHttpProtocol::Entry(entry->title(), entry->username(), QString(), entry->uuid().toHex()); + if (DatabaseWidget* dbWidget = m_dbTabWidget->currentDatabaseWidget()) { + if (Database* db = dbWidget->database()) { + if (Group* rootGroup = db->rootGroup()) { + const auto entries = rootGroup->entriesRecursive(); + for (Entry* entry: entries) { + if (!entry->url().isEmpty() || QUrl(entry->title()).isValid()) { + result << KeepassHttpProtocol::Entry(entry->title(), entry->username(), + QString(), entry->uuid().toHex()); + } + } + } + } + } return result; } @@ -430,11 +439,15 @@ Group * Service::findCreateAddEntryGroup() if (DatabaseWidget * dbWidget = m_dbTabWidget->currentDatabaseWidget()) if (Database * db = dbWidget->database()) if (Group * rootGroup = db->rootGroup()) { - const QString groupName = QLatin1String(KEEPASSHTTP_GROUP_NAME);//TODO: setting to decide where new keys are created + //TODO: setting to decide where new keys are created + const QString groupName = QLatin1String(KEEPASSHTTP_GROUP_NAME); - Q_FOREACH (const Group * g, rootGroup->groupsRecursive(true)) - if (g->name() == groupName) + const auto groups = rootGroup->groupsRecursive(true); + for (const Group * g: groups) { + if (g->name() == groupName) { return db->resolveGroup(g->uuid()); + } + } Group * group; group = new Group(); @@ -507,14 +520,18 @@ void Service::removeSharedEncryptionKeys() QMessageBox::Ok); } else if (Entry* entry = getConfigEntry()) { QStringList keysToRemove; - Q_FOREACH (const QString& key, entry->attributes()->keys()) - if (key.startsWith(ASSOCIATE_KEY_PREFIX)) + const auto keys = entry->attributes()->keys(); + for (const QString& key: keys) { + if (key.startsWith(ASSOCIATE_KEY_PREFIX)) { keysToRemove << key; + } + } if(keysToRemove.count()) { entry->beginUpdate(); - Q_FOREACH (const QString& key, keysToRemove) + for (const QString& key: asConst(keysToRemove)) { entry->attributes()->remove(key); + } entry->endUpdate(); const int count = keysToRemove.count(); @@ -548,7 +565,7 @@ void Service::removeStoredPermissions() progress.setWindowModality(Qt::WindowModal); uint counter = 0; - Q_FOREACH (Entry* entry, entries) { + for (Entry* entry: asConst(entries)) { if (progress.wasCanceled()) return; if (entry->attributes()->contains(KEEPASSHTTP_NAME)) { From 4c0e2af6e3912142a021e773f51cd62fb12aed53 Mon Sep 17 00:00:00 2001 From: Janek Bevendorff Date: Fri, 10 Mar 2017 15:56:24 +0100 Subject: [PATCH 50/93] Remove UTF-8 BOM --- src/zxcvbn/zxcvbn.cpp | 2 +- src/zxcvbn/zxcvbn.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/zxcvbn/zxcvbn.cpp b/src/zxcvbn/zxcvbn.cpp index c999adfa..52c0bb1f 100644 --- a/src/zxcvbn/zxcvbn.cpp +++ b/src/zxcvbn/zxcvbn.cpp @@ -1,4 +1,4 @@ -/********************************************************************************** +/********************************************************************************** * C implementation of the zxcvbn password strength estimation method. * Copyright (c) 2015, Tony Evans * All rights reserved. diff --git a/src/zxcvbn/zxcvbn.h b/src/zxcvbn/zxcvbn.h index 2d3ec52c..796d6b47 100644 --- a/src/zxcvbn/zxcvbn.h +++ b/src/zxcvbn/zxcvbn.h @@ -1,4 +1,4 @@ -#ifndef ZXCVBN_H_F98183CE2A01_INCLUDED +#ifndef ZXCVBN_H_F98183CE2A01_INCLUDED #define ZXCVBN_H_F98183CE2A01_INCLUDED /********************************************************************************** * C implementation of the zxcvbn password strength estimation method. From 8d487d31a4c757a7b1f4faad3af85737d32961d5 Mon Sep 17 00:00:00 2001 From: Janek Bevendorff Date: Fri, 10 Mar 2017 15:58:42 +0100 Subject: [PATCH 51/93] Replace Q_EMIT, Q_SIGNALS and Q_SLOTS macros with MOC keywords --- src/autotype/AutoType.h | 6 +-- src/autotype/AutoTypeSelectDialog.cpp | 2 +- src/autotype/AutoTypeSelectDialog.h | 6 +-- src/autotype/AutoTypeSelectView.h | 2 +- src/autotype/mac/AutoTypeMac.cpp | 2 +- src/autotype/mac/AutoTypeMac.h | 2 +- src/autotype/test/AutoTypeTest.h | 2 +- src/autotype/windows/AutoTypeWindows.cpp | 2 +- src/autotype/windows/AutoTypeWindows.h | 2 +- src/autotype/xcb/AutoTypeXCB.cpp | 2 +- src/autotype/xcb/AutoTypeXCB.h | 2 +- src/core/AutoTypeAssociations.cpp | 22 +++++----- src/core/AutoTypeAssociations.h | 2 +- src/core/Database.cpp | 4 +- src/core/Database.h | 4 +- src/core/Entry.cpp | 16 +++---- src/core/Entry.h | 4 +- src/core/EntryAttachments.cpp | 26 +++++------ src/core/EntryAttachments.h | 2 +- src/core/EntryAttributes.cpp | 40 ++++++++--------- src/core/EntryAttributes.h | 2 +- src/core/Group.cpp | 48 ++++++++++----------- src/core/Group.h | 2 +- src/core/InactivityTimer.cpp | 2 +- src/core/InactivityTimer.h | 4 +- src/core/Metadata.cpp | 14 +++--- src/core/Metadata.h | 2 +- src/gui/Application.cpp | 2 +- src/gui/Application.h | 4 +- src/gui/ChangeMasterKeyWidget.cpp | 4 +- src/gui/ChangeMasterKeyWidget.h | 4 +- src/gui/Clipboard.h | 4 +- src/gui/CloneDialog.h | 2 +- src/gui/DatabaseOpenWidget.cpp | 4 +- src/gui/DatabaseOpenWidget.h | 6 +-- src/gui/DatabaseRepairWidget.cpp | 16 +++---- src/gui/DatabaseRepairWidget.h | 4 +- src/gui/DatabaseSettingsWidget.cpp | 4 +- src/gui/DatabaseSettingsWidget.h | 4 +- src/gui/DatabaseTabWidget.cpp | 28 ++++++------ src/gui/DatabaseTabWidget.h | 6 +-- src/gui/DatabaseWidget.cpp | 28 ++++++------ src/gui/DatabaseWidget.h | 6 +-- src/gui/DatabaseWidgetStateSync.h | 4 +- src/gui/DragTabBar.h | 2 +- src/gui/EditWidgetIcons.cpp | 2 +- src/gui/EditWidgetIcons.h | 6 +-- src/gui/KMessageWidget.h | 4 +- src/gui/KeePass1OpenWidget.cpp | 2 +- src/gui/LineEdit.h | 2 +- src/gui/MainWindow.h | 4 +- src/gui/MessageWidget.h | 2 +- src/gui/PasswordComboBox.h | 2 +- src/gui/PasswordEdit.cpp | 2 +- src/gui/PasswordEdit.h | 6 +-- src/gui/PasswordGeneratorWidget.cpp | 4 +- src/gui/PasswordGeneratorWidget.h | 4 +- src/gui/SettingsWidget.cpp | 4 +- src/gui/SettingsWidget.h | 4 +- src/gui/UnlockDatabaseDialog.cpp | 2 +- src/gui/UnlockDatabaseDialog.h | 4 +- src/gui/WelcomeWidget.cpp | 2 +- src/gui/WelcomeWidget.h | 4 +- src/gui/entry/AutoTypeAssociationsModel.cpp | 2 +- src/gui/entry/AutoTypeAssociationsModel.h | 2 +- src/gui/entry/EditEntryWidget.cpp | 10 ++--- src/gui/entry/EditEntryWidget.h | 4 +- src/gui/entry/EntryAttachmentsModel.cpp | 2 +- src/gui/entry/EntryAttachmentsModel.h | 2 +- src/gui/entry/EntryAttributesModel.cpp | 4 +- src/gui/entry/EntryAttributesModel.h | 2 +- src/gui/entry/EntryModel.cpp | 6 +-- src/gui/entry/EntryModel.h | 6 +-- src/gui/entry/EntryView.cpp | 6 +-- src/gui/entry/EntryView.h | 6 +-- src/gui/group/EditGroupWidget.cpp | 4 +- src/gui/group/EditGroupWidget.h | 4 +- src/gui/group/GroupModel.cpp | 2 +- src/gui/group/GroupModel.h | 2 +- src/gui/group/GroupView.cpp | 4 +- src/gui/group/GroupView.h | 4 +- src/http/HttpPasswordGeneratorWidget.h | 2 +- src/http/OptionDialog.h | 4 +- src/http/Service.h | 2 +- src/streams/LayeredStream.h | 2 +- tests/TestAutoType.h | 2 +- tests/TestCryptoHash.h | 2 +- tests/TestCsvExporter.h | 2 +- tests/TestDeletedObjects.h | 2 +- tests/TestEntry.h | 2 +- tests/TestEntryModel.h | 2 +- tests/TestEntrySearcher.h | 2 +- tests/TestExporter.h | 2 +- tests/TestGroup.h | 2 +- tests/TestGroupModel.h | 2 +- tests/TestHashedBlockStream.h | 2 +- tests/TestKeePass1Reader.h | 2 +- tests/TestKeePass2RandomStream.h | 2 +- tests/TestKeePass2Reader.h | 2 +- tests/TestKeePass2Writer.h | 2 +- tests/TestKeePass2XmlReader.h | 2 +- tests/TestKeys.h | 2 +- tests/TestModified.h | 2 +- tests/TestRandom.h | 2 +- tests/TestSymmetricCipher.h | 2 +- tests/TestWildcardMatcher.h | 2 +- tests/gui/TestGui.h | 2 +- tests/gui/TestGuiPixmaps.h | 2 +- tests/modeltest.h | 4 +- 109 files changed, 274 insertions(+), 274 deletions(-) diff --git a/src/autotype/AutoType.h b/src/autotype/AutoType.h index 9799af4f..311eedaa 100644 --- a/src/autotype/AutoType.h +++ b/src/autotype/AutoType.h @@ -48,13 +48,13 @@ public: static AutoType* instance(); static void createTestInstance(); -public Q_SLOTS: +public slots: void performGlobalAutoType(const QList& dbList); -Q_SIGNALS: +signals: void globalShortcutTriggered(); -private Q_SLOTS: +private slots: void performAutoTypeFromGlobal(Entry* entry, const QString& sequence); void resetInAutoType(); void unloadPlugin(); diff --git a/src/autotype/AutoTypeSelectDialog.cpp b/src/autotype/AutoTypeSelectDialog.cpp index 6bb155b8..240dd723 100644 --- a/src/autotype/AutoTypeSelectDialog.cpp +++ b/src/autotype/AutoTypeSelectDialog.cpp @@ -91,7 +91,7 @@ void AutoTypeSelectDialog::emitEntryActivated(const QModelIndex& index) Entry* entry = m_view->entryFromIndex(index); accept(); - Q_EMIT entryActivated(entry, m_sequences[entry]); + emit entryActivated(entry, m_sequences[entry]); } void AutoTypeSelectDialog::entryRemoved() diff --git a/src/autotype/AutoTypeSelectDialog.h b/src/autotype/AutoTypeSelectDialog.h index 7b3909a1..3d9c684e 100644 --- a/src/autotype/AutoTypeSelectDialog.h +++ b/src/autotype/AutoTypeSelectDialog.h @@ -33,13 +33,13 @@ public: explicit AutoTypeSelectDialog(QWidget* parent = nullptr); void setEntries(const QList& entries, const QHash& sequences); -Q_SIGNALS: +signals: void entryActivated(Entry* entry, const QString& sequence); -public Q_SLOTS: +public slots: void done(int r) override; -private Q_SLOTS: +private slots: void emitEntryActivated(const QModelIndex& index); void entryRemoved(); diff --git a/src/autotype/AutoTypeSelectView.h b/src/autotype/AutoTypeSelectView.h index 749f6a9f..a781757b 100644 --- a/src/autotype/AutoTypeSelectView.h +++ b/src/autotype/AutoTypeSelectView.h @@ -32,7 +32,7 @@ public: protected: void mouseMoveEvent(QMouseEvent* event) override; -private Q_SLOTS: +private slots: void selectFirstEntry(); }; diff --git a/src/autotype/mac/AutoTypeMac.cpp b/src/autotype/mac/AutoTypeMac.cpp index e55c336c..08df6310 100644 --- a/src/autotype/mac/AutoTypeMac.cpp +++ b/src/autotype/mac/AutoTypeMac.cpp @@ -460,7 +460,7 @@ OSStatus AutoTypePlatformMac::hotkeyHandler(EventHandlerCallRef nextHandler, Eve if (::GetEventParameter(theEvent, kEventParamDirectObject, typeEventHotKeyID, nullptr, sizeof(hotkeyId), nullptr, &hotkeyId) == noErr && hotkeyId.id == HOTKEY_ID) { - Q_EMIT self->globalShortcutTriggered(); + emit self->globalShortcutTriggered(); } return noErr; diff --git a/src/autotype/mac/AutoTypeMac.h b/src/autotype/mac/AutoTypeMac.h index 475a4b99..5fbbf763 100644 --- a/src/autotype/mac/AutoTypeMac.h +++ b/src/autotype/mac/AutoTypeMac.h @@ -51,7 +51,7 @@ public: void sendChar(const QChar& ch, bool isKeyDown); void sendKey(Qt::Key key, bool isKeyDown); -Q_SIGNALS: +signals: void globalShortcutTriggered(); private: diff --git a/src/autotype/test/AutoTypeTest.h b/src/autotype/test/AutoTypeTest.h index 4feaab94..d9a86c3d 100644 --- a/src/autotype/test/AutoTypeTest.h +++ b/src/autotype/test/AutoTypeTest.h @@ -60,7 +60,7 @@ public: void addActionChar(AutoTypeChar* action); void addActionKey(AutoTypeKey* action); -Q_SIGNALS: +signals: void globalShortcutTriggered(); private: diff --git a/src/autotype/windows/AutoTypeWindows.cpp b/src/autotype/windows/AutoTypeWindows.cpp index 481caa83..0818a37b 100644 --- a/src/autotype/windows/AutoTypeWindows.cpp +++ b/src/autotype/windows/AutoTypeWindows.cpp @@ -96,7 +96,7 @@ int AutoTypePlatformWin::platformEventFilter(void* event) MSG *msg = static_cast(event); if (msg->message == WM_HOTKEY && msg->wParam == HOTKEY_ID) { - Q_EMIT globalShortcutTriggered(); + emit globalShortcutTriggered(); return 1; } diff --git a/src/autotype/windows/AutoTypeWindows.h b/src/autotype/windows/AutoTypeWindows.h index 7a8c4bca..f8b213cb 100644 --- a/src/autotype/windows/AutoTypeWindows.h +++ b/src/autotype/windows/AutoTypeWindows.h @@ -45,7 +45,7 @@ public: void sendChar(const QChar& ch, bool isKeyDown); void sendKey(Qt::Key key, bool isKeyDown); -Q_SIGNALS: +signals: void globalShortcutTriggered(); private: diff --git a/src/autotype/xcb/AutoTypeXCB.cpp b/src/autotype/xcb/AutoTypeXCB.cpp index a07a916c..e6ac74bb 100644 --- a/src/autotype/xcb/AutoTypeXCB.cpp +++ b/src/autotype/xcb/AutoTypeXCB.cpp @@ -214,7 +214,7 @@ int AutoTypePlatformX11::platformEventFilter(void* event) && (!QApplication::activeWindow() || QApplication::activeWindow()->isMinimized()) && m_loaded) { if (type == XCB_KEY_PRESS) { - Q_EMIT globalShortcutTriggered(); + emit globalShortcutTriggered(); } return 1; diff --git a/src/autotype/xcb/AutoTypeXCB.h b/src/autotype/xcb/AutoTypeXCB.h index 26d1e810..dc251e3f 100644 --- a/src/autotype/xcb/AutoTypeXCB.h +++ b/src/autotype/xcb/AutoTypeXCB.h @@ -59,7 +59,7 @@ public: void SendKeyPressedEvent(KeySym keysym); -Q_SIGNALS: +signals: void globalShortcutTriggered(); private: diff --git a/src/core/AutoTypeAssociations.cpp b/src/core/AutoTypeAssociations.cpp index 75d21fe3..5ec4eb3b 100644 --- a/src/core/AutoTypeAssociations.cpp +++ b/src/core/AutoTypeAssociations.cpp @@ -39,29 +39,29 @@ void AutoTypeAssociations::copyDataFrom(const AutoTypeAssociations* other) return; } - Q_EMIT aboutToReset(); + emit aboutToReset(); m_associations = other->m_associations; - Q_EMIT reset(); - Q_EMIT modified(); + emit reset(); + emit modified(); } void AutoTypeAssociations::add(const AutoTypeAssociations::Association& association) { int index = m_associations.size(); - Q_EMIT aboutToAdd(index); + emit aboutToAdd(index); m_associations.append(association); - Q_EMIT added(index); - Q_EMIT modified(); + emit added(index); + emit modified(); } void AutoTypeAssociations::remove(int index) { Q_ASSERT(index >= 0 && index < m_associations.size()); - Q_EMIT aboutToRemove(index); + emit aboutToRemove(index); m_associations.removeAt(index); - Q_EMIT removed(index); - Q_EMIT modified(); + emit removed(index); + emit modified(); } void AutoTypeAssociations::removeEmpty() @@ -81,8 +81,8 @@ void AutoTypeAssociations::update(int index, const AutoTypeAssociations::Associa if (m_associations.at(index) != association) { m_associations[index] = association; - Q_EMIT dataChanged(index); - Q_EMIT modified(); + emit dataChanged(index); + emit modified(); } } diff --git a/src/core/AutoTypeAssociations.h b/src/core/AutoTypeAssociations.h index 491a5db1..61ef3fd4 100644 --- a/src/core/AutoTypeAssociations.h +++ b/src/core/AutoTypeAssociations.h @@ -48,7 +48,7 @@ public: private: QList m_associations; -Q_SIGNALS: +signals: void modified(); void dataChanged(int index); void aboutToAdd(int index); diff --git a/src/core/Database.cpp b/src/core/Database.cpp index 33682038..60874fd8 100644 --- a/src/core/Database.cpp +++ b/src/core/Database.cpp @@ -227,7 +227,7 @@ bool Database::setKey(const CompositeKey& key, const QByteArray& transformSeed, if (updateChangedTime) { m_metadata->setMasterKeyChanged(QDateTime::currentDateTimeUtc()); } - Q_EMIT modifiedImmediate(); + emit modifiedImmediate(); return true; } @@ -285,7 +285,7 @@ void Database::recycleGroup(Group* group) void Database::merge(const Database* other) { m_rootGroup->merge(other->rootGroup()); - Q_EMIT modified(); + emit modified(); } void Database::setEmitModified(bool value) diff --git a/src/core/Database.h b/src/core/Database.h index 3cd5ed1b..e781e1aa 100644 --- a/src/core/Database.h +++ b/src/core/Database.h @@ -115,7 +115,7 @@ public: static Database* databaseByUuid(const Uuid& uuid); -Q_SIGNALS: +signals: void groupDataChanged(Group* group); void groupAboutToAdd(Group* group, int index); void groupAdded(); @@ -127,7 +127,7 @@ Q_SIGNALS: void modified(); void modifiedImmediate(); -private Q_SLOTS: +private slots: void startModifiedTimer(); private: diff --git a/src/core/Entry.cpp b/src/core/Entry.cpp index a2e72f7f..7edf7e78 100644 --- a/src/core/Entry.cpp +++ b/src/core/Entry.cpp @@ -62,7 +62,7 @@ template inline bool Entry::set(T& property, const T& value) { if (property != value) { property = value; - Q_EMIT modified(); + emit modified(); return true; } else { @@ -299,7 +299,7 @@ void Entry::setIcon(int iconNumber) m_data.iconNumber = iconNumber; m_data.customIcon = Uuid(); - Q_EMIT modified(); + emit modified(); emitDataChanged(); } } @@ -312,7 +312,7 @@ void Entry::setIcon(const Uuid& uuid) m_data.customIcon = uuid; m_data.iconNumber = 0; - Q_EMIT modified(); + emit modified(); emitDataChanged(); } } @@ -392,7 +392,7 @@ void Entry::setExpires(const bool& value) { if (m_data.timeInfo.expires() != value) { m_data.timeInfo.setExpires(value); - Q_EMIT modified(); + emit modified(); } } @@ -400,7 +400,7 @@ void Entry::setExpiryTime(const QDateTime& dateTime) { if (m_data.timeInfo.expiryTime() != dateTime) { m_data.timeInfo.setExpiryTime(dateTime); - Q_EMIT modified(); + emit modified(); } } @@ -419,7 +419,7 @@ void Entry::addHistoryItem(Entry* entry) Q_ASSERT(!entry->parent()); m_history.append(entry); - Q_EMIT modified(); + emit modified(); } void Entry::removeHistoryItems(const QList& historyEntries) @@ -437,7 +437,7 @@ void Entry::removeHistoryItems(const QList& historyEntries) delete entry; } - Q_EMIT modified(); + emit modified(); } void Entry::truncateHistory() @@ -633,7 +633,7 @@ void Entry::setGroup(Group* group) void Entry::emitDataChanged() { - Q_EMIT dataChanged(this); + emit dataChanged(this); } const Database* Entry::database() const diff --git a/src/core/Entry.h b/src/core/Entry.h index 38ec42d4..25b9bc38 100644 --- a/src/core/Entry.h +++ b/src/core/Entry.h @@ -147,7 +147,7 @@ public: void setUpdateTimeinfo(bool value); -Q_SIGNALS: +signals: /** * Emitted when a default attribute has been changed. */ @@ -155,7 +155,7 @@ Q_SIGNALS: void modified(); -private Q_SLOTS: +private slots: void emitDataChanged(); void updateTimeinfo(); void updateModifiedSinceBegin(); diff --git a/src/core/EntryAttachments.cpp b/src/core/EntryAttachments.cpp index 7bd080bf..a53a3c99 100644 --- a/src/core/EntryAttachments.cpp +++ b/src/core/EntryAttachments.cpp @@ -48,7 +48,7 @@ void EntryAttachments::set(const QString& key, const QByteArray& value) bool addAttachment = !m_attachments.contains(key); if (addAttachment) { - Q_EMIT aboutToBeAdded(key); + emit aboutToBeAdded(key); } if (addAttachment || m_attachments.value(key) != value) { @@ -57,14 +57,14 @@ void EntryAttachments::set(const QString& key, const QByteArray& value) } if (addAttachment) { - Q_EMIT added(key); + emit added(key); } else { - Q_EMIT keyModified(key); + emit keyModified(key); } if (emitModified) { - Q_EMIT modified(); + emit modified(); } } @@ -75,12 +75,12 @@ void EntryAttachments::remove(const QString& key) return; } - Q_EMIT aboutToBeRemoved(key); + emit aboutToBeRemoved(key); m_attachments.remove(key); - Q_EMIT removed(key); - Q_EMIT modified(); + emit removed(key); + emit modified(); } void EntryAttachments::clear() @@ -89,23 +89,23 @@ void EntryAttachments::clear() return; } - Q_EMIT aboutToBeReset(); + emit aboutToBeReset(); m_attachments.clear(); - Q_EMIT reset(); - Q_EMIT modified(); + emit reset(); + emit modified(); } void EntryAttachments::copyDataFrom(const EntryAttachments* other) { if (*this != *other) { - Q_EMIT aboutToBeReset(); + emit aboutToBeReset(); m_attachments = other->m_attachments; - Q_EMIT reset(); - Q_EMIT modified(); + emit reset(); + emit modified(); } } diff --git a/src/core/EntryAttachments.h b/src/core/EntryAttachments.h index 903ca10b..04c22cb3 100644 --- a/src/core/EntryAttachments.h +++ b/src/core/EntryAttachments.h @@ -38,7 +38,7 @@ public: bool operator==(const EntryAttachments& other) const; bool operator!=(const EntryAttachments& other) const; -Q_SIGNALS: +signals: void modified(); void keyModified(const QString& key); void aboutToBeAdded(const QString& key); diff --git a/src/core/EntryAttributes.cpp b/src/core/EntryAttributes.cpp index 865e853f..c689f8ad 100644 --- a/src/core/EntryAttributes.cpp +++ b/src/core/EntryAttributes.cpp @@ -98,7 +98,7 @@ void EntryAttributes::set(const QString& key, const QString& value, bool protect bool defaultAttribute = isDefaultAttribute(key); if (addAttribute && !defaultAttribute) { - Q_EMIT aboutToBeAdded(key); + emit aboutToBeAdded(key); } if (addAttribute || changeValue) { @@ -117,17 +117,17 @@ void EntryAttributes::set(const QString& key, const QString& value, bool protect } if (emitModified) { - Q_EMIT modified(); + emit modified(); } if (defaultAttribute && changeValue) { - Q_EMIT defaultKeyModified(); + emit defaultKeyModified(); } else if (addAttribute) { - Q_EMIT added(key); + emit added(key); } else if (emitModified) { - Q_EMIT customKeyModified(key); + emit customKeyModified(key); } } @@ -140,13 +140,13 @@ void EntryAttributes::remove(const QString& key) return; } - Q_EMIT aboutToBeRemoved(key); + emit aboutToBeRemoved(key); m_attributes.remove(key); m_protectedAttributes.remove(key); - Q_EMIT removed(key); - Q_EMIT modified(); + emit removed(key); + emit modified(); } void EntryAttributes::rename(const QString& oldKey, const QString& newKey) @@ -167,7 +167,7 @@ void EntryAttributes::rename(const QString& oldKey, const QString& newKey) QString data = value(oldKey); bool protect = isProtected(oldKey); - Q_EMIT aboutToRename(oldKey, newKey); + emit aboutToRename(oldKey, newKey); m_attributes.remove(oldKey); m_attributes.insert(newKey, data); @@ -176,8 +176,8 @@ void EntryAttributes::rename(const QString& oldKey, const QString& newKey) m_protectedAttributes.insert(newKey); } - Q_EMIT modified(); - Q_EMIT renamed(oldKey, newKey); + emit modified(); + emit renamed(oldKey, newKey); } void EntryAttributes::copyCustomKeysFrom(const EntryAttributes* other) @@ -186,7 +186,7 @@ void EntryAttributes::copyCustomKeysFrom(const EntryAttributes* other) return; } - Q_EMIT aboutToBeReset(); + emit aboutToBeReset(); // remove all non-default keys const QList keyList = keys(); @@ -207,8 +207,8 @@ void EntryAttributes::copyCustomKeysFrom(const EntryAttributes* other) } } - Q_EMIT reset(); - Q_EMIT modified(); + emit reset(); + emit modified(); } bool EntryAttributes::areCustomKeysDifferent(const EntryAttributes* other) @@ -235,13 +235,13 @@ bool EntryAttributes::areCustomKeysDifferent(const EntryAttributes* other) void EntryAttributes::copyDataFrom(const EntryAttributes* other) { if (*this != *other) { - Q_EMIT aboutToBeReset(); + emit aboutToBeReset(); m_attributes = other->m_attributes; m_protectedAttributes = other->m_protectedAttributes; - Q_EMIT reset(); - Q_EMIT modified(); + emit reset(); + emit modified(); } } @@ -259,7 +259,7 @@ bool EntryAttributes::operator!=(const EntryAttributes& other) const void EntryAttributes::clear() { - Q_EMIT aboutToBeReset(); + emit aboutToBeReset(); m_attributes.clear(); m_protectedAttributes.clear(); @@ -268,8 +268,8 @@ void EntryAttributes::clear() m_attributes.insert(key, ""); } - Q_EMIT reset(); - Q_EMIT modified(); + emit reset(); + emit modified(); } int EntryAttributes::attributesSize() diff --git a/src/core/EntryAttributes.h b/src/core/EntryAttributes.h index 78afe5ef..58f1db61 100644 --- a/src/core/EntryAttributes.h +++ b/src/core/EntryAttributes.h @@ -57,7 +57,7 @@ public: static const QString RememberCmdExecAttr; static bool isDefaultAttribute(const QString& key); -Q_SIGNALS: +signals: void modified(); void defaultKeyModified(); void customKeyModified(const QString& key); diff --git a/src/core/Group.cpp b/src/core/Group.cpp index 8c96bb07..d8d60998 100644 --- a/src/core/Group.cpp +++ b/src/core/Group.cpp @@ -74,7 +74,7 @@ template inline bool Group::set(P& property, const V& value) if (property != value) { property = value; updateTimeinfo(); - Q_EMIT modified(); + emit modified(); return true; } else { @@ -249,7 +249,7 @@ void Group::setUuid(const Uuid& uuid) void Group::setName(const QString& name) { if (set(m_data.name, name)) { - Q_EMIT dataChanged(this); + emit dataChanged(this); } } @@ -267,8 +267,8 @@ void Group::setIcon(int iconNumber) m_data.customIcon = Uuid(); updateTimeinfo(); - Q_EMIT modified(); - Q_EMIT dataChanged(this); + emit modified(); + emit dataChanged(this); } } @@ -281,8 +281,8 @@ void Group::setIcon(const Uuid& uuid) m_data.iconNumber = 0; updateTimeinfo(); - Q_EMIT modified(); - Q_EMIT dataChanged(this); + emit modified(); + emit dataChanged(this); } } @@ -296,7 +296,7 @@ void Group::setExpanded(bool expanded) if (m_data.isExpanded != expanded) { m_data.isExpanded = expanded; updateTimeinfo(); - Q_EMIT modified(); + emit modified(); } } @@ -325,7 +325,7 @@ void Group::setExpires(bool value) if (m_data.timeInfo.expires() != value) { m_data.timeInfo.setExpires(value); updateTimeinfo(); - Q_EMIT modified(); + emit modified(); } } @@ -334,7 +334,7 @@ void Group::setExpiryTime(const QDateTime& dateTime) if (m_data.timeInfo.expiryTime() != dateTime) { m_data.timeInfo.setExpiryTime(dateTime); updateTimeinfo(); - Q_EMIT modified(); + emit modified(); } } @@ -391,12 +391,12 @@ void Group::setParent(Group* parent, int index) recSetDatabase(parent->m_db); } QObject::setParent(parent); - Q_EMIT aboutToAdd(this, index); + emit aboutToAdd(this, index); Q_ASSERT(index <= parent->m_children.size()); parent->m_children.insert(index, this); } else { - Q_EMIT aboutToMove(this, parent, index); + emit aboutToMove(this, parent, index); m_parent->m_children.removeAll(this); m_parent = parent; QObject::setParent(parent); @@ -408,13 +408,13 @@ void Group::setParent(Group* parent, int index) m_data.timeInfo.setLocationChanged(QDateTime::currentDateTimeUtc()); } - Q_EMIT modified(); + emit modified(); if (!moveWithinDatabase) { - Q_EMIT added(); + emit added(); } else { - Q_EMIT moved(); + emit moved(); } } @@ -566,7 +566,7 @@ void Group::merge(const Group* other) } } - Q_EMIT modified(); + emit modified(); } Group* Group::findChildByName(const QString& name) @@ -623,7 +623,7 @@ void Group::addEntry(Entry* entry) Q_ASSERT(entry); Q_ASSERT(!m_entries.contains(entry)); - Q_EMIT entryAboutToAdd(entry); + emit entryAboutToAdd(entry); m_entries << entry; connect(entry, SIGNAL(dataChanged(Entry*)), SIGNAL(entryDataChanged(Entry*))); @@ -631,23 +631,23 @@ void Group::addEntry(Entry* entry) connect(entry, SIGNAL(modified()), m_db, SIGNAL(modifiedImmediate())); } - Q_EMIT modified(); - Q_EMIT entryAdded(entry); + emit modified(); + emit entryAdded(entry); } void Group::removeEntry(Entry* entry) { Q_ASSERT(m_entries.contains(entry)); - Q_EMIT entryAboutToRemove(entry); + emit entryAboutToRemove(entry); entry->disconnect(this); if (m_db) { entry->disconnect(m_db); } m_entries.removeAll(entry); - Q_EMIT modified(); - Q_EMIT entryRemoved(entry); + emit modified(); + emit entryRemoved(entry); } void Group::recSetDatabase(Database* db) @@ -693,10 +693,10 @@ void Group::recSetDatabase(Database* db) void Group::cleanupParent() { if (m_parent) { - Q_EMIT aboutToRemove(this); + emit aboutToRemove(this); m_parent->m_children.removeAll(this); - Q_EMIT modified(); - Q_EMIT removed(); + emit modified(); + emit removed(); } } diff --git a/src/core/Group.h b/src/core/Group.h index 3c054f97..e3e5e755 100644 --- a/src/core/Group.h +++ b/src/core/Group.h @@ -122,7 +122,7 @@ public: void copyDataFrom(const Group* other); void merge(const Group* other); -Q_SIGNALS: +signals: void dataChanged(Group* group); void aboutToAdd(Group* group, int index); diff --git a/src/core/InactivityTimer.cpp b/src/core/InactivityTimer.cpp index dd162e69..0cfc8f0d 100644 --- a/src/core/InactivityTimer.cpp +++ b/src/core/InactivityTimer.cpp @@ -73,7 +73,7 @@ void InactivityTimer::timeout() } if (m_active && !m_timer->isActive()) { - Q_EMIT inactivityDetected(); + emit inactivityDetected(); } m_emitMutx.unlock(); diff --git a/src/core/InactivityTimer.h b/src/core/InactivityTimer.h index ba571a5e..b9de80fb 100644 --- a/src/core/InactivityTimer.h +++ b/src/core/InactivityTimer.h @@ -33,13 +33,13 @@ public: void activate(); void deactivate(); -Q_SIGNALS: +signals: void inactivityDetected(); protected: bool eventFilter(QObject* watched, QEvent* event); -private Q_SLOTS: +private slots: void timeout(); private: diff --git a/src/core/Metadata.cpp b/src/core/Metadata.cpp index bf68af3c..a7207b59 100644 --- a/src/core/Metadata.cpp +++ b/src/core/Metadata.cpp @@ -55,7 +55,7 @@ template bool Metadata::set(P& property, const V& value) { if (property != value) { property = value; - Q_EMIT modified(); + emit modified(); return true; } else { @@ -69,7 +69,7 @@ template bool Metadata::set(P& property, const V& value, QDat if (m_updateDatetime) { dateTime = QDateTime::currentDateTimeUtc(); } - Q_EMIT modified(); + emit modified(); return true; } else { @@ -308,7 +308,7 @@ void Metadata::setGenerator(const QString& value) void Metadata::setName(const QString& value) { if (set(m_data.name, value, m_data.nameChanged)) { - Q_EMIT nameTextChanged(); + emit nameTextChanged(); } } @@ -391,7 +391,7 @@ void Metadata::addCustomIcon(const Uuid& uuid, const QImage& icon) m_customIconScaledCacheKeys[uuid] = QPixmapCache::Key(); m_customIconsOrder.append(uuid); Q_ASSERT(m_customIcons.count() == m_customIconsOrder.count()); - Q_EMIT modified(); + emit modified(); } void Metadata::addCustomIconScaled(const Uuid& uuid, const QImage& icon) @@ -422,7 +422,7 @@ void Metadata::removeCustomIcon(const Uuid& uuid) m_customIconScaledCacheKeys.remove(uuid); m_customIconsOrder.removeAll(uuid); Q_ASSERT(m_customIcons.count() == m_customIconsOrder.count()); - Q_EMIT modified(); + emit modified(); } void Metadata::copyCustomIcons(const QSet& iconList, const Metadata* otherMetadata) @@ -504,7 +504,7 @@ void Metadata::addCustomField(const QString& key, const QString& value) Q_ASSERT(!m_customFields.contains(key)); m_customFields.insert(key, value); - Q_EMIT modified(); + emit modified(); } void Metadata::removeCustomField(const QString& key) @@ -512,5 +512,5 @@ void Metadata::removeCustomField(const QString& key) Q_ASSERT(m_customFields.contains(key)); m_customFields.remove(key); - Q_EMIT modified(); + emit modified(); } diff --git a/src/core/Metadata.h b/src/core/Metadata.h index c35aed39..4f435d75 100644 --- a/src/core/Metadata.h +++ b/src/core/Metadata.h @@ -146,7 +146,7 @@ public: */ void copyAttributesFrom(const Metadata* other); -Q_SIGNALS: +signals: void nameTextChanged(); void modified(); diff --git a/src/gui/Application.cpp b/src/gui/Application.cpp index 34d1b868..98c373ed 100644 --- a/src/gui/Application.cpp +++ b/src/gui/Application.cpp @@ -96,7 +96,7 @@ bool Application::event(QEvent* event) { // Handle Apple QFileOpenEvent from finder (double click on .kdbx file) if (event->type() == QEvent::FileOpen) { - Q_EMIT openFile(static_cast(event)->file()); + emit openFile(static_cast(event)->file()); return true; } #ifdef Q_OS_MAC diff --git a/src/gui/Application.h b/src/gui/Application.h index 9bfe4d54..f4324fa2 100644 --- a/src/gui/Application.h +++ b/src/gui/Application.h @@ -33,10 +33,10 @@ public: bool event(QEvent* event) override; -Q_SIGNALS: +signals: void openFile(const QString& filename); -private Q_SLOTS: +private slots: #if defined(Q_OS_UNIX) void quitBySignal(); #endif diff --git a/src/gui/ChangeMasterKeyWidget.cpp b/src/gui/ChangeMasterKeyWidget.cpp index 812f7ec0..f7e5b33a 100644 --- a/src/gui/ChangeMasterKeyWidget.cpp +++ b/src/gui/ChangeMasterKeyWidget.cpp @@ -131,13 +131,13 @@ void ChangeMasterKeyWidget::generateKey() } m_ui->messageWidget->hideMessage(); - Q_EMIT editFinished(true); + emit editFinished(true); } void ChangeMasterKeyWidget::reject() { - Q_EMIT editFinished(false); + emit editFinished(false); } void ChangeMasterKeyWidget::setCancelEnabled(bool enabled) diff --git a/src/gui/ChangeMasterKeyWidget.h b/src/gui/ChangeMasterKeyWidget.h index fc4a1b9c..1f90c4c5 100644 --- a/src/gui/ChangeMasterKeyWidget.h +++ b/src/gui/ChangeMasterKeyWidget.h @@ -40,10 +40,10 @@ public: QLabel* headlineLabel(); void setCancelEnabled(bool enabled); -Q_SIGNALS: +signals: void editFinished(bool accepted); -private Q_SLOTS: +private slots: void generateKey(); void reject(); void createKeyFile(); diff --git a/src/gui/Clipboard.h b/src/gui/Clipboard.h index dafce70a..e0a16d26 100644 --- a/src/gui/Clipboard.h +++ b/src/gui/Clipboard.h @@ -31,10 +31,10 @@ public: static Clipboard* instance(); -public Q_SLOTS: +public slots: void clearCopiedText(); -private Q_SLOTS: +private slots: void clearClipboard(); private: diff --git a/src/gui/CloneDialog.h b/src/gui/CloneDialog.h index 277da4a8..094b0fe7 100644 --- a/src/gui/CloneDialog.h +++ b/src/gui/CloneDialog.h @@ -39,7 +39,7 @@ public: private: QScopedPointer m_ui; -private Q_SLOTS: +private slots: void cloneEntry(); protected: diff --git a/src/gui/DatabaseOpenWidget.cpp b/src/gui/DatabaseOpenWidget.cpp index 1e301614..b6220d93 100644 --- a/src/gui/DatabaseOpenWidget.cpp +++ b/src/gui/DatabaseOpenWidget.cpp @@ -130,7 +130,7 @@ void DatabaseOpenWidget::openDatabase() if (m_ui->messageWidget->isVisible()) { m_ui->messageWidget->animatedHide(); } - Q_EMIT editFinished(true); + emit editFinished(true); } else { m_ui->messageWidget->showMessage(tr("Unable to open the database.") @@ -174,7 +174,7 @@ CompositeKey DatabaseOpenWidget::databaseKey() void DatabaseOpenWidget::reject() { - Q_EMIT editFinished(false); + emit editFinished(false); } void DatabaseOpenWidget::activatePassword() diff --git a/src/gui/DatabaseOpenWidget.h b/src/gui/DatabaseOpenWidget.h index 34f401a0..405c7f3b 100644 --- a/src/gui/DatabaseOpenWidget.h +++ b/src/gui/DatabaseOpenWidget.h @@ -41,18 +41,18 @@ public: void enterKey(const QString& pw, const QString& keyFile); Database* database(); -Q_SIGNALS: +signals: void editFinished(bool accepted); protected: void showEvent(QShowEvent* event) override; CompositeKey databaseKey(); -protected Q_SLOTS: +protected slots: virtual void openDatabase(); void reject(); -private Q_SLOTS: +private slots: void activatePassword(); void activateKeyFile(); void browseKeyFile(); diff --git a/src/gui/DatabaseRepairWidget.cpp b/src/gui/DatabaseRepairWidget.cpp index e48e0f1f..2b003940 100644 --- a/src/gui/DatabaseRepairWidget.cpp +++ b/src/gui/DatabaseRepairWidget.cpp @@ -50,7 +50,7 @@ void DatabaseRepairWidget::openDatabase() QString errorMsg; if (!key.load(keyFilename, &errorMsg)) { MessageBox::warning(this, tr("Error"), tr("Can't open key file").append(":\n").append(errorMsg)); - Q_EMIT editFinished(false); + emit editFinished(false); return; } masterKey.addKey(key); @@ -62,7 +62,7 @@ void DatabaseRepairWidget::openDatabase() if (!file.open(QIODevice::ReadOnly)) { MessageBox::warning(this, tr("Error"), tr("Unable to open the database.").append("\n") .append(file.errorString())); - Q_EMIT editFinished(false); + emit editFinished(false); return; } if (m_db) { @@ -75,21 +75,21 @@ void DatabaseRepairWidget::openDatabase() switch (repairResult) { case KeePass2Repair::NothingTodo: MessageBox::information(this, tr("Error"), tr("Database opened fine. Nothing to do.")); - Q_EMIT editFinished(false); + emit editFinished(false); return; case KeePass2Repair::UnableToOpen: MessageBox::warning(this, tr("Error"), tr("Unable to open the database.").append("\n") .append(repair.errorString())); - Q_EMIT editFinished(false); + emit editFinished(false); return; case KeePass2Repair::RepairSuccess: m_db = repair.database(); MessageBox::warning(this, tr("Success"), tr("The database has been successfully repaired\nYou can now save it.")); - Q_EMIT editFinished(true); + emit editFinished(true); return; case KeePass2Repair::RepairFailed: MessageBox::warning(this, tr("Error"), tr("Unable to repair the database.")); - Q_EMIT editFinished(false); + emit editFinished(false); return; } } @@ -97,9 +97,9 @@ void DatabaseRepairWidget::openDatabase() void DatabaseRepairWidget::processEditFinished(bool result) { if (result) { - Q_EMIT success(); + emit success(); } else { - Q_EMIT error(); + emit error(); } } diff --git a/src/gui/DatabaseRepairWidget.h b/src/gui/DatabaseRepairWidget.h index 6775d2dc..67b48ce5 100644 --- a/src/gui/DatabaseRepairWidget.h +++ b/src/gui/DatabaseRepairWidget.h @@ -27,14 +27,14 @@ class DatabaseRepairWidget : public DatabaseOpenWidget public: explicit DatabaseRepairWidget(QWidget* parent = nullptr); -Q_SIGNALS: +signals: void success(); void error(); protected: void openDatabase() override; -private Q_SLOTS: +private slots: void processEditFinished(bool result); }; diff --git a/src/gui/DatabaseSettingsWidget.cpp b/src/gui/DatabaseSettingsWidget.cpp index 59a3c5c3..7c51edfd 100644 --- a/src/gui/DatabaseSettingsWidget.cpp +++ b/src/gui/DatabaseSettingsWidget.cpp @@ -124,12 +124,12 @@ void DatabaseSettingsWidget::save() truncateHistories(); } - Q_EMIT editFinished(true); + emit editFinished(true); } void DatabaseSettingsWidget::reject() { - Q_EMIT editFinished(false); + emit editFinished(false); } void DatabaseSettingsWidget::transformRoundsBenchmark() diff --git a/src/gui/DatabaseSettingsWidget.h b/src/gui/DatabaseSettingsWidget.h index 040e0dbe..733b32f8 100644 --- a/src/gui/DatabaseSettingsWidget.h +++ b/src/gui/DatabaseSettingsWidget.h @@ -38,10 +38,10 @@ public: void load(Database* db); -Q_SIGNALS: +signals: void editFinished(bool accepted); -private Q_SLOTS: +private slots: void save(); void reject(); void transformRoundsBenchmark(); diff --git a/src/gui/DatabaseTabWidget.cpp b/src/gui/DatabaseTabWidget.cpp index 910e9489..d679ce29 100644 --- a/src/gui/DatabaseTabWidget.cpp +++ b/src/gui/DatabaseTabWidget.cpp @@ -120,7 +120,7 @@ void DatabaseTabWidget::openDatabase(const QString& fileName, const QString& pw, QFileInfo fileInfo(fileName); QString canonicalFilePath = fileInfo.canonicalFilePath(); if (canonicalFilePath.isEmpty()) { - Q_EMIT messageGlobal(tr("File not found!"), MessageWidget::Error); + emit messageGlobal(tr("File not found!"), MessageWidget::Error); return; } @@ -141,7 +141,7 @@ void DatabaseTabWidget::openDatabase(const QString& fileName, const QString& pw, if (!file.open(QIODevice::ReadWrite)) { if (!file.open(QIODevice::ReadOnly)) { // can't open - Q_EMIT messageGlobal( + emit messageGlobal( tr("Unable to open the database.").append("\n").append(file.errorString()), MessageWidget::Error); return; } @@ -198,7 +198,7 @@ void DatabaseTabWidget::openDatabase(const QString& fileName, const QString& pw, insertDatabase(db, dbStruct); if (dbStruct.readOnly) { - Q_EMIT messageTab(tr("File opened in read only mode."), MessageWidget::Warning); + emit messageTab(tr("File opened in read only mode."), MessageWidget::Warning); } updateLastDatabases(dbStruct.filePath); @@ -209,7 +209,7 @@ void DatabaseTabWidget::openDatabase(const QString& fileName, const QString& pw, else { dbStruct.dbWidget->switchToOpenDatabase(dbStruct.filePath); } - Q_EMIT messageDismissGlobal(); + emit messageDismissGlobal(); } void DatabaseTabWidget::mergeDatabase() @@ -314,7 +314,7 @@ void DatabaseTabWidget::deleteDatabase(Database* db) delete db; if (emitDatabaseWithFileClosed) { - Q_EMIT databaseWithFileClosed(filePath); + emit databaseWithFileClosed(filePath); } } @@ -340,7 +340,7 @@ bool DatabaseTabWidget::saveDatabase(Database* db) // write the database to the file m_writer.writeDatabase(&saveFile, db); if (m_writer.hasError()) { - Q_EMIT messageTab(tr("Writing the database failed.").append("\n") + emit messageTab(tr("Writing the database failed.").append("\n") .append(m_writer.errorString()), MessageWidget::Error); return false; } @@ -350,17 +350,17 @@ bool DatabaseTabWidget::saveDatabase(Database* db) dbStruct.modified = false; dbStruct.dbWidget->databaseSaved(); updateTabName(db); - Q_EMIT messageDismissTab(); + emit messageDismissTab(); return true; } else { - Q_EMIT messageTab(tr("Writing the database failed.").append("\n") + emit messageTab(tr("Writing the database failed.").append("\n") .append(saveFile.errorString()), MessageWidget::Error); return false; } } else { - Q_EMIT messageTab(tr("Writing the database failed.").append("\n") + emit messageTab(tr("Writing the database failed.").append("\n") .append(saveFile.errorString()), MessageWidget::Error); return false; } @@ -503,7 +503,7 @@ void DatabaseTabWidget::exportToCsv() CsvExporter csvExporter; if (!csvExporter.exportDatabase(fileName, db)) { - Q_EMIT messageGlobal( + emit messageGlobal( tr("Writing the CSV file failed.").append("\n") .append(csvExporter.errorString()), MessageWidget::Error); } @@ -565,7 +565,7 @@ void DatabaseTabWidget::updateTabName(Database* db) } setTabText(index, tabName); - Q_EMIT tabNameChanged(); + emit tabNameChanged(); } void DatabaseTabWidget::updateTabNameFromDbSender() @@ -745,7 +745,7 @@ void DatabaseTabWidget::lockDatabases() // database has changed so we can't use the db variable anymore updateTabName(dbWidget->database()); - Q_EMIT databaseLocked(dbWidget); + emit databaseLocked(dbWidget); } } @@ -803,12 +803,12 @@ void DatabaseTabWidget::changeDatabase(Database* newDb, bool unsavedChanges) void DatabaseTabWidget::emitActivateDatabaseChanged() { - Q_EMIT activateDatabaseChanged(currentDatabaseWidget()); + emit activateDatabaseChanged(currentDatabaseWidget()); } void DatabaseTabWidget::emitDatabaseUnlockedFromDbWidgetSender() { - Q_EMIT databaseUnlocked(static_cast(sender())); + emit databaseUnlocked(static_cast(sender())); } void DatabaseTabWidget::connectDatabase(Database* newDb, Database* oldDb) diff --git a/src/gui/DatabaseTabWidget.h b/src/gui/DatabaseTabWidget.h index 8f01a987..d4955c6e 100644 --- a/src/gui/DatabaseTabWidget.h +++ b/src/gui/DatabaseTabWidget.h @@ -63,7 +63,7 @@ public: static const int LastDatabasesCount; -public Q_SLOTS: +public slots: void newDatabase(); void openDatabase(); void mergeDatabase(); @@ -80,7 +80,7 @@ public Q_SLOTS: void performGlobalAutoType(); void lockDatabases(); -Q_SIGNALS: +signals: void tabNameChanged(); void databaseWithFileClosed(QString filePath); void activateDatabaseChanged(DatabaseWidget* dbWidget); @@ -91,7 +91,7 @@ Q_SIGNALS: void messageDismissGlobal(); void messageDismissTab(); -private Q_SLOTS: +private slots: void updateTabName(Database* db); void updateTabNameFromDbSender(); void updateTabNameFromDbWidgetSender(); diff --git a/src/gui/DatabaseWidget.cpp b/src/gui/DatabaseWidget.cpp index 1cb1882d..f68fecdc 100644 --- a/src/gui/DatabaseWidget.cpp +++ b/src/gui/DatabaseWidget.cpp @@ -263,7 +263,7 @@ void DatabaseWidget::clearAllWidgets() void DatabaseWidget::emitCurrentModeChanged() { - Q_EMIT currentModeChanged(currentMode()); + emit currentModeChanged(currentMode()); } Database* DatabaseWidget::database() @@ -309,7 +309,7 @@ void DatabaseWidget::replaceDatabase(Database* db) Database* oldDb = m_db; m_db = db; m_groupView->changeDatabase(m_db); - Q_EMIT databaseChanged(m_db, m_databaseModified); + emit databaseChanged(m_db, m_databaseModified); delete oldDb; } @@ -700,7 +700,7 @@ void DatabaseWidget::updateMasterKey(bool accepted) } } else if (!m_db->hasKey()) { - Q_EMIT closeRequest(); + emit closeRequest(); return; } @@ -712,7 +712,7 @@ void DatabaseWidget::openDatabase(bool accepted) if (accepted) { replaceDatabase(static_cast(sender())->database()); setCurrentWidget(m_mainWidget); - Q_EMIT unlockedDatabase(); + emit unlockedDatabase(); // We won't need those anymore and KeePass1OpenWidget closes // the file in its dtor. @@ -727,7 +727,7 @@ void DatabaseWidget::openDatabase(bool accepted) if (m_databaseOpenWidget->database()) { delete m_databaseOpenWidget->database(); } - Q_EMIT closeRequest(); + emit closeRequest(); } } @@ -750,13 +750,13 @@ void DatabaseWidget::mergeDatabase(bool accepted) } setCurrentWidget(m_mainWidget); - Q_EMIT databaseMerged(m_db); + emit databaseMerged(m_db); } void DatabaseWidget::unlockDatabase(bool accepted) { if (!accepted) { - Q_EMIT closeRequest(); + emit closeRequest(); return; } @@ -775,7 +775,7 @@ void DatabaseWidget::unlockDatabase(bool accepted) setCurrentWidget(m_mainWidget); m_unlockDatabaseWidget->clearForms(); - Q_EMIT unlockedDatabase(); + emit unlockedDatabase(); if (sender() == m_unlockDatabaseDialog) { QList dbList; @@ -888,7 +888,7 @@ void DatabaseWidget::search(const QString& searchtext) return; } - Q_EMIT searchModeAboutToActivate(); + emit searchModeAboutToActivate(); Qt::CaseSensitivity caseSensitive = m_searchCaseSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive; @@ -907,7 +907,7 @@ void DatabaseWidget::search(const QString& searchtext) m_searchingLabel->setVisible(true); - Q_EMIT searchModeActivated(); + emit searchModeActivated(); } void DatabaseWidget::setSearchCaseSensitive(bool state) @@ -934,12 +934,12 @@ void DatabaseWidget::endSearch() { if (isInSearchMode()) { - Q_EMIT listModeAboutToActivate(); + emit listModeAboutToActivate(); // Show the normal entry view of the current group m_entryView->setGroup(currentGroup()); - Q_EMIT listModeActivated(); + emit listModeActivated(); } m_searchingLabel->setVisible(false); @@ -950,12 +950,12 @@ void DatabaseWidget::endSearch() void DatabaseWidget::emitGroupContextMenuRequested(const QPoint& pos) { - Q_EMIT groupContextMenuRequested(m_groupView->viewport()->mapToGlobal(pos)); + emit groupContextMenuRequested(m_groupView->viewport()->mapToGlobal(pos)); } void DatabaseWidget::emitEntryContextMenuRequested(const QPoint& pos) { - Q_EMIT entryContextMenuRequested(m_entryView->viewport()->mapToGlobal(pos)); + emit entryContextMenuRequested(m_entryView->viewport()->mapToGlobal(pos)); } bool DatabaseWidget::dbHasKey() const diff --git a/src/gui/DatabaseWidget.h b/src/gui/DatabaseWidget.h index 66ece053..3781af49 100644 --- a/src/gui/DatabaseWidget.h +++ b/src/gui/DatabaseWidget.h @@ -101,7 +101,7 @@ public: void ignoreNextAutoreload(); void refreshSearch(); -Q_SIGNALS: +signals: void closeRequest(); void currentModeChanged(DatabaseWidget::Mode mode); void groupChanged(); @@ -119,7 +119,7 @@ Q_SIGNALS: void entryColumnSizesChanged(); void updateSearch(QString text); -public Q_SLOTS: +public slots: void createEntry(); void cloneEntry(); void deleteEntries(); @@ -157,7 +157,7 @@ public Q_SLOTS: void showMessage(const QString& text, MessageWidget::MessageType type); void hideMessage(); -private Q_SLOTS: +private slots: void entryActivationSignalReceived(Entry* entry, EntryModel::ModelColumn column); void switchBackToEntryEdit(); void switchToHistoryView(Entry* entry); diff --git a/src/gui/DatabaseWidgetStateSync.h b/src/gui/DatabaseWidgetStateSync.h index a4861179..96ecd104 100644 --- a/src/gui/DatabaseWidgetStateSync.h +++ b/src/gui/DatabaseWidgetStateSync.h @@ -29,12 +29,12 @@ public: explicit DatabaseWidgetStateSync(QObject* parent = nullptr); ~DatabaseWidgetStateSync(); -public Q_SLOTS: +public slots: void setActive(DatabaseWidget* dbWidget); void restoreListView(); void restoreSearchView(); -private Q_SLOTS: +private slots: void blockUpdates(); void updateSplitterSizes(); void updateColumnSizes(); diff --git a/src/gui/DragTabBar.h b/src/gui/DragTabBar.h index a6117a04..38de10da 100644 --- a/src/gui/DragTabBar.h +++ b/src/gui/DragTabBar.h @@ -34,7 +34,7 @@ protected: void dropEvent(QDropEvent* event) override; void tabLayoutChange() override; -private Q_SLOTS: +private slots: void dragSwitchTab(); private: diff --git a/src/gui/EditWidgetIcons.cpp b/src/gui/EditWidgetIcons.cpp index d789eb49..7b46728c 100644 --- a/src/gui/EditWidgetIcons.cpp +++ b/src/gui/EditWidgetIcons.cpp @@ -276,7 +276,7 @@ void EditWidgetIcons::addCustomIcon() m_ui->customIconsView->setCurrentIndex(index); } else { - Q_EMIT messageEditEntry(tr("Can't read icon"), MessageWidget::Error); + emit messageEditEntry(tr("Can't read icon"), MessageWidget::Error); } } } diff --git a/src/gui/EditWidgetIcons.h b/src/gui/EditWidgetIcons.h index b0ff6c6c..745914bc 100644 --- a/src/gui/EditWidgetIcons.h +++ b/src/gui/EditWidgetIcons.h @@ -63,14 +63,14 @@ public: void reset(); void load(const Uuid& currentUuid, Database* database, const IconStruct& iconStruct, const QString& url = ""); -public Q_SLOTS: +public slots: void setUrl(const QString& url); -Q_SIGNALS: +signals: void messageEditEntry(QString, MessageWidget::MessageType); void messageEditEntryDismiss(); -private Q_SLOTS: +private slots: void downloadFavicon(); #ifdef WITH_XC_HTTP void fetchFavicon(const QUrl& url); diff --git a/src/gui/KMessageWidget.h b/src/gui/KMessageWidget.h index 4398e0f9..d47e78f9 100644 --- a/src/gui/KMessageWidget.h +++ b/src/gui/KMessageWidget.h @@ -224,7 +224,7 @@ public: */ bool isShowAnimationRunning() const; -public Q_SLOTS: +public slots: /** * Set the text of the message widget to @p text. * If the message widget is already visible, the text changes on the fly. @@ -277,7 +277,7 @@ public Q_SLOTS: */ void setIcon(const QIcon &icon); -Q_SIGNALS: +signals: /** * This signal is emitted when the user clicks a link in the text label. * The URL referred to by the href anchor is passed in contents. diff --git a/src/gui/KeePass1OpenWidget.cpp b/src/gui/KeePass1OpenWidget.cpp index b63bbc48..91586424 100644 --- a/src/gui/KeePass1OpenWidget.cpp +++ b/src/gui/KeePass1OpenWidget.cpp @@ -62,7 +62,7 @@ void KeePass1OpenWidget::openDatabase() if (m_db) { m_db->metadata()->setName(QFileInfo(m_filename).completeBaseName()); - Q_EMIT editFinished(true); + emit editFinished(true); } else { m_ui->messageWidget->showMessage(tr("Unable to open the database.").append("\n") diff --git a/src/gui/LineEdit.h b/src/gui/LineEdit.h index f5f05840..1695e855 100644 --- a/src/gui/LineEdit.h +++ b/src/gui/LineEdit.h @@ -34,7 +34,7 @@ public: protected: void resizeEvent(QResizeEvent* event) override; -private Q_SLOTS: +private slots: void updateCloseButton(const QString& text); private: diff --git a/src/gui/MainWindow.h b/src/gui/MainWindow.h index 5c7ccad8..7a314df8 100644 --- a/src/gui/MainWindow.h +++ b/src/gui/MainWindow.h @@ -39,7 +39,7 @@ public: MainWindow(); ~MainWindow(); -public Q_SLOTS: +public slots: void openDatabase(const QString& fileName, const QString& pw = QString(), const QString& keyFile = QString()); void appExit(); @@ -48,7 +48,7 @@ protected: void closeEvent(QCloseEvent* event) override; void changeEvent(QEvent* event) override; -private Q_SLOTS: +private slots: void setMenuActionState(DatabaseWidget::Mode mode = DatabaseWidget::None); void updateWindowTitle(); void showAboutDialog(); diff --git a/src/gui/MessageWidget.h b/src/gui/MessageWidget.h index 34c06743..a6c9425d 100644 --- a/src/gui/MessageWidget.h +++ b/src/gui/MessageWidget.h @@ -27,7 +27,7 @@ class MessageWidget : public KMessageWidget public: explicit MessageWidget(QWidget* parent = 0); -public Q_SLOTS: +public slots: void showMessage(const QString& text, MessageWidget::MessageType type); void hideMessage(); diff --git a/src/gui/PasswordComboBox.h b/src/gui/PasswordComboBox.h index 7c54e278..f7f118ed 100644 --- a/src/gui/PasswordComboBox.h +++ b/src/gui/PasswordComboBox.h @@ -35,7 +35,7 @@ public: void setNumberAlternatives(int alternatives); void showPopup(); -public Q_SLOTS: +public slots: void setEcho(bool echo); private: diff --git a/src/gui/PasswordEdit.cpp b/src/gui/PasswordEdit.cpp index 98a5e2a5..095a4e14 100644 --- a/src/gui/PasswordEdit.cpp +++ b/src/gui/PasswordEdit.cpp @@ -69,7 +69,7 @@ void PasswordEdit::setShowPassword(bool show) } } updateStylesheet(); - Q_EMIT showPasswordChanged(show); + emit showPasswordChanged(show); } bool PasswordEdit::passwordsEqual() const diff --git a/src/gui/PasswordEdit.h b/src/gui/PasswordEdit.h index 994576d2..d527432d 100644 --- a/src/gui/PasswordEdit.h +++ b/src/gui/PasswordEdit.h @@ -31,13 +31,13 @@ public: explicit PasswordEdit(QWidget* parent = nullptr); void enableVerifyMode(PasswordEdit* baseEdit); -public Q_SLOTS: +public slots: void setShowPassword(bool show); -Q_SIGNALS: +signals: void showPasswordChanged(bool show); -private Q_SLOTS: +private slots: void updateStylesheet(); void autocompletePassword(QString password); diff --git a/src/gui/PasswordGeneratorWidget.cpp b/src/gui/PasswordGeneratorWidget.cpp index 4a4b438e..11d50bae 100644 --- a/src/gui/PasswordGeneratorWidget.cpp +++ b/src/gui/PasswordGeneratorWidget.cpp @@ -145,8 +145,8 @@ void PasswordGeneratorWidget::generatePassword() void PasswordGeneratorWidget::applyPassword() { saveSettings(); - Q_EMIT appliedPassword(m_ui->editNewPassword->text()); - Q_EMIT dialogTerminated(); + emit appliedPassword(m_ui->editNewPassword->text()); + emit dialogTerminated(); } void PasswordGeneratorWidget::sliderMoved() diff --git a/src/gui/PasswordGeneratorWidget.h b/src/gui/PasswordGeneratorWidget.h index b8803f85..bfa6d684 100644 --- a/src/gui/PasswordGeneratorWidget.h +++ b/src/gui/PasswordGeneratorWidget.h @@ -43,11 +43,11 @@ public: void setStandaloneMode(bool standalone); void regeneratePassword(); -Q_SIGNALS: +signals: void appliedPassword(const QString& password); void dialogTerminated(); -private Q_SLOTS: +private slots: void applyPassword(); void generatePassword(); void updateApplyEnabled(const QString& password); diff --git a/src/gui/SettingsWidget.cpp b/src/gui/SettingsWidget.cpp index 8aa6982d..62af276e 100644 --- a/src/gui/SettingsWidget.cpp +++ b/src/gui/SettingsWidget.cpp @@ -196,7 +196,7 @@ void SettingsWidget::saveSettings() page.saveSettings(); } - Q_EMIT editFinished(true); + emit editFinished(true); } void SettingsWidget::reject() @@ -206,7 +206,7 @@ void SettingsWidget::reject() autoType()->registerGlobalShortcut(m_globalAutoTypeKey, m_globalAutoTypeModifiers); } - Q_EMIT editFinished(false); + emit editFinished(false); } void SettingsWidget::enableAutoSaveOnExit(bool checked) diff --git a/src/gui/SettingsWidget.h b/src/gui/SettingsWidget.h index e94f4876..7037b2e3 100644 --- a/src/gui/SettingsWidget.h +++ b/src/gui/SettingsWidget.h @@ -45,10 +45,10 @@ public: void addSettingsPage(ISettingsPage * page); void loadSettings(); -Q_SIGNALS: +signals: void editFinished(bool accepted); -private Q_SLOTS: +private slots: void saveSettings(); void reject(); void enableAutoSaveOnExit(bool checked); diff --git a/src/gui/UnlockDatabaseDialog.cpp b/src/gui/UnlockDatabaseDialog.cpp index 67949390..3d002f75 100644 --- a/src/gui/UnlockDatabaseDialog.cpp +++ b/src/gui/UnlockDatabaseDialog.cpp @@ -49,7 +49,7 @@ void UnlockDatabaseDialog::complete(bool r) { if (r) { accept(); - Q_EMIT unlockDone(true); + emit unlockDone(true); } else { reject(); } diff --git a/src/gui/UnlockDatabaseDialog.h b/src/gui/UnlockDatabaseDialog.h index 1ba6d2e0..daf8a0f1 100644 --- a/src/gui/UnlockDatabaseDialog.h +++ b/src/gui/UnlockDatabaseDialog.h @@ -36,10 +36,10 @@ public: void clearForms(); Database* database(); -Q_SIGNALS: +signals: void unlockDone(bool); -public Q_SLOTS: +public slots: void complete(bool r); private: diff --git a/src/gui/WelcomeWidget.cpp b/src/gui/WelcomeWidget.cpp index cb7a1de2..d327ea84 100644 --- a/src/gui/WelcomeWidget.cpp +++ b/src/gui/WelcomeWidget.cpp @@ -64,5 +64,5 @@ void WelcomeWidget::openDatabaseFromFile(QListWidgetItem* item) if (item->text().isEmpty()) { return; } - Q_EMIT openDatabaseFile(item->text()); + emit openDatabaseFile(item->text()); } \ No newline at end of file diff --git a/src/gui/WelcomeWidget.h b/src/gui/WelcomeWidget.h index dbd0d2e2..73c8c4f9 100644 --- a/src/gui/WelcomeWidget.h +++ b/src/gui/WelcomeWidget.h @@ -33,13 +33,13 @@ public: explicit WelcomeWidget(QWidget* parent = nullptr); ~WelcomeWidget(); -Q_SIGNALS: +signals: void newDatabase(); void openDatabase(); void openDatabaseFile(QString); void importKeePass1Database(); -private Q_SLOTS: +private slots: void openDatabaseFromFile(QListWidgetItem* item); private: diff --git a/src/gui/entry/AutoTypeAssociationsModel.cpp b/src/gui/entry/AutoTypeAssociationsModel.cpp index 49f6786b..4a76233b 100644 --- a/src/gui/entry/AutoTypeAssociationsModel.cpp +++ b/src/gui/entry/AutoTypeAssociationsModel.cpp @@ -103,7 +103,7 @@ QVariant AutoTypeAssociationsModel::data(const QModelIndex& index, int role) con void AutoTypeAssociationsModel::associationChange(int i) { - Q_EMIT dataChanged(index(i, 0), index(i, columnCount() - 1)); + emit dataChanged(index(i, 0), index(i, columnCount() - 1)); } void AutoTypeAssociationsModel::associationAboutToAdd(int i) diff --git a/src/gui/entry/AutoTypeAssociationsModel.h b/src/gui/entry/AutoTypeAssociationsModel.h index c75168c3..cef8bc66 100644 --- a/src/gui/entry/AutoTypeAssociationsModel.h +++ b/src/gui/entry/AutoTypeAssociationsModel.h @@ -36,7 +36,7 @@ public: QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; -public Q_SLOTS: +public slots: void associationChange(int i); void associationAboutToAdd(int i); void associationAdd(); diff --git a/src/gui/entry/EditEntryWidget.cpp b/src/gui/entry/EditEntryWidget.cpp index a3032505..4581c91f 100644 --- a/src/gui/entry/EditEntryWidget.cpp +++ b/src/gui/entry/EditEntryWidget.cpp @@ -212,7 +212,7 @@ void EditEntryWidget::emitHistoryEntryActivated(const QModelIndex& index) Q_ASSERT(!m_history); Entry* entry = m_historyModel->entryFromIndex(index); - Q_EMIT historyEntryActivated(entry); + emit historyEntryActivated(entry); } void EditEntryWidget::histEntryActivated(const QModelIndex& index) @@ -407,7 +407,7 @@ void EditEntryWidget::saveEntry() if (m_history) { clear(); hideMessage(); - Q_EMIT editFinished(false); + emit editFinished(false); return; } @@ -442,7 +442,7 @@ void EditEntryWidget::saveEntry() clear(); - Q_EMIT editFinished(true); + emit editFinished(true); } void EditEntryWidget::updateEntryData(Entry* entry) const @@ -487,7 +487,7 @@ void EditEntryWidget::cancel() if (m_history) { clear(); hideMessage(); - Q_EMIT editFinished(false); + emit editFinished(false); return; } @@ -498,7 +498,7 @@ void EditEntryWidget::cancel() clear(); - Q_EMIT editFinished(false); + emit editFinished(false); } void EditEntryWidget::clear() diff --git a/src/gui/entry/EditEntryWidget.h b/src/gui/entry/EditEntryWidget.h index 270542e8..4027dd11 100644 --- a/src/gui/entry/EditEntryWidget.h +++ b/src/gui/entry/EditEntryWidget.h @@ -63,11 +63,11 @@ public: void clear(); bool hasBeenModified() const; -Q_SIGNALS: +signals: void editFinished(bool accepted); void historyEntryActivated(Entry* entry); -private Q_SLOTS: +private slots: void saveEntry(); void cancel(); void togglePasswordGeneratorButton(bool checked); diff --git a/src/gui/entry/EntryAttachmentsModel.cpp b/src/gui/entry/EntryAttachmentsModel.cpp index 39ed69f1..08264138 100644 --- a/src/gui/entry/EntryAttachmentsModel.cpp +++ b/src/gui/entry/EntryAttachmentsModel.cpp @@ -97,7 +97,7 @@ QString EntryAttachmentsModel::keyByIndex(const QModelIndex& index) const void EntryAttachmentsModel::attachmentChange(const QString& key) { int row = m_entryAttachments->keys().indexOf(key); - Q_EMIT dataChanged(index(row, 0), index(row, columnCount()-1)); + emit dataChanged(index(row, 0), index(row, columnCount()-1)); } void EntryAttachmentsModel::attachmentAboutToAdd(const QString& key) diff --git a/src/gui/entry/EntryAttachmentsModel.h b/src/gui/entry/EntryAttachmentsModel.h index c2e238ae..6abcdc2e 100644 --- a/src/gui/entry/EntryAttachmentsModel.h +++ b/src/gui/entry/EntryAttachmentsModel.h @@ -34,7 +34,7 @@ public: QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; QString keyByIndex(const QModelIndex& index) const; -private Q_SLOTS: +private slots: void attachmentChange(const QString& key); void attachmentAboutToAdd(const QString& key); void attachmentAdd(); diff --git a/src/gui/entry/EntryAttributesModel.cpp b/src/gui/entry/EntryAttributesModel.cpp index b22380ae..1b1eab22 100644 --- a/src/gui/entry/EntryAttributesModel.cpp +++ b/src/gui/entry/EntryAttributesModel.cpp @@ -147,7 +147,7 @@ void EntryAttributesModel::attributeChange(const QString& key) { int row = m_attributes.indexOf(key); Q_ASSERT(row != -1); - Q_EMIT dataChanged(index(row, 0), index(row, columnCount()-1)); + emit dataChanged(index(row, 0), index(row, columnCount()-1)); } void EntryAttributesModel::attributeAboutToAdd(const QString& key) @@ -213,7 +213,7 @@ void EntryAttributesModel::attributeRename(const QString& oldKey, const QString& m_nextRenameDataChange = false; QModelIndex keyIndex = index(m_attributes.indexOf(newKey), 0); - Q_EMIT dataChanged(keyIndex, keyIndex); + emit dataChanged(keyIndex, keyIndex); } } diff --git a/src/gui/entry/EntryAttributesModel.h b/src/gui/entry/EntryAttributesModel.h index 1eec8bff..7d613c1f 100644 --- a/src/gui/entry/EntryAttributesModel.h +++ b/src/gui/entry/EntryAttributesModel.h @@ -38,7 +38,7 @@ public: QModelIndex indexByKey(const QString& key) const; QString keyByIndex(const QModelIndex& index) const; -private Q_SLOTS: +private slots: void attributeChange(const QString& key); void attributeAboutToAdd(const QString& key); void attributeAdd(); diff --git a/src/gui/entry/EntryModel.cpp b/src/gui/entry/EntryModel.cpp index 323a55c8..b28eaed4 100644 --- a/src/gui/entry/EntryModel.cpp +++ b/src/gui/entry/EntryModel.cpp @@ -64,7 +64,7 @@ void EntryModel::setGroup(Group* group) makeConnections(group); endResetModel(); - Q_EMIT switchedToGroupMode(); + emit switchedToGroupMode(); } void EntryModel::setEntryList(const QList& entries) @@ -101,7 +101,7 @@ void EntryModel::setEntryList(const QList& entries) } endResetModel(); - Q_EMIT switchedToEntryListMode(); + emit switchedToEntryListMode(); } int EntryModel::rowCount(const QModelIndex& parent) const @@ -315,7 +315,7 @@ void EntryModel::entryRemoved() void EntryModel::entryDataChanged(Entry* entry) { int row = m_entries.indexOf(entry); - Q_EMIT dataChanged(index(row, 0), index(row, columnCount()-1)); + emit dataChanged(index(row, 0), index(row, columnCount()-1)); } void EntryModel::severConnections() diff --git a/src/gui/entry/EntryModel.h b/src/gui/entry/EntryModel.h index 0183c47b..d12982d8 100644 --- a/src/gui/entry/EntryModel.h +++ b/src/gui/entry/EntryModel.h @@ -52,14 +52,14 @@ public: void setEntryList(const QList& entries); -Q_SIGNALS: +signals: void switchedToEntryListMode(); void switchedToGroupMode(); -public Q_SLOTS: +public slots: void setGroup(Group* group); -private Q_SLOTS: +private slots: void entryAboutToAdd(Entry* entry); void entryAdded(Entry* entry); void entryAboutToRemove(Entry* entry); diff --git a/src/gui/entry/EntryView.cpp b/src/gui/entry/EntryView.cpp index 31fae3e5..1bdd4fbc 100644 --- a/src/gui/entry/EntryView.cpp +++ b/src/gui/entry/EntryView.cpp @@ -57,7 +57,7 @@ void EntryView::keyPressEvent(QKeyEvent* event) emitEntryActivated(currentIndex()); #ifdef Q_OS_MAC // Pressing return does not emit the QTreeView::activated signal on mac os - Q_EMIT activated(currentIndex()); + emit activated(currentIndex()); #endif } @@ -83,7 +83,7 @@ void EntryView::setFirstEntryActive() setCurrentEntry(m_model->entryFromIndex(index)); } else { - Q_EMIT entrySelectionChanged(); + emit entrySelectionChanged(); } } @@ -96,7 +96,7 @@ void EntryView::emitEntryActivated(const QModelIndex& index) { Entry* entry = entryFromIndex(index); - Q_EMIT entryActivated(entry, static_cast(m_sortModel->mapToSource(index).column())); + emit entryActivated(entry, static_cast(m_sortModel->mapToSource(index).column())); } void EntryView::setModel(QAbstractItemModel* model) diff --git a/src/gui/entry/EntryView.h b/src/gui/entry/EntryView.h index fb9e3566..6a545f62 100644 --- a/src/gui/entry/EntryView.h +++ b/src/gui/entry/EntryView.h @@ -42,17 +42,17 @@ public: int numberOfSelectedEntries(); void setFirstEntryActive(); -public Q_SLOTS: +public slots: void setGroup(Group* group); -Q_SIGNALS: +signals: void entryActivated(Entry* entry, EntryModel::ModelColumn column); void entrySelectionChanged(); protected: void keyPressEvent(QKeyEvent* event) override; -private Q_SLOTS: +private slots: void emitEntryActivated(const QModelIndex& index); void switchToEntryListMode(); void switchToGroupMode(); diff --git a/src/gui/group/EditGroupWidget.cpp b/src/gui/group/EditGroupWidget.cpp index ac4e4ce9..4f2e9fec 100644 --- a/src/gui/group/EditGroupWidget.cpp +++ b/src/gui/group/EditGroupWidget.cpp @@ -130,7 +130,7 @@ void EditGroupWidget::save() } clear(); - Q_EMIT editFinished(true); + emit editFinished(true); } void EditGroupWidget::cancel() @@ -141,7 +141,7 @@ void EditGroupWidget::cancel() } clear(); - Q_EMIT editFinished(false); + emit editFinished(false); } void EditGroupWidget::clear() diff --git a/src/gui/group/EditGroupWidget.h b/src/gui/group/EditGroupWidget.h index 606cc77b..39f2c09b 100644 --- a/src/gui/group/EditGroupWidget.h +++ b/src/gui/group/EditGroupWidget.h @@ -43,12 +43,12 @@ public: void loadGroup(Group* group, bool create, Database* database); void clear(); -Q_SIGNALS: +signals: void editFinished(bool accepted); void messageEditEntry(QString, MessageWidget::MessageType); void messageEditEntryDismiss(); -private Q_SLOTS: +private slots: void save(); void cancel(); diff --git a/src/gui/group/GroupModel.cpp b/src/gui/group/GroupModel.cpp index 5aafc1a7..87eacf27 100644 --- a/src/gui/group/GroupModel.cpp +++ b/src/gui/group/GroupModel.cpp @@ -365,7 +365,7 @@ QMimeData* GroupModel::mimeData(const QModelIndexList& indexes) const void GroupModel::groupDataChanged(Group* group) { QModelIndex ix = index(group); - Q_EMIT dataChanged(ix, ix); + emit dataChanged(ix, ix); } void GroupModel::groupAboutToRemove(Group* group) diff --git a/src/gui/group/GroupModel.h b/src/gui/group/GroupModel.h index 0ef0ba99..899aa3fd 100644 --- a/src/gui/group/GroupModel.h +++ b/src/gui/group/GroupModel.h @@ -49,7 +49,7 @@ public: private: QModelIndex parent(Group* group) const; -private Q_SLOTS: +private slots: void groupDataChanged(Group* group); void groupAboutToRemove(Group* group); void groupRemoved(); diff --git a/src/gui/group/GroupView.cpp b/src/gui/group/GroupView.cpp index 18f7de80..e9649e44 100644 --- a/src/gui/group/GroupView.cpp +++ b/src/gui/group/GroupView.cpp @@ -112,7 +112,7 @@ void GroupView::expandGroup(Group* group, bool expand) void GroupView::emitGroupChanged(const QModelIndex& index) { - Q_EMIT groupChanged(m_model->groupFromIndex(index)); + emit groupChanged(m_model->groupFromIndex(index)); } void GroupView::setModel(QAbstractItemModel* model) @@ -123,7 +123,7 @@ void GroupView::setModel(QAbstractItemModel* model) void GroupView::emitGroupChanged() { - Q_EMIT groupChanged(currentGroup()); + emit groupChanged(currentGroup()); } void GroupView::syncExpandedState(const QModelIndex& parent, int start, int end) diff --git a/src/gui/group/GroupView.h b/src/gui/group/GroupView.h index 69ca8281..eaa29072 100644 --- a/src/gui/group/GroupView.h +++ b/src/gui/group/GroupView.h @@ -36,10 +36,10 @@ public: void setCurrentGroup(Group* group); void expandGroup(Group* group, bool expand = true); -Q_SIGNALS: +signals: void groupChanged(Group* group); -private Q_SLOTS: +private slots: void expandedChanged(const QModelIndex& index); void emitGroupChanged(const QModelIndex& index); void emitGroupChanged(); diff --git a/src/http/HttpPasswordGeneratorWidget.h b/src/http/HttpPasswordGeneratorWidget.h index f8e35c23..f9907600 100644 --- a/src/http/HttpPasswordGeneratorWidget.h +++ b/src/http/HttpPasswordGeneratorWidget.h @@ -38,7 +38,7 @@ public: void saveSettings(); void reset(); -private Q_SLOTS: +private slots: void sliderMoved(); void spinBoxChanged(); diff --git a/src/http/OptionDialog.h b/src/http/OptionDialog.h index 1b181915..ad535fdb 100644 --- a/src/http/OptionDialog.h +++ b/src/http/OptionDialog.h @@ -29,11 +29,11 @@ public: explicit OptionDialog(QWidget *parent = nullptr); ~OptionDialog(); -public Q_SLOTS: +public slots: void loadSettings(); void saveSettings(); -Q_SIGNALS: +signals: void removeSharedEncryptionKeys(); void removeStoredPermissions(); diff --git a/src/http/Service.h b/src/http/Service.h index 6452d605..b6ee5bea 100644 --- a/src/http/Service.h +++ b/src/http/Service.h @@ -38,7 +38,7 @@ public: virtual void updateEntry(const QString& id, const QString& uuid, const QString& login, const QString& password, const QString& url); virtual QString generatePassword(); -public Q_SLOTS: +public slots: void removeSharedEncryptionKeys(); void removeStoredPermissions(); diff --git a/src/streams/LayeredStream.h b/src/streams/LayeredStream.h index 8586b413..4ca7aba9 100644 --- a/src/streams/LayeredStream.h +++ b/src/streams/LayeredStream.h @@ -37,7 +37,7 @@ protected: QIODevice* const m_baseDevice; -private Q_SLOTS: +private slots: void closeStream(); }; diff --git a/tests/TestAutoType.h b/tests/TestAutoType.h index c585fec2..569bc8c7 100644 --- a/tests/TestAutoType.h +++ b/tests/TestAutoType.h @@ -31,7 +31,7 @@ class TestAutoType : public QObject { Q_OBJECT -private Q_SLOTS: +private slots: void initTestCase(); void init(); void cleanup(); diff --git a/tests/TestCryptoHash.h b/tests/TestCryptoHash.h index 05700f34..d31501ba 100644 --- a/tests/TestCryptoHash.h +++ b/tests/TestCryptoHash.h @@ -24,7 +24,7 @@ class TestCryptoHash : public QObject { Q_OBJECT -private Q_SLOTS: +private slots: void initTestCase(); void test(); }; diff --git a/tests/TestCsvExporter.h b/tests/TestCsvExporter.h index a8cfe7f2..39597f75 100644 --- a/tests/TestCsvExporter.h +++ b/tests/TestCsvExporter.h @@ -31,7 +31,7 @@ class TestCsvExporter : public QObject public: static const QString ExpectedHeaderLine; -private Q_SLOTS: +private slots: void init(); void initTestCase(); void cleanup(); diff --git a/tests/TestDeletedObjects.h b/tests/TestDeletedObjects.h index 27b70cce..d9645209 100644 --- a/tests/TestDeletedObjects.h +++ b/tests/TestDeletedObjects.h @@ -29,7 +29,7 @@ class TestDeletedObjects : public QObject private: void createAndDelete(Database* db, int delObjectsSize); -private Q_SLOTS: +private slots: void initTestCase(); void testDeletedObjectsFromFile(); void testDeletedObjectsFromNewDb(); diff --git a/tests/TestEntry.h b/tests/TestEntry.h index ed772d50..0c97c0b9 100644 --- a/tests/TestEntry.h +++ b/tests/TestEntry.h @@ -26,7 +26,7 @@ class TestEntry : public QObject { Q_OBJECT -private Q_SLOTS: +private slots: void initTestCase(); void testHistoryItemDeletion(); void testCopyDataFrom(); diff --git a/tests/TestEntryModel.h b/tests/TestEntryModel.h index 778392f2..df80331e 100644 --- a/tests/TestEntryModel.h +++ b/tests/TestEntryModel.h @@ -24,7 +24,7 @@ class TestEntryModel : public QObject { Q_OBJECT -private Q_SLOTS: +private slots: void initTestCase(); void test(); void testAttachmentsModel(); diff --git a/tests/TestEntrySearcher.h b/tests/TestEntrySearcher.h index 7c45451d..3965c22e 100644 --- a/tests/TestEntrySearcher.h +++ b/tests/TestEntrySearcher.h @@ -28,7 +28,7 @@ class TestEntrySearcher : public QObject { Q_OBJECT -private Q_SLOTS: +private slots: void initTestCase(); void cleanupTestCase(); diff --git a/tests/TestExporter.h b/tests/TestExporter.h index 15f9a7c3..8c994525 100644 --- a/tests/TestExporter.h +++ b/tests/TestExporter.h @@ -25,7 +25,7 @@ class TestExporter : public QObject { Q_OBJECT -private Q_SLOTS: +private slots: void initTestCase(); void testToDbExporter(); }; diff --git a/tests/TestGroup.h b/tests/TestGroup.h index 4a891ae6..c9ed8f08 100644 --- a/tests/TestGroup.h +++ b/tests/TestGroup.h @@ -25,7 +25,7 @@ class TestGroup : public QObject { Q_OBJECT -private Q_SLOTS: +private slots: void initTestCase(); void testParenting(); void testSignals(); diff --git a/tests/TestGroupModel.h b/tests/TestGroupModel.h index 093af9e0..1b5c0ab4 100644 --- a/tests/TestGroupModel.h +++ b/tests/TestGroupModel.h @@ -24,7 +24,7 @@ class TestGroupModel : public QObject { Q_OBJECT -private Q_SLOTS: +private slots: void initTestCase(); void test(); }; diff --git a/tests/TestHashedBlockStream.h b/tests/TestHashedBlockStream.h index 9aeac141..6c36f8e6 100644 --- a/tests/TestHashedBlockStream.h +++ b/tests/TestHashedBlockStream.h @@ -24,7 +24,7 @@ class TestHashedBlockStream : public QObject { Q_OBJECT -private Q_SLOTS: +private slots: void initTestCase(); void testWriteRead(); void testReset(); diff --git a/tests/TestKeePass1Reader.h b/tests/TestKeePass1Reader.h index 20acd4bb..9a5ab9e4 100644 --- a/tests/TestKeePass1Reader.h +++ b/tests/TestKeePass1Reader.h @@ -27,7 +27,7 @@ class TestKeePass1Reader : public QObject { Q_OBJECT -private Q_SLOTS: +private slots: void initTestCase(); void testBasic(); void testMasterKey(); diff --git a/tests/TestKeePass2RandomStream.h b/tests/TestKeePass2RandomStream.h index b001a05a..967ed9c9 100644 --- a/tests/TestKeePass2RandomStream.h +++ b/tests/TestKeePass2RandomStream.h @@ -24,7 +24,7 @@ class TestKeePass2RandomStream : public QObject { Q_OBJECT -private Q_SLOTS: +private slots: void initTestCase(); void test(); }; diff --git a/tests/TestKeePass2Reader.h b/tests/TestKeePass2Reader.h index 6f090de3..76ffe029 100644 --- a/tests/TestKeePass2Reader.h +++ b/tests/TestKeePass2Reader.h @@ -24,7 +24,7 @@ class TestKeePass2Reader : public QObject { Q_OBJECT -private Q_SLOTS: +private slots: void initTestCase(); void testNonAscii(); void testCompressed(); diff --git a/tests/TestKeePass2Writer.h b/tests/TestKeePass2Writer.h index 82288382..36a51dce 100644 --- a/tests/TestKeePass2Writer.h +++ b/tests/TestKeePass2Writer.h @@ -26,7 +26,7 @@ class TestKeePass2Writer : public QObject { Q_OBJECT -private Q_SLOTS: +private slots: void initTestCase(); void testBasic(); void testProtectedAttributes(); diff --git a/tests/TestKeePass2XmlReader.h b/tests/TestKeePass2XmlReader.h index ff83e259..628964b4 100644 --- a/tests/TestKeePass2XmlReader.h +++ b/tests/TestKeePass2XmlReader.h @@ -27,7 +27,7 @@ class TestKeePass2XmlReader : public QObject { Q_OBJECT -private Q_SLOTS: +private slots: void initTestCase(); void testMetadata(); void testCustomIcons(); diff --git a/tests/TestKeys.h b/tests/TestKeys.h index a6d0b7e1..683f0768 100644 --- a/tests/TestKeys.h +++ b/tests/TestKeys.h @@ -24,7 +24,7 @@ class TestKeys : public QObject { Q_OBJECT -private Q_SLOTS: +private slots: void initTestCase(); void testComposite(); void testCompositeKeyReadFromLine(); diff --git a/tests/TestModified.h b/tests/TestModified.h index ee598add..518bea7c 100644 --- a/tests/TestModified.h +++ b/tests/TestModified.h @@ -24,7 +24,7 @@ class TestModified : public QObject { Q_OBJECT -private Q_SLOTS: +private slots: void initTestCase(); void testSignals(); void testGroupSets(); diff --git a/tests/TestRandom.h b/tests/TestRandom.h index c879f945..323d6b61 100644 --- a/tests/TestRandom.h +++ b/tests/TestRandom.h @@ -38,7 +38,7 @@ class TestRandom : public QObject { Q_OBJECT -private Q_SLOTS: +private slots: void initTestCase(); void testUInt(); void testUIntRange(); diff --git a/tests/TestSymmetricCipher.h b/tests/TestSymmetricCipher.h index 17fa77a4..8259af62 100644 --- a/tests/TestSymmetricCipher.h +++ b/tests/TestSymmetricCipher.h @@ -24,7 +24,7 @@ class TestSymmetricCipher : public QObject { Q_OBJECT -private Q_SLOTS: +private slots: void initTestCase(); void testAes256CbcEncryption(); void testAes256CbcDecryption(); diff --git a/tests/TestWildcardMatcher.h b/tests/TestWildcardMatcher.h index c241c755..e2377093 100644 --- a/tests/TestWildcardMatcher.h +++ b/tests/TestWildcardMatcher.h @@ -26,7 +26,7 @@ class TestWildcardMatcher : public QObject { Q_OBJECT -private Q_SLOTS: +private slots: void testMatcher(); void testMatcher_data(); diff --git a/tests/gui/TestGui.h b/tests/gui/TestGui.h index c2e0e372..1ae29700 100644 --- a/tests/gui/TestGui.h +++ b/tests/gui/TestGui.h @@ -33,7 +33,7 @@ class TestGui : public QObject { Q_OBJECT -private Q_SLOTS: +private slots: void initTestCase(); void init(); void cleanup(); diff --git a/tests/gui/TestGuiPixmaps.h b/tests/gui/TestGuiPixmaps.h index ef0b664b..6e649c0f 100644 --- a/tests/gui/TestGuiPixmaps.h +++ b/tests/gui/TestGuiPixmaps.h @@ -26,7 +26,7 @@ class TestGuiPixmaps : public QObject { Q_OBJECT -private Q_SLOTS: +private slots: void initTestCase(); void testDatabaseIcons(); void testEntryIcons(); diff --git a/tests/modeltest.h b/tests/modeltest.h index 3dcf18ce..fdc5cf2f 100644 --- a/tests/modeltest.h +++ b/tests/modeltest.h @@ -46,7 +46,7 @@ class ModelTest : public QObject public: ModelTest( QAbstractItemModel *model, QObject *parent = 0 ); -private Q_SLOTS: +private slots: void nonDestructiveBasicTest(); void rowCount(); void columnCount(); @@ -55,7 +55,7 @@ private Q_SLOTS: void parent(); void data(); -protected Q_SLOTS: +protected slots: void runAllTests(); void layoutAboutToBeChanged(); void layoutChanged(); From 1984595d0d37e2626ed69fff077cf9dad5c3b459 Mon Sep 17 00:00:00 2001 From: rockihack Date: Fri, 3 Mar 2017 22:56:52 +0100 Subject: [PATCH 52/93] Enable DEP and ASLR. --- CMakeLists.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 08a42d1c..017ab49d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -127,6 +127,8 @@ if(MINGW) set(CMAKE_RC_COMPILER_INIT windres) enable_language(RC) set(CMAKE_RC_COMPILE_OBJECT " -O coff -i -o ") + # Enable DEP and ASLR + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--nxcompat -Wl,--dynamicbase") link_libraries(ws2_32 wsock32) endif() From 914b848e5849e26d1fe964c081a082fe8ad484b4 Mon Sep 17 00:00:00 2001 From: rockihack Date: Fri, 10 Mar 2017 13:09:52 +0100 Subject: [PATCH 53/93] Enable DEP+ASLR for cmake modules (autotype dll). --- CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 017ab49d..51773ba0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -129,6 +129,7 @@ if(MINGW) set(CMAKE_RC_COMPILE_OBJECT " -O coff -i -o ") # Enable DEP and ASLR set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--nxcompat -Wl,--dynamicbase") + set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} -Wl,--nxcompat -Wl,--dynamicbase") link_libraries(ws2_32 wsock32) endif() From 8a942422dac52859f887fff0483c9c09c4f97aab Mon Sep 17 00:00:00 2001 From: Janek Bevendorff Date: Fri, 10 Mar 2017 13:23:46 +0100 Subject: [PATCH 54/93] Harden Linux binary --- CMakeLists.txt | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 51773ba0..a0326997 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -68,7 +68,7 @@ endmacro(add_gcc_compiler_flags) add_definitions(-DQT_NO_EXCEPTIONS -DQT_STRICT_ITERATORS -DQT_NO_CAST_TO_ASCII) -add_gcc_compiler_flags("-fno-common -fstack-protector --param=ssp-buffer-size=4") +add_gcc_compiler_flags("-fno-common -fstack-protector-strong --param=ssp-buffer-size=4") add_gcc_compiler_flags("-Wall -Wextra -Wundef -Wpointer-arith -Wno-long-long") add_gcc_compiler_flags("-Wformat=2 -Wmissing-format-attribute") add_gcc_compiler_flags("-fvisibility=hidden") @@ -79,7 +79,7 @@ add_gcc_compiler_cxxflags("-Wnon-virtual-dtor -Wold-style-cast -Woverloaded-virt add_gcc_compiler_cflags("-Wchar-subscripts -Wwrite-strings") string(TOLOWER "${CMAKE_BUILD_TYPE}" CMAKE_BUILD_TYPE_LOWER) -if (CMAKE_BUILD_TYPE_LOWER MATCHES (release|relwithdebinfo|minsizerel)) +if (CMAKE_BUILD_TYPE_LOWER MATCHES "(release|relwithdebinfo|minsizerel)") add_gcc_compiler_flags("-D_FORTIFY_SOURCE=2") endif() @@ -105,10 +105,14 @@ if(CMAKE_COMPILER_IS_GNUCC) endif() if(CMAKE_SYSTEM_NAME STREQUAL "Linux") + if (CMAKE_COMPILER_IS_CLANGXX) + add_gcc_compiler_flags("-Qunused-arguments") + endif() + add_gcc_compiler_flags("-pie -fPIE") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--no-add-needed -Wl,--as-needed -Wl,--no-undefined") - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-z,relro") + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-z,relro,-z,now") set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} -Wl,--no-add-needed -Wl,--as-needed") - set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} -Wl,-z,relro") + set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} -Wl,-z,relro,-z,now") endif() add_gcc_compiler_cxxflags("-std=c++11") From 429bef6830ef1d3695e7edbed1fca4a29b6a7de6 Mon Sep 17 00:00:00 2001 From: Janek Bevendorff Date: Fri, 10 Mar 2017 18:06:22 +0100 Subject: [PATCH 55/93] Remove unused debug function --- src/keys/drivers/YubiKey.cpp | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/src/keys/drivers/YubiKey.cpp b/src/keys/drivers/YubiKey.cpp index ffb48fc7..dfbc57c6 100644 --- a/src/keys/drivers/YubiKey.cpp +++ b/src/keys/drivers/YubiKey.cpp @@ -143,21 +143,6 @@ bool YubiKey::getSerial(unsigned int& serial) return true; } -#ifdef QT_DEBUG -/** - * @brief printByteArray - debug raw data - * @param a array input - * @return string representation of array - */ -static inline QString printByteArray(const QByteArray& a) -{ - QString s; - for (int i = 0; i < a.size(); i++) - s.append(QString::number(a[i] & 0xff, 16).rightJustified(2, '0')); - return s; -} -#endif - YubiKey::ChallengeResult YubiKey::challenge(int slot, bool mayBlock, const QByteArray& challenge, QByteArray& response) { if (!m_mutex.tryLock()) { From 34f037be9293703d9e85647a51e876f821ad853f Mon Sep 17 00:00:00 2001 From: Janek Bevendorff Date: Fri, 10 Mar 2017 18:09:46 +0100 Subject: [PATCH 56/93] Enable -fstack-protector-strong flag only for GCC >= 4.9 and Clang --- CMakeLists.txt | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index a0326997..048bbf68 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -68,12 +68,18 @@ endmacro(add_gcc_compiler_flags) add_definitions(-DQT_NO_EXCEPTIONS -DQT_STRICT_ITERATORS -DQT_NO_CAST_TO_ASCII) -add_gcc_compiler_flags("-fno-common -fstack-protector-strong --param=ssp-buffer-size=4") +add_gcc_compiler_flags("-fno-common") add_gcc_compiler_flags("-Wall -Wextra -Wundef -Wpointer-arith -Wno-long-long") add_gcc_compiler_flags("-Wformat=2 -Wmissing-format-attribute") add_gcc_compiler_flags("-fvisibility=hidden") add_gcc_compiler_cxxflags("-fvisibility-inlines-hidden") +if((CMAKE_COMPILER_IS_GNUCXX AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 4.8.999) OR CMAKE_COMPILER_IS_CLANGXX) + add_gcc_compiler_flags("-fstack-protector-strong") +else() + add_gcc_compiler_flags("-fstack-protector --param=ssp-buffer-size=4") +endif() + add_gcc_compiler_cxxflags("-fno-exceptions -fno-rtti") add_gcc_compiler_cxxflags("-Wnon-virtual-dtor -Wold-style-cast -Woverloaded-virtual") add_gcc_compiler_cflags("-Wchar-subscripts -Wwrite-strings") From 2ff57c2eb75f64db39111c6a5a02cf7c1cad87e5 Mon Sep 17 00:00:00 2001 From: Janek Bevendorff Date: Fri, 10 Mar 2017 20:42:59 +0100 Subject: [PATCH 57/93] Coding style fixes --- src/gui/ChangeMasterKeyWidget.cpp | 13 ++++++++----- src/gui/DatabaseOpenWidget.cpp | 16 +++++++++------- 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/src/gui/ChangeMasterKeyWidget.cpp b/src/gui/ChangeMasterKeyWidget.cpp index bb963d3c..616b0ee0 100644 --- a/src/gui/ChangeMasterKeyWidget.cpp +++ b/src/gui/ChangeMasterKeyWidget.cpp @@ -166,16 +166,18 @@ void ChangeMasterKeyWidget::generateKey() #ifdef WITH_XC_YUBIKEY if (m_ui->challengeResponseGroup->isChecked()) { - int i = m_ui->comboChallengeResponse->currentIndex(); - i = m_ui->comboChallengeResponse->itemData(i).toInt(); + int selectionIndex = m_ui->comboChallengeResponse->currentIndex(); + int comboPayload = m_ui->comboChallengeResponse->itemData(selectionIndex).toInt(); - if (0 == i) { + if (0 == comboPayload) { m_ui->messageWidget->showMessage(tr("Changing master key failed: no YubiKey inserted."), MessageWidget::Error); return; } - bool blocking = i & true; - int slot = i >> 1; + + // read blocking mode from LSB and slot index number from second LSB + bool blocking = comboPayload & 1; + int slot = comboPayload >> 1; auto key = QSharedPointer(new YkChallengeResponseKey(slot, blocking)); m_key.addChallengeResponseKey(key); } @@ -212,6 +214,7 @@ void ChangeMasterKeyWidget::pollYubikey() void ChangeMasterKeyWidget::yubikeyDetected(int slot, bool blocking) { YkChallengeResponseKey yk(slot, blocking); + // add detected YubiKey to combo box and encode blocking mode in LSB, slot number in second LSB m_ui->comboChallengeResponse->addItem(yk.getName(), QVariant((slot << 1) | blocking)); m_ui->comboChallengeResponse->setEnabled(m_ui->challengeResponseGroup->isChecked()); m_ui->buttonRedetectYubikey->setEnabled(m_ui->challengeResponseGroup->isChecked()); diff --git a/src/gui/DatabaseOpenWidget.cpp b/src/gui/DatabaseOpenWidget.cpp index 2eebcae3..f7d43247 100644 --- a/src/gui/DatabaseOpenWidget.cpp +++ b/src/gui/DatabaseOpenWidget.cpp @@ -37,9 +37,9 @@ DatabaseOpenWidget::DatabaseOpenWidget(QWidget* parent) - : DialogyWidget(parent), - m_ui(new Ui::DatabaseOpenWidget()), - m_db(nullptr) + : DialogyWidget(parent) + , m_ui(new Ui::DatabaseOpenWidget()) + , m_db(nullptr) { m_ui->setupUi(this); @@ -208,11 +208,12 @@ CompositeKey DatabaseOpenWidget::databaseKey() } if (m_ui->checkChallengeResponse->isChecked()) { - int i = m_ui->comboChallengeResponse->currentIndex(); - i = m_ui->comboChallengeResponse->itemData(i).toInt(); + int selectionIndex = m_ui->comboChallengeResponse->currentIndex(); + int comboPayload = m_ui->comboChallengeResponse->itemData(selectionIndex).toInt(); - bool blocking = i & true; - int slot = i >> 1; + // read blocking mode from LSB and slot index number from second LSB + bool blocking = comboPayload & 1; + int slot = comboPayload >> 1; auto key = QSharedPointer(new YkChallengeResponseKey(slot, blocking)); masterKey.addChallengeResponseKey(key); } @@ -267,6 +268,7 @@ void DatabaseOpenWidget::pollYubikey() void DatabaseOpenWidget::yubikeyDetected(int slot, bool blocking) { YkChallengeResponseKey yk(slot, blocking); + // add detected YubiKey to combo box and encode blocking mode in LSB, slot number in second LSB m_ui->comboChallengeResponse->addItem(yk.getName(), QVariant((slot << 1) | blocking)); m_ui->comboChallengeResponse->setEnabled(true); m_ui->checkChallengeResponse->setEnabled(true); From 1b10aae74c21ff035d6ddadd7c17517c409bf937 Mon Sep 17 00:00:00 2001 From: louib Date: Fri, 10 Mar 2017 21:25:56 -0500 Subject: [PATCH 58/93] Updating README.md (#390) * Adding Yubikey 2FA to feature list * Added project url --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 2b6fd031..93da44c6 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,16 @@ -# KeePassXC - KeePass Cross-platform Community Edition +# KeePassXC [![Travis Build Status](https://travis-ci.org/keepassxreboot/keepassxc.svg?branch=develop)](https://travis-ci.org/keepassxreboot/keepassxc) [![Coverage Status](https://coveralls.io/repos/github/keepassxreboot/keepassxc/badge.svg)](https://coveralls.io/github/keepassxreboot/keepassxc) KeePassXC Authenticode Certificate Campaign! -[![Travis Build Status](https://travis-ci.org/keepassxreboot/keepassxc.svg?branch=develop)](https://travis-ci.org/keepassxreboot/keepassxc) [![Coverage Status](https://coveralls.io/repos/github/keepassxreboot/keepassxc/badge.svg)](https://coveralls.io/github/keepassxreboot/keepassxc) -KeePassXC Authenticode Certificate Campaign! +KeePass Cross-platform Community Edition ## About -KeePassXC is a community fork of [KeePassX](https://www.keepassx.org/) with the goal to extend and improve it with new features and bugfixes to provide a feature-rich, fully cross-platform and modern open-source password manager. +[KeePassXC](https://keepassxc.org) is a community fork of [KeePassX](https://www.keepassx.org/) with the goal to extend and improve it with new features and bugfixes to provide a feature-rich, fully cross-platform and modern open-source password manager. ## Additional features compared to KeePassX - Auto-Type on all three major platforms (Linux, Windows, OS X) - Stand-alone password generator - Password strength meter +- Yubikey 2FA support for unlocking databases - Using website favicons as entry icons - Merging of databases - Automatic reload when the database changed on disk From f12c6bf748e133addc4bcd7e20fc3065164645c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Steffen=20M=C3=BCller?= Date: Tue, 14 Mar 2017 14:55:25 +0100 Subject: [PATCH 59/93] Update feature and build instructions for Yubikey --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 93da44c6..aced5afb 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ KeePass Cross-platform Community Edition - Auto-Type on all three major platforms (Linux, Windows, OS X) - Stand-alone password generator - Password strength meter -- Yubikey 2FA support for unlocking databases +- YukiKey HMAC-SHA1 authentication for unlocking databases - Using website favicons as entry icons - Merging of databases - Automatic reload when the database changed on disk @@ -55,7 +55,7 @@ make -j8 sudo make install ``` -To enable autotype, add `-DWITH_XC_AUTOTYPE=ON` to the `cmake` command. KeePassHTTP support is compiled in by adding `-DWITH_XC_HTTP=ON`. If these options are not specified, KeePassXC will be built without these plugins. +To enable autotype, add `-DWITH_XC_AUTOTYPE=ON` to the `cmake` command. KeePassHTTP support is compiled in by adding `-DWITH_XC_HTTP=ON`. Yubikey HMAC-SHA1 authentication support is compiled in by adding `-DWITH_XC_YUBIKEY=ON`. If these options are not specified, KeePassXC will be built without these plugins. ### Contributing From 51b7ec2b26916abff475b878f562dfbdf961901b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Steffen=20M=C3=BCller?= Date: Wed, 15 Mar 2017 15:04:43 +0100 Subject: [PATCH 60/93] List all cmake build options --- README.md | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index aced5afb..e4951245 100644 --- a/README.md +++ b/README.md @@ -55,8 +55,18 @@ make -j8 sudo make install ``` -To enable autotype, add `-DWITH_XC_AUTOTYPE=ON` to the `cmake` command. KeePassHTTP support is compiled in by adding `-DWITH_XC_HTTP=ON`. Yubikey HMAC-SHA1 authentication support is compiled in by adding `-DWITH_XC_YUBIKEY=ON`. If these options are not specified, KeePassXC will be built without these plugins. +cmake accepts the following options: +``` + -DWITH_XC_AUTOTYPE=[ON|OFF] Enable/Disable Auto-Type. (default: ON) + -DWITH_XC_HTTP=[ON|OFF] Enable/Disable KeePassHTTP and Custom Icon Downloads. (default: OFF) + -DWITH_XC_YUBIKEY=[ON|OFF] Enable/Disable Yubikey HMAC-SHA1 authentication support. (default: OFF) + + -DWITH_TESTS=[ON|OFF] Enable/Disable building of unit tests (default: ON) + -DWITH_GUI_TESTS=[ON|OFF] Enable/Disable building of GUI tests (default: OFF) + -DWITH_DEV_BUILD=[ON|OFF] Enable/Disable deprecated method warnings. (default: OFF) + -DWITH_COVERAGE=[ON|OFF] Enable/Disable coverage tests. (GCC ONLY) (default: OFF) +``` ### Contributing From 65d4a0a8cd5c59ebdd36a20f9f70f174acf5867d Mon Sep 17 00:00:00 2001 From: Janek Bevendorff Date: Tue, 14 Mar 2017 14:29:09 +0100 Subject: [PATCH 61/93] Add ASAN option to CMake --- CMakeLists.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index ef1743df..2ea0fc8f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -32,6 +32,7 @@ include(CheckCXXSourceCompiles) option(WITH_TESTS "Enable building of unit tests" ON) option(WITH_GUI_TESTS "Enable building of GUI tests" OFF) option(WITH_DEV_BUILD "Use only for development. Disables/warns about deprecated methods." OFF) +option(WITH_ASAN "Enable address sanitizer checks" OFF) option(WITH_COVERAGE "Use to build with coverage tests. (GCC ONLY)." OFF) option(WITH_XC_AUTOTYPE "Include Auto-Type." ON) @@ -83,6 +84,9 @@ endif() add_gcc_compiler_cxxflags("-fno-exceptions -fno-rtti") add_gcc_compiler_cxxflags("-Wnon-virtual-dtor -Wold-style-cast -Woverloaded-virtual") add_gcc_compiler_cflags("-Wchar-subscripts -Wwrite-strings") +if(WITH_ASAN) + add_gcc_compiler_flags("-fsanitize=address") +endif() string(TOLOWER "${CMAKE_BUILD_TYPE}" CMAKE_BUILD_TYPE_LOWER) if (CMAKE_BUILD_TYPE_LOWER MATCHES "(release|relwithdebinfo|minsizerel)") From 28ec015ef4ef2f142f56983e089dd7197d7245df Mon Sep 17 00:00:00 2001 From: Janek Bevendorff Date: Tue, 14 Mar 2017 14:34:16 +0100 Subject: [PATCH 62/93] Add -DWITH_ASAN=ON requirement to pull request template --- .github/PULL_REQUEST_TEMPLATE.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index c83ca4e5..b9852f3c 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -3,11 +3,11 @@ ## Description -## Motivation and Context +## Motivation and context -## How Has This Been Tested? +## How has this been tested? @@ -29,5 +29,6 @@ - ✅ I have read the **CONTRIBUTING** document. **[REQUIRED]** - ✅ My code follows the code style of this project. **[REQUIRED]** - ✅ All new and existing tests passed. **[REQUIRED]** +- ✅ I have compiled and verified my code with `-DWITH_ASAN=ON`. **[REQUIRED]** - ✅ My change requires a change to the documentation and I have updated it accordingly. - ✅ I have added tests to cover my changes. From 504bd402630ad0eacdd59b3f884e269c48c027c3 Mon Sep 17 00:00:00 2001 From: Janek Bevendorff Date: Tue, 14 Mar 2017 15:32:48 +0100 Subject: [PATCH 63/93] Prevent massive end-of-process leak sanitizer dump --- CMakeLists.txt | 2 +- src/main.cpp | 16 ++++++++++++++-- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 2ea0fc8f..86dedbc1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -85,7 +85,7 @@ add_gcc_compiler_cxxflags("-fno-exceptions -fno-rtti") add_gcc_compiler_cxxflags("-Wnon-virtual-dtor -Wold-style-cast -Woverloaded-virtual") add_gcc_compiler_cflags("-Wchar-subscripts -Wwrite-strings") if(WITH_ASAN) - add_gcc_compiler_flags("-fsanitize=address") + add_gcc_compiler_flags("-fsanitize=address -DWITH_ASAN") endif() string(TOLOWER "${CMAKE_BUILD_TYPE}" CMAKE_BUILD_TYPE_LOWER) diff --git a/src/main.cpp b/src/main.cpp index 0618cefa..2563c843 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -28,6 +28,10 @@ #include "gui/MainWindow.h" #include "gui/MessageBox.h" +#ifdef WITH_ASAN +#include +#endif + #ifdef QT_STATIC #include @@ -130,6 +134,14 @@ int main(int argc, char** argv) } } } - - return app.exec(); + + int exitCode = app.exec(); + +#ifdef WITH_ASAN + // do leak check here to prevent massive tail of end-of-process leak errors from third-party libraries + __lsan_do_leak_check(); + __lsan_disable(); +#endif + + return exitCode; } From 2587bac30024f5e2c59a1c33bd25609e2466e000 Mon Sep 17 00:00:00 2001 From: Janek Bevendorff Date: Tue, 14 Mar 2017 14:53:29 +0100 Subject: [PATCH 64/93] Enable ASAN option in Travis build --- .travis.yml | 8 ++++---- CMakeLists.txt | 4 ++++ src/main.cpp | 4 ++-- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/.travis.yml b/.travis.yml index be05d6e4..e24d1d17 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,15 +13,15 @@ compiler: - gcc env: - - CONFIG=Release - - CONFIG=Debug + - CONFIG=Release ASAN_OPTIONS=detect_odr_violation=1:leak_check_at_exit=0 + - CONFIG=Debug ASAN_OPTIONS=detect_odr_violation=1:leak_check_at_exit=0 git: depth: 3 before_install: - if [ "$TRAVIS_OS_NAME" = "linux" ]; then sudo apt-get -qq update; fi - - if [ "$TRAVIS_OS_NAME" = "linux" ]; then sudo apt-get -qq install cmake libmicrohttpd10 libmicrohttpd-dev libxi-dev qtbase5-dev libqt5x11extras5-dev qttools5-dev qttools5-dev-tools libgcrypt20-dev zlib1g-dev libxtst-dev xvfb libyubikey-dev libykpers-1-dev; fi + - if [ "$TRAVIS_OS_NAME" = "linux" ]; then sudo apt-get -qq install cmake libclang-common-3.5-dev libxi-dev qtbase5-dev libqt5x11extras5-dev qttools5-dev qttools5-dev-tools libgcrypt20-dev zlib1g-dev libxtst-dev xvfb libyubikey-dev libykpers-1-dev; fi - if [ "$TRAVIS_OS_NAME" = "osx" ]; then brew update; fi - if [ "$TRAVIS_OS_NAME" = "osx" ]; then brew ls | grep -wq cmake || brew install cmake; fi - if [ "$TRAVIS_OS_NAME" = "osx" ]; then brew ls | grep -wq qt5 || brew install qt5; fi @@ -32,7 +32,7 @@ before_script: - mkdir build && pushd build script: - - cmake -DCMAKE_BUILD_TYPE=${CONFIG} -DWITH_GUI_TESTS=ON -DWITH_XC_HTTP=ON -DWITH_XC_AUTOTYPE=ON -DWITH_XC_YUBIKEY=ON $CMAKE_ARGS .. + - cmake -DCMAKE_BUILD_TYPE=${CONFIG} -DWITH_GUI_TESTS=ON -DWITH_ASAN=ON -DWITH_XC_HTTP=ON -DWITH_XC_AUTOTYPE=ON -DWITH_XC_YUBIKEY=ON $CMAKE_ARGS .. - make -j2 - if [ "$TRAVIS_OS_NAME" = "linux" ]; then make test ARGS+="-E testgui --output-on-failure"; fi - if [ "$TRAVIS_OS_NAME" = "linux" ]; then xvfb-run -a --server-args="-screen 0 800x600x24" make test ARGS+="-R testgui --output-on-failure"; fi diff --git a/CMakeLists.txt b/CMakeLists.txt index 86dedbc1..7b41ac2f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -86,6 +86,10 @@ add_gcc_compiler_cxxflags("-Wnon-virtual-dtor -Wold-style-cast -Woverloaded-virt add_gcc_compiler_cflags("-Wchar-subscripts -Wwrite-strings") if(WITH_ASAN) add_gcc_compiler_flags("-fsanitize=address -DWITH_ASAN") + + if(NOT (CMAKE_COMPILER_IS_GNUCXX AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.9)) + add_gcc_compiler_flags("-fsanitize=leak -DWITH_LSAN") + endif() endif() string(TOLOWER "${CMAKE_BUILD_TYPE}" CMAKE_BUILD_TYPE_LOWER) diff --git a/src/main.cpp b/src/main.cpp index 2563c843..8ed4ee14 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -28,7 +28,7 @@ #include "gui/MainWindow.h" #include "gui/MessageBox.h" -#ifdef WITH_ASAN +#if defined(WITH_ASAN) && defined(WITH_LSAN) #include #endif @@ -137,7 +137,7 @@ int main(int argc, char** argv) int exitCode = app.exec(); -#ifdef WITH_ASAN +#if defined(WITH_ASAN) && defined(WITH_LSAN) // do leak check here to prevent massive tail of end-of-process leak errors from third-party libraries __lsan_do_leak_check(); __lsan_disable(); From 9608464ed12b412d0a270d9baf34a647b22a2a49 Mon Sep 17 00:00:00 2001 From: Janek Bevendorff Date: Tue, 14 Mar 2017 21:24:32 +0100 Subject: [PATCH 65/93] Show error message when trying to use WITH_ASAN on Windows or OS X --- CMakeLists.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 7b41ac2f..9448763f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -85,6 +85,10 @@ add_gcc_compiler_cxxflags("-fno-exceptions -fno-rtti") add_gcc_compiler_cxxflags("-Wnon-virtual-dtor -Wold-style-cast -Woverloaded-virtual") add_gcc_compiler_cflags("-Wchar-subscripts -Wwrite-strings") if(WITH_ASAN) + if(NOT CMAKE_SYSTEM_NAME STREQUAL "Linux") + message(FATAL_ERROR "WITH_ASAN is only supported on Linux at the moment.") + endif() + add_gcc_compiler_flags("-fsanitize=address -DWITH_ASAN") if(NOT (CMAKE_COMPILER_IS_GNUCXX AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.9)) From 8b04040d7e1ce0b7c6e7184b1efcebb6d1426dcf Mon Sep 17 00:00:00 2001 From: Janek Bevendorff Date: Wed, 15 Mar 2017 15:26:40 +0100 Subject: [PATCH 66/93] Add WITH_ASAN option to README --- CMakeLists.txt | 4 ++-- README.md | 11 ++++++----- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9448763f..e7d22312 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -32,8 +32,8 @@ include(CheckCXXSourceCompiles) option(WITH_TESTS "Enable building of unit tests" ON) option(WITH_GUI_TESTS "Enable building of GUI tests" OFF) option(WITH_DEV_BUILD "Use only for development. Disables/warns about deprecated methods." OFF) -option(WITH_ASAN "Enable address sanitizer checks" OFF) -option(WITH_COVERAGE "Use to build with coverage tests. (GCC ONLY)." OFF) +option(WITH_ASAN "Enable address sanitizer checks (Linux only)" OFF) +option(WITH_COVERAGE "Use to build with coverage tests (GCC only)." OFF) option(WITH_XC_AUTOTYPE "Include Auto-Type." ON) option(WITH_XC_HTTP "Include KeePassHTTP and Custom Icon Downloads." OFF) diff --git a/README.md b/README.md index e4951245..fd439ded 100644 --- a/README.md +++ b/README.md @@ -58,14 +58,15 @@ sudo make install cmake accepts the following options: ``` - -DWITH_XC_AUTOTYPE=[ON|OFF] Enable/Disable Auto-Type. (default: ON) - -DWITH_XC_HTTP=[ON|OFF] Enable/Disable KeePassHTTP and Custom Icon Downloads. (default: OFF) - -DWITH_XC_YUBIKEY=[ON|OFF] Enable/Disable Yubikey HMAC-SHA1 authentication support. (default: OFF) + -DWITH_XC_AUTOTYPE=[ON|OFF] Enable/Disable Auto-Type (default: ON) + -DWITH_XC_HTTP=[ON|OFF] Enable/Disable KeePassHTTP and custom icon downloads (default: OFF) + -DWITH_XC_YUBIKEY=[ON|OFF] Enable/Disable YubiKey HMAC-SHA1 authentication support (default: OFF) -DWITH_TESTS=[ON|OFF] Enable/Disable building of unit tests (default: ON) -DWITH_GUI_TESTS=[ON|OFF] Enable/Disable building of GUI tests (default: OFF) - -DWITH_DEV_BUILD=[ON|OFF] Enable/Disable deprecated method warnings. (default: OFF) - -DWITH_COVERAGE=[ON|OFF] Enable/Disable coverage tests. (GCC ONLY) (default: OFF) + -DWITH_DEV_BUILD=[ON|OFF] Enable/Disable deprecated method warnings (default: OFF) + -DWITH_ASAN=[ON|OFF] Enable/Disable address sanitizer checks (Linux only) (default: OFF) + -DWITH_COVERAGE=[ON|OFF] Enable/Disable coverage tests (GCC only) (default: OFF) ``` ### Contributing From 52991f3d66eb5e7d7a5098dfe3b9bb7106466f52 Mon Sep 17 00:00:00 2001 From: Louis-Bertrand Varin Date: Mon, 30 Jan 2017 19:18:35 -0500 Subject: [PATCH 67/93] Add first keepassxc-cli version. --- src/CMakeLists.txt | 1 + {utils => src/cli}/CMakeLists.txt | 29 +++---- utils/kdbx-extract.cpp => src/cli/Extract.cpp | 9 +-- src/cli/Extract.h | 27 +++++++ utils/kdbx-merge.cpp => src/cli/Merge.cpp | 12 +-- src/cli/Merge.h | 27 +++++++ src/cli/keepassxc-cli.cpp | 77 +++++++++++++++++++ 7 files changed, 150 insertions(+), 32 deletions(-) rename {utils => src/cli}/CMakeLists.txt (58%) rename utils/kdbx-extract.cpp => src/cli/Extract.cpp (92%) create mode 100644 src/cli/Extract.h rename utils/kdbx-merge.cpp => src/cli/Merge.cpp (93%) create mode 100644 src/cli/Merge.h create mode 100644 src/cli/keepassxc-cli.cpp diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 5e221b91..1d998446 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -164,6 +164,7 @@ if(WITH_XC_HTTP) endif() add_subdirectory(autotype) +add_subdirectory(cli) set(autotype_SOURCES core/Tools.cpp diff --git a/utils/CMakeLists.txt b/src/cli/CMakeLists.txt similarity index 58% rename from utils/CMakeLists.txt rename to src/cli/CMakeLists.txt index 83f00b4b..4456cbea 100644 --- a/utils/CMakeLists.txt +++ b/src/cli/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (C) 2010 Felix Geyer +# Copyright (C) 2017 KeePassXC Team # # 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 @@ -13,24 +13,19 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -include_directories(../src) +set(cli_SOURCES + Merge.cpp + Merge.h + Extract.cpp + Extract.h) -add_executable(kdbx-extract kdbx-extract.cpp) -target_link_libraries(kdbx-extract +add_library(cli STATIC ${cli_SOURCES}) +target_link_libraries(cli Qt5::Core Qt5::Widgets) + +add_executable(keepassxc-cli keepassxc-cli.cpp) +target_link_libraries(keepassxc-cli + cli keepassx_core Qt5::Core ${GCRYPT_LIBRARIES} - ${GPGERROR_LIBRARIES} ${ZLIB_LIBRARIES}) - -add_executable(kdbx-merge kdbx-merge.cpp) -target_link_libraries(kdbx-merge - keepassx_core - Qt5::Core - ${GCRYPT_LIBRARIES} - ${GPGERROR_LIBRARIES} - ${ZLIB_LIBRARIES}) - - -add_executable(entropy-meter entropy-meter.cpp) -target_link_libraries(entropy-meter zxcvbn) diff --git a/utils/kdbx-extract.cpp b/src/cli/Extract.cpp similarity index 92% rename from utils/kdbx-extract.cpp rename to src/cli/Extract.cpp index 255f5d00..4ad65878 100644 --- a/utils/kdbx-extract.cpp +++ b/src/cli/Extract.cpp @@ -17,6 +17,8 @@ #include +#include "Extract.h" + #include #include #include @@ -30,7 +32,7 @@ #include "keys/FileKey.h" #include "keys/PasswordKey.h" -int main(int argc, char **argv) +int Extract::execute(int argc, char **argv) { QCoreApplication app(argc, argv); @@ -38,7 +40,6 @@ int main(int argc, char **argv) parser.setApplicationDescription(QCoreApplication::translate("main", "Extract and print a KeePassXC database file.")); parser.addPositionalArgument("database", QCoreApplication::translate("main", "path of the database to extract.")); - parser.addHelpOption(); parser.process(app); const QStringList args = parser.positionalArguments(); @@ -47,10 +48,6 @@ int main(int argc, char **argv) return 1; } - if (!Crypto::init()) { - qFatal("Fatal error while testing the cryptographic functions:\n%s", qPrintable(Crypto::errorString())); - } - static QTextStream inputTextStream(stdin, QIODevice::ReadOnly); QString line = inputTextStream.readLine(); CompositeKey key = CompositeKey::readFromLine(line); diff --git a/src/cli/Extract.h b/src/cli/Extract.h new file mode 100644 index 00000000..9a6638e4 --- /dev/null +++ b/src/cli/Extract.h @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2017 KeePassXC Team + * + * 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 + * the Free Software Foundation, either version 2 or (at your option) + * version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef KEEPASSXC_EXTRACT_H +#define KEEPASSXC_EXTRACT_H + +class Extract +{ +public: + static int execute(int argc, char** argv); +}; + +#endif // KEEPASSXC_EXTRACT_H diff --git a/utils/kdbx-merge.cpp b/src/cli/Merge.cpp similarity index 93% rename from utils/kdbx-merge.cpp rename to src/cli/Merge.cpp index da780ea1..bac8f4bd 100644 --- a/utils/kdbx-merge.cpp +++ b/src/cli/Merge.cpp @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -#include +#include "Merge.h" #include #include @@ -25,12 +25,11 @@ #include #include "core/Database.h" -#include "crypto/Crypto.h" #include "format/KeePass2Reader.h" #include "format/KeePass2Writer.h" #include "keys/CompositeKey.h" -int main(int argc, char **argv) +int Merge::execute(int argc, char** argv) { QCoreApplication app(argc, argv); @@ -43,7 +42,6 @@ int main(int argc, char **argv) QCommandLineOption samePasswordOption(QStringList() << "s" << "same-password", QCoreApplication::translate("main", "use the same password for both database files.")); - parser.addHelpOption(); parser.addOption(samePasswordOption); parser.process(app); @@ -53,10 +51,6 @@ int main(int argc, char **argv) return 1; } - if (!Crypto::init()) { - qFatal("Fatal error while testing the cryptographic functions:\n%s", qPrintable(Crypto::errorString())); - } - static QTextStream inputTextStream(stdin, QIODevice::ReadOnly); QString line1 = inputTextStream.readLine(); @@ -133,6 +127,6 @@ int main(int argc, char **argv) } qDebug("Successfully merged the database files.\n"); - return 1; + return 0; } diff --git a/src/cli/Merge.h b/src/cli/Merge.h new file mode 100644 index 00000000..dd9b8a4c --- /dev/null +++ b/src/cli/Merge.h @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2017 KeePassXC Team + * + * 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 + * the Free Software Foundation, either version 2 or (at your option) + * version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef KEEPASSXC_MERGE_H +#define KEEPASSXC_MERGE_H + +class Merge +{ +public: + static int execute(int argc, char** argv); +}; + +#endif // KEEPASSXC_MERGE_H diff --git a/src/cli/keepassxc-cli.cpp b/src/cli/keepassxc-cli.cpp new file mode 100644 index 00000000..086e544a --- /dev/null +++ b/src/cli/keepassxc-cli.cpp @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2017 KeePassXC Team + * + * 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 + * the Free Software Foundation, either version 2 or (at your option) + * version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include + +#include +#include +#include + +#include "config-keepassx.h" +#include "crypto/Crypto.h" + +int main(int argc, char **argv) +{ + + if (!Crypto::init()) { + qFatal("Fatal error while testing the cryptographic functions:\n%s", qPrintable(Crypto::errorString())); + return 1; + } + + QCoreApplication app(argc, argv); + app.setApplicationVersion(KEEPASSX_VERSION); + + QCommandLineParser parser; + parser.setApplicationDescription(QCoreApplication::translate("main", "KeepassXC command line interface.")); + parser.addPositionalArgument("command", QCoreApplication::translate("main", "Name of the command to execute.")); + + parser.addHelpOption(); + parser.addVersionOption(); + parser.process(app); + + const QStringList args = parser.positionalArguments(); + if (args.size() < 1) { + parser.showHelp(); + return 1; + } + + QString commandName = args.at(0); + + for (int i = 1; i < argc - 1; ++i) { + argv[i] = argv[i+1]; + } + argv[argc - 1] = nullptr; + argc--; + + if (commandName == "merge") + { + argv[0] = const_cast("keepassxc-cli merge"); + return Merge::execute(argc, argv); + } + + if (commandName == "extract") + { + argv[0] = const_cast("keepassxc-cli extract"); + return Extract::execute(argc, argv); + } + + qCritical("Invalid command %s.", qPrintable(commandName)); + parser.showHelp(); + return 1; + +} From 9cfc862b0787567a06c330c5f72ccd7bccc3e5bd Mon Sep 17 00:00:00 2001 From: Louis-Bertrand Varin Date: Thu, 2 Feb 2017 10:33:50 -0500 Subject: [PATCH 68/93] Disable core dumps (keepassxc-cli). --- src/cli/keepassxc-cli.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/cli/keepassxc-cli.cpp b/src/cli/keepassxc-cli.cpp index 086e544a..78b11164 100644 --- a/src/cli/keepassxc-cli.cpp +++ b/src/cli/keepassxc-cli.cpp @@ -23,10 +23,14 @@ #include #include "config-keepassx.h" +#include "core/Tools.h" #include "crypto/Crypto.h" int main(int argc, char **argv) { +#ifdef QT_NO_DEBUG + Tools::disableCoreDumps(); +#endif if (!Crypto::init()) { qFatal("Fatal error while testing the cryptographic functions:\n%s", qPrintable(Crypto::errorString())); From bf9b23539e172f1bd3aa9e2c4d3ff44726a2115c Mon Sep 17 00:00:00 2001 From: Louis-Bertrand Varin Date: Thu, 2 Feb 2017 10:51:33 -0500 Subject: [PATCH 69/93] Add dependency + adjust styling. --- src/cli/CMakeLists.txt | 1 + src/cli/keepassxc-cli.cpp | 14 ++++++-------- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/cli/CMakeLists.txt b/src/cli/CMakeLists.txt index 4456cbea..70dd420d 100644 --- a/src/cli/CMakeLists.txt +++ b/src/cli/CMakeLists.txt @@ -28,4 +28,5 @@ target_link_libraries(keepassxc-cli keepassx_core Qt5::Core ${GCRYPT_LIBRARIES} + ${GPGERROR_LIBRARIES} ${ZLIB_LIBRARIES}) diff --git a/src/cli/keepassxc-cli.cpp b/src/cli/keepassxc-cli.cpp index 78b11164..7df4d7d8 100644 --- a/src/cli/keepassxc-cli.cpp +++ b/src/cli/keepassxc-cli.cpp @@ -57,20 +57,18 @@ int main(int argc, char **argv) QString commandName = args.at(0); for (int i = 1; i < argc - 1; ++i) { - argv[i] = argv[i+1]; + argv[i] = argv[i + 1]; } argv[argc - 1] = nullptr; - argc--; + --argc; - if (commandName == "merge") - { - argv[0] = const_cast("keepassxc-cli merge"); + if (commandName == "merge") { + argv[0] = const_cast("keepassxc-cli merge"); return Merge::execute(argc, argv); } - if (commandName == "extract") - { - argv[0] = const_cast("keepassxc-cli extract"); + if (commandName == "extract") { + argv[0] = const_cast("keepassxc-cli extract"); return Extract::execute(argc, argv); } From 9b92e7f8e8fe6438f7916832ede22bdb9b22bdae Mon Sep 17 00:00:00 2001 From: Louis-Bertrand Varin Date: Thu, 2 Feb 2017 17:30:54 -0500 Subject: [PATCH 70/93] Use EXIT_FAILURE/SUCCESS --- src/cli/Extract.cpp | 11 ++++++----- src/cli/Merge.cpp | 24 +++++++++++++----------- src/cli/keepassxc-cli.cpp | 11 ++++++----- 3 files changed, 25 insertions(+), 21 deletions(-) diff --git a/src/cli/Extract.cpp b/src/cli/Extract.cpp index 4ad65878..73581d8f 100644 --- a/src/cli/Extract.cpp +++ b/src/cli/Extract.cpp @@ -15,6 +15,7 @@ * along with this program. If not, see . */ +#include #include #include "Extract.h" @@ -45,7 +46,7 @@ int Extract::execute(int argc, char **argv) const QStringList args = parser.positionalArguments(); if (args.size() != 1) { parser.showHelp(); - return 1; + return EXIT_FAILURE; } static QTextStream inputTextStream(stdin, QIODevice::ReadOnly); @@ -56,11 +57,11 @@ int Extract::execute(int argc, char **argv) QFile dbFile(databaseFilename); if (!dbFile.exists()) { qCritical("File %s does not exist.", qPrintable(databaseFilename)); - return 1; + return EXIT_FAILURE; } if (!dbFile.open(QIODevice::ReadOnly)) { qCritical("Unable to open file %s.", qPrintable(databaseFilename)); - return 1; + return EXIT_FAILURE; } KeePass2Reader reader; @@ -73,15 +74,15 @@ int Extract::execute(int argc, char **argv) if (reader.hasError()) { if (xmlData.isEmpty()) { qCritical("Error while reading the database:\n%s", qPrintable(reader.errorString())); - return 1; } else { qWarning("Error while parsing the database:\n%s\n", qPrintable(reader.errorString())); } + return EXIT_FAILURE; } QTextStream out(stdout); out << xmlData.constData() << "\n"; - return 0; + return EXIT_SUCCESS; } diff --git a/src/cli/Merge.cpp b/src/cli/Merge.cpp index bac8f4bd..caa8d19c 100644 --- a/src/cli/Merge.cpp +++ b/src/cli/Merge.cpp @@ -15,6 +15,8 @@ * along with this program. If not, see . */ +#include + #include "Merge.h" #include @@ -48,7 +50,7 @@ int Merge::execute(int argc, char** argv) const QStringList args = parser.positionalArguments(); if (args.size() != 2) { parser.showHelp(); - return 1; + return EXIT_FAILURE; } static QTextStream inputTextStream(stdin, QIODevice::ReadOnly); @@ -70,11 +72,11 @@ int Merge::execute(int argc, char** argv) QFile dbFile1(databaseFilename1); if (!dbFile1.exists()) { qCritical("File %s does not exist.", qPrintable(databaseFilename1)); - return 1; + return EXIT_FAILURE; } if (!dbFile1.open(QIODevice::ReadOnly)) { qCritical("Unable to open file %s.", qPrintable(databaseFilename1)); - return 1; + return EXIT_FAILURE; } KeePass2Reader reader1; @@ -82,7 +84,7 @@ int Merge::execute(int argc, char** argv) if (reader1.hasError()) { qCritical("Error while parsing the database:\n%s\n", qPrintable(reader1.errorString())); - return 1; + return EXIT_FAILURE; } @@ -90,11 +92,11 @@ int Merge::execute(int argc, char** argv) QFile dbFile2(databaseFilename2); if (!dbFile2.exists()) { qCritical("File %s does not exist.", qPrintable(databaseFilename2)); - return 1; + return EXIT_FAILURE; } if (!dbFile2.open(QIODevice::ReadOnly)) { qCritical("Unable to open file %s.", qPrintable(databaseFilename2)); - return 1; + return EXIT_FAILURE; } KeePass2Reader reader2; @@ -102,7 +104,7 @@ int Merge::execute(int argc, char** argv) if (reader2.hasError()) { qCritical("Error while parsing the database:\n%s\n", qPrintable(reader2.errorString())); - return 1; + return EXIT_FAILURE; } db1->merge(db2); @@ -110,7 +112,7 @@ int Merge::execute(int argc, char** argv) QSaveFile saveFile(databaseFilename1); if (!saveFile.open(QIODevice::WriteOnly)) { qCritical("Unable to open file %s for writing.", qPrintable(databaseFilename1)); - return 1; + return EXIT_FAILURE; } KeePass2Writer writer; @@ -118,15 +120,15 @@ int Merge::execute(int argc, char** argv) if (writer.hasError()) { qCritical("Error while updating the database:\n%s\n", qPrintable(writer.errorString())); - return 1; + return EXIT_FAILURE; } if (!saveFile.commit()) { qCritical("Error while updating the database:\n%s\n", qPrintable(writer.errorString())); - return 0; + return EXIT_FAILURE; } qDebug("Successfully merged the database files.\n"); - return 0; + return EXIT_SUCCESS; } diff --git a/src/cli/keepassxc-cli.cpp b/src/cli/keepassxc-cli.cpp index 7df4d7d8..e745dd09 100644 --- a/src/cli/keepassxc-cli.cpp +++ b/src/cli/keepassxc-cli.cpp @@ -15,13 +15,14 @@ * along with this program. If not, see . */ -#include -#include +#include #include #include #include +#include +#include #include "config-keepassx.h" #include "core/Tools.h" #include "crypto/Crypto.h" @@ -34,7 +35,7 @@ int main(int argc, char **argv) if (!Crypto::init()) { qFatal("Fatal error while testing the cryptographic functions:\n%s", qPrintable(Crypto::errorString())); - return 1; + return EXIT_FAILURE; } QCoreApplication app(argc, argv); @@ -51,7 +52,7 @@ int main(int argc, char **argv) const QStringList args = parser.positionalArguments(); if (args.size() < 1) { parser.showHelp(); - return 1; + return EXIT_FAILURE; } QString commandName = args.at(0); @@ -74,6 +75,6 @@ int main(int argc, char **argv) qCritical("Invalid command %s.", qPrintable(commandName)); parser.showHelp(); - return 1; + return EXIT_FAILURE; } From 992d8a90c746f31ba77ada572d1e1c88ab299d79 Mon Sep 17 00:00:00 2001 From: Louis-Bertrand Varin Date: Thu, 2 Feb 2017 17:54:39 -0500 Subject: [PATCH 71/93] Migrate entropy-meter to keepassxc-cli --- CMakeLists.txt | 1 - src/cli/CMakeLists.txt | 2 ++ .../cli/EntropyMeter.cpp | 4 ++- src/cli/EntropyMeter.h | 27 +++++++++++++++++++ src/cli/keepassxc-cli.cpp | 16 ++++++++--- 5 files changed, 44 insertions(+), 6 deletions(-) rename utils/entropy-meter.cpp => src/cli/EntropyMeter.cpp (98%) create mode 100644 src/cli/EntropyMeter.h diff --git a/CMakeLists.txt b/CMakeLists.txt index e7d22312..3dd43555 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -254,7 +254,6 @@ include(FeatureSummary) add_subdirectory(src) add_subdirectory(share) -add_subdirectory(utils) if(WITH_TESTS) add_subdirectory(tests) endif(WITH_TESTS) diff --git a/src/cli/CMakeLists.txt b/src/cli/CMakeLists.txt index 70dd420d..d678ff39 100644 --- a/src/cli/CMakeLists.txt +++ b/src/cli/CMakeLists.txt @@ -16,6 +16,8 @@ set(cli_SOURCES Merge.cpp Merge.h + EntropyMeter.cpp + EntropyMeter.h Extract.cpp Extract.h) diff --git a/utils/entropy-meter.cpp b/src/cli/EntropyMeter.cpp similarity index 98% rename from utils/entropy-meter.cpp rename to src/cli/EntropyMeter.cpp index 74f6bc11..ffaecc8e 100644 --- a/utils/entropy-meter.cpp +++ b/src/cli/EntropyMeter.cpp @@ -6,6 +6,8 @@ Copyright (c) 2016, KeePassXC Team See zxcvbn/zxcvbn.cpp for complete COPYRIGHT Notice */ +#include "EntropyMeter.h" + #include #include #include @@ -76,7 +78,7 @@ static void calculate(const char *pwd, int advanced) } } -int main(int argc, char **argv) +int EntropyMeter::execute(int argc, char **argv) { printf("KeePassXC Entropy Meter, based on zxcvbn-c.\nEnter your password below or pass it as argv\n"); printf(" Usage: entropy-meter [-a] [pwd1 pwd2 ...]\n> "); diff --git a/src/cli/EntropyMeter.h b/src/cli/EntropyMeter.h new file mode 100644 index 00000000..5034b966 --- /dev/null +++ b/src/cli/EntropyMeter.h @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2017 KeePassXC Team + * + * 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 + * the Free Software Foundation, either version 2 or (at your option) + * version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef KEEPASSXC_ENTROPYMETER_H +#define KEEPASSXC_ENTROPYMETER_H + +class EntropyMeter +{ +public: + static int execute(int argc, char** argv); +}; + +#endif // KEEPASSXC_ENTROPYMETER_H diff --git a/src/cli/keepassxc-cli.cpp b/src/cli/keepassxc-cli.cpp index e745dd09..e8719d9c 100644 --- a/src/cli/keepassxc-cli.cpp +++ b/src/cli/keepassxc-cli.cpp @@ -23,6 +23,7 @@ #include #include +#include #include "config-keepassx.h" #include "core/Tools.h" #include "crypto/Crypto.h" @@ -47,15 +48,17 @@ int main(int argc, char **argv) parser.addHelpOption(); parser.addVersionOption(); - parser.process(app); + // TODO : use process once the setOptionsAfterPositionalArgumentsMode (Qt 5.6) + // is available. Until then, options passed to sub-commands won't be + // recognized by this parser. + // parser.process(app); - const QStringList args = parser.positionalArguments(); - if (args.size() < 1) { + if (argc < 2) { parser.showHelp(); return EXIT_FAILURE; } - QString commandName = args.at(0); + QString commandName = argv[1]; for (int i = 1; i < argc - 1; ++i) { argv[i] = argv[i + 1]; @@ -73,6 +76,11 @@ int main(int argc, char **argv) return Extract::execute(argc, argv); } + if (commandName == "entropy-meter") { + argv[0] = const_cast("keepassxc-cli entropy-meter"); + return EntropyMeter::execute(argc, argv); + } + qCritical("Invalid command %s.", qPrintable(commandName)); parser.showHelp(); return EXIT_FAILURE; From 342d49d05048330d07f7062bcf989df5f9c8b21a Mon Sep 17 00:00:00 2001 From: Louis-Bertrand Varin Date: Thu, 2 Feb 2017 17:59:06 -0500 Subject: [PATCH 72/93] Missing zxcvbn dependency. --- src/cli/CMakeLists.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/cli/CMakeLists.txt b/src/cli/CMakeLists.txt index d678ff39..8b547636 100644 --- a/src/cli/CMakeLists.txt +++ b/src/cli/CMakeLists.txt @@ -31,4 +31,5 @@ target_link_libraries(keepassxc-cli Qt5::Core ${GCRYPT_LIBRARIES} ${GPGERROR_LIBRARIES} - ${ZLIB_LIBRARIES}) + ${ZLIB_LIBRARIES} + zxcvbn) From 805600ad448bb129f745bfded33d18adc86a714e Mon Sep 17 00:00:00 2001 From: Louis-Bertrand Varin Date: Thu, 2 Feb 2017 18:29:31 -0500 Subject: [PATCH 73/93] Installing keepassxc-cli executable. --- src/cli/CMakeLists.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/cli/CMakeLists.txt b/src/cli/CMakeLists.txt index 8b547636..b8dce663 100644 --- a/src/cli/CMakeLists.txt +++ b/src/cli/CMakeLists.txt @@ -33,3 +33,7 @@ target_link_libraries(keepassxc-cli ${GPGERROR_LIBRARIES} ${ZLIB_LIBRARIES} zxcvbn) + +install(TARGETS keepassxc-cli + BUNDLE DESTINATION . COMPONENT Runtime + RUNTIME DESTINATION ${BIN_INSTALL_DIR} COMPONENT Runtime) From e1e8f33f6776a82302f4fb64b7d69851407c0873 Mon Sep 17 00:00:00 2001 From: Louis-Bertrand Varin Date: Sat, 4 Feb 2017 14:52:43 -0500 Subject: [PATCH 74/93] Install path on Mac. --- CMakeLists.txt | 3 +++ src/cli/CMakeLists.txt | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 3dd43555..56baa8f8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -166,16 +166,19 @@ if(APPLE AND "${CMAKE_INSTALL_PREFIX}" STREQUAL "/usr/local") endif() if(MINGW) + set(CLI_INSTALL_DIR ".") set(BIN_INSTALL_DIR ".") set(PLUGIN_INSTALL_DIR ".") set(DATA_INSTALL_DIR "share") elseif(APPLE) + set(CLI_INSTALL_DIR "/usr/local/bin") set(BIN_INSTALL_DIR ".") set(PLUGIN_INSTALL_DIR "${PROGNAME}.app/Contents/PlugIns") set(DATA_INSTALL_DIR "${PROGNAME}.app/Contents/Resources") else() include(GNUInstallDirs) + set(CLI_INSTALL_DIR "${CMAKE_INSTALL_BINDIR}") set(BIN_INSTALL_DIR "${CMAKE_INSTALL_BINDIR}") set(PLUGIN_INSTALL_DIR "${CMAKE_INSTALL_LIBDIR}/keepassxc") set(DATA_INSTALL_DIR "${CMAKE_INSTALL_DATADIR}/keepassxc") diff --git a/src/cli/CMakeLists.txt b/src/cli/CMakeLists.txt index b8dce663..4f0386e3 100644 --- a/src/cli/CMakeLists.txt +++ b/src/cli/CMakeLists.txt @@ -36,4 +36,4 @@ target_link_libraries(keepassxc-cli install(TARGETS keepassxc-cli BUNDLE DESTINATION . COMPONENT Runtime - RUNTIME DESTINATION ${BIN_INSTALL_DIR} COMPONENT Runtime) + RUNTIME DESTINATION ${CLI_INSTALL_DIR} COMPONENT Runtime) From 782d1f17d1d4c57ac940655ab48481de2ede5ed1 Mon Sep 17 00:00:00 2001 From: Louis-Bertrand Varin Date: Sun, 5 Feb 2017 12:56:44 -0500 Subject: [PATCH 75/93] Using ++argv --- src/cli/keepassxc-cli.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/cli/keepassxc-cli.cpp b/src/cli/keepassxc-cli.cpp index e8719d9c..e532e95f 100644 --- a/src/cli/keepassxc-cli.cpp +++ b/src/cli/keepassxc-cli.cpp @@ -60,10 +60,8 @@ int main(int argc, char **argv) QString commandName = argv[1]; - for (int i = 1; i < argc - 1; ++i) { - argv[i] = argv[i + 1]; - } - argv[argc - 1] = nullptr; + // Removing the first cli argument before dispatching. + ++argv; --argc; if (commandName == "merge") { From b85941531df219cdc5547445df7e91318c026c3f Mon Sep 17 00:00:00 2001 From: Louis-Bertrand Varin Date: Sun, 5 Feb 2017 13:40:40 -0500 Subject: [PATCH 76/93] Keepass -> KeePass. --- src/cli/keepassxc-cli.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cli/keepassxc-cli.cpp b/src/cli/keepassxc-cli.cpp index e532e95f..a7e7c275 100644 --- a/src/cli/keepassxc-cli.cpp +++ b/src/cli/keepassxc-cli.cpp @@ -43,7 +43,7 @@ int main(int argc, char **argv) app.setApplicationVersion(KEEPASSX_VERSION); QCommandLineParser parser; - parser.setApplicationDescription(QCoreApplication::translate("main", "KeepassXC command line interface.")); + parser.setApplicationDescription(QCoreApplication::translate("main", "KeePassXC command line interface.")); parser.addPositionalArgument("command", QCoreApplication::translate("main", "Name of the command to execute.")); parser.addHelpOption(); From 7636a559f9408ee765665c9e94527bf9c63c0d21 Mon Sep 17 00:00:00 2001 From: Louis-Bertrand Varin Date: Mon, 13 Feb 2017 22:21:19 -0500 Subject: [PATCH 77/93] Remove unused imports. --- src/cli/Extract.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/cli/Extract.cpp b/src/cli/Extract.cpp index 73581d8f..b08039ae 100644 --- a/src/cli/Extract.cpp +++ b/src/cli/Extract.cpp @@ -27,11 +27,8 @@ #include #include "core/Database.h" -#include "crypto/Crypto.h" #include "format/KeePass2Reader.h" #include "keys/CompositeKey.h" -#include "keys/FileKey.h" -#include "keys/PasswordKey.h" int Extract::execute(int argc, char **argv) { From 7ca475f968deba1a659c0b03235afe806a2016bf Mon Sep 17 00:00:00 2001 From: Louis-Bertrand Varin Date: Mon, 13 Feb 2017 22:29:20 -0500 Subject: [PATCH 78/93] Add list to keepassxc-cli --- src/cli/CMakeLists.txt | 2 + src/cli/List.cpp | 101 ++++++++++++++++++++++++++++++++++++++ src/cli/List.h | 27 ++++++++++ src/cli/keepassxc-cli.cpp | 6 +++ 4 files changed, 136 insertions(+) create mode 100644 src/cli/List.cpp create mode 100644 src/cli/List.h diff --git a/src/cli/CMakeLists.txt b/src/cli/CMakeLists.txt index 4f0386e3..0ed5d991 100644 --- a/src/cli/CMakeLists.txt +++ b/src/cli/CMakeLists.txt @@ -14,6 +14,8 @@ # along with this program. If not, see . set(cli_SOURCES + List.cpp + List.h Merge.cpp Merge.h EntropyMeter.cpp diff --git a/src/cli/List.cpp b/src/cli/List.cpp new file mode 100644 index 00000000..2049cc1d --- /dev/null +++ b/src/cli/List.cpp @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2017 KeePassXC Team + * + * 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 + * the Free Software Foundation, either version 2 or (at your option) + * version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include + +#include "List.h" + +#include +#include +#include +#include +#include + +#include "core/Database.h" +#include "core/Entry.h" +#include "core/Group.h" +#include "format/KeePass2Reader.h" +#include "keys/CompositeKey.h" + +void printGroup(Group* group, QString baseName, int depth) { + + QTextStream out(stdout); + + QString groupName = baseName + group->name() + "/"; + QString indentation = QString(" ").repeated(depth); + + out << indentation << groupName << "\n"; + out.flush(); + + if (group->entries().isEmpty() && group->children().isEmpty()) { + out << indentation << " [empty]\n"; + return; + } + + for (Entry* entry : group->entries()) { + out << indentation << " " << entry->title() << " " << entry->uuid().toHex() << "\n"; + } + + for (Group* innerGroup : group->children()) { + printGroup(innerGroup, groupName, depth + 1); + } + +} + +int List::execute(int argc, char **argv) +{ + QCoreApplication app(argc, argv); + + QCommandLineParser parser; + parser.setApplicationDescription(QCoreApplication::translate("main", + "List the passwords in the database.")); + parser.addPositionalArgument("database", QCoreApplication::translate("main", "path of the database.")); + parser.process(app); + + const QStringList args = parser.positionalArguments(); + if (args.size() != 1) { + parser.showHelp(); + return EXIT_FAILURE; + } + + static QTextStream inputTextStream(stdin, QIODevice::ReadOnly); + QString line = inputTextStream.readLine(); + CompositeKey key = CompositeKey::readFromLine(line); + + QString databaseFilename = args.at(0); + QFile dbFile(databaseFilename); + if (!dbFile.exists()) { + qCritical("File %s does not exist.", qPrintable(databaseFilename)); + return EXIT_FAILURE; + } + if (!dbFile.open(QIODevice::ReadOnly)) { + qCritical("Unable to open file %s.", qPrintable(databaseFilename)); + return EXIT_FAILURE; + } + + KeePass2Reader reader; + Database* db = reader.readDatabase(&dbFile, key); + + if (reader.hasError()) { + qCritical("Error while parsing the database:\n%s\n", qPrintable(reader.errorString())); + return EXIT_FAILURE; + } + + printGroup(db->rootGroup(), QString(""), 0); + return EXIT_SUCCESS; +} diff --git a/src/cli/List.h b/src/cli/List.h new file mode 100644 index 00000000..76f086c6 --- /dev/null +++ b/src/cli/List.h @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2017 KeePassXC Team + * + * 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 + * the Free Software Foundation, either version 2 or (at your option) + * version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef KEEPASSXC_LIST_H +#define KEEPASSXC_LIST_H + +class List +{ +public: + static int execute(int argc, char** argv); +}; + +#endif // KEEPASSXC_LIST_H diff --git a/src/cli/keepassxc-cli.cpp b/src/cli/keepassxc-cli.cpp index a7e7c275..67eb8e8a 100644 --- a/src/cli/keepassxc-cli.cpp +++ b/src/cli/keepassxc-cli.cpp @@ -21,6 +21,7 @@ #include #include +#include #include #include #include @@ -64,6 +65,11 @@ int main(int argc, char **argv) ++argv; --argc; + if (commandName == "list") { + argv[0] = const_cast("keepassxc-cli list"); + return List::execute(argc, argv); + } + if (commandName == "merge") { argv[0] = const_cast("keepassxc-cli merge"); return Merge::execute(argc, argv); From 64dfada038f0f72465e2c6eebcb7ce42ace340b5 Mon Sep 17 00:00:00 2001 From: Louis-Bertrand Varin Date: Wed, 15 Feb 2017 21:01:50 -0500 Subject: [PATCH 79/93] Adding available commands. --- src/cli/keepassxc-cli.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/cli/keepassxc-cli.cpp b/src/cli/keepassxc-cli.cpp index 67eb8e8a..c96da4d1 100644 --- a/src/cli/keepassxc-cli.cpp +++ b/src/cli/keepassxc-cli.cpp @@ -44,7 +44,15 @@ int main(int argc, char **argv) app.setApplicationVersion(KEEPASSX_VERSION); QCommandLineParser parser; - parser.setApplicationDescription(QCoreApplication::translate("main", "KeePassXC command line interface.")); + + QString description("KeePassXC command line interface."); + description = description.append(QString("\n\nAvailable commands:")); + description = description.append(QString("\n extract\tExtract and print a KeePassXC database file.")); + description = description.append(QString("\n entropy-meter\tCalculate password entropy.")); + description = description.append(QString("\n list\t\tList database entries.")); + description = description.append(QString("\n merge\t\tMerge 2 KeePassXC database files.")); + parser.setApplicationDescription(QCoreApplication::translate("main", qPrintable(description))); + parser.addPositionalArgument("command", QCoreApplication::translate("main", "Name of the command to execute.")); parser.addHelpOption(); From f579345059815f55a88042ddb16e49a72a671b33 Mon Sep 17 00:00:00 2001 From: Louis-Bertrand Varin Date: Wed, 15 Feb 2017 21:05:40 -0500 Subject: [PATCH 80/93] Change cli commands description. --- src/cli/Extract.cpp | 2 +- src/cli/List.cpp | 2 +- src/cli/Merge.cpp | 2 +- src/cli/keepassxc-cli.cpp | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/cli/Extract.cpp b/src/cli/Extract.cpp index b08039ae..cbdb2a39 100644 --- a/src/cli/Extract.cpp +++ b/src/cli/Extract.cpp @@ -36,7 +36,7 @@ int Extract::execute(int argc, char **argv) QCommandLineParser parser; parser.setApplicationDescription(QCoreApplication::translate("main", - "Extract and print a KeePassXC database file.")); + "Extract and print the content of a database.")); parser.addPositionalArgument("database", QCoreApplication::translate("main", "path of the database to extract.")); parser.process(app); diff --git a/src/cli/List.cpp b/src/cli/List.cpp index 2049cc1d..0c1389d2 100644 --- a/src/cli/List.cpp +++ b/src/cli/List.cpp @@ -63,7 +63,7 @@ int List::execute(int argc, char **argv) QCommandLineParser parser; parser.setApplicationDescription(QCoreApplication::translate("main", - "List the passwords in the database.")); + "List database entries.")); parser.addPositionalArgument("database", QCoreApplication::translate("main", "path of the database.")); parser.process(app); diff --git a/src/cli/Merge.cpp b/src/cli/Merge.cpp index caa8d19c..fdc81b9f 100644 --- a/src/cli/Merge.cpp +++ b/src/cli/Merge.cpp @@ -37,7 +37,7 @@ int Merge::execute(int argc, char** argv) QCoreApplication app(argc, argv); QCommandLineParser parser; - parser.setApplicationDescription(QCoreApplication::translate("main", "Merge 2 KeePassXC database files.")); + parser.setApplicationDescription(QCoreApplication::translate("main", "Merge two databases.")); parser.addPositionalArgument("database1", QCoreApplication::translate("main", "path of the database to merge into.")); parser.addPositionalArgument("database2", QCoreApplication::translate("main", "path of the database to merge from.")); diff --git a/src/cli/keepassxc-cli.cpp b/src/cli/keepassxc-cli.cpp index c96da4d1..700f1263 100644 --- a/src/cli/keepassxc-cli.cpp +++ b/src/cli/keepassxc-cli.cpp @@ -47,10 +47,10 @@ int main(int argc, char **argv) QString description("KeePassXC command line interface."); description = description.append(QString("\n\nAvailable commands:")); - description = description.append(QString("\n extract\tExtract and print a KeePassXC database file.")); + description = description.append(QString("\n extract\tExtract and print the content of a database.")); description = description.append(QString("\n entropy-meter\tCalculate password entropy.")); description = description.append(QString("\n list\t\tList database entries.")); - description = description.append(QString("\n merge\t\tMerge 2 KeePassXC database files.")); + description = description.append(QString("\n merge\t\tMerge two databases.")); parser.setApplicationDescription(QCoreApplication::translate("main", qPrintable(description))); parser.addPositionalArgument("command", QCoreApplication::translate("main", "Name of the command to execute.")); From 98911af39646997250bfe9c0d68b34bfe9ae4542 Mon Sep 17 00:00:00 2001 From: Louis-Bertrand Varin Date: Thu, 16 Feb 2017 12:53:34 -0500 Subject: [PATCH 81/93] Fixed indentation. --- src/cli/keepassxc-cli.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/cli/keepassxc-cli.cpp b/src/cli/keepassxc-cli.cpp index 700f1263..85b679bd 100644 --- a/src/cli/keepassxc-cli.cpp +++ b/src/cli/keepassxc-cli.cpp @@ -74,23 +74,23 @@ int main(int argc, char **argv) --argc; if (commandName == "list") { - argv[0] = const_cast("keepassxc-cli list"); - return List::execute(argc, argv); + argv[0] = const_cast("keepassxc-cli list"); + return List::execute(argc, argv); } if (commandName == "merge") { - argv[0] = const_cast("keepassxc-cli merge"); - return Merge::execute(argc, argv); + argv[0] = const_cast("keepassxc-cli merge"); + return Merge::execute(argc, argv); } if (commandName == "extract") { - argv[0] = const_cast("keepassxc-cli extract"); - return Extract::execute(argc, argv); + argv[0] = const_cast("keepassxc-cli extract"); + return Extract::execute(argc, argv); } if (commandName == "entropy-meter") { - argv[0] = const_cast("keepassxc-cli entropy-meter"); - return EntropyMeter::execute(argc, argv); + argv[0] = const_cast("keepassxc-cli entropy-meter"); + return EntropyMeter::execute(argc, argv); } qCritical("Invalid command %s.", qPrintable(commandName)); From e01e9715b925da073c79a92e9c882416ca84706c Mon Sep 17 00:00:00 2001 From: thez3ro Date: Thu, 16 Feb 2017 20:26:51 +0100 Subject: [PATCH 82/93] text for inserting password --- src/cli/Extract.cpp | 5 ++++- src/cli/List.cpp | 4 ++++ src/cli/Merge.cpp | 7 +++++-- 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/cli/Extract.cpp b/src/cli/Extract.cpp index cbdb2a39..5bfb1185 100644 --- a/src/cli/Extract.cpp +++ b/src/cli/Extract.cpp @@ -33,6 +33,7 @@ int Extract::execute(int argc, char **argv) { QCoreApplication app(argc, argv); + QTextStream out(stdout); QCommandLineParser parser; parser.setApplicationDescription(QCoreApplication::translate("main", @@ -46,6 +47,9 @@ int Extract::execute(int argc, char **argv) return EXIT_FAILURE; } + out << "Insert the database password\n> "; + out.flush(); + static QTextStream inputTextStream(stdin, QIODevice::ReadOnly); QString line = inputTextStream.readLine(); CompositeKey key = CompositeKey::readFromLine(line); @@ -78,7 +82,6 @@ int Extract::execute(int argc, char **argv) return EXIT_FAILURE; } - QTextStream out(stdout); out << xmlData.constData() << "\n"; return EXIT_SUCCESS; diff --git a/src/cli/List.cpp b/src/cli/List.cpp index 0c1389d2..1702a469 100644 --- a/src/cli/List.cpp +++ b/src/cli/List.cpp @@ -60,6 +60,7 @@ void printGroup(Group* group, QString baseName, int depth) { int List::execute(int argc, char **argv) { QCoreApplication app(argc, argv); + QTextStream out(stdout); QCommandLineParser parser; parser.setApplicationDescription(QCoreApplication::translate("main", @@ -73,6 +74,9 @@ int List::execute(int argc, char **argv) return EXIT_FAILURE; } + out << "Insert the database password\n> "; + out.flush(); + static QTextStream inputTextStream(stdin, QIODevice::ReadOnly); QString line = inputTextStream.readLine(); CompositeKey key = CompositeKey::readFromLine(line); diff --git a/src/cli/Merge.cpp b/src/cli/Merge.cpp index fdc81b9f..0f4a9011 100644 --- a/src/cli/Merge.cpp +++ b/src/cli/Merge.cpp @@ -35,6 +35,7 @@ int Merge::execute(int argc, char** argv) { QCoreApplication app(argc, argv); + QTextStream out(stdout); QCommandLineParser parser; parser.setApplicationDescription(QCoreApplication::translate("main", "Merge two databases.")); @@ -53,8 +54,10 @@ int Merge::execute(int argc, char** argv) return EXIT_FAILURE; } + out << "Insert the database password\n> "; + out.flush(); + static QTextStream inputTextStream(stdin, QIODevice::ReadOnly); - QString line1 = inputTextStream.readLine(); CompositeKey key1 = CompositeKey::readFromLine(line1); @@ -128,7 +131,7 @@ int Merge::execute(int argc, char** argv) return EXIT_FAILURE; } - qDebug("Successfully merged the database files.\n"); + out << "Successfully merged the database files.\n"; return EXIT_SUCCESS; } From 15c2727a1d57980684218c60a36b1f29cf8e95a2 Mon Sep 17 00:00:00 2001 From: Louis-Bertrand Varin Date: Sun, 12 Mar 2017 13:34:56 -0400 Subject: [PATCH 83/93] Adding the show command. --- src/cli/CMakeLists.txt | 10 +++-- src/cli/Extract.cpp | 2 +- src/cli/List.cpp | 2 +- src/cli/Merge.cpp | 6 +-- src/cli/Show.cpp | 83 +++++++++++++++++++++++++++++++++++++++ src/cli/Show.h | 27 +++++++++++++ src/cli/keepassxc-cli.cpp | 28 ++++++++----- 7 files changed, 139 insertions(+), 19 deletions(-) create mode 100644 src/cli/Show.cpp create mode 100644 src/cli/Show.h diff --git a/src/cli/CMakeLists.txt b/src/cli/CMakeLists.txt index 0ed5d991..e090ad1d 100644 --- a/src/cli/CMakeLists.txt +++ b/src/cli/CMakeLists.txt @@ -14,14 +14,16 @@ # along with this program. If not, see . set(cli_SOURCES + EntropyMeter.cpp + EntropyMeter.h + Extract.cpp + Extract.h List.cpp List.h Merge.cpp Merge.h - EntropyMeter.cpp - EntropyMeter.h - Extract.cpp - Extract.h) + Show.cpp + Show.h) add_library(cli STATIC ${cli_SOURCES}) target_link_libraries(cli Qt5::Core Qt5::Widgets) diff --git a/src/cli/Extract.cpp b/src/cli/Extract.cpp index 5bfb1185..81a9ddf0 100644 --- a/src/cli/Extract.cpp +++ b/src/cli/Extract.cpp @@ -38,7 +38,7 @@ int Extract::execute(int argc, char **argv) QCommandLineParser parser; parser.setApplicationDescription(QCoreApplication::translate("main", "Extract and print the content of a database.")); - parser.addPositionalArgument("database", QCoreApplication::translate("main", "path of the database to extract.")); + parser.addPositionalArgument("database", QCoreApplication::translate("main", "Path of the database to extract.")); parser.process(app); const QStringList args = parser.positionalArguments(); diff --git a/src/cli/List.cpp b/src/cli/List.cpp index 1702a469..cfeba3ce 100644 --- a/src/cli/List.cpp +++ b/src/cli/List.cpp @@ -65,7 +65,7 @@ int List::execute(int argc, char **argv) QCommandLineParser parser; parser.setApplicationDescription(QCoreApplication::translate("main", "List database entries.")); - parser.addPositionalArgument("database", QCoreApplication::translate("main", "path of the database.")); + parser.addPositionalArgument("database", QCoreApplication::translate("main", "Path of the database.")); parser.process(app); const QStringList args = parser.positionalArguments(); diff --git a/src/cli/Merge.cpp b/src/cli/Merge.cpp index 0f4a9011..404892f7 100644 --- a/src/cli/Merge.cpp +++ b/src/cli/Merge.cpp @@ -39,11 +39,11 @@ int Merge::execute(int argc, char** argv) QCommandLineParser parser; parser.setApplicationDescription(QCoreApplication::translate("main", "Merge two databases.")); - parser.addPositionalArgument("database1", QCoreApplication::translate("main", "path of the database to merge into.")); - parser.addPositionalArgument("database2", QCoreApplication::translate("main", "path of the database to merge from.")); + parser.addPositionalArgument("database1", QCoreApplication::translate("main", "Path of the database to merge into.")); + parser.addPositionalArgument("database2", QCoreApplication::translate("main", "Path of the database to merge from.")); QCommandLineOption samePasswordOption(QStringList() << "s" << "same-password", - QCoreApplication::translate("main", "use the same password for both database files.")); + QCoreApplication::translate("main", "Use the same password for both database files.")); parser.addOption(samePasswordOption); parser.process(app); diff --git a/src/cli/Show.cpp b/src/cli/Show.cpp new file mode 100644 index 00000000..c4a3b1ac --- /dev/null +++ b/src/cli/Show.cpp @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2017 KeePassXC Team + * + * 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 + * the Free Software Foundation, either version 2 or (at your option) + * version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include + +#include "Show.h" + +#include +#include +#include +#include +#include + +#include "core/Database.h" +#include "core/Entry.h" +#include "core/Group.h" +#include "format/KeePass2Reader.h" +#include "keys/CompositeKey.h" + +int Show::execute(int argc, char **argv) +{ + QCoreApplication app(argc, argv); + QTextStream out(stdout); + + QCommandLineParser parser; + parser.setApplicationDescription(QCoreApplication::translate("main", + "Show a password.")); + parser.addPositionalArgument("database", QCoreApplication::translate("main", "Path of the database.")); + parser.addPositionalArgument("uuid", QCoreApplication::translate("main", "Uuid of the entry to show")); + parser.process(app); + + const QStringList args = parser.positionalArguments(); + if (args.size() != 2) { + parser.showHelp(); + return EXIT_FAILURE; + } + + out << "Insert the database password\n> "; + out.flush(); + + static QTextStream inputTextStream(stdin, QIODevice::ReadOnly); + QString line = inputTextStream.readLine(); + CompositeKey key = CompositeKey::readFromLine(line); + + QString databaseFilename = args.at(0); + QFile dbFile(databaseFilename); + if (!dbFile.exists()) { + qCritical("File %s does not exist.", qPrintable(databaseFilename)); + return EXIT_FAILURE; + } + if (!dbFile.open(QIODevice::ReadOnly)) { + qCritical("Unable to open file %s.", qPrintable(databaseFilename)); + return EXIT_FAILURE; + } + + KeePass2Reader reader; + Database* db = reader.readDatabase(&dbFile, key); + + if (reader.hasError()) { + qCritical("Error while parsing the database:\n%s\n", qPrintable(reader.errorString())); + return EXIT_FAILURE; + } + + Uuid uuid = Uuid::fromHex(args.at(1)); + Entry* entry = db->resolveEntry(uuid); + out << entry->password() << "\n"; + return EXIT_SUCCESS; +} diff --git a/src/cli/Show.h b/src/cli/Show.h new file mode 100644 index 00000000..aa06b5c9 --- /dev/null +++ b/src/cli/Show.h @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2017 KeePassXC Team + * + * 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 + * the Free Software Foundation, either version 2 or (at your option) + * version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef KEEPASSXC_SHOW_H +#define KEEPASSXC_SHOW_H + +class Show +{ +public: + static int execute(int argc, char** argv); +}; + +#endif // KEEPASSXC_SHOW_H diff --git a/src/cli/keepassxc-cli.cpp b/src/cli/keepassxc-cli.cpp index 85b679bd..1802870e 100644 --- a/src/cli/keepassxc-cli.cpp +++ b/src/cli/keepassxc-cli.cpp @@ -21,10 +21,12 @@ #include #include +#include +#include #include #include -#include -#include +#include + #include "config-keepassx.h" #include "core/Tools.h" #include "crypto/Crypto.h" @@ -51,6 +53,7 @@ int main(int argc, char **argv) description = description.append(QString("\n entropy-meter\tCalculate password entropy.")); description = description.append(QString("\n list\t\tList database entries.")); description = description.append(QString("\n merge\t\tMerge two databases.")); + description = description.append(QString("\n show\t\tShow a password.")); parser.setApplicationDescription(QCoreApplication::translate("main", qPrintable(description))); parser.addPositionalArgument("command", QCoreApplication::translate("main", "Name of the command to execute.")); @@ -73,6 +76,16 @@ int main(int argc, char **argv) ++argv; --argc; + if (commandName == "entropy-meter") { + argv[0] = const_cast("keepassxc-cli entropy-meter"); + return EntropyMeter::execute(argc, argv); + } + + if (commandName == "extract") { + argv[0] = const_cast("keepassxc-cli extract"); + return Extract::execute(argc, argv); + } + if (commandName == "list") { argv[0] = const_cast("keepassxc-cli list"); return List::execute(argc, argv); @@ -83,14 +96,9 @@ int main(int argc, char **argv) return Merge::execute(argc, argv); } - if (commandName == "extract") { - argv[0] = const_cast("keepassxc-cli extract"); - return Extract::execute(argc, argv); - } - - if (commandName == "entropy-meter") { - argv[0] = const_cast("keepassxc-cli entropy-meter"); - return EntropyMeter::execute(argc, argv); + if (commandName == "show") { + argv[0] = const_cast("keepassxc-cli show"); + return Show::execute(argc, argv); } qCritical("Invalid command %s.", qPrintable(commandName)); From db1bf889347d3327bcd6cd0a175780746f9dcee7 Mon Sep 17 00:00:00 2001 From: Louis-Bertrand Varin Date: Sun, 12 Mar 2017 13:37:20 -0400 Subject: [PATCH 84/93] Handling entry not found. --- src/cli/Show.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/cli/Show.cpp b/src/cli/Show.cpp index c4a3b1ac..b596e521 100644 --- a/src/cli/Show.cpp +++ b/src/cli/Show.cpp @@ -78,6 +78,11 @@ int Show::execute(int argc, char **argv) Uuid uuid = Uuid::fromHex(args.at(1)); Entry* entry = db->resolveEntry(uuid); + if (entry == nullptr) { + qCritical("No entry found with uuid %s", qPrintable(uuid.toHex())); + return EXIT_FAILURE; + } + out << entry->password() << "\n"; return EXIT_SUCCESS; } From 993f90cb2cfcc71da99ea6dfef092fab9463169a Mon Sep 17 00:00:00 2001 From: Louis-Bertrand Varin Date: Sun, 12 Mar 2017 13:47:05 -0400 Subject: [PATCH 85/93] Extracting openDatabaseFile. --- src/cli/Show.cpp | 20 ++------------------ src/core/Database.cpp | 25 +++++++++++++++++++++++++ src/core/Database.h | 1 + 3 files changed, 28 insertions(+), 18 deletions(-) diff --git a/src/cli/Show.cpp b/src/cli/Show.cpp index b596e521..9222a093 100644 --- a/src/cli/Show.cpp +++ b/src/cli/Show.cpp @@ -22,14 +22,12 @@ #include #include -#include #include #include #include "core/Database.h" #include "core/Entry.h" #include "core/Group.h" -#include "format/KeePass2Reader.h" #include "keys/CompositeKey.h" int Show::execute(int argc, char **argv) @@ -57,22 +55,8 @@ int Show::execute(int argc, char **argv) QString line = inputTextStream.readLine(); CompositeKey key = CompositeKey::readFromLine(line); - QString databaseFilename = args.at(0); - QFile dbFile(databaseFilename); - if (!dbFile.exists()) { - qCritical("File %s does not exist.", qPrintable(databaseFilename)); - return EXIT_FAILURE; - } - if (!dbFile.open(QIODevice::ReadOnly)) { - qCritical("Unable to open file %s.", qPrintable(databaseFilename)); - return EXIT_FAILURE; - } - - KeePass2Reader reader; - Database* db = reader.readDatabase(&dbFile, key); - - if (reader.hasError()) { - qCritical("Error while parsing the database:\n%s\n", qPrintable(reader.errorString())); + Database* db = Database::openDatabaseFile(args.at(0), key); + if (db == nullptr) { return EXIT_FAILURE; } diff --git a/src/core/Database.cpp b/src/core/Database.cpp index 23b56414..4aa9e3f5 100644 --- a/src/core/Database.cpp +++ b/src/core/Database.cpp @@ -25,6 +25,7 @@ #include "core/Metadata.h" #include "crypto/Random.h" #include "format/KeePass2.h" +#include "format/KeePass2Reader.h" QHash Database::m_uuidMap; @@ -355,3 +356,27 @@ const CompositeKey & Database::key() const return m_data.key; } +Database* Database::openDatabaseFile(QString fileName, CompositeKey key) +{ + + QFile dbFile(fileName); + if (!dbFile.exists()) { + qCritical("File %s does not exist.", qPrintable(fileName)); + return nullptr; + } + if (!dbFile.open(QIODevice::ReadOnly)) { + qCritical("Unable to open file %s.", qPrintable(fileName)); + return nullptr; + } + + KeePass2Reader reader; + Database* db = reader.readDatabase(&dbFile, key); + + if (reader.hasError()) { + qCritical("Error while parsing the database: %s", qPrintable(reader.errorString())); + return nullptr; + } + + return db; + +} diff --git a/src/core/Database.h b/src/core/Database.h index 3767bb30..16c14960 100644 --- a/src/core/Database.h +++ b/src/core/Database.h @@ -118,6 +118,7 @@ public: Uuid uuid(); static Database* databaseByUuid(const Uuid& uuid); + static Database* openDatabaseFile(QString fileName, CompositeKey key); signals: void groupDataChanged(Group* group); From 780e23301b5bf5e27e06ac8f2397f633ce7a3424 Mon Sep 17 00:00:00 2001 From: Louis-Bertrand Varin Date: Sun, 12 Mar 2017 13:56:30 -0400 Subject: [PATCH 86/93] Using openDatabaseFile in Merge. --- src/cli/Merge.cpp | 43 ++++++------------------------------------- 1 file changed, 6 insertions(+), 37 deletions(-) diff --git a/src/cli/Merge.cpp b/src/cli/Merge.cpp index 404892f7..118213b8 100644 --- a/src/cli/Merge.cpp +++ b/src/cli/Merge.cpp @@ -21,13 +21,11 @@ #include #include -#include #include #include #include #include "core/Database.h" -#include "format/KeePass2Reader.h" #include "format/KeePass2Writer.h" #include "keys/CompositeKey.h" @@ -71,50 +69,21 @@ int Merge::execute(int argc, char** argv) } - QString databaseFilename1 = args.at(0); - QFile dbFile1(databaseFilename1); - if (!dbFile1.exists()) { - qCritical("File %s does not exist.", qPrintable(databaseFilename1)); - return EXIT_FAILURE; - } - if (!dbFile1.open(QIODevice::ReadOnly)) { - qCritical("Unable to open file %s.", qPrintable(databaseFilename1)); + Database* db1 = Database::openDatabaseFile(args.at(0), key1); + if (db1 == nullptr) { return EXIT_FAILURE; } - KeePass2Reader reader1; - Database* db1 = reader1.readDatabase(&dbFile1, key1); - - if (reader1.hasError()) { - qCritical("Error while parsing the database:\n%s\n", qPrintable(reader1.errorString())); - return EXIT_FAILURE; - } - - - QString databaseFilename2 = args.at(1); - QFile dbFile2(databaseFilename2); - if (!dbFile2.exists()) { - qCritical("File %s does not exist.", qPrintable(databaseFilename2)); - return EXIT_FAILURE; - } - if (!dbFile2.open(QIODevice::ReadOnly)) { - qCritical("Unable to open file %s.", qPrintable(databaseFilename2)); - return EXIT_FAILURE; - } - - KeePass2Reader reader2; - Database* db2 = reader2.readDatabase(&dbFile2, key2); - - if (reader2.hasError()) { - qCritical("Error while parsing the database:\n%s\n", qPrintable(reader2.errorString())); + Database* db2 = Database::openDatabaseFile(args.at(1), key2); + if (db2 == nullptr) { return EXIT_FAILURE; } db1->merge(db2); - QSaveFile saveFile(databaseFilename1); + QSaveFile saveFile(args.at(0)); if (!saveFile.open(QIODevice::WriteOnly)) { - qCritical("Unable to open file %s for writing.", qPrintable(databaseFilename1)); + qCritical("Unable to open file %s for writing.", qPrintable(args.at(0))); return EXIT_FAILURE; } From cf2334391110d5443c37c00241bd53beee06d7db Mon Sep 17 00:00:00 2001 From: Louis-Bertrand Varin Date: Sun, 12 Mar 2017 13:57:44 -0400 Subject: [PATCH 87/93] Using openDatabaseFile in List. --- src/cli/List.cpp | 20 ++------------------ 1 file changed, 2 insertions(+), 18 deletions(-) diff --git a/src/cli/List.cpp b/src/cli/List.cpp index cfeba3ce..b2d673f5 100644 --- a/src/cli/List.cpp +++ b/src/cli/List.cpp @@ -22,14 +22,12 @@ #include #include -#include #include #include #include "core/Database.h" #include "core/Entry.h" #include "core/Group.h" -#include "format/KeePass2Reader.h" #include "keys/CompositeKey.h" void printGroup(Group* group, QString baseName, int depth) { @@ -81,22 +79,8 @@ int List::execute(int argc, char **argv) QString line = inputTextStream.readLine(); CompositeKey key = CompositeKey::readFromLine(line); - QString databaseFilename = args.at(0); - QFile dbFile(databaseFilename); - if (!dbFile.exists()) { - qCritical("File %s does not exist.", qPrintable(databaseFilename)); - return EXIT_FAILURE; - } - if (!dbFile.open(QIODevice::ReadOnly)) { - qCritical("Unable to open file %s.", qPrintable(databaseFilename)); - return EXIT_FAILURE; - } - - KeePass2Reader reader; - Database* db = reader.readDatabase(&dbFile, key); - - if (reader.hasError()) { - qCritical("Error while parsing the database:\n%s\n", qPrintable(reader.errorString())); + Database* db = Database::openDatabaseFile(args.at(0), key); + if (db == nullptr) { return EXIT_FAILURE; } From fd9d372e6ab9780cb433d8ec334fcedd6550290c Mon Sep 17 00:00:00 2001 From: Louis-Bertrand Varin Date: Sun, 12 Mar 2017 14:17:03 -0400 Subject: [PATCH 88/93] Adding second prompt for merge. --- src/cli/Merge.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/cli/Merge.cpp b/src/cli/Merge.cpp index 118213b8..aa399dd5 100644 --- a/src/cli/Merge.cpp +++ b/src/cli/Merge.cpp @@ -52,7 +52,7 @@ int Merge::execute(int argc, char** argv) return EXIT_FAILURE; } - out << "Insert the database password\n> "; + out << "Insert the first database password\n> "; out.flush(); static QTextStream inputTextStream(stdin, QIODevice::ReadOnly); @@ -64,6 +64,8 @@ int Merge::execute(int argc, char** argv) key2 = *key1.clone(); } else { + out << "Insert the second database password\n> "; + out.flush(); QString line2 = inputTextStream.readLine(); key2 = CompositeKey::readFromLine(line2); } From a661c17eca4659303880b174ba9850b5b0c05c6e Mon Sep 17 00:00:00 2001 From: Louis-Bertrand Varin Date: Sun, 12 Mar 2017 14:24:22 -0400 Subject: [PATCH 89/93] Adding group uuid to list. --- src/cli/List.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cli/List.cpp b/src/cli/List.cpp index b2d673f5..1e348810 100644 --- a/src/cli/List.cpp +++ b/src/cli/List.cpp @@ -37,7 +37,7 @@ void printGroup(Group* group, QString baseName, int depth) { QString groupName = baseName + group->name() + "/"; QString indentation = QString(" ").repeated(depth); - out << indentation << groupName << "\n"; + out << indentation << groupName << " " << group->uuid().toHex() << "\n"; out.flush(); if (group->entries().isEmpty() && group->children().isEmpty()) { From 558c75a452ccafa925c658db48a9e68e875c664b Mon Sep 17 00:00:00 2001 From: thez3ro Date: Thu, 16 Mar 2017 18:41:12 +0100 Subject: [PATCH 90/93] Add ASAN to keepassxc-cli --- src/cli/keepassxc-cli.cpp | 46 ++++++++++++++++++++++----------------- 1 file changed, 26 insertions(+), 20 deletions(-) diff --git a/src/cli/keepassxc-cli.cpp b/src/cli/keepassxc-cli.cpp index 1802870e..b27b7483 100644 --- a/src/cli/keepassxc-cli.cpp +++ b/src/cli/keepassxc-cli.cpp @@ -31,6 +31,10 @@ #include "core/Tools.h" #include "crypto/Crypto.h" +#if defined(WITH_ASAN) && defined(WITH_LSAN) +#include +#endif + int main(int argc, char **argv) { #ifdef QT_NO_DEBUG @@ -76,33 +80,35 @@ int main(int argc, char **argv) ++argv; --argc; + int exitCode = EXIT_FAILURE; + if (commandName == "entropy-meter") { argv[0] = const_cast("keepassxc-cli entropy-meter"); - return EntropyMeter::execute(argc, argv); - } - - if (commandName == "extract") { + exitCode = EntropyMeter::execute(argc, argv); + } else if (commandName == "extract") { argv[0] = const_cast("keepassxc-cli extract"); - return Extract::execute(argc, argv); - } - - if (commandName == "list") { + exitCode = Extract::execute(argc, argv); + } else if (commandName == "list") { argv[0] = const_cast("keepassxc-cli list"); - return List::execute(argc, argv); - } - - if (commandName == "merge") { + exitCode = List::execute(argc, argv); + } else if (commandName == "merge") { argv[0] = const_cast("keepassxc-cli merge"); - return Merge::execute(argc, argv); - } - - if (commandName == "show") { + exitCode = Merge::execute(argc, argv); + } else if (commandName == "show") { argv[0] = const_cast("keepassxc-cli show"); - return Show::execute(argc, argv); + exitCode = Show::execute(argc, argv); + } else { + qCritical("Invalid command %s.", qPrintable(commandName)); + parser.showHelp(); + exitCode = EXIT_FAILURE; } - qCritical("Invalid command %s.", qPrintable(commandName)); - parser.showHelp(); - return EXIT_FAILURE; +#if defined(WITH_ASAN) && defined(WITH_LSAN) + // do leak check here to prevent massive tail of end-of-process leak errors from third-party libraries + __lsan_do_leak_check(); + __lsan_disable(); +#endif + + return exitCode; } From e3602e3c75489fc39f752d05147ab189a3070555 Mon Sep 17 00:00:00 2001 From: thez3ro Date: Thu, 16 Mar 2017 20:31:14 +0100 Subject: [PATCH 91/93] fix regex for placeholders, fix #402, add regression test --- src/core/Entry.cpp | 3 ++- src/gui/DatabaseWidget.cpp | 18 +++++++-------- src/gui/entry/EntryModel.cpp | 6 ++--- tests/gui/TestGui.cpp | 44 ++++++++++++++++++++++++++++++++++++ tests/gui/TestGui.h | 1 + 5 files changed, 59 insertions(+), 13 deletions(-) diff --git a/src/core/Entry.cpp b/src/core/Entry.cpp index 7edf7e78..d1672c5b 100644 --- a/src/core/Entry.cpp +++ b/src/core/Entry.cpp @@ -649,7 +649,8 @@ const Database* Entry::database() const QString Entry::resolveMultiplePlaceholders(const QString& str) const { QString result = str; - QRegExp tmplRegEx("({.*})", Qt::CaseInsensitive, QRegExp::RegExp2); + QRegExp tmplRegEx("(\\{.*\\})", Qt::CaseInsensitive, QRegExp::RegExp2); + tmplRegEx.setMinimal(true); QStringList tmplList; int pos = 0; diff --git a/src/gui/DatabaseWidget.cpp b/src/gui/DatabaseWidget.cpp index 8a40b636..ca3257d0 100644 --- a/src/gui/DatabaseWidget.cpp +++ b/src/gui/DatabaseWidget.cpp @@ -407,7 +407,7 @@ void DatabaseWidget::copyTitle() return; } - setClipboardTextAndMinimize(currentEntry->resolvePlaceholder(currentEntry->title())); + setClipboardTextAndMinimize(currentEntry->resolveMultiplePlaceholders(currentEntry->title())); } void DatabaseWidget::copyUsername() @@ -418,7 +418,7 @@ void DatabaseWidget::copyUsername() return; } - setClipboardTextAndMinimize(currentEntry->resolvePlaceholder(currentEntry->username())); + setClipboardTextAndMinimize(currentEntry->resolveMultiplePlaceholders(currentEntry->username())); } void DatabaseWidget::copyPassword() @@ -429,7 +429,7 @@ void DatabaseWidget::copyPassword() return; } - setClipboardTextAndMinimize(currentEntry->resolvePlaceholder(currentEntry->password())); + setClipboardTextAndMinimize(currentEntry->resolveMultiplePlaceholders(currentEntry->password())); } void DatabaseWidget::copyURL() @@ -440,7 +440,7 @@ void DatabaseWidget::copyURL() return; } - setClipboardTextAndMinimize(currentEntry->resolvePlaceholder(currentEntry->url())); + setClipboardTextAndMinimize(currentEntry->resolveMultiplePlaceholders(currentEntry->url())); } void DatabaseWidget::copyNotes() @@ -451,7 +451,7 @@ void DatabaseWidget::copyNotes() return; } - setClipboardTextAndMinimize(currentEntry->resolvePlaceholder(currentEntry->notes())); + setClipboardTextAndMinimize(currentEntry->resolveMultiplePlaceholders(currentEntry->notes())); } void DatabaseWidget::copyAttribute(QAction* action) @@ -1176,7 +1176,7 @@ bool DatabaseWidget::currentEntryHasUsername() Q_ASSERT(false); return false; } - return !currentEntry->resolvePlaceholder(currentEntry->username()).isEmpty(); + return !currentEntry->resolveMultiplePlaceholders(currentEntry->username()).isEmpty(); } bool DatabaseWidget::currentEntryHasPassword() @@ -1186,7 +1186,7 @@ bool DatabaseWidget::currentEntryHasPassword() Q_ASSERT(false); return false; } - return !currentEntry->resolvePlaceholder(currentEntry->password()).isEmpty(); + return !currentEntry->resolveMultiplePlaceholders(currentEntry->password()).isEmpty(); } bool DatabaseWidget::currentEntryHasUrl() @@ -1196,7 +1196,7 @@ bool DatabaseWidget::currentEntryHasUrl() Q_ASSERT(false); return false; } - return !currentEntry->resolvePlaceholder(currentEntry->url()).isEmpty(); + return !currentEntry->resolveMultiplePlaceholders(currentEntry->url()).isEmpty(); } bool DatabaseWidget::currentEntryHasNotes() @@ -1206,7 +1206,7 @@ bool DatabaseWidget::currentEntryHasNotes() Q_ASSERT(false); return false; } - return !currentEntry->resolvePlaceholder(currentEntry->notes()).isEmpty(); + return !currentEntry->resolveMultiplePlaceholders(currentEntry->notes()).isEmpty(); } GroupView* DatabaseWidget::groupView() { diff --git a/src/gui/entry/EntryModel.cpp b/src/gui/entry/EntryModel.cpp index b28eaed4..6bc10376 100644 --- a/src/gui/entry/EntryModel.cpp +++ b/src/gui/entry/EntryModel.cpp @@ -139,19 +139,19 @@ QVariant EntryModel::data(const QModelIndex& index, int role) const } break; case Title: - result = entry->resolvePlaceholder(entry->title()); + result = entry->resolveMultiplePlaceholders(entry->title()); if (attr->isReference(EntryAttributes::TitleKey)) { result.prepend(tr("Ref: ","Reference abbreviation")); } return result; case Username: - result = entry->resolvePlaceholder(entry->username()); + result = entry->resolveMultiplePlaceholders(entry->username()); if (attr->isReference(EntryAttributes::UserNameKey)) { result.prepend(tr("Ref: ","Reference abbreviation")); } return result; case Url: - result = entry->resolvePlaceholder(entry->url()); + result = entry->resolveMultiplePlaceholders(entry->url()); if (attr->isReference(EntryAttributes::URLKey)) { result.prepend(tr("Ref: ","Reference abbreviation")); } diff --git a/tests/gui/TestGui.cpp b/tests/gui/TestGui.cpp index 7df5942e..1cb3d6ad 100644 --- a/tests/gui/TestGui.cpp +++ b/tests/gui/TestGui.cpp @@ -588,6 +588,50 @@ void TestGui::testCloneEntry() QCOMPARE(entryClone->title(), entryOrg->title() + QString(" - Clone")); } +void TestGui::testEntryPlaceholders() +{ + QToolBar* toolBar = m_mainWindow->findChild("toolBar"); + EntryView* entryView = m_dbWidget->findChild("entryView"); + + // Find the new entry action + QAction* entryNewAction = m_mainWindow->findChild("actionEntryNew"); + QVERIFY(entryNewAction->isEnabled()); + + // Find the button associated with the new entry action + QWidget* entryNewWidget = toolBar->widgetForAction(entryNewAction); + QVERIFY(entryNewWidget->isVisible()); + QVERIFY(entryNewWidget->isEnabled()); + + // Click the new entry button and check that we enter edit mode + QTest::mouseClick(entryNewWidget, Qt::LeftButton); + QCOMPARE(m_dbWidget->currentMode(), DatabaseWidget::EditMode); + + // Add entry "test" and confirm added + EditEntryWidget* editEntryWidget = m_dbWidget->findChild("editEntryWidget"); + QLineEdit* titleEdit = editEntryWidget->findChild("titleEdit"); + QTest::keyClicks(titleEdit, "test"); + QLineEdit* usernameEdit = editEntryWidget->findChild("usernameEdit"); + QTest::keyClicks(usernameEdit, "john"); + QLineEdit* urlEdit = editEntryWidget->findChild("urlEdit"); + QTest::keyClicks(urlEdit, "{TITLE}.{USERNAME}"); + QDialogButtonBox* editEntryWidgetButtonBox = editEntryWidget->findChild("buttonBox"); + QTest::mouseClick(editEntryWidgetButtonBox->button(QDialogButtonBox::Ok), Qt::LeftButton); + + QCOMPARE(entryView->model()->rowCount(), 2); + + QCOMPARE(m_dbWidget->currentMode(), DatabaseWidget::ViewMode); + QModelIndex item = entryView->model()->index(1, 1); + Entry* entry = entryView->entryFromIndex(item); + + QCOMPARE(entry->title(), QString("test")); + QCOMPARE(entry->url(), QString("{TITLE}.{USERNAME}")); + + // Test password copy + QClipboard *clipboard = QApplication::clipboard(); + m_dbWidget->copyURL(); + QTRY_COMPARE(clipboard->text(), QString("test.john")); +} + void TestGui::testDragAndDropEntry() { EntryView* entryView = m_dbWidget->findChild("entryView"); diff --git a/tests/gui/TestGui.h b/tests/gui/TestGui.h index 1ae29700..d05ab8f5 100644 --- a/tests/gui/TestGui.h +++ b/tests/gui/TestGui.h @@ -48,6 +48,7 @@ private slots: void testSearch(); void testDeleteEntry(); void testCloneEntry(); + void testEntryPlaceholders(); void testDragAndDropEntry(); void testDragAndDropGroup(); void testSaveAs(); From 0e5a1cc8e479db1d03cc8e13228a526e6c558655 Mon Sep 17 00:00:00 2001 From: thez3ro Date: Thu, 16 Mar 2017 20:38:56 +0100 Subject: [PATCH 92/93] resolve placeholders for custom attributes --- src/gui/DatabaseWidget.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gui/DatabaseWidget.cpp b/src/gui/DatabaseWidget.cpp index ca3257d0..5caf662c 100644 --- a/src/gui/DatabaseWidget.cpp +++ b/src/gui/DatabaseWidget.cpp @@ -462,7 +462,7 @@ void DatabaseWidget::copyAttribute(QAction* action) return; } - setClipboardTextAndMinimize(currentEntry->attributes()->value(action->text())); + setClipboardTextAndMinimize(currentEntry->resolveMultiplePlaceholders(currentEntry->attributes()->value(action->text()))); } void DatabaseWidget::setClipboardTextAndMinimize(const QString& text) From 7e515d9d5b91db0001b8c19fad0eaba0460b9900 Mon Sep 17 00:00:00 2001 From: Florin Andrei Date: Thu, 16 Mar 2017 11:58:16 -0700 Subject: [PATCH 93/93] Update README.md typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index fd439ded..a277e5b5 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ KeePass Cross-platform Community Edition - Auto-Type on all three major platforms (Linux, Windows, OS X) - Stand-alone password generator - Password strength meter -- YukiKey HMAC-SHA1 authentication for unlocking databases +- YubiKey HMAC-SHA1 authentication for unlocking databases - Using website favicons as entry icons - Merging of databases - Automatic reload when the database changed on disk