Feature : --key-file option for CLI (#816)

* removing readFromLine

* Removing gui-prompt

* execute uses an arg list.

* Testing with key-file

* Fixing the -a option in EntropyMeter.
This commit is contained in:
louib 2017-07-25 13:41:52 -04:00 committed by GitHub
parent 1edabc4b3c
commit 1d30283514
23 changed files with 92 additions and 189 deletions

View File

@ -22,16 +22,13 @@
#include "Clip.h"
#include <QApplication>
#include <QCommandLineParser>
#include <QStringList>
#include <QTextStream>
#include "cli/Utils.h"
#include "core/Database.h"
#include "core/Entry.h"
#include "core/Group.h"
#include "gui/UnlockDatabaseDialog.h"
Clip::Clip()
{
@ -43,24 +40,19 @@ Clip::~Clip()
{
}
int Clip::execute(int argc, char** argv)
int Clip::execute(QStringList arguments)
{
QStringList arguments;
// Skipping the first argument (keepassxc).
for (int i = 1; i < argc; ++i) {
arguments << QString(argv[i]);
}
QTextStream out(stdout);
QApplication app(argc, argv);
QCommandLineParser parser;
parser.setApplicationDescription(this->description);
parser.addPositionalArgument("database", QObject::tr("Path of the database."));
QCommandLineOption guiPrompt(QStringList() << "g"
<< "gui-prompt",
QObject::tr("Use a GUI prompt unlocking the database."));
parser.addOption(guiPrompt);
QCommandLineOption keyFile(QStringList() << "k"
<< "key-file",
QObject::tr("Key file of the database."),
QObject::tr("path"));
parser.addOption(keyFile);
parser.addPositionalArgument("entry", QObject::tr("Path of the entry to clip."));
parser.addPositionalArgument(
"timeout",
@ -74,16 +66,11 @@ int Clip::execute(int argc, char** argv)
return EXIT_FAILURE;
}
Database* db = nullptr;
if (parser.isSet("gui-prompt")) {
db = UnlockDatabaseDialog::openDatabasePrompt(args.at(0));
} else {
db = Database::unlockFromStdin(args.at(0));
}
Database* db = Database::unlockFromStdin(args.at(0), parser.value(keyFile));
if (!db) {
return EXIT_FAILURE;
}
return this->clipEntry(db, args.at(1), args.value(2));
}

View File

@ -25,7 +25,7 @@ class Clip : public Command
public:
Clip();
~Clip();
int execute(int argc, char** argv);
int execute(QStringList arguments);
int clipEntry(Database* database, QString entryPath, QString timeout);
};

View File

@ -35,7 +35,7 @@ Command::~Command()
{
}
int Command::execute(int, char**)
int Command::execute(QStringList)
{
return EXIT_FAILURE;
}

View File

@ -29,7 +29,7 @@ class Command
{
public:
virtual ~Command();
virtual int execute(int argc, char** argv);
virtual int execute(QStringList arguments);
QString name;
QString description;
QString getDescriptionLine();

View File

@ -97,17 +97,18 @@ static void calculate(const char *pwd, int advanced)
}
}
int EntropyMeter::execute(int argc, char **argv)
int EntropyMeter::execute(QStringList arguments)
{
printf("KeePassXC Entropy Meter, based on zxcvbn-c.\nEnter your password below or pass it as argv\n");
printf(" Usage: entropy-meter [-a] [pwd1 pwd2 ...]\n> ");
int i, advanced;
if ((argc > 1) && (argv[1][0] == '-') && (!strcmp(argv[1], "-a")))
int i, advanced = 0;
if (arguments.size() > 1 && arguments.at(1) == "-a")
{
advanced = 1;
arguments.removeAt(1);
}
i = 2;
if (i >= argc)
i = 1;
if (i >= arguments.size())
{
/* No test passwords on command line, so get them from stdin */
char line[500];
@ -131,9 +132,9 @@ int EntropyMeter::execute(int argc, char **argv)
else
{
/* Do the test passwords on the command line */
for(; i < argc; ++i)
for(; i < arguments.size(); ++i)
{
calculate(argv[i],advanced);
calculate(arguments.at(i).toLatin1(), advanced);
}
}
return 0;

View File

@ -25,7 +25,7 @@ class EntropyMeter : public Command
public:
EntropyMeter();
~EntropyMeter();
int execute(int argc, char** argv);
int execute(QStringList arguments);
};
#endif // KEEPASSXC_ENTROPYMETER_H

View File

@ -21,15 +21,14 @@
#include "Extract.h"
#include <QCommandLineParser>
#include <QCoreApplication>
#include <QFile>
#include <QStringList>
#include <QTextStream>
#include "cli/Utils.h"
#include "core/Database.h"
#include "format/KeePass2Reader.h"
#include "keys/CompositeKey.h"
#include "keys/PasswordKey.h"
Extract::Extract()
{
@ -41,15 +40,8 @@ Extract::~Extract()
{
}
int Extract::execute(int argc, char** argv)
int Extract::execute(QStringList arguments)
{
QStringList arguments;
// Skipping the first argument (keepassxc).
for (int i = 1; i < argc; ++i) {
arguments << QString(argv[i]);
}
QCoreApplication app(argc, argv);
QTextStream out(stdout);
QCommandLineParser parser;
@ -66,8 +58,12 @@ int Extract::execute(int argc, char** argv)
out << "Insert the database password\n> ";
out.flush();
CompositeKey compositeKey;
QString line = Utils::getPassword();
CompositeKey key = CompositeKey::readFromLine(line);
PasswordKey passwordKey;
passwordKey.setPassword(line);
compositeKey.addKey(passwordKey);
QString databaseFilename = args.at(0);
QFile dbFile(databaseFilename);
@ -82,7 +78,7 @@ int Extract::execute(int argc, char** argv)
KeePass2Reader reader;
reader.setSaveXml(true);
Database* db = reader.readDatabase(&dbFile, key);
Database* db = reader.readDatabase(&dbFile, compositeKey);
delete db;
QByteArray xmlData = reader.xmlData();

View File

@ -25,7 +25,7 @@ class Extract : public Command
public:
Extract();
~Extract();
int execute(int argc, char** argv);
int execute(QStringList arguments);
};
#endif // KEEPASSXC_EXTRACT_H

View File

@ -20,16 +20,12 @@
#include "List.h"
#include <QApplication>
#include <QCommandLineParser>
#include <QCoreApplication>
#include <QStringList>
#include <QTextStream>
#include "core/Database.h"
#include "core/Entry.h"
#include "core/Group.h"
#include "gui/UnlockDatabaseDialog.h"
List::List()
{
@ -41,14 +37,8 @@ List::~List()
{
}
int List::execute(int argc, char** argv)
int List::execute(QStringList arguments)
{
QStringList arguments;
// Skipping the first argument (keepassxc).
for (int i = 1; i < argc; ++i) {
arguments << QString(argv[i]);
}
QTextStream out(stdout);
QCommandLineParser parser;
@ -56,28 +46,20 @@ int List::execute(int argc, char** argv)
parser.addPositionalArgument("database", QObject::tr("Path of the database."));
parser.addPositionalArgument(
"group", QObject::tr("Path of the group to list. Default is /"), QString("[group]"));
QCommandLineOption guiPrompt(QStringList() << "g"
<< "gui-prompt",
QObject::tr("Use a GUI prompt unlocking the database."));
parser.addOption(guiPrompt);
QCommandLineOption keyFile(QStringList() << "k"
<< "key-file",
QObject::tr("Key file of the database."),
QObject::tr("path"));
parser.addOption(keyFile);
parser.process(arguments);
const QStringList args = parser.positionalArguments();
if (args.size() != 1 && args.size() != 2) {
QCoreApplication app(argc, argv);
out << parser.helpText().replace("keepassxc-cli", "keepassxc-cli ls");
return EXIT_FAILURE;
}
Database* db = nullptr;
if (parser.isSet("gui-prompt")) {
QApplication app(argc, argv);
db = UnlockDatabaseDialog::openDatabasePrompt(args.at(0));
} else {
QCoreApplication app(argc, argv);
db = Database::unlockFromStdin(args.at(0));
}
Database* db = Database::unlockFromStdin(args.at(0), parser.value(keyFile));
if (db == nullptr) {
return EXIT_FAILURE;
}

View File

@ -25,7 +25,7 @@ class List : public Command
public:
List();
~List();
int execute(int argc, char** argv);
int execute(QStringList arguments);
int listGroup(Database* database, QString groupPath = QString(""));
};

View File

@ -19,14 +19,10 @@
#include "Merge.h"
#include <QApplication>
#include <QCommandLineParser>
#include <QCoreApplication>
#include <QStringList>
#include <QTextStream>
#include "core/Database.h"
#include "gui/UnlockDatabaseDialog.h"
Merge::Merge()
{
@ -38,14 +34,8 @@ Merge::~Merge()
{
}
int Merge::execute(int argc, char** argv)
int Merge::execute(QStringList arguments)
{
QStringList arguments;
// Skipping the first argument (keepassxc).
for (int i = 1; i < argc; ++i) {
arguments << QString(argv[i]);
}
QTextStream out(stdout);
QCommandLineParser parser;
@ -55,52 +45,47 @@ int Merge::execute(int argc, char** argv)
QCommandLineOption samePasswordOption(
QStringList() << "s"
<< "same-password",
QObject::tr("Use the same password for both database files."));
<< "same-credentials",
QObject::tr("Use the same credentials for both database files."));
QCommandLineOption guiPrompt(QStringList() << "g"
<< "gui-prompt",
QObject::tr("Use a GUI prompt unlocking the database."));
parser.addOption(guiPrompt);
QCommandLineOption keyFile(QStringList() << "k"
<< "key-file",
QObject::tr("Key file of the database."),
QObject::tr("path"));
parser.addOption(keyFile);
QCommandLineOption keyFileFrom(QStringList() << "f"
<< "key-file-from",
QObject::tr("Key file of the database to merge from."),
QObject::tr("path"));
parser.addOption(keyFileFrom);
parser.addOption(samePasswordOption);
parser.process(arguments);
const QStringList args = parser.positionalArguments();
if (args.size() != 2) {
QCoreApplication app(argc, argv);
out << parser.helpText().replace("keepassxc-cli", "keepassxc-cli merge");
return EXIT_FAILURE;
}
Database* db1;
Database* db2;
if (parser.isSet("gui-prompt")) {
QApplication app(argc, argv);
db1 = UnlockDatabaseDialog::openDatabasePrompt(args.at(0));
if (!parser.isSet("same-password")) {
db2 = UnlockDatabaseDialog::openDatabasePrompt(args.at(1));
} else {
db2 = Database::openDatabaseFile(args.at(1), *(db1->key().clone()));
}
} else {
QCoreApplication app(argc, argv);
db1 = Database::unlockFromStdin(args.at(0));
if (!parser.isSet("same-password")) {
db2 = Database::unlockFromStdin(args.at(1));
} else {
db2 = Database::openDatabaseFile(args.at(1), *(db1->key().clone()));
}
}
Database* db1 = Database::unlockFromStdin(args.at(0), parser.value(keyFile));
if (db1 == nullptr) {
return EXIT_FAILURE;
}
Database* db2;
if (!parser.isSet("same-credentials")) {
db2 = Database::unlockFromStdin(args.at(1), parser.value(keyFileFrom));
} else {
db2 = Database::openDatabaseFile(args.at(1), *(db1->key().clone()));
}
if (db2 == nullptr) {
return EXIT_FAILURE;
}
db1->merge(db2);
QString errorMessage = db1->saveToFile(args.at(0));
if (!errorMessage.isEmpty()) {
qCritical("Unable to save database to file : %s", qPrintable(errorMessage));

View File

@ -25,7 +25,7 @@ class Merge : public Command
public:
Merge();
~Merge();
int execute(int argc, char** argv);
int execute(QStringList arguments);
};
#endif // KEEPASSXC_MERGE_H

View File

@ -21,8 +21,6 @@
#include "Show.h"
#include <QCommandLineParser>
#include <QCoreApplication>
#include <QStringList>
#include <QTextStream>
#include "core/Database.h"
@ -39,20 +37,18 @@ Show::~Show()
{
}
int Show::execute(int argc, char** argv)
int Show::execute(QStringList arguments)
{
QStringList arguments;
// Skipping the first argument (keepassxc).
for (int i = 1; i < argc; ++i) {
arguments << QString(argv[i]);
}
QCoreApplication app(argc, argv);
QTextStream out(stdout);
QCommandLineParser parser;
parser.setApplicationDescription(this->description);
parser.addPositionalArgument("database", QObject::tr("Path of the database."));
QCommandLineOption keyFile(QStringList() << "k"
<< "key-file",
QObject::tr("Key file of the database."),
QObject::tr("path"));
parser.addOption(keyFile);
parser.addPositionalArgument("entry", QObject::tr("Name of the entry to show."));
parser.process(arguments);
@ -62,7 +58,7 @@ int Show::execute(int argc, char** argv)
return EXIT_FAILURE;
}
Database* db = Database::unlockFromStdin(args.at(0));
Database* db = Database::unlockFromStdin(args.at(0), parser.value(keyFile));
if (db == nullptr) {
return EXIT_FAILURE;
}

View File

@ -25,7 +25,7 @@ class Show : public Command
public:
Show();
~Show();
int execute(int argc, char** argv);
int execute(QStringList arguments);
int showEntry(Database* database, QString entryPath);
};

View File

@ -43,6 +43,9 @@ int main(int argc, char** argv)
return EXIT_FAILURE;
}
QCoreApplication app(argc, argv);
app.setApplicationVersion(KEEPASSX_VERSION);
QTextStream out(stdout);
QStringList arguments;
for (int i = 0; i < argc; ++i) {
@ -67,8 +70,6 @@ int main(int argc, char** argv)
parser.parse(arguments);
if (parser.positionalArguments().size() < 1) {
QCoreApplication app(argc, argv);
app.setApplicationVersion(KEEPASSX_VERSION);
if (parser.isSet("version")) {
// Switch to parser.showVersion() when available (QT 5.4).
out << KEEPASSX_VERSION << endl;
@ -82,14 +83,14 @@ int main(int argc, char** argv)
if (command == nullptr) {
qCritical("Invalid command %s.", qPrintable(commandName));
QCoreApplication app(argc, argv);
app.setApplicationVersion(KEEPASSX_VERSION);
// showHelp exits the application immediately, so we need to set the
// exit code here.
parser.showHelp(EXIT_FAILURE);
}
int exitCode = command->execute(argc, argv);
// Removing the first argument (keepassxc).
arguments.removeFirst();
int exitCode = command->execute(arguments);
#if defined(WITH_ASAN) && defined(WITH_LSAN)
// do leak check here to prevent massive tail of end-of-process leak errors from third-party libraries

View File

@ -31,6 +31,9 @@
#include "format/KeePass2.h"
#include "format/KeePass2Reader.h"
#include "format/KeePass2Writer.h"
#include "keys/PasswordKey.h"
#include "keys/FileKey.h"
#include "keys/CompositeKey.h"
QHash<Uuid, Database*> Database::m_uuidMap;
@ -397,16 +400,27 @@ Database* Database::openDatabaseFile(QString fileName, CompositeKey key)
return db;
}
Database* Database::unlockFromStdin(QString databaseFilename)
Database* Database::unlockFromStdin(QString databaseFilename, QString keyFilename)
{
QTextStream outputTextStream(stdout);
outputTextStream << QString("Insert password to unlock " + databaseFilename + "\n> ");
outputTextStream.flush();
CompositeKey compositeKey;
QString line = Utils::getPassword();
CompositeKey key = CompositeKey::readFromLine(line);
return Database::openDatabaseFile(databaseFilename, key);
PasswordKey passwordKey;
passwordKey.setPassword(line);
compositeKey.addKey(passwordKey);
if (!keyFilename.isEmpty()) {
FileKey fileKey;
fileKey.load(keyFilename);
compositeKey.addKey(fileKey);
}
return Database::openDatabaseFile(databaseFilename, compositeKey);
}
QString Database::saveToFile(QString filePath)

View File

@ -122,7 +122,7 @@ public:
static Database* databaseByUuid(const Uuid& uuid);
static Database* openDatabaseFile(QString fileName, CompositeKey key);
static Database* unlockFromStdin(QString databaseFilename);
static Database* unlockFromStdin(QString databaseFilename, QString keyFilename = QString(""));
signals:
void groupDataChanged(Group* group);

View File

@ -54,20 +54,3 @@ void UnlockDatabaseDialog::complete(bool r)
reject();
}
}
Database* UnlockDatabaseDialog::openDatabasePrompt(QString databaseFilename)
{
UnlockDatabaseDialog* unlockDatabaseDialog = new UnlockDatabaseDialog();
unlockDatabaseDialog->setObjectName("Open database");
unlockDatabaseDialog->setDBFilename(databaseFilename);
unlockDatabaseDialog->show();
unlockDatabaseDialog->exec();
Database* db = unlockDatabaseDialog->database();
if (!db) {
qWarning("Could not open database %s.", qPrintable(databaseFilename));
}
delete unlockDatabaseDialog;
return db;
}

View File

@ -35,7 +35,6 @@ public:
void setDBFilename(const QString& filename);
void clearForms();
Database* database();
static Database* openDatabasePrompt(QString databaseFilename);
signals:
void unlockDone(bool);

View File

@ -80,29 +80,6 @@ CompositeKey& CompositeKey::operator=(const CompositeKey& key)
return *this;
}
/*
* Read a key from a line of input.
* If the line references a valid file
* path, the key is loaded from file.
*/
CompositeKey CompositeKey::readFromLine(QString line)
{
CompositeKey key;
if (QFile::exists(line)) {
FileKey fileKey;
fileKey.load(line);
key.addKey(fileKey);
}
else {
PasswordKey password;
password.setPassword(line);
key.addKey(password);
}
return key;
}
QByteArray CompositeKey::rawKey() const
{
CryptoHash cryptoHash(CryptoHash::Sha256);

View File

@ -46,7 +46,6 @@ public:
void addChallengeResponseKey(QSharedPointer<ChallengeResponseKey> key);
static int transformKeyBenchmark(int msec);
static CompositeKey readFromLine(QString line);
private:
static QByteArray transformKeyRaw(const QByteArray& key, const QByteArray& seed,

View File

@ -84,22 +84,6 @@ void TestKeys::testComposite()
delete compositeKey4;
}
void TestKeys::testCompositeKeyReadFromLine()
{
QString keyFilename = QString("%1/FileKeyXml.key").arg(QString(KEEPASSX_TEST_DATA_DIR));
CompositeKey compositeFileKey = CompositeKey::readFromLine(keyFilename);
FileKey fileKey;
fileKey.load(keyFilename);
QCOMPARE(compositeFileKey.rawKey().size(), fileKey.rawKey().size());
CompositeKey compositePasswordKey = CompositeKey::readFromLine(QString("password"));
PasswordKey passwordKey(QString("password"));
QCOMPARE(compositePasswordKey.rawKey().size(), passwordKey.rawKey().size());
}
void TestKeys::testFileKey()
{
QFETCH(QString, type);

View File

@ -28,7 +28,6 @@ class TestKeys : public QObject
private slots:
void initTestCase();
void testComposite();
void testCompositeKeyReadFromLine();
void testFileKey();
void testFileKey_data();
void testCreateFileKey();