0
0
mirror of https://github.com/keepassxreboot/keepassxc.git synced 2024-09-20 12:22:16 +02:00

Correct simultaneous saving with Yubikey

* Move mutex lock to right before challenge call and wait for up to 1 second for unlock
* Fix bug where ALREADY_RUNNING was interpreted as success and causing database corruption
This commit is contained in:
Jonathan White 2018-10-30 17:17:52 -04:00
parent d84ba23c81
commit 1a2721529d
4 changed files with 16 additions and 40 deletions

View File

@ -18,6 +18,7 @@
#include "keys/YkChallengeResponseKey.h"
#include "keys/drivers/YubiKey.h"
#include "core/AsyncTask.h"
#include "core/Tools.h"
#include "crypto/CryptoHash.h"
#include "crypto/Random.h"
@ -56,10 +57,8 @@ bool YkChallengeResponseKey::challenge(const QByteArray& challenge)
return this->challenge(challenge, 2);
}
bool YkChallengeResponseKey::challenge(const QByteArray& challenge, unsigned retries)
bool YkChallengeResponseKey::challenge(const QByteArray& challenge, unsigned int retries)
{
Q_ASSERT(retries > 0);
do {
--retries;
@ -67,28 +66,17 @@ bool YkChallengeResponseKey::challenge(const QByteArray& challenge, unsigned ret
emit userInteractionRequired();
}
QFuture<YubiKey::ChallengeResult> future = QtConcurrent::run(
[this, challenge]() { return YubiKey::instance()->challenge(m_slot, true, challenge, m_key); });
QEventLoop loop;
QFutureWatcher<YubiKey::ChallengeResult> watcher;
connect(&watcher, SIGNAL(finished()), &loop, SLOT(quit()));
watcher.setFuture(future);
loop.exec();
auto result = AsyncTask::runAndWaitForFuture([this, challenge]() {
return YubiKey::instance()->challenge(m_slot, true, challenge, m_key);
});
if (m_blocking) {
emit userConfirmed();
}
if (future.result() != YubiKey::ERROR) {
if (result == YubiKey::SUCCESS) {
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()) {
continue;
}
} while (retries > 0);
return false;

View File

@ -35,7 +35,7 @@ public:
QByteArray rawKey() const override;
bool challenge(const QByteArray& challenge) override;
bool challenge(const QByteArray& challenge, unsigned retries);
bool challenge(const QByteArray& challenge, unsigned int retries);
QString getName() const;
bool isBlocking() const;

View File

@ -161,19 +161,14 @@ bool YubiKey::getSerial(unsigned int& serial)
YubiKey::ChallengeResult YubiKey::challenge(int slot, bool mayBlock, const QByteArray& challenge, QByteArray& response)
{
if (!m_mutex.tryLock()) {
return ALREADY_RUNNING;
// ensure that YubiKey::init() succeeded
if (!init()) {
return ERROR;
}
int yk_cmd = (slot == 1) ? SLOT_CHAL_HMAC1 : SLOT_CHAL_HMAC2;
QByteArray paddedChallenge = challenge;
// ensure that YubiKey::init() succeeded
if (!init()) {
m_mutex.unlock();
return ERROR;
}
// yk_challenge_response() insists on 64 byte response buffer */
response.clear();
response.resize(64);
@ -194,9 +189,12 @@ YubiKey::ChallengeResult YubiKey::challenge(int slot, bool mayBlock, const QByte
c = reinterpret_cast<const unsigned char*>(paddedChallenge.constData());
r = reinterpret_cast<unsigned char*>(response.data());
int ret = yk_challenge_response(m_yk, yk_cmd, mayBlock, paddedChallenge.size(), c, response.size(), r);
emit challenged();
// Try to grab a lock for 1 second, fail out if not possible
if (!m_mutex.tryLock(1000)) {
return ALREADY_RUNNING;
}
int ret = yk_challenge_response(m_yk, yk_cmd, mayBlock, paddedChallenge.size(), c, response.size(), r);
m_mutex.unlock();
if (!ret) {

View File

@ -68,7 +68,7 @@ public:
* @param mayBlock operation is allowed to block
* @param challenge challenge input to YubiKey
* @param response response output from YubiKey
* @return true on success
* @return challenge result
*/
ChallengeResult challenge(int slot, bool mayBlock, const QByteArray& challenge, QByteArray& response);
@ -98,21 +98,11 @@ signals:
*/
void detectComplete();
/**
* Emitted when the YubiKey was challenged and has returned a response.
*/
void challenged();
/**
* Emitted when no Yubikey could be found.
*/
void notFound();
/**
* Emitted when detection is already running.
*/
void alreadyRunning();
private:
explicit YubiKey();
static YubiKey* m_instance;