From 2fd6f99bea1337e1b490a9056033fe0151f22ddc Mon Sep 17 00:00:00 2001 From: Kevin McCarthy Date: Thu, 10 Aug 2017 18:18:21 -0700 Subject: [PATCH] Change recvattach to allow nested encryption. (see #3728) * Add a FP and BODY array to the actx. These are used to allow proper cleanup. * Add HEADER and root_fp entries, to allow for index regeneration. * Separate out the compose and recvattach index generation functions. * Change the recvattach index generator to decrypt as encrypted parts are found. --- attach.c | 41 ++++++- attach.h | 17 ++- compose.c | 38 ++++++- protos.h | 1 - recvattach.c | 302 +++++++++++++++++++++++++++------------------------ 5 files changed, 249 insertions(+), 150 deletions(-) diff --git a/attach.c b/attach.c index 9b4b649e..6a12d82f 100644 --- a/attach.c +++ b/attach.c @@ -1059,6 +1059,36 @@ void mutt_actx_add_attach (ATTACH_CONTEXT *actx, ATTACHPTR *attach, MUTTMENU *me actx->idx[actx->idxlen++] = attach; } +void mutt_actx_add_fp (ATTACH_CONTEXT *actx, FILE *new_fp) +{ + int i; + + if (actx->fp_len == actx->fp_max) + { + actx->fp_max += 5; + safe_realloc (&actx->fp_idx, sizeof (FILE *) * actx->fp_max); + for (i = actx->fp_len; i < actx->fp_max; i++) + actx->fp_idx[i] = NULL; + } + + actx->fp_idx[actx->fp_len++] = new_fp; +} + +void mutt_actx_add_body (ATTACH_CONTEXT *actx, BODY *new_body) +{ + int i; + + if (actx->body_len == actx->body_max) + { + actx->body_max += 5; + safe_realloc (&actx->body_idx, sizeof (BODY *) * actx->body_max); + for (i = actx->body_len; i < actx->body_max; i++) + actx->body_idx[i] = NULL; + } + + actx->body_idx[actx->body_len++] = new_body; +} + void mutt_actx_free_entries (ATTACH_CONTEXT *actx) { int i; @@ -1070,8 +1100,15 @@ void mutt_actx_free_entries (ATTACH_CONTEXT *actx) FREE (&actx->idx[i]->tree); FREE (&actx->idx[i]); } - actx->idxlen = 0; + + for (i = 0; i < actx->fp_len; i++) + safe_fclose (&actx->fp_idx[i]); + actx->fp_len = 0; + + for (i = 0; i < actx->body_len; i++) + mutt_free_body (&actx->body_idx[i]); + actx->body_len = 0; } void mutt_free_attach_context (ATTACH_CONTEXT **pactx) @@ -1084,5 +1121,7 @@ void mutt_free_attach_context (ATTACH_CONTEXT **pactx) actx = *pactx; mutt_actx_free_entries (actx); FREE (&actx->idx); + FREE (&actx->fp_idx); + FREE (&actx->body_idx); FREE (pactx); /* __FREE_CHECKED__ */ } diff --git a/attach.h b/attach.h index 41d78087..80eed506 100644 --- a/attach.h +++ b/attach.h @@ -26,21 +26,34 @@ typedef struct attachptr { BODY *content; + FILE *fp; /* used in the recvattach menu. */ int parent_type; char *tree; int level; int num; unsigned int unowned : 1; /* don't unlink on detach */ + unsigned int decrypted : 1; /* not part of message as stored in the hdr->content. */ } ATTACHPTR; typedef struct attach_ctx { + HEADER *hdr; /* used by recvattach for updating */ + FILE *root_fp; /* used by recvattach for updating */ + ATTACHPTR **idx; short idxlen; short idxmax; + + FILE **fp_idx; /* Extra FILE* used for decryption */ + short fp_len; + short fp_max; + + BODY **body_idx; /* Extra BODY* used for decryption */ + short body_len; + short body_max; } ATTACH_CONTEXT; -void mutt_gen_attach_list (ATTACH_CONTEXT *, BODY *, int, int, int); +void mutt_attach_init (ATTACH_CONTEXT *); void mutt_update_tree (ATTACH_CONTEXT *); int mutt_view_attachment (FILE*, BODY *, int, HEADER *, ATTACH_CONTEXT *); @@ -60,6 +73,8 @@ void mutt_attach_forward (FILE *, HEADER *, ATTACH_CONTEXT *, BODY *); void mutt_attach_reply (FILE *, HEADER *, ATTACH_CONTEXT *, BODY *, int); void mutt_actx_add_attach (ATTACH_CONTEXT *actx, ATTACHPTR *attach, MUTTMENU *menu); +void mutt_actx_add_fp (ATTACH_CONTEXT *actx, FILE *new_fp); +void mutt_actx_add_body (ATTACH_CONTEXT *actx, BODY *new_body); void mutt_actx_free_entries (ATTACH_CONTEXT *actx); void mutt_free_attach_context (ATTACH_CONTEXT **pactx); diff --git a/compose.c b/compose.c index 4da1e5ff..87bcb47a 100644 --- a/compose.c +++ b/compose.c @@ -450,6 +450,38 @@ static int delete_attachment (MUTTMENU *menu, short *idxlen, int x) return (0); } +static void mutt_gen_compose_attach_list (ATTACH_CONTEXT *actx, + BODY *m, + int parent_type, + int level) +{ + ATTACHPTR *new; + + for (; m; m = m->next) + { + if (m->type == TYPEMULTIPART && m->parts + && (!(WithCrypto & APPLICATION_PGP) || !mutt_is_multipart_encrypted(m)) + ) + { + mutt_gen_compose_attach_list (actx, m->parts, m->type, level); + } + else + { + new = (ATTACHPTR *) safe_calloc (1, sizeof (ATTACHPTR)); + mutt_actx_add_attach (actx, new, NULL); + new->content = m; + m->aptr = new; + new->parent_type = parent_type; + new->level = level; + + /* We don't support multipart messages in the compose menu yet */ + } + } + + if (level == 0) + mutt_update_tree (actx); +} + static void update_idx (MUTTMENU *menu, ATTACH_CONTEXT *actx, ATTACHPTR *new) { new->level = (actx->idxlen > 0) ? actx->idx[actx->idxlen-1]->level : 0; @@ -665,8 +697,8 @@ int mutt_compose_menu (HEADER *msg, /* structure for new message */ rd.fcc = fcc; actx = safe_calloc (sizeof(ATTACH_CONTEXT), 1); - mutt_attach_init (msg->content); - mutt_gen_attach_list (actx, msg->content, -1, 0, 1); + mutt_gen_compose_attach_list (actx, msg->content, -1, 0); + mutt_attach_init (actx); menu = mutt_new_menu (MENU_COMPOSE); menu->offset = HDR_ATTACH; @@ -787,7 +819,7 @@ int mutt_compose_menu (HEADER *msg, /* structure for new message */ if (actx->idxlen && actx->idx[actx->idxlen - 1]->content->next) { mutt_actx_free_entries (actx); - mutt_gen_attach_list (actx, msg->content, -1, 0, 1); + mutt_gen_compose_attach_list (actx, msg->content, -1, 0); menu->data = actx->idx; menu->max = actx->idxlen; } diff --git a/protos.h b/protos.h index 802cd902..4f371684 100644 --- a/protos.h +++ b/protos.h @@ -163,7 +163,6 @@ void mutt_add_to_reference_headers (ENVELOPE *env, ENVELOPE *curenv, LIST ***pp, void mutt_adv_mktemp (char *, size_t); void mutt_alias_menu (char *, size_t, ALIAS *); void mutt_allow_interrupt (int); -void mutt_attach_init (BODY *); void mutt_block_signals (void); void mutt_block_signals_system (void); int mutt_body_handler (BODY *, STATE *); diff --git a/recvattach.c b/recvattach.c index 02e80e84..a0d44e15 100644 --- a/recvattach.c +++ b/recvattach.c @@ -40,6 +40,8 @@ #include #include +static void mutt_update_recvattach_menu (ATTACH_CONTEXT *actx, MUTTMENU *menu, int init); + static const char *Mailbox_is_read_only = N_("Mailbox is read-only."); #define CHECK_READONLY if (Context->readonly) \ @@ -98,49 +100,6 @@ void mutt_update_tree (ATTACH_CONTEXT *actx) } } -void mutt_gen_attach_list (ATTACH_CONTEXT *actx, - BODY *m, - int parent_type, - int level, - int compose) -{ - ATTACHPTR *new; - - for (; m; m = m->next) - { - if (m->type == TYPEMULTIPART && m->parts - && (compose || (parent_type == -1 && ascii_strcasecmp ("alternative", m->subtype))) - && (!(WithCrypto & APPLICATION_PGP) || !mutt_is_multipart_encrypted(m)) - ) - { - mutt_gen_attach_list (actx, m->parts, m->type, level, compose); - } - else - { - new = (ATTACHPTR *) safe_calloc (1, sizeof (ATTACHPTR)); - mutt_actx_add_attach (actx, new, NULL); - new->content = m; - m->aptr = new; - new->parent_type = parent_type; - new->level = level; - - /* We don't support multipart messages in the compose menu yet */ - if (!compose && !m->collapsed && - ((m->type == TYPEMULTIPART - && (!(WithCrypto & APPLICATION_PGP) - || !mutt_is_multipart_encrypted (m)) - ) - || mutt_is_message_type(m->type, m->subtype))) - { - mutt_gen_attach_list (actx, m->parts, m->type, level + 1, compose); - } - } - } - - if (level == 0) - mutt_update_tree (actx); -} - /* %c = character set: convert? * %C = character set * %D = deleted flag @@ -796,26 +755,13 @@ void mutt_print_attachment_list (FILE *fp, int tag, BODY *top) print_attachment_list (fp, tag, top, &state); } -static void -mutt_update_attach_index (ATTACH_CONTEXT *actx, BODY *cur, MUTTMENU *menu) -{ - mutt_actx_free_entries (actx); - mutt_gen_attach_list (actx, cur, -1, 0, 0); - - menu->max = actx->idxlen; - menu->data = actx->idx; - - if (menu->current >= menu->max) - menu->current = menu->max - 1; - menu_check_recenter (menu); - menu->redraw |= REDRAW_INDEX; -} - int mutt_attach_display_loop (MUTTMENU *menu, int op, FILE *fp, HEADER *hdr, BODY *cur, ATTACH_CONTEXT *actx, int recv) { + int i; + do { switch (op) @@ -854,7 +800,13 @@ mutt_attach_display_loop (MUTTMENU *menu, int op, FILE *fp, HEADER *hdr, immediately */ mutt_edit_content_type (hdr, actx->idx[menu->current]->content, fp); if (recv) - mutt_update_attach_index (actx, cur, menu); + { + /* Editing the content type can rewrite the body structure. */ + for (i = 0; i < actx->idxlen; i++) + actx->idx[i]->content = NULL; + mutt_actx_free_entries (actx); + mutt_update_recvattach_menu (actx, menu, 1); + } op = OP_VIEW_ATTACH; break; /* functions which are passed through from the pager */ @@ -881,6 +833,143 @@ mutt_attach_display_loop (MUTTMENU *menu, int op, FILE *fp, HEADER *hdr, return op; } +static void mutt_generate_recvattach_list (ATTACH_CONTEXT *actx, + HEADER *hdr, + BODY *m, + FILE *fp, + int parent_type, + int level, + int decrypted) +{ + ATTACHPTR *new; + BODY *new_body = NULL; + FILE *new_fp = NULL; + int need_secured, secured; + + for (; m; m = m->next) + { + need_secured = secured = 0; + + if ((WithCrypto & APPLICATION_SMIME) && + mutt_is_application_smime (m)) + { + need_secured = 1; + + if (!crypt_valid_passphrase (APPLICATION_SMIME)) + goto decrypt_failed; + + if (hdr->env) + crypt_smime_getkeys (hdr->env); + + secured = !crypt_smime_decrypt_mime (fp, &new_fp, m, &new_body); + + /* S/MIME nesting */ + if ((mutt_is_application_smime (new_body) & SMIMEOPAQUE)) + { + BODY *outer_new_body = new_body; + FILE *outer_fp = new_fp; + + new_body = NULL; + new_fp = NULL; + + secured = !crypt_smime_decrypt_mime (outer_fp, &new_fp, outer_new_body, + &new_body); + + mutt_free_body (&outer_new_body); + safe_fclose (&outer_fp); + } + + if (secured) + hdr->security |= SMIMEENCRYPT; + } + + if ((WithCrypto & APPLICATION_PGP) && + (mutt_is_multipart_encrypted (m) || + mutt_is_malformed_multipart_pgp_encrypted (m))) + { + need_secured = 1; + + if (!crypt_valid_passphrase (APPLICATION_PGP)) + goto decrypt_failed; + + secured = !crypt_pgp_decrypt_mime (fp, &new_fp, m, &new_body); + + if (secured) + hdr->security |= PGPENCRYPT; + } + + if (need_secured && secured) + { + mutt_actx_add_fp (actx, new_fp); + mutt_actx_add_body (actx, new_body); + mutt_generate_recvattach_list (actx, hdr, new_body, new_fp, parent_type, level, 1); + continue; + } + +decrypt_failed: + /* Fall through and show the original parts if decryption fails */ + if (need_secured && !secured) + mutt_error _("Can't decrypt encrypted message!"); + + /* Strip out the top level multipart */ + if (m->type == TYPEMULTIPART && + m->parts && + !need_secured && + (parent_type == -1 && ascii_strcasecmp ("alternative", m->subtype))) + { + mutt_generate_recvattach_list (actx, hdr, m->parts, fp, m->type, level, decrypted); + } + else + { + new = (ATTACHPTR *) safe_calloc (1, sizeof (ATTACHPTR)); + mutt_actx_add_attach (actx, new, NULL); + + new->content = m; + new->fp = fp; + m->aptr = new; + new->parent_type = parent_type; + new->level = level; + new->decrypted = decrypted; + + if (m->type == TYPEMULTIPART || + mutt_is_message_type(m->type, m->subtype)) + { + mutt_generate_recvattach_list (actx, hdr, m->parts, fp, m->type, level + 1, decrypted); + } + } + } +} + +void mutt_attach_init (ATTACH_CONTEXT *actx) +{ + int i; + + for (i = 0; i < actx->idxlen; i++) + { + actx->idx[i]->content->tagged = 0; + actx->idx[i]->content->collapsed = 0; + } +} + +static void mutt_update_recvattach_menu (ATTACH_CONTEXT *actx, MUTTMENU *menu, int init) +{ + if (init) + mutt_generate_recvattach_list (actx, actx->hdr, actx->hdr->content, + actx->root_fp, -1, 0, 0); + + /* TODO: regenerate virtual index */ + mutt_update_tree (actx); + + menu->max = actx->idxlen; + menu->data = actx->idx; + + if (menu->current >= menu->max) + menu->current = menu->max - 1; + menu_check_recenter (menu); + menu->redraw |= REDRAW_INDEX; +} + +/* TODO: fix this to use the index */ static void attach_collapse (BODY *b, short collapse, short init, short just_one) { short i; @@ -898,17 +987,6 @@ static void attach_collapse (BODY *b, short collapse, short init, short just_one } } -void mutt_attach_init (BODY *b) -{ - for (; b; b = b->next) - { - b->tagged = 0; - b->collapsed = 0; - if (b->parts) - mutt_attach_init (b->parts); - } -} - static const char *Function_not_permitted = N_("Function not permitted in attach-message mode."); #define CHECK_ATTACH if(option(OPTATTACHMSG)) \ @@ -923,9 +1001,6 @@ static const char *Function_not_permitted = N_("Function not permitted in attach void mutt_view_attachments (HEADER *hdr) { - int secured = 0; - int need_secured = 0; - char helpstr[LONG_STRING]; MUTTMENU *menu; BODY *cur = NULL; @@ -944,66 +1019,6 @@ void mutt_view_attachments (HEADER *hdr) if ((msg = mx_open_message (Context, hdr->msgno)) == NULL) return; - - if (WithCrypto && ((hdr->security & ENCRYPT) || - (mutt_is_application_smime(hdr->content) & SMIMEOPAQUE))) - { - need_secured = 1; - - if ((hdr->security & ENCRYPT) && !crypt_valid_passphrase(hdr->security)) - { - mx_close_message (Context, &msg); - return; - } - if ((WithCrypto & APPLICATION_SMIME) && (hdr->security & APPLICATION_SMIME)) - { - if (hdr->env) - crypt_smime_getkeys (hdr->env); - - if (mutt_is_application_smime(hdr->content)) - { - secured = ! crypt_smime_decrypt_mime (msg->fp, &fp, - hdr->content, &cur); - - /* S/MIME nesting */ - if ((mutt_is_application_smime (cur) & SMIMEOPAQUE)) - { - BODY *_cur = cur; - FILE *_fp = fp; - - fp = NULL; cur = NULL; - secured = !crypt_smime_decrypt_mime (_fp, &fp, _cur, &cur); - - mutt_free_body (&_cur); - safe_fclose (&_fp); - } - } - else - need_secured = 0; - } - if ((WithCrypto & APPLICATION_PGP) && (hdr->security & APPLICATION_PGP)) - { - if (mutt_is_multipart_encrypted(hdr->content) || - mutt_is_malformed_multipart_pgp_encrypted(hdr->content)) - secured = !crypt_pgp_decrypt_mime (msg->fp, &fp, hdr->content, &cur); - else - need_secured = 0; - } - - if (need_secured && !secured) - { - mx_close_message (Context, &msg); - mutt_error _("Can't decrypt encrypted message!"); - return; - } - } - - if (!WithCrypto || !need_secured) - { - fp = msg->fp; - cur = hdr->content; - } - menu = mutt_new_menu (MENU_ATTACH); menu->title = _("Attachments"); menu->make_entry = attach_entry; @@ -1012,9 +1027,10 @@ void mutt_view_attachments (HEADER *hdr) mutt_push_current_menu (menu); actx = safe_calloc (sizeof(ATTACH_CONTEXT), 1); - mutt_attach_init (cur); - attach_collapse (cur, 0, 1, 0); - mutt_update_attach_index (actx, cur, menu); + actx->hdr = hdr; + actx->root_fp = msg->fp; + mutt_update_recvattach_menu (actx, menu, 1); + mutt_attach_init (actx); FOREVER { @@ -1050,7 +1066,7 @@ void mutt_view_attachments (HEADER *hdr) attach_collapse (actx->idx[menu->current]->content, 1, 0, 1); else attach_collapse (actx->idx[menu->current]->content, 0, 1, 1); - mutt_update_attach_index (actx, cur, menu); + mutt_update_recvattach_menu (actx, menu, 0); break; case OP_FORGET_PASSPHRASE: @@ -1223,7 +1239,11 @@ void mutt_view_attachments (HEADER *hdr) case OP_EDIT_TYPE: mutt_edit_content_type (hdr, actx->idx[menu->current]->content, fp); - mutt_update_attach_index (actx, cur, menu); + /* Editing the content type can rewrite the body structure. */ + for (i = 0; i < actx->idxlen; i++) + actx->idx[i]->content = NULL; + mutt_actx_free_entries (actx); + mutt_update_recvattach_menu (actx, menu, 1); break; case OP_EXIT: @@ -1242,12 +1262,6 @@ void mutt_view_attachments (HEADER *hdr) mutt_free_attach_context (&actx); - if (WithCrypto && need_secured && secured) - { - safe_fclose (&fp); - mutt_free_body (&cur); - } - mutt_pop_current_menu (menu); mutt_menuDestroy (&menu); return; -- 2.50.0