diff options
author | Aaron Ball <nullspoon@oper.io> | 2018-10-24 17:52:07 -0600 |
---|---|---|
committer | Aaron Ball <nullspoon@oper.io> | 2018-10-24 17:58:32 -0600 |
commit | 25378d03e6fe81321952ca8e41580027f71943ed (patch) | |
tree | 29c0e2df47ad4d891884ed7584260f1b9401c344 | |
parent | 02d9ba096937a3f931080233bcd73c99ef9c612a (diff) | |
download | gpgedit-25378d03e6fe81321952ca8e41580027f71943ed.tar.gz gpgedit-25378d03e6fe81321952ca8e41580027f71943ed.tar.xz |
Implement re-encrypt support
gpg.c: Add new init_gpg function that more closely mimicks that defined
in the gpgme tests. This however initializes the passed-in context
object, reducing duplicate code. Removed old gpg_init.
Clean up linked library dependencies.
Implemented gpg_keyids_to_key_t() to convert arrays of key id strings to
an array of key objects (don't forget to free the array!).
Implemented gpg_recip_count, which counts the number of recipients for
the given gpg_op_decrypt_result_t object.
Implemented gpg_dump_key, which dumps all of the human-readable
information in a gpgme_key_t object to stdout. This is for debugging key
issues.
-rw-r--r-- | src/gpg.c | 244 | ||||
-rw-r--r-- | src/gpg.h | 15 | ||||
-rw-r--r-- | src/main.c | 48 |
3 files changed, 238 insertions, 69 deletions
@@ -21,12 +21,50 @@ #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); + if(err != GPG_ERR_NO_ERROR) { + printf("ERROR: "); + if(err == GPG_ERR_INV_VALUE) { + printf("GPG context is not valid pointer\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 { + printf("Unknown\n"); + } + return err; + } + + gpgme_set_protocol(*ctx, proto); + gpgme_set_armor(*ctx, 1); -int gpgme_data_fwrite(gpgme_data_t dh, FILE* out) { + return err; +} + + +int gpg_data_fwrite(gpgme_data_t dh, FILE* out) { char buf[READSIZE]; size_t total = 0; size_t size = 0; + gpgme_data_seek(dh, 0, SEEK_SET); + if(gpgme_data_read(dh, buf, 1) < 1) { + printf("Error: GPG data struct is empty. Nothing to write.\n"); + return -1; + } + // Set seek to beginning of file gpgme_data_seek(dh, 0, SEEK_SET); @@ -40,44 +78,182 @@ int gpgme_data_fwrite(gpgme_data_t dh, FILE* out) { } -int gpg_init(gpgme_ctx_t* ctx) { +int gpg_recip_count(gpgme_recipient_t firstrecip) { + gpgme_recipient_t recip; // Current recipient object + int count = 0; // Count of recipients + + // Get recipient list + recip = firstrecip; + while(recip != NULL) { + //printf("-- recipient: %s\n", recip->keyid); + recip = recip->next; + count++; + } + + return count; +} + + +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; - // Initialize - setlocale(LC_ALL, ""); - gpgme_check_version(NULL); - // Create context and check if anything failed - err = gpgme_new(ctx); + 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) { + printf("Recipient: %s\n", key->fpr); + printf(" protocol: %d\n", key->protocol); + printf(" revoked: %d\n", key->revoked); + printf(" expired: %d\n", key->expired); + printf(" disabled: %d\n", key->disabled); + printf(" invalid: %d\n", key->invalid); + printf(" can_encrypt: %d\n", key->can_encrypt); + printf(" can_sign: %d\n", key->can_sign); + printf(" can_certify: %d\n", key->can_certify); + printf(" can_auth: %d\n", key->can_authenticate); + printf(" qualified: %d\n", key->is_qualified); + printf(" secret: %d\n", key->secret); + printf(" origin: %d\n", key->origin); + printf(" issuer_serial: %s\n", key->issuer_serial); + printf(" issuer_name: %s\n", key->issuer_name); + printf(" chain_id: %s\n", key->chain_id); + printf(" last_update: %ld\n", key->last_update); +} + + +size_t gpg_encrypt_file(char* infile, char* outfile, char** keyids) { + gpgme_ctx_t ctx; + gpgme_data_t indata; + gpgme_data_t outdata; + gpgme_error_t err; + gpgme_key_t* keys = NULL; + + err = init_gpg(&ctx, GPGME_PROTOCOL_OPENPGP); if(err != GPG_ERR_NO_ERROR) { - printf("ERROR: "); - if(err == GPG_ERR_INV_VALUE) { - printf("GPG context is not valid pointer\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"); + printf("Error: %s\n", gpgme_strerror(err)); + return -1; + } + + // For debugging purposes + // 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)); + + err = gpgme_data_new(&outdata); + if(err != GPG_ERR_NO_ERROR) + printf("Error: %s\n", gpgme_strerror(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]); + // i++; + // } + + // Encrypt! + err = gpgme_op_encrypt(ctx, keys, GPGME_ENCRYPT_ALWAYS_TRUST, indata, outdata); + + 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\n"); + printf("Unknown error: %d\n", err); + printf("Message:\n %s\n", gpgme_strerror(err)); } - return -1; } + // For debugging purposes + // printf("No errors detecting during encryption.\n"); - gpgme_set_protocol(*ctx, GPGME_PROTOCOL_OpenPGP); - gpgme_set_armor(*ctx, 1); + // For debugging purposes + // printf("Writing to disk\n"); + // Write to disk! + FILE* refd = fopen(outfile, "w"); + gpg_data_fwrite(outdata, refd); + fclose(refd); + + // cleanup + gpg_keyids_release(keyids); + gpgme_data_release(indata); + gpgme_data_release(outdata); + gpgme_release(ctx); return 0; } -size_t gpg_decrypt(char* file, gpgme_ctx_t* ctx, gpgme_data_t plain) { +gpgme_decrypt_result_t +gpg_decrypt_file(char* infile, char* outfile, gpgme_ctx_t ctx) { gpgme_error_t err; - gpgme_data_t intxt; - //gpgme_decrypt_result_t* res; - + gpgme_data_t indata = NULL; + gpgme_data_t outdata = NULL; // Read ciphertext file into gpgme data struct, and fail if any errors // returned. - err = gpgme_data_new_from_file(&intxt, file, 1); + gpgme_data_new(&outdata); + err = gpgme_data_new_from_file(&indata, infile, 1); if(err != GPG_ERR_NO_ERROR) { printf("ERROR: "); if(err == GPG_ERR_INV_VALUE) { @@ -87,11 +263,11 @@ size_t gpg_decrypt(char* file, gpgme_ctx_t* ctx, gpgme_data_t plain) { } else if(err == GPG_ERR_ENOMEM) { printf("Insufficient memory to allocate data struct\n"); } - return err; + return NULL; } // Attempt to decrypt - err = gpgme_op_decrypt(*ctx, intxt, plain); + err = gpgme_op_decrypt(ctx, indata, outdata); if(err != GPG_ERR_NO_ERROR) { printf("ERROR: "); if(err == GPG_ERR_INV_VALUE) { @@ -103,15 +279,19 @@ size_t gpg_decrypt(char* file, gpgme_ctx_t* ctx, gpgme_data_t plain) { } else if(err == GPG_ERR_BAD_PASSPHRASE) { printf("Passphrase is incorrect\n"); } else { - printf("Unknown\n"); + printf("Unhandled error\n %s\n", gpgme_strerror(err)); } - return err; + return NULL; } + // Write decrypted data to outfile + FILE* outfd = fopen(outfile, "w"); + gpg_data_fwrite(outdata, outfd); - //res = gpgme_op_decrypt_result(ctx); // Clean up - gpgme_data_release(intxt); - return GPG_ERR_NO_ERROR; + fclose(outfd); + gpgme_data_release(indata); + gpgme_data_release(outdata); + return gpgme_op_decrypt_result(ctx); } #endif @@ -17,12 +17,19 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> -#include <unistd.h> #include <gpgme.h> #include <locale.h> -int gpgme_data_fwrite(gpgme_data_t, FILE*); +int gpg_data_fwrite(gpgme_data_t, FILE*); -int gpg_init(gpgme_ctx_t*); -size_t gpg_decrypt(char*, gpgme_ctx_t*, gpgme_data_t); +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**); + +gpgme_key_t* gpg_keyids_to_key_t(gpgme_ctx_t, char**); + +size_t gpg_encrypt_file(char*, char*, char**); @@ -19,10 +19,8 @@ #include <string.h> #include <unistd.h> #include <gpgme.h> -#include <locale.h> #include "gpg.h" - void system_edit(char* file) { char cmd[256]; // Buffer for editor command sprintf(cmd, "%s %s\n", "vim", file); @@ -31,51 +29,35 @@ void system_edit(char* file) { int main(int argc, char* argv[]) { - char tmpfile[64] = "/tmp/gpgedit-XXXXXX"; - FILE* fd; - gpgme_data_t plain; - gpgme_ctx_t ctx; + char tmpfile[32] = "/tmp/gpgedit-XXXXXX"; + gpgme_ctx_t decctx; + gpgme_error_t err; if(argc == 1) { printf("Please specify a gpg encrypted file to edit\n"); return 1; } - if(gpg_init(&ctx) != 0) { - printf("ERROR\n"); + // Initialize decryption context, create tmp file to write decrypted contents + // to, and run decryption operation. + err = init_gpg(&decctx, GPGME_PROTOCOL_OPENPGP); + if(err != GPG_ERR_NO_ERROR) { + printf("Error: %s\n", gpgme_strerror(err)); return 1; } - // Create empty initialized gpgme data struct for output - gpgme_data_new(&plain); - // Decrypt file - gpg_decrypt(argv[1], &ctx, plain); - // Get decryption result - gpgme_decrypt_result_t res = gpgme_op_decrypt_result(ctx); - - // Get recipient list - gpgme_recipient_t recip; - recip = res->recipients; - while(recip != NULL) { - printf("-- recipient: %s\n", recip->keyid); - recip = recip->next; - } - // Create tmp file to write decrypted contents to mkstemp(tmpfile); - fd = fopen(tmpfile, "w"); - - // Write to stdout - gpgme_data_fwrite(plain, fd); - - // Cleanup - fclose(fd); // close output file descriptor - gpgme_data_release(plain); // Release gpgme_data_t plain object - gpgme_release(ctx); + 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); + // Freedom! + gpgme_release(decctx); // Open the system editor system_edit(tmpfile); - // TODO: Encrypt and Write tmpfile to storage + // Re-encrypt the plaintext tmp file + gpg_encrypt_file(tmpfile, argv[1], keyids); // Clean up tmpfile unlink(tmpfile); |