Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix detection of hardware keys in keepassxc-cli #7593

Merged
merged 2 commits into from
Apr 3, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,9 @@ macro(check_add_gcc_compiler_flag FLAG)
endmacro(check_add_gcc_compiler_flag)

add_definitions(-DQT_NO_EXCEPTIONS -DQT_STRICT_ITERATORS -DQT_NO_CAST_TO_ASCII)
if(NOT IS_DEBUG_BUILD)
add_definitions(-DQT_NO_DEBUG_OUTPUT)
endif()

if(WITH_APP_BUNDLE)
add_definitions(-DWITH_APP_BUNDLE)
Expand Down
8 changes: 4 additions & 4 deletions share/translations/keepassxc_en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7233,10 +7233,6 @@ Please consider generating a new key file.</source>
<source>Invalid YubiKey serial %1</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Please present or touch your YubiKey to continue…</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Enter password to encrypt database (optional): </source>
<translation type="unfinished"></translation>
Expand Down Expand Up @@ -7760,6 +7756,10 @@ Kernel: %3 %4</source>
<source>Failed to sign challenge using Windows Hello.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Please present or touch your YubiKey to continue.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>QtIOCompressor</name>
Expand Down
1 change: 0 additions & 1 deletion src/autotype/AutoType.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -404,7 +404,6 @@ void AutoType::startGlobalAutoType(const QString& search)
tr("KeePassXC requires the Accessibility and Screen Recorder permission in order to perform global "
"Auto-Type. Screen Recording is necessary to use the window title to find entries. If you "
"already granted permission, you may have to restart KeePassXC."));
qDebug() << "Oh noes macOS.";
return;
}
}
Expand Down
8 changes: 4 additions & 4 deletions src/cli/Utils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -167,14 +167,14 @@ namespace Utils
}
}

auto conn = QObject::connect(YubiKey::instance(), &YubiKey::userInteractionRequest, [&] {
err << QObject::tr("Please present or touch your YubiKey to continue") << "\n\n" << flush;
QObject::connect(YubiKey::instance(), &YubiKey::userInteractionRequest, [&] {
err << QObject::tr("Please present or touch your YubiKey to continue.") << "\n\n" << flush;
});

auto key = QSharedPointer<ChallengeResponseKey>(new ChallengeResponseKey({serial, slot}));
compositeKey->addChallengeResponseKey(key);

QObject::disconnect(conn);
YubiKey::instance()->findValidKeys();
}
#else
Q_UNUSED(yubiKeySlot);
Expand Down Expand Up @@ -313,7 +313,7 @@ namespace Utils
// Other platforms understand UTF-8
if (clipProcess->write(text.toUtf8()) == -1) {
#endif
qDebug("Unable to write to process : %s", qPrintable(clipProcess->errorString()));
qWarning("Unable to write to process : %s", qPrintable(clipProcess->errorString()));
}
clipProcess->waitForBytesWritten();
clipProcess->closeWriteChannel();
Expand Down
2 changes: 0 additions & 2 deletions src/core/Merger.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,6 @@ QStringList Merger::merge()
changes << mergeDeletions(m_context);
changes << mergeMetadata(m_context);

// qDebug("Merged %s", qPrintable(changes.join("\n\t")));

// At this point we have a list of changes we may want to show the user
if (!changes.isEmpty()) {
m_context.m_targetDb->markAsModified();
Expand Down
2 changes: 1 addition & 1 deletion src/gui/DatabaseOpenWidget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -456,7 +456,7 @@ void DatabaseOpenWidget::pollHardwareKey()
m_ui->hardwareKeyProgress->setVisible(true);
m_pollingHardwareKey = true;

YubiKey::instance()->findValidKeys();
YubiKey::instance()->findValidKeysAsync();
}

void DatabaseOpenWidget::hardwareKeyResponse(bool found)
Expand Down
2 changes: 1 addition & 1 deletion src/gui/databasekey/YubiKeyEditWidget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ void YubiKeyEditWidget::pollYubikey()
m_compUi->comboChallengeResponse->setEnabled(false);
m_compUi->yubikeyProgress->setVisible(true);

YubiKey::instance()->findValidKeys();
YubiKey::instance()->findValidKeysAsync();
#endif
}

Expand Down
68 changes: 34 additions & 34 deletions src/keys/drivers/YubiKey.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,52 +20,36 @@
#include "YubiKeyInterfacePCSC.h"
#include "YubiKeyInterfaceUSB.h"

#include <QtConcurrent>

YubiKey::YubiKey()
: m_interfaces_detect_mutex(QMutex::Recursive)
{
int num_interfaces = 0;

if (YubiKeyInterfaceUSB::instance()->isInitialized()) {
++num_interfaces;
connect(YubiKeyInterfaceUSB::instance(), SIGNAL(challengeStarted()), this, SIGNAL(challengeStarted()));
connect(YubiKeyInterfaceUSB::instance(), SIGNAL(challengeCompleted()), this, SIGNAL(challengeCompleted()));
} else {
qDebug("YubiKey: USB interface is not initialized.");
}
connect(YubiKeyInterfaceUSB::instance(), SIGNAL(challengeStarted()), this, SIGNAL(challengeStarted()));
connect(YubiKeyInterfaceUSB::instance(), SIGNAL(challengeCompleted()), this, SIGNAL(challengeCompleted()));

if (YubiKeyInterfacePCSC::instance()->isInitialized()) {
++num_interfaces;
connect(YubiKeyInterfacePCSC::instance(), SIGNAL(challengeStarted()), this, SIGNAL(challengeStarted()));
connect(YubiKeyInterfacePCSC::instance(), SIGNAL(challengeCompleted()), this, SIGNAL(challengeCompleted()));
} else {
qDebug("YubiKey: PCSC interface is disabled or not initialized.");
}
connect(YubiKeyInterfacePCSC::instance(), SIGNAL(challengeStarted()), this, SIGNAL(challengeStarted()));
connect(YubiKeyInterfacePCSC::instance(), SIGNAL(challengeCompleted()), this, SIGNAL(challengeCompleted()));

// Collapse the detectComplete signals from all interfaces into one signal
// If multiple interfaces are used, wait for them all to finish
auto detect_handler = [this, num_interfaces](bool found) {
if (!m_interfaces_detect_mutex.tryLock(1000)) {
return;
}
m_interfaces_detect_found |= found;
m_interfaces_detect_completed++;
if (m_interfaces_detect_completed != -1 && m_interfaces_detect_completed == num_interfaces) {
m_interfaces_detect_completed = -1;
emit detectComplete(m_interfaces_detect_found);
}
m_interfaces_detect_mutex.unlock();
};
connect(YubiKeyInterfaceUSB::instance(), &YubiKeyInterfaceUSB::detectComplete, this, detect_handler);
connect(YubiKeyInterfacePCSC::instance(), &YubiKeyInterfacePCSC::detectComplete, this, detect_handler);

if (num_interfaces != 0) {
m_initialized = true;
// clang-format off
connect(&m_interactionTimer, SIGNAL(timeout()), this, SIGNAL(userInteractionRequest()));
connect(this, &YubiKey::challengeStarted, this, [this] { m_interactionTimer.start(); });
connect(this, &YubiKey::challengeCompleted, this, [this] { m_interactionTimer.stop(); });
// clang-format on
}
m_initialized = num_interfaces > 0;

m_interactionTimer.setSingleShot(true);
m_interactionTimer.setInterval(200);
connect(&m_interactionTimer, SIGNAL(timeout()), this, SIGNAL(userInteractionRequest()));
connect(this, &YubiKey::challengeStarted, this, [this] { m_interactionTimer.start(); });
connect(this, &YubiKey::challengeCompleted, this, [this] { m_interactionTimer.stop(); });
}

YubiKey* YubiKey::m_instance(nullptr);
Expand All @@ -84,12 +68,23 @@ bool YubiKey::isInitialized()
return m_initialized;
}

void YubiKey::findValidKeys()
bool YubiKey::findValidKeys()
{
m_interfaces_detect_completed = 0;
m_interfaces_detect_found = false;
YubiKeyInterfaceUSB::instance()->findValidKeys();
YubiKeyInterfacePCSC::instance()->findValidKeys();
bool found = false;
if (m_interfaces_detect_mutex.tryLock(1000)) {
found |= YubiKeyInterfaceUSB::instance()->findValidKeys();
found |= YubiKeyInterfacePCSC::instance()->findValidKeys();
m_interfaces_detect_mutex.unlock();
}
return found;
}

void YubiKey::findValidKeysAsync()
{
QtConcurrent::run([this] {
bool found = findValidKeys();
emit detectComplete(found);
});
}

QList<YubiKeySlot> YubiKey::foundKeys()
Expand Down Expand Up @@ -207,6 +202,11 @@ YubiKey::challenge(YubiKeySlot slot, const QByteArray& challenge, Botan::secure_
{
m_error.clear();

// Make sure we tried to find available keys
if (foundKeys().isEmpty()) {
findValidKeys();
}

if (YubiKeyInterfaceUSB::instance()->hasFoundKey(slot)) {
return YubiKeyInterfaceUSB::instance()->challenge(slot, challenge, response);
}
Expand Down
5 changes: 2 additions & 3 deletions src/keys/drivers/YubiKey.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,8 @@ class YubiKey : public QObject
static YubiKey* instance();
bool isInitialized();

void findValidKeys();
bool findValidKeys();
void findValidKeysAsync();

QList<YubiKeySlot> foundKeys();
QString getDisplayName(YubiKeySlot slot);
Expand Down Expand Up @@ -84,8 +85,6 @@ class YubiKey : public QObject
QTimer m_interactionTimer;
bool m_initialized = false;
QString m_error;
int m_interfaces_detect_completed = -1;
bool m_interfaces_detect_found = false;
QMutex m_interfaces_detect_mutex;

Q_DISABLE_COPY(YubiKey)
Expand Down
5 changes: 5 additions & 0 deletions src/keys/drivers/YubiKeyInterface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,11 @@ QMultiMap<unsigned int, QPair<int, QString>> YubiKeyInterface::foundKeys()

bool YubiKeyInterface::hasFoundKey(YubiKeySlot slot)
{
// A serial number of 0 implies use the first key
if (slot.first == 0 && !m_foundKeys.isEmpty()) {
return true;
}

for (const auto& key : m_foundKeys.values(slot.first)) {
if (slot.second == key.first) {
return true;
Expand Down
2 changes: 1 addition & 1 deletion src/keys/drivers/YubiKeyInterface.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ class YubiKeyInterface : public QObject
bool hasFoundKey(YubiKeySlot slot);
QString getDisplayName(YubiKeySlot slot);

virtual void findValidKeys() = 0;
virtual bool findValidKeys() = 0;
virtual YubiKey::ChallengeResult
challenge(YubiKeySlot slot, const QByteArray& challenge, Botan::secure_vector<char>& response) = 0;
virtual bool testChallenge(YubiKeySlot slot, bool* wouldBlock) = 0;
Expand Down
Loading