From 03d5348f4954acddd22367da64d8292587fbb3fa Mon Sep 17 00:00:00 2001 From: Damien Goutte-Gattat Date: Sat, 12 Jan 2019 20:04:40 +0000 Subject: [PATCH] Implement encryption only. Allow the use of -E option to specify a recipient to encrypt the message to. If several -E options are used, the message will be encrypted to all specified recipients. --- src/fmail.c | 121 ++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 113 insertions(+), 8 deletions(-) diff --git a/src/fmail.c b/src/fmail.c index 7156b02..757a019 100644 --- a/src/fmail.c +++ b/src/fmail.c @@ -114,6 +114,8 @@ typedef struct fmail_ctx { const char **attachments; size_t att_count; + const char **recipients; + size_t rcp_count; const char *footer; magic_t magic_ctx; gpgme_ctx_t crypto_ctx; @@ -300,7 +302,7 @@ process_text_body(fmail_ctx_t *ctx, FILE *out) -/* Signing stuff. */ +/* Crypto stuff. */ gpgme_ctx_t initialize_gpgme(void) @@ -402,6 +404,99 @@ sign_stream(gpgme_ctx_t ctx, FILE *in, FILE *out) gpgme_data_release(gout); } +static gpgme_key_t * +get_recipient_keys(gpgme_ctx_t ctx, const char **recipients, size_t nr) +{ + gpgme_key_t *keys, key; + gpgme_error_t gerr; + int i, j; + + keys = NULL; + + for ( i = j = gerr = 0; i < nr && ! gerr ; i++ ) { + gerr = gpgme_op_keylist_start (ctx, recipients[i], 0); + while ( ! gerr ) { + gerr = gpgme_op_keylist_next (ctx, &key); + if ( gerr ) + break; + + if ( j % 10 == 0 ) + keys = xrealloc(keys, j + 10); + keys[j++] = key; + } + if ( gpgme_err_code(gerr) == GPG_ERR_EOF ) + gerr = 0; + } + if ( gerr != 0 && gpgme_err_code(gerr) != GPG_ERR_EOF) + errx(EXIT_FAILURE, "cannot get recipient keys: %s", gpgme_strerror(gerr)); + + /* GpgME expects a NULL-terminated array of keys. */ + if ( j % 10 == 0 ) + keys = xrealloc(keys, j + 1); + keys[j++] = NULL; + + return keys; +} + +static void +encrypt_stream(gpgme_ctx_t ctx, + FILE *in, + FILE *out, + const char **recipients, + size_t nr) +{ + gpgme_data_t gin, gout; + gpgme_key_t *keys, *key; + gpgme_error_t gerr; + char boundary[32], buffer[512]; + int n; + + keys = get_recipient_keys(ctx, recipients, nr); + gpgme_data_new_from_stream(&gin, in); + gpgme_data_new(&gout); + + gerr = gpgme_op_encrypt(ctx, keys, 0, gin, gout); + if ( gerr != GPG_ERR_NO_ERROR ) + errx(EXIT_FAILURE, "encrypting failed: %s", gpgme_strerror(gerr)); + + generate_boundary(boundary, sizeof(boundary)); + fprintf(out, "Content-Type: multipart/encrypted;\r\n" + " boundary=\"%s\";\r\n" + " protocol=\"application/pgp-encrypted\r\n" + "\r\n" + "--%s\r\n" + "Content-Type: application/pgp-encrypted\r\n" + "\r\n" + "Version: 1\r\n" + "\r\n" + "--%s\r\n" + "Content-Type: application/octet-stream\r\n" + "\r\n", + boundary, boundary, boundary); + + gpgme_data_seek(gout, 0, SEEK_SET); + while ( (n = gpgme_data_read(gout, buffer, sizeof(buffer))) > 0 ) { + int i = 0; + + while ( i < n ) { + if ( buffer[i] == '\n' ) + fputc('\r', out); + fputc(buffer[i], out); + + i += 1; + } + } + + fprintf(out, "\r\n--%s--\r\n", boundary); + + gpgme_data_release(gin); + gpgme_data_release(gout); + + for ( key = keys; *key != NULL; key++ ) + gpgme_key_release(*key); + free(keys); +} + /* Attachments stuff. */ @@ -535,7 +630,7 @@ main(int argc, char *argv[]) char c, with_useragent, with_date; string_buffer_t *headers; fmail_ctx_t ctx; - int do_sign, do_encrypt; + int do_sign; struct option options[] = { { "help", 0, NULL, 'h' }, @@ -559,13 +654,15 @@ main(int argc, char *argv[]) setlocale(LC_ALL, ""); srand(time(NULL)); - with_useragent = with_date = do_sign = do_encrypt = 0; + with_useragent = with_date = do_sign = 0; headers = sb_new(0); ctx.crypto_ctx = NULL; ctx.magic_ctx = initialize_magic(); ctx.attachments = NULL; ctx.att_count = 0; + ctx.recipients = NULL; + ctx.rcp_count = 0; ctx.footer = NULL; ctx.input = stdin; @@ -589,7 +686,9 @@ main(int argc, char *argv[]) break; case 'E': - do_encrypt = 1; + if ( ctx.rcp_count % 10 == 0 ) + ctx.recipients = xrealloc(ctx.recipients, ctx.rcp_count + 10); + ctx.recipients[ctx.rcp_count++] = optarg; break; case 'a': @@ -636,9 +735,9 @@ main(int argc, char *argv[]) } } - if ( do_sign && do_encrypt ) + if ( do_sign && ctx.rcp_count ) errx(EXIT_FAILURE, "encrypt and sign is not currently supported"); - if ( do_sign || do_encrypt ) + if ( do_sign || ctx.rcp_count ) ctx.crypto_ctx = initialize_gpgme(); /* Generate automatic headers */ @@ -657,12 +756,15 @@ main(int argc, char *argv[]) /* Write all headers. */ fprintf(stdout, "%s", sb_get(headers)); - if ( do_sign ) { + if ( do_sign || ctx.rcp_count ) { FILE *tmp = tmpfile(); process_text_body(&ctx, tmp); fseek(tmp, 0, SEEK_SET); - sign_stream(ctx.crypto_ctx, tmp, stdout); + if ( do_sign ) + sign_stream(ctx.crypto_ctx, tmp, stdout); + else + encrypt_stream(ctx.crypto_ctx, tmp, stdout, ctx.recipients, ctx.rcp_count); fclose(tmp); gpgme_release(ctx.crypto_ctx); @@ -675,6 +777,9 @@ main(int argc, char *argv[]) if ( ctx.attachments ) free(ctx.attachments); + if ( ctx.recipients ) + free(ctx.recipients); + magic_close(ctx.magic_ctx); return EXIT_SUCCESS;