From 8213a660ed559679badb1158edb4269618435fe0 Mon Sep 17 00:00:00 2001 From: Aaron Ball Date: Fri, 26 Oct 2018 16:17:01 -0600 Subject: Adding supporting code for supplimentary recipients Previously, the code read the recipient list from the existing file. This was problematic when creating files from scratch, since one would not have recipients. This is also problematic if the user wanted to add a recipient to an existing file. This commits support for a growable recipient list, using the strarray code. This also splits the gpg_keyids code into its own library (gpg_keyids.h). Began preliminary move away fron in-line errors to a global error handler, gpg_failiferr. This is because inline error handling requires quite a lot of duplicate code, since nearly every operation needs to be checked for failure state. --- Makefile | 1 + src/gpg.c | 136 +++++++++++++------------------------------------------ src/gpg.h | 16 +++++-- src/gpg_keyids.c | 68 ++++++++++++++++++++++++++++ src/gpg_keyids.h | 35 ++++++++++++++ src/main.c | 10 +++- 6 files changed, 154 insertions(+), 112 deletions(-) create mode 100644 src/gpg_keyids.c create mode 100644 src/gpg_keyids.h diff --git a/Makefile b/Makefile index 71f3e4d..7dd840e 100644 --- a/Makefile +++ b/Makefile @@ -5,6 +5,7 @@ all: if [ ! -d obj ]; then mkdir obj; fi cc $(CCOPTS) $(DBG) -c src/strarray.c -o obj/strarray.o cc $(CCOPTS) $(DBG) -lgpgme -c src/gpg.c -o obj/gpg.o + cc $(CCOPTS) $(DBG) -lgpgme -c src/gpg_keyids.c -o obj/gpg_keyids.o cc $(CCOPTS) $(DBG) -lgpgme obj/*.o src/main.c -o $(out) debug: diff --git a/src/gpg.c b/src/gpg.c index 335d03f..0657add 100644 --- a/src/gpg.c +++ b/src/gpg.c @@ -13,39 +13,46 @@ // // You should have received a copy of the GNU General Public License // along with this program. If not, see . - #include "gpg.h" -#ifndef GPG_H -#define GPG_H - -#define READSIZE 256 - -gpgme_error_t init_gpg(gpgme_ctx_t* ctx, gpgme_protocol_t proto) { - gpgme_error_t err; - - gpgme_check_version (NULL); - setlocale (LC_ALL, ""); - gpgme_set_locale (NULL, LC_CTYPE, setlocale (LC_CTYPE, NULL)); - - err = gpgme_engine_check_version (proto); - err = gpgme_new(ctx); +int gpg_failiferr(gpgme_error_t err) { if(err != GPG_ERR_NO_ERROR) { printf("ERROR: "); if(err == GPG_ERR_INV_VALUE) { - printf("GPG context is not valid pointer\n"); + printf("Invalid data structure\n"); } else if(err == GPG_ERR_ENOMEM) { printf("Insufficient memory to allocate GPG context\n"); } else if(err == GPG_ERR_ENOMEM) { printf("Insufficient memory to allocate GPG context\n"); } else if(err == GPG_ERR_NOT_OPERATIONAL) { printf("GPGME not initialized\n"); + } else if(err == GPG_ERR_UNUSABLE_PUBKEY) { + printf("One or more public keys unusable\n"); + } else if(err == GPG_ERR_BAD_PASSPHRASE) { + printf("Incorrect passphrase\n"); } else { - printf("Unknown\n"); + printf("Unknown error: %d\n", err); } + printf("Message:\n %s\n", gpgme_strerror(err)); return err; } + return 0; +} + + +gpgme_error_t init_gpg(gpgme_ctx_t* ctx, gpgme_protocol_t proto) { + gpgme_error_t err; + + gpgme_check_version (NULL); + setlocale (LC_ALL, ""); + gpgme_set_locale (NULL, LC_CTYPE, setlocale (LC_CTYPE, NULL)); + + err = gpgme_engine_check_version (proto); + gpg_failiferr(err); + + err = gpgme_new(ctx); + gpg_failiferr(err); gpgme_set_protocol(*ctx, proto); gpgme_set_armor(*ctx, 1); @@ -94,70 +101,7 @@ int gpg_recip_count(gpgme_recipient_t firstrecip) { } -char** gpg_recip_get_keyids(gpgme_recipient_t firstrecip) { - gpgme_recipient_t recip; // Current recipient object - int count = 0; // Number of recipients - int i = 0; // Key index - char** keyids = NULL; - - count = gpg_recip_count(firstrecip); - keyids = (char**) malloc(sizeof(char*) * count + 1); - - // Write each recipient key id to the char array - recip = firstrecip; - while(recip != NULL) { - // For debugging purposes - //printf("Getting key for %s\n", recip->keyid); - keyids[i] = malloc(strlen(recip->keyid) + 1); - strcpy(keyids[i], recip->keyid); - - i++; - recip = recip->next; - } - - keyids[i] = NULL; - return keyids; -} - - -gpgme_key_t* gpg_keyids_to_key_t(gpgme_ctx_t ctx, char** keyids) { - gpgme_key_t* out; - gpgme_error_t err; - int count = 0; // Number of recipients - int i = 0; - - // Count the keys - while(keyids[count] != NULL) - count++; - - // Allocate the gpgme_key_t pointer array - out = malloc(sizeof(gpgme_key_t) * (count + 1)); - - // Loop over the keys back to zero - while(i < count) { - err = gpgme_get_key(ctx, keyids[i], &out[i], 0); - if(err != GPG_ERR_NO_ERROR) { - printf("Ooops! Something went wrong getting key %s\n", keyids[i]); - } - i++; - } - out[i] = NULL; - return out; -} - -void gpg_keyids_release(char** keyids) { - int i = 0; - - while(keyids[i] != NULL) { - free(keyids[i]); - i++; - } - free(keyids); -} - - - -void gpg_dump_key(gpgme_key_t key) { +void gpg_key_dump(gpgme_key_t key) { printf("Recipient: %s\n", key->fpr); printf(" protocol: %d\n", key->protocol); printf(" revoked: %d\n", key->revoked); @@ -186,46 +130,29 @@ size_t gpg_encrypt_file(char* infile, char* outfile, char** keyids) { gpgme_key_t* keys = NULL; err = init_gpg(&ctx, GPGME_PROTOCOL_OPENPGP); - if(err != GPG_ERR_NO_ERROR) { - printf("Error: %s\n", gpgme_strerror(err)); - return -1; - } + gpg_failiferr(err); // For debugging purposes - // printf("Reading updated data from memory\n"); - // Initialize data structures + // printf("Reading updated data from memory\n"); // Initialize data structures err = gpgme_data_new_from_file(&indata, infile, 1); - if(err != GPG_ERR_NO_ERROR) - printf("Error: %s\n", gpgme_strerror(err)); + gpg_failiferr(err); err = gpgme_data_new(&outdata); - if(err != GPG_ERR_NO_ERROR) - printf("Error: %s\n", gpgme_strerror(err)); + gpg_failiferr(err); // Get key id objects from keyids char array keys = gpg_keyids_to_key_t(ctx, keyids); // For debugging purposes // int i = 0; // while(keys[i] != NULL) { - // gpg_dump_key(keys[i]); + // gpg_key_dump(keys[i]); // i++; // } // Encrypt! err = gpgme_op_encrypt(ctx, keys, GPGME_ENCRYPT_ALWAYS_TRUST, indata, outdata); + gpg_failiferr(err); - if(err != GPG_ERR_NO_ERROR) { - if(err == GPG_ERR_UNUSABLE_PUBKEY) { - printf("One or more public keys unusable\n"); - } else if(err == GPG_ERR_INV_VALUE) { - printf("Invalid data structure\n"); - } else if(err == GPG_ERR_BAD_PASSPHRASE) { - printf("Incorrect passphrase\n"); - } else { - printf("Unknown error: %d\n", err); - printf("Message:\n %s\n", gpgme_strerror(err)); - } - } // For debugging purposes // printf("No errors detecting during encryption.\n"); @@ -294,4 +221,3 @@ gpg_decrypt_file(char* infile, char* outfile, gpgme_ctx_t ctx) { gpgme_data_release(outdata); return gpgme_op_decrypt_result(ctx); } -#endif diff --git a/src/gpg.h b/src/gpg.h index 38ae4b2..6e73d2e 100644 --- a/src/gpg.h +++ b/src/gpg.h @@ -14,22 +14,28 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . + +#ifndef GPGEDIT_GPG_H +#define GPGEDIT_GPG_H + #include #include #include #include #include +#include "gpg_keyids.h" + +#define READSIZE 256 + int gpg_data_fwrite(gpgme_data_t, FILE*); gpgme_error_t init_gpg(gpgme_ctx_t*, gpgme_protocol_t); gpgme_decrypt_result_t gpg_decrypt_file(char*, char*, gpgme_ctx_t); -char** gpg_recip_get_keyids(gpgme_recipient_t); - -void gpg_keyids_release(char**); +size_t gpg_encrypt_file(char*, char*, char**); -gpgme_key_t* gpg_keyids_to_key_t(gpgme_ctx_t, char**); +int gpg_failiferr(gpg_error_t); -size_t gpg_encrypt_file(char*, char*, char**); +#endif diff --git a/src/gpg_keyids.c b/src/gpg_keyids.c new file mode 100644 index 0000000..5b0edfe --- /dev/null +++ b/src/gpg_keyids.c @@ -0,0 +1,68 @@ +// GPGEdit edits GPG encrypted files +// Copyright (C) 2018 Aaron Ball +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +#include "gpg_keyids.h" + + +// gpg_keyids_load_from_recip: +// Converts a gpgme_recipient_t linked list to a string array of key ids. This +// is useful when storing a list of recipients for an already-encrypted file to +// be used when later re-encrypting that file. +// +// @keyids Pointer to existing keyids string array. Note that the pointer +// to this will be updated by the array growth process. +// @firstrecip First recipient in the linked list. The list will be traversed +// starting here. +void gpg_keyids_load_from_recip(char*** keyids, gpgme_recipient_t firstrecip) { + gpgme_recipient_t recip; // Current recipient object + // Write each recipient key id to the char array + recip = firstrecip; + while(recip != NULL) { + strarray_add(keyids, recip->keyid); + recip = recip->next; + } +} + + +// gpg_keyids_to_key_t: +// Converts a string array containing keyids (multiple identifier types are +// supported) into an array of gpgme_key_t objects. +// +// @ctx GPGME context (already initialized) +// @keyids Pointer to string array of key id strings +// +// @return gpgme_key_t* Array of gpgme_key_t objects, one for each keyid string +gpgme_key_t* gpg_keyids_to_key_t(gpgme_ctx_t ctx, char** keyids) { + gpgme_key_t* out; + gpgme_error_t err; + int count = 0; // Number of recipients + int i = 0; + + // Count the keys + while(keyids[count] != NULL) + count++; + + // Allocate the gpgme_key_t pointer array + out = (gpgme_key_t*) malloc(sizeof(gpgme_key_t) * (count + 1)); + + // Loop over the keys back to zero + while(i < count) { + err = gpgme_get_key(ctx, keyids[i], &out[i], 0); + gpg_failiferr(err); + i++; + } + out[i] = NULL; + return out; +} diff --git a/src/gpg_keyids.h b/src/gpg_keyids.h new file mode 100644 index 0000000..70ad9d9 --- /dev/null +++ b/src/gpg_keyids.h @@ -0,0 +1,35 @@ +// GPGEdit edits GPG encrypted files +// Copyright (C) 2018 Aaron Ball +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#ifndef GPGEDIT_GPG_KEYIDS_H +#define GPGEDIT_GPG_KEYIDS_H + +#include +#include +#include +#include +#include + +#include "gpg.h" +#include "strarray.h" + +void gpg_keyids_load_from_recip(char***, gpgme_recipient_t); +gpgme_key_t* gpg_keyids_to_key_t(gpgme_ctx_t, char**); + +//void gpg_keyids_release(char**); +//char** gpg_keyids_new(); + +#endif diff --git a/src/main.c b/src/main.c index f5ae1c7..7eed5b2 100644 --- a/src/main.c +++ b/src/main.c @@ -19,7 +19,10 @@ #include #include #include + #include "gpg.h" +#include "gpg_keyids.h" +#include "strarray.h" void system_edit(char* file) { char cmd[256]; // Buffer for editor command @@ -32,6 +35,7 @@ int main(int argc, char* argv[]) { char tmpfile[32] = "/tmp/gpgedit-XXXXXX"; gpgme_ctx_t decctx; gpgme_error_t err; + char** keyids; if(argc == 1) { printf("Please specify a gpg encrypted file to edit\n"); @@ -49,7 +53,8 @@ int main(int argc, char* argv[]) { mkstemp(tmpfile); gpgme_decrypt_result_t res = gpg_decrypt_file(argv[1], tmpfile, decctx); // Get keyids array from recipients list - char** keyids = gpg_recip_get_keyids(res->recipients); + keyids = strarray_new(); + gpg_keyids_load_from_recip(&keyids, res->recipients); // Freedom! gpgme_release(decctx); @@ -57,10 +62,11 @@ int main(int argc, char* argv[]) { system_edit(tmpfile); // Re-encrypt the plaintext tmp file + //printf("Re-encrypting file '%s' -> '%s'\n", tmpfile, argv[1]); gpg_encrypt_file(tmpfile, argv[1], keyids); // Clean up tmpfile - gpg_keyids_release(keyids); + strarray_release(&keyids); unlink(tmpfile); return 0; } -- cgit v1.2.3