From b84d38e7fb764ad2e9b228a62b3ac771de8a80cd Mon Sep 17 00:00:00 2001 From: Charlie Wang Date: Wed, 18 Jan 2023 19:06:06 -0500 Subject: [PATCH] Properly handle Windows Hello errors The KeyCredentialManager::RequestCreateAsync call can fail because we can end up in a situation where Windows Hello is initially available but then becomes unavailable, such as during a remote desktop session. This commit prevents a crash by moving the call into the try-catch. Fixes #7890 Also resets quick unlock if there is an unrecoverable error. This will not occur if the user merely canceled the Windows Hello dialog. --- share/translations/keepassxc_en.ts | 8 ++++---- src/gui/DatabaseOpenWidget.cpp | 7 ++++++- src/winhello/WindowsHello.cpp | 28 +++++++++++++++------------- 3 files changed, 25 insertions(+), 18 deletions(-) diff --git a/share/translations/keepassxc_en.ts b/share/translations/keepassxc_en.ts index dbe5c95e0..fd7a60e00 100644 --- a/share/translations/keepassxc_en.ts +++ b/share/translations/keepassxc_en.ts @@ -1494,10 +1494,6 @@ To prevent this error from appearing, you must go to "Database Settings / S Retry with empty password - - Failed to authenticate with Windows Hello - - Failed to authenticate with Touch ID @@ -1555,6 +1551,10 @@ If you do not have a key file, please leave the field empty. authenticate to access the database + + Failed to authenticate with Windows Hello: %1 + + DatabaseSettingWidgetMetaData diff --git a/src/gui/DatabaseOpenWidget.cpp b/src/gui/DatabaseOpenWidget.cpp index 882d8d057..f07e4b1af 100644 --- a/src/gui/DatabaseOpenWidget.cpp +++ b/src/gui/DatabaseOpenWidget.cpp @@ -339,7 +339,12 @@ QSharedPointer DatabaseOpenWidget::buildDatabaseKey() #ifdef Q_CC_MSVC if (!getWindowsHello()->getKey(m_filename, keyData)) { // Failed to retrieve Quick Unlock data - m_ui->messageWidget->showMessage(tr("Failed to authenticate with Windows Hello"), MessageWidget::Error); + auto error = getWindowsHello()->errorString(); + if (!error.isEmpty()) { + m_ui->messageWidget->showMessage(tr("Failed to authenticate with Windows Hello: %1").arg(error), + MessageWidget::Error); + resetQuickUnlock(); + } return {}; } #elif defined(Q_OS_MACOS) diff --git a/src/winhello/WindowsHello.cpp b/src/winhello/WindowsHello.cpp index 99e798c50..bc244cc26 100644 --- a/src/winhello/WindowsHello.cpp +++ b/src/winhello/WindowsHello.cpp @@ -64,22 +64,24 @@ namespace array_view(reinterpret_cast(challenge.data()), challenge.size())); return AsyncTask::runAndWaitForFuture([&] { - // The first time this is used a key-pair will be generated using the common name - auto result = - KeyCredentialManager::RequestCreateAsync(s_winHelloKeyName, KeyCredentialCreationOption::FailIfExists) - .get(); - - if (result.Status() == KeyCredentialStatus::CredentialAlreadyExists) { - result = KeyCredentialManager::OpenAsync(s_winHelloKeyName).get(); - } else if (result.Status() != KeyCredentialStatus::Success) { - error = QObject::tr("Failed to create Windows Hello credential."); - return false; - } - try { + // The first time this is used a key-pair will be generated using the common name + auto result = KeyCredentialManager::RequestCreateAsync(s_winHelloKeyName, + KeyCredentialCreationOption::FailIfExists) + .get(); + + if (result.Status() == KeyCredentialStatus::CredentialAlreadyExists) { + result = KeyCredentialManager::OpenAsync(s_winHelloKeyName).get(); + } else if (result.Status() != KeyCredentialStatus::Success) { + error = QObject::tr("Failed to create Windows Hello credential."); + return false; + } + const auto signature = result.Credential().RequestSignAsync(challengeBuffer).get(); if (signature.Status() != KeyCredentialStatus::Success) { - error = QObject::tr("Failed to sign challenge using Windows Hello."); + if (signature.Status() != KeyCredentialStatus::UserCanceled) { + error = QObject::tr("Failed to sign challenge using Windows Hello."); + } return false; }