Commit fd09917a authored by Lisa's avatar Lisa
Browse files

Final revision of akabei-key

parent a6be1a78
......@@ -5,11 +5,6 @@ set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake/modules")
set(DATA_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/share/akabei" CACHE "Akabei data's install dir" STRING)
set(SCRIPTS_INSTALL_DIR "${DATA_INSTALL_DIR}/python" CACHE "Akabei scripts' install dir" STRING)
# Compiles with debugging symbols (Debug mode)
if (CMAKE_COMPILER_IS_GNUCC)
set(CMAKE_C_FLAGS_DEBUG "-ggdb")
endif(CMAKE_COMPILER_IS_GNUCC)
find_package(AkabeiCore REQUIRED)
find_package(AkabeiClient REQUIRED)
find_package(MSGFMT REQUIRED)
......
......@@ -4,6 +4,10 @@ find_package(Gpgme REQUIRED)
set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake/modules")
if (CMAKE_COMPILER_IS_GNUCC)
set(CMAKE_C_FLAGS_DEBUG "-ggdb")
endif(CMAKE_COMPILER_IS_GNUCC)
set(akabei_key_SRCS main.cpp)
include_directories(${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_CURRENT_BINARY_DIR}
......
......@@ -11,35 +11,26 @@
#include <QCoreApplication>
#include <QStringList>
#include <QDebug>
#include <QDir>
#include <QProcess>
#include <QTextStream>
#include <sys/stat.h>
#include <errno.h>
#include <gpgme.h>
#include <iostream>
/* Utility macro for translating C-like boolean values into strings */
#define YESNO(x) ((x) == 1 ? "yes" : "no")
#define YESNO(x) ((x) == 1 ? QObject::tr("yes") : QObject::tr("no"))
using namespace std;
/* Keyring directory */
const QString AkabeiKeyringDir( "/etc/akabei.d/keyring" );
/*
* TODO: check that the trust level is set correctly
* make the import from server work FIXED
* fix paths for the keyring
* translations?
*/
using namespace std;
/*
* Compares the number of arguments passed from the command line with the minimum required by
* a specified operation.
*/
void countArgs(gpgme_ctx_t ctx, const QStringList& args, int minimum)
{
if (args.size() < minimum) {
QTextStream err(stderr);
err << "Error: wrong number of arguments. See \"" << args.at(0) << " help\" for details." << endl;
err << QObject::tr("Error: wrong number of arguments. See \"%1 help\" for details.").arg( args.first() ) << endl;
gpgme_release(ctx);
exit(-1);
}
......@@ -49,8 +40,8 @@ void printHelp()
{
QTextStream out(stdout);
out << ":: akabei-key v2.0 (Akabei)." << endl;
out << "usage: akabei-key [op] [args]" << endl;
out << QObject::tr(":: akabei-key v2.0 (Akabei).") << endl;
out << QObject::tr("usage: akabei-key [op] [args]") << endl;
out << endl;
out << "help, -h, --help" << endl;
......@@ -62,7 +53,7 @@ void printHelp()
out << "print <ID>" << endl;
out << " " << QObject::tr("prints information about the key identified by ID") << endl;
out << "import <file>" << endl;
out << " " << QObject::tr("imports and trusts a new key from file") << endl;
out << " " << QObject::tr("imports and edits a new key from file") << endl;
out << "get <ID>" << endl;
out << " " << QObject::tr("imports the key ID from the keyserver specified in gnupg.conf") << endl;
out << "remove <ID>" << endl;
......@@ -83,41 +74,33 @@ void initRing(QDir& keyring)
{
const QString AkabeiKeyringPub( "pubring.gpg" );
const QString AkabeiKeyringSec( "secring.gpg" );
const QString AkabeiKeyringTrust( "trustdb.gpg" );
const QString AkabeiKeyringConf( "gpg.conf" );
QTextStream err(stderr);
QTextStream out(stdout);
if (!keyring.mkpath(".")) {
err << "ERROR: keyring " << keyring.path() << " could not be created. Check your permissions." << endl;
err << QObject::tr("ERROR: keyring %1 could not be created. Make sure this initialization is run with root permissions.").arg( keyring.path() ) << endl;
return;
}
QFile pub( keyring.absoluteFilePath(AkabeiKeyringPub) );
if (!pub.open(QIODevice::ReadWrite)) {
err << "ERROR: public key file " << pub.fileName() << "could not be created." << endl;
err << QObject::tr("ERROR: public key file %1 could not be created.").arg( pub.fileName() ) << endl;
return;
}
pub.close();
QFile sec( keyring.absoluteFilePath(AkabeiKeyringSec) );
if (!sec.open(QIODevice::ReadWrite)) {
err << "ERROR: secret key file " << sec.fileName() << "could not be created." << endl;
err << QObject::tr("ERROR: secret key file %1 could not be created.").arg( sec.fileName() ) << endl;
return;
}
sec.close();
QFile trust( keyring.absoluteFilePath(AkabeiKeyringTrust) );
if (!trust.open(QIODevice::ReadWrite)) {
err << "ERROR: trust file " << trust.fileName() << "could not be created." << endl;
return;
}
trust.close();
QFile conf( keyring.absoluteFilePath(AkabeiKeyringConf) );
if (!conf.open(QIODevice::ReadWrite)) {
err << "ERROR: configuration file " << conf.fileName() << "could not be created." << endl;
err << QObject::tr("ERROR: configuration file %1 could not be created.").arg( conf.fileName() ) << endl;
return;
}
......@@ -129,26 +112,46 @@ void initRing(QDir& keyring)
conf.write("lock-never\n");
conf.write("keyserver hkp://pool.sks-keyservers.net\n");
conf.write("keyserver-options timeout=10\n");
conf.close();
cout << ":: Keyring initialized successfully." << endl;
out << QObject::tr(":: Keyring initialized successfully.") << endl;
}
/*
* Edits the key. Used mainly to change its trust level. Sadly a lot of issues arises if keys are edited
* through the API provided by gpgme/crypto, so just for this step we have to use the command line.
*/
void editKey(const char* fingerprint)
{
QTextStream out( stdout );
out << QObject::tr(":: Editing key... ") << endl;
QStringList args = QStringList() << "--homedir" << AkabeiKeyringDir << "--no-permission-warning" << "--edit-key" << fingerprint;
int ret = QProcess::execute("gpg", args);
if (ret < 0) {
QTextStream err(stderr);
err << QObject::tr("Couldn't edit keys.") << endl;
}
out << QObject::tr(":: Key edited successfully.") << endl;
}
/* Prints all public keys in the keyring */
void listKeys(gpgme_ctx_t ctx)
{
gpgme_key_t key;
gpgme_error_t err;
gpgme_error_t error;
QTextStream err( stderr );
if (!err) {
err = gpgme_op_keylist_start(ctx, "", 0);
if (!error) {
error = gpgme_op_keylist_start(ctx, "", 0);
QTextStream out(stdout);
while (!err) {
err = gpgme_op_keylist_next(ctx, &key);
while (!error) {
error = gpgme_op_keylist_next(ctx, &key);
if (err) {
if (error) {
break;
}
......@@ -157,102 +160,40 @@ void listKeys(gpgme_ctx_t ctx)
}
}
if (gpgme_err_code(err) != GPG_ERR_EOF) {
cerr << ":: Error while listing keys" << gpgme_strerror(err) << endl;
}
}
/*
* This callback is being invoked at each step of the key editing process started by the editKey() function.
* Parameters are:
* - f: a customizable argument. Here it records whether the editing has finished, and is initially set to false;
* - status: a code identifying the reason this function was called;
* - args: a string identifiying the current step
* - fd: where the user's answer must be written.
*/
gpgme_error_t editCallback(void* f, gpgme_status_code_t status, const char* args, int fd)
{
QTextStream out(stdout);
QString result;
QString processDesc(args);
bool* finished = (bool *)f;
out << "[-- Code: " << status << ", " << args << " --]" << endl;
if (fd >= 0) { /* this is a message requesting for some input */
if (processDesc == "keyedit.prompt") {
result = "trust";
} else if (processDesc == "edit_ownertrust.value") {
result = "5";
} else if (processDesc == "edit_ownertrust.set_ultimate.okay") {
result = "y";
(*finished) = true;
}
} else if ((*finished)) { /* this is just a status message, namely a confirmation for the previous action */
return GPG_ERR_USER_1; /* I found no other way of stopping the editing, so I use a customizable error code */
}
/* Write the result, if there is one */
if (!result.isEmpty()) {
gpgme_io_write(fd, result.toUtf8().data(), result.size());
gpgme_io_write(fd, "\n", 1);
}
return GPG_ERR_NO_ERROR;
}
void editKey(gpgme_ctx_t ctx, gpgme_key_t key)
{
bool finished = false;
gpgme_data_t output;
gpgme_error_t err;
gpgme_data_new(&output);
err = gpgme_op_edit(ctx, key, editCallback, ( void* )&finished, output);
if (err != GPG_ERR_NO_ERROR && err != GPG_ERR_USER_1) {
cerr << "Editing the key failed: " << gpgme_strerror(err) << endl;
gpgme_data_release(output);
return;
if (gpgme_err_code(error) != GPG_ERR_EOF) {
err << QObject::tr("Error while listing keys: %1").arg( gpgme_strerror(error) ) << endl;
}
cout << ":: Key edited successfully." << endl;
gpgme_data_release(output);
}
/* Imports a key from the filename and sets its trust level to 5 via editing */
void importKey(gpgme_ctx_t ctx, const QString& filename)
{
gpgme_data_t data;
gpgme_error_t err;
gpgme_key_t key;
gpgme_error_t error;
QTextStream out( stdout );
QTextStream err( stderr );
gpgme_data_new(&data);
err = gpgme_data_new_from_file(&data, filename.toUtf8().data(), 1);
error = gpgme_data_new_from_file(&data, filename.toUtf8().data(), 1);
if (err) {
cerr << "Couldn't import key data from file: " << gpgme_strerror(err) << endl;
if (error) {
err << QObject::tr("Couldn't import key data from file: %1").arg( gpgme_strerror(error) ) << endl;
gpgme_data_release(data);
return;
}
err = gpgme_op_import(ctx, data);
error = gpgme_op_import(ctx, data);
if (err) {
cerr << "Couldn't import key into keyring: " << gpgme_strerror(err) << endl;
if (error) {
err << QObject::tr("Couldn't import key into keyring: %1").arg( gpgme_strerror(error) ) << endl;
gpgme_data_release(data);
return;
}
cout << ":: Key imported successfully." << endl;
cout << ":: Editing key..." << endl;
out << QObject::tr(":: Key imported successfully.") << endl;
gpgme_import_result_t result = gpgme_op_import_result(ctx);
gpgme_get_key(ctx, result->imports->fpr, &key, 0);
editKey(ctx, key);
gpgme_key_release(key);
editKey(result->imports->fpr);
gpgme_data_release(data);
}
......@@ -260,24 +201,25 @@ void importKey(gpgme_ctx_t ctx, const QString& filename)
void printKeyById(gpgme_ctx_t ctx, const QString& id)
{
gpgme_key_t key;
gpgme_error_t err = gpgme_get_key(ctx, id.toUtf8().data(), &key, 0);
gpgme_error_t error = gpgme_get_key(ctx, id.toUtf8().data(), &key, 0);
QTextStream err(stderr);
if (err) {
cerr << "Couldn't print key: " << gpgme_strerror(err) << endl;
if (error) {
err << QObject::tr("Couldn't print key: %1").arg( gpgme_strerror(error) ) << endl;
return;
}
QTextStream out(stdout);
out << "-- Key " << key->subkeys->keyid << endl;
out << "Name: " << key->uids->name << endl;
out << "Email: " << key->uids->email << endl;
out << "Expired: " << YESNO(key->expired) << endl;
out << "Revoked: " << YESNO(key->revoked) << endl;
out << "Disabled: " << YESNO(key->disabled) << endl;
out << "Invalid: " << YESNO(key->invalid) << endl;
out << "Can encrypt: " << YESNO(key->invalid) << endl;
out << "Can sign: " << YESNO(key->can_sign) << endl;
out << "Can authenticate: " << YESNO(key->can_authenticate) << endl;
out << QObject::tr("-- Key ") << key->subkeys->keyid << endl;
out << QObject::tr("Name: ") << key->uids->name << endl;
out << QObject::tr("Email: ") << key->uids->email << endl;
out << QObject::tr("Expired: ") << YESNO(key->expired) << endl;
out << QObject::tr("Revoked: ") << YESNO(key->revoked) << endl;
out << QObject::tr("Disabled: ") << YESNO(key->disabled) << endl;
out << QObject::tr("Invalid: ") << YESNO(key->invalid) << endl;
out << QObject::tr("Can encrypt: ") << YESNO(key->invalid) << endl;
out << QObject::tr("Can sign: ") << YESNO(key->can_sign) << endl;
out << QObject::tr("Can authenticate: ") << YESNO(key->can_authenticate) << endl;
gpgme_key_release(key);
}
......@@ -286,30 +228,34 @@ void printKeyById(gpgme_ctx_t ctx, const QString& id)
void removeKey(gpgme_ctx_t ctx, const QString& id)
{
gpgme_key_t key;
gpgme_error_t err = gpgme_get_key(ctx, id.toUtf8().data(), &key, 0);
gpgme_error_t error = gpgme_get_key(ctx, id.toUtf8().data(), &key, 0);
QTextStream out( stdout );
QTextStream err( stderr );
if (err) {
cerr << "Couldn't find key: " << gpgme_strerror(err) << endl;
if (error) {
err << QObject::tr("Couldn't find key: %1").arg( gpgme_strerror(error) ) << endl;
return;
}
err = gpgme_op_delete(ctx, key, 1);
error = gpgme_op_delete(ctx, key, 1);
if (err) {
cerr << "Couldn't delete key: " << gpgme_strerror(err) << endl;
if (error) {
err << QObject::tr("Couldn't delete key: %1").arg( gpgme_strerror(error) ) << endl;
gpgme_key_release(key);
return;
}
cout << ":: Key deleted successfully." << endl;
out << QObject::tr(":: Key deleted successfully.") << endl;
gpgme_key_release(key);
}
/* Imports a key from a keyserver */
void getFromServer(gpgme_ctx_t ctx, QString id)
{
gpgme_error_t err;
gpgme_error_t error;
gpgme_key_t key;
QTextStream err( stderr );
QTextStream out( stdout );
/* Lookup works only when the key starts with this prefix */
if (!id.startsWith("0x")) {
......@@ -320,18 +266,40 @@ void getFromServer(gpgme_ctx_t ctx, QString id)
gpgme_keylist_mode_t mode = gpgme_get_keylist_mode(ctx);
mode &= ~GPGME_KEYLIST_MODE_LOCAL;
mode |= GPGME_KEYLIST_MODE_EXTERN;
err = gpgme_set_keylist_mode(ctx, mode);
error = gpgme_set_keylist_mode(ctx, mode);
if (error) {
err << QObject::tr("Couldn't set new mode for importing: %1").arg( gpgme_strerror(error) ) << endl;
return;
}
bool found = false;
error = gpgme_op_keylist_start(ctx, id.toUtf8().data(), 0);
if (err) {
cerr << "Couldn't set new mode for importing: " << gpgme_strerror(err) << endl;
if (error) {
err << QObject::tr("Couldn't start key listing: %1").arg( gpgme_strerror(error) ) << endl;
return;
}
/* Gets the key from the remote keyserver */
err = gpgme_get_key(ctx, id.toUtf8().data(), &key, 0);
while (!error) {
error = gpgme_op_keylist_next(ctx, &key);
if (error) {
break;
}
out << "- " << key->subkeys->keyid << " " << key->uids->name << " " << key->uids->email << endl;
if (QString::fromAscii(key->subkeys->keyid) == id) {
found = true;
break;
}
gpgme_key_release(key);
}
if (err) {
cerr << "Couldn't get the requested key: " << gpgme_strerror(err) << endl;
if (gpgme_err_code(error) != GPG_ERR_EOF || !found) {
err << QObject::tr("Couldn't find key on the server: %1").arg( gpgme_strerror(error) ) << endl;
return;
}
......@@ -339,38 +307,48 @@ void getFromServer(gpgme_ctx_t ctx, QString id)
gpgme_key_t* keys = (gpgme_key_t *)malloc( 2 * sizeof(gpgme_key_t) );
keys[0] = key;
keys[1] = NULL;
err = gpgme_op_import_keys(ctx, keys);
error = gpgme_op_import_keys(ctx, keys);
free(keys);
if (err) {
cerr << "Couldn't import the key: " << gpgme_strerror(err) << endl;
if (error) {
err << QObject::tr("Couldn't import the key: %1").arg( gpgme_strerror(error) ) << endl;
gpgme_key_release(key);
return;
}
editKey(ctx, key);
editKey(key->subkeys->fpr);
cout << ":: The following key has been imported: " << key->uids->name << " " << key->uids->email << " " << key->subkeys->keyid << endl;
out << QObject::tr(":: The following key has been imported: ") << key->uids->name << " " << key->uids->email << " " << key->subkeys->keyid << endl;
gpgme_key_release(key);
}
int main(int argc, char **argv)
{
const QString AkabeiKeyringDir( "etc/akabei.d/keyring/" );
{
QCoreApplication app(argc, argv);
QDir keyring(AkabeiKeyringDir);
if (!keyring.exists()) {
QTextStream out(stdout);
out << ":: akabei-key v2.0 (akabei)" << endl;
out << ":: It looks like you don't have an Akabei keyring yet." << endl;
out << ":: This application will initialize the ring and then exit. Run again your command to get the actual result." << endl;
out << ":: Starting initialization..." << endl;
QTextStream out( stdout );
QTextStream err( stderr );
if (!keyring.exists()) {
out << QObject::tr(":: akabei-key v2.0 (akabei)") << endl;
out << QObject::tr(":: It looks like you don't have an Akabei keyring yet.") << endl;
out << QObject::tr(":: This application will initialize the ring and then exit. Run again your command to get the actual result.") << endl;
out << QObject::tr(":: Starting initialization...") << endl;
initRing(keyring);
exit(0);
}
/*
* This check is needed otherwise all functionalities may stop working (for example, the key listing will be empty even
* with keys in the keyring).
* Running the script with sudo solves.
*/
QFile pub( keyring.absoluteFilePath("pubring.gpg") );
QFile sec( keyring.absoluteFilePath("secring.gpg") );
if (!pub.open(QIODevice::ReadWrite) || !sec.open(QIODevice::ReadWrite)) {
err << QObject::tr("Cannot access keyring files for writing. Run again this script with higher permissions.") << endl;
exit(-1);
}
QStringList args = QCoreApplication::arguments();
......@@ -396,10 +374,10 @@ int main(int argc, char **argv)
/* Sets the keyring directory */
gpgme_set_engine_info(GPGME_PROTOCOL_OpenPGP, NULL, AkabeiKeyringDir.toUtf8().data());
gpgme_check_version(NULL);
gpgme_error_t err = gpgme_new(&ctx);
gpgme_error_t error = gpgme_new(&ctx);
if (err) {
cerr << "Error initializing GPG: " << gpgme_strerror(err);
if (error) {
err << QObject::tr("Error initializing GPG: %1").arg( gpgme_strerror(error) );
exit(-1);
}
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment