CLI: Add Yubikey unlock support

This commit is contained in:
louib 2019-09-21 12:31:44 -04:00 committed by Jonathan White
parent 77fcde875e
commit 964478e78f
19 changed files with 816 additions and 529 deletions

View file

@ -52,6 +52,12 @@ const QCommandLineOption Command::KeyFileOption = QCommandLineOption(QStringList
const QCommandLineOption Command::NoPasswordOption =
QCommandLineOption(QStringList() << "no-password", QObject::tr("Deactivate password key for the database."));
const QCommandLineOption Command::YubiKeyOption =
QCommandLineOption(QStringList() << "y"
<< "yubikey",
QObject::tr("Yubikey slot used to encrypt the database."),
QObject::tr("slot"));
QMap<QString, Command*> commands;
Command::Command()

View file

@ -56,6 +56,7 @@ public:
static const QCommandLineOption QuietOption;
static const QCommandLineOption KeyFileOption;
static const QCommandLineOption NoPasswordOption;
static const QCommandLineOption YubiKeyOption;
};
#endif // KEEPASSXC_COMMAND_H

View file

@ -24,6 +24,9 @@ DatabaseCommand::DatabaseCommand()
positionalArguments.append({QString("database"), QObject::tr("Path of the database."), QString("")});
options.append(Command::KeyFileOption);
options.append(Command::NoPasswordOption);
#ifdef WITH_XC_YUBIKEY
options.append(Command::YubiKeyOption);
#endif
}
int DatabaseCommand::execute(const QStringList& arguments)
@ -37,6 +40,7 @@ int DatabaseCommand::execute(const QStringList& arguments)
auto db = Utils::unlockDatabase(args.at(0),
!parser->isSet(Command::NoPasswordOption),
parser->value(Command::KeyFileOption),
parser->value(Command::YubiKeyOption),
parser->isSet(Command::QuietOption) ? Utils::DEVNULL : Utils::STDOUT,
Utils::STDERR);
if (!db) {

View file

@ -43,6 +43,9 @@ const QCommandLineOption Merge::DryRunOption =
QCommandLineOption(QStringList() << "dry-run",
QObject::tr("Only print the changes detected by the merge operation."));
const QCommandLineOption Merge::YubiKeyFromOption(QStringList() << "yubikey-from",
QObject::tr("Yubikey slot for the second database."),
QObject::tr("slot"));
Merge::Merge()
{
name = QString("merge");
@ -51,6 +54,9 @@ Merge::Merge()
options.append(Merge::KeyFileFromOption);
options.append(Merge::NoPasswordFromOption);
options.append(Merge::DryRunOption);
#ifdef WITH_XC_YUBIKEY
options.append(Merge::YubiKeyFromOption);
#endif
positionalArguments.append({QString("database2"), QObject::tr("Path of the database to merge from."), QString("")});
}
@ -70,6 +76,7 @@ int Merge::executeWithDatabase(QSharedPointer<Database> database, QSharedPointer
db2 = Utils::unlockDatabase(fromDatabasePath,
!parser->isSet(Merge::NoPasswordFromOption),
parser->value(Merge::KeyFileFromOption),
parser->value(Merge::YubiKeyFromOption),
parser->isSet(Command::QuietOption) ? Utils::DEVNULL : Utils::STDOUT,
Utils::STDERR);
if (!db2) {

View file

@ -30,6 +30,7 @@ public:
static const QCommandLineOption SameCredentialsOption;
static const QCommandLineOption KeyFileFromOption;
static const QCommandLineOption NoPasswordFromOption;
static const QCommandLineOption YubiKeyFromOption;
static const QCommandLineOption DryRunOption;
};

View file

@ -102,6 +102,7 @@ namespace Utils
QSharedPointer<Database> unlockDatabase(const QString& databaseFilename,
const bool isPasswordProtected,
const QString& keyFilename,
const QString& yubiKeySlot,
FILE* outputDescriptor,
FILE* errorDescriptor)
{
@ -153,6 +154,31 @@ namespace Utils
compositeKey->addKey(fileKey);
}
#ifdef WITH_XC_YUBIKEY
if (!yubiKeySlot.isEmpty()) {
bool ok = false;
int slot = yubiKeySlot.toInt(&ok, 10);
if (!ok || (slot != 1 && slot != 2)) {
err << QObject::tr("Invalid YubiKey slot %1").arg(yubiKeySlot) << endl;
return {};
}
QString errorMessage;
bool blocking = YubiKey::instance()->checkSlotIsBlocking(slot, errorMessage);
if (!errorMessage.isEmpty()) {
err << errorMessage << endl;
return {};
}
auto key = QSharedPointer<YkChallengeResponseKeyCLI>(new YkChallengeResponseKeyCLI(
slot,
blocking,
QObject::tr("Please touch the button on your YubiKey to unlock %1").arg(databaseFilename),
outputDescriptor));
compositeKey->addChallengeResponseKey(key);
}
#endif
auto db = QSharedPointer<Database>::create();
QString error;
if (db->open(databaseFilename, compositeKey, &error, false)) {

View file

@ -25,6 +25,12 @@
#include "keys/PasswordKey.h"
#include <QtCore/qglobal.h>
#ifdef WITH_XC_YUBIKEY
#include "keys/YkChallengeResponseKey.h"
#include "keys/YkChallengeResponseKeyCLI.h"
#include "keys/drivers/YubiKey.h"
#endif
namespace Utils
{
extern FILE* STDOUT;
@ -38,6 +44,7 @@ namespace Utils
QSharedPointer<Database> unlockDatabase(const QString& databaseFilename,
const bool isPasswordProtected = true,
const QString& keyFilename = {},
const QString& yubiKeySlot = {},
FILE* outputDescriptor = STDOUT,
FILE* errorDescriptor = STDERR);

View file

@ -70,6 +70,9 @@ Specifies a path to a key file for unlocking the database. In a merge operation
.IP "--no-password"
Deactivate password key for the database.
.IP "-y, --yubikey <slot>"
Specifies a yubikey slot for unlocking the database. In a merge operation this option is used to specify the yubikey slot for the first database.
.IP "-q, --quiet <path>"
Silence password prompt and other secondary outputs.
@ -91,6 +94,9 @@ Path of the key file for the second database.
.IP "--no-password-from"
Deactivate password key for the database to merge from.
.IP "--yubikey-from <slot>"
Yubikey slot for the second database.
.IP "-s, --same-credentials"
Use the same credentials for unlocking both database.