mirror of
https://github.com/keepassxreboot/keepassxc.git
synced 2025-03-30 18:08:16 -04: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:
parent
d84ba23c81
commit
1a2721529d
@ -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;
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
|
Loading…
x
Reference in New Issue
Block a user