NeoMutt  2024-11-14-34-g5aaf0d
Teaching an old dog new tricks
DOXYGEN
Loading...
Searching...
No Matches
smime.c
Go to the documentation of this file.
1
34#include "config.h"
35#include <limits.h>
36#include <stdbool.h>
37#include <stdio.h>
38#include <string.h>
39#include <sys/stat.h>
40#include <sys/types.h>
41#include <unistd.h>
42#include "private.h"
43#include "mutt/lib.h"
44#include "address/lib.h"
45#include "config/lib.h"
46#include "email/lib.h"
47#include "core/lib.h"
48#include "alias/lib.h"
49#include "gui/lib.h"
50#include "mutt.h"
51#include "lib.h"
52#include "editor/lib.h"
53#include "expando/lib.h"
54#include "history/lib.h"
55#include "question/lib.h"
56#include "send/lib.h"
57#include "copy.h"
58#include "crypt.h"
59#include "cryptglue.h"
60#include "globals.h"
61#include "handler.h"
62#include "mutt_logging.h"
63#include "muttlib.h"
64#ifdef CRYPT_BACKEND_CLASSIC_SMIME
65#include "smime.h"
66#endif
67
69
71static char SmimePass[256];
73static time_t SmimeExpTime = 0; /* when does the cached passphrase expire? */
74
76static struct Buffer SmimeKeyToUse = { 0 };
78static struct Buffer SmimeCertToUse = { 0 };
80static struct Buffer SmimeIntermediateToUse = { 0 };
81
85void smime_init(void)
86{
90}
91
95void smime_cleanup(void)
96{
100}
101
106static void smime_key_free(struct SmimeKey **keylist)
107{
108 if (!keylist)
109 return;
110
111 struct SmimeKey *key = NULL;
112
113 while (*keylist)
114 {
115 key = *keylist;
116 *keylist = (*keylist)->next;
117
118 FREE(&key->email);
119 FREE(&key->hash);
120 FREE(&key->label);
121 FREE(&key->issuer);
122 FREE(&key);
123 }
124}
125
131static struct SmimeKey *smime_copy_key(struct SmimeKey *key)
132{
133 if (!key)
134 return NULL;
135
136 struct SmimeKey *copy = NULL;
137
138 copy = MUTT_MEM_CALLOC(1, struct SmimeKey);
139 copy->email = mutt_str_dup(key->email);
140 copy->hash = mutt_str_dup(key->hash);
141 copy->label = mutt_str_dup(key->label);
142 copy->issuer = mutt_str_dup(key->issuer);
143 copy->trust = key->trust;
144 copy->flags = key->flags;
145
146 return copy;
147}
148
149/*
150 * Queries and passphrase handling.
151 */
152
157{
158 memset(SmimePass, 0, sizeof(SmimePass));
159 SmimeExpTime = 0;
160}
161
166{
167 const time_t now = mutt_date_now();
168 if (now < SmimeExpTime)
169 {
170 /* Use cached copy. */
171 return true;
172 }
173
175
176 struct Buffer *buf = buf_pool_get();
177 const int rc = mw_get_field(_("Enter S/MIME passphrase:"), buf,
180 buf_pool_release(&buf);
181
182 if (rc == 0)
183 {
184 const short c_smime_timeout = cs_subset_number(NeoMutt->sub, "smime_timeout");
185 SmimeExpTime = mutt_date_add_timeout(now, c_smime_timeout);
186 return true;
187 }
188 else
189 {
190 SmimeExpTime = 0;
191 }
192
193 return false;
194}
195
196/*
197 * The OpenSSL interface
198 */
199
203void smime_command_a(const struct ExpandoNode *node, void *data,
204 MuttFormatFlags flags, struct Buffer *buf)
205{
206#ifdef HAVE_SMIME
207 const struct SmimeCommandContext *cctx = data;
208
209 const char *s = cctx->cryptalg;
210 buf_strcpy(buf, s);
211#endif
212}
213
217void smime_command_c(const struct ExpandoNode *node, void *data,
218 MuttFormatFlags flags, struct Buffer *buf)
219{
220#ifdef HAVE_SMIME
221 const struct SmimeCommandContext *cctx = data;
222
223 const char *s = cctx->certificates;
224 buf_strcpy(buf, s);
225#endif
226}
227
231void smime_command_C(const struct ExpandoNode *node, void *data,
232 MuttFormatFlags flags, struct Buffer *buf)
233{
234#ifdef HAVE_SMIME
235 const char *const c_smime_ca_location = cs_subset_path(NeoMutt->sub, "smime_ca_location");
236
237 struct Buffer *path = buf_pool_get();
238 struct Buffer *buf1 = buf_pool_get();
239 struct Buffer *buf2 = buf_pool_get();
240 struct stat st = { 0 };
241
242 buf_strcpy(path, c_smime_ca_location);
243 buf_expand_path(path);
244 buf_quote_filename(buf1, buf_string(path), true);
245
246 if ((stat(buf_string(path), &st) != 0) || !S_ISDIR(st.st_mode))
247 {
248 buf_printf(buf2, "-CAfile %s", buf_string(buf1));
249 }
250 else
251 {
252 buf_printf(buf2, "-CApath %s", buf_string(buf1));
253 }
254
255 buf_copy(buf, buf2);
256
257 buf_pool_release(&path);
258 buf_pool_release(&buf1);
259 buf_pool_release(&buf2);
260#endif
261}
262
266void smime_command_d(const struct ExpandoNode *node, void *data,
267 MuttFormatFlags flags, struct Buffer *buf)
268{
269#ifdef HAVE_SMIME
270 const struct SmimeCommandContext *cctx = data;
271
272 const char *s = cctx->digestalg;
273 buf_strcpy(buf, s);
274#endif
275}
276
280void smime_command_f(const struct ExpandoNode *node, void *data,
281 MuttFormatFlags flags, struct Buffer *buf)
282{
283#ifdef HAVE_SMIME
284 const struct SmimeCommandContext *cctx = data;
285
286 const char *s = cctx->fname;
287 buf_strcpy(buf, s);
288#endif
289}
290
294void smime_command_i(const struct ExpandoNode *node, void *data,
295 MuttFormatFlags flags, struct Buffer *buf)
296{
297#ifdef HAVE_SMIME
298 const struct SmimeCommandContext *cctx = data;
299
300 const char *s = cctx->intermediates;
301 buf_strcpy(buf, s);
302#endif
303}
304
308void smime_command_k(const struct ExpandoNode *node, void *data,
309 MuttFormatFlags flags, struct Buffer *buf)
310{
311#ifdef HAVE_SMIME
312 const struct SmimeCommandContext *cctx = data;
313
314 const char *s = cctx->key;
315 buf_strcpy(buf, s);
316#endif
317}
318
322void smime_command_s(const struct ExpandoNode *node, void *data,
323 MuttFormatFlags flags, struct Buffer *buf)
324{
325#ifdef HAVE_SMIME
326 const struct SmimeCommandContext *cctx = data;
327
328 const char *s = cctx->sig_fname;
329 buf_strcpy(buf, s);
330#endif
331}
332
339static void smime_command(struct Buffer *buf, struct SmimeCommandContext *cctx,
340 const struct Expando *exp)
341{
343 mutt_debug(LL_DEBUG2, "%s\n", buf_string(buf));
344}
345
368static pid_t smime_invoke(FILE **fp_smime_in, FILE **fp_smime_out, FILE **fp_smime_err,
369 int fp_smime_infd, int fp_smime_outfd, int fp_smime_errfd,
370 const char *fname, const char *sig_fname, const char *cryptalg,
371 const char *digestalg, const char *key, const char *certificates,
372 const char *intermediates, const struct Expando *exp)
373{
374 struct SmimeCommandContext cctx = { 0 };
375
376 if (!exp)
377 return (pid_t) -1;
378
379 cctx.fname = fname;
380 cctx.sig_fname = sig_fname;
381 cctx.key = key;
382 cctx.cryptalg = cryptalg;
383 cctx.digestalg = digestalg;
386
387 struct Buffer *cmd = buf_pool_get();
388 smime_command(cmd, &cctx, exp);
389
390 pid_t pid = filter_create_fd(buf_string(cmd), fp_smime_in, fp_smime_out, fp_smime_err,
391 fp_smime_infd, fp_smime_outfd, fp_smime_errfd, EnvList);
392 buf_pool_release(&cmd);
393 return pid;
394}
395
402static struct SmimeKey *smime_parse_key(char *buf)
403{
404 char *pend = NULL, *p = NULL;
405 int field = 0;
406
407 struct SmimeKey *key = MUTT_MEM_CALLOC(1, struct SmimeKey);
408
409 for (p = buf; p; p = pend)
410 {
411 /* Some users manually maintain their .index file, and use a tab
412 * as a delimiter, which the old parsing code (using fscanf)
413 * happened to allow. smime_keys uses a space, so search for both. */
414 if ((pend = strchr(p, ' ')) || (pend = strchr(p, '\t')) || (pend = strchr(p, '\n')))
415 *pend++ = 0;
416
417 /* For backward compatibility, don't count consecutive delimiters
418 * as an empty field. */
419 if (*p == '\0')
420 continue;
421
422 field++;
423
424 switch (field)
425 {
426 case 1: /* mailbox */
427 key->email = mutt_str_dup(p);
428 break;
429 case 2: /* hash */
430 key->hash = mutt_str_dup(p);
431 break;
432 case 3: /* label */
433 key->label = mutt_str_dup(p);
434 break;
435 case 4: /* issuer */
436 key->issuer = mutt_str_dup(p);
437 break;
438 case 5: /* trust */
439 key->trust = *p;
440 break;
441 case 6: /* purpose */
442 while (*p)
443 {
444 switch (*p++)
445 {
446 case 'e':
448 break;
449
450 case 's':
451 key->flags |= KEYFLAG_CANSIGN;
452 break;
453 }
454 }
455 break;
456 }
457 }
458
459 /* Old index files could be missing issuer, trust, and purpose,
460 * but anything less than that is an error. */
461 if (field < 3)
462 {
463 smime_key_free(&key);
464 return NULL;
465 }
466
467 if (field < 4)
468 key->issuer = mutt_str_dup("?");
469
470 if (field < 5)
471 key->trust = 't';
472
473 if (field < 6)
475
476 return key;
477}
478
485static struct SmimeKey *smime_get_candidates(const char *search, bool only_public_key)
486{
487 char buf[1024] = { 0 };
488 struct SmimeKey *key = NULL, *results = NULL;
489 struct SmimeKey **results_end = &results;
490
491 struct Buffer *index_file = buf_pool_get();
492 const char *const c_smime_certificates = cs_subset_path(NeoMutt->sub, "smime_certificates");
493 const char *const c_smime_keys = cs_subset_path(NeoMutt->sub, "smime_keys");
494 buf_printf(index_file, "%s/.index",
495 only_public_key ? NONULL(c_smime_certificates) : NONULL(c_smime_keys));
496
497 FILE *fp = mutt_file_fopen(buf_string(index_file), "r");
498 if (!fp)
499 {
500 mutt_perror("%s", buf_string(index_file));
501 buf_pool_release(&index_file);
502 return NULL;
503 }
504 buf_pool_release(&index_file);
505
506 while (fgets(buf, sizeof(buf), fp))
507 {
508 if (((*search == '\0')) || mutt_istr_find(buf, search))
509 {
510 key = smime_parse_key(buf);
511 if (key)
512 {
513 *results_end = key;
514 results_end = &key->next;
515 }
516 }
517 }
518
519 mutt_file_fclose(&fp);
520
521 return results;
522}
523
533static struct SmimeKey *smime_get_key_by_hash(const char *hash, bool only_public_key)
534{
535 struct SmimeKey *match = NULL;
536 struct SmimeKey *results = smime_get_candidates(hash, only_public_key);
537 for (struct SmimeKey *result = results; result; result = result->next)
538 {
539 if (mutt_istr_equal(hash, result->hash))
540 {
541 match = smime_copy_key(result);
542 break;
543 }
544 }
545
546 smime_key_free(&results);
547
548 return match;
549}
550
559static struct SmimeKey *smime_get_key_by_addr(const char *mailbox, KeyFlags abilities,
560 bool only_public_key, bool oppenc_mode)
561{
562 if (!mailbox)
563 return NULL;
564
565 struct SmimeKey *results = NULL, *result = NULL;
566 struct SmimeKey *matches = NULL;
567 struct SmimeKey **matches_end = &matches;
568 struct SmimeKey *match = NULL;
569 struct SmimeKey *trusted_match = NULL;
570 struct SmimeKey *valid_match = NULL;
571 struct SmimeKey *return_key = NULL;
572 bool multi_trusted_matches = false;
573
574 results = smime_get_candidates(mailbox, only_public_key);
575 for (result = results; result; result = result->next)
576 {
577 if (abilities && !(result->flags & abilities))
578 {
579 continue;
580 }
581
582 if (mutt_istr_equal(mailbox, result->email))
583 {
584 match = smime_copy_key(result);
585 *matches_end = match;
586 matches_end = &match->next;
587
588 if (match->trust == 't')
589 {
590 if (trusted_match && !mutt_istr_equal(match->hash, trusted_match->hash))
591 {
592 multi_trusted_matches = true;
593 }
594 trusted_match = match;
595 }
596 else if ((match->trust == 'u') || (match->trust == 'v'))
597 {
598 valid_match = match;
599 }
600 }
601 }
602
603 smime_key_free(&results);
604
605 if (matches)
606 {
607 if (oppenc_mode || !isatty(STDIN_FILENO))
608 {
609 const bool c_crypt_opportunistic_encrypt_strong_keys =
610 cs_subset_bool(NeoMutt->sub, "crypt_opportunistic_encrypt_strong_keys");
611 if (trusted_match)
612 return_key = smime_copy_key(trusted_match);
613 else if (valid_match && !c_crypt_opportunistic_encrypt_strong_keys)
614 return_key = smime_copy_key(valid_match);
615 else
616 return_key = NULL;
617 }
618 else if (trusted_match && !multi_trusted_matches)
619 {
620 return_key = smime_copy_key(trusted_match);
621 }
622 else
623 {
624 return_key = smime_copy_key(dlg_smime(matches, mailbox));
625 }
626
627 smime_key_free(&matches);
628 }
629
630 return return_key;
631}
632
640static struct SmimeKey *smime_get_key_by_str(const char *str, KeyFlags abilities, bool only_public_key)
641{
642 if (!str)
643 return NULL;
644
645 struct SmimeKey *results = NULL, *result = NULL;
646 struct SmimeKey *matches = NULL;
647 struct SmimeKey **matches_end = &matches;
648 struct SmimeKey *match = NULL;
649 struct SmimeKey *return_key = NULL;
650
651 results = smime_get_candidates(str, only_public_key);
652 for (result = results; result; result = result->next)
653 {
654 if (abilities && !(result->flags & abilities))
655 {
656 continue;
657 }
658
659 if (mutt_istr_equal(str, result->hash) ||
660 mutt_istr_find(result->email, str) || mutt_istr_find(result->label, str))
661 {
662 match = smime_copy_key(result);
663 *matches_end = match;
664 matches_end = &match->next;
665 }
666 }
667
668 smime_key_free(&results);
669
670 if (matches)
671 {
672 return_key = smime_copy_key(dlg_smime(matches, str));
673 smime_key_free(&matches);
674 }
675
676 return return_key;
677}
678
686static struct SmimeKey *smime_ask_for_key(const char *prompt, KeyFlags abilities, bool only_public_key)
687{
688 if (!prompt)
689 return NULL;
690
691 struct SmimeKey *key = NULL;
692 struct Buffer *resp = buf_pool_get();
693
695
696 while (true)
697 {
698 buf_reset(resp);
699 if (mw_get_field(prompt, resp, MUTT_COMP_NO_FLAGS, HC_OTHER, NULL, NULL) != 0)
700 {
701 goto done;
702 }
703
704 key = smime_get_key_by_str(buf_string(resp), abilities, only_public_key);
705 if (key)
706 goto done;
707
708 mutt_error(_("No matching keys found for \"%s\""), buf_string(resp));
709 }
710
711done:
712 buf_pool_release(&resp);
713 return key;
714}
715
723static void getkeys(const char *mailbox)
724{
725 const char *k = NULL;
726
727 struct SmimeKey *key = smime_get_key_by_addr(mailbox, KEYFLAG_CANENCRYPT, false, false);
728
729 if (!key)
730 {
731 struct Buffer *prompt = buf_pool_get();
732 buf_printf(prompt, _("Enter keyID for %s: "), mailbox);
733 key = smime_ask_for_key(buf_string(prompt), KEYFLAG_CANENCRYPT, false);
734 buf_pool_release(&prompt);
735 }
736
737 const char *const c_smime_keys = cs_subset_path(NeoMutt->sub, "smime_keys");
738 size_t smime_keys_len = mutt_str_len(c_smime_keys);
739
740 const char *const c_smime_default_key = cs_subset_string(NeoMutt->sub, "smime_default_key");
741 k = key ? key->hash : NONULL(c_smime_default_key);
742
743 /* if the key is different from last time */
744 if ((buf_len(&SmimeKeyToUse) <= smime_keys_len) ||
745 !mutt_istr_equal(k, SmimeKeyToUse.data + smime_keys_len + 1))
746 {
748 buf_printf(&SmimeKeyToUse, "%s/%s", NONULL(c_smime_keys), k);
749 const char *const c_smime_certificates = cs_subset_path(NeoMutt->sub, "smime_certificates");
750 buf_printf(&SmimeCertToUse, "%s/%s", NONULL(c_smime_certificates), k);
751 }
752
753 smime_key_free(&key);
754}
755
760{
761 const bool c_smime_decrypt_use_default_key = cs_subset_bool(NeoMutt->sub, "smime_decrypt_use_default_key");
762 const char *const c_smime_default_key = cs_subset_string(NeoMutt->sub, "smime_default_key");
763 if (c_smime_decrypt_use_default_key && c_smime_default_key)
764 {
765 const char *const c_smime_keys = cs_subset_path(NeoMutt->sub, "smime_keys");
766 buf_printf(&SmimeKeyToUse, "%s/%s", NONULL(c_smime_keys), c_smime_default_key);
767 const char *const c_smime_certificates = cs_subset_path(NeoMutt->sub, "smime_certificates");
768 buf_printf(&SmimeCertToUse, "%s/%s", NONULL(c_smime_certificates), c_smime_default_key);
769 return;
770 }
771
772 struct Address *a = NULL;
773 TAILQ_FOREACH(a, &env->to, entries)
774 {
775 if (mutt_addr_is_user(a))
776 {
778 return;
779 }
780 }
781
782 TAILQ_FOREACH(a, &env->cc, entries)
783 {
784 if (mutt_addr_is_user(a))
785 {
787 return;
788 }
789 }
790
791 struct Address *f = mutt_default_from(NeoMutt->sub);
793 mutt_addr_free(&f);
794}
795
799char *smime_class_find_keys(const struct AddressList *al, bool oppenc_mode)
800{
801 struct SmimeKey *key = NULL;
802 char *keyid = NULL, *keylist = NULL;
803 size_t keylist_size = 0;
804 size_t keylist_used = 0;
805
806 struct Address *a = NULL;
807 TAILQ_FOREACH(a, al, entries)
808 {
809 key = smime_get_key_by_addr(buf_string(a->mailbox), KEYFLAG_CANENCRYPT, true, oppenc_mode);
810 if (!key && !oppenc_mode && isatty(STDIN_FILENO))
811 {
812 struct Buffer *prompt = buf_pool_get();
813 buf_printf(prompt, _("Enter keyID for %s: "), buf_string(a->mailbox));
814 key = smime_ask_for_key(buf_string(prompt), KEYFLAG_CANENCRYPT, true);
815 buf_pool_release(&prompt);
816 }
817 if (!key)
818 {
819 if (!oppenc_mode)
820 mutt_message(_("No (valid) certificate found for %s"), buf_string(a->mailbox));
821 FREE(&keylist);
822 return NULL;
823 }
824
825 keyid = key->hash;
826 keylist_size += mutt_str_len(keyid) + 2;
827 MUTT_MEM_REALLOC(&keylist, keylist_size, char);
828 sprintf(keylist + keylist_used, "%s%s", keylist_used ? " " : "", keyid);
829 keylist_used = mutt_str_len(keylist);
830
831 smime_key_free(&key);
832 }
833 return keylist;
834}
835
847static int smime_handle_cert_email(const char *certificate, const char *mailbox,
848 bool copy, char ***buffer, int *num)
849{
850 char email[256] = { 0 };
851 int rc = -1, count = 0;
852 pid_t pid;
853
854 FILE *fp_err = mutt_file_mkstemp();
855 if (!fp_err)
856 {
857 mutt_perror(_("Can't create temporary file"));
858 return 1;
859 }
860
861 FILE *fp_out = mutt_file_mkstemp();
862 if (!fp_out)
863 {
864 mutt_file_fclose(&fp_err);
865 mutt_perror(_("Can't create temporary file"));
866 return 1;
867 }
868
869 const struct Expando *c_smime_get_cert_email_command =
870 cs_subset_expando(NeoMutt->sub, "smime_get_cert_email_command");
871 pid = smime_invoke(NULL, NULL, NULL, -1, fileno(fp_out), fileno(fp_err), certificate,
872 NULL, NULL, NULL, NULL, NULL, NULL, c_smime_get_cert_email_command);
873 if (pid == -1)
874 {
875 mutt_message(_("Error: unable to create OpenSSL subprocess"));
876 mutt_file_fclose(&fp_err);
877 mutt_file_fclose(&fp_out);
878 return 1;
879 }
880
881 filter_wait(pid);
882
883 fflush(fp_out);
884 rewind(fp_out);
885 fflush(fp_err);
886 rewind(fp_err);
887
888 while ((fgets(email, sizeof(email), fp_out)))
889 {
890 size_t len = mutt_str_len(email);
891 if (len && (email[len - 1] == '\n'))
892 email[len - 1] = '\0';
893 if (mutt_istr_startswith(email, mailbox))
894 rc = 1;
895
896 rc = (rc < 0) ? 0 : rc;
897 count++;
898 }
899
900 if (rc == -1)
901 {
902 mutt_endwin();
903 mutt_file_copy_stream(fp_err, stdout);
904 mutt_any_key_to_continue(_("Error: unable to create OpenSSL subprocess"));
905 rc = 1;
906 }
907 else if (rc == 0)
908 {
909 rc = 1;
910 }
911 else
912 {
913 rc = 0;
914 }
915
916 if (copy && buffer && num)
917 {
918 (*num) = count;
919 *buffer = MUTT_MEM_CALLOC(count, char *);
920 count = 0;
921
922 rewind(fp_out);
923 while ((fgets(email, sizeof(email), fp_out)))
924 {
925 size_t len = mutt_str_len(email);
926 if (len && (email[len - 1] == '\n'))
927 email[len - 1] = '\0';
928 (*buffer)[count] = MUTT_MEM_CALLOC(mutt_str_len(email) + 1, char);
929 strncpy((*buffer)[count], email, mutt_str_len(email));
930 count++;
931 }
932 }
933 else if (copy)
934 {
935 rc = 2;
936 }
937
938 mutt_file_fclose(&fp_out);
939 mutt_file_fclose(&fp_err);
940
941 return rc;
942}
943
949static char *smime_extract_certificate(const char *infile)
950{
951 FILE *fp_err = NULL;
952 FILE *fp_out = NULL;
953 FILE *fp_cert = NULL;
954 char *rc = NULL;
955 pid_t pid;
956 int empty;
957
958 struct Buffer *pk7out = buf_pool_get();
959 struct Buffer *certfile = buf_pool_get();
960
961 fp_err = mutt_file_mkstemp();
962 if (!fp_err)
963 {
964 mutt_perror(_("Can't create temporary file"));
965 goto cleanup;
966 }
967
968 buf_mktemp(pk7out);
969 fp_out = mutt_file_fopen(buf_string(pk7out), "w+");
970 if (!fp_out)
971 {
972 mutt_perror("%s", buf_string(pk7out));
973 goto cleanup;
974 }
975
976 /* Step 1: Convert the signature to a PKCS#7 structure, as we can't
977 * extract the full set of certificates directly. */
978 const struct Expando *c_smime_pk7out_command = cs_subset_expando(NeoMutt->sub, "smime_pk7out_command");
979 pid = smime_invoke(NULL, NULL, NULL, -1, fileno(fp_out), fileno(fp_err), infile,
980 NULL, NULL, NULL, NULL, NULL, NULL, c_smime_pk7out_command);
981 if (pid == -1)
982 {
983 mutt_any_key_to_continue(_("Error: unable to create OpenSSL subprocess"));
984 goto cleanup;
985 }
986
987 filter_wait(pid);
988
989 fflush(fp_out);
990 rewind(fp_out);
991 fflush(fp_err);
992 rewind(fp_err);
993 empty = (fgetc(fp_out) == EOF);
994 if (empty)
995 {
996 mutt_perror("%s", buf_string(pk7out));
997 mutt_file_copy_stream(fp_err, stdout);
998 goto cleanup;
999 }
1000 mutt_file_fclose(&fp_out);
1001
1002 buf_mktemp(certfile);
1003 fp_cert = mutt_file_fopen(buf_string(certfile), "w+");
1004 if (!fp_cert)
1005 {
1006 mutt_perror("%s", buf_string(certfile));
1008 goto cleanup;
1009 }
1010
1011 // Step 2: Extract the certificates from a PKCS#7 structure.
1012 const struct Expando *c_smime_get_cert_command = cs_subset_expando(NeoMutt->sub, "smime_get_cert_command");
1013 pid = smime_invoke(NULL, NULL, NULL, -1, fileno(fp_cert), fileno(fp_err),
1014 buf_string(pk7out), NULL, NULL, NULL, NULL, NULL, NULL,
1015 c_smime_get_cert_command);
1016 if (pid == -1)
1017 {
1018 mutt_any_key_to_continue(_("Error: unable to create OpenSSL subprocess"));
1020 goto cleanup;
1021 }
1022
1023 filter_wait(pid);
1024
1026
1027 fflush(fp_cert);
1028 rewind(fp_cert);
1029 fflush(fp_err);
1030 rewind(fp_err);
1031 empty = (fgetc(fp_cert) == EOF);
1032 if (empty)
1033 {
1034 mutt_file_copy_stream(fp_err, stdout);
1035 goto cleanup;
1036 }
1037
1038 mutt_file_fclose(&fp_cert);
1039
1040 rc = buf_strdup(certfile);
1041
1042cleanup:
1043 mutt_file_fclose(&fp_err);
1044 if (fp_out)
1045 {
1046 mutt_file_fclose(&fp_out);
1048 }
1049 if (fp_cert)
1050 {
1051 mutt_file_fclose(&fp_cert);
1052 mutt_file_unlink(buf_string(certfile));
1053 }
1054 buf_pool_release(&pk7out);
1055 buf_pool_release(&certfile);
1056 return rc;
1057}
1058
1064static char *smime_extract_signer_certificate(const char *infile)
1065{
1066 char *cert = NULL;
1067 struct Buffer *certfile = NULL;
1068 pid_t pid;
1069 int empty;
1070
1071 FILE *fp_err = mutt_file_mkstemp();
1072 if (!fp_err)
1073 {
1074 mutt_perror(_("Can't create temporary file"));
1075 return NULL;
1076 }
1077
1078 certfile = buf_pool_get();
1079 buf_mktemp(certfile);
1080 FILE *fp_out = mutt_file_fopen(buf_string(certfile), "w+");
1081 if (!fp_out)
1082 {
1083 mutt_file_fclose(&fp_err);
1084 mutt_perror("%s", buf_string(certfile));
1085 goto cleanup;
1086 }
1087
1088 /* Extract signer's certificate
1089 */
1090 const struct Expando *c_smime_get_signer_cert_command =
1091 cs_subset_expando(NeoMutt->sub, "smime_get_signer_cert_command");
1092 pid = smime_invoke(NULL, NULL, NULL, -1, -1, fileno(fp_err), infile, NULL, NULL, NULL,
1093 NULL, buf_string(certfile), NULL, c_smime_get_signer_cert_command);
1094 if (pid == -1)
1095 {
1096 mutt_any_key_to_continue(_("Error: unable to create OpenSSL subprocess"));
1097 goto cleanup;
1098 }
1099
1100 filter_wait(pid);
1101
1102 fflush(fp_out);
1103 rewind(fp_out);
1104 fflush(fp_err);
1105 rewind(fp_err);
1106 empty = (fgetc(fp_out) == EOF);
1107 if (empty)
1108 {
1109 mutt_endwin();
1110 mutt_file_copy_stream(fp_err, stdout);
1112 goto cleanup;
1113 }
1114
1115 mutt_file_fclose(&fp_out);
1116 cert = buf_strdup(certfile);
1117
1118cleanup:
1119 mutt_file_fclose(&fp_err);
1120 if (fp_out)
1121 {
1122 mutt_file_fclose(&fp_out);
1123 mutt_file_unlink(buf_string(certfile));
1124 }
1125 buf_pool_release(&certfile);
1126 return cert;
1127}
1128
1132void smime_class_invoke_import(const char *infile, const char *mailbox)
1133{
1134 char *certfile = NULL;
1135 struct Buffer *buf = NULL;
1136
1137 FILE *fp_out = NULL;
1138 FILE *fp_err = mutt_file_mkstemp();
1139 if (!fp_err)
1140 {
1141 mutt_perror(_("Can't create temporary file"));
1142 goto done;
1143 }
1144
1145 fp_out = mutt_file_mkstemp();
1146 if (!fp_out)
1147 {
1148 mutt_perror(_("Can't create temporary file"));
1149 goto done;
1150 }
1151
1152 buf = buf_pool_get();
1153 const bool c_smime_ask_cert_label = cs_subset_bool(NeoMutt->sub, "smime_ask_cert_label");
1154 if (c_smime_ask_cert_label)
1155 {
1156 if ((mw_get_field(_("Label for certificate: "), buf, MUTT_COMP_NO_FLAGS,
1157 HC_OTHER, NULL, NULL) != 0) ||
1158 buf_is_empty(buf))
1159 {
1160 goto done;
1161 }
1162 }
1163
1164 mutt_endwin();
1165 certfile = smime_extract_certificate(infile);
1166 if (certfile)
1167 {
1168 mutt_endwin();
1169
1170 const struct Expando *c_smime_import_cert_command =
1171 cs_subset_expando(NeoMutt->sub, "smime_import_cert_command");
1172 FILE *fp_smime_in = NULL;
1173 pid_t pid = smime_invoke(&fp_smime_in, NULL, NULL, -1, fileno(fp_out),
1174 fileno(fp_err), certfile, NULL, NULL, NULL, NULL,
1175 NULL, NULL, c_smime_import_cert_command);
1176 if (pid == -1)
1177 {
1178 mutt_message(_("Error: unable to create OpenSSL subprocess"));
1179 goto done;
1180 }
1181 fputs(buf_string(buf), fp_smime_in);
1182 fputc('\n', fp_smime_in);
1183 mutt_file_fclose(&fp_smime_in);
1184
1185 filter_wait(pid);
1186
1187 mutt_file_unlink(certfile);
1188 FREE(&certfile);
1189 }
1190
1191 fflush(fp_out);
1192 rewind(fp_out);
1193 fflush(fp_err);
1194 rewind(fp_err);
1195
1196 mutt_file_copy_stream(fp_out, stdout);
1197 mutt_file_copy_stream(fp_err, stdout);
1198
1199done:
1200 mutt_file_fclose(&fp_out);
1201 mutt_file_fclose(&fp_err);
1202 buf_pool_release(&buf);
1203}
1204
1208int smime_class_verify_sender(struct Email *e, struct Message *msg)
1209{
1210 const char *mbox = NULL, *certfile = NULL;
1211 int rc = 1;
1212
1213 struct Buffer *tempfname = buf_pool_get();
1214 buf_mktemp(tempfname);
1215 FILE *fp_out = mutt_file_fopen(buf_string(tempfname), "w");
1216 if (!fp_out)
1217 {
1218 mutt_perror("%s", buf_string(tempfname));
1219 goto cleanup;
1220 }
1221
1222 const bool encrypt = e->security & SEC_ENCRYPT;
1223 mutt_copy_message(fp_out, e, msg,
1225 encrypt ? (CH_MIME | CH_WEED | CH_NONEWLINE) : CH_NO_FLAGS, 0);
1226
1227 fflush(fp_out);
1228 mutt_file_fclose(&fp_out);
1229
1230 if (!TAILQ_EMPTY(&e->env->from))
1231 {
1233 mbox = buf_string(TAILQ_FIRST(&e->env->from)->mailbox);
1234 }
1235 else if (!TAILQ_EMPTY(&e->env->sender))
1236 {
1238 mbox = buf_string(TAILQ_FIRST(&e->env->sender)->mailbox);
1239 }
1240
1241 if (mbox)
1242 {
1243 certfile = smime_extract_signer_certificate(buf_string(tempfname));
1244 if (certfile)
1245 {
1246 mutt_file_unlink(buf_string(tempfname));
1247 if (smime_handle_cert_email(certfile, mbox, false, NULL, NULL))
1248 {
1249 if (isendwin())
1251 }
1252 else
1253 {
1254 rc = 0;
1255 }
1256 mutt_file_unlink(certfile);
1257 FREE(&certfile);
1258 }
1259 else
1260 {
1261 mutt_any_key_to_continue(_("no certfile"));
1262 }
1263 }
1264 else
1265 {
1266 mutt_any_key_to_continue(_("no mbox"));
1267 }
1268
1269 mutt_file_unlink(buf_string(tempfname));
1270
1271cleanup:
1272 buf_pool_release(&tempfname);
1273 return rc;
1274}
1275
1292static pid_t smime_invoke_encrypt(FILE **fp_smime_in, FILE **fp_smime_out,
1293 FILE **fp_smime_err, int fp_smime_infd,
1294 int fp_smime_outfd, int fp_smime_errfd,
1295 const char *fname, const char *uids)
1296{
1297 const char *const c_smime_encrypt_with = cs_subset_string(NeoMutt->sub, "smime_encrypt_with");
1298 const struct Expando *c_smime_encrypt_command = cs_subset_expando(NeoMutt->sub, "smime_encrypt_command");
1299 return smime_invoke(fp_smime_in, fp_smime_out, fp_smime_err, fp_smime_infd,
1300 fp_smime_outfd, fp_smime_errfd, fname, NULL, c_smime_encrypt_with,
1301 NULL, NULL, uids, NULL, c_smime_encrypt_command);
1302}
1303
1319static pid_t smime_invoke_sign(FILE **fp_smime_in, FILE **fp_smime_out,
1320 FILE **fp_smime_err, int fp_smime_infd, int fp_smime_outfd,
1321 int fp_smime_errfd, const char *fname)
1322{
1323 const char *const c_smime_sign_digest_alg = cs_subset_string(NeoMutt->sub, "smime_sign_digest_alg");
1324 const struct Expando *c_smime_sign_command = cs_subset_expando(NeoMutt->sub, "smime_sign_command");
1325 return smime_invoke(fp_smime_in, fp_smime_out, fp_smime_err, fp_smime_infd, fp_smime_outfd,
1326 fp_smime_errfd, fname, NULL, NULL, c_smime_sign_digest_alg,
1328 buf_string(&SmimeIntermediateToUse), c_smime_sign_command);
1329}
1330
1334struct Body *smime_class_build_smime_entity(struct Body *b, char *certlist)
1335{
1336 char buf[1024] = { 0 };
1337 char certfile[PATH_MAX] = { 0 };
1338 char *cert_end = NULL;
1339 FILE *fp_smime_in = NULL, *fp_smime_err = NULL, *fp_out = NULL, *fp_tmp = NULL;
1340 struct Body *b_enc = NULL;
1341 bool err = false;
1342 int empty, off;
1343 pid_t pid;
1344
1345 struct Buffer *tempfile = buf_pool_get();
1346 struct Buffer *smime_infile = buf_pool_get();
1347
1348 buf_mktemp(tempfile);
1349 fp_out = mutt_file_fopen(buf_string(tempfile), "w+");
1350 if (!fp_out)
1351 {
1352 mutt_perror("%s", buf_string(tempfile));
1353 goto cleanup;
1354 }
1355
1356 fp_smime_err = mutt_file_mkstemp();
1357 if (!fp_smime_err)
1358 {
1359 mutt_perror(_("Can't create temporary file"));
1360 goto cleanup;
1361 }
1362
1363 buf_mktemp(smime_infile);
1364 fp_tmp = mutt_file_fopen(buf_string(smime_infile), "w+");
1365 if (!fp_tmp)
1366 {
1367 mutt_perror("%s", buf_string(smime_infile));
1368 goto cleanup;
1369 }
1370
1371 *certfile = '\0';
1372 for (char *cert_start = certlist; cert_start; cert_start = cert_end)
1373 {
1374 cert_end = strchr(cert_start, ' ');
1375 if (cert_end)
1376 *cert_end = '\0';
1377 if (*cert_start)
1378 {
1379 off = mutt_str_len(certfile);
1380 const char *const c_smime_certificates = cs_subset_path(NeoMutt->sub, "smime_certificates");
1381 snprintf(certfile + off, sizeof(certfile) - off, "%s%s/%s",
1382 (off != 0) ? " " : "", NONULL(c_smime_certificates), cert_start);
1383 }
1384 if (cert_end)
1385 *cert_end++ = ' ';
1386 }
1387
1388 /* write a MIME entity */
1389 mutt_write_mime_header(b, fp_tmp, NeoMutt->sub);
1390 fputc('\n', fp_tmp);
1391 mutt_write_mime_body(b, fp_tmp, NeoMutt->sub);
1392 mutt_file_fclose(&fp_tmp);
1393
1394 pid = smime_invoke_encrypt(&fp_smime_in, NULL, NULL, -1, fileno(fp_out),
1395 fileno(fp_smime_err), buf_string(smime_infile), certfile);
1396 if (pid == -1)
1397 {
1398 mutt_file_unlink(buf_string(smime_infile));
1399 goto cleanup;
1400 }
1401
1402 mutt_file_fclose(&fp_smime_in);
1403
1404 filter_wait(pid);
1405 mutt_file_unlink(buf_string(smime_infile));
1406
1407 fflush(fp_out);
1408 rewind(fp_out);
1409 empty = (fgetc(fp_out) == EOF);
1410 mutt_file_fclose(&fp_out);
1411
1412 fflush(fp_smime_err);
1413 rewind(fp_smime_err);
1414 while (fgets(buf, sizeof(buf) - 1, fp_smime_err))
1415 {
1416 err = true;
1417 fputs(buf, stdout);
1418 }
1419 mutt_file_fclose(&fp_smime_err);
1420
1421 /* pause if there is any error output from SMIME */
1422 if (err)
1424
1425 if (empty)
1426 {
1427 /* fatal error while trying to encrypt message */
1428 if (!err)
1429 mutt_any_key_to_continue(_("No output from OpenSSL..."));
1430 mutt_file_unlink(buf_string(tempfile));
1431 goto cleanup;
1432 }
1433
1434 b_enc = mutt_body_new();
1435 b_enc->type = TYPE_APPLICATION;
1436 b_enc->subtype = mutt_str_dup("pkcs7-mime");
1437 mutt_param_set(&b_enc->parameter, "name", "smime.p7m");
1438 mutt_param_set(&b_enc->parameter, "smime-type", "enveloped-data");
1439 b_enc->encoding = ENC_BASE64; /* The output of OpenSSL SHOULD be binary */
1440 b_enc->use_disp = true;
1441 b_enc->disposition = DISP_ATTACH;
1442 b_enc->d_filename = mutt_str_dup("smime.p7m");
1443 b_enc->filename = buf_strdup(tempfile);
1444 b_enc->unlink = true; /* delete after sending the message */
1445 b_enc->parts = NULL;
1446 b_enc->next = NULL;
1447
1448cleanup:
1449 if (fp_out)
1450 {
1451 mutt_file_fclose(&fp_out);
1452 mutt_file_unlink(buf_string(tempfile));
1453 }
1454 mutt_file_fclose(&fp_smime_err);
1455 if (fp_tmp)
1456 {
1457 mutt_file_fclose(&fp_tmp);
1458 mutt_file_unlink(buf_string(smime_infile));
1459 }
1460 buf_pool_release(&tempfile);
1461 buf_pool_release(&smime_infile);
1462
1463 return b_enc;
1464}
1465
1478static char *openssl_md_to_smime_micalg(const char *md)
1479{
1480 if (!md)
1481 return NULL;
1482
1483 char *micalg = NULL;
1484 if (mutt_istr_startswith(md, "sha"))
1485 {
1486 mutt_str_asprintf(&micalg, "sha-%s", md + 3);
1487 }
1488 else
1489 {
1490 micalg = mutt_str_dup(md);
1491 }
1492
1493 return micalg;
1494}
1495
1499struct Body *smime_class_sign_message(struct Body *b, const struct AddressList *from)
1500{
1501 struct Body *b_sign = NULL;
1502 struct Body *rc = NULL;
1503 char buf[1024] = { 0 };
1504 struct Buffer *filetosign = NULL, *signedfile = NULL;
1505 FILE *fp_smime_in = NULL, *fp_smime_out = NULL, *fp_smime_err = NULL, *fp_sign = NULL;
1506 bool err = false;
1507 int empty = 0;
1508 pid_t pid;
1509 const char *intermediates = NULL;
1510
1511 const char *const c_smime_sign_as = cs_subset_string(NeoMutt->sub, "smime_sign_as");
1512 const char *const c_smime_default_key = cs_subset_string(NeoMutt->sub, "smime_default_key");
1513 const char *signas = c_smime_sign_as ? c_smime_sign_as : c_smime_default_key;
1514 if (!signas || (*signas == '\0'))
1515 {
1516 mutt_error(_("Can't sign: No key specified. Use Sign As."));
1517 return NULL;
1518 }
1519
1520 crypt_convert_to_7bit(b); /* Signed data _must_ be in 7-bit format. */
1521
1522 filetosign = buf_pool_get();
1523 signedfile = buf_pool_get();
1524
1525 buf_mktemp(filetosign);
1526 fp_sign = mutt_file_fopen(buf_string(filetosign), "w+");
1527 if (!fp_sign)
1528 {
1529 mutt_perror("%s", buf_string(filetosign));
1530 goto cleanup;
1531 }
1532
1533 buf_mktemp(signedfile);
1534 fp_smime_out = mutt_file_fopen(buf_string(signedfile), "w+");
1535 if (!fp_smime_out)
1536 {
1537 mutt_perror("%s", buf_string(signedfile));
1538 goto cleanup;
1539 }
1540
1541 mutt_write_mime_header(b, fp_sign, NeoMutt->sub);
1542 fputc('\n', fp_sign);
1543 mutt_write_mime_body(b, fp_sign, NeoMutt->sub);
1544 mutt_file_fclose(&fp_sign);
1545
1546 const char *const c_smime_keys = cs_subset_path(NeoMutt->sub, "smime_keys");
1547 const char *const c_smime_certificates = cs_subset_path(NeoMutt->sub, "smime_certificates");
1548 buf_printf(&SmimeKeyToUse, "%s/%s", NONULL(c_smime_keys), signas);
1549 buf_printf(&SmimeCertToUse, "%s/%s", NONULL(c_smime_certificates), signas);
1550
1551 struct SmimeKey *signas_key = smime_get_key_by_hash(signas, 1);
1552 if (!signas_key || mutt_str_equal("?", signas_key->issuer))
1553 intermediates = signas; /* so openssl won't complain in any case */
1554 else
1555 intermediates = signas_key->issuer;
1556
1557 buf_printf(&SmimeIntermediateToUse, "%s/%s", NONULL(c_smime_certificates), intermediates);
1558
1559 smime_key_free(&signas_key);
1560
1561 pid = smime_invoke_sign(&fp_smime_in, NULL, &fp_smime_err, -1,
1562 fileno(fp_smime_out), -1, buf_string(filetosign));
1563 if (pid == -1)
1564 {
1565 mutt_perror(_("Can't open OpenSSL subprocess"));
1566 mutt_file_unlink(buf_string(filetosign));
1567 goto cleanup;
1568 }
1569 fputs(SmimePass, fp_smime_in);
1570 fputc('\n', fp_smime_in);
1571 mutt_file_fclose(&fp_smime_in);
1572
1573 filter_wait(pid);
1574
1575 /* check for errors from OpenSSL */
1576 err = false;
1577 fflush(fp_smime_err);
1578 rewind(fp_smime_err);
1579 while (fgets(buf, sizeof(buf) - 1, fp_smime_err))
1580 {
1581 err = true;
1582 fputs(buf, stdout);
1583 }
1584 mutt_file_fclose(&fp_smime_err);
1585
1586 fflush(fp_smime_out);
1587 rewind(fp_smime_out);
1588 empty = (fgetc(fp_smime_out) == EOF);
1589 mutt_file_fclose(&fp_smime_out);
1590
1591 mutt_file_unlink(buf_string(filetosign));
1592
1593 if (err)
1595
1596 if (empty)
1597 {
1598 mutt_any_key_to_continue(_("No output from OpenSSL..."));
1599 mutt_file_unlink(buf_string(signedfile));
1600 goto cleanup; /* fatal error while signing */
1601 }
1602
1603 b_sign = mutt_body_new();
1604 b_sign->type = TYPE_MULTIPART;
1605 b_sign->subtype = mutt_str_dup("signed");
1606 b_sign->encoding = ENC_7BIT;
1607 b_sign->use_disp = false;
1608 b_sign->disposition = DISP_INLINE;
1609
1611
1612 const char *const c_smime_sign_digest_alg = cs_subset_string(NeoMutt->sub, "smime_sign_digest_alg");
1613 char *micalg = openssl_md_to_smime_micalg(c_smime_sign_digest_alg);
1614 mutt_param_set(&b_sign->parameter, "micalg", micalg);
1615 FREE(&micalg);
1616
1617 mutt_param_set(&b_sign->parameter, "protocol", "application/pkcs7-signature");
1618
1619 b_sign->parts = b;
1620 rc = b_sign;
1621
1622 b_sign->parts->next = mutt_body_new();
1623 b_sign = b_sign->parts->next;
1624 b_sign->type = TYPE_APPLICATION;
1625 b_sign->subtype = mutt_str_dup("pkcs7-signature");
1626 b_sign->filename = buf_strdup(signedfile);
1627 b_sign->d_filename = mutt_str_dup("smime.p7s");
1628 b_sign->use_disp = true;
1629 b_sign->disposition = DISP_ATTACH;
1630 b_sign->encoding = ENC_BASE64;
1631 b_sign->unlink = true; /* ok to remove this file after sending. */
1632
1633cleanup:
1634 if (fp_sign)
1635 {
1636 mutt_file_fclose(&fp_sign);
1637 mutt_file_unlink(buf_string(filetosign));
1638 }
1639 if (fp_smime_out)
1640 {
1641 mutt_file_fclose(&fp_smime_out);
1642 mutt_file_unlink(buf_string(signedfile));
1643 }
1644 buf_pool_release(&filetosign);
1645 buf_pool_release(&signedfile);
1646 return rc;
1647}
1648
1666static pid_t smime_invoke_verify(FILE **fp_smime_in, FILE **fp_smime_out,
1667 FILE **fp_smime_err, int fp_smime_infd,
1668 int fp_smime_outfd, int fp_smime_errfd,
1669 const char *fname, const char *sig_fname, int opaque)
1670{
1671 const struct Expando *c_smime_verify_opaque_command =
1672 cs_subset_expando(NeoMutt->sub, "smime_verify_opaque_command");
1673 const struct Expando *c_smime_verify_command = cs_subset_expando(NeoMutt->sub, "smime_verify_command");
1674 return smime_invoke(fp_smime_in, fp_smime_out, fp_smime_err, fp_smime_infd, fp_smime_outfd,
1675 fp_smime_errfd, fname, sig_fname, NULL, NULL, NULL, NULL, NULL,
1676 (opaque ? c_smime_verify_opaque_command : c_smime_verify_command));
1677}
1678
1694static pid_t smime_invoke_decrypt(FILE **fp_smime_in, FILE **fp_smime_out,
1695 FILE **fp_smime_err, int fp_smime_infd, int fp_smime_outfd,
1696 int fp_smime_errfd, const char *fname)
1697{
1698 const struct Expando *c_smime_decrypt_command = cs_subset_expando(NeoMutt->sub, "smime_decrypt_command");
1699 return smime_invoke(fp_smime_in, fp_smime_out, fp_smime_err, fp_smime_infd,
1700 fp_smime_outfd, fp_smime_errfd, fname, NULL, NULL, NULL,
1702 NULL, c_smime_decrypt_command);
1703}
1704
1708int smime_class_verify_one(struct Body *b, struct State *state, const char *tempfile)
1709{
1710 FILE *fp = NULL, *fp_smime_out = NULL, *fp_smime_err = NULL;
1711 pid_t pid;
1712 int badsig = -1;
1713
1714 LOFF_T tmpoffset = 0;
1715 size_t tmplength = 0;
1716 int orig_type = b->type;
1717
1718 struct Buffer *signedfile = buf_pool_get();
1719
1720 buf_printf(signedfile, "%s.sig", tempfile);
1721
1722 /* decode to a tempfile, saving the original destination */
1723 fp = state->fp_out;
1724 state->fp_out = mutt_file_fopen(buf_string(signedfile), "w");
1725 if (!state->fp_out)
1726 {
1727 mutt_perror("%s", buf_string(signedfile));
1728 goto cleanup;
1729 }
1730 /* decoding the attachment changes the size and offset, so save a copy
1731 * of the "real" values now, and restore them after processing */
1732 tmplength = b->length;
1733 tmpoffset = b->offset;
1734
1735 /* if we are decoding binary bodies, we don't want to prefix each
1736 * line with the prefix or else the data will get corrupted. */
1737 const char *save_prefix = state->prefix;
1738 state->prefix = NULL;
1739
1740 mutt_decode_attachment(b, state);
1741
1742 b->length = ftello(state->fp_out);
1743 b->offset = 0;
1744 mutt_file_fclose(&state->fp_out);
1745
1746 /* restore final destination and substitute the tempfile for input */
1747 state->fp_out = fp;
1748 fp = state->fp_in;
1749 state->fp_in = mutt_file_fopen(buf_string(signedfile), "r");
1750
1751 /* restore the prefix */
1752 state->prefix = save_prefix;
1753
1754 b->type = orig_type;
1755
1756 fp_smime_err = mutt_file_mkstemp();
1757 if (!fp_smime_err)
1758 {
1759 mutt_perror(_("Can't create temporary file"));
1760 goto cleanup;
1761 }
1762
1763 crypt_current_time(state, "OpenSSL");
1764
1765 pid = smime_invoke_verify(NULL, &fp_smime_out, NULL, -1, -1, fileno(fp_smime_err),
1766 tempfile, buf_string(signedfile), 0);
1767 if (pid != -1)
1768 {
1769 fflush(fp_smime_out);
1770 mutt_file_fclose(&fp_smime_out);
1771
1772 if (filter_wait(pid))
1773 {
1774 badsig = -1;
1775 }
1776 else
1777 {
1778 char *line = NULL;
1779 size_t linelen;
1780
1781 fflush(fp_smime_err);
1782 rewind(fp_smime_err);
1783
1784 line = mutt_file_read_line(line, &linelen, fp_smime_err, NULL, MUTT_RL_NO_FLAGS);
1785 if (linelen && mutt_istr_equal(line, "verification successful"))
1786 badsig = 0;
1787
1788 FREE(&line);
1789 }
1790 }
1791
1792 fflush(fp_smime_err);
1793 rewind(fp_smime_err);
1794 mutt_file_copy_stream(fp_smime_err, state->fp_out);
1795 mutt_file_fclose(&fp_smime_err);
1796
1797 state_attach_puts(state, _("[-- End of OpenSSL output --]\n\n"));
1798
1799 mutt_file_unlink(buf_string(signedfile));
1800
1801 b->length = tmplength;
1802 b->offset = tmpoffset;
1803
1804 /* restore the original source stream */
1805 mutt_file_fclose(&state->fp_in);
1806 state->fp_in = fp;
1807
1808cleanup:
1809 buf_pool_release(&signedfile);
1810 return badsig;
1811}
1812
1822static struct Body *smime_handle_entity(struct Body *b, struct State *state, FILE *fp_out_file)
1823{
1824 struct Buffer *tmpfname = buf_pool_get();
1825 FILE *fp_smime_out = NULL, *fp_smime_in = NULL, *fp_smime_err = NULL;
1826 FILE *fp_tmp = NULL, *fp_out = NULL;
1827 struct Body *p = NULL;
1828 pid_t pid = -1;
1830
1831 if (!(type & APPLICATION_SMIME))
1832 return NULL;
1833
1834 /* Because of the mutt_body_handler() we avoid the buffer pool. */
1835 fp_smime_out = mutt_file_mkstemp();
1836 if (!fp_smime_out)
1837 {
1838 mutt_perror(_("Can't create temporary file"));
1839 goto cleanup;
1840 }
1841
1842 fp_smime_err = mutt_file_mkstemp();
1843 if (!fp_smime_err)
1844 {
1845 mutt_perror(_("Can't create temporary file"));
1846 goto cleanup;
1847 }
1848
1849 buf_mktemp(tmpfname);
1850 fp_tmp = mutt_file_fopen(buf_string(tmpfname), "w+");
1851 if (!fp_tmp)
1852 {
1853 mutt_perror("%s", buf_string(tmpfname));
1854 goto cleanup;
1855 }
1856
1857 if (!mutt_file_seek(state->fp_in, b->offset, SEEK_SET))
1858 {
1859 goto cleanup;
1860 }
1861
1862 mutt_file_copy_bytes(state->fp_in, fp_tmp, b->length);
1863
1864 fflush(fp_tmp);
1865 mutt_file_fclose(&fp_tmp);
1866
1867 if ((type & SEC_ENCRYPT) &&
1868 ((pid = smime_invoke_decrypt(&fp_smime_in, NULL, NULL, -1, fileno(fp_smime_out),
1869 fileno(fp_smime_err), buf_string(tmpfname))) == -1))
1870 {
1871 mutt_file_unlink(buf_string(tmpfname));
1872 if (state->flags & STATE_DISPLAY)
1873 {
1874 state_attach_puts(state, _("[-- Error: unable to create OpenSSL subprocess --]\n"));
1875 }
1876 goto cleanup;
1877 }
1878 else if ((type & SEC_SIGNOPAQUE) &&
1879 ((pid = smime_invoke_verify(&fp_smime_in, NULL, NULL, -1,
1880 fileno(fp_smime_out), fileno(fp_smime_err), NULL,
1881 buf_string(tmpfname), SEC_SIGNOPAQUE)) == -1))
1882 {
1883 mutt_file_unlink(buf_string(tmpfname));
1884 if (state->flags & STATE_DISPLAY)
1885 {
1886 state_attach_puts(state, _("[-- Error: unable to create OpenSSL subprocess --]\n"));
1887 }
1888 goto cleanup;
1889 }
1890
1891 if (type & SEC_ENCRYPT)
1892 {
1895 fputs(SmimePass, fp_smime_in);
1896 fputc('\n', fp_smime_in);
1897 }
1898
1899 mutt_file_fclose(&fp_smime_in);
1900
1901 filter_wait(pid);
1902 mutt_file_unlink(buf_string(tmpfname));
1903
1904 if (state->flags & STATE_DISPLAY)
1905 {
1906 fflush(fp_smime_err);
1907 rewind(fp_smime_err);
1908
1909 const int c = fgetc(fp_smime_err);
1910 if (c != EOF)
1911 {
1912 ungetc(c, fp_smime_err);
1913
1914 crypt_current_time(state, "OpenSSL");
1915 mutt_file_copy_stream(fp_smime_err, state->fp_out);
1916 state_attach_puts(state, _("[-- End of OpenSSL output --]\n\n"));
1917 }
1918
1919 if (type & SEC_ENCRYPT)
1920 {
1921 state_attach_puts(state, _("[-- The following data is S/MIME encrypted --]\n"));
1922 }
1923 else
1924 {
1925 state_attach_puts(state, _("[-- The following data is S/MIME signed --]\n"));
1926 }
1927 }
1928
1929 fflush(fp_smime_out);
1930 rewind(fp_smime_out);
1931
1932 if (type & SEC_ENCRYPT)
1933 {
1934 /* void the passphrase, even if that wasn't the problem */
1935 if (fgetc(fp_smime_out) == EOF)
1936 {
1937 mutt_error(_("Decryption failed"));
1939 }
1940 rewind(fp_smime_out);
1941 }
1942
1943 if (fp_out_file)
1944 {
1945 fp_out = fp_out_file;
1946 }
1947 else
1948 {
1949 fp_out = mutt_file_mkstemp();
1950 if (!fp_out)
1951 {
1952 mutt_perror(_("Can't create temporary file"));
1953 goto cleanup;
1954 }
1955 }
1956 char buf[8192] = { 0 };
1957 while (fgets(buf, sizeof(buf) - 1, fp_smime_out))
1958 {
1959 const size_t len = mutt_str_len(buf);
1960 if ((len > 1) && (buf[len - 2] == '\r'))
1961 {
1962 buf[len - 2] = '\n';
1963 buf[len - 1] = '\0';
1964 }
1965 fputs(buf, fp_out);
1966 }
1967 fflush(fp_out);
1968 rewind(fp_out);
1969
1970 const long size = mutt_file_get_size_fp(fp_out);
1971 if (size == 0)
1972 {
1973 goto cleanup;
1974 }
1975 p = mutt_read_mime_header(fp_out, 0);
1976 if (p)
1977 {
1978 p->length = size - p->offset;
1979
1980 mutt_parse_part(fp_out, p);
1981
1982 if (state->flags & STATE_DISPLAY)
1984
1985 /* Store any protected headers in the parent so they can be
1986 * accessed for index updates after the handler recursion is done.
1987 * This is done before the handler to prevent a nested encrypted
1988 * handler from freeing the headers. */
1990 b->mime_headers = p->mime_headers;
1991 p->mime_headers = NULL;
1992
1993 if (state->fp_out)
1994 {
1995 rewind(fp_out);
1996 FILE *fp_tmp_buffer = state->fp_in;
1997 state->fp_in = fp_out;
1998 mutt_body_handler(p, state);
1999 state->fp_in = fp_tmp_buffer;
2000 }
2001
2002 /* Embedded multipart signed protected headers override the
2003 * encrypted headers. We need to do this after the handler so
2004 * they can be printed in the pager. */
2005 if (!(type & SMIME_SIGN) && mutt_is_multipart_signed(p) && p->parts &&
2006 p->parts->mime_headers)
2007 {
2010 p->parts->mime_headers = NULL;
2011 }
2012 }
2013 mutt_file_fclose(&fp_smime_out);
2014
2015 if (!fp_out_file)
2016 {
2017 mutt_file_fclose(&fp_out);
2018 mutt_file_unlink(buf_string(tmpfname));
2019 }
2020 fp_out = NULL;
2021
2022 if (state->flags & STATE_DISPLAY)
2023 {
2024 if (type & SEC_ENCRYPT)
2025 state_attach_puts(state, _("[-- End of S/MIME encrypted data --]\n"));
2026 else
2027 state_attach_puts(state, _("[-- End of S/MIME signed data --]\n"));
2028 }
2029
2030 if (type & SEC_SIGNOPAQUE)
2031 {
2032 char *line = NULL;
2033 size_t linelen;
2034
2035 rewind(fp_smime_err);
2036
2037 line = mutt_file_read_line(line, &linelen, fp_smime_err, NULL, MUTT_RL_NO_FLAGS);
2038 if (linelen && mutt_istr_equal(line, "verification successful"))
2039 b->goodsig = true;
2040 FREE(&line);
2041 }
2042 else if (p)
2043 {
2044 b->goodsig = p->goodsig;
2045 b->badsig = p->badsig;
2046 }
2047
2048cleanup:
2049 mutt_file_fclose(&fp_smime_out);
2050 mutt_file_fclose(&fp_smime_err);
2051 mutt_file_fclose(&fp_tmp);
2052 mutt_file_fclose(&fp_out);
2053 buf_pool_release(&tmpfname);
2054 return p;
2055}
2056
2060int smime_class_decrypt_mime(FILE *fp_in, FILE **fp_out, struct Body *b, struct Body **b_dec)
2061{
2062 struct State state = { 0 };
2063 LOFF_T tmpoffset = b->offset;
2064 size_t tmplength = b->length;
2065 int rc = -1;
2066
2068 return -1;
2069
2070 if (b->parts)
2071 return -1;
2072
2073 state.fp_in = fp_in;
2074 if (!mutt_file_seek(state.fp_in, b->offset, SEEK_SET))
2075 {
2076 return -1;
2077 }
2078
2079 FILE *fp_tmp = mutt_file_mkstemp();
2080 if (!fp_tmp)
2081 {
2082 mutt_perror(_("Can't create temporary file"));
2083 return -1;
2084 }
2085
2086 state.fp_out = fp_tmp;
2087 mutt_decode_attachment(b, &state);
2088 fflush(fp_tmp);
2089 b->length = ftello(state.fp_out);
2090 b->offset = 0;
2091 rewind(fp_tmp);
2092 state.fp_in = fp_tmp;
2093 state.fp_out = 0;
2094
2096 if (!*fp_out)
2097 {
2098 mutt_perror(_("Can't create temporary file"));
2099 goto bail;
2100 }
2101
2102 *b_dec = smime_handle_entity(b, &state, *fp_out);
2103 if (!*b_dec)
2104 goto bail;
2105
2106 (*b_dec)->goodsig = b->goodsig;
2107 (*b_dec)->badsig = b->badsig;
2108 rc = 0;
2109
2110bail:
2111 b->length = tmplength;
2112 b->offset = tmpoffset;
2113 mutt_file_fclose(&fp_tmp);
2114 if (*fp_out)
2115 rewind(*fp_out);
2116
2117 return rc;
2118}
2119
2123int smime_class_application_handler(struct Body *b, struct State *state)
2124{
2125 int rc = -1;
2126
2127 /* clear out any mime headers before the handler, so they can't be spoofed. */
2129
2130 struct Body *tattach = smime_handle_entity(b, state, NULL);
2131 if (tattach)
2132 {
2133 rc = 0;
2134 mutt_body_free(&tattach);
2135 }
2136 return rc;
2137}
2138
2143{
2144 struct SmimeKey *key = NULL;
2145 const char *prompt = NULL;
2146 const char *letters = NULL;
2147 const char *choices = NULL;
2148 int choice;
2149
2151 return e->security;
2152
2154
2155 /* Opportunistic encrypt is controlling encryption.
2156 * NOTE: "Signing" and "Clearing" only adjust the sign bit, so we have different
2157 * letter choices for those. */
2158 const bool c_crypt_opportunistic_encrypt = cs_subset_bool(NeoMutt->sub, "crypt_opportunistic_encrypt");
2159 if (c_crypt_opportunistic_encrypt && (e->security & SEC_OPPENCRYPT))
2160 {
2161 /* L10N: S/MIME options (opportunistic encryption is on) */
2162 prompt = _("S/MIME (s)ign, encrypt (w)ith, sign (a)s, (c)lear, or (o)ppenc mode off?");
2163 /* L10N: S/MIME options (opportunistic encryption is on) */
2164 letters = _("swaco");
2165 choices = "SwaCo";
2166 }
2167 else if (c_crypt_opportunistic_encrypt)
2168 {
2169 /* Opportunistic encryption option is set, but is toggled off
2170 * for this message. */
2171 /* L10N: S/MIME options (opportunistic encryption is off) */
2172 prompt = _("S/MIME (e)ncrypt, (s)ign, encrypt (w)ith, sign (a)s, (b)oth, (c)lear, or (o)ppenc mode?");
2173 /* L10N: S/MIME options (opportunistic encryption is off) */
2174 letters = _("eswabco");
2175 choices = "eswabcO";
2176 }
2177 else
2178 {
2179 /* Opportunistic encryption is unset */
2180 /* L10N: S/MIME options */
2181 prompt = _("S/MIME (e)ncrypt, (s)ign, encrypt (w)ith, sign (a)s, (b)oth, or (c)lear?");
2182 /* L10N: S/MIME options */
2183 letters = _("eswabc");
2184 choices = "eswabc";
2185 }
2186
2187 choice = mw_multi_choice(prompt, letters);
2188 if (choice > 0)
2189 {
2190 switch (choices[choice - 1])
2191 {
2192 case 'a': /* sign (a)s */
2193 key = smime_ask_for_key(_("Sign as: "), KEYFLAG_CANSIGN, false);
2194 if (key)
2195 {
2196 cs_subset_str_string_set(NeoMutt->sub, "smime_sign_as", key->hash, NULL);
2197 smime_key_free(&key);
2198
2199 e->security |= SEC_SIGN;
2200
2201 /* probably need a different passphrase */
2203 }
2204
2205 break;
2206
2207 case 'b': /* (b)oth */
2208 e->security |= (SEC_ENCRYPT | SEC_SIGN);
2209 break;
2210
2211 case 'c': /* (c)lear */
2212 e->security &= ~(SEC_ENCRYPT | SEC_SIGN);
2213 break;
2214
2215 case 'C':
2216 e->security &= ~SEC_SIGN;
2217 break;
2218
2219 case 'e': /* (e)ncrypt */
2220 e->security |= SEC_ENCRYPT;
2221 e->security &= ~SEC_SIGN;
2222 break;
2223
2224 case 'O': /* oppenc mode on */
2227 break;
2228
2229 case 'o': /* oppenc mode off */
2230 e->security &= ~SEC_OPPENCRYPT;
2231 break;
2232
2233 case 'S': /* (s)ign in oppenc mode */
2234 e->security |= SEC_SIGN;
2235 break;
2236
2237 case 's': /* (s)ign */
2238 e->security &= ~SEC_ENCRYPT;
2239 e->security |= SEC_SIGN;
2240 break;
2241
2242 case 'w': /* encrypt (w)ith */
2243 {
2244 e->security |= SEC_ENCRYPT;
2245 do
2246 {
2247 struct Buffer *errmsg = buf_pool_get();
2248 int rc = CSR_SUCCESS;
2249 switch (mw_multi_choice(_("Choose algorithm family: (1) DES, (2) RC2, (3) AES, or (c)lear?"),
2250 // L10N: Options for: Choose algorithm family: (1) DES, (2) RC2, (3) AES, or (c)lear?
2251 _("123c")))
2252 {
2253 case 1:
2254 switch (choice = mw_multi_choice(_("(1) DES, (2) Triple-DES?"),
2255 // L10N: Options for: (1) DES, (2) Triple-DES
2256 _("12")))
2257 {
2258 case 1:
2259 rc = cs_subset_str_string_set(NeoMutt->sub, "smime_encrypt_with",
2260 "des", errmsg);
2261 break;
2262 case 2:
2263 rc = cs_subset_str_string_set(NeoMutt->sub, "smime_encrypt_with",
2264 "des3", errmsg);
2265 break;
2266 }
2267 break;
2268
2269 case 2:
2270 switch (choice = mw_multi_choice(_("(1) RC2-40, (2) RC2-64, (3) RC2-128?"),
2271 // L10N: Options for: (1) RC2-40, (2) RC2-64, (3) RC2-128
2272 _("123")))
2273 {
2274 case 1:
2275 rc = cs_subset_str_string_set(NeoMutt->sub, "smime_encrypt_with",
2276 "rc2-40", errmsg);
2277 break;
2278 case 2:
2279 rc = cs_subset_str_string_set(NeoMutt->sub, "smime_encrypt_with",
2280 "rc2-64", errmsg);
2281 break;
2282 case 3:
2283 rc = cs_subset_str_string_set(NeoMutt->sub, "smime_encrypt_with",
2284 "rc2-128", errmsg);
2285 break;
2286 }
2287 break;
2288
2289 case 3:
2290 switch (choice = mw_multi_choice(_("(1) AES128, (2) AES192, (3) AES256?"),
2291 // L10N: Options for: (1) AES128, (2) AES192, (3) AES256
2292 _("123")))
2293 {
2294 case 1:
2295 rc = cs_subset_str_string_set(NeoMutt->sub, "smime_encrypt_with",
2296 "aes128", errmsg);
2297 break;
2298 case 2:
2299 rc = cs_subset_str_string_set(NeoMutt->sub, "smime_encrypt_with",
2300 "aes192", errmsg);
2301 break;
2302 case 3:
2303 rc = cs_subset_str_string_set(NeoMutt->sub, "smime_encrypt_with",
2304 "aes256", errmsg);
2305 break;
2306 }
2307 break;
2308
2309 case 4:
2310 rc = cs_subset_str_string_set(NeoMutt->sub, "smime_encrypt_with", NULL, errmsg);
2311 /* (c)lear */
2313
2314 case -1: /* Ctrl-G or Enter */
2315 choice = 0;
2316 break;
2317 }
2318
2319 if ((CSR_RESULT(rc) != CSR_SUCCESS) && !buf_is_empty(errmsg))
2320 mutt_error("%s", buf_string(errmsg));
2321
2322 buf_pool_release(&errmsg);
2323 } while (choice == -1);
2324 break;
2325 }
2326 }
2327 }
2328
2329 return e->security;
2330}
2331
2338 // clang-format off
2347 { -1, -1, NULL, NULL },
2348 // clang-format on
2349};
void mutt_addr_free(struct Address **ptr)
Free a single Address.
Definition: address.c:462
Email Address Handling.
Email Aliases.
void mutt_expand_aliases(struct AddressList *al)
Expand aliases in a List of Addresses.
Definition: alias.c:295
bool mutt_addr_is_user(const struct Address *addr)
Does the address belong to the user.
Definition: alias.c:600
int buf_printf(struct Buffer *buf, const char *fmt,...)
Format a string overwriting a Buffer.
Definition: buffer.c:161
size_t buf_len(const struct Buffer *buf)
Calculate the length of a Buffer.
Definition: buffer.c:491
void buf_dealloc(struct Buffer *buf)
Release the memory allocated by a buffer.
Definition: buffer.c:377
void buf_reset(struct Buffer *buf)
Reset an existing Buffer.
Definition: buffer.c:76
bool buf_is_empty(const struct Buffer *buf)
Is the Buffer empty?
Definition: buffer.c:291
size_t buf_strcpy(struct Buffer *buf, const char *s)
Copy a string into a Buffer.
Definition: buffer.c:395
size_t buf_copy(struct Buffer *dst, const struct Buffer *src)
Copy a Buffer's contents to another Buffer.
Definition: buffer.c:601
char * buf_strdup(const struct Buffer *buf)
Copy a Buffer's string.
Definition: buffer.c:571
void buf_alloc(struct Buffer *buf, size_t new_size)
Make sure a buffer can store at least new_size bytes.
Definition: buffer.c:337
static const char * buf_string(const struct Buffer *buf)
Convert a buffer to a const char * "string".
Definition: buffer.h:96
const char * cs_subset_string(const struct ConfigSubset *sub, const char *name)
Get a string config item by name.
Definition: helpers.c:291
short cs_subset_number(const struct ConfigSubset *sub, const char *name)
Get a number config item by name.
Definition: helpers.c:143
const char * cs_subset_path(const struct ConfigSubset *sub, const char *name)
Get a path config item by name.
Definition: helpers.c:168
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition: helpers.c:47
const struct Expando * cs_subset_expando(const struct ConfigSubset *sub, const char *name)
Get an Expando config item by name.
Definition: config_type.c:357
Convenience wrapper for the config headers.
#define CSR_RESULT(x)
Definition: set.h:52
#define CSR_SUCCESS
Action completed successfully.
Definition: set.h:35
int mutt_copy_message(FILE *fp_out, struct Email *e, struct Message *msg, CopyMessageFlags cmflags, CopyHeaderFlags chflags, int wraplen)
Copy a message from a Mailbox.
Definition: copy.c:907
Duplicate the structure of an entire email.
#define CH_NONEWLINE
Don't output terminating newline after the header.
Definition: copy.h:62
#define CH_WEED
Weed the headers?
Definition: copy.h:55
#define MUTT_CM_DECODE_SMIME
Used for decoding S/MIME messages.
Definition: copy.h:48
#define CH_MIME
Ignore MIME fields.
Definition: copy.h:63
#define MUTT_CM_NO_FLAGS
No flags are set.
Definition: copy.h:37
#define MUTT_CM_DECODE_CRYPT
Definition: copy.h:50
#define CH_NO_FLAGS
No flags are set.
Definition: copy.h:53
Convenience wrapper for the core headers.
void crypt_opportunistic_encrypt(struct Email *e)
Can all recipients be determined.
Definition: crypt.c:1045
SecurityFlags mutt_is_multipart_signed(struct Body *b)
Is a message signed?
Definition: crypt.c:408
SecurityFlags mutt_is_application_smime(struct Body *b)
Does the message use S/MIME?
Definition: crypt.c:609
void crypt_current_time(struct State *state, const char *app_name)
Print the current time.
Definition: crypt.c:65
void crypt_convert_to_7bit(struct Body *b)
Convert an email to 7bit encoding.
Definition: crypt.c:809
Signing/encryption multiplexor.
void crypt_smime_void_passphrase(void)
Wrapper for CryptModuleSpecs::void_passphrase()
Definition: cryptglue.c:412
Wrapper around crypto functions.
int mutt_any_key_to_continue(const char *s)
Prompt the user to 'press any key' and wait.
Definition: curs_lib.c:173
void mutt_endwin(void)
Shutdown curses.
Definition: curs_lib.c:151
@ ED_SMIME_CMD
Smime Command ED_SMI_ ExpandoDataSmimeCmd.
Definition: domain.h:55
@ ED_GLOBAL
Global ED_GLO_ ExpandoDataGlobal.
Definition: domain.h:44
Edit a string.
void mutt_body_free(struct Body **ptr)
Free a Body.
Definition: body.c:58
struct Body * mutt_body_new(void)
Create a new Body.
Definition: body.c:44
Structs that make up an email.
void mutt_parse_part(FILE *fp, struct Body *b)
Parse a MIME part.
Definition: parse.c:1822
struct Body * mutt_read_mime_header(FILE *fp, bool digest)
Parse a MIME header.
Definition: parse.c:1362
void mutt_env_free(struct Envelope **ptr)
Free an Envelope.
Definition: envelope.c:126
Parse Expando string.
int expando_render(const struct Expando *exp, const struct ExpandoRenderData *rdata, void *data, MuttFormatFlags flags, int max_cols, struct Buffer *buf)
Render an Expando + data into a string.
Definition: expando.c:118
int mutt_file_copy_stream(FILE *fp_in, FILE *fp_out)
Copy the contents of one file into another.
Definition: file.c:287
void buf_quote_filename(struct Buffer *buf, const char *filename, bool add_outer)
Quote a filename to survive the shell's quoting rules.
Definition: file.c:933
char * mutt_file_read_line(char *line, size_t *size, FILE *fp, int *line_num, ReadLineFlags flags)
Read a line from a file.
Definition: file.c:808
int mutt_file_copy_bytes(FILE *fp_in, FILE *fp_out, size_t size)
Copy some content from one file to another.
Definition: file.c:257
long mutt_file_get_size_fp(FILE *fp)
Get the size of a file.
Definition: file.c:1537
bool mutt_file_seek(FILE *fp, LOFF_T offset, int whence)
Wrapper for fseeko with error handling.
Definition: file.c:778
void mutt_file_unlink(const char *s)
Delete a file, carefully.
Definition: file.c:221
#define mutt_file_fclose(FP)
Definition: file.h:138
#define mutt_file_fopen(PATH, MODE)
Definition: file.h:137
#define MUTT_RL_NO_FLAGS
No flags are set.
Definition: file.h:40
char ** EnvList
Private copy of the environment variables.
Definition: globals.c:75
int smime_class_application_handler(struct Body *b, struct State *state)
Manage the MIME type "application/pgp" or "application/smime" - Implements CryptModuleSpecs::applicat...
Definition: smime.c:2123
int smime_class_decrypt_mime(FILE *fp_in, FILE **fp_out, struct Body *b, struct Body **b_dec)
Decrypt an encrypted MIME part - Implements CryptModuleSpecs::decrypt_mime() -.
Definition: smime.c:2060
char * smime_class_find_keys(const struct AddressList *al, bool oppenc_mode)
Find the keyids of the recipients of a message - Implements CryptModuleSpecs::find_keys() -.
Definition: smime.c:799
SecurityFlags smime_class_send_menu(struct Email *e)
Ask the user whether to sign and/or encrypt the email - Implements CryptModuleSpecs::send_menu() -.
Definition: smime.c:2142
struct Body * smime_class_sign_message(struct Body *b, const struct AddressList *from)
Cryptographically sign the Body of a message - Implements CryptModuleSpecs::sign_message() -.
Definition: smime.c:1499
struct Body * smime_class_build_smime_entity(struct Body *b, char *certlist)
Encrypt the email body to all recipients - Implements CryptModuleSpecs::smime_build_smime_entity() -.
Definition: smime.c:1334
void smime_class_getkeys(struct Envelope *env)
Get the S/MIME keys required to encrypt this email - Implements CryptModuleSpecs::smime_getkeys() -.
Definition: smime.c:759
void smime_class_invoke_import(const char *infile, const char *mailbox)
Add a certificate and update index file (externally) - Implements CryptModuleSpecs::smime_invoke_impo...
Definition: smime.c:1132
int smime_class_verify_sender(struct Email *e, struct Message *msg)
Does the sender match the certificate? - Implements CryptModuleSpecs::smime_verify_sender() -.
Definition: smime.c:1208
bool smime_class_valid_passphrase(void)
Ensure we have a valid passphrase - Implements CryptModuleSpecs::valid_passphrase() -.
Definition: smime.c:165
int smime_class_verify_one(struct Body *b, struct State *state, const char *tempfile)
Check a signed MIME part against a signature - Implements CryptModuleSpecs::verify_one() -.
Definition: smime.c:1708
void smime_class_void_passphrase(void)
Forget the cached passphrase - Implements CryptModuleSpecs::void_passphrase() -.
Definition: smime.c:156
void smime_command_a(const struct ExpandoNode *node, void *data, MuttFormatFlags flags, struct Buffer *buf)
Smime Command: algorithm - Implements ExpandoRenderData::get_string() -.
Definition: smime.c:203
void smime_command_C(const struct ExpandoNode *node, void *data, MuttFormatFlags flags, struct Buffer *buf)
Smime Command: CA location - Implements ExpandoRenderData::get_string() -.
Definition: smime.c:231
void smime_command_k(const struct ExpandoNode *node, void *data, MuttFormatFlags flags, struct Buffer *buf)
Smime Command: Key-pair - Implements ExpandoRenderData::get_string() -.
Definition: smime.c:308
void smime_command_i(const struct ExpandoNode *node, void *data, MuttFormatFlags flags, struct Buffer *buf)
Smime Command: Intermediate certificates - Implements ExpandoRenderData::get_string() -.
Definition: smime.c:294
void smime_command_f(const struct ExpandoNode *node, void *data, MuttFormatFlags flags, struct Buffer *buf)
Smime Command: Filename of message - Implements ExpandoRenderData::get_string() -.
Definition: smime.c:280
void smime_command_s(const struct ExpandoNode *node, void *data, MuttFormatFlags flags, struct Buffer *buf)
Smime Command: Filename of signature - Implements ExpandoRenderData::get_string() -.
Definition: smime.c:322
void smime_command_d(const struct ExpandoNode *node, void *data, MuttFormatFlags flags, struct Buffer *buf)
Smime Command: Message digest algorithm - Implements ExpandoRenderData::get_string() -.
Definition: smime.c:266
void smime_command_c(const struct ExpandoNode *node, void *data, MuttFormatFlags flags, struct Buffer *buf)
Smime Command: certificate IDs - Implements ExpandoRenderData::get_string() -.
Definition: smime.c:217
struct SmimeKey * dlg_smime(struct SmimeKey *keys, const char *query)
Get the user to select a key -.
Definition: dlg_smime.c:194
int mw_get_field(const char *prompt, struct Buffer *buf, CompletionFlags complete, enum HistoryClass hclass, const struct CompleteOps *comp_api, void *cdata)
Ask the user for a string -.
Definition: window.c:274
int mw_multi_choice(const char *prompt, const char *letters)
Offer the user a multiple choice question -.
Definition: question.c:63
int mutt_protected_headers_handler(struct Body *b_email, struct State *state)
Handler for protected headers - Implements handler_t -.
Definition: crypt.c:1117
#define mutt_error(...)
Definition: logging2.h:92
#define mutt_message(...)
Definition: logging2.h:91
#define mutt_debug(LEVEL,...)
Definition: logging2.h:89
#define mutt_perror(...)
Definition: logging2.h:93
Convenience wrapper for the gui headers.
int mutt_body_handler(struct Body *b, struct State *state)
Handler for the Body of an email.
Definition: handler.c:1631
void mutt_decode_attachment(const struct Body *b, struct State *state)
Decode an email's attachment.
Definition: handler.c:1905
Decide how to display email content.
int mutt_write_mime_header(struct Body *b, FILE *fp, struct ConfigSubset *sub)
Create a MIME header.
Definition: header.c:756
Read/write command history from/to a file.
@ HC_OTHER
Miscellaneous strings.
Definition: lib.h:59
@ LL_DEBUG2
Log at debug level 2.
Definition: logging2.h:44
#define FREE(x)
Definition: memory.h:55
#define MUTT_MEM_CALLOC(n, type)
Definition: memory.h:40
#define MUTT_MEM_REALLOC(pptr, n, type)
Definition: memory.h:43
static int search(struct Menu *menu, int op)
Search a menu.
Definition: functions.c:58
@ ENC_7BIT
7-bit text
Definition: mime.h:49
@ ENC_BASE64
Base-64 encoded text.
Definition: mime.h:52
@ TYPE_MULTIPART
Type: 'multipart/*'.
Definition: mime.h:37
@ TYPE_APPLICATION
Type: 'application/*'.
Definition: mime.h:33
@ DISP_ATTACH
Content is attached.
Definition: mime.h:63
@ DISP_INLINE
Content is inline.
Definition: mime.h:62
void mutt_generate_boundary(struct ParameterList *pl)
Create a unique boundary id for a MIME part.
Definition: multipart.c:86
time_t mutt_date_add_timeout(time_t now, time_t timeout)
Safely add a timeout to a given time_t value.
Definition: date.c:890
time_t mutt_date_now(void)
Return the number of seconds since the Unix epoch.
Definition: date.c:456
int filter_wait(pid_t pid)
Wait for the exit of a process and return its status.
Definition: filter.c:220
pid_t filter_create_fd(const char *cmd, FILE **fp_in, FILE **fp_out, FILE **fp_err, int fdin, int fdout, int fderr, char **envlist)
Run a command on a pipe (optionally connect stdin/stdout)
Definition: filter.c:62
Convenience wrapper for the library headers.
#define FALLTHROUGH
Definition: lib.h:111
#define _(a)
Definition: message.h:28
void state_attach_puts(struct State *state, const char *t)
Write a string to the state.
Definition: state.c:103
#define STATE_DISPLAY
Output is displayed to the user.
Definition: state.h:33
bool mutt_istr_equal(const char *a, const char *b)
Compare two strings, ignoring case.
Definition: string.c:672
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition: string.c:253
int mutt_str_asprintf(char **strp, const char *fmt,...)
Definition: string.c:803
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition: string.c:660
const char * mutt_istr_find(const char *haystack, const char *needle)
Find first occurrence of string (ignoring case)
Definition: string.c:521
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition: string.c:496
size_t mutt_str_copy(char *dest, const char *src, size_t dsize)
Copy a string into a buffer (guaranteeing NUL-termination)
Definition: string.c:581
size_t mutt_istr_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix, ignoring case.
Definition: string.c:242
Many unsorted constants and some structs.
#define MUTT_COMP_PASS
Password mode (no echo)
Definition: mutt.h:58
#define MUTT_COMP_UNBUFFERED
Ignore macro buffer.
Definition: mutt.h:59
#define MUTT_COMP_NO_FLAGS
No flags are set.
Definition: mutt.h:56
#define PATH_MAX
Definition: mutt.h:42
void mutt_clear_error(void)
Clear the message line (bottom line of screen)
Definition: mutt_logging.c:74
NeoMutt Logging.
void buf_expand_path(struct Buffer *buf)
Create the canonical path.
Definition: muttlib.c:315
Some miscellaneous functions.
uint16_t SecurityFlags
Flags, e.g. SEC_ENCRYPT.
Definition: lib.h:76
#define SEC_OPPENCRYPT
Opportunistic encrypt mode.
Definition: lib.h:86
uint16_t KeyFlags
Flags describing PGP/SMIME keys, e.g. KEYFLAG_CANSIGN.
Definition: lib.h:125
#define SEC_SIGNOPAQUE
Email has an opaque signature (encrypted)
Definition: lib.h:83
#define SMIME_SIGN
Definition: lib.h:103
#define KEYFLAG_CANENCRYPT
Key is suitable for encryption.
Definition: lib.h:128
#define APPLICATION_SMIME
Use SMIME to encrypt/sign.
Definition: lib.h:91
#define SEC_ENCRYPT
Email is encrypted.
Definition: lib.h:78
#define WithCrypto
Definition: lib.h:116
#define SEC_SIGN
Email is signed.
Definition: lib.h:79
#define KEYFLAG_CANSIGN
Key is suitable for signing.
Definition: lib.h:127
void mutt_param_set(struct ParameterList *pl, const char *attribute, const char *value)
Set a Parameter.
Definition: parameter.c:111
struct Buffer * buf_pool_get(void)
Get a Buffer from the pool.
Definition: pool.c:81
void buf_pool_release(struct Buffer **ptr)
Return a Buffer to the pool.
Definition: pool.c:94
Ask the user a question.
#define TAILQ_FOREACH(var, head, field)
Definition: queue.h:725
#define TAILQ_FIRST(head)
Definition: queue.h:723
#define TAILQ_EMPTY(head)
Definition: queue.h:721
#define MUTT_FORMAT_NO_FLAGS
No flags are set.
Definition: render.h:33
uint8_t MuttFormatFlags
Flags for expando_render(), e.g. MUTT_FORMAT_FORCESUBJ.
Definition: render.h:32
int mutt_write_mime_body(struct Body *b, FILE *fp, struct ConfigSubset *sub)
Write a MIME part.
Definition: body.c:300
Convenience wrapper for the send headers.
struct Address * mutt_default_from(struct ConfigSubset *sub)
Get a default 'from' Address.
Definition: send.c:1461
GUI display the mailboxes in a side panel.
static struct Buffer SmimeIntermediateToUse
Smime intermediate certificate to use.
Definition: smime.c:80
static struct SmimeKey * smime_get_key_by_hash(const char *hash, bool only_public_key)
Find a key by its hash.
Definition: smime.c:533
static pid_t smime_invoke_sign(FILE **fp_smime_in, FILE **fp_smime_out, FILE **fp_smime_err, int fp_smime_infd, int fp_smime_outfd, int fp_smime_errfd, const char *fname)
Use SMIME to sign a file.
Definition: smime.c:1319
static char SmimePass[256]
Cached Smime Passphrase.
Definition: smime.c:71
static time_t SmimeExpTime
Unix time when SmimePass expires.
Definition: smime.c:73
static void getkeys(const char *mailbox)
Get the keys for a mailbox.
Definition: smime.c:723
static struct SmimeKey * smime_copy_key(struct SmimeKey *key)
Copy an SMIME key.
Definition: smime.c:131
const struct ExpandoRenderData SmimeCommandRenderData[]
Callbacks for Smime Command Expandos.
Definition: smime.c:68
static struct SmimeKey * smime_get_key_by_str(const char *str, KeyFlags abilities, bool only_public_key)
Find an SMIME key by string.
Definition: smime.c:640
void smime_init(void)
Initialise smime globals.
Definition: smime.c:85
static pid_t smime_invoke_verify(FILE **fp_smime_in, FILE **fp_smime_out, FILE **fp_smime_err, int fp_smime_infd, int fp_smime_outfd, int fp_smime_errfd, const char *fname, const char *sig_fname, int opaque)
Use SMIME to verify a file.
Definition: smime.c:1666
void smime_cleanup(void)
Clean up smime globals.
Definition: smime.c:95
static char * smime_extract_signer_certificate(const char *infile)
Extract the signer's certificate.
Definition: smime.c:1064
static struct Body * smime_handle_entity(struct Body *b, struct State *state, FILE *fp_out_file)
Handle type application/pkcs7-mime.
Definition: smime.c:1822
static struct SmimeKey * smime_parse_key(char *buf)
Parse an SMIME key block.
Definition: smime.c:402
static pid_t smime_invoke_decrypt(FILE **fp_smime_in, FILE **fp_smime_out, FILE **fp_smime_err, int fp_smime_infd, int fp_smime_outfd, int fp_smime_errfd, const char *fname)
Use SMIME to decrypt a file.
Definition: smime.c:1694
static struct SmimeKey * smime_get_candidates(const char *search, bool only_public_key)
Find keys matching a string.
Definition: smime.c:485
static struct Buffer SmimeKeyToUse
Smime key to use.
Definition: smime.c:76
static char * smime_extract_certificate(const char *infile)
Extract an SMIME certificate from a file.
Definition: smime.c:949
static int smime_handle_cert_email(const char *certificate, const char *mailbox, bool copy, char ***buffer, int *num)
Process an email containing certificates.
Definition: smime.c:847
static struct SmimeKey * smime_get_key_by_addr(const char *mailbox, KeyFlags abilities, bool only_public_key, bool oppenc_mode)
Find an SIME key by address.
Definition: smime.c:559
static pid_t smime_invoke(FILE **fp_smime_in, FILE **fp_smime_out, FILE **fp_smime_err, int fp_smime_infd, int fp_smime_outfd, int fp_smime_errfd, const char *fname, const char *sig_fname, const char *cryptalg, const char *digestalg, const char *key, const char *certificates, const char *intermediates, const struct Expando *exp)
Run an SMIME command.
Definition: smime.c:368
static struct Buffer SmimeCertToUse
Smime certificate to use.
Definition: smime.c:78
static pid_t smime_invoke_encrypt(FILE **fp_smime_in, FILE **fp_smime_out, FILE **fp_smime_err, int fp_smime_infd, int fp_smime_outfd, int fp_smime_errfd, const char *fname, const char *uids)
Use SMIME to encrypt a file.
Definition: smime.c:1292
static void smime_key_free(struct SmimeKey **keylist)
Free a list of SMIME keys.
Definition: smime.c:106
static char * openssl_md_to_smime_micalg(const char *md)
Change the algorithm names.
Definition: smime.c:1478
static void smime_command(struct Buffer *buf, struct SmimeCommandContext *cctx, const struct Expando *exp)
Format an SMIME command string.
Definition: smime.c:339
static struct SmimeKey * smime_ask_for_key(const char *prompt, KeyFlags abilities, bool only_public_key)
Ask the user to select a key.
Definition: smime.c:686
SMIME helper routines.
@ ED_SMI_MESSAGE_FILE
SmimeCommandContext.fname.
Definition: smime.h:81
@ ED_SMI_ALGORITHM
SmimeCommandContext.cryptalg.
Definition: smime.h:76
@ ED_SMI_SIGNATURE_FILE
SmimeCommandContext.sig_fname.
Definition: smime.h:82
@ ED_SMI_DIGEST_ALGORITHM
SmimeCommandContext.digestalg.
Definition: smime.h:78
@ ED_SMI_CERTIFICATE_IDS
SmimeCommandContext.certificates.
Definition: smime.h:77
@ ED_SMI_KEY
SmimeCommandContext.key.
Definition: smime.h:80
@ ED_SMI_INTERMEDIATE_IDS
SmimeCommandContext.intermediates.
Definition: smime.h:79
Key value store.
#define NONULL(x)
Definition: string2.h:37
An email address.
Definition: address.h:36
struct Buffer * mailbox
Mailbox and host address.
Definition: address.h:38
The body of an email.
Definition: body.h:36
char * d_filename
filename to be used for the content-disposition header If NULL, filename is used instead.
Definition: body.h:56
struct Body * parts
parts of a multipart or message/rfc822
Definition: body.h:73
LOFF_T offset
offset where the actual data begins
Definition: body.h:52
bool unlink
If true, filename should be unlink()ed before free()ing this structure.
Definition: body.h:68
bool badsig
Bad cryptographic signature (needed to check encrypted s/mime-signatures)
Definition: body.h:43
struct Envelope * mime_headers
Memory hole protected headers.
Definition: body.h:76
LOFF_T length
length (in bytes) of attachment
Definition: body.h:53
struct ParameterList parameter
Parameters of the content-type.
Definition: body.h:63
bool use_disp
Content-Disposition uses filename= ?
Definition: body.h:47
unsigned int disposition
content-disposition, ContentDisposition
Definition: body.h:42
struct Body * next
next attachment in the list
Definition: body.h:72
char * subtype
content-type subtype
Definition: body.h:61
unsigned int encoding
content-transfer-encoding, ContentEncoding
Definition: body.h:41
bool goodsig
Good cryptographic signature.
Definition: body.h:45
unsigned int type
content-type primary type, ContentType
Definition: body.h:40
char * filename
When sending a message, this is the file to which this structure refers.
Definition: body.h:59
String manipulation buffer.
Definition: buffer.h:36
size_t dsize
Length of data.
Definition: buffer.h:39
char * data
Pointer to data.
Definition: buffer.h:37
The envelope/body of an email.
Definition: email.h:39
struct Envelope * env
Envelope information.
Definition: email.h:68
SecurityFlags security
bit 0-10: flags, bit 11,12: application, bit 13: traditional pgp See: ncrypt/lib.h pgplib....
Definition: email.h:43
The header of an Email.
Definition: envelope.h:57
struct AddressList to
Email's 'To' list.
Definition: envelope.h:60
struct AddressList cc
Email's 'Cc' list.
Definition: envelope.h:61
struct AddressList sender
Email's sender.
Definition: envelope.h:63
struct AddressList from
Email's 'From' list.
Definition: envelope.h:59
Basic Expando Node.
Definition: node.h:67
Parsed Expando trees.
Definition: expando.h:41
A local copy of an email.
Definition: message.h:34
Container for Accounts, Notifications.
Definition: neomutt.h:42
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:46
Data for a SIME command.
Definition: smime.h:59
const char * sig_fname
s
Definition: smime.h:64
const char * intermediates
i
Definition: smime.h:66
const char * digestalg
d
Definition: smime.h:62
const char * cryptalg
a
Definition: smime.h:61
const char * key
k
Definition: smime.h:60
const char * fname
f
Definition: smime.h:63
const char * certificates
c
Definition: smime.h:65
An SIME key.
Definition: smime.h:44
KeyFlags flags
Definition: smime.h:50
char * hash
Definition: smime.h:46
struct SmimeKey * next
Definition: smime.h:51
char * issuer
Definition: smime.h:48
char * email
Definition: smime.h:45
char * label
Definition: smime.h:47
char trust
i=Invalid r=revoked e=expired u=unverified v=verified t=trusted
Definition: smime.h:49
Keep track when processing files.
Definition: state.h:48
StateFlags flags
Flags, e.g. STATE_DISPLAY.
Definition: state.h:52
FILE * fp_out
File to write to.
Definition: state.h:50
FILE * fp_in
File to read from.
Definition: state.h:49
const char * prefix
String to add to the beginning of each output line.
Definition: state.h:51
int cs_subset_str_string_set(const struct ConfigSubset *sub, const char *name, const char *value, struct Buffer *err)
Set a config item by string.
Definition: subset.c:386
#define buf_mktemp(buf)
Definition: tmp.h:33
#define mutt_file_mkstemp()
Definition: tmp.h:36
@ ED_GLO_CERTIFICATE_PATH
Path of Smime certificates.
Definition: uid.h:34